Nabla public interface is very small, it is composed of only three interfaces and two classes.

In order to differentiate a function using Nabla, a
user-defined function must be provided as an implementation
of the UnivariateDifferentiable interface. It is passed as
the single parameter to the differentiate
method of an AutomaticDifferentiator instance. If the
existing class does not already implements the
UnivariateDifferentiable interface, it has to be wrapped
when provided to the differentiator.
As an example, consider the following problem. We have
a model variable which is an instance of a
class with a method evaluate:
double evaluate(double first, double second)
We want to compute its partial derivative with respect to
the second parameter, when the first parameter value is 2.5
and the second parameter ranges from -1 to +1. Here is a way
to do that:
UnivariateDerivative derivative =
new AutomaticDifferentiator().differentiate(new UnivariateDifferentiable() {
public double f(double t) {
return model.evaluate(2.5, t);
}
});
The derivative instance created by Nabla provides a
method f which computes both the value of the
primitive function (as the initial instance does) and the
value of its first derivative with respect to its input
parameter. This method has the following signature:
DifferentialPair f(DifferentialPair t)
It is important to note this method does not
use double as its input parameter
but DifferentialPair. This allows to handle both
the basic usage when one wants to compute the derivative of
the f function when its parameter is the
independent variable, but also more advanced cases when the
parameter t is not an independent variable.
Let's say t is computed by another
function g from a variable x
(t = g(x)). The signature of the derivative
function produced by Nabla allows to chain calls and compute
the composite derivative. We compute
first dt/dx from the derivative
function g'(x), and then pass the resulting
differential pair to the derivative
function f'(t) in order to get the
result dy/dx.
DifferentialPair dTdX = gDerivative(DifferentialPair.newVariable(2.0));
DifferentialPair dYdX = fDerivative(dTdX);
Simple derivatives are computed by passing independent
variables to the derivative functions. An independent
variable is represented by a DifferentialPair
with a first derivative set to 1. Composite derivatives are
computed by passing dependent variables to the derivative
functions. A dependent variable is represented by
a DifferentialPair which has been computed by a
previous derivative function and which may have any value as
its first derivative. A constant may be represented by
a DifferentialPair with a first derivative set
to 0.
The DifferentialPair class provides a general
constructor to build an instance from its value and
derivative. For convenience, it also provides factory methods
to build independent variables and constants:
// value = 1.0, first derivative = 2.0
DifferentialPair aPair = new DifferentialPair(1.0, 2.0);
// this is equivalent to new DifferentialPair(3.5, 1.0);
DifferentialPair variable = DifferentialPair.newVariable(3.5);
// this is equivalent to new DifferentialPair(Math.PI, 0.0)
DifferentialPair constant = DifferentialPair.newConstant(Math.PI);
One important thing to note is a consequence of the fact that the
differentiate method returns a new object when
called. This implies that we end up with two different
instances of two different classes that compute roughly
similar things: the original instance and the newly created
object. If the implementation of the f method
does use some attribute of the original class, then the class
of the newly created object should also provide a way to get
this value.
An important design choice in Nabla is that the newly
created instance does not copy the state of the
original object at derivation time, but instead is
permanently and tightly linked to this original instance and
uses it to get the values it needs when it needs them (even
if they are stored in private attributes). A direct
implication is that if the state of the original object is
changed after differentiation, all subsequent calls
to the f method of the already created
differentiated instance will reflect these changes in their
behavior. There is no need to bother about updating the
differentiated instance, it is already up-to-date.
As an example, consider again the problem above, where we wanted the derivative of a model with respect to its second parameter. Now we want to compute the same derivative as previously but we also want to be able to change the value of the first parameter, instead of sticking to the value 2.5. Here is a way to do this:
public class SetableFirstParameterModel implements UnivariateDifferentiable {
private Model model;
private double firstParameter;
public SetableFirstParameterModel(Model model, double firstParameter) {
this.model = model;
this.firstParameter = firstParameter;
}
public void setFirstParameter(double firstParameter) {
this.firstParameter = firstParameter;
}
public double f(double t) {
return model.evaluate(firstParameter, t);
}
}
When we build the derivative of an instance of this class,
this derivative will keep a reference to its primitive in
order to access the firstParameter private
field. If this field is changed on the primitive instance by
calling the setFirstParameter method, the
derivative will see the change immediately.
SetableFirstParameterModel setable = new SetableFirstParameterModel(model, 2.5);
UnivariateDerivative derivative = new AutomaticDifferentiator().differentiate(setable);
DifferentialPair t = DifferentialPair.newVariable(2.0);
// derivative with respect to second parameter when first parameter equals 2.5
double der25 = derivative.f(t).getFirstDerivative();
// derivative with respect to second parameter when first parameter equals 3.0
setable.setFirstParameter(3.0);
double der30 = derivative.f(t).getFirstDerivative();
Since the automatic differentiator can analyze only bytecode,
functions calling native code cannot be handled this way by
Nabla. There is a fallback procedure using finite differences.
The differences package provides several classes
that implement the UnivariateDifferentiator interface:
TwoPointsScheme
,
FourPointsScheme
,
SixPointsScheme
and
EightPointsScheme
.
These classes need a step size at construction time. For each call
to the derivative instance f method, they will call the
f of the primitive instance n+1 times where
n is the number of points of the method. One evaluation
is at the location defined by the parameter and the n
other evaluations are regularly distributed around it. So the two points
method will evaluate the primitive function 3 times at t-h,
t and t+h while the eight points method will
evaluate it 9 times at t-4h, t-3h ...
t+4h where h is the step size.
The step size and the number of points must be chosen with care as they influence both the accuracy of the result (which is only an approximation) and the computational cost. Small step size improve theoretical accuracy up to the point where numerical cancellations due to the finite precision of double numbers exceed the theoretical error due to finite differences modeling. Large number of points improve the accuracy but imply a large number of functions evaluation which can become prohibitive. There is no best choice that fits all needs, the right choice is problem-dependent.