Skip to content

Commit e46f829

Browse files
eric-wieserutensil
authored andcommitted
Correct information about blades of grade 0 (#90)
This: * Since the scalar 1 is a blade of grade 0: * Changes `Ga.blades[0]` from `[]` to `[1]` * Changes `Ga.blade_super_scripts[0]` from `[]` to `['']` * Changes `Ga.bases[0]` from `[]` to `[1]` * Changes `Ga.indexes[0]` from `()` (no indices) to `((),)` (a single empty index) * Introduces some private lists, which include this scalar element, and use them throughout internally: * `Ga._all_blades_lst` (Previously called `blades_lst0`, which has been removed) * `Ga._all_bases_lst` * `Ga._all_indexes_lst` * `Ga._all_mv_blades_lst` gh-64 tracks changing the public lists to include the scalar, which would be a breaking change * As a result of using these internally, the following attributes now also contain the scalar: * `Ga.blade_expansion_dict` * `Ga.base_expansion_dict` * `Ga.bases_to_indexes` * `Ga.bases_to_indexes_dict` * `Ga.bases_to_grades_dict` The hope is this can in future be used to eliminate a lot of special-casing of scalars. This fixes gh-82, although `ga.mv('name', 'grade', 0)` still produces somewhat strange if now correct results.
1 parent db0cd3b commit e46f829

File tree

3 files changed

+73
-59
lines changed

3 files changed

+73
-59
lines changed

galgebra/ga.py

Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -197,13 +197,13 @@ class Ga(metric.Metric):
197197
198198
.. attribute:: bases
199199
200-
List of bases (non-commutative sympy symbols). Only created for
201-
non-orthogonal basis vectors.
200+
List of bases (non-commutative sympy symbols) by grade.
201+
Only created for non-orthogonal basis vectors.
202202
203203
.. attribute:: blades
204204
205-
List of basis blades (non-commutative sympy symbols). For
206-
orthogonal basis vectors the same as bases.
205+
List of basis blades (non-commutative sympy symbols) by grade.
206+
For orthogonal basis vectors the same as bases.
207207
208208
.. attribute:: coord_vec
209209
@@ -426,7 +426,7 @@ def __init__(self, bases, **kwargs):
426426
if self.coords is not None:
427427
self.coords = list(self.coords)
428428

429-
self.e = mv.Mv(self.blades_lst[-1], ga=self) # Pseudo-scalar for geometric algebra
429+
self.e = mv.Mv(self._all_blades_lst[-1], ga=self) # Pseudo-scalar for geometric algebra
430430
self.e_sq = simplify(expand((self.e*self.e).scalar()))
431431

432432
if self.coords is not None:
@@ -601,7 +601,7 @@ def bases_dict(self, prefix=None):
601601
'''
602602
if prefix is None:
603603
prefix='e'
604-
bl = self.mv_blades_lst
604+
bl = self._all_mv_blades_lst[1:] # do not include the scalar, which is not named
605605
var_names = [prefix+''.join([k for k in str(b) if k.isdigit()]) for b in bl]
606606

607607
return {key:val for key,val in zip(var_names, bl)}
@@ -672,11 +672,15 @@ def basis_vectors(self):
672672

673673
def _build_basis_base_symbol(self, base_index):
674674
""" Build a symbol used for the `base_rep` from the given tuple """
675+
if not base_index:
676+
return S(1)
675677
symbol_str = '*'.join([str(self.basis[i]) for i in base_index])
676678
return Symbol(symbol_str, commutative=False)
677679

678680
def _build_basis_blade_symbol(self, base_index):
679681
""" Build a symbol used for the `blade_rep` from the given tuple """
682+
if not base_index:
683+
return S(1)
680684
if self.wedge_print:
681685
symbol_str = '^'.join([str(self.basis[i]) for i in base_index])
682686
else:
@@ -733,33 +737,32 @@ def _build_bases(self):
733737

734738
# index list for multivector bases and blades by grade
735739
basis_indexes = tuple(self.n_range)
736-
self.indexes = [()]
737-
self.indexes_lst = []
738-
for i in basis_indexes:
739-
base_tuple = tuple(combinations(basis_indexes, i + 1))
740+
self.indexes = []
741+
self._all_indexes_lst = []
742+
for i in range(len(basis_indexes) + 1):
743+
base_tuple = tuple(combinations(basis_indexes, i))
740744
self.indexes.append(base_tuple)
741-
self.indexes_lst += list(base_tuple)
745+
self._all_indexes_lst += list(base_tuple)
742746
self.indexes = tuple(self.indexes)
743747

744748
# list of non-commutative symbols for multivector bases and blades
745749
# by grade and as a flattened list
746750

747751
self.blades = []
748-
self.blades_lst = []
752+
self._all_blades_lst = []
749753
for grade_index in self.indexes:
750754
blades = []
751755
super_scripts = []
752756
for base_index in grade_index:
753757
blade_symbol = self._build_basis_blade_symbol(base_index)
758+
754759
blades.append(blade_symbol)
755-
self.blades_lst.append(blade_symbol)
760+
self._all_blades_lst.append(blade_symbol)
756761
self.blades.append(blades)
757762

758-
self.blades_lst0 = [S(1)] + self.blades_lst
759-
760763
self.blades_to_indexes = []
761764
self.indexes_to_blades = []
762-
for (index, blade) in zip(self.indexes_lst, self.blades_lst):
765+
for (index, blade) in zip(self._all_indexes_lst, self._all_blades_lst):
763766
self.blades_to_indexes.append((blade, index))
764767
self.indexes_to_blades.append((index, blade))
765768
self.blades_to_indexes_dict = OrderedDict(self.blades_to_indexes)
@@ -771,20 +774,19 @@ def _build_bases(self):
771774
self.blades_to_grades_dict[blade] = igrade
772775

773776
if not self.is_ortho:
774-
775777
self.bases = []
776-
self.bases_lst = []
778+
self._all_bases_lst = []
777779
for grade_index in self.indexes:
778780
bases = []
779781
for base_index in grade_index:
780782
base_symbol = self._build_basis_base_symbol(base_index)
781783
bases.append(base_symbol)
782-
self.bases_lst.append(base_symbol)
784+
self._all_bases_lst.append(base_symbol)
783785
self.bases.append(bases)
784786

785787
self.bases_to_indexes = []
786788
self.indexes_to_bases = []
787-
for (index, base) in zip(self.indexes_lst, self.bases_lst):
789+
for (index, base) in zip(self._all_indexes_lst, self._all_bases_lst):
788790
self.bases_to_indexes.append((base, index))
789791
self.indexes_to_bases.append((index, base))
790792
self.bases_to_indexes_dict = OrderedDict(self.bases_to_indexes)
@@ -815,28 +817,37 @@ def _build_bases(self):
815817
self.blade_super_scripts.append(super_scripts)
816818

817819
if self.debug:
818-
printer.oprint('indexes', self.indexes, 'list(indexes)', self.indexes_lst,
819-
'blades', self.blades, 'list(blades)', self.blades_lst,
820+
printer.oprint('indexes', self.indexes, 'list(indexes)', self._all_indexes_lst,
821+
'blades', self.blades, 'list(blades)', self._all_blades_lst,
820822
'blades_to_indexes_dict', self.blades_to_indexes_dict,
821823
'indexes_to_blades_dict', self.indexes_to_blades_dict,
822824
'blades_to_grades_dict', self.blades_to_grades_dict,
823825
'blade_super_scripts', self.blade_super_scripts)
824826
if not self.is_ortho:
825-
printer.oprint('bases', self.bases, 'list(bases)', self.bases_lst,
827+
printer.oprint('bases', self.bases, 'list(bases)', self._all_bases_lst,
826828
'bases_to_indexes_dict', self.bases_to_indexes_dict,
827829
'indexes_to_bases_dict', self.indexes_to_bases_dict,
828830
'bases_to_grades_dict', self.bases_to_grades_dict)
829831

830832
# create the Mv wrappers
831-
self.mv_blades_lst = [
833+
self._all_mv_blades_lst = [
832834
mv.Mv(obj, ga=self)
833-
for obj in self.blades_lst
835+
for obj in self._all_blades_lst
834836
]
835837
self.mv_basis = [
836838
mv.Mv(obj, ga=self)
837839
for obj in self.basis
838840
]
839841

842+
# TODO[gh-64]: For compatibility with old behavior, the public
843+
# properties do not include the scalar. We should consider making the
844+
# breaking change such that they do.
845+
self.indexes_lst = self._all_indexes_lst[1:]
846+
self.blades_lst = self._all_blades_lst[1:]
847+
self.mv_blades_lst = self._all_mv_blades_lst[1:]
848+
if not self.is_ortho:
849+
self.bases_lst = self._all_bases_lst[1:]
850+
840851
def _build_basis_product_tables(self):
841852
"""
842853
For the different products of geometric algebra bases/blade
@@ -1155,8 +1166,8 @@ def _build_non_orthogonal_mul_table(self):
11551166
mul_table = []
11561167
self.basic_mul_keys = []
11571168
self.basic_mul_values = []
1158-
for base1 in self.bases_lst:
1159-
for base2 in self.bases_lst:
1169+
for base1 in self._all_bases_lst:
1170+
for base2 in self._all_bases_lst:
11601171
key = base1 * base2
11611172
value = self.non_orthogonal_bases_products((base1, base2))
11621173
mul_table.append((key, value))
@@ -1195,10 +1206,10 @@ def _build_base_blade_conversions(self):
11951206
blade_index = []
11961207

11971208
# expand blade basis in terms of base basis
1198-
for blade in self.blades_lst:
1209+
for blade in self._all_blades_lst:
11991210
index = self.blades_to_indexes_dict[blade]
12001211
grade = len(index)
1201-
if grade == 1:
1212+
if grade <= 1:
12021213
blade_expansion.append(blade)
12031214
blade_index.append(index)
12041215
else:
@@ -1213,7 +1224,7 @@ def _build_base_blade_conversions(self):
12131224
blade_expansion.append(expand(a_W_A))
12141225

12151226
self.blade_expansion = blade_expansion
1216-
self.blade_expansion_dict = OrderedDict(list(zip(self.blades_lst, blade_expansion)))
1227+
self.blade_expansion_dict = OrderedDict(list(zip(self._all_blades_lst, blade_expansion)))
12171228

12181229
if self.debug:
12191230
print('blade_expansion_dict =', self.blade_expansion_dict)
@@ -1222,9 +1233,9 @@ def _build_base_blade_conversions(self):
12221233

12231234
base_expand = []
12241235

1225-
for (base, blade, index) in zip(self.bases_lst, self.blades_lst, self.indexes_lst):
1236+
for (base, blade, index) in zip(self._all_bases_lst, self._all_blades_lst, self._all_indexes_lst):
12261237
grade = len(index)
1227-
if grade == 1:
1238+
if grade <= 1:
12281239
base_expand.append((base, base))
12291240
else: # back substitution of tridiagonal system
12301241
tmp = self.blade_expansion_dict[blade]
@@ -1254,7 +1265,7 @@ def blade_to_base_rep(self, A):
12541265
return A
12551266
else:
12561267
#return(expand(A).subs(self.blade_expansion_dict))
1257-
return nc_subs(expand(A), self.blades_lst, self.blade_expansion)
1268+
return nc_subs(expand(A), self._all_blades_lst, self.blade_expansion)
12581269

12591270
###### Products (*,^,|,<,>) for multivector representations ########
12601271

@@ -1610,11 +1621,11 @@ def _build_reciprocal_basis(self,gsym):
16101621
# {E_n}^{-1} = \frac{E_n}{{E_n}^{2}}
16111622
# r_basis_j = sgn * duals[j] * E_n so it's not normalized, missing a factor of {E_n}^{-2}
16121623
"""
1613-
print('blades list =',self.blades_lst)
1624+
print('blades list =',self._all_blades_lst)
16141625
print('debug =',expand(self.base_to_blade_rep(self.mul(sgn * dual_base_rep, self.e.obj))))
16151626
print('collect arg =',expand(self.base_to_blade_rep(self.mul(sgn * dual_base_rep, self.e.obj))))
16161627
"""
1617-
r_basis_j = metric.collect(expand(self.base_to_blade_rep(self.mul(sgn * dual_base_rep, self.e.obj))), self.blades_lst)
1628+
r_basis_j = metric.collect(expand(self.base_to_blade_rep(self.mul(sgn * dual_base_rep, self.e.obj))), self._all_blades_lst)
16181629
self.r_basis.append(r_basis_j)
16191630
# sgn = (-1)**{j-1}
16201631
sgn = -sgn

galgebra/mv.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ def characterise_Mv(self):
124124
if isinstance(obj, Add):
125125
args = obj.args
126126
else:
127-
if obj in self.Ga.blades_lst:
127+
if obj in self.Ga._all_blades_lst:
128128
self.is_blade_rep = True
129129
self.i_grade = self.Ga.blades_to_grades_dict[obj]
130130
self.grades = [self.i_grade]
@@ -145,7 +145,7 @@ def characterise_Mv(self):
145145
c, nc = term.args_cnc(split_1=False)
146146
blade = nc[0]
147147
#print 'blade =',blade
148-
if blade in self.Ga.blades_lst:
148+
if blade in self.Ga._all_blades_lst:
149149
grade = self.Ga.blades_to_grades_dict[blade]
150150
if not grade in grades:
151151
grades.append(grade)
@@ -553,10 +553,10 @@ def Mv_str(self):
553553
self.characterise_Mv()
554554
self.obj = metric.Simp.apply(self.obj)
555555
if self.is_blade_rep or self.Ga.is_ortho:
556-
base_keys = self.Ga.blades_lst
556+
base_keys = self.Ga._all_blades_lst
557557
grade_keys = self.Ga.blades_to_grades_dict
558558
else:
559-
base_keys = self.Ga.bases_lst
559+
base_keys = self.Ga._all_bases_lst
560560
grade_keys = self.Ga.bases_to_grades_dict
561561
if isinstance(self.obj, Add): # collect coefficients of bases
562562
if self.obj.is_commutative:
@@ -640,10 +640,10 @@ def append_plus(c_str):
640640
return ZERO_STR
641641

642642
if self.is_blade_rep or self.Ga.is_ortho:
643-
base_keys = self.Ga.blades_lst
643+
base_keys = self.Ga._all_blades_lst
644644
grade_keys = self.Ga.blades_to_grades_dict
645645
else:
646-
base_keys = self.Ga.bases_lst
646+
base_keys = self.Ga._all_bases_lst
647647
grade_keys = self.Ga.bases_to_grades_dict
648648
if isinstance(self.obj, Add):
649649
args = self.obj.args
@@ -944,17 +944,15 @@ def get_grade(self, r):
944944

945945
def components(self):
946946
(coefs, bases) = metric.linear_expand(self.obj)
947-
bases_lst = self.Ga.blades_lst
948947
cb = list(zip(coefs, bases))
949-
cb = sorted(cb, key=lambda x: self.Ga.blades_lst0.index(x[1]))
948+
cb = sorted(cb, key=lambda x: self.Ga._all_blades_lst.index(x[1]))
950949
terms = []
951950
for (coef, base) in cb:
952951
terms.append(self.Ga.mv(coef * base))
953952
return terms
954953

955954
def get_coefs(self, grade):
956955
(coefs, bases) = metric.linear_expand(self.obj)
957-
bases_lst = self.Ga.blades_lst
958956
cb = list(zip(coefs, bases))
959957
cb = sorted(cb, key=lambda x: self.Ga.blades[grade].index(x[1]))
960958
(coefs, bases) = list(zip(*cb))
@@ -968,7 +966,7 @@ def blade_coefs(self, blade_lst=None):
968966
"""
969967

970968
if blade_lst is None:
971-
blade_lst = [self.Ga.mv(ONE)] + self.Ga.mv_blades_lst
969+
blade_lst = self.Ga._all_mv_blades_lst
972970

973971
#print 'Enter blade_coefs blade_lst =', blade_lst, type(blade_lst), [i.is_blade() for i in blade_lst]
974972

@@ -2301,7 +2299,7 @@ def Dop_mv_expand(self, modes=None):
23012299
for i in range(len(coefs)):
23022300
coefs[i] = coefs[i].simplify(modes)
23032301
terms = list(zip(coefs, bases))
2304-
return sorted(terms, key=lambda x: self.Ga.blades_lst0.index(x[1]))
2302+
return sorted(terms, key=lambda x: self.Ga._all_blades_lst.index(x[1]))
23052303

23062304
def Dop_str(self):
23072305
if len(self.terms) == 0:

test/test_mv.py

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -88,20 +88,25 @@ def test_rep_switching(self):
8888
def test_construction(self):
8989
(ga, e_1, e_2, e_3) = Ga.build('e*1|2|3')
9090

91+
def check(x, expected_grades):
92+
self.assertEqual(x.grades, expected_grades)
93+
self.assertNotEqual(x, 0)
94+
9195
# non-function symbol construction
92-
self.assertEqual(ga.mv('A', 'scalar').grades, [0])
93-
self.assertEqual(ga.mv('A', 0).grades, [0])
94-
self.assertEqual(ga.mv('A', 'vector').grades, [1])
95-
self.assertEqual(ga.mv('A', 'grade', 1).grades, [1])
96-
self.assertEqual(ga.mv('A', 1).grades, [1])
97-
self.assertEqual(ga.mv('A', 'bivector').grades, [2])
98-
self.assertEqual(ga.mv('A', 'grade2').grades, [2])
99-
self.assertEqual(ga.mv('A', 2).grades, [2])
100-
self.assertEqual(ga.mv('A', 'pseudo').grades, [3])
101-
self.assertEqual(ga.mv('A', 'spinor').grades, [0, 2])
102-
self.assertEqual(ga.mv('A', 'even').grades, [0, 2])
103-
self.assertEqual(ga.mv('A', 'odd').grades, [1, 3])
104-
self.assertEqual(ga.mv('A', 'mv').grades, [0, 1, 2, 3])
96+
check(ga.mv('A', 'scalar'), [0])
97+
check(ga.mv('A', 'grade', 0), [0])
98+
check(ga.mv('A', 0), [0])
99+
check(ga.mv('A', 'vector'), [1])
100+
check(ga.mv('A', 'grade', 1), [1])
101+
check(ga.mv('A', 1), [1])
102+
check(ga.mv('A', 'bivector'), [2])
103+
check(ga.mv('A', 'grade2'), [2])
104+
check(ga.mv('A', 2), [2])
105+
check(ga.mv('A', 'pseudo'), [3])
106+
check(ga.mv('A', 'spinor'), [0, 2])
107+
check(ga.mv('A', 'even'), [0, 2])
108+
check(ga.mv('A', 'odd'), [1, 3])
109+
check(ga.mv('A', 'mv'), [0, 1, 2, 3])
105110

106111
# illegal arguments
107112
with self.assertRaises(TypeError):

0 commit comments

Comments
 (0)