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  )