Commit 76db9913 authored by Taddeus Kroes's avatar Taddeus Kroes

Added rules that calculate logarithm exponents to see if a logarithm can be reduced to a number.

parent a0a12318
from itertools import combinations, product, ifilterfalse from itertools import combinations, product, ifilterfalse
import math
from .utils import find_variables, partition, divides, is_numeric_node from .utils import find_variables, partition, divides, is_numeric_node
from ..node import ExpressionLeaf as L, OP_LOG, OP_ADD, OP_MUL, OP_POW, \ from ..node import ExpressionLeaf as L, OP_LOG, OP_ADD, OP_MUL, OP_POW, \
...@@ -219,18 +220,29 @@ def match_factor_out_exponent(node): ...@@ -219,18 +220,29 @@ def match_factor_out_exponent(node):
This match simplifies a power with a variable in it to a multiplication: This match simplifies a power with a variable in it to a multiplication:
log(a ^ b) -> blog(a) log(a ^ b) -> blog(a)
log(a ^ -b) -> log((a ^ b) ^ -1) # =>* -log(a ^ b) log(a ^ -b) -> log((a ^ b) ^ -1) # =>* -log(a ^ b)
log(b, a) and a ** y = b with y in Z -> log(a ^ y, a) # =>* y
""" """
assert node.is_op(OP_LOG) assert node.is_op(OP_LOG)
p = [] p = []
exp, base = node
if node[0].is_power(): if exp.is_power():
a, b = node[0] a, b = exp
if b.negated: if b.negated:
p.append(P(node, split_negative_exponent)) p.append(P(node, split_negative_exponent))
if a == base:
p.append(P(node, factor_out_exponent_important))
else:
p.append(P(node, factor_out_exponent)) p.append(P(node, factor_out_exponent))
elif exp.is_numeric() and not exp.negated:
b, a = exp.value, base.value
y = int(round(math.log(b, a)))
if b == a ** y:
p.append(P(node, make_raised_base, (y,)))
return p return p
...@@ -257,7 +269,27 @@ def factor_out_exponent(root, args): ...@@ -257,7 +269,27 @@ def factor_out_exponent(root, args):
return b * log(a, base=base) return b * log(a, base=base)
MESSAGES[factor_out_exponent] = _('Factor out exponent {0[0][0]} from {0}.') MESSAGES[factor_out_exponent] = _('Factor out exponent {0[0][1]} from {0}.')
def factor_out_exponent_important(root, args):
return factor_out_exponent(root, args)
MESSAGES[factor_out_exponent_important] = MESSAGES[factor_out_exponent]
def make_raised_base(root, args):
"""
log(b, a) and b ** y = a with y in Z -> log(a ^ y, a) # =>* y
"""
exp, base = root
y = L(args[0])
return log(base.clone() ** y, base=base).negate(root.negated)
MESSAGES[make_raised_base] = _('Write {0[0]} as a power of {0[1]}.')
def match_factor_in_multiplicant(node): def match_factor_in_multiplicant(node):
......
from .factors import expand_double, expand_single from .factors import expand_double, expand_single
from .sort import move_constant from .sort import move_constant
from .numerics import reduce_fraction_constants from .numerics import reduce_fraction_constants, raise_numerics
from .logarithmic import factor_in_exponent_multiplicant, \ from .logarithmic import factor_in_exponent_multiplicant, \
factor_out_exponent, raised_base factor_out_exponent, raised_base, factor_out_exponent_important
from .derivatives import chain_rule from .derivatives import chain_rule
from .negation import double_negation, negated_factor, negated_nominator, \ from .negation import double_negation, negated_factor, negated_nominator, \
negated_denominator negated_denominator
...@@ -35,6 +35,8 @@ RELATIVE = [ ...@@ -35,6 +35,8 @@ RELATIVE = [
# Expand 'single' before 'double' to avoid unnessecary complexity # Expand 'single' before 'double' to avoid unnessecary complexity
(expand_single, expand_double), (expand_single, expand_double),
(factor_out_exponent_important, raise_numerics),
] ]
......
...@@ -6,7 +6,8 @@ from src.rules.logarithmic import log, match_constant_logarithm, \ ...@@ -6,7 +6,8 @@ from src.rules.logarithmic import log, match_constant_logarithm, \
factor_out_exponent, match_factor_in_multiplicant, \ factor_out_exponent, match_factor_in_multiplicant, \
factor_in_multiplicant, match_expand_terms, \ factor_in_multiplicant, match_expand_terms, \
expand_multiplication_terms, expand_division_terms, \ expand_multiplication_terms, expand_division_terms, \
factor_in_exponent_multiplicant factor_in_exponent_multiplicant, factor_out_exponent_important, \
make_raised_base
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
...@@ -140,6 +141,27 @@ class TestRulesLogarithmic(RulesTestCase): ...@@ -140,6 +141,27 @@ class TestRulesLogarithmic(RulesTestCase):
[P(root, split_negative_exponent), [P(root, split_negative_exponent),
P(root, factor_out_exponent)]) P(root, factor_out_exponent)])
def test_match_factor_out_exponent_important(self):
root = tree('log(10 ^ 2)')
self.assertEqualPos(match_factor_out_exponent(root),
[P(root, factor_out_exponent_important)])
def test_match_factor_out_exponent_make_raised_base(self):
root = tree('log(100)')
self.assertEqualPos(match_factor_out_exponent(root),
[P(root, make_raised_base, (2,))])
root = tree('log(1000)')
self.assertEqualPos(match_factor_out_exponent(root),
[P(root, make_raised_base, (3,))])
root = tree('log_2(16)')
self.assertEqualPos(match_factor_out_exponent(root),
[P(root, make_raised_base, (4,))])
root = tree('log(99)')
self.assertEqualPos(match_factor_out_exponent(root), [])
def test_split_negative_exponent(self): def test_split_negative_exponent(self):
root, expect = tree('log(a ^ -b), log((a ^ b) ^ -1)') root, expect = tree('log(a ^ -b), log((a ^ b) ^ -1)')
self.assertEqual(split_negative_exponent(root, ()), expect) self.assertEqual(split_negative_exponent(root, ()), expect)
...@@ -148,6 +170,17 @@ class TestRulesLogarithmic(RulesTestCase): ...@@ -148,6 +170,17 @@ class TestRulesLogarithmic(RulesTestCase):
((a, l2), l10) = root = tree('log(a ^ 2)') ((a, l2), l10) = root = tree('log(a ^ 2)')
self.assertEqual(factor_out_exponent(root, ()), l2 * log(a)) self.assertEqual(factor_out_exponent(root, ()), l2 * log(a))
def test_make_raised_base(self):
root, expect = tree('log(1000), log(10 ^ 3)')
self.assertEqual(make_raised_base(root, (3,)), expect)
root, expect = tree('log_2(64), log_2(2 ^ 4)')
self.assertEqual(make_raised_base(root, (4,)), expect)
def test_factor_out_exponent_important(self):
((a, l2), l10) = root = tree('log(10 ^ 2)')
self.assertEqual(factor_out_exponent_important(root, ()), l2 * log(a))
def test_match_factor_in_multiplicant(self): def test_match_factor_in_multiplicant(self):
(l2, log_3) = root = tree('2log(3)') (l2, log_3) = root = tree('2log(3)')
self.assertEqualPos(match_factor_in_multiplicant(root), self.assertEqualPos(match_factor_in_multiplicant(root),
......
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