Nabla is an automatic differentiator for mathematical functions.
Just like the mathematical Nabla operator transforms a function into its differential, the Nabla library transforms an existing java object implementing a function
double f(double) { ... }
into another java object that in addition to computing the
value of f like the original one also computes
its derivative. The created object is built by applying the
classical exact differentiation rules to the function
underlying expressions. There are no approximations
and no step sizes.
This approach has the following benefits:
The derivative instance remains tightly bound to the base instance (which is refered to as its primitive) throughout its lifetime. If the internal state of the primitive instance is mutated after Nabla has performed the transformation, the already created derivative instance which is bound to its primitive will use this mutated state for its computation automatically.
The following example should explain better what Nabla can do.
Let's consider a simple problem: we want to find the maximal value of a function:
f(t) = (6-t)/3 + cos(4t-4) e-0.9t

The maximal value is reached when the first derivative of
the function is equal to zero. So we need to compute the
first derivative f'(t) and find its
roots. Nabla will help in the first part:
computing f'(t).
The maximal value is reached when the first derivative of
the function is equal to zero. So we need to compute the
first derivative f'(t) and find its
roots. Nabla will help in the first part:
computing f'(t). We start by implementing the
function f(t):
UnivariateDifferentiable function = new UnivariateDifferentiable() {
public double f(double t) {
return (6 - t) / 3 + Math.cos(4 * t - 4) * Math.exp(-0.9 * t);
}
};
We use the Nabla automatic differentiator to differentiate our function and obtain an object implementing the derivative:
UnivariateDifferentiator differentiator = new AutomaticDifferentiator();
final UnivariateDerivative derivative = differentiator.differentiate(function);
We get the maximal value by calling a solver on the derivative. In this example, we will use the Brent solver from the commons-math library. Functions passed to any commons-math solver must implement a specific interface: UnivariateRealFunction . In order to comply with this requirement, we wrap the derivative object into another object adapting the interface and pass this wrapped derivative to the solver:
UnivariateRealFunction wrappedDerivative = new UnivariateRealFunction() {
public double value(double x) {
DifferentialPair t = DifferentialPair.newVariable(x);
return derivative.f(t).getFirstDerivative();
}
};
UnivariateRealSolver solver = new BrentSolver(wrappedDerivative);
double tMax = solver.solve(0.5, 1.5);
double yMax = derivative.f(DifferentialPair.newVariable(tMax)).getValue();
System.out.println("max value = " + yMax + ", at t = " + tMax +
" (" + solver.getIterationCount() + " iterations)");
We get the following result:
max value = 2.1097470218140537, at t = 0.8987751653383649 (7 iterations)
This example shows that Nabla creates an object that computes both the value and the derivative of a function, given only an instance of a class that computes the primitive function. Despite we had the source code available in this case, it was not used: transformation is done at runtime using only an instance of the primitive function. We can also observe that there is no configuration at all: the automatic differentiator is built using a no-argument constructor and the differentiation method has only the primitive object as a parameter.
We could also use the derivative object to compute both the value and the first derivative of our function at any point:
for (double t = 0.0; t < 1.0; t += 0.01) {
DifferentialPair y = derivative.f(DifferentialPair.newVariable(t));
System.out.println(t + " " + y.getValue() + " " + y.getFirstDerivative());
}
Basically, Nabla works by analyzing the bytecode of the original object, transforming the arithmetic operations along all possible execution passes using the differentiation, generating a new class on the fly with the transformed bytecode, and instantiating it to generate the derivative object. This works for any pure Java function, or more generally for any program using the Java platform as its execution environment.
The main drawback is that functions that call native code cannot be handled. For these functions, a fallback method is provided that uses finite differences (with 2, 4, 6 or 8 points schemes). This fallback method does not have the same advantages as the previous one: it needs configuration (number of points and step size), it is not exact, it is more computing intensive and it cannot be used too close to domain boundaries.