Commit 59de7640 authored by Taddeus Kroes's avatar Taddeus Kroes

Added more logarithmic rules.

parent a9885848
...@@ -22,7 +22,8 @@ from src.rules.derivatives import match_zero_derivative, \ ...@@ -22,7 +22,8 @@ from src.rules.derivatives import match_zero_derivative, \
match_const_deriv_multiplication, match_logarithmic, \ match_const_deriv_multiplication, match_logarithmic, \
match_goniometric, match_sum_product_rule, match_quotient_rule match_goniometric, match_sum_product_rule, match_quotient_rule
from src.rules.logarithmic import match_constant_logarithm, \ from src.rules.logarithmic import match_constant_logarithm, \
match_add_logarithms, match_raised_base match_add_logarithms, match_raised_base, match_factor_out_exponent, \
match_factor_in_multiplicant
RULES = { RULES = {
OP_ADD: [match_add_numerics, match_add_constant_fractions, OP_ADD: [match_add_numerics, match_add_constant_fractions,
...@@ -31,7 +32,8 @@ RULES = { ...@@ -31,7 +32,8 @@ RULES = {
OP_MUL: [match_multiply_numerics, match_expand, match_add_exponents, OP_MUL: [match_multiply_numerics, match_expand, match_add_exponents,
match_expand_and_add_fractions, match_multiply_zero, match_expand_and_add_fractions, match_multiply_zero,
match_negated_factor, match_multiply_one, match_negated_factor, match_multiply_one,
match_sort_multiplicants, match_multiply_fractions], match_sort_multiplicants, match_multiply_fractions,
match_factor_in_multiplicant],
OP_DIV: [match_subtract_exponents, match_divide_numerics, OP_DIV: [match_subtract_exponents, match_divide_numerics,
match_constant_division, match_divide_fractions, match_constant_division, match_divide_fractions,
match_negated_division, match_equal_fraction_parts], match_negated_division, match_equal_fraction_parts],
...@@ -50,5 +52,5 @@ RULES = { ...@@ -50,5 +52,5 @@ RULES = {
match_variable_power, match_const_deriv_multiplication, match_variable_power, match_const_deriv_multiplication,
match_logarithmic, match_goniometric, match_sum_product_rule, match_logarithmic, match_goniometric, match_sum_product_rule,
match_quotient_rule], match_quotient_rule],
OP_LOG: [match_constant_logarithm], OP_LOG: [match_constant_logarithm, match_factor_out_exponent],
} }
from itertools import combinations from itertools import combinations, product
from .utils import find_variables
from ..node import ExpressionNode as N, ExpressionLeaf as L, OP_LOG, E, \ from ..node import ExpressionNode as N, ExpressionLeaf as L, OP_LOG, E, \
OP_ADD, OP_MUL, OP_POW, Scope OP_ADD, OP_MUL, OP_POW, Scope
from ..possibilities import Possibility as P, MESSAGES from ..possibilities import Possibility as P, MESSAGES
...@@ -21,9 +22,9 @@ def match_constant_logarithm(node): ...@@ -21,9 +22,9 @@ def match_constant_logarithm(node):
""" """
log_1(a) -> # raise ValueError for base 1 log_1(a) -> # raise ValueError for base 1
log(1) -> 0 log(1) -> 0
log(a, a) -> 1 # Explicit possibility to prevent cycles
log(a, a) -> log(a) / log(a) # -> 1 log(a, a) -> log(a) / log(a) # -> 1
""" """
# TODO: base and raised
assert node.is_op(OP_LOG) assert node.is_op(OP_LOG)
raised, base = node raised, base = node
...@@ -38,7 +39,10 @@ def match_constant_logarithm(node): ...@@ -38,7 +39,10 @@ def match_constant_logarithm(node):
p.append(P(node, logarithm_of_one)) p.append(P(node, logarithm_of_one))
if raised == base: if raised == base:
# log(a, a) -> 1
p.append(P(node, base_equals_raised))
# log(a, a) -> log(a) / log(a) # -> 1 # log(a, a) -> log(a) / log(a) # -> 1
# TODO: When to do this except for this case?
p.append(P(node, divide_same_base)) p.append(P(node, divide_same_base))
return p return p
...@@ -50,12 +54,22 @@ def logarithm_of_one(root, args): ...@@ -50,12 +54,22 @@ def logarithm_of_one(root, args):
""" """
raised, base = root raised, base = root
return L(0) return L(0).negate(root.negated)
MESSAGES[logarithm_of_one] = _('Logarithm of one reduces to zero.') MESSAGES[logarithm_of_one] = _('Logarithm of one reduces to zero.')
def base_equals_raised(root, args):
"""
log(a, a) -> 1
"""
return L(1).negate(root.negated)
MESSAGES[base_equals_raised] = _('Logarithm {0} recuces to 1.')
def divide_same_base(root, args): def divide_same_base(root, args):
""" """
log(a, b) -> log(a) / log(b) log(a, b) -> log(a) / log(b)
...@@ -120,7 +134,7 @@ def add_logarithms(root, args): ...@@ -120,7 +134,7 @@ def add_logarithms(root, args):
MESSAGES[add_logarithms] = _('Apply log(a) + log(b) = log(ab).') MESSAGES[add_logarithms] = _('Apply log(a) + log(b) = log(ab).')
#_('Combine two logarithms with the same base: {2} and {3}.') #_('Combine logarithms with the same base: {2} and {3}.')
def expand_negations(root, args): def expand_negations(root, args):
...@@ -178,3 +192,87 @@ def raised_base(root, args): ...@@ -178,3 +192,87 @@ def raised_base(root, args):
MESSAGES[raised_base] = _('Apply g ^ log_g(a) = a on {0}.') MESSAGES[raised_base] = _('Apply g ^ log_g(a) = a on {0}.')
def match_factor_out_exponent(node):
"""
This match simplifies a power with a variable in it to a multiplication:
log(a ^ b) -> blog(a)
log(a ^ -b) -> log((a ^ b) ^ -1) # =>* -log(a ^ b)
"""
assert node.is_op(OP_LOG)
p = []
if node[0].is_power():
a, b = node[0]
if b.negated:
p.append(P(node, split_negative_exponent))
p.append(P(node, factor_out_exponent))
return p
def split_negative_exponent(root, args):
"""
log(a ^ -b) -> log((a ^ b) ^ -1) # =>* -log(a ^ b)
"""
(a, b), base = root
return log((a ** +b) ** -L(1), base=base)
MESSAGES[split_negative_exponent] = \
_('Split and factor out the negative exponent within logarithm {0}.')
def factor_out_exponent(root, args):
"""
log(a ^ b) -> blog(a)
"""
(a, b), base = root
return b * log(a, base=base)
MESSAGES[factor_out_exponent] = _('Factor out exponent {0[0][0]} from {0}.')
def match_factor_in_multiplicant(node):
"""
Only bring a multiplicant inside a logarithms if both the multiplicant and
the logaritm's content are constants. This will yield a new simplification
of constants inside the logarithm.
2log(2) -> log(2 ^ 2) # -> log(4)
2log(2 / 4) -> log((2 / 4) ^ 2) # =>* log(1 / 4)
"""
assert node.is_op(OP_MUL)
scope = Scope(node)
constants = filter(lambda n: n.is_int(), node)
logarithms = filter(lambda n: n.is_op(OP_LOG) \
and not len(find_variables(n)), node)
p = []
for constant, logarithm in product(constants, logarithms):
p.append(P(node, factor_in_multiplicant, (scope, constant, logarithm)))
return p
def factor_in_multiplicant(root, args):
"""
alog(b) -> log(b ^ a)
"""
scope, a, log_b = args
b, base = log_b
scope.replace(a, log(b ** a, base=base))
scope.remove(log_b)
return scope.as_nary_node()
MESSAGES[factor_in_multiplicant] = \
_('Bring multiplicant {2} into {3} as the exponent of {3[0]}.')
...@@ -232,7 +232,6 @@ class TestRulesDerivatives(RulesTestCase): ...@@ -232,7 +232,6 @@ class TestRulesDerivatives(RulesTestCase):
self.assertRewrite([ self.assertRewrite([
'der(e ^ x)', 'der(e ^ x)',
'e ^ x * ln(e)', 'e ^ x * ln(e)',
'e ^ x * (log(e) / log(e))',
'e ^ x * 1', 'e ^ x * 1',
'e ^ x', 'e ^ x',
]) ])
from src.rules.logarithmic import log, ln, match_constant_logarithm, \ from src.rules.logarithmic import log, ln, match_constant_logarithm, \
logarithm_of_one, divide_same_base, match_add_logarithms, \ base_equals_raised, logarithm_of_one, divide_same_base, \
add_logarithms, expand_negations, subtract_logarithms, \ match_add_logarithms, add_logarithms, expand_negations, \
match_raised_base, raised_base subtract_logarithms, match_raised_base, raised_base, \
match_factor_out_exponent, split_negative_exponent, \
factor_out_exponent, match_factor_in_multiplicant, \
factor_in_multiplicant
from src.node import Scope 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
...@@ -18,11 +21,13 @@ class TestRulesLogarithmic(RulesTestCase): ...@@ -18,11 +21,13 @@ class TestRulesLogarithmic(RulesTestCase):
root = tree('log 10') root = tree('log 10')
self.assertEqualPos(match_constant_logarithm(root), self.assertEqualPos(match_constant_logarithm(root),
[P(root, divide_same_base)]) [P(root, base_equals_raised),
P(root, divide_same_base)])
root = tree('log(a, a)') root = tree('log(a, a)')
self.assertEqualPos(match_constant_logarithm(root), self.assertEqualPos(match_constant_logarithm(root),
[P(root, divide_same_base)]) [P(root, base_equals_raised),
P(root, divide_same_base)])
def test_logarithm_of_one(self): def test_logarithm_of_one(self):
root = tree('log 1') root = tree('log 1')
...@@ -90,3 +95,38 @@ class TestRulesLogarithmic(RulesTestCase): ...@@ -90,3 +95,38 @@ class TestRulesLogarithmic(RulesTestCase):
def test_raised_base(self): def test_raised_base(self):
root, a = tree('2 ^ log_2(a), a') root, a = tree('2 ^ log_2(a), a')
self.assertEqual(raised_base(root, (root[1][0],)), a) self.assertEqual(raised_base(root, (root[1][0],)), a)
def test_match_factor_out_exponent(self):
for root in tree('log(a ^ 2), log(2 ^ a), log(a ^ a), log(2 ^ 2)'):
self.assertEqualPos(match_factor_out_exponent(root),
[P(root, factor_out_exponent)])
root = tree('log(a ^ -b)')
self.assertEqualPos(match_factor_out_exponent(root),
[P(root, split_negative_exponent),
P(root, factor_out_exponent)])
def test_split_negative_exponent(self):
root, expect = tree('log(a ^ -b), log((a ^ b) ^ -1)')
self.assertEqual(split_negative_exponent(root, ()), expect)
def test_factor_out_exponent(self):
((a, l2), l10) = root = tree('log(a ^ 2)')
self.assertEqual(factor_out_exponent(root, ()), l2 * log(a))
def test_match_factor_in_multiplicant(self):
root, log_3 = tree('2log(3), log(3)')
self.assertEqualPos(match_factor_in_multiplicant(root),
[P(root, factor_in_multiplicant, (Scope(root), 2, log_3))])
root = tree('2log(a)')
self.assertEqualPos(match_factor_in_multiplicant(root), [])
root = tree('alog(3)')
self.assertEqualPos(match_factor_in_multiplicant(root), [])
def test_factor_in_multiplicant(self):
root, expect = tree('2log(3), log(3 ^ 2)')
l2, log3 = root
self.assertEqual(factor_in_multiplicant(root, (Scope(root), l2, log3)),
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