5
5
import operator
6
6
import copy
7
7
from collections import OrderedDict
8
- from itertools import combinations
8
+ import itertools
9
9
import functools
10
10
from functools import reduce
11
- from typing import Tuple , TypeVar , Callable , Mapping
11
+ from typing import Tuple , TypeVar , Callable , Mapping , Sequence
12
12
13
13
from sympy import (
14
14
diff , Rational , Symbol , S , Mul , Add ,
27
27
zero = S (0 )
28
28
29
29
30
+ # needed to avoid clashes below
31
+ _mv = mv
32
+
33
+
30
34
def all_same (items ):
31
35
return all (x == items [0 ] for x in items )
32
36
@@ -832,7 +836,7 @@ def _build_bases(self):
832
836
# index list for multivector bases and blades by grade
833
837
basis_indexes = tuple (self .n_range )
834
838
self .indexes = GradedTuple (
835
- tuple (combinations (basis_indexes , i ))
839
+ tuple (itertools . combinations (basis_indexes , i ))
836
840
for i in range (len (basis_indexes ) + 1 )
837
841
)
838
842
@@ -1978,7 +1982,8 @@ def connection(self, rbase, key_base, mode, left):
1978
1982
self .connect [mode_key ].append ((key , C ))
1979
1983
return C
1980
1984
1981
- def ReciprocalFrame (self , basis , mode = 'norm' ):
1985
+ # need _mv as mv would refer to the method!
1986
+ def ReciprocalFrame (self , basis : Sequence [_mv .Mv ], mode : str = 'norm' ) -> Tuple [_mv .Mv ]:
1982
1987
"""
1983
1988
Compute the reciprocal frame of a set of vectors
1984
1989
@@ -1990,51 +1995,43 @@ def ReciprocalFrame(self, basis, mode='norm'):
1990
1995
normalization coefficient should be appended to the returned tuple.
1991
1996
One can divide by this coefficient to normalize the vectors.
1992
1997
"""
1993
- dim = len (basis )
1994
-
1995
- indexes = tuple (range (dim ))
1996
- index = [()]
1997
-
1998
- for i in indexes [- 2 :]:
1999
- index .append (tuple (combinations (indexes , i + 1 )))
2000
-
2001
- MFbasis = []
2002
-
2003
- for igrade in index [- 2 :]:
2004
- grade = []
2005
- for iblade in igrade :
2006
- blade = self .mv (S (1 ), 'scalar' )
2007
- for ibasis in iblade :
2008
- blade ^= basis [ibasis ]
2009
- blade = blade .trigsimp ()
2010
- grade .append (blade )
2011
- MFbasis .append (grade )
2012
- E = MFbasis [- 1 ][0 ]
2013
- E_sq = trigsimp ((E * E ).scalar ())
2014
1998
2015
- duals = copy .copy (MFbasis [- 2 ])
1999
+ def wedge_reduce (mvs ):
2000
+ """ wedge together a list of multivectors """
2001
+ if not mvs :
2002
+ return self .mv (S (1 ), 'scalar' )
2003
+ return functools .reduce (operator .xor , mvs ).trigsimp ()
2004
+
2005
+ E = wedge_reduce (basis )
2006
+
2007
+ # elements are such that `basis[i] ^ co_basis[i] == E`
2008
+ co_basis = [
2009
+ sign * wedge_reduce (basis_subset )
2010
+ for sign , basis_subset in zip (
2011
+ # alternating signs
2012
+ itertools .cycle ([S (1 ), S (- 1 )]),
2013
+ # tuples with one basis missing
2014
+ itertools .combinations (basis , len (basis ) - 1 ),
2015
+ )
2016
+ ]
2016
2017
2017
- duals .reverse ()
2018
- sgn = S (1 )
2019
- rbasis = []
2020
- for dual in duals :
2021
- recpv = (sgn * dual * E ).trigsimp ()
2022
- rbasis .append (recpv )
2023
- sgn = - sgn
2018
+ # take the dual without normalization
2019
+ r_basis = [(co_base * E ).trigsimp () for co_base in co_basis ]
2024
2020
2021
+ # normalize
2022
+ E_sq = trigsimp ((E * E ).scalar ())
2025
2023
if mode == 'norm' :
2026
- for i in range (dim ):
2027
- rbasis [i ] = rbasis [i ] / E_sq
2024
+ r_basis = [r_base / E_sq for r_base in r_basis ]
2028
2025
else :
2029
2026
if mode != 'append' :
2030
2027
# galgebra 0.5.0
2031
2028
warnings .warn (
2032
2029
"Mode {!r} not understood, falling back to {!r} but this "
2033
2030
"is deprecated" .format (mode , 'append' ),
2034
2031
DeprecationWarning , stacklevel = 2 )
2035
- rbasis .append (E_sq )
2032
+ r_basis .append (E_sq )
2036
2033
2037
- return tuple (rbasis )
2034
+ return tuple (r_basis )
2038
2035
2039
2036
def Mlt (self , * args , ** kwargs ):
2040
2037
return lt .Mlt (args [0 ], self , * args [1 :], ** kwargs )
0 commit comments