From 3a65d2d2c7f89d3d74109444b1501b3709bd6fce Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 29 Aug 2022 09:56:39 -0700 Subject: [PATCH 1/5] CompWithSym._canonicalize_sym_antisym: Factor out from __init__ --- src/sage/tensor/modules/comp.py | 37 +++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/sage/tensor/modules/comp.py b/src/sage/tensor/modules/comp.py index 04f14f782f0..8de1f365212 100644 --- a/src/sage/tensor/modules/comp.py +++ b/src/sage/tensor/modules/comp.py @@ -2996,7 +2996,21 @@ def __init__(self, ring, frame, nb_indices, start_index=0, """ Components.__init__(self, ring, frame, nb_indices, start_index, output_formatter) - self._sym = [] + self._sym, self._antisym = self._canonicalize_sym_antisym( + nb_indices, sym, antisym) + + @staticmethod + def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None): + r""" + Bring sym and antisym into their canonical form. + + EXAMPLES:: + + sage: from sage.tensor.modules.comp import CompWithSym + sage: CompWithSym._canonicalize_sym_antisym(6, [(2, 1)]) + ([(2, 1)], []) + """ + result_sym = [] if sym is not None and sym != []: if isinstance(sym[0], (int, Integer)): # a single symmetry is provided as a tuple or a range object; @@ -3007,11 +3021,11 @@ def __init__(self, ring, frame, nb_indices, start_index=0, raise IndexError("at least two index positions must be " + "provided to define a symmetry") for i in isym: - if i<0 or i>self._nid-1: + if i < 0 or i > nb_indices - 1: raise IndexError("invalid index position: " + str(i) + - " not in [0," + str(self._nid-1) + "]") - self._sym.append(tuple(isym)) - self._antisym = [] + " not in [0," + str(nb_indices-1) + "]") + result_sym.append(tuple(isym)) + result_antisym = [] if antisym is not None and antisym != []: if isinstance(antisym[0], (int, Integer)): # a single antisymmetry is provided as a tuple or a range @@ -3022,20 +3036,21 @@ def __init__(self, ring, frame, nb_indices, start_index=0, raise IndexError("at least two index positions must be " + "provided to define an antisymmetry") for i in isym: - if i<0 or i>self._nid-1: + if i < 0 or i > nb_indices - 1: raise IndexError("invalid index position: " + str(i) + - " not in [0," + str(self._nid-1) + "]") - self._antisym.append(tuple(isym)) + " not in [0," + str(nb_indices - 1) + "]") + result_antisym.append(tuple(isym)) # Final consistency check: index_list = [] - for isym in self._sym: + for isym in result_sym: index_list += isym - for isym in self._antisym: + for isym in result_antisym: index_list += isym if len(index_list) != len(set(index_list)): # There is a repeated index position: raise IndexError("incompatible lists of symmetries: the same " + - "index position appears more then once") + "index position appears more than once") + return result_sym, result_antisym def _repr_(self): r""" From 07ab9914adf5418070b9f326d99d282c18ddb35a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 29 Aug 2022 10:52:35 -0700 Subject: [PATCH 2/5] src/sage/tensor: Allow _sym, _antisym attributes to be tuples --- .../manifolds/differentiable/tensorfield.py | 4 +-- src/sage/tensor/modules/comp.py | 26 +++++++++++++------ src/sage/tensor/modules/free_module_tensor.py | 4 +-- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/sage/manifolds/differentiable/tensorfield.py b/src/sage/manifolds/differentiable/tensorfield.py index d5b63c9d81d..1370b104140 100644 --- a/src/sage/manifolds/differentiable/tensorfield.py +++ b/src/sage/manifolds/differentiable/tensorfield.py @@ -957,13 +957,13 @@ def symmetries(self): elif len(self._sym) == 1: s = "symmetry: {}; ".format(self._sym[0]) else: - s = "symmetries: {}; ".format(self._sym) + s = "symmetries: {}; ".format(list(self._sym)) if not self._antisym: a = "no antisymmetry" elif len(self._antisym) == 1: a = "antisymmetry: {}".format(self._antisym[0]) else: - a = "antisymmetries: {}".format(self._antisym) + a = "antisymmetries: {}".format(list(self._antisym)) print(s + a) #### End of simple accessors ##### diff --git a/src/sage/tensor/modules/comp.py b/src/sage/tensor/modules/comp.py index 8de1f365212..d9c3dfdfe3e 100644 --- a/src/sage/tensor/modules/comp.py +++ b/src/sage/tensor/modules/comp.py @@ -1773,12 +1773,12 @@ def __mul__(self, other): "same starting index") if isinstance(other, CompWithSym): sym = [] - if other._sym != []: + if other._sym: for s in other._sym: ns = tuple(s[i]+self._nid for i in range(len(s))) sym.append(ns) antisym = [] - if other._antisym != []: + if other._antisym: for s in other._antisym: ns = tuple(s[i]+self._nid for i in range(len(s))) antisym.append(ns) @@ -3011,7 +3011,12 @@ def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None): ([(2, 1)], []) """ result_sym = [] - if sym is not None and sym != []: + if sym is None: + sym = [] + else: + # Handle the case that sym is an iterator + sym = list(sym) + if sym: if isinstance(sym[0], (int, Integer)): # a single symmetry is provided as a tuple or a range object; # it is converted to a 1-item list: @@ -3026,7 +3031,12 @@ def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None): " not in [0," + str(nb_indices-1) + "]") result_sym.append(tuple(isym)) result_antisym = [] - if antisym is not None and antisym != []: + if antisym is None: + antisym = [] + else: + # Handle the case that antisym is an iterator + antisym = list(antisym) + if antisym: if isinstance(antisym[0], (int, Integer)): # a single antisymmetry is provided as a tuple or a range # object; it is converted to a 1-item list: @@ -3535,7 +3545,7 @@ def paral_sum(a, b, local_list_ind): com = tuple(set(isym).intersection(set(osym))) if len(com) > 1: common_antisym.append(com) - if common_sym != [] or common_antisym != []: + if common_sym or common_antisym: result = CompWithSym(self._ring, self._frame, self._nid, self._sindex, self._output_formatter, common_sym, common_antisym) @@ -3643,11 +3653,11 @@ def __mul__(self, other): sym = list(self._sym) antisym = list(self._antisym) if isinstance(other, CompWithSym): - if other._sym != []: + if other._sym: for s in other._sym: ns = tuple(s[i]+self._nid for i in range(len(s))) sym.append(ns) - if other._antisym != []: + if other._antisym: for s in other._antisym: ns = tuple(s[i]+self._nid for i in range(len(s))) antisym.append(ns) @@ -3973,7 +3983,7 @@ def non_redundant_index_generator(self): si = self._sindex imax = self._dim - 1 + si ind = [si for k in range(self._nid)] - sym = self._sym.copy() # we may modify this in the following + sym = list(self._sym) # we may modify this in the following antisym = self._antisym for pos in range(self._nid): for isym in antisym: diff --git a/src/sage/tensor/modules/free_module_tensor.py b/src/sage/tensor/modules/free_module_tensor.py index bbcc1ecb2e0..1fa457475a0 100644 --- a/src/sage/tensor/modules/free_module_tensor.py +++ b/src/sage/tensor/modules/free_module_tensor.py @@ -563,13 +563,13 @@ def symmetries(self): elif len(self._sym) == 1: s = "symmetry: {}; ".format(self._sym[0]) else: - s = "symmetries: {}; ".format(self._sym) + s = "symmetries: {}; ".format(list(self._sym)) if len(self._antisym) == 0: a = "no antisymmetry" elif len(self._antisym) == 1: a = "antisymmetry: {}".format(self._antisym[0]) else: - a = "antisymmetries: {}".format(self._antisym) + a = "antisymmetries: {}".format(list(self._antisym)) print(s+a) #### End of simple accessors ##### From 3619b4c163234f71c4e68d4c0442e56c5f4898dc Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 29 Aug 2022 11:04:11 -0700 Subject: [PATCH 3/5] src/sage/tensor, src/sage/manifolds: Sort _sym, _antisym, store as tuples of tuples --- src/sage/manifolds/differentiable/metric.py | 8 ++-- .../manifolds/differentiable/tensorfield.py | 39 ++----------------- src/sage/tensor/modules/comp.py | 29 ++++++++------ src/sage/tensor/modules/free_module_tensor.py | 39 ++----------------- 4 files changed, 29 insertions(+), 86 deletions(-) diff --git a/src/sage/manifolds/differentiable/metric.py b/src/sage/manifolds/differentiable/metric.py index 686af0d3505..445862d6795 100644 --- a/src/sage/manifolds/differentiable/metric.py +++ b/src/sage/manifolds/differentiable/metric.py @@ -647,7 +647,7 @@ def set(self, symbiform): raise TypeError("the argument must be a tensor field") if symbiform._tensor_type != (0,2): raise TypeError("the argument must be of tensor type (0,2)") - if symbiform._sym != [(0,1)]: + if symbiform._sym != ((0,1),): raise TypeError("the argument must be symmetric") if not symbiform._domain.is_subset(self._domain): raise TypeError("the symmetric bilinear form is not defined " + @@ -2301,7 +2301,7 @@ def set(self, symbiform): "values on a parallelizable domain") if symbiform._tensor_type != (0,2): raise TypeError("the argument must be of tensor type (0,2)") - if symbiform._sym != [(0,1)]: + if symbiform._sym != ((0,1),): raise TypeError("the argument must be symmetric") if symbiform._vmodule is not self._vmodule: raise TypeError("the symmetric bilinear form and the metric are " + @@ -2781,7 +2781,7 @@ def set(self, symbiform): raise TypeError("the argument must be a tensor field") if symbiform._tensor_type != (0,2): raise TypeError("the argument must be of tensor type (0,2)") - if symbiform._sym != [(0,1)]: + if symbiform._sym != ((0,1),): raise TypeError("the argument must be symmetric") if not symbiform._domain.is_subset(self._domain): raise TypeError("the symmetric bilinear form is not defined " + @@ -3019,7 +3019,7 @@ def set(self, symbiform): "values on a parallelizable domain") if symbiform._tensor_type != (0,2): raise TypeError("the argument must be of tensor type (0,2)") - if symbiform._sym != [(0,1)]: + if symbiform._sym != ((0,1),): raise TypeError("the argument must be symmetric") if symbiform._vmodule is not self._vmodule: raise TypeError("the symmetric bilinear form and the metric are " + diff --git a/src/sage/manifolds/differentiable/tensorfield.py b/src/sage/manifolds/differentiable/tensorfield.py index 1370b104140..a2aad4d4937 100644 --- a/src/sage/manifolds/differentiable/tensorfield.py +++ b/src/sage/manifolds/differentiable/tensorfield.py @@ -58,6 +58,7 @@ from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ from sage.structure.element import ModuleElementWithMutability +from sage.tensor.modules.comp import CompWithSym from sage.tensor.modules.free_module_tensor import FreeModuleTensor from sage.tensor.modules.tensor_with_indices import TensorWithIndices @@ -495,40 +496,8 @@ def __init__( self._restrictions = {} # dict. of restrictions of self on subdomains # of self._domain, with the subdomains as keys # Treatment of symmetry declarations: - self._sym = [] - if sym is not None and sym != []: - if isinstance(sym[0], (int, Integer)): - # a single symmetry is provided as a tuple -> 1-item list: - sym = [tuple(sym)] - for isym in sym: - if len(isym) > 1: - for i in isym: - if i < 0 or i > self._tensor_rank - 1: - raise IndexError("invalid position: {}".format(i) + - " not in [0,{}]".format(self._tensor_rank-1)) - self._sym.append(tuple(isym)) - self._antisym = [] - if antisym is not None and antisym != []: - if isinstance(antisym[0], (int, Integer)): - # a single antisymmetry is provided as a tuple -> 1-item list: - antisym = [tuple(antisym)] - for isym in antisym: - if len(isym) > 1: - for i in isym: - if i < 0 or i > self._tensor_rank - 1: - raise IndexError("invalid position: {}".format(i) + - " not in [0,{}]".format(self._tensor_rank-1)) - self._antisym.append(tuple(isym)) - # Final consistency check: - index_list = [] - for isym in self._sym: - index_list += isym - for isym in self._antisym: - index_list += isym - if len(index_list) != len(set(index_list)): - # There is a repeated index position: - raise IndexError("incompatible lists of symmetries: the same " + - "position appears more than once") + self._sym, self._antisym = CompWithSym._canonicalize_sym_antisym( + self._tensor_rank, sym, antisym) # Initialization of derived quantities: self._init_derived() @@ -591,7 +560,7 @@ def _repr_(self): """ # Special cases - if self._tensor_type == (0,2) and self._sym == [(0,1)]: + if self._tensor_type == (0,2) and self._sym == ((0,1),): description = "Field of symmetric bilinear forms " if self._name is not None: description += self._name + " " diff --git a/src/sage/tensor/modules/comp.py b/src/sage/tensor/modules/comp.py index d9c3dfdfe3e..3318c7b3ebf 100644 --- a/src/sage/tensor/modules/comp.py +++ b/src/sage/tensor/modules/comp.py @@ -3008,7 +3008,7 @@ def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None): sage: from sage.tensor.modules.comp import CompWithSym sage: CompWithSym._canonicalize_sym_antisym(6, [(2, 1)]) - ([(2, 1)], []) + (((1, 2),), ()) """ result_sym = [] if sym is None: @@ -3023,8 +3023,8 @@ def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None): sym = [tuple(sym)] for isym in sym: if len(isym) < 2: - raise IndexError("at least two index positions must be " + - "provided to define a symmetry") + # Drop trivial symmetry + continue for i in isym: if i < 0 or i > nb_indices - 1: raise IndexError("invalid index position: " + str(i) + @@ -3043,8 +3043,8 @@ def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None): antisym = [tuple(antisym)] for isym in antisym: if len(isym) < 2: - raise IndexError("at least two index positions must be " + - "provided to define an antisymmetry") + # Drop trivial antisymmetry + continue for i in isym: if i < 0 or i > nb_indices - 1: raise IndexError("invalid index position: " + str(i) + @@ -3060,6 +3060,11 @@ def _canonicalize_sym_antisym(nb_indices, sym=None, antisym=None): # There is a repeated index position: raise IndexError("incompatible lists of symmetries: the same " + "index position appears more than once") + # Canonicalize sort order, make tuples + result_sym = [tuple(sorted(s)) for s in result_sym] + result_antisym = [tuple(sorted(s)) for s in result_antisym] + result_sym = tuple(sorted(result_sym)) + result_antisym = tuple(sorted(result_antisym)) return result_sym, result_antisym def _repr_(self): @@ -3392,9 +3397,9 @@ def swap_adjacent_indices(self, pos1, pos2, pos3): [[0, 7, 8], [-7, 0, 9], [-8, -9, 0]]] sage: c1 = c.swap_adjacent_indices(0,1,3) sage: c._antisym # c is antisymmetric with respect to the last pair of indices... - [(1, 2)] + ((1, 2),) sage: c1._antisym #...while c1 is antisymmetric with respect to the first pair of indices - [(0, 1)] + ((0, 1),) sage: c[0,1,2] 3 sage: c1[1,2,0] @@ -3415,6 +3420,8 @@ def swap_adjacent_indices(self, pos1, pos2, pos3): for s in self._antisym: new_s = [new_lpos.index(pos) for pos in s] result._antisym.append(tuple(sorted(new_s))) + result._sym, result._antisym = self._canonicalize_sym_antisym( + self._nid, result._sym, result._antisym) # The values: for ind, val in self._comp.items(): new_ind = ind[:pos1] + ind[pos2:pos3] + ind[pos1:pos2] + ind[pos3:] @@ -4159,7 +4166,7 @@ def symmetrize(self, *pos): (0, 0, 1) ], with symmetry on the index positions (0, 1), with symmetry on the index positions (2, 3) sage: a1._sym # a1 has two distinct symmetries: - [(0, 1), (2, 3)] + ((0, 1), (2, 3)) sage: a[0,1,2,0] == a[0,0,2,1] # a is symmetric w.r.t. positions 1 and 3 True sage: a1[0,1,2,0] == a1[0,0,2,1] # a1 is not @@ -4437,10 +4444,10 @@ def antisymmetrize(self, *pos): (1, 0, 0), (0, 1, 0), (0, 0, 1) - ], with antisymmetry on the index positions (1, 3), - with antisymmetry on the index positions (0, 2) + ], with antisymmetry on the index positions (0, 2), + with antisymmetry on the index positions (1, 3) sage: s._antisym # the antisymmetry (0,1,2) has been reduced to (0,2), since 1 is involved in the new antisymmetry (1,3): - [(1, 3), (0, 2)] + ((0, 2), (1, 3)) Partial antisymmetrization of 4-indices components with a symmetry on the first two indices:: diff --git a/src/sage/tensor/modules/free_module_tensor.py b/src/sage/tensor/modules/free_module_tensor.py index 1fa457475a0..4d6d05c57b8 100644 --- a/src/sage/tensor/modules/free_module_tensor.py +++ b/src/sage/tensor/modules/free_module_tensor.py @@ -309,41 +309,8 @@ def __init__( # bases, with the bases as keys (initially empty) # Treatment of symmetry declarations: - self._sym = [] - if sym is not None and sym != []: - if isinstance(sym[0], (int, Integer)): - # a single symmetry is provided as a tuple -> 1-item list: - sym = [tuple(sym)] - for isym in sym: - if len(isym) > 1: - for i in isym: - if i<0 or i>self._tensor_rank-1: - raise IndexError("invalid position: " + str(i) + - " not in [0," + str(self._tensor_rank-1) + "]") - self._sym.append(tuple(isym)) - self._antisym = [] - if antisym is not None and antisym != []: - if isinstance(antisym[0], (int, Integer)): - # a single antisymmetry is provided as a tuple -> 1-item list: - antisym = [tuple(antisym)] - for isym in antisym: - if len(isym) > 1: - for i in isym: - if i<0 or i>self._tensor_rank-1: - raise IndexError("invalid position: " + str(i) + - " not in [0," + str(self._tensor_rank-1) + "]") - self._antisym.append(tuple(isym)) - - # Final consistency check: - index_list = [] - for isym in self._sym: - index_list += isym - for isym in self._antisym: - index_list += isym - if len(index_list) != len(set(index_list)): - # There is a repeated index position: - raise IndexError("incompatible lists of symmetries: the same " + - "position appears more than once") + self._sym, self._antisym = CompWithSym._canonicalize_sym_antisym( + self._tensor_rank, sym, antisym) # Initialization of derived quantities: FreeModuleTensor._init_derived(self) @@ -405,7 +372,7 @@ def _repr_(self): """ # Special cases - if self._tensor_type == (0,2) and self._sym == [(0,1)]: + if self._tensor_type == (0,2) and self._sym == ((0,1),): description = "Symmetric bilinear form " else: # Generic case From 970c6c88e3b1e8efc54fccff8b68a67c83bdcf5b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 29 Aug 2022 11:50:42 -0700 Subject: [PATCH 4/5] src/sage/tensor, src/sage/manifolds: Sort _sym, _antisym, store as tuples of tuples (fixup) --- .../manifolds/differentiable/pseudo_riemannian_submanifold.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/manifolds/differentiable/pseudo_riemannian_submanifold.py b/src/sage/manifolds/differentiable/pseudo_riemannian_submanifold.py index 2eaef7510c8..3aef31eb703 100644 --- a/src/sage/manifolds/differentiable/pseudo_riemannian_submanifold.py +++ b/src/sage/manifolds/differentiable/pseudo_riemannian_submanifold.py @@ -1173,7 +1173,7 @@ def ambient_second_fundamental_form(self): g.restrict(chart.domain()).contract(pf[j]) * self.scalar_field({chart: k.comp(chart.frame())[:][i, j]}) for i in range(self._dim) for j in range(self._dim)) - gam_rst._sym = [(0, 1)] + gam_rst._sym = ((0, 1),) self._ambient_second_fundamental_form.set_restriction(gam_rst) charts = iter(self.top_charts()) From 9f15e00a4396a993fe412b41993cd542d4f177a0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 29 Aug 2022 11:50:57 -0700 Subject: [PATCH 5/5] src/sage/tensor: Allow _sym, _antisym attributes to be tuples (fixup) --- src/sage/manifolds/differentiable/vectorfield_module.py | 8 ++++---- src/sage/tensor/modules/finite_rank_free_module.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/manifolds/differentiable/vectorfield_module.py b/src/sage/manifolds/differentiable/vectorfield_module.py index cf3f1dc687d..393218bc7c0 100644 --- a/src/sage/manifolds/differentiable/vectorfield_module.py +++ b/src/sage/manifolds/differentiable/vectorfield_module.py @@ -787,7 +787,7 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, # a single antisymmetry is provided as a tuple or a # range object; it is converted to a 1-item list: antisym = [tuple(antisym)] - if isinstance(antisym, list): + if isinstance(antisym, (tuple, list)): antisym0 = antisym[0] else: antisym0 = antisym @@ -799,7 +799,7 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, # a single antisymmetry is provided as a tuple or a # range object; it is converted to a 1-item list: antisym = [tuple(antisym)] - if isinstance(antisym, list): + if isinstance(antisym, (tuple, list)): antisym0 = antisym[0] else: antisym0 = antisym @@ -2102,7 +2102,7 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, # a single antisymmetry is provided as a tuple or a # range object; it is converted to a 1-item list: antisym = [tuple(antisym)] - if isinstance(antisym, list): + if isinstance(antisym, (tuple, list)): antisym0 = antisym[0] else: antisym0 = antisym @@ -2114,7 +2114,7 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, # a single antisymmetry is provided as a tuple or a # range object; it is converted to a 1-item list: antisym = [tuple(antisym)] - if isinstance(antisym, list): + if isinstance(antisym, (tuple, list)): antisym0 = antisym[0] else: antisym0 = antisym diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 091dc252ad5..c0692104581 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -1458,7 +1458,7 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, # a single antisymmetry is provided as a tuple or a range # object; it is converted to a 1-item list: antisym = [tuple(antisym)] - if isinstance(antisym, list): + if isinstance(antisym, (tuple, list)): antisym0 = antisym[0] else: antisym0 = antisym @@ -1470,7 +1470,7 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, # a single antisymmetry is provided as a tuple or a range # object; it is converted to a 1-item list: antisym = [tuple(antisym)] - if isinstance(antisym, list): + if isinstance(antisym, (tuple, list)): antisym0 = antisym[0] else: antisym0 = antisym