test_rules_derivatives.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  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.derivatives import get_derivation_variable, \
  16. match_zero_derivative, match_one_derivative, one_derivative, \
  17. zero_derivative, match_variable_power, variable_root, \
  18. variable_exponent, match_const_deriv_multiplication, \
  19. const_deriv_multiplication, chain_rule, match_logarithmic, \
  20. logarithmic, match_goniometric, sinus, cosinus, tangens, \
  21. match_sum_product_rule, sum_rule, product_rule, match_quotient_rule, \
  22. quotient_rule, power_rule
  23. from src.node import Scope, sin, cos, ln, der
  24. from src.possibilities import Possibility as P
  25. from tests.rulestestcase import RulesTestCase, tree
  26. class TestRulesDerivatives(RulesTestCase):
  27. def test_get_derivation_variable(self):
  28. xy0, xy1, x, l1 = tree('d/dx xy, (xy)\', x\', 1\'')
  29. self.assertEqual(get_derivation_variable(xy0), 'x')
  30. self.assertEqual(get_derivation_variable(xy1), 'x')
  31. self.assertEqual(get_derivation_variable(x), 'x')
  32. self.assertIsNone(get_derivation_variable(l1))
  33. def test_match_zero_derivative(self):
  34. root = tree('d/dy x')
  35. self.assertEqualPos(match_zero_derivative(root),
  36. [P(root, zero_derivative)])
  37. root = tree('d/dx 2')
  38. self.assertEqualPos(match_zero_derivative(root),
  39. [P(root, zero_derivative)])
  40. def test_zero_derivative(self):
  41. root = tree('d/dx 1')
  42. self.assertEqual(zero_derivative(root, ()), 0)
  43. def test_match_one_derivative(self):
  44. root = tree('d/dx x')
  45. self.assertEqualPos(match_one_derivative(root),
  46. [P(root, one_derivative)])
  47. root = tree('d/dx x')
  48. self.assertEqualPos(match_one_derivative(root),
  49. [P(root, one_derivative)])
  50. def test_one_derivative(self):
  51. root = tree('d/dx x')
  52. self.assertEqual(one_derivative(root, ()), 1)
  53. def test_match_const_deriv_multiplication(self):
  54. root = tree('d/dx 2x')
  55. l2, x = root[0]
  56. self.assertEqualPos(match_const_deriv_multiplication(root),
  57. [P(root, const_deriv_multiplication, (Scope(root[0]), l2, x))])
  58. (x, y), x = root = tree('d/dx xy')
  59. self.assertEqualPos(match_const_deriv_multiplication(root),
  60. [P(root, const_deriv_multiplication, (Scope(root[0]), y, x))])
  61. def test_match_const_deriv_multiplication_multiple_constants(self):
  62. root = tree('d/dx 2x * 3')
  63. (l2, x), l3 = root[0]
  64. scope = Scope(root[0])
  65. self.assertEqualPos(match_const_deriv_multiplication(root),
  66. [P(root, const_deriv_multiplication, (scope, l2, x)),
  67. P(root, const_deriv_multiplication, (scope, l3, x))])
  68. def test_const_deriv_multiplication(self):
  69. root = tree('d/dx 2x')
  70. l2, x = root[0]
  71. args = Scope(root[0]), l2, x
  72. self.assertEqual(const_deriv_multiplication(root, args),
  73. l2 * der(x, x))
  74. self.assertRewrite(["[2x]'", "2[x]'", '2 * 1', '2'])
  75. def test_match_variable_power(self):
  76. root, x, l2 = tree('d/dx x ^ 2, x, 2')
  77. self.assertEqualPos(match_variable_power(root),
  78. [P(root, variable_root)])
  79. root = tree('d/dx 2 ^ x')
  80. self.assertEqualPos(match_variable_power(root),
  81. [P(root, variable_exponent)])
  82. def test_match_variable_power_chain_rule(self):
  83. root, x, l2, x3 = tree('d/dx (x ^ 3) ^ 2, x, 2, x ^ 3')
  84. self.assertEqualPos(match_variable_power(root),
  85. [P(root, chain_rule, (x3, variable_root, ()))])
  86. root = tree('d/dx 2 ^ x ^ 3')
  87. self.assertEqualPos(match_variable_power(root),
  88. [P(root, chain_rule, (x3, variable_exponent, ()))])
  89. # Below is not mathematically underivable, it's just not within the
  90. # scope of our program
  91. root, x = tree('d/dx x ^ x, x')
  92. self.assertEqualPos(match_variable_power(root),
  93. [P(root, power_rule)])
  94. def test_power_rule(self):
  95. root, expect = tree("[x ^ x]', [e ^ ln(x ^ x)]'")
  96. self.assertEqual(power_rule(root, ()), expect)
  97. def test_power_rule_chain(self):
  98. self.assertRewrite([
  99. "[x ^ x]'",
  100. "[e ^ (ln(x ^ x))]'",
  101. "e ^ (ln(x ^ x))[ln(x ^ x)]'",
  102. "x ^ x * [ln(x ^ x)]'",
  103. "x ^ x * [x ln(x)]'",
  104. "x ^ x * ([x]' * ln(x) + x[ln(x)]')",
  105. "x ^ x * (1ln(x) + x[ln(x)]')",
  106. "x ^ x * (ln(x) + x[ln(x)]')",
  107. "x ^ x * (ln(x) + x * 1 / x)",
  108. "x ^ x * (ln(x) + (x * 1) / x)",
  109. "x ^ x * (ln(x) + x / x)",
  110. "x ^ x * (ln(x) + 1)",
  111. "x ^ x * ln(x) + x ^ x * 1",
  112. "x ^ x * ln(x) + x ^ x",
  113. ])
  114. def test_variable_root(self):
  115. root = tree('d/dx x ^ 2')
  116. x, n = root[0]
  117. self.assertEqual(variable_root(root, ()), n * x ** (n - 1))
  118. def test_variable_root_with_negation(self):
  119. root = tree('d/dx -x ^ 2')
  120. x, n = root[0]
  121. self.assertEqual(variable_root(root, ()), -n * x ** (n - 1))
  122. def test_variable_exponent(self):
  123. root = tree('d/dx 2 ^ x')
  124. g, x = root[0]
  125. self.assertEqual(variable_exponent(root, ()), g ** x * ln(g))
  126. root = tree('d/dx e ^ x')
  127. e, x = root[0]
  128. self.assertEqual(variable_exponent(root, ()), e ** x)
  129. root = tree('d/dx -e ^ x')
  130. e, x = root[0]
  131. self.assertEqual(variable_exponent(root, ()), -e ** x)
  132. def test_chain_rule(self):
  133. root = tree('(2 ^ x ^ 3)\'')
  134. l2, x3 = root[0]
  135. x, l3 = x3
  136. self.assertEqual(chain_rule(root, (x3, variable_exponent, ())),
  137. l2 ** x3 * ln(l2) * der(x3))
  138. def test_match_logarithmic(self):
  139. root = tree('d/dx log(x)')
  140. self.assertEqualPos(match_logarithmic(root), [P(root, logarithmic)])
  141. def test_match_logarithmic_chain_rule(self):
  142. root, f = tree('d/dx log(x ^ 2), x ^ 2')
  143. self.assertEqualPos(match_logarithmic(root),
  144. [P(root, chain_rule, (f, logarithmic, ()))])
  145. def test_logarithmic(self):
  146. root, x, l1, l10 = tree('d/dx log(x), x, 1, 10')
  147. self.assertEqual(logarithmic(root, ()), l1 / (x * ln(l10)))
  148. root, x, l1, l10 = tree('d/dx ln(x), x, 1, 10')
  149. self.assertEqual(logarithmic(root, ()), l1 / x)
  150. def test_match_goniometric(self):
  151. root = tree('d/dx sin(x)')
  152. self.assertEqualPos(match_goniometric(root), [P(root, sinus)])
  153. root = tree('d/dx cos(x)')
  154. self.assertEqualPos(match_goniometric(root), [P(root, cosinus)])
  155. root = tree('d/dx tan(x)')
  156. self.assertEqualPos(match_goniometric(root), [P(root, tangens)])
  157. def test_match_goniometric_chain_rule(self):
  158. root, x2 = tree('d/dx sin(x ^ 2), x ^ 2')
  159. self.assertEqualPos(match_goniometric(root),
  160. [P(root, chain_rule, (x2, sinus, ()))])
  161. root = tree('d/dx cos(x ^ 2)')
  162. self.assertEqualPos(match_goniometric(root),
  163. [P(root, chain_rule, (x2, cosinus, ()))])
  164. def test_sinus(self):
  165. root, x = tree('d/dx sin(x), x')
  166. self.assertEqual(sinus(root, ()), cos(x))
  167. def test_cosinus(self):
  168. root, x = tree('d/dx cos(x), x')
  169. self.assertEqual(cosinus(root, ()), -sin(x))
  170. def test_tangens(self):
  171. root, x = tree('d/dx tan(x), x')
  172. self.assertEqual(tangens(root, ()), der(sin(x) / cos(x), x))
  173. root = tree('tan(x)\'')
  174. self.assertEqual(tangens(root, ()), der(sin(x) / cos(x)))
  175. def test_match_sum_product_rule_sum(self):
  176. root = tree('d/dx (x ^ 2 + x)')
  177. x2, x = f = root[0]
  178. self.assertEqualPos(match_sum_product_rule(root),
  179. [P(root, sum_rule, (Scope(f), x2)),
  180. P(root, sum_rule, (Scope(f), x))])
  181. root = tree('d/dx (x ^ 2 + 3 + x)')
  182. self.assertEqualPos(match_sum_product_rule(root),
  183. [P(root, sum_rule, (Scope(root[0]), x2)),
  184. P(root, sum_rule, (Scope(root[0]), x))])
  185. def test_match_sum_product_rule_product(self):
  186. root = tree('d/dx x ^ 2 * x')
  187. x2, x = f = root[0]
  188. self.assertEqualPos(match_sum_product_rule(root),
  189. [P(root, product_rule, (Scope(f), x2)),
  190. P(root, product_rule, (Scope(f), x))])
  191. def test_match_sum_product_rule_none(self):
  192. root = tree('d/dx (2 + 2)')
  193. self.assertEqualPos(match_sum_product_rule(root), [])
  194. root = tree('d/dx x ^ 2 * 2')
  195. self.assertEqualPos(match_sum_product_rule(root), [])
  196. def test_sum_rule(self):
  197. root = tree('(x ^ 2 + x)\'')
  198. x2, x = f = root[0]
  199. self.assertEqual(sum_rule(root, (Scope(f), x2)), der(x2) + der(x))
  200. self.assertEqual(sum_rule(root, (Scope(f), x)), der(x) + der(x2))
  201. root = tree('(x ^ 2 + 3 + x)\'')
  202. (x2, l3), x = f = root[0]
  203. self.assertEqual(sum_rule(root, (Scope(f), x2)), der(x2) + der(l3 + x))
  204. self.assertEqual(sum_rule(root, (Scope(f), x)), der(x) + der(x2 + l3))
  205. def test_product_rule(self):
  206. root = tree('(x ^ 2 * x)\'')
  207. x2, x = f = root[0]
  208. self.assertEqual(product_rule(root, (Scope(f), x2)),
  209. der(x2) * x + x2 * der(x))
  210. self.assertEqual(product_rule(root, (Scope(f), x)),
  211. der(x) * x2 + x * der(x2))
  212. root = tree('(x ^ 2 * x * x ^ 3)\'')
  213. (x2, x), x3 = f = root[0]
  214. self.assertEqual(product_rule(root, (Scope(f), x2)),
  215. der(x2) * (x * x3) + x2 * der(x * x3))
  216. self.assertEqual(product_rule(root, (Scope(f), x)),
  217. der(x) * (x2 * x3) + x * der(x2 * x3))
  218. self.assertEqual(product_rule(root, (Scope(f), x3)),
  219. der(x3) * (x2 * x) + x3 * der(x2 * x))
  220. def test_match_quotient_rule(self):
  221. root = tree('d/dx x ^ 2 / x')
  222. self.assertEqualPos(match_quotient_rule(root),
  223. [P(root, quotient_rule)])
  224. root = tree('d/dx x ^ 2 / 2')
  225. self.assertEqualPos(match_quotient_rule(root), [])
  226. def test_quotient_rule(self):
  227. root = tree('(x ^ 2 / x)\'')
  228. f, g = root[0]
  229. self.assertEqual(quotient_rule(root, ()),
  230. (der(f) * g - f * der(g)) / g ** 2)
  231. #def test_natural_pase_chain(self):
  232. # self.assertRewrite([
  233. # 'der(e ^ x)',
  234. # 'e ^ x * ln(e)',
  235. # 'e ^ x * 1',
  236. # 'e ^ x',
  237. # ])