1 /* Copyright (C) 2004 Viktor T. Toth <http://www.vttoth.com/>
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU General Public License as
5 * published by the Free Software Foundation; either version 2 of
6 * the License, or (at your option) any later version.
8 * This program is distributed in the hope that it will be
9 * useful, but WITHOUT ANY WARRANTY; without even the implied
10 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
13 * Algebraic tensor manipulation
17 if get('atensor,'version)#false then error("ATENSOR already loaded!")$
22 aform:diagmatrix(3,1);
28 abasep(v):=if not atom(v) and mapatom(v) and op(v)=asymbol and length(v)=1
29 and part(v,1)>0 and part(v,1)<=adim then true else false;
31 /* declare(..,scalarp) doesn't work on function names, which is why
32 we need some special rules here. */
33 scalarfunp(x):=if not atom(x) and (op(x)=nounify(sf) or op(x)=nounify(af))
34 then true else scalarp(x);
35 nonscfunp(x):=if atom(x) or (op(x)#nounify(sf) and op(x)#nounify(af))
36 then not scalarp(x) else false;
38 matchdeclare([nonsc1,nonsc2], nonscfunp());
39 matchdeclare([scalarf],scalarfunp());
40 matchdeclare([abasev1,abasev2],abasep());
42 defrule(scalarfun1,nonsc1.scalarf,nonsc1*scalarf);
43 defrule(scalarfun2,scalarf.nonsc2,nonsc2*scalarf);
45 defrule(grassmann1,nonsc1.nonsc1,0);
46 defrule(grassmann2,nonsc1.nonsc2,
47 if ordergreatp(nonsc1,nonsc2) then -nonsc2.nonsc1 else nonsc1.nonsc2);
48 defrule(clifford1,nonsc1.nonsc1,sf(nonsc1,nonsc1));
49 defrule(clifford2,nonsc1.nonsc2,
50 if ordergreatp(nonsc1,nonsc2) then -nonsc2.nonsc1+2*sf(nonsc2,nonsc1) else nonsc1.nonsc2);
51 defrule(symmetric,nonsc1.nonsc2,
52 if ordergreatp(nonsc1,nonsc2) then nonsc2.nonsc1 else nonsc1.nonsc2);
53 defrule(symplectic,nonsc1.nonsc2,
54 if ordergreatp(nonsc1,nonsc2) then nonsc2.nonsc1-2*af(nonsc2,nonsc1)
56 defrule(lie_envelop,nonsc1.nonsc2,
57 if ordergreatp(nonsc1,nonsc2) then nonsc2.nonsc1-2*av(nonsc2,nonsc1)
59 defrule(complex1,abasev1.abasev1,-1);
62 [grassmann,[grassmann1,grassmann2]],
63 [clifford,[clifford1,clifford2]],
64 [symmetric,[symmetric]],
65 [symplectic,[symplectic]],
66 [lie_envelop,[lie_envelop]]
69 /* Recursively simplify an atensor expression, with special attention
70 paid to the fact that mnctimes can have multiple arguments;
71 furthermore, we don't want dot products to end up as arguments
72 to av(), af(), or sf(). The counter j is to ensure that the function
73 always terminates. Admittedly, this is not a very efficient
74 implementation and can use improvements, but it does provide results
75 that are compatible with commercial MACSYMA. */
78 [i,j,exp1,exp2,exp3,done],
79 if not mapatom(exp) then exp:map(atensimp,exp),
88 if not atom(exp1) and op(exp1)="." and length(args(exp1))>2 then block
91 for i thru length(argv)-1 do
93 exp2:"."(argv[i],argv[i+1]),
97 exp1:apply(".",append(rest(argv,-length(argv)+i-1),[exp3],
106 if exp#exp1 then exp:atensimp(exp1) else
107 for i thru length(atenrules) do
108 if atenrules[i][1]=alg_type then
110 exp2:apply('applyb1,cons(exp,atenrules[i][2])),
112 exp2:applyb1(exp2,scalarfun1,scalarfun2),
114 if exp=exp2 then j:10
115 else exp:(if mapatom(exp2) then exp2 else map(atensimp,exp2))
121 init_atensor(algname, [optdims]):=
123 if algname='universal or algname='grassmann or algname='clifford or
124 algname='symmetric or algname='symplectic or algname='lie_envelop then
127 if alg_type='symplectic then
131 if length(optdims)>0 then
134 aform:zeromatrix(optdims[1],optdims[1]),
138 aform[i,j]:if evenp(i+j) then 1 else -1,
139 aform[j,i]:-aform[i,j]
141 if length(optdims)>1 then
143 for i thru optdims[2] do
144 aform:addrow(aform,makelist(0,j,1,adim)),
145 adim:adim+optdims[2],
146 for i thru optdims[2] do
147 aform:addcol(aform,makelist(0,j,1,adim))
149 if length(optdims)>2 then error("Invalid optional dimensions"),
153 else if alg_type='clifford then block
156 if length(optdims)>0 then p:optdims[1],
157 if length(optdims)>1 then z:optdims[2],
158 if length(optdims)>2 then n:optdims[3],
159 if length(optdims)>3 then error("Invalid optional dimensions"),
164 for i from p+1 thru p+z do aform[i,i]:0,
165 for i from p+z+1 thru adim do aform[i,i]:-1
170 else if length(optdims)>0 then
172 if length(optdims)>1 then error("Invalid optional dimensions"),
175 if alg_type='lie_envelop then
177 for i thru adim do for j thru i do
178 if i=j then aform[i,j]:0
179 else aform[i,j]:-(aform[j,i]:(remainder(2*adim+2-i-j,adim)+1)*
180 signum(i-j)*(if oddp(i+j) then 1 else -1))
190 else if algname='complex then init_atensor(clifford,0,0,1)
191 else if algname='quaternion then init_atensor(clifford,0,0,2)
192 else if algname='pauli then init_atensor(clifford,3)
193 else if algname='dirac then init_atensor(clifford,3,0,1)
194 else error("Unknown algebra name"),
195 if optdims#[] then error("No optional dimensions permitted for this algebra"),
199 af(u,v):=if abasep(u) and abasep(v) then aform[part(u,1),part(v,1)]
201 sf(u,v):=if abasep(u) and abasep(v) then aform[part(u,1),part(v,1)]
203 av(%u,%v):=if abasep(%u) and abasep(%v) then block
205 [i:aform[part(%u,1),part(%v,1)]],
206 if abs(i)>0 and abs(i)<=adim then asymbol[abs(i)]*signum(i) else 0
211 put('atensor,'v20081223,'version)$