From c130aef044c912b588d7fc51b235ba7102005a9b Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 12 Apr 2023 12:50:21 +0900 Subject: [PATCH 1/8] Initial implementation of down-up algebras and their Verma modules. --- src/doc/en/reference/algebras/index.rst | 1 + src/doc/en/reference/references/index.rst | 8 + src/sage/algebras/catalog.py | 2 + src/sage/algebras/down_up_algebra.py | 698 ++++++++++++++++++++++ 4 files changed, 709 insertions(+) create mode 100644 src/sage/algebras/down_up_algebra.py diff --git a/src/doc/en/reference/algebras/index.rst b/src/doc/en/reference/algebras/index.rst index 1aae4090861..acc6324490f 100644 --- a/src/doc/en/reference/algebras/index.rst +++ b/src/doc/en/reference/algebras/index.rst @@ -48,6 +48,7 @@ Named associative algebras sage/algebras/clifford_algebra sage/algebras/cluster_algebra sage/combinat/descent_algebra + sage/algebras/down_up_algebra fusion_rings sage/algebras/hall_algebra sage/combinat/posets/incidence_algebras diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index d19d4616a61..2a9b66bb2eb 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -1123,6 +1123,10 @@ REFERENCES: Achtzelle (Oktatope)", Verhand. Konik. Akad. Wetenschap, Erste Sectie, 10 (1910) +.. [BR1998] Georgia Benkart and Tom Roby. *Down-up algebras*. + J. Algebra, **209** no. 1 (1999), pp. 305-335. + :doi:`10.1006/jabr.1998.7511`. + .. [Br2000] Kenneth S. Brown, *Semigroups, rings, and Markov chains*, :arxiv:`math/0006145v1`. @@ -1615,6 +1619,10 @@ REFERENCES: \J. Algebr. Comb. **39** (2014) pp. 17-51. :doi:`10.1007/s10801-013-0437-x`, :arxiv:`1108.1776`. +.. [CM2000] Paula A.A.B. Carvalho and Ian M. Musson. *Down-up algebras and + their representation theory*. J. Algebra. **228** no. 1, (2000), + pp. 286-310. :doi:`10.1006/jabr.1999.8263` + .. [CM2012] \M. Cabanes, I. Marin, *On ternary quotients of cubic Hecke algebras*, Comm. Math. Phys. (2012), Volume 314, Issue 1, pp 57-92. :doi:`10.1007/s00220-012-1519-7`, :arxiv:`1010.1465`. diff --git a/src/sage/algebras/catalog.py b/src/sage/algebras/catalog.py index 0896d684938..83f06e26cf6 100644 --- a/src/sage/algebras/catalog.py +++ b/src/sage/algebras/catalog.py @@ -23,6 +23,7 @@ - :class:`algebras.Descent ` - :class:`algebras.DifferentialWeyl ` +- :class:`algebras.DownUp ` - :class:`algebras.Exterior ` - :class:`algebras.FiniteDimensional ` @@ -134,6 +135,7 @@ lazy_import('sage.algebras.quantum_groups.quantum_group_gap', 'QuantumGroup') lazy_import('sage.algebras.quantum_groups.ace_quantum_onsager', 'ACEQuantumOnsagerAlgebra', 'AlternatingCentralExtensionQuantumOnsager') +lazy_import('sage.algebras.down_up_algebra', 'DownUpAlgebra', 'DownUp') lazy_import('sage.algebras.yangian', 'Yangian') del lazy_import # We remove the object from here so it doesn't appear under tab completion diff --git a/src/sage/algebras/down_up_algebra.py b/src/sage/algebras/down_up_algebra.py new file mode 100644 index 00000000000..e71a28f4e6d --- /dev/null +++ b/src/sage/algebras/down_up_algebra.py @@ -0,0 +1,698 @@ +""" +Down-Up Algebras + +AUTHORS: + +- Travis Scrimshaw (2023-4): initial version +""" + +#***************************************************************************** +# Copyright (C) 2023 Travis Scrimshaw +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.misc.cachefunc import cached_method +from sage.categories.algebras import Algebras +from sage.categories.modules import Modules +from sage.categories.rings import Rings +from sage.combinat.free_module import CombinatorialFreeModule +from sage.sets.non_negative_integers import NonNegativeIntegers +from sage.categories.sets_cat import cartesian_product +from sage.sets.family import Family +from sage.misc.lazy_list import lazy_list +from sage.misc.misc_c import prod +from sage.modules.free_module import FreeModule + +class DownUpAlgebra(CombinatorialFreeModule): + r""" + The down-up algebra. + + Let `R` be a commutative ring, and let `\alpha, \beta, \gamma \in R`. + The *down-up algebra* is the associative unital algebra + `DU(\alpha, \beta, \gamma)` generated by `d, u` with relations + + .. MATH:: + + \begin{aligned} + d^2u & = \alpha dud + \beta ud^2 + \gamma d, + \\ du^2 & = \alpha udu + \beta u^2d + \gamma u. + \end{aligned} + + The down-up algebra has a PBW-type basis given by + + .. MATH:: + + \{ u^i (du)^j d^k \mid i,j,k \in \ZZ_{\geq 0} \}. + + This algebra originates in the study of posets. For a poset `P`, + we define operators acting on `R[P]` by + + .. MATH:: + + d(y) = \sum_x x \qquad\qquad u(y) = \sum_z z, + + where `y` covers `x` and `z` covers `y`. For `r`-differential posets + we have `du - ud = r 1` and afford a representation of a + :class:`Weyl algebra `. + + EXAMPLES: + + We begin by constructing the down-up algebra and perform some + basic computations:: + + sage: R. = QQ[] + sage: DU = algebras.DownUp(a, b, g) + sage: d, u = DU.gens() + sage: d * u + (d*u) + sage: u * d + u*d + sage: d^2 * u + b*u*d^2 + a*(d*u)*d + g*d + sage: d * u^2 + b*u^2*d + a*u*(d*u) + g*u + + We verify some examples of Proposition 3.5 in [BR1988]_, which states + that the 0-th degree part is commutative:: + + sage: DU0 = [u^i * (d*u)^j * d^i for i,j in + ....: cartesian_product([range(3), range(3)])] + sage: all(x.degree() == 0 for x in DU0) + True + sage: all(x * y == y * x for x, y in cartesian_product([DU0, DU0])) + True + + We verify that `DU(2, -1, \gamma)` can be desribed as the universal + enveloping algebra of the 3-dimensional Lie algebra spanned by `x,y,z` + satisfying `z = [x, y]`, `[x, z] = \gamma x`, and `[z, y] = \gamma y`:: + + sage: R. = QQ[] + sage: L = LieAlgebra(R, {('x','y'): {'z': 1}, ('x','z'): {'x': g}, ('z','y'): {'y': g}}, names='x,y,z') + sage: x, y, z = L.basis() + sage: (L[x, y], L[x, z], L[z, y]) + (z, g*x, g*y) + sage: x,y,z = L.pbw_basis().gens() + sage: x^2*y - 2*x*y*x + y*x^2 == g*x + True + sage: x*y^2 - 2*y*x*y + y^2*x == g*y + True + sage: DU = algebras.DownUp(2, -1, g) + sage: d, u = DU.gens() + sage: d^2*u - 2*d*u*d + u*d^2 == g*d + True + sage: d*u^2 - 2*u*d*u + u^2*d == g*u + True + + .. TODO:: + + Implement the homogenized version. + + REFERENCES: + + - [BR1998]_ + - [CM2000]_ + """ + @staticmethod + def __classcall_private__(cls, alpha, beta, gamma, base_ring=None): + r""" + Standardize input to ensure a unique representation. + + TESTS:: + + sage: R. = QQ[] + sage: DU1 = algebras.DownUp(a, 1, g) + sage: DU2 = algebras.DownUp(a, R.one(), g) + sage: DU3 = algebras.DownUp(a, 1, g, R) + sage: DU1 is DU2 and DU2 is DU3 + True + """ + if base_ring is None: + from sage.structure.element import get_coercion_model + base_ring = get_coercion_model().common_parent(alpha, beta, gamma) + if base_ring not in Rings().Commutative(): + raise TypeError("base ring must be a commutative ring") + alpha = base_ring(alpha) + beta = base_ring(beta) + gamma = base_ring(gamma) + return super().__classcall__(cls, alpha, beta, gamma, base_ring=base_ring) + + def __init__(self, alpha, beta, gamma, base_ring): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: R. = QQ[] + sage: DU = algebras.DownUp(a, b, g) + sage: d, u = DU.gens() + sage: elts = [d, u, d^2, u^2, d*u, u*d] + sage: TestSuite(DU).run(elements=elts) + sage: elts += [d*(d*u)*u] + sage: TestSuite(DU).run(elements=elts) # long time + """ + self._alpha = alpha + self._beta = beta + self._gamma = gamma + cat = Algebras(base_ring).WithBasis().Graded() + if self._beta: + from sage.categories.domains import Domains + cat &= Domains() + indices = cartesian_product([NonNegativeIntegers()] * 3) + CombinatorialFreeModule.__init__(self, base_ring, indices, category=cat, sorting_reverse=True) + self._assign_names(['d', 'u']) + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: DU = algebras.DownUp(1, 2, 3) + sage: DU + Down-Up algebra with parameters (1, 2, 3) over Integer Ring + """ + return "Down-Up algebra with parameters ({}, {}, {}) over {}".format( + self._alpha, self._beta, self._gamma, self.base_ring()) + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: R. = QQ[] + sage: DU = algebras.DownUp(a, b, g) + sage: latex(DU) + \mathcal{DU}(a,b,g) + """ + return "\\mathcal{DU}(%s,%s,%s)"%(self._alpha, self._beta, self._gamma) + + def _repr_term(self, m): + r""" + Return a string representation of the basis element indexed by ``m``. + + EXAMPLES:: + + sage: R. = QQ[] + sage: DU = algebras.DownUp(a, b, g) + sage: I = DU.indices() + sage: DU._repr_term(I([1,0,5])) + 'u*d^5' + sage: DU._repr_term(I([6,3,1])) + 'u^6*(d*u)^3*d' + sage: DU._repr_term(I([0,1,2])) + '(d*u)*d^2' + sage: DU._repr_term(I([0,0,0])) + '1' + """ + if not any(m): + return '1' + ret = '' + for i, s in enumerate(['u', '(d*u)', 'd']): + if not m[i]: + continue + if ret: + ret += '*' + if m[i] == 1: + ret += s + else: + ret += f"{s}^{m[i]}" + return ret + + def _latex_term(self, m): + r""" + Return a latex representation for the basis element indexed by ``m``. + + EXAMPLES:: + + sage: R. = QQ[] + sage: DU = algebras.DownUp(a, b, g) + sage: I = DU.indices() + sage: DU._latex_term(I([1,0,5])) + 'ud^{5}' + sage: DU._latex_term(I([6,3,1])) + 'u^{6}(du)^{3}d' + sage: DU._latex_term(I([0,1,2])) + '(du)d^{2}' + sage: DU._latex_term(I([0,0,0])) + '1' + """ + if all(val == 0 for val in m): + return '1' + ret = '' + for i, s in enumerate(['u', '(du)', 'd']): + if not m[i]: + continue + if m[i] == 1: + ret += s + else: + ret += f"{s}^{{{m[i]}}}" + return ret + + @cached_method + def algebra_generators(self): + r""" + Return the algebra generators of ``self``. + + EXAMPLES:: + + sage: DU = algebras.DownUp(2, 3, 4) + sage: dict(DU.algebra_generators()) + {'d': d, 'u': u} + """ + u = self.monomial(self._indices([1,0,0])) + d = self.monomial(self._indices([0,0,1])) + return Family({'d': d, 'u': u}) + + @cached_method + def gens(self): + r""" + Return the generators of ``self``. + + EXAMPLES:: + + sage: DU = algebras.DownUp(2, 3, 4) + sage: DU.gens() + (d, u) + """ + G = self.algebra_generators() + return (G['d'], G['u']) + + @cached_method + def one_basis(self): + r""" + Return the index of the basis element of `1`. + + EXAMPLES:: + + sage: DU = algebras.DownUp(2, 3, 4) + sage: DU.one_basis() + (0, 0, 0) + """ + return self._indices([0, 0, 0]) + + def product_on_basis(self, m1, m2): + r""" + Return the product of the basis elements indexed by ``m1`` and ``m2``. + + EXAMPLES:: + + sage: R. = QQ[] + sage: DU = algebras.DownUp(a, b, g) + sage: I = DU.indices() + sage: DU.product_on_basis(I([2,0,0]), I([4,0,0])) + u^6 + sage: DU.product_on_basis(I([2,0,0]), I([0,4,0])) + u^2*(d*u)^4 + sage: DU.product_on_basis(I([2,0,0]), I([0,0,4])) + u^2*d^4 + sage: DU.product_on_basis(I([0,2,0]), I([0,4,0])) + (d*u)^6 + sage: DU.product_on_basis(I([0,2,0]), I([0,0,4])) + (d*u)^2*d^4 + sage: DU.product_on_basis(I([0,0,2]), I([0,0,4])) + d^6 + sage: DU.product_on_basis(I([5,3,1]), I([1,0,4])) + u^5*(d*u)^4*d^4 + + sage: DU.product_on_basis(I([0,1,0]), I([1,0,0])) + b*u^2*d + a*u*(d*u) + g*u + sage: DU.product_on_basis(I([0,0,2]), I([1,0,0])) + b*u*d^2 + a*(d*u)*d + g*d + sage: DU.product_on_basis(I([0,0,1]), I([2,0,0])) + b*u^2*d + a*u*(d*u) + g*u + sage: DU.product_on_basis(I([0,0,1]), I([0,1,0])) + b*u*d^2 + a*(d*u)*d + g*d + + sage: DU.product_on_basis(I([0,1,0]), I([3,0,0])) + (a^2*b+b^2)*u^4*d + (a^3+2*a*b)*u^3*(d*u) + (a^2*g+a*g+b*g+g)*u^3 + sage: DU.product_on_basis(I([1,1,3]), I([0,1,1])) + (a^2*b^2+b^3)*u^3*d^6 + (a^3*b+a*b^2)*u^2*(d*u)*d^5 + (a^2*b*g+b^2*g)*u^2*d^5 + + (a^3+2*a*b)*u*(d*u)^2*d^4 + (a^2*g+a*g+b*g+g)*u*(d*u)*d^4 + """ + # Check trivial cases + if not any(m1): + return self.monomial(m2) + if not any(m2): + return self.monomial(m1) + + u1, du1, d1 = m1 + u2, du2, d2 = m2 + I = self._indices + + if not d1: + if not u2: + return self.monomial(I([u1, du1+du2, d2])) + # else u2 > 0 + if not du1: + return self.monomial(I([u1+u2, du2, d2])) + # Perform du * u reduction + lhs = self.monomial(I([u1, du1-1, 0])) + mid = self._from_dict({I([1,1,0]): self._alpha, + I([2,0,1]): self._beta, + I([1,0,0]): self._gamma}) + rhs = self.monomial(I([u2-1, du2, d2])) + else: # d1 > 0 + if not u2: + if not du2: + return self.monomial(I([u1, du1, d1+d2])) + # Perform a d * du reduction + lhs = self.monomial(I([u1, du1, d1-1])) + mid = self._from_dict({I([0,1,1]): self._alpha, + I([1,0,2]): self._beta, + I([0,0,1]): self._gamma}) + rhs = self.monomial(I([0, du2-1, d2])) + elif u2 > 1: + # Perform d * u^2 reduction + lhs = self.monomial(I([u1, du1, d1-1])) + mid = self._from_dict({I([1,1,0]): self._alpha, + I([2,0,1]): self._beta, + I([1,0,0]): self._gamma}) + rhs = self.monomial(I([u2-2, du2, d2])) + elif u2 == 1: + if d1 == 1: + return self.monomial(I([u1, du1+du2+1, d2])) + # Perform a d^2 * u reduction + lhs = self.monomial(I([u1, du1, d1-2])) + mid = self._from_dict({I([0,1,1]): self._alpha, + I([1,0,2]): self._beta, + I([0,0,1]): self._gamma}) + rhs = self.monomial(I([0, du2, d2])) + + if lhs == self.one(): + if rhs == self.one(): + return mid + return mid * rhs + if rhs == self.one(): + return lhs * mid + return lhs * mid * rhs + + def degree_on_basis(self, m): + r""" + Return the degree of the basis element indexed by ``m``. + + EXAMPLES:: + + sage: R. = QQ[] + sage: DU = algebras.DownUp(a, b, g) + sage: I = DU.indices() + sage: DU.degree_on_basis(I([0, 3, 2])) + -2 + sage: DU.degree_on_basis(I([2, 3, 0])) + 2 + sage: DU.degree_on_basis(I([2, 0, 3])) + -1 + sage: DU.degree_on_basis(I([3, 10, 3])) + 0 + """ + return m[0] - m[2] + + def verma_module(self, la): + r""" + Return the Verma module `V(\lambda)` of ``self``. + + EXAMPLES:: + + sage: R. = QQ[] + sage: DU = algebras.DownUp(a, b, g) + sage: DU.verma_module(5) + Verma module of weight 5 of Down-Up algebra with parameters (a, b, g) + over Multivariate Polynomial Ring in a, b, g over Rational Field + """ + return VermaModule(self, la) + + +class VermaModule(CombinatorialFreeModule): + r""" + The Verma module `V(\lambda)` of a down-up algebra. + + The Verma module `V(\lambda)` for the down-up algebra generated + by `d, u` is the span of `\{v_n \mid n \in \ZZ_{\geq 0} \}` + satisfying the relations + + .. MATH:: + + d \cdot v_n = \lambda_{n-1} v_{n-1} \qquad\qquad + u \cdot v_n = v_{n+1}, + + where `\lambda_n = \alpha \lambda_{n-1} + \beta \lambda_{n-2} + \gamma` + and we set `\lambda_0 = \lambda` and `\lambda_{-1} = 0`. + + By Proposition 2.4 in [BR1998]_, `V(\lambda)` is simple if and + only if `\lambda_n \neq 0` for all `n \geq 0`. Moreover, a maximal + submodule is spanned by `\{ v_n \mid n > m \}`, where `m` is the + minimal index such that `\lambda_m = 0`. Moreover, this is unique + unless `\gamma = \lambda = 0`. + + EXAMPLES:: + + sage: R. = QQ[] + sage: DU = algebras.DownUp(0, b, 1) + sage: d, u = DU.gens() + sage: V = DU.verma_module(a) + sage: list(V.weights()[:6]) + [a, 1, a*b + 1, b + 1, a*b^2 + b + 1, b^2 + b + 1] + sage: v = V.basis() + sage: d^2 * v[2] + a*v[0] + sage: d * (d * v[2]) + a*v[0] + + The weight is computed by looking at the scalars associated to the + action of `du` and `ud`:: + + sage: d*u * v[3] + (b+1)*v[3] + sage: u*d * v[3] + (a*b+1)*v[3] + sage: v[3].degree() + (b + 1, a*b + 1) + """ + @staticmethod + def __classcall_private__(cls, DU, la): + """ + Normalize input to ensure a unique representation. + + EXAMPLES:: + + sage: R. = QQ[] + sage: DU = algebras.DownUp(a, b, g) + sage: from sage.algebras.down_up_algebra import VermaModule + sage: VermaModule(DU, 5) is VermaModule(DU, R(5)) + True + sage: VermaModule(DU, 1/a) + Traceback (most recent call last): + ... + TypeError: fraction must have unit denominator + """ + R = DU.base_ring() + la = R(la) + return super().__classcall__(cls, DU, la) + + def __init__(self, DU, la): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: R. = QQ[] + sage: DU = algebras.DownUp(a, b, g) + sage: V = DU.verma_module(5) + sage: TestSuite(V).run() + sage: V = DU.verma_module(0) + sage: TestSuite(V).run() + + sage: DU = algebras.DownUp(a, 0, g) + sage: V = DU.verma_module(5) + sage: TestSuite(V).run() + sage: V = DU.verma_module(0) + sage: TestSuite(V).run() + + sage: DU = algebras.DownUp(a, 1-a, 0) + sage: V = DU.verma_module(5) + sage: TestSuite(V).run() + sage: V = DU.verma_module(0) + sage: TestSuite(V).run() + """ + self._DU = DU + R = DU.base_ring() + + def _la_iter(): + m2 = la + yield la + m2 = R.zero() + m1 = la + while True: + cur = DU._alpha * m1 + DU._beta * m2 + DU._gamma + yield cur + m2 = m1 + m1 = cur + + self._weights = lazy_list(_la_iter()) + cat = Modules(R).WithBasis().Graded() + CombinatorialFreeModule.__init__(self, R, NonNegativeIntegers(), + prefix='v', category=cat) + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: DU = algebras.DownUp(1, 2, 3) + sage: DU.verma_module(5) + Verma module of weight 5 of Down-Up algebra with parameters (1, 2, 3) over Integer Ring + """ + return f"Verma module of weight {self._weights[0]} of {self._DU}" + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: DU = algebras.DownUp(1, 2, 3) + sage: latex(DU.verma_module(5)) + V\left(5\right) + """ + return f"V\\left({self._weights[0]}\\right)" + + def highest_weight_vector(self): + r""" + Return the highest weight vector of ``self``. + + EXAMPLES:: + + sage: DU = algebras.DownUp(1, 2, 3) + sage: V = DU.verma_module(5) + sage: V.highest_weight_vector() + v[0] + """ + return self.basis()[0] + + def weights(self): + r""" + Return the sequence of weights `(\lambda_n)_{n=0}^{\infty}`. + + EXAMPLES:: + + sage: R. = QQ[] + sage: DU = algebras.DownUp(a, b, g) + sage: V = DU.verma_module(5) + sage: V.weights() + lazy list [5, 5*a + g, 5*a^2 + a*g + 5*b + g, ...] + + sage: V = DU.verma_module(0) + sage: DU = algebras.DownUp(a, 1-a, 0) + sage: V = DU.verma_module(0) + sage: V.weights() + lazy list [0, 0, 0, ...] + """ + return self._weights + + def degree_on_basis(self, n): + r""" + Return the degree of the basis element indexed by ``n``. + + This is the vector with the pair `(\lambda_n, \lambda_{n-1})`. + + EXAMPLES:: + + sage: R. = QQ[] + sage: DU = algebras.DownUp(a, b, g) + sage: V = DU.verma_module(5) + sage: V.degree_on_basis(0) + (5, 0) + sage: V.degree_on_basis(1) + (5*a + g, 5) + sage: V.degree_on_basis(2) + (5*a^2 + a*g + 5*b + g, 5*a + g) + """ + R = self.base_ring() + V = FreeModule(R, 2) + if not n: + return V([self._weights[0], R.zero()]) + return V([self._weights[n], self._weights[n-1]]) + + def _action_on_basis(self, m, n): + r""" + Return the action of a basis element of the down-up algebra indexed + by ``m`` on the basis element of ``self`` indexed by ``n``. + + EXAMPLES:: + + sage: R. = QQ[] + sage: DU = algebras.DownUp(0, b, 1) + sage: I = DU.indices() + sage: V = DU.verma_module(1) + sage: V.weights() + lazy list [1, 1, b + 1, ...] + sage: V._action_on_basis(I([0,0,1]), 0) + 0 + sage: V._action_on_basis(I([0,1,0]), 0) + v[0] + sage: V._action_on_basis(I([1,0,0]), 0) + v[1] + + sage: V._action_on_basis(I([0,0,1]), 3) + (b+1)*v[2] + sage: V._action_on_basis(I([0,1,0]), 3) + (b+1)*v[3] + sage: V._action_on_basis(I([1,0,0]), 3) + v[4] + + sage: V._action_on_basis(I([0,0,3]), 3) + (b+1)*v[0] + sage: V._action_on_basis(I([1,2,1]), 3) + (b^3+3*b^2+3*b+1)*v[3] + + sage: V = DU.verma_module(0) + sage: V._action_on_basis(I([0,0,1]), 1) + 0 + """ + if m[2] > n: + return self.zero() + np = n - m[2] + coeff = prod(self._weights[np:n]) * self._weights[np] ** m[1] + return self.term(n - m[2] + m[0], coeff) + + class Element(CombinatorialFreeModule.Element): + def _acted_upon_(self, scalar, self_on_left): + r""" + Return the action of ``scalar`` (an element of the base ring or + the defining down-up algebra) on ``self``. + + EXAMPLES:: + + sage: R. = QQ[] + sage: DU = algebras.DownUp(0, b, 1) + sage: d, u = DU.gens() + sage: V = DU.verma_module(a) + sage: it = iter(DU.basis()) + sage: scalars = [next(it) for _ in range(10)]; scalars + [1, u, (d*u), d, u^2, u*(d*u), u*d, (d*u)^2, (d*u)*d, d^2] + sage: vecs = [V.basis()[0], V.basis()[1], V.basis()[6]] + sage: all((x * y) * v == x * (y * v) + ....: for x in scalars for y in scalars for v in vecs) + True + sage: 5 * V.basis()[3] + 5*v[3] + """ + ret = super()._acted_upon_(scalar, self_on_left) + if ret is not None: + return ret + P = self.parent() + try: + scalar = P._DU(scalar) + except (TypeError, ValueError): + return None + if self_on_left: + return None + return P.linear_combination((P._action_on_basis(m, n), mc*nc) + for m, mc in scalar._monomial_coefficients.items() + for n, nc in self._monomial_coefficients.items()) From 2deed7af1e099266c6420f2e5a10a51693ee927d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 16 Apr 2023 08:16:33 +0200 Subject: [PATCH 2/8] Update down_up_algebra.py fix the reference BR1998 --- src/sage/algebras/down_up_algebra.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/algebras/down_up_algebra.py b/src/sage/algebras/down_up_algebra.py index e71a28f4e6d..e0e3d1dd779 100644 --- a/src/sage/algebras/down_up_algebra.py +++ b/src/sage/algebras/down_up_algebra.py @@ -77,7 +77,7 @@ class DownUpAlgebra(CombinatorialFreeModule): sage: d * u^2 b*u^2*d + a*u*(d*u) + g*u - We verify some examples of Proposition 3.5 in [BR1988]_, which states + We verify some examples of Proposition 3.5 in [BR1998]_, which states that the 0-th degree part is commutative:: sage: DU0 = [u^i * (d*u)^j * d^i for i,j in From 5998596c2c7c02a6b1d5c92e745ba89d39291247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 16 Apr 2023 09:51:30 +0200 Subject: [PATCH 3/8] Update down_up_algebra.py fix a typo in the doc --- src/sage/algebras/down_up_algebra.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/algebras/down_up_algebra.py b/src/sage/algebras/down_up_algebra.py index e0e3d1dd779..dbac73f8bca 100644 --- a/src/sage/algebras/down_up_algebra.py +++ b/src/sage/algebras/down_up_algebra.py @@ -87,7 +87,7 @@ class DownUpAlgebra(CombinatorialFreeModule): sage: all(x * y == y * x for x, y in cartesian_product([DU0, DU0])) True - We verify that `DU(2, -1, \gamma)` can be desribed as the universal + We verify that `DU(2, -1, \gamma)` can be described as the universal enveloping algebra of the 3-dimensional Lie algebra spanned by `x,y,z` satisfying `z = [x, y]`, `[x, z] = \gamma x`, and `[z, y] = \gamma y`:: From 49097fe0268308da1b3ca86a8b2ba6d83d5ec5ba Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 18 Apr 2023 10:33:29 +0900 Subject: [PATCH 4/8] Changing degree into weight for Verma module elements. --- src/sage/algebras/down_up_algebra.py | 144 ++++++++++++++++++++++----- 1 file changed, 119 insertions(+), 25 deletions(-) diff --git a/src/sage/algebras/down_up_algebra.py b/src/sage/algebras/down_up_algebra.py index dbac73f8bca..23bdcb7aa77 100644 --- a/src/sage/algebras/down_up_algebra.py +++ b/src/sage/algebras/down_up_algebra.py @@ -416,6 +416,10 @@ def verma_module(self, la): r""" Return the Verma module `V(\lambda)` of ``self``. + .. SEEALSO:: + + :class:`~sage.algebras.down_up_algebra.VermaModule` + EXAMPLES:: sage: R. = QQ[] @@ -470,8 +474,29 @@ class VermaModule(CombinatorialFreeModule): (b+1)*v[3] sage: u*d * v[3] (a*b+1)*v[3] - sage: v[3].degree() + sage: v[3].weight() (b + 1, a*b + 1) + + An `U(\mathfrak{sl}_2)` example:: + + sage: DU = algebras.DownUp(2, -1, -2) + sage: d, u = DU.gens() + sage: V = DU.verma_module(5) + sage: list(V.weights()[:10]) + [5, 8, 9, 8, 5, 0, -7, -16, -27, -40] + sage: v6 = V.basis()[6] + sage: d * v6 + 0 + sage: [V.basis()[i].weight() for i in range(6)] + [(5, 0), (8, 5), (9, 8), (8, 9), (5, 8), (0, 5)] + + Note that these are the same `\mathfrak{sl}_2` weights from the usual + construction of the irreducible representation `V(5)` (but they are + different as `\mathfrak{gl}_2` weights):: + + sage: B = crystals.Tableaux(['A',1], shape=[5]) + sage: [b.weight() for b in B] + [(5, 0), (4, 1), (3, 2), (2, 3), (1, 4), (0, 5)] """ @staticmethod def __classcall_private__(cls, DU, la): @@ -534,7 +559,7 @@ def _la_iter(): m1 = cur self._weights = lazy_list(_la_iter()) - cat = Modules(R).WithBasis().Graded() + cat = Modules(R).WithBasis() CombinatorialFreeModule.__init__(self, R, NonNegativeIntegers(), prefix='v', category=cat) @@ -592,32 +617,16 @@ def weights(self): sage: V = DU.verma_module(0) sage: V.weights() lazy list [0, 0, 0, ...] - """ - return self._weights - - def degree_on_basis(self, n): - r""" - Return the degree of the basis element indexed by ``n``. - This is the vector with the pair `(\lambda_n, \lambda_{n-1})`. + We reproduce the Fibonacci numbers example from [BR1998]_:: - EXAMPLES:: - - sage: R. = QQ[] - sage: DU = algebras.DownUp(a, b, g) - sage: V = DU.verma_module(5) - sage: V.degree_on_basis(0) - (5, 0) - sage: V.degree_on_basis(1) - (5*a + g, 5) - sage: V.degree_on_basis(2) - (5*a^2 + a*g + 5*b + g, 5*a + g) + sage: R. = QQ[] + sage: DU = algebras.DownUp(1, 1, 0, R) + sage: V = DU.verma_module(la) + sage: list(V.weights()[:11]) + [la, la, 2*la, 3*la, 5*la, 8*la, 13*la, 21*la, 34*la, 55*la, 89*la] """ - R = self.base_ring() - V = FreeModule(R, 2) - if not n: - return V([self._weights[0], R.zero()]) - return V([self._weights[n], self._weights[n-1]]) + return self._weights def _action_on_basis(self, m, n): r""" @@ -696,3 +705,88 @@ def _acted_upon_(self, scalar, self_on_left): return P.linear_combination((P._action_on_basis(m, n), mc*nc) for m, mc in scalar._monomial_coefficients.items() for n, nc in self._monomial_coefficients.items()) + + def is_weight_vector(self): + """ + Return if ``self`` is a weight vector. + + EXAMPLES:: + + sage: DU = algebras.DownUp(2, -1, -2) + sage: V = DU.verma_module(5) + sage: V.zero().is_weight_vector() + False + sage: B = V.basis() + sage: [B[i].weight() for i in range(6)] + [(5, 0), (8, 5), (9, 8), (8, 9), (5, 8), (0, 5)] + sage: B[5].is_weight_vector() + True + sage: v = B[0] + B[1] + sage: v.is_weight_vector() + False + + sage: DU = algebras.DownUp(2, -1, 0) + sage: V = DU.verma_module(0) + sage: B = V.basis() + sage: v = sum(i*B[i] for i in range(1,5)) + sage: v.is_weight_vector() + True + """ + if not self: + return False + + P = self.parent() + R = P.base_ring() + weights = P._weights + + def get_wt(n): + if not n: + return (R(P._weights[0]), R.zero()) + return (R(P._weights[n]), R(P._weights[n-1])) + + it = iter(self._monomial_coefficients) + wt = get_wt(next(it)) + return all(get_wt(n) == wt for n in it) + + def weight(self): + r""" + Return the weight of ``self``. + + For `v_n`, this is the vector with the pair + `(\lambda_n, \lambda_{n-1})`. + + EXAMPLES:: + + sage: R. = QQ[] + sage: DU = algebras.DownUp(a, b, g) + sage: V = DU.verma_module(5) + sage: B = V.basis() + sage: B[0].weight() + (5, 0) + sage: B[1].weight() + (5*a + g, 5) + sage: B[2].weight() + (5*a^2 + a*g + 5*b + g, 5*a + g) + + sage: V.zero().weight() + Traceback (most recent call last): + ... + ValueError: the zero element does not have well-defined weight + sage: (B[0] + B[1]).weight() + Traceback (most recent call last): + ... + ValueError: not a weight vector + """ + if not self: + raise ValueError("the zero element does not have well-defined weight") + if not self.is_weight_vector(): + raise ValueError("not a weight vector") + P = self.parent() + R = P.base_ring() + V = FreeModule(R, 2) + weights = P._weights + it = iter(self._monomial_coefficients) + n = next(it) + if not n: + return V([P._weights[0], R.zero()]) + return V([P._weights[n], P._weights[n-1]]) From 3ccebf0f67a6484c611c884b9b0301a4bcc69dd3 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 18 Apr 2023 11:28:22 +0900 Subject: [PATCH 5/8] Adding some more examples and doc. --- src/sage/algebras/down_up_algebra.py | 48 ++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/sage/algebras/down_up_algebra.py b/src/sage/algebras/down_up_algebra.py index 23bdcb7aa77..d7371dd6241 100644 --- a/src/sage/algebras/down_up_algebra.py +++ b/src/sage/algebras/down_up_algebra.py @@ -59,6 +59,17 @@ class DownUpAlgebra(CombinatorialFreeModule): where `y` covers `x` and `z` covers `y`. For `r`-differential posets we have `du - ud = r 1` and afford a representation of a :class:`Weyl algebra `. + This is obtained from DU(0, 1, 2r)`. For a `(q,r)`-differential poset, + we have the `d` and `u` operators satisfying + + .. MATH:: + + \begin{aligned} + d^2u & = q(q+1) dud - q^3 ud^2 + r d, + \\ du^2 & = q(q+1) udu - q^3 u^2d + r u, + \end{aligned} + + or `\alpha = q(q+1)`, `\beta = -q^3`, and `\gamma = r`. EXAMPLES: @@ -108,6 +119,32 @@ class DownUpAlgebra(CombinatorialFreeModule): sage: d*u^2 - 2*u*d*u + u^2*d == g*u True + Young's lattice is known to be a differential poset. Thus we can + construct a representation of `DU(0, 1, 2)` on this poset (which + gives a proof that Fomin's :class:`growth diagrams ` + are equivalent to edge local rules or shadow lines construction + for :func:`RSK`):: + + sage: DU = algebras.DownUp(0, 1, 2) + sage: d, u = DU.gens() + sage: d^2*u == 0*d*u*d + 1*u*d*d + 2*d + True + sage: d*u^2 == 0*u*d*u + 1*u*u*d + 2*u + True + + sage: YL = CombinatorialFreeModule(DU.base_ring(), Partitions()) + sage: def d_action(la): + ....: return YL.sum_of_monomials(la.remove_cell(*c) for c in la.removable_cells()) + sage: def u_action(la): + ....: return YL.sum_of_monomials(la.add_cell(*c) for c in la.addable_cells()) + sage: D = YL.module_morphism(on_basis=d_action, codomain=YL) + sage: U = YL.module_morphism(on_basis=u_action, codomain=YL) + sage: for la in PartitionsInBox(5, 5): + ....: b = YL.basis()[la] + ....: assert (D*D*U)(b) == 0*(D*U*D)(b) + 1*(U*D*D)(b) + 2*D(b) + ....: assert (D*U*U)(b) == 0*(U*D*U)(la) + 1*(U*U*D)(b) + 2*U(b) + ....: assert (D*U)(b) == (U*D)(b) + b # the Weyl algebra relation + .. TODO:: Implement the homogenized version. @@ -497,6 +534,17 @@ class VermaModule(CombinatorialFreeModule): sage: B = crystals.Tableaux(['A',1], shape=[5]) sage: [b.weight() for b in B] [(5, 0), (4, 1), (3, 2), (2, 3), (1, 4), (0, 5)] + + An example with periodic weights (see Theorem 2.13 of [BR1998]_):: + + sage: k. = CyclotomicField(6) + sage: al = z6 + 1 + sage: (al - 1)^6 == 1 + True + sage: DU = algebras.DownUp(al, 1-al, 0) + sage: V = DU.verma_module(5) + sage: list(V.weights()[:8]) + [5, 5*z6 + 5, 10*z6, 10*z6 - 5, 5*z6 - 5, 0, 5, 5*z6 + 5] """ @staticmethod def __classcall_private__(cls, DU, la): From 8b57537be5854374ef98363cd427b9b1cd630260 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 18 Apr 2023 13:07:05 +0900 Subject: [PATCH 6/8] Fixing a few typos. --- src/sage/algebras/down_up_algebra.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/algebras/down_up_algebra.py b/src/sage/algebras/down_up_algebra.py index d7371dd6241..4b1a3e0f0b4 100644 --- a/src/sage/algebras/down_up_algebra.py +++ b/src/sage/algebras/down_up_algebra.py @@ -59,7 +59,7 @@ class DownUpAlgebra(CombinatorialFreeModule): where `y` covers `x` and `z` covers `y`. For `r`-differential posets we have `du - ud = r 1` and afford a representation of a :class:`Weyl algebra `. - This is obtained from DU(0, 1, 2r)`. For a `(q,r)`-differential poset, + This is obtained from `DU(0, 1, 2r)`. For a `(q,r)`-differential poset, we have the `d` and `u` operators satisfying .. MATH:: @@ -69,7 +69,8 @@ class DownUpAlgebra(CombinatorialFreeModule): \\ du^2 & = q(q+1) udu - q^3 u^2d + r u, \end{aligned} - or `\alpha = q(q+1)`, `\beta = -q^3`, and `\gamma = r`. + or `\alpha = q(q+1)`, `\beta = -q^3`, and `\gamma = r`. Specializing + `q = -1` recovers the `r`-differential poset relation. EXAMPLES: @@ -478,7 +479,7 @@ class VermaModule(CombinatorialFreeModule): .. MATH:: - d \cdot v_n = \lambda_{n-1} v_{n-1} \qquad\qquad + d \cdot v_n = \lambda_{n-1} v_{n-1}, \qquad\qquad u \cdot v_n = v_{n+1}, where `\lambda_n = \alpha \lambda_{n-1} + \beta \lambda_{n-2} + \gamma` From c962e4fbd33725746c45cc9c497a6609eadbf33b Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 8 May 2023 12:05:10 +0900 Subject: [PATCH 7/8] Improving the documentation. --- src/sage/algebras/down_up_algebra.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/sage/algebras/down_up_algebra.py b/src/sage/algebras/down_up_algebra.py index 4b1a3e0f0b4..651c232479a 100644 --- a/src/sage/algebras/down_up_algebra.py +++ b/src/sage/algebras/down_up_algebra.py @@ -57,9 +57,10 @@ class DownUpAlgebra(CombinatorialFreeModule): d(y) = \sum_x x \qquad\qquad u(y) = \sum_z z, where `y` covers `x` and `z` covers `y`. For `r`-differential posets - we have `du - ud = r 1` and afford a representation of a + we have `du - ud = r 1`, and thus it affords a representation of a :class:`Weyl algebra `. - This is obtained from `DU(0, 1, 2r)`. For a `(q,r)`-differential poset, + This Weyl algebra is obtained as the quotient of `DU(0, 1, 2r)` by the + ideal generated by `du - ud - r`. For a `(q, r)`-differential poset, we have the `d` and `u` operators satisfying .. MATH:: @@ -72,6 +73,13 @@ class DownUpAlgebra(CombinatorialFreeModule): or `\alpha = q(q+1)`, `\beta = -q^3`, and `\gamma = r`. Specializing `q = -1` recovers the `r`-differential poset relation. + Two other noteworthy quotients are: + + - the `q`-Weyl algebra from `DU(0, q^2, q+1)` by the ideal generated by + `du - qud - 1`, and + - the quantum plane `R_q[d, u]`, where `du = qud`, from `DU(2q, -q^2, 0)` + by the ideal generated by `du - qud`. + EXAMPLES: We begin by constructing the down-up algebra and perform some @@ -104,11 +112,12 @@ class DownUpAlgebra(CombinatorialFreeModule): satisfying `z = [x, y]`, `[x, z] = \gamma x`, and `[z, y] = \gamma y`:: sage: R. = QQ[] - sage: L = LieAlgebra(R, {('x','y'): {'z': 1}, ('x','z'): {'x': g}, ('z','y'): {'y': g}}, names='x,y,z') + sage: L = LieAlgebra(R, {('x','y'): {'z': 1}, ('x','z'): {'x': g}, ('z','y'): {'y': g}}, + ....: names='x,y,z') sage: x, y, z = L.basis() sage: (L[x, y], L[x, z], L[z, y]) (z, g*x, g*y) - sage: x,y,z = L.pbw_basis().gens() + sage: x, y, z = L.pbw_basis().gens() sage: x^2*y - 2*x*y*x + y*x^2 == g*x True sage: x*y^2 - 2*y*x*y + y^2*x == g*y From a932b10d6f99fdd12a1ed75e2242979b8b02b808 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 9 May 2023 19:42:58 +0900 Subject: [PATCH 8/8] Fixing some details and a few slight doc tweaks. --- src/sage/algebras/down_up_algebra.py | 32 +++++++++++++++++----------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/sage/algebras/down_up_algebra.py b/src/sage/algebras/down_up_algebra.py index 651c232479a..c5fe5361282 100644 --- a/src/sage/algebras/down_up_algebra.py +++ b/src/sage/algebras/down_up_algebra.py @@ -1,4 +1,4 @@ -""" +r""" Down-Up Algebras AUTHORS: @@ -6,15 +6,15 @@ - Travis Scrimshaw (2023-4): initial version """ -#***************************************************************************** -# Copyright (C) 2023 Travis Scrimshaw +# **************************************************************************** +# Copyright (C) 2023 Travis Scrimshaw # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.cachefunc import cached_method from sage.categories.algebras import Algebras @@ -461,11 +461,9 @@ def degree_on_basis(self, m): def verma_module(self, la): r""" - Return the Verma module `V(\lambda)` of ``self``. - - .. SEEALSO:: - - :class:`~sage.algebras.down_up_algebra.VermaModule` + Return the :class:`Verma module + ` + `V(\lambda)` of ``self``. EXAMPLES:: @@ -647,7 +645,8 @@ def _latex_(self): def highest_weight_vector(self): r""" - Return the highest weight vector of ``self``. + Return the highest weight vector of ``self`` that generates + ``self`` as a down-up module. EXAMPLES:: @@ -729,6 +728,9 @@ def _action_on_basis(self, m, n): return self.term(n - m[2] + m[0], coeff) class Element(CombinatorialFreeModule.Element): + r""" + An element of a Verma module of a down-up algebra. + """ def _acted_upon_(self, scalar, self_on_left): r""" Return the action of ``scalar`` (an element of the base ring or @@ -749,6 +751,12 @@ def _acted_upon_(self, scalar, self_on_left): True sage: 5 * V.basis()[3] 5*v[3] + sage: V.basis()[0] * d + Traceback (most recent call last): + ... + TypeError: unsupported operand parent(s) for *: + 'Verma module of weight a of Down-Up algebra ...' + and 'Down-Up algebra ...' """ ret = super()._acted_upon_(scalar, self_on_left) if ret is not None: @@ -765,7 +773,7 @@ def _acted_upon_(self, scalar, self_on_left): for n, nc in self._monomial_coefficients.items()) def is_weight_vector(self): - """ + r""" Return if ``self`` is a weight vector. EXAMPLES::