Commit 68ce57a9 authored by Taddeus Kroes's avatar Taddeus Kroes

Added some rules for logarithms.

parent acbc9fb7
from ..node import OP_ADD, OP_MUL, OP_DIV, OP_POW, OP_NEG, OP_SIN, OP_COS, \
OP_TAN, OP_DER
OP_TAN, OP_DER, OP_LOG
from .groups import match_combine_groups
from .factors import match_expand
from .powers import match_add_exponents, match_subtract_exponents, \
......@@ -21,10 +21,13 @@ from src.rules.derivatives import match_zero_derivative, \
match_one_derivative, match_variable_power, \
match_const_deriv_multiplication, match_logarithmic, \
match_goniometric, match_sum_product_rule, match_quotient_rule
from src.rules.logarithmic import match_constant_logarithm, \
match_add_logarithms
RULES = {
OP_ADD: [match_add_numerics, match_add_constant_fractions,
match_combine_groups, match_add_quadrants],
match_combine_groups, match_add_quadrants,
match_add_logarithms],
OP_MUL: [match_multiply_numerics, match_expand, match_add_exponents,
match_expand_and_add_fractions, match_multiply_zero,
match_negated_factor, match_multiply_one,
......@@ -46,4 +49,5 @@ RULES = {
match_variable_power, match_const_deriv_multiplication,
match_logarithmic, match_goniometric, match_sum_product_rule,
match_quotient_rule],
OP_LOG: [match_constant_logarithm],
}
from ..node import ExpressionNode as N, ExpressionLeaf as L, OP_LOG, E
from itertools import combinations
from ..node import ExpressionNode as N, ExpressionLeaf as L, OP_LOG, E, \
OP_ADD, OP_MUL, Scope
from ..possibilities import Possibility as P, MESSAGES
from ..translate import _
......@@ -12,3 +15,140 @@ def log(exponent, base=10):
def ln(exponent):
return log(exponent, base=E)
def match_constant_logarithm(node):
"""
log_1(a) -> # raise ValueError for base 1
log(1) -> 0
log(a, a) -> log(a) / log(a) # -> 1
"""
# TODO: base and raised
assert node.is_op(OP_LOG)
raised, base = node
if base == 1:
raise ValueError('Logarithm with base 1 does not exist.')
p = []
if raised == 1:
p.append(P(node, logarithm_of_one))
if raised == base:
p.append(P(node, divide_same_base))
return p
def logarithm_of_one(root, args):
"""
log(1) -> 0
"""
raised, base = root
return log(raised) / log(base)
MESSAGES[logarithm_of_one] = _('Logarithm of one reduces to zero.')
def divide_same_base(root, args):
"""
log(a, b) -> log(a) / log(b)
"""
raised, base = root
return log(raised) / log(base)
MESSAGES[divide_same_base] = _('Apply log_b(a) -> log(a) / log(b).')
def match_add_logarithms(node):
"""
log(a) + log(b) -> log(ab)
-log(a) - log(b) -> -(log(a) + log(b)) # -> -log(ab)
log(a) - log(b) -> log(a / b)
-log(a) + log(b) -> log(b / a)
"""
assert node.is_op(OP_ADD)
p = []
scope = Scope(node)
logarithms = filter(lambda n: n.is_op(OP_LOG), scope)
for log_a, log_b in combinations(logarithms, 2):
# Compare base
if log_a[1] != log_b[1]:
continue
a_negated = log_a.negated == 1
b_negated = log_b.negated == 1
if not log_a.negated and not log_b.negated:
# log(a) + log(b) -> log(ab)
p.append(P(node, add_logarithms, (scope, log_a, log_b)))
elif a_negated and b_negated:
# -log(a) - log(b) -> -(log(a) + log(b))
p.append(P(node, expand_negations, (scope, log_a, log_b)))
elif not log_a.negated and b_negated:
# log(a) - log(b) -> log(a / b)
p.append(P(node, subtract_logarithms, (scope, log_a, log_b)))
elif a_negated and not log_b.negated:
# -log(a) + log(b) -> log(b / a)
p.append(P(node, subtract_logarithms, (scope, log_b, log_a)))
return p
def add_logarithms(root, args):
"""
log(a) + log(b) -> log(ab)
"""
scope, log_a, log_b = args
a, base = log_a
b = log_b[0]
scope.replace(log_a, log(a * b, base=base))
scope.remove(log_b)
return scope.as_nary_node()
MESSAGES[add_logarithms] = _('Apply log(a) + log(b) = log(ab).')
#_('Combine two logarithms with the same base: {2} and {3}.')
def expand_negations(root, args):
"""
-log(a) - log(b) -> -(log(a) + log(b)) # -> -log(ab)
"""
scope, log_a, log_b = args
scope.replace(log_a, -(+log_a + +log_b))
scope.remove(log_b)
return scope.as_nary_node()
MESSAGES[expand_negations] = \
_('Apply -log(a) - log(b) = -(log(a) + log(b)).')
def subtract_logarithms(root, args):
"""
log(a) - log(b) -> log(a / b)
"""
scope, log_a, log_b = args
a, base = log_a
b = log_b[0]
scope.replace(log_a, log(a / b, base=base))
scope.remove(log_b)
return scope.as_nary_node()
MESSAGES[subtract_logarithms] = _('Apply log(a) - log(b) = log(a / b).')
from src.rules.logarithmic import log, ln, match_constant_logarithm, \
logarithm_of_one, divide_same_base, match_add_logarithms, \
add_logarithms, expand_negations, subtract_logarithms
from src.node import Scope
from src.possibilities import Possibility as P
from tests.rulestestcase import RulesTestCase, tree
class TestRulesLogarithmic(RulesTestCase):
def test_match_constant_logarithm(self):
self.assertRaises(ValueError, tree, 'log_1(a)')
root = tree('log 1')
self.assertEqualPos(match_constant_logarithm(root),
[P(root, logarithm_of_one)])
root = tree('log 10')
self.assertEqualPos(match_constant_logarithm(root),
[P(root, divide_same_base)])
root = tree('log(a, a)')
self.assertEqualPos(match_constant_logarithm(root),
[P(root, divide_same_base)])
def test_match_add_logarithms(self):
log_a, log_b = root = tree('log a + log b')
self.assertEqualPos(match_add_logarithms(root),
[P(root, add_logarithms, (Scope(root), log_a, log_b))])
log_a, log_b = root = tree('-log a - log b')
self.assertEqualPos(match_add_logarithms(root),
[P(root, expand_negations, (Scope(root), log_a, log_b))])
log_a, log_b = root = tree('log a - log b')
self.assertEqualPos(match_add_logarithms(root),
[P(root, subtract_logarithms, (Scope(root), log_a, log_b))])
log_a, log_b = root = tree('-log a + log b')
self.assertEqualPos(match_add_logarithms(root),
[P(root, subtract_logarithms, (Scope(root), log_b, log_a))])
def test_add_logarithms(self):
root, a, b = tree('log a + log b, a, b')
log_a, log_b = root
self.assertEqual(add_logarithms(root, (Scope(root), log_a, log_b)),
log(a * b))
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