Discussion
Old answer There are some pitfalls in this simple approach. Since Set
works by evaluating the right-hand side before making the definition, if F
, x
or xdot
have any values (OwnValues
or in the case of F
also DownValues
for F[x]
), they will be used in the definition, creating a different function than the one created when they have no values. In the case where F
is undefined, then the definition will depend on the external symbol F
.
These things may or may not be desired, or even may be unimportant. For instance, since D[F[x], x]
will not work if x
has a numeric value. So it is unlikely to be important to Block
the symbol in creating the definition. It is more likely that the dependence of the definition of F
will be a source of annoyance and perhaps confusion. For instance if F[x_] := Cos[x]
is executed before the definition of Fdot
, Fdot[x0, xdot0]
will always use Sin[x0] * xdot0
(unless a new definition is executed). If F[x_] := Cos[x]
is executed after the definition of Fdot
, Fdot[x0, xdot0]
will use F'[x0] * xdot0
; thus the definition of F
in force at the time of execution will be used, so that if F
is changed at some point, the value of ``Fdot[x0, xdot0]` will automatically be change, too.
Here is a general function that creates a function to evaluate a differential expression (i.e. in terms of x
, Dt[x]
, Dt[Dt[x]]
etc.) at inputs of the form {x0, dx0, ddx0,...}
, similar to the way one lists the values of a function and its derivatives in Interpolation
. The expression to be evaluated is stored explicitly, so any dependence on external symbols is clear.
dxvecClearAll[dFunction];
dFunction[df_, var: _Symbol] := dFunction[df, {2var}];
dFunction[df_, 3vars: {__Symbol}][vals__] /; (*Length[{vals}] replacement== valuesLength@Flatten[{vars}] for:=
Dt[x] df /. Flatten @ MapThread[
Thread[NestList[Dt, Dt@Dt[x]#1, etc.Length[#2] *)- 1] -> #2] &,
dyvec = {5Flatten[{vars}], 7Flatten @* List /@ {vals};}];
Dt@Dt[x(* Sin[y]]
%optional /.formatting dt_Dt*)
Format[dFunction[df_, vars:> With[{levels__Symbol}]] := Level[dt
HoldForm[dFunction][
Union @ DeleteCases[
Cases[Hold[df], s_Symbol /; Context[s] === "Global`", Infinity, Heads -1]}> True],
Switch[First@levels Alternatives @@ vars],
vars]
The Format
value, which is optional and shown below, might take a long time on a large expression df
. If that is a concern, delete it, delete just the Union @...
, or wrap the Union
in TimeConstrained
:
TimeConstrained[Union@ ..., 0.01] x,/. dxvec[[Length@levels]]$Aborted -> "\[LeftSkeleton]\[RightSkeleton]"
Examples and further utilities:
Undefined function F
:
Clear[F];
Fdot2 = dFunction[Dt@F[x], x]
Fdotdot = dFunction[Dt@Dt@F[x, y], {x, y}]
Fdot2[{2, dyvec[[Length@levels]],3}]
(* 3 F'[2] _*)
Fdotdot[{2, dt]3, /;5}, MatchQ[levels{1, 7, 11}]
Defining F
causes Fdot
to have the corresponding value:
F[x_] := Cos[x];
Fdot2[{_Symbol2, ___Dt3}]]]
(*
2-3 Cos[y]Sin[2] Dt[x] Dt[y]*)
Using a previously defined function U
:
ClearAll[U, +G, M, m, x, Cos[y]y, Dt[Dt[y]]z];
U[x, y, z] := -G xM Dt[y]^2m/z;
energy Sin[y]= dFunction[U[x, y, z] + (1/2) Dt[Dt[x]]m Sin[y](Dt[x]^2 + Dt[y]^2 + Dt[z]^2), {x, y, z}]
One can get the expression stored in the dFunction
with First
:
First @ energy
(* 20-((G Cos[y]m M)/z) + 71/2 xm Cos[y](Dt[x]^2 + 3Dt[y]^2 Sin[y]+ -Dt[z]^2) 25 x Sin[y]
*)
I can adapt thisOr one could add methods to a function, if it's on track....If not, I'll delete it.the definition so that
energy["Expression"]
Alternative wayyields the expression above. One might also want to constructput the replacement rulecode from the Format
into its own method, that can probably be generalized easily..to inspect a complicated expression for parameters.
With[{repldFunction[df_, =vars <|x: ->{__Symbol}]["Expression"] dxvec:= df;
dFunction[df_, yvars ->: dyvec|>{__Symbol},
]["IndependentVariables"] %:= /.vars;
dFunction[df_, dt_Dtvars :> With[{levels__Symbol}]["Parameters"] := Level[dt,
-1]} dFunction[df,
vars]["Parameters"] =
repl[First@levels][[Length@levels]] /; Union@DeleteCases[
FreeQ[repl[First@levels]Cases[Hold[df], Missing]s_Symbol &&/; Context[s] === "Global`", Infinity, Heads -> True],
Alternatives MatchQ[levels@@ vars];
dFunction[df_, vars : {_Symbol__Symbol}]["Properties"] :=
{"Expression", ___Dt"IndependentVariables", "Parameters"}];
With
TimeConstrained[dFunction[df, &&vars]["Parameters"], 0.01] /.
$Aborted -> "\[LeftSkeleton]\[RightSkeleton]"
in the Format
value:
d10 = Length@levelsdFunction[Nest[Dt, <=Sin[a Length@repl[First@levels]x^2], 10], {x}]
d20 = dFunction[Nest[Dt, Sin[a x^2], 20], ]]{x}]
Since the "Parameters"
property method is cached above, one can compute it explicitly and then it will be displayed.
d20["Parameters"]
d20
On the other hand, cacheing the value does store the differential expression df
in the SubValues
(What is the distinction between DownValues, UpValues, SubValues, and OwnValues?) of dFunction
. In the case of d20
, that adds about 200K to the memory footprint of dFunction
, whose basic definitions take up only 6240
bytes.
[Original attempt can be seen in the edit history.]