Commit e3a64795 authored by Taddeüs Kroes's avatar Taddeüs Kroes

Fixed some integral rules using negation and added a power rule:

- Added a power rule that factors out negation from a raised root.
- Reduction of "int_(-r)^(r) pi (r^2 - x^2) dx" is now supported.
parent 8f84aef4
...@@ -19,7 +19,7 @@ from .groups import match_combine_groups ...@@ -19,7 +19,7 @@ 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, \
match_multiply_exponents, match_duplicate_exponent, \ match_multiply_exponents, match_duplicate_exponent, \
match_raised_fraction, match_remove_negative_exponent, \ match_raised_fraction, match_remove_negative_child, \
match_exponent_to_root, match_extend_exponent, match_constant_exponent match_exponent_to_root, match_extend_exponent, match_constant_exponent
from .numerics import match_add_numerics, match_divide_numerics, \ from .numerics import match_add_numerics, match_divide_numerics, \
match_multiply_numerics, match_raise_numerics match_multiply_numerics, match_raise_numerics
...@@ -59,7 +59,7 @@ RULES = { ...@@ -59,7 +59,7 @@ RULES = {
match_negated_division, match_extract_fraction_terms, match_negated_division, match_extract_fraction_terms,
match_division_in_denominator], match_division_in_denominator],
OP_POW: [match_multiply_exponents, match_duplicate_exponent, OP_POW: [match_multiply_exponents, match_duplicate_exponent,
match_raised_fraction, match_remove_negative_exponent, match_raised_fraction, match_remove_negative_child,
match_exponent_to_root, match_extend_exponent, match_exponent_to_root, match_extend_exponent,
match_constant_exponent, match_raise_numerics, match_constant_exponent, match_raise_numerics,
match_raised_base], match_raised_base],
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
from .utils import find_variables, substitute, find_variable from .utils import find_variables, substitute, find_variable
from ..node import ExpressionLeaf as L, OP_INT, OP_INT_INDEF, OP_MUL, OP_DIV, \ from ..node import ExpressionLeaf as L, OP_INT, OP_INT_INDEF, OP_MUL, OP_DIV, \
OP_LOG, OP_SIN, OP_COS, Scope, sin, cos, ln, integral, indef, \ OP_LOG, OP_SIN, OP_COS, Scope, sin, cos, ln, integral, indef, \
absolute, OP_ADD absolute, OP_ADD, negate
from ..possibilities import Possibility as P, MESSAGES from ..possibilities import Possibility as P, MESSAGES
from ..translate import _ from ..translate import _
...@@ -188,7 +188,7 @@ def match_factor_out_constant(node): ...@@ -188,7 +188,7 @@ def match_factor_out_constant(node):
fx, x = node[:2] fx, x = node[:2]
if fx.negated: if fx.negated:
return [P(node, split_negation_to_constant)] return [P(node, factor_out_integral_negation)]
if not fx.is_op(OP_MUL): if not fx.is_op(OP_MUL):
return [] return []
...@@ -203,15 +203,15 @@ def match_factor_out_constant(node): ...@@ -203,15 +203,15 @@ def match_factor_out_constant(node):
return p return p
def split_negation_to_constant(root, args): def factor_out_integral_negation(root, args):
""" """
int -f(x) dx -> int -1 * f(x) dx # =>* -int f(x) dx int -f(x) dx -> int -1 * f(x) dx # =>* -int f(x) dx
""" """
return integral(-L(1) * root[0].reduce_negation(), *root[1:]) return -integral(root[0].reduce_negation(), *root[1:])
MESSAGES[split_negation_to_constant] = _('Write the negation of {0[0]} as an' \ MESSAGES[factor_out_integral_negation] = \
' explicit `-1` and bring it outside of the integral.') _('Bring the negation of {0[0]} outside of the integral.')
def factor_out_constant(root, args): def factor_out_constant(root, args):
...@@ -221,7 +221,7 @@ def factor_out_constant(root, args): ...@@ -221,7 +221,7 @@ def factor_out_constant(root, args):
scope, c = args scope, c = args
scope.remove(c) scope.remove(c)
return c * integral(scope.as_nary_node(), *root[1:]) return negate(c * integral(scope.as_nary_node(), *root[1:]), root.negated)
MESSAGES[factor_out_constant] = _('Factor out {2} from integral {0}.') MESSAGES[factor_out_constant] = _('Factor out {2} from integral {0}.')
...@@ -351,11 +351,11 @@ def sum_rule_integral(root, args): ...@@ -351,11 +351,11 @@ def sum_rule_integral(root, args):
int f(x) + g(x) dx -> int f(x) dx + int g(x) dx int f(x) + g(x) dx -> int f(x) dx + int g(x) dx
""" """
scope, f = args scope, f = args
x = root[1] args = root[1:]
scope.remove(f) scope.remove(f)
addition = integral(f, x) + integral(scope.as_nary_node(), x) addition = integral(f, *args) + integral(scope.as_nary_node(), *args)
return addition.negate(root.negated) return negate(addition, root.negated)
MESSAGES[sum_rule_integral] = _('Apply the sum rule to {0}.') MESSAGES[sum_rule_integral] = _('Apply the sum rule to {0}.')
......
...@@ -204,32 +204,47 @@ MESSAGES[raised_fraction] = _('Apply the exponent {2} to the nominator and' ...@@ -204,32 +204,47 @@ MESSAGES[raised_fraction] = _('Apply the exponent {2} to the nominator and'
' denominator of fraction {1}.') ' denominator of fraction {1}.')
def match_remove_negative_exponent(node): def match_remove_negative_child(node):
""" """
a ^ -p -> 1 / a ^ p a ^ -p -> 1 / a ^ p
(-a) ^ p and p is an odd number -> -a ^ p
""" """
assert node.is_op(OP_POW) assert node.is_op(OP_POW)
a, p = node a, p = node
pos = []
if p.negated: if p.negated:
return [P(node, remove_negative_exponent, (a, p))] pos.append(P(node, remove_negative_exponent))
return [] if a.negated and p.is_int() and p.value & 1:
pos.append(P(node, remove_negative_root))
return pos
def remove_negative_exponent(root, args): def remove_negative_exponent(root, args):
""" """
a^-p -> 1 / a^p a ^ -p -> 1 / a ^ p
""" """
a, p = args a, p = root
return L(1) / a ** p.reduce_negation() return L(1) / a ** p.reduce_negation()
MESSAGES[remove_negative_exponent] = _('Remove negative exponent {2}.') MESSAGES[remove_negative_exponent] = _('Remove negative exponent {2}.')
def remove_negative_root(root, args):
"""
(-a) ^ p and p is an odd number -> -a ^ p
"""
a, p = root
return -(a.reduce_negation() ** p)
MESSAGES[remove_negative_root] = _('Remove negative exponent {2}.')
def match_exponent_to_root(node): def match_exponent_to_root(node):
""" """
a^(1 / m) -> sqrt(a, m) a^(1 / m) -> sqrt(a, m)
......
...@@ -16,7 +16,7 @@ from src.rules.integrals import indef, choose_constant, solve_integral, \ ...@@ -16,7 +16,7 @@ from src.rules.integrals import indef, choose_constant, solve_integral, \
match_solve_indef, solve_indef, match_integrate_variable_power, \ match_solve_indef, solve_indef, match_integrate_variable_power, \
integrate_variable_root, integrate_variable_exponent, \ integrate_variable_root, integrate_variable_exponent, \
match_constant_integral, constant_integral, single_variable_integral, \ match_constant_integral, constant_integral, single_variable_integral, \
match_factor_out_constant, split_negation_to_constant, \ match_factor_out_constant, factor_out_integral_negation, \
factor_out_constant, match_division_integral, division_integral, \ factor_out_constant, match_division_integral, division_integral, \
extend_division_integral, match_function_integral, \ extend_division_integral, match_function_integral, \
logarithm_integral, sinus_integral, cosinus_integral, \ logarithm_integral, sinus_integral, cosinus_integral, \
...@@ -111,11 +111,11 @@ class TestRulesIntegrals(RulesTestCase): ...@@ -111,11 +111,11 @@ class TestRulesIntegrals(RulesTestCase):
root = tree('int -x2 dx') root = tree('int -x2 dx')
self.assertEqualPos(match_factor_out_constant(root), self.assertEqualPos(match_factor_out_constant(root),
[P(root, split_negation_to_constant)]) [P(root, factor_out_integral_negation)])
def test_split_negation_to_constant(self): def test_factor_out_integral_negation(self):
root, expect = tree('int -x ^ 2 dx, int (-1)x ^ 2 dx') root, expect = tree('int -x ^ 2 dx, -int x ^ 2 dx')
self.assertEqual(split_negation_to_constant(root, ()), expect) self.assertEqual(factor_out_integral_negation(root, ()), expect)
def test_factor_out_constant(self): def test_factor_out_constant(self):
root, expect = tree('int cx dx, c int x dx') root, expect = tree('int cx dx, c int x dx')
......
...@@ -17,8 +17,8 @@ from src.rules.powers import match_add_exponents, add_exponents, \ ...@@ -17,8 +17,8 @@ from src.rules.powers import match_add_exponents, add_exponents, \
match_multiply_exponents, multiply_exponents, \ match_multiply_exponents, multiply_exponents, \
match_duplicate_exponent, duplicate_exponent, \ match_duplicate_exponent, duplicate_exponent, \
match_raised_fraction, raised_fraction, \ match_raised_fraction, raised_fraction, \
match_remove_negative_exponent, remove_negative_exponent, \ match_remove_negative_child, remove_negative_exponent, \
match_exponent_to_root, exponent_to_root, \ remove_negative_root, match_exponent_to_root, exponent_to_root, \
match_constant_exponent, remove_power_of_zero, remove_power_of_one match_constant_exponent, remove_power_of_zero, remove_power_of_one
from src.node import Scope, ExpressionNode as N from src.node import Scope, ExpressionNode as N
from src.possibilities import Possibility as P from src.possibilities import Possibility as P
...@@ -122,13 +122,22 @@ class TestRulesPowers(RulesTestCase): ...@@ -122,13 +122,22 @@ class TestRulesPowers(RulesTestCase):
self.assertEqual(raised_fraction(root, (ab, p)), a ** p / b ** p) self.assertEqual(raised_fraction(root, (ab, p)), a ** p / b ** p)
def test_match_remove_negative_exponent(self): def test_match_remove_negative_child(self):
a, p = tree('a,p') root = tree('a ^ -p')
root = a ** -p self.assertEqualPos(match_remove_negative_child(root),
[P(root, remove_negative_exponent)])
possibilities = match_remove_negative_exponent(root) root = tree('(-a) ^ 3')
self.assertEqualPos(possibilities, self.assertEqualPos(match_remove_negative_child(root),
[P(root, remove_negative_exponent, (a, -p))]) [P(root, remove_negative_root)])
root = tree('(-a) ^ 2')
self.assertEqualPos(match_remove_negative_child(root), [])
root = tree('(-a) ^ -3')
self.assertEqualPos(match_remove_negative_child(root),
[P(root, remove_negative_exponent),
P(root, remove_negative_root)])
def test_match_exponent_to_root(self): def test_match_exponent_to_root(self):
a, n, m, l1 = tree('a,n,m,1') a, n, m, l1 = tree('a,n,m,1')
...@@ -178,10 +187,16 @@ class TestRulesPowers(RulesTestCase): ...@@ -178,10 +187,16 @@ class TestRulesPowers(RulesTestCase):
def test_remove_negative_exponent(self): def test_remove_negative_exponent(self):
a, p, l1 = tree('a,-p,1') a, p, l1 = tree('a,-p,1')
root = a ** p root = a ** p
self.assertEqualNodes(remove_negative_exponent(root, ()),
self.assertEqualNodes(remove_negative_exponent(root, (a, p)),
l1 / a ** +p) l1 / a ** +p)
def test_remove_negative_root(self):
root, expect = tree('(-a) ^ 3, -a ^ 3')
self.assertEqualNodes(remove_negative_root(root, ()), expect)
root, expect = tree('(-a) ^ -3, -a ^ -3')
self.assertEqualNodes(remove_negative_root(root, ()), expect)
def test_exponent_to_root(self): def test_exponent_to_root(self):
a, n, m, l1 = tree('a,n,m,1') a, n, m, l1 = tree('a,n,m,1')
root = a ** (n / m) root = a ** (n / m)
......
...@@ -40,3 +40,7 @@ class TestValidation(TestCase): ...@@ -40,3 +40,7 @@ class TestValidation(TestCase):
#def test_advanced_failure(self): #def test_advanced_failure(self):
# self.assertFalse(validate('(x-1)^3+(x-1)^3', '4a+4b')) # self.assertFalse(validate('(x-1)^3+(x-1)^3', '4a+4b'))
def test_sphere_volume(self):
self.assertTrue(validate('int_(-r)^(r) pi * (r^2 - x^2) dx',
'4 / 3 * pi * r ^ 3'))
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