Skip to content
Snippets Groups Projects
Commit 68ce57a9 authored by Taddeus Kroes's avatar Taddeus Kroes
Browse files

Added some rules for logarithms.

parent acbc9fb7
No related branches found
No related tags found
No related merge requests found
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_TAN, OP_DER, OP_LOG
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, \
...@@ -21,10 +21,13 @@ from src.rules.derivatives import match_zero_derivative, \ ...@@ -21,10 +21,13 @@ from src.rules.derivatives import match_zero_derivative, \
match_one_derivative, match_variable_power, \ match_one_derivative, match_variable_power, \
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, \
match_add_logarithms
RULES = { RULES = {
OP_ADD: [match_add_numerics, match_add_constant_fractions, 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, 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,
...@@ -46,4 +49,5 @@ RULES = { ...@@ -46,4 +49,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],
} }
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 ..possibilities import Possibility as P, MESSAGES
from ..translate import _ from ..translate import _
...@@ -12,3 +15,140 @@ def log(exponent, base=10): ...@@ -12,3 +15,140 @@ def log(exponent, base=10):
def ln(exponent): def ln(exponent):
return log(exponent, base=E) 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))
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment