SecondDerivativeMidpointFormula.php 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. <?php
  2. namespace MathPHP\NumericalAnalysis\NumericalDifferentiation;
  3. use MathPHP\Exception;
  4. /**
  5. * Second Derivative Midpoint Formula
  6. *
  7. * In numerical analysis, the Second Derivative Midpoint formula is used for approximating
  8. * the second derivative of a function at a point in its domain.
  9. *
  10. * We can either directly supply a set of inputs and their corresponding outputs
  11. * for said function, or if we explicitly know the function, we can define it as a
  12. * callback function and then generate a set of points by evaluating that function
  13. * at 3 points between a start and end point.
  14. */
  15. class SecondDerivativeMidpointFormula extends NumericalDifferentiation
  16. {
  17. /**
  18. * Use the Second Derivative Midpoint Formula to approximate the second derivative of a
  19. * function at our $target. Our input can support either a set of arrays, or a callback
  20. * function with arguments (to produce a set of arrays). Each array in our
  21. * input contains two numbers which correspond to coordinates (x, y) or
  22. * equivalently, (x, f(x)), of the function f(x) whose derivative we are
  23. * approximating.
  24. *
  25. * The Second Derivative Midpoint Formula requires we supply 3 points that are evenly
  26. * spaced apart, and that our target equals the x-components of the midpoint.
  27. *
  28. * Example: differentiate(2, function($x) {return $x**2;}, 0, 4 ,3) will produce
  29. * a set of arrays by evaluating the callback at 3 evenly spaced points
  30. * between 0 and 4. Then, this array will be used in our approximation.
  31. *
  32. * Second Derivative Midpoint Formula:
  33. *
  34. * 1 h²
  35. * f″(x₀) = - [f(x₀-h) - 2f(x₀) + f(x₀+h)] - - f⁽⁴⁾(ζ)
  36. * h² 12
  37. *
  38. * where ζ lies between x₀ - h and x₀ + h
  39. *
  40. * @param float $target
  41. * The value at which we are approximating the derivative
  42. * @param callable|array<array{int|float, int|float}> $source
  43. * The source of our approximation. Should be either
  44. * a callback function or a set of arrays. Each array
  45. * (point) contains precisely two numbers, an x and y.
  46. * Example array: [[1,2], [2,3], [3,4]].
  47. * Example callback: function($x) {return $x**2;}
  48. * @param int|float ...$args
  49. * The arguments of our callback function: start,
  50. * end, and n. Example: differentiate($target, $source, 0, 8, 3).
  51. * If $source is a set of points, do not input any
  52. * $args. Example: approximate($source).
  53. *
  54. * @return float
  55. * The approximation of f'($target), i.e. the derivative
  56. * of our input at our target point
  57. *
  58. * @throws Exception\BadDataException
  59. */
  60. public static function differentiate(float $target, $source, ...$args)
  61. {
  62. // Get an array of points from our $source argument
  63. $points = self::getPoints($source, $args);
  64. // Validate input, sort points, make sure spacing is constant, and make
  65. // sure our target is contained in an interval supplied by our $source
  66. self::validate($points, $degree = 3);
  67. $sorted = self::sort($points);
  68. self::assertSpacingConstant($sorted);
  69. // Descriptive constants
  70. $x = self::X;
  71. $y = self::Y;
  72. // Guard clause - target must equal the x-component of the midpoint
  73. if ($sorted[1][$x] != $target) {
  74. throw new Exception\BadDataException('Your target must be the midpoint of your input');
  75. }
  76. // Initialize
  77. $h = ($sorted[2][$x] - $sorted[0][$x]) / 2;
  78. /*
  79. * 1 h²
  80. * f″(x₀) = - [f(x₀-h) - 2f(x₀) + f(x₀+h)] - - f⁽⁴⁾(ζ)
  81. * h² 12
  82. *
  83. * where ζ lies between x₀ - h and x₀ + h
  84. */
  85. $f⟮x₀⧿h⟯ = $sorted[0][$y];
  86. $f⟮x₀⟯ = $sorted[1][$y];
  87. $f⟮x₀⧾h⟯ = $sorted[2][$y];
  88. $derivative = ($f⟮x₀⧿h⟯ - 2 * $f⟮x₀⟯ + $f⟮x₀⧾h⟯) / ($h ** 2);
  89. return $derivative;
  90. }
  91. }