4 The module symbolicGA implements symbolic Geometric Algebra in python.
5 The relevant references for this module are:
7 1. "Geometric Algebra for Physicists" by C. Doran and A. Lazenby,
8 Cambridge University Press, 2003.
10 2. GiNaC 1.4.1, An open framwork for symbolic computation with the
11 C++ programming language, 9/5/2007, http://www.ginac.de
13 3. Symbolic Computation with GiNaC and Python 1.3.5, 2007-02-01,
14 http://swiginac.berlios.de/ginac-tutorial.py.html
16 4. Sympy Tutorial, http://code.google.com/p/sympy/wiki/Tutorial
18 5. "Design of a Python Module for Symbolic Geometric Algebra
19 Calculations" by Alan Bromborsky, included as symbolGA.pdf
23 import os
,sys
,string
,types
,re
,copy
25 from latex_out
import *
27 NUMPAT
= re
.compile( '([\-0-9])|([\-0-9]/[0-9])')
28 """Re pattern for rational number"""
30 ZERO
= sympy
.Rational(0)
31 ONE
= sympy
.Rational(1)
32 TWO
= sympy
.Rational(2)
33 HALF
= sympy
.Rational(1,2)
39 def set_main(main_program
):
41 MAIN_PROGRAM
= main_program
45 if type(lst
) == types
.ListType
:
54 Returns rational numbers compatible with symbols.
55 Input is a string representing a fraction or integer
58 if type(num_str
) == types
.IntType
:
62 tmp
= num_str
.split('/')
69 return(sympy
.Rational(a
,b
))
74 Symbol converts a string to a sympy/sympy symbol.
76 sym
= sympy
.Symbol(sym_str
)
80 return(sympy
.expand(expr
))
83 def collect(expr
,lst
):
85 Wrapper for sympy.collect and sympy.collect.
86 See references 2, 3, and 4.
88 lst
= MV
.scalar_to_symbol(lst
)
89 return(sympy
.collect(expr
,lst
))
91 def sqrfree(expr
,lst
):
93 Wrapper for sympy.sqrfree and sympy.factor.
94 See references 2, 3, and 4.
96 return(sympy
.sqrfree(expr
,lst
))
98 def collect_common_factors(expr
):
100 Wrapper for sympy.collect_common_factors and sympy.factor.
101 See references 2, 3, and 4.
103 return(sympy
.collect_common_factors(expr
))
106 return(sympy
.sqrt(expr
))
112 return(type(a
) == types
.IntType
)
114 def make_null_array(n
):
116 Return list of n empty lists.
123 def test_int_flgs(lst
):
125 Test if all elements in list are 0.
134 Calculates the combinations of the integers [0,N-1] taken P at a time.
135 The output is a list of lists of integers where the inner lists are
136 the different combinations. Each combination is sorted in ascending
139 def rloop(n
,p
,combs
,comb
):
144 rloop(i
,np
,combs
,newcomb
)
155 Return string equivalent metric tensor for signature (p,q).
172 def make_scalars(symnamelst
):
174 make_symbols takes a string of symbol names separated by
175 blanks and converts them to MV scalars separately
176 accessible by the main program and addition returns a list
180 if type(symnamelst
) == types
.StringType
:
181 symnamelst
= symnamelst
.split()
184 for name
in symnamelst
:
186 tmp
= MV(tmp
,'scalar')
187 scalar_lst
.append(tmp
)
188 setattr(MAIN_PROGRAM
,name
,tmp
)
192 def make_symbols(symnamelst
):
194 make_symbols takes a string of symbol names separated by
195 blanks and converts them to MV scalars separately
196 accessible by the main program and addition returns a list
200 if type(symnamelst
) == types
.StringType
:
201 symnamelst
= symnamelst
.split()
204 for name
in symnamelst
:
207 setattr(MAIN_PROGRAM
,name
,tmp
)
213 Test if string represents a rational number.
216 if NUMPAT
.match(numstr
):
220 def dualsort(lst1
, lst2
):
222 Inplace dual sort of lst1 and lst2 keyed on sorted lst1.
224 _indices
= range(len(lst1
))
225 _indices
.sort(key
=lst2
.__getitem__
)
226 lst1
[:] = map(lst1
.__getitem__
, _indices
)
227 lst2
[:] = map(lst2
.__getitem__
, _indices
)
232 Calculates the comutator product (A*B-B*A)/2 for
235 return(HALF
*(A
*B
-B
*A
))
238 def pad_zeros(value
,n
):
240 Pad list with zeros to lenght n. If length is > n
241 truncate list. Return padded list.
245 value
= value
+(n
-nvalue
)*[ZERO
]
249 pad_zeros
= staticmethod(pad_zeros
)
251 def define_basis(basis
):
253 Calculates all the MV static variables needed for
254 basis operations. See reference 5 section 2.
257 MV
.vsyms
= make_symbols(MV
.vbasis
)
258 MV
.n
= len(MV
.vbasis
)
261 MV
.n1rg
= range(MV
.n1
)
263 MV
.index
= range(MV
.n
)
265 MV
.basis
= (MV
.n
+1)*[0]
266 MV
.basislabel
= (MV
.n
+1)*[0]
268 MV
.basislabel
[0] = '1'
269 MV
.nbasis
= numpy
.array((MV
.n
+1)*[1],dtype
=numpy
.object)
270 for igrade
in range(1,MV
.n
+1):
271 tmp
= comb(MV
.n
,igrade
)
274 MV
.nbasis
[igrade
] = ntmp
275 MV
.basis
[igrade
] = tmp
277 for i
in range(ntmp
):
281 gradelabels
.append(bstr
)
282 MV
.basislabel
[igrade
] = gradelabels
284 print 'basis strings =',MV
.vbasis
285 print 'basis symbols =',MV
.vsyms
286 print 'basis labels =',MV
.basislabel
287 print 'basis =',MV
.basis
288 print 'grades =',MV
.nbasis
289 print 'index =',MV
.index
291 define_basis
= staticmethod(define_basis
)
293 def define_metric(metric
):
295 Calculates all the MV static variables needed for
296 metric operations. See reference 5 section 2.
300 MV
.metric
= numpy
.array(MV
.n
*[MV
.n
*[ZERO
]],dtype
=numpy
.object)
302 metric
= numpy
.array(MV
.n
*[MV
.n
*['#']],dtype
=numpy
.object)
307 MV
.metric
[i
][j
] = numeric(gij
)
312 gij
= '('+MV
.vbasis
[j
]+'**2)'
313 name
= MV
.vbasis
[j
]+'sq'
315 gij
= '('+MV
.vbasis
[min(i
,j
)]+'.'+MV
.vbasis
[max(i
,j
)]+')'
316 name
= MV
.vbasis
[min(i
,j
)]+'dot'+MV
.vbasis
[max(i
,j
)]
318 MV
.metric
[i
][j
] = tmp
322 setattr(MAIN_PROGRAM
,name
,tmp
)
325 print 'metric =',MV
.metric
327 define_metric
= staticmethod(define_metric
)
329 def define_reciprocal_frame():
331 Calculates unscaled reciprocal vectors (MV.brecp) and scale
332 factor (MV.Esq). The ith scaled reciprocal vector is
333 (1/MV.Esq)*MV.brecp[i]. The pseudoscalar for the set of
334 basis vectors is MV.E.
342 for i
in range(1,MV
.n
):
343 MV
.E
= MV
.E^MV
.bvec
[i
]
344 for i
in range(MV
.n
):
348 for j
in range(MV
.n
):
353 MV
.Esq
= (MV
.E
*MV
.E
)()
354 MV
.Esq_inv
= ONE
/MV
.Esq
355 for i
in range(MV
.n
):
356 MV
.brecp
[i
] = MV
.brecp
[i
]*MV
.Esq_inv
358 define_reciprocal_frame
= staticmethod(define_reciprocal_frame
)
360 def reduce_basis_loop(blst
):
362 Makes one pass through basis product representation for
363 reduction of representation to normal form.
364 See reference 5 section 3.
372 if blst
[istep
] == blst
[jstep
]:
375 blst
= blst
[:istep
]+blst
[jstep
+1:]
378 if len(blst
) <= 1 or jstep
== nblst
-1:
382 return(MV
.metric
[i
][i
],blst
,blst_flg
)
383 if blst
[istep
] > blst
[jstep
]:
384 blst1
= blst
[:istep
]+blst
[jstep
+1:]
385 a1
= TWO
*MV
.metric
[blst
[jstep
]][blst
[istep
]]
386 blst
= blst
[:istep
]+[blst
[jstep
]]+[blst
[istep
]]+blst
[jstep
+1:]
391 return(a1
,blst1
,blst1_flg
,blst
)
394 reduce_basis_loop
= staticmethod(reduce_basis_loop
)
396 def reduce_basis(blst
):
398 Repetively applies reduce_basis_loop to basis
399 product representation untill normal form is
400 realized. See reference 5 section 3.
407 blst_expand
.append([])
408 blst_expand
[0].append([])
409 blst_coef
[0].append(ONE
)
410 return(blst_coef
,blst_expand
)
414 while test_int_flgs(blst_flg
):
415 for i
in range(len(blst_flg
)):
417 tmp
= MV
.reduce_basis_loop(blst_expand
[i
])
422 blst_coef
[i
] = tmp
[0]*blst_coef
[i
]
423 blst_expand
[i
] = tmp
[1]
426 blst_coef
[i
] = -blst_coef
[i
]
428 blst_expand
[i
] = tmp
[3]
429 blst_coef
.append(-blst_coef
[i
]*tmp
[0])
430 blst_expand
.append(tmp
[1])
431 blst_flg
.append(tmp
[2])
432 (blst_coef
,blst_expand
) = MV
.combine_common_factors(blst_coef
,blst_expand
)
433 return(blst_coef
,blst_expand
)
434 reduce_basis
= staticmethod(reduce_basis
)
436 def combine_common_factors(blst_coef
,blst_expand
):
439 for i
in range(MV
.n1
):
440 new_blst_coef
.append([])
441 new_blst_expand
.append([])
442 nfac
= len(blst_coef
)
443 for ifac
in range(nfac
):
444 blen
= len(blst_expand
[ifac
])
445 new_blst_coef
[blen
].append(blst_coef
[ifac
])
446 new_blst_expand
[blen
].append(blst_expand
[ifac
])
447 for i
in range(MV
.n1
):
448 if len(new_blst_coef
[i
]) > 1:
449 MV
.contract(new_blst_coef
[i
],new_blst_expand
[i
])
450 return(new_blst_coef
,new_blst_expand
)
451 combine_common_factors
= staticmethod(combine_common_factors
)
453 def contract(coefs
,bases
):
454 dualsort(coefs
,bases
)
459 if bases
[i
] == bases
[j
]:
476 contract
= staticmethod(contract
)
478 def convert(coefs
,bases
):
481 for igrade
in MV
.n1rg
:
485 nbases
= MV
.nbasis
[igrade
]
486 mv
.mv
[igrade
] = numpy
.array(nbases
*[ZERO
],dtype
=numpy
.object)
487 nbaserg
= range(len(base
))
488 for ibase
in nbaserg
:
490 k
= MV
.basis
[igrade
].index(base
[ibase
])
491 mv
.mv
[igrade
][k
] = coef
[ibase
]
493 mv
.mv
[igrade
] = numpy
.array([coef
[0]],dtype
=numpy
.object)
495 convert
= staticmethod(convert
)
497 def set_str_format(str_mode
=0):
498 MV
.str_mode
= str_mode
500 set_str_format
= staticmethod(set_str_format
)
502 def str_rep(mv
,lst_mode
=0):
504 Converts internal representation of a multivector to a string
505 for outputing. If lst_mode = 1, str_rep outputs a list of
506 strings where each string contains one multivector coefficient
507 concatenated with the corressponding base or blade symbol.
512 mv
.convert_to_blades()
513 labels
= MV
.bladelabel
516 labels
= MV
.basislabel
518 labels
= MV
.bladelabel
520 for igrade
in MV
.n1rg
:
522 if isinstance(mv
.mv
[igrade
],numpy
.ndarray
):
524 for x
in mv
.mv
[igrade
]:
527 if xstr
== '+1' or xstr
== '1' or xstr
== '-1':
528 if xstr
== '+1' or xstr
== '1':
536 xstr
= '+{'+xstr
[1:]+'}'
537 value
+= xstr
+labels
[igrade
][j
]
538 if MV
.str_mode
and not lst_mode
:
548 if len(value
) > 1 and value
[0] == '+':
555 value
= LaTeXstr(value
)
557 str_rep
= staticmethod(str_rep
)
560 MV
.latexflg
= not MV
.latexflg
562 LaTeX
= staticmethod(LaTeX
)
564 def setup(basis
,metric
='',debug
=0):
566 MV.setup initializes the MV class by calculating the static
567 multivector tables required for geometric algebra operations
568 on multivectors. See reference 5 section 2 for details on
569 basis and metric arguments.
577 MV
.rframe_flg
= False
578 if type(basis
) == types
.StringType
:
579 basislst
= basis
.split()
580 MV
.define_basis(basislst
)
581 if len(metric
) > 0 and type(metric
) == types
.StringType
:
582 tmps
= metric
.split(',')
590 MV
.define_metric(metric
)
591 MV
.multiplication_table()
593 MV
.inverse_blade_table()
597 for name
in MV
.vbasis
:
598 bvar
= MV(value
=isym
,mvtype
='basisvector',mvname
=name
)
601 setattr(MAIN_PROGRAM
,name
,bvar
)
603 return('Setup of '+basis
+' complete!')
604 setup
= staticmethod(setup
)
608 Set multivector output to blade representation.
612 print_blades
=staticmethod(print_blades
)
616 Set multivector output to base representation.
620 print_bases
=staticmethod(print_bases
)
622 def multiplication_table():
624 Calculate geometric product base multiplication table.
625 See reference 5 section 3 for details.
628 for igrade
in MV
.n1rg
:
630 for ibase
in range(MV
.nbasis
[igrade
]):
631 MV
.mtable
[igrade
].append([])
635 base1
= MV
.basis
[igrade
][ibase
]
636 for jgrade
in MV
.n1rg
:
637 MV
.mtable
[igrade
][ibase
].append([])
638 for jbase
in range(MV
.nbasis
[jgrade
]):
642 base2
= MV
.basis
[jgrade
][jbase
]
644 (coefs
,bases
) = MV
.reduce_basis(base
)
645 product
= MV
.convert(coefs
,bases
)
646 product
.name
= '('+MV
.basislabel
[igrade
][ibase
]+')('+MV
.basislabel
[jgrade
][jbase
]+')'
647 MV
.mtable
[igrade
][ibase
][jgrade
].append(product
)
649 print 'Multiplication Table:'
650 for level1
in MV
.mtable
:
651 for level2
in level1
:
652 for level3
in level2
:
656 multiplication_table
= staticmethod(multiplication_table
)
658 def geometric_product(mv1
,mv2
):
660 MV.geometric_product(mv1,mv2) calculates the geometric
661 product the multivectors mv1 and mv2 (mv1*mv2). See
662 reference 5 section 3.
665 if isinstance(mv1
,MV
) and isinstance(mv2
,MV
):
666 bladeflg1
= mv1
.bladeflg
667 bladeflg2
= mv2
.bladeflg
669 mv1
.convert_from_blades()
671 mv2
.convert_from_blades()
673 for igrade
in MV
.n1rg
:
674 gradei
= mv1
.mv
[igrade
]
675 if isinstance(gradei
,numpy
.ndarray
):
676 for ibase
in range(MV
.nbasis
[igrade
]):
679 for jgrade
in MV
.n1rg
:
680 gradej
= mv2
.mv
[jgrade
]
681 if isinstance(gradej
,numpy
.ndarray
):
682 for jbase
in range(MV
.nbasis
[jgrade
]):
685 xixj
= MV
.mtable
[igrade
][ibase
][jgrade
][jbase
].scalar_mul(xi
*xj
)
686 product
.add_in_place(xixj
)
689 mv1
.convert_to_blades()
691 mv1
.convert_to_blades()
692 if bladeflg1
and bladeflg2
:
693 product
.convert_to_blades()
695 if isinstance(mv1
,MV
):
696 product
= mv1
.scalar_mul(mv2
)
698 product
= mv2
.scalar_mul(mv1
)
700 geometric_product
= staticmethod(geometric_product
)
702 def wedge(igrade1
,blade1
,vector2
,name
=''):
704 Calculate the outer product of a multivector blade
705 and a vector. See reference 5 section 5 for details.
715 wedge
= staticmethod(wedge
)
719 Calculate basis blades in terms of bases. See reference 5
720 section 5 for details. Used to convert from blade to base
725 basis_str
= MV
.basislabel
[1]
726 for igrade
in MV
.n1rg
:
727 MV
.bladelabel
.append([])
729 MV
.bladelabel
[0].append('1')
730 tmp
= [MV(value
=ONE
,mvtype
='scalar',mvname
='1')]
733 for ibase
in range(MV
.nbasis
[1]):
734 MV
.bladelabel
[1].append(basis_str
[ibase
])
735 tmp
.append(MV(value
=ibase
,mvtype
='basisvector',mvname
=basis_str
[ibase
]))
738 basis
= MV
.basis
[igrade
]
739 nblades
= MV
.nbasis
[igrade
]
744 name
+= basis_str
[i
]+'^'
746 MV
.bladelabel
[igrade
].append(name
)
747 lblade
= MV
.basis
[igrade
-1].index(blade
[:-1])
750 blade1
= MV
.btable
[igrade1
][lblade
]
751 vector2
= MV
.btable
[1][rblade
]
752 b1Wv2
= MV
.wedge(igrade1
,blade1
,vector2
,name
)
754 MV
.btable
.append(tmp
)
757 for grade
in MV
.btable
:
760 print 'Blade Labels:'
763 blade_table
= staticmethod(blade_table
)
765 def inverse_blade_table():
767 Calculate bases in terms of basis blades. See reference 5
768 section 5 for details. Used to convert from base to blade
772 for igrade
in MV
.n1rg
:
774 tmp
= [MV(value
=ONE
,mvtype
='scalar',mvname
='1')]
777 for ibase
in range(MV
.nbasis
[1]):
778 tmp
.append(MV(value
=ibase
,mvtype
='basisvector'))
782 for blade
in MV
.btable
[igrade
]:
784 for i
in range(igrade
-1):
785 invblade
.mv
[i
] = -blade
.mv
[i
]
786 invblade
.mv
[igrade
] = +blade
.mv
[igrade
]
787 invblade
.bladeflg
= 1
791 for ibase
in range(MV
.nbasis
[jgrade
]):
792 invblade
.substitute_base(jgrade
,ibase
,MV
.ibtable
[jgrade
][ibase
])
794 invblade
.name
= MV
.basislabel
[igrade
][iblade
]
797 MV
.ibtable
.append(tmp
)
799 print 'Inverse Blade Tabel:'
800 for grade
in MV
.ibtable
:
804 inverse_blade_table
= staticmethod(inverse_blade_table
)
806 def outer_product(mv1
,mv2
):
808 MV.outer_product(mv1,mv2) calculates the outer (exterior,wedge)
809 product of the multivectors mv1 and mv2 (mv1^mv2). See
810 reference 5 section 6.
812 if isinstance(mv1
,MV
) and isinstance(mv2
,MV
):
815 mv1
.convert_to_blades()
816 mv2
.convert_to_blades()
817 for igrade1
in MV
.n1rg
:
818 if isinstance(mv1
.mv
[igrade1
],numpy
.ndarray
):
819 pg1
= mv1
.project(igrade1
)
820 for igrade2
in MV
.n1rg
:
821 igrade
= igrade1
+igrade2
823 if isinstance(mv2
.mv
[igrade2
],numpy
.ndarray
):
824 pg2
= mv2
.project(igrade2
)
826 product
.add_in_place(pg1pg2
.project(igrade
))
828 if isinstance(mv1
,MV
):
829 product
= mv1
.scalar_mul(mv2
)
831 product
= mv2
.scalar_mul(mv1
)
833 outer_product
= staticmethod(outer_product
)
835 def inner_product(mv1
,mv2
):
837 MV.inner_product(mv1,mv2) calculates the inner (scalar,dot)
838 product of the multivectors mv1 and mv2 (mv1|mv2). See
839 reference 5 section 6.
841 if isinstance(mv1
,MV
) and isinstance(mv2
,MV
):
844 mv1
.convert_to_blades()
845 mv2
.convert_to_blades()
846 for igrade1
in range(1,MV
.n1
):
847 if isinstance(mv1
.mv
[igrade1
],numpy
.ndarray
):
848 pg1
= mv1
.project(igrade1
)
849 for igrade2
in range(1,MV
.n1
):
850 igrade
= (igrade1
-igrade2
).__abs
__()
851 if isinstance(mv2
.mv
[igrade2
],numpy
.ndarray
):
852 pg2
= mv2
.project(igrade2
)
854 product
.add_in_place(pg1pg2
.project(igrade
))
857 inner_product
= staticmethod(inner_product
)
859 def addition(mv1
,mv2
):
861 MV.addition(mv1,mv2) calculates the sum
862 of the multivectors mv1 and mv2 (mv1+mv2).
865 if isinstance(mv1
,MV
) and isinstance(mv2
,MV
):
866 if mv1
.bladeflg
or mv2
.bladeflg
:
867 mv1
.convert_to_blades()
868 mv2
.convert_to_blades()
871 if isinstance(mv1
.mv
[i
],numpy
.ndarray
) and isinstance(mv2
.mv
[i
],numpy
.ndarray
):
872 sum.mv
[i
] = mv1
.mv
[i
]+mv2
.mv
[i
]
874 if isinstance(mv1
.mv
[i
],numpy
.ndarray
) and not isinstance(mv2
.mv
[i
],numpy
.ndarray
):
875 sum.mv
[i
] = +mv1
.mv
[i
]
877 if isinstance(mv2
.mv
[i
],numpy
.ndarray
) and not isinstance(mv1
.mv
[i
],numpy
.ndarray
):
878 sum.mv
[i
] = +mv2
.mv
[i
]
880 if isinstance(mv1
,MV
):
882 if isinstance(sum.mv
[0],numpy
,ndarray
):
883 sum.mv
[0] += numpy
.array([mv2
],dtype
=numpy
.object)
885 sum.mv
[0] = numpy
.array([mv2
],dtype
=numpy
.object)
888 if isinstance(sum.mv
[0],numpy
.ndarray
):
889 sum.mv
[0] += numpy
.array([mv1
],dtype
=numpy
.object)
891 sum.mv
[0] = numpy
.array([mv1
],dtype
=numpy
.object)
893 addition
= staticmethod(addition
)
895 def subtraction(mv1
,mv2
):
897 MV.subtraction(mv1,mv2) calculates the difference
898 of the multivectors mv1 and mv2 (mv1-mv2).
901 if isinstance(mv1
,MV
) and isinstance(mv2
,MV
):
902 if mv1
.bladeflg
or mv2
.bladeflg
:
903 mv1
.convert_to_blades()
904 mv2
.convert_to_blades()
907 if isinstance(mv1
.mv
[i
],numpy
.ndarray
) and isinstance(mv2
.mv
[i
],numpy
.ndarray
):
908 diff
.mv
[i
] = mv1
.mv
[i
]-mv2
.mv
[i
]
910 if isinstance(mv1
.mv
[i
],numpy
.ndarray
) and not isinstance(mv2
.mv
[i
],numpy
.ndarray
):
911 diff
.mv
[i
] = +mv1
.mv
[i
]
913 if not isinstance(mv1
.mv
[i
],numpy
.ndarray
) and isinstance(mv2
.mv
[i
],numpy
.ndarray
):
914 diff
.mv
[i
] = -mv2
.mv
[i
]
916 if isinstance(mv1
,MV
):
918 if isinstandce(diff
.mv
[0],numpy
.ndarray
):
919 diff
.mv
[0] -= numpy
.array([mv2
],dtype
=numpy
.object)
921 diff
.mv
[0] = -numpy
.array([mv2
],dtype
=numpy
.object)
924 if isinstance(diff
.mv
[0],numpy
.ndarray
):
925 diff
.mv
[0] += numpy
.array([mv1
],dtype
=numpy
.object)
927 diff
.mv
[0] = +numpy
.array([mv1
],dtype
=numpy
.object)
929 subtraction
= staticmethod(subtraction
)
931 def scalar_to_symbol(scalar
):
932 if isinstance(scalar
,MV
):
933 return(scalar
.mv
[0][0])
934 if type(scalar
) == types
.ListType
:
937 sym
.append(MV
.scalar_to_symbol(x
))
940 scalar_to_symbol
= staticmethod(scalar_to_symbol
)
942 def __init__(self
,value
='',mvtype
='',mvname
=''):
944 Initialization of multivector X. Inputs are as follows
948 default default Zero multivector
949 'basisvector' int i ith basis vector
950 'basisbivector' int i ith basis bivector
951 'scalar' symbol x scalar of value x
952 'grade' [int i, symbol array A] X.grade(i) = A
953 'vector' symbol array A X.grade(1) = A
954 'grade2' symbol array A X.grade(2) = A
956 mvname is name of multivector.
960 self
.bladeflg
= 0 #1 for blade expansion
962 if mvtype
== 'basisvector':
963 self
.mv
[1] = numpy
.array(MV
.nbasis
[1]*[ZERO
],dtype
=numpy
.object)
964 self
.mv
[1][value
] = ONE
965 if mvtype
== 'basisbivector':
966 self
.mv
[2] = numpy
.array(MV
.nbasis
[2]*[ZERO
],dtype
=numpy
.object)
967 self
.mv
[2][value
] = ONE
968 if mvtype
== 'scalar':
969 self
.mv
[0] = numpy
.array([value
],dtype
=numpy
.object)
970 if mvtype
== 'grade':
973 coefs
= MV
.pad_zeros(coefs
,MV
.nbasis
[igrade
])
974 self
.mv
[igrade
] = numpy
.array(coefs
,dtype
=numpy
.object)
975 if mvtype
== 'vector':
976 value
= MV
.pad_zeros(value
,MV
.nbasis
[1])
977 self
.mv
[1] = numpy
.array(value
,dtype
=numpy
.object)
978 if mvtype
== 'grade2':
979 value
= MV
.pad_zeros(value
,MV
.nbasis
[2])
980 self
.mv
[2] = numpy
.array(value
,dtype
=numpy
.object)
984 X.max_grade() is maximum grade of non-zero grades of X.
986 for i
in range(MV
.n
,-1,-1):
987 if isinstance(self
.mv
[i
],numpy
.ndarray
):
991 def coord(xname
,offset
=0):
994 xi_str
+= xname
+str(i
+offset
)+' '
995 xi
= make_symbols(xi_str
)
998 coord
= staticmethod(coord
)
1001 if isint(self
.mv
[1]):
1003 return(self
.mv
[1][i
])
1005 def named(mvname
,value
='',mvtype
=''):
1007 tmp
= MV(value
=value
,mvtype
=mvtype
,mvname
=name
)
1008 setattr(sys
.modules
[__name__
],name
,tmp
)
1010 named
=staticmethod(named
)
1014 print a
.name
,' =',a
.mv
1016 printnm
= staticmethod(printnm
)
1019 """See MV.str_rep(self)"""
1020 return(MV
.str_rep(self
))
1022 def printmv(self
,name
=''):
1028 title
+= self
.name
+' = '
1029 print title
+MV
.str_rep(self
)
1032 def add_in_place(self
,mv
):
1034 X.add_in_place(mv) increments multivector X by multivector
1037 if self
.bladeflg
or mv
.bladeflg
:
1038 self
.convert_to_blades()
1039 mv
.convert_to_blades()
1041 if isinstance(self
.mv
[i
],numpy
.ndarray
) and isinstance(mv
.mv
[i
],numpy
.ndarray
):
1042 self
.mv
[i
] += mv
.mv
[i
]
1044 if not isinstance(self
.mv
[i
],numpy
.ndarray
) and isinstance(mv
.mv
[i
],numpy
.ndarray
):
1045 self
.mv
[i
] = +mv
.mv
[i
]
1048 def sub_in_place(self
,mv
):
1050 X.sub_in_place(mv) decrements multivector X by multivector
1053 if self
.bladeflg
or mv
.bladeflg
:
1054 self
.convert_to_blades()
1055 mv
.convert_to_blades()
1057 if isinstance(self
.mv
[i
],numpy
.ndarray
) and isinstance(mv
.mv
[i
],numpy
.ndarray
):
1058 self
.mv
[i
] -= mv
.mv
[i
]
1060 if not isinstance(self
.mv
[i
],numpy
.ndarray
) and isinstance(mv
.mv
[i
],numpy
.ndarray
):
1061 self
.mv
[i
] = +mv
.mv
[i
]
1066 p
.bladeflg
= self
.bladeflg
1068 if isinstance(self
.mv
[i
],numpy
.ndarray
):
1069 p
.mv
[i
] = +self
.mv
[i
]
1074 n
.bladeflg
= self
.bladeflg
1076 if isinstance(self
.mv
[i
],numpy
.ndarray
):
1077 n
.mv
[i
] = -self
.mv
[i
]
1080 def __add_ab__(self
,mv
):
1081 self
.add_in_place(mv
)
1084 def __sub_ab__(self
,mv
):
1085 self
.sub_in_place(mv
)
1088 def __add__(self
,mv
):
1089 """See MV.addition(self,mv)"""
1090 return(MV
.addition(self
,mv
))
1092 def __radd__(self
,mv
):
1093 """See MV.addition(mv,self)"""
1094 return(MV
.addition(mv
,self
))
1096 def __sub__(self
,mv
):
1097 """See MV.subtraction(self,mv)"""
1098 return(MV
.subtraction(self
,mv
))
1100 def __rsub__(self
,mv
):
1101 """See MV.subtraction(mv,self)"""
1102 return(MV
.subtraction(mv
,self
))
1104 def __xor__(self
,mv
):
1105 """See MV.outer_product(self,mv)"""
1106 return(MV
.outer_product(self
,mv
))
1108 def __pow__(self
,mv
):
1109 """See MV.outer_product(self,mv)"""
1110 return(MV
.outer_product(self
,mv
))
1112 def __rxor__(self
,mv
):
1113 """See MV.outer_product(mv,self)"""
1114 return(MV
.outer_product(mv
,self
))
1116 def __or__(self
,mv
):
1117 """See MV.inner_product(self,mv)"""
1118 return(MV
.inner_product(self
,mv
))
1120 def __ror__(self
,mv
):
1121 """See MV.inner_product(mv,self)"""
1122 return(MV
.inner_product(mv
,self
))
1124 def scalar_mul(self
,c
):
1126 Y = X.scalar_mul(c), multiply multivector X by scalar c and return
1130 mv
.bladeflg
= self
.bladeflg
1131 mv
.puregrade
= self
.puregrade
1133 if isinstance(self
.mv
[i
],numpy
.ndarray
):
1134 mv
.mv
[i
] = self
.mv
[i
]*c
1137 def scalar_mul_inplace(self
,c
):
1139 X.scalar_mul_inplace(c), multiply multivector X by scalar c and save
1143 if isinstance(self
.mv
[i
],numpy
.ndarray
):
1144 self
.mv
[i
] = self
.mv
[i
]*c
1147 def __mul__(self
,mv
):
1148 """See MV.geometric_product(self,mv)"""
1149 return(MV
.geometric_product(self
,mv
))
1151 def __rmul__(self
,mv
):
1152 """See MV.geometric_product(mv,self)"""
1153 return(MV
.geometric_product(mv
,self
))
1155 def __div__(self
,scalar
):
1157 div
.bladeflg
= self
.bladeflg
1159 if isinstance(self
.mv
[i
],numpy
.ndarray
):
1160 div
.mv
[i
] = self
.mv
[i
]/scalar
1163 def __div_ab__(self
,scalar
):
1165 if isinstance(self
.mv
[i
],numpy
.ndarray
):
1166 self
.mv
[i
] /= scalar
1169 def __call__(self
,igrade
=0,ibase
=0):
1171 X(i,j) returns symbol in ith grade and jth base or blade of
1174 if not isinstance(self
.mv
[igrade
],numpy
.ndarray
):
1176 return(self
.mv
[igrade
][ibase
])
1178 def __eq__(self
,mv
):
1179 if not isinstance(mv
,MV
):
1181 for (mvi
,mvj
) in zip(self
.mv
,mv
.mv
):
1182 if isint(mvi
) ^
isint(mvj
):
1184 if isinstance(mvi
,numpy
.ndarray
) and isinstance(mvj
,numpy
.ndarray
):
1185 for (x
,y
) in zip(mvi
,mvj
):
1190 def copy(self
,sub
=0):
1192 Y = X.copy(), make a deep copy of multivector X in multivector
1193 Y so that Y can be modified without affecting X.
1196 cpy
.name
= self
.name
1197 cpy
.bladeflg
= self
.bladeflg
1198 cpy
.puregrade
= self
.puregrade
1201 if isinstance(self
.mv
[i
],numpy
.ndarray
):
1202 cpy
.mv
[i
] = -self
.mv
[i
]
1204 if isinstance(self
.mv
[i
],numpy
.ndarray
):
1205 cpy
.mv
[i
] = +self
.mv
[i
]
1208 def substitute_base(self
,igrade
,base
,mv
):
1209 if not isinstance(self
.mv
[igrade
],numpy
.ndarray
):
1211 if isinstance(base
,numpy
.ndarray
):
1212 ibase
= MV
.basis
[igrade
].index(base
)
1215 coef
= self
.mv
[igrade
][ibase
]
1218 self
.mv
[igrade
][ibase
] = ZERO
1219 self
.add_in_place(mv
*coef
)
1222 def convert_to_blades(self
):
1224 X.convert_to_blades(), inplace convert base representation
1225 to blade representation. See reference 5 section 5.
1230 for igrade
in range(2,MV
.n1
):
1231 if isinstance(self
.mv
[igrade
],numpy
.ndarray
):
1232 for ibase
in range(MV
.nbasis
[igrade
]):
1233 coef
= self
.mv
[igrade
][ibase
]
1234 if (not coef
== ZERO
):
1235 self
.mv
[igrade
][ibase
] = ZERO
1236 self
.add_in_place(MV
.ibtable
[igrade
][ibase
]*coef
)
1239 def convert_from_blades(self
):
1241 X.convert_from_blades(), inplace convert blade representation
1242 to base representation. See reference 5 section 5.
1244 if not self
.bladeflg
:
1247 for igrade
in range(2,MV
.n1
):
1248 if isinstance(self
.mv
[igrade
],numpy
.ndarray
):
1249 for ibase
in range(MV
.nbasis
[igrade
]):
1250 coef
= self
.mv
[igrade
][ibase
]
1251 if (not coef
== ZERO
):
1252 self
.mv
[igrade
][ibase
] = ZERO
1253 self
.add_in_place(MV
.btable
[igrade
][ibase
]*coef
)
1256 def project(self
,r
):
1258 Grade projection operator. For multivector X, X.project(r)
1259 returns multivector of grade r components of X.
1264 self
.convert_to_blades()
1265 if not isinstance(self
.mv
[r
],numpy
.ndarray
):
1267 grade_r
.bladeflg
= 1
1268 grade_r
.puregrade
= 1
1269 grade_r
.mv
[r
] = +self
.mv
[r
]
1274 Even grade projection operator. For multivector X, X.even()
1275 returns multivector of even grade components of X.
1278 self
.convert_to_blades()
1279 egrades
.bladeflg
= self
.bladeflg
1280 egrades
.puregrade
= self
.puregrade
1281 for igrade
in range(0,MV
.n1
,2):
1282 egrades
.mv
[igrade
] = +self
.mv
[igrade
]
1287 Odd grade projection operator. For multivector X, X.odd()
1288 returns multivector of odd grade components of X.
1291 self
.convert_to_blades()
1292 ogrades
.bladeflg
= self
.bladeflg
1293 ogrades
.puregrade
= self
.puregrade
1294 for igrade
in range(1,MV
.n1
,2):
1295 ogrades
.mv
[igrade
] = +self
.mv
[igrade
]
1300 Revisioin operator. For multivector X, X.rev()
1301 returns reversed multivector of X.
1304 self
.convert_to_blades()
1305 revmv
.bladeflg
= self
.bladeflg
1306 revmv
.puregrade
= self
.puregrade
1307 for igrade
in MV
.n1rg
:
1308 if isinstance(self
.mv
[igrade
],numpy
.ndarray
):
1309 if igrade
< 2 or (not (((igrade
*(igrade
-1))/2)%2)):
1310 revmv
.mv
[igrade
] = +self
.mv
[igrade
]
1312 revmv
.mv
[igrade
] = -self
.mv
[igrade
]
1315 def collect(self
,lst
):
1317 Applies sympy/sympy collect function to each component
1320 for igrade
in MV
.n1rg
:
1321 if isinstance(self
.mv
[igrade
],numpy
.ndarray
):
1322 for ibase
in range(MV
.nbasis
[igrade
]):
1323 if self
.mv
[igrade
][ibase
] != ZERO
:
1324 self
.mv
[igrade
][ibase
] = \
1325 collect(self
.mv
[igrade
][ibase
],lst
)
1326 self
.mv
[igrade
][ibase
] = \
1327 collect(self
.mv
[igrade
][ibase
],lst
)
1330 def sqrfree(self
,lst
):
1332 Applies sympy/sympy sqrfree function to each component
1335 for igrade
in MV
.n1rg
:
1336 if isinstance(self
.mv
[igrade
],numpy
.ndarray
):
1337 for ibase
in range(MV
.nbasis
[igrade
]):
1338 if self
.mv
[igrade
][ibase
] != ZERO
:
1339 self
.mv
[igrade
][ibase
] = \
1340 sqrfree(self
.mv
[igrade
][ibase
],lst
)
1343 def collect(self
,faclst
):
1345 Applies sympy collect_common_factors function
1346 to each component of multivector.
1348 for igrade
in MV
.n1rg
:
1349 if isinstance(self
.mv
[igrade
],numpy
.ndarray
):
1350 for ibase
in range(MV
.nbasis
[igrade
]):
1351 if self
.mv
[igrade
][ibase
] != ZERO
:
1352 self
.mv
[igrade
][ibase
] = \
1353 sympy
.collect(self
.mv
[igrade
][ibase
],faclst
)
1356 def subs(self
,var
,substitute
):
1358 Applies sympy subs function
1359 to each component of multivector.
1361 for igrade
in MV
.n1rg
:
1362 if isinstance(self
.mv
[igrade
],numpy
.ndarray
):
1363 for ibase
in range(MV
.nbasis
[igrade
]):
1364 if self
.mv
[igrade
][ibase
] != ZERO
:
1365 self
.mv
[igrade
][ibase
] = \
1366 self
.mv
[igrade
][ibase
].subs(var
,substitute
)
1371 Applies sympy subs function
1372 to each component of multivector.
1374 for igrade
in MV
.n1rg
:
1375 if isinstance(self
.mv
[igrade
],numpy
.ndarray
):
1376 for ibase
in range(MV
.nbasis
[igrade
]):
1377 if self
.mv
[igrade
][ibase
] != ZERO
:
1378 self
.mv
[igrade
][ibase
] = \
1379 sympy
.simplify(self
.mv
[igrade
][ibase
])
1384 Applies sympy cancle function
1385 to each component of multivector.
1387 for igrade
in MV
.n1rg
:
1388 if isinstance(self
.mv
[igrade
],numpy
.ndarray
):
1389 for ibase
in range(MV
.nbasis
[igrade
]):
1390 if self
.mv
[igrade
][ibase
] != ZERO
:
1391 self
.mv
[igrade
][ibase
] = \
1392 sympy
.cancel(self
.mv
[igrade
][ibase
])
1397 Applies sympy cancle function
1398 to each component of multivector.
1400 for igrade
in MV
.n1rg
:
1401 if isinstance(self
.mv
[igrade
],numpy
.ndarray
):
1402 for ibase
in range(MV
.nbasis
[igrade
]):
1403 if self
.mv
[igrade
][ibase
] != ZERO
:
1404 self
.mv
[igrade
][ibase
] = \
1405 sympy
.trim(self
.mv
[igrade
][ibase
])
1410 Applies sympy/sympy expand function to each component
1413 for igrade
in MV
.n1rg
:
1414 if isinstance(self
.mv
[igrade
],numpy
.ndarray
):
1415 for ibase
in range(MV
.nbasis
[igrade
]):
1416 if self
.mv
[igrade
][ibase
] != ZERO
:
1417 self
.mv
[igrade
][ibase
] = \
1418 self
.mv
[igrade
][ibase
].expand()
1425 if isinstance(self
.mv
[i
],numpy
.ndarray
):
1434 Convert zero numpy arrays to single interge zero place holder
1435 in grade list for instanciated multivector. For example if
1436 numpy array of grade one components is a zero array then replace
1437 with single integer equal to zero.
1440 if not isint(self
.mv
[i
]):
1442 for x
in self
.mv
[i
]:
1451 ## def __init__(self,fct_lst):
1453 ## Initialize linear transformation. fct_lst is a list of vectors A_{i}
1454 ## such that A_{i} = LT(a_{i}) where the a_{i} are the basis vectos of
1457 ## self.fct_lst = fct_lst
1459 ## self.ltblades = [[],self.fct_lst]
1460 ## for igrade in range(2,MV.n1):
1462 ## for ibasis in range(MV.nbasis[igrade]):
1463 ## ilst = MV.basis[igrade][ibasis][:-1]
1464 ## jlst = MV.basis[igrade-1]
1465 ## iblade = jlst.index(ilst)
1466 ## jblade = MV.basis[igrade][ibasis][-1]
1467 ## tmp.append(self.ltblades[igrade-1][iblade]^self.fct_lst[jblade])
1468 ## self.ltblades.append(tmp)
1471 ## def Fvector(self,mv):
1473 ## Return the linear transformation of an arbitrary vector.
1475 ## mv.convert_to_blades()
1476 ## if mv.is_pure() == 1:
1478 ## fofa.bladeflg = 1
1479 ## for i in range(MV.nbasis[1]):
1480 ## fofa += mv.mv[1][i]*self.fct_lst[i]
1481 ## fofa.collect_common_factors()
1482 ## return(fofa/self.norm)
1483 ## print 'Error in LT.F, mv =',mv,' not a vector!'
1486 ## def __call__(self,mv):
1488 ## Return the linear transformation of an arbitrary multivector.
1490 ## if mv.is_pure() == 1:
1491 ## return(self.Fvector(mv))
1493 ## mv.convert_to_blades()
1494 ## fofx.bladeflg = 1
1495 ## for igrade in range(1,MV.n1):
1496 ## if isinstance(mv.mv[igrade],numpy.ndarray):
1497 ## for ibase in range(MV.nbasis[igrade]):
1498 ## if mv.mv[igrade][ibase] != ZERO:
1499 ## fofx.add_in_place(mv.mv[igrade][ibase]*self.ltblades[igrade][ibase])
1500 ## fofx.collect_common_factors()
1501 ## return(fofx/self.norm)
1503 ## def __str__(self):
1505 ## for ibasis in range(MV.n):
1506 ## if self.norm != ONE:
1507 ## LTstr += 'F('+MV.basislabel[1][ibasis]+') = ('
1509 ## LTstr += 'F('+MV.basislabel[1][ibasis]+') = '
1510 ## LTstr += MV.str_rep(self.fct_lst[ibasis])
1511 ## if self.norm != ONE:
1512 ## LTstr += ')/('+str(self.norm)+')\n'
1515 ## return(LTstr[:-1])
1518 ## MV.define_reciprocal()
1521 ## norm = norm*self.norm
1522 ## for ibasis in range(MV.n):
1525 ## for jbasis in range(MV.n):
1526 ## tmp.add_in_place(MV.brecp[jbasis]*(MV.bvec[ibasis]|self.fct_lst[jbasis]))