FixedPointIterationTest.php 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. <?php
  2. namespace MathPHP\Tests\NumericalAnalysis\RootFinding;
  3. use MathPHP\Expression\Polynomial;
  4. use MathPHP\NumericalAnalysis\RootFinding\FixedPointIteration;
  5. use MathPHP\Exception;
  6. class FixedPointIterationTest 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. * @throws \Exception
  13. */
  14. public function testSolvePolynomialWithFourRootsUsingClosure()
  15. {
  16. // Given f(x) = x⁴ + 8x³ -13x² -92x + 96 = 0
  17. // Note that f(x) has a root at 1
  18. // Rewrite f(x) = 0 as (x⁴ + 8x³ -13x² + 96)/92 = x
  19. // Thus, g(x) = (x⁴ + 8x³ -13x² + 96)/92
  20. $func = function ($x) {
  21. return ($x ** 4 + 8 * $x ** 3 - 13 * $x ** 2 + 96) / 92;
  22. };
  23. $tol = 0.00001;
  24. // g(0) = 96/92, where 0 < 96/92 < 2
  25. // g(2) = 124/92, where 0 < 124/92 < 2
  26. // g'(x) = (4x³ + 24x² - 26x)/92 is continuous
  27. // g'(x) has no root on [0, 2]. Thus, the derivative of g(x) does not
  28. // change direction on [0, 2]. So, if g(2) > g(0), then 0 < g(x) < 2
  29. // for all x in [0, 2]. So, there is a root in [0, 2]
  30. // And
  31. $a = 0;
  32. $b = 2;
  33. $p = 0;
  34. $expected = 1;
  35. // When solving for f(x) = 0 where x is 1
  36. // And switching a and b and test that they get reversed properly
  37. $x1 = FixedPointIteration::solve($func, $a, $b, $p, $tol);
  38. $x2 = FixedPointIteration::solve($func, $b, $a, $p, $tol);
  39. // Then
  40. $this->assertEqualsWithDelta($expected, $x1, $tol);
  41. $this->assertEqualsWithDelta($expected, $x2, $tol);
  42. }
  43. /**
  44. * @test Solve f(x) = x⁴ + 8x³ -13x² -92x + 96
  45. * Polynomial has 4 roots: 3, 1, -8 and -4
  46. * Uses Polynomial object
  47. * @throws \Exception
  48. */
  49. public function testSolvePolynomialWithFourRootsUsingPolynomial()
  50. {
  51. // Given f(x) = x⁴ + 8x³ -13x² -92x + 96 = 0
  52. // Note that f(x) has a root at 1
  53. // Rewrite f(x) = 0 as (x⁴ + 8x³ -13x² + 96)/92 = x
  54. // Thus, g(x) = (x⁴ + 8x³ -13x² + 96)/92
  55. $polynomial = new Polynomial([1 / 92, 8 / 92, -13 / 92, 96 / 92]);
  56. $tol = 0.00001;
  57. // g(0) = 96/92, where 0 < 96/92 < 2
  58. // g(2) = 124/92, where 0 < 124/92 < 2
  59. // g'(x) = (4x³ + 24x² - 26x)/92 is continuous
  60. // g'(x) has no root on [0, 2]. Thus, the derivative of g(x) does not
  61. // change direction on [0, 2]. So, if g(2) > g(0), then 0 < g(x) < 2
  62. // for all x in [0, 2]. So, there is a root in [0, 2]
  63. // And
  64. $a = 0;
  65. $b = 2;
  66. $p = 0;
  67. $expected = 1;
  68. // When solving for f(x) = 0 where x is 1
  69. // And switching a and b and test that they get reversed properly
  70. $x1 = FixedPointIteration::solve($polynomial, $a, $b, $p, $tol);
  71. $x2 = FixedPointIteration::solve($polynomial, $b, $a, $p, $tol);
  72. // Then
  73. $this->assertEqualsWithDelta($expected, $x1, $tol);
  74. $this->assertEqualsWithDelta($expected, $x2, $tol);
  75. }
  76. /**
  77. * @test Solve negative tolerance
  78. * @throws \Exception
  79. */
  80. public function testFixedPointIterationExceptionNegativeTolerance()
  81. {
  82. // Given
  83. $func = function ($x) {
  84. return ($x ** 4 + 8 * $x ** 3 - 13 * $x ** 2 + 96) / 92;
  85. };
  86. $tol = -0.00001;
  87. $a = 0;
  88. $b = 3;
  89. $p = 0;
  90. // Then
  91. $this->expectException(Exception\OutOfBoundsException::class);
  92. // When
  93. $x = FixedPointIteration::solve($func, $a, $b, $p, $tol);
  94. }
  95. /**
  96. * @test Solve zero interval
  97. * @throws \Exception
  98. */
  99. public function testFixedPointIterationExceptionZeroInterval()
  100. {
  101. // Given
  102. $func = function ($x) {
  103. return ($x ** 4 + 8 * $x ** 3 - 13 * $x ** 2 + 96) / 92;
  104. };
  105. $tol = 0.00001;
  106. $a = 3;
  107. $b = 3;
  108. $p = 3;
  109. // Then
  110. $this->expectException(Exception\BadDataException::class);
  111. // When
  112. $x = FixedPointIteration::solve($func, $a, $b, $p, $tol);
  113. }
  114. /**
  115. * @test Solve guess not in interval
  116. * @throws \Exception
  117. */
  118. public function testFixedPointIterationExceptionGuessNotInInterval()
  119. {
  120. // Given
  121. $func = function ($x) {
  122. return ($x ** 4 + 8 * $x ** 3 - 13 * $x ** 2 + 96) / 92;
  123. };
  124. $tol = 0.00001;
  125. $a = 0;
  126. $b = 3;
  127. $p = -1;
  128. // Then
  129. $this->expectException(Exception\OutOfBoundsException::class);
  130. // When
  131. $x = FixedPointIteration::solve($func, $a, $b, $p, $tol);
  132. }
  133. }