powers.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. from itertools import combinations
  2. from ..node import ExpressionNode as N, ExpressionLeaf as L, \
  3. OP_NEG, OP_MUL, OP_DIV, OP_POW
  4. from ..possibilities import Possibility as P
  5. from .utils import nary_node
  6. def match_add_exponents(node):
  7. """
  8. a^p * a^q -> a^(p + q)
  9. """
  10. assert node.is_op(OP_MUL)
  11. p = []
  12. powers = {}
  13. for n in node.get_scope():
  14. if n.is_op(OP_POW):
  15. # Order powers by their roots, e.g. a^p and a^q are put in the same
  16. # list because of the mutual 'a'
  17. s = str(n[0])
  18. if s in powers:
  19. powers[s].append(n)
  20. else:
  21. powers[s] = [n]
  22. for root, occurrences in powers.iteritems():
  23. # If a root has multiple occurences, their exponents can be added to
  24. # create a single power with that root
  25. if len(occurrences) > 1:
  26. for pair in combinations(occurrences, 2):
  27. p.append(P(node, add_exponents, pair))
  28. return p
  29. def match_subtract_exponents(node):
  30. """
  31. a^p / a^q -> a^(p - q)
  32. a^p / a -> a^(p - 1)
  33. a / a^q -> a^(1 - q)
  34. """
  35. assert node.is_op(OP_DIV)
  36. left, right = node
  37. left_pow, right_pow = left.is_op(OP_POW), right.is_op(OP_POW)
  38. if left_pow and right_pow and left[0] == right[0]:
  39. # A power is divided by a power with the same root
  40. return [P(node, subtract_exponents, tuple(left) + (right[1],))]
  41. if left_pow and left[0] == right:
  42. # A power is divided by a its root
  43. return [P(node, subtract_exponents, tuple(left) + (1,))]
  44. if right_pow and left == right[0]:
  45. # An identifier is divided by a power of itself
  46. return [P(node, subtract_exponents, (left, 1, right[1]))]
  47. return []
  48. def match_multiply_exponents(node):
  49. """
  50. (a^p)^q -> a^(pq)
  51. """
  52. assert node.is_op(OP_POW)
  53. left, right = node
  54. if left.is_op(OP_POW):
  55. return [P(node, multiply_exponents, tuple(left) + (right,))]
  56. return []
  57. def match_duplicate_exponent(node):
  58. """
  59. (ab)^p -> a^p * b^p
  60. """
  61. assert node.is_op(OP_POW)
  62. left, right = node
  63. if left.is_op(OP_MUL):
  64. return [P(node, duplicate_exponent, tuple(left) + (right,))]
  65. return []
  66. def match_remove_negative_exponent(node):
  67. """
  68. a^-p -> 1 / a^p
  69. """
  70. assert node.is_op(OP_POW)
  71. left, right = node
  72. if right.is_op(OP_NEG):
  73. return [P(node, remove_negative_exponent, (left, right[0]))]
  74. return []
  75. def match_exponent_to_root(node):
  76. """
  77. a^(1 / m) -> sqrt(a, m)
  78. a^(n / m) -> sqrt(a^n, m)
  79. """
  80. assert node.is_op(OP_POW)
  81. left, right = node
  82. if right.is_op(OP_DIV):
  83. return [P(node, exponent_to_root, (left,) + tuple(right))]
  84. return []
  85. def add_exponents(root, args):
  86. """
  87. a^p * a^q -> a^(p + q)
  88. """
  89. n0, n1 = args
  90. a, p = n0
  91. q = n1[1]
  92. scope = root.get_scope()
  93. # Replace the left node with the new expression
  94. scope[scope.index(n0)] = a ** (p + q)
  95. # Remove the right node
  96. scope.remove(n1)
  97. return nary_node('*', scope)
  98. def subtract_exponents(root, args):
  99. """
  100. a^p / a^q -> a^(p - q)
  101. """
  102. a, p, q = args
  103. return a ** (p - q)
  104. def multiply_exponents(root, args):
  105. """
  106. (a^p)^q -> a^(pq)
  107. """
  108. a, p, q = args
  109. return a ** (p * q)
  110. def duplicate_exponent(root, args):
  111. """
  112. (ab)^p -> a^p * b^p
  113. """
  114. a, b, p = args
  115. return a ** p * b ** p
  116. def remove_negative_exponent(root, args):
  117. """
  118. a^-p -> 1 / a^p
  119. """
  120. a, p = args
  121. return L(1) / a ** p
  122. def exponent_to_root(root, args):
  123. """
  124. a^(1 / m) -> sqrt(a, m)
  125. a^(n / m) -> sqrt(a^n, m)
  126. """
  127. a, n, m = args
  128. return N('sqrt', a if n == 1 else a ** n, m)