def __classcall_private__(cls, cartan_type, la, mu): """ Normalize input to ensure a unique representation. TESTS:: sage: K1 = crystals.KacModule(['A', [2,1]], [2,1], [1]) sage: K2 = crystals.KacModule(CartanType(['A', [2,1]]), (2,1), (1,)) sage: K1 is K2 True """ cartan_type = CartanType(cartan_type) la = _Partitions(la) mu = _Partitions(mu) return super(CrystalOfKacModule, cls).__classcall__(cls, cartan_type, la, mu)
def __classcall_private__(cls, cartan_type, la, mu): """ Normalize input to ensure a unique representation. TESTS:: sage: K1 = crystals.KacModule(['A', [2,1]], [2,1], [1]) sage: K2 = crystals.KacModule(CartanType(['A', [2,1]]), (2,1), (1,)) sage: K1 is K2 True """ cartan_type = CartanType(cartan_type) la = _Partitions(la) mu = _Partitions(mu) return super(CrystalOfKacModule, cls).__classcall__(cls, cartan_type, la, mu)
def some_elements(self): r""" Return some elements of ``self``. EXAMPLES:: sage: L = lie_algebras.SymplecticDerivation(QQ, 5) sage: L.some_elements() [a1*a2, b1*b3, a1*a1*a2, b3*b4, a1*a4*b3, a1*a2 - 1/2*a1*a2*a2*a5 + a1*a1*a2*b1*b4] """ d = self.monomial g = self._g return [d( _Partitions([2,1]) ), d( _Partitions([g+3,g+1]) ), d( _Partitions([2,1,1])), d( _Partitions([2*g-1,2*g-2]) ), d( _Partitions([2*g-2,g-1,1]) ), self.an_element()]
def _an_element_(self): r""" Return an element of ``self``. EXAMPLES:: sage: L = lie_algebras.SymplecticDerivation(QQ, 5) sage: L.an_element() a1*a2 - 1/2*a1*a2*a2*a5 + a1*a1*a2*b1*b4 """ d = self.monomial return ( d( _Partitions([2,1]) ) - self.base_ring().an_element() * d( _Partitions([5,2,2,1]) ) + d( _Partitions([2*self._g-1, self._g+1, 2, 1, 1]) ) )
def spin_polynomial_square(part, weight, length): r""" Returns the spin polynomial associated with ``part``, ``weight``, and ``length``, with the substitution `t \to t^2` made. EXAMPLES:: sage: from sage.combinat.ribbon_tableau import spin_polynomial_square sage: spin_polynomial_square([6,6,6],[4,2],3) t^12 + t^10 + 2*t^8 + t^6 + t^4 sage: spin_polynomial_square([6,6,6],[4,1,1],3) t^12 + 2*t^10 + 3*t^8 + 2*t^6 + t^4 sage: spin_polynomial_square([3,3,3,2,1], [2,2], 3) t^7 + t^5 sage: spin_polynomial_square([3,3,3,2,1], [2,1,1], 3) 2*t^7 + 2*t^5 + t^3 sage: spin_polynomial_square([3,3,3,2,1], [1,1,1,1], 3) 3*t^7 + 5*t^5 + 3*t^3 + t sage: spin_polynomial_square([5,4,3,2,1,1,1], [2,2,1], 3) 2*t^9 + 6*t^7 + 2*t^5 sage: spin_polynomial_square([[6]*6, [3,3]], [4,4,2], 3) 3*t^18 + 5*t^16 + 9*t^14 + 6*t^12 + 3*t^10 """ R = ZZ['t'] if part in _Partitions: part = SkewPartition([part,_Partitions([])]) elif part in SkewPartitions(): part = SkewPartition(part) if part == [[],[]] and weight == []: return R.one() t = R.gen() return R(graph_implementation_rec(part, weight, length, functools.partial(spin_rec,t))[0])
def spin_polynomial_square(part, weight, length): r""" Returns the spin polynomial associated with ``part``, ``weight``, and ``length``, with the substitution `t \to t^2` made. EXAMPLES:: sage: from sage.combinat.ribbon_tableau import spin_polynomial_square sage: spin_polynomial_square([6,6,6],[4,2],3) t^12 + t^10 + 2*t^8 + t^6 + t^4 sage: spin_polynomial_square([6,6,6],[4,1,1],3) t^12 + 2*t^10 + 3*t^8 + 2*t^6 + t^4 sage: spin_polynomial_square([3,3,3,2,1], [2,2], 3) t^7 + t^5 sage: spin_polynomial_square([3,3,3,2,1], [2,1,1], 3) 2*t^7 + 2*t^5 + t^3 sage: spin_polynomial_square([3,3,3,2,1], [1,1,1,1], 3) 3*t^7 + 5*t^5 + 3*t^3 + t sage: spin_polynomial_square([5,4,3,2,1,1,1], [2,2,1], 3) 2*t^9 + 6*t^7 + 2*t^5 sage: spin_polynomial_square([[6]*6, [3,3]], [4,4,2], 3) 3*t^18 + 5*t^16 + 9*t^14 + 6*t^12 + 3*t^10 """ R = ZZ['t'] t = R.gen() if part in _Partitions: part = SkewPartition([part,_Partitions([])]) elif part in SkewPartitions(): part = SkewPartition(part) if part == [[],[]] and weight == []: return t.parent()(1) return R(graph_implementation_rec(part, weight, length, functools.partial(spin_rec,t))[0])
def bottom_schur_function(self, partition, degree=None): r""" Return the least-degree component of ``s[partition]``, where ``s`` denotes the Schur basis of the symmetric functions, and the grading is not the usual grading on the symmetric functions but rather the grading which gives every `p_i` degree `1`. This least-degree component has its degree equal to the Frobenius rank of ``partition``, while the degree with respect to the usual grading is still the size of ``partition``. This method requires the base ring to be a (commutative) `\QQ`-algebra. This restriction is unavoidable, since the least-degree component (in general) has noninteger coefficients in all classical bases of the symmetric functions. The optional keyword ``degree`` allows taking any homogeneous component rather than merely the least-degree one. Specifically, if ``degree`` is set, then the ``degree``-th component will be returned. REFERENCES: .. [ClSt03] Peter Clifford, Richard P. Stanley, *Bottom Schur functions*. :arxiv:`math/0311382v2`. EXAMPLES:: sage: Sym = SymmetricFunctions(QQ) sage: p = Sym.p() sage: p.bottom_schur_function([2,2,1]) -1/6*p[3, 2] + 1/4*p[4, 1] sage: p.bottom_schur_function([2,1]) -1/3*p[3] sage: p.bottom_schur_function([3]) 1/3*p[3] sage: p.bottom_schur_function([1,1,1]) 1/3*p[3] sage: p.bottom_schur_function(Partition([1,1,1])) 1/3*p[3] sage: p.bottom_schur_function([2,1], degree=1) -1/3*p[3] sage: p.bottom_schur_function([2,1], degree=2) 0 sage: p.bottom_schur_function([2,1], degree=3) 1/3*p[1, 1, 1] sage: p.bottom_schur_function([2,2,1], degree=3) 1/8*p[2, 2, 1] - 1/6*p[3, 1, 1] """ from sage.combinat.partition import _Partitions s = self.realization_of().schur() partition = _Partitions(partition) if degree is None: degree = partition.frobenius_rank() s_partition = self(s[partition]) return self.sum_of_terms([(p, coeff) for p, coeff in s_partition if len(p) == degree], distinct=True)
def bracket_on_basis(self, x, y): r""" Return the bracket of basis elements indexed by ``x`` and ``y``, where ``i < j``. EXAMPLES:: sage: L = lie_algebras.SymplecticDerivation(QQ, 5) sage: L.bracket_on_basis([5,2,1], [5,1,1]) 0 sage: L.bracket_on_basis([6,1], [3,1,1]) -2*a1*a1*a3 sage: L.bracket_on_basis([9,2,1], [4,1,1]) -a1*a1*a1*a2 sage: L.bracket_on_basis([5,5,2], [6,1,1]) 0 sage: L.bracket_on_basis([5,5,5], [10,3]) 3*a3*a5*a5 sage: L.bracket_on_basis([10,10,10], [5,3]) -3*a3*b5*b5 """ g = self._g ret = {} one = self.base_ring().one() for i,xi in enumerate(x): for j,yj in enumerate(y): # The symplectic form will be 0 if (xi <= g and yj <= g) or (xi > g and yj > g): continue if xi <= g and yj > g: if xi != yj - g: continue m = _Partitions(sorted(x[:i] + x[i+1:] + y[:j] + y[j+1:], reverse=True)) if m in ret: ret[m] += one else: ret[m] = one else: # if ci > g and yj <= g: if xi - g != yj: continue m = _Partitions(sorted(x[:i] + x[i+1:] + y[:j] + y[j+1:], reverse=True)) if m in ret: ret[m] -= one else: ret[m] = -one return self._from_dict(ret, remove_zeros=True)
def compat(n, mu, nu): r""" Generate all possible partitions of `n` that can precede `\mu, \nu` in a rigging sequence. INPUT: - ``n`` -- a positive integer - ``mu``, ``nu`` -- partitions OUTPUT: - a list of partitions EXAMPLES:: sage: from sage.combinat.sf.kfpoly import * sage: compat(4, [1], [2,1]) [[1, 1, 1, 1], [2, 1, 1], [2, 2], [3, 1], [4]] sage: compat(3, [1], [2,1]) [[1, 1, 1], [2, 1], [3]] sage: compat(2, [1], []) [[2]] sage: compat(3, [1], []) [[2, 1], [3]] sage: compat(3, [2], [1]) [[3]] sage: compat(4, [1,1], []) [[2, 2], [3, 1], [4]] sage: compat(4, [2], []) [[4]] """ l = max(len(mu), len(nu)) mmu = list(mu) + [0] * (l - len(mu)) nnu = list(nu) + [0] * (l - len(nu)) bd = [] sa = 0 for i in range(l): sa += 2 * mmu[i] - nnu[i] bd.append(sa) for la in ZS1_iterator(n): if dom(la, bd): return [ x.conjugate() for x in _Partitions(la).dominated_partitions() ] return [] # _Partitions([])
def compat(n, mu, nu): r""" Generate all possible partitions of `n` that can precede `\mu, \nu` in a rigging sequence. INPUT: - ``n`` -- a positive integer - ``mu``, ``nu`` -- partitions OUTPUT: - a list of partitions EXAMPLES:: sage: from sage.combinat.sf.kfpoly import * sage: compat(4, [1], [2,1]) [[1, 1, 1, 1], [2, 1, 1], [2, 2], [3, 1], [4]] sage: compat(3, [1], [2,1]) [[1, 1, 1], [2, 1], [3]] sage: compat(2, [1], []) [[2]] sage: compat(3, [1], []) [[2, 1], [3]] sage: compat(3, [2], [1]) [[3]] sage: compat(4, [1,1], []) [[2, 2], [3, 1], [4]] sage: compat(4, [2], []) [[4]] """ l = max(len(mu), len(nu)) mmu = list(mu) + [0]*(l-len(mu)) nnu = list(nu) + [0]*(l-len(nu)) bd = [] sa = 0 for i in range(l): sa += 2*mmu[i] - nnu[i] bd.append(sa) for la in ZS1_iterator(n): if dom(la, bd): return [x.conjugate() for x in _Partitions(la).dominated_partitions()] return [] # _Partitions([])
def kfpoly(mu, nu, t=None): r""" Return the Kostka-Foulkes polynomial `K_{\mu, \nu}(t)` by generating all rigging sequences for the shape `\mu`, and then selecting those of content `\nu`. INPUT: - ``mu``, ``nu`` -- partitions - ``t`` -- an optional parameter (default: ``None``) OUTPUT: - the Koskta-Foulkes polynomial indexed by partitions ``mu`` and ``nu`` and evaluated at the parameter ``t``. If ``t`` is ``None`` the resulting polynomial is in the polynomial ring `\ZZ['t']`. EXAMPLES:: sage: from sage.combinat.sf.kfpoly import kfpoly sage: kfpoly([2,2], [2,1,1]) t sage: kfpoly([4], [2,1,1]) t^3 sage: kfpoly([4], [2,2]) t^2 sage: kfpoly([1,1,1,1], [2,2]) 0 TESTS:: sage: kfpoly([], []) 1 """ if mu == nu: return 1 if t is None: t = polygen(ZZ, 't') nuc = _Partitions(nu).conjugate() f = lambda x: weight(x, t) if x[0] == nuc else 0 return sum(f(rg) for rg in riggings(mu))
def __classcall_private__(cls, ct, shape): """ Normalize input to ensure a unique representation. TESTS:: sage: crystals.Tableaux(['A', [1, 2]], shape=[2,1]) Crystal of BKK tableaux of shape [2, 1] of gl(2|3) sage: crystals.Tableaux(['A', [1, 1]], shape=[3,3,3]) Traceback (most recent call last): ... ValueError: invalid hook shape """ ct = CartanType(ct) shape = _Partitions(shape) if len(shape) > ct.m + 1 and shape[ct.m+1] > ct.n + 1: raise ValueError("invalid hook shape") return super(CrystalOfBKKTableaux, cls).__classcall__(cls, ct, shape)
def __classcall_private__(cls, ct, shape): """ Normalize input to ensure a unique representation. TESTS:: sage: crystals.Tableaux(['A', [1, 2]], shape=[2,1]) Crystal of BKK tableaux of shape [2, 1] of gl(2|3) sage: crystals.Tableaux(['A', [1, 1]], shape=[3,3,3]) Traceback (most recent call last): ... ValueError: invalid hook shape """ ct = CartanType(ct) shape = _Partitions(shape) if len(shape) > ct.m + 1 and shape[ct.m + 1] > ct.n + 1: raise ValueError("invalid hook shape") return super(CrystalOfBKKTableaux, cls).__classcall__(cls, ct, shape)
def kfpoly(mu, nu, t=None): r""" Returns the Kostka-Foulkes polynomial `K_{\mu, \nu}(t)` by generating all rigging sequences for the shape `\mu`, and then selecting those of content `\nu`. INPUT: - ``mu``, ``nu`` -- partitions - ``t`` -- an optional parameter (default: ``None``) OUTPUT: - the Koskta-Foulkes polynomial indexed by partitions ``mu`` and ``nu`` and evaluated at the parameter ``t``. If ``t`` is ``None`` the resulting polynomial is in the polynomial ring `\mathbb{Z}['t']`. EXAMPLES:: sage: from sage.combinat.sf.kfpoly import kfpoly sage: kfpoly([2,2], [2,1,1]) t sage: kfpoly([4], [2,1,1]) t^3 sage: kfpoly([4], [2,2]) t^2 sage: kfpoly([1,1,1,1], [2,2]) 0 """ if mu == nu: return 1 elif mu == []: return 0 if t is None: t = polygen(ZZ, 't') nuc = _Partitions(nu).conjugate() f = lambda x: weight(x, t) if x[0] == nuc else 0 res = sum(f(rg) for rg in riggings(mu)) return res
def __classcall_private__(cls, n=None): r""" This class returns the appropriate parent based on arguments. See the documentation for :class:`StandardTableaux` for more information. TESTS:: sage: SST = StandardSuperTableaux(); SST Standard super tableaux sage: StandardSuperTableaux(3) Standard super tableaux of size 3 sage: StandardSuperTableaux([2,2]) Standard super tableaux of shape [2, 2] sage: StandardSuperTableaux(-1) Traceback (most recent call last): ... ValueError: the argument must be a non-negative integer or a partition sage: StandardSuperTableaux([[1]]) Traceback (most recent call last): ... ValueError: the argument must be a non-negative integer or a partition """ from sage.combinat.partition import _Partitions from sage.combinat.skew_partition import SkewPartitions if n is None: return StandardSuperTableaux_all() elif n in _Partitions: return StandardSuperTableaux_shape(_Partitions(n)) elif n in SkewPartitions(): raise NotImplementedError("standard super tableau for skew " "partitions is not implemented yet") if not isinstance(n, (int, Integer)) or n < 0: raise ValueError("the argument must be a non-negative integer" " or a partition") return StandardSuperTableaux_size(n)
def __classcall_private__(cls, shape, weight, length): """ Normalize input to ensure a unique representation. EXAMPLES:: sage: R = RibbonTableaux([[2,1],[]],[1,1,1],1) sage: R2 = RibbonTableaux(SkewPartition([[2,1],[]]),(1,1,1),1) sage: R is R2 True """ if shape in _Partitions: shape = _Partitions(shape) shape = SkewPartition([shape, shape.core(length)]) else: shape = SkewPartition(shape) if shape.size() != length*sum(weight): raise ValueError("Incompatible shape and weight") return super(RibbonTableaux, cls).__classcall__(cls, shape, tuple(weight), length)
def __classcall_private__(cls, shape, weight, length): """ Normalize input to ensure a unique representation. EXAMPLES:: sage: R = RibbonTableaux([[2,1],[]],[1,1,1],1) sage: R2 = RibbonTableaux(SkewPartition([[2,1],[]]),(1,1,1),1) sage: R is R2 True """ if shape in _Partitions: shape = _Partitions(shape) shape = SkewPartition([shape, shape.core(length)]) else: shape = SkewPartition(shape) if shape.size() != length*sum(weight): raise ValueError("Incompatible shape and weight") return super(RibbonTableaux, cls).__classcall__(cls, shape, tuple(weight), length)
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 = _Partitions(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 _supp_to_s(self, gamma): r""" This is a helper function that is not meant to be called directly. Given the support of an element `x_1^{\gamma_1} x_2^{\gamma_2} \cdots x_\ell^{\gamma_\ell}` in the ``ShiftingOperatorAlgebra``, return the appropriate `s_\gamma` in the Schur basis using "Schur function straightening" in [BMPS2018]_ Proposition 4.1. EXAMPLES:: sage: S = ShiftingOperatorAlgebra(QQ) sage: s = SymmetricFunctions(QQ).s() sage: S._supp_to_s(S([3,2,1]).support_of_term()) s[3, 2, 1] sage: S._supp_to_s(S([2,3,1]).support_of_term()) 0 sage: S._supp_to_s(S([2,4,-1,1]).support_of_term()) s[3, 3] sage: S._supp_to_s(S([3,2,0]).support_of_term()) s[3, 2] """ def number_of_noninversions(lis): return sum(1 for i, val in enumerate(lis) for j in range(i + 1, len(lis)) if val < lis[j]) # i < j is already enforced rho = list(range(len(gamma) - 1, -1, -1)) combined = [g + r for g, r in zip(gamma, rho)] if len(set(combined)) == len(combined) and all(e >= 0 for e in combined): sign = (-1)**number_of_noninversions(combined) sort_combined = sorted(combined, reverse=True) new_gamma = [sc - r for sc, r in zip(sort_combined, rho)] return sign * self._sym_s(_Partitions(new_gamma)) else: return self._sym_s.zero()
def _element_constructor_(self, x): """ Convert ``x`` into ``self``, if coercion failed. INPUT: - ``x`` -- an element of the symmetric functions EXAMPLES:: sage: s = SymmetricFunctions(QQ).s() sage: s(2) 2*s[] sage: s([2,1]) # indirect doctest s[2, 1] sage: McdJ = SymmetricFunctions(QQ['q','t'].fraction_field()).macdonald().J() sage: s = SymmetricFunctions(McdJ.base_ring()).s() sage: s._element_constructor_(McdJ(s[2,1])) s[2, 1] TESTS: Check that non-Schur bases raise an error when given skew partitions (:trac:`19218`):: sage: e = SymmetricFunctions(QQ).e() sage: e([[2,1],[1]]) Traceback (most recent call last): ... TypeError: do not know how to make x (= [[2, 1], [1]]) an element of self """ R = self.base_ring() eclass = self.element_class if isinstance(x, int): x = Integer(x) ############## # Partitions # ############## if x in _Partitions: return eclass(self, {_Partitions(x): R.one()}) # Todo: discard all of this which is taken care by Sage's coercion # (up to changes of base ring) ############## # Dual bases # ############## elif sfa.is_SymmetricFunction(x) and hasattr(x, 'dual'): #Check to see if it is the dual of some other basis #If it is, try to coerce its corresponding element #in the other basis return self(x.dual()) ################################################################## # Symmetric Functions, same basis, possibly different coeff ring # ################################################################## # self.Element is used below to test if another symmetric # function is expressed in the same basis but in another # ground ring. This idiom is fragile and depends on the # internal (unstable) specifications of parents and categories # # TODO: find the right idiom # # One cannot use anymore self.element_class: it is build by # the category mechanism, and depends on the coeff ring. elif isinstance(x, self.Element): P = x.parent() #same base ring if P is self: return x #different base ring else: return eclass(self, dict([ (e1,R(e2)) for e1,e2 in x._monomial_coefficients.items()])) ################################################## # Classical Symmetric Functions, different basis # ################################################## elif isinstance(x, SymmetricFunctionAlgebra_classical.Element): R = self.base_ring() xP = x.parent() xm = x.monomial_coefficients() #determine the conversion function. try: t = conversion_functions[(xP.basis_name(),self.basis_name())] except AttributeError: raise TypeError("do not know how to convert from %s to %s"%(xP.basis_name(), self.basis_name())) if R == QQ and xP.base_ring() == QQ: if xm: return self._from_dict(t(xm)._monomial_coefficients, coerce=True) else: return self.zero() else: f = lambda part: self._from_dict(t( {part: ZZ.one()} )._monomial_coefficients) return self._apply_module_endomorphism(x, f) ############################### # Hall-Littlewood Polynomials # ############################### elif isinstance(x, hall_littlewood.HallLittlewood_generic.Element): # #Qp: Convert to Schur basis and then convert to self # if isinstance(x, hall_littlewood.HallLittlewood_qp.Element): Qp = x.parent() sx = Qp._s._from_cache(x, Qp._s_cache, Qp._self_to_s_cache, t=Qp.t) return self(sx) # #P: Convert to Schur basis and then convert to self # elif isinstance(x, hall_littlewood.HallLittlewood_p.Element): P = x.parent() sx = P._s._from_cache(x, P._s_cache, P._self_to_s_cache, t=P.t) return self(sx) # #Q: Convert to P basis and then convert to self # elif isinstance(x, hall_littlewood.HallLittlewood_q.Element): return self( x.parent()._P( x ) ) ####### # LLT # ####### #Convert to m and then to self. elif isinstance(x, llt.LLT_generic.Element): P = x.parent() BR = self.base_ring() zero = BR.zero() PBR = P.base_ring() if not BR.has_coerce_map_from(PBR): raise TypeError("no coerce map from x's parent's base ring (= %s) to self's base ring (= %s)"%(PBR, self.base_ring())) z_elt = {} for m, c in x._monomial_coefficients.iteritems(): n = sum(m) P._m_cache(n) for part in P._self_to_m_cache[n][m]: z_elt[part] = z_elt.get(part, zero) + BR(c*P._self_to_m_cache[n][m][part].subs(t=P.t)) m = P._sym.monomial() return self( m._from_dict(z_elt) ) ######################### # Macdonald Polynomials # ######################### elif isinstance(x, macdonald.MacdonaldPolynomials_generic.Element): if isinstance(x, macdonald.MacdonaldPolynomials_j.Element): J = x.parent() sx = J._s._from_cache(x, J._s_cache, J._self_to_s_cache, q=J.q, t=J.t) return self(sx) elif isinstance(x, (macdonald.MacdonaldPolynomials_q.Element, macdonald.MacdonaldPolynomials_p.Element)): J = x.parent()._J jx = J(x) sx = J._s._from_cache(jx, J._s_cache, J._self_to_s_cache, q=J.q, t=J.t) return self(sx) elif isinstance(x, (macdonald.MacdonaldPolynomials_h.Element,macdonald.MacdonaldPolynomials_ht.Element)): H = x.parent() sx = H._self_to_s(x) return self(sx) elif isinstance(x, macdonald.MacdonaldPolynomials_s.Element): S = x.parent() sx = S._s._from_cache(x, S._s_cache, S._self_to_s_cache, q=S.q, t=S.t) return self(sx) else: raise TypeError #################### # Jack Polynomials # #################### elif isinstance(x, jack.JackPolynomials_generic.Element): if isinstance(x, jack.JackPolynomials_p.Element): P = x.parent() mx = P._m._from_cache(x, P._m_cache, P._self_to_m_cache, t=P.t) return self(mx) if isinstance(x, (jack.JackPolynomials_j.Element, jack.JackPolynomials_q.Element)): return self( x.parent()._P(x) ) else: raise TypeError #################################################### # Bases defined by orthogonality and triangularity # #################################################### elif isinstance(x, orthotriang.SymmetricFunctionAlgebra_orthotriang.Element): #Convert to its base and then to self xp = x.parent() if self is xp._sf_base: return xp._sf_base._from_cache(x, xp._base_cache, xp._self_to_base_cache) else: return self( xp._sf_base(x) ) ################################# # Last shot -- try calling R(x) # ################################# else: try: return eclass(self, {_Partitions([]): R(x)}) except Exception: raise TypeError("do not know how to make x (= {}) an element of self".format(x))
def __classcall_private__(cls, cartan_type, shapes=None, shape=None): """ Normalizes the input arguments to ensure unique representation, and to delegate the construction of spin tableaux. EXAMPLES:: sage: T1 = crystals.Tableaux(CartanType(['A',3]), shape = [2,2]) sage: T2 = crystals.Tableaux(['A',3], shape = (2,2)) sage: T3 = crystals.Tableaux(['A',3], shapes = ([2,2],)) sage: T2 is T1, T3 is T1 (True, True) sage: T1 = crystals.Tableaux(['A', [1,1]], shape=[3,1,1,1]) sage: T1 Crystal of BKK tableaux of shape [3, 1, 1, 1] of gl(2|2) sage: T2 = crystals.Tableaux(['A', [1,1]], [3,1,1,1]) sage: T1 is T2 True """ cartan_type = CartanType(cartan_type) if cartan_type.letter == 'A' and isinstance(cartan_type, SuperCartanType_standard): if shape is None: shape = shapes shape = _Partitions(shape) from sage.combinat.crystals.bkk_crystals import CrystalOfBKKTableaux return CrystalOfBKKTableaux(cartan_type, shape=shape) if cartan_type.letter == 'Q': if any(shape[i] == shape[i + 1] for i in range(len(shape) - 1)): raise ValueError("not a strict partition") shape = _Partitions(shape) return CrystalOfQueerTableaux(cartan_type, shape=shape) n = cartan_type.rank() # standardize shape/shapes input into a tuple of tuples # of length n, or n+1 in type A assert operator.xor(shape is not None, shapes is not None) if shape is not None: shapes = (shape, ) if cartan_type.type() == "A": n1 = n + 1 else: n1 = n if not all(all(i == 0 for i in shape[n1:]) for shape in shapes): raise ValueError( "shapes should all have length at most equal to the rank or the rank + 1 in type A" ) spin_shapes = tuple((tuple(shape) + (0, ) * (n1 - len(shape)))[:n1] for shape in shapes) try: shapes = tuple( tuple(trunc(i) for i in shape) for shape in spin_shapes) except Exception: raise ValueError( "shapes should all be partitions or half-integer partitions") if spin_shapes == shapes: shapes = tuple( _Partitions(shape) if shape[n1 - 1] in NN else shape for shape in shapes) return super(CrystalOfTableaux, cls).__classcall__(cls, cartan_type, shapes) # Handle the construction of a crystals of spin tableaux # Caveat: this currently only supports all shapes being half # integer partitions of length the rank for type B and D. In # particular, for type D, the spins all have to be plus or all # minus spins if any(len(sh) != n for sh in shapes): raise ValueError( "the length of all half-integer partition shapes should be the rank" ) if any(2 * i % 2 != 1 for shape in spin_shapes for i in shape): raise ValueError( "shapes should be either all partitions or all half-integer partitions" ) if any( any(i < j for i, j in zip(shape, shape[1:-1] + (abs(shape[-1]), ))) for shape in spin_shapes): raise ValueError("entries of each shape must be weakly decreasing") if cartan_type.type() == 'D': if all(i >= 0 for shape in spin_shapes for i in shape): S = CrystalOfSpinsPlus(cartan_type) elif all(shape[-1] < 0 for shape in spin_shapes): S = CrystalOfSpinsMinus(cartan_type) else: raise ValueError( "in type D spins should all be positive or negative") else: if any(i < 0 for shape in spin_shapes for i in shape): raise ValueError("shapes should all be partitions") S = CrystalOfSpins(cartan_type) B = CrystalOfTableaux(cartan_type, shapes=shapes) T = TensorProductOfCrystals(S, B, generators=[[S.module_generators[0], x] for x in B.module_generators]) T.rename("The crystal of tableaux of type %s and shape(s) %s" % (cartan_type, list(list(shape) for shape in spin_shapes))) T.shapes = spin_shapes return T
def bottom_schur_function(self, partition, degree=None): r""" Return the least-degree component of ``s[partition]``, where ``s`` denotes the Schur basis of the symmetric functions, and the grading is not the usual grading on the symmetric functions but rather the grading which gives every `p_i` degree `1`. This least-degree component has its degree equal to the Frobenius rank of ``partition``, while the degree with respect to the usual grading is still the size of ``partition``. This method requires the base ring to be a (commutative) `\QQ`-algebra. This restriction is unavoidable, since the least-degree component (in general) has noninteger coefficients in all classical bases of the symmetric functions. The optional keyword ``degree`` allows taking any homogeneous component rather than merely the least-degree one. Specifically, if ``degree`` is set, then the ``degree``-th component will be returned. REFERENCES: .. [ClSt03] Peter Clifford, Richard P. Stanley, *Bottom Schur functions*. :arxiv:`math/0311382v2`. EXAMPLES:: sage: Sym = SymmetricFunctions(QQ) sage: p = Sym.p() sage: p.bottom_schur_function([2,2,1]) -1/6*p[3, 2] + 1/4*p[4, 1] sage: p.bottom_schur_function([2,1]) -1/3*p[3] sage: p.bottom_schur_function([3]) 1/3*p[3] sage: p.bottom_schur_function([1,1,1]) 1/3*p[3] sage: p.bottom_schur_function(Partition([1,1,1])) 1/3*p[3] sage: p.bottom_schur_function([2,1], degree=1) -1/3*p[3] sage: p.bottom_schur_function([2,1], degree=2) 0 sage: p.bottom_schur_function([2,1], degree=3) 1/3*p[1, 1, 1] sage: p.bottom_schur_function([2,2,1], degree=3) 1/8*p[2, 2, 1] - 1/6*p[3, 1, 1] """ from sage.combinat.partition import _Partitions s = self.realization_of().schur() partition = _Partitions(partition) if degree is None: degree = partition.frobenius_rank() s_partition = self(s[partition]) return self.sum_of_terms( [(p, coeff) for p, coeff in s_partition if len(p) == degree], distinct=True)
def P(i): return _Partitions([i]) if i else _Partitions([])
def _element_constructor_(self, x): """ Convert ``x`` into ``self``, if coercion failed. INPUT: - ``x`` -- an element of the symmetric functions EXAMPLES:: sage: s = SymmetricFunctions(QQ).s() sage: s(2) 2*s[] sage: s([2,1]) # indirect doctest s[2, 1] sage: McdJ = SymmetricFunctions(QQ['q','t'].fraction_field()).macdonald().J() sage: s = SymmetricFunctions(McdJ.base_ring()).s() sage: s._element_constructor_(McdJ(s[2,1])) s[2, 1] TESTS: Check that non-Schur bases raise an error when given skew partitions (:trac:`19218`):: sage: e = SymmetricFunctions(QQ).e() sage: e([[2,1],[1]]) Traceback (most recent call last): ... TypeError: do not know how to make x (= [[2, 1], [1]]) an element of self """ R = self.base_ring() eclass = self.element_class if isinstance(x, int): x = Integer(x) ############## # Partitions # ############## if x in _Partitions: return eclass(self, {_Partitions(x): R.one()}) # Todo: discard all of this which is taken care by Sage's coercion # (up to changes of base ring) ############## # Dual bases # ############## elif sfa.is_SymmetricFunction(x) and hasattr(x, 'dual'): #Check to see if it is the dual of some other basis #If it is, try to coerce its corresponding element #in the other basis return self(x.dual()) ################################################################## # Symmetric Functions, same basis, possibly different coeff ring # ################################################################## # self.Element is used below to test if another symmetric # function is expressed in the same basis but in another # ground ring. This idiom is fragile and depends on the # internal (unstable) specifications of parents and categories # # TODO: find the right idiom # # One cannot use anymore self.element_class: it is build by # the category mechanism, and depends on the coeff ring. elif isinstance(x, self.Element): P = x.parent() #same base ring if P is self: return x #different base ring else: return eclass( self, dict([(e1, R(e2)) for e1, e2 in x._monomial_coefficients.items()])) ################################################## # Classical Symmetric Functions, different basis # ################################################## elif isinstance(x, SymmetricFunctionAlgebra_classical.Element): R = self.base_ring() xP = x.parent() xm = x.monomial_coefficients() #determine the conversion function. try: t = conversion_functions[(xP.basis_name(), self.basis_name())] except AttributeError: raise TypeError("do not know how to convert from %s to %s" % (xP.basis_name(), self.basis_name())) if R == QQ and xP.base_ring() == QQ: if xm: return self._from_dict(t(xm)._monomial_coefficients, coerce=True) else: return self.zero() else: f = lambda part: self._from_dict( t({ part: ZZ.one() })._monomial_coefficients) return self._apply_module_endomorphism(x, f) ############################### # Hall-Littlewood Polynomials # ############################### elif isinstance(x, hall_littlewood.HallLittlewood_generic.Element): # #Qp: Convert to Schur basis and then convert to self # if isinstance(x, hall_littlewood.HallLittlewood_qp.Element): Qp = x.parent() sx = Qp._s._from_cache(x, Qp._s_cache, Qp._self_to_s_cache, t=Qp.t) return self(sx) # #P: Convert to Schur basis and then convert to self # elif isinstance(x, hall_littlewood.HallLittlewood_p.Element): P = x.parent() sx = P._s._from_cache(x, P._s_cache, P._self_to_s_cache, t=P.t) return self(sx) # #Q: Convert to P basis and then convert to self # elif isinstance(x, hall_littlewood.HallLittlewood_q.Element): return self(x.parent()._P(x)) ####### # LLT # ####### #Convert to m and then to self. elif isinstance(x, llt.LLT_generic.Element): P = x.parent() BR = self.base_ring() zero = BR.zero() PBR = P.base_ring() if not BR.has_coerce_map_from(PBR): raise TypeError( "no coerce map from x's parent's base ring (= %s) to self's base ring (= %s)" % (PBR, self.base_ring())) z_elt = {} for m, c in x._monomial_coefficients.iteritems(): n = sum(m) P._m_cache(n) for part in P._self_to_m_cache[n][m]: z_elt[part] = z_elt.get(part, zero) + BR( c * P._self_to_m_cache[n][m][part].subs(t=P.t)) m = P._sym.monomial() return self(m._from_dict(z_elt)) ######################### # Macdonald Polynomials # ######################### elif isinstance(x, macdonald.MacdonaldPolynomials_generic.Element): if isinstance(x, macdonald.MacdonaldPolynomials_j.Element): J = x.parent() sx = J._s._from_cache(x, J._s_cache, J._self_to_s_cache, q=J.q, t=J.t) return self(sx) elif isinstance(x, (macdonald.MacdonaldPolynomials_q.Element, macdonald.MacdonaldPolynomials_p.Element)): J = x.parent()._J jx = J(x) sx = J._s._from_cache(jx, J._s_cache, J._self_to_s_cache, q=J.q, t=J.t) return self(sx) elif isinstance(x, (macdonald.MacdonaldPolynomials_h.Element, macdonald.MacdonaldPolynomials_ht.Element)): H = x.parent() sx = H._self_to_s(x) return self(sx) elif isinstance(x, macdonald.MacdonaldPolynomials_s.Element): S = x.parent() sx = S._s._from_cache(x, S._s_cache, S._self_to_s_cache, q=S.q, t=S.t) return self(sx) else: raise TypeError #################### # Jack Polynomials # #################### elif isinstance(x, jack.JackPolynomials_generic.Element): if isinstance(x, jack.JackPolynomials_p.Element): P = x.parent() mx = P._m._from_cache(x, P._m_cache, P._self_to_m_cache, t=P.t) return self(mx) if isinstance(x, (jack.JackPolynomials_j.Element, jack.JackPolynomials_q.Element)): return self(x.parent()._P(x)) else: raise TypeError #################################################### # Bases defined by orthogonality and triangularity # #################################################### elif isinstance( x, orthotriang.SymmetricFunctionAlgebra_orthotriang.Element): #Convert to its base and then to self xp = x.parent() if self is xp._sf_base: return xp._sf_base._from_cache(x, xp._base_cache, xp._self_to_base_cache) else: return self(xp._sf_base(x)) ################################# # Last shot -- try calling R(x) # ################################# else: try: return eclass(self, {_Partitions([]): R(x)}) except Exception: raise TypeError( "do not know how to make x (= {}) an element of self". format(x))
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 indeterminate 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 Check that :trac:`25715` is fixed:: sage: parent(q_subgroups_of_abelian_group([2], [1], algorithm='delsarte')) Univariate Polynomial Ring in q over Integer Ring sage: q_subgroups_of_abelian_group([7,7,1], []) 1 sage: q_subgroups_of_abelian_group([7,7,1], [0,0]) 1 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, 2018): Implemented the Birkhoff algorithm and refactoring """ if q is None: q = ZZ['q'].gen() la_c = _Partitions(la).conjugate() mu_c = _Partitions(mu).conjugate() k = mu_c.length() if not mu_c: # There is only one trivial subgroup return parent(q)(1) if not la_c.contains(mu_c): return parent(q)(0) 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")
REFERENCES: - :wikipedia:`Q-Pochhammer_symbol` """ if q is None: q = ZZ['q'].gen() if n not in ZZ: raise ValueError("{} must be an integer".format(n)) R = parent(q) one = R(1) if n < 0: return R.prod(one / (one - a / q**-k) for k in range(1, -n + 1)) return R.prod((one - a * q**k) for k in range(n)) @cached_function(key=lambda t, q: (_Partitions(t), q)) def q_jordan(t, q=None): r""" Return the `q`-Jordan number of `t`. If `q` is the power of a prime number, the output is the number of complete flags in `\GF{q}^N` (where `N` is the size of `t`) stable under a linear nilpotent endomorphism `f_t` whose Jordan type is given by `t`, i.e. such that for all `i`: .. MATH:: \dim (\ker f_t^i) = t[0] + \cdots + t[i-1] If `q` is unspecified, then it defaults to using the generator `q` for a univariate polynomial ring over the integers.
def P(i): return _Partitions([i]) if i else _Partitions([]) T = self.tensor_square()
def rectify(self, inner=None, verbose=False): """ Rectify ``self``. This gives the usual rectification of a skew standard tableau and gives a generalisation to skew semistandard tableaux. The usual construction uses jeu-de-taquin but here we use the Bender-Knuth involutions. EXAMPLES:: sage: st = SkewTableau([[None, None, None, 4],[None,None,1,6],[None,None,5],[2,3]]) sage: path_tableaux.SemistandardPathTableau(st).rectify() [(), (1,), (1, 1), (2, 1, 0), (3, 1, 0, 0), (3, 2, 0, 0, 0), (4, 2, 0, 0, 0, 0)] sage: path_tableaux.SemistandardPathTableau(st).rectify(verbose=True) [[(3, 2, 2), (3, 3, 2, 0), (3, 3, 2, 1, 0), (3, 3, 2, 2, 0, 0), (4, 3, 2, 2, 0, 0, 0), (4, 3, 3, 2, 0, 0, 0, 0), (4, 4, 3, 2, 0, 0, 0, 0, 0)], [(3, 2), (3, 3, 0), (3, 3, 1, 0), (3, 3, 2, 0, 0), (4, 3, 2, 0, 0, 0), (4, 3, 3, 0, 0, 0, 0), (4, 4, 3, 0, 0, 0, 0, 0)], [(3,), (3, 1), (3, 1, 1), (3, 2, 1, 0), (4, 2, 1, 0, 0), (4, 3, 1, 0, 0, 0), (4, 4, 1, 0, 0, 0, 0)], [(), (1,), (1, 1), (2, 1, 0), (3, 1, 0, 0), (3, 2, 0, 0, 0), (4, 2, 0, 0, 0, 0)]] TESTS:: sage: S = SemistandardSkewTableaux([[5,3,3],[3,1]],[3,2,2]) sage: LHS = [path_tableaux.SemistandardPathTableau(st.rectify()) for st in S] sage: RHS = [path_tableaux.SemistandardPathTableau(st).rectify() for st in S] sage: LHS == RHS True sage: st = SkewTableau([[None, None, None, 4],[None,None,1,6],[None,None,5],[2,3]]) sage: pt = path_tableaux.SemistandardPathTableau(st) sage: SP = [path_tableaux.SemistandardPathTableau(it) for it in StandardTableaux([3,2,2])] sage: len(set(pt.rectify(inner=ip) for ip in SP)) 1 """ if not self.is_skew(): return self n = len(self) pp = self[0] P = self.parent() if inner is None: initial = [pp[:r] for r in range(len(pp))] elif _Partitions(inner[-1]) == _Partitions(pp): initial = list(inner)[:-1] else: raise ValueError( f"the final shape{inner[-1]} must agree with the initial shape {pp}" ) r = len(initial) path = P.element_class(P, initial + list(self)) if verbose: rect = [self] for i in range(r): for j in range(n - 1): path = path.local_rule(r + j - i) if verbose: rect.append( P.element_class(P, list(path)[r - i - 1:r + n - i - 1])) if verbose: return rect else: return P.element_class(P, list(path)[:n])
def P(i): return _Partitions([i]) if i else _Partitions([]) T = self.tensor_square()