Coverage for local/lib/python2.7/site-packages/sage/combinat/free_module.py : 65%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# -*- coding: utf-8 -*- """ Free modules """ #***************************************************************************** # Copyright (C) 2007 Mike Hansen <mhansen@gmail.com>, # 2007-2009 Nicolas M. Thiery <nthiery at users.sf.net> # 2010 Christian Stump <christian.stump@univie.ac.at> # # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function from six.moves import range
from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent from sage.structure.indexed_generators import IndexedGenerators, parse_indices_names from sage.modules.module import Module from sage.rings.all import Integer from sage.structure.element import parent from sage.modules.with_basis.indexed_element import IndexedFreeModuleElement from sage.sets.finite_enumerated_set import FiniteEnumeratedSet from sage.combinat.cartesian_product import CartesianProduct_iters from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute from sage.categories.morphism import SetMorphism from sage.categories.all import Category, Sets, ModulesWithBasis from sage.categories.tensor import tensor import sage.data_structures.blas_dict as blas from sage.typeset.ascii_art import AsciiArt from sage.typeset.unicode_art import UnicodeArt from sage.misc.superseded import deprecation
import six
class CombinatorialFreeModule(UniqueRepresentation, Module, IndexedGenerators): r""" Class for free modules with a named basis
INPUT:
- ``R`` - base ring
- ``basis_keys`` - list, tuple, family, set, etc. defining the indexing set for the basis of this module
- ``element_class`` - the class of which elements of this module should be instances (optional, default None, in which case the elements are instances of :class:`~sage.modules.with_basis.indexed_element.IndexedFreeModuleElement`)
- ``category`` - the category in which this module lies (optional, default None, in which case use the "category of modules with basis" over the base ring ``R``); this should be a subcategory of :class:`ModulesWithBasis`
For the options controlling the printing of elements, see :class:`~sage.structure.indexed_generators.IndexedGenerators`.
.. NOTE::
These print options may also be accessed and modified using the :meth:`print_options` method, after the module has been defined.
EXAMPLES:
We construct a free module whose basis is indexed by the letters a, b, c::
sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) sage: F Free module generated by {'a', 'b', 'c'} over Rational Field
Its basis is a family, indexed by a, b, c::
sage: e = F.basis() sage: e Finite family {'a': B['a'], 'c': B['c'], 'b': B['b']}
::
sage: [x for x in e] [B['a'], B['b'], B['c']] sage: [k for k in e.keys()] ['a', 'b', 'c']
Let us construct some elements, and compute with them::
sage: e['a'] B['a'] sage: 2*e['a'] 2*B['a'] sage: e['a'] + 3*e['b'] B['a'] + 3*B['b']
Some uses of :meth:`sage.categories.commutative_additive_semigroups.CommutativeAdditiveSemigroups.ParentMethods.summation` and :meth:`.sum`::
sage: F = CombinatorialFreeModule(QQ, [1,2,3,4]) sage: F.summation(F.monomial(1), F.monomial(3)) B[1] + B[3]
sage: F = CombinatorialFreeModule(QQ, [1,2,3,4]) sage: F.sum(F.monomial(i) for i in [1,2,3]) B[1] + B[2] + B[3]
Note that free modules with a given basis and parameters are unique::
sage: F1 = CombinatorialFreeModule(QQ, (1,2,3,4)) sage: F1 is F True
The identity of the constructed free module depends on the order of the basis and on the other parameters, like the prefix. Note that :class:`CombinatorialFreeModule` is a :class:`~sage.structure.unique_representation.UniqueRepresentation`. Hence, two combinatorial free modules evaluate equal if and only if they are identical::
sage: F1 = CombinatorialFreeModule(QQ, (1,2,3,4)) sage: F1 is F True sage: F1 = CombinatorialFreeModule(QQ, [4,3,2,1]) sage: F1 == F False sage: F2 = CombinatorialFreeModule(QQ, [1,2,3,4], prefix='F') sage: F2 == F False
Because of this, if you create a free module with certain parameters and then modify its prefix or other print options, this affects all modules which were defined using the same parameters. ::
sage: F2.print_options(prefix='x') sage: F2.prefix() 'x' sage: F3 = CombinatorialFreeModule(QQ, [1,2,3,4], prefix='F') sage: F3 is F2 # F3 was defined just like F2 True sage: F3.prefix() 'x' sage: F4 = CombinatorialFreeModule(QQ, [1,2,3,4], prefix='F', bracket=True) sage: F4 == F2 # F4 was NOT defined just like F2 False sage: F4.prefix() 'F'
sage: F2.print_options(prefix='F') #reset for following doctests
The constructed module is in the category of modules with basis over the base ring::
sage: CombinatorialFreeModule(QQ, Partitions()).category() Category of vector spaces with basis over Rational Field
If furthermore the index set is finite (i.e. in the category ``Sets().Finite()``), then the module is declared as being finite dimensional::
sage: CombinatorialFreeModule(QQ, [1,2,3,4]).category() Category of finite dimensional vector spaces with basis over Rational Field sage: CombinatorialFreeModule(QQ, Partitions(3), ....: category=Algebras(QQ).WithBasis()).category() Category of finite dimensional algebras with basis over Rational Field
See :mod:`sage.categories.examples.algebras_with_basis` and :mod:`sage.categories.examples.hopf_algebras_with_basis` for illustrations of the use of the ``category`` keyword, and see :class:`sage.combinat.root_system.weight_space.WeightSpace` for an example of the use of ``element_class``.
Customizing print and LaTeX representations of elements::
sage: F = CombinatorialFreeModule(QQ, ['a','b'], prefix='x') sage: original_print_options = F.print_options() sage: sorted(original_print_options.items()) [('bracket', None), ('latex_bracket', False), ('latex_prefix', None), ('latex_scalar_mult', None), ('prefix', 'x'), ('scalar_mult', '*'), ('sorting_key', <function <lambda> at ...>), ('sorting_reverse', False), ('string_quotes', True), ('tensor_symbol', None)]
sage: e = F.basis() sage: e['a'] - 3 * e['b'] x['a'] - 3*x['b']
sage: F.print_options(prefix='x', scalar_mult=' ', bracket='{') sage: e['a'] - 3 * e['b'] x{'a'} - 3 x{'b'} sage: latex(e['a'] - 3 * e['b']) x_{a} - 3 x_{b}
sage: F.print_options(latex_prefix='y') sage: latex(e['a'] - 3 * e['b']) y_{a} - 3 y_{b}
sage: F.print_options(sorting_reverse=True) sage: e['a'] - 3 * e['b'] -3 x{'b'} + x{'a'} sage: F.print_options(**original_print_options) # reset print options
sage: F = CombinatorialFreeModule(QQ, [(1,2), (3,4)]) sage: e = F.basis() sage: e[(1,2)] - 3 * e[(3,4)] B[(1, 2)] - 3*B[(3, 4)]
sage: F.print_options(bracket=['_{', '}']) sage: e[(1,2)] - 3 * e[(3,4)] B_{(1, 2)} - 3*B_{(3, 4)}
sage: F.print_options(prefix='', bracket=False) sage: e[(1,2)] - 3 * e[(3,4)] (1, 2) - 3*(3, 4)
TESTS:
Before :trac:`14054`, combinatorial free modules violated the unique parent condition. That caused a problem. The tensor product construction involves maps, but maps check that their domain and the parent of a to-be-mapped element are identical (not just equal). However, the tensor product was cached by a :class:`~sage.misc.cachefunc.cached_method`, which involves comparison by equality (not identity). Hence, the last line of the following example used to fail with an assertion error::
sage: F = CombinatorialFreeModule(ZZ, [1,2,3], prefix="F") sage: G = CombinatorialFreeModule(ZZ, [1,2,3,4], prefix="G") sage: f = F.monomial(1) + 2 * F.monomial(2) sage: g = 2*G.monomial(3) + G.monomial(4) sage: tensor([f, g]) 2*F[1] # G[3] + F[1] # G[4] + 4*F[2] # G[3] + 2*F[2] # G[4] sage: F = CombinatorialFreeModule(ZZ, [1,2,3], prefix='x') sage: G = CombinatorialFreeModule(ZZ, [1,2,3,4], prefix='y') sage: f = F.monomial(1) + 2 * F.monomial(2) sage: g = 2*G.monomial(3) + G.monomial(4) sage: tensor([f, g]) 2*x[1] # y[3] + x[1] # y[4] + 4*x[2] # y[3] + 2*x[2] # y[4]
We check that we can use the shorthand ``C.<a,b,...> = ...``::
sage: C.<x,y,z> = CombinatorialFreeModule(QQ) sage: C Free module generated by {'x', 'y', 'z'} over Rational Field sage: a = x - y + 4*z; a x - y + 4*z sage: a.parent() is C True
TESTS::
sage: XQ = SchubertPolynomialRing(QQ) sage: XZ = SchubertPolynomialRing(ZZ) sage: XQ == XZ False sage: XQ == XQ True """
@staticmethod def __classcall_private__(cls, base_ring, basis_keys=None, category=None, prefix=None, names=None, **keywords): """ TESTS::
sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) sage: G = CombinatorialFreeModule(QQ, ('a','b','c')) sage: F is G True
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'], latex_bracket=['LEFT', 'RIGHT']) sage: F.print_options()['latex_bracket'] ('LEFT', 'RIGHT')
sage: F is G False
We check that the category is properly straightened::
sage: F = CombinatorialFreeModule(QQ, ['a','b']) sage: F1 = CombinatorialFreeModule(QQ, ['a','b'], category = ModulesWithBasis(QQ)) sage: F2 = CombinatorialFreeModule(QQ, ['a','b'], category = [ModulesWithBasis(QQ)]) sage: F3 = CombinatorialFreeModule(QQ, ['a','b'], category = (ModulesWithBasis(QQ),)) sage: F4 = CombinatorialFreeModule(QQ, ['a','b'], category = (ModulesWithBasis(QQ),CommutativeAdditiveSemigroups())) sage: F5 = CombinatorialFreeModule(QQ, ['a','b'], category = (ModulesWithBasis(QQ),Category.join((LeftModules(QQ), RightModules(QQ))))) sage: F1 is F, F2 is F, F3 is F, F4 is F, F5 is F (True, True, True, True, True)
sage: G = CombinatorialFreeModule(QQ, ['a','b'], category = AlgebrasWithBasis(QQ)) sage: F is G False """ basis_keys = tuple(basis_keys) # bracket or latex_bracket might be lists, so convert # them to tuples so that they're hashable. keywords['bracket'] = tuple(bracket)
base_ring, basis_keys, category=category, prefix=prefix, names=names, **keywords)
# We make this explicitly a Python class so that the methods, # specifically _mul_, from category framework still works. -- TCS # We also need to deal with the old pickles too. -- TCS Element = IndexedFreeModuleElement
@lazy_attribute def element_class(self): """ The (default) class for the elements of this parent
Overrides :meth:`Parent.element_class` to force the construction of Python class. This is currently needed to inherit really all the features from categories, and in particular the initialization of ``_mul_`` in :meth:`Magmas.ParentMethods.__init_extra__`.
EXAMPLES::
sage: A = Algebras(QQ).WithBasis().example(); A An example of an algebra with basis: the free algebra on the generators ('a', 'b', 'c') over Rational Field
sage: A.element_class.mro() [<class 'sage.categories.examples.algebras_with_basis.FreeAlgebra_with_category.element_class'>, <... 'sage.modules.with_basis.indexed_element.IndexedFreeModuleElement'>, ...] sage: a,b,c = A.algebra_generators() sage: a * b B[word: ab]
TESTS::
sage: A.__class__.element_class.__module__ 'sage.combinat.free_module' """ name="%s.element_class"%self.__class__.__name__, module=self.__class__.__module__, inherit=True)
def __init__(self, R, basis_keys=None, element_class=None, category=None, prefix=None, names=None, **kwds): r""" TESTS::
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
sage: F.category() Category of finite dimensional vector spaces with basis over Rational Field
One may specify the category this module belongs to::
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'], category=AlgebrasWithBasis(QQ)) sage: F.category() Category of finite dimensional algebras with basis over Rational Field
sage: F = CombinatorialFreeModule(GF(3), ['a','b','c'], ....: category=(Modules(GF(3)).WithBasis(), Semigroups())) sage: F.category() Join of Category of finite semigroups and Category of finite dimensional modules with basis over Finite Field of size 3 and Category of vector spaces with basis over Finite Field of size 3
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'], category = FiniteDimensionalModulesWithBasis(QQ)) sage: F.basis() Finite family {'a': B['a'], 'c': B['c'], 'b': B['b']} sage: F.category() Category of finite dimensional vector spaces with basis over Rational Field
sage: TestSuite(F).run()
TESTS:
Regression test for :trac:`10127`: ``self._indices`` needs to be set early enough, in case the initialization of the categories use ``self.basis().keys()``. This occured on several occasions in non trivial constructions. In the following example, :class:`AlgebrasWithBasis` constructs ``Homset(self,self)`` to extend by bilinearity method ``product_on_basis``, which in turn triggers ``self._repr_()``::
sage: class MyAlgebra(CombinatorialFreeModule): ....: def _repr_(self): ....: return "MyAlgebra on %s"%(self.basis().keys()) ....: def product_on_basis(self,i,j): ....: pass sage: MyAlgebra(ZZ, ZZ, category = AlgebrasWithBasis(QQ)) MyAlgebra on Integer Ring
A simpler example would be welcome!
We check that unknown options are caught::
sage: CombinatorialFreeModule(ZZ, [1,2,3], keyy=2) Traceback (most recent call last): ... ValueError: keyy is not a valid print option. """ #Make sure R is a ring with unit element raise TypeError("Argument R must be a ring.")
# The following is to ensure that basis keys is indeed a parent. # tuple/list are converted to FiniteEnumeratedSet and set/frozenset to # Set # (e.g. root systems passes lists)
# This is needed for the Cartesian product # TODO: Remove this duplication from __classcall_private__
# ignore the optional 'key' since it only affects CachedRepresentation kwds['sorting_key'] = kwds.pop('monomial_key') kwds['sorting_reverse'] = kwds.pop('monomial_reverse')
# For backwards compatibility _repr_term = IndexedGenerators._repr_generator _latex_term = IndexedGenerators._latex_generator
def _ascii_art_term(self, m): r""" Return an ascii art representation of the term indexed by ``m``.
TESTS::
sage: R = NonCommutativeSymmetricFunctions(QQ).R() sage: ascii_art(R.one()) # indirect doctest 1 """
def _unicode_art_term(self, m): r""" Return an unicode art representation of the term indexed by ``m``.
TESTS::
sage: R = NonCommutativeSymmetricFunctions(QQ).R() sage: unicode_art(R.one()) # indirect doctest 1 """ except Exception: pass
# mostly for backward compatibility @lazy_attribute def _element_class(self): """ TESTS::
sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) sage: F._element_class <class 'sage.combinat.free_module.CombinatorialFreeModule_with_category.element_class'> """
def _an_element_(self): """ EXAMPLES::
sage: CombinatorialFreeModule(QQ, ("a", "b", "c")).an_element() 2*B['a'] + 2*B['b'] + 3*B['c'] sage: CombinatorialFreeModule(QQ, ("a", "b", "c"))._an_element_() 2*B['a'] + 2*B['b'] + 3*B['c'] sage: CombinatorialFreeModule(QQ, ()).an_element() 0 sage: CombinatorialFreeModule(QQ, ZZ).an_element() 3*B[-1] + B[0] + 3*B[1] sage: CombinatorialFreeModule(QQ, RR).an_element() B[1.00000000000000] """ # Try a couple heuristics to build a not completely trivial # element, while handling cases where R.an_element is not # implemented, or R has no iterator, or R has few elements.
# What semantic do we want for containment? # Accepting anything that can be coerced is not reasonable, especially # if we allow coercion from the enumerated set. # Accepting anything that can be converted is an option, but that would # be expensive. So far, x in self if x.parent() == self
def __contains__(self, x): """ TESTS::
sage: F = CombinatorialFreeModule(QQ,["a", "b"]) sage: G = CombinatorialFreeModule(ZZ,["a", "b"]) sage: F.monomial("a") in F True sage: G.monomial("a") in F False sage: "a" in F False sage: 5/3 in F False """
def _element_constructor_(self, x): """ Convert ``x`` into ``self``.
EXAMPLES::
sage: F = CombinatorialFreeModule(QQ,["a", "b"]) sage: F(F.monomial("a")) # indirect doctest B['a']
Do not rely on the following feature which may be removed in the future::
sage: QS3 = SymmetricGroupAlgebra(QQ,3) sage: QS3([2,3,1]) # indirect doctest [2, 3, 1]
instead, use::
sage: P = QS3.basis().keys() sage: QS3.monomial(P([2,3,1])) # indirect doctest [2, 3, 1]
or: sage: B = QS3.basis() sage: B[P([2,3,1])] [2, 3, 1]
TODO: The symmetric group algebra (and in general, combinatorial free modules on word-like object could instead provide an appropriate short-hand syntax QS3[2,3,1]).
Rationale: this conversion is ambiguous in situations like::
sage: F = CombinatorialFreeModule(QQ,[0,1])
Is ``0`` the zero of the base ring, or the index of a basis element? I.e. should the result be ``0`` or ``B[0]``?
sage: F = CombinatorialFreeModule(QQ,[0,1]) sage: F(0) # this feature may eventually disappear 0 sage: F(1) Traceback (most recent call last): ... TypeError: do not know how to make x (= 1) an element of Free module generated by ... over Rational Field
It is preferable not to rely either on the above, and instead, use::
sage: F.zero() 0
Note that, on the other hand, conversions from the ground ring are systematically defined (and mathematically meaningful) for algebras.
Conversions between distinct free modules are not allowed any more::
sage: F = CombinatorialFreeModule(ZZ, ["a", "b"]); F.rename("F") sage: G = CombinatorialFreeModule(QQ, ["a", "b"]); G.rename("G") sage: H = CombinatorialFreeModule(ZZ, ["a", "b", "c"]); H.rename("H") sage: G(F.monomial("a")) Traceback (most recent call last): ... TypeError: do not know how to make x (= B['a']) an element of self (=G) sage: H(F.monomial("a")) Traceback (most recent call last): ... TypeError: do not know how to make x (= B['a']) an element of self (=H)
Here is a real life example illustrating that this yielded mathematically wrong results::
sage: S = SymmetricFunctions(QQ) sage: s = S.s(); p = S.p() sage: ss = tensor([s,s]); pp = tensor([p,p]) sage: a = tensor((s[2],s[2]))
The following originally used to yield ``p[[2]] # p[[2]]``, and if there was no natural coercion between ``s`` and ``p``, this would raise a ``NotImplementedError``. Since :trac:`15305`, this takes the coercion between ``s`` and ``p`` and lifts it to the tensor product. ::
sage: pp(a) 1/4*p[1, 1] # p[1, 1] + 1/4*p[1, 1] # p[2] + 1/4*p[2] # p[1, 1] + 1/4*p[2] # p[2]
Extensions of the ground ring should probably be reintroduced at some point, but via coercions, and with stronger sanity checks (ensuring that the codomain is really obtained by extending the scalar of the domain; checking that they share the same class is not sufficient).
TESTS:
Conversion from the ground ring is implemented for algebras::
sage: QS3 = SymmetricGroupAlgebra(QQ,3) sage: QS3(2) 2*[1, 2, 3] """
#Coerce ints to Integers
else: #x is an element of the basis enumerated set; # This is a very ugly way of testing this isinstance(self._indices.element_class, type) and isinstance(x, self._indices.element_class)) or (parent(x) == self._indices)): else: try: return self._coerce_end(x) except TypeError: pass
def _convert_map_from_(self, S): """ Implement the conversion from formal sums.
EXAMPLES::
sage: E = CombinatorialFreeModule(QQ, ["a", "b", "c"]) sage: f = FormalSum([[2, "a"], [3, "b"]]); f 2*a + 3*b sage: E._convert_map_from_(f.parent()) Generic morphism: From: Abelian Group of all Formal Finite Sums over Integer Ring To: Free module generated by {'a', 'b', 'c'} over Rational Field sage: E(f) 2*B['a'] + 3*B['b']
sage: E._convert_map_from_(ZZ) """ lambda x: self.sum_of_terms( (G(g), K(c)) for c,g in x ))
def _an_element_impl(self): # TODO: REMOVE? """ Return an element of ``self``, namely the zero element.
EXAMPLES::
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c']) sage: F._an_element_impl() 0 sage: _.parent() is F True """
def _first_ngens(self, n): """ Used by the preparser for ``F.<x> = ...``.
EXAMPLES::
sage: C = CombinatorialFreeModule(QQ, ZZ) sage: C._first_ngens(3) (B[0], B[1], B[-1])
sage: R.<x,y> = FreeAlgebra(QQ, 2) sage: x,y (x, y) """ # Try gens first for compatibility with classes that # rely on this (e.g., FreeAlgebra)
def dimension(self): """ Return the dimension of the free module (which is given by the number of elements in the basis).
EXAMPLES::
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c']) sage: F.dimension() 3 sage: F.basis().cardinality() 3 sage: F.basis().keys().cardinality() 3
Rank is available as a synonym::
sage: F.rank() 3
::
sage: s = SymmetricFunctions(QQ).schur() sage: s.dimension() +Infinity """
rank = dimension
def is_exact(self): r""" Return ``True`` if elements of ``self`` have exact representations, which is true of ``self`` if and only if it is true of ``self.basis().keys()`` and ``self.base_ring()``.
EXAMPLES::
sage: GroupAlgebra(GL(3, GF(7))).is_exact() True sage: GroupAlgebra(GL(3, GF(7)), RR).is_exact() False sage: GroupAlgebra(GL(3, pAdicRing(7))).is_exact() # not implemented correctly (not my fault)! False """ # The index set may not have a check for exactness # Assume the index set is exact if we cannot check return False except AttributeError: pass
def set_order(self, order): """ Set the order of the elements of the basis.
If :meth:`set_order` has not been called, then the ordering is the one used in the generation of the elements of self's associated enumerated set.
.. WARNING::
Many cached methods depend on this order, in particular for constructing subspaces and quotients. Changing the order after some computations have been cached does not invalidate the cache, and is likely to introduce inconsistencies.
EXAMPLES::
sage: QS2 = SymmetricGroupAlgebra(QQ,2) sage: b = list(QS2.basis().keys()) sage: b.reverse() sage: QS2.set_order(b) sage: QS2.get_order() [[2, 1], [1, 2]] """
@cached_method def get_order(self): """ Return the order of the elements in the basis.
EXAMPLES::
sage: QS2 = SymmetricGroupAlgebra(QQ,2) sage: QS2.get_order() # note: order changed on 2009-03-13 [[2, 1], [1, 2]] """
def get_order_cmp(self): """ Return a comparison function on the basis indices that is compatible with the current term order.
DEPRECATED by :trac:`24548`.
EXAMPLES::
sage: A = FiniteDimensionalAlgebrasWithBasis(QQ).example() sage: Acmp = A.get_order_cmp() doctest:warning...: DeprecationWarning: comparison should use keys See http://trac.sagemath.org/24548 for details.
sage: sorted(A.basis().keys(), Acmp) ['x', 'y', 'a', 'b'] sage: A.set_order(list(reversed(A.basis().keys()))) sage: Acmp = A.get_order_cmp() sage: sorted(A.basis().keys(), Acmp) ['b', 'a', 'y', 'x'] """
def _order_cmp(self, x, y): """ Compare `x` and `y` w.r.t. the term order.
DEPRECATED by :trac:`24548`.
INPUT:
- ``x``, ``y`` -- indices of the basis of ``self``
OUTPUT:
`-1`, `0`, or `1` depending on whether `x<y`, `x==y`, or `x>y` w.r.t. the term order.
EXAMPLES::
sage: A = CombinatorialFreeModule(QQ, ['x','y','a','b']) sage: A.set_order(['x', 'y', 'a', 'b']) sage: A._order_cmp('x', 'y') doctest:warning...: DeprecationWarning: comparison should use keys See http://trac.sagemath.org/24548 for details. -1 sage: A._order_cmp('y', 'y') 0 sage: A._order_cmp('a', 'y') 1 """ else:
def get_order_key(self): """ Return a comparison key on the basis indices that is compatible with the current term order.
EXAMPLES::
sage: A = FiniteDimensionalAlgebrasWithBasis(QQ).example() sage: A.set_order(['x', 'y', 'a', 'b']) sage: Akey = A.get_order_key() sage: sorted(A.basis().keys(), key=Akey) ['x', 'y', 'a', 'b'] sage: A.set_order(list(reversed(A.basis().keys()))) sage: Akey = A.get_order_key() sage: sorted(A.basis().keys(), key=Akey) ['b', 'a', 'y', 'x'] """
def _order_key(self, x): """ Return a key for `x` compatible with the term order.
INPUT:
- ``x`` -- indices of the basis of ``self``
EXAMPLES::
sage: A = CombinatorialFreeModule(QQ, ['x','y','a','b']) sage: A.set_order(['x', 'y', 'a', 'b']) sage: A._order_key('x') 0 sage: A._order_key('y') 1 sage: A._order_key('a') 2 """
def from_vector(self, vector): """ Build an element of ``self`` from a (sparse) vector.
.. SEEALSO:: :meth:`get_order`, :meth:`CombinatorialFreeModule.Element._vector_`
EXAMPLES::
sage: QS3 = SymmetricGroupAlgebra(QQ, 3) sage: b = QS3.from_vector(vector((2, 0, 0, 0, 0, 4))); b 2*[1, 2, 3] + 4*[3, 2, 1] sage: a = 2*QS3([1,2,3])+4*QS3([3,2,1]) sage: a == b True """
def sum(self, iter_of_elements): """ Return the sum of all elements in ``iter_of_elements``.
Overrides method inherited from commutative additive monoid as it is much faster on dicts directly.
INPUT:
- ``iter_of_elements`` -- iterator of elements of ``self``
EXAMPLES::
sage: F = CombinatorialFreeModule(QQ,[1,2]) sage: f = F.an_element(); f 2*B[1] + 2*B[2] sage: F.sum( f for _ in range(5) ) 10*B[1] + 10*B[2] """
def linear_combination(self, iter_of_elements_coeff, factor_on_left=True): """ Return the linear combination `\lambda_1 v_1 + \cdots + \lambda_k v_k` (resp. the linear combination `v_1 \lambda_1 + \cdots + v_k \lambda_k`) where ``iter_of_elements_coeff`` iterates through the sequence `((\lambda_1, v_1), ..., (\lambda_k, v_k))`.
INPUT:
- ``iter_of_elements_coeff`` -- iterator of pairs ``(element, coeff)`` with ``element`` in ``self`` and ``coeff`` in ``self.base_ring()``
- ``factor_on_left`` -- (optional) if ``True``, the coefficients are multiplied from the left if ``False``, the coefficients are multiplied from the right
EXAMPLES::
sage: F = CombinatorialFreeModule(QQ,[1,2]) sage: f = F.an_element(); f 2*B[1] + 2*B[2] sage: F.linear_combination( (f,i) for i in range(5) ) 20*B[1] + 20*B[2] """ for element, coeff in iter_of_elements_coeff), factor_on_left=factor_on_left ), remove_zeros=False)
def term(self, index, coeff=None): """ Construct a term in ``self``.
INPUT:
- ``index`` -- the index of a basis element - ``coeff`` -- an element of the coefficient ring (default: one)
EXAMPLES::
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c']) sage: F.term('a',3) 3*B['a'] sage: F.term('a') B['a']
Design: should this do coercion on the coefficient ring? """
def _monomial(self, index): """ TESTS::
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c']) sage: F._monomial('a') B['a'] """
@lazy_attribute def monomial(self): """ Return the basis element indexed by ``i``.
INPUT:
- ``i`` -- an element of the index set
EXAMPLES::
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c']) sage: F.monomial('a') B['a']
``F.monomial`` is in fact (almost) a map::
sage: F.monomial Term map from {'a', 'b', 'c'} to Free module generated by {'a', 'b', 'c'} over Rational Field """ # Should use a real Map, as soon as combinatorial_classes are enumerated sets, and therefore parents
def sum_of_terms(self, terms, distinct=False): """ Construct a sum of terms of ``self``.
INPUT:
- ``terms`` -- a list (or iterable) of pairs ``(index, coeff)`` - ``distinct`` -- (default: ``False``) whether the indices are guaranteed to be distinct
EXAMPLES::
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c']) sage: F.sum_of_terms([('a',2), ('c',3)]) 2*B['a'] + 3*B['c']
If ``distinct`` is True, then the construction is optimized::
sage: F.sum_of_terms([('a',2), ('c',3)], distinct = True) 2*B['a'] + 3*B['c']
.. WARNING::
Use ``distinct=True`` only if you are sure that the indices are indeed distinct::
sage: F.sum_of_terms([('a',2), ('a',3)], distinct = True) 3*B['a']
Extreme case::
sage: F.sum_of_terms([]) 0 """
@cached_method def zero(self): """ EXAMPLES::
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c']) sage: F.zero() 0 """
def _from_dict(self, d, coerce=False, remove_zeros=True): r""" Construct an element of ``self`` from an ``{index: coefficient}`` dictionary.
INPUT:
- ``d`` -- a dictionary ``{index: coeff}`` where each ``index`` is the index of a basis element and each ``coeff`` belongs to the coefficient ring ``self.base_ring()``
- ``coerce`` -- a boolean (default: ``False``), whether to coerce the coefficients ``coeff`` to the coefficient ring
- ``remove_zeros`` -- a boolean (default: ``True``), if some coefficients ``coeff`` may be zero and should therefore be removed
EXAMPLES::
sage: e = SymmetricFunctions(QQ).elementary() sage: s = SymmetricFunctions(QQ).schur() sage: a = e([2,1]) + e([1,1,1]); a e[1, 1, 1] + e[2, 1] sage: s._from_dict(a.monomial_coefficients()) s[1, 1, 1] + s[2, 1]
If the optional argument ``coerce`` is ``True``, then the coefficients are coerced into the base ring of ``self``::
sage: part = Partition([2,1]) sage: d = {part:1} sage: a = s._from_dict(d,coerce=True); a s[2, 1] sage: a.coefficient(part).parent() Rational Field
With ``remove_zeros=True``, zero coefficients are removed::
sage: s._from_dict({part:0}) 0
.. WARNING::
With ``remove_zeros=True``, it is assumed that no coefficient of the dictionary is zero. Otherwise, this may lead to illegal results::
sage: list(s._from_dict({part:0}, remove_zeros=False)) [([2, 1], 0)] """
class CombinatorialFreeModuleElement(CombinatorialFreeModule.Element): """ Deprecated. Use :class:`sage.modules.with_basis.indexed_element.IndexedFreeModuleElement` or :class:`CombinatorialFreeModule.Element` instead. """ def __init__(self, *args, **kwds): """ TESTS::
sage: from sage.combinat.free_module import CombinatorialFreeModuleElement sage: class Test(CombinatorialFreeModule): ....: class Element(CombinatorialFreeModuleElement): ....: pass sage: T = Test(QQ, (1,2)) sage: T.an_element() doctest:warning ... DeprecationWarning: CombinatorialFreeModuleElement is deprecated. Use IndexedFreeModuleElement or CombinatorialFreeModule.Element instead. See http://trac.sagemath.org/22632 for details. 2*B[1] + 2*B[2] """ " Use IndexedFreeModuleElement" " or CombinatorialFreeModule.Element instead.")
class CombinatorialFreeModule_Tensor(CombinatorialFreeModule): """ Tensor Product of Free Modules
EXAMPLES:
We construct two free modules, assign them short names, and construct their tensor product::
sage: F = CombinatorialFreeModule(ZZ, [1,2]); F.__custom_name = "F" sage: G = CombinatorialFreeModule(ZZ, [3,4]); G.__custom_name = "G" sage: T = tensor([F, G]); T F # G
sage: T.category() Category of finite dimensional tensor products of modules with basis over Integer Ring
sage: T.construction() # todo: not implemented [tensor, ]
T is a free module, with same base ring as F and G::
sage: T.base_ring() Integer Ring
The basis of T is indexed by tuples of basis indices of F and G::
sage: T.basis().keys() Image of Cartesian product of {1, 2}, {3, 4} by <... 'tuple'> sage: T.basis().keys().list() [(1, 3), (1, 4), (2, 3), (2, 4)]
FIXME: Should elements of a CartesianProduct be tuples (making them hashable)?
Here are the basis elements themselves::
sage: T.basis().cardinality() 4 sage: list(T.basis()) [B[1] # B[3], B[1] # B[4], B[2] # B[3], B[2] # B[4]]
The tensor product is associative and flattens sub tensor products::
sage: H = CombinatorialFreeModule(ZZ, [5,6]); H.rename("H") sage: tensor([F, tensor([G, H])]) F # G # H sage: tensor([tensor([F, G]), H]) F # G # H sage: tensor([F, G, H]) F # G # H
We now compute the tensor product of elements of free modules::
sage: f = F.monomial(1) + 2 * F.monomial(2) sage: g = 2*G.monomial(3) + G.monomial(4) sage: h = H.monomial(5) + H.monomial(6) sage: tensor([f, g]) 2*B[1] # B[3] + B[1] # B[4] + 4*B[2] # B[3] + 2*B[2] # B[4]
Again, the tensor product is associative on elements::
sage: tensor([f, tensor([g, h])]) == tensor([f, g, h]) True sage: tensor([tensor([f, g]), h]) == tensor([f, g, h]) True
Note further that the tensor product spaces need not preexist::
sage: t = tensor([f, g, h]) sage: t.parent() F # G # H
TESTS::
sage: tensor([tensor([F, G]), H]) == tensor([F, G, H]) True sage: tensor([F, tensor([G, H])]) == tensor([F, G, H]) True """ @staticmethod def __classcall_private__(cls, modules, **options): """ TESTS::
sage: F = CombinatorialFreeModule(ZZ, [1,2]) sage: G = CombinatorialFreeModule(ZZ, [3,4]) sage: H = CombinatorialFreeModule(ZZ, [4]) sage: tensor([tensor([F, G]), H]) == tensor([F, G, H]) True sage: tensor([F, tensor([G, H])]) == tensor([F, G, H]) True
Check that :trac:`19608` is fixed::
sage: T = tensor([F, G, H]) sage: T in Modules(ZZ).FiniteDimensional() True """ # should check the base ring # flatten the list of modules so that tensor(A, tensor(B,C)) gets rewritten into tensor(A, B, C)
def __init__(self, modules, **options): """ TESTS::
sage: F = CombinatorialFreeModule(ZZ, [1,2]); F F """ for module in modules]).map(tuple) # the following is not the best option, but it's better than nothing.
def _repr_(self): """ This is customizable by setting ``self.print_options('tensor_symbol'=...)``.
TESTS::
sage: F = CombinatorialFreeModule(ZZ, [1,2,3]) sage: G = CombinatorialFreeModule(ZZ, [1,2,3,8]) sage: F.rename("F") sage: G.rename("G") sage: T = tensor([F, G]) sage: T # indirect doctest F # G sage: T.print_options(tensor_symbol= ' @ ') # note the spaces sage: T # indirect doctest F @ G
To avoid a side\--effect on another doctest, we revert the change::
sage: T.print_options(tensor_symbol= ' # ') """ symb = tensor.symbol else: symb = tensor.symbol # TODO: make this overridable by setting _name
def _ascii_art_(self, term): """ TESTS::
sage: R = NonCommutativeSymmetricFunctions(QQ).R() sage: Partitions.options(diagram_str="#", convention="french") sage: ascii_art(tensor((R[1,2], R[3,1,2]))) R # R # ### ## # ## """ symb = tensor.symbol else: symb = tensor.symbol
_ascii_art_term = _ascii_art_
def _unicode_art_(self, term): """ TESTS::
sage: R = NonCommutativeSymmetricFunctions(QQ).R() sage: Partitions.options(diagram_str="#", convention="french") sage: unicode_art(tensor((R[1,2], R[3,1,2]))) R # R ┌┐ ┌┬┬┐ ├┼┐ └┴┼┤ └┴┘ ├┼┐ └┴┘ """ symb = tensor.symbol else: symb = tensor.symbol
_unicode_art_term = _unicode_art_
def _latex_(self): """ TESTS::
sage: F = CombinatorialFreeModule(ZZ, [1,2,3]) sage: G = CombinatorialFreeModule(ZZ, [1,2,3,8]) sage: F.rename("F") sage: G.rename("G") sage: latex(tensor([F, F, G])) # indirect doctest \text{\texttt{F}} \otimes \text{\texttt{F}} \otimes \text{\texttt{G}} sage: F._latex_ = lambda : "F" sage: G._latex_ = lambda : "G" sage: latex(tensor([F, F, G])) # indirect doctest F \otimes F \otimes G """
def _repr_term(self, term): """ TESTS::
sage: F = CombinatorialFreeModule(ZZ, [1,2,3], prefix="F") sage: G = CombinatorialFreeModule(ZZ, [1,2,3,4], prefix="G") sage: f = F.monomial(1) + 2 * F.monomial(2) sage: g = 2*G.monomial(3) + G.monomial(4) sage: tensor([f, g]) # indirect doctest 2*F[1] # G[3] + F[1] # G[4] + 4*F[2] # G[3] + 2*F[2] # G[4] """ symb = tensor.symbol else: symb = tensor.symbol
def _latex_term(self, term): """ TESTS::
sage: F = CombinatorialFreeModule(ZZ, [1,2,3], prefix='x') sage: G = CombinatorialFreeModule(ZZ, [1,2,3,4], prefix='y') sage: f = F.monomial(1) + 2 * F.monomial(2) sage: g = 2*G.monomial(3) + G.monomial(4) sage: latex(tensor([f, g])) # indirect doctest 2x_{1} \otimes y_{3} + x_{1} \otimes y_{4} + 4x_{2} \otimes y_{3} + 2x_{2} \otimes y_{4} """
@cached_method def tensor_constructor(self, modules): r""" INPUT:
- ``modules`` -- a tuple `(F_1,\dots,F_n)` of free modules whose tensor product is self
Returns the canonical multilinear morphism from `F_1 \times \dots \times F_n` to `F_1 \otimes \dots \otimes F_n`
EXAMPLES::
sage: F = CombinatorialFreeModule(ZZ, [1,2]); F.__custom_name = "F" sage: G = CombinatorialFreeModule(ZZ, [3,4]); G.__custom_name = "G" sage: H = CombinatorialFreeModule(ZZ, [5,6]); H.rename("H")
sage: f = F.monomial(1) + 2 * F.monomial(2) sage: g = 2*G.monomial(3) + G.monomial(4) sage: h = H.monomial(5) + H.monomial(6) sage: FG = tensor([F, G ]) sage: phi_fg = FG.tensor_constructor((F, G)) sage: phi_fg(f,g) 2*B[1] # B[3] + B[1] # B[4] + 4*B[2] # B[3] + 2*B[2] # B[4]
sage: FGH = tensor([F, G, H]) sage: phi_fgh = FGH.tensor_constructor((F, G, H)) sage: phi_fgh(f, g, h) 2*B[1] # B[3] # B[5] + 2*B[1] # B[3] # B[6] + B[1] # B[4] # B[5] + B[1] # B[4] # B[6] + 4*B[2] # B[3] # B[5] + 4*B[2] # B[3] # B[6] + 2*B[2] # B[4] # B[5] + 2*B[2] # B[4] # B[6]
sage: phi_fg_h = FGH.tensor_constructor((FG, H)) sage: phi_fg_h(phi_fg(f, g), h) 2*B[1] # B[3] # B[5] + 2*B[1] # B[3] # B[6] + B[1] # B[4] # B[5] + B[1] # B[4] # B[6] + 4*B[2] # B[3] # B[5] + 4*B[2] # B[3] # B[6] + 2*B[2] # B[4] # B[5] + 2*B[2] # B[4] # B[6] """ # a list l such that l[i] is True if modules[i] is readily a tensor product # the tensor_constructor, on basis elements # TODO: make this into an element of Hom( A x B, C ) when those will exist
def _tensor_of_elements(self, elements): """ Returns the tensor product of the specified elements. The result should be in ``self``.
EXAMPLES::
sage: F = CombinatorialFreeModule(ZZ, [1,2]); F.__custom_name = "F" sage: G = CombinatorialFreeModule(ZZ, [3,4]); G.__custom_name = "G" sage: H = CombinatorialFreeModule(ZZ, [5,6]); H.rename("H")
sage: f = F.monomial(1) + 2 * F.monomial(2) sage: g = 2*G.monomial(3) + G.monomial(4) sage: h = H.monomial(5) + H.monomial(6)
sage: GH = tensor([G, H]) sage: gh = GH._tensor_of_elements([g, h]); gh 2*B[3] # B[5] + 2*B[3] # B[6] + B[4] # B[5] + B[4] # B[6]
sage: FGH = tensor([F, G, H]) sage: FGH._tensor_of_elements([f, g, h]) 2*B[1] # B[3] # B[5] + 2*B[1] # B[3] # B[6] + B[1] # B[4] # B[5] + B[1] # B[4] # B[6] + 4*B[2] # B[3] # B[5] + 4*B[2] # B[3] # B[6] + 2*B[2] # B[4] # B[5] + 2*B[2] # B[4] # B[6]
sage: FGH._tensor_of_elements([f, gh]) 2*B[1] # B[3] # B[5] + 2*B[1] # B[3] # B[6] + B[1] # B[4] # B[5] + B[1] # B[4] # B[6] + 4*B[2] # B[3] # B[5] + 4*B[2] # B[3] # B[6] + 2*B[2] # B[4] # B[5] + 2*B[2] # B[4] # B[6] """
def _coerce_map_from_(self, R): """ Return ``True`` if there is a coercion from ``R`` into ``self`` and ``False`` otherwise. The things that coerce into ``self`` are:
- Anything with a coercion into ``self.base_ring()``.
- A tensor algebra whose factors have a coercion into the corresponding factors of ``self``.
TESTS::
sage: C = CombinatorialFreeModule(ZZ, ZZ) sage: C2 = CombinatorialFreeModule(ZZ, NN) sage: M = C.module_morphism(lambda x: C2.monomial(abs(x)), codomain=C2) sage: M.register_as_coercion() sage: C2(C.basis()[3]) B[3] sage: C2(C.basis()[3] + C.basis()[-3]) 2*B[3] sage: S = C.tensor(C) sage: S2 = C2.tensor(C2) sage: S2.has_coerce_map_from(S) True sage: S.has_coerce_map_from(S2) False sage: S.an_element() 3*B[0] # B[-1] + 2*B[0] # B[0] + 2*B[0] # B[1] sage: S2(S.an_element()) 2*B[0] # B[0] + 5*B[0] # B[1]
::
sage: C = CombinatorialFreeModule(ZZ, Set([1,2])) sage: D = CombinatorialFreeModule(ZZ, Set([2,4])) sage: f = C.module_morphism(on_basis=lambda x: D.monomial(2*x), codomain=D) sage: f.register_as_coercion() sage: T = tensor((C,C)) sage: p = D.an_element() sage: T(tensor((p,p))) Traceback (most recent call last): ... NotImplementedError sage: T = tensor((D,D)) sage: p = C.an_element() sage: T(tensor((p,p))) 4*B[2] # B[2] + 4*B[2] # B[4] + 4*B[4] # B[2] + 4*B[4] # B[4] """ and isinstance(R, CombinatorialFreeModule_Tensor) \ and len(R._sets) == len(self._sets) \ and all(self._sets[i].has_coerce_map_from(M) for i,M in enumerate(R._sets)): for i,M in enumerate(modules)] [vector_map[i](M.monomial(x[i])) for i,M in enumerate(modules)]), codomain=self)
class CartesianProductWithFlattening(object): """ A class for Cartesian product constructor, with partial flattening """ def __init__(self, flatten): """ INPUT:
- ``flatten`` -- a tuple of booleans
This constructs a callable which accepts ``len(flatten)`` arguments, and builds a tuple out them. When ``flatten[i]``, the i-th argument itself should be a tuple which is flattened in the result.
sage: from sage.combinat.free_module import CartesianProductWithFlattening sage: CartesianProductWithFlattening([True, False, True, True]) <sage.combinat.free_module.CartesianProductWithFlattening object at ...>
"""
def __call__(self, *indices): """ EXAMPLES::
sage: from sage.combinat.free_module import CartesianProductWithFlattening sage: cp = CartesianProductWithFlattening([True, False, True, True]) sage: cp((1,2), (3,4), (5,6), (7,8)) (1, 2, (3, 4), 5, 6, 7, 8) sage: cp((1,2,3), 4, (5,6), (7,8)) (1, 2, 3, 4, 5, 6, 7, 8)
"""
# TODO: find a way to avoid this hack to allow for cross references CombinatorialFreeModule.Tensor = CombinatorialFreeModule_Tensor
class CombinatorialFreeModule_CartesianProduct(CombinatorialFreeModule): """ An implementation of Cartesian products of modules with basis
EXAMPLES:
We construct two free modules, assign them short names, and construct their Cartesian product::
sage: F = CombinatorialFreeModule(ZZ, [4,5]); F.__custom_name = "F" sage: G = CombinatorialFreeModule(ZZ, [4,6]); G.__custom_name = "G" sage: H = CombinatorialFreeModule(ZZ, [4,7]); H.__custom_name = "H" sage: S = cartesian_product([F, G]) sage: S F (+) G sage: S.basis() Lazy family (Term map from Disjoint union of Family ({4, 5}, {4, 6}) to F (+) G(i))_{i in Disjoint union of Family ({4, 5}, {4, 6})}
Note that the indices of the basis elements of F and G intersect non trivially. This is handled by forcing the union to be disjoint::
sage: list(S.basis()) [B[(0, 4)], B[(0, 5)], B[(1, 4)], B[(1, 6)]]
We now compute the Cartesian product of elements of free modules::
sage: f = F.monomial(4) + 2 * F.monomial(5) sage: g = 2*G.monomial(4) + G.monomial(6) sage: h = H.monomial(4) + H.monomial(7) sage: cartesian_product([f,g]) B[(0, 4)] + 2*B[(0, 5)] + 2*B[(1, 4)] + B[(1, 6)] sage: cartesian_product([f,g,h]) B[(0, 4)] + 2*B[(0, 5)] + 2*B[(1, 4)] + B[(1, 6)] + B[(2, 4)] + B[(2, 7)] sage: cartesian_product([f,g,h]).parent() F (+) G (+) H
TODO: choose an appropriate semantic for Cartesian products of Cartesian products (associativity?)::
sage: S = cartesian_product([cartesian_product([F, G]), H]) # todo: not implemented F (+) G (+) H """
def __init__(self, modules, **options): r""" TESTS::
sage: F = CombinatorialFreeModule(ZZ, [2,4,5]) sage: G = CombinatorialFreeModule(ZZ, [2,4,7]) sage: C = cartesian_product([F, G]) ; C Free module generated by {2, 4, 5} over Integer Ring (+) Free module generated by {2, 4, 7} over Integer Ring sage: TestSuite(C).run() """ # should check the base ring DisjointUnionEnumeratedSets( [module.basis().keys() for module in modules], keepkey=True), **options)
def _sets_keys(self): """ In waiting for self._sets.keys()
TESTS::
sage: F = CombinatorialFreeModule(ZZ, [2,4,5]) sage: G = CombinatorialFreeModule(ZZ, [2,4,7]) sage: CP = cartesian_product([F, G]) sage: CP._sets_keys() [0, 1] """
def _repr_(self): """ TESTS::
sage: F = CombinatorialFreeModule(ZZ, [2,4,5]) sage: CP = cartesian_product([F, F]); CP # indirect doctest Free module generated by {2, 4, 5} over Integer Ring (+) Free module generated by {2, 4, 5} over Integer Ring sage: F.__custom_name = "F"; CP F (+) F """ # TODO: make this overridable by setting _name
@cached_method def cartesian_embedding(self, i): """ Return the natural embedding morphism of the ``i``-th Cartesian factor (summand) of ``self`` into ``self``.
INPUT:
- ``i`` -- an integer
EXAMPLES::
sage: F = CombinatorialFreeModule(ZZ, [4,5]); F.__custom_name = "F" sage: G = CombinatorialFreeModule(ZZ, [4,6]); G.__custom_name = "G" sage: S = cartesian_product([F, G]) sage: phi = S.cartesian_embedding(0) sage: phi(F.monomial(4) + 2 * F.monomial(5)) B[(0, 4)] + 2*B[(0, 5)] sage: phi(F.monomial(4) + 2 * F.monomial(6)).parent() == S True
TESTS::
sage: phi(G.monomial(4)) Traceback (most recent call last): ... AssertionError """ codomain = self)
summand_embedding = cartesian_embedding
@cached_method def cartesian_projection(self, i): """ Return the natural projection onto the `i`-th Cartesian factor (summand) of ``self``.
INPUT:
- ``i`` -- an integer
EXAMPLES::
sage: F = CombinatorialFreeModule(ZZ, [4,5]); F.__custom_name = "F" sage: G = CombinatorialFreeModule(ZZ, [4,6]); G.__custom_name = "G" sage: S = cartesian_product([F, G]) sage: x = S.monomial((0,4)) + 2 * S.monomial((0,5)) + 3 * S.monomial((1,6)) sage: S.cartesian_projection(0)(x) B[4] + 2*B[5] sage: S.cartesian_projection(1)(x) 3*B[6] sage: S.cartesian_projection(0)(x).parent() == F True sage: S.cartesian_projection(1)(x).parent() == G True """
summand_projection = cartesian_projection
def _cartesian_product_of_elements(self, elements): """ Return the Cartesian product of the elements.
INPUT:
- ``elements`` -- a tuple with one element of each Cartesian factor of ``self``
EXAMPLES::
sage: F = CombinatorialFreeModule(ZZ, [4,5]); F.__custom_name = "F" sage: G = CombinatorialFreeModule(ZZ, [4,6]); G.__custom_name = "G" sage: S = cartesian_product([F, G]) sage: f = F.monomial(4) + 2 * F.monomial(5) sage: g = 2*G.monomial(4) + G.monomial(6) sage: S._cartesian_product_of_elements([f, g]) B[(0, 4)] + 2*B[(0, 5)] + 2*B[(1, 4)] + B[(1, 6)] sage: S._cartesian_product_of_elements([f, g]).parent() == S True
"""
def cartesian_factors(self): """ Return the factors of the Cartesian product.
EXAMPLES::
sage: F = CombinatorialFreeModule(ZZ, [4,5]); F.__custom_name = "F" sage: G = CombinatorialFreeModule(ZZ, [4,6]); G.__custom_name = "G" sage: S = cartesian_product([F, G]) sage: S.cartesian_factors() (F, G)
"""
class Element(CombinatorialFreeModule.Element): # TODO: get rid of this inheritance pass
CombinatorialFreeModule.CartesianProduct = CombinatorialFreeModule_CartesianProduct |