# Revision history [back]

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(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()
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 cth 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(u)()^2
sage: func_diff.func_diff(L, u)
|--> -3*u(D(u)())^2*D[0, 0](u)()*D[0, 0](u)(D(u)()) - 6*u(D(u)())*D(u)(D(u)())^2*D[0, 0](u)() + 3*u()^2 + D(u)(u())*D[0, 0](u)(u()) - D[0, 0](u)()