Commit 71bb5823 authored by Taddeus Kroes's avatar Taddeus Kroes

Implemented substitution for solving sets of linear equations..

parent ce4874ef
...@@ -294,8 +294,9 @@ class Parser(BisonParser): ...@@ -294,8 +294,9 @@ class Parser(BisonParser):
while self.possibilities: while self.possibilities:
# Find the first implicit possibliity in the list # Find the first implicit possibliity in the list
# FIXME: Is it smart to apply a rule that is not a hint? # FIXME: Is it smart to apply a rule that is not a hint?
# ANSWER: Yes, but there must be an extra list that prevents # ANSWER: Yes, but there must be something like an extra list
# deliberately generated implicit rules from being applied # that prevents deliberately generated implicit rules from
# being applied
#sugg = self.possibilities[0] #sugg = self.possibilities[0]
#if sugg.handler not in IMPLICIT_RULES: #if sugg.handler not in IMPLICIT_RULES:
......
from ..node import OP_ADD, OP_MUL, OP_DIV, OP_POW, OP_NEG, OP_SIN, OP_COS, \ 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_ABS, OP_SQRT OP_TAN, OP_DER, OP_LOG, OP_INT, OP_INT_INDEF, OP_EQ, OP_ABS, OP_SQRT, \
OP_AND
from .groups import match_combine_groups from .groups import match_combine_groups
from .factors import match_expand from .factors import match_expand
from .powers import match_add_exponents, match_subtract_exponents, \ from .powers import match_add_exponents, match_subtract_exponents, \
...@@ -27,7 +28,7 @@ from .integrals import match_solve_indef, match_constant_integral, \ ...@@ -27,7 +28,7 @@ from .integrals import match_solve_indef, match_constant_integral, \
match_integrate_variable_power, match_factor_out_constant, \ match_integrate_variable_power, match_factor_out_constant, \
match_division_integral, match_function_integral, \ match_division_integral, match_function_integral, \
match_sum_rule_integral, match_remove_indef_constant match_sum_rule_integral, match_remove_indef_constant
from .lineq import match_move_term from .lineq import match_move_term, match_multiple_equations
from .absolute import match_factor_out_abs_term from .absolute import match_factor_out_abs_term
from .sqrt import match_reduce_sqrt from .sqrt import match_reduce_sqrt
...@@ -67,4 +68,5 @@ RULES = { ...@@ -67,4 +68,5 @@ RULES = {
OP_EQ: [match_move_term], OP_EQ: [match_move_term],
OP_ABS: [match_factor_out_abs_term], OP_ABS: [match_factor_out_abs_term],
OP_SQRT: [match_reduce_sqrt], OP_SQRT: [match_reduce_sqrt],
OP_AND: [match_multiple_equations],
} }
from .utils import find_variable, evals_to_numeric from itertools import permutations
from .utils import find_variable, evals_to_numeric, substitute
from ..node import ExpressionLeaf as L, Scope, OP_EQ, OP_ADD, OP_MUL, OP_DIV, \ from ..node import ExpressionLeaf as L, Scope, OP_EQ, OP_ADD, OP_MUL, OP_DIV, \
eq, OP_ABS eq, OP_ABS, OP_AND
from ..possibilities import Possibility as P, MESSAGES from ..possibilities import Possibility as P, MESSAGES
from ..translate import _ from ..translate import _
...@@ -148,3 +150,47 @@ def split_absolute_equation(root, args): ...@@ -148,3 +150,47 @@ def split_absolute_equation(root, args):
MESSAGES[split_absolute_equation] = _('Split absolute equation {0} into a ' \ MESSAGES[split_absolute_equation] = _('Split absolute equation {0} into a ' \
'negative and a positive equation.') 'negative and a positive equation.')
def match_multiple_equations(node):
"""
Multiple equations can be solved using substitution and/or elimination.
Substitution rule:
x = a ^^ f(x) = g(x) -> x = a ^^ f(a) = g(a) # Substitute x with a
Substitution example:
x = ay + b ^^ cx + dy = e -> x = ay + b ^^ c(ay + b) + dy = e
# =>* x = eval(a * eval((e - bc) / (ca + b)) + b)
# ^^ y = eval((e - bc) / (ca + b))
"""
assert node.is_op(OP_AND)
scope = Scope(node)
equations = filter(lambda exp: exp.is_op(OP_EQ), scope)
p = []
if len(equations) < 2:
return p
for eq0, eq1 in permutations(equations, 2):
x, subs = eq0
# Substitution rule
if x.is_variable() and eq1.contains(x):
p.append(P(node, substitute_variable, (scope, x, subs, eq1)))
return p
def substitute_variable(root, args):
"""
Substitution rule:
x = a ^^ f(x) = g(x) -> x = a ^^ f(a) = g(a) # Substitute x with a
"""
scope, x, subs, eq = args
scope.replace(eq, substitute(eq, x, subs))
return scope.as_nary_node()
MESSAGES[substitute_variable] = _('Substitute {2} with {3} in {4}.')
...@@ -12,6 +12,7 @@ from .fractions import multiply_with_fraction, extract_fraction_terms, \ ...@@ -12,6 +12,7 @@ from .fractions import multiply_with_fraction, extract_fraction_terms, \
from .integrals import factor_out_constant, integrate_variable_root from .integrals import factor_out_constant, integrate_variable_root
from .powers import remove_power_of_one from .powers import remove_power_of_one
from .sqrt import quadrant_sqrt, extract_sqrt_mult_priority from .sqrt import quadrant_sqrt, extract_sqrt_mult_priority
from .lineq import substitute_variable, swap_sides
# Functions to move to the beginning of the possibilities list. Pairs of within # Functions to move to the beginning of the possibilities list. Pairs of within
...@@ -39,7 +40,7 @@ LOW = [ ...@@ -39,7 +40,7 @@ LOW = [
# higher priority than B. This list ignores occurences in the HIGH or LOW lists # higher priority than B. This list ignores occurences in the HIGH or LOW lists
# above # above
RELATIVE = [ RELATIVE = [
# Precedences needed for 'power rule' # Precedences needed for 'power rule' (derivative of an exponentiation)
(chain_rule, raised_base), (chain_rule, raised_base),
(raised_base, factor_out_exponent), (raised_base, factor_out_exponent),
...@@ -58,11 +59,15 @@ RELATIVE = [ ...@@ -58,11 +59,15 @@ RELATIVE = [
# root first # root first
(extract_sqrt_mult_priority, multiply_numerics), (extract_sqrt_mult_priority, multiply_numerics),
# sqrt(2 ^ 2) -> 2 # not sqrt 4 # sqrt(2 ^ 2) -> 2 # rather than sqrt(4)
(quadrant_sqrt, raise_numerics), (quadrant_sqrt, raise_numerics),
# # Prevent cycles that are caused by multiplication reductions when
# splitting up fractions
(extract_fraction_terms, multiply_numerics), (extract_fraction_terms, multiply_numerics),
# Prevent useless swapping when solving multiple equations
(substitute_variable, swap_sides),
] ]
...@@ -84,6 +89,5 @@ IMPLICIT_RULES = [ ...@@ -84,6 +89,5 @@ IMPLICIT_RULES = [
remove_zero, remove_zero,
remove_power_of_one, remove_power_of_one,
negated_factor, negated_factor,
multiply_numerics,
add_numerics, add_numerics,
] ]
from src.rules.lineq import match_move_term, swap_sides, subtract_term, \ from src.rules.lineq import match_move_term, swap_sides, subtract_term, \
divide_term, multiply_term, split_absolute_equation divide_term, multiply_term, split_absolute_equation, \
match_multiple_equations, substitute_variable
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
...@@ -107,3 +109,15 @@ class TestRulesLineq(RulesTestCase): ...@@ -107,3 +109,15 @@ class TestRulesLineq(RulesTestCase):
'x = -a * 1', 'x = -a * 1',
'x = -a', 'x = -a',
]) ])
def test_match_multiple_equations(self):
eq0, eq1 = root = tree('x = 2 ^^ ay + x = 3')
x = eq0[0]
self.assertEqualPos(match_multiple_equations(root),
[P(root, substitute_variable, (Scope(root), x, 2, eq1))])
def test_substitute_variable(self):
root, expect = tree('x = 2 ^^ ay + x = 3, x = 2 ^^ ay + 2 = 3')
(x, l2), eq = root
self.assertEqual(substitute_variable(root, ((Scope(root), x, l2, eq))),
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