FW: Symbolic computation with vector fields and tensors

• To: mathgroup at smc.vnet.net
• Subject: [mg46803] FW: [mg46767] Symbolic computation with vector fields and tensors
• From: "Wolf, Hartmut" <Hartmut.Wolf at t-systems.com>
• Date: Tue, 9 Mar 2004 04:30:46 -0500 (EST)
• Sender: owner-wri-mathgroup at wolfram.com

```An amendment to my reply, see below

-----Original Message-----
From: Wolf, Hartmut
To: mathgroup at smc.vnet.net
Subject: [mg46803] RE: [mg46767] Symbolic computation with vector fields and
tensors

>-----Original Message-----
>From: J Krugman [mailto:jkrugman345 at yahbitoo.com]
To: mathgroup at smc.vnet.net
>Sent: Sunday, March 07, 2004 7:34 AM
>To: mathgroup at smc.vnet.net
>Subject: [mg46803] [mg46767] Symbolic computation with vector fields and tensors
>
>
>
>I'm trying to set up a symbolic computation involving covariant and
>contravariant vector fields, and second-order covariant tensors.
>Mathematica is probably the best tool for this, but I'm having a hard
>time getting past square one.
>
>My first problem is in getting Mathematica to recognize the standard
>algebra of functions, whereby "f + g" denotes the function whose value
>at x is f[x] + g[x], etc.  For example:
>
>  In[1]=
>  f[x_] := x+1;
>  g[x_] := 3x;
>  h = f + g;
>  h[x]
>
>  Out[4]=
>  (f+g)[x]
>
>I know that I can always define h "pointwise" with the statement
>h[x_]:=f[x]+g[x], instead of the "functional" approach I use above,
>but I want to avoid this if possible.  I'm also aware of Through, but
>I want Mathematica to perform these conversions automatically (e.g. in
>respond to Expand) without prompting from me.
>
>A trickier problem is illustrated by the following. L and M are two
>differential operators, and the map LD returns the differential
>operator obtained from the commutator (in the sense of composition) of
>its two arguments:
>
>In[5]:=
>L[f_] := A[1] Derivative[1, 0][f] + A[2] Derivative[0, 1][f];
>M[f_] := B[1] Derivative[1, 0][f] + B[2] Derivative[0, 1][f];
>LD[L_, M_][f_] := Composition[L, M][f] - Composition[M, L][f];
>LD[L, M][f]//Expand//OutputForm
>
>  In[5]:=
>  L[f_] := A[1] Derivative[1, 0][f] + A[2] Derivative[0, 1][f];
>  M[f_] := B[1] Derivative[1, 0][f] + B[2] Derivative[0, 1][f];
>  LD[L_, M_][f_] := Composition[L, M][f] - Composition[M, L][f];
>  LD[L, M][f]//Expand//OutputForm
>
>  Out[8]//OutputForm=
>                (0,1)         (1,0) (0,1)
>  -(B[2] (A[2] f      + A[1] f     )     ) +
>
>                (0,1)         (1,0) (0,1)
>    A[2] (B[2] f      + B[1] f     )      -
>
>                (0,1)         (1,0) (1,0)
>    B[1] (A[2] f      + A[1] f     )      +
>
>                (0,1)         (1,0) (1,0)
>    A[1] (B[2] f      + B[1] f     )
>
>
>How can I get Mathematica to compute, for example, the first partial
>derivative of
>
>          (0,1)         (1,0)
>   (A[2] f      + A[1] f     )
>
>and do so automatically (e.g. in response to Expand).  In fact,
>how can I get Mathematica to acknowledge the linearity of the
>derivative and Leibniz's rule?
>
>   In[12]:=
>   Expand[Derivative[1,0][a b + c d]]//OutputForm
>
>   Out[12]//OutputForm=
>              (1,0)
>   (a b + c d)
>
>(Incidentally, I need Derivative, and not D, because I want to be
>able to specify partial derivatives in terms of sets of
>subscripts/superscripts.)
>
>
>I wish I had better technical documentation for Mathematica.  The
>Mathematica Book is basically a large collection of examples, which,
>however clever or illuminating, is no substitute for formal APIs.
>The number of important details that the Mathematica Book, despite
>its heft, leaves unsaid is vast.  As a result, I end up figuring
>out these details by sheer trial and error.  (I know that Mr.
>Wolfram is very enthusiastic about "computer experimentation", but
>I trust that he is not trying to promote it by making the Mathematica's
>documentation cryptic.)  In some cases, like those that lead to
>this post, my trial and error gets me nowhere.  Is there anything
>better as far as technical reference material for Mathematica goes?
>
>Thanks!
>
>jill
>
>P.S. To send me mail, splice out the string bit from my address.
>

Jill,

let's see first, what we can do with the existing documentation!  If you
look at Help for Derivative you'll read:

...

[] Derivative is generated when you apply D to functions whose derivatives
Mathematica does not know.

[] Mathematica attempts to convert Derivative[n][f] and so on to pure
functions. Whenever Derivative[n][f] is generated, Mathematica rewrites it
as D[f[#]&, {#, n}]. If Mathematica finds an explicit value for this
derivative, it returns this value. Otherwise, it returns the original
Derivative form.

...

Look also at the examples there.

...

You have to tell Derivative what to do! More exactly, Derivative doesn't do
anything, it just indicates expressions to be differentiated, the working
horse is D.

Just an idea, you might try something like

In[41]:=
slotOn[expr_, slotd_] :=
With[{slots = Sequence @@ (Slot /@ Range[slotd])},
Function[Evaluate[Replace[expr, s_Symbol :> s[slots], {-1}]]]]

In[44]:=
slotOff[expr_] := expr /. Function[body_] :> (body /. x_[Slot[_] ..] :> x)

In[45]:=
Derivative[1, 0][slotOn[a b + c d, 2]] // slotOff
Out[45]//OutputForm=
(1,0)      (1,0)      (1,0)      (1,0)
b a      + a b      + d c      + c d

Mathematica), what A[1] etc. are (also considered as functions? or
constants?). If you try to follow this idea, of course, you have to refine
that function slotOn; i.e. tell more precisely which symbols are to be
considered as functions (and perhaps, where they do appear in the
expressions).

The most simple, or better, way however, would be to follow help, and mark
your functions as such from the very beginning, e.g.

In[24]:=
f = (# + 1 &);
g = (3# &);
h = Thread[f + g, Function]

Out[26]= (#1 + 1) + 3 #1 &

where necessary -- it might be a bit tricky!). If I do, assuming A[1], A[2],
etc. were constants, I get

In[49]:= LD[L, M][f[#1, #2] &] // OutputForm
Out[49]//OutputForm=
(0,1) (0,1)                  (1,0) (0,1)
-(B[2] (A[2] (f     )     [#1, #2] + A[1] (f     )     [#1, #2]) +

(0,1) (1,0)
B[1] (A[2] (f     )     [#1, #2] +

(1,0) (1,0)
A[1] (f     )     [#1, #2])) +

(0,1) (0,1)
(A[2] (B[2] (f     )     [#1, #2] +

(1,0) (0,1)
B[1] (f     )     [#1, #2]) +

(0,1) (1,0)                  (1,0) (1,0)
A[1] (B[2] (f     )     [#1, #2] + B[1] (f     )     [#1, #2])

) &

As Function has the HoldAll Attribute you can't just expand this, you have
to expand the body of the function, e.g. like this

In[50]:= LD[L, M][f[#1, #2] &] /.
Function[body_] :> With[{expdb = Expand[body]}, expdb & /; True]

Out[50]= 0 &

The result is the identical zero function (as you might compute by hand).

As with this observation...

In[53]:= Derivative[1, 1][f[#1, #2] &] // OutputForm
Out[53]//OutputForm=
(1,0) (0,1)
(f     )     [#1, #2] &

we see how Mathematica tries to differentiate; if you don't like the result,
you might have to add your rules to bring that back to

In[56]:= Derivative[1, 1][f][#1, #2] & // OutputForm
Out[56]//OutputForm=
(1,1)
f     [#1, #2] &

and other similar forms.

Another remark might be in place: Obviously you try doing something that has
been done serveral times before. Perhaps you should take a glimpse into
those available packages, and only then try something special, as you prefer
it.  And writing such a package isn't quite easy.

Good luck!

---------------
Amendment:

Perhaps I should have been more specific, and use the less trivial
interpretation for A, B:

In[1]:= L[f_Function] :=
Thread[(A[2][#1, #2] &) Derivative[0, 1][f], Function], Function]

In[2]:= L[f[#1, #2] &] // OutputForm
Out[2]//OutputForm=
(0,1)                         (1,0)
A[2][#1, #2] f     [#1, #2] + A[1][#1, #2] f     [#1, #2] &

In[3]:= M[f_Function] :=
Thread[(B[2][#1, #2] &) Derivative[0, 1][f], Function], Function]

In[6]:= LD[L_, M_][f_] :=
Function]

To understand the need for the inner Thread, consider FullForm.

In[13]:= LD[L, M][f[#1, #2] &] /.
Function[body_] :>
With[{newb = Expand[body]}, newb & /; True] // OutputForm
Out[13]//OutputForm=
(0,1)               (0,1)
-(B[2][#1, #2] f     [#1, #2] (A[2])     [#1, #2]) +

(0,1)               (0,1)
A[2][#1, #2] f     [#1, #2] (B[2])     [#1, #2] -

(0,1)          (1,0)
B[2][#1, #2] (A[1])     [#1, #2] f     [#1, #2] +

(0,1)          (1,0)
A[2][#1, #2] (B[1])     [#1, #2] f     [#1, #2] -

(1,0)               (1,0)
B[1][#1, #2] f     [#1, #2] (A[1])     [#1, #2] -

(0,1)               (1,0)
B[1][#1, #2] f     [#1, #2] (A[2])     [#1, #2] +

(1,0)               (1,0)
A[1][#1, #2] f     [#1, #2] (B[1])     [#1, #2] +

(0,1)               (1,0)
A[1][#1, #2] f     [#1, #2] (B[2])     [#1, #2] &

Now, if you like

In[15]:= % // slotOff // OutputForm
Out[15]//OutputForm=
(0,1)       (0,1)          (0,1)       (0,1)
-(B[2] f      (A[2])     ) + A[2] f      (B[2])      -

(0,1)  (1,0)              (0,1)  (1,0)
B[2] (A[1])      f      + A[2] (B[1])      f      -

(1,0)       (1,0)         (0,1)       (1,0)
B[1] f      (A[1])      - B[1] f      (A[2])      +

(1,0)       (1,0)         (0,1)       (1,0)
A[1] f      (B[1])      + A[1] f      (B[2])

--
Hartmut Wolf

```

• Prev by Date: RE: Symbolic computation with vector fields and tensors
• Next by Date: Re: append to a file
• Previous by thread: RE: Symbolic computation with vector fields and tensors