diff --git a/src/doc/en/reference/tensor_free_modules/tensors.rst b/src/doc/en/reference/tensor_free_modules/tensors.rst index 434ea734191..65b7786a1cb 100644 --- a/src/doc/en/reference/tensor_free_modules/tensors.rst +++ b/src/doc/en/reference/tensor_free_modules/tensors.rst @@ -4,6 +4,8 @@ Tensors .. toctree:: :maxdepth: 2 + sage/tensor/modules/reflexive_module + sage/tensor/modules/tensor_free_module sage/tensor/modules/tensor_free_submodule diff --git a/src/sage/manifolds/differentiable/diff_form_module.py b/src/sage/manifolds/differentiable/diff_form_module.py index 37feeaa7650..04d85a0817d 100644 --- a/src/sage/manifolds/differentiable/diff_form_module.py +++ b/src/sage/manifolds/differentiable/diff_form_module.py @@ -47,6 +47,7 @@ from sage.manifolds.differentiable.diff_form import DiffForm, DiffFormParal from sage.manifolds.differentiable.tensorfield import TensorField from sage.manifolds.differentiable.tensorfield_paral import TensorFieldParal +from sage.tensor.modules.reflexive_module import ReflexiveModule_abstract class DiffFormModule(UniqueRepresentation, Parent): @@ -534,6 +535,31 @@ def base_module(self): """ return self._vmodule + tensor = tensor_product = ReflexiveModule_abstract.tensor_product + + def tensor_type(self): + r""" + Return the tensor type of ``self`` if ``self`` is a module of 1-forms. + + In this case, the pair `(0, 1)` is returned, indicating that the module + is identified with the dual of the base module. + + For differential forms of other degrees, an exception is raised. + + EXAMPLES:: + + sage: M = Manifold(3, 'M') + sage: M.diff_form_module(1).tensor_type() + (0, 1) + sage: M.diff_form_module(2).tensor_type() + Traceback (most recent call last): + ... + NotImplementedError + """ + if self._degree == 1: + return (0, 1) + raise NotImplementedError + def degree(self): r""" Return the degree of the differential forms in ``self``. diff --git a/src/sage/manifolds/differentiable/tensorfield.py b/src/sage/manifolds/differentiable/tensorfield.py index a2aad4d4937..db4a2a05aa1 100644 --- a/src/sage/manifolds/differentiable/tensorfield.py +++ b/src/sage/manifolds/differentiable/tensorfield.py @@ -176,8 +176,8 @@ class TensorField(ModuleElementWithMutability): Module T^(0,2)(S^2) of type-(0,2) tensors fields on the 2-dimensional differentiable manifold S^2 sage: t.parent().category() - Category of modules over Algebra of differentiable scalar fields on the - 2-dimensional differentiable manifold S^2 + Category of tensor products of modules over Algebra of differentiable scalar fields + on the 2-dimensional differentiable manifold S^2 The parent of `t` is not a free module, for the sphere `S^2` is not parallelizable:: diff --git a/src/sage/manifolds/differentiable/tensorfield_module.py b/src/sage/manifolds/differentiable/tensorfield_module.py index cbc73f9e520..6347189848f 100644 --- a/src/sage/manifolds/differentiable/tensorfield_module.py +++ b/src/sage/manifolds/differentiable/tensorfield_module.py @@ -27,9 +27,11 @@ """ # ***************************************************************************** -# Copyright (C) 2015 Eric Gourgoulhon -# Copyright (C) 2015 Michal Bejger -# Copyright (C) 2016 Travis Scrimshaw +# Copyright (C) 2015-2018 Eric Gourgoulhon +# 2015 Michal Bejger +# 2016 Travis Scrimshaw +# 2020 Michael Jung +# 2022 Matthias Koeppe # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -41,6 +43,7 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent from sage.categories.modules import Modules +from sage.tensor.modules.reflexive_module import ReflexiveModule_tensor from sage.tensor.modules.tensor_free_module import TensorFreeModule from sage.manifolds.differentiable.tensorfield import TensorField from sage.manifolds.differentiable.tensorfield_paral import TensorFieldParal @@ -51,7 +54,8 @@ from sage.manifolds.differentiable.automorphismfield import (AutomorphismField, AutomorphismFieldParal) -class TensorFieldModule(UniqueRepresentation, Parent): + +class TensorFieldModule(UniqueRepresentation, ReflexiveModule_tensor): r""" Module of tensor fields of a given type `(k,l)` along a differentiable manifold `U` with values on a differentiable manifold `M`, via a @@ -123,8 +127,8 @@ class TensorFieldModule(UniqueRepresentation, Parent): `T^{(2,0)}(M)` is a module over the algebra `C^k(M)`:: sage: T20.category() - Category of modules over Algebra of differentiable scalar fields on the - 2-dimensional differentiable manifold M + Category of tensor products of modules over Algebra of differentiable scalar fields + on the 2-dimensional differentiable manifold M sage: T20.base_ring() is M.scalar_field_algebra() True @@ -226,10 +230,16 @@ class TensorFieldModule(UniqueRepresentation, Parent): [1 0] [0 1] + TESTS:: + + sage: T11.tensor_factors() + [Module X(M) of vector fields on the 2-dimensional differentiable manifold M, + Module Omega^1(M) of 1-forms on the 2-dimensional differentiable manifold M] + """ Element = TensorField - def __init__(self, vector_field_module, tensor_type): + def __init__(self, vector_field_module, tensor_type, category=None): r""" Construct a module of tensor fields taking values on a (a priori) not parallelizable differentiable manifold. @@ -281,7 +291,8 @@ def __init__(self, vector_field_module, tensor_type): # the member self._ring is created for efficiency (to avoid calls to # self.base_ring()): self._ring = domain.scalar_field_algebra() - Parent.__init__(self, base=self._ring, category=Modules(self._ring)) + category = Modules(self._ring).TensorProducts().or_subcategory(category) + Parent.__init__(self, base=self._ring, category=category) self._domain = domain self._dest_map = dest_map self._ambient_domain = vector_field_module._ambient_domain diff --git a/src/sage/manifolds/differentiable/vectorfield_module.py b/src/sage/manifolds/differentiable/vectorfield_module.py index 580eec814ab..ac8e7dc1290 100644 --- a/src/sage/manifolds/differentiable/vectorfield_module.py +++ b/src/sage/manifolds/differentiable/vectorfield_module.py @@ -54,13 +54,14 @@ from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.tensor.modules.finite_rank_free_module import FiniteRankFreeModule +from sage.tensor.modules.reflexive_module import ReflexiveModule_base if TYPE_CHECKING: from sage.manifolds.differentiable.diff_map import DiffMap from sage.manifolds.differentiable.manifold import DifferentiableManifold -class VectorFieldModule(UniqueRepresentation, Parent): +class VectorFieldModule(UniqueRepresentation, ReflexiveModule_base): r""" Module of vector fields along a differentiable manifold `U` with values on a differentiable manifold `M`, via a differentiable @@ -731,8 +732,8 @@ def general_linear_group(self): self._general_linear_group = AutomorphismFieldGroup(self) return self._general_linear_group - def tensor(self, tensor_type, name=None, latex_name=None, sym=None, - antisym=None, specific_type=None): + def _tensor(self, tensor_type, name=None, latex_name=None, sym=None, + antisym=None, specific_type=None): r""" Construct a tensor on ``self``. @@ -776,10 +777,6 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, sage: XM.tensor((1,2), name='t') Tensor field t of type (1,2) on the 2-dimensional differentiable manifold M - sage: XM.tensor((1,0), name='a') - Vector field a on the 2-dimensional differentiable manifold M - sage: XM.tensor((0,2), name='a', antisym=(0,1)) - 2-form a on the 2-dimensional differentiable manifold M .. SEEALSO:: @@ -824,6 +821,82 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, self, tensor_type, name=name, latex_name=latex_name, sym=sym, antisym=antisym) + def tensor(self, *args, **kwds): + r""" + Construct a tensor field on the domain of ``self`` or a tensor product of ``self`` with other modules. + + If ``args`` consist of other parents, just delegate to :meth:`tensor_product`. + + Otherwise, construct a tensor (i.e., a tensor field on the domain of + the vector field module) from the following input. + + INPUT: + + - ``tensor_type`` -- pair (k,l) with k being the contravariant rank + and l the covariant rank + - ``name`` -- (string; default: ``None``) name given to the tensor + - ``latex_name`` -- (string; default: ``None``) LaTeX symbol to denote + the tensor; if none is provided, the LaTeX symbol is set to ``name`` + - ``sym`` -- (default: ``None``) a symmetry or a list of symmetries + among the tensor arguments: each symmetry is described by a tuple + containing the positions of the involved arguments, with the + convention position=0 for the first argument; for instance: + + * ``sym=(0,1)`` for a symmetry between the 1st and 2nd arguments + * ``sym=[(0,2),(1,3,4)]`` for a symmetry between the 1st and 3rd + arguments and a symmetry between the 2nd, 4th and 5th arguments + + - ``antisym`` -- (default: ``None``) antisymmetry or list of + antisymmetries among the arguments, with the same convention + as for ``sym`` + - ``specific_type`` -- (default: ``None``) specific subclass of + :class:`~sage.manifolds.differentiable.tensorfield.TensorField` for + the output + + OUTPUT: + + - instance of + :class:`~sage.manifolds.differentiable.tensorfield.TensorField` + representing the tensor defined on the vector field module with the + provided characteristics + + EXAMPLES:: + + sage: M = Manifold(2, 'M') + sage: XM = M.vector_field_module() + sage: XM.tensor((1,2), name='t') + Tensor field t of type (1,2) on the 2-dimensional differentiable + manifold M + sage: XM.tensor((1,0), name='a') + Vector field a on the 2-dimensional differentiable manifold M + sage: XM.tensor((0,2), name='a', antisym=(0,1)) + 2-form a on the 2-dimensional differentiable manifold M + + Delegation to :meth:`tensor_product`:: + + sage: M = Manifold(2, 'M') + sage: XM = M.vector_field_module() + sage: XM.tensor(XM) + Module T^(2,0)(M) of type-(2,0) tensors fields on the 2-dimensional differentiable manifold M + sage: XM.tensor(XM, XM.dual(), XM) + Module T^(3,1)(M) of type-(3,1) tensors fields on the 2-dimensional differentiable manifold M + sage: XM.tensor(XM).tensor(XM.dual().tensor(XM.dual())) + Traceback (most recent call last): + ... + AttributeError: 'TensorFieldModule_with_category' object has no attribute '_basis_sym' + + .. SEEALSO:: + + :class:`~sage.manifolds.differentiable.tensorfield.TensorField` + for more examples and documentation. + """ + # Until https://trac.sagemath.org/ticket/30373 is done, + # TensorProductFunctor._functor_name is "tensor", so this method + # also needs to double as the tensor product construction + if isinstance(args[0], Parent): + return self.tensor_product(*args, **kwds) + return self._tensor(*args, **kwds) + def alternating_contravariant_tensor(self, degree, name=None, latex_name=None): r""" diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 61a44d022d4..534c99d0b3f 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -548,8 +548,14 @@ class :class:`~sage.modules.free_module.FreeModule_generic` from sage.tensor.modules.free_module_alt_form import FreeModuleAltForm from sage.tensor.modules.free_module_element import FiniteRankFreeModuleElement from sage.tensor.modules.free_module_tensor import FreeModuleTensor +from sage.tensor.modules.reflexive_module import ( + ReflexiveModule_abstract, + ReflexiveModule_base, + ReflexiveModule_dual, +) -class FiniteRankFreeModule_abstract(UniqueRepresentation, Parent): + +class FiniteRankFreeModule_abstract(UniqueRepresentation, ReflexiveModule_abstract): r""" Abstract base class for free modules of finite rank over a commutative ring. """ @@ -619,130 +625,6 @@ def _latex_(self): else: return self._latex_name - def tensor_power(self, n): - r""" - Return the ``n``-fold tensor product of ``self``. - - EXAMPLES:: - - sage: M = FiniteRankFreeModule(QQ, 2) - sage: M.tensor_power(3) - Free module of type-(3,0) tensors on the 2-dimensional vector space over the Rational Field - sage: M.tensor_module(1,2).tensor_power(3) - Free module of type-(3,6) tensors on the 2-dimensional vector space over the Rational Field - """ - tensor_type = self.tensor_type() - return self.base_module().tensor_module(n * tensor_type[0], n * tensor_type[1]) - - def tensor_product(self, *others): - r""" - Return the tensor product of ``self`` and ``others``. - - EXAMPLES:: - - sage: M = FiniteRankFreeModule(QQ, 2) - sage: M.tensor_product(M) - Free module of type-(2,0) tensors on the 2-dimensional vector space over the Rational Field - sage: M.tensor_product(M.dual()) - Free module of type-(1,1) tensors on the 2-dimensional vector space over the Rational Field - sage: M.dual().tensor_product(M, M.dual()) - Free module of type-(1,2) tensors on the 2-dimensional vector space over the Rational Field - sage: M.tensor_product(M.tensor_module(1,2)) - Free module of type-(2,2) tensors on the 2-dimensional vector space over the Rational Field - sage: M.tensor_module(1,2).tensor_product(M) - Free module of type-(2,2) tensors on the 2-dimensional vector space over the Rational Field - sage: M.tensor_module(1,1).tensor_product(M.tensor_module(1,2)) - Free module of type-(2,3) tensors on the 2-dimensional vector space over the Rational Field - - sage: Sym2M = M.tensor_module(2, 0, sym=range(2)); Sym2M - Free module of fully symmetric type-(2,0) tensors on the 2-dimensional vector space over the Rational Field - sage: Sym01x23M = Sym2M.tensor_product(Sym2M); Sym01x23M - Free module of type-(4,0) tensors on the 2-dimensional vector space over the Rational Field, - with symmetry on the index positions (0, 1), with symmetry on the index positions (2, 3) - sage: Sym01x23M._index_maps - ((0, 1), (2, 3)) - - sage: N = M.tensor_module(3, 3, sym=[1, 2], antisym=[3, 4]); N - Free module of type-(3,3) tensors on the 2-dimensional vector space over the Rational Field, - with symmetry on the index positions (1, 2), - with antisymmetry on the index positions (3, 4) - sage: NxN = N.tensor_product(N); NxN - Free module of type-(6,6) tensors on the 2-dimensional vector space over the Rational Field, - with symmetry on the index positions (1, 2), with symmetry on the index positions (4, 5), - with antisymmetry on the index positions (6, 7), with antisymmetry on the index positions (9, 10) - sage: NxN._index_maps - ((0, 1, 2, 6, 7, 8), (3, 4, 5, 9, 10, 11)) - """ - from sage.modules.free_module_element import vector - from .comp import CompFullySym, CompFullyAntiSym, CompWithSym - - base_module = self.base_module() - if not all(module.base_module() == base_module for module in others): - raise NotImplementedError('all factors must be tensor modules over the same base module') - factors = [self] + list(others) - result_tensor_type = sum(vector(factor.tensor_type()) for factor in factors) - result_sym = [] - result_antisym = [] - # Keep track of reordering of the contravariant and covariant indices - # (compatible with FreeModuleTensor.__mul__) - index_maps = [] - running_indices = vector([0, result_tensor_type[0]]) - for factor in factors: - tensor_type = factor.tensor_type() - index_map = tuple(i + running_indices[0] for i in range(tensor_type[0])) - index_map += tuple(i + running_indices[1] for i in range(tensor_type[1])) - index_maps.append(index_map) - - if tensor_type[0] + tensor_type[1] > 1: - basis_sym = factor._basis_sym() - all_indices = tuple(range(tensor_type[0] + tensor_type[1])) - if isinstance(basis_sym, CompFullySym): - sym = [all_indices] - antisym = [] - elif isinstance(basis_sym, CompFullyAntiSym): - sym = [] - antisym = [all_indices] - elif isinstance(basis_sym, CompWithSym): - sym = basis_sym._sym - antisym = basis_sym._antisym - else: - sym = antisym = [] - - def map_isym(isym): - return tuple(index_map[i] for i in isym) - - result_sym.extend(tuple(index_map[i] for i in isym) for isym in sym) - result_antisym.extend(tuple(index_map[i] for i in isym) for isym in antisym) - - running_indices += vector(tensor_type) - - result = base_module.tensor_module(*result_tensor_type, - sym=result_sym, antisym=result_antisym) - result._index_maps = tuple(index_maps) - return result - - def tensor(self, *args, **kwds): - # Until https://trac.sagemath.org/ticket/30373 is done, - # TensorProductFunctor._functor_name is "tensor", so here we delegate. - r""" - Return the tensor product of ``self`` and ``others``. - - This method is invoked when :class:`~sage.categories.tensor.TensorProductFunctor` - is applied to parents. - - It just delegates to :meth:`tensor_product`. - - EXAMPLES:: - - sage: M = FiniteRankFreeModule(QQ, 2); M - 2-dimensional vector space over the Rational Field - sage: M20 = M.tensor_module(2, 0); M20 - Free module of type-(2,0) tensors on the 2-dimensional vector space over the Rational Field - sage: tensor([M20, M20]) - Free module of type-(4,0) tensors on the 2-dimensional vector space over the Rational Field - """ - return self.tensor_product(*args, **kwds) - def rank(self) -> int: r""" Return the rank of the free module ``self``. @@ -1092,7 +974,7 @@ def _test_isomorphism_with_fixed_basis(self, **options): tester.assertEqual(morphism.codomain().rank(), self.rank()) -class FiniteRankFreeModule(FiniteRankFreeModule_abstract): +class FiniteRankFreeModule(ReflexiveModule_base, FiniteRankFreeModule_abstract): r""" Free module of finite rank over a commutative ring. @@ -3430,34 +3312,8 @@ def identity_map(self, name='Id', latex_name=None): self._identity_map.set_name(name=name, latex_name=latex_name) return self._identity_map - def base_module(self): - r""" - Return the free module on which ``self`` is constructed, namely ``self`` itself. - - EXAMPLES:: - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: M.base_module() is M - True - - """ - return self - - def tensor_type(self): - r""" - Return the tensor type of ``self``, the pair `(1, 0)`. - - EXAMPLES:: - - sage: M = FiniteRankFreeModule(ZZ, 3) - sage: M.tensor_type() - (1, 0) - - """ - return (1, 0) - - -class FiniteRankDualFreeModule(FiniteRankFreeModule_abstract): +class FiniteRankDualFreeModule(ReflexiveModule_dual, FiniteRankFreeModule_abstract): r""" Dual of a free module of finite rank over a commutative ring. @@ -3584,24 +3440,6 @@ def __init__(self, fmodule, name=None, latex_name=None): latex_name=latex_name) fmodule._all_modules.add(self) - def construction(self): - r""" - Return the functorial construction of ``self``. - - This implementation just returns ``None``, as no functorial construction is implemented. - - TESTS:: - - sage: from sage.tensor.modules.ext_pow_free_module import ExtPowerDualFreeModule - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: e = M.basis('e') - sage: A = M.dual() - sage: A.construction() is None - True - """ - # No construction until we extend VectorFunctor with a parameter 'dual' - return None - #### Parent methods def _element_constructor_(self, comp=[], basis=None, name=None, @@ -3736,16 +3574,3 @@ def base_module(self): """ return self._fmodule - - def tensor_type(self): - r""" - Return the tensor type of ``self``. - - EXAMPLES:: - - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: M.dual().tensor_type() - (0, 1) - - """ - return (0, 1) diff --git a/src/sage/tensor/modules/reflexive_module.py b/src/sage/tensor/modules/reflexive_module.py new file mode 100644 index 00000000000..f969d547811 --- /dev/null +++ b/src/sage/tensor/modules/reflexive_module.py @@ -0,0 +1,385 @@ +r""" +Base classes for reflexive modules +""" + +from sage.misc.abstract_method import abstract_method +from sage.structure.parent import Parent + + +class ReflexiveModule_abstract(Parent): + r""" + Abstract base class for reflexive modules. + + An `R`-module `M` is *reflexive* if the natural map from `M` to its double + dual `M^{**}` is an isomorphism. + + In the category of `R`-modules, the dual module `M^*` is + the `R`-module of linear functionals `\phi:\ M \longrightarrow R`. + However, we do not make the assumption that the dual module + (obtained by :meth:`dual`) is in the category :class:`Homsets`. + + We identify the double dual `M^{**}` with `M`. + + Tensor products of reflexive modules are reflexive. We identify all + tensor products of `k` copies of `M` and `l` copies of `M^*` and + denote it by `T^{(k,l)}(M)`. The :meth:`tensor_type` of such a tensor + product is the pair `(k, l)`, and `M` is called its :meth:`base_module`. + + There are three abstract subclasses: + + - :class:`ReflexiveModule_base` is the base class for implementations + of base modules `M`. + + - :class:`ReflexiveModule_dual` is the base class for implementations + of duals `M^*`. + + - :class:`ReflexiveModule_tensor` is the base class for implementations + of tensor modules `T^{(k,l)}(M)`. + + TESTS:: + + sage: from sage.tensor.modules.reflexive_module import ( + ....: ReflexiveModule_abstract, ReflexiveModule_base, + ....: ReflexiveModule_dual, ReflexiveModule_tensor) + sage: M = FiniteRankFreeModule(ZZ, 3) + sage: isinstance(M, ReflexiveModule_abstract) + True + sage: isinstance(M, ReflexiveModule_base) + True + sage: isinstance(M.dual(), ReflexiveModule_abstract) + True + sage: isinstance(M.dual(), ReflexiveModule_dual) + True + sage: isinstance(M.tensor_module(1, 1), ReflexiveModule_abstract) + True + sage: isinstance(M.tensor_module(1, 1), ReflexiveModule_tensor) + True + """ + + @abstract_method(optional=True) + def tensor_type(self): + r""" + Return the tensor type of ``self``. + + OUTPUT: + + - pair `(k,l)` such that ``self`` is the module tensor product + `T^{(k,l)}(M)`, where `M` is the :meth:`base_module` of ``self``. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3) + sage: T = M.tensor_module(1, 2) + sage: T.tensor_type() + (1, 2) + """ + + @abstract_method + def base_module(self): + r""" + Return the module on which ``self`` is constructed. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3) + sage: M.base_module() is M + True + sage: M.dual().base_module() is M + True + sage: M.tensor_module(1, 2).base_module() is M + True + """ + + def dual(self): + r""" + Return the dual module. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3) + sage: M.dual() + Dual of the Rank-3 free module over the Integer Ring + sage: M.dual().dual() + Rank-3 free module over the Integer Ring + sage: M.tensor_module(1, 2) + Free module of type-(1,2) tensors on the Rank-3 free module over the Integer Ring + sage: M.tensor_module(1, 2).dual() + Free module of type-(2,1) tensors on the Rank-3 free module over the Integer Ring + """ + k, l = self.tensor_type() + return self.base_module().tensor_module(l, k) + + def tensor(self, *args, **kwds): + # Until https://trac.sagemath.org/ticket/30373 is done, + # TensorProductFunctor._functor_name is "tensor", so here we delegate. + r""" + Return the tensor product of ``self`` and ``others``. + + This method is invoked when :class:`~sage.categories.tensor.TensorProductFunctor` + is applied to parents. + + It just delegates to :meth:`tensor_product`. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(QQ, 2); M + 2-dimensional vector space over the Rational Field + sage: M20 = M.tensor_module(2, 0); M20 + Free module of type-(2,0) tensors on the 2-dimensional vector space over the Rational Field + sage: tensor([M20, M20]) + Free module of type-(4,0) tensors on the 2-dimensional vector space over the Rational Field + """ + return self.tensor_product(*args, **kwds) + + def tensor_power(self, n): + r""" + Return the ``n``-fold tensor product of ``self``. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(QQ, 2) + sage: M.tensor_power(3) + Free module of type-(3,0) tensors on the 2-dimensional vector space over the Rational Field + sage: M.tensor_module(1,2).tensor_power(3) + Free module of type-(3,6) tensors on the 2-dimensional vector space over the Rational Field + """ + tensor_type = self.tensor_type() + return self.base_module().tensor_module(n * tensor_type[0], n * tensor_type[1]) + + def tensor_product(self, *others): + r""" + Return the tensor product of ``self`` and ``others``. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(QQ, 2) + sage: M.tensor_product(M) + Free module of type-(2,0) tensors on the 2-dimensional vector space over the Rational Field + sage: M.tensor_product(M.dual()) + Free module of type-(1,1) tensors on the 2-dimensional vector space over the Rational Field + sage: M.dual().tensor_product(M, M.dual()) + Free module of type-(1,2) tensors on the 2-dimensional vector space over the Rational Field + sage: M.tensor_product(M.tensor_module(1,2)) + Free module of type-(2,2) tensors on the 2-dimensional vector space over the Rational Field + sage: M.tensor_module(1,2).tensor_product(M) + Free module of type-(2,2) tensors on the 2-dimensional vector space over the Rational Field + sage: M.tensor_module(1,1).tensor_product(M.tensor_module(1,2)) + Free module of type-(2,3) tensors on the 2-dimensional vector space over the Rational Field + + sage: Sym2M = M.tensor_module(2, 0, sym=range(2)); Sym2M + Free module of fully symmetric type-(2,0) tensors on the 2-dimensional vector space over the Rational Field + sage: Sym01x23M = Sym2M.tensor_product(Sym2M); Sym01x23M + Free module of type-(4,0) tensors on the 2-dimensional vector space over the Rational Field, + with symmetry on the index positions (0, 1), with symmetry on the index positions (2, 3) + sage: Sym01x23M._index_maps + ((0, 1), (2, 3)) + + sage: N = M.tensor_module(3, 3, sym=[1, 2], antisym=[3, 4]); N + Free module of type-(3,3) tensors on the 2-dimensional vector space over the Rational Field, + with symmetry on the index positions (1, 2), + with antisymmetry on the index positions (3, 4) + sage: NxN = N.tensor_product(N); NxN + Free module of type-(6,6) tensors on the 2-dimensional vector space over the Rational Field, + with symmetry on the index positions (1, 2), with symmetry on the index positions (4, 5), + with antisymmetry on the index positions (6, 7), with antisymmetry on the index positions (9, 10) + sage: NxN._index_maps + ((0, 1, 2, 6, 7, 8), (3, 4, 5, 9, 10, 11)) + """ + from sage.modules.free_module_element import vector + from .comp import CompFullySym, CompFullyAntiSym, CompWithSym + + base_module = self.base_module() + if not all(module.base_module() == base_module for module in others): + raise NotImplementedError('all factors must be tensor modules over the same base module') + factors = [self] + list(others) + result_tensor_type = sum(vector(factor.tensor_type()) for factor in factors) + result_sym = [] + result_antisym = [] + # Keep track of reordering of the contravariant and covariant indices + # (compatible with FreeModuleTensor.__mul__) + index_maps = [] + running_indices = vector([0, result_tensor_type[0]]) + for factor in factors: + tensor_type = factor.tensor_type() + index_map = tuple(i + running_indices[0] for i in range(tensor_type[0])) + index_map += tuple(i + running_indices[1] for i in range(tensor_type[1])) + index_maps.append(index_map) + + if tensor_type[0] + tensor_type[1] > 1: + basis_sym = factor._basis_sym() + all_indices = tuple(range(tensor_type[0] + tensor_type[1])) + if isinstance(basis_sym, CompFullySym): + sym = [all_indices] + antisym = [] + elif isinstance(basis_sym, CompFullyAntiSym): + sym = [] + antisym = [all_indices] + elif isinstance(basis_sym, CompWithSym): + sym = basis_sym._sym + antisym = basis_sym._antisym + else: + sym = antisym = [] + + def map_isym(isym): + return tuple(index_map[i] for i in isym) + + result_sym.extend(tuple(index_map[i] for i in isym) for isym in sym) + result_antisym.extend(tuple(index_map[i] for i in isym) for isym in antisym) + + running_indices += vector(tensor_type) + + result = base_module.tensor_module(*result_tensor_type, + sym=result_sym, antisym=result_antisym) + result._index_maps = tuple(index_maps) + return result + + +class ReflexiveModule_base(ReflexiveModule_abstract): + r""" + Abstract base class for reflexive modules that are base modules. + + TESTS:: + + sage: from sage.tensor.modules.reflexive_module import ReflexiveModule_base + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: isinstance(M, ReflexiveModule_base) + True + """ + + def base_module(self): + r""" + Return the free module on which ``self`` is constructed, namely ``self`` itself. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: M.base_module() is M + True + + sage: M = Manifold(2, 'M') + sage: XM = M.vector_field_module() + sage: XM.base_module() is XM + True + """ + return self + + def tensor_type(self): + r""" + Return the tensor type of ``self``, the pair `(1, 0)`. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3) + sage: M.tensor_type() + (1, 0) + + sage: M = Manifold(2, 'M') + sage: XM = M.vector_field_module() + sage: XM.tensor_type() + (1, 0) + """ + return (1, 0) + + def dual(self): + r""" + Return the dual module. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: M.dual() + Dual of the Rank-3 free module M over the Integer Ring + """ + return self.tensor_module(0, 1) + + @abstract_method + def tensor_module(self, k, l, **kwds): + r""" + Return the module of all tensors of type `(k, l)` defined on ``self``. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3) + sage: M.tensor_module(1, 2) + Free module of type-(1,2) tensors on the Rank-3 free module over the Integer Ring + """ + + +class ReflexiveModule_dual(ReflexiveModule_abstract): + r""" + Abstract base class for reflexive modules that are the duals of base modules. + + TESTS:: + + sage: from sage.tensor.modules.reflexive_module import ReflexiveModule_dual + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: isinstance(M.dual(), ReflexiveModule_dual) + True + """ + + def tensor_type(self): + r""" + Return the tensor type of ``self``. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: M.dual().tensor_type() + (0, 1) + """ + return (0, 1) + + def construction(self): + r""" + Return the functorial construction of ``self``. + + This implementation just returns ``None``, as no functorial construction is implemented. + + TESTS:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: e = M.basis('e') + sage: A = M.dual() + sage: A.construction() is None + True + """ + # Until https://trac.sagemath.org/ticket/34605 is done + return None + + +class ReflexiveModule_tensor(ReflexiveModule_abstract): + r""" + Abstract base class for reflexive modules that are tensor products of base modules. + + TESTS:: + + sage: from sage.tensor.modules.reflexive_module import ReflexiveModule_tensor + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: isinstance(M.tensor_module(1, 1), ReflexiveModule_tensor) + True + """ + + def tensor_factors(self): + r""" + Return the tensor factors of this tensor module. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: T = M.tensor_module(2, 3) + sage: T.tensor_factors() + [Rank-3 free module M over the Integer Ring, + Rank-3 free module M over the Integer Ring, + Dual of the Rank-3 free module M over the Integer Ring, + Dual of the Rank-3 free module M over the Integer Ring, + Dual of the Rank-3 free module M over the Integer Ring] + """ + tensor_type = self.tensor_type() + if tensor_type == (0,1): # case of the dual + raise NotImplementedError + bmodule = self.base_module() + factors = [bmodule] * tensor_type[0] + dmodule = bmodule.dual() + if tensor_type[1]: + factors += [dmodule] * tensor_type[1] + return factors diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index b04e8581148..3dd3a573c89 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -67,9 +67,11 @@ from sage.tensor.modules.free_module_morphism import \ FiniteRankFreeModuleMorphism from sage.tensor.modules.free_module_automorphism import FreeModuleAutomorphism +from sage.tensor.modules.reflexive_module import ReflexiveModule_tensor + from .tensor_free_submodule_basis import TensorFreeSubmoduleBasis_sym -class TensorFreeModule(FiniteRankFreeModule_abstract): +class TensorFreeModule(ReflexiveModule_tensor, FiniteRankFreeModule_abstract): r""" Class for the free modules over a commutative ring `R` that are tensor products of a given free module `M` over `R` with itself and its @@ -367,29 +369,6 @@ def __init__(self, fmodule, tensor_type, name=None, latex_name=None, category=No super().__init__(fmodule._ring, rank, name=name, latex_name=latex_name, category=category) fmodule._all_modules.add(self) - def tensor_factors(self): - r""" - Return the tensor factors of this tensor module. - - EXAMPLES:: - - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: T = M.tensor_module(2, 3) - sage: T.tensor_factors() - [Rank-3 free module M over the Integer Ring, - Rank-3 free module M over the Integer Ring, - Dual of the Rank-3 free module M over the Integer Ring, - Dual of the Rank-3 free module M over the Integer Ring, - Dual of the Rank-3 free module M over the Integer Ring] - """ - if self._tensor_type == (0,1): # case of the dual - raise NotImplementedError - factors = [self._fmodule] * self._tensor_type[0] - dmodule = self._fmodule.dual() - if self._tensor_type[1]: - factors += [dmodule] * self._tensor_type[1] - return factors - #### Parent Methods def _element_constructor_(self, comp=[], basis=None, name=None,