import random
from sage.all import Integer, Poset
from sage.combinat.partition import Partitions
from sage.combinat.posets.hasse_diagram import HasseDiagram
from sage.combinat.root_system.cartan_matrix import CartanMatrix
from sage.graphs.digraph import DiGraph
from sage.matrix.constructor import matrix
from sage.matrix.special import zero_matrix
from sage.misc.latex import LatexExpr
from sage.misc.latex_standalone import TikzPicture
from sage.modules.free_module_element import vector
from sage.structure.element import Element
from quiver import *
from quiver import Quiver as BaseQuiver
[docs]
def quiver_from_cartan_matrix(C):
r"""Returns the quiver :math:`Q` given by a Cartan matrix :math:`C`
INPUT:
- ``C`` -- a square matrix with entries in :math:`\mathbb{Z}`
OUTPUT: The quiver :math:`Q` given by a Cartan matrix :math:`C`
EXAMPLE::
sage: from quivercombinatorics import *
sage: C = [[2, -1, 0], [-1, 2, -2], [0, -2, 2]]
sage: quiver_from_cartan_matrix(C)
a quiver with 3 vertices and 3 arrows
"""
for i in range(len(C)):
C[i][i] /= 2
for j in range(i):
C[i][j] = 0
E = matrix.identity(len(C)) - matrix(C)
return Quiver(E)
[docs]
def random_quiver(vertices, max_arrows_per_edge):
r"""Returns a randomly generated quiver
INPUT:
- ``vertices`` -- number of vertices you want in the quiver, in :math:`\mathbb{N}_0`
- ``max_arrows_per_edge`` -- maximum of number of arrows you want in the quiver, in :math:`\mathbb{N}_0`
OUTPUT: A randomly generated quiver with the prescribed vertices and maximum number of edges
EXAMPLES::
sage: from quivercombinatorics import *
sage: random_quiver(3, 2)
a quiver with 3 vertices and 6 arrows
sage: from quivercombinatorics import *
sage: random_quiver(46, 25)
a quiver with 46 vertices and 26388 arrows
"""
A = [[random.randint(0, max_arrows_per_edge) for _ in range(vertices)] for _ in range(vertices)]
G = DiGraph(matrix(A))
return Quiver(A)
[docs]
def N_set(S, v):
r"""For a list of vectors :math:`S` in :math:`\mathbb{Z}Q_0`, returns all possible sums of vectors in :math:`S` as a list, up to the upper bound :math:`v`. Though not directly about quivers, this is a helper function to define :math:`\Sigma_\lambda`
INPUT:
- ``S`` -- a list of vectors :math:`S` in :math:`\mathbb{Z}Q_0`
- ``v`` -- vector in :math:`\mathbb{Z}Q_0`
OUTPUT: A list of all possible sums of vectors in :math:`S` as a list, up to the upper bound :math:`v`
EXAMPLE::
sage: from quivercombinatorics import *
sage: N_set([[0, 1]], [0, 3])
[(0, 1), (0, 2), (0, 3)]
"""
try:
v = vector(v)
except TypeError:
v = vector([v])
reachable = {tuple(a) for a in S}
changed = True
while changed:
new_reachable = set(reachable)
for a in reachable:
a_vec = vector(a)
for b in S:
b_vec = vector(b)
s = a_vec + b_vec
if all(s[i] <= v[i] for i in range(len(v))):
new_reachable.add(tuple(s))
changed = (new_reachable != reachable)
reachable = new_reachable
return [vector(a) for a in reachable]
[docs]
def vector_decomposition(x, S):
r"""For a list of vectors :math:`S` in :math:`\mathbb{Z}Q_0`, returns all possible sums of vectors in :math:`S` as a list that sum up to :math:`x`. This is a helper function in order to define CB-decompositions
INPUT:
- ``x`` -- a vector in :math:`\mathbb{Z}Q_0`
- ``S`` -- a list of vectors :math:`S` in :math:`\mathbb{Z}Q_0`
OUTPUT: All possible sums of vectors in :math:`S` as a list that sum up to :math:`x`
EXAMPLE::
sage: from quivercombinatorics import *
sage: vector_decomposition((4, 5), [(0, 1), (1, 0), (1, 1)])
[[[(0, 1), 1], [(1, 1), 4]],
[[(0, 1), 2], [(1, 0), 1], [(1, 1), 3]],
[[(0, 1), 3], [(1, 0), 2], [(1, 1), 2]],
[[(0, 1), 4], [(1, 0), 3], [(1, 1), 1]],
[[(0, 1), 5], [(1, 0), 4]]]
"""
x = vector(x)
if all(i == 0 for i in x):
return [[]]
if not S:
return []
s = vector(S[0])
decomps = vector_decomposition(x, S[1:])
n = 1
while all(i >= 0 for i in x - n*s):
current = vector_decomposition(x - n*s, S[1:])
current = [[[s,n]] + item for item in current]
decomps = decomps + current
n += 1
return sorted(decomps)
[docs]
def small_decomposition(v, n):
r"""For a vector :math:`v` in :math:`\mathbb{Z}Q_0`, it returns all possible partitions of the representation type :math:`[v,n]`
INPUT:
- ``v`` -- a vector in :math:`\mathbb{Z}Q_0`
- ``n`` -- a natural number in :math:`\mathbb{N}`
OUTPUT: All possible partitions of the representation type :math:`[v,n]`
EXAMPLE::
sage: from quivercombinatorics import *
sage: small_decomposition((1, 1), 3)
[[[(1, 1), 1], [(1, 1), 1], [(1, 1), 1]],
[[(1, 1), 2], [(1, 1), 1]],
[[(1, 1), 3]]]
"""
output = []
for partition in Partitions(n).list():
temp = []
for i in partition:
temp = temp + [[v,i]]
output = output + [temp]
return sorted(output)
[docs]
def ext_dimension_vector(tau):
"""For a representation type :math:`\\tau`, returns the associated dimension vector
INPUT:
- ``tau`` -- a representation type, i.e., a list whose elements are 2-tuples, the first element is in :math:`\mathbb{N}Q_0`, and the second is in :math:`\mathbb{N}`
OUTPUT: a vector in :math:`\mathbb{N}Q_0`
EXAMPLE::
sage: from quivercombinatorics import *
sage: tau = [[(1, 1), 2], [(1, 1), 1], [(1, 1), 1], [(1, 1), 1]]
sage: ext_dimension_vector(tau)
(2, 1, 1, 1)
"""
return vector([pair[1] for pair in tau])
[docs]
def D_map(m, tau):
r"""Evaluates a map :math:`D:\mathbb{Z}^k\to\mathbb{Z}^n` from dimension vectors of the :math:`\mathrm{ext}`-quiver to dimension vectors of the original quiver by :math:`D(m_1,\dots,m_k):=\sum_{i=1}^k m_i\beta^{(i)}`
INPUT:
- ``m`` -- a vector in :math:`\mathbb{Z}\operatorname{ext}(Q)_0`
- ``tau`` -- a representation type of the original quiver, i.e., a list whose elements are 2-tuples, the first element is in :math:`\mathbb{N}Q_0`, and the second is in :math:`\mathbb{N}`
OUTPUT: a vector in :math:`\mathbb{Z}Q_0`
EXAMPLE::
sage: from quivercombinatorics import *
sage: tau = [[(1, 1), 2], [(1, 1), 1], [(1, 1), 1], [(1, 1), 1]]
sage: D_map((1, 2, 8, -3), tau)
(8, 8)
"""
return sum(m[i]*vector(tau[i][0]) for i in range(len(m)))
[docs]
def D_lifting(tau, L):
r"""Applies ``D_map`` to a representation type of the :math:`\mathrm{ext}`-quiver
INPUT:
- ``L`` -- a representation type of the :math:`\mathrm{ext}`-quiver, i.e., a list whose elements are 2-tuples, the first element is in :math:`\mathbb{N}\mathrm{ext}(Q)_0`, and the second is in :math:`\mathbb{N}`
- ``tau`` -- a representation type, i.e., a list whose elements are 2-tuples, the first element is in :math:`\mathbb{N}Q_0`, and the second is in :math:`\mathbb{N}`
OUTPUT: a representation type of the :math:`\mathrm{ext}`-quiver
EXAMPLE::
sage: from quivercombinatorics import *
sage: tau = [[(1, 1), 2], [(1, 1), 1], [(1, 1), 1], [(1, 1), 1]]
sage: D_lifting([[(1, 2, 8, -3), 3]], tau)
[[(8, 8), 3]]
"""
if len(tau) > 1 and isinstance(tau[1], str):
return (sorted([[D_map(pair[0], L), pair[1]] for pair in tau[0]]), tau[1])
else:
return sorted([[D_map(pair[0], L), pair[1]] for pair in tau])
[docs]
def is_direct_successor(epsilon, rho):
r"""Verifies whether ``epsilon`` is the direct successor of ``rho``
INPUT:
- ``epsilon`` -- an element of :math:`\mathbb{N}^n`
- ``rho`` -- an element of :math:`\mathbb{N}^n`
OUTPUT: ``True`` if ``epsilon`` is the direct successor of ``rho``, and ``False`` otherwise
EXAMPLE::
sage: from quivercombinatorics import *
sage: rho = [((1, 0), 2), ((1, 0), 3)]
sage: epsilon = [((1, 0), 5)]
sage: is_direct_successor(epsilon, rho)
True
"""
epsilon_copy = list(epsilon)
rho_copy = list(rho)
for pair_e in list(epsilon_copy):
for pair_r in list(rho_copy):
if vector(pair_e[0]) == vector(pair_r[0]) and pair_e[1] == pair_r[1]:
epsilon_copy.remove(pair_e)
rho_copy.remove(pair_r)
break
if len(epsilon_copy) == 2 and len(rho_copy) == 1:
alpha_r, m_r = rho_copy[0]
gamma_r, p_r = epsilon_copy[0]
gamma_r1, p_r1 = epsilon_copy[1]
return (m_r == p_r == p_r1) and (gamma_r + gamma_r1 == alpha_r)
elif len(epsilon_copy) == 1 and len(rho_copy) == 2:
alpha_r, m_r = rho_copy[0]
alpha_r1, m_r1 = rho_copy[1]
gamma_r1, p_r1 = epsilon_copy[0]
return (m_r + m_r1 == p_r1) and (alpha_r == alpha_r1 == gamma_r1)
return False
class Quiver(BaseQuiver):
[docs]
def p_function(self, x):
r"""Outputs the function :math:`p(x) = 1 - \frac{1}{2}(x, x)`, where :math:`(x, x)` is the symmetrized Euler form. Note that :math:`p(x)\geq 0` if :math:`x` is a root, and :math:`p(x) = 0` if and only if :math:`x` is a real root
INPUT:
- ``x`` -- an element of :math:`\mathbb{Z}Q_0`
OUTPUT: :math:`1 - \frac{1}{2}(x, x)`
EXAMPLE::
sage: from quivercombinatorics import *
sage: Q = Quiver([[0, 1], [1, 0]])
sage: Q.p_function((2, 3))
0.0
"""
return 1 - 0.5 * self.symmetrized_euler_form(x, x)
[docs]
def R_lambda_plus(self, l, v):
r"""Returns a list of elements of :math:`R_\lambda^+`, the set of positive roots :math:`\alpha` with :math:`\alpha\cdot\lambda=0`, up to the upper bound :math:`v`
INPUT:
- ``l`` -- an element of :math:`\mathbb{Z}Q_0`
- ``v`` -- an element of :math:`\mathbb{N}Q_0`
OUTPUT: A list of elements of :math:`R_\lambda^+`, where ``l`` is :math:`\lambda`, the set of positive roots :math:`\alpha` with :math:`\alpha\cdot\lambda=0`, up to the upper bound :math:`v`
EXAMPLE::
sage: from quivercombinatorics import *
sage: Q = Quiver([[0, 1], [1, 0]])
sage: Q.R_lambda_plus((1, -1), (5, 5))
[(1, 1), (2, 2), (3, 3), (4, 4)]
"""
return list(
filter(
lambda a: self.is_root(a) and a.dot_product(vector(l)) == 0,
self.all_subdimension_vectors(v, proper=True, nonzero=True)
)
)
[docs]
def sigma_lambda(self, l, v):
r"""Returns a list of elements of :math:`\Sigma_\lambda`, up to the upper bound :math:`v`
INPUT:
- ``l`` -- an element of :math:`\mathbb{Z}Q_0`
- ``v`` -- an element of :math:`\mathbb{N}Q_0`
OUTPUT: A list of elements of :math:`\Sigma_\lambda`, where ``l`` is :math:`\lambda`, up to the upper bound :math:`v`
EXAMPLE::
sage: from quivercombinatorics import *
sage: Q = Quiver([[0, 1], [1, 0]])
sage: Q.sigma_lambda((1, -1), (5, 5))
[(1, 1)]
"""
v = self._coerce_dimension_vector(v)
l = self._coerce_vector(l)
NR_lambda_plus = N_set(self.R_lambda_plus(l, v), v)
return list(
filter(
lambda a: not any(self.symmetrized_euler_form(b, a - b) > -2 and all(x >= 0 for x in a - b) and any(x > 0 for x in a - b) for b in NR_lambda_plus),
NR_lambda_plus
)
)
[docs]
def all_representation_types(self, l, x):
r"""Returns a list of representation types of a quiver, with respect to :math:`x`
INPUT:
- ``l`` -- an element of :math:`\mathbb{Z}Q_0`
- ``x`` -- an element of :math:`\mathbb{N}Q_0`
OUTPUT: A list of representation types of a quiver, with respect to :math:`x`, and where ``l`` is :math:`\lambda`. Each representation type is stored as a list whose elements are 2-tuples, the first element is in :math:`\mathbb{N}Q_0`, and the second is in :math:`\mathbb{N}`
EXAMPLES::
sage: from quivercombinatorics import *
sage: Q = Quiver([[0, 1], [1, 0]])
sage: Q.all_representation_types((1, -1), (5, 5))
[[[(1, 1), 1], [(1, 1), 1], [(1, 1), 1], [(1, 1), 1], [(1, 1), 1]],
[[(1, 1), 1], [(1, 1), 1], [(1, 1), 1], [(1, 1), 2]],
[[(1, 1), 1], [(1, 1), 1], [(1, 1), 3]],
[[(1, 1), 1], [(1, 1), 2], [(1, 1), 2]],
[[(1, 1), 1], [(1, 1), 4]],
[[(1, 1), 2], [(1, 1), 3]],
[[(1, 1), 5]]]
sage: from quivercombinatorics import *
sage: C = [[2, -1, 0], [-1, 2, -2], [0, -2, 2]]
sage: Q = quiver_from_cartan_matrix(C)
sage: Q.all_representation_types((0, 0, 0), (2, 4, 3))
[[[(0, 0, 1), 1],
[(0, 1, 0), 2],
[(0, 1, 1), 1],
[(0, 1, 1), 1],
[(1, 0, 0), 2]],
[[(0, 0, 1), 1], [(0, 1, 0), 2], [(0, 1, 1), 2], [(1, 0, 0), 2]],
[[(0, 0, 1), 2], [(0, 1, 0), 3], [(0, 1, 1), 1], [(1, 0, 0), 2]],
[[(0, 0, 1), 3], [(0, 1, 0), 4], [(1, 0, 0), 2]],
[[(0, 1, 0), 1],
[(0, 1, 1), 1],
[(0, 1, 1), 1],
[(0, 1, 1), 1],
[(1, 0, 0), 2]],
[[(0, 1, 0), 1], [(0, 1, 1), 1], [(0, 1, 1), 2], [(1, 0, 0), 2]],
[[(0, 1, 0), 1], [(0, 1, 1), 3], [(1, 0, 0), 2]],
[[(2, 4, 3), 1]]]
"""
x = self._coerce_dimension_vector(x)
all_decomps = vector_decomposition(x, self.sigma_lambda(l, x))
all_reps = []
for decomp in all_decomps:
current = [[]]
for pair in decomp:
next_current = []
if self.is_imaginary_root(pair[0]):
expansions = small_decomposition(pair[0], pair[1])
for item in current:
for temp in expansions:
next_current.append(sorted(item + temp))
else:
for item in current:
next_current.append(sorted(item + [pair]))
current = next_current
all_reps.extend(current)
return sorted(all_reps)
[docs]
def symplectic_leaf_dimension(self, tau):
r"""Returns the dimension of the symplectic leaf corresponding to the representation type :math:`\tau`
INPUT:
- ``tau`` -- a representation type, i.e., a list whose elements are 2-tuples, the first element is in :math:`\mathbb{N}Q_0`, and the second is in :math:`\mathbb{N}`
OUTPUT: The corresponding symplectic leaf dimension, i.e., :math:`2p` applied to every element of :math:`\tau`
EXAMPLE::
sage: from quivercombinatorics import *
sage: Q = Quiver([[0, 1], [1, 0]])
sage: Q.symplectic_leaf_dimension([[(1, 1), 2], [(1, 1), 3]])
4
"""
d = 0
for pair in tau:
d += 2 - self.symmetrized_euler_form(pair[0], pair[0])
return d
[docs]
def CB_decomposition(self, l, x):
r"""Returns the CB decomposition, which is the representation type maximizing :math:`p`
INPUT:
- ``l`` -- an element of :math:`\mathbb{Z}Q_0`
- ``x`` -- an element of :math:`\mathbb{N}Q_0`
OUTPUT: The CB decomposition with respect to :math:`x`, where ``l`` is :math:`\lambda`
EXAMPLE::
sage: from quivercombinatorics import *
sage: Q = Quiver([[0, 1], [1, 0]])
sage: Q.CB_decomposition((1, -1), (5, 5))
[[(1, 1), 1], [(1, 1), 1], [(1, 1), 1], [(1, 1), 1], [(1, 1), 1]]
"""
all_reps = self.all_representation_types(l, x)
d_max = 0
max_rep = []
for tau in all_reps:
d = self.symplectic_leaf_dimension(tau)
if d >= d_max:
d_max = d
max_rep = tau
return max_rep
[docs]
def quiver_variety_dimension(self, l, x):
r"""Returns the dimension of the quiver variety
INPUT:
- ``l`` -- an element of :math:`\mathbb{Z}Q_0`
- ``x`` -- an element of :math:`\mathbb{N}Q_0`
OUTPUT: The dimension of the corresponding quiver variety
EXAMPLE::
sage: from quivercombinatorics import *
sage: Q = Quiver([[0, 1], [1, 0]])
sage: Q.quiver_variety_dimension((1, -1), (5, 5))
10
"""
return self.symplectic_leaf_dimension(self.CB_decomposition(l, x))
[docs]
def codimension_two_leaves(self, l, x):
r"""Returns the codimension 2 leaves of the quiver variety
INPUT:
- ``l`` -- an element of :math:`\mathbb{Z}Q_0`
- ``x`` -- an element of :math:`\mathbb{N}Q_0`
OUTPUT: The representation types of the codimension 2 leaves of the quiver variety
EXAMPLE::
sage: from quivercombinatorics import *
sage: Q = Quiver([[0, 1], [1, 0]])
sage: Q.codimension_two_leaves((0, 0), (5, 5))
[[[(0, 1), 1],
[(1, 0), 1],
[(1, 1), 1],
[(1, 1), 1],
[(1, 1), 1],
[(1, 1), 1]],
[[(1, 1), 1], [(1, 1), 1], [(1, 1), 1], [(1, 1), 2]]]
"""
d = self.quiver_variety_dimension(l, x)
all_reps = self.all_representation_types(l, x)
leaves = []
for tau in all_reps:
if self.symplectic_leaf_dimension(tau) + 2 == d:
leaves = leaves + [tau]
return leaves
[docs]
def ext_quiver(self, tau):
r"""Given a representation type, returns the ext-quiver
INPUT:
- ``tau`` -- a representation type, i.e., a list whose elements are 2-tuples, the first element is in :math:`\mathbb{N}Q_0`, and the second is in :math:`\mathbb{N}`
OUTPUT: The :math:`\mathrm{ext}`-quiver
EXAMPLE::
sage: from quivercombinatorics import *
sage: Q = Quiver([[0, 1], [1, 0]])
sage: tau = [[(1, 1), 2], [(1, 1), 1], [(1, 1), 1], [(1, 1), 1]]
sage: Q.ext_quiver(tau)
a quiver with 4 vertices and 4 arrows
"""
k = len(tau)
A = [[0 for i in range(k)] for j in range(k)]
for i in range(k):
A[i][i] = self.p_function(tau[i][0])
for j in range(i):
A[i][j] = -self.symmetrized_euler_form(tau[i][0], tau[j][0])
return Quiver(A)
[docs]
def is_minimal_imaginary_root(self, x):
r"""Tests whether a vector is a vector is a minimal imaginary root
INPUT:
- ``x`` -- a vector in :math:`\mathbb{Z}Q_0`
OUTPUT: ``True`` if ``x`` is a minimal imaginary root, and ``False`` otherwise
EXAMPLE::
sage: from quivercombinatorics import *
sage: Q = KroneckerQuiver(2)
sage: Q.is_minimal_imaginary_root((1, 1))
True
"""
return self.is_imaginary_root(x) and not any([self.is_root(b) and self.is_imaginary_root(b) for b in self.all_subdimension_vectors(x, proper=True, nonzero=True)])
[docs]
def all_minimal_imaginary_positive_roots(self, v):
r"""Returns all minimal imaginary positive roots up to a bound :math:`v`
INPUT:
- ``v`` -- a vector in :math:`\mathbb{N}Q_0`
OUTPUT: A list of all minimal imaginary positive roots up to a bound :math:`v`
EXAMPLE::
sage: from quivercombinatorics import *
sage: Q = KroneckerQuiver(2)
sage: Q.all_minimal_imaginary_positive_roots((3, 3))
[(1, 1)]
"""
return list(
filter(
lambda a: self.is_minimal_imaginary_root(a),
self.all_subdimension_vectors(v, proper=True, nonzero=True)
)
)
[docs]
def all_subminimal_representation_types(self, v):
r"""Returns all subminimal representation types up to a bound :math:`v`, and classifies them by affine Dynkin type, or :math:`a_{r-1}`, :math:`c_{g}`, :math:`m_{g}`
INPUT:
- ``v`` -- a vector in :math:`\mathbb{N}Q_0`
OUTPUT: A list of 2-tuples, the first element is a subminimal representation type up to a bound :math:`v`, the second element is its corresponding affine Dynkin type, or :math:`a_{r-1}`, :math:`c_{g}`, :math:`m_{g}`
EXAMPLES::
sage: from quivercombinatorics import *
sage: Q = CyclicQuiver(3)
sage: Q.all_subminimal_representation_types((3, 3, 3))
[([[(0, 0, 1), 2], [(0, 1, 0), 2], [(1, 0, 0), 2], [(1, 1, 1), 1]], 'A_{2}'),
([[(0, 0, 1), 1], [(0, 1, 0), 1], [(1, 0, 0), 1], [(1, 1, 1), 2]], 'A_{2}'),
([[(1, 1, 1), 3]], 'A_{2}')]
sage: from quivercombinatorics import *
sage: Q = LoopQuiver(3)
sage: Q.all_subminimal_representation_types((5))
[([[(1), 1], [(1), 4]], 'm_{3}'), ([[(1), 2], [(1), 3]], 'm_{3}')]
sage: from quivercombinatorics import *
sage: A = [[0, 1, 0, 3], [1, 0, 0, 0], [0, 0, 2, 0], [0, 0, 0, 0]]
sage: Q = Quiver(A)
sage: Q.all_subminimal_representation_types((1, 1, 4, 1))
[([[(0, 0, 0, 1), 1], [(0, 0, 1, 0), 4], [(1, 1, 0, 0), 1]], 'A_{1}'),
([[(0, 0, 1, 0), 4], [(0, 1, 0, 0), 1], [(1, 0, 0, 1), 1]], 'a_{2}'),
([[(0, 0, 0, 1), 1],
[(0, 0, 1, 0), 1],
[(0, 0, 1, 0), 3],
[(0, 1, 0, 0), 1],
[(1, 0, 0, 0), 1]],
'm_{2}'),
([[(0, 0, 0, 1), 1],
[(0, 0, 1, 0), 2],
[(0, 0, 1, 0), 2],
[(0, 1, 0, 0), 1],
[(1, 0, 0, 0), 1]],
'c_{2}')]
"""
rep_types_with_labels = []
try:
v = vector(v)
except TypeError:
v = vector([v])
n = len(v)
A = self.adjacency_matrix()
G = DiGraph(matrix(A))
G.remove_loops()
Q_without_loops = Quiver.from_digraph(G)
minimal_imaginary_positive_roots_up_to_v = list(
filter(
lambda a: Q_without_loops.is_minimal_imaginary_root(a),
Q_without_loops.all_subdimension_vectors(v, proper=False, nonzero=True)
)
)
for delta in minimal_imaginary_positive_roots_up_to_v:
k = 1
while all(v[i] - k * delta[i] >= 0 for i in range(n)):
rep_type = [[delta, k]]
for i in range(n):
if v[i] - k * delta[i]:
rep_type = rep_type + [[vector([1 if j == i else 0 for j in range(n)]), v[i] - k * delta[i]]]
supp = Q_without_loops.support(delta)
subquiver = Q_without_loops.full_subquiver(supp)
label = ""
if len(supp) == 2:
r = subquiver.adjacency_matrix()[0][1] + subquiver.adjacency_matrix()[1][0]
if r == 2:
label = f"A_{{1}}"
elif len(supp) >= 3 and CartanMatrix(subquiver.cartan_matrix()).is_affine():
subquiver_type = CartanMatrix(subquiver.cartan_matrix()).cartan_type()
label = f"{subquiver_type.type()}_{{{subquiver_type.rank() - 1}}}"
if label: rep_types_with_labels.append((sorted(rep_type),label))
k = k + 1
for i in range(n):
for j in range(i):
r = Q_without_loops.adjacency_matrix()[i][j] + Q_without_loops.adjacency_matrix()[j][i]
if r >= 3 and v[i] > 0 and v[j] > 0:
delta = vector([1 if l == i or l == j else 0 for l in range(n)])
k = 1
while v[i] >= k and v[j] >= k:
rep_type = [[delta, k]]
for l in range(n):
if v[l] - k * delta[l]:
rep_type = rep_type + [[vector([1 if s == l else 0 for s in range(n)]), v[l] - k * delta[l]]]
rep_types_with_labels.append((sorted(rep_type),f"a_{{{r-1}}}"))
k = k + 1
for i in range(n):
g = self.adjacency_matrix()[i][i]
if g >= 1:
for a in range(1, v[i] // 2 + 1):
b = v[i] - a
rep_type = [[vector([1 if j == i else 0 for j in range(n)]), a], [vector([1 if j == i else 0 for j in range(n)]), b]]
for k in range(n):
if k != i and v[i]:
rep_type = rep_type + [[vector([1 if j == k else 0 for j in range(n)]), v[k]]]
label = ""
if a == b:
label = f"c_{{{g}}}"
else:
label = f"m_{{{g}}}"
rep_types_with_labels.append((sorted(rep_type),label))
return rep_types_with_labels
[docs]
def minimal_degenerations(self, L):
r"""Returns all minimal degenerations of a a symplectic leaf, corresponding to the representation type :math:`L`
INPUT:
- ``L`` -- a representation type, i.e., a list whose elements are 2-tuples, the first element is in :math:`\mathbb{N}Q_0`, and the second is in :math:`\mathbb{N}`
OUTPUT: A list of representation types
EXAMPLES::
sage: from quivercombinatorics import *
sage: A = [[0, 1, 0, 3], [1, 0, 0, 0], [0, 0, 2, 0], [0, 0, 0, 0]]
sage: Q = Quiver(A)
sage: Q.minimal_degenerations([[(0, 0, 0, 1), 1], [(0, 0, 1, 0), 4], [(1, 1, 0, 0), 1]])
[([[(0, 0, 1, 0), 4], [(1, 1, 0, 1), 1]], '$a_{2}(1)$'),
([[(0, 0, 0, 1), 1], [(0, 0, 1, 0), 1], [(0, 0, 1, 0), 3], [(1, 1, 0, 0), 1]],
'$m_{2}(1)$'),
([[(0, 0, 0, 1), 1], [(0, 0, 1, 0), 2], [(0, 0, 1, 0), 2], [(1, 1, 0, 0), 1]],
'$c_{2}(1)$')]
"""
Qtilde = self.ext_quiver(L)
n = ext_dimension_vector(L)
rep_types = Qtilde.all_subminimal_representation_types(n)
lifted_rep_types_with_counts = []
for tau in rep_types:
rep_type = D_lifting(tau, L)
if lifted_rep_types_with_counts and lifted_rep_types_with_counts[-1][0] == rep_type:
lifted_rep_types_with_counts[-1][1] += 1
else:
lifted_rep_types_with_counts.append([rep_type, 1])
return [(sorted(tau[0][0]),fr'{tau[0][1]}({tau[1]})') for tau in lifted_rep_types_with_counts]
[docs]
def all_decompositions(self, v):
r"""Constructs all decompositions of a given dimension vector :math:`v`
INPUT:
- ``v`` -- an element of :math:`\mathbb{N}Q_0`
OUTPUT: A list of decompositions of `v`
EXAMPLE::
sage: from quivercombinatorics import *
sage: A = [[0, 1, 0, 3], [1, 0, 0, 0], [0, 0, 2, 0], [0, 0, 0, 0]]
sage: Q = Quiver(A)
sage: Q.all_decompositions((1, 0, 2, 0))
[[[(0, 0, 1, 0), 1], [(0, 0, 1, 0), 1], [(1, 0, 0, 0), 1]],
[[(0, 0, 1, 0), 1], [(1, 0, 1, 0), 1]],
[[(0, 0, 1, 0), 2], [(1, 0, 0, 0), 1]],
[[(0, 0, 2, 0), 1], [(1, 0, 0, 0), 1]],
[[(1, 0, 2, 0), 1]]]
"""
all_decomps = vector_decomposition(v, self.all_subdimension_vectors(v, proper=False, nonzero=True))
all_reps = []
for decomp in all_decomps:
current = [[]]
for pair in decomp:
current = [sorted(temp + item) for item in current for temp in small_decomposition(pair[0], pair[1])]
all_reps = all_reps + current
return sorted(all_reps)
[docs]
def get_Hasse_diagram(self, l, v, method=1):
r"""Applies Method 1 or Method 2 to obtain data for the Hasse diagram of minimal degenerations
INPUT:
- ``l`` -- an element of :math:`\mathbb{Z}Q_0`
- ``v`` -- an element of :math:`\mathbb{N}Q_0`
- ``method`` -- When set to ``1``, Method 1 will be used to obtain the Hasse diagram of minimal degenerations. When set to ``2``, Method 2 will be used to obtain the Hasse diagram of minimal degenerations.
OUTPUT:
- ``leaves_poset`` -- the underlying poset of the Hasse diagram of minimal degenerations
- ``all_leaves`` -- a list of all symplectic leaves, with respect to their representation types, so a list of representation types, i.e., a list whose elements are 2-tuples, the first element is in :math:`\mathbb{N}Q_0`, and the second is in :math:`\mathbb{N}`
- ``dimensions`` -- a list of dimensions of all the symplectic leaves, using ``symplectic_leaf_dimension``
- ``edge_labels`` -- a list of 3-tuples, the first two entries of each tuple define the edge, and the last entry is the edge label of the corresponding minimal degeneration, classified by ``all_subminimal_representation_types``
EXAMPLE::
sage: from quivercombinatorics import *
sage: C = [[2, -1, 0], [-1, 2, -2], [0, -2, 2]]
sage: Q = quiver_from_cartan_matrix(C)
sage: Q.get_Hasse_diagram((0, 0, 0), (2, 4, 3))
(Finite poset containing 8 elements,
[[[(0, 0, 1), 1],
[(0, 1, 0), 2],
[(0, 1, 1), 1],
[(0, 1, 1), 1],
[(1, 0, 0), 2]],
[[(0, 0, 1), 1], [(0, 1, 0), 2], [(0, 1, 1), 2], [(1, 0, 0), 2]],
[[(0, 0, 1), 2], [(0, 1, 0), 3], [(0, 1, 1), 1], [(1, 0, 0), 2]],
[[(0, 0, 1), 3], [(0, 1, 0), 4], [(1, 0, 0), 2]],
[[(0, 1, 0), 1],
[(0, 1, 1), 1],
[(0, 1, 1), 1],
[(0, 1, 1), 1],
[(1, 0, 0), 2]],
[[(0, 1, 0), 1], [(0, 1, 1), 1], [(0, 1, 1), 2], [(1, 0, 0), 2]],
[[(0, 1, 0), 1], [(0, 1, 1), 3], [(1, 0, 0), 2]],
[[(2, 4, 3), 1]]],
[4, 2, 2, 0, 6, 4, 2, 8],
[(0, 4, 'A_{1}(1)'),
(1, 5, 'A_{1}(1)'),
(1, 0, 'c_{1}(1)'),
(2, 0, 'A_{1}(1)'),
(2, 5, 'A_{1}(1)'),
(3, 2, 'A_{1}(1)'),
(3, 1, 'A_{1}(1)'),
(3, 6, 'A_{1}(1)'),
(4, 7, 'D_{4}(1)'),
(5, 4, 'c_{1}(1)'),
(6, 5, 'm_{1}(1)')])
"""
if method != 1 and method != 2:
raise ValueError("Method can only take values 1 or 2.")
if isinstance(l, (int, Integer)):
l = (l,)
if isinstance(v, (int, Integer)):
v = (v,)
all_leaves = self.all_representation_types(l, v)
dimensions = [self.symplectic_leaf_dimension(tau) for tau in all_leaves]
num_of_leaves = len(all_leaves)
edge_labels = []
relations = []
if method == 1:
elements = list(range(num_of_leaves))
for i in range(num_of_leaves):
for L in self.minimal_degenerations(all_leaves[i]):
j = all_leaves.index(L[0])
relations.append((elements[i], elements[j]))
edge_labels.append((elements[i], elements[j], L[1]))
leaves_poset = Poset((elements, relations), cover_relations = False)
elif method == 2:
decomps = self.all_decompositions(v)
indices = [decomps.index(x) for x in all_leaves]
num_of_decomps = len(decomps)
elements = range(num_of_leaves)
for i in range(num_of_decomps):
for j in range(i):
if is_direct_successor(decomps[i], decomps[j]):
relations.append((i, j))
elif is_direct_successor(decomps[j], decomps[i]):
relations.append((j, i))
P = Poset((elements, relations), cover_relations=False)
relabelling = {indices[i]: i for i in range(num_of_leaves)}
leaves_poset = P.subposet(indices).relabel(relabelling)
for i in range(num_of_leaves):
for tau, label in self.minimal_degenerations(all_leaves[i]):
if tau in all_leaves:
j = all_leaves.index(tau)
if leaves_poset.covers(i, j):
edge_labels.append((i, j, label))
return leaves_poset, all_leaves, dimensions, edge_labels
[docs]
def plot_Hasse_diagram(self, l, v, method=1, format="tikz", filename="output"):
r"""Applies Method 1 or Method 2 to plot the Hasse diagram for minimal degenerations
INPUT:
- ``l`` -- an element of :math:`\mathbb{Z}Q_0`
- ``v`` -- an element of :math:`\mathbb{N}Q_0`
- ``method`` -- When set to ``1``, Method 1 will be used to obtain the Hasse diagram of minimal degenerations. When set to ``2``, Method 2 will be used to obtain the Hasse diagram of minimal degenerations.
- ``format`` -- Takes values "tikz", "dot", and "sage", depending on whether you want a ``.tex`` file with *tikzpicture*, a ``.dot`` file, or a sage Hasse diagram output
- ``filename`` -- filename of output
OUTPUT:
- the Hasse diagram for minimal degenerations
EXAMPLE::
sage: C = [[2, -1, 0], [-1, 2, -2], [0, -2, 2]]
sage: Q = quiver_from_cartan_matrix(C)
sage: Q.plot_Hasse_diagram((0, 0, 0), (2, 4, 3))
\documentclass[tikz]{standalone}
\begin{document}
\begin{tikzpicture}[>=latex,line join=bevel,]
%%
\begin{scope}
\pgfsetstrokecolor{black}
---
77 lines not printed (5057 characters in total).
Use print to see the full content.
---
\draw (221.5bp,185.5bp) node {$c_{1}(1)$};
\draw [->] (6) ..controls (293.59bp,110.99bp) and (289.95bp,119.79bp) .. (284.0bp,126.0bp) .. controls (277.86bp,132.41bp) and (269.81bp,137.25bp) .. (5);
\draw (320.5bp,118.5bp) node {$m_{1}(1)$};
%
\end{tikzpicture}
\end{document}
"""
if format not in ["dot", "tikz", "sage"]:
raise ValueError("Possible formats are sage, dot and tikz.")
P, _, dimensions, edge_labels = self.get_Hasse_diagram(l, v, method)
H = P.hasse_diagram()
possible_dims = sorted(list(set(dimensions)))
if format == "sage":
heights = {
j: [
i
for i in range(len(dimensions))
if dimensions[i] == possible_dims[j]
]
for j in range(len(possible_dims))
}
pos = H.layout_acyclic_dummy(heights=heights)
H.set_pos(pos)
for i, j, label in edge_labels:
H.set_edge_label(i, j, label)
return H
elif format == "dot" or format == "tikz":
lines = []
lines.append('digraph G {')
lines.append(' graph [splines=true, overlap=false, rankdir="BT", d2toptions="-e utf8", labeldistance=0.5, nodesep = 0.1, ranksep = 0.2]')
lines.append(' node [shape=none]')
string = ' '
for dim in possible_dims:
lines.append(f' dim{dim} [texlbl="$\dim={dim}$"]')
string = string + f'dim{dim} -> '
string = string[:-3] + '[style=invis]'
lines.append(string)
for i in range(len(dimensions)):
lines.append(f' {i} [texlbl="$L_{{{i}}}$"]')
for dim in possible_dims:
string = ' {rank=same;'+f' dim{dim}'
for i in range(len(dimensions)):
if dimensions[i] == dim:
string += f'; {i}'
string += '}'
lines.append(string)
for i, j, label in edge_labels:
if H.has_edge(i, j):
lines.append(f' {i} -> {j} [texlbl="${label}$", label="{label}", lp="0,0"]')
lines.append('}')
s = '\n'.join(lines)
if format == "tikz":
import dot2tex
t = TikzPicture(dot2tex.dot2tex(s, format='tikz', figonly='True', prog='dot', rankdir='down'))
_ = t.tex(filename+".tex")
return t
else:
with open(filename+".dot", "w") as f:
f.write(s)
return s
BaseQuiver.p_function = p_function
BaseQuiver.R_lambda_plus = R_lambda_plus
BaseQuiver.N_set = N_set
BaseQuiver.sigma_lambda = sigma_lambda
BaseQuiver.vector_decomposition = vector_decomposition
BaseQuiver.small_decomposition = small_decomposition
BaseQuiver.all_representation_types = all_representation_types
BaseQuiver.symplectic_leaf_dimension = symplectic_leaf_dimension
BaseQuiver.CB_decomposition = CB_decomposition
BaseQuiver.quiver_variety_dimension = quiver_variety_dimension
BaseQuiver.codimension_two_leaves = codimension_two_leaves
BaseQuiver.ext_quiver = ext_quiver
BaseQuiver.ext_dimension_vector = ext_dimension_vector
BaseQuiver.is_minimal_imaginary_root = is_minimal_imaginary_root
BaseQuiver.all_minimal_imaginary_positive_roots = all_minimal_imaginary_positive_roots
BaseQuiver.all_subminimal_representation_types = all_subminimal_representation_types
BaseQuiver.D_map = D_map
BaseQuiver.D_lifting = D_lifting
BaseQuiver.minimal_degenerations = minimal_degenerations
BaseQuiver.all_decompositions = all_decompositions
BaseQuiver.is_direct_successor = is_direct_successor
BaseQuiver.get_Hasse_diagram = get_Hasse_diagram
BaseQuiver.plot_Hasse_diagram = plot_Hasse_diagram