NewtonsMethodTest.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. <?php
  2. namespace MathPHP\Tests\NumericalAnalysis\RootFinding;
  3. use MathPHP\Expression\Polynomial;
  4. use MathPHP\NumericalAnalysis\RootFinding\NewtonsMethod;
  5. class NewtonsMethodTest extends \PHPUnit\Framework\TestCase
  6. {
  7. /**
  8. * @test Solve f(x) = x⁴ + 8x³ -13x² -92x + 96
  9. * Polynomial has 4 roots: 3, 1, -8 and -4
  10. * Uses \Closure object
  11. * @dataProvider dataProviderForPolynomial
  12. * @param float[] $args
  13. * @param int $expected
  14. * @throws \Exception
  15. */
  16. public function testSolvePolynomialWithFourRootsUsingClosure(array $args, int $expected)
  17. {
  18. // Given
  19. $func = function ($x) {
  20. return $x ** 4 + 8 * $x ** 3 - 13 * $x ** 2 - 92 * $x + 96;
  21. };
  22. // And
  23. $target = 0;
  24. $position = 0;
  25. $tol = 0.00001;
  26. // When solving for f(x) = 0 where x is $expected
  27. $x = NewtonsMethod::solve($func, $args, $target, $tol, $position);
  28. // Then
  29. $this->assertEqualsWithDelta($expected, $x, $tol);
  30. }
  31. /**
  32. * @test Solve f(x) = x⁴ + 8x³ -13x² -92x + 96
  33. * Polynomial has 4 roots: 3, 1, -8 and -4
  34. * Uses Polynomial object
  35. * @dataProvider dataProviderForPolynomial
  36. * @param float[] $args
  37. * @param int $expected
  38. * @throws \Exception
  39. */
  40. public function testSolvePolynomialWithFourRootsUsingPolynomial(array $args, int $expected)
  41. {
  42. // Given
  43. $polynomial = new Polynomial([1, 8, -13, -92, 96]);
  44. // And
  45. $target = 0;
  46. $position = 0;
  47. $tol = 0.00001;
  48. // When solving for f(x) = 0 where x is $expected
  49. $x = NewtonsMethod::solve($polynomial, $args, $target, $tol, $position);
  50. // Then
  51. $this->assertEqualsWithDelta($expected, $x, $tol);
  52. }
  53. /**
  54. * @return array (args, expected)
  55. */
  56. public function dataProviderForPolynomial(): array
  57. {
  58. return [
  59. 'solving for f(x) = 0 where x is -4' => [[-4.1], -4],
  60. 'solving for f(x) = 0 where x is -8' => [[-8.4], -8],
  61. 'solving for f(x) = 0 where x is 3' => [[3.5], 3],
  62. 'When solving f(x) = 0 where x is 1' => [[-.3], 1],
  63. ];
  64. }
  65. /**
  66. * @test Solve f(x) = x³ - x + 1
  67. * Polynomial has a root of approximately -1.324717
  68. * @throws \Exception
  69. */
  70. public function testXCubedSubtractXPlusOne()
  71. {
  72. // Given
  73. $func = function ($x) {
  74. return $x ** 3 - $x + 1;
  75. };
  76. // And
  77. $expected = -1.324717;
  78. $args = [-1];
  79. $target = 0;
  80. $position = 0;
  81. $tol = 0.00001;
  82. // When
  83. $root = NewtonsMethod::solve($func, $args, $target, $tol, $position);
  84. // Then
  85. $this->assertEqualsWithDelta($expected, $root, $tol);
  86. }
  87. /**
  88. * @test Solve f(x) = x² - 5
  89. * Polynomial has a root of √5
  90. * @throws \Exception
  91. */
  92. public function testXSquaredSubtractFive()
  93. {
  94. // Given
  95. $func = function ($x) {
  96. return $x ** 2 - 5;
  97. };
  98. // And
  99. $expected = \sqrt(5);
  100. $args = [2];
  101. $target = 0;
  102. $position = 0;
  103. $tol = 0.00001;
  104. // When
  105. $root = NewtonsMethod::solve($func, $args, $target, $tol, $position);
  106. // Then
  107. $this->assertEqualsWithDelta($expected, $root, $tol);
  108. }
  109. /**
  110. * @test Solve \cos(x) - 2x
  111. * Has a root of approximately 0.450183
  112. * @throws \Exception
  113. */
  114. public function testCosXSubtractTwoX()
  115. {
  116. // Given
  117. $func = function ($x) {
  118. return \cos($x) - 2 * $x;
  119. };
  120. // And
  121. $expected = 0.450183;
  122. $args = [0];
  123. $target = 0;
  124. $position = 0;
  125. $tol = 0.00001;
  126. // When
  127. $root = NewtonsMethod::solve($func, $args, $target, $tol, $position);
  128. // Then
  129. $this->assertEqualsWithDelta($expected, $root, $tol);
  130. }
  131. /**
  132. * @test Solve \cos(x) = x
  133. * Has a root of approximately 0.7390851332
  134. * @throws \Exception
  135. */
  136. public function testCosXEqualsX()
  137. {
  138. // Given
  139. $func = function ($x) {
  140. return \cos($x);
  141. };
  142. // And
  143. $x = 0.7390851332;
  144. $args = [0.6];
  145. $target = $x;
  146. $position = 0;
  147. $tol = 0.00001;
  148. // When
  149. $root = NewtonsMethod::solve($func, $args, $target, $tol, $position);
  150. // Then
  151. $this->assertEqualsWithDelta($x, $root, $tol);
  152. }
  153. /**
  154. * @test Solve with negative tolerance
  155. * @throws \Exception
  156. */
  157. public function testNewtonsMethodExceptionNegativeTolerance()
  158. {
  159. // Given
  160. $func = function ($x) {
  161. return $x ** 4 + 8 * $x ** 3 - 13 * $x ** 2 - 92 * $x + 96;
  162. };
  163. // And
  164. $args = [-4.1];
  165. $target = 0;
  166. $position = 0;
  167. $tol = -0.00001;
  168. // Then
  169. $this->expectException('\Exception');
  170. // When
  171. $x = NewtonsMethod::solve($func, $args, $target, $tol, $position);
  172. }
  173. /**
  174. * @test Solve with near zero slope
  175. * @throws \Exception
  176. */
  177. public function testNewtonsMethodNearZeroSlopeNAN()
  178. {
  179. // Given
  180. $func = function ($x) {
  181. return $x / $x;
  182. };
  183. // And
  184. $args = [0.1];
  185. $target = 0;
  186. $position = 0;
  187. $tol = 0.00001;
  188. // When
  189. $x = NewtonsMethod::solve($func, $args, $target, $tol, $position);
  190. // Then
  191. $this->assertNan($x);
  192. }
  193. /**
  194. * @test Solve with no real solutions
  195. * @throws \Exception
  196. */
  197. public function testNewtonsMethodNoRealSolutionsNAN()
  198. {
  199. // Given
  200. $func = function ($x) {
  201. return $x ** 2 + 3 * $x + 3;
  202. };
  203. // And
  204. $args = [0.1];
  205. $target = 0;
  206. $position = 0;
  207. $tol = 0.00001;
  208. // When
  209. $x = NewtonsMethod::solve($func, $args, $target, $tol, $position);
  210. // Then
  211. $this->assertNan($x);
  212. }
  213. /**
  214. * @test Solve f(x) = ³√x for ³√x = 0
  215. * Has no solution
  216. * @throws \Exception
  217. */
  218. public function testNoSolutionCubeRootX()
  219. {
  220. // Given
  221. $func = function ($x) {
  222. return $x ** (1 / 3);
  223. };
  224. // And
  225. $args = [1];
  226. $target = 0;
  227. $position = 0;
  228. $tol = 0.00001;
  229. // When
  230. $root = NewtonsMethod::solve($func, $args, $target, $tol, $position);
  231. // Then
  232. $this->assertNan($root);
  233. }
  234. }