integrals.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. from .utils import find_variables, infinity, replace_variable, find_variable, \
  2. absolute
  3. from .logarithmic import ln
  4. #from .goniometry import sin, cos
  5. from ..node import ExpressionNode as N, ExpressionLeaf as L, OP_INT, \
  6. OP_INT_INDEF, OP_MUL, OP_DIV, Scope
  7. from ..possibilities import Possibility as P, MESSAGES
  8. from ..translate import _
  9. def integral(*args):
  10. """
  11. Create an integral node.
  12. """
  13. return N(OP_INT, *args)
  14. def indef(*args):
  15. """
  16. Create an indefinite integral node.
  17. """
  18. return N(OP_INT_INDEF, *args)
  19. def choose_constant(integral):
  20. """
  21. Choose a constant to be added to the antiderivative.
  22. """
  23. # TODO: comments
  24. occupied = find_variables(integral)
  25. c = 'c'
  26. i = 96
  27. while c in occupied:
  28. i += 2 if i == 98 else 1
  29. c = chr(i)
  30. return L(c)
  31. def solve_integral(integral, F):
  32. """
  33. Solve an integral given its anti-derivative F:
  34. - First, finish the anti-derivative by adding a constant.
  35. - If no bounds are specified, return the anti-derivative.
  36. - Given a lower bound a and upper bound b, the solution is the indefinite
  37. integral [F(x)]_a^b. If F(x) contains multiple variables so that the 'x'
  38. is not identified by 'find_variable(F)' (which is used by the indefinite
  39. integral), skip the reduction of the indefinite integral and return the
  40. solution F(b) - F(a).
  41. """
  42. F += choose_constant(integral)
  43. if len(integral) < 3:
  44. return F
  45. x, lbnd, ubnd = integral[1:4]
  46. if x != find_variable(F):
  47. return replace_variable(F, x, ubnd) - replace_variable(F, x, lbnd)
  48. return indef(F, lbnd, ubnd)
  49. def match_solve_indef(node):
  50. """
  51. [F(x)]_a^b -> F(b) - F(a)
  52. """
  53. assert node.is_op(OP_INT_INDEF)
  54. return [P(node, solve_indef)]
  55. def solve_indef(root, args):
  56. """
  57. [F(x)]_a^b -> F(b) - F(a)
  58. """
  59. Fx, a, b = root
  60. x = find_variable(Fx)
  61. return replace_variable(Fx, x, b) - replace_variable(Fx, x, a)
  62. def match_integrate_variable_power(node):
  63. """
  64. int x ^ n dx -> x ^ (n + 1) / (n + 1)
  65. int g ^ x dx -> g ^ x / ln(g)
  66. """
  67. assert node.is_op(OP_INT)
  68. f, x = node[:2]
  69. if f.is_power():
  70. root, exponent = f
  71. if root == x and not exponent.contains(x):
  72. return [P(node, integrate_variable_root)]
  73. if exponent == x and not root.contains(x):
  74. return [P(node, integrate_variable_exponent)]
  75. return []
  76. def integrate_variable_root(root, args):
  77. """
  78. int x ^ n dx -> x ^ (n + 1) / (n + 1)
  79. """
  80. x, n = root[0]
  81. return solve_integral(root, x ** (n + 1) / (n + 1))
  82. MESSAGES[integrate_variable_root] = \
  83. _('Apply standard integral int(x ^ n) = x ^ (n + 1) / (n + 1) + c.')
  84. def integrate_variable_exponent(root, args):
  85. """
  86. int g ^ x dx -> g ^ x / ln(g)
  87. """
  88. g, x = root[0]
  89. return solve_integral(root, g ** x / ln(g))
  90. MESSAGES[integrate_variable_exponent] = \
  91. _('Apply standard integral int(g ^ x) = g ^ x / ln(g) + c.')
  92. def match_constant_integral(node):
  93. """
  94. int c dx -> cx
  95. """
  96. assert node.is_op(OP_INT)
  97. fx, x = node[:2]
  98. if not fx.contains(x):
  99. return [P(node, constant_integral)]
  100. return []
  101. def constant_integral(root, args):
  102. """
  103. int c dx -> cx
  104. """
  105. c, x = root[:2]
  106. return solve_integral(root, c * x)
  107. MESSAGES[constant_integral] = _('{0[0]} does not contain {0[1]}, so its ' \
  108. 'integral over {0[1]} is its multiplication with {0[1]}.')
  109. def match_factor_out_constant(node):
  110. """
  111. int cf(x) dx -> c int f(x) dx
  112. """
  113. assert node.is_op(OP_INT)
  114. fx, x = node[:2]
  115. if not fx.is_op(OP_MUL):
  116. return []
  117. p = []
  118. scope = Scope(fx)
  119. for n in scope:
  120. if not n.contains(x):
  121. p.append(P(node, factor_out_constant, (scope, n)))
  122. return p
  123. def factor_out_constant(root, args):
  124. """
  125. int cf(x) dx -> c int f(x) dx
  126. """
  127. scope, c = args
  128. scope.remove(c)
  129. return c * integral(scope.as_nary_node(), *root[1:])
  130. MESSAGES[factor_out_constant] = _('Factor out {2} from integral {0}.')
  131. def match_division_integral(node):
  132. """
  133. int 1 / x dx -> ln|x|
  134. int a / x dx -> int a(1 / x) dx # -> a int 1 / x dx -> aln|x|
  135. """
  136. assert node.is_op(OP_INT)
  137. fx, x = node[:2]
  138. if fx.is_op(OP_DIV) and fx[1] == x:
  139. if fx[0] == 1:
  140. return [P(node, division_integral)]
  141. return [P(node, extend_division_integral)]
  142. return []
  143. def division_integral(root, args):
  144. """
  145. int 1 / x dx -> ln|x|
  146. """
  147. return solve_integral(root, ln(absolute(root[0][1])))
  148. MESSAGES[division_integral] = \
  149. _('1 / {0[1]} has the standard ant-derivative ln|{0[1]}|.')
  150. def extend_division_integral(root, args):
  151. """
  152. int a / x dx -> int a(1 / x) dx # -> a int 1 / x dx -> aln|x|
  153. """
  154. a, x = root[0]
  155. return integral(a * (L(1) / x), *root[1:])
  156. MESSAGES[extend_division_integral] = _('Bring nominator {0[0][0]} out of the' \
  157. ' fraction to obtain a standard 1 / {0[0][1]} integral.')