diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index b2f82fdae21..61c23512c0c 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -169,6 +169,7 @@ Comprehensive Module list sage/combinat/posets/incidence_algebras sage/combinat/posets/lattices sage/combinat/posets/linear_extensions + sage/combinat/posets/mobius_algebra sage/combinat/posets/poset_examples sage/combinat/posets/posets sage/combinat/q_analogues diff --git a/src/sage/algebras/catalog.py b/src/sage/algebras/catalog.py index 262e8113f44..06910534633 100644 --- a/src/sage/algebras/catalog.py +++ b/src/sage/algebras/catalog.py @@ -25,6 +25,7 @@ - :class:`algebras.Incidence ` - :class:`algebras.IwahoriHecke ` +- :class:`algebras.Mobius ` - :class:`algebras.Jordan ` - :class:`algebras.NilCoxeter @@ -56,6 +57,7 @@ lazy_import('sage.algebras.schur_algebra', 'SchurAlgebra', 'Schur') lazy_import('sage.algebras.commutative_dga', 'GradedCommutativeAlgebra', 'GradedCommutative') lazy_import('sage.combinat.posets.incidence_algebras', 'IncidenceAlgebra', 'Incidence') +lazy_import('sage.combinat.posets.mobius_algebra', 'MobiusAlgebra', 'Mobius') lazy_import('sage.combinat.free_prelie_algebra', 'FreePreLieAlgebra', 'FreePreLie') del lazy_import # We remove the object from here so it doesn't appear under tab completion diff --git a/src/sage/combinat/posets/__init__.py b/src/sage/combinat/posets/__init__.py index 9f5c8c89853..b54cd07d799 100644 --- a/src/sage/combinat/posets/__init__.py +++ b/src/sage/combinat/posets/__init__.py @@ -17,6 +17,8 @@ - :ref:`sage.combinat.posets.cartesian_product` +- :ref:`sage.combinat.posets.mobius_algebra` + - :ref:`sage.combinat.tamari_lattices` - :ref:`sage.combinat.interval_posets` - :ref:`sage.combinat.shard_order` diff --git a/src/sage/combinat/posets/lattices.py b/src/sage/combinat/posets/lattices.py index 7e316701d31..0b985eeb448 100644 --- a/src/sage/combinat/posets/lattices.py +++ b/src/sage/combinat/posets/lattices.py @@ -55,6 +55,7 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** + from sage.categories.finite_lattice_posets import FiniteLatticePosets from sage.combinat.posets.posets import Poset, FinitePoset from sage.combinat.posets.elements import (LatticePosetElement, @@ -1147,6 +1148,37 @@ def frattini_sublattice(self): return LatticePoset(self.subposet([self[x] for x in self._hasse_diagram.frattini_sublattice()])) + def moebius_algebra(self, R): + """ + Return the Mobius algebra of ``self`` over ``R``. + + EXAMPLES:: + + sage: L = posets.BooleanLattice(4) + sage: L.moebius_algebra(QQ) + Moebius algebra of Finite lattice containing 16 elements over Rational Field + """ + from sage.combinat.posets.moebius_algebra import MoebiusAlgebra + return MoebiusAlgebra(R, self) + + def quantum_moebius_algebra(self, q=None): + """ + Return the quantum Mobius algebra of ``self`` with parameter ``q``. + + INPUT: + + - ``q`` -- (optional) the deformation parameter `q` + + EXAMPLES:: + + sage: L = posets.BooleanLattice(4) + sage: L.quantum_moebius_algebra() + Quantum Moebius algebra of Finite lattice containing 16 elements + with q=q over Univariate Laurent Polynomial Ring in q over Integer Ring + """ + from sage.combinat.posets.moebius_algebra import QuantumMoebiusAlgebra + return QuantumMoebiusAlgebra(self, q) + ############################################################################ FiniteMeetSemilattice._dual_class = FiniteJoinSemilattice diff --git a/src/sage/combinat/posets/moebius_algebra.py b/src/sage/combinat/posets/moebius_algebra.py new file mode 100644 index 00000000000..1d57ca89062 --- /dev/null +++ b/src/sage/combinat/posets/moebius_algebra.py @@ -0,0 +1,732 @@ +# -*- coding: utf-8 -*- +r""" +Möbius Algebras +""" +#***************************************************************************** +# Copyright (C) 2014 Travis Scrimshaw , +# +# Distributed under the terms of the GNU General Public License (GPL) +# +# This code is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# The full text of the GPL is available at: +# +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.misc.cachefunc import cached_method +from sage.misc.bindable_class import BindableClass +from sage.misc.lazy_attribute import lazy_attribute +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.categories.algebras import Algebras +from sage.categories.realizations import Realizations, Category_realization_of_parent +from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets +from sage.combinat.posets.lattices import LatticePoset +from sage.combinat.free_module import CombinatorialFreeModule +from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing +from sage.rings.all import ZZ + +class BasisAbstract(CombinatorialFreeModule, BindableClass): + """ + Abstract base class for a basis. + """ + def __getitem__(self, x): + """ + Return the basis element indexed by ``x``. + + INPUT: + + - ``x`` -- an element of the lattice + + EXAMPLES:: + + sage: L = posets.BooleanLattice(4) + sage: E = L.moebius_algebra(QQ).E() + sage: E[5] + E[5] + sage: C = L.quantum_moebius_algebra().C() + sage: C[5] + C[5] + """ + L = self.realization_of()._lattice + return self.monomial(L(x)) + +class MoebiusAlgebra(Parent, UniqueRepresentation): + r""" + The möbius algebra of a lattice. + + Let `L` be a lattice. The *Möbius algebra* `M_L` was originally + constructed by Solomon and has a natural basis + `\{ E_x \mid x \in L \}` with multiplication given by + `E_x \cdot E_y = E_{x \vee y}`. Moreover this has a basis given by + orthogonal idempotents `\{ I_x \mid x \in L \}` (so + `I_x I_y = \delta_{xy} I_x` where `\delta` is the Kronecker delta) + related to the natural basis by + + .. MATH:: + + I_x = \sum_{y \leq x} \mu_L(y, x) E_x, + + where `\mu_L` is the Möbius function of `L`. + + REFERENCES: + + .. [Greene73] Curtis Greene. + *On the Möbius algebra of a partially ordered set*. + Advances in Mathematics, **10**, 1973. + :doi:`10.1016/0001-8708(73)90106-0`. + + .. [Etienne98] Gwihen Etienne. + *On the Möbius algebra of geometric lattices*. + European Journal of Combinatorics, **19**, 1998. + :doi:`10.1006/eujc.1998.0227`. + """ + def __init__(self, R, L): + """ + Initialize ``self``. + + TESTS:: + + sage: L = posets.BooleanLattice(4) + sage: M = L.moebius_algebra(QQ) + sage: TestSuite(M).run() + """ + if not L.is_lattice(): + raise ValueError("L must be a lattice") + cat = Algebras(R).Commutative().WithBasis() + if L in FiniteEnumeratedSets(): + cat = cat.FiniteDimensional() + self._lattice = L + self._category = cat + Parent.__init__(self, base=R, category=self._category.WithRealizations()) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: L = posets.BooleanLattice(4) + sage: L.moebius_algebra(QQ) + Moebius algebra of Finite lattice containing 16 elements over Rational Field + """ + return "Moebius algebra of {} over {}".format(self._lattice, self.base_ring()) + + def a_realization(self): + r""" + Return a particular realization of ``self`` (the `B`-basis). + + EXAMPLES:: + + sage: L = posets.BooleanLattice(4) + sage: M = L.moebius_algebra(QQ) + sage: M.a_realization() + Moebius algebra of Finite lattice containing 16 elements + over Rational Field in the natural basis + """ + return self.E() + + def lattice(self): + """ + Return the defining lattice of ``self``. + + EXAMPLES:: + + sage: L = posets.BooleanLattice(4) + sage: M = L.moebius_algebra(QQ) + sage: M.lattice() + Finite lattice containing 16 elements + sage: M.lattice() == L + True + """ + return self._lattice + + class E(BasisAbstract): + r""" + The natural basis of a Möbius algebra. + + Let `E_x` and `E_y` be basis elements of `M_L` for some lattice `L`. + Multiplication is given by `E_x E_y = E_{x \vee y}`. + """ + def __init__(self, M, prefix='E'): + """ + Initialize ``self``. + + TESTS:: + + sage: L = posets.BooleanLattice(4) + sage: M = L.moebius_algebra(QQ) + sage: TestSuite(M.E()).run() + """ + self._basis_name = "natural" + CombinatorialFreeModule.__init__(self, M.base_ring(), + tuple(M._lattice), + prefix=prefix, + category=MoebiusAlgebraBases(M)) + + @cached_method + def _to_idempotent_basis(self, x): + """ + Convert the element indexed by ``x`` to the idempotent basis. + + EXAMPLES:: + + sage: M = posets.BooleanLattice(4).moebius_algebra(QQ) + sage: E = M.E() + sage: all(E(E._to_idempotent_basis(x)) == E.monomial(x) + ....: for x in E.basis().keys()) + True + """ + M = self.realization_of() + I = M.idempotent() + return I.sum_of_monomials(M._lattice.order_ideal([x])) + + def product_on_basis(self, x, y): + """ + Return the product of basis elements indexed by ``x`` and ``y``. + + EXAMPLES:: + + sage: L = posets.BooleanLattice(4) + sage: E = L.moebius_algebra(QQ).E() + sage: E.product_on_basis(5, 14) + E[15] + sage: E.product_on_basis(2, 8) + E[10] + """ + return self.monomial(self.realization_of()._lattice.join(x, y)) + + @cached_method + def one(self): + """ + Return the element ``1`` of ``self``. + + EXAMPLES:: + + sage: L = posets.BooleanLattice(4) + sage: E = L.moebius_algebra(QQ).E() + sage: E.one() + E[0] + """ + elts = self.realization_of()._lattice.minimal_elements() + return self.sum_of_monomials(elts) + + natural = E + + class I(BasisAbstract): + """ + The (orthogonal) idempotent basis of a Möbius algebra. + + Let `I_x` and `I_y` be basis elements of `M_L` for some lattice `L`. + Multiplication is given by `I_x I_y = \delta_{xy} I_x` where + `\delta_{xy}` is the Kronecker delta. + """ + def __init__(self, M, prefix='I'): + """ + Initialize ``self``. + + TESTS:: + + sage: L = posets.BooleanLattice(4) + sage: M = L.moebius_algebra(QQ) + sage: TestSuite(M.I()).run() + """ + self._basis_name = "idempotent" + CombinatorialFreeModule.__init__(self, M.base_ring(), + tuple(M._lattice), + prefix=prefix, + category=MoebiusAlgebraBases(M)) + + ## Change of basis: + E = M.E() + self.module_morphism(self._to_natural_basis, + codomain=E, category=self.category(), + triangular='upper', unitriangular=True + ).register_as_coercion() + + E.module_morphism(E._to_idempotent_basis, + codomain=self, category=self.category(), + triangular='upper', unitriangular=True + ).register_as_coercion() + + + @cached_method + def _to_natural_basis(self, x): + """ + Convert the element indexed by ``x`` to the natural basis. + + EXAMPLES:: + + sage: M = posets.BooleanLattice(4).moebius_algebra(QQ) + sage: I = M.I() + sage: all(I(I._to_natural_basis(x)) == I.monomial(x) + ....: for x in I.basis().keys()) + True + """ + M = self.realization_of() + N = M.natural() + mobius = M._lattice.mobius_function + return N.sum_of_terms((y, mobius(y,x)) for y in M._lattice.order_ideal([x])) + + def product_on_basis(self, x, y): + """ + Return the product of basis elements indexed by ``x`` and ``y``. + + EXAMPLES:: + + sage: L = posets.BooleanLattice(4) + sage: I = L.moebius_algebra(QQ).I() + sage: I.product_on_basis(5, 14) + 0 + sage: I.product_on_basis(2, 2) + I[2] + """ + if x == y: + return self.monomial(x) + return self.zero() + + @cached_method + def one(self): + """ + Return the element ``1`` of ``self``. + + EXAMPLES:: + + sage: L = posets.BooleanLattice(4) + sage: I = L.moebius_algebra(QQ).I() + sage: I.one() + I[0] + I[1] + I[2] + I[3] + I[4] + I[5] + I[6] + I[7] + I[8] + + I[9] + I[10] + I[11] + I[12] + I[13] + I[14] + I[15] + """ + return self.sum_of_monomials(self.realization_of()._lattice) + + def __getitem__(self, x): + """ + Return the basis element indexed by ``x``. + + INPUT: + + - ``x`` -- an element of the lattice + + EXAMPLES:: + + sage: L = posets.BooleanLattice(4) + sage: I = L.moebius_algebra(QQ).I() + sage: I[5] + I[5] + """ + L = self.realization_of()._lattice + return self.monomial(L(x)) + + idempotent = I + +class QuantumMoebiusAlgebra(Parent, UniqueRepresentation): + r""" + The quantum Möbius algebra of a lattice. + + Let `L` be a lattice, and we define the *quantum Möbius algebra* `M_L(q)` + as the algebra with basis `\{ E_x \mid x \in L \}` with + multiplication given by + + .. MATH:: + + E_x E_y = \sum_{z \geq a \geq x \vee y} \mu_L(a, z) + q^{\operatorname{crk} a} E_z, + + where `\mu_L` is the Möbius function of `L` and `\operatorname{crk}` + is the corank function (i.e., `\operatorname{crk} a = + \operatorname{rank} L - \operatorname{rank}` a). At `q = 1`, this + reduces to the multiplication formula originally given by Solomon. + """ + def __init__(self, L, q=None): + """ + Initialize ``self``. + + TESTS:: + + sage: L = posets.BooleanLattice(4) + sage: M = L.quantum_moebius_algebra() + sage: TestSuite(M).run() # long time + """ + if not L.is_lattice(): + raise ValueError("L must be a lattice") + if q is None: + q = LaurentPolynomialRing(ZZ, 'q').gen() + self._q = q + R = q.parent() + cat = Algebras(R).WithBasis() + if L in FiniteEnumeratedSets(): + cat = cat.Commutative().FiniteDimensional() + self._lattice = L + self._category = cat + Parent.__init__(self, base=R, category=self._category.WithRealizations()) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: L = posets.BooleanLattice(4) + sage: L.quantum_moebius_algebra() + Quantum Moebius algebra of Finite lattice containing 16 elements + with q=q over Univariate Laurent Polynomial Ring in q over Integer Ring + """ + return "Quantum Moebius algebra of {} with q={} over {}".format( + self._lattice, self._q, self.base_ring()) + + def a_realization(self): + r""" + Return a particular realization of ``self`` (the `B`-basis). + + EXAMPLES:: + + sage: L = posets.BooleanLattice(4) + sage: M = L.quantum_moebius_algebra() + sage: M.a_realization() + Quantum Moebius algebra of Finite lattice containing 16 elements + with q=q over Univariate Laurent Polynomial Ring in q + over Integer Ring in the natural basis + """ + return self.E() + + def lattice(self): + """ + Return the defining lattice of ``self``. + + EXAMPLES:: + + sage: L = posets.BooleanLattice(4) + sage: M = L.quantum_moebius_algebra() + sage: M.lattice() + Finite lattice containing 16 elements + sage: M.lattice() == L + True + """ + return self._lattice + + class E(BasisAbstract): + r""" + The natural basis of a quantum Möbius algebra. + + Let `E_x` and `E_y` be basis elements of `M_L` for some lattice `L`. + Multiplication is given by + + .. MATH:: + + E_x E_y = \sum_{z \geq a \geq x \vee y} \mu_L(a, z) + q^{\operatorname{crk} a} E_z, + + where `\mu_L` is the Möbius function of `L` and `\operatorname{crk}` + is the corank function (i.e., `\operatorname{crk} a = + \operatorname{rank} L - \operatorname{rank}` a). + """ + def __init__(self, M, prefix='E'): + """ + Initialize ``self``. + + TESTS:: + + sage: L = posets.BooleanLattice(4) + sage: M = L.quantum_moebius_algebra() + sage: TestSuite(M.E()).run() # long time + """ + self._basis_name = "natural" + CombinatorialFreeModule.__init__(self, M.base_ring(), + tuple(M._lattice), + prefix=prefix, + category=MoebiusAlgebraBases(M)) + + def product_on_basis(self, x, y): + """ + Return the product of basis elements indexed by ``x`` and ``y``. + + EXAMPLES:: + + sage: L = posets.BooleanLattice(4) + sage: E = L.quantum_moebius_algebra().E() + sage: E.product_on_basis(5, 14) + E[15] + sage: E.product_on_basis(2, 8) + q^2*E[10] + (q-q^2)*E[11] + (q-q^2)*E[14] + (1-2*q+q^2)*E[15] + """ + L = self.realization_of()._lattice + q = self.realization_of()._q + mobius = L.mobius_function + rank = L.rank_function() + R = L.rank() + j = L.join(x,y) + return self.sum_of_terms(( z, mobius(a,z) * q**(R - rank(a)) ) + for z in L.order_filter([j]) + for a in L.closed_interval(j, z)) + + @cached_method + def one(self): + """ + Return the element ``1`` of ``self``. + + EXAMPLES:: + + sage: L = posets.BooleanLattice(4) + sage: E = L.quantum_moebius_algebra().E() + sage: all(E.one() * b == b for b in E.basis()) + True + """ + L = self.realization_of()._lattice + q = self.realization_of()._q + mobius = L.mobius_function + rank = L.rank_function() + R = L.rank() + return self.sum_of_terms((x, mobius(y,x) * q**(rank(y) - R)) + for x in L for y in L.order_ideal([x])) + + natural = E + + class C(BasisAbstract): + r""" + The characteristic basis of a quantum Möbius algebra. + + The characteristic basis `\{ C_x \mid x \in L \}` of `M_L` + for some lattice `L` is defined by + + .. MATH:: + + C_x = \sum_{a \geq x} P(F^x; q) E_a, + + where `F^x = \{ y \in L \mid y \geq x \}` is the principal order + filter of `x` and `P(F^x; q)` is the characteristic polynomial + of the (sub)poset `F^x`. + """ + def __init__(self, M, prefix='C'): + """ + Initialize ``self``. + + TESTS:: + + sage: L = posets.BooleanLattice(3) + sage: M = L.quantum_moebius_algebra() + sage: TestSuite(M.C()).run() # long time + """ + self._basis_name = "characteristic" + CombinatorialFreeModule.__init__(self, M.base_ring(), + tuple(M._lattice), + prefix=prefix, + category=MoebiusAlgebraBases(M)) + + ## Change of basis: + E = M.E() + phi = self.module_morphism(self._to_natural_basis, + codomain=E, category=self.category(), + triangular='lower', unitriangular=True) + + phi.register_as_coercion() + (~phi).register_as_coercion() + + @cached_method + def _to_natural_basis(self, x): + """ + Convert the element indexed by ``x`` to the natural basis. + + EXAMPLES:: + + sage: M = posets.BooleanLattice(4).quantum_moebius_algebra() + sage: C = M.C() + sage: all(C(C._to_natural_basis(x)) == C.monomial(x) + ....: for x in C.basis().keys()) + True + """ + M = self.realization_of() + N = M.natural() + q = M._q + R = M.base_ring() + L = M._lattice + poly = lambda x,y: L.subposet(L.closed_interval(x, y)).characteristic_polynomial() + # This is a workaround until #17554 is fixed... + subs = lambda p,q: R.sum( c * q**e for e,c in enumerate(p.list()) ) + # ...at which point, we can do poly(x,y)(q=q) + return N.sum_of_terms((y, subs(poly(x,y), q)) + for y in L.order_filter([x])) + + characteristic_basis = C + + class KL(BasisAbstract): + """ + The Kazhdan-Lusztig basis of a quantum Möbius algebra. + + The Kazhdan-Lusztig basis `\{ B_x \mid x \in L \}` of `M_L` + for some lattice `L` is defined by + + .. MATH:: + + B_x = \sum_{y \geq x} P_{x,y}(q) E_a, + + where `P_{x,y}(q)` is the Kazhdan-Lusztig polynomial of `L`, + following the definition given in [EPW14]_. + + EXAMPLES: + + We construct some examples of Proposition 4.5 of [EPW14]_:: + + sage: M = posets.BooleanLattice(4).quantum_moebius_algebra() + sage: KL = M.KL() + sage: KL[4] * KL[5] + (q^2+q^3)*KL[5] + (q+2*q^2+q^3)*KL[7] + (q+2*q^2+q^3)*KL[13] + + (1+3*q+3*q^2+q^3)*KL[15] + sage: KL[4] * KL[15] + (1+3*q+3*q^2+q^3)*KL[15] + sage: KL[4] * KL[10] + (q+3*q^2+3*q^3+q^4)*KL[14] + (1+4*q+6*q^2+4*q^3+q^4)*KL[15] + """ + def __init__(self, M, prefix='KL'): + """ + Initialize ``self``. + + TESTS:: + + sage: L = posets.BooleanLattice(4) + sage: M = L.quantum_moebius_algebra() + sage: TestSuite(M.KL()).run() # long time + """ + self._basis_name = "Kazhdan-Lusztig" + CombinatorialFreeModule.__init__(self, M.base_ring(), + tuple(M._lattice), + prefix=prefix, + category=MoebiusAlgebraBases(M)) + + ## Change of basis: + E = M.E() + phi = self.module_morphism(self._to_natural_basis, + codomain=E, category=self.category(), + triangular='lower', unitriangular=True) + + phi.register_as_coercion() + (~phi).register_as_coercion() + + @cached_method + def _to_natural_basis(self, x): + """ + Convert the element indexed by ``x`` to the natural basis. + + EXAMPLES:: + + sage: M = posets.BooleanLattice(4).quantum_moebius_algebra() + sage: KL = M.KL() + sage: all(KL(KL._to_natural_basis(x)) == KL.monomial(x) # long time + ....: for x in KL.basis().keys()) + True + """ + M = self.realization_of() + L = M._lattice + E = M.E() + q = M._q + R = M.base_ring() + rank = L.rank_function() + # This is a workaround until #17554 is fixed... + subs = lambda p,q: R.sum( c * q**e for e,c in enumerate(p.list()) ) + return E.sum_of_terms((y, q**(rank(y) - rank(x)) * + subs(L.kazhdan_lusztig_polynomial(x, y), q**-2)) + for y in L.order_filter([x])) + + kazhdan_lusztig = KL + +class MoebiusAlgebraBases(Category_realization_of_parent): + r""" + The category of bases of a Möbius algebra. + + INPUT: + + - ``base`` -- a Möbius algebra + + TESTS:: + + sage: from sage.combinat.posets.moebius_algebra import MoebiusAlgebraBases + sage: M = posets.BooleanLattice(4).moebius_algebra(QQ) + sage: bases = MoebiusAlgebraBases(M) + sage: M.E() in bases + True + """ + def _repr_(self): + r""" + Return the representation of ``self``. + + EXAMPLES:: + + sage: from sage.combinat.posets.moebius_algebra import MoebiusAlgebraBases + sage: M = posets.BooleanLattice(4).moebius_algebra(QQ) + sage: MoebiusAlgebraBases(M) + Category of bases of Moebius algebra of Finite lattice + containing 16 elements over Rational Field + """ + return "Category of bases of {}".format(self.base()) + + def super_categories(self): + r""" + The super categories of ``self``. + + EXAMPLES:: + + sage: from sage.combinat.posets.moebius_algebra import MoebiusAlgebraBases + sage: M = posets.BooleanLattice(4).moebius_algebra(QQ) + sage: bases = MoebiusAlgebraBases(M) + sage: bases.super_categories() + [Category of finite dimensional commutative algebras with basis over Rational Field, + Category of realizations of Moebius algebra of Finite lattice + containing 16 elements over Rational Field] + """ + return [self.base()._category, Realizations(self.base())] + + class ParentMethods: + def _repr_(self): + """ + Text representation of this basis of a Möbius algebra. + + EXAMPLES:: + + sage: M = posets.BooleanLattice(4).moebius_algebra(QQ) + sage: M.E() + Moebius algebra of Finite lattice containing 16 elements + over Rational Field in the natural basis + sage: M.I() + Moebius algebra of Finite lattice containing 16 elements + over Rational Field in the idempotent basis + """ + return "{} in the {} basis".format(self.realization_of(), self._basis_name) + + def product_on_basis(self, x, y): + """ + Return the product of basis elements indexed by ``x`` and ``y``. + + EXAMPLES:: + + sage: L = posets.BooleanLattice(4) + sage: C = L.quantum_moebius_algebra().C() + sage: C.product_on_basis(5, 14) + q^3*C[15] + sage: C.product_on_basis(2, 8) + q^4*C[10] + """ + R = self.realization_of().a_realization() + return self(R(self.monomial(x)) * R(self.monomial(y))) + + @cached_method + def one(self): + """ + Return the element ``1`` of ``self``. + + EXAMPLES:: + + sage: L = posets.BooleanLattice(4) + sage: C = L.quantum_moebius_algebra().C() + sage: all(C.one() * b == b for b in C.basis()) + True + """ + R = self.realization_of().a_realization() + return self(R.one()) + + class ElementMethods: + pass + diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index bd2afa2c60c..6bfdf6434fe 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -190,6 +190,7 @@ :meth:`~FinitePoset.is_linear_extension` | Return whether ``l`` is a linear extension of ``self``. :meth:`~FinitePoset.isomorphic_subposets_iterator` | Return an iterator over the subposets isomorphic to another poset. :meth:`~FinitePoset.isomorphic_subposets` | Return all subposets isomorphic to another poset. + :meth:`~FinitePoset.kazhdan_lusztig_polynomial` | Return the Kazhdan-Lusztig polynomial `P_{x,y}(q)` of ``self``. :meth:`~FinitePoset.lequal_matrix` | Computes the matrix whose ``(i,j)`` entry is 1 if ``self.linear_extension()[i] < self.linear_extension()[j]`` and 0 otherwise. :meth:`~FinitePoset.level_sets` | Return a list l such that l[i+1] is the set of minimal elements of the poset obtained by removing the elements in l[0], l[1], ..., l[i]. :meth:`~FinitePoset.linear_extension` | Return a linear extension of this poset. @@ -229,6 +230,7 @@ from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute from sage.misc.misc_c import prod +from sage.functions.other import floor from sage.categories.category import Category from sage.categories.sets_cat import Sets from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets @@ -240,6 +242,7 @@ from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.rings.polynomial.polynomial_ring import polygen +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.graphs.digraph import DiGraph from sage.graphs.digraph_generators import digraphs from sage.combinat.posets.hasse_diagram import HasseDiagram @@ -5684,6 +5687,137 @@ def incidence_algebra(self, R, prefix='I'): from sage.combinat.posets.incidence_algebras import IncidenceAlgebra return IncidenceAlgebra(R, self, prefix) + @cached_method(key=lambda self,x,y,l: (x,y)) + def _kl_poly(self, x=None, y=None, canonical_labels=None): + r""" + Cached Kazhdan-Lusztig polynomial of ``self`` for generic `q`. + + .. SEEALSO:: + + :meth:`kazhdan_lusztig_polynomial` + + EXAMPLES:: + + sage: L = posets.SymmetricGroupWeakOrderPoset(4) + sage: L._kl_poly() + 1 + sage: x = '2314' + sage: y = '3421' + sage: L._kl_poly(x, y) + -q + 1 + + AUTHORS: + + - Travis Scrimshaw (27-12-2014) + """ + R = PolynomialRing(ZZ, 'q') + q = R.gen(0) + + # Handle some special cases + if self.cardinality() == 0: + return q.parent().zero() + if not self.rank(): + return q.parent().one() + + if canonical_labels is None: + canonical_labels = x is None and y is None + + if x is not None or y is not None: + if x == y: + return q.parent().one() + if x is None: + x = self.minimal_elements()[0] + if y is None: + y = self.maximal_elements()[0] + if not self.le(x, y): + return q.parent().zero() + P = self.subposet(self.interval(x, y)) + return P.kazhdan_lusztig_polynomial(q=q, canonical_labels=canonical_labels) + + min_elt = self.minimal_elements()[0] + if canonical_labels: + sublat = lambda P: self.subposet(P).canonical_label() + else: + sublat = lambda P: self.subposet(P) + poly = -sum(sublat(self.order_ideal([x])).characteristic_polynomial() + * sublat(self.order_filter([x])).kazhdan_lusztig_polynomial() + for x in self if x != min_elt) + tr = floor(self.rank()/2) + 1 + ret = poly.truncate(tr) + return ret(q=q) + + def kazhdan_lusztig_polynomial(self, x=None, y=None, q=None, canonical_labels=None): + r""" + Return the Kazhdan-Lusztig polynomial `P_{x,y}(q)` of ``self``. + + We follow the definition given in [EPW14]_. Let `G` denote a + graded poset with unique minimal and maximal elements and `\chi_G` + denote the characteristic polynomial of `G`. Let `I_x` and `F^x` + denote the principal order ideal and filter of `x` respectively. + Define the *Kazhdan-Lusztig polynomial* of `G` as the unique + polynomial `P_G(q)` satisfying the following: + + 1. If `\operatorname{rank} G = 0`, then `P_G(q) = 1`. + 2. If `\operatorname{rank} G > 0`, then `\deg P_G(q) < + \frac{1}{2} \operatorname{rank} G`. + 3. We have + + .. MATH:: + + q^{\operatorname{rank} G} P_G(q^{-1}) + = \sum_{x \in G} \chi_{I_x}(q) P_{F^x}(q). + + We then extend this to `P_{x,y}(q)` by considering the subposet + corresponding to the (closed) interval `[x, y]`. We also + define `P_{\emptyset}(q) = 0` (so if `x \not\leq y`, + then `P_{x,y}(q) = 0`). + + INPUT: + + - ``q`` -- (default: `q \in \ZZ[q]`) the indeterminate `q` + - ``x`` -- (default: the minimal element) the element `x` + - ``y`` -- (default: the maximal element) the element `y` + - ``canonical_labels`` -- (optional) for subposets, use the + canonical labeling (this can limit recursive calls for posets + with large amounts of symmetry, but producing the labeling + takes time); if not specified, this is ``True`` if ``x`` + and ``y`` are both not specified and ``False`` otherwise + + EXAMPLES:: + + sage: L = posets.BooleanLattice(3) + sage: L.kazhdan_lusztig_polynomial() + 1 + + :: + + sage: L = posets.SymmetricGroupWeakOrderPoset(4) + sage: L.kazhdan_lusztig_polynomial() + 1 + sage: x = '2314' + sage: y = '3421' + sage: L.kazhdan_lusztig_polynomial(x, y) + -q + 1 + sage: L.kazhdan_lusztig_polynomial(x, y, var('t')) + -t + 1 + + REFERENCES: + + .. [EPW14] Ben Elias, Nicholas Proudfoot, and Max Wakefield. + *The Kazhdan-Lusztig polynomial of a matroid*. 2014. + :arxiv:`1412.7408`. + + AUTHORS: + + - Travis Scrimshaw (27-12-2014) + """ + if not self.is_ranked(): + raise ValueError("poset is not ranked") + if q is None: + q = PolynomialRing(ZZ, 'q').gen(0) + poly = self._kl_poly(x, y, canonical_labels) + return poly(q=q) + FinitePoset._dual_class = FinitePoset ##### Posets #####