Bläddra i källkod

Added a set of reduction rules for expressions with absolute values.

Taddeus Kroes 14 år sedan
förälder
incheckning
ea8ce3ac20
6 ändrade filer med 197 tillägg och 1 borttagningar
  1. 7 0
      src/node.py
  2. 1 0
      src/parser.py
  3. 3 1
      src/rules/__init__.py
  4. 97 0
      src/rules/absolute.py
  5. 1 0
      tests/test_parser.py
  6. 88 0
      tests/test_rules_absolute.py

+ 7 - 0
src/node.py

@@ -700,6 +700,13 @@ def eq(left, right):
     return ExpressionNode(OP_EQ, left, right)
 
 
+def sqrt(exp):
+    """
+    Create a square root node.
+    """
+    return ExpressionNode(OP_SQRT, exp)
+
+
 def negation_to_node(node):
     """
     Recursively replace negation flags inside a node by explicit unary negation

+ 1 - 0
src/parser.py

@@ -189,6 +189,7 @@ class Parser(BisonParser):
                 + '|(\))\s*([\x00-\x09\x0b-\x19a-z0-9])'      # )a  -> ) * a
                 + '|([\x00-\x09\x0b-\x19a-z])\s*'
                   + '([\x00-\x09\x0b-\x19a-z])'               # ab  -> a * b
+                + '|(\|)(\|)'                                 # ||  -> | * |
                 + '|([0-9])\s*([\x00-\x09\x0b-\x19a-z])'      # 4a  -> 4 * a
                 + '|([\x00-\x09\x0b-\x19a-z])([0-9])'         # a4  -> a ^ 4
                 + '|([\x00-\x09\x0b-\x19a-z0-9])(\s+[0-9]))'  # a 4 -> a * 4

+ 3 - 1
src/rules/__init__.py

@@ -1,5 +1,5 @@
 from ..node import OP_ADD, OP_MUL, OP_DIV, OP_POW, OP_NEG, OP_SIN, OP_COS, \
-        OP_TAN, OP_DER, OP_LOG, OP_INT, OP_INT_INDEF, OP_EQ
+        OP_TAN, OP_DER, OP_LOG, OP_INT, OP_INT_INDEF, OP_EQ, OP_ABS
 from .groups import match_combine_groups
 from .factors import match_expand
 from .powers import match_add_exponents, match_subtract_exponents, \
@@ -28,6 +28,7 @@ from src.rules.integrals import match_solve_indef, match_constant_integral, \
         match_integrate_variable_power, match_factor_out_constant, \
         match_division_integral, match_function_integral
 from src.rules.lineq import match_move_term
+from src.rules.absolute import match_factor_out_abs_term
 
 RULES = {
         OP_ADD: [match_add_numerics, match_add_fractions,
@@ -61,4 +62,5 @@ RULES = {
                  match_function_integral],
         OP_INT_INDEF: [match_solve_indef],
         OP_EQ: [match_move_term],
+        OP_ABS: [match_factor_out_abs_term],
         }

+ 97 - 0
src/rules/absolute.py

@@ -0,0 +1,97 @@
+from .utils import evals_to_numeric
+from ..node import OP_ABS, OP_SQRT, OP_MUL, OP_POW, Scope, absolute, sqrt
+from ..possibilities import Possibility as P, MESSAGES
+from ..translate import _
+
+
+def match_factor_out_abs_term(node):
+    """
+    |-a|            ->  |a|
+    |c| and c in Z  ->  eval(|c|)
+    |sqrt a|        ->  sqrt |a|
+    |ab|            ->  |a||b|
+    |abc|           ->  |a||bc|
+    |abc|           ->  |c||ab|
+    |abc|           ->  |b||ac|
+    |a ^ c| and eval(c) in Z  ->  |a| ^ c
+    """
+    assert node.is_op(OP_ABS)
+
+    exp = node[0]
+
+    if exp.negated:
+        return [P(node, remove_absolute_negation)]
+
+    if exp.is_numeric():
+        return [P(node, absolute_numeric)]
+
+    if exp.is_op(OP_MUL):
+        scope = Scope(exp)
+
+        return [P(node, factor_out_abs_term, (scope, n)) for n in scope]
+
+    if exp.is_op(OP_SQRT):
+        return [P(node, factor_out_abs_sqrt)]
+
+    if exp.is_op(OP_POW) and evals_to_numeric(exp[1]):
+        return [P(node, factor_out_abs_exponent)]
+
+    return []
+
+
+def remove_absolute_negation(root, args):
+    """
+    |-a|  ->  |a|
+    """
+    return absolute(+root[0]).negate(root.negated)
+
+
+MESSAGES[remove_absolute_negation] =  \
+        _('The absolute value of a negated expression is the expression.')
+
+
+def absolute_numeric(root, args):
+    """
+    |c| and c in Z  ->  eval(|c|)
+    """
+    return root[0].negate(root.negated)
+
+
+MESSAGES[absolute_numeric] = _('The absolute value of {0[0]} is {0[0]}.')
+
+
+def factor_out_abs_term(root, args):
+    """
+    |ab|  ->  |a||b|
+    """
+    scope, a = args
+    scope.remove(a)
+
+    return (absolute(a) * absolute(scope.as_nary_node())).negate(root.negated)
+
+
+MESSAGES[factor_out_abs_term] = _('Split the multplication in absolute ' \
+        'value {0} into a multiplication of absolute values.')
+
+
+def factor_out_abs_sqrt(root, args):
+    """
+    |sqrt a|  ->  sqrt|a|
+    """
+    return sqrt(absolute(root[0][0])).negate(root.negated)
+
+
+MESSAGES[factor_out_abs_sqrt] = \
+        _('Move the absolute value in {0} to the operand of the square root.')
+
+
+def factor_out_abs_exponent(root, args):
+    """
+    |a ^ c| and eval(c) in Z  ->  |a| ^ c
+    """
+    a, c = root[0]
+
+    return (absolute(a) ** c).negate(root.negated)
+
+
+MESSAGES[factor_out_abs_exponent] = _('Factor out the exponent in {0}.')

+ 1 - 0
tests/test_parser.py

@@ -62,6 +62,7 @@ class TestParser(unittest.TestCase):
         # FIXME: self.assertEqual(tree('|a|(b)'), tree('|a| * (b)'))
         # FIXME: self.assertEqual(tree('a|b|'), tree('a * |b|'))
         # FIXME: self.assertEqual(tree('|a|b'), tree('|a| * b'))
+        self.assertEqual(tree('|a||b|'), tree('|a| * |b|'))
 
     def test_functions(self):
         x = tree('x')

+ 88 - 0
tests/test_rules_absolute.py

@@ -0,0 +1,88 @@
+from src.rules.absolute import match_factor_out_abs_term, \
+        remove_absolute_negation, factor_out_abs_sqrt, absolute_numeric, \
+        factor_out_abs_term, factor_out_abs_exponent
+from src.node import ExpressionLeaf as L, Scope
+from src.possibilities import Possibility as P
+from tests.rulestestcase import RulesTestCase, tree
+
+
+class TestRulesAbsolute(RulesTestCase):
+
+    def test_match_factor_out_abs_term_negation(self):
+        root = tree('|-a|')
+        self.assertEqualPos(match_factor_out_abs_term(root),
+                [P(root, remove_absolute_negation)])
+
+    def test_match_factor_out_abs_term_numeric(self):
+        root = tree('|2|')
+        self.assertEqualPos(match_factor_out_abs_term(root),
+                [P(root, absolute_numeric)])
+
+        root = tree('|a|')
+        self.assertEqualPos(match_factor_out_abs_term(root), [])
+
+    def test_match_factor_out_abs_term_mult(self):
+        ((a, b),) = (ab,) = root = tree('|ab|')
+        self.assertEqualPos(match_factor_out_abs_term(root),
+                [P(root, factor_out_abs_term, (Scope(ab), a)),
+                 P(root, factor_out_abs_term, (Scope(ab), b))])
+
+        (((a, b), c),) = (abc,) = root = tree('|abc|')
+        self.assertEqualPos(match_factor_out_abs_term(root),
+                [P(root, factor_out_abs_term, (Scope(abc), a)),
+                 P(root, factor_out_abs_term, (Scope(abc), b)),
+                 P(root, factor_out_abs_term, (Scope(abc), c))])
+
+    def test_match_factor_out_abs_term_sqrt(self):
+        root = tree('|sqrt a|')
+        self.assertEqualPos(match_factor_out_abs_term(root),
+                [P(root, factor_out_abs_sqrt)])
+
+    def test_match_factor_out_abs_term_exponent(self):
+        root = tree('|a ^ 2|')
+        self.assertEqualPos(match_factor_out_abs_term(root),
+                [P(root, factor_out_abs_exponent)])
+
+        root = tree('|a ^ b|')
+        self.assertEqualPos(match_factor_out_abs_term(root), [])
+
+    def test_remove_absolute_negation(self):
+        root, expect = tree('|-a|, |a|')
+        self.assertEqual(remove_absolute_negation(root, ()), expect)
+
+        root, expect = tree('-|-a|, -|a|')
+        self.assertEqual(remove_absolute_negation(root, ()), expect)
+
+    def test_absolute_numeric(self):
+        root, expect = tree('|2|, 2')
+        self.assertEqual(absolute_numeric(root, ()), expect)
+
+        root, expect = tree('-|2|, -2')
+        self.assertEqual(absolute_numeric(root, ()), expect)
+
+    def test_factor_out_abs_term(self):
+        root, expect = tree('|abc|, |a||bc|')
+        (((a, b), c),) = (abc,) = root
+        self.assertEqual(factor_out_abs_term(root, (Scope(abc), a)), expect)
+
+        root, expect = tree('|abc|, |b||ac|')
+        (((a, b), c),) = (abc,) = root
+        self.assertEqual(factor_out_abs_term(root, (Scope(abc), b)), expect)
+
+        root, expect = tree('-|abc|, -|a||bc|')
+        (((a, b), c),) = (abc,) = root
+        self.assertEqual(factor_out_abs_term(root, (Scope(abc), a)), expect)
+
+    def test_factor_out_abs_sqrt(self):
+        root, expect = tree('|sqrt a|, sqrt|a|')
+        self.assertEqual(factor_out_abs_sqrt(root, ()), expect)
+
+        root, expect = tree('-|sqrt a|, -sqrt|a|')
+        self.assertEqual(factor_out_abs_sqrt(root, ()), expect)
+
+    def test_factor_out_abs_exponent(self):
+        root, expect = tree('|a ^ 2|, |a| ^ 2')
+        self.assertEqual(factor_out_abs_exponent(root, ()), expect)
+
+        root, expect = tree('-|a ^ 2|, -|a| ^ 2')
+        self.assertEqual(factor_out_abs_exponent(root, ()), expect)