فهرست منبع

Added rules and chain test for solving der(x ^ x) (among others).

Taddeus Kroes 14 سال پیش
والد
کامیت
0a006952ae
5فایلهای تغییر یافته به همراه84 افزوده شده و 16 حذف شده
  1. 23 6
      src/rules/derivatives.py
  2. 24 5
      src/rules/logarithmic.py
  3. 2 1
      src/strategy.py
  4. 28 2
      tests/test_rules_derivatives.py
  5. 7 2
      tests/test_rules_logarithmic.py

+ 23 - 6
src/rules/derivatives.py

@@ -2,7 +2,7 @@ from .utils import find_variables, first_sorted_variable
 from .logarithmic import ln
 from .goniometry import sin, cos
 from ..node import ExpressionNode as N, ExpressionLeaf as L, Scope, OP_DER, \
-        OP_MUL, OP_LOG, OP_SIN, OP_COS, OP_TAN, OP_ADD, OP_DIV
+        OP_MUL, OP_LOG, OP_SIN, OP_COS, OP_TAN, OP_ADD, OP_DIV, E
 from ..possibilities import Possibility as P, MESSAGES
 from ..translate import _
 
@@ -142,9 +142,10 @@ MESSAGES[const_deriv_multiplication] = \
 
 def match_variable_power(node):
     """
-    der(x ^ n)     ->  n * x ^ (n - 1)
-    der(x ^ n, x)  ->  n * x ^ (n - 1)
-    der(f(x) ^ n)  ->  n * f(x) ^ (n - 1) * der(f(x))  # Chain rule
+    der(x ^ n)           ->  n * x ^ (n - 1)
+    der(x ^ n, x)        ->  n * x ^ (n - 1)
+    der(f(x) ^ n)        ->  n * f(x) ^ (n - 1) * der(f(x))  # Chain rule
+    der(f(x) ^ g(x), x)  ->  der(e ^ ln(f(x) ^ g(x)), x)
     """
     assert node.is_op(OP_DER)
 
@@ -157,13 +158,16 @@ def match_variable_power(node):
     evars = find_variables(exponent)
     x = get_derivation_variable(node, rvars | evars)
 
-    if x in rvars and x not in evars:
+    if x in rvars and x in evars:
+        return [P(node, power_rule)]
+
+    if x in rvars:
         if root.is_variable():
             return [P(node, variable_root)]
 
         return [P(node, chain_rule, (root, variable_root, ()))]
 
-    if not x in rvars and x in evars:
+    if x in evars:
         if exponent.is_variable():
             return [P(node, variable_exponent)]
 
@@ -172,6 +176,19 @@ def match_variable_power(node):
     return []
 
 
+def power_rule(root, args):
+    """
+    [f(x) ^ g(x)]'  ->  [e ^ ln(f(x) ^ g(x))]'
+    """
+    x = second_arg(root)
+
+    return der(L(E) ** ln(root[0]), x)
+
+
+MESSAGES[power_rule] = \
+        _('Write {0} as a logarithm to be able to separate root and exponent.')
+
+
 def variable_root(root, args):
     """
     der(x ^ n, x)  ->  n * x ^ (n - 1)

+ 24 - 5
src/rules/logarithmic.py

@@ -1,6 +1,6 @@
 from itertools import combinations, product
 
-from .utils import find_variables
+from .utils import find_variables, partition
 from ..node import ExpressionNode as N, ExpressionLeaf as L, OP_LOG, E, \
         OP_ADD, OP_MUL, OP_POW, Scope
 from ..possibilities import Possibility as P, MESSAGES
@@ -172,7 +172,8 @@ MESSAGES[subtract_logarithms] = _('Apply log(a) - log(b) = log(a / b).')
 
 def match_raised_base(node):
     """
-    g ^ log_g(a)  ->  a
+    g ^ log_g(a)     ->  a
+    g ^ (blog_g(a))  ->  g ^ log_g(a ^ b)
     """
     assert node.is_op(OP_POW)
 
@@ -181,7 +182,25 @@ def match_raised_base(node):
     if exponent.is_op(OP_LOG) and exponent[1] == root:
         return [P(node, raised_base, (exponent[0],))]
 
-    return []
+    p = []
+
+    if exponent.is_op(OP_MUL):
+        scope = Scope(exponent)
+        is_matching_logarithm = lambda n: n.is_op(OP_LOG) and n[1] == root
+        logs, others = partition(is_matching_logarithm, scope)
+
+        for other, log in product(others, logs):
+            # TODO: Give this function a high precedence
+            p.append(P(node, factor_in_exponent_multiplicant,
+                       (scope, other, log)))
+
+    return p
+
+
+def factor_in_exponent_multiplicant(root, args):
+    r, e = root
+
+    return r ** factor_in_multiplicant(e, args)
 
 
 def raised_base(root, args):
@@ -251,9 +270,9 @@ def match_factor_in_multiplicant(node):
     assert node.is_op(OP_MUL)
 
     scope = Scope(node)
-    constants = filter(lambda n: n.is_int(), node)
+    constants = filter(lambda n: n.is_int(), scope)
     logarithms = filter(lambda n: n.is_op(OP_LOG) \
-                                  and not len(find_variables(n)), node)
+                                  and not len(find_variables(n)), scope)
     p = []
 
     for constant, logarithm in product(constants, logarithms):

+ 2 - 1
src/strategy.py

@@ -1,5 +1,6 @@
 from rules.sort import move_constant
 from rules.numerics import reduce_fraction_constants, fraction_to_int_fraction
+from rules.logarithmic import factor_in_exponent_multiplicant
 
 
 def pick_suggestion(possibilities):
@@ -9,7 +10,7 @@ def pick_suggestion(possibilities):
     # TODO: pick the best suggestion.
     for suggestion, p in enumerate(possibilities + [None]):
         if p and p.handler not in [move_constant, fraction_to_int_fraction,
-                reduce_fraction_constants]:
+                reduce_fraction_constants, factor_in_exponent_multiplicant]:
             break
 
     if not p:

+ 28 - 2
tests/test_rules_derivatives.py

@@ -5,7 +5,7 @@ from src.rules.derivatives import der, get_derivation_variable, \
         const_deriv_multiplication, chain_rule, match_logarithmic, \
         logarithmic, match_goniometric, sinus, cosinus, tangens, \
         match_sum_product_rule, sum_rule, product_rule, match_quotient_rule, \
-        quotient_rule
+        quotient_rule, power_rule
 from src.rules.logarithmic import ln
 from src.rules.goniometry import sin, cos
 from src.node import Scope
@@ -94,7 +94,33 @@ class TestRulesDerivatives(RulesTestCase):
         # Below is not mathematically underivable, it's just not within the
         # scope of our program
         root, x = tree('der(x ^ x), x')
-        self.assertEqualPos(match_variable_power(root), [])
+        self.assertEqualPos(match_variable_power(root),
+                [P(root, power_rule)])
+
+    def test_power_rule(self):
+        root, expect = tree("[x ^ x]', [e ^ ln(x ^ x)]'")
+        self.assertEqual(power_rule(root, ()), expect)
+
+    def test_power_rule_chain(self):
+        self.assertRewrite([
+            "[x ^ x]'",
+            "[e ^ ln(x ^ x)]'",
+            "[e ^ (xln(x))]'",
+            "e ^ (xln(x))ln(e)[xln(x)]'",
+            "e ^ (xln(x))1[xln(x)]'",
+            "e ^ (xln(x))[xln(x)]'",
+            "e ^ (xln(x))([x]' * ln(x) + x[ln(x)]')",
+            "e ^ (xln(x))(1ln(x) + x[ln(x)]')",
+            "e ^ (xln(x))(ln(x) + x[ln(x)]')",
+            "e ^ (xln(x))(ln(x) + x(1 / (xln(e))))",
+            "e ^ (xln(x))(ln(x) + x(1 / (x * 1)))",
+            "e ^ (xln(x))(ln(x) + x(1 / x))",
+            "e ^ (xln(x))(ln(x) + x * 1 / x)",
+            "e ^ (xln(x))(ln(x) + x / x)",
+            "e ^ (xln(x))(ln(x) + 1)",
+            "e ^ ln(x ^ x)(ln(x) + 1)",
+            # FIXME: "x ^ x(ln(x) + 1)",  ->  needs strategy
+        ])
 
     def test_variable_root(self):
         root = tree('der(x ^ 2)')

+ 7 - 2
tests/test_rules_logarithmic.py

@@ -115,9 +115,14 @@ class TestRulesLogarithmic(RulesTestCase):
         self.assertEqual(factor_out_exponent(root, ()), l2 * log(a))
 
     def test_match_factor_in_multiplicant(self):
-        root, log_3 = tree('2log(3), log(3)')
+        (l2, log_3) = root = tree('2log(3)')
         self.assertEqualPos(match_factor_in_multiplicant(root),
-                [P(root, factor_in_multiplicant, (Scope(root), 2, log_3))])
+                [P(root, factor_in_multiplicant, (Scope(root), l2, log_3))])
+
+        (l2, log_3), l4 = root = tree('2log(3)4')
+        self.assertEqualPos(match_factor_in_multiplicant(root),
+                [P(root, factor_in_multiplicant, (Scope(root), l2, log_3)),
+                 P(root, factor_in_multiplicant, (Scope(root), l4, log_3))])
 
         root = tree('2log(a)')
         self.assertEqualPos(match_factor_in_multiplicant(root), [])