SecantMethodTest.php 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. <?php
  2. namespace MathPHP\Tests\NumericalAnalysis\RootFinding;
  3. use MathPHP\Expression\Polynomial;
  4. use MathPHP\NumericalAnalysis\RootFinding\SecantMethod;
  5. class SecantMethodTest 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 int $p₀
  13. * @param int $p₁
  14. * @param int $expected
  15. * @throws \Exception
  16. */
  17. public function testSolvePolynomialWithFourRootsUsingClosure(int $p₀, int $p₁, int $expected)
  18. {
  19. // Given
  20. $func = function ($x) {
  21. return $x ** 4 + 8 * $x ** 3 - 13 * $x ** 2 - 92 * $x + 96;
  22. };
  23. $tol = 0.00001;
  24. // When solving for f(x) = 0 where x $expected
  25. $x = SecantMethod::solve($func, $p₀, $p₁, $tol);
  26. // Then
  27. $this->assertEqualsWithDelta($expected, $x, $tol);
  28. }
  29. /**
  30. * @test Solve f(x) = x⁴ + 8x³ -13x² -92x + 96
  31. * Polynomial has 4 roots: 3, 1, -8 and -4
  32. * Uses Polynomial object
  33. * @dataProvider dataProviderForPolynomial
  34. * @param int $p₀
  35. * @param int $p₁
  36. * @param int $expected
  37. * @throws \Exception
  38. */
  39. public function testSolvePolynomialWithFourRootsUsingPolynomial(int $p₀, int $p₁, int $expected)
  40. {
  41. // Given
  42. $polynomial = new Polynomial([1, 8, -13, -92, 96]);
  43. $tol = 0.00001;
  44. // When solving for f(x) = 0 where x is $expected
  45. $x = SecantMethod::solve($polynomial, $p₀, $p₁, $tol);
  46. // Then
  47. $this->assertEqualsWithDelta($expected, $x, $tol);
  48. }
  49. /**
  50. * @return array (p₀, p₁, expected)
  51. */
  52. public function dataProviderForPolynomial(): array
  53. {
  54. return [
  55. 'solving for f(x) = 0 where x is -4' => [-5, -2, -4],
  56. 'solving for f(x) = 0 where x is -8' => [-10, -7, -8],
  57. 'solving for f(x) = 0 where x is 3' => [2, 5, 3],
  58. 'solving for f(x) = 0 where x is 1' => [-1, 2, 1],
  59. 'Solve for f(x) = 0 where x is 1: Switch p₀ and p₁ and test that they get reversed properly' => [-1, 2, 1],
  60. ];
  61. }
  62. /**
  63. * @test Solve f(x) = x³ - x + 1
  64. * Polynomial has a root of approximately -1.324717
  65. * @throws \Exception
  66. */
  67. public function testXCubedSubtractXPlusOne()
  68. {
  69. // Given
  70. $func = function ($x) {
  71. return $x ** 3 - $x + 1;
  72. };
  73. // And
  74. $expected = -1.324717;
  75. $p₁ = -3;
  76. $p₀ = 1;
  77. $tol = 0.00001;
  78. // When
  79. $root = SecantMethod::solve($func, $p₀, $p₁, $tol);
  80. // Then
  81. $this->assertEqualsWithDelta($expected, $root, $tol);
  82. }
  83. /**
  84. * @test Solve f(x) = x² - 5
  85. * Polynomial has a root of √5
  86. * @throws \Exception
  87. */
  88. public function testXSquaredSubtractFive()
  89. {
  90. // Given
  91. $func = function ($x) {
  92. return $x ** 2 - 5;
  93. };
  94. // And
  95. $expected = \sqrt(5);
  96. $p₁ = 1;
  97. $p₀ = 5;
  98. $tol = 0.00001;
  99. // When
  100. $root = SecantMethod::solve($func, $p₀, $p₁, $tol);
  101. // Then
  102. $this->assertEqualsWithDelta($expected, $root, $tol);
  103. }
  104. /**
  105. * @test Solve \cos(x) - 2x
  106. * Has a root of approximately 0.450183
  107. * @throws \Exception
  108. */
  109. public function testCosXSubtractTwoX()
  110. {
  111. // Given
  112. $func = function ($x) {
  113. return \cos($x) - 2 * $x;
  114. };
  115. // And
  116. $expected = 0.450183;
  117. $p₁ = 0;
  118. $p₀ = 3;
  119. $tol = 0.00001;
  120. // When
  121. $root = SecantMethod::solve($func, $p₀, $p₁, $tol);
  122. // Then
  123. $this->assertEqualsWithDelta($expected, $root, $tol);
  124. }
  125. /**
  126. * @test Solve with negative tolerance
  127. * @throws \Exception
  128. */
  129. public function testExceptionNegativeTolerance()
  130. {
  131. // Given
  132. $func = function ($x) {
  133. return $x ** 4 + 8 * $x ** 3 - 13 * $x ** 2 - 92 * $x + 96;
  134. };
  135. // And
  136. $tol = -0.00001;
  137. $p₀ = -1;
  138. $p₁ = 2;
  139. // Then
  140. $this->expectException('\Exception');
  141. // When
  142. $x = SecantMethod::solve($func, $p₀, $p₁, $tol);
  143. }
  144. /**
  145. * @test Solve with zero interval
  146. * @throws \Exception
  147. */
  148. public function testExceptionZeroInterval()
  149. {
  150. // Given
  151. $func = function ($x) {
  152. return $x ** 4 + 8 * $x ** 3 - 13 * $x ** 2 - 92 * $x + 96;
  153. };
  154. // And
  155. $tol = 0.00001;
  156. $p₀ = 1;
  157. $p₁ = 1;
  158. // Then
  159. $this->expectException('\Exception');
  160. // When
  161. $x = SecantMethod::solve($func, $p₀, $p₁, $tol);
  162. }
  163. }