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 }