The OpenD Programming Language

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

NameDescription
<a class="xref" href="mir.ediff.Const.html">Const</a>A double precision constant with an immidiate value
<a class="xref" href="mir.ediff.Var.html">Var</a>A double precision named variable with an immidiate value
<tt class="inline-code">+</tt>Constructs the sum of two expressions
<tt class="inline-code">-</tt>Constructs the difference of two expressions
<tt class="inline-code">*</tt>Constructs the product of two expressions
<tt class="inline-code">/</tt>Constructs the quotient of two expressions
<a class="xref" href="mir.ediff.derivativeOf.html">derivativeOf</a>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.
<a class="xref" href="mir.ediff.powi.html">powi</a>Constructs of the expression raised to the specified (positive or negative) compile-time integral power.
<a class="xref" href="mir.ediff.log.html">log</a>Constructs the natural logarithm of the expression.
<a class="xref" href="mir.ediff.exp.html">exp</a>Constructs the natural exponent of the expression.
<a class="xref" href="mir.ediff.sqrt.html">sqrt</a>Constructs the square root of the expression.
<a class="xref" href="normalDistribution.html">normalDistribution</a>Constructs the cumulative normal distribution function of the expression.

Expression composition

NameDescription
<a class="xref" href="mir.ediff.composeAt.html">composeAt</a>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

Derivative set definition

NameDescription
<a class="xref" href="mir.ediff.Derivative.html">Derivative</a>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.
<a class="xref" href="mir.ediff.Minus.html">Minus</a>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.

Function definition

NameDescription
<a class="xref" href="mir.ediff.DependsOn.html">DependsOn</a>User defined attribute that should applied on struct definition of a user-defined expression function
<a class="xref" href="mir.ediff.Dependencies.html">Dependencies</a>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.
.getDerivativeThe generic method, which a user expression function should define. The method should accept a compile-time array of variable names and evaluate the corresponding partial derivative.
<a class="xref" href="ediffOperators.Mixintemplatethatpropagates`+`.`-`.`*`.and`/`binaryoperatorsforuserdefinedexpressionfunctions..html">ediffOperators.Mixintemplatethatpropagates`+`.`-`.`*`.and`/`binaryoperatorsforuserdefinedexpressionfunctions.</a>

Expression evaluation

NameDescription
<a class="xref" href="mir.ediff.getFunctionValue.html">getFunctionValue</a>Evaluates function value. It is shortcut for getDerivative of zero order.
<a class="xref" href="mir.ediff.getDerivative.html">getDerivative</a>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.
<a class="xref" href="mir.ediff.setDerivatives.2.html">setDerivatives</a>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.

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.

Members

Aliases

getFunctionValue
alias getFunctionValue(bool strict = true) = getDerivative!(string[].init, strict)

Evaluates function value. It is shortcut for getDerivative of zero order.

Enums

Minus
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.

Functions

exp
auto exp(T power)

Constructs the natural exponent of the expression.

log
auto log(T value)

Constructs the natural logarithm of the expression.

normalCDF
auto normalCDF(T value)

Constructs the cumulative normal distribution function of the expression

powi
auto powi(T value)

Constructs of the expression raised to the specified (positive or negative) compile-time integral power.

setDerivatives
void setDerivatives(D derivatives, E expression)

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.

sqrt
auto sqrt(T value)

Constructs the square root of the expression.

Mixin templates

ediffOperators
mixintemplate ediffOperators()

Structs

Const
struct Const

A double precision constant with an immidiate value

DependsOn
struct DependsOn

User defined attribute that should applied on struct definition of a user-defined expression function.

Derivative
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.

Templates

Dependencies
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.

Var
template Var(string name)

A double precision named variable with an immidiate value

composeAt
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.

derivativeOf
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.

getDerivative
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.

setDerivatives
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.

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));
///
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"]));
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);

Meta

Authors

Ilia Ki