test_rules_integrals.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. # This file is part of TRS (http://math.kompiler.org)
  2. #
  3. # TRS is free software: you can redistribute it and/or modify it under the
  4. # terms of the GNU Affero General Public License as published by the Free
  5. # Software Foundation, either version 3 of the License, or (at your option) any
  6. # later version.
  7. #
  8. # TRS is distributed in the hope that it will be useful, but WITHOUT ANY
  9. # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  10. # A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
  11. # details.
  12. #
  13. # You should have received a copy of the GNU Affero General Public License
  14. # along with TRS. If not, see <http://www.gnu.org/licenses/>.
  15. from src.rules.integrals import choose_constant, solve_integral, \
  16. match_solve_definite, solve_definite, match_integrate_variable_power, \
  17. integrate_variable_root, integrate_variable_exponent, \
  18. match_constant_integral, constant_integral, single_variable_integral, \
  19. match_factor_out_constant, factor_out_integral_negation, \
  20. factor_out_constant, match_division_integral, division_integral, \
  21. extend_division_integral, match_function_integral, \
  22. logarithm_integral, sinus_integral, cosinus_integral, \
  23. match_sum_rule_integral, sum_rule_integral, \
  24. match_remove_definite_constant, remove_definite_constant
  25. from src.node import Scope, int_def
  26. from src.possibilities import Possibility as P
  27. from tests.rulestestcase import RulesTestCase, tree
  28. class TestRulesIntegrals(RulesTestCase):
  29. def test_choose_constant(self):
  30. a, b, c = tree('A, B, C')
  31. self.assertEqual(choose_constant(tree('int x ^ n')), c)
  32. self.assertEqual(choose_constant(tree('int x ^ c')), a)
  33. self.assertEqual(choose_constant(tree('int a ^ c da')), b)
  34. def test_match_solve_definite(self):
  35. root = tree('[x ^ 2]_a^b')
  36. self.assertEqualPos(match_solve_definite(root),
  37. [P(root, solve_definite)])
  38. def test_solve_integral(self):
  39. root, F, Fc = tree('int x ^ 2 dx, 1 / 3 x ^ 3, 1 / 3 x ^ 3 + C')
  40. self.assertEqual(solve_integral(root, F), Fc)
  41. x2, x, a, b = root = tree('int_a^b x ^ 2 dx')
  42. self.assertEqual(solve_integral(root, F), int_def(Fc, a, b))
  43. def test_solve_integral_skip_definite(self):
  44. root, x, C, l1 = tree('int_a^b y ^ x dy, x, C, 1')
  45. F = tree('1 / (x + 1)y ^ (x + 1)')
  46. y, a, b = root[1:4]
  47. Fx = lambda y: l1 / (x + 1) * y ** (x + 1) + C
  48. self.assertEqual(solve_integral(root, F), Fx(b) - Fx(a))
  49. def test_solve_definite(self):
  50. root, expect = tree('[x ^ 2]_a^b, b ^ 2 - a ^ 2')
  51. self.assertEqual(solve_definite(root, ()), expect)
  52. def test_match_integrate_variable_power(self):
  53. root = tree('int x ^ n')
  54. self.assertEqualPos(match_integrate_variable_power(root),
  55. [P(root, integrate_variable_root)])
  56. root = tree('int x ^ n')
  57. self.assertEqualPos(match_integrate_variable_power(root),
  58. [P(root, integrate_variable_root)])
  59. root = tree('int -x ^ n')
  60. self.assertEqualPos(match_integrate_variable_power(root), [])
  61. for root in tree('int g ^ x, int g ^ x'):
  62. self.assertEqualPos(match_integrate_variable_power(root),
  63. [P(root, integrate_variable_exponent)])
  64. def test_integrate_variable_root(self):
  65. root, expect = tree('int x ^ n, 1 / (n + 1) * x ^ (n + 1) + C')
  66. self.assertEqual(integrate_variable_root(root, ()), expect)
  67. def test_integrate_variable_exponent(self):
  68. root, expect = tree('int g ^ x, g ^ x / ln(g) + C')
  69. self.assertEqual(integrate_variable_exponent(root, ()), expect)
  70. def test_match_constant_integral(self):
  71. root = tree('int x dx')
  72. self.assertEqualPos(match_constant_integral(root),
  73. [P(root, single_variable_integral)])
  74. root = tree('int 2')
  75. self.assertEqualPos(match_constant_integral(root),
  76. [P(root, constant_integral)])
  77. root = tree('int c dx')
  78. self.assertEqualPos(match_constant_integral(root),
  79. [P(root, constant_integral)])
  80. def test_single_variable_integral(self):
  81. root, expect = tree('int x, int x ^ 1')
  82. self.assertEqual(single_variable_integral(root, ()), expect)
  83. def test_constant_integral(self):
  84. root, expect = tree('int 2, 2x + C')
  85. self.assertEqual(constant_integral(root, ()), expect)
  86. root, expect = tree('int_0^4 2, [2x + C]_0^4')
  87. self.assertEqual(constant_integral(root, ()), expect)
  88. def test_match_factor_out_constant(self):
  89. root, c, cx = tree('int cx dx, c, cx')
  90. self.assertEqualPos(match_factor_out_constant(root),
  91. [P(root, factor_out_constant, (Scope(cx), c))])
  92. root = tree('int -x2 dx')
  93. self.assertEqualPos(match_factor_out_constant(root),
  94. [P(root, factor_out_integral_negation)])
  95. def test_factor_out_integral_negation(self):
  96. root, expect = tree('int -x ^ 2 dx, -int x ^ 2 dx')
  97. self.assertEqual(factor_out_integral_negation(root, ()), expect)
  98. def test_factor_out_constant(self):
  99. root, expect = tree('int cx dx, c int x dx')
  100. c, x2 = cx2 = root[0]
  101. self.assertEqual(factor_out_constant(root, (Scope(cx2), c)), expect)
  102. def test_match_division_integral(self):
  103. root0, root1 = tree('int 1 / x, int 2 / x')
  104. self.assertEqualPos(match_division_integral(root0),
  105. [P(root0, division_integral)])
  106. self.assertEqualPos(match_division_integral(root1),
  107. [P(root1, extend_division_integral)])
  108. def test_division_integral(self):
  109. root, expect = tree('int 1 / x dx, ln|x| + C')
  110. self.assertEqual(division_integral(root, ()), expect)
  111. def test_extend_division_integral(self):
  112. root, expect = tree('int a / x dx, int a(1 / x) dx')
  113. self.assertEqual(extend_division_integral(root, ()), expect)
  114. def test_match_division_integral_chain(self):
  115. self.assertRewrite([
  116. 'int a / x',
  117. 'int a * 1 / x dx',
  118. 'a(int 1 / x dx)',
  119. 'a(ln(|x|) + C)',
  120. 'a ln(|x|) + aC',
  121. # FIXME: 'a ln(|x|) + C', # ac -> C
  122. ])
  123. def test_match_function_integral(self):
  124. root = tree('int ln x')
  125. self.assertEqualPos(match_function_integral(root),
  126. [P(root, logarithm_integral)])
  127. root = tree('int sin x')
  128. self.assertEqualPos(match_function_integral(root),
  129. [P(root, sinus_integral)])
  130. root = tree('int cos x')
  131. self.assertEqualPos(match_function_integral(root),
  132. [P(root, cosinus_integral)])
  133. root = tree('int sqrt x')
  134. self.assertEqualPos(match_function_integral(root), [])
  135. def test_logarithm_integral(self):
  136. root, expect = tree('int ln x, (xlnx - x) / ln e + C')
  137. self.assertEqual(logarithm_integral(root, ()), expect)
  138. def test_sinus_integral(self):
  139. root, expect = tree('int sin x, -cos x + C')
  140. self.assertEqual(sinus_integral(root, ()), expect)
  141. def test_cosinus_integral(self):
  142. root, expect = tree('int cos x, sin x + C')
  143. self.assertEqual(cosinus_integral(root, ()), expect)
  144. def test_match_sum_rule_integral(self):
  145. (f, g), x = root = tree('int (2x + 3x) dx')
  146. self.assertEqualPos(match_sum_rule_integral(root),
  147. [P(root, sum_rule_integral, (Scope(root[0]), f))])
  148. ((f, g), h), x = root = tree('int (2x + 3x + 4x) dx')
  149. self.assertEqualPos(match_sum_rule_integral(root),
  150. [P(root, sum_rule_integral, (Scope(root[0]), f)),
  151. P(root, sum_rule_integral, (Scope(root[0]), g)),
  152. P(root, sum_rule_integral, (Scope(root[0]), h))])
  153. def test_sum_rule_integral(self):
  154. ((f, g), h), x = root = tree('int (2x + 3x + 4x) dx')
  155. self.assertEqual(sum_rule_integral(root, (Scope(root[0]), f)),
  156. tree('int 2x dx + int (3x + 4x) dx'))
  157. self.assertEqual(sum_rule_integral(root, (Scope(root[0]), g)),
  158. tree('int 3x dx + int (2x + 4x) dx'))
  159. self.assertEqual(sum_rule_integral(root, (Scope(root[0]), h)),
  160. tree('int 4x dx + int (2x + 3x) dx'))
  161. def test_match_remove_definite_constant(self):
  162. Fx, a, b = root = tree('[2x + C]_a^b')
  163. self.assertEqualPos(match_remove_definite_constant(root),
  164. [P(root, remove_definite_constant, (Scope(Fx), Fx[1]))])
  165. Fx, a, b = root = tree('[2x + x]_a^b')
  166. self.assertEqualPos(match_remove_definite_constant(root), [])
  167. Fx, a, b = root = tree('[2x]_a^b')
  168. self.assertEqualPos(match_remove_definite_constant(root), [])
  169. def test_remove_definite_constant(self):
  170. root, e = tree('[2x + C]_a^b, [2x]_a^b')
  171. Fx = root[0]
  172. self.assertEqual(remove_definite_constant(root, (Scope(Fx), Fx[1])), e)