def __init__(self, partition, ring=None, cache_matrices=True): r""" An irreducible representation of the symmetric group corresponding to ``partition``. For more information, see the documentation for :func:`SymmetricGroupRepresentation`. EXAMPLES:: sage: spc = SymmetricGroupRepresentation([3]) sage: spc([3,2,1]) [1] sage: spc == loads(dumps(spc)) True sage: spc = SymmetricGroupRepresentation([3], cache_matrices=False) sage: spc([3,2,1]) [1] sage: spc == loads(dumps(spc)) True """ self._partition = Partition(partition) self._ring = ring if not ring is None else self._default_ring if cache_matrices is False: self.representation_matrix = self._representation_matrix_uncached
def coproduct_on_basis(self, a): """ Return the coproduct of the basis element indexed by ``a``. EXAMPLES:: sage: R = PolynomialRing(ZZ, 'q').fraction_field() sage: q = R.gen() sage: I = HallAlgebra(R, q).monomial_basis() sage: I.coproduct_on_basis(Partition([1])) I[] # I[1] + I[1] # I[] sage: I.coproduct_on_basis(Partition([2])) I[] # I[2] + 1/q*I[1] # I[1] + I[2] # I[] sage: I.coproduct_on_basis(Partition([2,1])) I[] # I[2, 1] + 1/q*I[1] # I[1, 1] + I[1] # I[2] + 1/q*I[1, 1] # I[1] + I[2] # I[1] + I[2, 1] # I[] sage: R.<q> = LaurentPolynomialRing(ZZ) sage: I = HallAlgebra(R, q).monomial_basis() sage: I.coproduct_on_basis(Partition([2,1])) I[] # I[2, 1] + (q^-1)*I[1] # I[1, 1] + I[1] # I[2] + (q^-1)*I[1, 1] # I[1] + I[2] # I[1] + I[2, 1] # I[] """ S = self.tensor_square() return S.prod(S.sum_of_terms([( (Partition([r]), Partition([n-r]) ), self._q**(-r*(n-r)) ) for r in range(n+1)], distinct=True) for n in a)
def __classcall_private__(cls, part, k): r""" Implements the shortcut ``Core(part, k)`` to ``Cores(k,l)(part)`` where `l` is the length of the core. TESTS:: sage: c = Core([2,1],4); c [2, 1] sage: c.parent() 4-Cores of length 3 sage: type(c) <class 'sage.combinat.core.Cores_length_with_category.element_class'> sage: Core([2,1],3) Traceback (most recent call last): ... ValueError: [2, 1] is not a 3-core """ if isinstance(part, cls): return part part = Partition(part) if not part.is_core(k): raise ValueError, "%s is not a %s-core"%(part, k) l = sum(part.k_boundary(k).row_lengths()) return Cores(k, l)(part)
def sum_of_partitions(self, la): r""" Return the sum over all sets partitions whose shape is ``la``, scaled by `\prod_i m_i!` where `m_i` is the multiplicity of `i` in ``la``. INPUT: - ``la`` -- an integer partition OUTPUT: - an element of ``self`` EXAMPLES:: sage: w = SymmetricFunctionsNonCommutingVariables(QQ).dual().w() sage: w.sum_of_partitions([2,1,1]) 2*w{{1}, {2}, {3, 4}} + 2*w{{1}, {2, 3}, {4}} + 2*w{{1}, {2, 4}, {3}} + 2*w{{1, 2}, {3}, {4}} + 2*w{{1, 3}, {2}, {4}} + 2*w{{1, 4}, {2}, {3}} """ la = Partition(la) c = prod([factorial(_) for _ in la.to_exp()]) P = SetPartitions() return self.sum_of_terms([(P(m), c) for m in SetPartitions(sum(la), la)], distinct=True)
def coproduct_on_generators(self, i): r""" Return coproduct on generators for power sums `p_i` (for `i > 0`). The elements `p_i` are primitive elements. INPUT: - ``self`` -- the power sum basis of the symmetric functions - ``i`` -- a positive integer OUTPUT: - the result of the coproduct on the generator `p(i)` EXAMPLES:: sage: Sym = SymmetricFunctions(QQ) sage: p = Sym.powersum() sage: p.coproduct_on_generators(2) p[] # p[2] + p[2] # p[] """ Pi = Partition([i]) P0 = Partition([]) T = self.tensor_square() return T.sum_of_monomials([(Pi, P0), (P0, Pi)])
def sum_of_partitions(self, la): """ Return the sum over all sets partitions whose shape is ``la``, scaled by `\prod_i m_i!` where `m_i` is the multiplicity of `i` in ``la``. INPUT: - ``la`` -- an integer partition OUTPUT: - an element of ``self`` EXAMPLES:: sage: w = SymmetricFunctionsNonCommutingVariables(QQ).dual().w() sage: w.sum_of_partitions([2,1,1]) 2*w{{1}, {2}, {3, 4}} + 2*w{{1}, {2, 3}, {4}} + 2*w{{1}, {2, 4}, {3}} + 2*w{{1, 2}, {3}, {4}} + 2*w{{1, 3}, {2}, {4}} + 2*w{{1, 4}, {2}, {3}} """ la = Partition(la) c = prod([factorial(_) for _ in la.to_exp()]) P = SetPartitions() return self.sum_of_terms([(P(m), c) for m in SetPartitions(sum(la), la)], distinct=True)
def __classcall_private__(cls, part, k): r""" Implements the shortcut ``Core(part, k)`` to ``Cores(k,l)(part)`` where `l` is the length of the core. TESTS:: sage: c = Core([2,1],4); c [2, 1] sage: c.parent() 4-Cores of length 3 sage: type(c) <class 'sage.combinat.core.Cores_length_with_category.element_class'> sage: Core([2,1],3) Traceback (most recent call last): ... ValueError: [2, 1] is not a 3-core """ if isinstance(part, cls): return part part = Partition(part) if not part.is_core(k): raise ValueError, "%s is not a %s-core" % (part, k) l = sum(part.k_boundary(k).row_lengths()) return Cores(k, l)(part)
def __classcall_private__(cls, w, n, x=None, k=None): r""" Classcall to mend the input. TESTS:: sage: A = crystals.AffineFactorization([[3,1],[1]], 4, k=3); A Crystal on affine factorizations of type A3 associated to s3*s2*s1 sage: AC = crystals.AffineFactorization([Core([4,1],4),Core([1],4)], 4, k=3) sage: AC is A True """ if k is not None: from sage.combinat.core import Core from sage.combinat.partition import Partition W = WeylGroup(['A', k, 1], prefix='s') if isinstance(w[0], Core): w = [w[0].to_bounded_partition(), w[1].to_bounded_partition()] else: w = [Partition(w[0]), Partition(w[1])] w0 = W.from_reduced_word(w[0].from_kbounded_to_reduced_word(k)) w1 = W.from_reduced_word(w[1].from_kbounded_to_reduced_word(k)) w = w0 * (w1.inverse()) return super(AffineFactorizationCrystal, cls).__classcall__(cls, w, n, x)
def __init__(self, core, parent): """ TESTS:: sage: C = Cores(4,3) sage: c = C([2,1]); c [2, 1] sage: type(c) <class 'sage.combinat.core.Cores_length_with_category.element_class'> sage: c.parent() 4-Cores of length 3 sage: TestSuite(c).run() sage: C = Cores(3,3) sage: C([2,1]) Traceback (most recent call last): ... ValueError: [2, 1] is not a 3-core """ k = parent.k part = Partition(core) if not part.is_core(k): raise ValueError, "%s is not a %s-core" % (part, k) CombinatorialObject.__init__(self, core) Element.__init__(self, parent)
def bidecompositions(p): r""" Iterator through the pair of partitions ``(q1,q2)`` such that the union of the parts of ``q1`` and ``q2`` equal ``p``. EXAMPLES:: sage: import surface_dynamics.interval_exchanges.rauzy_class_cardinality as rcc sage: list(rcc.bidecompositions(Partition([3,1]))) [([], [3, 1]), ([3], [1]), ([1], [3]), ([3, 1], [])] sage: list(rcc.bidecompositions(Partition([2,1,1]))) [([], [2, 1, 1]), ([2], [1, 1]), ([1], [2, 1]), ([2, 1], [1]), ([1, 1], [2]), ([2, 1, 1], [])] """ from itertools import product exp = p.to_exp() for i in product(*tuple(xrange(i + 1) for i in exp)): p1 = Partition(exp=i) p2 = Partition(exp=[exp[j] - i[j] for j in xrange(len(exp))]) yield p1, p2
def __init__(self, core, parent): """ TESTS:: sage: C = Cores(4,3) sage: c = C([2,1]); c [2, 1] sage: type(c) <class 'sage.combinat.core.Cores_length_with_category.element_class'> sage: c.parent() 4-Cores of length 3 sage: TestSuite(c).run() sage: C = Cores(3,3) sage: C([2,1]) Traceback (most recent call last): ... ValueError: [2, 1] is not a 3-core """ k = parent.k part = Partition(core) if not part.is_core(k): raise ValueError, "%s is not a %s-core"%(part, k) CombinatorialObject.__init__(self, core) Element.__init__(self, parent)
def coproduct_on_basis(self, la): """ Return the coproduct of the basis element indexed by ``la``. EXAMPLES:: sage: R = PolynomialRing(ZZ, 'q').fraction_field() sage: q = R.gen() sage: H = HallAlgebra(R, q) sage: H.coproduct_on_basis(Partition([1,1])) H[] # H[1, 1] + 1/q*H[1] # H[1] + H[1, 1] # H[] sage: H.coproduct_on_basis(Partition([2])) H[] # H[2] + ((q-1)/q)*H[1] # H[1] + H[2] # H[] sage: H.coproduct_on_basis(Partition([2,1])) H[] # H[2, 1] + ((q^2-1)/q^2)*H[1] # H[1, 1] + 1/q*H[1] # H[2] + ((q^2-1)/q^2)*H[1, 1] # H[1] + 1/q*H[2] # H[1] + H[2, 1] # H[] """ S = self.tensor_square() if all(x == 1 for x in la): n = len(la) return S.sum_of_terms([((Partition( [1] * r), Partition([1] * (n - r))), self._q**(-r * (n - r))) for r in range(n + 1)], distinct=True) I = HallAlgebraMonomials(self.base_ring(), self._q) la = self.monomial(la) return S(I(la).coproduct())
def _llt_generic(self, skp, stat): r""" Takes in partition, list of partitions, or a list of skew partitions as well as a function which takes in two partitions and a level and returns a coefficient. INPUT: - ``self`` -- a family of LLT symmetric functions bases - ``skp`` -- a partition or a list of partitions or a list of skew partitions - ``stat`` -- a function which accepts two partitions and a value for the level and returns a coefficient which is a polynomial in a parameter `t`. The first partition is the index of the LLT function, the second partition is the index of the monomial basis element. OUTPUT: - returns the monomial expansion of the LLT symmetric function indexed by ``skp`` EXAMPLES:: sage: L3 = SymmetricFunctions(FractionField(QQ['t'])).llt(3) sage: f = lambda skp,mu,level: QQ(1) sage: L3._llt_generic([3,2,1],f) m[1, 1] + m[2] sage: L3._llt_generic([[2,1],[1],[2]],f) m[1, 1, 1, 1, 1, 1] + m[2, 1, 1, 1, 1] + m[2, 2, 1, 1] + m[2, 2, 2] + m[3, 1, 1, 1] + m[3, 2, 1] + m[3, 3] + m[4, 1, 1] + m[4, 2] + m[5, 1] + m[6] sage: L3._llt_generic([[[2,2],[1]],[[2,1],[]]],f) m[1, 1, 1, 1] + m[2, 1, 1] + m[2, 2] + m[3, 1] + m[4] """ if skp in _Partitions: m = (sum(skp) / self.level()).floor() if m == 0: raise ValueError("level (%s=) must divide %s " % (sum(skp), self.level())) mu = Partitions( ZZ(sum(skp) / self.level()) ) elif isinstance(skp, list) and skp[0] in sage.combinat.skew_partition.SkewPartitions(): #skp is a list of skew partitions skp2 = [Partition(core=[], quotient=[skp[i][0] for i in range(len(skp))])] skp2 += [Partition(core=[], quotient=[skp[i][1] for i in range(len(skp))])] mu = Partitions(ZZ((skp2[0].size()-skp2[1].size()) / self.level())) skp = skp2 elif isinstance(skp, list) and skp[0] in _Partitions: #skp is a list of partitions skp = Partition(core=[], quotient=skp) mu = Partitions( ZZ(sum(skp) / self.level()) ) else: raise ValueError("LLT polynomials not defined for %s"%skp) BR = self.base_ring() return sum([ BR(stat(skp,nu,self.level()).subs(t=self.t))*self._m(nu) for nu in mu])
def __init__(self, *args, **kwds): r""" TESTS:: sage: from surface_dynamics.interval_exchanges.marked_partition import MarkedPartition sage: p = MarkedPartition([3,2,1], 1,(2,0)) sage: loads(dumps(p)) == p True """ from sage.combinat.partition import Partition if len(args) == 1 and isinstance(args[0], MarkedPartition): from copy import copy self.p = copy(args[0].p) self.m = copy(args[0].m) elif len(args) == 1 and isinstance(args[0], str): import re x = re.compile("(?P<mark>[^[]*)[^[]*\[(?P<parts>[^]]*)]") m = x.match(args[0]) self.m = Marking(m.groupdict()["mark"]) self.p = Partition( sorted(map(Integer, m.groupdict()["parts"].split(',')), reverse=True)) elif len(args) == 1 and isinstance(args[0], (list, tuple)) and len( args[0]) == 3: self.p = Partition(sorted(args[0][0], reverse=True)) self.m = Marking(args[0][1], args[0][2]) elif len(args) == 3: self.p = Partition(sorted(args[0], reverse=True)) self.m = Marking(args[1], args[2]) else: raise ValueError("can not build marked partition from given data") if kwds.get("check", True): if self.p == Partition([]): if self.m.t != 2 or self.m.data[0] != 0 or self.m.data[1] != 0: raise ValueError( "empty partition has only type 2 with data (0,0)") elif self.m.t == 1: if self.m.data[0] not in self.p: raise ValueError("%d is not an element of parts" % data[0]) else: if ((self.m.data[0] not in self.p or self.m.data[1] not in self.p) or (self.m.data[0] == self.m.data[1] and list(self.p).count(self.m.data[0]) < 2)): raise ValueError( "parts do not contains (m_l,m_r) = (%d,%d)" % (self.m.data[0], self.m.data[1]))
def affine_grassmannian_to_core(self): r""" Bijection between affine Grassmannian elements of type `A_k^{(1)}` and `(k+1)`-cores. INPUT: - ``self`` -- an affine Grassmannian element of some affine Weyl group of type `A_k^{(1)}` Recall that an element `w` of an affine Weyl group is affine Grassmannian if all its all reduced words end in 0, see :meth:`is_affine_grassmannian`. OUTPUT: - a `(k+1)`-core See also :meth:`affine_grassmannian_to_partition`. EXAMPLES:: sage: W = WeylGroup(['A',2,1]) sage: w = W.from_reduced_word([0,2,1,0]) sage: la = w.affine_grassmannian_to_core(); la [4, 2] sage: type(la) <class 'sage.combinat.core.Cores_length_with_category.element_class'> sage: la.to_grassmannian() == w True sage: w = W.from_reduced_word([0,2,1]) sage: w.affine_grassmannian_to_core() Traceback (most recent call last): ... ValueError: Error! this only works on type 'A' affine Grassmannian elements """ from sage.combinat.partition import Partition from sage.combinat.core import Core if not self.is_affine_grassmannian() or not self.parent( ).cartan_type().letter == 'A': raise ValueError( "Error! this only works on type 'A' affine Grassmannian elements" ) out = Partition([]) rword = self.reduced_word() kp1 = self.parent().n for i in range(len(rword)): for c in (x for x in out.outside_corners() if (x[1] - x[0]) % kp1 == rword[-i - 1]): out = out.add_cell(c[0], c[1]) return Core(out._list, kp1)
def _g_to_kh_on_basis(self,la): r""" Returns the expansion of the K-`k`-Schur function in the homogeneous basis. See method _DualGrothendieck for the code. INPUT: - ``la`` -- A `k`-bounded partition. OUTPUT: - A symmetric function in the homogeneous basis. EXAMPLES:: sage: g = SymmetricFunctions(QQ).kBoundedSubspace(3,1).K_kschur() sage: g._g_to_kh_on_basis([2,1]) h[2] + h[2, 1] - h[3] sage: g._g_to_kh_on_basis([]) h[] sage: g._g_to_kh_on_basis([4,1]) Traceback (most recent call last): ValueError: Partition should be 3-bounded """ if la != [] and (la[0] > self.k): raise ValueError, "Partition should be %d-bounded"%self.k return self._DualGrothendieck(Partition(la))
def module_generator(self, shape): """ This yields the module generator (or highest weight element) of a classical crystal of given shape. The module generator is the unique tableau with equal shape and content. EXAMPLES:: sage: T = crystals.Tableaux(['D',3], shape = [1,1]) sage: T.module_generator([1,1]) [[1], [2]] sage: T = crystals.Tableaux(['D',4],shape=[2,2,2,-2]) sage: T.module_generator(tuple([2,2,2,-2])) [[1, 1], [2, 2], [3, 3], [-4, -4]] sage: T.cardinality() 294 sage: T = crystals.Tableaux(['D',4],shape=[2,2,2,2]) sage: T.module_generator(tuple([2,2,2,2])) [[1, 1], [2, 2], [3, 3], [4, 4]] sage: T.cardinality() 294 """ type = self.cartan_type() if type[0] == 'D' and len(shape) == type[1] and shape[type[1]-1] < 0: invert = True shape = shape[:-1] + (-shape[type[1]-1],) else: invert = False p = Partition(shape).conjugate() # The column canonical tableau, read by columns module_generator = flatten([[val-i for i in range(val)] for val in p]) if invert: module_generator = [(-x if x == type[1] else x) for x in module_generator] return self(list=[self.letters(x) for x in module_generator])
def _comp_to_par(self, comp): """ Return the partition if the composition is actually a partition. Otherwise returns nothing. INPUT: - ``comp`` -- a composition OUTPUT: - ``comp`` as a partition, if it is sorted; otherwise returns ``None`` (nothing). EXAMPLES:: sage: NCSF=NonCommutativeSymmetricFunctions(QQ) sage: L = NCSF.elementary() sage: L._comp_to_par(Composition([1,1,3,1,2])) sage: L.sum_of_partition_rearrangements(Composition([])) L[] sage: L._comp_to_par(Composition([3,2,1,1])) [3, 2, 1, 1] """ try: return Partition(comp) except ValueError: return None
def _element_constructor_(self, list, **options): """ Construct a :class:`KirillovReshetikhinTableauxElement`. EXAMPLES:: sage: KRT = KirillovReshetikhinTableaux(['A', 4, 1], 2, 1) sage: KRT([3, 4]) # indirect doctest [[4], [3]] sage: KRT([4, 3]) [[3], [4]] """ if "shape" in options: return self._fill(Partition(options["shape"]).conjugate()) if isinstance(list, KirillovReshetikhinGenericCrystalElement): # Check to make sure it can be converted if list.cartan_type() != self.cartan_type().affine() \ or list.parent().r() != self._r or list.parent().s() != self._s: raise ValueError( "The Kirillov-Reshetikhin crystal must have the same Cartan type and shape" ) # To build a KR tableau from a KR crystal: # 1 - start with a highest weight KR tableau generated from the # shape of the KR crystal # 2 - determine a path from the KR crystal to its highest weight # 3 - apply the inverse path to the highest weight KR tableau lifted = list.lift() shape = lifted.to_tableau().shape().conjugate() f_str = reversed(lifted.to_highest_weight()[1]) return self._fill(shape).f_string(f_str) return KirillovReshetikhinTableaux._element_constructor_( self, list, **options)
def graph_implementation_rec(skp, weight, length, function): """ TESTS:: sage: from sage.combinat.ribbon_tableau import graph_implementation_rec, list_rec sage: graph_implementation_rec(SkewPartition([[1], []]), [1], 1, list_rec) [[[], [[1]]]] sage: graph_implementation_rec(SkewPartition([[2, 1], []]), [1, 2], 1, list_rec) [[[], [[2], [1, 2]]]] sage: graph_implementation_rec(SkewPartition([[], []]), [0], 1, list_rec) [[[], []]] """ if sum(weight) == 0: weight = [] partp = skp[0].conjugate() ell = len(partp) outer = skp[1] outer_len = len(outer) # Some tests in order to know if the shape and the weight are compatible. if weight and weight[-1] <= len(partp): perms = permutation.Permutations([0] * (len(partp) - weight[-1]) + [length] * (weight[-1])).list() else: return function([], [], skp, weight, length) selection = [] for j in range(len(perms)): retire = [(val + ell - (i + 1) - perms[j][i]) for i, val in enumerate(partp)] retire.sort(reverse=True) retire = [val - ell + (i + 1) for i, val in enumerate(retire)] if retire[-1] >= 0 and retire == sorted(retire, reverse=True): retire = Partition(retire).conjugate() # Cutting branches if the retired partition has a line strictly included into the inner one if len(retire) >= outer_len: append = True for k in range(outer_len): if retire[k] - outer[k] < 0: append = False break if append: selection.append([retire, perms[j]]) #selection contains the list of current nodes if len(weight) == 1: return function([], selection, skp, weight, length) else: #The recursive calls permit us to construct the list of the sons #of all current nodes in selection a = [ graph_implementation_rec([p[0], outer], weight[:-1], length, function) for p in selection ] return function(a, selection, skp, weight, length)
def __classcall_private__(cls, s, part=None): """ Normalize input to ensure a unique representation. EXAMPLES:: sage: S = SetPartitions(4) sage: T = SetPartitions([1,2,3,4]) sage: S is T True """ if isinstance(s, (int, Integer)): s = frozenset(range(1, s + 1)) else: try: if s.cardinality() == infinity: raise ValueError("The set must be finite") except AttributeError: pass s = frozenset(s) if part is not None: if isinstance(part, (int, Integer)): if len(s) < part: raise ValueError("part must be <= len(set)") else: return SetPartitions_setn(s, part) else: if part not in Partitions(len(s)): raise ValueError("part must be a partition of %s" % len(s)) else: return SetPartitions_setparts(s, Partition(part)) else: return SetPartitions_set(s)
def syt_promotion(lam): r""" Return the invertible finite discrete dynamical system consisting of all standard tableaux of shape ``lam`` (a given partition) and evolving according to promotion. EXAMPLES:: sage: F = finite_dynamical_systems.syt_promotion([4, 4, 4]) sage: sorted(F.orbit_lengths()) [3, 3, 4, 4, 4, 6, 6, 6, 6, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12] sage: G = finite_dynamical_systems.syt_promotion([4, 3, 1]) sage: sorted(G.orbit_lengths()) [16, 22, 32] sage: G.verify_inverse_evolution() True """ from sage.combinat.partition import Partition lam = Partition(lam) from sage.combinat.tableau import StandardTableaux X = StandardTableaux(lam) return InvertibleFiniteDynamicalSystem( X, lambda T: T.promotion(), inverse=lambda T: T.promotion_inverse())
def _stretch_gen(self, k, ao): """ EXAMPLES:: sage: from sage.combinat.species.generating_series import CycleIndexSeriesRing sage: p = SFAPower(QQ) sage: CIS = CycleIndexSeriesRing(p) sage: f = CIS([p([1])]) sage: g = f._stretch_gen(2,0) sage: [g.next() for i in range(10)] [p[2], 0, p[2], 0, p[2], 0, p[2], 0, p[2], 0] """ from sage.combinat.partition import Partition BR = self.base_ring() zero = BR(0) stretch_k = lambda p: Partition([k * i for i in p]) yield self.coefficient(0).map_support(stretch_k) n = 1 while True: for i in range(k - 1): yield BR(0) yield self.coefficient(n).map_support(stretch_k) n += 1
def module_generator(self, shape): """ This yields the module generator (or highest weight element) of a classical crystal of given shape. The module generator is the unique tableau with equal shape and content. EXAMPLE:: sage: T = CrystalOfTableaux(['D',3], shape = [1,1]) sage: T.module_generator([1,1]) [[1], [2]] """ type = self.cartan_type() if type[0] == 'D' and len(shape) == type[1] and shape[type[1]-1] < 0: invert = True shape = shape[:-1]+(-shape[type[1]-1],) else: invert = False p = Partition(shape).conjugate() # The column canonical tableau, read by columns module_generator = flatten([[p[j]-i for i in range(p[j])] for j in range(len(p))]) if invert: for i in range(type[1]): if module_generator[i] == type[1]: module_generator[i] = -type[1] return self(list=[self.letters(x) for x in module_generator])
def split(p, k, i=0): r""" Splits the i-th term of p into two parts of size k and n-k-1 There is a symmetry split(p, k, i) = split(p, p[i]-k-1, i) INPUT: - ``p`` - a partition - ``k`` - an integer between 2 and p[i] - ``i`` - integer - the index of the element to split OUTPUT: a partition EXAMPLES:: sage: import surface_dynamics.interval_exchanges.rauzy_class_cardinality as rcc sage: p = Partition([5,1]) sage: rcc.split(p,1,0) [3, 1, 1] sage: rcc.split(p,2,0) [2, 2, 1] sage: rcc.split(p,3,0) [3, 1, 1] """ l = list(p) n = l.pop(i) l.append(n - k - 1) l.append(k) l.sort(reverse=True) return Partition(l)
def _cycle_type(self, s): """ EXAMPLES:: sage: cis = species.PartitionSpecies().cycle_index_series() sage: [cis._cycle_type(p) for p in Partitions(3)] [[3, 1, 1], [2, 1, 1, 1], [1, 1, 1, 1, 1]] sage: cis = species.PermutationSpecies().cycle_index_series() sage: [cis._cycle_type(p) for p in Partitions(3)] [[3, 1, 1, 1], [2, 2, 1, 1], [1, 1, 1, 1, 1, 1]] sage: cis = species.SetSpecies().cycle_index_series() sage: [cis._cycle_type(p) for p in Partitions(3)] [[1], [1], [1]] """ if s == []: return self._card(0) res = [] for k in range(1, self._upper_bound_for_longest_cycle(s) + 1): e = 0 for d in divisors(k): m = moebius(d) if m == 0: continue u = s.power(k / d) e += m * self.count(u) res.extend([k] * int(e / k)) res.reverse() return Partition(res)
def loop_type(self, other=None): r""" Return the loop type of ``self``. INPUT: - ``other`` -- a perfect matching of the same set of ``self``. (if the second argument is empty, the method :meth:`an_element` is called on the parent of the first) OUTPUT: If we draw the two perfect matchings simultaneously as edges of a graph, the graph obtained is a union of cycles of even lengths. The function returns the ordered list of the semi-length of these cycles (considered as a partition) EXAMPLES:: sage: m = PerfectMatching([('a','e'),('b','c'),('d','f')]) sage: n = PerfectMatching([('a','b'),('d','f'),('e','c')]) sage: m.loop_type(n) [2, 1] TESTS:: sage: m = PerfectMatching([]); m.loop_type() [] """ return Partition( sorted((len(l) // 2 for l in self.loops_iterator(other)), reverse=True))
def _stretch_gen(self, k, ao): """ EXAMPLES:: sage: from sage.combinat.species.generating_series import CycleIndexSeriesRing sage: p = SymmetricFunctions(QQ).power() sage: CIS = CycleIndexSeriesRing(QQ) sage: f = CIS([p([1])]) # This is the power series whose all coefficients ....: # are p[1]. Not combinatorially meaningful! sage: g = f._stretch_gen(2,0) sage: [next(g) for i in range(10)] [p[2], 0, p[2], 0, p[2], 0, p[2], 0, p[2], 0] """ from sage.combinat.partition import Partition BR = self.base_ring() zero = BR.zero() stretch_k = lambda p: Partition([k * i for i in p]) yield self.coefficient(0).map_support(stretch_k) n = 1 while True: for i in range(k - 1): yield zero yield self.coefficient(n).map_support(stretch_k) n += 1
def G_quotient(self, r, b=-1, label_swap_xy=False): r""" Calculates the G-quotient of `self` with respect to the $(r,b)$-action, First, the G-abacus of `self` is computed. Second, the path sequence of each abacus wire is converted back to a partition. Note that the abacus uses the convention {1:N, 0:E} in English notation for partition border paths Due to differences in conventions effectively swapping xy-coordinates for cell colouring (vs. content), the order of partitions in the $(r,r-1)$-quotient ($b=-1$ special case) differs from the classical $r$-quotient by a reflection of indices. This can be accounted for by setting the optional `label_swap_xy` keyword argument to `True`. A cell $(i,j)$ is mapped as `Partition.content` -> $j - i$, whilst $(r,-1)$-colour -> $i - j (mod r)$ (as case of $(r,b)$-colour -> $i + bj (mod r)$). Returns: an $r$-tuple of partitions """ # Sagemath uses the convention {1:E, 0:N} when reading partition from a path sequence, so we have to swap '0's and '1's p_list = [ Partition(zero_one=invert_zero_one(wire)) for wire in self.G_abacus(r, b) ] # Reflect the order of partitions in the $b=-1$ case `G_quotient` to account for differences in conventions for cell colouring # for compatibility with `Partition.quotient`. if label_swap_xy: p_list = [p_list[0]] + p_list[:0:-1] # Cast the list of partitions in the quotient as a `PartitionTuple` for compatibility with the `Partition.quotient` method return PartitionTuple(p_list)
def graph_implementation_rec(skp, weight, length, function): """ TESTS:: sage: from sage.combinat.ribbon_tableau import graph_implementation_rec, list_rec sage: graph_implementation_rec(SkewPartition([[1], []]), [1], 1, list_rec) [[[], [[1]]]] sage: graph_implementation_rec(SkewPartition([[2, 1], []]), [1, 2], 1, list_rec) [[[], [[2], [1, 2]]]] sage: graph_implementation_rec(SkewPartition([[], []]), [0], 1, list_rec) [[[], []]] """ if sum(weight) == 0: weight = [] partp = skp[0].conjugate() ## Some tests in order to know if the shape and the weight are compatible. if weight != [] and weight[-1] <= len(partp): perms = permutation.Permutations([0] * (len(partp) - weight[-1]) + [length] * (weight[-1])).list() else: return function([], [], skp, weight, length) selection = [] for j in range(len(perms)): retire = [(partp[i] + len(partp) - (i + 1) - perms[j][i]) for i in range(len(partp))] retire.sort(reverse=True) retire = [retire[i] - len(partp) + (i + 1) for i in range(len(retire))] if retire[-1] >= 0 and retire == [i for i in reversed(sorted(retire))]: retire = Partition([x for x in retire if x != 0]).conjugate() # Cutting branches if the retired partition has a line strictly included into the inner one append = True padded_retire = retire + [0] * (len(skp[1]) - len(retire)) for k in range(len(skp[1])): if padded_retire[k] - skp[1][k] < 0: append = False break if append: selection.append([retire, perms[j]]) #selection contains the list of current nodes #print "selection", selection if len(weight) == 1: return function([], selection, skp, weight, length) else: #The recursive calls permit us to construct the list of the sons #of all current nodes in selection a = [ graph_implementation_rec([p[0], skp[1]], weight[:-1], length, function) for p in selection ] #print "a", a return function(a, selection, skp, weight, length)
def affine_grassmannian_to_core(self): r""" Bijection between affine Grassmannian elements of type `A_k^{(1)}` and `(k+1)`-cores. INPUT: - ``self`` -- an affine Grassmannian element of some affine Weyl group of type `A_k^{(1)}` Recall that an element `w` of an affine Weyl group is affine Grassmannian if all its all reduced words end in 0, see :meth:`is_affine_grassmannian`. OUTPUT: - a `(k+1)`-core See also :meth:`affine_grassmannian_to_partition`. EXAMPLES:: sage: W = WeylGroup(['A',2,1]) sage: w = W.from_reduced_word([0,2,1,0]) sage: la = w.affine_grassmannian_to_core(); la [4, 2] sage: type(la) <class 'sage.combinat.core.Cores_length_with_category.element_class'> sage: la.to_grassmannian() == w True sage: w = W.from_reduced_word([0,2,1]) sage: w.affine_grassmannian_to_core() Traceback (most recent call last): ... ValueError: Error! this only works on type 'A' affine Grassmannian elements """ from sage.combinat.partition import Partition from sage.combinat.core import Core if not self.is_affine_grassmannian() or not self.parent().cartan_type().letter == "A": raise ValueError("Error! this only works on type 'A' affine Grassmannian elements") out = Partition([]) rword = self.reduced_word() kp1 = self.parent().n for i in range(len(rword)): for c in (x for x in out.outside_corners() if (x[1] - x[0]) % kp1 == rword[-i - 1]): out = out.add_cell(c[0], c[1]) return Core(out._list, kp1)
def Podium(data): r""" If ``data`` is an integer than the standard podium with ``data`` steps is returned. Otherwise, ``data`` should be a weakly decreasing list of integers (i.e. a integer partition). EXAMPLES:: sage: from surface_dynamics.all import * sage: o = origamis.Podium([3,3,2,1]) sage: o Podium origami with partition [3, 3, 2, 1] sage: print o (1,2,3)(4,5,6)(7,8)(9) (1,4,7,9)(2,5,8)(3,6) """ from sage.combinat.partition import Partition if isinstance(data, (int,Integer)): p = Partition([i for i in xrange(data,0,-1)]) else: p = Partition(data) p = Partition(data) q = p.conjugate() r=[] positions = [] i = 0 for j,jj in enumerate(p): r.extend(xrange(i+1,i+jj)) r.append(i) i += jj positions.extend((k,j) for k in xrange(jj)) u = [None]*sum(p) for j in xrange(len(q)): k = j for jj in xrange(q[j]-1): u[k] = k+p[jj] k += p[jj] u[k] = j return Origami(r,u,positions=positions,name="Podium origami with partition %s" %str(p),as_tuple=True)
def contained_partitions(l): """ Nested function returning those partitions contained in the partition `l` """ if l == Partition([]): return l return flatten( [l, [contained_partitions(m) for m in lower_covers(l)]])
def arith_prod_of_partitions(l1, l2): # Given two partitions `l_1` and `l_2`, we construct a new partition `l_1 \\boxtimes l_2` by # the following procedure: each pair of parts `a \\in l_1` and `b \\in l_2` contributes # `\\gcd (a, b)`` parts of size `\\lcm (a, b)` to `l_1 \\boxtimes l_2`. If `l_1` and `l_2` # are partitions of integers `n` and `m`, respectively, then `l_1 \\boxtimes l_2` is a # partition of `nm`. term_iterable = chain.from_iterable(repeat(lcm(pair), gcd(pair)) for pair in product(l1, l2)) return Partition(sorted(term_iterable, reverse=True))
def __classcall_private__(cls, shape, weight): r""" Straighten arguments before unique representation. TESTS:: sage: LR = LittlewoodRichardsonTableaux([3,2,1],[[2,1],[2,1]]) sage: TestSuite(LR).run() sage: LittlewoodRichardsonTableaux([3,2,1],[[2,1]]) Traceback (most recent call last): ... ValueError: the sizes of shapes and sequence of weights do not match """ shape = Partition(shape) weight = tuple(Partition(a) for a in weight) if shape.size() != sum(a.size() for a in weight): raise ValueError("the sizes of shapes and sequence of weights do not match") return super(LittlewoodRichardsonTableaux, cls).__classcall__(cls, shape, weight)
def __classcall_private__(cls, deg, par): r""" Create a primary similarity class type. EXAMPLES:: sage: PrimarySimilarityClassType(2, [3, 2, 1]) [2, [3, 2, 1]] The parent class is the class of primary similarity class types of order `d|\lambda\`:: sage: PT = PrimarySimilarityClassType(2, [3, 2, 1]) sage: PT.parent().size() 12 """ par = Partition(par) P = PrimarySimilarityClassTypes(par.size() * deg) return P(deg, par)
def count(self, t): """ Return the number of structures corresponding to a certain cycle type ``t``. EXAMPLES:: sage: from sage.combinat.species.generating_series import CycleIndexSeriesRing sage: p = SymmetricFunctions(QQ).power() sage: CIS = CycleIndexSeriesRing(QQ) sage: f = CIS([0, p([1]), 2*p([1,1]), 3*p([2,1])]) sage: f.count([1]) 1 sage: f.count([1,1]) 4 sage: f.count([2,1]) 6 """ t = Partition(t) return t.aut() * self.coefficient_cycle_type(t)
def coefficient_cycle_type(self, t): """ Returns the coefficient of a cycle type ``t`` in ``self``. EXAMPLES:: sage: from sage.combinat.species.generating_series import CycleIndexSeriesRing sage: p = SymmetricFunctions(QQ).power() sage: CIS = CycleIndexSeriesRing(QQ) sage: f = CIS([0, p([1]), 2*p([1,1]),3*p([2,1])]) sage: f.coefficient_cycle_type([1]) 1 sage: f.coefficient_cycle_type([1,1]) 2 sage: f.coefficient_cycle_type([2,1]) 3 """ t = Partition(t) p = self.coefficient(t.size()) return p.coefficient(t)
def marking_iterator(profile,left=None,standard=False): r""" Returns the marked profile associated to a partition EXAMPLES:: sage: import surface_dynamics.interval_exchanges.rauzy_class_cardinality as rcc sage: p = Partition([3,2,2]) sage: list(rcc.marking_iterator(p)) [(1, 2, 0), (1, 2, 1), (1, 3, 0), (1, 3, 1), (1, 3, 2), (2, 2, 2), (2, 2, 3), (2, 3, 2)] """ e = Partition(sorted(profile,reverse=True)).to_exp_dict() if left is not None: assert(left in e) if left is not None: keys = [left] else: keys = e.keys() for m in keys: if standard: angles = range(1,m-1) else: angles = range(0,m) for a in angles: yield (1,m,a) for m_l in keys: for m_r in e: if m_l != m_r or e[m_l] > 1: yield (2,m_l,m_r)
def hall_polynomial(nu, mu, la, q=None): r""" Return the (classical) Hall polynomial `P^{\nu}_{\mu,\lambda}` (where `\nu`, `\mu` and `\lambda` are the inputs ``nu``, ``mu`` and ``la``). Let `\nu,\mu,\lambda` be partitions. The Hall polynomial `P^{\nu}_{\mu,\lambda}(q)` (in the indeterminate `q`) is defined as follows: Specialize `q` to a prime power, and consider the category of `\GF{q}`-vector spaces with a distinguished nilpotent endomorphism. The morphisms in this category shall be the linear maps commuting with the distinguished endomorphisms. The *type* of an object in the category will be the Jordan type of the distinguished endomorphism; this is a partition. Now, if `N` is any fixed object of type `\nu` in this category, then the polynomial `P^{\nu}_{\mu,\lambda}(q)` specialized at the prime power `q` counts the number of subobjects `L` of `N` having type `\lambda` such that the quotient object `N / L` has type `\mu`. This determines the values of the polynomial `P^{\nu}_{\mu,\lambda}` at infinitely many points (namely, at all prime powers), and hence `P^{\nu}_{\mu,\lambda}` is uniquely determined. The degree of this polynomial is at most `n(\nu) - n(\lambda) - n(\mu)`, where `n(\kappa) = \sum_i (i-1) \kappa_i` for every partition `\kappa`. (If this is negative, then the polynomial is zero.) These are the structure coefficients of the :class:`(classical) Hall algebra <HallAlgebra>`. If `\lvert \nu \rvert \neq \lvert \mu \rvert + \lvert \lambda \rvert`, then we have `P^{\nu}_{\mu,\lambda} = 0`. More generally, if the Littlewood-Richardson coefficient `c^{\nu}_{\mu,\lambda}` vanishes, then `P^{\nu}_{\mu,\lambda} = 0`. The Hall polynomials satisfy the symmetry property `P^{\nu}_{\mu,\lambda} = P^{\nu}_{\lambda,\mu}`. ALGORITHM: If `\lambda = (1^r)` and `\lvert \nu \rvert = \lvert \mu \rvert + \lvert \lambda \rvert`, then we compute `P^{\nu}_{\mu,\lambda}` as follows (cf. Example 2.4 in [Schiffmann]_): First, write `\nu = (1^{l_1}, 2^{l_2}, \ldots, n^{l_n})`, and define a sequence `r = r_0 \geq r_1 \geq \cdots \geq r_n` such that .. MATH:: \mu = \left( 1^{l_1 - r_0 + 2r_1 - r_2}, 2^{l_2 - r_1 + 2r_2 - r_3}, \ldots , (n-1)^{l_{n-1} - r_{n-2} + 2r_{n-1} - r_n}, n^{l_n - r_{n-1} + r_n} \right). Thus if `\mu = (1^{m_1}, \ldots, n^{m_n})`, we have the following system of equations: .. MATH:: \begin{aligned} m_1 & = l_1 - r + 2r_1 - r_2, \\ m_2 & = l_2 - r_1 + 2r_2 - r_3, \\ & \vdots , \\ m_{n-1} & = l_{n-1} - r_{n-2} + 2r_{n-1} - r_n, \\ m_n & = l_n - r_{n-1} + r_n \end{aligned} and solving for `r_i` and back substituting we obtain the equations: .. MATH:: \begin{aligned} r_n & = r_{n-1} + m_n - l_n, \\ r_{n-1} & = r_{n-2} + m_{n-1} - l_{n-1} + m_n - l_n, \\ & \vdots , \\ r_1 & = r + \sum_{k=1}^n (m_k - l_k), \end{aligned} or in general we have the recursive equation: .. MATH:: r_i = r_{i-1} + \sum_{k=i}^n (m_k - l_k). This, combined with the condition that `r_0 = r`, determines the `r_i` uniquely (recursively). Next we define .. MATH:: t = (r_{n-2} - r_{n-1})(l_n - r_{n-1}) + (r_{n-3} - r_{n-2})(l_{n-1} + l_n - r_{n-2}) + \cdots + (r_0 - r_1)(l_2 + \cdots + l_n - r_1), and with these notations we have .. MATH:: P^{\nu}_{\mu,(1^r)} = q^t \binom{l_n}{r_{n-1}}_q \binom{l_{n-1}}{r_{n-2} - r_{n-1}}_q \cdots \binom{l_1}{r_0 - r_1}_q. To compute `P^{\nu}_{\mu,\lambda}` in general, we compute the product `I_{\mu} I_{\lambda}` in the Hall algebra and return the coefficient of `I_{\nu}`. EXAMPLES:: sage: from sage.combinat.hall_polynomial import hall_polynomial sage: hall_polynomial([1,1],[1],[1]) q + 1 sage: hall_polynomial([2],[1],[1]) 1 sage: hall_polynomial([2,1],[2],[1]) q sage: hall_polynomial([2,2,1],[2,1],[1,1]) q^2 + q sage: hall_polynomial([2,2,2,1],[2,2,1],[1,1]) q^4 + q^3 + q^2 sage: hall_polynomial([3,2,2,1], [3,2], [2,1]) q^6 + q^5 sage: hall_polynomial([4,2,1,1], [3,1,1], [2,1]) 2*q^3 + q^2 - q - 1 sage: hall_polynomial([4,2], [2,1], [2,1], 0) 1 """ if q is None: q = ZZ['q'].gen() R = q.parent() # Make sure they are partitions nu = Partition(nu) mu = Partition(mu) la = Partition(la) if sum(nu) != sum(mu) + sum(la): return R.zero() if all(x == 1 for x in la): r = [len(la)] # r will be [r_0, r_1, ..., r_n]. exp_nu = nu.to_exp() # exp_nu == [l_1, l_2, ..., l_n]. exp_mu = mu.to_exp() # exp_mu == [m_1, m_2, ..., m_n]. n = max(len(exp_nu), len(exp_mu)) for k in range(n): r.append(r[-1] + sum(exp_mu[k:]) - sum(exp_nu[k:])) # Now, r is [r_0, r_1, ..., r_n]. exp_nu += [0]*(n - len(exp_nu)) # Pad with 0's until it has length n # Note that all -1 for exp_nu is due to indexing t = sum((r[k-2] - r[k-1])*(sum(exp_nu[k-1:]) - r[k-1]) for k in range(2,n+1)) if t < 0: # This case needs short-circuiting, since otherwise q**-t # might throw an exception if q is non-invertible. return R.zero() return q**t * q_binomial(exp_nu[n-1], r[n-1], q) \ * prod([q_binomial(exp_nu[k-1], r[k-1] - r[k], q) for k in range(1, n)], R.one()) from sage.algebras.hall_algebra import HallAlgebra H = HallAlgebra(R, q) return (H[mu]*H[la]).coefficient(nu)
def q_subgroups_of_abelian_group(la, mu, q=None, algorithm='birkhoff'): r""" Return the `q`-number of subgroups of type ``mu`` in a finite abelian group of type ``la``. INPUT: - ``la`` -- type of the ambient group as a :class:`Partition` - ``mu`` -- type of the subgroup as a :class:`Partition` - ``q`` -- (default: ``None``) an indeterminat or a prime number; if ``None``, this defaults to `q \in \ZZ[q]` - ``algorithm`` -- (default: ``'birkhoff'``) the algorithm to use can be one of the following: - ``'birkhoff`` -- use the Birkhoff formula from [Bu87]_ - ``'delsarte'`` -- use the formula from [Delsarte48]_ OUTPUT: The number of subgroups of type ``mu`` in a group of type ``la`` as a polynomial in ``q``. ALGORITHM: Let `q` be a prime number and `\lambda = (\lambda_1, \ldots, \lambda_l)` be a partition. A finite abelian `q`-group is of type `\lambda` if it is isomorphic to .. MATH:: \ZZ / q^{\lambda_1} \ZZ \times \cdots \times \ZZ / q^{\lambda_l} \ZZ. The formula from [Bu87]_ works as follows: Let `\lambda` and `\mu` be partitions. Let `\lambda^{\prime}` and `\mu^{\prime}` denote the conjugate partitions to `\lambda` and `\mu`, respectively. The number of subgroups of type `\mu` in a group of type `\lambda` is given by .. MATH:: \prod_{i=1}^{\mu_1} q^{\mu^{\prime}_{i+1} (\lambda^{\prime}_i - \mu^{\prime}_i)} \binom{\lambda^{\prime}_i - \mu^{\prime}_{i+1}} {\mu^{\prime}_i - \mu^{\prime}_{i+1}}_q The formula from [Delsarte48]_ works as follows: Let `\lambda` and `\mu` be partitions. Let `(s_1, s_2, \ldots, s_l)` and `(r_1, r_2, \ldots, r_k)` denote the parts of the partitions conjugate to `\lambda` and `\mu` respectively. Let .. MATH:: \mathfrak{F}(\xi_1, \ldots, \xi_k) = \xi_1^{r_2} \xi_2^{r_3} \cdots \xi_{k-1}^{r_k} \prod_{i_1=r_2}^{r_1-1} (\xi_1-q^{i_1}) \prod_{i_2=r_3}^{r_2-1} (\xi_2-q^{i_2}) \cdots \prod_{i_k=0}^{r_k-1} (\xi_k-q^{-i_k}). Then the number of subgroups of type `\mu` in a group of type `\lambda` is given by .. MATH:: \frac{\mathfrak{F}(q^{s_1}, q^{s_2}, \ldots, q^{s_k})}{\mathfrak{F} (q^{r_1}, q^{r_2}, \ldots, q^{r_k})}. EXAMPLES:: sage: from sage.combinat.q_analogues import q_subgroups_of_abelian_group sage: q_subgroups_of_abelian_group([1,1], [1]) q + 1 sage: q_subgroups_of_abelian_group([3,3,2,1], [2,1]) q^6 + 2*q^5 + 3*q^4 + 2*q^3 + q^2 sage: R.<t> = QQ[] sage: q_subgroups_of_abelian_group([5,3,1], [3,1], t) t^4 + 2*t^3 + t^2 sage: q_subgroups_of_abelian_group([5,3,1], [3,1], 3) 144 sage: q_subgroups_of_abelian_group([1,1,1], [1]) == q_subgroups_of_abelian_group([1,1,1], [1,1]) True sage: q_subgroups_of_abelian_group([5], [3]) 1 sage: q_subgroups_of_abelian_group([1], [2]) 0 sage: q_subgroups_of_abelian_group([2], [1,1]) 0 TESTS: Check the same examples with ``algorithm='delsarte'``:: sage: q_subgroups_of_abelian_group([1,1], [1], algorithm='delsarte') q + 1 sage: q_subgroups_of_abelian_group([3,3,2,1], [2,1], algorithm='delsarte') q^6 + 2*q^5 + 3*q^4 + 2*q^3 + q^2 sage: q_subgroups_of_abelian_group([5,3,1], [3,1], t, algorithm='delsarte') t^4 + 2*t^3 + t^2 sage: q_subgroups_of_abelian_group([5,3,1], [3,1], 3, algorithm='delsarte') 144 sage: q_subgroups_of_abelian_group([1,1,1], [1], algorithm='delsarte') == q_subgroups_of_abelian_group([1,1,1], [1,1]) True sage: q_subgroups_of_abelian_group([5], [3], algorithm='delsarte') 1 sage: q_subgroups_of_abelian_group([1], [2], algorithm='delsarte') 0 sage: q_subgroups_of_abelian_group([2], [1,1], algorithm='delsarte') 0 REFERENCES: .. [Bu87] Butler, Lynne M. *A unimodality result in the enumeration of subgroups of a finite abelian group.* Proceedings of the American Mathematical Society 101, no. 4 (1987): 771-775. :doi:`10.1090/S0002-9939-1987-0911049-8` .. [Delsarte48] \S. Delsarte, *Fonctions de Möbius Sur Les Groupes Abeliens Finis*, Annals of Mathematics, second series, Vol. 45, No. 3, (Jul 1948), pp. 600-609. http://www.jstor.org/stable/1969047 AUTHORS: - Amritanshu Prasad (2013-06-07): Implemented the Delsarte algorithm - Tomer Bauer (2013-09-26): Implemented the Birkhoff algorithm """ if q is None: q = ZZ['q'].gens()[0] la_c = Partition(la).conjugate() mu_c = Partition(mu).conjugate() k = mu_c.length() if not la_c.contains(mu_c): return q.parent().zero() if algorithm == 'delsarte': def F(args): prd = lambda j: prod(args[j]-q**i for i in range(mu_c[j+1],mu_c[j])) F1 = prod(args[i]**mu_c[i+1] * prd(i) for i in range(k-1)) return F1 * prod(args[k-1]-q**i for i in range(mu_c[k-1])) return F([q**ss for ss in la_c[:k]])/F([q**rr for rr in mu_c]) if algorithm == 'birkhoff': fac1 = q**(sum(mu_c[i+1] * (la_c[i]-mu_c[i]) for i in range(k-1))) fac2 = prod(q_binomial(la_c[i]-mu_c[i+1], mu_c[i]-mu_c[i+1], q=q) for i in range(k-1)) fac3 = q_binomial(la_c[k-1], mu_c[k-1], q=q) return prod([fac1, fac2, fac3]) raise ValueError("invalid algorithm choice")
class SymmetricGroupRepresentation_generic_class(SageObject): r""" Generic methods for a representation of the symmetric group. """ _default_ring = None def __init__(self, partition, ring=None, cache_matrices=True): r""" An irreducible representation of the symmetric group corresponding to ``partition``. For more information, see the documentation for :func:`SymmetricGroupRepresentation`. EXAMPLES:: sage: spc = SymmetricGroupRepresentation([3]) sage: spc([3,2,1]) [1] sage: spc == loads(dumps(spc)) True sage: spc = SymmetricGroupRepresentation([3], cache_matrices=False) sage: spc([3,2,1]) [1] sage: spc == loads(dumps(spc)) True """ self._partition = Partition(partition) self._ring = ring if not ring is None else self._default_ring if cache_matrices is False: self.representation_matrix = self._representation_matrix_uncached def __eq__(self, other): r""" Test for equality. EXAMPLES:: sage: spc1 = SymmetricGroupRepresentation([3], cache_matrices=True) sage: spc1([3,1,2]) [1] sage: spc2 = loads(dumps(spc1)) sage: spc1 == spc2 True :: sage: spc3 = SymmetricGroupRepresentation([3], cache_matrices=False) sage: spc3([3,1,2]) [1] sage: spc4 = loads(dumps(spc3)) sage: spc3 == spc4 True TESTS: The following tests against some bug that was fixed in :trac:`8611`:: sage: spc = SymmetricGroupRepresentation([3]) sage: spc.important_info = 'Sage rules' sage: spc == SymmetricGroupRepresentation([3]) True """ if not isinstance(other, type(other)): return False return (self._ring,self._partition)==(other._ring,other._partition) # # both self and other must have caching enabled # if 'representation_matrix' in self.__dict__: # if 'representation_matrix' not in other.__dict__: # return False # else: # for key in self.__dict__: # if key != 'representation_matrix': # if self.__dict__[key] != other.__dict__[key]: # return False # else: # return True # else: # if 'representation_matrix' in other.__dict__: # return False # else: # return self.__dict__.__eq__(other.__dict__) def __call__(self, permutation): r""" Return the image of ``permutation`` in the representation. EXAMPLES:: sage: spc = SymmetricGroupRepresentation([2,1]) sage: spc([1,3,2]) [ 1 0] [ 1 -1] """ return self.representation_matrix(Permutation(permutation)) def __iter__(self): r""" Iterate over the matrices representing the elements of the symmetric group. EXAMPLES:: sage: spc = SymmetricGroupRepresentation([1,1,1]) sage: list(spc) [[1], [-1], [-1], [1], [1], [-1]] """ for permutation in Permutations(self._partition.size()): yield self.representation_matrix(permutation) def verify_representation(self): r""" Verify the representation: tests that the images of the simple transpositions are involutions and tests that the braid relations hold. EXAMPLES:: sage: spc = SymmetricGroupRepresentation([1,1,1]) sage: spc.verify_representation() True sage: spc = SymmetricGroupRepresentation([4,2,1]) sage: spc.verify_representation() True """ n = self._partition.size() transpositions = [] for i in range(1,n): si = Permutation(range(1,i) + [i+1,i] + range(i+2,n+1)) transpositions.append(si) repn_matrices = map(self.representation_matrix, transpositions) for (i,si) in enumerate(repn_matrices): for (j,sj) in enumerate(repn_matrices): if i == j: if si*sj != si.parent().identity_matrix(): return False, "si si != 1 for i = %s" % (i,) elif abs(i-j) > 1: if si*sj != sj*si: return False, "si sj != sj si for (i,j) =(%s,%s)" % (i,j) else: if si*sj*si != sj*si*sj: return False, "si sj si != sj si sj for (i,j) = (%s,%s)" % (i,j) return True def to_character(self): r""" Return the character of the representation. EXAMPLES: The trivial character:: sage: rho = SymmetricGroupRepresentation([3]) sage: chi = rho.to_character(); chi Character of Symmetric group of order 3! as a permutation group sage: chi.values() [1, 1, 1] sage: all(chi(g) == 1 for g in SymmetricGroup(3)) True The sign character:: sage: rho = SymmetricGroupRepresentation([1,1,1]) sage: chi = rho.to_character(); chi Character of Symmetric group of order 3! as a permutation group sage: chi.values() [1, -1, 1] sage: all(chi(g) == g.sign() for g in SymmetricGroup(3)) True The defining representation:: sage: triv = SymmetricGroupRepresentation([4]) sage: hook = SymmetricGroupRepresentation([3,1]) sage: def_rep = lambda p : triv(p).block_sum(hook(p)).trace() sage: map(def_rep, Permutations(4)) [4, 2, 2, 1, 1, 2, 2, 0, 1, 0, 0, 1, 1, 0, 2, 1, 0, 0, 0, 1, 1, 2, 0, 0] sage: [p.to_matrix().trace() for p in Permutations(4)] [4, 2, 2, 1, 1, 2, 2, 0, 1, 0, 0, 1, 1, 0, 2, 1, 0, 0, 0, 1, 1, 2, 0, 0] """ from sage.groups.perm_gps.permgroup_named import SymmetricGroup Sym = SymmetricGroup(sum(self._partition)) values = [self(g).trace() for g in Sym.conjugacy_classes_representatives()] return Sym.character(values)
def HighestWeightCrystal(dominant_weight, model=None): r""" Return the highest weight crystal of highest weight ``dominant_weight`` of the given ``model``. INPUT: - ``dominant_weight`` -- a dominant weight - ``model`` -- (optional) if not specified, then we have the following default models: * types `A_n, B_n, C_n, D_n, G_2` - :class:`tableaux <sage.combinat.crystals.tensor_product.CrystalOfTableaux>` * types `E_{6,7}` - :class:`type E finite dimensional crystal <FiniteDimensionalHighestWeightCrystal_TypeE>` * all other types - :class:`LS paths <sage.combinat.crystals.littelmann_path.CrystalOfLSPaths>` otherwise can be one of the following: * ``'Tableaux'`` - :class:`KN tableaux <sage.combinat.crystals.tensor_product.CrystalOfTableaux>` * ``'TypeE'`` - :class:`type E finite dimensional crystal <FiniteDimensionalHighestWeightCrystal_TypeE>` * ``'NakajimaMonomials'`` - :class:`Nakajima monomials <sage.combinat.crystals.monomial_crystals.CrystalOfNakajimaMonomials>` * ``'LSPaths'`` - :class:`LS paths <sage.combinat.crystals.littelmann_path.CrystalOfLSPaths>` * ``'AlcovePaths'`` - :class:`alcove paths <sage.combinat.crystals.alcove_path.CrystalOfAlcovePaths>` * ``'GeneralizedYoungWalls'`` - :class:`generalized Young walls <sage.combinat.crystals.generalized_young_walls.CrystalOfGeneralizedYoungWalls>` * ``'RiggedConfigurations'`` - :class:`rigged configurations <sage.combinat.rigged_configurations.rc_crystal.CrystalOfRiggedConfigurations>` EXAMPLES:: sage: La = RootSystem(['A',2]).weight_lattice().fundamental_weights() sage: wt = La[1] + La[2] sage: crystals.HighestWeight(wt) The crystal of tableaux of type ['A', 2] and shape(s) [[2, 1]] sage: La = RootSystem(['C',2]).weight_lattice().fundamental_weights() sage: wt = 5*La[1] + La[2] sage: crystals.HighestWeight(wt) The crystal of tableaux of type ['C', 2] and shape(s) [[6, 1]] Some type `E` examples:: sage: C = CartanType(['E',6]) sage: La = C.root_system().weight_lattice().fundamental_weights() sage: T = crystals.HighestWeight(La[1]) sage: T.cardinality() 27 sage: T = crystals.HighestWeight(La[6]) sage: T.cardinality() 27 sage: T = crystals.HighestWeight(La[2]) sage: T.cardinality() 78 sage: T = crystals.HighestWeight(La[4]) sage: T.cardinality() 2925 sage: T = crystals.HighestWeight(La[3]) sage: T.cardinality() 351 sage: T = crystals.HighestWeight(La[5]) sage: T.cardinality() 351 sage: C = CartanType(['E',7]) sage: La = C.root_system().weight_lattice().fundamental_weights() sage: T = crystals.HighestWeight(La[1]) sage: T.cardinality() 133 sage: T = crystals.HighestWeight(La[2]) sage: T.cardinality() 912 sage: T = crystals.HighestWeight(La[3]) sage: T.cardinality() 8645 sage: T = crystals.HighestWeight(La[4]) sage: T.cardinality() 365750 sage: T = crystals.HighestWeight(La[5]) sage: T.cardinality() 27664 sage: T = crystals.HighestWeight(La[6]) sage: T.cardinality() 1539 sage: T = crystals.HighestWeight(La[7]) sage: T.cardinality() 56 An example with an affine type:: sage: C = CartanType(['C',2,1]) sage: La = C.root_system().weight_lattice().fundamental_weights() sage: T = crystals.HighestWeight(La[1]) sage: sorted(T.subcrystal(max_depth=3), key=str) [(-Lambda[0] + 3*Lambda[1] - Lambda[2] - delta,), (-Lambda[0] + Lambda[1] + Lambda[2] - delta,), (-Lambda[1] + 2*Lambda[2] - delta,), (2*Lambda[0] - Lambda[1],), (Lambda[0] + Lambda[1] - Lambda[2],), (Lambda[0] - Lambda[1] + Lambda[2],), (Lambda[1],)] Using the various models:: sage: La = RootSystem(['F',4]).weight_lattice().fundamental_weights() sage: wt = La[1] + La[4] sage: crystals.HighestWeight(wt) The crystal of LS paths of type ['F', 4] and weight Lambda[1] + Lambda[4] sage: crystals.HighestWeight(wt, model='NakajimaMonomials') Highest weight crystal of modified Nakajima monomials of Cartan type ['F', 4] and highest weight Lambda[1] + Lambda[4] sage: crystals.HighestWeight(wt, model='AlcovePaths') Highest weight crystal of alcove paths of type ['F', 4] and weight Lambda[1] + Lambda[4] sage: crystals.HighestWeight(wt, model='RiggedConfigurations') Crystal of rigged configurations of type ['F', 4] and weight Lambda[1] + Lambda[4] """ cartan_type = dominant_weight.parent().cartan_type() if model is None: if cartan_type.is_finite(): if cartan_type.type() == 'E': model = 'TypeE' elif cartan_type.type() in ['A','B','C','D','G']: model = 'Tableaux' else: model = 'LSPaths' else: model = 'LSPaths' if model == 'Tableaux': sh = sum([[i]*c for i,c in dominant_weight], []) sh = Partition(reversed(sh)) return CrystalOfTableaux(cartan_type, shape=sh.conjugate()) if model == 'TypeE': if not cartan_type.is_finite() or cartan_type.type() != 'E': raise ValueError("only for finite type E") if cartan_type.rank() == 6: return FiniteDimensionalHighestWeightCrystal_TypeE6(dominant_weight) elif cartan_type.rank() == 7: return FiniteDimensionalHighestWeightCrystal_TypeE7(dominant_weight) raise NotImplementedError if model == 'NakajimaMonomials': # Make sure it's in the weight lattice P = dominant_weight.parent().root_system.weight_lattice() wt = P.sum_of_terms((i, c) for i,c in dominant_weight) return CrystalOfNakajimaMonomials(cartan_type, wt) if model == 'LSPaths': # Make sure it's in the (extended) weight space if cartan_type.is_affine(): P = dominant_weight.parent().root_system.weight_space(extended=True) else: P = dominant_weight.parent().root_system.weight_space() wt = P.sum_of_terms((i, c) for i,c in dominant_weight) return CrystalOfLSPaths(wt) if model == 'AlcovePaths': # Make sure it's in the weight space P = dominant_weight.parent().root_system.weight_space() wt = P.sum_of_terms((i, c) for i,c in dominant_weight) return CrystalOfAlcovePaths(wt, highest_weight_crystal=True) if model == 'GeneralizedYoungWalls': if not cartan_type.is_affine(): raise ValueError("only for affine types") if cartan_type.type() != 'A': raise NotImplementedError("only for affine type A") # Make sure it's in the weight lattice P = dominant_weight.parent().root_system.weight_space() wt = P.sum_of_terms((i, c) for i,c in dominant_weight) return CrystalOfGeneralizedYoungWalls(cartan_type.rank(), wt) if model == 'RiggedConfigurations': # Make sure it's in the weight lattice P = dominant_weight.parent().root_system.weight_lattice() wt = P.sum_of_terms((i, c) for i,c in dominant_weight) return CrystalOfRiggedConfigurations(cartan_type, wt) raise ValueError("invalid model")
def insertion_tableau(skp, perm, evaluation, tableau, length): """ INPUT: - ``skp`` -- skew partitions - ``perm, evaluation`` -- non-negative integers - ``tableau`` -- skew tableau - ``length`` -- integer TESTS:: sage: from sage.combinat.ribbon_tableau import insertion_tableau sage: insertion_tableau([[1], []], [1], 1, [[], []], 1) [[], [[1]]] sage: insertion_tableau([[2, 1], []], [1, 1], 2, [[], [[1]]], 1) [[], [[2], [1, 2]]] sage: insertion_tableau([[2, 1], []], [0, 0], 3, [[], [[2], [1, 2]]], 1) [[], [[2], [1, 2]]] sage: insertion_tableau([[1, 1], []], [1], 2, [[], [[1]]], 1) [[], [[2], [1]]] sage: insertion_tableau([[2], []], [0, 1], 2, [[], [[1]]], 1) [[], [[1, 2]]] sage: insertion_tableau([[2, 1], []], [0, 1], 3, [[], [[2], [1]]], 1) [[], [[2], [1, 3]]] sage: insertion_tableau([[1, 1], []], [2], 1, [[], []], 2) [[], [[1], [0]]] sage: insertion_tableau([[2], []], [2, 0], 1, [[], []], 2) [[], [[1, 0]]] sage: insertion_tableau([[2, 2], []], [0, 2], 2, [[], [[1], [0]]], 2) [[], [[1, 2], [0, 0]]] sage: insertion_tableau([[2, 2], []], [2, 0], 2, [[], [[1, 0]]], 2) [[], [[2, 0], [1, 0]]] sage: insertion_tableau([[2, 2], [1]], [3, 0], 1, [[], []], 3) [[1], [[1, 0], [0]]] """ psave = Partition(skp[1]) partc = skp[1] + [0]*(len(skp[0])-len(skp[1])) tableau = SkewTableau(expr=tableau).to_expr()[1] for k in range(len(tableau)): tableau[-(k+1)] += [0]* ( skp[0][k] - partc[k] - len(tableau[-(k+1)])) ## We construct a tableau from the southwest corner to the northeast one tableau = [[0] * (skp[0][k] - partc[k]) for k in reversed(range(len(tableau), len(skp[0])))] + tableau tableau = SkewTableaux().from_expr([skp[1], tableau]).conjugate() tableau = tableau.to_expr()[1] skp = SkewPartition(skp).conjugate().to_list() skp[1].extend( [0]*(len(skp[0])-len(skp[1])) ) if len(perm) > len(skp[0]): return None for k in range(len(perm)): if perm[ -(k+1) ] !=0: tableau[len(tableau)-len(perm)+k][ skp[0][len(perm)-(k+1)] - skp[1][ len(perm)-(k+1) ] - 1 ] = evaluation return SkewTableau(expr=[psave.conjugate(),tableau]).conjugate().to_expr()
def _delta_irr_rec(p, marking): r""" Internal recursive function called by :func:`delta_irr`. """ if len(p) == 0: return 0 if marking[0] == 1: m = marking[1] a = marking[2] i = p.index(m) pp = Partition(p._list[:i]+p._list[i+1:]) # the partition p' N = (-1)**a* delta_std( pp._list + [m+2], (1,m+2,m-a)) for m1 in xrange(1,m-1,2): m2 = m-m1-1 for a1 in xrange(max(0,a-m2),min(a,m1)): a2 = a - a1 - 1 for p1,p2 in bidecompositions(pp): l1 = sorted([m1]+p1._list,reverse=True) l2 = sorted([m2+2]+p2._list,reverse=True) N += (-1)**a2*(_delta_irr_rec(Partition(l1),(1,m1,a1)) * spin_difference_for_standard_permutations(Partition(l2),(1,m2+2,m2-a2))) return N elif marking[0] == 2: m1 = marking[1] m2 = marking[2] i1 = p.index(m1) i2 = p.index(m2) if m1 == m2: i2 += 1 if i2 < i1: i1,i2 = i2,i1 pp = Partition(p._list[:i1] + p._list[i1+1:i2] + p._list[i2+1:]) N = d(Partition(sorted(pp._list+[m1+m2+1],reverse=True))) / pp.centralizer_size() # nb of standard permutations that corrresponds to extension of good # guys for p1,p2 in bidecompositions(Partition(pp)): for k1 in xrange(1,m1,2): # remove (k1|.) (k2 o m_2) k2 = m1-k1-1 q1 = Partition(sorted(p1._list+[k1],reverse=True)) q2 = Partition(sorted(p2._list+[k2+m2+1],reverse=True)) for a in xrange(k1): # a is a angle N += _delta_irr_rec(q1, (1,k1,a)) * d(q2) / p2.centralizer_size() for k1 in xrange(1,m2,2): # remove (m_1 o k1) (k2|.) k2 = m2-k1-1 l1 = sorted(p1._list+[m1,k1],reverse=True) l2 = sorted(p2._list+[k2+2],reverse=True) for a in xrange(1,k2+1): # a is an angle for standard perm N += (_delta_irr_rec(Partition(l1), (2,m1,k1)) * spin_difference_for_standard_permutations(Partition(l2), (1,k2+2,a))) for m in pp.to_exp_dict(): # remove (m_1 o k_1) (k_2 o m_2) for k1+k2+1 an other zero q = pp._list[:] del q[q.index(m)] for p1,p2 in bidecompositions(Partition(q)): for k1 in xrange(1,m,2): k2 = m-k1-1 q1 = Partition(sorted(p1._list+[m1,k1],reverse=True)) q2 = Partition(sorted(p2._list+[k2+m2+1],reverse=True)) N += _delta_irr_rec(q1, (2,m1,k1)) * d(q2) / p2.centralizer_size() return N
def _gamma_irr_rec(p, marking): r""" Internal recursive function called by :func:`gamma_irr` """ if len(p) == 0: return 1 if marking[0] == 1: m = marking[1] a = marking[2] i = p.index(m) pp = Partition(p._list[:i]+p._list[i+1:]) # the partition p' N = gamma_std(pp._list + [m+2],(1,m+2,m-a)) for m1 in xrange(1,m-1): m2 = m-m1-1 for a1 in xrange(max(0,a-m2),min(a,m1)): a2 = a - a1 - 1 for p1,p2 in bidecompositions(pp): l1 = sorted([m1]+p1._list,reverse=True) l2 = sorted([m2+2]+p2._list,reverse=True) if (sum(l1)+len(l1)) % 2 == 0 and (sum(l2)+len(l2)) % 2 == 0: N -= (_gamma_irr_rec(Partition(l1), (1,m1,a1)) * gamma_std(Partition(l2),(1,m2+2,m2-a2))) return N elif marking[0] == 2: m1 = marking[1] m2 = marking[2] i1 = p.index(m1) i2 = p.index(m2) if m1 == m2: i2 += 1 if i2 < i1: i1,i2 = i2,i1 pp = Partition(p._list[:i1] + p._list[i1+1:i2] + p._list[i2+1:]) N = gamma_std(pp._list + [m1+1,m2+1],(2,m1+1,m2+1)) for p1,p2 in bidecompositions(pp): for k1 in xrange(1,m1): # remove (m'_1|.) (m''_1 o m_2) k2 = m1-k1-1 l1 = sorted(p1._list+[k1],reverse=True) l2 = sorted(p2._list+[k2+1,m2+1],reverse=True) if (sum(l1)+len(l1)) %2 == 0 and (sum(l2)+len(l2)) %2 == 0: for a in xrange(k1): # a is an angle N -= (_gamma_irr_rec(Partition(l1), (1,k1,a))* gamma_std(Partition(l2),(2,k2+1,m2+1))) for k1 in xrange(1,m2): # remove (m_1 o m'_2) (m''_2|.) k2 = m2-k1-1 l1 = sorted(p1._list+[m1,k1],reverse=True) l2 = sorted(p2._list+[k2+2],reverse=True) if (sum(l1)+len(l1)) %2 == 0 and (sum(l2)+len(l2)) %2 == 0: for a in xrange(1,k2+1): # a is an angle for standard perm N -= (_gamma_irr_rec(Partition(l1), (2,m1,k1)) * gamma_std(Partition(l2),(1,k2+2,a))) for m in pp.to_exp_dict(): # remove (m_1, k_1) (k_2, m_2) for k1+k2+1 an other zero q = pp._list[:] del q[q.index(m)] for p1,p2 in bidecompositions(Partition(q)): for k1 in xrange(1,m): k2 = m-k1-1 l1 = sorted(p1._list+[m1,k1],reverse=True) l2 = sorted(p2._list+[k2+1,m2+1],reverse=True) if (sum(l1)+len(l1))%2 == 0 and (sum(l2)+len(l2))%2 == 0: N -= (_gamma_irr_rec(Partition(l1), (2,m1,k1)) * gamma_std(Partition(l2),(2,k2+1,m2+1))) return N else: raise ValueError, "marking must be a 3-tuple of the form (1,m,a) or (2,m1,m2)"
def is_gale_ryser(r,s): r""" Tests whether the given sequences satisfy the condition of the Gale-Ryser theorem. Given a binary matrix `B` of dimension `n\times m`, the vector of row sums is defined as the vector whose `i^{\mbox{th}}` component is equal to the sum of the `i^{\mbox{th}}` row in `A`. The vector of column sums is defined similarly. If, given a binary matrix, these two vectors are easy to compute, the Gale-Ryser theorem lets us decide whether, given two non-negative vectors `r,s`, there exists a binary matrix whose row/colum sums vectors are `r` and `s`. This functions answers accordingly. INPUT: - ``r``, ``s`` -- lists of non-negative integers. ALGORITHM: Without loss of generality, we can assume that: - The two given sequences do not contain any `0` ( which would correspond to an empty column/row ) - The two given sequences are ordered in decreasing order (reordering the sequence of row (resp. column) sums amounts to reordering the rows (resp. columns) themselves in the matrix, which does not alter the columns (resp. rows) sums. We can then assume that `r` and `s` are partitions (see the corresponding class ``Partition``) If `r^*` denote the conjugate of `r`, the Gale-Ryser theorem asserts that a binary Matrix satisfying the constraints exists if and only if `s\preceq r^*`, where `\preceq` denotes the domination order on partitions. EXAMPLES:: sage: from sage.combinat.integer_vector import is_gale_ryser sage: is_gale_ryser([4,2,2],[3,3,1,1]) True sage: is_gale_ryser([4,2,1,1],[3,3,1,1]) True sage: is_gale_ryser([3,2,1,1],[3,3,1,1]) False REMARK: In the literature, what we are calling a Gale-Ryser sequence sometimes goes by the (rather generic-sounding) term ''realizable sequence''. """ # The sequences only contan non-negative integers if [x for x in r if x<0] or [x for x in s if x<0]: return False # builds the corresponding partitions, i.e. # removes the 0 and sorts the sequences from sage.combinat.partition import Partition r2 = Partition(sorted([x for x in r if x>0], reverse=True)) s2 = Partition(sorted([x for x in s if x>0], reverse=True)) # If the two sequences only contained zeroes if len(r2) == 0 and len(s2) == 0: return True rstar = Partition(r2).conjugate() # same number of 1s domination return len(rstar) <= len(s2) and sum(r2) == sum(s2) and rstar.dominates(s)