|
@@ -1,7 +1,9 @@
|
|
|
from itertools import combinations
|
|
from itertools import combinations
|
|
|
|
|
|
|
|
from .utils import find_variables
|
|
from .utils import find_variables
|
|
|
-from ..node import Scope, OP_DERIV, ExpressionNode as N, ExpressionLeaf as L
|
|
|
|
|
|
|
+from .logarithmic import ln
|
|
|
|
|
+from ..node import ExpressionNode as N, ExpressionLeaf as L, Scope, OP_DERIV, \
|
|
|
|
|
+ OP_MUL
|
|
|
from ..possibilities import Possibility as P, MESSAGES
|
|
from ..possibilities import Possibility as P, MESSAGES
|
|
|
from ..translate import _
|
|
from ..translate import _
|
|
|
|
|
|
|
@@ -36,23 +38,48 @@ def get_derivation_variable(node, variables=None):
|
|
|
return list(variables)[0]
|
|
return list(variables)[0]
|
|
|
|
|
|
|
|
|
|
|
|
|
-def match_constant_derivative(node):
|
|
|
|
|
|
|
+def chain_rule(root, args):
|
|
|
"""
|
|
"""
|
|
|
- der(x) -> 1
|
|
|
|
|
- der(x, x) -> 1
|
|
|
|
|
- der(x, y) -> x
|
|
|
|
|
|
|
+ Apply the chain rule:
|
|
|
|
|
+ [f(g(x)]' -> f'(g(x)) * g'(x)
|
|
|
|
|
+
|
|
|
|
|
+ f'(g(x)) is not expressable in the current syntax, so calculate it directly
|
|
|
|
|
+ using the application function in the arguments. g'(x) is simply expressed
|
|
|
|
|
+ as der(g(x), x).
|
|
|
|
|
+ """
|
|
|
|
|
+ g, f_deriv, f_deriv_args = args
|
|
|
|
|
+ x = root[1] if len(root) > 1 else None
|
|
|
|
|
+
|
|
|
|
|
+ return f_deriv(root, f_deriv_args) * der(g, x)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def match_zero_derivative(node):
|
|
|
|
|
+ """
|
|
|
|
|
+ der(x, y) -> 0
|
|
|
der(n) -> 0
|
|
der(n) -> 0
|
|
|
"""
|
|
"""
|
|
|
assert node.is_op(OP_DERIV)
|
|
assert node.is_op(OP_DERIV)
|
|
|
|
|
|
|
|
variables = find_variables(node[0])
|
|
variables = find_variables(node[0])
|
|
|
- var = get_derivation_variable(node, variables=variables)
|
|
|
|
|
|
|
+ var = get_derivation_variable(node, variables)
|
|
|
|
|
|
|
|
if not var or var not in variables:
|
|
if not var or var not in variables:
|
|
|
- return [P(node, zero_derivative, ())]
|
|
|
|
|
|
|
+ return [P(node, zero_derivative)]
|
|
|
|
|
+
|
|
|
|
|
+ return []
|
|
|
|
|
+
|
|
|
|
|
|
|
|
- if (node[0] == node[1] if len(node) > 1 else node[0].is_variable()):
|
|
|
|
|
- return [P(node, one_derivative, ())]
|
|
|
|
|
|
|
+def match_one_derivative(node):
|
|
|
|
|
+ """
|
|
|
|
|
+ der(x) -> 1 # Implicit x
|
|
|
|
|
+ der(x, x) -> 1 # Explicit x
|
|
|
|
|
+ """
|
|
|
|
|
+ assert node.is_op(OP_DERIV)
|
|
|
|
|
+
|
|
|
|
|
+ var = get_derivation_variable(node)
|
|
|
|
|
+
|
|
|
|
|
+ if var and node[0] == L(var):
|
|
|
|
|
+ return [P(node, one_derivative)]
|
|
|
|
|
|
|
|
return []
|
|
return []
|
|
|
|
|
|
|
@@ -70,7 +97,8 @@ MESSAGES[one_derivative] = _('Variable {0[0]} has derivative 1.')
|
|
|
|
|
|
|
|
def zero_derivative(root, args):
|
|
def zero_derivative(root, args):
|
|
|
"""
|
|
"""
|
|
|
- der(n) -> 0
|
|
|
|
|
|
|
+ der(x, y) -> 0
|
|
|
|
|
+ der(n) -> 0
|
|
|
"""
|
|
"""
|
|
|
return L(0)
|
|
return L(0)
|
|
|
|
|
|
|
@@ -78,27 +106,89 @@ def zero_derivative(root, args):
|
|
|
MESSAGES[zero_derivative] = _('Constant {0[0]} has derivative 0.')
|
|
MESSAGES[zero_derivative] = _('Constant {0[0]} has derivative 0.')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+def match_const_deriv_multiplication(node):
|
|
|
|
|
+ """
|
|
|
|
|
+ [f(c * x)]' -> c * [f(x)]'
|
|
|
|
|
+ """
|
|
|
|
|
+ assert node.is_op(OP_DERIV)
|
|
|
|
|
+
|
|
|
|
|
+ p = []
|
|
|
|
|
+
|
|
|
|
|
+ if node[0].is_op(OP_MUL):
|
|
|
|
|
+ scope = Scope(node[0])
|
|
|
|
|
+
|
|
|
|
|
+ for n in scope:
|
|
|
|
|
+ if n.is_numeric():
|
|
|
|
|
+ p.append(P(node, const_deriv_multiplication, (scope, n)))
|
|
|
|
|
+
|
|
|
|
|
+ return p
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def const_deriv_multiplication(root, args):
|
|
|
|
|
+ """
|
|
|
|
|
+ [f(c * x)]' -> c * [f(x)]'
|
|
|
|
|
+ """
|
|
|
|
|
+ scope, c = args
|
|
|
|
|
+
|
|
|
|
|
+ scope.remove(c)
|
|
|
|
|
+ x = L(get_derivation_variable(root))
|
|
|
|
|
+
|
|
|
|
|
+ # FIXME: is the explicit 'x' parameter necessary?
|
|
|
|
|
+ return c * der(scope.as_nary_node(), x)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+MESSAGES[const_deriv_multiplication] = \
|
|
|
|
|
+ _('Bring multiplication with {2} in derivative {0} to the outside.')
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
def match_variable_power(node):
|
|
def match_variable_power(node):
|
|
|
"""
|
|
"""
|
|
|
der(x ^ n) -> n * x ^ (n - 1)
|
|
der(x ^ n) -> n * x ^ (n - 1)
|
|
|
der(x ^ n, x) -> n * x ^ (n - 1)
|
|
der(x ^ n, x) -> n * x ^ (n - 1)
|
|
|
- der(x ^ f(x)) -> n * x ^ (n - 1)
|
|
|
|
|
|
|
+ der(f(x) ^ n) -> n * f(x) ^ (n - 1) * der(f(x)) # Chain rule
|
|
|
"""
|
|
"""
|
|
|
assert node.is_op(OP_DERIV)
|
|
assert node.is_op(OP_DERIV)
|
|
|
|
|
|
|
|
- if node[0].is_power():
|
|
|
|
|
- x, n = node[0]
|
|
|
|
|
|
|
+ if not node[0].is_power():
|
|
|
|
|
+ return []
|
|
|
|
|
+
|
|
|
|
|
+ root, exponent = node[0]
|
|
|
|
|
+
|
|
|
|
|
+ rvars = find_variables(root)
|
|
|
|
|
+ evars = find_variables(exponent)
|
|
|
|
|
+ x = get_derivation_variable(node, rvars | evars)
|
|
|
|
|
|
|
|
- if x.is_variable():
|
|
|
|
|
- return [P(node, variable_power, ())]
|
|
|
|
|
|
|
+ if x in rvars and x not in evars:
|
|
|
|
|
+ if root.is_variable():
|
|
|
|
|
+ return [P(node, variable_root)]
|
|
|
|
|
+
|
|
|
|
|
+ return [P(node, chain_rule, (root, variable_root, ()))]
|
|
|
|
|
+ elif not x in rvars and x in evars:
|
|
|
|
|
+ if exponent.is_variable():
|
|
|
|
|
+ return [P(node, variable_exponent)]
|
|
|
|
|
+
|
|
|
|
|
+ return [P(node, chain_rule, (root, variable_exponent, ()))]
|
|
|
|
|
|
|
|
return []
|
|
return []
|
|
|
|
|
|
|
|
|
|
|
|
|
-def variable_power(root, args):
|
|
|
|
|
|
|
+def variable_root(root, args):
|
|
|
"""
|
|
"""
|
|
|
der(x ^ n, x) -> n * x ^ (n - 1)
|
|
der(x ^ n, x) -> n * x ^ (n - 1)
|
|
|
"""
|
|
"""
|
|
|
- x, n = args
|
|
|
|
|
|
|
+ x, n = root[0]
|
|
|
|
|
+
|
|
|
|
|
+ return n * x ** (n - 1)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def variable_exponent(root, args):
|
|
|
|
|
+ """
|
|
|
|
|
+ der(g ^ x, x) -> g ^ x * ln(g)
|
|
|
|
|
+
|
|
|
|
|
+ Note that (in combination with logarithmic/constant rules):
|
|
|
|
|
+ der(e ^ x) -> e ^ x * ln(e) -> e ^ x * 1 -> e ^ x
|
|
|
|
|
+ """
|
|
|
|
|
+ # TODO: Put above example 'der(e ^ x)' in unit test
|
|
|
|
|
+ g, x = root[0]
|
|
|
|
|
|
|
|
- return n * x ^ (n - 1)
|
|
|
|
|
|
|
+ return g ** x * ln(g)
|