1 | initial version |
The trick is to substitute u
with a variable, differentiate, then substitute back.
Given the python module called func_diff.py
(shown later), one can use it as follows:
goofy@wdeb:temp$ ls
func_diff.py
goofy@wdeb:temp$ sage
----------------------------------------------------------------------
| Sage Version 4.6.2, Release Date: 2011-02-25 |
| Type notebook() for the GUI, and license() for information. |
----------------------------------------------------------------------
sage: import func_diff
sage: u(x) = function('u', x)
sage: u
x |--> u(x)
sage: L = u^3 + (1/2)*u.diff(x)^2
sage: L
x |--> u(x)^3 + 1/2*D[0](u)(x)^2
sage: func_diff.func_diff(L, u)
x |--> 3*u(x)^2 - D[0, 0](u)(x)
Note that func_diff
as-is does not actually compute the functional derivative. In the context of @cswiercz's definition, it takes $L$, not $I$, as the input. But this should be relatively easy to change. Simply use func_diff(I.diff(x), u)
Here is the module func_diff.py
:
from sage.all import *
import sage.symbolic.operators
def is_op_du(expr_op, u):
is_derivative = isinstance(
expr_op,
sage.symbolic.operators.FDerivativeOperator
)
if is_derivative:
# Returns True if the differentiated function is `u`.
return expr_op.function() == u.operator()
else:
return False
def iter_du_orders(expr, u):
for sub_expr in expr.operands():
if sub_expr == []:
# hit end of tree
continue
elif is_op_du(sub_expr.operator(), u):
# yield order of differentiation
yield len(sub_expr.operator().parameter_set())
else:
# iterate into sub expression
for order in iter_du_orders(sub_expr, u):
yield order
def func_diff(L, u_in):
# `u` must be a callable symbolic expression
# in one variable.
if len(u_in.variables()) == 1:
x = u_in.variables()[0]
u = u_in.function(x)
else:
raise TypeError
# This variable name must not collide
# with an existing one.
# I use an empty string in hopes that
# nobody else does this...
t = SR.var('')
result = SR(0)
# `orders` is the set of all
# orders of differentiation of `u`
orders = set(iter_du_orders(L, u)).union((0,))
for c in orders:
du = u(x).diff(x, c)
sign = Integer(-1)**c
# Temporarily replace all `c`th derivatives of `u` with `t`;
# differentiate; then substitute back.
dL_du = L.subs({du:t}).diff(t).subs({t:du})
# Append intermediate term to `result`
result += sign * dL_du.diff(x, c)
return result
The bulk of the code is rather uninteresting. For example, the generator iter_du_orders
iterates through the given expression L
and returns all of the orders of differention of u
in L
.
Unfornately, there is a bug which I do not know how to cleanly circumvent. It is due to the line t = SR.var('')
, which creates a temporary variable. This variable is used to substitute out all instances of $\frac{d^n u}{d x^n}$ in L
when computing the derivatives. The problem is if L
itself contains a variable with an empty-string representation (i.e. the same as that of t
), then things will get pretty messed up. For example:
sage: b = SR.var('')
sage: b
sage: u = function('u', b).function(b)
sage: u
|--> u()
sage: L = u^3 + (1/2)*u.diff(b)^2
sage: L
|--> u()^3 + 1/2*D[0](u)()^2
sage: func_diff.func_diff(L, u)
|--> -3*u(D[0](u)())^2*D[0, 0](u)()*D[0, 0](u)(D[0](u)()) - 6*u(D[0](u)())*D[0](u)(D[0](u)())^2*D[0, 0](u)() + 3*u()^2 + D[0](u)(u())*D[0, 0](u)(u()) - D[0, 0](u)()