diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 70edbb92226..4d413841317 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -20,19 +20,21 @@ 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: - ``ct`` -- the Cartan type of a simple (finite-dimensional) Lie algebra - ``k`` -- a nonnegative integer - - (optional) ``conjugate`` -- (default ``False``) set ``True`` to obtain the complex conjugate ring - - (optional) ``cyclotomic_order`` -- (default computed depending on ``ct`` and ``k``) + - ``conjugate`` -- (default ``False``) set ``True`` to obtain + the complex conjugate ring + - ``cyclotomic_order`` -- (default computed depending on ``ct`` and ``k``) The cyclotomic order is an integer `N` such that all computations will return elements of the cyclotomic field of `N`-th roots of unity. Normally you will never need to change this but consider changing it - if :meth:`to_field` ever returns ``None``. + if :meth:`root_of_unity` ever returns ``None``. This algebra has a basis (sometimes called *primary fields* but here called *simple objects*) indexed by the weights of level `\leq k`. @@ -90,8 +92,8 @@ class FusionRing(WeylCharacterRing): I0 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:`CombinatorialFreeModule.set_order` to reorder the basis:: + This is the order used by methods such as :meth:`s_matrix`. 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()] @@ -188,8 +190,8 @@ class FusionRing(WeylCharacterRing): Section 5.3 of [RoStWa2009]_. Here we check the example 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 + 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`:: @@ -218,41 +220,41 @@ class FusionRing(WeylCharacterRing): The term *modular tensor category* refers to the fact that associated with the category there is a projective representation of the modular - group `SL(2,\mathbb{Z})`. We recall that this group is generated by + group `SL(2,\ZZ)`. We recall that this group is generated by .. MATH:: S = \begin{pmatrix} & -1\\1\end{pmatrix},\qquad T = \begin{pmatrix} 1 & 1\\ &1 \end{pmatrix} - subject tot the relations `(ST)^3=S^2`, `S^2T=TS^2`, `S^4=I`. + subject to the relations `(ST)^3 = S^2`, `S^2T = TS^2`, and `S^4 = I`. Let `s` be the normalized S-matrix, and `t` the diagonal matrix whose entries are the twists of the simple objects. Let `s` the unitary S-matrix and `t` the matrix of twists, and `C` the conjugation matrix :meth:`conj_matrix`. Let - + .. MATH:: - D_+ = \sum_i d_i^2\theta_i, \qquad D_- = d_i^2\theta_i^{-1}, + D_+ = \sum_i d_i^2 \theta_i, \qquad D_- = d_i^2 \theta_i^{-1}, - where `d_i` and `\theta_i` are the quantum dimensions and twists - of the simple objects. Let `c` be the virasoro central charge, a rational - number that is computed in :meth:`virasoro_central_charge`. It is known - that + where `d_i` and `\theta_i` are the quantum dimensions and twists of the + simple objects. Let `c` be the Virasoro central charge, a rational number + that is computed in :meth:`virasoro_central_charge`. It is known that .. MATH:: - \sqrt{\frac{D_+}{D_-}}=e^{i\pi c/4}. + \sqrt{\frac{D_+}{D_-}} = e^{i\pi c/4}. - It is proved in [BaKi2001]_, equation (3.1.17) that + It is proved in [BaKi2001]_ Equation (3.1.17) that .. MATH:: - (st)^3= e^{i\pi c/4} s^2,\qquad s^2=C,\qquad C^2=1,\qquad Ct=tC. + (st)^3 = e^{i\pi c/4} s^2, \qquad + s^2 = C, \qquad C^2 = 1, \qquad Ct = tC. - Therefore `S\mapsto s, T\mapsto t` is a projective representation - of `SL(2,\mathbb{Z})`. Let us confirm these identities for the Fibonacci MTC - ``FusionRing("G2",1)``:: + Therefore `S \mapsto s, T \mapsto t` is a projective representation + of `SL(2, \ZZ)`. Let us confirm these identities for the Fibonacci MTC + ``FusionRing("G2", 1)``:: sage: R = FusionRing("G2",1) sage: S = R.s_matrix(unitary=True) @@ -260,13 +262,12 @@ class FusionRing(WeylCharacterRing): sage: C = R.conj_matrix() sage: c = R.virasoro_central_charge(); c 14/5 - sage: (S*T)^3 == R.to_field(c/4)*S^2 + sage: (S*T)^3 == R.root_of_unity(c/4) * S^2 True sage: S^2 == C True sage: C*T == T*C True - """ @staticmethod def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjugate=False, cyclotomic_order=None): @@ -281,7 +282,7 @@ def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjug sage: F1 is F2 and F2 is F3 True - sage: A23 = FusionRing('A2', 3) + sage: A23 = FusionRing('A2', 3) # long time sage: TestSuite(A23).run() sage: B22 = FusionRing('B2', 2) @@ -309,7 +310,9 @@ def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjug sage: TestSuite(E81).run() """ return super(FusionRing, cls).__classcall__(cls, ct, base_ring=base_ring, - prefix=prefix, style=style, k=k, conjugate=conjugate, cyclotomic_order=cyclotomic_order) + prefix=prefix, style=style, k=k, + conjugate=conjugate, + cyclotomic_order=cyclotomic_order) def _test_verlinde(self, **options): """ @@ -319,7 +322,6 @@ def _test_verlinde(self, **options): sage: G22 = FusionRing("G2",2) sage: G22._test_verlinde() - """ tester = self._tester(**options) c = self.global_q_dimension() @@ -331,17 +333,17 @@ def _test_verlinde(self, **options): tester.assertEqual(v, c * self.N_ijk(x,y,z)) def _test_total_q_order(self, **options): - """ + r""" + Check that the total quantum order is real and positive. + The total quantum order is the positive square root - of the global quantum dimension. Check that it is - real and positive. This indirectly test the + of the global quantum dimension. This indirectly test the Virasoro central charge. EXAMPLES:: sage: G22 = FusionRing("G2",2) sage: G22._test_total_q_order() - """ tester = self._tester(**options) tqo = self.total_q_order() @@ -417,10 +419,11 @@ def fusion_labels(self, labels=None, inject_variables=False): inject_variable(labels[j], B[b]) self._fusion_labels = d + @cached_method def field(self): r""" Return a cyclotomic field large enough to - contain the `2\ell`-th roots of unity, as well as + contain the `2 \ell`-th roots of unity, as well as all the S-matrix entries. EXAMPLES:: @@ -432,26 +435,25 @@ def field(self): """ return CyclotomicField(4 * self._cyclotomic_order) - def to_field(self, r): - """ - Returns `e^{i\pi r}` as an element of ``self.field()`` if possible. + def root_of_unity(self, r): + r""" + Return `e^{i\pi r}` as an element of ``self.field()`` if possible. INPUT: - = ``r`` -- a rational number + - ``r`` -- a rational number EXAMPLES:: - sage: A11=FusionRing("A1",1) + sage: A11 = FusionRing("A1",1) sage: A11.field() Cyclotomic Field of order 24 and degree 8 - sage: [A11.to_field(2/x) for x in [1..7]] + sage: [A11.root_of_unity(2/x) for x in [1..7]] [1, -1, zeta24^4 - 1, zeta24^6, None, zeta24^4, None] - """ n = 2 * r * self._cyclotomic_order if n in ZZ: - return self.field().gen()**n + return self.field().gen() ** n else: return None @@ -463,7 +465,7 @@ def get_order(self): EXAMPLES:: - sage: A14=FusionRing("A1",4) + 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]]) @@ -510,7 +512,7 @@ def fusion_level(self): def fusion_l(self): r""" - Return the product `{\ell}=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 @@ -531,43 +533,45 @@ def fusion_l(self): return self._l def virasoro_central_charge(self): - r"""Return the Virasoro central charge of the WZW conformal - field theory associated with the Fusion Ring. If `\mathfrak{g}` - is the corresponding semisimple Lie algebra, this is + r""" + Return the Virasoro central charge of the WZW conformal + field theory associated with the Fusion Ring. + + If `\mathfrak{g}` is the corresponding semisimple Lie algebra, this is .. MATH:: - \frac{k\dim\mathfrak{g}}{k+h^\vee} + \frac{k\dim\mathfrak{g}}{k+h^\vee}, where `k` is the level and `h^\vee` is the dual Coxeter number. - See [DFMS1996]_ equation (15.61). + See [DFMS1996]_ Equation (15.61). Let `d_i` and `theta_i` be the quantum dimensions and - twists of the simple objects. By Proposition 2.3 in [RoStWa2009]_ - there exists a rational number c such that `D_+/\sqrt{D}=e^{i\pi c/4}` - where `D_+=\sum d_i^2\theta_i` is computed in :meth:`D_plus` and - `D=\sum d_i^2>0` is computed by :meth:`global_q_dimension`. Squaring - this identity and remembering that `D_+D_-=D` gives + twists of the simple objects. By Proposition 2.3 in [RoStWa2009]_, + there exists a rational number `c` such that + `D_+ / \sqrt{D} = e^{i\pi c/4}`, where `D_+ = \sum d_i^2 \theta_i` + is computed in :meth:`D_plus` and `D = \sum d_i^2 > 0` is computed + by :meth:`global_q_dimension`. Squaring this identity and + remembering that `D_+ D_- = D` gives .. MATH:: - D_+/D_- = e^{i\pi c/2}. + D_+ / D_- = e^{i\pi c/2}. EXAMPLES:: - sage: R = FusionRing("A1",2) + sage: R = FusionRing("A1", 2) sage: c = R.virasoro_central_charge(); c 3/2 sage: Dp = R.D_plus(); Dp 2*zeta32^6 sage: Dm = R.D_minus(); Dm -2*zeta32^10 - sage: Dp/Dm == R.to_field(c/2) + sage: Dp / Dm == R.root_of_unity(c/2) True - """ - dim_g = len(self.space().roots())+self.cartan_type()[1] - return self._conj*self._k*dim_g/(self._k+self._h_check) + dim_g = len(self.space().roots()) + self.cartan_type().rank() + return self._conj * self._k * dim_g / (self._k + self._h_check) def conj_matrix(self): r""" @@ -580,10 +584,9 @@ def conj_matrix(self): [1 0 0] [0 0 1] [0 1 0] - """ b = self.basis().list() - return matrix([[i==j.dual() for i in b] for j in b]) + return matrix(ZZ, [[i == j.dual() for i in b] for j in b]) def twists_matrix(self): r""" @@ -595,16 +598,15 @@ def twists_matrix(self): sage: B21=FusionRing("B2",1) sage: [x.twist() for x in B21.basis().list()] [0, 1, 5/8] - sage: [B21.to_field(x.twist()) for x in B21.basis().list()] + sage: [B21.root_of_unity(x.twist()) for x in B21.basis().list()] [1, -1, zeta32^10] sage: B21.twists_matrix() [ 1 0 0] [ 0 -1 0] [ 0 0 zeta32^10] - """ B = self.basis() - return diagonal_matrix(self.to_field(B[x].twist()) for x in self.get_order()) + return diagonal_matrix(self.root_of_unity(B[x].twist()) for x in self.get_order()) @cached_method def N_ijk(self, elt_i, elt_j, elt_k): @@ -658,7 +660,7 @@ def Nk_ij(self, elt_i, elt_j, elt_k): @cached_method def s_ij(self, elt_i, elt_j): r""" - Return the element of the S-matrix of this FusionRing corresponding to + Return the element of the S-matrix of this fusion ring corresponding to the given elements. This is computed using the formula @@ -683,17 +685,20 @@ def s_ij(self, elt_i, elt_j): [1, -zeta60^14 + zeta60^6 + zeta60^4, -zeta60^14 + zeta60^6 + zeta60^4, -1] """ ijtwist = elt_i.twist() + elt_j.twist() - return sum(k.q_dimension() * self.Nk_ij(elt_i, k, elt_j) * self.to_field(k.twist() - ijtwist) for k in self.basis()) + return sum(k.q_dimension() * self.Nk_ij(elt_i, k, elt_j) + * self.root_of_unity(k.twist() - ijtwist) + for k in self.basis()) def s_matrix(self, unitary=False): r""" - Return the S-matrix of this FusionRing. + Return the S-matrix of this fusion ring. OPTIONAL: - - ``unitary`` -- (default: False) set True to obtain the unitary S-matrix. + - ``unitary`` -- (default: ``False``) set to ``True`` to obtain + the unitary S-matrix - Without the `unitary` parameter, this is the matrix denoted + Without the ``unitary`` parameter, this is the matrix denoted `\widetilde{s}` in [BaKi2001]_. EXAMPLES:: @@ -718,7 +723,7 @@ def s_matrix(self, unitary=False): b = self.basis() S = matrix([[self.s_ij(b[x], b[y]) for x in self.get_order()] for y in self.get_order()]) if unitary: - return S/self.total_q_order() + return S / self.total_q_order() else: return S @@ -735,7 +740,7 @@ def global_q_dimension(self): return sum(x.q_dimension()**2 for x in self.basis()) def total_q_order(self): - """ + r""" Return the positive square root of ``self.global_q_dimension()`` as an element of ``self.field()``. @@ -748,20 +753,20 @@ def total_q_order(self): True sage: tqo^2 == F.global_q_dimension() True - """ c = self.virasoro_central_charge() - return self.D_plus() * self.to_field(-c/4) + return self.D_plus() * self.root_of_unity(-c/4) def D_plus(self): r""" Return `\sum d_i^2\theta_i` where `i` runs through the simple objects, `d_i` is the quantum dimension and `\theta_i` is the twist. + This is denoted `p_+` in [BaKi2001]_ Chapter 3. EXAMPLES:: - sage: B31=FusionRing("B3",1) + sage: B31 = FusionRing("B3",1) sage: Dp = B31.D_plus(); Dp 2*zeta48^13 - 2*zeta48^5 sage: Dm = B31.D_minus(); Dm @@ -770,21 +775,21 @@ def D_plus(self): True sage: c = B31.virasoro_central_charge(); c 7/2 - sage: Dp/Dm == B31.to_field(c/2) + sage: Dp/Dm == B31.root_of_unity(c/2) True - """ - return sum((x.q_dimension())**2*self.to_field(x.twist()) for x in self.basis()) + return sum((x.q_dimension())**2 * self.root_of_unity(x.twist()) for x in self.basis()) def D_minus(self): r""" - Return `\sum d_i^2\theta_i^{-1}` where `i` runs through the simple objects, - `d_i` is the quantum dimension and `\theta_i` is the twist. + Return `\sum d_i^2\theta_i^{-1}` where `i` runs through the simple + objects, `d_i` is the quantum dimension and `\theta_i` is the twist. + This is denoted `p_-` in [BaKi2001]_ Chapter 3. EXAMPLES:: - sage: E83=FusionRing("E8",3,conjugate=True) + sage: E83 = FusionRing("E8",3,conjugate=True) sage: [Dp,Dm] = [E83.D_plus(), E83.D_minus()] sage: Dp*Dm == E83.global_q_dimension() True @@ -792,9 +797,8 @@ def D_minus(self): -248/11 sage: Dp*Dm == E83.global_q_dimension() True - """ - return sum((x.q_dimension())**2*self.to_field(-x.twist()) for x in self.basis()) + return sum((x.q_dimension())**2 * self.root_of_unity(-x.twist()) for x in self.basis()) class Element(WeylCharacterRing.Element): """ @@ -841,7 +845,7 @@ def twist(self): 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 + `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 @@ -849,29 +853,36 @@ def twist(self): EXAMPLES:: - sage: G21=FusionRing("G2",1) + sage: G21 = FusionRing("G2", 1) sage: [x.twist() for x in G21.basis()] [0, 4/5] - sage: [G21.to_field(x.twist()) for x in G21.basis()] + sage: [G21.root_of_unity(x.twist()) for x in G21.basis()] [1, zeta60^14 - zeta60^4] sage: zeta60 = G21.field().gen() sage: zeta60^((4/5)*(60/2)) zeta60^14 - zeta60^4 + sage: F42 = FusionRing("F4", 2) + sage: [x.twist() for x in F42.basis()] + [0, 18/11, 2/11, 12/11, 4/11] + + sage: E62 = FusionRing("E6", 2) + sage: [x.twist() for x in E62.basis()] + [0, 26/21, 12/7, 8/21, 8/21, 26/21, 2/3, 4/7, 2/3] """ if not self.is_simple_object(): raise ValueError("quantum twist is only available for simple objects of a FusionRing") P = self.parent() rho = P.space().rho() - lam = self.weight() + # We copy self.weight() to skip the test (which was already done + # by self.is_simple_object()). + lam = next(iter(self._monomial_coefficients)) 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: - twist += 2 - return twist + f = twist.floor() + twist -= f + return twist + (f % 2) @cached_method def q_dimension(self):