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 -*- r""" An element in an indexed free module.
AUTHORS:
- Travis Scrimshaw (03-2017): Moved code from :mod:`sage.combinat.free_module`. """
#***************************************************************************** # Copyright (C) 2017 Travis Scrimshaw <tcscrims at gmail.com> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # http://www.gnu.org/licenses/ #*****************************************************************************
from __future__ import absolute_import, division, print_function
from sage.structure.element cimport parent from sage.structure.richcmp cimport richcmp, rich_to_bool from cpython.object cimport Py_NE, Py_EQ
from sage.misc.misc import repr_lincomb from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute from sage.typeset.ascii_art import AsciiArt, empty_ascii_art from sage.typeset.unicode_art import UnicodeArt, empty_unicode_art from sage.categories.all import Category, Sets, ModulesWithBasis from sage.data_structures.blas_dict cimport add, negate, scal, axpy
cdef class IndexedFreeModuleElement(ModuleElement): def __init__(self, M, x): """ Create a combinatorial module element. This should never be called directly, but only through the parent combinatorial free module's :meth:`__call__` method.
TESTS::
sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) sage: B = F.basis() sage: f = B['a'] + 3*B['c']; f B['a'] + 3*B['c'] sage: f == loads(dumps(f)) True """
def __iter__(self): """ EXAMPLES::
sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) sage: B = F.basis() sage: f = B['a'] + 3*B['c'] sage: [i for i in sorted(f)] [('a', 1), ('c', 3)]
::
sage: s = SymmetricFunctions(QQ).schur() sage: a = s([2,1]) + s([3]) sage: [i for i in sorted(a)] [([2, 1], 1), ([3], 1)] """
def __contains__(self, x): """ Returns whether or not a combinatorial object x indexing a basis element is in the support of self.
EXAMPLES::
sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) sage: B = F.basis() sage: f = B['a'] + 3*B['c'] sage: 'a' in f True sage: 'b' in f False
::
sage: s = SymmetricFunctions(QQ).schur() sage: a = s([2,1]) + s([3]) sage: Partition([2,1]) in a True sage: Partition([1,1,1]) in a False """
def __hash__(self): """ Return the hash value for ``self``.
The result is cached.
EXAMPLES::
sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) sage: B = F.basis() sage: f = B['a'] + 3*B['c'] sage: hash(f) 6429418278783588506 # 64-bit 726440090 # 32-bit
sage: F = RootSystem(['A',2]).ambient_space() sage: f = F.simple_root(0) sage: hash(f) 6920829894162680369 # 64-bit -528971215 # 32-bit
This uses the recipe that was proposed for frozendicts in :pep:`416` (and adds the hash of the parent). This recipe relies on the hash function for frozensets which uses tricks to mix the hash values of the items in case they are similar.
.. TODO::
It would be desirable to make the hash value depend on the hash value of the parent. See :trac:`15959`. """
def __reduce__(self): """ For pickling.
EXAMPLES::
sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) sage: loads(dumps(F.an_element())) == F.an_element() True """
def __setstate__(self, state): r""" For unpickling old ``CombinatorialFreeModuleElement`` classes. See :trac:`22632` and register_unpickle_override below.
EXAMPLES::
sage: loads(b'x\x9c\x95R\xcbn\x131\x14\xd5\x00\r\x89KK\xcb\xa3' ....: b'\xbc\xa1\xbc\xd3\xcd,\xe0\x0f\n\xad\xc4\xa2Y\x0c\xb2XZ' ....: b'\x8e\xe7N\xe6\x8a\xb1\xa7\xd7\x0f\x91,F\x82E&\xe2\xafq3' ....: b'\x13\xa4"X\xb0\xb1}\xae}\xce=\xf7\xc8\xdf\xaf(\'g\x90:o' ....: b'\x83\xf2\xc1B\x9a/\x8c\xd4\xa8\x84\xaa\xa4s\xec2\xa2d' ....: b'\xcc\xdf\x7f\xa8\xf5\x14\x8d\xf4\xb5EY\x9dZ\x80\xb3:' ....: b'\x0f\x15\x88o\xe8K\xa1\xa4\x87Ym\x17)T\xa0\xc1\xf8\x8eH}' ....: b'\x17\xd5S\xd3"\xd2\x84^\xf3\xd8?\xf4N:\x01FW\x95\x10\xd3' ....: b'\x80\x95G#\x04\x9b\x81\x97\xde[F\xd7:I\x8dN\xad\x17\xa6dU' ....: b'\t\r\xbe\xacsF[\xe5\xd6\x9f\x83\x05\x83\x14@X8\xb7\xe0' ....: b'\xa2\xb2\xf4X\x1b\x16\x8c\x85<(`4\xe8=v\x13 \xb8\xb43' ....: b'\xe8\xd8Y\xbf\xd3\xf5\xee\x89E3s)\x9a\xf8\x10\xac\xb8@' ....: b'\xecS\x07\xb2\x8b3\r\x8f2\x1a-\x1bb|\x98\xa3;\x97^\x95' ....: b'\xb4\xfd\xd3\xad\xe8FF;|\xbbKJ\xce\xb1\xd6\xb4\xcbG_":' ....: b'\x96\x0e\x1d\xdd\\e\xb4W\xee\xf2\xfdS4\xe8\xe1#\xc6\x00' ....: b'\\4)+\xda\x8fW\xb7\xf8\xce\xe5To\xb7\x19\xddi\xe9\xeed2' ....: b'\xf1\x19\x1d\x1c\xfd\xa0{\xe5\xe0\xff\x93ft\xbf\x1cm\x88' ....: b'\x0e\xbcK\x8bu\x7f\x01&h\xb01\x8f\\\xc42\xeb\\\x9d\xfc.~' ....: b'\x8e5z\xc0\x939O\x16-=\\6+z\x94\xd1\xe3\xb6\xa1\'c>\xdc' ....: b'\xfc\x04zZ\xee\xf1A\xcc\xbc\xc09=\xe3\xc9qX\xd1aF\xcf' ....: b'\x1bz\xc1\x0f\xa23S\xeb\xe8F\xa8\x1a\x8a\x02\x15\xc6\xe9' ....: b'\x1c\xbdl\xe8\xd58\xaa\xfe%n\xa6\xe5W\x10\x1b@\xafy\xf2n' ....: b'\x99\xd1\x9b\xe8\xa2\xec\xcfo\x83k\xa7\xe9/\xc1\xe1\t\x17') 2*B['x'] + 2*B['y'] """
cpdef dict monomial_coefficients(self, bint copy=True): """ Return the internal dictionary which has the combinatorial objects indexing the basis as keys and their corresponding coefficients as values.
INPUT:
- ``copy`` -- (default: ``True``) if ``self`` is internally represented by a dictionary ``d``, then make a copy of ``d``; if ``False``, then this can cause undesired behavior by mutating ``d``
EXAMPLES::
sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) sage: B = F.basis() sage: f = B['a'] + 3*B['c'] sage: d = f.monomial_coefficients() sage: d['a'] 1 sage: d['c'] 3
To run through the monomials of an element, it is better to use the idiom::
sage: for (t,c) in f: ....: print("{} {}".format(t,c)) a 1 c 3
::
sage: s = SymmetricFunctions(QQ).schur() sage: a = s([2,1])+2*s([3,2]) sage: d = a.monomial_coefficients() sage: type(d) <... 'dict'> sage: d[ Partition([2,1]) ] 1 sage: d[ Partition([3,2]) ] 2 """
def _sorted_items_for_printing(self): """ Returns the items (i.e terms) of ``self``, sorted for printing
EXAMPLES::
sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) sage: B = F.basis() sage: f = B['a'] + 2*B['c'] + 3 * B['b'] sage: f._sorted_items_for_printing() [('a', 1), ('b', 3), ('c', 2)] sage: F.print_options(sorting_reverse=True) sage: f._sorted_items_for_printing() [('c', 2), ('b', 3), ('a', 1)] sage: F.print_options(sorting_reverse=False) #reset to original state
.. SEEALSO:: :meth:`_repr_`, :meth:`_latex_`, :meth:`print_options` """ except Exception: # Sorting the output is a plus, but if we can't, no big deal pass
def _repr_(self): """ EXAMPLES::
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], prefix='F') sage: e = F.basis() sage: e['a'] + 2*e['b'] # indirect doctest F['a'] + 2*F['b'] sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], prefix='') sage: e = F.basis() sage: e['a'] + 2*e['b'] # indirect doctest ['a'] + 2*['b'] sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], prefix='', scalar_mult=' ', bracket=False) sage: e = F.basis() sage: e['a'] + 2*e['b'] # indirect doctest 'a' + 2 'b'
Controling the order of terms by providing a comparison function on elements of the support::
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], ....: sorting_reverse=True) sage: e = F.basis() sage: e['a'] + 3*e['b'] + 2*e['c'] 2*B['c'] + 3*B['b'] + B['a']
sage: F = CombinatorialFreeModule(QQ, ['ac', 'ba', 'cb'], ....: sorting_key=lambda x: x[1]) sage: e = F.basis() sage: e['ac'] + 3*e['ba'] + 2*e['cb'] 3*B['ba'] + 2*B['cb'] + B['ac'] """
def _ascii_art_(self): """ TESTS::
sage: M = QuasiSymmetricFunctions(QQ).M() sage: ascii_art(M[1,3]**2) # indirect doctest 4*M + 2*M + 2*M + 2*M + 2*M + M *** ****** *** *** *** ****** *** * * **** *** ** * * *** * ** * * sage: ascii_art(M.zero()) 0 """
repr_monomial = str
scalar_mult = "*"
b = empty_ascii_art # "" else: else: else: return AsciiArt(["1"]) else:
def _unicode_art_(self): """ TESTS::
sage: M = QuasiSymmetricFunctions(QQ).M() sage: unicode_art(M[1,1]**2) # indirect doctest 6*M + 2*M + 2*M + 2*M + M ┌┐ ┌┬┐ ┌┐ ┌┐ ┌┬┐ ├┤ ├┼┘ ┌┼┤ ├┤ ┌┼┼┘ ├┤ ├┤ ├┼┘ ┌┼┤ └┴┘ ├┤ └┘ └┘ └┴┘ └┘ """
repr_monomial = str
scalar_mult = "*"
coeff = "-" b = empty_unicode_art # "" else: coeff = " - %s" % coeff[1:] else: else: return "0" return UnicodeArt(["1"]) else:
def _latex_(self): r""" EXAMPLES::
sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) sage: B = F.basis() sage: f = B['a'] + 3*B['c'] sage: latex(f) B_{a} + 3B_{c}
::
sage: QS3 = SymmetricGroupAlgebra(QQ,3) sage: a = 2 + QS3([2,1,3]) sage: latex(a) #indirect doctest 2[1, 2, 3] + [2, 1, 3]
::
sage: F = CombinatorialFreeModule(QQ, ['a','b'], prefix='beta', latex_prefix='\\beta') sage: x = F.an_element() sage: x 2*beta['a'] + 2*beta['b'] sage: latex(x) 2\beta_{a} + 2\beta_{b}
Controling the order of terms by providing a comparison function on elements of the support::
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], ....: sorting_reverse=True) sage: e = F.basis() sage: latex(e['a'] + 3*e['b'] + 2*e['c']) 2B_{c} + 3B_{b} + B_{a}
sage: F = CombinatorialFreeModule(QQ, ['ac', 'ba', 'cb'], ....: sorting_key=lambda x: x[1]) sage: e = F.basis() sage: latex(e['ac'] + 3*e['ba'] + 2*e['cb']) 3B_{ba} + 2B_{cb} + B_{ac} """
cpdef _richcmp_(self, other, int op): """ Rich comparison for equal parents.
EXAMPLES::
sage: F1 = CombinatorialFreeModule(QQ, [1, 2, 3]) sage: F2 = CombinatorialFreeModule(QQ, [1, 2, 3], prefix = "g") sage: F1.zero() == F1.zero() True sage: F1.zero() == F1.an_element() False sage: F1.an_element() == F1.an_element() True sage: F1.an_element() is None False
::
sage: F3 = CombinatorialFreeModule(QQ, ['a','b','c']) sage: F3.an_element() != F3.an_element() False sage: F3.an_element() != F3.zero() True
::
sage: s = SymmetricFunctions(QQ).schur() sage: a = s([2,1]) sage: b = s([1,1,1]) sage: a == b False
.. TODO::
Currently, if ``self`` and ``other`` do not have the same parent, seemingly equal elements do not evaluate equal, since conversions between different modules have not been established.
::
sage: F1.zero() == 0 True sage: F1(0) 0
::
sage: F1.zero() == F2.zero() False sage: F1(F2.zero()) Traceback (most recent call last): ... TypeError: do not know how to make x (= 0) an element of self (=Free module generated by {1, 2, 3} over Rational Field) sage: F = AlgebrasWithBasis(QQ).example() sage: F.one() == 1 True sage: 1 == F.one() True sage: 2 * F.one() == int(2) True sage: int(2) == 2 * F.one() True
sage: S = SymmetricFunctions(QQ); s = S.s(); p = S.p() sage: p[2] == s[2] - s[1, 1] True sage: p[2] == s[2] False
This feature is disputable, in particular since it can make equality testing costly. It may be removed at some point.
Equality testing can be a bit tricky when the order of terms can vary because their indices are incomparable with ``cmp``. The following test did fail before :trac:`12489` ::
sage: F = CombinatorialFreeModule(QQ, Subsets([1,2,3])) sage: x = F.an_element() sage: (x+F.zero()).terms() # random [2*B[{1}], 3*B[{2}], B[{}]] sage: x.terms() # random [2*B[{1}], B[{}], 3*B[{2}]] sage: x+F.zero() == x True
TESTS::
sage: TestSuite(F1).run() sage: TestSuite(F).run() """
# Not equal
cpdef _add_(self, other): """ EXAMPLES::
sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) sage: B = F.basis() sage: B['a'] + 3*B['c'] B['a'] + 3*B['c']
::
sage: s = SymmetricFunctions(QQ).schur() sage: s([2,1]) + s([5,4]) # indirect doctest s[2, 1] + s[5, 4] sage: a = s([2,1]) + 0 sage: len(a.monomial_coefficients()) 1 """
cpdef _neg_(self): """ EXAMPLES::
sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) sage: B = F.basis() sage: f = B['a'] + 3*B['c'] sage: -f -B['a'] - 3*B['c']
::
sage: s = SymmetricFunctions(QQ).schur() sage: -s([2,1]) # indirect doctest -s[2, 1] """
cpdef _sub_(self, other): """ EXAMPLES::
sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) sage: B = F.basis() sage: B['a'] - 3*B['c'] B['a'] - 3*B['c']
::
sage: s = SymmetricFunctions(QQ).schur() sage: s([2,1]) - s([5,4]) # indirect doctest s[2, 1] - s[5, 4] """
cpdef _coefficient_fast(self, m): """ Return the coefficient of ``m`` in ``self``, where ``m`` is key in ``self._monomial_coefficients``.
EXAMPLES::
sage: p = Partition([2,1]) sage: q = Partition([1,1,1]) sage: s = SymmetricFunctions(QQ).schur() sage: a = s(p) sage: a._coefficient_fast([2,1]) Traceback (most recent call last): ... TypeError: unhashable type: 'list'
::
sage: a._coefficient_fast(p) 1 sage: a._coefficient_fast(q) 0 """
def __getitem__(self, m): """ EXAMPLES::
sage: s = SymmetricFunctions(QQ).schur() sage: p = Partition([2,1]) sage: q = Partition([1,1,1]) sage: a = s(p) sage: a[p] 1 sage: a[q] 0 """
def _vector_(self, new_base_ring=None): """ Returns ``self`` as a dense vector
INPUT:
- ``new_base_ring`` -- a ring (default: ``None``)
OUTPUT: a dense :func:`FreeModule` vector
.. WARNING:: This will crash/run forever if ``self`` is infinite dimensional!
.. SEEALSO::
- :func:`vector` - :meth:`CombinatorialFreeModule.get_order` - :meth:`CombinatorialFreeModule.from_vector` - :meth:`CombinatorialFreeModule._dense_free_module`
EXAMPLES::
sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) sage: B = F.basis() sage: f = B['a'] - 3*B['c'] sage: f._vector_() (1, 0, -3)
One can use equivalently::
sage: f.to_vector() (1, 0, -3) sage: vector(f) (1, 0, -3)
More examples::
sage: QS3 = SymmetricGroupAlgebra(QQ, 3) sage: a = 2*QS3([1,2,3]) + 4*QS3([3,2,1]) sage: a._vector_() (2, 0, 0, 0, 0, 4) sage: a.to_vector() (2, 0, 0, 0, 0, 4) sage: vector(a) (2, 0, 0, 0, 0, 4) sage: a == QS3.from_vector(a.to_vector()) True
If ``new_base_ring`` is specified, then a vector over ``new_base_ring`` is returned::
sage: a._vector_(RDF) (2.0, 0.0, 0.0, 0.0, 0.0, 4.0)
.. NOTE::
:trac:`13406`: the current implementation has been optimized, at the price of breaking the encapsulation for FreeModule elements creation, with the following use case as metric, on a 2008' Macbook Pro::
sage: F = CombinatorialFreeModule(QQ, range(10)) sage: f = F.an_element() sage: %timeit f._vector_() # not tested 625 loops, best of 3: 17.5 micros per loop
Other use cases may call for different or further optimizations. """
to_vector = _vector_
cpdef _acted_upon_(self, scalar, bint self_on_left): """ Return the action of ``scalar`` (an element of the base ring) on ``self``.
EXAMPLES::
sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) sage: B = F.basis() sage: B['a']*(1/2) # indirect doctest 1/2*B['a'] sage: B['a']/2 1/2*B['a'] sage: B['a']*2 # indirect doctest 2*B['a'] sage: B['a']*int(2) # indirect doctest 2*B['a']
sage: 1/2*B['a'] 1/2*B['a'] sage: 2*B['a'] # indirect doctest 2*B['a'] sage: int(2)*B['a'] # indirect doctest 2*B['a']
TESTS::
sage: F.get_action(QQ, operator.mul, True) Right scalar multiplication by Rational Field on Free module generated by {'a', 'b', 'c'} over Rational Field sage: F.get_action(QQ, operator.mul, False) Left scalar multiplication by Rational Field on Free module generated by {'a', 'b', 'c'} over Rational Field sage: F.get_action(ZZ, operator.mul, True) Right scalar multiplication by Integer Ring on Free module generated by {'a', 'b', 'c'} over Rational Field sage: F.get_action(F, operator.mul, True) sage: F.get_action(F, operator.mul, False)
This also works when a coercion of the coefficient is needed, for example with polynomials or fraction fields (:trac:`8832`)::
sage: P.<q> = QQ['q'] sage: V = CombinatorialFreeModule(P, Permutations()) sage: el = V(Permutation([3,1,2])) sage: (3/2)*el 3/2*B[[3, 1, 2]]
sage: P.<q> = QQ['q'] sage: F = FractionField(P) sage: V = CombinatorialFreeModule(F, Words()) sage: w = Words()('abc') sage: (1+q)*V(w) (q+1)*B[word: abc] sage: ((1+q)/q)*V(w) ((q+1)/q)*B[word: abc]
.. TODO::
Add non commutative tests. """ # With the current design, the coercion model does not have # enough information to detect a priori that this method only # accepts scalars; so it tries on some elements(), and we need # to make sure to report an error. # Temporary needed by coercion (see Polynomial/FractionField tests). else:
cpdef _lmul_(self, Element right): """ For backward compatibility.
EXAMPLES::
sage: C = CombinatorialFreeModule(QQ, [1,2,3]) sage: C.an_element()._lmul_(2) 4*B[1] + 4*B[2] + 6*B[3] """
cpdef _rmul_(self, Element left): """ For backward compatibility.
EXAMPLES::
sage: C = CombinatorialFreeModule(QQ, [1,2,3]) sage: C.an_element()._rmul_(2) 4*B[1] + 4*B[2] + 6*B[3] """
def __truediv__(left, x): """ Division by coefficients.
EXAMPLES::
sage: F = CombinatorialFreeModule(QQ, [1,2,3]) sage: x = F._from_dict({1:2, 2:3}) sage: from operator import truediv sage: truediv(x, 2) B[1] + 3/2*B[2]
::
sage: F = CombinatorialFreeModule(QQ, [1,2,3]) sage: B = F.basis() sage: f = 2*B[2] + 4*B[3] sage: truediv(f, 2) B[2] + 2*B[3]
TESTS::
sage: truediv(x, x) Traceback (most recent call last): ... TypeError: unable to convert 2*B[1] + 3*B[2] to a rational sage: truediv("hello", x) Traceback (most recent call last): ... TypeError: unsupported operand type(s) for /: 'str' and 'CombinatorialFreeModule_with_category.element_class' """
def __div__(left, right): """ Forward old-style division to true division.
EXAMPLES::
sage: F = CombinatorialFreeModule(QQ, [1,2,3]) sage: x = F._from_dict({1:2, 2:3}) sage: x/2 B[1] + 3/2*B[2] """
def _unpickle_element(C, d): """ Unpickle an element in ``C`` given by ``d``.
EXAMPLES::
sage: from sage.modules.with_basis.indexed_element import _unpickle_element sage: C = CombinatorialFreeModule(QQ, [1,2,3]) sage: _unpickle_element(C, {1: -2, 3: -12}) -2*B[1] - 12*B[3] """
# Handle old CombinatorialFreeModuleElement pickles, see trac #22632 from sage.structure.sage_object import register_unpickle_override register_unpickle_override("sage.combinat.free_module", "CombinatorialFreeModuleElement", IndexedFreeModuleElement) |