Report a bug
If you spot a problem with this page, click here to create a GitHub issue.
Improve this page
Quickly fork, edit online, and submit a pull request for this page. Requires a signed-in GitHub account. This works well for small changes. If you'd like to make larger changes you may want to consider using a local clone.

mir.ediff

Expression differentiation

The module provides API for rapid evaluation of a user-requested set of partial derivatives of any order.
The implementation operates with double precision numbers.

Expression construction

Name Description

Expression composition

Name Description

Derivative set definition

Name Description

Function definition

Name Description

Expression evaluation

Name Description

Optimization note

During function differentiation, the resulting set of expressions likely contains a lot of identical calls of elementary functions. LLVM efficiently eliminates equivalent calls of intrinsic functions such as powi, log, exp, and sqrt. On the other hand, it can't eliminate identical calls of complex functions. It is highly recommended to evaluate a set of partial derivatives immediately after constructing a complex expression such as normalDistribution.
Authors:
Ilia Ki
Examples:
///
static struct D
{
    @Derivative()
    double _;
    @Derivative("a")
    double a;
    @Derivative("b")
    double b;
    @Minus @Derivative("a", "b") 
    double mab;
}

auto d = D(3, 4, 5, -6);
assert(d.powi!2.setDerivatives!D == D(9, 24, 30, -76));
Examples:
///
static struct Greeks
{
    @Derivative("spot")
    double delta;
    @Derivative("spot", "spot")
    double gamma;
    @Minus @Derivative("time", "spot") 
    double charm;
}

auto greeks = Greeks(2, 3, 4);

auto dspot = derivativeOf!"spot"(&greeks);

assert(greeks.delta is dspot.getFunctionValue);
assert(greeks.gamma is dspot.getDerivative!(["spot"]));
assert(greeks.charm is -dspot.getDerivative!(["time"]));
Examples:
import mir.test;
import mir.math;

// Test Const
static assert(Dependencies!Const == [].DependsOn);
auto c = 5.Const;
static assert(c.getDerivative!(["any"]) == 0);
assert(c.getFunctionValue == 5);

// Test Var
auto spot = 7.Var!"spot";
static assert(Dependencies!(Var!"spot") == ["spot"].DependsOn);
static assert(spot.getDerivative!(["spot"]) == 1);
static assert(spot.getDerivative!(["other"]) == 0);
assert(spot.getFunctionValue == 7);

// Test integer power and exponent
auto f1 = exp(3.Const * spot.powi!(-2));
static assert(Dependencies!(typeof(f1)) == ["spot"].DependsOn);
assert(f1.getFunctionValue == mir.math.exp(3 * 7.0 ^^ -2));
assert(f1.getDerivative!(["spot"]).approxEqual(3 * -2 * 7.0 ^^ -3 * mir.math.exp(3 * 7.0 ^^ -2)));
// Test DerivativeOf
assert(f1.derivativeOf!"spot".getFunctionValue == f1.getDerivative!(["spot"]));
// Test product and sum
assert(f1.derivativeOf!"spot".derivativeOf!"spot".getFunctionValue.approxEqual((3 * (-2 * 7.0 ^^ -3)) ^^ 2 * mir.math.exp(3 * 7.0 ^^ -2) + 3 * (6 * 7.0 ^^ -4)* mir.math.exp(3 * 7.0 ^^ -2)));

auto strike = 9.Var!"strike";

auto f2 = strike * f1 + strike;
assert(f2.getDerivative!(["strike"]).approxEqual(1 + f1.getFunctionValue));
// Test log
assert(f2.log.getFunctionValue == mir.math.log(f2.getFunctionValue));
assert(f2.log.getDerivative!(["strike"]) == getFunctionValue(f2.powi!(-1) * (1.Const + f1)));
assert(f2.sqrt.getFunctionValue == mir.math.sqrt(f2.getFunctionValue));
assert(f2.sqrt.getDerivative!(["strike"]) == getFunctionValue(f2.sqrt.powi!(-1)  * 0.5.Const * (1.Const + f1)));

// Compose
auto barrier = 13.Var!"barrier";
auto fc = barrier.powi!2 / strike;
auto f3 = f2.composeAt!"strike"(fc);
assert(f3.getFunctionValue == f2.getFunctionValue);
assert(f3.getDerivative!(["vol"]) == f2.getDerivative!(["vol"]));
assert(f3.getDerivative!(["strike"]) == f2.getDerivative!(["strike"]) * fc.getDerivative!(["strike"]));
f3.getDerivative!(["barrier"]).shouldApprox == f2.getDerivative!(["strike"]) * fc.getDerivative!(["barrier"]);
getDerivative!(["barrier"])(f3 + barrier).shouldApprox == f2.getDerivative!(["strike"]) * fc.getDerivative!(["barrier"]) + 1;
f3.getDerivative!(["strike", "barrier"]).shouldApprox == 
    f2.getDerivative!(["strike", "strike"]) * fc.getDerivative!(["strike"]) * fc.getDerivative!(["barrier"]) +
    f2.getDerivative!(["strike"]) * fc.getDerivative!(["strike", "barrier"]);

/// normalDistribution
import mir.math.func.normal: constantNormalCDF = normalCDF, normalPDF;
barrier.powi!2.normalCDF.getFunctionValue.shouldApprox == constantNormalCDF(13.0 ^^ 2);
barrier.powi!2.normalCDF.getDerivative!(["barrier"]).shouldApprox == normalPDF(13.0 ^^ 2) * (2 * 13);
template ediffOperators()
struct DependsOn;
User defined attribute that should applied on struct definition of a user-defined expression function.
string[] variables;
const pure nothrow @nogc @safe auto contains(string variable);
const pure nothrow @nogc @safe auto containsAll(string[] variables);
const pure nothrow @nogc @safe auto containsAny(string[] variables);
DependsOn opBinary(string op : "~")(DependsOn rhs);
Set union
template Dependencies(T)
Fetchs a set of variables the expression is depends on. First, it checks if the expression has DependsOn UDA ; otherwise, iterates over members with Derivative UDA and collects variable names.
struct Derivative;
User defined attribute that should applied on a struct member definition of a user-defined set of derivatives. The attribute holds an unordered set with duplicates of variables names to reflect which partial derivative this member contains.
string[] variables;
pure nothrow @nogc @trusted this(string[] variables...);
enum Minus;
User-defined attribute that can be applied on struct member definition along with Derivative to denote that the member holds a negative value of the partial derivative. This attribute is useful for financial code where verbal definitions may denote negative partial derivatives.
template getFunctionValue(bool strict = true)
Evaluates function value. It is shortcut for getDerivative of zero order.
Parameters:
strict The parameter is used when the expression can't evaluate the function. If true, prints error at compile-time; otherwise, getFunctionValues returns NaN.
template getDerivative(string[] variables, bool strict = true)
Evaluates partial derivative for user-defined compiletime set of variables. First, it checks if the expression has .getDerivative methods and uses it, otherwise iterates over members with Derivative UDA and tries to find a member that holds the required partial derivative.
Parameters:
variables array that denotes partial derivative
strict The parameter is used when the expression can't evaluate the derivative. If true, prints error at compile-time; otherwise, getDerivative returns NaN.
void setDerivatives(bool strict = true, D, E)(ref scope D derivatives, E expression);

template setDerivatives(D, bool strict = true)
Evaluates partial derivatives and function value, if any, for a user-provided set of partial derivatives. The derivative set can be defined with Derivative and Minus UDAs.
Parameters:
D type of the requested set of partial derivatives
strict The parameter is used when the expression can't evaluate the derivative. If true, prints error at compile-time; otherwise, the corresponding member is set to NaN.
D derivatives a structure that holds set of partial derivatives (optional)
E expression expression
template composeAt(string position)
Given a compiletime variable name v and two expressions F(v, U) and G(Y), constructs a composition F ∘ G (U, Y) as v = G.
Parameters:
position name of the variable to compose functions at.
auto composeAt(F, G)(const F f, const G g);
Parameters:
F f F expression
G g G expression
template derivativeOf(string variable)
Constructs a partial derivative of one order. The function can be applied in any stage any number of times. The function does NOT evaluate the expression.
@property auto derivativeOf(T)(const T value);
Parameters:
T value expression
struct Const;
A double precision constant with an immidiate value
double value;
Immidiate value
struct Var(string name);
A double precision named variable with an immidiate value
double value;
Immidiate value
auto powi(int power, T)(const T value)
if (is(T == struct));
Constructs of the expression raised to the specified (positive or negative) compile-time integral power.
Parameters:
power integral power (compile-time)
T value expression
auto exp(T)(const T power)
if (is(T == struct));
Constructs the natural exponent of the expression.
Parameters:
T power expression
auto log(T)(const T value)
if (is(T == struct));
Constructs the natural logarithm of the expression.
Parameters:
T value expression
auto sqrt(T)(const T value)
if (is(T == struct));
Constructs the square root of the expression.
Parameters:
T value expression
auto normalCDF(T)(const T value)
if (is(T == struct));
Constructs the cumulative normal distribution function of the expression
Parameters:
T value expression