Commit 0d06e0ae authored by Taddeus Kroes's avatar Taddeus Kroes

Implemented retrieval possibilities successfully.

parent 206246be
......@@ -93,25 +93,29 @@ class ExpressionNode(Node, ExpressionBase):
node.parent = self.parent
self.parent = None
def get_order(self):
if self.is_power() and self[0].is_identifier() and self[1].is_leaf():
# a ^ 3
return (self[0].value, self[1].value, 1)
if not self.op & OP_MUL:
def get_polynome(self):
"""
Identifier nodes of all polynomes, tuple format is:
(identifier, exponent, coefficient, literal_exponent)
"""
if self.is_power():
# a ^ e
return (self[0], self[1], ExpressionLeaf(1), True)
if self.op != OP_MUL:
return
for n0, n1 in [(0, 1), (1, 0)]:
if self[n0].is_numeric():
if self[n1].is_identifier():
# 2 * a
return (self[n1].value, 1, self[n0].value)
# c * a
return (self[n1], ExpressionLeaf(1), self[n0], False)
elif self[n1].is_power():
# 2 * a ^ 3
# c * a ^ e
coeff, power = self
root, exponent = power
if power[0].is_identifier() and power[1].is_leaf():
return (power[0].value, power[1].value, coeff.value)
return (root, exponent, coeff, True)
def get_scope(self):
""""""
......@@ -119,7 +123,7 @@ class ExpressionNode(Node, ExpressionBase):
#op = OP_ADD | OP_SUB if self.op & (OP_ADD | OP_SUB) else self.op
for child in self:
if not child.is_leaf() and child.op & self.op:
if not child.is_leaf() and child.op == self.op:
scope += child.get_scope()
else:
scope.append(child)
......@@ -133,9 +137,13 @@ class ExpressionLeaf(Leaf, ExpressionBase):
self.type = TYPE_MAP[type(args[0])]
def get_order(self):
if self.is_identifier():
return (self.value, 1, 1)
def get_polynome(self):
"""
Identifier nodes of all polynomes, tuple format is:
(identifier, exponent, coefficient, literal_exponent)
"""
# a = 1 * a ^ 1
return (self, ExpressionLeaf(1), ExpressionLeaf(1), False)
def replace(self, node):
if not hasattr(self, 'parent'):
......
......@@ -11,9 +11,14 @@ class Possibility(object):
def __repr__(self):
return str(self)
# TODO: Add unit tests
def __eq__(self, other):
self_arg0, self_arg1 = zip(*self.args)
other_arg0, other_arg1 = zip(*other.args)
return self.handler == other.handler \
and map(hash, self.args) == map(hash, other.args)
and self_arg1 == other_arg1 \
and map(hash, self_arg0) == map(hash, other_arg0)
def filter_duplicates(items):
......
......@@ -11,28 +11,25 @@ def match_expand(node):
a * (b + c) -> ab + ac
"""
assert node.type == TYPE_OPERATOR
assert node.op & OP_MUL
assert node.op == OP_MUL
# TODO: fix!
return []
p = []
# 'a' parts
left = []
# '(b + c)' parts
right = []
p = []
a = []
bc = []
for n in node.get_scope():
if node.type == TYPE_OPERATOR:
if n.op & OP_ADD:
right.append(n)
else:
left.append(n)
if n.is_leaf():
a.append(n)
elif n.op == OP_ADD:
bc.append(n)
if len(left) and len(right):
for l in left:
for r in right:
p.append(P(node, expand_single, l, r))
if a and bc:
for a_node in a:
for bc_node in bc:
p.append(P(node, expand_single, a_node, bc_node))
return p
......@@ -64,83 +61,77 @@ def match_combine_factors(node):
k0 * v ^ n + exp + k1 * v ^ n -> exp + (k0 + k1) * v ^ n
"""
assert node.type == TYPE_OPERATOR
assert node.op & OP_ADD
assert node.op == OP_ADD
p = []
# Collect all nodes that can be combined
# Numeric leaves
numerics = []
# Identifier leaves of all orders, tuple format is;
# (identifier, exponent, coefficient)
orders = []
# Collect all nodes that can be combined:
# a ^ e = 1 * a ^ e
# c * a = c * a ^ 1
# c * a ^ e
# a = 1 * a ^ 1
#
# Identifier nodes of all polynomes, tuple format is:
# (identifier, exponent, coefficient, literal_coefficient)
polys = []
for n in node.get_scope():
if not n.is_leaf():
order = n.get_order()
if order:
orders.append(order)
elif n.is_numeric():
numerics.append(n)
elif n.is_identifier():
orders.append((n.value, 1, 1))
if len(numerics) > 1:
for num0, num1 in combinations(numerics, 2):
p.append(P(node, combine_numerics, (num0, num1)))
if len(orders) > 1:
for order0, order1 in combinations(orders, 2):
id0, exponent0, coeff0 = order0
id1, exponent1, coeff1 = order1
if id0 == id1 and exponent0 == exponent1:
# Same identifier and exponent -> combine coefficients
args = order0 + (coeff1,)
p.append(P(node, combine_orders, args))
return p
polynome = n.get_polynome()
if polynome:
polys.append((n, polynome))
def combine_numerics(root, args):
"""
Combine two numeric leaves in an n-ary plus.
# Each combination of powers of the same value and polynome can be added
if len(polys) >= 2:
for left, right in combinations(polys, 2):
r0, e0, c0 = left[1][:3]
r1, e1, c1 = right[1][:3]
Example:
>>> 3 + 4 -> 7
"""
others = list(set(root.get_scope()) - set(args))
value = sum([n.value for n in args])
if (r0.is_numeric() and r1.is_numeric() and e0 == e1 == Leaf(1)) \
or (r0 == r1 and e0 == e1):
# Both numeric and same exponent -> combine coefficients and
# roots, or:
# Same identifier and exponent -> combine coefficients
p.append(P(node, combine_polynomes, (left, right)))
return nary_node('+', others + [Leaf(value)])
return p
def combine_orders(root, args):
def combine_polynomes(root, args):
"""
Combine two identifier multiplications of any order in an n-ary plus.
Combine two identifier multiplications of any polynome in an n-ary plus.
Example:
3x + 4x -> 7x
c * a ^ b + d * a ^ b -> (c + d) * a ^ b
"""
identifier, exponent, coeff0, coeff1, others = args
left, right = args
nl, pl = left
nr, pr = right
c0, r0, e0 = pl
c1, r1, e1 = pr
coeff = coeff0 + coeff1
scope = root.get_scope()
if not exponent:
# a ^ 0 -> 1
ident = Leaf(1)
elif exponent == 1:
# a ^ 1 -> a
ident = Leaf(identifier)
if r0.is_numeric() and r1.is_numeric() and e0 == e1 == 1:
new_root = Leaf(r0.value + r1.value)
else:
# a ^ n -> a ^ n
ident = Node('^', Leaf(identifier), Leaf(exponent))
new_root = r0
if coeff == 1:
combined = ident
if pl[3] or pr[3]:
# literal a ^ 1 -> a ^ 1
power = Node('^', pl[0], pl[1])
else:
combined = Node('*', Leaf(coeff), ident)
# nonliteral a ^ 1 -> a
power = pl[0]
# replacement: (c + d) * a ^ b
# a, b and c are from 'left', d is from 'right'.
replacement = Node('*', Node('+', pl[2], pr[2]), power)
# Replace the left node with the new expression
scope[scope.index(nl)] = replacement
# Remove the right node
scope.remove(nr)
return nary_node('+', others + [combined])
return nary_node('+', scope)
......@@ -51,23 +51,23 @@ class TestNode(unittest.TestCase):
self.assertTrue(L(1.5).is_numeric())
self.assertFalse(L('a').is_numeric())
def test_get_order_identifier(self):
self.assertEqual(L('a').get_order(), ('a', 1, 1))
def test_get_polynome_identifier(self):
self.assertEqual(L('a').get_polynome(), (L('a'), L(1), L(1), False))
def test_get_order_None(self):
self.assertIsNone(L(1).get_order())
def test_get_polynome_None(self):
self.assertIsNone(N('+').get_polynome())
def test_get_order_power(self):
def test_get_polynome_power(self):
power = N('^', L('a'), L(2))
self.assertEqual(power.get_order(), ('a', 2, 1))
self.assertEqual(power.get_polynome(), (L('a'), L(2), L(1), True))
def test_get_order_coefficient_exponent_int(self):
def test_get_polynome_coefficient_exponent_int(self):
times = N('*', L(3), N('^', L('a'), L(2)))
self.assertEqual(times.get_order(), ('a', 2, 3))
self.assertEqual(times.get_polynome(), (L('a'), L(2), L(3), True))
def test_get_order_coefficient_exponent_id(self):
def test_get_polynome_coefficient_exponent_id(self):
times = N('*', L(3), N('^', L('a'), L('b')))
self.assertEqual(times.get_order(), ('a', 'b', 3))
self.assertEqual(times.get_polynome(), (L('a'), L('b'), L(3), True))
def test_get_scope_binary(self):
plus = N('+', *self.l[:2])
......
import unittest
from src.node import ExpressionNode as N, ExpressionLeaf as L
from src.rules.poly import match_combine_factors, combine_polynomes
from src.possibilities import Possibility as P
from src.parser import Parser
from tests.parser import ParserWrapper
def tree(exp, **kwargs):
return ParserWrapper(Parser, **kwargs).run([exp])
class TestRulesPoly(unittest.TestCase):
#def test_match_combine_factors_numeric_combinations(self):
# l0, l1, l2 = L(1), L(2), L(2)
# plus = N('+', N('+', l0, l1), l2)
# p = match_combine_factors(plus)
# self.assertEqualPos(p, [P(plus, combine_polynomes, (l0, l1)),
# P(plus, combine_polynomes, (l0, l2)),
# P(plus, combine_polynomes, (l1, l2))])
def assertEqualPos(self, possibilities, expected):
self.assertEqual(len(possibilities), len(expected))
for p, e in zip(possibilities, expected):
self.assertEqual(p.root, e.root)
self.assertEqual(p, e)
def test_numeric(self):
l1, l2 = root = tree('1+2')
self.assertEqualPos(match_combine_factors(root),
[P(root, combine_polynomes, ((l1, (l1, l1, l1, False)),
(l2, (l2, l1, l1, False))))])
a1, a2 = root = tree('a+a')
self.assertEqualPos(match_combine_factors(root),
[P(root, combine_polynomes, ((a1, (a1, l1, l1, False)),
(a2, (a2, l1, l1, False))))])
a1, a2 = root = tree('a+2a')
self.assertEqualPos(match_combine_factors(root),
[P(root, combine_polynomes, ((a1, (a1, l1, l1, False)),
(a2, (a2[1], l1, l2, False))))])
a1, a2 = root = tree('a2+a2')
self.assertEqualPos(match_combine_factors(root),
[P(root, combine_polynomes, ((a1, (a1[0], l2, l1, True)),
(a2, (a2[0], l2, l1, True))))])
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