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):
while self.possibilities:
# Find the first implicit possibliity in the list
# FIXME: Is it smart to apply a rule that is not a hint?
# ANSWER: Yes, but there must be an extra list that prevents
# deliberately generated implicit rules from being applied
# ANSWER: Yes, but there must be something like an extra list
# that prevents deliberately generated implicit rules from
# being applied
#sugg = self.possibilities[0]
#if sugg.handler not in IMPLICIT_RULES:
......
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 .factors import match_expand
from .powers import match_add_exponents, match_subtract_exponents, \
......@@ -27,7 +28,7 @@ from .integrals import match_solve_indef, match_constant_integral, \
match_integrate_variable_power, match_factor_out_constant, \
match_division_integral, match_function_integral, \
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 .sqrt import match_reduce_sqrt
......@@ -67,4 +68,5 @@ RULES = {
OP_EQ: [match_move_term],
OP_ABS: [match_factor_out_abs_term],
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, \
eq, OP_ABS
eq, OP_ABS, OP_AND
from ..possibilities import Possibility as P, MESSAGES
from ..translate import _
......@@ -148,3 +150,47 @@ def split_absolute_equation(root, args):
MESSAGES[split_absolute_equation] = _('Split absolute equation {0} into a ' \
'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, \
from .integrals import factor_out_constant, integrate_variable_root
from .powers import remove_power_of_one
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
......@@ -39,7 +40,7 @@ LOW = [
# higher priority than B. This list ignores occurences in the HIGH or LOW lists
# above
RELATIVE = [
# Precedences needed for 'power rule'
# Precedences needed for 'power rule' (derivative of an exponentiation)
(chain_rule, raised_base),
(raised_base, factor_out_exponent),
......@@ -58,11 +59,15 @@ RELATIVE = [
# root first
(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),
#
# Prevent cycles that are caused by multiplication reductions when
# splitting up fractions
(extract_fraction_terms, multiply_numerics),
# Prevent useless swapping when solving multiple equations
(substitute_variable, swap_sides),
]
......@@ -84,6 +89,5 @@ IMPLICIT_RULES = [
remove_zero,
remove_power_of_one,
negated_factor,
multiply_numerics,
add_numerics,
]
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 tests.rulestestcase import RulesTestCase, tree
......@@ -107,3 +109,15 @@ class TestRulesLineq(RulesTestCase):
'x = -a * 1',
'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