Source code for msinvar.brane_tilings

r"""
Brane tilings and molten crystal counting

This code is based on :arxiv:`0809.0117`.
See also :arXiv:`2012.14358`

Given a brane tiling (a bipartite graph embedded in a torus) we associate with
it a quiver with potential (Q,W).
For any vertex `i\in Q_0`, we consider the set `\Delta_i` of paths starting
at i up to an equivalence relation induced by the potential W.
This set has a poset structure and can be interpreted as a crystal embedded
in `\mathbb R^3`.
For example, a brane tiling for `\mathbb C^3` corresponds to a quiver with one
vertex 0, three loops x,y,z and potential W=x[y,z].
The corresponding Jacobian algebra is `\mathbb C[x,y,z]` which has a basis 
parametrized by `\Delta_0=\mathbb N^3`, our crystal.

We define molten crystals as complements of finite ideals 
`I\subset \Delta_i`, meaning a subset I such that `u\le v` and `v\in I`
implies `u\in I`.
For any path `u\in\Delta_i`, let `t(u)\in Q_0` be the target vertex of u.
Define the dimension vector of I to be 
`\dim I=\sum_{u\in I}e_{t(u)}\in\mathbb N^{Q_0}`.
We compute the partition function
`Z_{\Delta_i}(x)=\sum_{I\subset\Delta_i}x^{\dim I}`
which is closely related to numerical DT invariants of (Q,W).

EXAMPLES::

    sage: from msinvar import *
    sage: CQ=CyclicQuiver(1)
    sage: PQ=CQ.translation_PQ(); PQ
    Ginzburg PQ: Quiver with 1 vertices, 3 arrows and potential with 2 terms
    sage: Q=BTQuiver(PQ)
    sage: Z=Q.partition_func(0, 8); Z
    1 + x + 3*x^2 + 6*x^3 + 13*x^4 + 24*x^5 + 48*x^6 + 86*x^7 + 160*x^8
    sage: Z.Log()
    x + 2*x^2 + 3*x^3 + 4*x^4 + 5*x^5 + 6*x^6 + 7*x^7 + 8*x^8

::
    
    sage: BTD[1]
    ['Conifold=Y10',
     'Phi[1,2,1]*Phi[2,1,1]*Phi[1,2,2]*Phi[2,1,2]-Phi[1,2,1]*Phi[2,1,2]*Phi[1,2,2]*Phi[2,1,1]']
    sage: Q=BTQuiver(potential=BTD[1][1]); Q
    Quiver with 2 vertices, 4 arrows and potential with 2 terms
    sage: Z=Q.NCDT(1, 5); Z
    1 + x0 - 2*x0*x1 - 4*x0^2*x1 + x0*x1^2 - 2*x0^3*x1 + 8*x0^2*x1^2 + 14*x0^3*x1^2 - 4*x0^2*x1^3
    sage: Z.Log()
    x0 - x0^2 - 2*x0*x1 - 2*x0^2*x1 + x0*x1^2 + 6*x0^2*x1^2 + 3*x0^3*x1^2 - 2*x0^2*x1^3

::
    
    sage: CQ=CyclicQuiver(3)
    sage: PQ=CQ.translation_PQ(1); PQ
    Translation PQ: Quiver with 3 vertices, 9 arrows and potential with 6 terms
    sage: Q=BTQuiver(PQ,prec=[1,1,1])
    sage: Q.intAtt_from_crystals().dict() # numerical integer attractor inv. 
    {(0, 0, 1): 1, (0, 1, 0): 1, (1, 0, 0): 1, (1, 1, 1): -3}

"""

# *****************************************************************************
#  Copyright (C) 2021 Sergey Mozgovoy <mozhov@gmail.com>
#
#  Distributed under the terms of the GNU General Public License (GPL)
#                  http://www.gnu.org/licenses/
# *****************************************************************************

from sage.matrix.constructor import matrix, vector
from msinvar.quivers import Quiver
from msinvar.utils import vec
from msinvar.posets import Poset
from msinvar.tm_polynomials import TMPoly
from sage.rings.rational_field import QQ
from msinvar import Invariant, WCS, Stability


[docs]class BTQuiver(Quiver): """Quiver with potential associated with a brane tiling. Contains the method :meth:`wt` such that two paths are equivalent (equal in the Jacobian algebra if and only if their weights are equal). """ def __init__(self, *args, **kw): super().__init__(*args, **kw) A = matrix([self.path2vec(p) for p in self._potential]) m = len(self._potential) A1 = A.augment(vector([1]*m)) l = matrix(A1.right_kernel().basis()).columns() self.wzero = [0]*len(l[0]) self._wt = {a: l[self.arrow_num(a)] for a in self.arrows()}
[docs] def wt(self, a): """Weight of an arrow or a path.""" if isinstance(a, tuple): # a is an arrow return self._wt[a] return vec.add(self._wt[x] for x in a)
[docs] def path2vec(self, p): """Multiplicities of all arrows in a path.""" v = [0]*self.arrow_num() for a in p: v[self.arrow_num(a)] = +1 return v
[docs] def ideal_dim(self, I): """Dimension vector of an ideal I that consists of atoms (paths).""" vert = self.vertices() d = {i: 0 for i in vert} for u in I: d[u.t] += 1 return tuple(d[vert[i]] for i in range(len(vert)))
[docs] def add_new_arrows(self, P, n): """ Add new arrows to the atoms in the poset ``P``. - ``P`` -- poset of atoms, - ``n`` -- index in ``P``, where atoms that require new arrows start. """ n1 = len(P.vert) for k in range(n, n1): u = P.vert[k] for a in self.arrows(): if u.t == self.s(a): v = u.add_arrow(a) for v1 in P.vert: if v.equal(v1): v = v1 break if v != v1: P.vert.append(v) P.rel.append([u, v]) return n1
[docs] def create_path_poset(self, i, N=10): """Creat the poset of atoms (paths) that start at the vertex ``i`` and have length <= ``N``.""" u = Atom(self, i) P = Poset(rel=[], vert=[u]) n = 0 for k in range(N): n = self.add_new_arrows(P, n) P.vert = set(P.vert) return P
[docs] def crystal_dict(self, i, N=5): return crystal_dict(self, i, N)
[docs] def NCDT(self, i, N=5): return NCDT(self, i, N)
[docs] def partition_func(self, i, N=5): return partition_func(self, i, N)
[docs] def partition_func1(self, i, N=5): return partition_func1(self, i, N)
[docs] def ratAtt_from_crystals(self): """See :meth:`ratAtt_from_crystals`.""" return ratAtt_from_crystals(self)
[docs] def intAtt_from_crystals(self): """See :meth:`intAtt_from_crystals`.""" return intAtt_from_crystals(self)
[docs]class Atom: """ Atom -- equivalence class of a path in the Jacobian algebra. - ``Q`` -- a BTQuiver, - ``t`` -- a target, - ``wt`` -- a weight. """ def __init__(self, Q, t, wt=None): self.Q = Q self.t = t if wt is None: wt = Q.wzero self.wt = wt
[docs] def add_arrow(self, a): wt = vec.add(self.wt, self.Q.wt(a)) t = self.Q.t(a) return Atom(self.Q, t, wt)
[docs] def equal(self, u): return self.t == u.t and vec.equal(self.wt, u.wt)
def __repr__(self): return "Atom "+str(self.t)+"-"+str(self.wt)
[docs]def crystal_dict(Q, i, N=5): """Dictionary counting ideals (molten crystals) in the crystal of atoms that start at the vertex ``i`` and have length <= ``N``. We keep track of the dimension vectors of ideals.""" P = Q.create_path_poset(i, N) dct = {} for I in P.ideals(N): d = tuple(Q.ideal_dim(I)) if d not in dct: dct[d] = 0 dct[d] += 1 return dct
[docs]def NCDT(Q, i, N=5): """Numerical NCDT invariants counting (with a sign) ideals (molten crystals) in the crystal of atoms that start at the vertex ``i`` and have length <= ``N``. We keep track of the dimension vectors of ideals.""" R = TMPoly(QQ, Q.vertex_num(), 'x', prec=N) i0 = Q.vertex_num(i) dct = {d: c*(-1)**(d[i0]+Q.eform(d, d)) for d, c in crystal_dict(Q, i, N).items()} return R(dct)
[docs]def partition_func(Q, i, N=5): """Partition function of ideals (molten crystals) in the crystal of atoms that start at the vertex ``i`` and have length <= ``N``. We keep track of the dimension vectors of ideals.""" R = TMPoly(QQ, Q.vertex_num(), 'x', prec=N) return R(crystal_dict(Q, i, N))
[docs]def partition_func1(Q, i, N=5): """Partition function of ideals (molten crystals) in the crystal of atoms that start at the vertex ``i`` and have length <= ``N``. We keep track only of the sizes of ideals.""" R = TMPoly(QQ, 1, 'x', prec=N) dct = {} for d, c in crystal_dict(Q, i, N).items(): n = (sum(d),) if n not in dct: dct[n] = 0 dct[n] += c return R(dct)
# data base of some brane tilings from https://www.lpthe.jussieu.fr/~pioline/computing.html BTD = [ ["C^3", 'Phi[1,1,1]*Phi[1,1,2]*Phi[1,1,3]-Phi[1,1,1]*Phi[1,1,3]*Phi[1,1,2]'], ["Conifold=Y10", 'Phi[1,2,1]*Phi[2,1,1]*Phi[1,2,2]*Phi[2,1,2]-Phi[1,2,1]*Phi[2,1,2]*Phi[1,2,2]*Phi[2,1,1]'], ["C^2xC/2", 'Phi[1,1,1]*Phi[1,2,2]*Phi[2,1,1]+Phi[1,1,1]*Phi[1,2,1]*Phi[2,1,2]-Phi[1,2,2]*Phi[2,1,1]*Phi[2,2,1]+Phi[1,2,1]*Phi[2,1,2]*Phi[2,2,1]'], ["C^2xC/3", 'Phi[1,1,1]*Phi[1,2,1]*Phi[2,1,1]+Phi[2,2,1]*Phi[2,3,1]*Phi[3,2,1]+Phi[1,3,1]*Phi[3,1,1]*Phi[3,3,1]-Phi[1,2,1]*Phi[2,1,1]*Phi[2,2,1]+Phi[1,1,1]*Phi[1,3,1]*Phi[3,1,1]+Phi[2,3,1]*Phi[3,2,1]*Phi[3,3,1]'], ["PdP6=C^3/2x2", 'Phi[1,3,1]*Phi[2,1,1]*Phi[3,2,1]+Phi[1,2,1]*Phi[2,4,1]*Phi[4,1,1]+Phi[2,3,1]*Phi[3,4,1]*Phi[4,2,1]+Phi[1,4,1]*Phi[3,1,1]*Phi[4,3,1]-Phi[1,2,1]*Phi[2,3,1]*Phi[3,1,1]+Phi[1,3,1]*Phi[3,4,1]*Phi[4,1,1]+Phi[1,4,1]*Phi[2,1,1]*Phi[4,2,1]+Phi[2,4,1]*Phi[3,2,1]*Phi[4,3,1]'], ["SPP=L121", 'Phi[1,1,1]*Phi[1,3,1]*Phi[3,1,1]+Phi[1,2,1]*Phi[2,1,1]*Phi[2,3,1]*Phi[3,2,1]-Phi[1,1,1]*Phi[1,2,1]*Phi[2,1,1]+Phi[1,3,1]*Phi[2,3,1]*Phi[3,1,1]*Phi[3,2,1]'], ["P2=C^3/(1,1,1)", 'Phi[1,2,2]*Phi[2,3,3]*Phi[3,1,1]+Phi[1,2,3]*Phi[2,3,1]*Phi[3,1,2]+Phi[1,2,1]*Phi[2,3,2]*Phi[3,1,3]-Phi[1,2,3]*Phi[2,3,2]*Phi[3,1,1]+Phi[1,2,1]*Phi[2,3,3]*Phi[3,1,2]+Phi[1,2,2]*Phi[2,3,1]*Phi[3,1,3]'], ["F0.1=P1xP1", 'Phi[1,2,2]*Phi[2,4,2]*Phi[4,1,1]+Phi[1,3,1]*Phi[3,4,2]*Phi[4,1,2]+Phi[1,3,2]*Phi[3,4,1]*Phi[4,1,3]+Phi[1,2,1]*Phi[2,4,1]*Phi[4,1,4]-Phi[1,3,2]*Phi[3,4,2]*Phi[4,1,1]+Phi[1,2,2]*Phi[2,4,1]*Phi[4,1,2]+Phi[1,2,1]*Phi[2,4,2]*Phi[4,1,3]+Phi[1,3,1]*Phi[3,4,1]*Phi[4,1,4]'], ["F0.2=P1xP1", 'Phi[1,2,1]*Phi[2,3,2]*Phi[3,4,2]*Phi[4,1,1]+Phi[1,2,1]*Phi[2,3,1]*Phi[3,4,2]*Phi[4,1,2]-Phi[1,2,2]*Phi[2,3,2]*Phi[3,4,1]*Phi[4,1,1]+Phi[1,2,2]*Phi[2,3,1]*Phi[3,4,1]*Phi[4,1,2]'], ["F1=dP1=Y21=L312", 'Phi[1,2,1]*Phi[2,3,2]*Phi[3,4,2]*Phi[4,1,1]+Phi[1,3,1]*Phi[3,4,1]*Phi[4,1,2]+Phi[2,3,1]*Phi[3,4,3]*Phi[4,2,1]-Phi[1,3,1]*Phi[3,4,3]*Phi[4,1,1]+Phi[1,2,1]*Phi[2,3,1]*Phi[3,4,2]*Phi[4,1,2]+Phi[2,3,2]*Phi[3,4,1]*Phi[4,2,1]'], ["F2=C^3/(1,1,2)", 'Phi[1,2,1]*Phi[2,3,2]*Phi[3,1,1]+Phi[1,2,2]*Phi[2,4,1]*Phi[4,1,1]+Phi[1,3,1]*Phi[3,4,1]*Phi[4,1,2]+Phi[2,3,1]*Phi[3,4,2]*Phi[4,2,1]-Phi[1,2,2]*Phi[2,3,1]*Phi[3,1,1]+Phi[1,3,1]*Phi[3,4,2]*Phi[4,1,1]+Phi[1,2,1]*Phi[2,4,1]*Phi[4,1,2]+Phi[2,3,2]*Phi[3,4,1]*Phi[4,2,1]'], ["dP2.1", 'Phi[1,3,1]*Phi[3,4,1]*Phi[4,1,1]+Phi[1,2,1]*Phi[2,4,2]*Phi[4,5,2]*Phi[5,1,1]+Phi[2,4,1]*Phi[4,5,1]*Phi[5,2,1]+Phi[3,4,2]*Phi[4,5,3]*Phi[5,3,1]-Phi[1,2,1]*Phi[2,4,1]*Phi[4,1,1]+Phi[1,3,1]*Phi[3,4,2]*Phi[4,5,1]*Phi[5,1,1]+Phi[2,4,2]*Phi[4,5,3]*Phi[5,2,1]+Phi[3,4,1]*Phi[4,5,2]*Phi[5,3,1]'], ["dP2.2", 'Phi[1,2,1]*Phi[2,3,2]*Phi[3,4,1]*Phi[4,1,1]+Phi[1,2,2]*Phi[2,3,1]*Phi[3,5,1]*Phi[5,1,1]+Phi[2,4,1]*Phi[4,5,1]*Phi[5,2,1]-Phi[1,2,2]*Phi[2,4,1]*Phi[4,1,1]+Phi[1,2,1]*Phi[2,3,1]*Phi[3,4,1]*Phi[4,5,1]*Phi[5,1,1]+Phi[2,3,2]*Phi[3,5,1]*Phi[5,2,1]'], ["PdP2", 'Phi[1,2,2]*Phi[2,3,1]*Phi[3,1,1]+Phi[1,3,1]*Phi[3,4,1]*Phi[4,1,1]+Phi[1,3,1]*Phi[3,5,1]*Phi[5,1,1]+Phi[2,3,1]*Phi[3,4,1]*Phi[4,5,1]*Phi[5,2,1]-Phi[1,2,1]*Phi[2,3,2]*Phi[3,1,1]+Phi[1,2,2]*Phi[2,4,1]*Phi[4,1,1]+Phi[1,2,1]*Phi[2,4,1]*Phi[4,5,1]*Phi[5,1,1]+Phi[2,3,2]*Phi[3,5,1]*Phi[5,2,1]'], ["dP3.1", 'Phi[1,3,1]*Phi[3,5,1]*Phi[5,1,1]+Phi[1,2,1]*Phi[2,3,1]*Phi[3,4,1]*Phi[4,5,1]*Phi[5,6,1]*Phi[6,1,1]+Phi[2,4,1]*Phi[4,6,1]*Phi[6,2,1]-Phi[1,2,1]*Phi[2,4,1]*Phi[4,5,1]*Phi[5,1,1]+Phi[1,3,1]*Phi[3,4,1]*Phi[4,6,1]*Phi[6,1,1]+Phi[2,3,1]*Phi[3,5,1]*Phi[5,6,1]*Phi[6,2,1]'], ["dP3.2", 'Phi[1,2,2]*Phi[2,4,1]*Phi[4,1,1]+Phi[1,3,1]*Phi[3,5,1]*Phi[5,1,1]+Phi[1,2,1]*Phi[2,3,1]*Phi[3,4,1]*Phi[4,6,1]*Phi[6,1,1]+Phi[2,5,1]*Phi[5,6,1]*Phi[6,2,1]-Phi[1,3,1]*Phi[3,4,1]*Phi[4,1,1]+Phi[1,2,1]*Phi[2,5,1]*Phi[5,1,1]+Phi[1,2,2]*Phi[2,3,1]*Phi[3,5,1]*Phi[5,6,1]*Phi[6,1,1]+Phi[2,4,1]*Phi[4,6,1]*Phi[6,2,1]'], ["dP3.3", 'Phi[1,3,1]*Phi[3,5,1]*Phi[5,1,1]+Phi[1,2,2]*Phi[2,4,1]*Phi[4,5,1]*Phi[5,1,1]+Phi[1,2,2]*Phi[2,3,1]*Phi[3,6,1]*Phi[6,1,2]+Phi[1,4,1]*Phi[4,6,1]*Phi[6,1,2]-Phi[1,2,1]*Phi[2,3,1]*Phi[3,5,1]*Phi[5,1,2]+Phi[1,4,1]*Phi[4,5,1]*Phi[5,1,2]+Phi[1,3,1]*Phi[3,6,1]*Phi[6,1,1]+Phi[1,2,1]*Phi[2,4,1]*Phi[4,6,1]*Phi[6,1,1]'], ["dP3.4", 'Phi[1,2,1]*Phi[2,4,1]*Phi[4,1,1]+Phi[1,3,1]*Phi[3,4,1]*Phi[4,1,2]+Phi[1,2,3]*Phi[2,5,1]*Phi[5,1,1]+Phi[1,3,3]*Phi[3,5,1]*Phi[5,1,2]+Phi[1,2,2]*Phi[2,6,1]*Phi[6,1,1]+Phi[1,3,2]*Phi[3,6,1]*Phi[6,1,2]-Phi[1,3,2]*Phi[3,4,1]*Phi[4,1,1]+Phi[1,2,2]*Phi[2,4,1]*Phi[4,1,2]+Phi[1,3,1]*Phi[3,5,1]*Phi[5,1,1]+Phi[1,2,1]*Phi[2,5,1]*Phi[5,1,2]+Phi[1,3,3]*Phi[3,6,1]*Phi[6,1,1]+Phi[1,2,3]*Phi[2,6,1]*Phi[6,1,2]'], ["L131", 'Phi[1,1,1]*Phi[1,2,1]*Phi[2,1,1]+Phi[2,2,1]*Phi[2,3,1]*Phi[3,2,1]+Phi[1,4,1]*Phi[3,4,1]*Phi[4,1,1]*Phi[4,3,1]-Phi[1,2,1]*Phi[2,1,1]*Phi[2,2,1]+Phi[1,1,1]*Phi[1,4,1]*Phi[4,1,1]+Phi[2,3,1]*Phi[3,2,1]*Phi[3,4,1]*Phi[4,3,1]'], ["L152", 'Phi[1,2,1]*Phi[2,3,1]*Phi[3,1,1]+Phi[2,3,2]*Phi[3,4,1]*Phi[4,2,1]+Phi[1,4,1]*Phi[4,5,1]*Phi[5,1,1]+Phi[1,2,2]*Phi[2,6,1]*Phi[6,1,1]+Phi[3,4,2]*Phi[4,6,1]*Phi[5,3,1]*Phi[6,5,1]-Phi[1,2,2]*Phi[2,3,2]*Phi[3,1,1]+Phi[2,3,1]*Phi[3,4,2]*Phi[4,2,1]+Phi[3,4,1]*Phi[4,5,1]*Phi[5,3,1]+Phi[1,4,1]*Phi[4,6,1]*Phi[6,1,1]+Phi[1,2,1]*Phi[2,6,1]*Phi[5,1,1]*Phi[6,5,1]'], ["C^3/(1,1,3)", 'Phi[1,2,1]*Phi[2,3,2]*Phi[3,1,1]+Phi[2,3,1]*Phi[3,4,2]*Phi[4,2,1]+Phi[1,2,2]*Phi[2,5,1]*Phi[5,1,1]+Phi[1,4,1]*Phi[4,5,1]*Phi[5,1,2]+Phi[3,4,1]*Phi[4,5,2]*Phi[5,3,1]-Phi[1,2,2]*Phi[2,3,1]*Phi[3,1,1]+Phi[2,3,2]*Phi[3,4,1]*Phi[4,2,1]+Phi[1,4,1]*Phi[4,5,2]*Phi[5,1,1]+Phi[1,2,1]*Phi[2,5,1]*Phi[5,1,2]+Phi[3,4,2]*Phi[4,5,1]*Phi[5,3,1]'], ["Y23=L153", 'Phi[1,4,1]*Phi[2,1,2]*Phi[3,2,1]*Phi[4,3,1]+Phi[3,5,1]*Phi[4,3,2]*Phi[5,4,1]+Phi[1,6,2]*Phi[2,1,1]*Phi[6,2,1]+Phi[4,6,1]*Phi[5,4,2]*Phi[6,5,1]+Phi[1,6,1]*Phi[5,1,1]*Phi[6,5,2]-Phi[1,4,1]*Phi[2,1,1]*Phi[3,2,1]*Phi[4,3,2]+Phi[3,5,1]*Phi[4,3,1]*Phi[5,4,2]+Phi[1,6,1]*Phi[2,1,2]*Phi[6,2,1]+Phi[1,6,2]*Phi[5,1,1]*Phi[6,5,1]+Phi[4,6,1]*Phi[5,4,1]*Phi[6,5,2]'], ["C^3/(1,1,4)", 'Phi[1,2,1]*Phi[2,3,2]*Phi[3,1,1]+Phi[2,3,1]*Phi[3,4,2]*Phi[4,2,1]+Phi[3,4,1]*Phi[4,5,2]*Phi[5,3,1]+Phi[1,2,2]*Phi[2,6,1]*Phi[6,1,1]+Phi[1,5,1]*Phi[5,6,1]*Phi[6,1,2]+Phi[4,5,1]*Phi[5,6,2]*Phi[6,4,1]-Phi[1,2,2]*Phi[2,3,1]*Phi[3,1,1]+Phi[2,3,2]*Phi[3,4,1]*Phi[4,2,1]+Phi[3,4,2]*Phi[4,5,1]*Phi[5,3,1]+Phi[1,5,1]*Phi[5,6,2]*Phi[6,1,1]+Phi[1,2,1]*Phi[2,6,1]*Phi[6,1,2]+Phi[4,5,2]*Phi[5,6,1]*Phi[6,4,1]'], ["PdP3a=C^3/(1,2,3)", 'Phi[1,2,1]*Phi[2,4,1]*Phi[4,1,1]+Phi[1,4,1]*Phi[4,5,1]*Phi[5,1,1]+Phi[2,3,1]*Phi[3,5,1]*Phi[5,2,1]+Phi[1,3,1]*Phi[3,6,1]*Phi[6,1,1]+Phi[2,5,1]*Phi[5,6,1]*Phi[6,2,1]+Phi[3,4,1]*Phi[4,6,1]*Phi[6,3,1]-Phi[1,3,1]*Phi[3,4,1]*Phi[4,1,1]+Phi[1,2,1]*Phi[2,5,1]*Phi[5,1,1]+Phi[2,4,1]*Phi[4,5,1]*Phi[5,2,1]+Phi[1,4,1]*Phi[4,6,1]*Phi[6,1,1]+Phi[2,3,1]*Phi[3,6,1]*Phi[6,2,1]+Phi[3,5,1]*Phi[5,6,1]*Phi[6,3,1]'], ["Y30", 'Phi[1,2,2]*Phi[2,3,1]*Phi[3,4,1]*Phi[4,1,1]+Phi[1,2,2]*Phi[2,5,1]*Phi[5,6,1]*Phi[6,1,1]+Phi[3,4,1]*Phi[4,5,1]*Phi[5,6,2]*Phi[6,3,1]-Phi[1,2,1]*Phi[2,3,1]*Phi[3,4,2]*Phi[4,1,1]+Phi[1,2,1]*Phi[2,5,1]*Phi[5,6,2]*Phi[6,1,1]+Phi[3,4,2]*Phi[4,5,1]*Phi[5,6,1]*Phi[6,3,1]'], ["Y31", 'Phi[1,2,2]*Phi[2,3,1]*Phi[3,4,1]*Phi[4,1,1]+Phi[3,4,2]*Phi[4,5,1]*Phi[5,6,1]*Phi[6,3,1]+Phi[5,6,2]*Phi[6,1,1]*Phi[1,5,1]+Phi[6,1,2]*Phi[1,2,1]*Phi[2,6,1]-Phi[1,2,1]*Phi[2,3,1]*Phi[3,4,2]*Phi[4,1,1]+Phi[3,4,1]*Phi[4,5,1]*Phi[5,6,2]*Phi[6,3,1]+Phi[5,6,1]*Phi[6,1,2]*Phi[1,5,1]+Phi[6,1,1]*Phi[1,2,2]*Phi[2,6,1]'] ]
[docs]def BT_example(n=None): """Brane tilings from the database by `Boris Pioline <https://www.lpthe.jussieu.fr/~pioline/computing.html>`_. ``n`` -- number of the brane tiling. If None, we return the whole list. """ if n is None: return {i: BT_example(i) for i in range(len(BTD))} d = BTD[n] return BTQuiver(potential=d[1], name=d[0])
[docs]def ratAtt_from_crystals(Q): """Numerical rational attractor invariants obtained by recursion from the NCDT invariants (computed by counting crystals) and the flow tree formula. This algorithm works for any brane tiling. EXAMPLE:: sage: from msinvar import * sage: Q=BT_example(6); Q P2=C^3/(1,1,1): Quiver with 3 vertices, 9 arrows and potential with 6 terms sage: Q.prec([1,1,1]) sage: Q.ratAtt_from_crystals().dict() {(0, 0, 1): 1, (0, 1, 0): 1, (1, 0, 0): 1, (1, 1, 1): -3} """ N = sum(Q.prec()) Z = [Invariant(Q.NCDT(i, N)) for i in Q.vertices()] r = Q.vertex_num() W = WCS(rank=r+1, sform=None, prec=Q.prec()+[1]) z = Stability([0]*r+[1]) def f(d): dn = sum(d) if dn == 0: return 0 if dn == 1: return 1 i, m = next((i, m) for i, m in enumerate(d) if m != 0) W.sform = lambda a, b: Q.sform(a, b)-a[-1]*b[i]+b[-1]*a[i] def f1(e): if sum(e) == 1: return QQ(1) if e[-1] == 1: return QQ(0) if vec.equal(d, e): return QQ(0) return J(e[:-1]) ncdt = W.flow_tree_formula(z, f1, quant=False) return (ncdt(list(d)+[1])-Z[i](d))/m*(-1)**(m) J = Invariant(f, Q) return J
[docs]def intAtt_from_crystals(Q): """Numerical integer attractor invariants obtained by recursion from the NCDT invariants (computed by counting crystals) and the flow tree formula. This algorithm works for any brane tiling. EXAMPLE:: sage: from msinvar import * sage: Q=BT_example(6); Q P2=C^3/(1,1,1): Quiver with 3 vertices, 9 arrows and potential with 6 terms sage: Q.prec([1,1,1]) sage: Q.intAtt_from_crystals().dict() {(0, 0, 1): 1, (0, 1, 0): 1, (1, 0, 0): 1, (1, 1, 1): -3} """ from msinvar.invariants import rat2int_num return rat2int_num(ratAtt_from_crystals(Q))