Skip to content

Commit

Permalink
Trac #34451: sage.tensor: Canonicalize sym, antisym
Browse files Browse the repository at this point in the history
for #30229.

The new static method `CompWithSym._canonicalize_sym_antisym` brings
`sym` and `antisym` into a canonical form as sorted tuples of sorted
tuples, with trivial symmetries and antisymmetries dropped.

Using it also removes some code duplication.

URL: https://trac.sagemath.org/34451
Reported by: mkoeppe
Ticket author(s): Matthias Koeppe
Reviewer(s): Eric Gourgoulhon
  • Loading branch information
Release Manager committed Sep 20, 2022
2 parents 5b3c6fa + 66009e7 commit 53a523e
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 197 deletions.
8 changes: 4 additions & 4 deletions src/sage/manifolds/differentiable/metric.py
Expand Up @@ -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 " +
Expand Down Expand Up @@ -2299,7 +2299,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 " +
Expand Down Expand Up @@ -2779,7 +2779,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 " +
Expand Down Expand Up @@ -3017,7 +3017,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 " +
Expand Down
Expand Up @@ -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())
Expand Down
43 changes: 6 additions & 37 deletions src/sage/manifolds/differentiable/tensorfield.py
Expand Up @@ -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

Expand Down Expand Up @@ -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()

Expand Down Expand Up @@ -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 + " "
Expand Down Expand Up @@ -957,13 +926,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 #####
Expand Down
70 changes: 21 additions & 49 deletions src/sage/manifolds/differentiable/vectorfield_module.py
Expand Up @@ -773,7 +773,10 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None,
AutomorphismField
from sage.manifolds.differentiable.metric import (PseudoRiemannianMetric,
DegenerateMetric)
if tensor_type==(1,0):
from sage.tensor.modules.comp import CompWithSym
sym, antisym = CompWithSym._canonicalize_sym_antisym(
tensor_type[0] + tensor_type[1], sym, antisym)
if tensor_type == (1,0):
return self.element_class(self, name=name,
latex_name=latex_name)
elif tensor_type == (0,1):
Expand All @@ -783,31 +786,14 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None,
return self.automorphism(name=name,
latex_name=latex_name)
elif tensor_type[0] == 0 and tensor_type[1] > 1 and 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:
antisym = [tuple(antisym)]
if isinstance(antisym, list):
antisym0 = antisym[0]
else:
antisym0 = antisym
if len(antisym0) == tensor_type[1]:
if len(antisym[0]) == tensor_type[1]:
return self.alternating_form(tensor_type[1], name=name,
latex_name=latex_name)
elif tensor_type[0] > 1 and tensor_type[1] == 0 and 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:
antisym = [tuple(antisym)]
if isinstance(antisym, list):
antisym0 = antisym[0]
else:
antisym0 = antisym
if len(antisym0) == tensor_type[0]:
if len(antisym[0]) == tensor_type[0]:
return self.alternating_contravariant_tensor(
tensor_type[0], name=name,
latex_name=latex_name)
elif tensor_type==(0,2) and specific_type is not None:
tensor_type[0], name=name, latex_name=latex_name)
elif tensor_type == (0,2) and specific_type is not None:
if issubclass(specific_type, PseudoRiemannianMetric):
return self.metric(name, latex_name=latex_name)
# NB: the signature is not treated
Expand All @@ -816,9 +802,9 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None,
return self.metric(name, latex_name=latex_name,
signature=(0, sign-1, 1))
# Generic case
return self.tensor_module(*tensor_type).element_class(self,
tensor_type, name=name, latex_name=latex_name,
sym=sym, antisym=antisym)
return self.tensor_module(*tensor_type).element_class(
self, tensor_type, name=name, latex_name=latex_name,
sym=sym, antisym=antisym)

def alternating_contravariant_tensor(self, degree, name=None,
latex_name=None):
Expand Down Expand Up @@ -2088,6 +2074,9 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None,
AutomorphismField, AutomorphismFieldParal)
from sage.manifolds.differentiable.metric import (PseudoRiemannianMetric,
DegenerateMetric)
from sage.tensor.modules.comp import CompWithSym
sym, antisym = CompWithSym._canonicalize_sym_antisym(
tensor_type[0] + tensor_type[1], sym, antisym)
if tensor_type == (1,0):
return self.element_class(self, name=name,
latex_name=latex_name)
Expand All @@ -2098,31 +2087,14 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None,
(AutomorphismField, AutomorphismFieldParal)):
return self.automorphism(name=name, latex_name=latex_name)
elif tensor_type[0] == 0 and tensor_type[1] > 1 and 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:
antisym = [tuple(antisym)]
if isinstance(antisym, list):
antisym0 = antisym[0]
else:
antisym0 = antisym
if len(antisym0) == tensor_type[1]:
if len(antisym[0]) == tensor_type[1]:
return self.alternating_form(tensor_type[1], name=name,
latex_name=latex_name)
elif tensor_type[0] > 1 and tensor_type[1] == 0 and 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:
antisym = [tuple(antisym)]
if isinstance(antisym, list):
antisym0 = antisym[0]
else:
antisym0 = antisym
if len(antisym0) == tensor_type[0]:
if len(antisym[0]) == tensor_type[0]:
return self.alternating_contravariant_tensor(
tensor_type[0], name=name,
latex_name=latex_name)
elif tensor_type==(0,2) and specific_type is not None:
tensor_type[0], name=name, latex_name=latex_name)
elif tensor_type == (0,2) and specific_type is not None:
if issubclass(specific_type, PseudoRiemannianMetric):
return self.metric(name, latex_name=latex_name)
# NB: the signature is not treated
Expand All @@ -2131,9 +2103,9 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None,
return self.metric(name, latex_name=latex_name,
signature=(0, sign-1, 1))
# Generic case
return self.tensor_module(*tensor_type).element_class(self,
tensor_type, name=name, latex_name=latex_name,
sym=sym, antisym=antisym)
return self.tensor_module(*tensor_type).element_class(
self, tensor_type, name=name, latex_name=latex_name,
sym=sym, antisym=antisym)

def tensor_from_comp(self, tensor_type, comp, name=None,
latex_name=None):
Expand Down

0 comments on commit 53a523e

Please sign in to comment.