Skip to content
Snippets Groups Projects
Commit 712dbdbc authored by Taddeus Kroes's avatar Taddeus Kroes
Browse files

Added rules (and tests) for sorting polynomes and monomials.

parent 464b95ee
No related branches found
No related tags found
No related merge requests found
...@@ -14,7 +14,7 @@ from .fractions import match_constant_division, match_add_fractions, \ ...@@ -14,7 +14,7 @@ from .fractions import match_constant_division, match_add_fractions, \
match_extract_fraction_terms, match_division_in_denominator match_extract_fraction_terms, match_division_in_denominator
from .negation import match_negated_factor, match_negate_polynome, \ from .negation import match_negated_factor, match_negate_polynome, \
match_negated_division match_negated_division
from .sort import match_sort_polynome, match_sort_molinome from .sort import match_sort_polynome, match_sort_monomial
from .goniometry import match_add_quadrants, match_negated_parameter, \ from .goniometry import match_add_quadrants, match_negated_parameter, \
match_half_pi_subtraction, match_standard_radian match_half_pi_subtraction, match_standard_radian
from .derivatives import match_zero_derivative, \ from .derivatives import match_zero_derivative, \
...@@ -36,11 +36,11 @@ from .sqrt import match_reduce_sqrt ...@@ -36,11 +36,11 @@ from .sqrt import match_reduce_sqrt
RULES = { RULES = {
OP_ADD: [match_add_numerics, match_add_fractions, OP_ADD: [match_add_numerics, match_add_fractions,
match_combine_groups, match_add_quadrants, match_combine_groups, match_add_quadrants,
match_add_logarithms], match_add_logarithms, match_sort_polynome],
OP_MUL: [match_multiply_numerics, match_expand, match_add_exponents, OP_MUL: [match_multiply_numerics, match_expand, match_add_exponents,
match_multiply_zero, match_negated_factor, match_multiply_zero, match_negated_factor,
match_multiply_fractions, match_factor_in_multiplicant, match_multiply_fractions, match_factor_in_multiplicant,
match_sort_molinome], match_sort_monomial],
OP_DIV: [match_subtract_exponents, match_divide_numerics, OP_DIV: [match_subtract_exponents, match_divide_numerics,
match_constant_division, match_divide_fractions, match_constant_division, match_divide_fractions,
match_negated_division, match_extract_fraction_terms, match_negated_division, match_extract_fraction_terms,
......
...@@ -98,4 +98,5 @@ IMPLICIT_RULES = [ ...@@ -98,4 +98,5 @@ IMPLICIT_RULES = [
remove_power_of_one, remove_power_of_one,
negated_factor, negated_factor,
add_numerics, add_numerics,
swap_factors,
] ]
from .utils import iter_pairs from .utils import iter_pairs, evals_to_numeric
from ..node import ExpressionNode as N, Scope, OP_ADD, OP_MUL from ..node import ExpressionNode as N, Scope, OP_ADD, OP_MUL
from ..possibilities import Possibility as P, MESSAGES from ..possibilities import Possibility as P, MESSAGES
from ..translate import _ from ..translate import _
def swap_moni((left, right)): def get_power_prop(node):
left_num, right_num = left.is_numeric(), right.is_numeric() """
Get the power properties of a node needed for sorting, being the variable
name and exponent value.
"""
if node.is_variable():
return node.value, 1
if left_num and right_num: if node.is_power():
return False root, exponent = node
if root.is_variable():
if exponent.is_numeric():
# Numeric exponent: value can be compared to other exponents
exp = exponent.actual_value()
else:
# No numeric exponent: same precedence as the variable itself
exp = 1
return root.value, exp
def swap_mono((left, right)):
"""
Check if a pair of left and right multiplication factors in a monomial
should be swapped to be correctly sorted.
"""
# Numerics should always be on the left of a monomial
if evals_to_numeric(right):
return not evals_to_numeric(left)
left_prop = get_power_prop(left)
right_prop = get_power_prop(right)
if left_prop and right_prop:
left_var, left_exp = left_prop
right_var, right_exp = right_prop
if left_var == right_var:
# Same variable, compare exponents
return left_exp > right_exp
# Compare variable names alphabetically
return left_var > right_var
return False
def get_poly_prop(node):
"""
Get the polynome properties of an monomial in a polynome, being the leading
variable name and the exponent it is raised to.
"""
if node.is_op(OP_MUL):
scope = Scope(node)
power = None
left_var, right_var = left.is_variable(), right.is_variable() for n in scope:
new_power = get_power_prop(n)
if left_var and right_var: if new_power:
return cmp(left.value, right.value) if not power:
var, exp = power = new_power
continue
if left_var and right_num: new_var, new_exp = new_power
return True
if (new_exp > exp if new_var == var else new_var < var):
var, exp = power = new_power
return power
return get_power_prop(node)
def swap_poly((left, right)): def swap_poly((left, right)):
pass """
Check if a pair of left and right addition factors in a polynomial should
be swapped to be correctly sorted.
"""
left_poly = get_poly_prop(left)
right_poly = get_poly_prop(right)
if not left_poly:
return bool(right_poly)
elif not right_poly:
return False
def match_sort_polynome(node): left_var, left_exp = left_poly
right_var, right_exp = right_poly
if left_var == right_var:
# Same variable, compare exponents
return left_exp < right_exp
# Compare variable names alphabetically
return left_var > right_var
def match_sort_monomial(node):
""" """
Sort a monomial, pursuing the following form:
x^0 * x^1 * ... * x^n
For example: 2xx^2
""" """
assert node.is_op(OP_ADD) assert node.is_op(OP_MUL)
scope = Scope(node) scope = Scope(node)
return [P(node, swap_factors, (scope, l, r)) return [P(node, swap_factors, (scope, l, r))
for l, r in filter(swap_poly, iter_pairs(scope))] for l, r in filter(swap_mono, iter_pairs(scope))]
def match_sort_molinome(node): def match_sort_polynome(node):
""" """
x * 2 -> 2x # variable > number Sort a polynome, pursuing the following form:
x ^ 2 * x ^ 3 -> x ^ 3 * x ^ 2 # exponents c_n * x^n * ... * c_1 * x^1 * c_0 * x^0
yx -> xy # alphabetically For example: 2x^2 + x - 3
""" """
assert node.is_op(OP_MUL) assert node.is_op(OP_ADD)
scope = Scope(node) scope = Scope(node)
return [P(node, swap_factors, (scope, l, r)) return [P(node, swap_factors, (scope, l, r))
for l, r in filter(swap_moni, iter_pairs(scope))] for l, r in filter(swap_poly, iter_pairs(scope))]
def swap_factors(root, args): def swap_factors(root, args):
""" """
left * right f * g -> g * f
""" """
scope, left, right = args scope, left, right = args
...@@ -60,4 +143,4 @@ def swap_factors(root, args): ...@@ -60,4 +143,4 @@ def swap_factors(root, args):
return scope.as_nary_node() return scope.as_nary_node()
MESSAGES[swap_factors] = _('Move {2} to the left of {1}.') MESSAGES[swap_factors] = _('Move {3} to the left of {2}.')
from src.rules.sort import match_sort_polynome, swap_factors, \ from src.rules.sort import match_sort_polynome, swap_factors, get_poly_prop, \
match_sort_molinome, iter_pairs match_sort_monomial, iter_pairs, swap_mono, swap_poly, get_power_prop
from src.node import Scope from src.node import Scope
from src.possibilities import Possibility as P from src.possibilities import Possibility as P
from tests.rulestestcase import RulesTestCase, tree from tests.rulestestcase import RulesTestCase, tree
...@@ -7,21 +7,74 @@ from tests.rulestestcase import RulesTestCase, tree ...@@ -7,21 +7,74 @@ from tests.rulestestcase import RulesTestCase, tree
class TestRulesSort(RulesTestCase): class TestRulesSort(RulesTestCase):
def test_match_sort_molinome_constant(self): def test_swap_mono(self):
self.assertTrue(swap_mono(tree('x, 1 / 2')))
self.assertFalse(swap_mono(tree('2, 1 / 2')))
self.assertTrue(swap_mono(tree('x, 2')))
self.assertFalse(swap_mono(tree('2, x')))
self.assertTrue(swap_mono(tree('x ^ 2, 2')))
self.assertTrue(swap_mono(tree('y, x')))
self.assertFalse(swap_mono(tree('x, y')))
self.assertFalse(swap_mono(tree('x, x')))
self.assertTrue(swap_mono(tree('x ^ 3, x ^ 2')))
self.assertFalse(swap_mono(tree('x ^ 2, x ^ 3')))
def test_swap_poly(self):
self.assertTrue(swap_poly(tree('2, x')))
self.assertFalse(swap_poly(tree('x, 2')))
self.assertTrue(swap_poly(tree('a, a^2')))
self.assertFalse(swap_poly(tree('a^2, a')))
self.assertTrue(swap_poly(tree('y, x')))
self.assertFalse(swap_poly(tree('x, y')))
self.assertFalse(swap_poly(tree('x, x')))
self.assertFalse(swap_poly(tree('x ^ 3, x ^ 2')))
self.assertTrue(swap_poly(tree('x ^ 2, x ^ 3')))
def test_get_power_prop(self):
self.assertEqual(get_power_prop(tree('a')), ('a', 1))
self.assertEqual(get_power_prop(tree('a ^ b')), ('a', 1))
self.assertEqual(get_power_prop(tree('a ^ 2')), ('a', 2))
self.assertEqual(get_power_prop(tree('a ^ -2')), ('a', -2))
self.assertIsNone(get_power_prop(tree('1')))
def test_get_poly_prop(self):
self.assertEqual(get_poly_prop(tree('a ^ 2')), ('a', 2))
self.assertEqual(get_poly_prop(tree('2a ^ 2')), ('a', 2))
self.assertEqual(get_poly_prop(tree('ca ^ 2 * 2')), ('a', 2))
self.assertEqual(get_poly_prop(tree('ab ^ 2')), ('a', 1))
self.assertEqual(get_poly_prop(tree('a^3 * a^2')), ('a', 3))
self.assertEqual(get_poly_prop(tree('a^2 * a^3')), ('a', 3))
self.assertIsNone(get_poly_prop(tree('1')))
def test_match_sort_monomial_constant(self):
x, l2 = root = tree('x * 2') x, l2 = root = tree('x * 2')
self.assertEqualPos(match_sort_molinome(root), self.assertEqualPos(match_sort_monomial(root),
[P(root, swap_factors, (Scope(root), x, l2))]) [P(root, swap_factors, (Scope(root), x, l2))])
root = tree('2x') root = tree('2x')
self.assertEqualPos(match_sort_molinome(root), []) self.assertEqualPos(match_sort_monomial(root), [])
def test_match_sort_monomial_variables(self):
y, x = root = tree('yx')
self.assertEqualPos(match_sort_monomial(root),
[P(root, swap_factors, (Scope(root), y, x))])
root = tree('xy')
self.assertEqualPos(match_sort_monomial(root), [])
#def test_match_sort_molinome_variables(self): def test_match_sort_polynome(self):
# y, x = root = tree('yx') x, x2 = root = tree('x + x ^ 2')
# self.assertEqualPos(match_sort_molinome(root), self.assertEqualPos(match_sort_polynome(root),
# [P(root, swap_factors, (Scope(root), y, x))]) [P(root, swap_factors, (Scope(root), x, x2))])
# root = tree('xy') root = tree('x + 2')
# self.assertEqualPos(match_sort_molinome(root), []) self.assertEqualPos(match_sort_polynome(root), [])
def test_swap_factors(self): def test_swap_factors(self):
x, l2 = root = tree('x * 2') x, l2 = root = tree('x * 2')
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment