Basic usage

Public API

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

UnivariateDifferentiable
is the interface representing the mathematical function that should be differentiated. The user-defined class are provided to Nabla as classes implementing this interface
UnivariateDerivative
is the interface representing the differentiated function created by Nabla.
UnivariateDifferentiator
is the interface implemented by the various differentiators provided by Nabla.
DifferentialPair
is a simple container holding both a value and a derivative. It can be considered simply as an enhanced double that is used both as the type of input parameters and return value of differentiated functions.
AutomaticDifferentiator
is the main implementation of the UnivariateDifferentiator interface. It performs differentiation by bytecode analysis and generation, using the exact differentiation rules in order to create functions that compute exact differentials.

UML class diagram of Nabla public API

Differentiating user-defined functions

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);
                }
            });
        

Differential pairs

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);
       

Advanced use

Updating the base and differentiated objects

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();
        

Functions calling native code

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.