derivatives.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. from .utils import find_variables, first_sorted_variable
  2. from ..node import ExpressionLeaf as L, Scope, OP_DER, OP_MUL, OP_LOG, \
  3. OP_SIN, OP_COS, OP_TAN, OP_ADD, OP_DIV, E, sin, cos, der, ln
  4. from ..possibilities import Possibility as P, MESSAGES
  5. from ..translate import _
  6. def second_arg(node):
  7. """
  8. Get the second child of a node if it exists, None otherwise.
  9. """
  10. return node[1] if len(node) > 1 else None
  11. def get_derivation_variable(node, variables=None):
  12. """
  13. Find the variable to derive over.
  14. >>> print get_derivation_variable(der(L('x')))
  15. 'x'
  16. """
  17. if len(node) > 1:
  18. assert node[1].is_identifier()
  19. return node[1].value
  20. if not variables:
  21. variables = find_variables(node)
  22. if not len(variables):
  23. return None
  24. return first_sorted_variable(variables)
  25. def chain_rule(root, args):
  26. """
  27. Apply the chain rule:
  28. [f(g(x)]' -> f'(g(x)) * g'(x)
  29. f'(g(x)) is not expressable in the current syntax, so calculate it directly
  30. using the application function in the arguments. g'(x) is simply expressed
  31. as der(g(x), x).
  32. """
  33. g, f_deriv, f_deriv_args = args
  34. x = root[1] if len(root) > 1 else None
  35. return f_deriv(root, f_deriv_args) * der(g, x)
  36. MESSAGES[chain_rule] = _('Apply the chain rule to {0}.')
  37. def match_zero_derivative(node):
  38. """
  39. der(x, y) -> 0
  40. der(n) -> 0
  41. """
  42. assert node.is_op(OP_DER)
  43. variables = find_variables(node[0])
  44. var = get_derivation_variable(node, variables)
  45. if not var or var not in variables:
  46. return [P(node, zero_derivative)]
  47. return []
  48. def zero_derivative(root, args):
  49. """
  50. der(x, y) -> 0
  51. der(n) -> 0
  52. """
  53. return L(0)
  54. MESSAGES[zero_derivative] = _('Constant {0[0]} has derivative 0.')
  55. def match_one_derivative(node):
  56. """
  57. der(x) -> 1 # Implicit x
  58. der(x, x) -> 1 # Explicit x
  59. """
  60. assert node.is_op(OP_DER)
  61. var = get_derivation_variable(node)
  62. if var and node[0] == L(var):
  63. return [P(node, one_derivative)]
  64. return []
  65. def one_derivative(root, args):
  66. """
  67. der(x) -> 1
  68. der(x, x) -> 1
  69. """
  70. return L(1)
  71. MESSAGES[one_derivative] = _('Variable {0[0]} has derivative 1.')
  72. def match_const_deriv_multiplication(node):
  73. """
  74. der(c * f(x), x) -> c * der(f(x), x)
  75. """
  76. assert node.is_op(OP_DER)
  77. p = []
  78. if node[0].is_op(OP_MUL):
  79. x = L(get_derivation_variable(node))
  80. scope = Scope(node[0])
  81. for n in scope:
  82. if not n.contains(x):
  83. p.append(P(node, const_deriv_multiplication, (scope, n, x)))
  84. return p
  85. def const_deriv_multiplication(root, args):
  86. """
  87. der(c * f(x), x) -> c * der(f(x), x)
  88. """
  89. scope, c, x = args
  90. scope.remove(c)
  91. return c * der(scope.as_nary_node(), x)
  92. MESSAGES[const_deriv_multiplication] = \
  93. _('Bring multiplication with {2} in derivative {0} to the outside.')
  94. def match_variable_power(node):
  95. """
  96. der(x ^ n) -> n * x ^ (n - 1)
  97. der(x ^ n, x) -> n * x ^ (n - 1)
  98. der(f(x) ^ n) -> n * f(x) ^ (n - 1) * der(f(x)) # Chain rule
  99. der(f(x) ^ g(x), x) -> der(e ^ ln(f(x) ^ g(x)), x)
  100. """
  101. assert node.is_op(OP_DER)
  102. if not node[0].is_power():
  103. return []
  104. root, exponent = node[0]
  105. rvars = find_variables(root)
  106. evars = find_variables(exponent)
  107. x = get_derivation_variable(node, rvars | evars)
  108. if x in rvars and x in evars:
  109. return [P(node, power_rule)]
  110. if x in rvars:
  111. if root.is_variable():
  112. return [P(node, variable_root)]
  113. return [P(node, chain_rule, (root, variable_root, ()))]
  114. if x in evars:
  115. if exponent.is_variable():
  116. return [P(node, variable_exponent)]
  117. return [P(node, chain_rule, (exponent, variable_exponent, ()))]
  118. return []
  119. def power_rule(root, args):
  120. """
  121. [f(x) ^ g(x)]' -> [e ^ ln(f(x) ^ g(x))]'
  122. """
  123. x = second_arg(root)
  124. return der(L(E) ** ln(root[0]), x)
  125. MESSAGES[power_rule] = \
  126. _('Write {0} as a logarithm to be able to separate root and exponent.')
  127. def variable_root(root, args):
  128. """
  129. der(x ^ n, x) -> n * x ^ (n - 1)
  130. """
  131. x, n = root[0]
  132. return n * x ** (n - 1)
  133. MESSAGES[variable_root] = \
  134. _('Apply standard derivative d/dx x ^ n = n * x ^ (n - 1) on {0}.')
  135. def variable_exponent(root, args):
  136. """
  137. der(g ^ x, x) -> g ^ x * ln(g)
  138. Shortcut rule (because of presence on formula list):
  139. der(e ^ x, x) -> e ^ x
  140. """
  141. g, x = root[0]
  142. if g == E:
  143. return g ** x
  144. return g ** x * ln(g)
  145. MESSAGES[variable_exponent] = \
  146. _('Apply standard derivative d/dx g ^ x = g ^ x * ln g.')
  147. def match_logarithmic(node):
  148. """
  149. der(log(x, g), x) -> 1 / (x * ln(g))
  150. der(log(f(x), g), x) -> 1 / (f(x) * ln(g)) * der(f(x), x)
  151. """
  152. assert node.is_op(OP_DER)
  153. x = get_derivation_variable(node)
  154. if x and node[0].is_op(OP_LOG):
  155. f = node[0][0]
  156. x = L(x)
  157. if f == x:
  158. return [P(node, logarithmic, ())]
  159. if f.contains(x):
  160. return [P(node, chain_rule, (f, logarithmic, ()))]
  161. return []
  162. def logarithmic(root, args):
  163. """
  164. der(log(x, g), x) -> 1 / (xln(g))
  165. Shortcut function (because of presence on formula list):
  166. der(ln(x), x) -> 1 / x
  167. """
  168. x, g = root[0]
  169. if g == E:
  170. return L(1) / x
  171. return L(1) / (x * ln(g))
  172. MESSAGES[logarithmic] = \
  173. _('Apply standard derivative d/dx log(x, g) = 1 / (x * ln(g)).')
  174. def match_goniometric(node):
  175. """
  176. der(sin(x), x) -> cos(x)
  177. der(sin(f(x)), x) -> cos(f(x)) * der(f(x), x)
  178. der(cos(x), x) -> -sin(x)
  179. der(cos(f(x)), x) -> -sin(f(x)) * der(f(x), x)
  180. der(tan(x), x) -> der(sin(x) / cos(x), x)
  181. """
  182. assert node.is_op(OP_DER)
  183. x = get_derivation_variable(node)
  184. if x and not node[0].is_leaf:
  185. op = node[0].op
  186. if op in (OP_SIN, OP_COS):
  187. f = node[0][0]
  188. x = L(x)
  189. handler = sinus if op == OP_SIN else cosinus
  190. if f == x:
  191. return [P(node, handler)]
  192. if f.contains(x):
  193. return [P(node, chain_rule, (f, handler, ()))]
  194. if op == OP_TAN:
  195. return [P(node, tangens)]
  196. return []
  197. def sinus(root, args):
  198. """
  199. der(sin(x), x) -> cos(x)
  200. """
  201. return cos(root[0][0])
  202. MESSAGES[sinus] = _('Apply standard derivative d/dx sin(x) = cos(x).')
  203. def cosinus(root, args):
  204. """
  205. der(cos(x), x) -> -sin(x)
  206. """
  207. return -sin(root[0][0])
  208. MESSAGES[cosinus] = _('Apply standard derivative d/dx cos(x) = -sin(x).')
  209. def tangens(root, args):
  210. """
  211. der(tan(x), x) -> der(sin(x) / cos(x), x)
  212. """
  213. x = root[0][0]
  214. return der(sin(x) / cos(x), second_arg(root))
  215. MESSAGES[tangens] = \
  216. _('Convert the tanges to a division and apply the product rule.')
  217. def match_sum_product_rule(node):
  218. """
  219. [f(x) + g(x)]' -> f'(x) + g'(x)
  220. [f(x) * g(x)]' -> f'(x) * g(x) + f(x) * g'(x)
  221. """
  222. assert node.is_op(OP_DER)
  223. x = get_derivation_variable(node)
  224. if not x or node[0].is_leaf or node[0].op not in (OP_ADD, OP_MUL):
  225. return []
  226. scope = Scope(node[0])
  227. x = L(x)
  228. functions = [n for n in scope if n.contains(x)]
  229. if node[0].op == OP_MUL:
  230. if len(functions) < 2:
  231. return []
  232. handler = product_rule
  233. else:
  234. handler = sum_rule
  235. return [P(node, handler, (scope, f)) for f in functions]
  236. def sum_rule(root, args):
  237. """
  238. [f(x) + g(x)]' -> f'(x) + g'(x)
  239. """
  240. scope, f = args
  241. x = second_arg(root)
  242. scope.remove(f)
  243. return der(f, x) + der(scope.as_nary_node(), x)
  244. MESSAGES[sum_rule] = _('Apply the sum rule to {0}.')
  245. def product_rule(root, args):
  246. """
  247. [f(x) * g(x)]' -> f'(x) * g(x) + f(x) * g'(x)
  248. Note that implicitely:
  249. [f(x) * g(x) * h(x)]' -> f'(x) * (g(x) * h(x)) + f(x) * [g(x) * h(x)]'
  250. """
  251. scope, f = args
  252. x = second_arg(root)
  253. scope.remove(f)
  254. gh = scope.as_nary_node()
  255. return der(f, x) * gh + f * der(gh, x)
  256. MESSAGES[product_rule] = _('Apply the product rule to {0}.')
  257. def match_quotient_rule(node):
  258. """
  259. [f(x) / g(x)]' -> (f'(x) * g(x) - f(x) * g'(x)) / g(x) ^ 2
  260. """
  261. assert node.is_op(OP_DER)
  262. x = get_derivation_variable(node)
  263. if not x or not node[0].is_op(OP_DIV):
  264. return []
  265. f, g = node[0]
  266. x = L(x)
  267. if f.contains(x) and g.contains(x):
  268. return [P(node, quotient_rule)]
  269. return []
  270. def quotient_rule(root, args):
  271. """
  272. [f(x) / g(x)]' -> (f'(x) * g(x) - f(x) * g'(x)) / g(x) ^ 2
  273. """
  274. f, g = root[0]
  275. x = second_arg(root)
  276. return (der(f, x) * g - f * der(g, x)) / g ** 2
  277. MESSAGES[quotient_rule] = _('Apply the quotient rule to {0}.')