integrals.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. from .utils import find_variables, first_sorted_variable, infinity, \
  2. replace_variable
  3. from .logarithmic import ln
  4. #from .goniometry import sin, cos
  5. from ..node import ExpressionNode as N, ExpressionLeaf as L, OP_INT
  6. from ..possibilities import Possibility as P, MESSAGES
  7. from ..translate import _
  8. def integral(f, *args):
  9. """
  10. Create an integral node.
  11. """
  12. return N(OP_INT, *((f,) + args))
  13. #def integral_params(integral):
  14. # """
  15. # Get integral parameters:
  16. # - If f(x) and x are both specified, return them.
  17. # - If only f(x) is specified, find x.
  18. # """
  19. # if len(integral) > 1:
  20. # assert integral[1].is_identifier()
  21. # return tuple(integral[:2])
  22. #
  23. # f = integral[0]
  24. # variables = find_variables(integral)
  25. #
  26. # if not len(variables):
  27. # return f, None
  28. #
  29. # return f, L(first_sorted_variable(variables))
  30. def choose_constant(integral):
  31. """
  32. Choose a constant to be added to the antiderivative.
  33. """
  34. # TODO: comments
  35. occupied = find_variables(integral)
  36. c = 'c'
  37. i = 96
  38. while c in occupied:
  39. i += 2 if i == 98 else 1
  40. c = chr(i)
  41. return L(c)
  42. def solve_integral(integral, F):
  43. """
  44. Solve an integral given its anti-derivative F:
  45. - First, finish the anti-derivative by adding a constant.
  46. - If no bounds are specified, return the anti-derivative.
  47. - If only a lower bound is specified, set the upper bound to infinity.
  48. - Given a lower bound a and upper bound b, the solution is F(b) - F(a).
  49. """
  50. F += choose_constant(integral)
  51. if len(integral) < 3:
  52. return F
  53. x = integral[1]
  54. lower = integral[2]
  55. upper = infinity() if len(integral) < 4 else integral[3]
  56. # TODO: add notation [F(x)]_a^b
  57. return replace_variable(F, x, lower) - replace_variable(F, x, upper)
  58. def match_integrate_variable_power(node):
  59. """
  60. int(x ^ n, x) -> x ^ (n + 1) / (n + 1) + c
  61. int(g ^ x, x) -> g ^ x / ln(g)
  62. """
  63. assert node.is_op(OP_INT)
  64. f, x = node
  65. if f.is_power():
  66. root, exponent = f
  67. if root == x and not exponent.contains(x):
  68. return [P(node, integrate_variable_root)]
  69. if exponent == x and not root.contains(x):
  70. return [P(node, integrate_variable_exponent)]
  71. return []
  72. def integrate_variable_root(root, args):
  73. """
  74. int(x ^ n, x) -> x ^ (n + 1) / (n + 1) + c
  75. """
  76. x, n = root[0]
  77. return solve_integral(root, x ** (n + 1) / (n + 1))
  78. MESSAGES[integrate_variable_root] = \
  79. _('Apply standard integral int(x ^ n) = x ^ (n + 1) / (n + 1) + c.')
  80. def integrate_variable_exponent(root, args):
  81. """
  82. int(g ^ x, x) -> g ^ x / ln(g)
  83. """
  84. g, x = root[0]
  85. return solve_integral(root, g ** x / ln(g))
  86. MESSAGES[integrate_variable_exponent] = \
  87. _('Apply standard integral int(g ^ x) = g ^ x / ln(g) + c.')