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:
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:
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:
Some other examples.
A commutator:
Operator arithmetic:
Question (15605):
Question (43775):
Question (20519):
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.