def coproduct_on_basis(self, compo): r""" Returns the coproduct of a Monomial basis element. Combinatorial rule: deconcatenation. INPUT: - ``compo`` -- composition OUTPUT: - The coproduct applied to the Monomial quasi-symmetric function indexed by ``compo``, expressed in the Monomial basis. EXAMPLES:: sage: M=QuasiSymmetricFunctions(QQ).Monomial() sage: M[4,2,3].coproduct() M[] # M[4, 2, 3] + M[4] # M[2, 3] + M[4, 2] # M[3] + M[4, 2, 3] # M[] sage: M.coproduct_on_basis(Composition([])) M[] # M[] """ return self.tensor_square().sum_of_monomials((Composition(compo[:i]), Composition(compo[i:])) for i in range(0,len(compo)+1))
def __classcall__(cls, row_sums, column_sums): r""" Normalize the inputs so that they are hashable. INPUT: - ``row_sums`` -- list, tuple, or anything defining a Composition - ``column_sums`` -- list, tuple, or anything defining a Composition EXAMPLES:: sage: from sage.combinat.integer_matrices import IntegerMatrices sage: IM = IntegerMatrices([4,4,5], [3,7,1,2]); IM Non-negative integer matrices with row sums [4, 4, 5] and column sums [3, 7, 1, 2] sage: IM = IntegerMatrices((4,4,5), (3,7,1,2)); IM Non-negative integer matrices with row sums [4, 4, 5] and column sums [3, 7, 1, 2] sage: IM = IntegerMatrices(Composition([4,4,5]), Composition([3,7,1,2])); IM Non-negative integer matrices with row sums [4, 4, 5] and column sums [3, 7, 1, 2] """ from sage.combinat.composition import Composition row_sums = Composition(row_sums) column_sums = Composition(column_sums) return super(IntegerMatrices, cls).__classcall__(cls, row_sums, column_sums)
def __getitem__(self, c, *rest): """ This method implements the abuses of notations:: sage: Psi = NonCommutativeSymmetricFunctions(QQ).Psi() sage: Psi[2,1] Psi[2, 1] sage: Psi[[2,1]] Psi[2, 1] sage: Psi[Composition([2,1])] Psi[2, 1] .. todo:: This should call ``super.monomial`` if the input can't be made into a composition so as not to interfere with the standard notation ``Psi['x,y,z']``. This could possibly be shared with Sym, FQSym, and other algebras with bases indexed by list-like objects """ if isinstance(c, Composition): assert len(rest) == 0 else: if len(rest) > 0 or isinstance(c, (int, Integer)): c = Composition([c] + list(rest)) else: c = Composition(list(c)) return self.monomial(c)
def m_to_s_stat(R, I, K): r""" Returns the statistic for the expansion of the Monomial basis element indexed by two compositions, as in formula (36) of Tevlin's "Noncommutative Analogs of Monomial Symmetric Functions, Cauchy Identity, and Hall Scalar Product". INPUT: - ``R`` -- A ring - ``I``, ``K`` -- compositions OUTPUT: - An integer EXAMPLES:: sage: from sage.combinat.ncsf_qsym.combinatorics import m_to_s_stat sage: m_to_s_stat(QQ,Composition([2,1]), Composition([1,1,1])) -1 sage: m_to_s_stat(QQ,Composition([3]), Composition([1,2])) -2 """ stat = 0 for J in Compositions(I.size()): if (I.is_finer(J) and K.is_finer(J)): pvec = [ 0 ] + Composition(I).refinement_splitting_lengths(J).partial_sums() pp = prod(R(len(I) - pvec[i]) for i in range(len(pvec) - 1)) stat += R((-1)**(len(I) - len(K)) / pp * coeff_lp(K, J)) return stat
def to_composition(self, x): r""" The composition corresponding to the integer matrix. This is the composition obtained by reading the entries of the matrix from left to right along each row, and reading the rows from top to bottom, ignore zeros. INPUT: - ``x`` -- matrix EXAMPLES:: sage: from sage.combinat.integer_matrices import IntegerMatrices sage: IM = IntegerMatrices([3,2,2], [2,5]); IM Non-negative integer matrices with row sums [3, 2, 2] and column sums [2, 5] sage: IM.list() [ [2 1] [1 2] [1 2] [0 3] [0 3] [0 3] [0 2] [1 1] [0 2] [2 0] [1 1] [0 2] [0 2], [0 2], [1 1], [0 2], [1 1], [2 0] ] sage: for m in IM: print(IM.to_composition(m)) [2, 1, 2, 2] [1, 2, 1, 1, 2] [1, 2, 2, 1, 1] [3, 2, 2] [3, 1, 1, 1, 1] [3, 2, 2] """ from sage.combinat.composition import Composition return Composition([entry for row in x for entry in row if entry != 0])
def sum_of_partition_rearrangements(self, par): """ Returns the sum of all basis elements indexed by compositions which can be shuffled to obtain a partition. INPUT: - ``par`` -- a partition OUTPUT: - Returns the sum of all ``self`` basis elements indexed by compositions which are shuffles of ``par``. EXAMPLES:: sage: NCSF=NonCommutativeSymmetricFunctions(QQ) sage: elementary = NCSF.elementary() sage: elementary.sum_of_partition_rearrangements(Partition([2,2,1])) L[1, 2, 2] + L[2, 1, 2] + L[2, 2, 1] sage: elementary.sum_of_partition_rearrangements(Partition([3,2,1])) L[1, 2, 3] + L[1, 3, 2] + L[2, 1, 3] + L[2, 3, 1] + L[3, 1, 2] + L[3, 2, 1] sage: elementary.sum_of_partition_rearrangements(Partition([])) L[] """ return self.sum_of_monomials( Composition(comp) for comp in Permutations(par) )
def __classcall_private__(cls, s=None, c=None): """ Choose the correct parent based upon input. EXAMPLES:: sage: OrderedSetPartitions(4) Ordered set partitions of {1, 2, 3, 4} sage: OrderedSetPartitions(4, [1, 2, 1]) Ordered set partitions of {1, 2, 3, 4} into parts of size [1, 2, 1] """ if s is None: if c is not None: raise NotImplementedError( "cannot specify 'c' without specifying 's'") return OrderedSetPartitions_all() if isinstance(s, (int, Integer)): if s < 0: raise ValueError("s must be non-negative") s = frozenset(range(1, s + 1)) else: s = frozenset(s) if c is None: return OrderedSetPartitions_s(s) if isinstance(c, (int, Integer)): return OrderedSetPartitions_sn(s, c) if c not in Compositions(len(s)): raise ValueError("c must be a composition of %s" % len(s)) return OrderedSetPartitions_scomp(s, Composition(c))
def number_of_fCT(content_comp, shape_comp): r""" Returns the number of Immaculate tableau of shape ``shape_comp`` and content ``content_comp``. INPUT: - ``content_comp``, ``shape_comp`` -- compositions OUTPUT: - An integer EXAMPLES:: sage: from sage.combinat.ncsf_qsym.combinatorics import number_of_fCT sage: number_of_fCT(Composition([3,1]), Composition([1,3])) 0 sage: number_of_fCT(Composition([1,2,1]), Composition([1,3])) 1 sage: number_of_fCT(Composition([1,1,3,1]), Composition([2,1,3])) 2 """ if content_comp.to_partition().length() == 1: if shape_comp.to_partition().length() == 1: return 1 else: return 0 C = Compositions(content_comp.size() - content_comp[-1], outer=list(shape_comp)) s = 0 for x in C: if len(x) >= len(shape_comp) - 1: s += number_of_fCT(Composition(content_comp[:-1]), x) return s
def __init__(self, s, comp): """ TESTS:: sage: OS = OrderedSetPartitions([1,2,3,4], [2,1,1]) sage: OS == loads(dumps(OS)) True """ OrderedSetPartitions.__init__(self, s) self.c = Composition(comp)
def __init__(self, e): """ TESTS:: sage: LW21 = LyndonWords([2,1]); LW21 Lyndon words with evaluation [2, 1] sage: LW21 == loads(dumps(LW21)) True """ self.e = Composition(e)
def descent_composition(self): r""" Return the composition corresponding to the set of all `i` that do not have `i+1` appearing strictly to the left of `i` in ``self``. EXAMPLES:: sage: CompositionTableau([[1],[3,2],[4,4]]).descent_composition() [1, 2, 2] """ return Composition(from_subset=(self.descent_set(), self.size()))
def shape_composition(self): r""" Return a Composition object which is the shape of ``self``. EXAMPLES:: sage: CompositionTableau([[1,1],[3,2],[4,4,3]]).shape_composition() [2, 2, 3] sage: CompositionTableau([[2,1],[3],[4]]).shape_composition() [2, 1, 1] """ return Composition([len(row) for row in self])
def __init__(self, e): """ TESTS:: sage: N = Necklaces([2,2,2]) sage: N == loads(dumps(N)) True """ if isinstance(e, Composition): self.e = e else: self.e = Composition(e)
def __classcall_private__(cls, content): """ Return the correct parent object, with standardized parameters. EXAMPLES:: sage: Necklaces([2,1,1]) is Necklaces(Composition([2,1,1])) True """ if isinstance(content, Composition): return super(Necklaces_evaluation, cls).__classcall__(cls, content) else: content = Composition(content) return super(Necklaces_evaluation, cls).__classcall__(cls, content)
def __init__(self, content): r""" Initialize ``self``. TESTS:: sage: N = Necklaces([2,2,2]) sage: N == loads(dumps(N)) True """ if isinstance(content, Composition): self._content = content else: self._content = Composition(content)
def weight(self): r""" Return a composition where entry `i` is the number of times that `i` appears in ``self``. EXAMPLES:: sage: CompositionTableau([[1],[3,2],[4,4]]).weight() [1, 1, 1, 2, 0] """ w = {i: 0 for i in range(1, self.size() + 1)} for row in self: for i in row: w[i] += 1 return Composition([w[i] for i in range(1, self.size() + 1)])
def to_composition(self): r""" Return the integer composition whose parts are the sizes of the sets in ``self``. EXAMPLES:: sage: S = OrderedSetPartitions(5) sage: x = S([[3,5,4], [1, 2]]) sage: x.to_composition() [3, 2] sage: y = S([[3,1], [2], [5,4]]) sage: y.to_composition() [2, 1, 2] """ return Composition([len(p) for p in self])
def __iter__(self): """ TESTS:: sage: LyndonWords(3,3).list() # indirect doctest [word: 112, word: 113, word: 122, word: 123, word: 132, word: 133, word: 223, word: 233] """ for c in IntegerVectors(self._k, self._n): cf = [] nonzero_indices = [] for i, x in enumerate(c): if x: nonzero_indices.append(i) cf.append(x) for lw in LyndonWords_evaluation(Composition(cf)): yield self._words([nonzero_indices[x - 1] + 1 for x in lw], check=False)
def one_basis(self): r""" This returns the empty composition. OUTPUT - The empty composition. EXAMPLES:: sage: L=NonCommutativeSymmetricFunctions(QQ).L() sage: parent(L) <class 'sage.combinat.ncsf_qsym.ncsf.NonCommutativeSymmetricFunctions.Elementary_with_category'> sage: parent(L).one_basis() [] """ return Composition([])
def to_composition(self): r""" Concatenate the antisymmetric and symmetric parts to a composition. OUTPUT: - a (possibly weak) composition EXAMPLES:: sage: SuperPartition([[3,1],[2,2,1]]).to_composition() [3, 1, 2, 2, 1] sage: SuperPartition([[2,1,0],[3,3]]).to_composition() [2, 1, 0, 3, 3] sage: SuperPartition([[2,1,0],[3,3]]).to_composition().parent() Compositions of non-negative integers """ return Composition(self[0] + self[1])
def descent_composition(t): """ Return the descent composition of a standard tableau ``t``. This is the composition of the size of `t` whose partial sums are the elements of the descent set of ``t`` (see :meth:`descent_set`). EXAMPLES:: sage: from sage.combinat.chas.fsym import descent_composition sage: t = StandardTableau([[1,3,4,7],[2,5,6],[8]]) sage: descent_composition(t) [1, 3, 3, 1] sage: descent_composition([[1, 3, 5], [2, 4]]) [1, 2, 2] """ n = sum(len(row) for row in t) return Composition(from_subset=(descent_set(t), n))
def m_to_s_stat(R, I, K): r""" Return the coefficient of the complete non-commutative symmetric function `S^K` in the expansion of the monomial non-commutative symmetric function `M^I` with respect to the complete basis over the ring `R`. This is the coefficient in formula (36) of Tevlin's paper [Tev2007]_. INPUT: - ``R`` -- A ring, supposed to be a `\QQ`-algebra - ``I``, ``K`` -- compositions OUTPUT: - The coefficient of `S^K` in the expansion of `M^I` in the complete basis of the non-commutative symmetric functions over ``R``. EXAMPLES:: sage: from sage.combinat.ncsf_qsym.combinatorics import m_to_s_stat sage: m_to_s_stat(QQ, Composition([2,1]), Composition([1,1,1])) -1 sage: m_to_s_stat(QQ, Composition([3]), Composition([1,2])) -2 sage: m_to_s_stat(QQ, Composition([2,1,2]), Composition([2,1,2])) 8/3 """ stat = 0 for J in Compositions(I.size()): if (I.is_finer(J) and K.is_finer(J)): pvec = [ 0 ] + Composition(I).refinement_splitting_lengths(J).partial_sums() pp = prod(R(len(I) - pvec[i]) for i in range(len(pvec) - 1)) stat += R((-1)**(len(I) - len(K)) / pp * coeff_lp(K, J)) return stat
def number_of_SSRCT(content_comp, shape_comp): r""" The number of semi-standard reverse composition tableaux. The dual quasisymmetric-Schur functions satisfy a left Pieri rule where `S_n dQS_\gamma` is a sum over dual quasisymmetric-Schur functions indexed by compositions which contain the composition `\gamma`. The definition of an SSRCT comes from this rule. The number of SSRCT of content `\beta` and shape `\alpha` is equal to the number of SSRCT of content `(\beta_2, \ldots, \beta_\ell)` and shape `\gamma` where `dQS_\alpha` appears in the expansion of `S_{\beta_1} dQS_\gamma`. In sage the recording tableau for these objects are called :class:`~sage.combinat.composition_tableau.CompositionTableaux`. INPUT: - ``content_comp``, ``shape_comp`` -- compositions OUTPUT: - An integer EXAMPLES:: sage: from sage.combinat.ncsf_qsym.combinatorics import number_of_SSRCT sage: number_of_SSRCT(Composition([3,1]), Composition([1,3])) 0 sage: number_of_SSRCT(Composition([1,2,1]), Composition([1,3])) 1 sage: number_of_SSRCT(Composition([1,1,2,2]), Composition([3,3])) 2 sage: all(CompositionTableaux(be).cardinality() ....: == sum(number_of_SSRCT(al,be)*binomial(4,len(al)) ....: for al in Compositions(4)) ....: for be in Compositions(4)) True """ if len(content_comp) == 1: if len(shape_comp) == 1: return ZZ.one() else: return ZZ.zero() s = ZZ.zero() cond = lambda al, be: all(al[j] <= be_val and not any(al[ i] <= k and k <= be[i] for k in range(al[j], be_val) for i in range(j)) for j, be_val in enumerate(be)) C = Compositions(content_comp.size() - content_comp[0], inner=[1] * len(shape_comp), outer=list(shape_comp)) for x in C: if cond(x, shape_comp): s += number_of_SSRCT(Composition(content_comp[1:]), x) if shape_comp[0] <= content_comp[0]: C = Compositions( content_comp.size() - content_comp[0], inner=[min(val, shape_comp[0] + 1) for val in shape_comp[1:]], outer=shape_comp[1:]) Comps = Compositions() for x in C: if cond([shape_comp[0]] + list(x), shape_comp): s += number_of_SSRCT(Comps(content_comp[1:]), x) return s
def shape(self): from sage.combinat.composition import Composition return Composition([len(B) for B in self.value])
def __classcall_private__(cls, *args, **kwargs): r""" This is a factory class which returns the appropriate parent based on arguments. See the documentation for :class:`CompositionTableaux` for more information. TESTS:: sage: CT = CompositionTableaux(3); CT Composition Tableaux of size 3 and maximum entry 3 sage: CT = CompositionTableaux(size=3); CT Composition Tableaux of size 3 and maximum entry 3 sage: CT = CompositionTableaux([1,2]); CT Composition tableaux of shape [1, 2] and maximun entry 3 sage: CT = CompositionTableaux(shape=[1,2]); CT Composition tableaux of shape [1, 2] and maximun entry 3 sage: CT = CompositionTableaux(shape=[]); CT Composition tableaux of shape [] and maximun entry 0 sage: CT = CompositionTableaux(0); CT Composition Tableaux of size 0 and maximum entry 0 sage: CT = CompositionTableaux(max_entry=3); CT Composition tableaux with maximum entry 3 sage: CT = CompositionTableaux([1,2],max_entry=3); CT Composition tableaux of shape [1, 2] and maximun entry 3 sage: CT = CompositionTableaux(size=2,shape=[1,2]); CT Traceback (most recent call last): ... ValueError: size and shape are different sizes """ # Process keyword arguments first n = kwargs.get('n', None) size = kwargs.get('size', n) comp = kwargs.get('comp', None) shape = kwargs.get('shape', comp) max_entry = kwargs.get('max_entry', None) # Process positional arguments if args: # The first arg could be either a size or a shape if isinstance(args[0], (int, Integer)): if size is not None: raise ValueError("size was specified more than once") else: size = args[0] else: if shape is not None: raise ValueError("the shape was specified more than once") shape = args[0] # Consistency checks if size is not None: if not isinstance(size, (int, Integer)): raise ValueError("size must be an integer") elif size < 0: raise ValueError("size must be non-negative") if shape is not None: # use in (and not isinstance) below so that lists can be used as # shorthand if not shape in Compositions(): raise ValueError("shape must be a composition") if any(i == 0 for i in shape): raise ValueError("shape must have non-zero parts") shape = Composition(shape) if (size is not None) and (shape is not None): if sum(shape) != size: raise ValueError("size and shape are different sizes") if max_entry is not None: if not isinstance(max_entry, (int, Integer)): raise ValueError("max_entry must be an integer") elif max_entry <= 0: raise ValueError("max_entry must be positive") # Dispatch to appropriate class if (shape is not None): return CompositionTableaux_shape(shape, max_entry) if (size is not None): return CompositionTableaux_size(size, max_entry) return CompositionTableaux_all(max_entry)
def LyndonWords(e=None, k=None): """ Returns the combinatorial class of Lyndon words. A Lyndon word `w` is a word that is lexicographically less than all of its rotations. Equivalently, whenever `w` is split into two non-empty substrings, `w` is lexicographically less than the right substring. INPUT: - no input at all or - ``e`` - integer, size of alphabet - ``k`` - integer, length of the words or - ``e`` - a composition OUTPUT: A combinatorial class of Lyndon words. EXAMPLES:: sage: LyndonWords() Lyndon words If e is an integer, then e specifies the length of the alphabet; k must also be specified in this case:: sage: LW = LyndonWords(3,3); LW Lyndon words from an alphabet of size 3 of length 3 sage: LW.first() word: 112 sage: LW.last() word: 233 sage: LW.random_element() # random word: 112 sage: LW.cardinality() 8 If e is a (weak) composition, then it returns the class of Lyndon words that have evaluation e:: sage: LyndonWords([2, 0, 1]).list() [word: 113] sage: LyndonWords([2, 0, 1, 0, 1]).list() [word: 1135, word: 1153, word: 1315] sage: LyndonWords([2, 1, 1]).list() [word: 1123, word: 1132, word: 1213] """ if e is None and k is None: return LyndonWords_class() elif isinstance(e, (int, Integer)): if e > 0: if not isinstance(k, (int, Integer)): raise TypeError("k must be a non-negative integer") if k < 0: raise TypeError("k must be a non-negative integer") return LyndonWords_nk(Integer(e), Integer(k)) elif e in Compositions(): return LyndonWords_evaluation(Composition(e)) raise TypeError("e must be a positive integer or a composition")
def from_polynomial(self, f, check=True): """ Returns the quasi-symmetric function in the Monomial basis corresponding to the quasi-symmetric polynomial ``f``. INPUT: - ``f`` -- a polynomial in finitely many variables over the same base ring as ``self``. It is assumed that this polynomial is quasi-symmetric. - ``check`` -- boolean (default: ``True``), checks whether the polynomial is indeed quasi-symmetric. OUTPUT: - quasi-symmetric function in the Monomial basis EXAMPLES:: sage: P = PolynomialRing(QQ, 'x', 3) sage: x = P.gens() sage: f = x[0] + x[1] + x[2] sage: QSym = QuasiSymmetricFunctions(QQ) sage: QSym.from_polynomial(f) M[1] Beware of setting ``check=False``:: sage: f = x[0] + 2*x[1] + x[2] sage: QSym.from_polynomial(f, check=True) Traceback (most recent call last): ... ValueError: x0 + 2*x1 + x2 is not a quasi-symmetric polynomial sage: QSym.from_polynomial(f, check=False) M[1] To expand the quasi-symmetric function in a basis other than the Monomial basis, the following shorthands are provided:: sage: M = QSym.Monomial() sage: f = x[0]**2+x[1]**2+x[2]**2 sage: g = M.from_polynomial(f); g M[2] sage: F = QSym.Fundamental() sage: F(g) -F[1, 1] + F[2] sage: F.from_polynomial(f) -F[1, 1] + F[2] """ assert self.base_ring() == f.base_ring() exponent_coefficient = f.dict() z = {} for (e, c) in exponent_coefficient.iteritems(): I = Composition([ei for ei in e if ei > 0]) if I not in z: z[I] = c out = self.Monomial()._from_dict(z) if check and out.expand(f.parent().ngens(), f.parent().gens()) != f: raise ValueError("%s is not a quasi-symmetric polynomial" % f) return out
def _proc(self, vect): """ Return the shuffle of ``w1`` with ``w2`` with 01-vector ``vect``. The 01-vector of a shuffle is a list of 0s and 1s whose length is the sum of the lengths of ``w1`` and ``w2``, and whose `k`-th entry is `1` if the `k`-th letter of the shuffle is taken from ``w1`` and `0` if it is taken from ``w2``. EXAMPLES:: sage: from sage.combinat.words.shuffle_product import ShuffleProduct_w1w2 sage: w, u = map(Words("abcd"), ["ab", "cd"]) sage: S = ShuffleProduct_w1w2(w,u) sage: S._proc([0,1,0,1]) word: cadb sage: S._proc([1,1,0,0]) word: abcd sage: I = Composition([1, 1]) sage: J = Composition([2]) sage: S = ShuffleProduct_w1w2(I, J) sage: S._proc([1,0,1]) [1, 2, 1] TESTS: Sage is no longer confused by a too-restrictive parent of `I` when shuffling two compositions `I` and `J` (cf. :trac:`15131`):: sage: I = Compositions(2)([1, 1]) sage: J = Composition([2]) sage: S = ShuffleProduct_w1w2(I, J) sage: S._proc([1,0,1]) [1, 2, 1] sage: S.list() [[1, 1, 2], [1, 2, 1], [2, 1, 1]] """ i1 = -1 i2 = -1 res = [] for v in vect: if v == 1: i1 += 1 res.append(self._w1[i1]) else: i2 += 1 res.append(self._w2[i2]) try: return self._w1.parent()(res) except ValueError: # Special situation: the parent of w1 is too # restrictive to be cast on res. if isinstance(self._w1, Composition): return Composition(res) elif isinstance(self._w1, Word_class): return Word(res) return res