Skip to content
This repository has been archived by the owner on Jan 30, 2023. It is now read-only.

Commit

Permalink
Merge #30229
Browse files Browse the repository at this point in the history
  • Loading branch information
Matthias Koeppe committed Aug 28, 2022
2 parents e2d2ea8 + ae2ff23 commit 9ace3e2
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 28 deletions.
26 changes: 18 additions & 8 deletions src/sage/tensor/modules/tensor_free_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,8 @@ def _element_constructor_(self, comp=[], basis=None, name=None,
self._fmodule is endo.domain():
resu = self.element_class(self._fmodule, (1,1),
name=endo._name,
latex_name=endo._latex_name)
latex_name=endo._latex_name,
parent=self)
for basis, mat in endo._matrices.items():
resu.add_comp(basis[0])[:] = mat
else:
Expand All @@ -450,7 +451,8 @@ def _element_constructor_(self, comp=[], basis=None, name=None,
resu = self.element_class(self._fmodule, (p,0),
name=tensor._name,
latex_name=tensor._latex_name,
antisym=asym)
antisym=asym,
parent=self)
for basis, comp in tensor._components.items():
resu._components[basis] = comp.copy()
elif isinstance(comp, FreeModuleAltForm):
Expand All @@ -467,7 +469,8 @@ def _element_constructor_(self, comp=[], basis=None, name=None,
asym = range(p)
resu = self.element_class(self._fmodule, (0,p), name=form._name,
latex_name=form._latex_name,
antisym=asym)
antisym=asym,
parent=self)
for basis, comp in form._components.items():
resu._components[basis] = comp.copy()
elif isinstance(comp, FreeModuleAutomorphism):
Expand All @@ -478,7 +481,8 @@ def _element_constructor_(self, comp=[], basis=None, name=None,
raise TypeError("cannot coerce the {}".format(autom) +
" to an element of {}".format(self))
resu = self.element_class(self._fmodule, (1,1), name=autom._name,
latex_name=autom._latex_name)
latex_name=autom._latex_name,
parent=self)
for basis, comp in autom._components.items():
resu._components[basis] = comp.copy()
elif isinstance(comp, FreeModuleTensor):
Expand All @@ -489,14 +493,15 @@ def _element_constructor_(self, comp=[], basis=None, name=None,
" to an element of {}".format(self))
resu = self.element_class(self._fmodule, self._tensor_type,
name=name, latex_name=latex_name,
sym=sym, antisym=antisym)
sym=sym, antisym=antisym,
parent=self)
for basis, comp in tensor._components.items():
resu._components[basis] = comp.copy()
else:
# Standard construction:
resu = self.element_class(self._fmodule, self._tensor_type,
name=name, latex_name=latex_name,
sym=sym, antisym=antisym)
sym=sym, antisym=antisym, parent=self)
if comp:
resu.set_comp(basis)[:] = comp
return resu
Expand Down Expand Up @@ -731,7 +736,9 @@ def basis(self, symbol, latex_symbol=None, from_family=None,
sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
sage: T = M.tensor_module(1,1)
sage: e_T = T.basis('e'); e_T
<sage.tensor.modules.tensor_free_submodule_basis.TensorFreeSubmoduleBasis_comp... object at ...>
Standard basis on the
Free module of type-(1,1) tensors on the Rank-3 free module M over the Integer Ring
induced by Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring
sage: for a in e_T: a.display()
e_0⊗e^0
e_0⊗e^1
Expand All @@ -746,7 +753,10 @@ def basis(self, symbol, latex_symbol=None, from_family=None,
sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp
sage: Sym2M = TensorFreeSubmodule_comp(M, (2, 0), sym=range(2))
sage: e_Sym2M = Sym2M.basis('e'); e_Sym2M
<sage.tensor.modules.tensor_free_submodule_basis.TensorFreeSubmoduleBasis_comp... object at ...>
Standard basis on the
Free module of type-(2,0) tensors with Fully symmetric 2-indices components w.r.t. (0, 1, 2)
on the Rank-3 free module M over the Integer Ring
induced by Basis (e_0,e_1,e_2) on the Rank-3 free module M over the Integer Ring
sage: for a in e_Sym2M: a.display()
e_0⊗e_0
e_0⊗e_1 + e_1⊗e_0
Expand Down
171 changes: 156 additions & 15 deletions src/sage/tensor/modules/tensor_free_submodule.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,6 @@ def __init__(self, fmodule, tensor_type, name=None, latex_name=None,
sage: e = M.basis('e')
sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5)))
sage: TestSuite(Sym0123x45M).run()
Traceback (most recent call last):
...
The following tests failed: _test_not_implemented_methods, _test_zero
"""
self._fmodule = fmodule
self._tensor_type = tuple(tensor_type)
Expand Down Expand Up @@ -252,30 +249,55 @@ def is_coarsening_of(self_sym_list, other_sym_list):

def _element_constructor_(self, comp=[], basis=None, name=None,
latex_name=None, sym=None, antisym=None):
r"""
TESTS::
sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp
sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
sage: e = M.basis('e')
sage: T60M = M.tensor_module(6, 0)
sage: Sym0123x45M = TensorFreeSubmodule_comp(M, (6, 0), sym=((0, 1, 2, 3), (4, 5)))
sage: Sym0123x45M(e[0]*e[0]*e[0]*e[0]*e[1]*e[2])
Traceback (most recent call last):
...
ValueError: this tensor does not have the symmetries of
Free module of type-(6,0) tensors with 6-indices components w.r.t. (0, 1, 2),
with symmetry on the index positions (0, 1, 2, 3),
with symmetry on the index positions (4, 5)
on the Rank-3 free module M over the Integer Ring
sage: t = Sym0123x45M(e[0]*e[0]*e[0]*e[0]*e[1]*e[2] + e[0]*e[0]*e[0]*e[0]*e[2]*e[1]); t.disp()
e_0⊗e_0⊗e_0⊗e_0⊗e_1⊗e_2 + e_0⊗e_0⊗e_0⊗e_0⊗e_2⊗e_1
sage: t.parent()._name
'Sym^{0,1,2,3}(M)⊗Sym^{4,5}(M)'
"""
if sym is not None or antisym is not None:
# Refuse to create a tensor with finer symmetries
# than those defining the subspace
if not self._is_symmetry_coarsening_of((sym, antisym), self._basis_comp()):
raise ValueError("cannot create a tensor with symmetries {} as an element of {}".
format((sym, antisym), self))
try:
comp_parent = comp.parent()
except AttributeError:
comp_parent = None
if comp_parent == self.ambient_module():
resu = comp
# comp is already a tensor. If its declared symmetries are coarser
# than the symmetries defining self, we can use it directly.
if self._is_symmetry_coarsening_of(resu, self._basis_comp()):
return resu
raise ValueError(f"cannot create a tensor with symmetries {sym=}, {antisym=} "
f"as an element of {self}")

if sym is None:
sym = self._basis_comp()._sym
if antisym is None:
antisym = self._basis_comp()._antisym

resu = super()._element_constructor_(comp=comp,
basis=basis, name=name,
latex_name=latex_name,
sym=sym, antisym=antisym)
if not resu._components:
# fast path for zero tensor
return resu

try:
if self.reduce(resu):
raise ValueError(f"this tensor does not have the symmetries of {self}")
except TypeError:
# Averaging over the orbits of a tensor that does not have the required
# symmetries can lead to "TypeError: no conversion of this rational to integer"
raise ValueError(f"this tensor does not have the symmetries of {self}")

return resu

def is_submodule(self, other):
Expand Down Expand Up @@ -340,3 +362,122 @@ def lift(self):
on the Rank-3 free module M over the Integer Ring
"""
return self.module_morphism(function=lambda x: x, codomain=self.ambient())

@lazy_attribute
def reduce(self):
r"""
The reduce map.
This map reduces elements of the ambient space modulo this
submodule.
EXAMPLES::
sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp
sage: M = FiniteRankFreeModule(QQ, 3, name='M')
sage: e = M.basis('e')
sage: X = M.tensor_module(6, 0)
sage: Y = M.tensor_module(6, 0, sym=((0, 1, 2, 3), (4, 5)))
sage: Y.reduce
Generic endomorphism of Free module of type-(6,0) tensors on the 3-dimensional vector space M over the Rational Field
sage: t = e[0]*e[0]*e[0]*e[0]*e[1]*e[2]; t.disp()
e_0⊗e_0⊗e_0⊗e_0⊗e_1⊗e_2 = e_0⊗e_0⊗e_0⊗e_0⊗e_1⊗e_2
sage: r = Y.reduce(t); r
Type-(6,0) tensor on the 3-dimensional vector space M over the Rational Field
sage: r.disp()
1/2 e_0⊗e_0⊗e_0⊗e_0⊗e_1⊗e_2 - 1/2 e_0⊗e_0⊗e_0⊗e_0⊗e_2⊗e_1
sage: r.parent()._name
'T^(6, 0)(M)'
If the base ring is not a field, this may fail::
sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
sage: e = M.basis('e')
sage: X = M.tensor_module(6, 0)
sage: Y = M.tensor_module(6, 0, sym=((0, 1, 2, 3), (4, 5)))
sage: Y.reduce
Generic endomorphism of Free module of type-(6,0) tensors on the Rank-3 free module M over the Integer Ring
sage: t = e[0]*e[0]*e[0]*e[0]*e[1]*e[2]; t.disp()
e_0⊗e_0⊗e_0⊗e_0⊗e_1⊗e_2 = e_0⊗e_0⊗e_0⊗e_0⊗e_1⊗e_2
sage: Y.reduce(t)
Traceback (most recent call last):
...
TypeError: no conversion of this rational to integer
TESTS::
sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
sage: e = M.basis('e')
sage: X = M.tensor_module(6, 0)
sage: Y = M.tensor_module(6, 0, sym=((0, 1, 2, 3), (4, 5)))
sage: all(Y.reduce(u.lift()) == 0 for u in Y.basis('e'))
True
"""
sym = self._basis_comp()._sym
antisym = self._basis_comp()._antisym

def _reduce_element(x):
if not x._components:
# zero tensor - methods symmetrize, antisymmetrize are broken
return x
# TODO: Implement a fast symmetry check, either as part of the symmetrize/antisymmetrize methods,
# or as a separate method
symmetrized = x
for s in sym:
symmetrized = symmetrized.symmetrize(*s)
for s in antisym:
symmetrized = symmetrized.antisymmetrize(*s)
return x - symmetrized

return self.ambient().module_morphism(function=_reduce_element, codomain=self.ambient())

@lazy_attribute
def retract(self):
r"""
The retract map from the ambient space.
This is a partial map, which gives an error for elements not in the subspace.
Calling this map on elements of the ambient space is the same as calling the
element constructor of ``self``.
EXAMPLES::
sage: from sage.tensor.modules.tensor_free_submodule import TensorFreeSubmodule_comp
sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
sage: e = M.basis('e')
sage: X = M.tensor_module(6, 0)
sage: Y = M.tensor_module(6, 0, sym=((0, 1, 2, 3), (4, 5)))
sage: e_Y = Y.basis('e')
sage: Y.retract
Generic morphism:
From: Free module of type-(6,0) tensors on the Rank-3 free module M over the Integer Ring
To: Free module of type-(6,0) tensors with 6-indices components w.r.t. (0, 1, 2),
with symmetry on the index positions (0, 1, 2, 3),
with symmetry on the index positions (4, 5)
on the Rank-3 free module M over the Integer Ring
sage: t = e[0]*e[0]*e[0]*e[0]*e[1]*e[2]; t.disp()
e_0⊗e_0⊗e_0⊗e_0⊗e_1⊗e_2 = e_0⊗e_0⊗e_0⊗e_0⊗e_1⊗e_2
sage: Y.retract(t)
Traceback (most recent call last):
...
ValueError: this tensor does not have the symmetries of
Free module of type-(6,0) tensors with 6-indices components w.r.t. (0, 1, 2),
with symmetry on the index positions (0, 1, 2, 3),
with symmetry on the index positions (4, 5)
on the Rank-3 free module M over the Integer Ring
sage: t = e[0]*e[0]*e[0]*e[0]*e[1]*e[2] + e[0]*e[0]*e[0]*e[0]*e[2]*e[1]
sage: y = Y.retract(t); y
Type-(6,0) tensor on the Rank-3 free module M over the Integer Ring
sage: y.disp()
e_0⊗e_0⊗e_0⊗e_0⊗e_1⊗e_2 + e_0⊗e_0⊗e_0⊗e_0⊗e_2⊗e_1
sage: y.parent()._name
'Sym^{0,1,2,3}(M)⊗Sym^{4,5}(M)'
TESTS::
sage: all(Y.retract(u.lift()) == u for u in e_Y)
True
"""
return self.ambient().module_morphism(function=lambda x: self(x), codomain=self)
5 changes: 0 additions & 5 deletions src/sage/tensor/modules/tensor_free_submodule_basis.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,3 @@ def __getitem__(self, index):
element = tensor_module([])
element.set_comp(base_module_basis)[index] = 1
return element

# Todo:
# dual basis
# add test for dual
# lift/reduce/retract

0 comments on commit 9ace3e2

Please sign in to comment.