From 16b56a8ab1030548f61ada5ee35517897d7f3542 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Fri, 11 Jan 2019 15:24:43 -0800 Subject: [PATCH 01/34] new methods for FusionRings: s-matrix, q_dimension, twists etc --- src/doc/en/reference/references/index.rst | 8 + .../combinat/root_system/weyl_characters.py | 260 +++++++++++++++++- 2 files changed, 253 insertions(+), 15 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index f488f0a8cbb..311720794a8 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -4040,6 +4040,10 @@ REFERENCES: **N** +.. [NaiRow2011] Naidu and Rowell, A finiteness property for braided fusion + categories. Algebr. Represent. Theory 14 (2011), no. 5, 837–855. + :arXiv:`0903.4157`. + .. [Nas1950] John Nash. *Equilibrium points in n-person games.* Proceedings of the National Academy of Sciences 36.1 (1950): 48-49. @@ -4529,6 +4533,10 @@ REFERENCES: .. [Rot2006] Ron Roth, Introduction to Coding Theory, Cambridge University Press, 2006 +.. [Row2006] Eric Rowell, From quantum groups to unitary modular tensor categories. + In Representations of algebraic groups, quantum groups, and Lie algebras, + Contemp. Math., 413, Amer. Math. Soc., Providence, RI, 2006. + :arXiv:`math/0503226`. .. [RR1997] Arun Ram and Jeffrey Remmel. *Applications of the Frobenius formulas and the characters of the symmetric group and the diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index 744fcda226e..bd0d0e8b200 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -11,16 +11,21 @@ from __future__ import print_function import sage.combinat.root_system.branching_rules +from operator import mul from sage.categories.all import Category, Algebras, AlgebrasWithBasis from sage.combinat.free_module import CombinatorialFreeModule +from sage.combinat.q_analogues import q_int from sage.combinat.root_system.cartan_type import CartanType from sage.combinat.root_system.root_system import RootSystem +from sage.matrix.special import diagonal_matrix +from sage.matrix.constructor import matrix from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet from sage.misc.functional import is_even from sage.misc.misc import inject_variable -from sage.rings.all import ZZ +from sage.rings.all import ZZ, CC +from sage.rings.number_field.number_field import CyclotomicField class WeylCharacterRing(CombinatorialFreeModule): @@ -92,7 +97,7 @@ class WeylCharacterRing(CombinatorialFreeModule): https://doc.sagemath.org/html/en/thematic_tutorials/lie.html """ @staticmethod - def __classcall__(cls, ct, base_ring=ZZ, prefix=None, style="lattice", k=None): + def __classcall__(cls, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, conjugate=False): """ TESTS:: @@ -108,9 +113,9 @@ def __classcall__(cls, ct, base_ring=ZZ, prefix=None, style="lattice", k=None): prefix = ct[0]+str(ct[1]) else: prefix = repr(ct) - return super(WeylCharacterRing, cls).__classcall__(cls, ct, base_ring=base_ring, prefix=prefix, style=style, k=k) + return super(WeylCharacterRing, cls).__classcall__(cls, ct, base_ring=base_ring, prefix=prefix, style=style, k=k, conjugate=conjugate) - def __init__(self, ct, base_ring=ZZ, prefix=None, style="lattice", k=None): + def __init__(self, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, conjugate=False): """ EXAMPLES:: @@ -146,7 +151,7 @@ def __init__(self, ct, base_ring=ZZ, prefix=None, style="lattice", k=None): def next_level(wt): return [wt + la for la in fw if self.level(wt + la) <= k] B = list(RecursivelyEnumeratedSet([self._space.zero()], next_level)) - B = [self._space.from_vector_notation(wt, style=style) for wt in B] + B = [self._space.from_vector_notation(wt, style="coroots") for wt in B] else: B = self._space @@ -162,6 +167,22 @@ def next_level(wt): # Register the partial inverse as a conversion self.register_conversion(self.retract) + # Record properties of the FusionRing + if k is not None: + #TODO: implement conjugate functionality + if ct[0] in ['A','D','E']: + self._m_g = 1 + self._nf = 1 + elif ct[0] in ['B', 'C', 'F']: + self._m_g = 2 + self._nf = 2 + else: + self._m_g = 3 + self._nf = 1 + h_check = ct.dual_coxeter_number() + self._l = self._m_g * (self._k + h_check) + self._conj = (-1)**conjugate + @cached_method def ambient(self): """ @@ -195,7 +216,7 @@ def lift_on_basis(self, irr): sage: A2.lift_on_basis(v) 2*a2(1,1,1) + a2(1,2,0) + a2(1,0,2) + a2(2,1,0) + a2(2,0,1) + a2(0,1,2) + a2(0,2,1) - This is consistent with the analogous calculation with symmetric + This is consistent with the analoguous calculation with symmetric Schur functions:: sage: s = SymmetricFunctions(QQ).s() @@ -1221,13 +1242,12 @@ def dual(self): for k in d) def highest_weight(self): - r""" - Return the parametrizing dominant weight of an irreducible - character or simple element of a FusionRing. - - This method is only available for basis elements. + """ + This method is only available for basis elements. Returns the + parametrizing dominant weight of an irreducible character or + simple element of a FusionRing. - EXAMPLES:: + Examples:: sage: G2 = WeylCharacterRing("G2", style="coroots") sage: [x.highest_weight() for x in [G2(1,0),G2(0,1)]] @@ -1598,6 +1618,76 @@ def multiplicity(self, other): raise ValueError("{} is not irreducible".format(other)) return self.coefficient(other.support()[0]) + def is_simple_obj(self): + """ + Determine whether element is a simple object of the FusionRing. + """ + return self.parent()._k is not None and len(self.monomial_coefficients())==1 + + def twist(self): + r""" + Compute the object's twist. Returns a rational number `h_X` such that + `e^{(i \pi h_X)}` is the twist of `X`. + + We compute the twists following p.7 of [Row2006]_, noting that the bilinear form + is normalized so that `\langle\alpha, \alpha\rangle = 2` for SHORT roots. + """ + if not self.is_simple_obj(): + raise ValueError("quantum twist is only available for simple objects of a FusionRing") + rho = sum(self.parent().positive_roots())/2 + lam = self.highest_weight() + inner = lam.inner_product(lam+2*rho) + twist = self.parent()._conj*self.parent()._nf*inner/self.parent().fusion_l() + #Reduce to canonical form + while twist > 2: + twist -= 2 + while twist < 0: + twist += 2 + return twist + + def q_dimension(self): + r"""" + INPUT: + + - ``b`` -- a fusion ring basis element. + + This returns the quantum dimension as an element of the cyclotomic + field of the `2l`-th roots of unity, where `l = m(k+h^\vee)` + with `m=1,2,3` depending on whether type is simply, doubly or + triply laced, `k` is the level and `h^\vee` is the dual Coxeter number. + + EXAMPLE:: + + sage: B22=FusionRing("B2",2) + sage: [(b.q_dimension())^2 for b in B22.basis()] + [1, 5, 4, 1, 5, 4] + """ + if not self.is_simple_obj(): + raise ValueError("quantum twist is only available for simple objects of a FusionRing") + lam = self.highest_weight() + space = self.parent().space() + rho = space.rho() + l = self.parent().fusion_l() + num = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(lam+rho)) for alpha in space.positive_roots()], 1) + den = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(rho)) for alpha in space.positive_roots()], 1) + expr = num/den + pr = expr.parent().ring() + q = pr.gen() + expr = pr(expr) + expr = expr.substitute(q=q**2)/q**(expr.degree()) + zet = self.parent().q_field.gen() + return expr.substitute(q=zet) + + def fusion_matrix(self): + """ + Return a matrix containing the object's fusion coefficients. + """ + if not self.is_simple_obj(): + raise ValueError("fusion matrix is only available for simple objects of a FusionRing") + ord_basis = self.parent().ordered_basis() + wts = [x.highest_weight() for x in ord_basis] + rows = [[wt in (self*obj).monomial_coefficients() for wt in wts] for obj in ord_basis] + return matrix(rows) def irreducible_character_freudenthal(hwv, debug=False): r""" @@ -2254,7 +2344,7 @@ class FusionRing(WeylCharacterRing): - [Walton1990]_ """ @staticmethod - def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots"): + def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjugate=False): """ Normalize input to ensure a unique representation. @@ -2279,7 +2369,22 @@ def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots"): sage: TestSuite(D41).run() """ return super(FusionRing, cls).__classcall__(cls, ct, base_ring=base_ring, - prefix=prefix, style=style, k=k) + prefix=prefix, style=style, k=k, conjugate=conjugate) + + @lazy_attribute + def q_field(self): + """ + Return the generator of the cyclotomic field of 2l-th roots of unity, where + l is the fusion_l of the category (see above). + + This field contains the twists, categorical dimensions, and the entries of the + S-matrix. + """ + self._K = CyclotomicField(2*self._l) + return self._K + + def _element_constructor(self,weight): + return self._parent._element_constructor(self._parent,weight) def some_elements(self): """ @@ -2294,8 +2399,133 @@ def some_elements(self): return [self.monomial(x) for x in self.fundamental_weights() if self.level(x) <= self._k] - def fusion_labels(self, labels=None, key=str): + def fusion_k(self): r""" + Return the level of the FusionRing. + + EXAMPLES:: + sage: B22=FusionRing('B2',2) + sage: B22.fusion_k() + 2 + """ + return self._k + + def fusion_l(self): + r""" + Returns the product `m_g(k + h^\vee)`, where `m_g` denotes the + square of the ratio of the lengths of long to short roots of + the underlying Lie algebra, `k` denotes the level of the FusionRing, + and `h^\vee` denotes the dual Coxeter number of the underlying Lie + algebra. + + This value is used to define the associated root of unity q. + + EXAMPLES:: + sage: B22=FusionRing('B2',2) + sage: B22.fusion_l() + 10 + sage: D52=FusionRing('D5',2) + sage: D52.fusion_l() + 10 + """ + return self._l + + def twists_matrix(self): + r""" + Return a diagonal matrix describing the twist corresponding to + each simple object in the ``FusionRing``. + + EXAMPLES:: + + sage: B22=FusionRing('B2',2) + sage: B22.twists_matrix() + [ 0 0 0 0 0 0] + [ 0 2 0 0 0 0] + [ 0 0 4/5 0 0 0] + [ 0 0 0 6/5 0 0] + [ 0 0 0 0 1/2 0] + [ 0 0 0 0 0 3/2] + """ + return diagonal_matrix(obj.twist() for obj in self.ordered_basis()) + + def q_dims(self): + """ + Return a list of quantum dimensions of the simple objects. + """ + return [x.q_dimension() for x in self.ordered_basis()] + + def ordered_basis(self): + """ + Returns a basis of simple objects ordered according to + [NaiRow2011]_. For type A level 1, the basis is ordered by the + fundamental weights. The order is described in [Fuchs1994]_. + + """ + ct = self._cartan_type[0] + k = self._k + r = self.rank() + wts = self.fundamental_weights() + ord_basis = list() + #Order bases for dimension 2 algebras + if self.dimension() == 2: + ord_basis.append(self(wts[1]*0)) + for wt in self.basis(): + wt = wt.highest_weight() + if wt.inner_product(wt) > 0: + ord_basis.append(self(wt)) + if ct == 'A' and k == 1: + ord_basis = [self(wts[1]*0)] + ord_basis += [self(wts[i+1]) for i in range(r)] + if ct == 'A' and k == 2: + #TODO: generalize to higher rank + if r == 1: + ord_basis = [self(wts[1]*0), self(wts[1]), self(wts[1]*2)] + if ct == 'B' and k == 2: + ord_basis = [self(wts[1]*0), self(2*wts[1])] + ord_basis += [self(wts[i]) for i in range(1, r)] + ord_basis += [self(2*wts[r]), self(wts[r]), self(wts[1]+wts[r])] + if ct == 'D' and k == 1: + if r % 2 == 0: + ord_basis = sorted(self.basis().values()) + else: + temp = sorted(self.basis().values(), reverse=1) + ord_basis = [temp.pop()] + ord_basis.extend(temp) + if ct == 'D' and k == 2: + ord_basis = [self(wts[1]*0), self(2*wts[1]), self(2*wts[r-1]), self(2*wts[r])] + ord_basis += [self(wts[i]) for i in range(1, r-1)] + ord_basis += [self(wts[r-1] + wts[r]), self(wts[r-1]), self(wts[r])] + ord_basis += [self(wts[1] + wts[r-1]), self(wts[1] + wts[r])] + if ct == 'E' and k == 1: + if r == 6: + ord_basis = [self(wts[1]*0), self(wts[1]), self(wts[6])] + if ct == 'E' and k == 2: + if r == 8: + ord_basis = [self(wts[1]*0), self(wts[8]), self(wts[1])] + if not ord_basis: + raise ValueError("ordered basis not yet available for this FusionRing") + return ord_basis + + def s_matrix(self): + """ + Return the S-matrix of the FusionRing. + """ + dims = self.q_dims() + ord_basis = self.ordered_basis() + twists = [x.twist() for x in ord_basis] + fusion_mats = [x.fusion_matrix() for x in ord_basis] + rng = range(len(ord_basis)) + #TODO: find smallest field containing entries of S + q = self.q_field + S = matrix(CC, len(ord_basis)) + for i in rng: + for j in rng: + S[i,j] = sum(fusion_mats[i][k,j]*(-1)**twists[k]*dims[k] for k in rng) + S[i,j] /= (-1)**(twists[i]+twists[j]) + return S + + def fusion_labels(self, labels=None): + """ Set the labels of the basis. INPUT: From dd4f94820316cea0c3bd7e96f6fc2d3cb2126a60 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Thu, 17 Jan 2019 13:59:38 -0800 Subject: [PATCH 02/34] created FusionRing.Element class --- .../combinat/root_system/weyl_characters.py | 142 +++++++++--------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index bd0d0e8b200..b930149674b 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -1618,77 +1618,6 @@ def multiplicity(self, other): raise ValueError("{} is not irreducible".format(other)) return self.coefficient(other.support()[0]) - def is_simple_obj(self): - """ - Determine whether element is a simple object of the FusionRing. - """ - return self.parent()._k is not None and len(self.monomial_coefficients())==1 - - def twist(self): - r""" - Compute the object's twist. Returns a rational number `h_X` such that - `e^{(i \pi h_X)}` is the twist of `X`. - - We compute the twists following p.7 of [Row2006]_, noting that the bilinear form - is normalized so that `\langle\alpha, \alpha\rangle = 2` for SHORT roots. - """ - if not self.is_simple_obj(): - raise ValueError("quantum twist is only available for simple objects of a FusionRing") - rho = sum(self.parent().positive_roots())/2 - lam = self.highest_weight() - inner = lam.inner_product(lam+2*rho) - twist = self.parent()._conj*self.parent()._nf*inner/self.parent().fusion_l() - #Reduce to canonical form - while twist > 2: - twist -= 2 - while twist < 0: - twist += 2 - return twist - - def q_dimension(self): - r"""" - INPUT: - - - ``b`` -- a fusion ring basis element. - - This returns the quantum dimension as an element of the cyclotomic - field of the `2l`-th roots of unity, where `l = m(k+h^\vee)` - with `m=1,2,3` depending on whether type is simply, doubly or - triply laced, `k` is the level and `h^\vee` is the dual Coxeter number. - - EXAMPLE:: - - sage: B22=FusionRing("B2",2) - sage: [(b.q_dimension())^2 for b in B22.basis()] - [1, 5, 4, 1, 5, 4] - """ - if not self.is_simple_obj(): - raise ValueError("quantum twist is only available for simple objects of a FusionRing") - lam = self.highest_weight() - space = self.parent().space() - rho = space.rho() - l = self.parent().fusion_l() - num = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(lam+rho)) for alpha in space.positive_roots()], 1) - den = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(rho)) for alpha in space.positive_roots()], 1) - expr = num/den - pr = expr.parent().ring() - q = pr.gen() - expr = pr(expr) - expr = expr.substitute(q=q**2)/q**(expr.degree()) - zet = self.parent().q_field.gen() - return expr.substitute(q=zet) - - def fusion_matrix(self): - """ - Return a matrix containing the object's fusion coefficients. - """ - if not self.is_simple_obj(): - raise ValueError("fusion matrix is only available for simple objects of a FusionRing") - ord_basis = self.parent().ordered_basis() - wts = [x.highest_weight() for x in ord_basis] - rows = [[wt in (self*obj).monomial_coefficients() for wt in wts] for obj in ord_basis] - return matrix(rows) - def irreducible_character_freudenthal(hwv, debug=False): r""" Return the dictionary of multiplicities for the irreducible @@ -2577,3 +2506,74 @@ def fusion_labels(self, labels=None): d[t] = labels[j] inject_variable(labels[j], b) self._fusion_labels = d + + class Element(WeylCharacterRing.Element): + """ + A class for FusionRing elements + """ + def is_simple_obj(self): + """ + Determine whether element is a simple object of the FusionRing. + """ + return self.parent()._k is not None and len(self.monomial_coefficients())==1 + + def twist(self): + r""" + Compute the object's twist. Returns a rational number `h_X` such that + `e^{(i \pi h_X)}` is the twist of `X`. + + We compute the twists following p.7 of [Row2006]_, noting that the bilinear form + is normalized so that `\langle\alpha, \alpha\rangle = 2` for SHORT roots. + """ + if not self.is_simple_obj(): + raise ValueError("quantum twist is only available for simple objects of a FusionRing") + rho = sum(self.parent().positive_roots())/2 + lam = self.highest_weight() + inner = lam.inner_product(lam+2*rho) + twist = self.parent()._conj*self.parent()._nf*inner/self.parent().fusion_l() + #Reduce to canonical form + while twist > 2: + twist -= 2 + while twist < 0: + twist += 2 + return twist + + def q_dimension(self): + r"""" + This returns the quantum dimension as an element of the cyclotomic + field of the `2l`-th roots of unity, where `l = m(k+h^\vee)` + with `m=1,2,3` depending on whether type is simply, doubly or + triply laced, `k` is the level and `h^\vee` is the dual Coxeter number. + + EXAMPLE:: + + sage: B22=FusionRing("B2",2) + sage: [(b.q_dimension())^2 for b in B22.basis()] + [1, 5, 4, 1, 5, 4] + """ + if not self.is_simple_obj(): + raise ValueError("quantum twist is only available for simple objects of a FusionRing") + lam = self.highest_weight() + space = self.parent().space() + rho = space.rho() + l = self.parent().fusion_l() + num = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(lam+rho)) for alpha in space.positive_roots()], 1) + den = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(rho)) for alpha in space.positive_roots()], 1) + expr = num/den + pr = expr.parent().ring() + q = pr.gen() + expr = pr(expr) + expr = expr.substitute(q=q**2)/q**(expr.degree()) + zet = self.parent().q_field.gen() + return expr.substitute(q=zet) + + def fusion_matrix(self): + """ + Return a matrix containing the object's fusion coefficients. + """ + if not self.is_simple_obj(): + raise ValueError("fusion matrix is only available for simple objects of a FusionRing") + ord_basis = self.parent().ordered_basis() + wts = [x.highest_weight() for x in ord_basis] + rows = [[wt in (self*obj).monomial_coefficients() for wt in wts] for obj in ord_basis] + return matrix(rows) From eb09e3feb3278d2a0579a4eb1a84024becca14a6 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Tue, 29 Jan 2019 12:28:01 -0800 Subject: [PATCH 03/34] doctests added, updated q_field, and S-matrix computation --- .../combinat/root_system/weyl_characters.py | 107 ++++++++++++++++-- 1 file changed, 96 insertions(+), 11 deletions(-) diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index b930149674b..52daa76c8e9 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -2308,8 +2308,17 @@ def q_field(self): This field contains the twists, categorical dimensions, and the entries of the S-matrix. + + EXAMPLES:: + + sage: B22=FusionRing("B2",2) + sage: B22.q_field + Cyclotomic Field of order 40 and degree 16 + sage: A11=FusionRing('A1',1) + sage: A11.q_field + Cyclotomic Field of order 12 and degree 4 """ - self._K = CyclotomicField(2*self._l) + self._K = CyclotomicField(4*self._l) return self._K def _element_constructor(self,weight): @@ -2333,6 +2342,7 @@ def fusion_k(self): Return the level of the FusionRing. EXAMPLES:: + sage: B22=FusionRing('B2',2) sage: B22.fusion_k() 2 @@ -2350,6 +2360,7 @@ def fusion_l(self): This value is used to define the associated root of unity q. EXAMPLES:: + sage: B22=FusionRing('B2',2) sage: B22.fusion_l() 10 @@ -2378,8 +2389,17 @@ def twists_matrix(self): return diagonal_matrix(obj.twist() for obj in self.ordered_basis()) def q_dims(self): - """ + r""" Return a list of quantum dimensions of the simple objects. + + EXAMPLES:: + + sage: F41=FusionRing('F4',1,conjugate=True) + sage: F41.q_dims() + [1, -zeta80^24 + zeta80^16 + 1] + sage: B22=FusionRing("B2",2) + sage: B22.q_dims() + [1, 1, 2, 2, -2*zeta40^12 + 2*zeta40^8 + 1, -2*zeta40^12 + 2*zeta40^8 + 1] """ return [x.q_dimension() for x in self.ordered_basis()] @@ -2435,22 +2455,54 @@ def ordered_basis(self): raise ValueError("ordered basis not yet available for this FusionRing") return ord_basis - def s_matrix(self): + def s_ij(self, elt_i, elt_j, fusion_mat_i=[]): """ - Return the S-matrix of the FusionRing. + Return the element of the S-matrix of this FusionRing corresponding to + the given elements. + + EXAMPLES:: + + #TODO: update docstring using next iteration of ordered basis method """ dims = self.q_dims() ord_basis = self.ordered_basis() twists = [x.twist() for x in ord_basis] + rng = range(len(ord_basis)) + j = ord_basis.index(elt_j) + fusion_matrix = fusion_mat_i if fusion_mat_i else elt_i.fusion_matrix() + q = self.q_field.gen() + l = self.fusion_l() + s_ij = sum(fusion_matrix[k,j]*q**(2*l*twists[k])*dims[k] for k in rng) + s_ij *= q**(-2*l*(elt_i.twist() + elt_j.twist())) + return s_ij + + def s_matrix(self): + r""" + Return the S-matrix of this FusionRing. + + EXAMPLES:: + + sage: D91=FusionRing('D9',1) + sage: D91.s_matrix() + [ 1 1 1 1] + [ 1 1 -1 -1] + [ 1 -1 -zeta68^17 zeta68^17] + [ 1 -1 zeta68^17 -zeta68^17] + + sage: D41=FusionRing('D4',1) + sage: D41.s_matrix() + [ 1 1 1 1] + [ 1 1 -1 -1] + [ 1 -1 1 -1] + [ 1 -1 -1 1] + """ + ord_basis = self.ordered_basis() fusion_mats = [x.fusion_matrix() for x in ord_basis] rng = range(len(ord_basis)) - #TODO: find smallest field containing entries of S - q = self.q_field - S = matrix(CC, len(ord_basis)) + S = matrix(self.q_field, len(ord_basis)) for i in rng: for j in rng: - S[i,j] = sum(fusion_mats[i][k,j]*(-1)**twists[k]*dims[k] for k in rng) - S[i,j] /= (-1)**(twists[i]+twists[j]) + S[i,j] = self.s_ij(ord_basis[i], ord_basis[j], fusion_mats[i]) return S def fusion_labels(self, labels=None): @@ -2514,6 +2566,17 @@ class Element(WeylCharacterRing.Element): def is_simple_obj(self): """ Determine whether element is a simple object of the FusionRing. + + EXAMPLES:: + + sage: B22=FusionRing("B2",2) + sage: elt=B22.some_elements()[0] + sage: elt.is_simple_obj() + True + sage: elt**2 + B22(0,0) + B22(0,2) + B22(2,0) + sage: (elt**2).is_simple_obj() + False """ return self.parent()._k is not None and len(self.monomial_coefficients())==1 @@ -2524,6 +2587,19 @@ def twist(self): We compute the twists following p.7 of [Row2006]_, noting that the bilinear form is normalized so that `\langle\alpha, \alpha\rangle = 2` for SHORT roots. + + EXAMPLES:: + + sage: G21=FusionRing('G2',1) + sage: G21.basis() + Finite family {(0, 0, 0): G21(0,0), (1, 0, -1): G21(1,0)} + sage: G21(1,0).twist() + 4/5 + sage: F41=FusionRing('F4',1,conjugate=True) + sage: F41.basis() + Finite family {(0, 0, 0, 0): F41(0,0,0,0), (1, 0, 0, 0): F41(0,0,0,1)} + sage: F41(0,0,0,1).twist() + 4/5 """ if not self.is_simple_obj(): raise ValueError("quantum twist is only available for simple objects of a FusionRing") @@ -2561,15 +2637,24 @@ def q_dimension(self): den = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(rho)) for alpha in space.positive_roots()], 1) expr = num/den pr = expr.parent().ring() - q = pr.gen() + q = pr.gen()**2 expr = pr(expr) expr = expr.substitute(q=q**2)/q**(expr.degree()) zet = self.parent().q_field.gen() return expr.substitute(q=zet) def fusion_matrix(self): - """ + r""" Return a matrix containing the object's fusion coefficients. + + EXAMPLES:: + + sage: G21=FusionRing('G2',1) + sage: G21.basis() + Finite family {(0, 0, 0): G21(0,0), (1, 0, -1): G21(1,0)} + sage: G21(1,0).fusion_matrix() + [0 1] + [1 1] """ if not self.is_simple_obj(): raise ValueError("fusion matrix is only available for simple objects of a FusionRing") From 7be38baf1ea4689f38445e530c322638c7c8533f Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Fri, 1 Feb 2019 13:42:01 -0800 Subject: [PATCH 04/34] revised doctest --- src/sage/combinat/root_system/weyl_characters.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index 52daa76c8e9..6fb11e4d11a 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -2408,7 +2408,6 @@ def ordered_basis(self): Returns a basis of simple objects ordered according to [NaiRow2011]_. For type A level 1, the basis is ordered by the fundamental weights. The order is described in [Fuchs1994]_. - """ ct = self._cartan_type[0] k = self._k @@ -2569,13 +2568,14 @@ def is_simple_obj(self): EXAMPLES:: - sage: B22=FusionRing("B2",2) - sage: elt=B22.some_elements()[0] - sage: elt.is_simple_obj() + sage: A22=FusionRing("A2",2) + sage: x = A22(1,0); x + A22(1,0) + sage: x.is_simple_obj() True - sage: elt**2 - B22(0,0) + B22(0,2) + B22(2,0) - sage: (elt**2).is_simple_obj() + sage: x^2 + A22(0,1) + A22(2,0) + sage: (x^2).is_simple_obj() False """ return self.parent()._k is not None and len(self.monomial_coefficients())==1 From a5bc48ff83422c2a3c85eae779d5cf0d5a9da940 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Fri, 1 Feb 2019 15:24:17 -0800 Subject: [PATCH 05/34] work on revising self.s_ij --- .../combinat/root_system/weyl_characters.py | 66 ++++++++++++------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index 6fb11e4d11a..eec82c8fa07 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -2301,10 +2301,19 @@ def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjug prefix=prefix, style=style, k=k, conjugate=conjugate) @lazy_attribute + def _q_field(self): + """ + The cyclotomic field of 4l-th roots of unity, where + l is the fusion_l of the category (see above). Call this + lazy attribute via the method `self.q_field()`. + """ + self._K = CyclotomicField(4*self._l) + return self._K + def q_field(self): """ - Return the generator of the cyclotomic field of 2l-th roots of unity, where - l is the fusion_l of the category (see above). + Return the cyclotomic field of 4l-th roots of unity, where + `l` is the ``fusion_l`` of the category (see above). This field contains the twists, categorical dimensions, and the entries of the S-matrix. @@ -2312,14 +2321,13 @@ def q_field(self): EXAMPLES:: sage: B22=FusionRing("B2",2) - sage: B22.q_field + sage: B22.q_field() Cyclotomic Field of order 40 and degree 16 sage: A11=FusionRing('A1',1) - sage: A11.q_field + sage: A11.q_field() Cyclotomic Field of order 12 and degree 4 """ - self._K = CyclotomicField(4*self._l) - return self._K + return self._q_field def _element_constructor(self,weight): return self._parent._element_constructor(self._parent,weight) @@ -2454,14 +2462,9 @@ def ordered_basis(self): raise ValueError("ordered basis not yet available for this FusionRing") return ord_basis - def s_ij(self, elt_i, elt_j, fusion_mat_i=[]): + def s_ij_legacy(self, elt_i, elt_j, fusion_mat_i=[]): """ - Return the element of the S-matrix of this FusionRing corresponding to - the given elements. - - EXAMPLES:: - - #TODO: update docstring using next iteration of ordered basis method + Remove this soon """ dims = self.q_dims() ord_basis = self.ordered_basis() @@ -2469,12 +2472,31 @@ def s_ij(self, elt_i, elt_j, fusion_mat_i=[]): rng = range(len(ord_basis)) j = ord_basis.index(elt_j) fusion_matrix = fusion_mat_i if fusion_mat_i else elt_i.fusion_matrix() - q = self.q_field.gen() + q = self.q_field().gen() l = self.fusion_l() s_ij = sum(fusion_matrix[k,j]*q**(2*l*twists[k])*dims[k] for k in rng) s_ij *= q**(-2*l*(elt_i.twist() + elt_j.twist())) return s_ij + def s_ij(self, elt_i, elt_j): + """ + Return the element of the S-matrix of this FusionRing corresponding to + the given elements. + + INPUT: + + - ``elt_i``, ``elt_j`` -- elements of the fusion basis + + EXAMPLES:: + + #TODO: update docstring using next iteration of ordered basis method + """ + l = self.fusion_l() + K = self.q_field() + q = K.gen() + ijtwist = -2*l*(elt_i.twist() + elt_j.twist()) + return sum(self(k).q_dimension()*q**(2*l*self(k).twist()+ijtwist) for k in (elt_i.dual()*elt_j).monomial_coefficients()) + def s_matrix(self): r""" Return the S-matrix of this FusionRing. @@ -2498,7 +2520,7 @@ def s_matrix(self): ord_basis = self.ordered_basis() fusion_mats = [x.fusion_matrix() for x in ord_basis] rng = range(len(ord_basis)) - S = matrix(self.q_field, len(ord_basis)) + S = matrix(self.q_field(), len(ord_basis)) for i in rng: for j in rng: S[i,j] = self.s_ij(ord_basis[i], ord_basis[j], fusion_mats[i]) @@ -2562,7 +2584,7 @@ class Element(WeylCharacterRing.Element): """ A class for FusionRing elements """ - def is_simple_obj(self): + def is_simple_object(self): """ Determine whether element is a simple object of the FusionRing. @@ -2571,11 +2593,11 @@ def is_simple_obj(self): sage: A22=FusionRing("A2",2) sage: x = A22(1,0); x A22(1,0) - sage: x.is_simple_obj() + sage: x.is_simple_object() True sage: x^2 A22(0,1) + A22(2,0) - sage: (x^2).is_simple_obj() + sage: (x^2).is_simple_object() False """ return self.parent()._k is not None and len(self.monomial_coefficients())==1 @@ -2601,7 +2623,7 @@ def twist(self): sage: F41(0,0,0,1).twist() 4/5 """ - if not self.is_simple_obj(): + if not self.is_simple_object(): raise ValueError("quantum twist is only available for simple objects of a FusionRing") rho = sum(self.parent().positive_roots())/2 lam = self.highest_weight() @@ -2627,7 +2649,7 @@ def q_dimension(self): sage: [(b.q_dimension())^2 for b in B22.basis()] [1, 5, 4, 1, 5, 4] """ - if not self.is_simple_obj(): + if not self.is_simple_object(): raise ValueError("quantum twist is only available for simple objects of a FusionRing") lam = self.highest_weight() space = self.parent().space() @@ -2640,7 +2662,7 @@ def q_dimension(self): q = pr.gen()**2 expr = pr(expr) expr = expr.substitute(q=q**2)/q**(expr.degree()) - zet = self.parent().q_field.gen() + zet = self.parent().q_field().gen() return expr.substitute(q=zet) def fusion_matrix(self): @@ -2656,7 +2678,7 @@ def fusion_matrix(self): [0 1] [1 1] """ - if not self.is_simple_obj(): + if not self.is_simple_object(): raise ValueError("fusion matrix is only available for simple objects of a FusionRing") ord_basis = self.parent().ordered_basis() wts = [x.highest_weight() for x in ord_basis] From 5438494edc15effd7dce7ef5c3658411ceac243a Mon Sep 17 00:00:00 2001 From: Galit Anikeeva Date: Tue, 28 Apr 2020 19:45:06 -0700 Subject: [PATCH 06/34] Update to py3. Fix doctests. --- src/sage/combinat/root_system/weyl_characters.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index eec82c8fa07..ee40a4bdc06 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -10,6 +10,7 @@ # **************************************************************************** from __future__ import print_function +from functools import reduce import sage.combinat.root_system.branching_rules from operator import mul from sage.categories.all import Category, Algebras, AlgebrasWithBasis @@ -2523,10 +2524,10 @@ def s_matrix(self): S = matrix(self.q_field(), len(ord_basis)) for i in rng: for j in rng: - S[i,j] = self.s_ij(ord_basis[i], ord_basis[j], fusion_mats[i]) + S[i,j] = self.s_ij(ord_basis[i], ord_basis[j]) return S - def fusion_labels(self, labels=None): + def fusion_labels(self, labels=None, key=str): """ Set the labels of the basis. @@ -2647,7 +2648,7 @@ def q_dimension(self): sage: B22=FusionRing("B2",2) sage: [(b.q_dimension())^2 for b in B22.basis()] - [1, 5, 4, 1, 5, 4] + [1, 4, 5, 1, 5, 4] """ if not self.is_simple_object(): raise ValueError("quantum twist is only available for simple objects of a FusionRing") From aabbb247e360d3329dc71163c74d38aaf348d880 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Tue, 16 Jun 2020 08:22:26 -0700 Subject: [PATCH 07/34] use get_order instead of ordered_basis --- src/sage/combinat/root_system/weyl_characters.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index ee40a4bdc06..fce5b1617e1 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -2395,7 +2395,8 @@ def twists_matrix(self): [ 0 0 0 0 1/2 0] [ 0 0 0 0 0 3/2] """ - return diagonal_matrix(obj.twist() for obj in self.ordered_basis()) + return diagonal_matrix(self.basis()[x].twist() for x in self.get_order()) + # return diagonal_matrix(obj.twist() for obj in self.ordered_basis()) def q_dims(self): r""" @@ -2410,7 +2411,9 @@ def q_dims(self): sage: B22.q_dims() [1, 1, 2, 2, -2*zeta40^12 + 2*zeta40^8 + 1, -2*zeta40^12 + 2*zeta40^8 + 1] """ - return [x.q_dimension() for x in self.ordered_basis()] + b = self.basis() + return [b[x].q_dimension() for x in self.get_order()] + # return [x.q_dimension() for x in self.ordered_basis()] def ordered_basis(self): """ @@ -2498,7 +2501,12 @@ def s_ij(self, elt_i, elt_j): ijtwist = -2*l*(elt_i.twist() + elt_j.twist()) return sum(self(k).q_dimension()*q**(2*l*self(k).twist()+ijtwist) for k in (elt_i.dual()*elt_j).monomial_coefficients()) + def s_matrix(self): + b = self.basis() + return matrix([[self.s_ij(b[x],b[y]) for x in self.get_order()] for y in self.get_order()]) + + def s_matrix_old(self): r""" Return the S-matrix of this FusionRing. @@ -2519,7 +2527,6 @@ def s_matrix(self): [ 1 -1 -1 1] """ ord_basis = self.ordered_basis() - fusion_mats = [x.fusion_matrix() for x in ord_basis] rng = range(len(ord_basis)) S = matrix(self.q_field(), len(ord_basis)) for i in rng: From 72c2c44a23f4cbf317be408e7f996658c990fa58 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Tue, 16 Jun 2020 10:35:37 -0700 Subject: [PATCH 08/34] docstring edits --- .../combinat/root_system/weyl_characters.py | 120 ++---------------- 1 file changed, 12 insertions(+), 108 deletions(-) diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index fce5b1617e1..ffb8cfb795a 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -2389,14 +2389,13 @@ def twists_matrix(self): sage: B22=FusionRing('B2',2) sage: B22.twists_matrix() [ 0 0 0 0 0 0] - [ 0 2 0 0 0 0] - [ 0 0 4/5 0 0 0] - [ 0 0 0 6/5 0 0] - [ 0 0 0 0 1/2 0] - [ 0 0 0 0 0 3/2] + [ 0 4/5 0 0 0 0] + [ 0 0 1/2 0 0 0] + [ 0 0 0 2 0 0] + [ 0 0 0 0 3/2 0] + [ 0 0 0 0 0 6/5] """ return diagonal_matrix(self.basis()[x].twist() for x in self.get_order()) - # return diagonal_matrix(obj.twist() for obj in self.ordered_basis()) def q_dims(self): r""" @@ -2409,78 +2408,10 @@ def q_dims(self): [1, -zeta80^24 + zeta80^16 + 1] sage: B22=FusionRing("B2",2) sage: B22.q_dims() - [1, 1, 2, 2, -2*zeta40^12 + 2*zeta40^8 + 1, -2*zeta40^12 + 2*zeta40^8 + 1] + [1, 2, -2*zeta40^12 + 2*zeta40^8 + 1, 1, -2*zeta40^12 + 2*zeta40^8 + 1, 2] """ b = self.basis() return [b[x].q_dimension() for x in self.get_order()] - # return [x.q_dimension() for x in self.ordered_basis()] - - def ordered_basis(self): - """ - Returns a basis of simple objects ordered according to - [NaiRow2011]_. For type A level 1, the basis is ordered by the - fundamental weights. The order is described in [Fuchs1994]_. - """ - ct = self._cartan_type[0] - k = self._k - r = self.rank() - wts = self.fundamental_weights() - ord_basis = list() - #Order bases for dimension 2 algebras - if self.dimension() == 2: - ord_basis.append(self(wts[1]*0)) - for wt in self.basis(): - wt = wt.highest_weight() - if wt.inner_product(wt) > 0: - ord_basis.append(self(wt)) - if ct == 'A' and k == 1: - ord_basis = [self(wts[1]*0)] - ord_basis += [self(wts[i+1]) for i in range(r)] - if ct == 'A' and k == 2: - #TODO: generalize to higher rank - if r == 1: - ord_basis = [self(wts[1]*0), self(wts[1]), self(wts[1]*2)] - if ct == 'B' and k == 2: - ord_basis = [self(wts[1]*0), self(2*wts[1])] - ord_basis += [self(wts[i]) for i in range(1, r)] - ord_basis += [self(2*wts[r]), self(wts[r]), self(wts[1]+wts[r])] - if ct == 'D' and k == 1: - if r % 2 == 0: - ord_basis = sorted(self.basis().values()) - else: - temp = sorted(self.basis().values(), reverse=1) - ord_basis = [temp.pop()] - ord_basis.extend(temp) - if ct == 'D' and k == 2: - ord_basis = [self(wts[1]*0), self(2*wts[1]), self(2*wts[r-1]), self(2*wts[r])] - ord_basis += [self(wts[i]) for i in range(1, r-1)] - ord_basis += [self(wts[r-1] + wts[r]), self(wts[r-1]), self(wts[r])] - ord_basis += [self(wts[1] + wts[r-1]), self(wts[1] + wts[r])] - if ct == 'E' and k == 1: - if r == 6: - ord_basis = [self(wts[1]*0), self(wts[1]), self(wts[6])] - if ct == 'E' and k == 2: - if r == 8: - ord_basis = [self(wts[1]*0), self(wts[8]), self(wts[1])] - if not ord_basis: - raise ValueError("ordered basis not yet available for this FusionRing") - return ord_basis - - def s_ij_legacy(self, elt_i, elt_j, fusion_mat_i=[]): - """ - Remove this soon - """ - dims = self.q_dims() - ord_basis = self.ordered_basis() - twists = [x.twist() for x in ord_basis] - rng = range(len(ord_basis)) - j = ord_basis.index(elt_j) - fusion_matrix = fusion_mat_i if fusion_mat_i else elt_i.fusion_matrix() - q = self.q_field().gen() - l = self.fusion_l() - s_ij = sum(fusion_matrix[k,j]*q**(2*l*twists[k])*dims[k] for k in rng) - s_ij *= q**(-2*l*(elt_i.twist() + elt_j.twist())) - return s_ij def s_ij(self, elt_i, elt_j): """ @@ -2493,7 +2424,10 @@ def s_ij(self, elt_i, elt_j): EXAMPLES:: - #TODO: update docstring using next iteration of ordered basis method + sage: G21=FusionRing("G2",1) + sage: b=G21.basis() + sage: [G21.s_ij(x,y) for x in b for y in b] + [1, -zeta60^14 + zeta60^6 + zeta60^4, -zeta60^14 + zeta60^6 + zeta60^4, -1] """ l = self.fusion_l() K = self.q_field() @@ -2501,12 +2435,7 @@ def s_ij(self, elt_i, elt_j): ijtwist = -2*l*(elt_i.twist() + elt_j.twist()) return sum(self(k).q_dimension()*q**(2*l*self(k).twist()+ijtwist) for k in (elt_i.dual()*elt_j).monomial_coefficients()) - def s_matrix(self): - b = self.basis() - return matrix([[self.s_ij(b[x],b[y]) for x in self.get_order()] for y in self.get_order()]) - - def s_matrix_old(self): r""" Return the S-matrix of this FusionRing. @@ -2526,13 +2455,8 @@ def s_matrix_old(self): [ 1 -1 1 -1] [ 1 -1 -1 1] """ - ord_basis = self.ordered_basis() - rng = range(len(ord_basis)) - S = matrix(self.q_field(), len(ord_basis)) - for i in rng: - for j in rng: - S[i,j] = self.s_ij(ord_basis[i], ord_basis[j]) - return S + b = self.basis() + return matrix([[self.s_ij(b[x],b[y]) for x in self.get_order()] for y in self.get_order()]) def fusion_labels(self, labels=None, key=str): """ @@ -2672,23 +2596,3 @@ def q_dimension(self): expr = expr.substitute(q=q**2)/q**(expr.degree()) zet = self.parent().q_field().gen() return expr.substitute(q=zet) - - def fusion_matrix(self): - r""" - Return a matrix containing the object's fusion coefficients. - - EXAMPLES:: - - sage: G21=FusionRing('G2',1) - sage: G21.basis() - Finite family {(0, 0, 0): G21(0,0), (1, 0, -1): G21(1,0)} - sage: G21(1,0).fusion_matrix() - [0 1] - [1 1] - """ - if not self.is_simple_object(): - raise ValueError("fusion matrix is only available for simple objects of a FusionRing") - ord_basis = self.parent().ordered_basis() - wts = [x.highest_weight() for x in ord_basis] - rows = [[wt in (self*obj).monomial_coefficients() for wt in wts] for obj in ord_basis] - return matrix(rows) From 05d15baa266d175bb56353450786c1417dd73bb1 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Wed, 17 Jun 2020 05:56:53 -0700 Subject: [PATCH 09/34] get_order uncached in FusionRing class only. I think this change should be made instead in free_module.py, where @cached_method should be removed from get_order. However since that change would be more global we have this temporary fix. --- src/sage/combinat/root_system/weyl_characters.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index ffb8cfb795a..7b2e5ee6999 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -2333,6 +2333,16 @@ def q_field(self): def _element_constructor(self,weight): return self._parent._element_constructor(self._parent,weight) + def get_order(self): + """ + This duplicates the functionality inherited from combinat.free_module + but it is not cached. The caching of get_order causes inconsistent + results after calling set_order. + """ + if self._order is None: + self.set_order(self.basis().keys().list()) + return self._order + def some_elements(self): """ Return some elements of ``self``. From 4e5a9197f025c24f2ce42d66d0b9543225a50a0a Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Fri, 19 Jun 2020 10:36:01 -0700 Subject: [PATCH 10/34] revision of fusion_labels --- .../combinat/root_system/weyl_characters.py | 102 +++++++++++------- 1 file changed, 61 insertions(+), 41 deletions(-) diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index 7b2e5ee6999..02678db2451 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -2241,30 +2241,40 @@ class FusionRing(WeylCharacterRing): weights of the basis elements, then assign new labels to them:: sage: B22 = FusionRing("B2", 2) - sage: basis = sorted(B22.basis(), key=str) - sage: basis - [B22(0,0), B22(0,1), B22(0,2), B22(1,0), B22(1,1), B22(2,0)] - sage: [x.highest_weight() for x in basis] - [(0, 0), (1/2, 1/2), (1, 1), (1, 0), (3/2, 1/2), (2, 0)] - sage: B22.fusion_labels(['1','X','Y2','Y1','Xp','Z']) - sage: relabeled_basis = sorted(B22.basis(), key=str) - sage: relabeled_basis - [1, X, Xp, Y1, Y2, Z] - sage: [(x, x.highest_weight()) for x in relabeled_basis] - [(1, (0, 0)), + sage: b = [B22(x) for x in B22.get_order()]; b + [B22(0,0), B22(1,0), B22(0,1), B22(2,0), B22(1,1), B22(0,2)] + sage: [x.highest_weight() for x in b] + [(0, 0), (1, 0), (1/2, 1/2), (2, 0), (3/2, 1/2), (1, 1)] + sage: B22.fusion_labels(['I0','Y1','X','Z','Xp','Y2']) + sage: b = [B22(x) for x in B22.get_order()]; b + [I0, Y1, X, Z, Xp, Y2] + sage: [(x, x.highest_weight()) for x in b] + [(I0, (0, 0)), + (Y1, (1, 0)), (X, (1/2, 1/2)), + (Z, (2, 0)), (Xp, (3/2, 1/2)), - (Y1, (1, 0)), - (Y2, (1, 1)), - (Z, (2, 0))] + (Y2, (1, 1))] sage: X*Y1 X + Xp sage: Z*Z - 1 + I0 + + A fixed order of the basis keys is avalailable with :meth:`get_order`. + This is the order used by methods such as :meth:`s_matrix`. + You may use :meth:`set_order` to reorder the basis:: + + sage: B22.set_order([x.highest_weight() for x in [I0,Y1,Y2,X,Xp,Z]]) + sage: [B22(x) for x in B22.get_order()] + [I0, Y1, Y2, X, Xp, Z] + + To reset the labels and the order to their defaults, + you may run `fusion_labels` with no parameter: + + sage: B22.fusion_labels() + sage: [B22(x) for x in B22.get_order()] + [B22(0,0), B22(1,0), B22(0,1), B22(2,0), B22(1,1), B22(0,2)] - sage: C22 = FusionRing("C2", 2) - sage: sorted(C22.basis(), key=str) - [C22(0,0), C22(0,1), C22(0,2), C22(1,0), C22(1,1), C22(2,0)] REFERENCES: @@ -2335,9 +2345,22 @@ def _element_constructor(self,weight): def get_order(self): """ - This duplicates the functionality inherited from combinat.free_module - but it is not cached. The caching of get_order causes inconsistent - results after calling set_order. + Returns the weights of the basis vectors in a fixed order. + You may change the order of the basis using :meth:`set_order` + + EXAMPLES:: + + sage: A14=FusionRing("A1",4) + sage: w = A14.get_order(); w + [(0, 0), (1/2, -1/2), (1, -1), (3/2, -3/2), (2, -2)] + sage: A14.set_order([w[k] for k in [0,4,1,3,2]]) + sage: [A14(x) for x in A14.get_order()] + [A14(0), A14(4), A14(1), A14(3), A14(2)] + + This duplicates :meth:`get_order` from `combinat.free_module`. + However unlike the `combinat.free_module` method with the same + name this `get_order` is not cached. Caching of get_order causes + inconsistent results after calling `set_order`. """ if self._order is None: self.set_order(self.basis().keys().list()) @@ -2468,31 +2491,27 @@ def s_matrix(self): b = self.basis() return matrix([[self.s_ij(b[x],b[y]) for x in self.get_order()] for y in self.get_order()]) - def fusion_labels(self, labels=None, key=str): + def fusion_labels(self, labels=None): """ Set the labels of the basis. INPUT: - - ``labels`` -- (default: ``None``) a list of strings - - ``key`` -- (default: ``str``) key to use to sort basis + - ``labels`` -- (default: ``None``) a list of strings or string - The length of the list ``labels`` must equal the + If ``labels`` is a list, the length of the list must equal the number of basis elements. These become the names of - the basis elements. If ``labels`` is ``None``, then - this resets the labels to the default. + the basis elements. + + If ``labels`` is a string, this is treated as a prefix and a + list of names is generated. - Note that the basis is stored as unsorted data, so to obtain - consistent results, it should be sorted when applying - labels. The argument ``key`` (default ``str``) specifies how - to sort the basis. If you call this with ``key=None``, then no - sorting is done. This may lead to random results, at least - with Python 3. + If ``labels`` is ``None``, then this resets the labels to the default. EXAMPLES:: sage: A13 = FusionRing("A1", 3) - sage: A13.fusion_labels(['x0','x1','x2','x3']) + sage: A13.fusion_labels("x") sage: fb = list(A13.basis()); fb [x0, x1, x2, x3] sage: Matrix([[x*y for y in A13.basis()] for x in A13.basis()]) @@ -2508,18 +2527,19 @@ def fusion_labels(self, labels=None, key=str): [A13(0), A13(1), A13(2), A13(3)] """ if labels is None: + self._order = None self._fusion_labels = None return + elif type(labels) == str: + labels = [labels+"%d"%k for k in range(len(self.basis()))] + elif len(labels) != len(self.basis()): + raise ValueError('invalid data') d = {} - if key: - fb = sorted(self.basis(), key=key) - else: - fb = list(self.basis()) + fb = list(self.get_order()) for j, b in enumerate(fb): - wt = b.highest_weight() - t = tuple([wt.inner_product(x) for x in self.simple_coroots()]) + t = tuple([b.inner_product(x) for x in self.simple_coroots()]) d[t] = labels[j] - inject_variable(labels[j], b) + inject_variable(labels[j], self(b)) self._fusion_labels = d class Element(WeylCharacterRing.Element): From d7a8849a464d341ef7561b6ec47998d8ceb90089 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Sat, 20 Jun 2020 16:34:29 -0700 Subject: [PATCH 11/34] FusionRing moved to a separate file fusion_ring.py --- src/doc/en/reference/combinat/module_list.rst | 1 + src/sage/combinat/root_system/__init__.py | 1 + src/sage/combinat/root_system/all.py | 4 +- src/sage/combinat/root_system/fusion_ring.py | 444 ++++++++++++++++++ .../combinat/root_system/weyl_characters.py | 417 ---------------- 5 files changed, 448 insertions(+), 419 deletions(-) create mode 100644 src/sage/combinat/root_system/fusion_ring.py diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index 64e844e9b8a..86cdeafcaf5 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -278,6 +278,7 @@ Comprehensive Module list sage/combinat/root_system/weight_lattice_realizations sage/combinat/root_system/weight_space sage/combinat/root_system/weyl_characters + sage/combinat/root_system/fusion_ring sage/combinat/root_system/weyl_group sage/combinat/rooted_tree sage/combinat/rsk diff --git a/src/sage/combinat/root_system/__init__.py b/src/sage/combinat/root_system/__init__.py index fa932ab28ef..b04c503fad1 100644 --- a/src/sage/combinat/root_system/__init__.py +++ b/src/sage/combinat/root_system/__init__.py @@ -77,6 +77,7 @@ --------------------- - :ref:`sage.combinat.root_system.weyl_characters` +- :ref:`sage.combinat.root_system.fusion_ring` - :ref:`sage.combinat.root_system.integrable_representations` - :ref:`sage.combinat.root_system.branching_rules` - :ref:`sage.combinat.root_system.hecke_algebra_representation` diff --git a/src/sage/combinat/root_system/all.py b/src/sage/combinat/root_system/all.py index 8ed7e596a17..ccb40064690 100644 --- a/src/sage/combinat/root_system/all.py +++ b/src/sage/combinat/root_system/all.py @@ -19,8 +19,8 @@ 'ExtendedAffineWeylGroup') lazy_import('sage.combinat.root_system.coxeter_group', 'CoxeterGroup') lazy_import('sage.combinat.root_system.weyl_characters', ['WeylCharacterRing', - 'WeightRing', - 'FusionRing']) + 'WeightRing']) +lazy_import('sage.combinat.root_system.fusion_ring', ['FusionRing']) from .branching_rules import BranchingRule, branching_rule_from_plethysm, branching_rule lazy_import('sage.combinat.root_system.non_symmetric_macdonald_polynomials', 'NonSymmetricMacdonaldPolynomials') diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py new file mode 100644 index 00000000000..6b77d8e2ad5 --- /dev/null +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -0,0 +1,444 @@ +""" +Fusion Rings +""" +# **************************************************************************** +# Copyright (C) 2019 Daniel Bump +# Nicolas Thiery +# Guillermo Aboumrad +# +# Distributed under the terms of the GNU General Public License (GPL) +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.combinat.root_system.root_system import RootSystem +from sage.combinat.root_system.weyl_characters import WeylCharacterRing +from functools import reduce +import sage.combinat.root_system.branching_rules +from operator import mul +from sage.categories.all import Category, Algebras, AlgebrasWithBasis +from sage.combinat.free_module import CombinatorialFreeModule +from sage.combinat.q_analogues import q_int +from sage.combinat.root_system.cartan_type import CartanType +from sage.combinat.root_system.root_system import RootSystem +from sage.matrix.special import diagonal_matrix +from sage.matrix.constructor import matrix +from sage.misc.lazy_attribute import lazy_attribute +from sage.misc.misc import inject_variable +from sage.rings.all import ZZ, CC +from sage.rings.number_field.number_field import CyclotomicField + +class FusionRing(WeylCharacterRing): + r""" + Return the Fusion Ring (Verlinde Algebra) of level ``k``. + + INPUT: + + - ``ct`` -- the Cartan type of a simple (finite-dimensional) Lie algebra + - ``k`` -- a nonnegative integer + + This algebra has a basis indexed by the weights of level `\leq k`. + It is implemented as a variant of the :class:`WeylCharacterRing`. + + EXAMPLES:: + + sage: A22 = FusionRing("A2",2) + sage: [f1, f2] = A22.fundamental_weights() + sage: M = [A22(x) for x in [0*f1, 2*f1, 2*f2, f1+f2, f2, f1]] + sage: [M[3] * x for x in M] + [A22(1,1), + A22(0,1), + A22(1,0), + A22(0,0) + A22(1,1), + A22(0,1) + A22(2,0), + A22(1,0) + A22(0,2)] + + You may assign your own labels to the basis elements. In the next + example, we create the `SO(5)` fusion ring of level `2`, check the + weights of the basis elements, then assign new labels to them:: + + sage: B22 = FusionRing("B2", 2) + sage: b = [B22(x) for x in B22.get_order()]; b + [B22(0,0), B22(1,0), B22(0,1), B22(2,0), B22(1,1), B22(0,2)] + sage: [x.highest_weight() for x in b] + [(0, 0), (1, 0), (1/2, 1/2), (2, 0), (3/2, 1/2), (1, 1)] + sage: B22.fusion_labels(['I0','Y1','X','Z','Xp','Y2']) + sage: b = [B22(x) for x in B22.get_order()]; b + [I0, Y1, X, Z, Xp, Y2] + sage: [(x, x.highest_weight()) for x in b] + [(I0, (0, 0)), + (Y1, (1, 0)), + (X, (1/2, 1/2)), + (Z, (2, 0)), + (Xp, (3/2, 1/2)), + (Y2, (1, 1))] + sage: X*Y1 + X + Xp + sage: Z*Z + I0 + + A fixed order of the basis keys is avalailable with :meth:`get_order`. + This is the order used by methods such as :meth:`s_matrix`. + You may use :meth:`set_order` to reorder the basis:: + + sage: B22.set_order([x.highest_weight() for x in [I0,Y1,Y2,X,Xp,Z]]) + sage: [B22(x) for x in B22.get_order()] + [I0, Y1, Y2, X, Xp, Z] + + To reset the labels and the order to their defaults, + you may run `fusion_labels` with no parameter: + + sage: B22.fusion_labels() + sage: [B22(x) for x in B22.get_order()] + [B22(0,0), B22(1,0), B22(0,1), B22(2,0), B22(1,1), B22(0,2)] + + + REFERENCES: + + - [DFMS1996]_ Chapter 16 + - [Feingold2004]_ + - [Fuchs1994]_ + - [Walton1990]_ + """ + @staticmethod + def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjugate=False): + """ + Normalize input to ensure a unique representation. + + TESTS:: + + sage: F1 = FusionRing('B3', 2) + sage: F2 = FusionRing(CartanType('B3'), QQ(2), ZZ) + sage: F3 = FusionRing(CartanType('B3'), int(2), style="coroots") + sage: F1 is F2 and F2 is F3 + True + + sage: A23 = FusionRing('A2', 3) + sage: TestSuite(A23).run() + + sage: B22 = FusionRing('B2', 2) + sage: TestSuite(B22).run() + + sage: C31 = FusionRing('C3', 1) + sage: TestSuite(C31).run() + + sage: D41 = FusionRing('D4', 1) + sage: TestSuite(D41).run() + """ + return super(FusionRing, cls).__classcall__(cls, ct, base_ring=base_ring, + prefix=prefix, style=style, k=k, conjugate=conjugate) + + @lazy_attribute + def _q_field(self): + """ + The cyclotomic field of 4l-th roots of unity, where + l is the fusion_l of the category (see above). Call this + lazy attribute via the method `self.q_field()`. + """ + self._K = CyclotomicField(4*self._l) + return self._K + + def q_field(self): + """ + Return the cyclotomic field of 4l-th roots of unity, where + `l` is the ``fusion_l`` of the category (see above). + + This field contains the twists, categorical dimensions, and the entries of the + S-matrix. + + EXAMPLES:: + + sage: B22=FusionRing("B2",2) + sage: B22.q_field() + Cyclotomic Field of order 40 and degree 16 + sage: A11=FusionRing('A1',1) + sage: A11.q_field() + Cyclotomic Field of order 12 and degree 4 + """ + return self._q_field + + def _element_constructor(self,weight): + return self._parent._element_constructor(self._parent,weight) + + def get_order(self): + """ + Returns the weights of the basis vectors in a fixed order. + You may change the order of the basis using :meth:`set_order` + + EXAMPLES:: + + sage: A14=FusionRing("A1",4) + sage: w = A14.get_order(); w + [(0, 0), (1/2, -1/2), (1, -1), (3/2, -3/2), (2, -2)] + sage: A14.set_order([w[k] for k in [0,4,1,3,2]]) + sage: [A14(x) for x in A14.get_order()] + [A14(0), A14(4), A14(1), A14(3), A14(2)] + + This duplicates :meth:`get_order` from `combinat.free_module`. + However unlike the `combinat.free_module` method with the same + name this `get_order` is not cached. Caching of get_order causes + inconsistent results after calling `set_order`. + """ + if self._order is None: + self.set_order(self.basis().keys().list()) + return self._order + + def some_elements(self): + """ + Return some elements of ``self``. + + EXAMPLES:: + + sage: D41 = FusionRing('D4', 1) + sage: D41.some_elements() + [D41(1,0,0,0), D41(0,0,1,0), D41(0,0,0,1)] + """ + return [self.monomial(x) for x in self.fundamental_weights() + if self.level(x) <= self._k] + + def fusion_k(self): + r""" + Return the level of the FusionRing. + + EXAMPLES:: + + sage: B22=FusionRing('B2',2) + sage: B22.fusion_k() + 2 + """ + return self._k + + def fusion_l(self): + r""" + Returns the product `m_g(k + h^\vee)`, where `m_g` denotes the + square of the ratio of the lengths of long to short roots of + the underlying Lie algebra, `k` denotes the level of the FusionRing, + and `h^\vee` denotes the dual Coxeter number of the underlying Lie + algebra. + + This value is used to define the associated root of unity q. + + EXAMPLES:: + + sage: B22=FusionRing('B2',2) + sage: B22.fusion_l() + 10 + sage: D52=FusionRing('D5',2) + sage: D52.fusion_l() + 10 + """ + return self._l + + def twists_matrix(self): + r""" + Return a diagonal matrix describing the twist corresponding to + each simple object in the ``FusionRing``. + + EXAMPLES:: + + sage: B22=FusionRing('B2',2) + sage: B22.twists_matrix() + [ 0 0 0 0 0 0] + [ 0 4/5 0 0 0 0] + [ 0 0 1/2 0 0 0] + [ 0 0 0 2 0 0] + [ 0 0 0 0 3/2 0] + [ 0 0 0 0 0 6/5] + """ + return diagonal_matrix(self.basis()[x].twist() for x in self.get_order()) + + def q_dims(self): + r""" + Return a list of quantum dimensions of the simple objects. + + EXAMPLES:: + + sage: F41=FusionRing('F4',1,conjugate=True) + sage: F41.q_dims() + [1, -zeta80^24 + zeta80^16 + 1] + sage: B22=FusionRing("B2",2) + sage: B22.q_dims() + [1, 2, -2*zeta40^12 + 2*zeta40^8 + 1, 1, -2*zeta40^12 + 2*zeta40^8 + 1, 2] + """ + b = self.basis() + return [b[x].q_dimension() for x in self.get_order()] + + def s_ij(self, elt_i, elt_j): + """ + Return the element of the S-matrix of this FusionRing corresponding to + the given elements. + + INPUT: + + - ``elt_i``, ``elt_j`` -- elements of the fusion basis + + EXAMPLES:: + + sage: G21=FusionRing("G2",1) + sage: b=G21.basis() + sage: [G21.s_ij(x,y) for x in b for y in b] + [1, -zeta60^14 + zeta60^6 + zeta60^4, -zeta60^14 + zeta60^6 + zeta60^4, -1] + """ + l = self.fusion_l() + K = self.q_field() + q = K.gen() + ijtwist = -2*l*(elt_i.twist() + elt_j.twist()) + return sum(self(k).q_dimension()*q**(2*l*self(k).twist()+ijtwist) for k in (elt_i.dual()*elt_j).monomial_coefficients()) + + def s_matrix(self): + r""" + Return the S-matrix of this FusionRing. + + EXAMPLES:: + + sage: D91=FusionRing('D9',1) + sage: D91.s_matrix() + [ 1 1 1 1] + [ 1 1 -1 -1] + [ 1 -1 -zeta68^17 zeta68^17] + [ 1 -1 zeta68^17 -zeta68^17] + + sage: D41=FusionRing('D4',1) + sage: D41.s_matrix() + [ 1 1 1 1] + [ 1 1 -1 -1] + [ 1 -1 1 -1] + [ 1 -1 -1 1] + """ + b = self.basis() + return matrix([[self.s_ij(b[x],b[y]) for x in self.get_order()] for y in self.get_order()]) + + def fusion_labels(self, labels=None): + """ + Set the labels of the basis. + + INPUT: + + - ``labels`` -- (default: ``None``) a list of strings or string + + If ``labels`` is a list, the length of the list must equal the + number of basis elements. These become the names of + the basis elements. + + If ``labels`` is a string, this is treated as a prefix and a + list of names is generated. + + If ``labels`` is ``None``, then this resets the labels to the default. + + EXAMPLES:: + + sage: A13 = FusionRing("A1", 3) + sage: A13.fusion_labels("x") + sage: fb = list(A13.basis()); fb + [x0, x1, x2, x3] + sage: Matrix([[x*y for y in A13.basis()] for x in A13.basis()]) + [ x0 x1 x2 x3] + [ x1 x0 + x2 x1 + x3 x2] + [ x2 x1 + x3 x0 + x2 x1] + [ x3 x2 x1 x0] + + We reset the labels to the default:: + + sage: A13.fusion_labels() + sage: fb + [A13(0), A13(1), A13(2), A13(3)] + """ + if labels is None: + self._order = None + self._fusion_labels = None + return + elif type(labels) == str: + labels = [labels+"%d"%k for k in range(len(self.basis()))] + elif len(labels) != len(self.basis()): + raise ValueError('invalid data') + d = {} + fb = list(self.get_order()) + for j, b in enumerate(fb): + t = tuple([b.inner_product(x) for x in self.simple_coroots()]) + d[t] = labels[j] + inject_variable(labels[j], self(b)) + self._fusion_labels = d + + class Element(WeylCharacterRing.Element): + """ + A class for FusionRing elements + """ + def is_simple_object(self): + """ + Determine whether element is a simple object of the FusionRing. + + EXAMPLES:: + + sage: A22=FusionRing("A2",2) + sage: x = A22(1,0); x + A22(1,0) + sage: x.is_simple_object() + True + sage: x^2 + A22(0,1) + A22(2,0) + sage: (x^2).is_simple_object() + False + """ + return self.parent()._k is not None and len(self.monomial_coefficients())==1 + + def twist(self): + r""" + Compute the object's twist. Returns a rational number `h_X` such that + `e^{(i \pi h_X)}` is the twist of `X`. + + We compute the twists following p.7 of [Row2006]_, noting that the bilinear form + is normalized so that `\langle\alpha, \alpha\rangle = 2` for SHORT roots. + + EXAMPLES:: + + sage: G21=FusionRing('G2',1) + sage: G21.basis() + Finite family {(0, 0, 0): G21(0,0), (1, 0, -1): G21(1,0)} + sage: G21(1,0).twist() + 4/5 + sage: F41=FusionRing('F4',1,conjugate=True) + sage: F41.basis() + Finite family {(0, 0, 0, 0): F41(0,0,0,0), (1, 0, 0, 0): F41(0,0,0,1)} + sage: F41(0,0,0,1).twist() + 4/5 + """ + if not self.is_simple_object(): + raise ValueError("quantum twist is only available for simple objects of a FusionRing") + rho = sum(self.parent().positive_roots())/2 + lam = self.highest_weight() + inner = lam.inner_product(lam+2*rho) + twist = self.parent()._conj*self.parent()._nf*inner/self.parent().fusion_l() + #Reduce to canonical form + while twist > 2: + twist -= 2 + while twist < 0: + twist += 2 + return twist + + def q_dimension(self): + r"""" + This returns the quantum dimension as an element of the cyclotomic + field of the `2l`-th roots of unity, where `l = m(k+h^\vee)` + with `m=1,2,3` depending on whether type is simply, doubly or + triply laced, `k` is the level and `h^\vee` is the dual Coxeter number. + + EXAMPLE:: + + sage: B22=FusionRing("B2",2) + sage: [(b.q_dimension())^2 for b in B22.basis()] + [1, 4, 5, 1, 5, 4] + """ + if not self.is_simple_object(): + raise ValueError("quantum twist is only available for simple objects of a FusionRing") + lam = self.highest_weight() + space = self.parent().space() + rho = space.rho() + l = self.parent().fusion_l() + num = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(lam+rho)) for alpha in space.positive_roots()], 1) + den = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(rho)) for alpha in space.positive_roots()], 1) + expr = num/den + pr = expr.parent().ring() + q = pr.gen()**2 + expr = pr(expr) + expr = expr.substitute(q=q**2)/q**(expr.degree()) + zet = self.parent().q_field().gen() + return expr.substitute(q=zet) diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index 02678db2451..38f9b8451e8 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -2209,420 +2209,3 @@ def demazure_lusztig(self, i, v): return self.demazure_lusztig(i.reduced_word(), v) except Exception: raise ValueError("unknown index {}".format(i)) - - -class FusionRing(WeylCharacterRing): - r""" - Return the Fusion Ring (Verlinde Algebra) of level ``k``. - - INPUT: - - - ``ct`` -- the Cartan type of a simple (finite-dimensional) Lie algebra - - ``k`` -- a nonnegative integer - - This algebra has a basis indexed by the weights of level `\leq k`. - It is implemented as a variant of the :class:`WeylCharacterRing`. - - EXAMPLES:: - - sage: A22 = FusionRing("A2",2) - sage: [f1, f2] = A22.fundamental_weights() - sage: M = [A22(x) for x in [0*f1, 2*f1, 2*f2, f1+f2, f2, f1]] - sage: [M[3] * x for x in M] - [A22(1,1), - A22(0,1), - A22(1,0), - A22(0,0) + A22(1,1), - A22(0,1) + A22(2,0), - A22(1,0) + A22(0,2)] - - You may assign your own labels to the basis elements. In the next - example, we create the `SO(5)` fusion ring of level `2`, check the - weights of the basis elements, then assign new labels to them:: - - sage: B22 = FusionRing("B2", 2) - sage: b = [B22(x) for x in B22.get_order()]; b - [B22(0,0), B22(1,0), B22(0,1), B22(2,0), B22(1,1), B22(0,2)] - sage: [x.highest_weight() for x in b] - [(0, 0), (1, 0), (1/2, 1/2), (2, 0), (3/2, 1/2), (1, 1)] - sage: B22.fusion_labels(['I0','Y1','X','Z','Xp','Y2']) - sage: b = [B22(x) for x in B22.get_order()]; b - [I0, Y1, X, Z, Xp, Y2] - sage: [(x, x.highest_weight()) for x in b] - [(I0, (0, 0)), - (Y1, (1, 0)), - (X, (1/2, 1/2)), - (Z, (2, 0)), - (Xp, (3/2, 1/2)), - (Y2, (1, 1))] - sage: X*Y1 - X + Xp - sage: Z*Z - I0 - - A fixed order of the basis keys is avalailable with :meth:`get_order`. - This is the order used by methods such as :meth:`s_matrix`. - You may use :meth:`set_order` to reorder the basis:: - - sage: B22.set_order([x.highest_weight() for x in [I0,Y1,Y2,X,Xp,Z]]) - sage: [B22(x) for x in B22.get_order()] - [I0, Y1, Y2, X, Xp, Z] - - To reset the labels and the order to their defaults, - you may run `fusion_labels` with no parameter: - - sage: B22.fusion_labels() - sage: [B22(x) for x in B22.get_order()] - [B22(0,0), B22(1,0), B22(0,1), B22(2,0), B22(1,1), B22(0,2)] - - - REFERENCES: - - - [DFMS1996]_ Chapter 16 - - [Feingold2004]_ - - [Fuchs1994]_ - - [Walton1990]_ - """ - @staticmethod - def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjugate=False): - """ - Normalize input to ensure a unique representation. - - TESTS:: - - sage: F1 = FusionRing('B3', 2) - sage: F2 = FusionRing(CartanType('B3'), QQ(2), ZZ) - sage: F3 = FusionRing(CartanType('B3'), int(2), style="coroots") - sage: F1 is F2 and F2 is F3 - True - - sage: A23 = FusionRing('A2', 3) - sage: TestSuite(A23).run() - - sage: B22 = FusionRing('B2', 2) - sage: TestSuite(B22).run() - - sage: C31 = FusionRing('C3', 1) - sage: TestSuite(C31).run() - - sage: D41 = FusionRing('D4', 1) - sage: TestSuite(D41).run() - """ - return super(FusionRing, cls).__classcall__(cls, ct, base_ring=base_ring, - prefix=prefix, style=style, k=k, conjugate=conjugate) - - @lazy_attribute - def _q_field(self): - """ - The cyclotomic field of 4l-th roots of unity, where - l is the fusion_l of the category (see above). Call this - lazy attribute via the method `self.q_field()`. - """ - self._K = CyclotomicField(4*self._l) - return self._K - - def q_field(self): - """ - Return the cyclotomic field of 4l-th roots of unity, where - `l` is the ``fusion_l`` of the category (see above). - - This field contains the twists, categorical dimensions, and the entries of the - S-matrix. - - EXAMPLES:: - - sage: B22=FusionRing("B2",2) - sage: B22.q_field() - Cyclotomic Field of order 40 and degree 16 - sage: A11=FusionRing('A1',1) - sage: A11.q_field() - Cyclotomic Field of order 12 and degree 4 - """ - return self._q_field - - def _element_constructor(self,weight): - return self._parent._element_constructor(self._parent,weight) - - def get_order(self): - """ - Returns the weights of the basis vectors in a fixed order. - You may change the order of the basis using :meth:`set_order` - - EXAMPLES:: - - sage: A14=FusionRing("A1",4) - sage: w = A14.get_order(); w - [(0, 0), (1/2, -1/2), (1, -1), (3/2, -3/2), (2, -2)] - sage: A14.set_order([w[k] for k in [0,4,1,3,2]]) - sage: [A14(x) for x in A14.get_order()] - [A14(0), A14(4), A14(1), A14(3), A14(2)] - - This duplicates :meth:`get_order` from `combinat.free_module`. - However unlike the `combinat.free_module` method with the same - name this `get_order` is not cached. Caching of get_order causes - inconsistent results after calling `set_order`. - """ - if self._order is None: - self.set_order(self.basis().keys().list()) - return self._order - - def some_elements(self): - """ - Return some elements of ``self``. - - EXAMPLES:: - - sage: D41 = FusionRing('D4', 1) - sage: D41.some_elements() - [D41(1,0,0,0), D41(0,0,1,0), D41(0,0,0,1)] - """ - return [self.monomial(x) for x in self.fundamental_weights() - if self.level(x) <= self._k] - - def fusion_k(self): - r""" - Return the level of the FusionRing. - - EXAMPLES:: - - sage: B22=FusionRing('B2',2) - sage: B22.fusion_k() - 2 - """ - return self._k - - def fusion_l(self): - r""" - Returns the product `m_g(k + h^\vee)`, where `m_g` denotes the - square of the ratio of the lengths of long to short roots of - the underlying Lie algebra, `k` denotes the level of the FusionRing, - and `h^\vee` denotes the dual Coxeter number of the underlying Lie - algebra. - - This value is used to define the associated root of unity q. - - EXAMPLES:: - - sage: B22=FusionRing('B2',2) - sage: B22.fusion_l() - 10 - sage: D52=FusionRing('D5',2) - sage: D52.fusion_l() - 10 - """ - return self._l - - def twists_matrix(self): - r""" - Return a diagonal matrix describing the twist corresponding to - each simple object in the ``FusionRing``. - - EXAMPLES:: - - sage: B22=FusionRing('B2',2) - sage: B22.twists_matrix() - [ 0 0 0 0 0 0] - [ 0 4/5 0 0 0 0] - [ 0 0 1/2 0 0 0] - [ 0 0 0 2 0 0] - [ 0 0 0 0 3/2 0] - [ 0 0 0 0 0 6/5] - """ - return diagonal_matrix(self.basis()[x].twist() for x in self.get_order()) - - def q_dims(self): - r""" - Return a list of quantum dimensions of the simple objects. - - EXAMPLES:: - - sage: F41=FusionRing('F4',1,conjugate=True) - sage: F41.q_dims() - [1, -zeta80^24 + zeta80^16 + 1] - sage: B22=FusionRing("B2",2) - sage: B22.q_dims() - [1, 2, -2*zeta40^12 + 2*zeta40^8 + 1, 1, -2*zeta40^12 + 2*zeta40^8 + 1, 2] - """ - b = self.basis() - return [b[x].q_dimension() for x in self.get_order()] - - def s_ij(self, elt_i, elt_j): - """ - Return the element of the S-matrix of this FusionRing corresponding to - the given elements. - - INPUT: - - - ``elt_i``, ``elt_j`` -- elements of the fusion basis - - EXAMPLES:: - - sage: G21=FusionRing("G2",1) - sage: b=G21.basis() - sage: [G21.s_ij(x,y) for x in b for y in b] - [1, -zeta60^14 + zeta60^6 + zeta60^4, -zeta60^14 + zeta60^6 + zeta60^4, -1] - """ - l = self.fusion_l() - K = self.q_field() - q = K.gen() - ijtwist = -2*l*(elt_i.twist() + elt_j.twist()) - return sum(self(k).q_dimension()*q**(2*l*self(k).twist()+ijtwist) for k in (elt_i.dual()*elt_j).monomial_coefficients()) - - def s_matrix(self): - r""" - Return the S-matrix of this FusionRing. - - EXAMPLES:: - - sage: D91=FusionRing('D9',1) - sage: D91.s_matrix() - [ 1 1 1 1] - [ 1 1 -1 -1] - [ 1 -1 -zeta68^17 zeta68^17] - [ 1 -1 zeta68^17 -zeta68^17] - - sage: D41=FusionRing('D4',1) - sage: D41.s_matrix() - [ 1 1 1 1] - [ 1 1 -1 -1] - [ 1 -1 1 -1] - [ 1 -1 -1 1] - """ - b = self.basis() - return matrix([[self.s_ij(b[x],b[y]) for x in self.get_order()] for y in self.get_order()]) - - def fusion_labels(self, labels=None): - """ - Set the labels of the basis. - - INPUT: - - - ``labels`` -- (default: ``None``) a list of strings or string - - If ``labels`` is a list, the length of the list must equal the - number of basis elements. These become the names of - the basis elements. - - If ``labels`` is a string, this is treated as a prefix and a - list of names is generated. - - If ``labels`` is ``None``, then this resets the labels to the default. - - EXAMPLES:: - - sage: A13 = FusionRing("A1", 3) - sage: A13.fusion_labels("x") - sage: fb = list(A13.basis()); fb - [x0, x1, x2, x3] - sage: Matrix([[x*y for y in A13.basis()] for x in A13.basis()]) - [ x0 x1 x2 x3] - [ x1 x0 + x2 x1 + x3 x2] - [ x2 x1 + x3 x0 + x2 x1] - [ x3 x2 x1 x0] - - We reset the labels to the default:: - - sage: A13.fusion_labels() - sage: fb - [A13(0), A13(1), A13(2), A13(3)] - """ - if labels is None: - self._order = None - self._fusion_labels = None - return - elif type(labels) == str: - labels = [labels+"%d"%k for k in range(len(self.basis()))] - elif len(labels) != len(self.basis()): - raise ValueError('invalid data') - d = {} - fb = list(self.get_order()) - for j, b in enumerate(fb): - t = tuple([b.inner_product(x) for x in self.simple_coroots()]) - d[t] = labels[j] - inject_variable(labels[j], self(b)) - self._fusion_labels = d - - class Element(WeylCharacterRing.Element): - """ - A class for FusionRing elements - """ - def is_simple_object(self): - """ - Determine whether element is a simple object of the FusionRing. - - EXAMPLES:: - - sage: A22=FusionRing("A2",2) - sage: x = A22(1,0); x - A22(1,0) - sage: x.is_simple_object() - True - sage: x^2 - A22(0,1) + A22(2,0) - sage: (x^2).is_simple_object() - False - """ - return self.parent()._k is not None and len(self.monomial_coefficients())==1 - - def twist(self): - r""" - Compute the object's twist. Returns a rational number `h_X` such that - `e^{(i \pi h_X)}` is the twist of `X`. - - We compute the twists following p.7 of [Row2006]_, noting that the bilinear form - is normalized so that `\langle\alpha, \alpha\rangle = 2` for SHORT roots. - - EXAMPLES:: - - sage: G21=FusionRing('G2',1) - sage: G21.basis() - Finite family {(0, 0, 0): G21(0,0), (1, 0, -1): G21(1,0)} - sage: G21(1,0).twist() - 4/5 - sage: F41=FusionRing('F4',1,conjugate=True) - sage: F41.basis() - Finite family {(0, 0, 0, 0): F41(0,0,0,0), (1, 0, 0, 0): F41(0,0,0,1)} - sage: F41(0,0,0,1).twist() - 4/5 - """ - if not self.is_simple_object(): - raise ValueError("quantum twist is only available for simple objects of a FusionRing") - rho = sum(self.parent().positive_roots())/2 - lam = self.highest_weight() - inner = lam.inner_product(lam+2*rho) - twist = self.parent()._conj*self.parent()._nf*inner/self.parent().fusion_l() - #Reduce to canonical form - while twist > 2: - twist -= 2 - while twist < 0: - twist += 2 - return twist - - def q_dimension(self): - r"""" - This returns the quantum dimension as an element of the cyclotomic - field of the `2l`-th roots of unity, where `l = m(k+h^\vee)` - with `m=1,2,3` depending on whether type is simply, doubly or - triply laced, `k` is the level and `h^\vee` is the dual Coxeter number. - - EXAMPLE:: - - sage: B22=FusionRing("B2",2) - sage: [(b.q_dimension())^2 for b in B22.basis()] - [1, 4, 5, 1, 5, 4] - """ - if not self.is_simple_object(): - raise ValueError("quantum twist is only available for simple objects of a FusionRing") - lam = self.highest_weight() - space = self.parent().space() - rho = space.rho() - l = self.parent().fusion_l() - num = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(lam+rho)) for alpha in space.positive_roots()], 1) - den = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(rho)) for alpha in space.positive_roots()], 1) - expr = num/den - pr = expr.parent().ring() - q = pr.gen()**2 - expr = pr(expr) - expr = expr.substitute(q=q**2)/q**(expr.degree()) - zet = self.parent().q_field().gen() - return expr.substitute(q=zet) From 7c37c54a65e8e2d1942c4d1b0c95c9f9ecdd4657 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Sat, 20 Jun 2020 17:28:00 -0700 Subject: [PATCH 12/34] the highest weight method for FusionRings is renamed more appropriately weight --- src/sage/combinat/root_system/fusion_ring.py | 48 +++++++++++++------ .../combinat/root_system/weyl_characters.py | 8 +--- 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 6b77d8e2ad5..7512304ef1b 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -36,8 +36,18 @@ class FusionRing(WeylCharacterRing): - ``ct`` -- the Cartan type of a simple (finite-dimensional) Lie algebra - ``k`` -- a nonnegative integer - This algebra has a basis indexed by the weights of level `\leq k`. - It is implemented as a variant of the :class:`WeylCharacterRing`. + This algebra has a basis (sometimes called *primary fields*) + indexed by the weights of level `\leq k`. These arise as + the fusion algebras of WZW conformal field theories, or from + quantum groups at roots of unity. The :class:`FusionRing` class is + implemented as a variant of the :class:`WeylCharacterRing`. + + REFERENCES: + + - [DFMS1996]_ Chapter 16 + - [Feingold2004]_ + - [Fuchs1994]_ + - [Walton1990]_ EXAMPLES:: @@ -59,12 +69,12 @@ class FusionRing(WeylCharacterRing): sage: B22 = FusionRing("B2", 2) sage: b = [B22(x) for x in B22.get_order()]; b [B22(0,0), B22(1,0), B22(0,1), B22(2,0), B22(1,1), B22(0,2)] - sage: [x.highest_weight() for x in b] + sage: [x.weight() for x in b] [(0, 0), (1, 0), (1/2, 1/2), (2, 0), (3/2, 1/2), (1, 1)] sage: B22.fusion_labels(['I0','Y1','X','Z','Xp','Y2']) sage: b = [B22(x) for x in B22.get_order()]; b [I0, Y1, X, Z, Xp, Y2] - sage: [(x, x.highest_weight()) for x in b] + sage: [(x, x.weight()) for x in b] [(I0, (0, 0)), (Y1, (1, 0)), (X, (1/2, 1/2)), @@ -80,7 +90,7 @@ class FusionRing(WeylCharacterRing): This is the order used by methods such as :meth:`s_matrix`. You may use :meth:`set_order` to reorder the basis:: - sage: B22.set_order([x.highest_weight() for x in [I0,Y1,Y2,X,Xp,Z]]) + sage: B22.set_order([x.weight() for x in [I0,Y1,Y2,X,Xp,Z]]) sage: [B22(x) for x in B22.get_order()] [I0, Y1, Y2, X, Xp, Z] @@ -91,13 +101,6 @@ class FusionRing(WeylCharacterRing): sage: [B22(x) for x in B22.get_order()] [B22(0,0), B22(1,0), B22(0,1), B22(2,0), B22(1,1), B22(0,2)] - - REFERENCES: - - - [DFMS1996]_ Chapter 16 - - [Feingold2004]_ - - [Fuchs1994]_ - - [Walton1990]_ """ @staticmethod def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjugate=False): @@ -360,7 +363,7 @@ def fusion_labels(self, labels=None): class Element(WeylCharacterRing.Element): """ - A class for FusionRing elements + A class for FusionRing elements. """ def is_simple_object(self): """ @@ -380,6 +383,21 @@ def is_simple_object(self): """ return self.parent()._k is not None and len(self.monomial_coefficients())==1 + def weight(self): + """ + This method is only available for basis elements. Returns the + parametrizing dominant weight in the level `k` alcove. + + EXAMPLES:: + + sage: A21 = FusionRing("A2",1) + sage: sorted([x.weight() for x in A21.basis()]) + [(0, 0, 0), (1/3, 1/3, -2/3), (2/3, -1/3, -1/3)] + """ + if len(self.monomial_coefficients()) != 1: + raise ValueError("fusion weight is valid for basis elements only") + return self.leading_support() + def twist(self): r""" Compute the object's twist. Returns a rational number `h_X` such that @@ -404,7 +422,7 @@ def twist(self): if not self.is_simple_object(): raise ValueError("quantum twist is only available for simple objects of a FusionRing") rho = sum(self.parent().positive_roots())/2 - lam = self.highest_weight() + lam = self.weight() inner = lam.inner_product(lam+2*rho) twist = self.parent()._conj*self.parent()._nf*inner/self.parent().fusion_l() #Reduce to canonical form @@ -429,7 +447,7 @@ def q_dimension(self): """ if not self.is_simple_object(): raise ValueError("quantum twist is only available for simple objects of a FusionRing") - lam = self.highest_weight() + lam = self.weight() space = self.parent().space() rho = space.rho() l = self.parent().fusion_l() diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index 38f9b8451e8..c90e2fdf0e6 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -1245,17 +1245,13 @@ def dual(self): def highest_weight(self): """ This method is only available for basis elements. Returns the - parametrizing dominant weight of an irreducible character or - simple element of a FusionRing. + parametrizing dominant weight of an irreducible character. - Examples:: + EXAMPLES:: sage: G2 = WeylCharacterRing("G2", style="coroots") sage: [x.highest_weight() for x in [G2(1,0),G2(0,1)]] [(1, 0, -1), (2, -1, -1)] - sage: A21 = FusionRing("A2",1) - sage: sorted([x.highest_weight() for x in A21.basis()]) - [(0, 0, 0), (1/3, 1/3, -2/3), (2/3, -1/3, -1/3)] """ if len(self.monomial_coefficients()) != 1: raise ValueError("fusion weight is valid for basis elements only") From 8216f329a7dca729c64b18769cfa54f5c907900b Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Mon, 22 Jun 2020 06:26:05 -0700 Subject: [PATCH 13/34] fix unused imports in fusion_ring and weyl_characters found by pyflakes --- src/sage/combinat/root_system/fusion_ring.py | 8 +------- src/sage/combinat/root_system/weyl_characters.py | 9 +-------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 7512304ef1b..84405e0e75d 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -13,18 +13,13 @@ from sage.combinat.root_system.root_system import RootSystem from sage.combinat.root_system.weyl_characters import WeylCharacterRing from functools import reduce -import sage.combinat.root_system.branching_rules from operator import mul -from sage.categories.all import Category, Algebras, AlgebrasWithBasis -from sage.combinat.free_module import CombinatorialFreeModule from sage.combinat.q_analogues import q_int -from sage.combinat.root_system.cartan_type import CartanType -from sage.combinat.root_system.root_system import RootSystem from sage.matrix.special import diagonal_matrix from sage.matrix.constructor import matrix from sage.misc.lazy_attribute import lazy_attribute from sage.misc.misc import inject_variable -from sage.rings.all import ZZ, CC +from sage.rings.all import ZZ from sage.rings.number_field.number_field import CyclotomicField class FusionRing(WeylCharacterRing): @@ -450,7 +445,6 @@ def q_dimension(self): lam = self.weight() space = self.parent().space() rho = space.rho() - l = self.parent().fusion_l() num = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(lam+rho)) for alpha in space.positive_roots()], 1) den = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(rho)) for alpha in space.positive_roots()], 1) expr = num/den diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index c90e2fdf0e6..53064302fcc 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -10,24 +10,17 @@ # **************************************************************************** from __future__ import print_function -from functools import reduce import sage.combinat.root_system.branching_rules -from operator import mul from sage.categories.all import Category, Algebras, AlgebrasWithBasis from sage.combinat.free_module import CombinatorialFreeModule -from sage.combinat.q_analogues import q_int from sage.combinat.root_system.cartan_type import CartanType from sage.combinat.root_system.root_system import RootSystem -from sage.matrix.special import diagonal_matrix from sage.matrix.constructor import matrix from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet from sage.misc.functional import is_even -from sage.misc.misc import inject_variable -from sage.rings.all import ZZ, CC -from sage.rings.number_field.number_field import CyclotomicField - +from sage.rings.all import ZZ class WeylCharacterRing(CombinatorialFreeModule): r""" From e840d380132aed369298a464db0ae0e9e4297a5b Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Mon, 22 Jun 2020 06:43:45 -0700 Subject: [PATCH 14/34] remove misspelled (and therefore unused) method _element_constructor from fusion_ring. The (correctly spelled) _element_constructor_ method is inherited from class WeylCharacterRing so this should not be needed --- src/sage/combinat/root_system/fusion_ring.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 84405e0e75d..8e691a7176d 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -154,9 +154,6 @@ def q_field(self): """ return self._q_field - def _element_constructor(self,weight): - return self._parent._element_constructor(self._parent,weight) - def get_order(self): """ Returns the weights of the basis vectors in a fixed order. From 929c13f798580ac4d4d7450ccfae4bc4194f86ee Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Mon, 22 Jun 2020 12:22:16 -0700 Subject: [PATCH 15/34] patchbot requested changes on pyflakes, blocks, coverage --- src/sage/combinat/root_system/fusion_ring.py | 18 +++++++++++------- .../combinat/root_system/weyl_characters.py | 1 - 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 8e691a7176d..f0231a1fdaf 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -10,7 +10,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.combinat.root_system.root_system import RootSystem + from sage.combinat.root_system.weyl_characters import WeylCharacterRing from functools import reduce from operator import mul @@ -131,6 +131,13 @@ def _q_field(self): The cyclotomic field of 4l-th roots of unity, where l is the fusion_l of the category (see above). Call this lazy attribute via the method `self.q_field()`. + + EXAMPLES:: + + sage: B22=FusionRing("B2",2) + sage: B22.q_field() + Cyclotomic Field of order 40 and degree 16 + """ self._K = CyclotomicField(4*self._l) return self._K @@ -145,9 +152,6 @@ def q_field(self): EXAMPLES:: - sage: B22=FusionRing("B2",2) - sage: B22.q_field() - Cyclotomic Field of order 40 and degree 16 sage: A11=FusionRing('A1',1) sage: A11.q_field() Cyclotomic Field of order 12 and degree 4 @@ -156,7 +160,7 @@ def q_field(self): def get_order(self): """ - Returns the weights of the basis vectors in a fixed order. + This returns the weights of the basis vectors in a fixed order. You may change the order of the basis using :meth:`set_order` EXAMPLES:: @@ -204,7 +208,7 @@ def fusion_k(self): def fusion_l(self): r""" - Returns the product `m_g(k + h^\vee)`, where `m_g` denotes the + This Returns the product `m_g(k + h^\vee)`, where `m_g` denotes the square of the ratio of the lengths of long to short roots of the underlying Lie algebra, `k` denotes the level of the FusionRing, and `h^\vee` denotes the dual Coxeter number of the underlying Lie @@ -431,7 +435,7 @@ def q_dimension(self): with `m=1,2,3` depending on whether type is simply, doubly or triply laced, `k` is the level and `h^\vee` is the dual Coxeter number. - EXAMPLE:: + EXAMPLES:: sage: B22=FusionRing("B2",2) sage: [(b.q_dimension())^2 for b in B22.basis()] diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index 53064302fcc..e96bea32753 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -15,7 +15,6 @@ from sage.combinat.free_module import CombinatorialFreeModule from sage.combinat.root_system.cartan_type import CartanType from sage.combinat.root_system.root_system import RootSystem -from sage.matrix.constructor import matrix from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet From fc9f90399488962e2e09114ee34e95b19944b1fe Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Tue, 23 Jun 2020 10:08:45 -0700 Subject: [PATCH 16/34] new methods Nk_ij and N_ijk, and doc revision --- src/sage/combinat/root_system/fusion_ring.py | 72 +++++++++++++++++--- 1 file changed, 61 insertions(+), 11 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index f0231a1fdaf..261cd77b428 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -90,7 +90,7 @@ class FusionRing(WeylCharacterRing): [I0, Y1, Y2, X, Xp, Z] To reset the labels and the order to their defaults, - you may run `fusion_labels` with no parameter: + you may run :meth:`fusion_labels` with no parameter:: sage: B22.fusion_labels() sage: [B22(x) for x in B22.get_order()] @@ -128,9 +128,9 @@ def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjug @lazy_attribute def _q_field(self): """ - The cyclotomic field of 4l-th roots of unity, where - l is the fusion_l of the category (see above). Call this - lazy attribute via the method `self.q_field()`. + The cyclotomic field of `4\ell`-th roots of unity, where + `\ell` is computed by :meth:`fusion_l`. Call this + lazy attribute via the method :meth:`q_field()`. EXAMPLES:: @@ -144,8 +144,8 @@ def _q_field(self): def q_field(self): """ - Return the cyclotomic field of 4l-th roots of unity, where - `l` is the ``fusion_l`` of the category (see above). + Return the cyclotomic field of `4\ell`-th roots of unity, where + `\ell` is computed by :meth:`fusion_l`. This field contains the twists, categorical dimensions, and the entries of the S-matrix. @@ -172,10 +172,10 @@ def get_order(self): sage: [A14(x) for x in A14.get_order()] [A14(0), A14(4), A14(1), A14(3), A14(2)] - This duplicates :meth:`get_order` from `combinat.free_module`. - However unlike the `combinat.free_module` method with the same - name this `get_order` is not cached. Caching of get_order causes - inconsistent results after calling `set_order`. + This duplicates :meth:`get_order` from :mod:`combinat.free_module`. + However unlike the :mod:`combinat.free_module` method with the same + name this :meth:`get_order` is not cached. Caching of :meth:`get_order` causes + inconsistent results after calling :meth:`set_order`. """ if self._order is None: self.set_order(self.basis().keys().list()) @@ -214,7 +214,7 @@ def fusion_l(self): and `h^\vee` denotes the dual Coxeter number of the underlying Lie algebra. - This value is used to define the associated root of unity q. + This value is used to define the associated root of unity ``q``. EXAMPLES:: @@ -261,6 +261,55 @@ def q_dims(self): b = self.basis() return [b[x].q_dimension() for x in self.get_order()] + def N_ijk(self, elt_i, elt_j, elt_k): + """ + INPUT: + + - ``elt_i``, ``elt_j``, ``elt_k`` -- elements of the fusion basis + + Returns the symmetric fusion coefficient `N_{ijk}`. This + is the same as `N_{ij}^{k\\ast}`, where $N_{ij}^k$ are the + structure coefficients of the ring (see :meth:`Nk_ij`), + and `k\\ast`` denotes the dual element. The coefficient `N_{ijk}` + is unchanged under permutations of the three basis vectors. + + EXAMPLES:: + + sage: G23=FusionRing("G2",3) + sage: G23.fusion_labels("g") + sage: b = G23.basis().list(); b + [g0, g1, g2, g3, g4, g5] + sage: [(x,y,z) for x in b for y in b for z in b if G23.N_ijk(x,y,z)>1] + [(g3, g3, g3), (g3, g3, g4), (g3, g4, g3), (g4, g3, g3)] + sage: all([G23.N_ijk(x,y,z)==G23.N_ijk(y,z,x) for x in b for y in b for z in b]) + True + sage: all([G23.N_ijk(x,y,z)==G23.N_ijk(y,x,z) for x in b for y in b for z in b]) + True + + """ + return (elt_i*elt_j).monomial_coefficients().get(elt_k.dual().weight(),0) + + def Nk_ij(self, elt_i, elt_j, elt_k): + """ + Returns the fusion coefficient `N^k_{ij}`. These are + the structure coefficients of the fusion ring, so + + .. MATH:: + + i*j = \sum_k N_{ij}^k\,k + + as the example shows. + + EXAMPLES:: + + sage: A22=FusionRing("A2",2) + sage: b = A22.basis().list() + sage: all(x*y == sum(A22.Nk_ij(x,y,k)*k for k in b) for x in b for y in b) + True + + """ + return (elt_i*elt_j).monomial_coefficients().get(elt_k.weight(),0) + def s_ij(self, elt_i, elt_j): """ Return the element of the S-matrix of this FusionRing corresponding to @@ -281,6 +330,7 @@ def s_ij(self, elt_i, elt_j): K = self.q_field() q = K.gen() ijtwist = -2*l*(elt_i.twist() + elt_j.twist()) + return sum(self(k).q_dimension()*q**(2*l*self(k).twist()+ijtwist) for k in (elt_i.dual()*elt_j).monomial_coefficients()) def s_matrix(self): From 29bfb87397ca7eca8014c0808d9cda7c3567288c Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Wed, 24 Jun 2020 08:58:40 -0700 Subject: [PATCH 17/34] revision of s_ij and self._nf. Doc revision --- src/doc/en/reference/references/index.rst | 6 ++++ src/sage/combinat/root_system/fusion_ring.py | 29 ++++++++++++++----- .../combinat/root_system/weyl_characters.py | 5 ++-- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 311720794a8..b67d72feee1 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -344,6 +344,9 @@ REFERENCES: **B** +.. [BK] Bakalov and Kirillov, Lectures on tensor categories and modular functors, + AMS (2001). + .. [Ba1994] Kaushik Basu. *The Traveler's Dilemma: Paradoxes of Rationality in Game Theory*. The American Economic Review (1994): 391-395. @@ -1946,6 +1949,9 @@ REFERENCES: 1981, Pages 108-125, ISSN 0097-3165,:doi:`10.1016/0097-3165(81)90007-8`. (http://www.sciencedirect.com/science/article/pii/0097316581900078) +.. [EGNO] Pavel Etingof, Shlomo Gelaki, Dmitri Nikshych and Victor Ostrik, + "Tensor Categories", AMS Mathematical Surveys and Monographs 205 (2015). + .. [EGHLSVY] Pavel Etingof, Oleg Golberg, Sebastian Hensel, Tiankai Liu, Alex Schwendner, Dmitry Vaintrob, Elena Yudovina, "Introduction to representation theory", diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 261cd77b428..e304c464436 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -39,9 +39,12 @@ class FusionRing(WeylCharacterRing): REFERENCES: + - [BK]_ Chapter 3 - [DFMS1996]_ Chapter 16 + - [EGNO]_ Chapter 8 - [Feingold2004]_ - [Fuchs1994]_ + - [Row2006]_ - [Walton1990]_ EXAMPLES:: @@ -214,7 +217,7 @@ def fusion_l(self): and `h^\vee` denotes the dual Coxeter number of the underlying Lie algebra. - This value is used to define the associated root of unity ``q``. + This value is used to define the associated root of unity `q=e^{i\\pi/\ell}`. EXAMPLES:: @@ -296,7 +299,7 @@ def Nk_ij(self, elt_i, elt_j, elt_k): .. MATH:: - i*j = \sum_k N_{ij}^k\,k + i*j = \sum_{k} N_{ij}^k k as the example shows. @@ -313,7 +316,14 @@ def Nk_ij(self, elt_i, elt_j, elt_k): def s_ij(self, elt_i, elt_j): """ Return the element of the S-matrix of this FusionRing corresponding to - the given elements. + the given elements. This is computed using the formula + + .. MATH:: + + s_{i,j} = \\frac{1}{\\theta_i\\theta_j}\\sum_k N_{ik}^j d_k\\theta_k + + where `\\theta_k` is the twist and `d_k` is the quantum + dimension. See [Row2006]_ equation (2.2) or [EGNO]_ Proposition 8.13.8. INPUT: @@ -325,13 +335,15 @@ def s_ij(self, elt_i, elt_j): sage: b=G21.basis() sage: [G21.s_ij(x,y) for x in b for y in b] [1, -zeta60^14 + zeta60^6 + zeta60^4, -zeta60^14 + zeta60^6 + zeta60^4, -1] + """ l = self.fusion_l() K = self.q_field() q = K.gen() ijtwist = -2*l*(elt_i.twist() + elt_j.twist()) - - return sum(self(k).q_dimension()*q**(2*l*self(k).twist()+ijtwist) for k in (elt_i.dual()*elt_j).monomial_coefficients()) + mc = (elt_i.dual()*elt_j).monomial_coefficients() + return sum(self(k).q_dimension()*self.Nk_ij(elt_i,self(k),elt_j)*q**(2*l*self(k).twist()+ijtwist) for k in mc) + # return sum(self(k).q_dimension()*q**(2*l*self(k).twist()+ijtwist) for k in (elt_i.dual()*elt_j).monomial_coefficients()) def s_matrix(self): r""" @@ -446,11 +458,12 @@ def weight(self): def twist(self): r""" - Compute the object's twist. Returns a rational number `h_X` such that - `e^{(i \pi h_X)}` is the twist of `X`. + Compute the object's twist. This returns a rational number `h` such that + `\theta = e^{i \pi h}` is the twist of ``self``. This method is + only available for simple objects. We compute the twists following p.7 of [Row2006]_, noting that the bilinear form - is normalized so that `\langle\alpha, \alpha\rangle = 2` for SHORT roots. + is normalized so that `\langle\alpha, \alpha\rangle = 2` for short roots. EXAMPLES:: diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index e96bea32753..674e00d3575 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -165,12 +165,13 @@ def next_level(wt): #TODO: implement conjugate functionality if ct[0] in ['A','D','E']: self._m_g = 1 - self._nf = 1 elif ct[0] in ['B', 'C', 'F']: self._m_g = 2 - self._nf = 2 else: self._m_g = 3 + if ct[0] in ['B','F']: + self._nf = 2 + else: self._nf = 1 h_check = ct.dual_coxeter_number() self._l = self._m_g * (self._k + h_check) From cda861d170f5b777e77581cd79b232e93dea75e8 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Thu, 25 Jun 2020 15:11:42 -0700 Subject: [PATCH 18/34] verlinde formula, larger cyclotomic field, doc revision --- src/sage/combinat/root_system/fusion_ring.py | 91 ++++++++++++------- .../combinat/root_system/weyl_characters.py | 6 ++ 2 files changed, 64 insertions(+), 33 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index e304c464436..3b5c4de48e4 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -21,6 +21,7 @@ from sage.misc.misc import inject_variable from sage.rings.all import ZZ from sage.rings.number_field.number_field import CyclotomicField +from sage.misc.cachefunc import cached_method class FusionRing(WeylCharacterRing): r""" @@ -99,6 +100,41 @@ class FusionRing(WeylCharacterRing): sage: [B22(x) for x in B22.get_order()] [B22(0,0), B22(1,0), B22(0,1), B22(2,0), B22(1,1), B22(0,2)] + The fusion ring has a number of methods that reflect its role + as the Grothendieck ring of a modular tensor category. These + include a twist method :meth:twist for its elements related + to the ribbon element, and the S-matrix :meth:`s_ij`. + + Let us check the Verlinde formula. This famous identity states + that + + .. MATH:: + + N_{ijk} = \sum_l \frac{S(i,\ell)\,S(j,\ell)\,S(k,\ell)}{S(I,\ell)} + + where ``I`` is the unit object. We may define a function that corresponds + to the right-hand side:: + + sage: def V(i,j,k): + ....: R = i.parent() + ....: return sum(R.s_ij(i,l)*R.s_ij(j,l)*R.s_ij(k,l)/R.s_ij(R.one(),l) for l in R.basis()) + + This does not produce ``self.N_ijk(i,j,k)`` exactly, because our S-matrix + is omitting a normalization factor. The following code to check the + Verlinde formula takes this into account:: + + sage: def test_verlinde(R): + ....: b0 = R.one() + ....: c = V(b0, b0, b0) + ....: return all(V(i,j,k)==c*R.N_ijk(i,j,k) for i in R.basis() for j in R.basis() for k in R.basis()) + + Every fusion ring should pass this test:: + + sage: test_verlinde(FusionRing("A2",1)) + True + sage: test_verlinde(FusionRing("B4",2)) + True + """ @staticmethod def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjugate=False): @@ -128,39 +164,23 @@ def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjug return super(FusionRing, cls).__classcall__(cls, ct, base_ring=base_ring, prefix=prefix, style=style, k=k, conjugate=conjugate) - @lazy_attribute - def _q_field(self): + def field(self): """ - The cyclotomic field of `4\ell`-th roots of unity, where - `\ell` is computed by :meth:`fusion_l`. Call this - lazy attribute via the method :meth:`q_field()`. + This returns a cyclotomic field large enough to + contain the `2l`-th roots of unity, as well as + all the S-matrix entries. EXAMPLES:: - sage: B22=FusionRing("B2",2) - sage: B22.q_field() + sage: FusionRing("A2",2).field() + Cyclotomic Field of order 60 and degree 16 + sage: FusionRing("B2",2).field() Cyclotomic Field of order 40 and degree 16 """ - self._K = CyclotomicField(4*self._l) - return self._K - - def q_field(self): - """ - Return the cyclotomic field of `4\ell`-th roots of unity, where - `\ell` is computed by :meth:`fusion_l`. - - This field contains the twists, categorical dimensions, and the entries of the - S-matrix. - - EXAMPLES:: - - sage: A11=FusionRing('A1',1) - sage: A11.q_field() - Cyclotomic Field of order 12 and degree 4 - """ - return self._q_field + return CyclotomicField(4*self._fg*self._l) + def get_order(self): """ This returns the weights of the basis vectors in a fixed order. @@ -217,7 +237,8 @@ def fusion_l(self): and `h^\vee` denotes the dual Coxeter number of the underlying Lie algebra. - This value is used to define the associated root of unity `q=e^{i\\pi/\ell}`. + This value is used to define the associated root `2\ell`-th + of unity `q=e^{i\pi/\ell}`. EXAMPLES:: @@ -264,6 +285,7 @@ def q_dims(self): b = self.basis() return [b[x].q_dimension() for x in self.get_order()] + @cached_method def N_ijk(self, elt_i, elt_j, elt_k): """ INPUT: @@ -292,6 +314,7 @@ def N_ijk(self, elt_i, elt_j, elt_k): """ return (elt_i*elt_j).monomial_coefficients().get(elt_k.dual().weight(),0) + @cached_method def Nk_ij(self, elt_i, elt_j, elt_k): """ Returns the fusion coefficient `N^k_{ij}`. These are @@ -313,6 +336,7 @@ def Nk_ij(self, elt_i, elt_j, elt_k): """ return (elt_i*elt_j).monomial_coefficients().get(elt_k.weight(),0) + @cached_method def s_ij(self, elt_i, elt_j): """ Return the element of the S-matrix of this FusionRing corresponding to @@ -337,17 +361,17 @@ def s_ij(self, elt_i, elt_j): [1, -zeta60^14 + zeta60^6 + zeta60^4, -zeta60^14 + zeta60^6 + zeta60^4, -1] """ - l = self.fusion_l() - K = self.q_field() + l = self.fusion_l()*self._fg + K = self.field() q = K.gen() ijtwist = -2*l*(elt_i.twist() + elt_j.twist()) mc = (elt_i.dual()*elt_j).monomial_coefficients() return sum(self(k).q_dimension()*self.Nk_ij(elt_i,self(k),elt_j)*q**(2*l*self(k).twist()+ijtwist) for k in mc) - # return sum(self(k).q_dimension()*q**(2*l*self(k).twist()+ijtwist) for k in (elt_i.dual()*elt_j).monomial_coefficients()) def s_matrix(self): r""" - Return the S-matrix of this FusionRing. + Return the S-matrix of this FusionRing. This is the matrix denoted + `\widetilde{s}` in [BK]_. It is not normalized to be unitary. EXAMPLES:: @@ -491,8 +515,9 @@ def twist(self): twist += 2 return twist + @cached_method def q_dimension(self): - r"""" + r""" This returns the quantum dimension as an element of the cyclotomic field of the `2l`-th roots of unity, where `l = m(k+h^\vee)` with `m=1,2,3` depending on whether type is simply, doubly or @@ -505,7 +530,7 @@ def q_dimension(self): [1, 4, 5, 1, 5, 4] """ if not self.is_simple_object(): - raise ValueError("quantum twist is only available for simple objects of a FusionRing") + raise ValueError("quantum dimension is only available for simple objects of a FusionRing") lam = self.weight() space = self.parent().space() rho = space.rho() @@ -516,5 +541,5 @@ def q_dimension(self): q = pr.gen()**2 expr = pr(expr) expr = expr.substitute(q=q**2)/q**(expr.degree()) - zet = self.parent().q_field().gen() + zet = self.parent().field().gen()**(self.parent()._fg) return expr.substitute(q=zet) diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index 674e00d3575..6b49f8b932c 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -176,6 +176,12 @@ def next_level(wt): h_check = ct.dual_coxeter_number() self._l = self._m_g * (self._k + h_check) self._conj = (-1)**conjugate + if ct[0] == 'A': + self._fg = ct[1]+1 + elif ct[0] == 'E' and ct[1] == 6: + self._fg = 3 + else: + self._fg = 1 @cached_method def ambient(self): From a0650b220912d0c7acc7b9165596f4d33e663dc1 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Thu, 25 Jun 2020 16:29:30 -0700 Subject: [PATCH 19/34] total_quantum_order --- src/sage/combinat/root_system/fusion_ring.py | 37 ++++++++++++++++---- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 3b5c4de48e4..98b8bb5e467 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -105,27 +105,38 @@ class FusionRing(WeylCharacterRing): include a twist method :meth:twist for its elements related to the ribbon element, and the S-matrix :meth:`s_ij`. + There are two natural normalizations of the S-matrix. Both + are explained in Chapter 3 of [BK]_. The one that is computed + by the method :meth:`s_matrix`, or whose individual entries + are computed by :meth:`s_ij` is denoted `\\tilde{s}` in + [BK]_. It is not unitary. To make it unitary, one would + divide by the square root of `D=\\sum d_i^2` where the sum is over all simple + objects and `d_i` is the quantum dimension, computed by the + method :meth:`q_dimension`. The quantity `D` is computed by + :meth:`q_dimension`. + Let us check the Verlinde formula. This famous identity states that .. MATH:: - N_{ijk} = \sum_l \frac{S(i,\ell)\,S(j,\ell)\,S(k,\ell)}{S(I,\ell)} + N_{ijk} = \sum_l \frac{s(i,\ell)\,s(j,\ell)\,s(k,\ell)}{s(I,\ell)} - where ``I`` is the unit object. We may define a function that corresponds - to the right-hand side:: + where ``I`` is the unit object. In this formula `s` is the normalized + unitary S-matrix denoted `s` in [BK]_. We may define a function that corresponds + to the right-hand side, except using `\\tilde{s}` instead of `s`. sage: def V(i,j,k): ....: R = i.parent() ....: return sum(R.s_ij(i,l)*R.s_ij(j,l)*R.s_ij(k,l)/R.s_ij(R.one(),l) for l in R.basis()) - This does not produce ``self.N_ijk(i,j,k)`` exactly, because our S-matrix - is omitting a normalization factor. The following code to check the + This does not produce ``self.N_ijk(i,j,k)`` exactly, because of the + missing normalization factor. The following code to check the Verlinde formula takes this into account:: sage: def test_verlinde(R): ....: b0 = R.one() - ....: c = V(b0, b0, b0) + ....: c = R.total_quantum_order() ....: return all(V(i,j,k)==c*R.N_ijk(i,j,k) for i in R.basis() for j in R.basis() for k in R.basis()) Every fusion ring should pass this test:: @@ -443,6 +454,19 @@ def fusion_labels(self, labels=None): inject_variable(labels[j], self(b)) self._fusion_labels = d + def total_quantum_order(self): + """ + Return `\sum d_i^2`, where the sum is over all simple objects + and `d_i` is the quantum dimension. + + EXAMPLES:: + + sage: FusionRing("E6",1).total_quantum_order() + 3 + + """ + return sum(x.q_dimension()**2 for x in self.basis()) + class Element(WeylCharacterRing.Element): """ A class for FusionRing elements. @@ -543,3 +567,4 @@ def q_dimension(self): expr = expr.substitute(q=q**2)/q**(expr.degree()) zet = self.parent().field().gen()**(self.parent()._fg) return expr.substitute(q=zet) + From 308a0938c292d25d3fce57e9dc517c7dffe3b38e Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Thu, 25 Jun 2020 16:47:40 -0700 Subject: [PATCH 20/34] minor doc revision --- src/sage/combinat/root_system/fusion_ring.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 98b8bb5e467..02de7eb2af1 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -102,15 +102,15 @@ class FusionRing(WeylCharacterRing): The fusion ring has a number of methods that reflect its role as the Grothendieck ring of a modular tensor category. These - include a twist method :meth:twist for its elements related - to the ribbon element, and the S-matrix :meth:`s_ij`. + include a twist method :meth:`twist` for its elements related + to the ribbon structure, and the S-matrix :meth:`s_ij`. There are two natural normalizations of the S-matrix. Both are explained in Chapter 3 of [BK]_. The one that is computed by the method :meth:`s_matrix`, or whose individual entries - are computed by :meth:`s_ij` is denoted `\\tilde{s}` in + are computed by :meth:`s_ij` is denoted `\tilde{s}` in [BK]_. It is not unitary. To make it unitary, one would - divide by the square root of `D=\\sum d_i^2` where the sum is over all simple + divide by the square root of `D=\sum d_i^2` where the sum is over all simple objects and `d_i` is the quantum dimension, computed by the method :meth:`q_dimension`. The quantity `D` is computed by :meth:`q_dimension`. @@ -124,7 +124,7 @@ class FusionRing(WeylCharacterRing): where ``I`` is the unit object. In this formula `s` is the normalized unitary S-matrix denoted `s` in [BK]_. We may define a function that corresponds - to the right-hand side, except using `\\tilde{s}` instead of `s`. + to the right-hand side, except using `\tilde{s}` instead of `s`:: sage: def V(i,j,k): ....: R = i.parent() @@ -178,7 +178,7 @@ def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjug def field(self): """ This returns a cyclotomic field large enough to - contain the `2l`-th roots of unity, as well as + contain the `2\ell`-th roots of unity, as well as all the S-matrix entries. EXAMPLES:: From 487966cda5bd5777247959103046d57ab417f0da Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 26 Jun 2020 13:02:13 +1000 Subject: [PATCH 21/34] Reviewer changes to fusion ring enhancements. --- src/doc/en/reference/references/index.rst | 14 +- src/sage/combinat/root_system/fusion_ring.py | 314 ++++++++++-------- .../combinat/root_system/weyl_characters.py | 10 +- 3 files changed, 193 insertions(+), 145 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 4207e521835..c70b096a4cc 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -344,8 +344,8 @@ REFERENCES: **B** -.. [BK] Bakalov and Kirillov, Lectures on tensor categories and modular functors, - AMS (2001). +.. [BK2001] Bakalov and Kirillov, *Lectures on tensor categories and modular functors*, + AMS (2001). .. [Ba1994] Kaushik Basu. *The Traveler's Dilemma: Paradoxes of Rationality in Game Theory*. The American Economic Review @@ -1956,8 +1956,8 @@ REFERENCES: 1981, Pages 108-125, ISSN 0097-3165,:doi:`10.1016/0097-3165(81)90007-8`. (http://www.sciencedirect.com/science/article/pii/0097316581900078) -.. [EGNO] Pavel Etingof, Shlomo Gelaki, Dmitri Nikshych and Victor Ostrik, - "Tensor Categories", AMS Mathematical Surveys and Monographs 205 (2015). +.. [EGNO20015] Pavel Etingof, Shlomo Gelaki, Dmitri Nikshych and Victor Ostrik, + *Tensor Categories*, AMS Mathematical Surveys and Monographs 205 (2015). .. [EGHLSVY] Pavel Etingof, Oleg Golberg, Sebastian Hensel, Tiankai Liu, Alex Schwendner, Dmitry Vaintrob, Elena Yudovina, @@ -4552,9 +4552,9 @@ REFERENCES: .. [Rot2006] Ron Roth, Introduction to Coding Theory, Cambridge University Press, 2006 -.. [Row2006] Eric Rowell, From quantum groups to unitary modular tensor categories. - In Representations of algebraic groups, quantum groups, and Lie algebras, - Contemp. Math., 413, Amer. Math. Soc., Providence, RI, 2006. +.. [Row2006] Eric Rowell, *From quantum groups to unitary modular tensor categories*. + In Representations of algebraic groups, quantum groups, and Lie algebras, + Contemp. Math., **413**, Amer. Math. Soc., Providence, RI, 2006. :arXiv:`math/0503226`. .. [RR1997] Arun Ram and Jeffrey Remmel. *Applications of the Frobenius diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 02de7eb2af1..ee54f976df3 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -10,16 +10,12 @@ # https://www.gnu.org/licenses/ # **************************************************************************** - from sage.combinat.root_system.weyl_characters import WeylCharacterRing -from functools import reduce -from operator import mul from sage.combinat.q_analogues import q_int from sage.matrix.special import diagonal_matrix from sage.matrix.constructor import matrix -from sage.misc.lazy_attribute import lazy_attribute from sage.misc.misc import inject_variable -from sage.rings.all import ZZ +from sage.rings.integer_ring import ZZ from sage.rings.number_field.number_field import CyclotomicField from sage.misc.cachefunc import cached_method @@ -40,9 +36,9 @@ class FusionRing(WeylCharacterRing): REFERENCES: - - [BK]_ Chapter 3 + - [BK2001]_ Chapter 3 - [DFMS1996]_ Chapter 16 - - [EGNO]_ Chapter 8 + - [EGNO2015]_ Chapter 8 - [Feingold2004]_ - [Fuchs1994]_ - [Row2006]_ @@ -63,14 +59,15 @@ class FusionRing(WeylCharacterRing): You may assign your own labels to the basis elements. In the next example, we create the `SO(5)` fusion ring of level `2`, check the - weights of the basis elements, then assign new labels to them:: + weights of the basis elements, then assign new labels to them while + injecting them into the global namespace:: sage: B22 = FusionRing("B2", 2) sage: b = [B22(x) for x in B22.get_order()]; b [B22(0,0), B22(1,0), B22(0,1), B22(2,0), B22(1,1), B22(0,2)] sage: [x.weight() for x in b] [(0, 0), (1, 0), (1/2, 1/2), (2, 0), (3/2, 1/2), (1, 1)] - sage: B22.fusion_labels(['I0','Y1','X','Z','Xp','Y2']) + sage: B22.fusion_labels(['I0','Y1','X','Z','Xp','Y2'], inject_variables=True) sage: b = [B22(x) for x in B22.get_order()]; b [I0, Y1, X, Z, Xp, Y2] sage: [(x, x.weight()) for x in b] @@ -80,9 +77,9 @@ class FusionRing(WeylCharacterRing): (Z, (2, 0)), (Xp, (3/2, 1/2)), (Y2, (1, 1))] - sage: X*Y1 + sage: X * Y1 X + Xp - sage: Z*Z + sage: Z * Z I0 A fixed order of the basis keys is avalailable with :meth:`get_order`. @@ -93,11 +90,17 @@ class FusionRing(WeylCharacterRing): sage: [B22(x) for x in B22.get_order()] [I0, Y1, Y2, X, Xp, Z] - To reset the labels and the order to their defaults, - you may run :meth:`fusion_labels` with no parameter:: + To reset the labels, you may run :meth:`fusion_labels` with no parameter:: sage: B22.fusion_labels() sage: [B22(x) for x in B22.get_order()] + [B22(0,0), B22(1,0), B22(0,2), B22(0,1), B22(1,1), B22(2,0)] + + To reset the order to the default, simply set it to the list of basis + element keys:: + + sage: B22.set_order(B22.basis().keys().list()) + sage: [B22(x) for x in B22.get_order()] [B22(0,0), B22(1,0), B22(0,1), B22(2,0), B22(1,1), B22(0,2)] The fusion ring has a number of methods that reflect its role @@ -106,29 +109,30 @@ class FusionRing(WeylCharacterRing): to the ribbon structure, and the S-matrix :meth:`s_ij`. There are two natural normalizations of the S-matrix. Both - are explained in Chapter 3 of [BK]_. The one that is computed + are explained in Chapter 3 of [BK2001]_. The one that is computed by the method :meth:`s_matrix`, or whose individual entries are computed by :meth:`s_ij` is denoted `\tilde{s}` in - [BK]_. It is not unitary. To make it unitary, one would - divide by the square root of `D=\sum d_i^2` where the sum is over all simple - objects and `d_i` is the quantum dimension, computed by the - method :meth:`q_dimension`. The quantity `D` is computed by - :meth:`q_dimension`. + [BK2001]_. It is not unitary. To make it unitary, one would + divide by the square root of `D = \sum_V d_i(V)^2` where the sum + is over all simple objects `V` and `d_i(V)` is the quantum dimension + of `V` computed by the method :meth:`q_dimension`. The quantity `D` + is computed by :meth:`q_dimension`. - Let us check the Verlinde formula. This famous identity states - that + Let us check the Verlinde formula. This famous identity states that .. MATH:: - N_{ijk} = \sum_l \frac{s(i,\ell)\,s(j,\ell)\,s(k,\ell)}{s(I,\ell)} + N_{ijk} = \sum_l \frac{s(i,\ell)\,s(j,\ell)\,s(k,\ell)}{s(I,\ell)}, where ``I`` is the unit object. In this formula `s` is the normalized - unitary S-matrix denoted `s` in [BK]_. We may define a function that corresponds - to the right-hand side, except using `\tilde{s}` instead of `s`:: + unitary S-matrix denoted `s` in [BK2001]_. We may define a function that + corresponds to the right-hand side, except using `\tilde{s}` instead + of `s`:: sage: def V(i,j,k): ....: R = i.parent() - ....: return sum(R.s_ij(i,l)*R.s_ij(j,l)*R.s_ij(k,l)/R.s_ij(R.one(),l) for l in R.basis()) + ....: return sum(R.s_ij(i,l) * R.s_ij(j,l) * R.s_ij(k,l) / R.s_ij(R.one(),l) + ....: for l in R.basis()) This does not produce ``self.N_ijk(i,j,k)`` exactly, because of the missing normalization factor. The following code to check the @@ -137,7 +141,8 @@ class FusionRing(WeylCharacterRing): sage: def test_verlinde(R): ....: b0 = R.one() ....: c = R.total_quantum_order() - ....: return all(V(i,j,k)==c*R.N_ijk(i,j,k) for i in R.basis() for j in R.basis() for k in R.basis()) + ....: return all(V(i,j,k) == c * R.N_ijk(i,j,k) for i in R.basis() + ....: for j in R.basis() for k in R.basis()) Every fusion ring should pass this test:: @@ -145,7 +150,6 @@ class FusionRing(WeylCharacterRing): True sage: test_verlinde(FusionRing("B4",2)) True - """ @staticmethod def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjugate=False): @@ -176,8 +180,8 @@ def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjug prefix=prefix, style=style, k=k, conjugate=conjugate) def field(self): - """ - This returns a cyclotomic field large enough to + r""" + Return a cyclotomic field large enough to contain the `2\ell`-th roots of unity, as well as all the S-matrix entries. @@ -187,13 +191,11 @@ def field(self): Cyclotomic Field of order 60 and degree 16 sage: FusionRing("B2",2).field() Cyclotomic Field of order 40 and degree 16 - """ + return CyclotomicField(4 * self._fg * self._l) - return CyclotomicField(4*self._fg*self._l) - def get_order(self): - """ + r""" This returns the weights of the basis vectors in a fixed order. You may change the order of the basis using :meth:`set_order` @@ -206,10 +208,12 @@ def get_order(self): sage: [A14(x) for x in A14.get_order()] [A14(0), A14(4), A14(1), A14(3), A14(2)] - This duplicates :meth:`get_order` from :mod:`combinat.free_module`. - However unlike the :mod:`combinat.free_module` method with the same - name this :meth:`get_order` is not cached. Caching of :meth:`get_order` causes - inconsistent results after calling :meth:`set_order`. + .. WARNING:: + + This duplicates :meth:`get_order` from + :class:`CombinatorialFreeModule` except the result + is *not* cached. Caching of :meth:`get_order` causes + inconsistent results after calling :meth:`set_order`. """ if self._order is None: self.set_order(self.basis().keys().list()) @@ -230,11 +234,11 @@ def some_elements(self): def fusion_k(self): r""" - Return the level of the FusionRing. + Return the level of ``self``. EXAMPLES:: - sage: B22=FusionRing('B2',2) + sage: B22 = FusionRing('B2',2) sage: B22.fusion_k() 2 """ @@ -242,21 +246,21 @@ def fusion_k(self): def fusion_l(self): r""" - This Returns the product `m_g(k + h^\vee)`, where `m_g` denotes the - square of the ratio of the lengths of long to short roots of + Return the product `m_g(k + h^\vee)`, where `m_g` denotes the + square of the ratio of the lengths of long to short roots of the underlying Lie algebra, `k` denotes the level of the FusionRing, and `h^\vee` denotes the dual Coxeter number of the underlying Lie algebra. This value is used to define the associated root `2\ell`-th - of unity `q=e^{i\pi/\ell}`. + of unity `q = e^{i\pi/\ell}`. EXAMPLES:: - sage: B22=FusionRing('B2',2) + sage: B22 = FusionRing('B2',2) sage: B22.fusion_l() 10 - sage: D52=FusionRing('D5',2) + sage: D52 = FusionRing('D5',2) sage: D52.fusion_l() 10 """ @@ -269,7 +273,7 @@ def twists_matrix(self): EXAMPLES:: - sage: B22=FusionRing('B2',2) + sage: B22 = FusionRing('B2',2) sage: B22.twists_matrix() [ 0 0 0 0 0 0] [ 0 4/5 0 0 0 0] @@ -278,7 +282,8 @@ def twists_matrix(self): [ 0 0 0 0 3/2 0] [ 0 0 0 0 0 6/5] """ - return diagonal_matrix(self.basis()[x].twist() for x in self.get_order()) + B = self.basis() + return diagonal_matrix(B[x].twist() for x in self.get_order()) def q_dims(self): r""" @@ -286,10 +291,10 @@ def q_dims(self): EXAMPLES:: - sage: F41=FusionRing('F4',1,conjugate=True) + sage: F41 = FusionRing('F4',1,conjugate=True) sage: F41.q_dims() [1, -zeta80^24 + zeta80^16 + 1] - sage: B22=FusionRing("B2",2) + sage: B22 = FusionRing("B2",2) sage: B22.q_dims() [1, 2, -2*zeta40^12 + 2*zeta40^8 + 1, 1, -2*zeta40^12 + 2*zeta40^8 + 1, 2] """ @@ -298,102 +303,106 @@ def q_dims(self): @cached_method def N_ijk(self, elt_i, elt_j, elt_k): - """ + r""" + Return the symmetric fusion coefficient `N_{ijk}`. + INPUT: - ``elt_i``, ``elt_j``, ``elt_k`` -- elements of the fusion basis - Returns the symmetric fusion coefficient `N_{ijk}`. This - is the same as `N_{ij}^{k\\ast}`, where $N_{ij}^k$ are the - structure coefficients of the ring (see :meth:`Nk_ij`), - and `k\\ast`` denotes the dual element. The coefficient `N_{ijk}` + This is the same as `N_{ij}^{k\ast}`, where `N_{ij}^k` are + the structure coefficients of the ring (see :meth:`Nk_ij`), + and `k\ast`` denotes the dual element. The coefficient `N_{ijk}` is unchanged under permutations of the three basis vectors. EXAMPLES:: - sage: G23=FusionRing("G2",3) + sage: G23 = FusionRing("G2", 3) sage: G23.fusion_labels("g") sage: b = G23.basis().list(); b [g0, g1, g2, g3, g4, g5] - sage: [(x,y,z) for x in b for y in b for z in b if G23.N_ijk(x,y,z)>1] + sage: [(x,y,z) for x in b for y in b for z in b if G23.N_ijk(x,y,z) > 1] [(g3, g3, g3), (g3, g3, g4), (g3, g4, g3), (g4, g3, g3)] - sage: all([G23.N_ijk(x,y,z)==G23.N_ijk(y,z,x) for x in b for y in b for z in b]) + sage: all(G23.N_ijk(x,y,z)==G23.N_ijk(y,z,x) for x in b for y in b for z in b) True - sage: all([G23.N_ijk(x,y,z)==G23.N_ijk(y,x,z) for x in b for y in b for z in b]) + sage: all(G23.N_ijk(x,y,z)==G23.N_ijk(y,x,z) for x in b for y in b for z in b) True - """ - return (elt_i*elt_j).monomial_coefficients().get(elt_k.dual().weight(),0) + return (elt_i * elt_j).monomial_coefficients().get(elt_k.dual().weight(), 0) @cached_method def Nk_ij(self, elt_i, elt_j, elt_k): """ - Returns the fusion coefficient `N^k_{ij}`. These are - the structure coefficients of the fusion ring, so - - .. MATH:: + Return the fusion coefficient `N^k_{ij}`. - i*j = \sum_{k} N_{ij}^k k + These are the structure coefficients of the fusion ring, so - as the example shows. + .. MATH:: + + i * j = \sum_{k} N_{ij}^k k. EXAMPLES:: - sage: A22=FusionRing("A2",2) + sage: A22 = FusionRing("A2", 2) sage: b = A22.basis().list() sage: all(x*y == sum(A22.Nk_ij(x,y,k)*k for k in b) for x in b for y in b) True - """ - return (elt_i*elt_j).monomial_coefficients().get(elt_k.weight(),0) + return (elt_i * elt_j).monomial_coefficients(copy=False).get(elt_k.weight(), 0) @cached_method def s_ij(self, elt_i, elt_j): - """ - Return the element of the S-matrix of this FusionRing corresponding to + r""" + Return the element of the S-matrix of this FusionRing corresponding to the given elements. This is computed using the formula .. MATH:: - s_{i,j} = \\frac{1}{\\theta_i\\theta_j}\\sum_k N_{ik}^j d_k\\theta_k + s_{i,j} = \frac{1}{\theta_i\theta_j} \sum_k N_{ik}^j d_k \theta_k, - where `\\theta_k` is the twist and `d_k` is the quantum - dimension. See [Row2006]_ equation (2.2) or [EGNO]_ Proposition 8.13.8. + where `\theta_k` is the twist and `d_k` is the quantum + dimension. See [Row2006]_ Equation (2.2) or [EGNO2015]_ + Proposition 8.13.8. INPUT: - ``elt_i``, ``elt_j`` -- elements of the fusion basis - EXAMPLES:: + EXAMPLES:: - sage: G21=FusionRing("G2",1) - sage: b=G21.basis() - sage: [G21.s_ij(x,y) for x in b for y in b] + sage: G21 = FusionRing("G2", 1) + sage: b = G21.basis() + sage: [G21.s_ij(x, y) for x in b for y in b] [1, -zeta60^14 + zeta60^6 + zeta60^4, -zeta60^14 + zeta60^6 + zeta60^4, -1] - """ - l = self.fusion_l()*self._fg + l = self.fusion_l() * self._fg K = self.field() q = K.gen() - ijtwist = -2*l*(elt_i.twist() + elt_j.twist()) - mc = (elt_i.dual()*elt_j).monomial_coefficients() - return sum(self(k).q_dimension()*self.Nk_ij(elt_i,self(k),elt_j)*q**(2*l*self(k).twist()+ijtwist) for k in mc) + ijtwist = -2 * l * (elt_i.twist() + elt_j.twist()) + mc = (elt_i.dual() * elt_j).monomial_coefficients(copy=False) + B = self.basis() + return sum(B[k].q_dimension() * self.Nk_ij(elt_i, B[k], elt_j) * q**(2*l*B[k].twist() + ijtwist) + for k in mc) def s_matrix(self): r""" - Return the S-matrix of this FusionRing. This is the matrix denoted - `\widetilde{s}` in [BK]_. It is not normalized to be unitary. + Return the S-matrix of this FusionRing. + + .. NOTE:: - EXAMPLES:: + This is the matrix denoted `\widetilde{s}` in [BK2001]_. + It is not normalized to be unitary. + + EXAMPLES:: - sage: D91=FusionRing('D9',1) + sage: D91 = FusionRing('D9', 1) sage: D91.s_matrix() [ 1 1 1 1] [ 1 1 -1 -1] [ 1 -1 -zeta68^17 zeta68^17] [ 1 -1 zeta68^17 -zeta68^17] - sage: D41=FusionRing('D4',1) + sage: D41 = FusionRing('D4', 1) sage: D41.s_matrix() [ 1 1 1 1] [ 1 1 -1 -1] @@ -401,19 +410,22 @@ def s_matrix(self): [ 1 -1 -1 1] """ b = self.basis() - return matrix([[self.s_ij(b[x],b[y]) for x in self.get_order()] for y in self.get_order()]) + return matrix([[self.s_ij(b[x], b[y]) for x in self.get_order()] for y in self.get_order()]) - def fusion_labels(self, labels=None): - """ + def fusion_labels(self, labels=None, inject_variables=False): + r""" Set the labels of the basis. INPUT: - ``labels`` -- (default: ``None``) a list of strings or string + - ``inject_variables`` -- (default: ``False``) if ``True``, then + inject the variable names into the global namespace; note that + this could override objects already defined If ``labels`` is a list, the length of the list must equal the number of basis elements. These become the names of - the basis elements. + the basis elements. If ``labels`` is a string, this is treated as a prefix and a list of names is generated. @@ -432,26 +444,41 @@ def fusion_labels(self, labels=None): [ x2 x1 + x3 x0 + x2 x1] [ x3 x2 x1 x0] + We give an example where the variables are injected into the + global namepsace:: + + sage: A13.fusion_labels("y", inject_variables=True) + sage: y0 + y0 + sage: y0.parent() is A13 + True + We reset the labels to the default:: sage: A13.fusion_labels() sage: fb [A13(0), A13(1), A13(2), A13(3)] + sage: y0 + A13(0) """ if labels is None: - self._order = None + # Remove the fusion labels self._fusion_labels = None return - elif type(labels) == str: - labels = [labels+"%d"%k for k in range(len(self.basis()))] - elif len(labels) != len(self.basis()): + + B = self.basis() + if isinstance(labels, str): + labels = [labels + str(k) for k in range(len(B))] + elif len(labels) != len(B): raise ValueError('invalid data') + d = {} - fb = list(self.get_order()) - for j, b in enumerate(fb): - t = tuple([b.inner_product(x) for x in self.simple_coroots()]) + ac = self.simple_coroots() + for j, b in enumerate(self.get_order()): + t = tuple([b.inner_product(x) for x in ac]) d[t] = labels[j] - inject_variable(labels[j], self(b)) + if inject_variables: + inject_variable(labels[j], B[b]) self._fusion_labels = d def total_quantum_order(self): @@ -463,7 +490,6 @@ def total_quantum_order(self): sage: FusionRing("E6",1).total_quantum_order() 3 - """ return sum(x.q_dimension()**2 for x in self.basis()) @@ -472,12 +498,12 @@ class Element(WeylCharacterRing.Element): A class for FusionRing elements. """ def is_simple_object(self): - """ - Determine whether element is a simple object of the FusionRing. + r""" + Determine whether ``self`` is a simple object of the fusion ring. EXAMPLES:: - sage: A22=FusionRing("A2",2) + sage: A22 = FusionRing("A2", 2) sage: x = A22(1,0); x A22(1,0) sage: x.is_simple_object() @@ -487,12 +513,13 @@ def is_simple_object(self): sage: (x^2).is_simple_object() False """ - return self.parent()._k is not None and len(self.monomial_coefficients())==1 + return self.parent()._k is not None and len(self._monomial_coefficients) == 1 def weight(self): - """ - This method is only available for basis elements. Returns the - parametrizing dominant weight in the level `k` alcove. + r""" + Return the parametrizing dominant weight in the level `k` alcove. + + This method is only available for basis elements. EXAMPLES:: @@ -500,27 +527,29 @@ def weight(self): sage: sorted([x.weight() for x in A21.basis()]) [(0, 0, 0), (1/3, 1/3, -2/3), (2/3, -1/3, -1/3)] """ - if len(self.monomial_coefficients()) != 1: + if len(self._monomial_coefficients) != 1: raise ValueError("fusion weight is valid for basis elements only") - return self.leading_support() + return next(iter(self._monomial_coefficients)) def twist(self): r""" - Compute the object's twist. This returns a rational number `h` such that - `\theta = e^{i \pi h}` is the twist of ``self``. This method is - only available for simple objects. + Compute the object's twist. This returns a rational number `h` + such that `\theta = e^{i \pi h}` is the twist of ``self``. + + This method is only available for simple objects. - We compute the twists following p.7 of [Row2006]_, noting that the bilinear form - is normalized so that `\langle\alpha, \alpha\rangle = 2` for short roots. + We compute the twists following p.7 of [Row2006]_, noting that + the bilinear form is normalized so that + `\langle \alpha, \alpha \rangle = 2` for short roots. EXAMPLES:: - sage: G21=FusionRing('G2',1) + sage: G21 = FusionRing('G2', 1) sage: G21.basis() Finite family {(0, 0, 0): G21(0,0), (1, 0, -1): G21(1,0)} sage: G21(1,0).twist() 4/5 - sage: F41=FusionRing('F4',1,conjugate=True) + sage: F41 = FusionRing('F4', 1, conjugate=True) sage: F41.basis() Finite family {(0, 0, 0, 0): F41(0,0,0,0), (1, 0, 0, 0): F41(0,0,0,1)} sage: F41(0,0,0,1).twist() @@ -528,11 +557,12 @@ def twist(self): """ if not self.is_simple_object(): raise ValueError("quantum twist is only available for simple objects of a FusionRing") - rho = sum(self.parent().positive_roots())/2 + P = self.parent() + rho = P.space().rho() lam = self.weight() - inner = lam.inner_product(lam+2*rho) - twist = self.parent()._conj*self.parent()._nf*inner/self.parent().fusion_l() - #Reduce to canonical form + inner = lam.inner_product(lam + 2*rho) + twist = P._conj * P._nf * inner / P.fusion_l() + # Reduce to canonical form while twist > 2: twist -= 2 while twist < 0: @@ -542,29 +572,47 @@ def twist(self): @cached_method def q_dimension(self): r""" - This returns the quantum dimension as an element of the cyclotomic - field of the `2l`-th roots of unity, where `l = m(k+h^\vee)` + Return the quantum dimension as an element of the cyclotomic + field of the `2l`-th roots of unity, where `l = m (k+h^\vee)` with `m=1,2,3` depending on whether type is simply, doubly or - triply laced, `k` is the level and `h^\vee` is the dual Coxeter number. + triply laced, `k` is the level and `h^\vee` is the dual + Coxeter number. EXAMPLES:: - sage: B22=FusionRing("B2",2) + sage: B22 = FusionRing("B2",2) sage: [(b.q_dimension())^2 for b in B22.basis()] [1, 4, 5, 1, 5, 4] """ if not self.is_simple_object(): raise ValueError("quantum dimension is only available for simple objects of a FusionRing") + P = self.parent() lam = self.weight() - space = self.parent().space() + space = P.space() rho = space.rho() - num = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(lam+rho)) for alpha in space.positive_roots()], 1) - den = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(rho)) for alpha in space.positive_roots()], 1) - expr = num/den - pr = expr.parent().ring() - q = pr.gen()**2 - expr = pr(expr) - expr = expr.substitute(q=q**2)/q**(expr.degree()) - zet = self.parent().field().gen()**(self.parent()._fg) + powers = {} + for alpha in space.positive_roots(): + val = alpha.inner_product(lam + rho) + if val in powers: + powers[val] += 1 + else: + powers[val] = 1 + val = alpha.inner_product(rho) + if val in powers: + powers[val] -= 1 + else: + powers[val] = -1 + R = ZZ['q'] + q = R.gen() + expr = R.fraction_field().one() + for val in powers: + exp = powers[val] + if exp > 0: + expr *= q_int(P._nf * val, q)**exp + elif exp < 0: + expr /= q_int(P._nf * val, q)**(-exp) + expr = R(expr) + expr = expr.substitute(q=q**4) / (q**(2*expr.degree())) + zet = P.field().gen() ** P._fg return expr.substitute(q=zet) diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index 6b49f8b932c..f145c003176 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -163,21 +163,21 @@ def next_level(wt): # Record properties of the FusionRing if k is not None: #TODO: implement conjugate functionality - if ct[0] in ['A','D','E']: + if ct[0] in ['A', 'D', 'E']: self._m_g = 1 elif ct[0] in ['B', 'C', 'F']: self._m_g = 2 - else: + else: self._m_g = 3 if ct[0] in ['B','F']: self._nf = 2 else: self._nf = 1 h_check = ct.dual_coxeter_number() - self._l = self._m_g * (self._k + h_check) - self._conj = (-1)**conjugate + self._l = self._m_g * (self._k + h_check) + self._conj = (-1) ** conjugate if ct[0] == 'A': - self._fg = ct[1]+1 + self._fg = ct[1] + 1 elif ct[0] == 'E' and ct[1] == 6: self._fg = 3 else: From 8c4cc0d2e7f9b0b6e26b9172817bee85ee542369 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Fri, 26 Jun 2020 09:25:53 -0700 Subject: [PATCH 22/34] [BK2001] => [BaKi2001] to avoid clash with Bruns and Koch --- src/doc/en/reference/references/index.rst | 4 ++-- src/sage/combinat/root_system/fusion_ring.py | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index c70b096a4cc..3d4597bc726 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -344,7 +344,7 @@ REFERENCES: **B** -.. [BK2001] Bakalov and Kirillov, *Lectures on tensor categories and modular functors*, +.. [BaKi2001] Bakalov and Kirillov, *Lectures on tensor categories and modular functors*, AMS (2001). .. [Ba1994] Kaushik Basu. *The Traveler's Dilemma: Paradoxes of @@ -1956,7 +1956,7 @@ REFERENCES: 1981, Pages 108-125, ISSN 0097-3165,:doi:`10.1016/0097-3165(81)90007-8`. (http://www.sciencedirect.com/science/article/pii/0097316581900078) -.. [EGNO20015] Pavel Etingof, Shlomo Gelaki, Dmitri Nikshych and Victor Ostrik, +.. [EGNO2015] Pavel Etingof, Shlomo Gelaki, Dmitri Nikshych and Victor Ostrik, *Tensor Categories*, AMS Mathematical Surveys and Monographs 205 (2015). .. [EGHLSVY] Pavel Etingof, Oleg Golberg, Sebastian Hensel, Tiankai diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index ee54f976df3..85910853d5e 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -36,7 +36,7 @@ class FusionRing(WeylCharacterRing): REFERENCES: - - [BK2001]_ Chapter 3 + - [BaKi2001]_ Chapter 3 - [DFMS1996]_ Chapter 16 - [EGNO2015]_ Chapter 8 - [Feingold2004]_ @@ -109,10 +109,10 @@ class FusionRing(WeylCharacterRing): to the ribbon structure, and the S-matrix :meth:`s_ij`. There are two natural normalizations of the S-matrix. Both - are explained in Chapter 3 of [BK2001]_. The one that is computed + are explained in Chapter 3 of [BaKi2001]_. The one that is computed by the method :meth:`s_matrix`, or whose individual entries are computed by :meth:`s_ij` is denoted `\tilde{s}` in - [BK2001]_. It is not unitary. To make it unitary, one would + [BaKi2001]_. It is not unitary. To make it unitary, one would divide by the square root of `D = \sum_V d_i(V)^2` where the sum is over all simple objects `V` and `d_i(V)` is the quantum dimension of `V` computed by the method :meth:`q_dimension`. The quantity `D` @@ -125,7 +125,7 @@ class FusionRing(WeylCharacterRing): N_{ijk} = \sum_l \frac{s(i,\ell)\,s(j,\ell)\,s(k,\ell)}{s(I,\ell)}, where ``I`` is the unit object. In this formula `s` is the normalized - unitary S-matrix denoted `s` in [BK2001]_. We may define a function that + unitary S-matrix denoted `s` in [BaKi2001]_. We may define a function that corresponds to the right-hand side, except using `\tilde{s}` instead of `s`:: @@ -390,7 +390,7 @@ def s_matrix(self): .. NOTE:: - This is the matrix denoted `\widetilde{s}` in [BK2001]_. + This is the matrix denoted `\widetilde{s}` in [BaKi2001]_. It is not normalized to be unitary. EXAMPLES:: From 5a486782132739fea437c71e6e3765cf6e628d68 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Fri, 26 Jun 2020 21:56:11 -0700 Subject: [PATCH 23/34] doc revision --- src/sage/combinat/root_system/fusion_ring.py | 40 +++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 85910853d5e..a0e1978a7bf 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -28,11 +28,12 @@ class FusionRing(WeylCharacterRing): - ``ct`` -- the Cartan type of a simple (finite-dimensional) Lie algebra - ``k`` -- a nonnegative integer - This algebra has a basis (sometimes called *primary fields*) - indexed by the weights of level `\leq k`. These arise as - the fusion algebras of WZW conformal field theories, or from - quantum groups at roots of unity. The :class:`FusionRing` class is - implemented as a variant of the :class:`WeylCharacterRing`. + This algebra has a basis (sometimes called *primary fields* but here + called *simple objects*) indexed by the weights of level `\leq k`. + These arise as the fusion algebras of WZW conformal field theories, + or as Grothendieck groups of tilting modules for quantum groups at + roots of unity. The :class:`FusionRing` class is implemented as a + variant of the :class:`WeylCharacterRing`. REFERENCES: @@ -82,9 +83,9 @@ class FusionRing(WeylCharacterRing): sage: Z * Z I0 - A fixed order of the basis keys is avalailable with :meth:`get_order`. + A fixed order of the basis keys is available with :meth:`get_order`. This is the order used by methods such as :meth:`s_matrix`. - You may use :meth:`set_order` to reorder the basis:: + You may use :meth:`CombinatorialFreeModule.set_order` to reorder the basis:: sage: B22.set_order([x.weight() for x in [I0,Y1,Y2,X,Xp,Z]]) sage: [B22(x) for x in B22.get_order()] @@ -105,7 +106,7 @@ class FusionRing(WeylCharacterRing): The fusion ring has a number of methods that reflect its role as the Grothendieck ring of a modular tensor category. These - include a twist method :meth:`twist` for its elements related + include a twist method :meth:`Element.twist` for its elements related to the ribbon structure, and the S-matrix :meth:`s_ij`. There are two natural normalizations of the S-matrix. Both @@ -115,8 +116,8 @@ class FusionRing(WeylCharacterRing): [BaKi2001]_. It is not unitary. To make it unitary, one would divide by the square root of `D = \sum_V d_i(V)^2` where the sum is over all simple objects `V` and `d_i(V)` is the quantum dimension - of `V` computed by the method :meth:`q_dimension`. The quantity `D` - is computed by :meth:`q_dimension`. + of `V` computed by the method :meth:`Element.q_dimension`. The quantity `D` + is computed by :meth:`total_quantum_order`. Let us check the Verlinde formula. This famous identity states that @@ -195,9 +196,8 @@ def field(self): return CyclotomicField(4 * self._fg * self._l) def get_order(self): - r""" - This returns the weights of the basis vectors in a fixed order. - You may change the order of the basis using :meth:`set_order` + r"""This returns the weights of the basis vectors in a fixed order. + You may change the order of the basis using :meth:`CombinatorialFreeModule.set_order` EXAMPLES:: @@ -212,8 +212,10 @@ def get_order(self): This duplicates :meth:`get_order` from :class:`CombinatorialFreeModule` except the result - is *not* cached. Caching of :meth:`get_order` causes - inconsistent results after calling :meth:`set_order`. + is *not* cached. Caching of + :meth:`CombinatorialFreeModule.get_order` causes inconsistent + results after calling :meth:`CombinatorialFreeModule.set_order`. + """ if self._order is None: self.set_order(self.basis().keys().list()) @@ -246,7 +248,7 @@ def fusion_k(self): def fusion_l(self): r""" - Return the product `m_g(k + h^\vee)`, where `m_g` denotes the + Return the product `{\ell}=m_g(k + h^\vee)`, where `m_g` denotes the square of the ratio of the lengths of long to short roots of the underlying Lie algebra, `k` denotes the level of the FusionRing, and `h^\vee` denotes the dual Coxeter number of the underlying Lie @@ -391,7 +393,9 @@ def s_matrix(self): .. NOTE:: This is the matrix denoted `\widetilde{s}` in [BaKi2001]_. - It is not normalized to be unitary. + It is not normalized to be unitary. To obtain a unitary + matrix, divide by `\sqrt{D}` where `D` is computed + by :meth:`total_quantum_order`. EXAMPLES:: @@ -573,7 +577,7 @@ def twist(self): def q_dimension(self): r""" Return the quantum dimension as an element of the cyclotomic - field of the `2l`-th roots of unity, where `l = m (k+h^\vee)` + field of the `2\ell`-th roots of unity, where `l = m (k+h^\vee)` with `m=1,2,3` depending on whether type is simply, doubly or triply laced, `k` is the level and `h^\vee` is the dual Coxeter number. From 504472f2010ba746db0e3ec12f3e1b87228b6152 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Sat, 27 Jun 2020 08:58:15 -0700 Subject: [PATCH 24/34] add Ising MTC as an example --- src/doc/en/reference/references/index.rst | 3 ++ src/sage/combinat/root_system/fusion_ring.py | 32 ++++++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 3d4597bc726..2f12e4f60e8 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -4557,6 +4557,9 @@ REFERENCES: Contemp. Math., **413**, Amer. Math. Soc., Providence, RI, 2006. :arXiv:`math/0503226`. +.. [RoStWa2009] Eric Rowell, Richard Stong and Zhenghan Wang, *On classification + of modular tensor categories*, Comm. Math. Phys. 292, 343--389, 2009. + .. [RR1997] Arun Ram and Jeffrey Remmel. *Applications of the Frobenius formulas and the characters of the symmetric group and the Hecke algebras of type A*. J. Algebraic Combin. diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index a0e1978a7bf..77246df6cbe 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -20,8 +20,7 @@ from sage.misc.cachefunc import cached_method class FusionRing(WeylCharacterRing): - r""" - Return the Fusion Ring (Verlinde Algebra) of level ``k``. + r"""Return the Fusion Ring (Verlinde Algebra) of level ``k``. INPUT: @@ -151,6 +150,33 @@ class FusionRing(WeylCharacterRing): True sage: test_verlinde(FusionRing("B4",2)) True + + As an exercise, the reader may verify the examples in + Section 5.3 of [RoStWa2009]_. Here we check the example + of the Ising modular tensor product, which is related + to the BPZ minimal model `M(4,3)` or to an `E_8` coset + model. See [DFMS1996]_ sections 7.4.2 and + 18.4.1. [RoStWa2009]_ Example 5.3.4 tells us how to + construct it as the conjugate of the `E_8` level 2 + :class:`FusionRing`:: + + sage: I = FusionRing("E8",2,conjugate=True) + sage: I.fusion_labels(["i0","p","s"],inject_variables=True) + sage: b = I.basis().list(); b + [i0, p, s] + sage: [[x*y for x in b] for y in b] + [[i0, p, s], [p, i0, s], [s, s, i0 + p]] + sage: [x.q_dimension()^2 for x in b] + [1, 1, 2] + sage: I.s_matrix() + [ 1 1 -zeta128^48 + zeta128^16] + [ 1 1 zeta128^48 - zeta128^16] + [-zeta128^48 + zeta128^16 zeta128^48 - zeta128^16 0] + sage: I.s_matrix().apply_map(lambda x:x^2) + [1 1 2] + [1 1 2] + [2 2 0] + """ @staticmethod def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjugate=False): @@ -196,7 +222,7 @@ def field(self): return CyclotomicField(4 * self._fg * self._l) def get_order(self): - r"""This returns the weights of the basis vectors in a fixed order. + r"""Return the weights of the basis vectors in a fixed order. You may change the order of the basis using :meth:`CombinatorialFreeModule.set_order` EXAMPLES:: From 336e6c01e814e060023f7d3f50164862d8c622b5 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Sat, 27 Jun 2020 09:13:17 -0700 Subject: [PATCH 25/34] typo --- src/sage/combinat/root_system/fusion_ring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 77246df6cbe..043e9bee0b6 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -153,7 +153,7 @@ class FusionRing(WeylCharacterRing): As an exercise, the reader may verify the examples in Section 5.3 of [RoStWa2009]_. Here we check the example - of the Ising modular tensor product, which is related + of the Ising modular tensor category, which is related to the BPZ minimal model `M(4,3)` or to an `E_8` coset model. See [DFMS1996]_ sections 7.4.2 and 18.4.1. [RoStWa2009]_ Example 5.3.4 tells us how to From c8d94ca553339d6357d9e5b2f52f0400a04e9a64 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Sat, 27 Jun 2020 09:19:46 -0700 Subject: [PATCH 26/34] add twists to Ising example --- src/sage/combinat/root_system/fusion_ring.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 043e9bee0b6..4d6219d5302 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -166,6 +166,8 @@ class FusionRing(WeylCharacterRing): [i0, p, s] sage: [[x*y for x in b] for y in b] [[i0, p, s], [p, i0, s], [s, s, i0 + p]] + sage: [x.twist() for x in b] + [0, 1, 1/8] sage: [x.q_dimension()^2 for x in b] [1, 1, 2] sage: I.s_matrix() From 04cefcf9dca68aff2bef03c2d4778cc6df7ab796 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Sat, 27 Jun 2020 18:10:24 -0700 Subject: [PATCH 27/34] renamed total_quantum_order => global_q_dimension --- src/sage/combinat/root_system/fusion_ring.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 4d6219d5302..263a4cb2ea7 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -116,7 +116,7 @@ class FusionRing(WeylCharacterRing): divide by the square root of `D = \sum_V d_i(V)^2` where the sum is over all simple objects `V` and `d_i(V)` is the quantum dimension of `V` computed by the method :meth:`Element.q_dimension`. The quantity `D` - is computed by :meth:`total_quantum_order`. + is computed by :meth:`global_q_dimension`. Let us check the Verlinde formula. This famous identity states that @@ -140,7 +140,7 @@ class FusionRing(WeylCharacterRing): sage: def test_verlinde(R): ....: b0 = R.one() - ....: c = R.total_quantum_order() + ....: c = R.global_q_dimension() ....: return all(V(i,j,k) == c * R.N_ijk(i,j,k) for i in R.basis() ....: for j in R.basis() for k in R.basis()) @@ -168,6 +168,8 @@ class FusionRing(WeylCharacterRing): [[i0, p, s], [p, i0, s], [s, s, i0 + p]] sage: [x.twist() for x in b] [0, 1, 1/8] + sage: I.global_q_dimension() + 4 sage: [x.q_dimension()^2 for x in b] [1, 1, 2] sage: I.s_matrix() @@ -423,7 +425,7 @@ def s_matrix(self): This is the matrix denoted `\widetilde{s}` in [BaKi2001]_. It is not normalized to be unitary. To obtain a unitary matrix, divide by `\sqrt{D}` where `D` is computed - by :meth:`total_quantum_order`. + by :meth:`global_q_dimension`. EXAMPLES:: @@ -513,14 +515,14 @@ def fusion_labels(self, labels=None, inject_variables=False): inject_variable(labels[j], B[b]) self._fusion_labels = d - def total_quantum_order(self): + def global_q_dimension(self): """ Return `\sum d_i^2`, where the sum is over all simple objects and `d_i` is the quantum dimension. EXAMPLES:: - sage: FusionRing("E6",1).total_quantum_order() + sage: FusionRing("E6",1).global_q_dimension() 3 """ return sum(x.q_dimension()**2 for x in self.basis()) From c1338b932e8e42a19ddba27b0915fb7487ede207 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Sat, 27 Jun 2020 21:54:55 -0700 Subject: [PATCH 28/34] doc cleanup --- src/sage/combinat/root_system/fusion_ring.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 263a4cb2ea7..a26b40aac70 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -20,7 +20,8 @@ from sage.misc.cachefunc import cached_method class FusionRing(WeylCharacterRing): - r"""Return the Fusion Ring (Verlinde Algebra) of level ``k``. + r""" + Return the Fusion Ring (Verlinde Algebra) of level ``k``. INPUT: @@ -120,9 +121,9 @@ class FusionRing(WeylCharacterRing): Let us check the Verlinde formula. This famous identity states that - .. MATH:: + .. MATH:: - N_{ijk} = \sum_l \frac{s(i,\ell)\,s(j,\ell)\,s(k,\ell)}{s(I,\ell)}, + N_{ijk} = \sum_l \frac{s(i,\ell)\,s(j,\ell)\,s(k,\ell)}{s(I,\ell)}, where ``I`` is the unit object. In this formula `s` is the normalized unitary S-matrix denoted `s` in [BaKi2001]_. We may define a function that @@ -226,7 +227,9 @@ def field(self): return CyclotomicField(4 * self._fg * self._l) def get_order(self): - r"""Return the weights of the basis vectors in a fixed order. + r""" + Return the weights of the basis vectors in a fixed order. + You may change the order of the basis using :meth:`CombinatorialFreeModule.set_order` EXAMPLES:: @@ -364,7 +367,7 @@ def N_ijk(self, elt_i, elt_j, elt_k): @cached_method def Nk_ij(self, elt_i, elt_j, elt_k): - """ + r""" Return the fusion coefficient `N^k_{ij}`. These are the structure coefficients of the fusion ring, so @@ -386,7 +389,9 @@ def Nk_ij(self, elt_i, elt_j, elt_k): def s_ij(self, elt_i, elt_j): r""" Return the element of the S-matrix of this FusionRing corresponding to - the given elements. This is computed using the formula + the given elements. + + This is computed using the formula .. MATH:: @@ -516,7 +521,7 @@ def fusion_labels(self, labels=None, inject_variables=False): self._fusion_labels = d def global_q_dimension(self): - """ + r""" Return `\sum d_i^2`, where the sum is over all simple objects and `d_i` is the quantum dimension. From 55b2e6be8c143481dd885f694d9beafe27aa4d68 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Sun, 28 Jun 2020 07:03:14 -0700 Subject: [PATCH 29/34] added _test_verlinde method to check Verlinde formula --- src/sage/combinat/root_system/fusion_ring.py | 37 +++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index a26b40aac70..b4608505563 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -149,7 +149,7 @@ class FusionRing(WeylCharacterRing): sage: test_verlinde(FusionRing("A2",1)) True - sage: test_verlinde(FusionRing("B4",2)) + sage: test_verlinde(FusionRing("B4",2)) # long time (.56s) True As an exercise, the reader may verify the examples in @@ -207,10 +207,45 @@ def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjug sage: D41 = FusionRing('D4', 1) sage: TestSuite(D41).run() + + sage: G22 = FusionRing('G2', 2) + sage: TestSuite(G22).run() + + sage: F41 = FusionRing('F4', 1) + sage: TestSuite(F41).run() + + sage: E61 = FusionRing('E6', 1) + sage: TestSuite(E61).run() + + sage: E71 = FusionRing('E7', 1) + sage: TestSuite(E71).run() + + sage: E81 = FusionRing('E8', 1) + sage: TestSuite(E81).run() + """ return super(FusionRing, cls).__classcall__(cls, ct, base_ring=base_ring, prefix=prefix, style=style, k=k, conjugate=conjugate) + def _test_verlinde(self, **options): + """ + Check the Verlinde formula for this :class:`FusionRing` instance. + + EXAMPLES:: + + sage: G22=FusionRing("G2",2) + sage: G22._test_verlinde() + + """ + tester = self._tester(**options) + c = self.global_q_dimension() + i0 = self.one() + for x in self.basis(): + for y in self.basis(): + for z in self.basis(): + v = sum(self.s_ij(x,w)*self.s_ij(y,w)*self.s_ij(z,w)/self.s_ij(i0,w) for w in self.basis()) + tester.assertEqual(v,c*self.N_ijk(x,y,z)) + def field(self): r""" Return a cyclotomic field large enough to From 0974b086cdb6994313c5e004a55b9499df0a8a6f Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Sun, 28 Jun 2020 19:36:33 -0700 Subject: [PATCH 30/34] explain about conjugate in Verlinde formula --- src/sage/combinat/root_system/fusion_ring.py | 30 +++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index b4608505563..d4767a6e632 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -20,8 +20,7 @@ from sage.misc.cachefunc import cached_method class FusionRing(WeylCharacterRing): - r""" - Return the Fusion Ring (Verlinde Algebra) of level ``k``. + r"""Return the Fusion Ring (Verlinde Algebra) of level ``k``. INPUT: @@ -119,16 +118,33 @@ class FusionRing(WeylCharacterRing): of `V` computed by the method :meth:`Element.q_dimension`. The quantity `D` is computed by :meth:`global_q_dimension`. - Let us check the Verlinde formula. This famous identity states that + Let us check the Verlinde formula, which is [DFMS1996]_ (16.3). This + famous identity states that + + .. MATH:: + + N^k_{ij} = \sum_l \frac{s(i,\ell)\,s(j,\ell)\,\overline{s(k,\ell)}}{s(I,\ell)}, + + where `N^k_{ij}` are the fusion coefficients, i.e. the structure + constants of the fusion ring, and ``I`` is the unit object. + The S-matrix has the property that if `i*` denotes the dual + object of `i`, implemented in Sage as ``i.dual()``, then + + .. MATH:: + + s(i*,j) = s(i,j*) = \overline{s(i,j)}. + + This is equation (16.5) in [DFMS1996]_. Thus with `N_{ijk}=N^{k*}_{ij}` + the Verlinde formula is equivalent to .. MATH:: N_{ijk} = \sum_l \frac{s(i,\ell)\,s(j,\ell)\,s(k,\ell)}{s(I,\ell)}, - where ``I`` is the unit object. In this formula `s` is the normalized - unitary S-matrix denoted `s` in [BaKi2001]_. We may define a function that - corresponds to the right-hand side, except using `\tilde{s}` instead - of `s`:: + In this formula `s` is the normalized unitary S-matrix + denoted `s` in [BaKi2001]_. We may define a function that + corresponds to the right-hand side, except using + `\tilde{s}` instead of `s`:: sage: def V(i,j,k): ....: R = i.parent() From e9de384fb376b67faa18d530bbdc8193a9fdbc03 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Sun, 28 Jun 2020 21:25:28 -0700 Subject: [PATCH 31/34] revision of twists docstring --- src/sage/combinat/root_system/fusion_ring.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index d4767a6e632..ad8147b82bc 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -622,15 +622,16 @@ def weight(self): return next(iter(self._monomial_coefficients)) def twist(self): - r""" - Compute the object's twist. This returns a rational number `h` + r"""Compute the object's twist. This returns a rational number `h` such that `\theta = e^{i \pi h}` is the twist of ``self``. - This method is only available for simple objects. - - We compute the twists following p.7 of [Row2006]_, noting that - the bilinear form is normalized so that - `\langle \alpha, \alpha \rangle = 2` for short roots. + This method is only available for simple objects. If + `\lambda` is the weight of the object, then + `h = \langle\lambda,\lambda+2\rho\rangle` where + `\rho` is half the sum of the positive roots. + As in [Row2006]_, this requires normalizing + the invariant bilinear form so that `\langle \alpha, \alpha + \rangle = 2` for short roots. EXAMPLES:: @@ -644,6 +645,7 @@ def twist(self): Finite family {(0, 0, 0, 0): F41(0,0,0,0), (1, 0, 0, 0): F41(0,0,0,1)} sage: F41(0,0,0,1).twist() 4/5 + """ if not self.is_simple_object(): raise ValueError("quantum twist is only available for simple objects of a FusionRing") From 0e97174cbc8b8d000ab8f160d2cf0a167ce65b3d Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Sun, 28 Jun 2020 21:56:20 -0700 Subject: [PATCH 32/34] fix newline before docstring --- src/sage/combinat/root_system/fusion_ring.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index ad8147b82bc..4fb51fd12b7 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -622,8 +622,9 @@ def weight(self): return next(iter(self._monomial_coefficients)) def twist(self): - r"""Compute the object's twist. This returns a rational number `h` - such that `\theta = e^{i \pi h}` is the twist of ``self``. + r""" + Return a rational number `h` such that `\theta = e^{i \pi h}` + is the twist of ``self``. This method is only available for simple objects. If `\lambda` is the weight of the object, then From 642e80f1afbd6456bfb697fca3b72f8ed4218114 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 29 Jun 2020 15:43:10 +1000 Subject: [PATCH 33/34] Final reviewer changes. --- src/sage/combinat/root_system/fusion_ring.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 4fb51fd12b7..dfb5a66f293 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -20,7 +20,8 @@ from sage.misc.cachefunc import cached_method class FusionRing(WeylCharacterRing): - r"""Return the Fusion Ring (Verlinde Algebra) of level ``k``. + r""" + Return the Fusion Ring (Verlinde Algebra) of level ``k``. INPUT: @@ -238,7 +239,6 @@ def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjug sage: E81 = FusionRing('E8', 1) sage: TestSuite(E81).run() - """ return super(FusionRing, cls).__classcall__(cls, ct, base_ring=base_ring, prefix=prefix, style=style, k=k, conjugate=conjugate) @@ -249,18 +249,18 @@ def _test_verlinde(self, **options): EXAMPLES:: - sage: G22=FusionRing("G2",2) + sage: G22 = FusionRing("G2",2) sage: G22._test_verlinde() - """ tester = self._tester(**options) c = self.global_q_dimension() i0 = self.one() - for x in self.basis(): - for y in self.basis(): - for z in self.basis(): - v = sum(self.s_ij(x,w)*self.s_ij(y,w)*self.s_ij(z,w)/self.s_ij(i0,w) for w in self.basis()) - tester.assertEqual(v,c*self.N_ijk(x,y,z)) + S = tester.some_elements() + from sage.misc.misc import some_tuples + B = self.basis() + for x,y,z in some_tuples(B, 3, tester._max_runs): + v = sum(self.s_ij(x,w) * self.s_ij(y,w) * self.s_ij(z,w) / self.s_ij(i0,w) for w in B) + tester.assertEqual(v, c * self.N_ijk(x,y,z)) def field(self): r""" From 8918e84803d65fa44b65b7fbffc6a7316b3a02c6 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Mon, 29 Jun 2020 07:43:25 -0700 Subject: [PATCH 34/34] remove S = tester.some_elements() --- src/sage/combinat/root_system/fusion_ring.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index dfb5a66f293..ed76e05bbee 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -255,7 +255,6 @@ def _test_verlinde(self, **options): tester = self._tester(**options) c = self.global_q_dimension() i0 = self.one() - S = tester.some_elements() from sage.misc.misc import some_tuples B = self.basis() for x,y,z in some_tuples(B, 3, tester._max_runs):