BisectionMethodTest.php 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. <?php
  2. namespace MathPHP\Tests\NumericalAnalysis\RootFinding;
  3. use MathPHP\Expression\Polynomial;
  4. use MathPHP\NumericalAnalysis\RootFinding\BisectionMethod;
  5. use MathPHP\Exception;
  6. class BisectionMethodTest extends \PHPUnit\Framework\TestCase
  7. {
  8. /**
  9. * @test Solve f(x) = x⁴ + 8x³ -13x² -92x + 96
  10. * Polynomial has 4 roots: 3, 1, -8 and -4
  11. * Uses \Closure object
  12. * @dataProvider dataProviderForPolynomial
  13. * @param int $a
  14. * @param int $b
  15. * @param int $expected
  16. * @throws \Exception
  17. */
  18. public function testSolvePolynomialWithFourRootsUsingClosure(int $a, int $b, int $expected)
  19. {
  20. // Given f(x) = x⁴ + 8x³ -13x² -92x + 96
  21. // This polynomial has 4 roots: 3, 1 ,-8 and -4
  22. $func = function ($x) {
  23. return $x ** 4 + 8 * $x ** 3 - 13 * $x ** 2 - 92 * $x + 96;
  24. };
  25. $tol = 0.00001;
  26. // When
  27. $x = BisectionMethod::solve($func, $a, $b, $tol);
  28. // The
  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 int $a
  37. * @param int $b
  38. * @param int $expected
  39. * @throws \Exception
  40. */
  41. public function testSolvePolynomialWithFourRootsUsingPolynomial(int $a, int $b, int $expected)
  42. {
  43. // Given f(x) = x⁴ + 8x³ -13x² -92x + 96
  44. // This polynomial has 4 roots: 3, 1 ,-8 and -4
  45. $polynomial = new Polynomial([1, 8, -13, -92, 96]);
  46. $tol = 0.00001;
  47. // When
  48. $x = BisectionMethod::solve($polynomial, $a, $b, $tol);
  49. // The
  50. $this->assertEqualsWithDelta($expected, $x, $tol);
  51. }
  52. /**
  53. * @return array (a, b, expected)
  54. */
  55. public function dataProviderForPolynomial(): array
  56. {
  57. return [
  58. 'f(x) = 0 where x is -4' => [-7, 0, -4],
  59. 'f(x) = 0 where x is -8' => [-10, -5, -8],
  60. 'f(x) = 0 where x is 3' => [2, 5, 3],
  61. 'f(x) = 0 where x is 1' => [0, 2, 1],
  62. 'f(x) = 0 where x is 1 (Switch a and b and test that they get reversed properly)' => [2, 0, 1],
  63. ];
  64. }
  65. /**
  66. * @test Solve more polynomials
  67. * @throws \Exception
  68. * Example from https://en.wikipedia.org/wiki/Bisection_method
  69. */
  70. public function testSolveXCubedSubtractXSubtractTwo()
  71. {
  72. // Given f(x) = x³ - x - 2
  73. $func = function ($x) {
  74. return $x ** 3 - $x - 2;
  75. };
  76. $tol = 0.001;
  77. // And solving for f(x) = 0 where x is about 1.521 (Find the root 1.521)
  78. $a = 1;
  79. $b = 2;
  80. $expected = 1.521;
  81. // When
  82. $x = BisectionMethod::solve($func, $a, $b, $tol);
  83. // Then
  84. $this->assertEqualsWithDelta($expected, $x, $tol);
  85. }
  86. /**
  87. * @test Solve more polynomials
  88. * @throws \Exception
  89. * Example from https://ece.uwaterloo.ca/~dwharder/NumericalAnalysis/10RootFinding/bisection/examples.html
  90. */
  91. public function testSolveXSquaredSubtractThree()
  92. {
  93. // Given f(x) = x² - 3
  94. $func = function ($x) {
  95. return $x ** 2 - 3;
  96. };
  97. $tol = 0.01;
  98. // And solving for f(x) = 0 where x is about 1.7344 (Find the root 1.7344)
  99. $a = 1;
  100. $b = 2;
  101. $expected = 1.7344;
  102. // When
  103. $x = BisectionMethod::solve($func, $a, $b, $tol);
  104. // Then
  105. $this->assertEqualsWithDelta($expected, $x, $tol);
  106. }
  107. /**
  108. * @test Solve more polynomials
  109. * @throws \Exception
  110. * Example from https://ece.uwaterloo.ca/~dwharder/NumericalAnalysis/10RootFinding/bisection/examples.html
  111. */
  112. public function testSolveEToNegativeXTimesSomeStuff()
  113. {
  114. // Given f(x) = e⁻ˣ (3.2 sin(x) - 0.5\cos(x))
  115. $func = function ($x) {
  116. return \exp(-$x) * ((3.2 * \sin($x)) - (0.5 * \cos($x)));
  117. };
  118. $tol = 0.0001;
  119. // And solving for f(x) = 0 where x is about 3.2968 (Find the root 3.2968)
  120. $a = 3;
  121. $b = 4;
  122. $expected = 3.2968;
  123. // When
  124. $x = BisectionMethod::solve($func, $a, $b, $tol);
  125. // Then
  126. $this->assertEqualsWithDelta($expected, $x, $tol);
  127. }
  128. /**
  129. * @test Solve with negative tolerance
  130. * @throws \Exception
  131. */
  132. public function testBisectionMethodExceptionNegativeTolerance()
  133. {
  134. // Given
  135. $func = function ($x) {
  136. return $x ** 4 + 8 * $x ** 3 - 13 * $x ** 2 - 92 * $x + 96;
  137. };
  138. // And
  139. $tol = -0.00001;
  140. $a = 0;
  141. $b = 2;
  142. // Then
  143. $this->expectException(Exception\OutOfBoundsException::class);
  144. // When
  145. $x = BisectionMethod::solve($func, $a, $b, $tol);
  146. }
  147. /**
  148. * @test Solve with zero interval
  149. * @throws \Exception
  150. */
  151. public function testBisectionMethodExceptionZeroInterval()
  152. {
  153. // Given
  154. $func = function ($x) {
  155. return $x ** 4 + 8 * $x ** 3 - 13 * $x ** 2 - 92 * $x + 96;
  156. };
  157. // And
  158. $tol = 0.00001;
  159. $a = 2;
  160. $b = 2;
  161. // Then
  162. $this->expectException(Exception\BadDataException::class);
  163. // When
  164. $x = BisectionMethod::solve($func, $a, $b, $tol);
  165. }
  166. /**
  167. * @test Solve with same signs
  168. * @throws \Exception
  169. */
  170. public function testBisectionMethodExceptionSameSigns()
  171. {
  172. // Given
  173. $func = function ($x) {
  174. return $x + 96;
  175. };
  176. // And
  177. $tol = 0.00001;
  178. $a = 0;
  179. $b = 1;
  180. // Then
  181. $this->expectException(Exception\BadDataException::class);
  182. // When
  183. $x = BisectionMethod::solve($func, $a, $b, $tol);
  184. }
  185. }