The OpenD Programming Language

1 /++
2 $(H1 Expression differentiation)
3 
4 The module provides API for rapid evaluation of a user-requested set of partial derivatives of any order.
5 
6 The implementation operates with double precision numbers.
7 
8 $(BOOKTABLE $(H2 Expression construction),
9 $(TR $(TH Name) $(TH Description))
10 $(T2 $(LREF Const), A double precision constant with an immidiate value)
11 $(T2 $(LREF Var), A double precision named variable with an immidiate value)
12 $(T2 `+`, Constructs the sum of two expressions)
13 $(T2 `-`, Constructs the difference of two expressions)
14 $(T2 `*`, Constructs the product of two expressions)
15 $(T2 `/`, Constructs the quotient of two expressions)
16 $(T2 $(LREF derivativeOf), Constructs a partial derivative of one order.
17     The function can be applied in any stage any number of times.
18     The function does NOT evaluate the expression.)
19 $(T2 $(LREF powi), Constructs of the expression raised to the specified (positive or negative) compile-time integral power.)
20 $(T2 $(LREF log), Constructs the natural logarithm of the expression.)
21 $(T2 $(LREF exp), Constructs the natural exponent of the expression.)
22 $(T2 $(LREF sqrt), Constructs the square root of the expression.)
23 $(T2 $(LREF normalDistribution), Constructs the cumulative normal distribution function of the expression.)
24 )
25 
26 $(BOOKTABLE $(H2 Expression composition),
27 $(TR $(TH Name) $(TH Description))
28 $(T2 $(LREF composeAt), Given a compiletime variable name `v` and two expressions `F(v, U)` and `G(Y)`,
29     constructs a composition `F ∘ G (U, Y)` as `v = G`)
30 )
31 
32 $(BOOKTABLE $(H2 Derivative set definition),
33 $(TR $(TH Name) $(TH Description))
34 $(T2 $(LREF Derivative), User defined attribute that should applied on a struct member definition of a user-defined set of derivatives.
35     The attribute holds an unordered set with duplicates of variables names to reflect which partial derivative this member contains.)
36 $(T2 $(LREF Minus), User-defined attribute that can be applied on struct member definition along with $(LREF Derivative) to denote
37     that the member holds a negative value of the partial derivative.
38     This attribute is useful for financial code where verbal definitions may denote negative partial derivatives.)
39 )
40 
41 $(BOOKTABLE $(H2 Function definition),
42 $(TR $(TH Name) $(TH Description))
43 $(T2 $(LREF DependsOn), User defined attribute that should applied on struct definition of a user-defined expression function)
44 $(T2 $(LREF Dependencies), Fetchs a set of variables the expression is depends on.
45     First, it checks if the expression has $(LREF DependsOn) UDA
46     ; otherwise, iterates over members with $(LREF Derivative) UDA and collects variable names. )
47 $(T2 .getDerivative, The generic method, which a user expression function should define.
48     The method should accept a compile-time array of variable names and evaluate the corresponding partial derivative.)
49 $(T2 $(LREF ediffOperators, Mixin template that propagates `+`, `-`, `*`, and `/` binary operators for user defined expression functions.))
50 )
51 
52 $(BOOKTABLE $(H2 Expression evaluation),
53 $(TR $(TH Name) $(TH Description))
54 $(T2 $(LREF getFunctionValue), Evaluates function value. It is shortcut for $(LREF getDerivative) of zero order.)
55 $(T2 $(LREF getDerivative), Evaluates partial derivative for user-defined compiletime set of variables.
56     First, it checks if the expression has `.getDerivative` methods and uses it,
57     otherwise iterates over members with $(LREF Derivative) UDA and tries to find a member that holds the required partial derivative.)
58 $(T2 $(LREF setDerivatives), Evaluates partial derivatives and function value, if any, for a user-provided set of partial derivatives.
59     The derivative set can be defined with $(LREF Derivative) and $(LREF Minus) UDAs.)
60 )
61 
62 $(H2 Optimization note)
63 
64 During function differentiation, the resulting set of expressions likely contains a lot of
65 identical calls of elementary functions. LLVM  efficiently eliminates equivalent calls of intrinsic
66 functions such as `powi`, `log`, `exp`, and `sqrt`.
67 On the other hand, it can't eliminate identical calls of complex functions.
68 It is highly recommended to evaluate a set of partial derivatives immediately after constructing
69 a complex expression such as $(LREF normalDistribution).
70 
71 Authors: Ilia Ki
72 +/
73 module mir.ediff;
74 
75 ///
76 version(mir_test)
77 unittest
78 {
79     ///
80     static struct D
81     {
82         @Derivative()
83         double _;
84         @Derivative("a")
85         double a;
86         @Derivative("b")
87         double b;
88         @Minus @Derivative("a", "b") 
89         double mab;
90     }
91 
92     auto d = D(3, 4, 5, -6);
93     assert(d.powi!2.setDerivatives!D == D(9, 24, 30, -76));
94 }
95 ///
96 version(mir_test)
97 unittest
98 {
99     ///
100     static struct Greeks
101     {
102         @Derivative("spot")
103         double delta;
104         @Derivative("spot", "spot")
105         double gamma;
106         @Minus @Derivative("time", "spot") 
107         double charm;
108     }
109 
110     auto greeks = Greeks(2, 3, 4);
111 
112     auto dspot = derivativeOf!"spot"(&greeks);
113 
114     assert(greeks.delta is dspot.getFunctionValue);
115     assert(greeks.gamma is dspot.getDerivative!(["spot"]));
116     assert(greeks.charm is -dspot.getDerivative!(["time"]));
117 }
118 
119 ///
120 version(mir_test)
121 unittest
122 {
123     import mir.test;
124     import mir.math;
125 
126     // Test Const
127     static assert(Dependencies!Const == [].DependsOn);
128     auto c = 5.Const;
129     static assert(c.getDerivative!(["any"]) == 0);
130     assert(c.getFunctionValue == 5);
131 
132     // Test Var
133     auto spot = 7.Var!"spot";
134     static assert(Dependencies!(Var!"spot") == ["spot"].DependsOn);
135     static assert(spot.getDerivative!(["spot"]) == 1);
136     static assert(spot.getDerivative!(["other"]) == 0);
137     assert(spot.getFunctionValue == 7);
138 
139     // Test integer power and exponent
140     auto f1 = exp(3.Const * spot.powi!(-2));
141     static assert(Dependencies!(typeof(f1)) == ["spot"].DependsOn);
142     assert(f1.getFunctionValue == mir.math.exp(3 * 7.0 ^^ -2));
143     assert(f1.getDerivative!(["spot"]).approxEqual(3 * -2 * 7.0 ^^ -3 * mir.math.exp(3 * 7.0 ^^ -2)));
144     // Test DerivativeOf
145     assert(f1.derivativeOf!"spot".getFunctionValue == f1.getDerivative!(["spot"]));
146     // Test product and sum
147     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)));
148 
149     auto strike = 9.Var!"strike";
150 
151     auto f2 = strike * f1 + strike;
152     assert(f2.getDerivative!(["strike"]).approxEqual(1 + f1.getFunctionValue));
153     // Test log
154     assert(f2.log.getFunctionValue == mir.math.log(f2.getFunctionValue));
155     assert(f2.log.getDerivative!(["strike"]) == getFunctionValue(f2.powi!(-1) * (1.Const + f1)));
156     assert(f2.sqrt.getFunctionValue == mir.math.sqrt(f2.getFunctionValue));
157     assert(f2.sqrt.getDerivative!(["strike"]) == getFunctionValue(f2.sqrt.powi!(-1)  * 0.5.Const * (1.Const + f1)));
158 
159     // Compose
160     auto barrier = 13.Var!"barrier";
161     auto fc = barrier.powi!2 / strike;
162     auto f3 = f2.composeAt!"strike"(fc);
163     assert(f3.getFunctionValue == f2.getFunctionValue);
164     assert(f3.getDerivative!(["vol"]) == f2.getDerivative!(["vol"]));
165     assert(f3.getDerivative!(["strike"]) == f2.getDerivative!(["strike"]) * fc.getDerivative!(["strike"]));
166     f3.getDerivative!(["barrier"]).shouldApprox == f2.getDerivative!(["strike"]) * fc.getDerivative!(["barrier"]);
167     getDerivative!(["barrier"])(f3 + barrier).shouldApprox == f2.getDerivative!(["strike"]) * fc.getDerivative!(["barrier"]) + 1;
168     f3.getDerivative!(["strike", "barrier"]).shouldApprox == 
169         f2.getDerivative!(["strike", "strike"]) * fc.getDerivative!(["strike"]) * fc.getDerivative!(["barrier"]) +
170         f2.getDerivative!(["strike"]) * fc.getDerivative!(["strike", "barrier"]);
171 
172     /// normalDistribution
173     import mir.math.func.normal: constantNormalCDF = normalCDF, normalPDF;
174     barrier.powi!2.normalCDF.getFunctionValue.shouldApprox == constantNormalCDF(13.0 ^^ 2);
175     barrier.powi!2.normalCDF.getDerivative!(["barrier"]).shouldApprox == normalPDF(13.0 ^^ 2) * (2 * 13);
176 }
177 
178 import mir.algorithm.iteration: uniq;
179 import mir.array.allocation;
180 import mir.math.common;
181 import mir.ndslice.sorting: sort;
182 import std.traits: Unqual, isPointer, PointerTarget;
183 import mir.internal.meta: getUDAs, hasUDA;
184 
185 @fmamath:
186 
187 /++
188 +/
189 mixin template ediffOperators()
190 {
191 const:
192     auto opBinary(string op, R)(const R rhs)
193         if ((op == "+" || op == "-") && is(R == struct))
194     {
195         return Sum!(Unqual!(typeof(this)), R, op == "-")(this, rhs);
196     }
197 
198     auto opBinary(string op, R)(const R rhs)
199         if (op == "*" && is(R == struct))
200     {
201         alias L = Unqual!(typeof(this));
202         static if  (is(R == Const) && is(L == Const))
203         {
204             return Const(this.value * rhs.value);
205         }
206         else
207         static if (is(R == Const) && is(L == Powi!(_power, _LT), size_t _power, _LT))
208         {
209             return L(this.value, this.coefficient * rhs.value);
210         }
211         else
212         static if (is(L == Const) && is(R == Powi!(_power, _RT), size_t _power, _RT))
213         {
214             return R(rhs.value, rhs.coefficient * this.value);
215         }
216         else
217         {
218             return Product!(L, R)(this, rhs);
219         }
220     }
221 
222     auto opBinaryRight(string op, L)(const L lhs)
223         if ((op == "+" || op == "*") && is(L == struct))
224     {
225         return this.opBinary!op(lhs);
226     }
227 
228     auto opBinaryRight(string op, L)(const L lhs)
229         if ((op == "-") && is(L == struct))
230     {
231         return Sum!(L, Unqual!(typeof(this)), true)(lhs, this);
232     }
233 
234     auto opBinary(string op, R)(const R rhs)
235         if (op == "/" && is(R == struct))
236     {
237         alias L = Unqual!(typeof(this));
238         alias A = L;
239         alias B = R;
240         alias a = this;
241         alias b = rhs;
242         mixin _div;
243         return result;
244     }
245 
246     auto opBinaryRight(string op, L)(const L lhs)
247         if ((op == "/") && is(L == struct))
248     {
249         alias R = Unqual!(typeof(this));
250         alias A = L;
251         alias B = R;
252         alias a = lhs;
253         alias b = this;
254         mixin _div;
255         return result;
256     }
257 }
258 
259 private mixin template _div()
260 {
261     // A / B
262     static if (is(A == Const) && is(B == Const))
263         auto result = Const(a.value / b.value);
264     else
265     static if (is(A == Const))
266         auto result = Powi!(-1, B)(b, a.value);
267     else
268     static if (is(B == Const))
269         auto result = Const(1.0 / b.value) * a;
270     else
271         auto result = b.powi!(-1) * a;
272 }
273 
274 private template Sum(A, B, bool diff = false)
275 {
276 
277     @(Dependencies!A ~ Dependencies!B)
278     struct Sum
279     {
280         A a;
281         B b;
282 
283     @fmamath:
284 
285         template getDerivative(string[] variables, bool strict = true)
286         {
287             static if (Dependencies!(typeof(this)).containsAll(variables))
288                 auto getDerivative() const @property
289                 {
290                     double ret = 0;
291                     static if (Dependencies!A.containsAll(variables))
292                         ret = a.getDerivative!(variables, strict);
293                     static if (Dependencies!B.containsAll(variables))
294                     {
295                         static if (diff)
296                             ret -= b.getDerivative!(variables, strict);
297                         else
298                             ret += b.getDerivative!(variables, strict);
299                     }
300                     return ret;
301                 }
302             else
303                 enum double getDerivative = 0;
304         }
305 
306         mixin ediffOperators;
307     }
308 }
309 
310 private template Product(A, B)
311 {
312     @(Dependencies!A ~ Dependencies!B)
313     struct Product
314     {
315         A a;
316         B b;
317 
318         template getDerivative(string[] variables, bool strict = true)
319         {
320         @fmamath:
321             static if (Dependencies!(typeof(this)).containsAll(variables))
322                 auto getDerivative() const @property
323                 {
324                     static if (variables.length == 0)
325                     {
326                         return a.getFunctionValue!strict * b.getFunctionValue!strict;
327                     }
328                     else
329                     {
330                         enum variable = variables[0];
331                         static if (!Dependencies!A.contains(variable))
332                             return (a * b.derivativeOf!variable).getDerivative!(variables[1 .. $], strict);
333                         else
334                         static if (!Dependencies!B.contains(variable))
335                             return (a.derivativeOf!variable * b).getDerivative!(variables[1 .. $], strict);
336                         else
337                             return (a * b.derivativeOf!variable + a.derivativeOf!variable * b).getDerivative!(variables[1 .. $], strict);
338                     }
339                 }
340             else
341                 enum double getDerivative = 0;
342         }
343 
344         mixin ediffOperators;
345     }
346 }
347 
348 /++
349 User defined attribute that should applied on struct definition of a user-defined expression function.
350 +/
351 struct DependsOn
352 {
353     ///
354     string[] variables;
355 
356 @safe pure nothrow:
357     ///
358     auto contains(string variable) const @nogc
359     {
360         foreach (v; variables)
361             if (v == variable)
362                 return true;
363         return false;
364     }
365 
366     ///
367     auto containsAll(string[] variables) const @nogc
368     {
369         foreach (v; variables)
370             if (!contains(v))
371                 return false;
372         return true;
373     }
374 
375     ///
376     auto containsAny(string[] variables) const @nogc
377     {
378         foreach (v; variables)
379             if (contains(v))
380                 return true;
381         return false;
382     }
383 
384     /// Set union
385     DependsOn opBinary(string op : "~")(DependsOn rhs)
386     {
387         return (this.variables ~ rhs.variables).sort.uniq.array.DependsOn;
388     }
389 }
390 
391 /++
392 Fetchs a set of variables the expression is depends on.
393     First, it checks if the expression has $(LREF DependsOn) UDA
394     ; otherwise, iterates over members with $(LREF Derivative) UDA and collects variable names.
395 +/
396 template Dependencies(T)
397 {
398     static if (isPointer!T)
399         enum Dependencies = Dependencies!(PointerTarget!T);
400     else
401     static if (hasUDA!(T, DependsOn))
402         enum DependsOn Dependencies = getUDAs!(T, DependsOn)[0];
403     else
404     {
405         enum DependsOn Dependencies = () {
406             string[] variables;
407             static foreach (member; __traits(allMembers, T))
408             {
409                 static if (hasUDA!(T, member, Derivative))
410                 {
411                     variables ~= getUDAs!(T, member, Derivative)[0].variables;
412                 }
413             }
414             return variables.sort.uniq.array.DependsOn;
415         } ();
416     }
417 }
418 
419 /++
420 User defined attribute that should applied on a struct member definition of a user-defined set of derivatives.
421     The attribute holds an unordered set with duplicates of variables names to reflect which partial derivative this member contains.
422 +/
423 struct Derivative
424 {
425     ///
426     string[] variables;
427 
428 @trusted pure nothrow @nogc:
429 
430     ///
431     this(string[] variables...)
432     {
433         this.variables = variables.sort;
434     }
435 }
436 
437 /++
438 User-defined attribute that can be applied on struct member definition along with $(LREF Derivative) to denote
439     that the member holds a negative value of the partial derivative.
440     This attribute is useful for financial code where verbal definitions may denote negative partial derivatives.
441 +/
442 enum Minus;
443 
444 /++
445 Evaluates function value. It is shortcut for $(LREF getDerivative) of zero order.
446 Params:
447     strict = The parameter is used when the expression can't evaluate the function. If true, prints error at compile-time; otherwise, `getFunctionValues` returns NaN.
448 +/
449 alias getFunctionValue(bool strict = true) = getDerivative!(string[].init, strict);
450 
451 /++
452 Evaluates partial derivative for user-defined compiletime set of variables.
453     First, it checks if the expression has `.getDerivative` methods and uses it,
454     otherwise iterates over members with $(LREF Derivative) UDA and tries to find a member that holds the required partial derivative.
455 Params:
456     variables = array that denotes partial derivative
457     strict = The parameter is used when the expression can't evaluate the derivative. If true, prints error at compile-time; otherwise, `getDerivative` returns NaN.
458 +/
459 template getDerivative(string[] variables, bool strict = true)
460 {
461     import mir.ndslice.topology: pairwise;
462     import mir.algorithm.iteration: all;
463 
464 @fmamath:
465 
466     static if (variables.length == 0 || variables.pairwise!"a <= b".all)
467     {
468         auto ref getDerivative(T)(auto ref T value)
469         {
470             static if (__traits(hasMember, T, "getDerivative"))
471                 return value.getDerivative!(variables, strict);
472             else
473             {
474                 import std.meta: anySatisfy;
475 
476                 static if (isPointer!T)
477                     alias V = PointerTarget!T;
478                 else
479                     alias V = T;
480                 template hasDerivative(string member)
481                 {
482                     static if (hasUDA!(V, member, Derivative))
483                         enum hasDerivative = variables == getUDAs!(V, member, Derivative)[0].variables;
484                     else
485                         enum hasDerivative = false;
486                 }
487                 static if (anySatisfy!(hasDerivative, __traits(allMembers, V)))
488                 {
489                     static foreach (member; __traits(allMembers, V))
490                     {
491                         static if (hasDerivative!member)
492                         {
493                             static if (hasUDA!(V, member, Minus))
494                                 return -__traits(getMember, value, member);
495                             else
496                                 return __traits(getMember, value, member);
497                         }
498                     }
499                 }
500                 else
501                 static if (strict)
502                 {
503                     static assert(0, Unqual!V.stringof ~ "'_" ~ variables.stringof ~ " not found");
504                 }
505                 else
506                 {
507                     return double.nan;
508                 }
509             }
510         }
511     }
512     else
513     {
514         import mir.ndslice.sorting: sort;
515         alias getDerivative = .getDerivative!(variables.sort, strict);
516     }
517 }
518 
519 /++
520 Evaluates partial derivatives and function value, if any, for a user-provided set of partial derivatives.
521     The derivative set can be defined with $(LREF Derivative) and $(LREF Minus) UDAs.
522 Params:
523     D = type of the requested set of partial derivatives
524     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.
525     derivatives = a structure that holds set of partial derivatives (optional)
526     expression = expression
527 +/
528 void setDerivatives(bool strict = true, D, E)(scope ref D derivatives, E expression)
529 {
530     static if (__traits(hasMember, D, "setDerivatives"))
531         return derivatives.setDerivatives!strict(expression);
532     else
533     {
534         import std.traits: isPointer, PointerTarget;
535 
536         static foreach (member; __traits(allMembers, D))
537         {
538             static if (hasUDA!(D, member, Derivative))
539             {
540                 static if (hasUDA!(D, member, Minus))
541                     __traits(getMember, derivatives, member) = -expression.getDerivative!(getUDAs!(D, member, Derivative)[0].variables, strict);
542                 else
543                     __traits(getMember, derivatives, member) = expression.getDerivative!(getUDAs!(D, member, Derivative)[0].variables, strict);
544             }
545         }
546     }
547 }
548 
549 /// ditto
550 template setDerivatives(D, bool strict = true)
551 {
552     ///
553     D setDerivatives(E)(E expression)
554     {
555         D ret;
556         .setDerivatives!strict(ret, expression);
557         return ret;
558     }
559 }
560 
561 private auto removeVariable(DependsOn variables, string variable)
562 {
563     string[] ret;
564     foreach (v; variables.variables)
565         if (v != variable)
566             ret ~= v;
567     return ret.DependsOn;
568 }
569 
570 private template ComposeAt(F, G, string position)
571     if (Dependencies!F.contains(position))
572 {
573     @(Dependencies!F.removeVariable(position) ~ Dependencies!G)
574     struct ComposeAt
575     {
576         ///
577         F f;
578         ///
579         G g;
580 
581         ///
582         template getDerivative(string[] variables, bool strict = true)
583         {
584             static if (Dependencies!(typeof(this)).containsAll(variables))
585                 ///
586                 auto ref getDerivative() const @property
587                 {
588                     static if (!Dependencies!G.containsAny(variables))
589                         return f.getDerivative!(variables, strict);
590                     else
591                     {
592                         static if (Dependencies!F.contains(variables[0]) && variables[0] != position)
593                             auto a = f.derivativeOf!(variables[0]).composeAt!position(g).getDerivative!(variables[1 .. $], strict);
594                         else
595                             enum a = 0;
596                         static if (Dependencies!G.contains(variables[0]))
597                             auto b = (f.derivativeOf!position.composeAt!position(g) * g.derivativeOf!(variables[0])).getDerivative!(variables[1 .. $], strict);
598                         else
599                             enum b = 0;
600                         return a + b;
601                     }
602                 }
603             else
604                 enum double getDerivative = 0;
605         }
606 
607         mixin ediffOperators;
608     }
609 }
610 
611 /++
612 Given a compiletime variable name `v` and two expressions `F(v, U)` and `G(Y)`,
613     constructs a composition `F ∘ G (U, Y)` as `v = G`.
614 
615 Params:
616     position = name of the variable to compose functions at.
617 +/
618 template composeAt(string position)
619 {
620     /++
621     Params:
622         f = F expression
623         g = G expression
624     +/
625     auto composeAt(F, G)(const F f, const G g)
626     {
627         return ComposeAt!(F, G, position)(f, g);
628     }
629 }
630 
631 private template DerivativeOf(T, string variable)
632 {
633     @Dependencies!T
634     struct DerivativeOf
635     {
636         // Underlying expression
637         T underlying;
638 
639     @fmamath:
640 
641         ///
642         auto ref getDerivative(string[] variables, bool strict = true)() const @property
643         {
644             return underlying.getDerivative!(variable ~ variables, strict);
645         }
646 
647         mixin ediffOperators;
648     }
649 }
650 
651 /++
652 Constructs a partial derivative of one order.
653     The function can be applied in any stage any number of times.
654     The function does NOT evaluate the expression.
655 +/
656 template derivativeOf(string variable)
657 {
658 @fmamath:
659 
660     /++
661     Params:
662         value = expression
663     +/
664     auto derivativeOf(T)(const T value) @property
665     {
666         static if (isPointer!T)
667             return DerivativeOf!(const(PointerTarget!T)*, variable)(value);
668         else
669             return DerivativeOf!(T, variable)(value);
670     }
671 }
672 
673 /++
674 A double precision constant with an immidiate value
675 +/
676 @DependsOn()
677 struct Const
678 {
679     /// Immidiate value
680     double value;
681 
682     template getDerivative(string[] variables, bool strict = true)
683     {
684         static if (variables.length == 0)
685             alias getDerivative = value;
686         else
687             enum double getDerivative = 0;
688     }
689 
690     mixin ediffOperators;
691 }
692 
693 /++
694 A double precision named variable with an immidiate value
695 +/
696 template Var(string name)
697 {
698     ///
699     @DependsOn([name])
700     struct Var
701     {
702         /// Immidiate value
703         double value;
704 
705         template getDerivative(string[] variables, bool strict = true)
706         {
707             static if (variables.length == 0)
708                 alias getDerivative = value;
709             else
710                 enum double getDerivative = variables == [name];
711         }
712 
713         mixin ediffOperators;
714     }
715 }
716 
717 private template Powi(int power, T)
718     if (power)
719 {
720     @Dependencies!T
721     struct Powi
722     {
723         T value;
724         double coefficient = 1;
725 
726         template getDerivative(string[] variables, bool strict = true)
727         {
728         @fmamath:
729             static if (Dependencies!(typeof(this)).containsAll(variables))
730                 auto getDerivative() const @property
731                 {
732                     static if (variables.length == 0)
733                     {
734                         static if (power == 0)
735                             return coefficient;
736                         else
737                         static if (power == 1)
738                             return coefficient * value.getFunctionValue!strict;
739                         else
740                         static if (power == 2)
741                             return coefficient * value.getFunctionValue!strict ^^ 2;
742                         else
743                         static if (power == -1)
744                             return coefficient / value.getFunctionValue!strict;
745                         else
746                         static if (power == -2)
747                             return coefficient / value.getFunctionValue!strict ^^ 2;
748                         else
749                             return coefficient * mir.math.common.powi(value.getFunctionValue!strict, power);
750                     }
751                     else
752                     {
753                         static if (power == 1)
754                             auto v = coefficient.Const;
755                         else
756                             auto v = Powi!(power - 1, T)(value, coefficient * power);
757                         static if (is(T == Var!(variables[0])))
758                             return v.getDerivative!(variables[1 .. $], strict);
759                         else
760                             return (v * value.derivativeOf!(variables[0])).getDerivative!(variables[1 .. $], strict);
761                     }
762                 }
763             else
764                 enum double getDerivative = 0;
765         }
766 
767         mixin ediffOperators;
768     }
769 }
770 
771 /++
772 Constructs of the expression raised to the specified (positive or negative) compile-time integral power.
773 
774 Params:
775     power = integral power (compile-time)
776     value = expression
777 +/
778 auto powi(int power, T)(const T value)
779     if (is(T == struct))
780 {
781     static if (power == 0)
782         return 1.Const;
783     else
784         return Powi!(power, T)(value);
785 }
786 
787 private template Exp(T)
788 {
789     @Dependencies!T
790     struct Exp
791     {
792         /// Power function
793         T power;
794 
795         template getDerivative(string[] variables, bool strict = true)
796         {
797         @fmamath:
798             static if (Dependencies!(typeof(this)).containsAll(variables))
799                 auto getDerivative() const @property
800                 {
801                     static if (variables.length == 0)
802                         return mir.math.common.exp(power.getFunctionValue!strict);
803                     else
804                         return (this * power.derivativeOf!(variables[0])).getDerivative!(variables[1 .. $], strict);
805                 }
806             else
807                 enum double getDerivative = 0;
808         }
809 
810         mixin ediffOperators;
811     }
812 }
813 
814 /++
815 Constructs the natural exponent of the expression.
816 
817 Params:
818     power = expression
819 +/
820 auto exp(T)(const T power)
821     if (is(T == struct))
822 {
823     return Exp!T(power);
824 }
825 
826 private template Log(T)
827 {
828     @Dependencies!T
829     struct Log
830     {
831         T value;
832 
833         template getDerivative(string[] variables, bool strict = true)
834         {
835         @fmamath:
836             static if (Dependencies!(typeof(this)).containsAll(variables))
837                 auto getDerivative() const @property
838                 {
839                     static if (variables.length == 0)
840                         return mir.math.common.log(value.getFunctionValue!strict);
841                     else
842                         return (value.derivativeOf!(variables[0]) / value).getDerivative!(variables[1 .. $], strict);
843                 }
844             else
845                 enum double getDerivative = 0;
846         }
847 
848         mixin ediffOperators;
849     }
850 }
851 
852 /++
853 Constructs the natural logarithm of the expression.
854 
855 Params:
856     value = expression
857 +/
858 auto log(T)(const T value)
859     if (is(T == struct))
860 {
861     return Log!T(value);
862 }
863 
864 private template Sqrt(T)
865 {
866     @Dependencies!T
867     struct Sqrt
868     {
869         T value;
870 
871         template getDerivative(string[] variables, bool strict = true)
872         {
873         @fmamath:
874             static if (Dependencies!(typeof(this)).containsAll(variables))
875                 auto getDerivative() const @property
876                 {
877                     static if (variables.length == 0)
878                         return mir.math.common.sqrt(value.getFunctionValue!strict);
879                     else
880                         return (Powi!(-1, Sqrt!T)(this, 0.5) * value.derivativeOf!(variables[0])).getDerivative!(variables[1 .. $], strict);
881                 }
882             else
883                 enum double getDerivative = 0;
884         }
885 
886         mixin ediffOperators;
887     }
888 }
889 
890 /++
891 Constructs the square root of the expression.
892 
893 Params:
894     value = expression
895 +/
896 auto sqrt(T)(const T value)
897     if (is(T == struct))
898 {
899     return Sqrt!T(value);
900 }
901 
902 private template NormalCDF(T)
903 {
904     @Dependencies!T
905     struct NormalCDF
906     {
907         /// Square root argument function
908         T value;
909 
910         template getDerivative(string[] variables, bool strict = true)
911         {
912         @fmamath:
913             static if (Dependencies!(typeof(this)).containsAll(variables))
914                 auto getDerivative() const @property
915                 {
916                     import mir.math.func.normal: normalCDF, SQRT2PIINV;
917                     static if (variables.length == 0)
918                         return normalCDF(value.getFunctionValue!strict);
919                     else
920                         return (SQRT2PIINV.Const * Powi!(2, T)(value, -0.5).exp * value.derivativeOf!(variables[0])).getDerivative!(variables[1 .. $], strict);
921                 }
922             else
923                 enum double getDerivative = 0;
924         }
925 
926         mixin ediffOperators;
927     }
928 }
929 
930 /++
931 Constructs the cumulative normal distribution function of the expression
932 
933 Params:
934     value = expression
935 +/
936 auto normalCDF(T)(const T value)
937     if (is(T == struct))
938 {
939     return NormalCDF!T(value);
940 }