gonum.org/v1/gonum@v0.15.1-0.20240517103525-f853624cb1bb/diff/fd/diff.go (about) 1 // Copyright ©2014 The Gonum Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package fd 6 7 import ( 8 "math" 9 "runtime" 10 ) 11 12 // A Point is a stencil location in a finite difference formula. 13 type Point struct { 14 Loc float64 15 Coeff float64 16 } 17 18 // Formula represents a finite difference formula on a regularly spaced grid 19 // that approximates the derivative of order k of a function f at x as 20 // 21 // d^k f(x) ≈ (1 / Step^k) * \sum_i Coeff_i * f(x + Step * Loc_i). 22 // 23 // Step must be positive, or the finite difference formula will panic. 24 type Formula struct { 25 // Stencil is the set of sampling Points which are used to estimate the 26 // derivative. The locations will be scaled by Step and are relative to x. 27 Stencil []Point 28 Derivative int // The order of the approximated derivative. 29 Step float64 // Default step size for the formula. 30 } 31 32 func (f Formula) isZero() bool { 33 return f.Stencil == nil && f.Derivative == 0 && f.Step == 0 34 } 35 36 // Settings is the settings structure for computing finite differences. 37 type Settings struct { 38 // Formula is the finite difference formula used 39 // for approximating the derivative. 40 // Zero value indicates a default formula. 41 Formula Formula 42 // Step is the distance between points of the stencil. 43 // If equal to 0, formula's default step will be used. 44 Step float64 45 46 OriginKnown bool // Flag that the value at the origin x is known. 47 OriginValue float64 // Value at the origin (only used if OriginKnown is true). 48 49 Concurrent bool // Should the function calls be executed concurrently. 50 } 51 52 // Forward represents a first-order accurate forward approximation 53 // to the first derivative. 54 var Forward = Formula{ 55 Stencil: []Point{{Loc: 0, Coeff: -1}, {Loc: 1, Coeff: 1}}, 56 Derivative: 1, 57 Step: 2e-8, 58 } 59 60 // Forward2nd represents a first-order accurate forward approximation 61 // to the second derivative. 62 var Forward2nd = Formula{ 63 Stencil: []Point{{Loc: 0, Coeff: 1}, {Loc: 1, Coeff: -2}, {Loc: 2, Coeff: 1}}, 64 Derivative: 2, 65 Step: 1e-4, 66 } 67 68 // Backward represents a first-order accurate backward approximation 69 // to the first derivative. 70 var Backward = Formula{ 71 Stencil: []Point{{Loc: -1, Coeff: -1}, {Loc: 0, Coeff: 1}}, 72 Derivative: 1, 73 Step: 2e-8, 74 } 75 76 // Backward2nd represents a first-order accurate forward approximation 77 // to the second derivative. 78 var Backward2nd = Formula{ 79 Stencil: []Point{{Loc: 0, Coeff: 1}, {Loc: -1, Coeff: -2}, {Loc: -2, Coeff: 1}}, 80 Derivative: 2, 81 Step: 1e-4, 82 } 83 84 // Central represents a second-order accurate centered approximation 85 // to the first derivative. 86 var Central = Formula{ 87 Stencil: []Point{{Loc: -1, Coeff: -0.5}, {Loc: 1, Coeff: 0.5}}, 88 Derivative: 1, 89 Step: 6e-6, 90 } 91 92 // Central2nd represents a second-order accurate centered approximation 93 // to the second derivative. 94 var Central2nd = Formula{ 95 Stencil: []Point{{Loc: -1, Coeff: 1}, {Loc: 0, Coeff: -2}, {Loc: 1, Coeff: 1}}, 96 Derivative: 2, 97 Step: 1e-4, 98 } 99 100 var negativeStep = "fd: negative step" 101 102 // checkFormula checks if the formula is valid, and panics otherwise. 103 func checkFormula(formula Formula) { 104 if formula.Derivative == 0 || formula.Stencil == nil || formula.Step <= 0 { 105 panic("fd: bad formula") 106 } 107 } 108 109 // computeWorkers returns the desired number of workers given the concurrency 110 // level and number of evaluations. 111 func computeWorkers(concurrent bool, evals int) int { 112 if !concurrent { 113 return 1 114 } 115 nWorkers := runtime.GOMAXPROCS(0) 116 if nWorkers > evals { 117 nWorkers = evals 118 } 119 return nWorkers 120 } 121 122 // usesOrigin returns whether the stencil uses the origin, which is true iff 123 // one of the locations in the stencil equals 0. 124 func usesOrigin(stencil []Point) bool { 125 for _, pt := range stencil { 126 if pt.Loc == 0 { 127 return true 128 } 129 } 130 return false 131 } 132 133 // getOrigin returns the value at the origin. It returns originValue if originKnown 134 // is true. It returns the value returned by f if stencil contains a point with 135 // zero location, and NaN otherwise. 136 func getOrigin(originKnown bool, originValue float64, f func() float64, stencil []Point) float64 { 137 if originKnown { 138 return originValue 139 } 140 for _, pt := range stencil { 141 if pt.Loc == 0 { 142 return f() 143 } 144 } 145 return math.NaN() 146 } 147 148 const ( 149 badDerivOrder = "fd: invalid derivative order" 150 )