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

Applied most of the new fraction rules.

parent 7a9605a7
No related branches found
No related tags found
No related merge requests found
......@@ -11,7 +11,7 @@ from .numerics import match_add_numerics, match_divide_numerics, \
match_raise_numerics
from .fractions import match_constant_division, match_add_fractions, \
match_multiply_fractions, match_divide_fractions, \
match_equal_fraction_parts
match_extract_fraction_terms
from .negation import match_negated_factor, match_negate_polynome, \
match_negated_division
from .sort import match_sort_multiplicants
......@@ -39,7 +39,7 @@ RULES = {
match_factor_in_multiplicant],
OP_DIV: [match_subtract_exponents, match_divide_numerics,
match_constant_division, match_divide_fractions,
match_negated_division, match_equal_fraction_parts],
match_negated_division, match_extract_fraction_terms],
OP_POW: [match_multiply_exponents, match_duplicate_exponent,
match_raised_fraction, match_remove_negative_exponent,
match_exponent_to_root, match_extend_exponent,
......
......@@ -180,7 +180,7 @@ MESSAGES[constant_to_fraction] = \
def match_multiply_fractions(node):
"""
a / b * (c / d) -> ac / (bd)
a / b * c and a, c in Z or (a = 1 and eval(b) not in Z) -> ac / b
a / b * c and (c in Z or eval(a / b) not in Z) -> ac / b
"""
assert node.is_op(OP_MUL)
......@@ -195,8 +195,9 @@ def match_multiply_fractions(node):
for ab, c in product(fractions, others):
a, b = ab
if (a.is_numeric() and c.is_numeric()) or \
(a == 1 and evals_to_numeric(b)):
#if (a.is_numeric() and c.is_numeric()) or \
# (a == 1 and evals_to_numeric(b)):
if c.is_numeric() or not evals_to_numeric(ab):
p.append(P(node, multiply_with_fraction, (scope, ab, c)))
return p
......@@ -243,6 +244,9 @@ def match_divide_fractions(node):
Examples:
a / b / c -> a / (bc)
a / (b / c) -> ac / b
Note that:
a / b / (c / d) ->* ad / bd # chain test!
"""
assert node.is_op(OP_DIV)
......@@ -260,11 +264,11 @@ def match_divide_fractions(node):
def divide_fraction(root, args):
"""
a / b / c -> a / (bc)
a / b / c -> a / (bc)
"""
a, b, c = args
(a, b), c = root
return a / (b * c)
return (a / (b * c)).negate(root.negated)
MESSAGES[divide_fraction] = _('Move {3} to denominator of fraction {1} / {2}.')
......@@ -272,38 +276,18 @@ MESSAGES[divide_fraction] = _('Move {3} to denominator of fraction {1} / {2}.')
def divide_by_fraction(root, args):
"""
a / (b / c) -> ac / b
a / (b / c) -> ac / b
"""
a, b, c = args
a, bc = root
b, c = bc
return a * c / b
return (a * c / b).negate(root.negated + bc.negated)
MESSAGES[divide_by_fraction] = \
_('Move {3} to nominator of fraction {1} / {2}.')
#def match_extract_divided_fractions(node):
# """
# Reduce divisions of fractions to a single fraction.
#
# Examples:
# a / b / c -> a / bc
# a / (b / c) -> ac / b
# # IMPLICIT: a / b / (c / d) ->* ad / bd -> validation test!
# """
# assert node.is_op(OP_DIV)
#
# nom, denom = node
# n_scope, d_scope = fraction_scopes(node)
# is_division = lambda n: n.is_op(OP_DIV)
# n_fractions, n_others = partition(is_division, n_scope)
# d_fractions, d_others = partition(is_division, d_scope)
#
#
# return []
def fraction_scopes(node):
"""
Get the multiplication scopes of the nominator and denominator of a
......@@ -344,7 +328,7 @@ def is_power_combination(a, b):
return a == b
def match_equal_fraction_parts(node):
def match_extract_fraction_terms(node):
"""
Divide nominator and denominator by the same part.
......@@ -357,6 +341,7 @@ def match_equal_fraction_parts(node):
#a ^ p * b / a -> a ^ p / a * b / 1
#a * b / a ^ q -> a / a ^ q * b / 1
"""
# TODO: ac / b -> a / b * c
assert node.is_op(OP_DIV)
nominator, denominator = node
......@@ -369,31 +354,16 @@ def match_equal_fraction_parts(node):
# Look for matching parts in scopes
for n, d in product(n_scope, d_scope):
if is_power_combination(n, d):
p.append(P(N, extract_fraction_terms, (n_scope, d_scope, n, d)))
#for i, n in enumerate(n_scope):
# for j, d in enumerate(d_scope):
# if n.equals(d, ignore_negation=True):
# p.append(P(node, divide_fraction_parts,
# (negate(n, 0), n_scope, d_scope, i, j)))
# if n.is_op(OP_POW):
# a = n[0]
# if d == a or (d.is_op(OP_POW) and d[0] == a):
# # a ^ p * b / a -> a ^ p / a * b
# p.append(P(node, extract_divided_roots,
# (a, n_scope, d_scope, i, j)))
# elif d.is_op(OP_POW) and n == d[0]:
# # a * b / a ^ q -> a / a ^ q * b
# p.append(P(node, extract_divided_roots,
# (d[0], n_scope, d_scope, i, j)))
p.append(P(node, extract_fraction_terms, (n_scope, d_scope, n, d)))
return p
def extract_fraction_terms(root, args):
"""
ab / a -> a / a * (b / 1)
a / (ba) -> a / a * (1 / b)
a * c / (ae) -> a / a * (c / e)
a ^ b * c / (a ^ d * e) -> a ^ b / a ^ d * (c / e)
"""
n_scope, d_scope, n, d = args
......@@ -406,72 +376,9 @@ def extract_fraction_terms(root, args):
if len(d_scope) == 1:
d_scope.replace(d, L(1))
else:
d_scope.remove(n)
d_scope.remove(d)
return n / d * (n_scope.as_nary_node() / d_scope.as_nary_node())
#def remove_from_scopes(n_scope, d_scope, i, j):
# a_n, a_d = n_scope[i], d_scope[j]
#
# del n_scope[i]
# del d_scope[j]
#
# if not n_scope:
# # Last element of nominator scope, replace by 1
# nom = L(1)
# elif len(n_scope) == 1:
# # Only one element left, no multiplication
# nom = n_scope[0]
# else:
# # Still a multiplication
# nom = nary_node('*', n_scope)
#
# if not d_scope:
# denom = L(1)
# elif len(n_scope) == 1:
# denom = d_scope[0]
# else:
# denom = nary_node('*', d_scope)
#
# return a_n, a_d, nom, denom
#
#
#def divide_fraction_parts(root, args):
# """
# Divide nominator and denominator by the same part.
#
# Examples:
# ab / (ac) -> b / c
# ab / a -> b / 1
# a / (ab) -> 1 / b
# -ab / a -> -b / 1
# """
# a, n_scope, d_scope, i, j = args
# n, d = root
# a_n, a_d, nom, denom = remove_from_scopes(n_scope, d_scope, i, j)
#
# # Move negation of removed part to nominator and denominator
# return nom.negate(n.negated + a_n.negated) \
# / denom.negate(d.negated + a_d.negated)
#
#
#MESSAGES[divide_fraction_parts] = \
# _('Divide nominator and denominator in {0} by {1}.')
#
#
#def extract_divided_roots(root, args):
# """
# a ^ p * b / a ^ q -> a ^ p / a ^ q * b / 1
# a ^ p * b / a -> a ^ p / a * b / 1
# a * b / a ^ q -> a / a ^ q * b / 1
# """
# a, n_scope, d_scope, i, j = args
# n, d = root
# ap, aq, nom, denom = remove_from_scopes(n_scope, d_scope, i, j)
#
# return ap / aq * nom.negate(n.negated) / denom.negate(d.negated)
#
#
#MESSAGES[extract_divided_roots] = \
# _('Extract the root {1} from nominator and denominator in {0}.')
MESSAGES[extract_fraction_terms] = _('Extract {3} / {4} from fraction {0}.')
from itertools import combinations
from .utils import evals_to_numeric
from ..node import ExpressionLeaf as Leaf, Scope, OP_ADD, OP_MUL, nary_node, \
negate
from ..possibilities import Possibility as P, MESSAGES
......@@ -35,9 +36,7 @@ def match_combine_groups(node):
l = len(n_scope)
for i, sub_node in enumerate(n_scope):
# TODO: use utitlity function evals_to_numeric
#if evals_to_numeric(sub_node):
if sub_node.is_numeric():
if evals_to_numeric(sub_node):
others = [n_scope[j] for j in range(i) + range(i + 1, l)]
if len(others) == 1:
......
......@@ -2,9 +2,9 @@ from src.rules.fractions import match_constant_division, division_by_one, \
division_of_zero, division_by_self, match_add_fractions, \
equalize_denominators, add_nominators, match_multiply_fractions, \
multiply_fractions, multiply_with_fraction, match_divide_fractions, \
divide_fraction, divide_by_fraction, match_equal_fraction_parts, \
divide_fraction, divide_by_fraction, match_extract_fraction_terms, \
constant_to_fraction, extract_fraction_terms
from src.node import Scope
from src.node import ExpressionNode as N, Scope, OP_MUL
from src.possibilities import Possibility as P
from tests.rulestestcase import RulesTestCase, tree
......@@ -181,119 +181,82 @@ class TestRulesFractions(RulesTestCase):
(a, b), c = root = tree('a / b / c')
self.assertEqual(divide_fraction(root, (a, b, c)), a / (b * c))
(a, b), c = root = tree('-a / b / c')
self.assertEqual(divide_fraction(root, (a, b, c)), -(a / (b * c)))
root = tree('a / b / -c')
self.assertEqual(divide_fraction(root, (a, b, c)), a / (b * -c))
def test_divide_by_fraction(self):
a, (b, c) = root = tree('a / (b / c)')
self.assertEqual(divide_by_fraction(root, (a, b, c)), a * c / b)
def test_match_equal_fraction_parts(self):
a, (b, c) = root = tree('-a / (b / c)')
self.assertEqual(divide_by_fraction(root, (a, b, c)), -(a * c / b))
root = tree('a / -(b / c)')
self.assertEqual(divide_by_fraction(root, (a, b, c)), -(a * c / b))
def test_match_extract_fraction_terms(self):
root, a, b, c = tree('ab / (ca), a, b, c')
n, d = root
self.assertEqualPos(match_equal_fraction_parts(root),
self.assertEqualPos(match_extract_fraction_terms(root),
[P(root, extract_fraction_terms, (Scope(n), Scope(d), a, a))])
lscp = lambda l: Scope(N(OP_MUL, l))
n, d = root = tree('ab / a')
self.assertEqualPos(match_equal_fraction_parts(root),
[P(root, extract_fraction_terms, (Scope(n), Scope(d), a, a))])
self.assertEqualPos(match_extract_fraction_terms(root),
[P(root, extract_fraction_terms, (Scope(n), lscp(d), a, a))])
n, d = root = tree('a / (ab)')
self.assertEqualPos(match_equal_fraction_parts(root),
[P(root, extract_fraction_terms, (Scope(n), Scope(d), a, a))])
self.assertEqualPos(match_extract_fraction_terms(root),
[P(root, extract_fraction_terms, (lscp(n), Scope(d), a, a))])
n, d = root = tree('abc / (cba)')
self.assertEqualPos(match_equal_fraction_parts(root),
[P(root, extract_fraction_terms, (Scope(n), scope(d), a, a)),
P(root, extract_fraction_terms, (Scope(n), scope(d), b, b)),
P(root, extract_fraction_terms, (Scope(n), scope(d), c, c))])
self.assertEqualPos(match_extract_fraction_terms(root),
[P(root, extract_fraction_terms, (Scope(n), Scope(d), a, a)),
P(root, extract_fraction_terms, (Scope(n), Scope(d), b, b)),
P(root, extract_fraction_terms, (Scope(n), Scope(d), c, c))])
root = tree('a / a')
self.assertEqualPos(match_equal_fraction_parts(root), [])
(ap, b), aq = root = tree('a ^ p * b / a ^ q')
self.assertequalpos(match_equal_fraction_parts(root),
[p(root, extract_fraction_terms, (a, [ap, b], [aq], 0, 0))])
(a, b), aq = root = tree('a * b / a ^ q')
self.assertequalpos(match_equal_fraction_parts(root),
[p(root, extract_fraction_terms, (a, [a, b], [aq], 0, 0))])
(ap, b), a = root = tree('a ^ p * b / a')
self.assertequalpos(match_equal_fraction_parts(root),
[p(root, extract_fraction_terms, (a, [ap, b], [a], 0, 0))])
#def test_match_equal_fraction_parts(self):
# (a, b), (c, a) = root = tree('ab / (ca)')
# self.assertEqualPos(match_equal_fraction_parts(root),
# [P(root, divide_fraction_parts, (a, [a, b], [c, a], 0, 1))])
# (a, b), a = root = tree('ab / a')
# self.assertEqualPos(match_equal_fraction_parts(root),
# [P(root, divide_fraction_parts, (a, [a, b], [a], 0, 0))])
# a, (a, b) = root = tree('a / (ab)')
# self.assertEqualPos(match_equal_fraction_parts(root),
# [P(root, divide_fraction_parts, (a, [a], [a, b], 0, 0))])
# root = tree('abc / (cba)')
# ((a, b), c) = root[0]
# s0, s1 = [a, b, c], [c, b, a]
# self.assertEqualPos(match_equal_fraction_parts(root),
# [P(root, divide_fraction_parts, (a, s0, s1, 0, 2)),
# P(root, divide_fraction_parts, (b, s0, s1, 1, 1)),
# P(root, divide_fraction_parts, (c, s0, s1, 2, 0))])
# root = tree('-a / a')
# self.assertEqualPos(match_equal_fraction_parts(root),
# [P(root, divide_fraction_parts, (a, [-a], [a], 0, 0))])
# (ap, b), aq = root = tree('a ^ p * b / a ^ q')
# self.assertEqualPos(match_equal_fraction_parts(root),
# [P(root, extract_divided_roots, (a, [ap, b], [aq], 0, 0))])
# (a, b), aq = root = tree('a * b / a ^ q')
# self.assertEqualPos(match_equal_fraction_parts(root),
# [P(root, extract_divided_roots, (a, [a, b], [aq], 0, 0))])
# (ap, b), a = root = tree('a ^ p * b / a')
# self.assertEqualPos(match_equal_fraction_parts(root),
# [P(root, extract_divided_roots, (a, [ap, b], [a], 0, 0))])
#def test_divide_fraction_parts(self):
# (a, b), (c, a) = root = tree('ab / (ca)')
# result = divide_fraction_parts(root, (a, [a, b], [c, a], 0, 1))
# self.assertEqual(result, b / c)
# (a, b), a = root = tree('ab / a')
# result = divide_fraction_parts(root, (a, [a, b], [a], 0, 0))
# self.assertEqual(result, b / 1)
# root, l1 = tree('a / (ab), 1')
# a, (a, b) = root
# result = divide_fraction_parts(root, (a, [a], [a, b], 0, 0))
# self.assertEqual(result, l1 / b)
# root = tree('abc / (cba)')
# ((a, b), c) = root[0]
# result = divide_fraction_parts(root, (a, [a, b, c], [c, b, a], 0, 2))
# self.assertEqual(result, b * c / (c * b))
# result = divide_fraction_parts(root, (b, [a, b, c], [c, b, a], 1, 1))
# self.assertEqual(result, a * c / (c * a))
# result = divide_fraction_parts(root, (c, [a, b, c], [c, b, a], 2, 0))
# self.assertEqual(result, a * b / (b * a))
# (a, b), a = root = tree('-ab / a')
# result = divide_fraction_parts(root, (a, [-a, b], [a], 0, 0))
# self.assertEqual(result, -b / 1)
#def test_extract_divided_roots(self):
# r, a = tree('a ^ p * b / a ^ q, a')
# ((a, p), b), (a, q) = (ap, b), aq = r
# self.assertEqual(extract_divided_roots(r, (a, [ap, b], [aq], 0, 0)),
# a ** p / a ** q * b / 1)
# r = tree('a * b / a ^ q, a')
# self.assertEqual(extract_divided_roots(r, (a, [a, b], [aq], 0, 0)),
# a / a ** q * b / 1)
# r = tree('a ^ p * b / a, a')
# self.assertEqual(extract_divided_roots(r, (a, [ap, b], [a], 0, 0)),
# a ** p / a * b / 1)
self.assertEqualPos(match_extract_fraction_terms(root), [])
(ap, b), aq = n, d = root = tree('a ^ p * b / a ^ q')
self.assertEqualPos(match_extract_fraction_terms(root),
[P(root, extract_fraction_terms, (Scope(n), lscp(d), ap, aq))])
(a, b), aq = n, d = root = tree('a * b / a ^ q')
self.assertEqualPos(match_extract_fraction_terms(root),
[P(root, extract_fraction_terms, (Scope(n), lscp(d), a, aq))])
(ap, b), a = n, d = root = tree('a ^ p * b / a')
self.assertEqualPos(match_extract_fraction_terms(root),
[P(root, extract_fraction_terms, (Scope(n), lscp(d), ap, a))])
def test_extract_fraction_terms_basic(self):
root, expect = tree('ab / (ca), a / a * (b / c)')
n, d = root
self.assertEqual(extract_fraction_terms(root,
(Scope(n), Scope(d), n[0], d[1])), expect)
def test_extract_fraction_terms_leaf(self):
root, expect = tree('ba / a, a / a * (b / 1)')
n, d = root
self.assertEqual(extract_fraction_terms(root,
(Scope(n), Scope(N(OP_MUL, d)), n[1], d)), expect)
root, expect = tree('a / (ab), a / a * (1 / b)')
n, d = root
self.assertEqual(extract_fraction_terms(root,
(Scope(N(OP_MUL, n)), Scope(d), n, d[0])), expect)
def test_extract_fraction_terms_chain(self):
self.assertRewrite([
'a ^ 3 * 4 / (a ^ 2 * 5)',
'a ^ 3 / a ^ 2 * (4 / 5)',
'a ^ (3 - 2)(4 / 5)',
'a ^ 1 * (4 / 5)',
'a(4 / 5)',
# FIXME: '4 / 5 * a',
])
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