SimpsonsRuleTest.php 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. <?php
  2. namespace MathPHP\Tests\NumericalAnalysis\NumericalIntegration;
  3. use MathPHP\Expression\Polynomial;
  4. use MathPHP\NumericalAnalysis\NumericalIntegration\SimpsonsRule;
  5. class SimpsonsRuleTest extends \PHPUnit\Framework\TestCase
  6. {
  7. /**
  8. * @test approximate with points (0, 1), (1.5, 6.25) and (3, 16)
  9. * @throws \Exception
  10. *
  11. * f(x) = x² + 2x + 1
  12. * Antiderivative F(x) = (1/3)x³ + x² + x
  13. * Indefinite integral over [0, 3] = F(3) - F(0) = 21
  14. *
  15. * h denotes the size of subintervals, or equivalently, the distance between two points
  16. * ζ₁, ζ₂, ... denotes the max of the fourth derivative of f(x) on interval 1, 2, ...
  17. * f'(x) = 2x + 2
  18. * f''(x) = 2
  19. * f'''(x) = 0
  20. * f''''(x) = 0
  21. * ζ = f''''(x) = 0
  22. * Error = O(h^5 * ζ) = 0
  23. *
  24. * Approximate with points (0, 1), (1.5, 6.25) and (3, 16)
  25. * Error = 0
  26. */
  27. public function testApproximateWithPoints()
  28. {
  29. // Given
  30. $points = [[0, 1], [1.5, 6.25], [3, 16]];
  31. $tol = 0;
  32. $expected = 21;
  33. // When
  34. $x = SimpsonsRule::approximate($points);
  35. // Then
  36. $this->assertEqualsWithDelta($expected, $x, $tol);
  37. }
  38. /**
  39. * @test approximate with points not sorted: (0, 1), (1.5, 6.25) and (3, 16)
  40. * @throws \Exception
  41. *
  42. * f(x) = x² + 2x + 1
  43. * Antiderivative F(x) = (1/3)x³ + x² + x
  44. * Indefinite integral over [0, 3] = F(3) - F(0) = 21
  45. *
  46. * h denotes the size of subintervals, or equivalently, the distance between two points
  47. * ζ₁, ζ₂, ... denotes the max of the fourth derivative of f(x) on interval 1, 2, ...
  48. * f'(x) = 2x + 2
  49. * f''(x) = 2
  50. * f'''(x) = 0
  51. * f''''(x) = 0
  52. * ζ = f''''(x) = 0
  53. * Error = O(h^5 * ζ) = 0
  54. *
  55. * Approximate with points (0, 1), (1.5, 6.25) and (3, 16)
  56. * Error = 0
  57. */
  58. public function testApproximateWithPointsNotSorted()
  59. {
  60. // Given
  61. $points = [[1.5, 6.25], [3, 16], [0, 1]];
  62. $tol = 0;
  63. $expected = 21;
  64. // When
  65. $x = SimpsonsRule::approximate($points);
  66. // Then
  67. $this->assertEqualsWithDelta($expected, $x, $tol);
  68. }
  69. /**
  70. * @test approximate using callback
  71. * @throws \Exception
  72. *
  73. * f(x) = x² + 2x + 1
  74. * Antiderivative F(x) = (1/3)x³ + x² + x
  75. * Indefinite integral over [0, 3] = F(3) - F(0) = 21
  76. *
  77. * h denotes the size of subintervals, or equivalently, the distance between two points
  78. * ζ₁, ζ₂, ... denotes the max of the fourth derivative of f(x) on interval 1, 2, ...
  79. * f'(x) = 2x + 2
  80. * f''(x) = 2
  81. * f'''(x) = 0
  82. * f''''(x) = 0
  83. * ζ = f''''(x) = 0
  84. * Error = O(h^5 * ζ) = 0
  85. */
  86. public function testApproximateUsingCallback()
  87. {
  88. // Given x² + 2x + 1
  89. $func = $func = function ($x) {
  90. return $x ** 2 + 2 * $x + 1;
  91. };
  92. $start = 0;
  93. $end = 3;
  94. $n = 3;
  95. $tol = 0;
  96. $expected = 21;
  97. // When
  98. $x = SimpsonsRule::approximate($func, $start, $end, $n);
  99. // Then
  100. $this->assertEqualsWithDelta($expected, $x, $tol);
  101. }
  102. /**
  103. * @test approximate using polynomial
  104. * @throws \Exception
  105. *
  106. * f(x) = x² + 2x + 1
  107. * Antiderivative F(x) = (1/3)x³ + x² + x
  108. * Indefinite integral over [0, 3] = F(3) - F(0) = 21
  109. *
  110. * h denotes the size of subintervals, or equivalently, the distance between two points
  111. * ζ₁, ζ₂, ... denotes the max of the fourth derivative of f(x) on interval 1, 2, ...
  112. * f'(x) = 2x + 2
  113. * f''(x) = 2
  114. * f'''(x) = 0
  115. * f''''(x) = 0
  116. * ζ = f''''(x) = 0
  117. * Error = O(h^5 * ζ) = 0
  118. */
  119. public function testApproximateUsingPolynomial()
  120. {
  121. // Given x² + 2x + 1
  122. $polynomial = new Polynomial([1, 2, 1]);
  123. $start = 0;
  124. $end = 3;
  125. $n = 3;
  126. $tol = 0;
  127. $expected = 21;
  128. // When
  129. $x = SimpsonsRule::approximate($polynomial, $start, $end, $n);
  130. // Then
  131. $this->assertEqualsWithDelta($expected, $x, $tol);
  132. }
  133. /**
  134. * @test approximate error when there are not even even number of subintervals, or equivalently, there are not an add number of points
  135. * @throws \Exception
  136. */
  137. public function testApproximateErrorSubintervalsNotEven()
  138. {
  139. // Given
  140. $points = [[0,0], [4,4], [2,2], [6,6]];
  141. // Then
  142. $this->expectException(\Exception::class);
  143. // When
  144. SimpsonsRule::approximate($points);
  145. }
  146. /**
  147. * @test approximate error when there is not constant spacing between points
  148. * @throws \Exception
  149. */
  150. public function testNonConstantSpacingException()
  151. {
  152. // Given
  153. $points = [[0,0], [3,3], [2,2]];
  154. // Then
  155. $this->expectException(\Exception::class);
  156. // When
  157. SimpsonsRule::approximate($points);
  158. }
  159. }