Commit ea8ce3ac authored by Taddeus Kroes's avatar Taddeus Kroes

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

parent 7d0df6c9
......@@ -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
......
......@@ -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
......
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],
}
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}.')
......@@ -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')
......
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)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment