39
$\begingroup$

How do I create an operator which acts like a derivative to everything to the right of it:

for example:

$ \left( \partial_x + \partial_y + z \right ) x \psi $

How do I make that evaluate to:

$x \partial_x \psi + \psi + x \partial_y \psi + z x \psi $

(I want the derivatives to distribute...)

$\endgroup$

6 Answers 6

31
$\begingroup$

A general idea as to how this can be done in a consistent way is explained in the help documents under NonCommutativeMultiply. The thing is that you want to use your operators in an algebraic notation, and that's what that page discusses.

If, on the other hand, you're happy with a more formal Mathematica notation, then you would have the easier task of defining operators simply as

dx := D[#, x] &
dy := D[#, y] &

and using them as follows:

dx@f[x, y, z]

$f^{(1,0,0)}(x,y,z)$

Combining operators would then be done using Composition:

dxy = Composition[dx, dy];
dxy[f[x, y, z]]

$f^{(1,1,0)}(x,y,z)$

Edit

Here is another approach that's sort of intermediate between the very simple D[#,x]& scheme and the more complicated realization of an operator algebra in the linked reference from the documentation.

To make the operators satisfy the axioms of a vector space, we'd have to define their addition among each other and the multiplication with scalars. This can be done most conveniently if we don't use patterns to represent the operators, but Functions. So here I repeat the operator definitions - they act the same way as the dx, dy defined above, but their definition is stored differently:

dx = Function[{f}, D[f, x]];
dy = Function[{f}, D[f, y]];

Now I define the multiplication of an operator with a scalar:

multiplyOp[scalar_, op_] := Function[{f1}, scalar op[f1]];

For simplicity, I always assume that the scalar is given as the first argument, and the second argument is an operator, e.g., dx etc. Note that the arguments here are not x or y (the assumed independent variables on which functions depend), because multiplyOp maps operators onto operators.

Finally, we need the addition of two (or more) operators, which is again a mapping from operators (a sequence of them) onto operators:

addOps[ops__] := Function[{f1}, Total@Map[#[f1] &, {ops}]];

Both addition and multiplication are mapped back to their usual meaning in these functions, by defining how the combined new operators act on a test function f1 (which is in turn a function of x, y, and z - depending on the dimension).

To illustrate the way these operations are used, take the example in the question,

$\left(\partial_x+\partial_y+z\right)x\psi$

and write it with our syntax:

addOps[dx, dy, multiplyOp[z, Identity]]@(x ψ[x, y])

$x \psi ^{(0,1)}(x,y)+x \psi ^{(1,0)}(x,y)+\psi (x,y)+x z \psi (x,y)$

This is the correct result (the result quoted originally in the post was actually missing an x).

Note how I added the scalar z above: in this syntax, it first has to be made into an operator using multiplyOp[z, Identity]. The Identity operator is very useful for this.

Of course these expressions with addOps and multiplyOp aren't as easy to read as the ones with simple + signs, but on the bright side it can also be beneficial pedagogically to separate the "operator operations" clearly from the operations between the functions they act on.

Edit 2

In response to the comment, I'll add a nicer notation, but without modifying the last approach. So I'll simply introduce new symbols for the operations defined above, using some of the operator symbols that Mathematica knows in terms of their operator precedence, but has no pre-defined meanings for:

  • CirclePlus ⊕ typed as escc+esc
  • CircleDot ⊙ typed as escc.esc
  • CircleTimes ⊗ typed as escc*esc

I'll use them as follows:

CirclePlus[ops__] := addOps[ops];
CircleDot[scalar_, op_] := multiplyOp[scalar, op];
CircleTimes[ops__] := Composition[ops];

With this, we can now use Infix notation to write in a more "natural" fashion:

(dx ⊕ dy ⊕ z⊙Identity)@(x ψ[x,y])

$x \psi ^{(0,1)}(x,y)+x \psi ^{(1,0)}(x,y)+\psi (x,y)+x z \psi (x,y)$

As the third operator, CircleTimes ⊗, I've also defined the composition of operators. That allows us to do things like commutators:

commutator = dx ⊗ x⊙Identity ⊕ (-x)⊙Identity ⊗ dx;

I'm relying on the fact that ⊙ has higher precedence than ⊗ which in turn has higher precedence than ⊕ (according to the documentation).

As expected, the commutator is unity, as we can check by applying to a test function:

commutator@f[x]

f[x]

$\endgroup$
3
  • $\begingroup$ very nicely done. I imagine it would be possible to unprotect and modify the addition and multiplication signs to make this simpler to read? $\endgroup$ Commented May 3, 2012 at 22:00
  • $\begingroup$ @AndrewSpott I've added a more readable notation in a way that doesn't require unprotecting Times and Plus, so the symbols used for operators are still distinct from the usual ones, but more intuitive (I hope) than before. $\endgroup$
    – Jens
    Commented May 4, 2012 at 7:06
  • $\begingroup$ thanks! that is really slick. The commutator is actually what I'm interested in, thanks. $\endgroup$ Commented May 4, 2012 at 7:09
22
$\begingroup$

Update

I've created a paclet. Install with:

PacletInstall["https://github.com/carlwoll/DifferentialOperator/releases/download/0.1/DifferentialOperator-0.0.1.paclet"]

and load with:

<<DifferentialOperator`

Original post

Here's an approach I've been playing with that attempts to mimic traditional notation for these kinds of operators. The exposition is a little long, but the payoff is a very natural approach to handling operators.

DifferentialOperator

The first step is to create an operator form for derivatives that can be entered easily using the keyboard, and formats as expected. I call the operator form DifferentialOperator, and it has the following SubValues/UpValues:

DifferentialOperator[z__:x][f_] := D[f, z]

DifferentialOperator /: DifferentialOperator[z__:x]^n_Integer?Positive := Apply[
    DifferentialOperator,
    Flatten @ ConstantArray[{z}, n]
]

Here is an example:

L = DifferentialOperator[x, y]
L @ Exp[x y]

DifferentialOperator[x, y]

E^(x y) + E^(x y) x y

The above definition is a nice operator form, but the formatting is rather long, so I give DifferentialOperator a nice format below:

DifferentialOperator /: MakeBoxes[DifferentialOperator[], form_] := InterpretationBox[
    "\[PartialD]", DifferentialOperator[]
]

DifferentialOperator /: MakeBoxes[DifferentialOperator[x__], form_] := With[
    {sub = RowBox @ BoxForm`MakeInfixForm[{x},",", form]},

    InterpretationBox[
        SubscriptBox["\[PartialD]",sub],
        DifferentialOperator[x]
    ]
]

Now, the operator L looks much more like the usual derivative operator:

L //TeXForm

$\partial _{x,y}$

Finally, typing DifferentialOperator is rather tedious and ugly, so I use InputAutoReplacements to streamline the input:

CurrentValue[EvaluationNotebook[], InputAutoReplacements] = {
    "pd" -> TemplateBox[
        {"\"\[PartialD]\""},
        "Partial",
        DisplayFunction -> (StyleBox[#, ShowStringCharacters->False]&),
        InterpretationFunction -> (
            RowBox[{"operator", "[", RowBox[{"DifferentialOperator","[","]"}],"]"}]&
        ),
        Editable->False,
        Selectable->False
    ],
    ParentList
};

A few comments here:

a. I use a TemplateBox so that editing of the \[PartialD string is prevented.

b. I use "\"\[PartialD]\"" instead of just "\[PartialD]" so that input parsing works as expected.

c. I use ShowStringCharacters->False so that one doesn't see the quotes I added above.

d. The InterpretationFunction includes an operator wrapper that will be discussed later.

Here is a short animation showing me enter the OP operator into an input cell:

enter image description here

Composition

Differential operators don't commute with expressions, e.g., $\partial ⋅ x \neq x ⋅ \partial$, so I will use CenterDot for composition of operators.

I can't just use Composition because scalar operators have an implied Identity, but I can give CenterDot special rules to accommodate this. Here are the arithmetical rules:

(* arithmetic *)
CenterDot[___, 0, ___] = 0;

CenterDot[a_, c__] + CenterDot[b_, c__] ^:= CenterDot[a+b,c]
a_?scalarQ CenterDot[b_, c___] ^:= CenterDot[a b, c]

and the operator rules (SubValues):

SetAttributes[CenterDot,{Flat,OneIdentity}]

(* nested function application *)
CenterDot[a__, b_][x_] := CenterDot[a][CenterDot[b][x]]

(* function application *)
CenterDot[a_Plus][x_] := CenterDot[#][x]&/@a
CenterDot[a_?scalarQ][x_] := a x
CenterDot[a_?scalarQ b_?differentialQ][x_] := a CenterDot[b][x]
CenterDot[d_DifferentialOperator][x_] := d[x]

The operator wrapper

You may have noticed the operator wrapper in the above InterpretationFunction. Suppose one were to use $L = \partial + x$. Without the operator wrapper, the head would just be Plus, and applying L to a function L[f] would evaluate to Plus[DifferentialOperator[], x][f], and in order to have this evaluate further one would need to modify the System` symbol Plus (not a good idea). The operator wrapper automatically absorbs typical arithmetic operations so that arithmetic with an operator object will produce an operator object. This means that L[f] evaluates to an operator object, and we can give rules for operator in order to get the operator to act on a function. Here are the definitions for operator:

SetAttributes[operator, {Flat, OneIdentity}]

(* addition *)
operator[a_]+c_ ^:= operator[a+c]

(* scalar multiplication *)
c_?scalarQ operator[a_] ^:= operator[c a]

(* composition *)
operator[a_,b__] := operator[CenterDot[a,b]]

operator[a_]\[CenterDot]c_ ^:= operator[CenterDot[a,c]]
c_\[CenterDot]operator[a_] ^:= operator[CenterDot[c,a]]

(* power *)
operator /: operator[a_]^n_Integer := operator[CenterDot@@ConstantArray[a,n]]

(* subscripted nabla *)
Subscript[operator[DifferentialOperator[]], x__] ^:= operator[DifferentialOperator[x]]

(* utilities*)
scalarQ = FreeQ[DifferentialOperator];
differentialQ = Not @* scalarQ;

Some examples:

enter image description here

Notice how the head of every expression is operator. Now, we can give a definition for operator:

(* function application *)
operator[a_][x_] := CenterDot[a][x]

Finally, we need a format for operator, as we don't want to see it in the input or output:

operator/:MakeBoxes[operator[a__], form_]:=If[Length@Hold[a]>1,
    StyleBox[MakeBoxes[CenterDot[a], form], Bold],
    StyleBox[MakeBoxes[a, form], Bold]
]

I use Bold to indicate that the object is an operator:

L = Subscript[operator[DifferentialOperator[]], t,t] - 3 Subscript[operator[DifferentialOperator[]], t] + 2;
L //TeXForm

$\bf{\partial _{t,t}-3 \partial _t+2}$

Examples

That's it for the framework. Now, for the example in this question, we have:

enter image description here

Some other examples.

A commutator:

enter image description here

Operator arithmetic:

enter image description here

Question (15605):

enter image description here

Question (43775):

enter image description here

Question (20519):

enter image description here

I haven't included it here, but the next step is to perform simplifications of the CenterDot objects, in particular so that commutators can be defined.

$\endgroup$
1
  • $\begingroup$ Good work. I added some rules for the composition of differential operators in tensor form. $\endgroup$
    – hlren
    Commented May 20, 2020 at 21:10
15
$\begingroup$

Something like this

f = D[#, x] + D[#, y] + z # &

seems to work. Use as follows:

f[x ψ[x, y, z]]

to give

$x \psi ^{(0,1,0)}(x,y,z)+x \psi ^{(1,0,0)}(x,y,z)+x z \psi (x,y,z)+\psi (x,y,z)$

$\endgroup$
10
$\begingroup$

Since no one mentioned it yet, Through can do something similar:

(Derivative[1, 0] + Derivative[0, 1])[f] // Through

will evaluate to

Mathematica graphics

$\endgroup$
3
$\begingroup$

This simple solution will work only on conmutative operators:

applyOp[anything_] := 
     Expand[anything] //. Times[d : Except[_Function] .., a__Function] 
                          :> (Composition[a][Times[d]])

And defining operators like Jens:

dx := D[#, x] &
dy := D[#, y] &

But no need to define z as operator. You can do:

applyOp[(dx + dy + z) (x psi[x, y, z])]
(* --->
psi[x,y,z]+x z psi[x,y,z]+x (psi^(0,1,0))[x,y,z]+x (psi^(1,0,0))[x,y,z]
*)

EDITED: Composition of operators is only on conmutative operators, derivatives are not.

$\endgroup$
2
  • $\begingroup$ Your last example uses non-commuting operators (dx and x don't commute, nor do dy and y) and therefore violates the restriction you identified yourself (try da := D[#,a]&; applyOp[da a] to see a result which you surely didn't expect). However your suggestion has a more important flaw: You do not distinguish between the operator and the function it is applied to. Most importantly, the operator $\partial_x f(\hat x)$ is not the same as the operator $f'(\hat x)$, but equals the operator $(f'(\hat x) + f(\hat x)\partial_x)$. $\endgroup$
    – celtschk
    Commented May 3, 2012 at 8:28
  • $\begingroup$ @celtschk you are totally right. I should not answer at nights... I will change it to a non-conmutative way. $\endgroup$
    – FJRA
    Commented May 3, 2012 at 13:05
2
$\begingroup$

Not sure if this is what you want. Anyway:

d[psi_[a_, b_, c_]] := D[psi, a] + D[psi, b] + c psi

p[x_, y_, z_] := x + y;

d[p[x, y, z]]
(*
-> 2 + (x + y) z
*)
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.