finMath.net

Algorithmic Differentiation and Dependency Injection

Getting Started

Checkout finmath-experiments from git and run maven (mvn or mvnw) from its directory. This will start a JShell. See Getting Started for details.

Experiment 0: Monte-Carlo Simulation and Valuation (Recap)

Run the following experiment from your JShell (which was launched from finmath-experiments).

Initial Setup

In the experiments on the Monte-Carlo simulation a simulation object and a product object has been created and the delta of option has been evaluated using finite difference. Repeat this:

Create the model and its simulation:

JShell:

				import net.finmath.montecarlo.*;
				import net.finmath.montecarlo.process.*;
				import net.finmath.montecarlo.assetderivativevaluation.*;
				import net.finmath.montecarlo.assetderivativevaluation.models.*;
				import net.finmath.stochastic.*;
				import net.finmath.time.*;
				
				double modelInitialValue = 100.0;
				double modelRiskFreeRate = 0.05;
				double modelVolatility = 0.20;
				
				// Create a model
				var model = new BlackScholesModel(modelInitialValue, modelRiskFreeRate, modelVolatility);
				
				// Create a corresponding MC process
				var td = new TimeDiscretizationFromArray(0.0, 300, 0.01);
				var brownianMotion = new BrownianMotionFromMersenneRandomNumbers(td, 1, 10000, 3231);
				var process = new EulerSchemeFromProcessModel(model, brownianMotion);
				
				// Using the process (Euler scheme), create an MC simulation of a Black-Scholes model
				var simulation = new MonteCarloAssetModel(process);
			

Create the product:

JShell:

				import net.finmath.montecarlo.assetderivativevaluation.products.*;
				
				double maturity = 3.0;
				double strike = 106.0;
				
				var europeanOption = new EuropeanOption(maturity, strike);
			

Experiment 1 - Finite difference (requires run of "Initial Setup")

We like to calculate a finite difference approximation of the partial derivative of the value \( V \) (being europeanOption.getValue(0.0, simulation)) with respect to \( S_{0} \) (being the initialValue of simulation) that is \( \partial V/ \partial S_{0} \approx \frac{V(M(S_0+h))-V(M(S_0-h))}{2h} \) (where \( M(x) \) refers to the model with the initial value \( x \)).

The object simulation has a method getCloneWithModifiedData(Map) which allows to create a new simulation with modified data. As argument we pass a Map of the parameters to modify, in our case Map.of("initialValue", modelInitialValue+h))

JShell: (continued)

				double h = 1E-2;
				var valueUpShift = europeanOption.getValue(simulation.getCloneWithModifiedData(Map.of("initialValue", modelInitialValue+h)));
				var valueDownShift = europeanOption.getValue(simulation.getCloneWithModifiedData(Map.of("initialValue", modelInitialValue-h)));
				var deltaNumerical = (valueUpShift - valueDownShift) / (2 * h);
			
The value reported for is 0.67361....

Algorithmic Differentiation

Experiment 2

In order to enable algorithmic differentiation we make a small modification to the creation of the model (and hence to the simulation): we “inject” a factory creating AD enabled random variables into the model. As a result, the value calculated has the additional method getGradient. The difference of the following experiment is line

			RandomVariableFactory randomVariableFactory = new RandomVariableDifferentiableAADFactory();
		
creating a factory to create AAD enabled RandomVariables, and passing it to the model

			var model = new BlackScholesModel(modelInitialValue, modelRiskFreeRate, modelVolatility, randomVariableFactory);
		
Injecting the factory has the effect that

			var valueOfEuropeanOption = (RandomVariableDifferentiable) europeanOption.getValue(0.0, simulation);
		
returns an object of type RandomVariableDifferentiableAAD.

Remark: The code below requires that the object europeanOption has been initialised. If you haven't done so you can and execute it before.

JShell: (requires that the object europeanOption has been created in Experiment 3)

				import net.finmath.montecarlo.*;
				import net.finmath.montecarlo.assetderivativevaluation.*;
				import net.finmath.montecarlo.assetderivativevaluation.models.*;
				import net.finmath.montecarlo.automaticdifferentiation.*;
				import net.finmath.montecarlo.automaticdifferentiation.backward.*;
				import net.finmath.montecarlo.process.*;
				import net.finmath.time.*;
				
				double modelInitialValue = 100.0;
				double modelRiskFreeRate = 0.05;
				double modelVolatility = 0.20;
				
				RandomVariableFactory randomVariableFactory = new RandomVariableDifferentiableAADFactory();
				
				// Create a model
				var model = new BlackScholesModel(modelInitialValue, modelRiskFreeRate, modelVolatility, randomVariableFactory);
				
				// Create a corresponding MC process
				var td = new TimeDiscretizationFromArray(0.0, 300, 0.01);
				var brownianMotion = new BrownianMotionFromMersenneRandomNumbers(td, 1, 10000, 3231);
				var process = new EulerSchemeFromProcessModel(model, brownianMotion);
				
				// Using the process (Euler scheme), create an MC simulation of a Black-Scholes model
				var simulation = new MonteCarloAssetModel(process);
				
				var valueOfEuropeanOption = (RandomVariableDifferentiable) europeanOption.getValue(0.0, simulation);
				
				var initialValue = (RandomVariableDifferentiable) model.getInitialValue(process)[0];
				
				var deltaPathwise = valueOfEuropeanOption.getGradient().get(initialValue.getID());
				var deltaValue = deltaPathwise.average().doubleValue();
			
The value reported for deltaValue is 0.6736406559419106 (compare this to the finite difference approximation 0.6736119988879352.

The variable deltaPathwise is the algorithmic differentiation of valueOfEuropeanOption with respect to the variable initialValue. It is a path-wise derivative \( \partial V(t_{n},\omega_{k}) / \partial S(t_{0},\omega_{k}) \) (note: the initial value is a deterministic random variable, i.e. \( S(t_{0}, \omega_{k}) \equiv S_{0} \quad \forall k \)). Hence deltaPathwise is a random variable too - the path wise differentiation of the (discounted) payoff with respect to \( S_{0} \). We may plot this path-wise differentiation:

JShell: (continued)

				import net.finmath.plots.*;

				var underlying = simulation.getAssetValue(maturity, 0 /* assetIndex */);
				
				var plot = Plots.createHistogramBehindValues(underlying, deltaPathwise, 100 /* bins */, 5.0 /* stddev */);
				plot.setTitle("European option path-wise delta and distribution of underlying");
				plot.setXAxisLabel("underlying").setYAxisLabel("path-wise delta");
				plot.show();
			

Forward versus Backward mode Algorithmic Differentiation

Experiment 3

The following code is a minor modification of that in experiment 2, instead of RandomVariableDifferentiableAADFactory we use RandomVariableDifferentiableADFactory, that is AD instead of AAD. The class RandomVariableDifferentiableADFactory resides in the package automaticdifferentiation.forward in net.finmath.montecarlo, while the class RandomVariableDifferentiableADFactory resides in automaticdifferentiation.backward in net.finmath.montecarlo.
JShell: (requires that the object europeanOption has been created)

				import net.finmath.montecarlo.*;
				import net.finmath.montecarlo.assetderivativevaluation.*;
				import net.finmath.montecarlo.assetderivativevaluation.models.*;
				import net.finmath.montecarlo.automaticdifferentiation.*;
				import net.finmath.montecarlo.automaticdifferentiation.forward.*;
				import net.finmath.montecarlo.process.*;
				import net.finmath.time.*;
				
				double modelInitialValue = 100.0;
				double modelRiskFreeRate = 0.05;
				double modelVolatility = 0.20;
				
				RandomVariableFactory randomVariableFactory = new RandomVariableDifferentiableADFactory();
				
				// Create a model
				var model = new BlackScholesModel(modelInitialValue, modelRiskFreeRate, modelVolatility, randomVariableFactory);
				
				// Create a corresponding MC process
				var td = new TimeDiscretizationFromArray(0.0, 300, 0.01);
				var brownianMotion = new BrownianMotionFromMersenneRandomNumbers(td, 1, 10000, 3231);
				var process = new EulerSchemeFromProcessModel(model, brownianMotion);
				
				// Using the process (Euler scheme), create an MC simulation of a Black-Scholes model
				var simulation = new MonteCarloAssetModel(process);
				
				var valueOfEuropeanOption = (RandomVariableDifferentiable) europeanOption.getValue(0.0, simulation);
				
				var initialValue = (RandomVariableDifferentiable) model.getInitialValue(process)[0];
				
				var deltaPathwise = valueOfEuropeanOption.getGradient().get(initialValue.getID());
				var deltaValue = deltaPathwise.average().doubleValue();
			
The value reported is 0.6736406559419106 (identical to the AAD implementation).