numerics.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. from itertools import combinations
  2. from ..node import ExpressionLeaf as Leaf, Scope, negate, OP_DIV, OP_MUL
  3. from ..possibilities import Possibility as P, MESSAGES
  4. from ..translate import _
  5. def add_numerics(root, args):
  6. """
  7. Combine two constants to a single constant in an n-ary addition.
  8. Example:
  9. 2 + 3 -> 5
  10. 2 + -3 -> -1
  11. -2 + 3 -> 1
  12. -2 + -3 -> -5
  13. """
  14. n0, n1, c0, c1 = args
  15. scope = Scope(root)
  16. # Replace the left node with the new expression
  17. scope.remove(n0, Leaf(c0.actual_value() + c1.actual_value()))
  18. # Remove the right node
  19. scope.remove(n1)
  20. return scope.as_nary_node()
  21. MESSAGES[add_numerics] = _('Combine the constants {1} and {2}.')
  22. #def match_subtract_numerics(node):
  23. # """
  24. # 3 - 2 -> 2.0
  25. # 3.0 - 2 -> 1.0
  26. # 3 - 2.0 -> 1.0
  27. # 3.0 - 2.0 -> 1.0
  28. # """
  29. # # TODO: This should be handled by match_combine_polynomes
  30. # assert node.is_op(OP_MUL)
  31. def match_divide_numerics(node):
  32. """
  33. Combine two constants to a single constant in a division, if it does not
  34. lead to a decrease in precision.
  35. Example:
  36. 6 / 2 -> 3
  37. 3 / 2 -> 3 / 2 # 1.5 would mean a decrease in precision
  38. 3.0 / 2 -> 1.5
  39. 3 / 2.0 -> 1.5
  40. 3.0 / 2.0 -> 1.5
  41. 3 / 1.0 -> 3 # Exceptional case: division of integer by 1.0 keeps
  42. # integer precision
  43. """
  44. assert node.is_op(OP_DIV)
  45. n, d = node
  46. divide = False
  47. dv = d.value
  48. if n.is_int() and d.is_int():
  49. # 6 / 2 -> 3
  50. # 3 / 2 -> 3 / 2
  51. divide = not divmod(n.value, dv)[1]
  52. elif n.is_numeric() and d.is_numeric():
  53. if d == 1.0:
  54. # 3 / 1.0 -> 3
  55. dv = 1
  56. # 3.0 / 2 -> 1.5
  57. # 3 / 2.0 -> 1.5
  58. # 3.0 / 2.0 -> 1.5
  59. divide = True
  60. return [P(node, divide_numerics, (n.value, dv))] if divide else []
  61. def divide_numerics(root, args):
  62. """
  63. Combine two constants to a single constant in a division.
  64. Examples:
  65. 6 / 2 -> 3
  66. 3.0 / 2 -> 1.5
  67. 3 / 2.0 -> 1.5
  68. 3.0 / 2.0 -> 1.5
  69. 3 / 1.0 -> 3
  70. """
  71. n, d = args
  72. return Leaf(n / d)
  73. MESSAGES[divide_numerics] = _('Divide constant {1} by constant {2}.')
  74. def match_multiply_zero(node):
  75. """
  76. a * 0 -> 0
  77. 0 * a -> 0
  78. -0 * a -> -0
  79. 0 * -a -> -0
  80. -0 * -a -> 0
  81. """
  82. assert node.is_op(OP_MUL)
  83. left, right = node
  84. if (left.is_leaf and left.value == 0) \
  85. or (right.is_leaf and right.value == 0):
  86. return [P(node, multiply_zero, (left.negated + right.negated,))]
  87. return []
  88. def multiply_zero(root, args):
  89. """
  90. a * 0 -> 0
  91. 0 * a -> 0
  92. -0 * a -> -0
  93. 0 * -a -> -0
  94. -0 * -a -> 0
  95. """
  96. return negate(Leaf(0), args[0])
  97. MESSAGES[multiply_zero] = _('Multiplication with zero yields zero.')
  98. def match_multiply_numerics(node):
  99. """
  100. 3 * 2 -> 6
  101. 3.0 * 2 -> 6.0
  102. 3 * 2.0 -> 6.0
  103. 3.0 * 2.0 -> 6.0
  104. """
  105. assert node.is_op(OP_MUL)
  106. p = []
  107. numerics = []
  108. for n in Scope(node):
  109. if n.is_numeric():
  110. numerics.append((n, n.actual_value()))
  111. for (n0, v0), (n1, v1) in combinations(numerics, 2):
  112. p.append(P(node, multiply_numerics, (n0, n1, v0, v1)))
  113. return p
  114. def multiply_numerics(root, args):
  115. """
  116. Combine two constants to a single constant in an n-ary multiplication.
  117. Example:
  118. 2 * 3 -> 6
  119. """
  120. n0, n1, v0, v1 = args
  121. scope = []
  122. value = v0 * v1
  123. if value > 0:
  124. substitution = Leaf(value)
  125. else:
  126. substitution = -Leaf(-value)
  127. scope = Scope(root)
  128. # Replace the left node with the new expression
  129. scope.remove(n0, substitution)
  130. # Remove the right node
  131. scope.remove(n1)
  132. return scope.as_nary_node()
  133. MESSAGES[multiply_numerics] = _('Multiply constant {1} with {2}.')