diff --git a/src/sage/manifolds/differentiable/manifold.py b/src/sage/manifolds/differentiable/manifold.py index 7fa589be987..d650d4eb712 100644 --- a/src/sage/manifolds/differentiable/manifold.py +++ b/src/sage/manifolds/differentiable/manifold.py @@ -1438,9 +1438,9 @@ def tensor_field_module(self, tensor_type, dest_map=None): Free module T^(2,1)(U) of type-(2,1) tensors fields on the Open subset U of the 3-dimensional differentiable manifold M sage: TU.category() - Category of finite dimensional modules over Algebra of - differentiable scalar fields on the Open subset U of the - 3-dimensional differentiable manifold M + Category of tensor products of finite dimensional modules + over Algebra of differentiable scalar fields + on the Open subset U of the 3-dimensional differentiable manifold M sage: TU.base_ring() Algebra of differentiable scalar fields on the Open subset U of the 3-dimensional differentiable manifold M diff --git a/src/sage/manifolds/differentiable/tensorfield_module.py b/src/sage/manifolds/differentiable/tensorfield_module.py index d68264a472c..cbc73f9e520 100644 --- a/src/sage/manifolds/differentiable/tensorfield_module.py +++ b/src/sage/manifolds/differentiable/tensorfield_module.py @@ -652,8 +652,8 @@ class TensorFieldFreeModule(TensorFreeModule): `T^{(2,0)}(\RR^3)` is a module over the algebra `C^k(\RR^3)`:: sage: T20.category() - Category of finite dimensional modules over Algebra of differentiable - scalar fields on the 3-dimensional differentiable manifold R^3 + Category of tensor products of finite dimensional modules over + Algebra of differentiable scalar fields on the 3-dimensional differentiable manifold R^3 sage: T20.base_ring() is M.scalar_field_algebra() True diff --git a/src/sage/manifolds/differentiable/vectorfield_module.py b/src/sage/manifolds/differentiable/vectorfield_module.py index bd76a7547d6..580eec814ab 100644 --- a/src/sage/manifolds/differentiable/vectorfield_module.py +++ b/src/sage/manifolds/differentiable/vectorfield_module.py @@ -506,7 +506,7 @@ def destination_map(self): """ return self._dest_map - def tensor_module(self, k, l): + def tensor_module(self, k, l, *, sym=None, antisym=None): r""" Return the module of type-`(k,l)` tensors on ``self``. @@ -552,6 +552,8 @@ def tensor_module(self, k, l): for more examples and documentation. """ + if sym or antisym: + raise NotImplementedError try: return self._tensor_modules[(k,l)] except KeyError: @@ -1731,7 +1733,7 @@ def destination_map(self) -> DiffMap: """ return self._dest_map - def tensor_module(self, k, l): + def tensor_module(self, k, l, *, sym=None, antisym=None): r""" Return the free module of all tensors of type `(k, l)` defined on ``self``. @@ -1780,6 +1782,8 @@ def tensor_module(self, k, l): for more examples and documentation. """ + if sym or antisym: + raise NotImplementedError try: return self._tensor_modules[(k,l)] except KeyError: @@ -2031,7 +2035,7 @@ def basis(self, symbol=None, latex_symbol=None, from_frame=None, symbol_dual=symbol_dual, latex_symbol_dual=latex_symbol_dual) - def tensor(self, tensor_type, name=None, latex_name=None, sym=None, + def _tensor(self, tensor_type, name=None, latex_name=None, sym=None, antisym=None, specific_type=None): r""" Construct a tensor on ``self``. diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 8c007727799..61a44d022d4 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -721,6 +721,28 @@ def map_isym(isym): 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``. @@ -2122,7 +2144,7 @@ def _test_basis(self, tester=None, **options): TestSuite(b).run(verbose=tester._verbose, prefix=tester._prefix + " ", raise_on_failure=is_sub_testsuite) - def tensor(self, tensor_type, name=None, latex_name=None, sym=None, + def _tensor(self, tensor_type, name=None, latex_name=None, sym=None, antisym=None): r""" Construct a tensor on the free module ``self``. @@ -2131,10 +2153,81 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, - ``tensor_type`` -- pair ``(k, l)`` with ``k`` being the contravariant rank and ``l`` the covariant rank + + - ``name`` -- (default: ``None``) string; name given to the tensor + + - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to + denote the tensor; if none is provided, the LaTeX symbol is set + to ``name`` + + - ``sym`` -- (default: ``None``) a symmetry or an iterable 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 iterable of + antisymmetries among the arguments, with the same convention + as for ``sym`` + + OUTPUT: + + - instance of + :class:`~sage.tensor.modules.free_module_tensor.FreeModuleTensor` + representing the tensor defined on ``self`` with the provided + characteristics + + EXAMPLES: + + Tensors on a rank-3 free module:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: t = M._tensor((1,0), name='t') ; t + Element t of the Rank-3 free module M over the Integer Ring + """ + from .comp import CompWithSym + sym, antisym = CompWithSym._canonicalize_sym_antisym( + tensor_type[0] + tensor_type[1], sym, antisym) + # Special cases: + if tensor_type == (1,0): + return self.element_class(self, name=name, latex_name=latex_name) + elif tensor_type == (0,1): + return self.linear_form(name=name, latex_name=latex_name) + elif tensor_type[0] == 0 and tensor_type[1] > 1 and antisym: + 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 len(antisym[0]) == tensor_type[0]: + return self.alternating_contravariant_tensor(tensor_type[0], + name=name, latex_name=latex_name) + # Generic case: + return self.tensor_module(*tensor_type).element_class(self, + tensor_type, name=name, latex_name=latex_name, + sym=sym, antisym=antisym) + + def tensor(self, *args, **kwds): + r""" + Construct a tensor on the free module ``self`` or a tensor product with other modules. + + If ``args`` consist of other parents, just delegate to :meth:`tensor_product`. + + Otherwise, construct a tensor from the following input. + + INPUT: + + - ``tensor_type`` -- pair ``(k, l)`` with ``k`` being the + contravariant rank and ``l`` the covariant rank + - ``name`` -- (default: ``None``) string; name given to the tensor + - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the tensor; if none is provided, the LaTeX symbol is set to ``name`` + - ``sym`` -- (default: ``None``) a symmetry or an iterable of symmetries among the tensor arguments: each symmetry is described by a tuple containing the positions of the involved arguments, with the @@ -2189,26 +2282,12 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, sage: M.tensor((3,0), antisym=[[]]) Type-(3,0) tensor on the Rank-3 free module M over the Integer Ring """ - from .comp import CompWithSym - sym, antisym = CompWithSym._canonicalize_sym_antisym( - tensor_type[0] + tensor_type[1], sym, antisym) - # Special cases: - if tensor_type == (1,0): - return self.element_class(self, name=name, latex_name=latex_name) - elif tensor_type == (0,1): - return self.linear_form(name=name, latex_name=latex_name) - elif tensor_type[0] == 0 and tensor_type[1] > 1 and antisym: - 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 len(antisym[0]) == tensor_type[0]: - return self.alternating_contravariant_tensor(tensor_type[0], - name=name, latex_name=latex_name) - # Generic case: - return self.tensor_module(*tensor_type).element_class(self, - tensor_type, name=name, latex_name=latex_name, - sym=sym, antisym=antisym) + # 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 tensor_from_comp(self, tensor_type, comp, name=None, latex_name=None): r""" diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index 39174258ebb..b04e8581148 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -58,6 +58,7 @@ # http://www.gnu.org/licenses/ #****************************************************************************** +from sage.categories.modules import Modules from sage.misc.cachefunc import cached_method from sage.tensor.modules.finite_rank_free_module import FiniteRankFreeModule_abstract from sage.tensor.modules.free_module_tensor import FreeModuleTensor @@ -126,7 +127,7 @@ class TensorFreeModule(FiniteRankFreeModule_abstract): ``T`` is a module (actually a free module) over `\ZZ`:: sage: T.category() - Category of finite dimensional modules over Integer Ring + Category of tensor products of finite dimensional modules over Integer Ring sage: T in Modules(ZZ) True sage: T.rank() @@ -336,7 +337,7 @@ class TensorFreeModule(FiniteRankFreeModule_abstract): Element = FreeModuleTensor - def __init__(self, fmodule, tensor_type, name=None, latex_name=None): + def __init__(self, fmodule, tensor_type, name=None, latex_name=None, category=None): r""" TESTS:: @@ -347,33 +348,47 @@ def __init__(self, fmodule, tensor_type, name=None, latex_name=None): """ self._fmodule = fmodule self._tensor_type = tuple(tensor_type) + ring = fmodule._ring rank = pow(fmodule._rank, tensor_type[0] + tensor_type[1]) if self._tensor_type == (0,1): # case of the dual + category = Modules(ring).FiniteDimensional().or_subcategory(category) if name is None and fmodule._name is not None: name = fmodule._name + '*' if latex_name is None and fmodule._latex_name is not None: latex_name = fmodule._latex_name + r'^*' else: + category = Modules(ring).FiniteDimensional().TensorProducts().or_subcategory(category) if name is None and fmodule._name is not None: name = 'T^' + str(self._tensor_type) + '(' + fmodule._name + \ ')' if latex_name is None and fmodule._latex_name is not None: latex_name = r'T^{' + str(self._tensor_type) + r'}\left(' + \ fmodule._latex_name + r'\right)' - super().__init__(fmodule._ring, rank, name=name, latex_name=latex_name) + super().__init__(fmodule._ring, rank, name=name, latex_name=latex_name, category=category) fmodule._all_modules.add(self) - def construction(self): + def tensor_factors(self): r""" - TESTS:: + 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.construction() is None - True + 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] """ - # No construction until https://trac.sagemath.org/ticket/31276 provides tensor_product methods - return None + 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 diff --git a/src/sage/tensor/modules/tensor_free_submodule.py b/src/sage/tensor/modules/tensor_free_submodule.py index 5f6964f8546..ff4f739ac0d 100644 --- a/src/sage/tensor/modules/tensor_free_submodule.py +++ b/src/sage/tensor/modules/tensor_free_submodule.py @@ -178,6 +178,24 @@ def power_name(op, s, latex=False): latex_name=latex_name, category=category, ambient=ambient) + def construction(self): + # TODO: Define the symmetry group and its action (https://trac.sagemath.org/ticket/34495), + # return the construction functor for invariant subobjects. + r""" + Return the functorial construction of ``self``. + + This implementation just returns ``None``. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: Sym2M = M.tensor_module(2, 0, sym=range(2)); Sym2M + Free module of fully symmetric type-(2,0) tensors on the Rank-3 free module M over the Integer Ring + sage: Sym2M.construction() is None + True + """ + return None + @cached_method def _basis_sym(self): r"""