passagemath-graphs 10.5.43__cp312-cp312-macosx_14_0_arm64.whl → 10.6.1rc2__cp312-cp312-macosx_14_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {passagemath_graphs-10.5.43.dist-info → passagemath_graphs-10.6.1rc2.dist-info}/METADATA +5 -6
- {passagemath_graphs-10.5.43.dist-info → passagemath_graphs-10.6.1rc2.dist-info}/RECORD +132 -130
- sage/combinat/abstract_tree.py +188 -17
- sage/combinat/cluster_algebra_quiver/interact.py +1 -2
- sage/combinat/cluster_algebra_quiver/mutation_type.py +518 -519
- sage/combinat/cluster_algebra_quiver/quiver.py +233 -205
- sage/combinat/designs/covering_design.py +2 -6
- sage/combinat/designs/database.py +11 -10
- sage/combinat/designs/designs_pyx.cpython-312-darwin.so +0 -0
- sage/combinat/designs/designs_pyx.pyx +2 -2
- sage/combinat/designs/evenly_distributed_sets.cpython-312-darwin.so +0 -0
- sage/combinat/designs/evenly_distributed_sets.pyx +4 -4
- sage/combinat/designs/gen_quadrangles_with_spread.cpython-312-darwin.so +0 -0
- sage/combinat/designs/latin_squares.py +53 -20
- sage/combinat/designs/orthogonal_arrays.py +2 -1
- sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-312-darwin.so +0 -0
- sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +22 -21
- sage/combinat/designs/resolvable_bibd.py +191 -157
- sage/combinat/designs/subhypergraph_search.cpython-312-darwin.so +0 -0
- sage/combinat/designs/subhypergraph_search.pyx +4 -4
- sage/combinat/designs/twographs.py +2 -2
- sage/combinat/finite_state_machine.py +6 -6
- sage/combinat/posets/bubble_shuffle.py +247 -0
- sage/combinat/posets/d_complete.py +3 -3
- sage/combinat/posets/elements.py +3 -3
- sage/combinat/posets/hasse_cython.cpython-312-darwin.so +0 -0
- sage/combinat/posets/hasse_cython.pyx +1 -1
- sage/combinat/posets/hasse_diagram.py +16 -22
- sage/combinat/posets/hochschild_lattice.py +158 -0
- sage/combinat/posets/incidence_algebras.py +14 -16
- sage/combinat/posets/lattices.py +51 -53
- sage/combinat/posets/linear_extension_iterator.cpython-312-darwin.so +0 -0
- sage/combinat/posets/linear_extensions.py +10 -12
- sage/combinat/posets/moebius_algebra.py +4 -4
- sage/combinat/posets/poset_examples.py +70 -23
- sage/combinat/posets/posets.py +294 -103
- sage/databases/knotinfo_db.py +2 -1
- sage/graphs/asteroidal_triples.cpython-312-darwin.so +0 -0
- sage/graphs/asteroidal_triples.pyx +24 -3
- sage/graphs/base/boost_graph.cpython-312-darwin.so +0 -0
- sage/graphs/base/boost_graph.pxd +3 -3
- sage/graphs/base/c_graph.cpython-312-darwin.so +0 -0
- sage/graphs/base/c_graph.pyx +1 -1
- sage/graphs/base/dense_graph.cpython-312-darwin.so +0 -0
- sage/graphs/base/dense_graph.pxd +5 -3
- sage/graphs/base/dense_graph.pyx +44 -0
- sage/graphs/base/graph_backends.cpython-312-darwin.so +0 -0
- sage/graphs/base/sparse_graph.cpython-312-darwin.so +0 -0
- sage/graphs/base/static_dense_graph.cpython-312-darwin.so +0 -0
- sage/graphs/base/static_sparse_backend.cpython-312-darwin.so +0 -0
- sage/graphs/base/static_sparse_backend.pyx +8 -5
- sage/graphs/base/static_sparse_graph.cpython-312-darwin.so +0 -0
- sage/graphs/base/static_sparse_graph.pyx +86 -15
- sage/graphs/bipartite_graph.py +59 -36
- sage/graphs/centrality.cpython-312-darwin.so +0 -0
- sage/graphs/centrality.pyx +82 -9
- sage/graphs/cographs.py +1 -1
- sage/graphs/comparability.cpython-312-darwin.so +0 -0
- sage/graphs/comparability.pyx +64 -26
- sage/graphs/connectivity.cpython-312-darwin.so +0 -0
- sage/graphs/convexity_properties.cpython-312-darwin.so +0 -0
- sage/graphs/convexity_properties.pyx +52 -9
- sage/graphs/digraph.py +439 -95
- sage/graphs/digraph_generators.py +174 -102
- sage/graphs/distances_all_pairs.cpython-312-darwin.so +0 -0
- sage/graphs/dot2tex_utils.py +1 -1
- sage/graphs/edge_connectivity.cpython-312-darwin.so +0 -0
- sage/graphs/generators/basic.py +1 -1
- sage/graphs/generators/distance_regular.cpython-312-darwin.so +0 -0
- sage/graphs/generators/distance_regular.pyx +1 -1
- sage/graphs/generators/families.py +37 -27
- sage/graphs/generators/random.py +2 -2
- sage/graphs/generators/smallgraphs.py +3 -3
- sage/graphs/generic_graph.py +558 -86
- sage/graphs/generic_graph_pyx.cpython-312-darwin.so +0 -0
- sage/graphs/generic_graph_pyx.pyx +58 -11
- sage/graphs/genus.cpython-312-darwin.so +0 -0
- sage/graphs/genus.pyx +3 -4
- sage/graphs/graph.py +291 -8
- sage/graphs/graph_coloring.cpython-312-darwin.so +0 -0
- sage/graphs/graph_database.py +67 -12
- sage/graphs/graph_decompositions/bandwidth.cpython-312-darwin.so +0 -0
- sage/graphs/graph_decompositions/clique_separators.cpython-312-darwin.so +0 -0
- sage/graphs/graph_decompositions/clique_separators.pyx +24 -3
- sage/graphs/graph_decompositions/cutwidth.cpython-312-darwin.so +0 -0
- sage/graphs/graph_decompositions/fast_digraph.cpython-312-darwin.so +0 -0
- sage/graphs/graph_decompositions/fast_digraph.pyx +1 -1
- sage/graphs/graph_decompositions/graph_products.cpython-312-darwin.so +0 -0
- sage/graphs/graph_decompositions/graph_products.pyx +67 -21
- sage/graphs/graph_decompositions/modular_decomposition.cpython-312-darwin.so +0 -0
- sage/graphs/graph_decompositions/slice_decomposition.cpython-312-darwin.so +0 -0
- sage/graphs/graph_decompositions/slice_decomposition.pyx +34 -8
- sage/graphs/graph_decompositions/tree_decomposition.cpython-312-darwin.so +0 -0
- sage/graphs/graph_decompositions/vertex_separation.cpython-312-darwin.so +0 -0
- sage/graphs/graph_generators.py +45 -32
- sage/graphs/graph_generators_pyx.cpython-312-darwin.so +0 -0
- sage/graphs/graph_generators_pyx.pyx +15 -15
- sage/graphs/graph_latex.py +1 -1
- sage/graphs/graph_list.py +52 -9
- sage/graphs/graph_plot.py +7 -0
- sage/graphs/hyperbolicity.cpython-312-darwin.so +0 -0
- sage/graphs/hyperbolicity.pyx +2 -0
- sage/graphs/independent_sets.cpython-312-darwin.so +0 -0
- sage/graphs/isoperimetric_inequalities.cpython-312-darwin.so +0 -0
- sage/graphs/isoperimetric_inequalities.pyx +42 -6
- sage/graphs/line_graph.cpython-312-darwin.so +0 -0
- sage/graphs/line_graph.pyx +153 -37
- sage/graphs/matching_covered_graph.py +84 -60
- sage/graphs/orientations.py +3 -18
- sage/graphs/path_enumeration.cpython-312-darwin.so +0 -0
- sage/graphs/path_enumeration.pyx +2 -2
- sage/graphs/spanning_tree.cpython-312-darwin.so +0 -0
- sage/graphs/strongly_regular_db.cpython-312-darwin.so +0 -0
- sage/graphs/strongly_regular_db.pyx +15 -15
- sage/graphs/traversals.cpython-312-darwin.so +0 -0
- sage/graphs/traversals.pyx +13 -12
- sage/graphs/trees.cpython-312-darwin.so +0 -0
- sage/graphs/tutte_polynomial.py +1 -1
- sage/graphs/views.cpython-312-darwin.so +0 -0
- sage/graphs/weakly_chordal.cpython-312-darwin.so +0 -0
- sage/graphs/weakly_chordal.pyx +50 -8
- sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-312-darwin.so +0 -0
- sage/knots/free_knotinfo_monoid.py +3 -3
- sage/knots/knotinfo.py +102 -82
- sage/knots/link.py +72 -39
- sage/topology/cubical_complex.py +4 -5
- sage/topology/delta_complex.py +4 -4
- sage/topology/simplicial_complex.py +0 -1
- sage/topology/simplicial_complex_catalog.py +6 -0
- sage/topology/simplicial_complex_examples.py +4 -16
- {passagemath_graphs-10.5.43.dist-info → passagemath_graphs-10.6.1rc2.dist-info}/WHEEL +0 -0
- {passagemath_graphs-10.5.43.dist-info → passagemath_graphs-10.6.1rc2.dist-info}/top_level.txt +0 -0
@@ -21,11 +21,11 @@ AUTHORS:
|
|
21
21
|
# Distributed under the terms of the GNU General Public License (GPL)
|
22
22
|
# https://www.gnu.org/licenses/
|
23
23
|
# ****************************************************************************
|
24
|
-
|
24
|
+
from copy import copy
|
25
25
|
from pathlib import Path
|
26
26
|
import pickle
|
27
|
+
from typing import Any, Iterator
|
27
28
|
|
28
|
-
from copy import copy
|
29
29
|
|
30
30
|
from sage.misc.cachefunc import cached_function
|
31
31
|
from sage.misc.flatten import flatten
|
@@ -34,7 +34,7 @@ from sage.combinat.combination import Combinations
|
|
34
34
|
from sage.combinat.cluster_algebra_quiver.quiver_mutation_type import QuiverMutationType
|
35
35
|
|
36
36
|
|
37
|
-
def is_mutation_finite(M, nr_of_checks=None):
|
37
|
+
def is_mutation_finite(M, nr_of_checks=None) -> tuple[bool, Any]:
|
38
38
|
r"""
|
39
39
|
Use a non-deterministic method by random mutations in various
|
40
40
|
directions. Can result in a wrong answer.
|
@@ -50,20 +50,20 @@ def is_mutation_finite(M, nr_of_checks=None):
|
|
50
50
|
|
51
51
|
ALGORITHM:
|
52
52
|
|
53
|
-
A quiver is mutation infinite if and only if every edge label (a
|
53
|
+
A quiver is mutation infinite if and only if every edge label (a, -b) satisfy a*b > 4.
|
54
54
|
Thus, we apply random mutations in random directions
|
55
55
|
|
56
56
|
EXAMPLES::
|
57
57
|
|
58
58
|
sage: from sage.combinat.cluster_algebra_quiver.mutation_type import is_mutation_finite
|
59
59
|
|
60
|
-
sage: Q = ClusterQuiver(['A',10]) # needs sage.modules
|
60
|
+
sage: Q = ClusterQuiver(['A', 10]) # needs sage.modules
|
61
61
|
sage: M = Q.b_matrix() # needs sage.modules
|
62
62
|
sage: is_mutation_finite(M) # needs sage.modules
|
63
63
|
(True, None)
|
64
64
|
|
65
65
|
sage: # needs sage.modules
|
66
|
-
sage: Q = ClusterQuiver([(0,1),(1,2),(2,3),(3,4),(4,5),(5,6),(6,7),(7,8),(2,9)])
|
66
|
+
sage: Q = ClusterQuiver([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (2, 9)])
|
67
67
|
sage: M = Q.b_matrix()
|
68
68
|
sage: is_mutation_finite(M) # random
|
69
69
|
(False, [9, 6, 9, 8, 9, 4, 0, 4, 5, 2, 1, 0, 1, 0, 7, 1, 9, 2, 5, 7, 8, 6, 3, 0, 2, 5, 4, 2, 6, 9, 2, 7, 3, 5, 3, 7, 9, 5, 9, 0, 2, 7, 9, 2, 4, 2, 1, 6, 9, 4, 3, 5, 0, 8, 2, 9, 5, 3, 7, 0, 1, 8, 3, 7, 2, 7, 3, 4, 8, 0, 4, 9, 5, 2, 8, 4, 8, 1, 7, 8, 9, 1, 5, 0, 8, 7, 4, 8, 9, 8, 0, 7, 4, 7, 1, 2, 8, 6, 1, 3, 9, 3, 9, 1, 3, 2, 4, 9, 5, 1, 2, 9, 4, 8, 5, 3, 4, 6, 8, 9, 2, 5, 9, 4, 6, 2, 1, 4, 9, 6, 0, 9, 8, 0, 4, 7, 9, 2, 1, 6])
|
@@ -97,7 +97,7 @@ def is_mutation_finite(M, nr_of_checks=None):
|
|
97
97
|
return True, None
|
98
98
|
|
99
99
|
|
100
|
-
def _triangles(dg):
|
100
|
+
def _triangles(dg) -> list[tuple[list, bool]]:
|
101
101
|
"""
|
102
102
|
Return a list of all oriented triangles in the digraph ``dg``.
|
103
103
|
|
@@ -105,53 +105,50 @@ def _triangles(dg):
|
|
105
105
|
|
106
106
|
sage: # needs sage.modules
|
107
107
|
sage: from sage.combinat.cluster_algebra_quiver.mutation_type import _triangles
|
108
|
-
sage: Q = ClusterQuiver(['A',3])
|
108
|
+
sage: Q = ClusterQuiver(['A', 3])
|
109
109
|
sage: _triangles(Q.digraph())
|
110
110
|
[]
|
111
|
-
sage: Q.mutate([0,1])
|
111
|
+
sage: Q.mutate([0, 1])
|
112
112
|
sage: _triangles(Q.digraph())
|
113
|
-
[([(
|
114
|
-
sage: Q2 = ClusterQuiver(['A',[1,2],1])
|
113
|
+
[([(0, 1), (1, 2), (2, 0)], True)]
|
114
|
+
sage: Q2 = ClusterQuiver(['A', [1, 2], 1])
|
115
115
|
sage: _triangles(Q2.digraph())
|
116
|
-
[([(1,
|
116
|
+
[([(1, 0), (1, 2), (2, 0)], False)]
|
117
117
|
sage: Q2.mutate(2)
|
118
118
|
sage: _triangles(Q2.digraph())
|
119
|
-
[([(1, 0), (
|
119
|
+
[([(1, 0), (2, 1), (0, 2)], True)]
|
120
120
|
"""
|
121
|
-
|
122
|
-
V = list(dg)
|
121
|
+
from itertools import combinations
|
123
122
|
trians = []
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
flat_trians.append( flat_trian )
|
150
|
-
trians.append( ( [(v1,v),(v1,v2),(v,v2)], False ) )
|
123
|
+
for x in dg.vertices(sort=True):
|
124
|
+
nx = sorted(y for y in dg.neighbor_iterator(x) if x < y)
|
125
|
+
for y, z in combinations(nx, 2):
|
126
|
+
if dg.has_edge(y, z):
|
127
|
+
if dg.has_edge(x, y):
|
128
|
+
if dg.has_edge(z, x):
|
129
|
+
trians.append(([(x, y), (y, z), (z, x)], True))
|
130
|
+
else:
|
131
|
+
trians.append(([(x, y), (y, z), (x, z)], False))
|
132
|
+
else:
|
133
|
+
if dg.has_edge(z, x):
|
134
|
+
trians.append(([(y, x), (y, z), (z, x)], False))
|
135
|
+
else:
|
136
|
+
trians.append(([(y, x), (y, z), (x, z)], False))
|
137
|
+
elif dg.has_edge(z, y):
|
138
|
+
if dg.has_edge(x, y):
|
139
|
+
if dg.has_edge(z, x):
|
140
|
+
trians.append(([(x, y), (z, y), (z, x)], False))
|
141
|
+
else:
|
142
|
+
trians.append(([(x, y), (z, y), (x, z)], False))
|
143
|
+
else:
|
144
|
+
if dg.has_edge(z, x):
|
145
|
+
trians.append(([(y, x), (z, y), (z, x)], False))
|
146
|
+
else:
|
147
|
+
trians.append(([(y, x), (z, y), (x, z)], True))
|
151
148
|
return trians
|
152
149
|
|
153
150
|
|
154
|
-
def _all_induced_cycles_iter(dg):
|
151
|
+
def _all_induced_cycles_iter(dg) -> Iterator[tuple]:
|
155
152
|
"""
|
156
153
|
Return an iterator for all induced oriented cycles of length
|
157
154
|
greater than or equal to 4 in the digraph ``dg``.
|
@@ -160,14 +157,14 @@ def _all_induced_cycles_iter(dg):
|
|
160
157
|
|
161
158
|
sage: # needs sage.modules
|
162
159
|
sage: from sage.combinat.cluster_algebra_quiver.mutation_type import _all_induced_cycles_iter
|
163
|
-
sage: Q = ClusterQuiver(['A',[6,0],1]); Q
|
160
|
+
sage: Q = ClusterQuiver(['A', [6, 0], 1]); Q
|
164
161
|
Quiver on 6 vertices of type ['D', 6]
|
165
162
|
sage: next(_all_induced_cycles_iter(Q.digraph()))
|
166
163
|
([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 0)], True)
|
167
164
|
sage: Q.mutate(0)
|
168
165
|
sage: next(_all_induced_cycles_iter(Q.digraph()))
|
169
166
|
([(1, 2), (2, 3), (3, 4), (4, 5), (5, 1)], True)
|
170
|
-
sage: Q2 = ClusterQuiver(['A',[2,3],1])
|
167
|
+
sage: Q2 = ClusterQuiver(['A', [2, 3], 1])
|
171
168
|
sage: next(_all_induced_cycles_iter(Q2.digraph()))
|
172
169
|
([(1, 0), (1, 2), (3, 2), (3, 4), (4, 0)], False)
|
173
170
|
"""
|
@@ -175,7 +172,7 @@ def _all_induced_cycles_iter(dg):
|
|
175
172
|
E = dg_new.edges(sort=True)
|
176
173
|
for v1, v2, label in E:
|
177
174
|
dg_new.add_edge((v2, v1, label))
|
178
|
-
induced_sets = []
|
175
|
+
induced_sets: list[set] = []
|
179
176
|
cycle_iter = dg_new.all_cycles_iterator(simple=True)
|
180
177
|
for cycle in cycle_iter:
|
181
178
|
if len(cycle) > 3:
|
@@ -195,7 +192,7 @@ def _all_induced_cycles_iter(dg):
|
|
195
192
|
# a debug function
|
196
193
|
|
197
194
|
|
198
|
-
def _false_return(s=False):
|
195
|
+
def _false_return(s=False) -> str:
|
199
196
|
"""
|
200
197
|
Return 'unknown'.
|
201
198
|
|
@@ -213,12 +210,12 @@ def _false_return(s=False):
|
|
213
210
|
return 'unknown'
|
214
211
|
|
215
212
|
|
216
|
-
def _reset_dg(dg, vertices, dict_in_out, del_vertices):
|
213
|
+
def _reset_dg(dg, vertices, dict_in_out, del_vertices) -> None:
|
217
214
|
"""
|
218
|
-
Delete the specified vertices (del_vertices) from the DiGraph dg
|
219
|
-
and the lists vertices and dict_in_out
|
215
|
+
Delete the specified vertices (``del_vertices``) from the DiGraph ``dg``,
|
216
|
+
and the lists ``vertices`` and ``dict_in_out``.
|
220
217
|
|
221
|
-
Note that vertices and dict_in_out are the vertices of dg and a
|
218
|
+
Note that ``vertices`` and ``dict_in_out`` are the vertices of ``dg`` and a
|
222
219
|
dictionary of in- and out-degrees that depend on the digraph
|
223
220
|
``dg`` but they are passed through as arguments so the function
|
224
221
|
can change their values.
|
@@ -227,12 +224,12 @@ def _reset_dg(dg, vertices, dict_in_out, del_vertices):
|
|
227
224
|
|
228
225
|
sage: # needs sage.modules
|
229
226
|
sage: from sage.combinat.cluster_algebra_quiver.mutation_type import _reset_dg
|
230
|
-
sage: dg = ClusterQuiver(['A',[2,2],1]).digraph(); dg
|
227
|
+
sage: dg = ClusterQuiver(['A', [2, 2], 1]).digraph(); dg
|
231
228
|
Digraph on 4 vertices
|
232
229
|
sage: vertices = list(dg)
|
233
230
|
sage: dict_in_out = {}
|
234
231
|
sage: for v in vertices: dict_in_out[v] = (dg.in_degree(v), dg.out_degree(v), dg.degree(v))
|
235
|
-
sage: _reset_dg(dg,vertices, dict_in_out, [1])
|
232
|
+
sage: _reset_dg(dg, vertices, dict_in_out, [1])
|
236
233
|
sage: dg
|
237
234
|
Digraph on 3 vertices
|
238
235
|
sage: vertices
|
@@ -274,16 +271,16 @@ def _check_special_BC_cases(dg, n, check_letter_list, check_twist_list,
|
|
274
271
|
sage: dg = DiGraph(1)
|
275
272
|
sage: _check_special_BC_cases(dg, 3, ['BC'], [1], ['A'], [[0]])
|
276
273
|
['BC', 2, 1]
|
277
|
-
sage: dg = DiGraph(2); dg.add_edge([0,1])
|
274
|
+
sage: dg = DiGraph(2); dg.add_edge([0, 1])
|
278
275
|
sage: _check_special_BC_cases(dg, 4, ['BC'], [1], ['A'], [[0]])
|
279
276
|
['BC', 3, 1]
|
280
|
-
sage: dg = DiGraph(2); dg.add_edge([0,1])
|
281
|
-
sage: _check_special_BC_cases(dg, 4, ['BB'], [1], ['A'], [[0,1]])
|
277
|
+
sage: dg = DiGraph(2); dg.add_edge([0, 1])
|
278
|
+
sage: _check_special_BC_cases(dg, 4, ['BB'], [1], ['A'], [[0, 1]])
|
282
279
|
['BB', 3, 1]
|
283
|
-
sage: _check_special_BC_cases(dg, 4, ['C', 'CD'], [None, None], ['A', 'D'], [
|
280
|
+
sage: _check_special_BC_cases(dg, 4, ['C', 'CD'], [None, None], ['A', 'D'], [[], [0]])
|
284
281
|
['C', 4]
|
285
282
|
sage: dg.add_edges([[1, 2], [1, 3]])
|
286
|
-
sage: _check_special_BC_cases(dg, 4, ['C', 'CD'], [None, None], ['A', 'D'], [
|
283
|
+
sage: _check_special_BC_cases(dg, 4, ['C', 'CD'], [None, None], ['A', 'D'], [[], [0]])
|
287
284
|
['CD', 3, 1]
|
288
285
|
"""
|
289
286
|
# if dg is not connected, mutation type is not recognized.
|
@@ -291,7 +288,7 @@ def _check_special_BC_cases(dg, n, check_letter_list, check_twist_list,
|
|
291
288
|
return 'unknown'
|
292
289
|
# divides into cases depending on whether or not a list 'conn_vert_list' of connecting vertices is given.
|
293
290
|
if conn_vert_list:
|
294
|
-
mut_type = _connected_mutation_type_AAtildeD(
|
291
|
+
mut_type = _connected_mutation_type_AAtildeD(dg, ret_conn_vert=True)
|
295
292
|
# when 'conn_vert_list' is given, the output of _connected_mutation_type_AAtildeD is
|
296
293
|
# either 'unknown' or a pair (mut_type, conn_verts). Then, it is tested if the vertices can be glued together as desired.
|
297
294
|
if not mut_type == 'unknown':
|
@@ -299,11 +296,11 @@ def _check_special_BC_cases(dg, n, check_letter_list, check_twist_list,
|
|
299
296
|
else:
|
300
297
|
# when conn_vert_list == False, the output of _connected_mutation_type _AAtildeD is simply 'unknown' or the mutation type.
|
301
298
|
# no 'connecting vertices' need to be computed.
|
302
|
-
mut_type = _connected_mutation_type_AAtildeD(
|
299
|
+
mut_type = _connected_mutation_type_AAtildeD(dg, ret_conn_vert=False)
|
303
300
|
conn_verts = []
|
304
301
|
# when the mutation type is recognized, program now tries more specifically to figure out 'letter' and 'twist'
|
305
302
|
if not mut_type == 'unknown':
|
306
|
-
for i in range(
|
303
|
+
for i in range(len(check_letter_list)):
|
307
304
|
check_letter = check_letter_list[i]
|
308
305
|
check_twist = check_twist_list[i]
|
309
306
|
hope_letter = hope_letter_list[i]
|
@@ -311,7 +308,7 @@ def _check_special_BC_cases(dg, n, check_letter_list, check_twist_list,
|
|
311
308
|
conn_vert = set(conn_vert_list[i])
|
312
309
|
else:
|
313
310
|
conn_vert = set()
|
314
|
-
# Now, tries to connect up the quiver components (keeping in mind ['D',3] - ['A',3] equivalence)
|
311
|
+
# Now, tries to connect up the quiver components (keeping in mind ['D', 3] - ['A', 3] equivalence)
|
315
312
|
if hope_letter == 'D' and mut_type._letter == 'A' and mut_type._rank == 3 and not mut_type._twist:
|
316
313
|
hope_letter = 'A'
|
317
314
|
if conn_vert_list:
|
@@ -335,74 +332,74 @@ def _connected_mutation_type(dg):
|
|
335
332
|
sage: # needs sage.modules
|
336
333
|
sage: from sage.combinat.cluster_algebra_quiver.mutation_type import _connected_mutation_type
|
337
334
|
sage: from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver
|
338
|
-
sage: dg = ClusterQuiver(['A',3]).digraph(); _connected_mutation_type(
|
335
|
+
sage: dg = ClusterQuiver(['A', 3]).digraph(); _connected_mutation_type(dg)
|
339
336
|
['A', 3]
|
340
|
-
sage: dg = ClusterQuiver(['D',7]).digraph(); _connected_mutation_type(
|
337
|
+
sage: dg = ClusterQuiver(['D', 7]).digraph(); _connected_mutation_type(dg)
|
341
338
|
['D', 7]
|
342
|
-
sage: dg = ClusterQuiver(['BC',4,1]).digraph(); _connected_mutation_type(
|
339
|
+
sage: dg = ClusterQuiver(['BC', 4, 1]).digraph(); _connected_mutation_type(dg)
|
343
340
|
['BC', 4, 1]
|
344
341
|
"""
|
345
|
-
dg = DiGraph(
|
342
|
+
dg = DiGraph(dg)
|
346
343
|
# defining some shorthands
|
347
344
|
n = dg.order()
|
348
345
|
edges = dg.edges(sort=True)
|
349
346
|
vertices = list(dg)
|
350
|
-
# initializing lists of the edges with labels (2
|
347
|
+
# initializing lists of the edges with labels (2, -1) or (1, -2); (4, -1) or (1, -4); or (2, -2), respectively
|
351
348
|
exc_labels = []
|
352
349
|
exc_labels41 = []
|
353
350
|
double_edges = []
|
354
351
|
# letter = None
|
355
352
|
|
356
353
|
# replacing higher labels by multiple edges. Multiple edges and acyclic is a sign that quiver is infinite mutation type with the exception of A_tilde where there might be one multiple edge with multiplicity 2. Multiple edges is at least a sign that the quiver is of 'undetermined finite mutation type'.
|
357
|
-
dg.allow_multiple_edges(
|
354
|
+
dg.allow_multiple_edges(True)
|
358
355
|
for edge in edges:
|
359
356
|
label = edge[2]
|
360
|
-
if label not in [(1
|
357
|
+
if label not in [(1, -1), (2, -2), (1, -2), (2, -1), (4, -1), (1, -4)]:
|
361
358
|
# _false_return(i) is a simple function that simply returns 'unknown'. For debugging purposes, it
|
362
359
|
# can also output 'DEBUG: error i' if desired.
|
363
360
|
# this command is used many times in this code, something times without the argument i.
|
364
361
|
return _false_return(2)
|
365
|
-
elif label == (2
|
366
|
-
dg.set_edge_label(
|
367
|
-
dg.add_edge(
|
368
|
-
double_edges.append(
|
369
|
-
if len(
|
362
|
+
elif label == (2, -2):
|
363
|
+
dg.set_edge_label(edge[0], edge[1], 1)
|
364
|
+
dg.add_edge(edge[0], edge[1], 1)
|
365
|
+
double_edges.append(edge)
|
366
|
+
if len(double_edges) > 1:
|
370
367
|
return _false_return()
|
371
|
-
elif label == (1
|
372
|
-
dg.set_edge_label(
|
373
|
-
elif label in [(2
|
374
|
-
exc_labels.append(
|
375
|
-
elif label in [(1
|
376
|
-
exc_labels41.append(
|
368
|
+
elif label == (1, -1):
|
369
|
+
dg.set_edge_label(edge[0], edge[1], 1)
|
370
|
+
elif label in [(2, -1), (1, -2)]:
|
371
|
+
exc_labels.append(edge)
|
372
|
+
elif label in [(1, -4), (4, -1)]:
|
373
|
+
exc_labels41.append(edge)
|
377
374
|
|
378
375
|
# creating a dictionary of in-, out- and total degrees
|
379
376
|
dict_in_out = {}
|
380
377
|
for v in vertices:
|
381
378
|
dict_in_out[v] = (dg.in_degree(v), dg.out_degree(v), dg.degree(v))
|
382
379
|
|
383
|
-
if len(
|
380
|
+
if len(exc_labels) + len(exc_labels41) + len(double_edges) > 4:
|
384
381
|
return _false_return()
|
385
382
|
|
386
|
-
# test for the labels (4
|
383
|
+
# test for the labels (4, -1) and (1, -4) which can only appear in affine type BC
|
387
384
|
if exc_labels41:
|
388
|
-
# tests a two-vertex quiver to see if it is of type ['BC',1,1]
|
385
|
+
# tests a two-vertex quiver to see if it is of type ['BC', 1, 1]
|
389
386
|
if len(exc_labels41) == 1 and dict_in_out[exc_labels41[0][0]][2] == dict_in_out[exc_labels41[0][1]][2] == 1:
|
390
|
-
return QuiverMutationType(['BC',1,1])
|
391
|
-
# test if quiver contains a triangle T with edges [
|
387
|
+
return QuiverMutationType(['BC', 1, 1])
|
388
|
+
# test if quiver contains a triangle T with edges [(0, 1, (2, -1)), (2, 0, (2, -1)), (1, 2, (1, -4))] or [(0, 1, (1, -2)), (2, 0, (1, -2)), (1, 2, (4, -1))].
|
392
389
|
if len(exc_labels41) == 1 and len(exc_labels) == 2:
|
393
|
-
bool2 = exc_labels41[0][2] == (4
|
394
|
-
bool3 = exc_labels41[0][2] == (1
|
390
|
+
bool2 = exc_labels41[0][2] == (4, -1) and exc_labels[0][2] == exc_labels[1][2] == (1, -2)
|
391
|
+
bool3 = exc_labels41[0][2] == (1, -4) and exc_labels[0][2] == exc_labels[1][2] == (2, -1)
|
395
392
|
if bool2 or bool3:
|
396
393
|
v1, v2, label = exc_labels41[0]
|
397
394
|
label1, label2 = exc_labels
|
398
|
-
# delete the two vertices associated to the edge with label (1
|
395
|
+
# delete the two vertices associated to the edge with label (1, -4) or (4, -1) and test if the rest of the quiver is of type A.
|
399
396
|
# the third vertex of the triangle T should be a connecting_vertex.
|
400
397
|
if label1[1] == label2[0] and label2[1] == v1 and v2 == label1[0] and dict_in_out[v1][2] == dict_in_out[v2][2] == 2:
|
401
|
-
_reset_dg(
|
402
|
-
return _check_special_BC_cases(
|
398
|
+
_reset_dg(dg, vertices, dict_in_out, [v1, v2])
|
399
|
+
return _check_special_BC_cases(dg, n, ['BC'], [1], ['A'], [[label1[1]]])
|
403
400
|
elif label1[0] == label2[1] and label1[1] == v1 and v2 == label2[0] and dict_in_out[v1][2] == dict_in_out[v2][2] == 2:
|
404
|
-
_reset_dg(
|
405
|
-
return _check_special_BC_cases(
|
401
|
+
_reset_dg(dg, vertices, dict_in_out, [v1, v2])
|
402
|
+
return _check_special_BC_cases(dg, n, ['BC'], [1], ['A'], [[label1[0]]])
|
406
403
|
else:
|
407
404
|
return _false_return()
|
408
405
|
else:
|
@@ -410,14 +407,14 @@ def _connected_mutation_type(dg):
|
|
410
407
|
else:
|
411
408
|
return _false_return()
|
412
409
|
|
413
|
-
# the program now performs further tests in the case that there are no edges of type (1
|
410
|
+
# the program now performs further tests in the case that there are no edges of type (1, -4) nor (4, -1)
|
414
411
|
|
415
412
|
# first test for affine type C: if there are 4 exceptional labels, test if both belong to triangles with leaves
|
416
|
-
if len(
|
413
|
+
if len(exc_labels) == 4:
|
417
414
|
exc_labels12 = [labl for labl in exc_labels if labl[2] == (1, -2)]
|
418
415
|
exc_labels21 = [labl for labl in exc_labels if labl[2] == (2, -1)]
|
419
416
|
# check that we have two labels of one kind and one label of the other
|
420
|
-
if len(
|
417
|
+
if len(exc_labels12) != 2 or len(exc_labels21) != 2:
|
421
418
|
return _false_return()
|
422
419
|
|
423
420
|
label121 = exc_labels12[0]
|
@@ -447,166 +444,166 @@ def _connected_mutation_type(dg):
|
|
447
444
|
else:
|
448
445
|
return _false_return()
|
449
446
|
|
450
|
-
# tests for which configuration the two (1
|
451
|
-
bool1 = dg.has_edge(label121[1],label211[0],1) and dict_in_out[label211[1]][0] == dict_in_out[label211[1]][1] == 1
|
452
|
-
bool2 = dg.has_edge(label122[1],label212[0],1) and dict_in_out[label212[1]][0] == dict_in_out[label212[1]][1] == 1
|
453
|
-
bool12 = not (
|
454
|
-
bool3 = dg.has_edge(label211[1],label121[0],1) and dict_in_out[label121[1]][0] == dict_in_out[label121[1]][1] == 1
|
455
|
-
bool4 = dg.has_edge(label212[1],label122[0],1) and dict_in_out[label122[1]][0] == dict_in_out[label122[1]][1] == 1
|
456
|
-
bool34 = not (
|
457
|
-
bool5 = dg.has_edge(label211[1],label121[0],1) and dict_in_out[label121[1]][0] == dict_in_out[label121[1]][1] == 1
|
458
|
-
bool6 = dg.has_edge(label122[1],label212[0],1) and dict_in_out[label212[1]][0] == dict_in_out[label212[1]][1] == 1
|
459
|
-
bool56 = not (
|
460
|
-
bool7 = dg.has_edge(label212[1],label122[0],1) and dict_in_out[label122[1]][0] == dict_in_out[label122[1]][1] == 1
|
461
|
-
bool8 = dg.has_edge(label121[1],label211[0],1) and dict_in_out[label211[1]][0] == dict_in_out[label211[1]][1] == 1
|
462
|
-
bool78 = not (
|
463
|
-
|
464
|
-
nb1 = len(
|
465
|
-
nb2 = len(
|
466
|
-
nb3 = len(
|
467
|
-
nb4 = len(
|
447
|
+
# tests for which configuration the two (1, -2) and two (2, -1) edges are in.
|
448
|
+
bool1 = dg.has_edge(label121[1], label211[0], 1) and dict_in_out[label211[1]][0] == dict_in_out[label211[1]][1] == 1
|
449
|
+
bool2 = dg.has_edge(label122[1], label212[0], 1) and dict_in_out[label212[1]][0] == dict_in_out[label212[1]][1] == 1
|
450
|
+
bool12 = not (label121[1] == label122[1] and label211[0] == label212[0])
|
451
|
+
bool3 = dg.has_edge(label211[1], label121[0], 1) and dict_in_out[label121[1]][0] == dict_in_out[label121[1]][1] == 1
|
452
|
+
bool4 = dg.has_edge(label212[1], label122[0], 1) and dict_in_out[label122[1]][0] == dict_in_out[label122[1]][1] == 1
|
453
|
+
bool34 = not (label211[1] == label212[1] and label121[0] == label122[0])
|
454
|
+
bool5 = dg.has_edge(label211[1], label121[0], 1) and dict_in_out[label121[1]][0] == dict_in_out[label121[1]][1] == 1
|
455
|
+
bool6 = dg.has_edge(label122[1], label212[0], 1) and dict_in_out[label212[1]][0] == dict_in_out[label212[1]][1] == 1
|
456
|
+
bool56 = not (label211[1] == label122[1] and label121[0] == label212[0])
|
457
|
+
bool7 = dg.has_edge(label212[1], label122[0], 1) and dict_in_out[label122[1]][0] == dict_in_out[label122[1]][1] == 1
|
458
|
+
bool8 = dg.has_edge(label121[1], label211[0], 1) and dict_in_out[label211[1]][0] == dict_in_out[label211[1]][1] == 1
|
459
|
+
bool78 = not (label212[1] == label121[1] and label122[0] == label211[0])
|
460
|
+
|
461
|
+
nb1 = len(set(dg.neighbors(label121[1])).intersection(dg.neighbors(label211[0]))) <= 1
|
462
|
+
nb2 = len(set(dg.neighbors(label122[1])).intersection(dg.neighbors(label212[0]))) <= 1
|
463
|
+
nb3 = len(set(dg.neighbors(label211[1])).intersection(dg.neighbors(label121[0]))) <= 1
|
464
|
+
nb4 = len(set(dg.neighbors(label212[1])).intersection(dg.neighbors(label122[0]))) <= 1
|
468
465
|
|
469
466
|
if bool1 and bool2 and bool12 and nb1 and nb2:
|
470
|
-
v1,v2 = label211[1],label212[1]
|
471
|
-
_reset_dg(
|
472
|
-
return _check_special_BC_cases(
|
467
|
+
v1, v2 = label211[1], label212[1]
|
468
|
+
_reset_dg(dg, vertices, dict_in_out, [v1, v2])
|
469
|
+
return _check_special_BC_cases(dg, n, ['CC'], [1], ['A'])
|
473
470
|
if bool3 and bool4 and bool34 and nb3 and nb4:
|
474
|
-
v1,v2 = label121[1],label122[1]
|
475
|
-
_reset_dg(
|
476
|
-
return _check_special_BC_cases(
|
471
|
+
v1, v2 = label121[1], label122[1]
|
472
|
+
_reset_dg(dg, vertices, dict_in_out, [v1, v2])
|
473
|
+
return _check_special_BC_cases(dg, n, ['BB'], [1], ['A'])
|
477
474
|
elif bool5 and bool6 and bool56 and nb2 and nb3:
|
478
|
-
v1,v2 = label121[1],label212[1]
|
479
|
-
_reset_dg(
|
480
|
-
return _check_special_BC_cases(
|
475
|
+
v1, v2 = label121[1], label212[1]
|
476
|
+
_reset_dg(dg, vertices, dict_in_out, [v1, v2])
|
477
|
+
return _check_special_BC_cases(dg, n, ['BC'], [1], ['A'])
|
481
478
|
elif bool7 and bool8 and bool78 and nb1 and nb4:
|
482
|
-
v1,v2 = label122[1],label211[1]
|
483
|
-
_reset_dg(
|
484
|
-
return _check_special_BC_cases(
|
479
|
+
v1, v2 = label122[1], label211[1]
|
480
|
+
_reset_dg(dg, vertices, dict_in_out, [v1, v2])
|
481
|
+
return _check_special_BC_cases(dg, n, ['BC'], [1], ['A'])
|
485
482
|
else:
|
486
483
|
return _false_return()
|
487
484
|
|
488
485
|
# first test for affine type C: if there are three exceptional labels, we must be in both cases below of the same construction
|
489
|
-
elif len(
|
486
|
+
elif len(exc_labels) == 3:
|
490
487
|
exc_labels12 = [labl for labl in exc_labels if labl[2] == (1, -2)]
|
491
488
|
exc_labels21 = [labl for labl in exc_labels if labl[2] == (2, -1)]
|
492
489
|
# check that we have two labels of one kind and one label of the other
|
493
490
|
if exc_labels12 == [] or exc_labels21 == []:
|
494
491
|
return _false_return()
|
495
|
-
if len(
|
496
|
-
label1,label2 = exc_labels12
|
492
|
+
if len(exc_labels12) == 2:
|
493
|
+
label1, label2 = exc_labels12
|
497
494
|
label3 = exc_labels21[0]
|
498
495
|
if dict_in_out[label2[0]][2] == 1 or dict_in_out[label2[1]][2] == 1:
|
499
496
|
label1, label2 = label2, label1
|
500
497
|
if dict_in_out[label1[0]][2] == 1:
|
501
|
-
if label2[1] == label3[0] and dict_in_out[label2[1]][2] == 2 and dg.has_edge(label3[1],label2[0],1):
|
502
|
-
v1,v2 = label3[1],label2[0]
|
503
|
-
_reset_dg(
|
504
|
-
if len(
|
498
|
+
if label2[1] == label3[0] and dict_in_out[label2[1]][2] == 2 and dg.has_edge(label3[1], label2[0], 1):
|
499
|
+
v1, v2 = label3[1], label2[0]
|
500
|
+
_reset_dg(dg, vertices, dict_in_out, [label2[1]])
|
501
|
+
if len(set(dg.neighbors_out(v2)).intersection(dg.neighbors_in(v1))) > 0:
|
505
502
|
return _false_return()
|
506
|
-
elif len(
|
507
|
-
return _check_special_BC_cases(
|
503
|
+
elif len(set(dg.neighbors_out(v1)).intersection(dg.neighbors_in(v2))) > 0:
|
504
|
+
return _check_special_BC_cases(dg, n, ['BC'], [1], ['A'], [[v1, v2]])
|
508
505
|
else:
|
509
|
-
return _check_special_BC_cases(
|
510
|
-
elif label3[1] == label2[0] and dict_in_out[label3[1]][2] == 2 and dg.has_edge(label2[1],label3[0],1):
|
511
|
-
v1,v2 = label2[1],label3[0]
|
512
|
-
_reset_dg(
|
513
|
-
if len(
|
506
|
+
return _check_special_BC_cases(dg, n, ['BC'], [1], ['A'])
|
507
|
+
elif label3[1] == label2[0] and dict_in_out[label3[1]][2] == 2 and dg.has_edge(label2[1], label3[0], 1):
|
508
|
+
v1, v2 = label2[1], label3[0]
|
509
|
+
_reset_dg(dg, vertices, dict_in_out, [label3[1]])
|
510
|
+
if len(set(dg.neighbors_out(v2)).intersection(dg.neighbors_in(v1))) > 0:
|
514
511
|
return _false_return()
|
515
|
-
elif len(
|
516
|
-
return _check_special_BC_cases(
|
512
|
+
elif len(set(dg.neighbors_out(v1)).intersection(dg.neighbors_in(v2))) > 0:
|
513
|
+
return _check_special_BC_cases(dg, n, ['CC'], [1], ['A'], [[v1, v2]])
|
517
514
|
else:
|
518
|
-
return _check_special_BC_cases(
|
515
|
+
return _check_special_BC_cases(dg, n, ['CC'], [1], ['A'])
|
519
516
|
else:
|
520
517
|
return _false_return()
|
521
518
|
elif dict_in_out[label1[1]][2] == 1:
|
522
|
-
if label3[1] == label2[0] and dict_in_out[label3[1]][2] == 2 and dg.has_edge(label2[1],label3[0],1):
|
523
|
-
v1,v2 = label2[1],label3[0]
|
524
|
-
_reset_dg(
|
525
|
-
if len(
|
519
|
+
if label3[1] == label2[0] and dict_in_out[label3[1]][2] == 2 and dg.has_edge(label2[1], label3[0], 1):
|
520
|
+
v1, v2 = label2[1], label3[0]
|
521
|
+
_reset_dg(dg, vertices, dict_in_out, [label3[1]])
|
522
|
+
if len(set(dg.neighbors_out(v2)).intersection(dg.neighbors_in(v1))) > 0:
|
526
523
|
return _false_return()
|
527
|
-
elif len(
|
528
|
-
return _check_special_BC_cases(
|
524
|
+
elif len(set(dg.neighbors_out(v1)).intersection(dg.neighbors_in(v2))) > 0:
|
525
|
+
return _check_special_BC_cases(dg, n, ['BC'], [1], ['A'], [[v1, v2]])
|
529
526
|
else:
|
530
|
-
return _check_special_BC_cases(
|
531
|
-
elif label2[1] == label3[0] and dict_in_out[label2[1]][2] == 2 and dg.has_edge(label3[1],label2[0],1):
|
532
|
-
v1,v2 = label3[1],label2[0]
|
533
|
-
_reset_dg(
|
534
|
-
if len(
|
527
|
+
return _check_special_BC_cases(dg, n, ['BC'], [1], ['A'])
|
528
|
+
elif label2[1] == label3[0] and dict_in_out[label2[1]][2] == 2 and dg.has_edge(label3[1], label2[0], 1):
|
529
|
+
v1, v2 = label3[1], label2[0]
|
530
|
+
_reset_dg(dg, vertices, dict_in_out, [label2[1]])
|
531
|
+
if len(set(dg.neighbors_out(v2)).intersection(dg.neighbors_in(v1))) > 0:
|
535
532
|
return _false_return()
|
536
|
-
elif len(
|
537
|
-
return _check_special_BC_cases(
|
533
|
+
elif len(set(dg.neighbors_out(v1)).intersection(dg.neighbors_in(v2))) > 0:
|
534
|
+
return _check_special_BC_cases(dg, n, ['BB'], [1], ['A'], [[v1, v2]])
|
538
535
|
else:
|
539
|
-
return _check_special_BC_cases(
|
536
|
+
return _check_special_BC_cases(dg, n, ['BB'], [1], ['A'])
|
540
537
|
else:
|
541
538
|
return _false_return()
|
542
|
-
elif label1[1] == label2[1] == label3[0] and dict_in_out[label1[1]][2] == 3 and dg.has_edge(label3[1],label1[0],1) and dg.has_edge(label3[1],label2[0],1) and dict_in_out[label2[0]][2] == dict_in_out[label1[0]][2] == 2:
|
543
|
-
_reset_dg(
|
544
|
-
return _check_special_BC_cases(
|
545
|
-
elif label1[0] == label2[0] == label3[1] and dict_in_out[label1[0]][2] == 3 and dg.has_edge(label1[1],label3[0],1) and dg.has_edge(label2[1],label3[0],1) and dict_in_out[label2[1]][2] == dict_in_out[label1[1]][2] == 2:
|
546
|
-
_reset_dg(
|
547
|
-
return _check_special_BC_cases(
|
539
|
+
elif label1[1] == label2[1] == label3[0] and dict_in_out[label1[1]][2] == 3 and dg.has_edge(label3[1], label1[0], 1) and dg.has_edge(label3[1], label2[0], 1) and dict_in_out[label2[0]][2] == dict_in_out[label1[0]][2] == 2:
|
540
|
+
_reset_dg(dg, vertices, dict_in_out, [label1[1]])
|
541
|
+
return _check_special_BC_cases(dg, n, ['BD'], [1], ['D'])
|
542
|
+
elif label1[0] == label2[0] == label3[1] and dict_in_out[label1[0]][2] == 3 and dg.has_edge(label1[1], label3[0], 1) and dg.has_edge(label2[1], label3[0], 1) and dict_in_out[label2[1]][2] == dict_in_out[label1[1]][2] == 2:
|
543
|
+
_reset_dg(dg, vertices, dict_in_out, [label1[0]])
|
544
|
+
return _check_special_BC_cases(dg, n, ['CD'], [1], ['D'])
|
548
545
|
else:
|
549
546
|
return _false_return()
|
550
|
-
elif len(
|
551
|
-
label1,label2 = exc_labels21
|
547
|
+
elif len(exc_labels21) == 2:
|
548
|
+
label1, label2 = exc_labels21
|
552
549
|
label3 = exc_labels12[0]
|
553
550
|
if dict_in_out[label2[0]][2] == 1 or dict_in_out[label2[1]][2] == 1:
|
554
551
|
label1, label2 = label2, label1
|
555
552
|
if dict_in_out[label1[1]][2] == 1:
|
556
|
-
if label2[1] == label3[0] and dict_in_out[label2[1]][2] == 2 and dg.has_edge(label3[1],label2[0],1):
|
553
|
+
if label2[1] == label3[0] and dict_in_out[label2[1]][2] == 2 and dg.has_edge(label3[1], label2[0], 1):
|
557
554
|
v1, v2 = label3[1], label2[0]
|
558
|
-
_reset_dg(
|
559
|
-
if len(
|
555
|
+
_reset_dg(dg, vertices, dict_in_out, [label2[1]])
|
556
|
+
if len(set(dg.neighbors_out(v2)).intersection(dg.neighbors_in(v1))) > 0:
|
560
557
|
return _false_return()
|
561
|
-
elif len(
|
562
|
-
return _check_special_BC_cases(
|
558
|
+
elif len(set(dg.neighbors_out(v1)).intersection(dg.neighbors_in(v2))) > 0:
|
559
|
+
return _check_special_BC_cases(dg, n, ['CC'], [1], ['A'], [[v1, v2]])
|
563
560
|
else:
|
564
|
-
return _check_special_BC_cases(
|
565
|
-
elif label3[1] == label2[0] and dict_in_out[label3[1]][2] == 2 and dg.has_edge(label2[1],label3[0],1):
|
561
|
+
return _check_special_BC_cases(dg, n, ['CC'], [1], ['A'])
|
562
|
+
elif label3[1] == label2[0] and dict_in_out[label3[1]][2] == 2 and dg.has_edge(label2[1], label3[0], 1):
|
566
563
|
v1, v2 = label2[1], label3[0]
|
567
|
-
_reset_dg(
|
568
|
-
if len(
|
564
|
+
_reset_dg(dg, vertices, dict_in_out, [label3[1]])
|
565
|
+
if len(set(dg.neighbors_out(v2)).intersection(dg.neighbors_in(v1))) > 0:
|
569
566
|
return _false_return()
|
570
|
-
elif len(
|
571
|
-
return _check_special_BC_cases(
|
567
|
+
elif len(set(dg.neighbors_out(v1)).intersection(dg.neighbors_in(v2))) > 0:
|
568
|
+
return _check_special_BC_cases(dg, n, ['BC'], [1], ['A'], [[v1, v2]])
|
572
569
|
else:
|
573
|
-
return _check_special_BC_cases(
|
570
|
+
return _check_special_BC_cases(dg, n, ['BC'], [1], ['A'])
|
574
571
|
else:
|
575
572
|
return _false_return()
|
576
573
|
elif dict_in_out[label1[0]][2] == 1:
|
577
|
-
if label3[1] == label2[0] and dict_in_out[label3[1]][2] == 2 and dg.has_edge(label2[1],label3[0],1):
|
574
|
+
if label3[1] == label2[0] and dict_in_out[label3[1]][2] == 2 and dg.has_edge(label2[1], label3[0], 1):
|
578
575
|
v1, v2 = label2[1], label3[0]
|
579
|
-
_reset_dg(
|
580
|
-
if len(
|
576
|
+
_reset_dg(dg, vertices, dict_in_out, [label3[1]])
|
577
|
+
if len(set(dg.neighbors_out(v2)).intersection(dg.neighbors_in(v1))) > 0:
|
581
578
|
return _false_return()
|
582
|
-
elif len(
|
583
|
-
return _check_special_BC_cases(
|
579
|
+
elif len(set(dg.neighbors_out(v1)).intersection(dg.neighbors_in(v2))) > 0:
|
580
|
+
return _check_special_BC_cases(dg, n, ['BB'], [1], ['A'], [[v1, v2]])
|
584
581
|
else:
|
585
|
-
return _check_special_BC_cases(
|
586
|
-
elif label2[1] == label3[0] and dict_in_out[label2[1]][2] == 2 and dg.has_edge(label3[1],label2[0],1):
|
582
|
+
return _check_special_BC_cases(dg, n, ['BB'], [1], ['A'])
|
583
|
+
elif label2[1] == label3[0] and dict_in_out[label2[1]][2] == 2 and dg.has_edge(label3[1], label2[0], 1):
|
587
584
|
v1, v2 = label3[1], label2[0]
|
588
|
-
_reset_dg(
|
589
|
-
if len(
|
585
|
+
_reset_dg(dg, vertices, dict_in_out, [label2[1]])
|
586
|
+
if len(set(dg.neighbors_out(v2)).intersection(dg.neighbors_in(v1))) > 0:
|
590
587
|
return _false_return()
|
591
|
-
elif len(
|
592
|
-
return _check_special_BC_cases(
|
588
|
+
elif len(set(dg.neighbors_out(v1)).intersection(dg.neighbors_in(v2))) > 0:
|
589
|
+
return _check_special_BC_cases(dg, n, ['BC'], [1], ['A'], [[v1, v2]])
|
593
590
|
else:
|
594
|
-
return _check_special_BC_cases(
|
591
|
+
return _check_special_BC_cases(dg, n, ['BC'], [1], ['A'])
|
595
592
|
else:
|
596
593
|
return _false_return()
|
597
|
-
elif label1[0] == label2[0] == label3[1] and dict_in_out[label1[0]][2] == 3 and dg.has_edge(label1[1],label3[0],1) and dict_in_out[label1[1]][2] == 2 and dg.has_edge(label2[1],label3[0],1) and dict_in_out[label2[1]][2] == 2:
|
598
|
-
_reset_dg(
|
599
|
-
return _check_special_BC_cases(
|
600
|
-
elif label1[1] == label2[1] == label3[0] and dict_in_out[label3[0]][2] == 3 and dg.has_edge(label3[1],label1[0],1) and dict_in_out[label1[0]][2] == 2 and dg.has_edge(label3[1],label2[0],1) and dict_in_out[label2[0]][2] == 2:
|
601
|
-
_reset_dg(
|
602
|
-
return _check_special_BC_cases(
|
594
|
+
elif label1[0] == label2[0] == label3[1] and dict_in_out[label1[0]][2] == 3 and dg.has_edge(label1[1], label3[0], 1) and dict_in_out[label1[1]][2] == 2 and dg.has_edge(label2[1], label3[0], 1) and dict_in_out[label2[1]][2] == 2:
|
595
|
+
_reset_dg(dg, vertices, dict_in_out, [label3[1]])
|
596
|
+
return _check_special_BC_cases(dg, n, ['BD'], [1], ['D'])
|
597
|
+
elif label1[1] == label2[1] == label3[0] and dict_in_out[label3[0]][2] == 3 and dg.has_edge(label3[1], label1[0], 1) and dict_in_out[label1[0]][2] == 2 and dg.has_edge(label3[1], label2[0], 1) and dict_in_out[label2[0]][2] == 2:
|
598
|
+
_reset_dg(dg, vertices, dict_in_out, [label3[0]])
|
599
|
+
return _check_special_BC_cases(dg, n, ['CD'], [1], ['D'])
|
603
600
|
else:
|
604
601
|
return _false_return()
|
605
602
|
|
606
603
|
# first test for finite types B and C: if there are two exceptional labels, they must belong to an oriented triangle and the vertex between must be a leaf
|
607
604
|
# first test for affine type C: if there are two exceptional labels, they must belong to leaves
|
608
605
|
# first test for affine type B: if there are two exceptional labels, they must be...
|
609
|
-
elif len(
|
606
|
+
elif len(exc_labels) == 2:
|
610
607
|
label1, label2 = exc_labels
|
611
608
|
if label1[1] == label2[0]:
|
612
609
|
pass
|
@@ -618,9 +615,9 @@ def _connected_mutation_type(dg):
|
|
618
615
|
label1, label2 = label2, label1
|
619
616
|
if label1[2] == (1, -2) and label2[2] == (2, -1):
|
620
617
|
if label1[1] == label2[1] and dict_in_out[label1[1]][2] == 2 and dict_in_out[label1[0]][2] == 1 and dict_in_out[label2[0]][2] == 1:
|
621
|
-
return QuiverMutationType(['BC',2,1])
|
618
|
+
return QuiverMutationType(['BC', 2, 1])
|
622
619
|
elif label1[0] == label2[0] and dict_in_out[label1[0]][2] == 2 and dict_in_out[label1[1]][2] == 1 and dict_in_out[label2[1]][2] == 1:
|
623
|
-
return QuiverMutationType(['BC',2,1])
|
620
|
+
return QuiverMutationType(['BC', 2, 1])
|
624
621
|
# the cases in affine type B/C are checked where the exceptional labels connect to leaves
|
625
622
|
v11, v12, label1 = label1
|
626
623
|
v21, v22, label2 = label2
|
@@ -638,53 +635,53 @@ def _connected_mutation_type(dg):
|
|
638
635
|
return _false_return()
|
639
636
|
if label1 == label2:
|
640
637
|
if in_out1 == in_out2 == 'in':
|
641
|
-
if label1 == (1
|
642
|
-
return _check_special_BC_cases(
|
638
|
+
if label1 == (1, -2):
|
639
|
+
return _check_special_BC_cases(dg, n, ['BB'], [1], ['A'])
|
643
640
|
else:
|
644
|
-
return _check_special_BC_cases(
|
641
|
+
return _check_special_BC_cases(dg, n, ['CC'], [1], ['A'])
|
645
642
|
elif in_out1 == in_out2 == 'out':
|
646
|
-
if label1 == (1
|
647
|
-
return _check_special_BC_cases(
|
643
|
+
if label1 == (1, -2):
|
644
|
+
return _check_special_BC_cases(dg, n, ['CC'], [1], ['A'])
|
648
645
|
else:
|
649
|
-
return _check_special_BC_cases(
|
646
|
+
return _check_special_BC_cases(dg, n, ['BB'], [1], ['A'])
|
650
647
|
else:
|
651
|
-
return _check_special_BC_cases(
|
648
|
+
return _check_special_BC_cases(dg, n, ['BC'], [1], ['A'])
|
652
649
|
else:
|
653
650
|
if in_out1 == in_out2:
|
654
|
-
return _check_special_BC_cases(
|
651
|
+
return _check_special_BC_cases(dg, n, ['BC'], [1], ['A'])
|
655
652
|
else:
|
656
|
-
if label1 == (1
|
653
|
+
if label1 == (1, -2):
|
657
654
|
if in_out1 == 'in':
|
658
|
-
return _check_special_BC_cases(
|
655
|
+
return _check_special_BC_cases(dg, n, ['BB'], [1], ['A'])
|
659
656
|
else:
|
660
|
-
return _check_special_BC_cases(
|
657
|
+
return _check_special_BC_cases(dg, n, ['CC'], [1], ['A'])
|
661
658
|
else:
|
662
659
|
if in_out1 == 'in':
|
663
|
-
return _check_special_BC_cases(
|
660
|
+
return _check_special_BC_cases(dg, n, ['CC'], [1], ['A'])
|
664
661
|
else:
|
665
|
-
return _check_special_BC_cases(
|
662
|
+
return _check_special_BC_cases(dg, n, ['BB'], [1], ['A'])
|
666
663
|
|
667
|
-
v1,v,label1 = label1
|
668
|
-
v,v2,label2 = label2
|
664
|
+
v1, v, label1 = label1
|
665
|
+
v, v2, label2 = label2
|
669
666
|
if dg.has_multiple_edges():
|
670
|
-
if all(
|
667
|
+
if all(edge == (v2, v1, 1) for edge in dg.multiple_edges()):
|
671
668
|
if dict_in_out[v2][2] == dict_in_out[v1][2] == 3:
|
672
|
-
_reset_dg(
|
673
|
-
if label1 == (1
|
674
|
-
return _check_special_BC_cases(
|
675
|
-
elif label1 == (2
|
676
|
-
return _check_special_BC_cases(
|
669
|
+
_reset_dg(dg, vertices, dict_in_out, [v1, v2])
|
670
|
+
if label1 == (1, -2) and label2 == (2, -1):
|
671
|
+
return _check_special_BC_cases(dg, n, ['CC'], [1], ['A'], [[v]])
|
672
|
+
elif label1 == (2, -1) and label2 == (1, -2):
|
673
|
+
return _check_special_BC_cases(dg, n, ['BB'], [1], ['A'], [[v]])
|
677
674
|
else:
|
678
675
|
return _false_return()
|
679
676
|
elif dict_in_out[v][0] == dict_in_out[v][1] == 1:
|
680
677
|
dg.remove_multiple_edges()
|
681
|
-
dg = DiGraph(
|
682
|
-
_reset_dg(
|
678
|
+
dg = DiGraph(dg)
|
679
|
+
_reset_dg(dg, vertices, dict_in_out, [v])
|
683
680
|
if dict_in_out[v1][0] == dict_in_out[v1][1] == dict_in_out[v2][0] == dict_in_out[v2][1] == 1 and next(dg.neighbor_out_iterator(v1)) == next(dg.neighbor_in_iterator(v2)):
|
684
|
-
if label1 == (2
|
685
|
-
return _check_special_BC_cases(
|
686
|
-
elif label1 == (1
|
687
|
-
return _check_special_BC_cases(
|
681
|
+
if label1 == (2, -1) and label2 == (1, -2):
|
682
|
+
return _check_special_BC_cases(dg, n, ['CD'], [1], ['A'])
|
683
|
+
elif label1 == (1, -2) and label2 == (2, -1):
|
684
|
+
return _check_special_BC_cases(dg, n, ['BD'], [1], ['A'])
|
688
685
|
else:
|
689
686
|
return _false_return()
|
690
687
|
else:
|
@@ -694,73 +691,73 @@ def _connected_mutation_type(dg):
|
|
694
691
|
elif not dict_in_out[v][0] == 1 or not dict_in_out[v][1] == 1:
|
695
692
|
return _false_return()
|
696
693
|
else:
|
697
|
-
if dg.has_edge(v2,v1,1):
|
698
|
-
nr_same_neighbors = len(
|
699
|
-
nr_other_neighbors = len(
|
700
|
-
nr_contained_cycles = len([
|
694
|
+
if dg.has_edge(v2, v1, 1):
|
695
|
+
nr_same_neighbors = len(set(dg.neighbors_out(v1)).intersection(dg.neighbors_in(v2)))
|
696
|
+
nr_other_neighbors = len(set(dg.neighbors_out(v2)).intersection(dg.neighbors_in(v1)))
|
697
|
+
nr_contained_cycles = len([cycle for cycle, is_oriented in _all_induced_cycles_iter(dg) if v1 in flatten(cycle) and v2 in flatten(cycle)])
|
701
698
|
if nr_same_neighbors + nr_other_neighbors + nr_contained_cycles > 2:
|
702
699
|
return _false_return()
|
703
|
-
if label1 == (2
|
700
|
+
if label1 == (2, -1) and label2 == (1, -2):
|
704
701
|
if n == 4 and (nr_same_neighbors == 2 or nr_other_neighbors == 1):
|
705
|
-
return QuiverMutationType(['CD',n-1,1])
|
702
|
+
return QuiverMutationType(['CD', n - 1, 1])
|
706
703
|
# checks for affine A
|
707
704
|
if nr_same_neighbors + nr_other_neighbors > 1:
|
708
|
-
mt_tmp = _check_special_BC_cases(
|
705
|
+
mt_tmp = _check_special_BC_cases(dg, n, ['C', 'CD'], [None, None], ['A', 'D'], [[], [v]])
|
709
706
|
else:
|
710
|
-
_reset_dg(
|
711
|
-
mt_tmp = _check_special_BC_cases(
|
707
|
+
_reset_dg(dg, vertices, dict_in_out, [v])
|
708
|
+
mt_tmp = _check_special_BC_cases(dg, n, ['C', 'CD'], [None, None], ['A', 'D'])
|
712
709
|
if mt_tmp == 'unknown':
|
713
|
-
dg.delete_edges([[v2,v1],[v1,v],[v,v2]])
|
714
|
-
dg.add_edges([[v1,v2,1],[v,v1,1],[v2,v,1]])
|
710
|
+
dg.delete_edges([[v2, v1], [v1, v], [v, v2]])
|
711
|
+
dg.add_edges([[v1, v2, 1], [v, v1, 1], [v2, v, 1]])
|
715
712
|
if nr_same_neighbors + nr_other_neighbors > 1:
|
716
|
-
#_reset_dg(
|
717
|
-
return _check_special_BC_cases(
|
713
|
+
# _reset_dg(dg, vertices, dict_in_out, [v])
|
714
|
+
return _check_special_BC_cases(dg, n, ['CD'], [None], ['D'], [[v]])
|
718
715
|
else:
|
719
|
-
return _check_special_BC_cases(
|
716
|
+
return _check_special_BC_cases(dg, n, ['CD'], [None], ['D'])
|
720
717
|
else:
|
721
718
|
return mt_tmp
|
722
|
-
elif label1 == (1
|
719
|
+
elif label1 == (1, -2) and label2 == (2, -1):
|
723
720
|
if n == 4 and (nr_same_neighbors == 2 or nr_other_neighbors == 1):
|
724
|
-
return QuiverMutationType(['BD',n-1,1])
|
721
|
+
return QuiverMutationType(['BD', n - 1, 1])
|
725
722
|
# checks for affine A
|
726
723
|
if nr_same_neighbors + nr_other_neighbors > 1:
|
727
|
-
mt_tmp = _check_special_BC_cases(
|
724
|
+
mt_tmp = _check_special_BC_cases(dg, n, ['B', 'BD'], [None, None], ['A', 'D'], [[], [v]])
|
728
725
|
else:
|
729
|
-
_reset_dg(
|
730
|
-
mt_tmp = _check_special_BC_cases(
|
726
|
+
_reset_dg(dg, vertices, dict_in_out, [v])
|
727
|
+
mt_tmp = _check_special_BC_cases(dg, n, ['B', 'BD'], [None, None], ['A', 'D'])
|
731
728
|
if mt_tmp == 'unknown':
|
732
|
-
dg.delete_edges([[v2,v1],[v1,v],[v,v2]])
|
733
|
-
dg.add_edges([[v1,v2,1],[v,v1,1],[v2,v,1]])
|
729
|
+
dg.delete_edges([[v2, v1], [v1, v], [v, v2]])
|
730
|
+
dg.add_edges([[v1, v2, 1], [v, v1, 1], [v2, v, 1]])
|
734
731
|
if nr_same_neighbors + nr_other_neighbors > 1:
|
735
|
-
#_reset_dg(
|
736
|
-
return _check_special_BC_cases(
|
732
|
+
# _reset_dg(dg, vertices, dict_in_out, [v])
|
733
|
+
return _check_special_BC_cases(dg, n, ['BD'], [None], ['D'], [[v]])
|
737
734
|
else:
|
738
|
-
return _check_special_BC_cases(
|
735
|
+
return _check_special_BC_cases(dg, n, ['BD'], [None], ['D'])
|
739
736
|
else:
|
740
737
|
return mt_tmp
|
741
738
|
else:
|
742
739
|
return _false_return()
|
743
740
|
elif dict_in_out[v1][2] == 1 and dict_in_out[v2][2] == 1:
|
744
|
-
if label1 == (1
|
745
|
-
return _check_special_BC_cases(
|
746
|
-
elif label1 == (2
|
747
|
-
return _check_special_BC_cases(
|
748
|
-
elif label1 == (1
|
749
|
-
return _check_special_BC_cases(
|
750
|
-
elif label1 == (2
|
751
|
-
return _check_special_BC_cases(
|
741
|
+
if label1 == (1, -2) and label2 == (1, -2):
|
742
|
+
return _check_special_BC_cases(dg, n, ['BC'], [1], ['A'])
|
743
|
+
elif label1 == (2, -1) and label2 == (2, -1):
|
744
|
+
return _check_special_BC_cases(dg, n, ['BC'], [1], ['A'])
|
745
|
+
elif label1 == (1, -2) and label2 == (2, -1):
|
746
|
+
return _check_special_BC_cases(dg, n, ['CC'], [1], ['A'])
|
747
|
+
elif label1 == (2, -1) and label2 == (1, -2):
|
748
|
+
return _check_special_BC_cases(dg, n, ['BB'], [1], ['A'])
|
752
749
|
else:
|
753
750
|
return _false_return()
|
754
751
|
elif dict_in_out[v][0] == dict_in_out[v][1] == 1 and dict_in_out[v1][0] == dict_in_out[v1][1] == 1 and dict_in_out[v2][0] == dict_in_out[v2][1] == 1:
|
755
|
-
_reset_dg(
|
756
|
-
if n == 4 and (
|
757
|
-
return _check_special_BC_cases(
|
758
|
-
elif n > 4 and (
|
759
|
-
return _check_special_BC_cases(
|
760
|
-
elif n == 4 and (
|
761
|
-
return _check_special_BC_cases(
|
762
|
-
elif n > 4 and (
|
763
|
-
return _check_special_BC_cases(
|
752
|
+
_reset_dg(dg, vertices, dict_in_out, [v])
|
753
|
+
if n == 4 and (label1, label2) == ((2, -1), (1, -2)):
|
754
|
+
return _check_special_BC_cases(dg, n, ['CD'], [1], ['A'])
|
755
|
+
elif n > 4 and (label1, label2) == ((2, -1), (1, -2)):
|
756
|
+
return _check_special_BC_cases(dg, n, ['CD'], [1], ['D'])
|
757
|
+
elif n == 4 and (label1, label2) == ((1, -2), (2, -1)):
|
758
|
+
return _check_special_BC_cases(dg, n, ['BD'], [1], ['A'])
|
759
|
+
elif n > 4 and (label1, label2) == ((1, -2), (2, -1)):
|
760
|
+
return _check_special_BC_cases(dg, n, ['BD'], [1], ['D'])
|
764
761
|
else:
|
765
762
|
return _false_return()
|
766
763
|
else:
|
@@ -768,35 +765,35 @@ def _connected_mutation_type(dg):
|
|
768
765
|
|
769
766
|
# second tests for finite types B and C: if there is only one exceptional label, it must belong to a leaf
|
770
767
|
# also tests for affine type B: this exceptional label must belong to a leaf of a type D quiver
|
771
|
-
elif len(
|
768
|
+
elif len(exc_labels) == 1:
|
772
769
|
label = exc_labels[0]
|
773
770
|
v_out = label[0]
|
774
771
|
v_in = label[1]
|
775
772
|
label = label[2]
|
776
|
-
if label == (1
|
777
|
-
if dict_in_out[
|
778
|
-
#_reset_dg(
|
779
|
-
return _check_special_BC_cases(
|
780
|
-
elif dict_in_out[
|
781
|
-
#_reset_dg(
|
782
|
-
return _check_special_BC_cases(
|
773
|
+
if label == (1, -2):
|
774
|
+
if dict_in_out[v_in][0] == 1 and dict_in_out[v_in][1] == 0:
|
775
|
+
# _reset_dg(dg, vertices, dict_in_out, [v_in])
|
776
|
+
return _check_special_BC_cases(dg, n, ['B', 'BD'], [None, 1], ['A', 'D'], [[v_in], [v_in]])
|
777
|
+
elif dict_in_out[v_out][0] == 0 and dict_in_out[v_out][1] == 1:
|
778
|
+
# _reset_dg(dg, vertices, dict_in_out, [v_out])
|
779
|
+
return _check_special_BC_cases(dg, n, ['C', 'CD'], [None, 1], ['A', 'D'], [[v_out], [v_out]])
|
783
780
|
else:
|
784
781
|
return _false_return()
|
785
|
-
elif label == (2
|
786
|
-
if dict_in_out[
|
787
|
-
#_reset_dg(
|
788
|
-
return _check_special_BC_cases(
|
789
|
-
elif dict_in_out[
|
790
|
-
#_reset_dg(
|
791
|
-
return _check_special_BC_cases(
|
782
|
+
elif label == (2, -1):
|
783
|
+
if dict_in_out[v_out][0] == 0 and dict_in_out[v_out][1] == 1:
|
784
|
+
# _reset_dg(dg, vertices, dict_in_out, [v_out])
|
785
|
+
return _check_special_BC_cases(dg, n, ['B', 'BD'], [None, 1], ['A', 'D'], [[v_out], [v_out]])
|
786
|
+
elif dict_in_out[v_in][0] == 1 and dict_in_out[v_in][1] == 0:
|
787
|
+
# _reset_dg(dg, vertices, dict_in_out, [v_in])
|
788
|
+
return _check_special_BC_cases(dg, n, ['C', 'CD'], [None, 1], ['A', 'D'], [[v_in], [v_in]])
|
792
789
|
else:
|
793
790
|
return _false_return()
|
794
791
|
|
795
|
-
# if no edges of type (1
|
792
|
+
# if no edges of type (1, -2) nor (2, -1), then tests for type A, affine A, or D.
|
796
793
|
return _connected_mutation_type_AAtildeD(dg)
|
797
794
|
|
798
795
|
|
799
|
-
def _connected_mutation_type_AAtildeD(dg, ret_conn_vert=False):
|
796
|
+
def _connected_mutation_type_AAtildeD(dg: DiGraph, ret_conn_vert=False):
|
800
797
|
"""
|
801
798
|
Return mutation type of ClusterQuiver(dg) for DiGraph dg if it is
|
802
799
|
of type finite A, affine A, or finite D.
|
@@ -829,14 +826,14 @@ def _connected_mutation_type_AAtildeD(dg, ret_conn_vert=False):
|
|
829
826
|
|
830
827
|
sage: # needs sage.modules
|
831
828
|
sage: from sage.combinat.cluster_algebra_quiver.mutation_type import _connected_mutation_type_AAtildeD
|
832
|
-
sage: Q = ClusterQuiver(['A',[7,0],1]); Q.mutate([0,1,4])
|
833
|
-
sage: _connected_mutation_type_AAtildeD(Q.digraph(),ret_conn_vert=True)
|
829
|
+
sage: Q = ClusterQuiver(['A', [7, 0], 1]); Q.mutate([0, 1, 4])
|
830
|
+
sage: _connected_mutation_type_AAtildeD(Q.digraph(), ret_conn_vert=True)
|
834
831
|
[['D', 7], [0, 4]]
|
835
|
-
sage: Q2 = ClusterQuiver(['A',[5,2],1]); Q2.mutate([4,5])
|
836
|
-
sage: _connected_mutation_type_AAtildeD(Q2.digraph()
|
832
|
+
sage: Q2 = ClusterQuiver(['A', [5, 2], 1]); Q2.mutate([4, 5])
|
833
|
+
sage: _connected_mutation_type_AAtildeD(Q2.digraph())
|
837
834
|
['A', [2, 5], 1]
|
838
|
-
sage: Q3 = ClusterQuiver(['E',6]); Q3.mutate([5,2,1])
|
839
|
-
sage: _connected_mutation_type_AAtildeD(Q3.digraph(),ret_conn_vert=True)
|
835
|
+
sage: Q3 = ClusterQuiver(['E', 6]); Q3.mutate([5, 2, 1])
|
836
|
+
sage: _connected_mutation_type_AAtildeD(Q3.digraph(), ret_conn_vert=True)
|
840
837
|
'unknown'
|
841
838
|
"""
|
842
839
|
# naming the vertices
|
@@ -848,11 +845,11 @@ def _connected_mutation_type_AAtildeD(dg, ret_conn_vert=False):
|
|
848
845
|
# check if any vertices have a neighborhood with two leaves. If so, prune the two leaves and retest on this smaller digraph.
|
849
846
|
# note that this step is unnecessary for digraphs with fewer than four vertices.
|
850
847
|
for v in vertices:
|
851
|
-
dead_neighbors = [
|
852
|
-
if len(
|
853
|
-
dg_tmp = DiGraph(
|
854
|
-
dg_tmp.delete_vertices(
|
855
|
-
type_tmp = _connected_mutation_type_AAtildeD(
|
848
|
+
dead_neighbors = [v_n for v_n in dg.neighbors(v) if dg.degree(v_n) == 1]
|
849
|
+
if len(dead_neighbors) >= 2:
|
850
|
+
dg_tmp = DiGraph(dg)
|
851
|
+
dg_tmp.delete_vertices(dead_neighbors[:2])
|
852
|
+
type_tmp = _connected_mutation_type_AAtildeD(dg_tmp, ret_conn_vert=True)
|
856
853
|
if type_tmp == 'unknown':
|
857
854
|
return _false_return()
|
858
855
|
# if smaller digraph is of finite A type with v as a 'connecting vertex', then glueing back the two leaves yields type finite D.
|
@@ -862,9 +859,9 @@ def _connected_mutation_type_AAtildeD(dg, ret_conn_vert=False):
|
|
862
859
|
if n == 4:
|
863
860
|
type_tmp[1].extend(dead_neighbors[:2])
|
864
861
|
if ret_conn_vert:
|
865
|
-
return [
|
862
|
+
return [QuiverMutationType(['D', n]), type_tmp[1]]
|
866
863
|
else:
|
867
|
-
return QuiverMutationType(
|
864
|
+
return QuiverMutationType(['D', n])
|
868
865
|
# note that if v is not a 'connecting vertex' then we make no conclusion either way.
|
869
866
|
else:
|
870
867
|
return _false_return(3)
|
@@ -873,11 +870,11 @@ def _connected_mutation_type_AAtildeD(dg, ret_conn_vert=False):
|
|
873
870
|
|
874
871
|
# Exception 1 (Type 2 of D_n)
|
875
872
|
exception_graph1 = DiGraph()
|
876
|
-
exception_graph1.add_edges([(0,1),(1,2),(2,3),(3,0)])
|
873
|
+
exception_graph1.add_edges([(0, 1), (1, 2), (2, 3), (3, 0)])
|
877
874
|
|
878
875
|
# Exception 2 (Type 3 of D_n)
|
879
876
|
exception_graph2 = DiGraph()
|
880
|
-
exception_graph2.add_edges([(0,1),(1,2),(0,3),(3,2),(2,0)])
|
877
|
+
exception_graph2.add_edges([(0, 1), (1, 2), (0, 3), (3, 2), (2, 0)])
|
881
878
|
|
882
879
|
# Let c_1 be a pair of 2-valent vertices and c_2 be a pair of two other vertices.
|
883
880
|
# If together, they make an induced 4-cycle and deleting c_1 yields two connected components,
|
@@ -888,13 +885,13 @@ def _connected_mutation_type_AAtildeD(dg, ret_conn_vert=False):
|
|
888
885
|
# it regardless of orientation. Then check if the digraph has exactly two connected
|
889
886
|
# components, and again this testing method is rerun on both components.
|
890
887
|
|
891
|
-
for c1 in Combinations(
|
892
|
-
del_vertices = list(
|
893
|
-
del_vertices.remove(
|
894
|
-
del_vertices.remove(
|
895
|
-
for c2 in Combinations(
|
888
|
+
for c1 in Combinations([vertex for vertex in vertices if dg.degree(vertex) == 2], 2):
|
889
|
+
del_vertices = list(vertices)
|
890
|
+
del_vertices.remove(c1[0])
|
891
|
+
del_vertices.remove(c1[1])
|
892
|
+
for c2 in Combinations(del_vertices, 2):
|
896
893
|
comb = c1 + c2
|
897
|
-
sg = dg.subgraph(
|
894
|
+
sg = dg.subgraph(comb)
|
898
895
|
|
899
896
|
# Exception 1 case (4-cycle):
|
900
897
|
edges = sg.edges(sort=True, labels=False)
|
@@ -907,19 +904,19 @@ def _connected_mutation_type_AAtildeD(dg, ret_conn_vert=False):
|
|
907
904
|
if len(components) != 2:
|
908
905
|
return _false_return(4)
|
909
906
|
else:
|
910
|
-
dg_tmp1 = dg_tmp.subgraph(
|
911
|
-
type_tmp1 = _connected_mutation_type_AAtildeD(
|
912
|
-
dg_tmp2 = dg_tmp.subgraph(
|
913
|
-
type_tmp2 = _connected_mutation_type_AAtildeD(
|
907
|
+
dg_tmp1 = dg_tmp.subgraph(components[0])
|
908
|
+
type_tmp1 = _connected_mutation_type_AAtildeD(dg_tmp1, ret_conn_vert=True)
|
909
|
+
dg_tmp2 = dg_tmp.subgraph(components[1])
|
910
|
+
type_tmp2 = _connected_mutation_type_AAtildeD(dg_tmp2, ret_conn_vert=True)
|
914
911
|
|
915
912
|
if type_tmp1 == 'unknown' or type_tmp2 == 'unknown':
|
916
913
|
return _false_return()
|
917
914
|
|
918
915
|
# Assuming that the two components are recognized, initialize this in a format it can be returned as output
|
919
916
|
type_tmp = []
|
920
|
-
type_tmp.append(
|
917
|
+
type_tmp.append([type_tmp1[0], type_tmp2[0]])
|
921
918
|
type_tmp[0].sort(key=str)
|
922
|
-
type_tmp.append(
|
919
|
+
type_tmp.append(type_tmp1[1] + type_tmp2[1])
|
923
920
|
type_tmp[1].sort(key=str)
|
924
921
|
|
925
922
|
# Need to make sure the two vertices in c2 are both 'connecting vertices'.
|
@@ -929,52 +926,52 @@ def _connected_mutation_type_AAtildeD(dg, ret_conn_vert=False):
|
|
929
926
|
if type_tmp[0][0].letter() == 'A' and type_tmp[0][0].is_finite() and type_tmp[0][1].letter() == 'A' and type_tmp[0][1].is_finite():
|
930
927
|
if ret_conn_vert:
|
931
928
|
type_tmp[1].extend(c1)
|
932
|
-
#type_tmp[1].remove(c2[0])
|
933
|
-
#type_tmp[1].remove(c2[1])
|
934
|
-
return [
|
929
|
+
# type_tmp[1].remove(c2[0])
|
930
|
+
# type_tmp[1].remove(c2[1])
|
931
|
+
return [QuiverMutationType(['D', n]), type_tmp[1]]
|
935
932
|
else:
|
936
|
-
return QuiverMutationType(
|
933
|
+
return QuiverMutationType(['D', n])
|
937
934
|
|
938
935
|
# Exception 2 case (triangulated square):
|
939
|
-
if sg.is_isomorphic(
|
940
|
-
dg_tmp = DiGraph(
|
941
|
-
dg_tmp.delete_vertices(
|
942
|
-
if tuple(
|
943
|
-
dg_tmp.delete_edge(
|
936
|
+
if sg.is_isomorphic(exception_graph2):
|
937
|
+
dg_tmp = DiGraph(dg)
|
938
|
+
dg_tmp.delete_vertices(c1)
|
939
|
+
if tuple(c2) in dg_tmp.edges(sort=True, labels=False):
|
940
|
+
dg_tmp.delete_edge(tuple(c2))
|
944
941
|
else:
|
945
942
|
c2.reverse()
|
946
|
-
dg_tmp.delete_edge(
|
943
|
+
dg_tmp.delete_edge(tuple(c2))
|
947
944
|
components = dg_tmp.connected_components(sort=False)
|
948
945
|
if len(components) != 2:
|
949
946
|
return _false_return(7)
|
950
947
|
else:
|
951
|
-
dg_tmp1 = dg_tmp.subgraph(
|
952
|
-
type_tmp1 = _connected_mutation_type_AAtildeD(
|
948
|
+
dg_tmp1 = dg_tmp.subgraph(components[0])
|
949
|
+
type_tmp1 = _connected_mutation_type_AAtildeD(dg_tmp1, ret_conn_vert=True)
|
953
950
|
|
954
951
|
if type_tmp1 == 'unknown':
|
955
952
|
return _false_return()
|
956
|
-
dg_tmp2 = dg_tmp.subgraph(
|
957
|
-
type_tmp2 = _connected_mutation_type_AAtildeD(
|
953
|
+
dg_tmp2 = dg_tmp.subgraph(components[1])
|
954
|
+
type_tmp2 = _connected_mutation_type_AAtildeD(dg_tmp2, ret_conn_vert=True)
|
958
955
|
|
959
956
|
# Assuming that the two components are recognized, initialize this in
|
960
957
|
# a format it can be returned as output (just as above)
|
961
958
|
type_tmp = []
|
962
|
-
type_tmp.append(
|
959
|
+
type_tmp.append([type_tmp1[0], type_tmp2[0]])
|
963
960
|
type_tmp[0].sort(key=str)
|
964
|
-
type_tmp.append(
|
961
|
+
type_tmp.append(type_tmp1[1] + type_tmp2[1])
|
965
962
|
type_tmp[1].sort(key=str)
|
966
963
|
if type_tmp2 == 'unknown':
|
967
964
|
return _false_return()
|
968
|
-
if not set(c2).issubset(type_tmp[1]) and len(
|
965
|
+
if not set(c2).issubset(type_tmp[1]) and len(set(type_tmp[1]).intersection(c2)) == 1:
|
969
966
|
return _false_return(5.5)
|
970
967
|
if type_tmp[0][0].letter() == 'A' and type_tmp[0][0].is_finite() and type_tmp[0][1].letter() == 'A' and type_tmp[0][1].is_finite():
|
971
968
|
if ret_conn_vert:
|
972
969
|
type_tmp[1].remove(c2[0])
|
973
970
|
type_tmp[1].remove(c2[1])
|
974
|
-
#type_tmp[1].extend(c1)
|
975
|
-
return [
|
971
|
+
# type_tmp[1].extend(c1)
|
972
|
+
return [QuiverMutationType(['D', n]), type_tmp[1]]
|
976
973
|
else:
|
977
|
-
return QuiverMutationType(
|
974
|
+
return QuiverMutationType(['D', n])
|
978
975
|
|
979
976
|
# The following tests are done regardless of the number of vertices in dg.
|
980
977
|
# If there are 1, 2, or 3 vertices in dg, we would have skipped above tests and gone directly here.
|
@@ -987,7 +984,7 @@ def _connected_mutation_type_AAtildeD(dg, ret_conn_vert=False):
|
|
987
984
|
multiple_edges = dg.multiple_edges(labels=False)
|
988
985
|
if len(multiple_edges) > 2:
|
989
986
|
return _false_return(14)
|
990
|
-
|
987
|
+
if len(multiple_edges) == 2:
|
991
988
|
# we think of the double-edge as a long_cycle, an unoriented 2-cycle.
|
992
989
|
long_cycle = [multiple_edges, ['A', n - 1, 1]]
|
993
990
|
|
@@ -997,145 +994,148 @@ def _connected_mutation_type_AAtildeD(dg, ret_conn_vert=False):
|
|
997
994
|
dict_in_out[v] = (dg.in_degree(v), dg.out_degree(v), dg.degree(v))
|
998
995
|
|
999
996
|
# computing the absolute degree of dg
|
1000
|
-
abs_deg = max(
|
997
|
+
abs_deg = max([x[2] for x in list(dict_in_out.values())])
|
1001
998
|
|
1002
|
-
# edges = dg.edges(sort=True, labels=False
|
999
|
+
# edges = dg.edges(sort=True, labels=False)
|
1003
1000
|
|
1004
1001
|
# test that no vertex has valency more than 4
|
1005
1002
|
if abs_deg > 4:
|
1006
1003
|
return _false_return(16)
|
1007
|
-
else:
|
1008
|
-
# constructing all oriented and unoriented triangles
|
1009
|
-
trians = _triangles( dg )
|
1010
|
-
oriented_trians = [ trian[0] for trian in trians if trian[1] ]
|
1011
|
-
unoriented_trians = [ trian[0] for trian in trians if not trian[1] ]
|
1012
|
-
|
1013
|
-
oriented_trian_edges = []
|
1014
|
-
for oriented_trian in oriented_trians:
|
1015
|
-
oriented_trian_edges.extend( oriented_trian )
|
1016
|
-
|
1017
|
-
# test that no edge is in more than two oriented triangles
|
1018
|
-
multiple_trian_edges = []
|
1019
|
-
for edge in oriented_trian_edges:
|
1020
|
-
count = oriented_trian_edges.count(edge)
|
1021
|
-
if count > 2:
|
1022
|
-
return _false_return(17)
|
1023
|
-
elif count == 2:
|
1024
|
-
multiple_trian_edges.append( edge )
|
1025
|
-
multiple_trian_edges = list(set(multiple_trian_edges))
|
1026
|
-
|
1027
|
-
# test that there at most three edges appearing in exactly two oriented triangles
|
1028
|
-
count = len(multiple_trian_edges)
|
1029
|
-
if count >= 4:
|
1030
|
-
return _false_return(321)
|
1031
|
-
# if two edges appearing in exactly two oriented triangles, test that the two edges together
|
1032
|
-
# determine a unique triangle
|
1033
|
-
elif count > 1:
|
1034
|
-
test_triangles = [[tuple(trian) for trian in oriented_trians
|
1035
|
-
if edge in trian]
|
1036
|
-
for edge in multiple_trian_edges]
|
1037
|
-
unique_triangle = set.intersection(*map(set, test_triangles))
|
1038
|
-
if len(unique_triangle) != 1:
|
1039
|
-
return _false_return(19)
|
1040
|
-
else:
|
1041
|
-
# if a long_cycle had previously been found, this unique oriented triangle is a second long_cycle, a contradiction.
|
1042
|
-
if long_cycle:
|
1043
|
-
return _false_return(20)
|
1044
|
-
else:
|
1045
|
-
unique_triangle = unique_triangle.pop()
|
1046
|
-
long_cycle = [ unique_triangle, QuiverMutationType( ['D',n] ) ]
|
1047
|
-
# if one edge appearing in exactly two oriented triangles, test that it is not a double-edge and then
|
1048
|
-
# test that either the third or fourth vertices (from the oriented triangles) is of degree 2.
|
1049
|
-
# Then initializes the long_cycle as this triangle including the degree 2 vertex, as long as no other long_cycles.
|
1050
|
-
elif count == 1 and not dg.has_multiple_edges() and multiple_trian_edges[0] not in dg.multiple_edges():
|
1051
|
-
multiple_trian_edge = multiple_trian_edges[0]
|
1052
|
-
neighbors = list(set(dg.neighbors( multiple_trian_edge[0] )).intersection(dg.neighbors( multiple_trian_edge[1] )))
|
1053
|
-
if dg.degree( neighbors[0] ) == 2:
|
1054
|
-
unique_triangle = [ multiple_trian_edge, ( multiple_trian_edge[1], neighbors[0] ), ( neighbors[0], multiple_trian_edge[0] ) ]
|
1055
|
-
elif dg.degree( neighbors[1] ) == 2:
|
1056
|
-
unique_triangle = [ multiple_trian_edge, ( multiple_trian_edge[1], neighbors[1] ), ( neighbors[1], multiple_trian_edge[0] ) ]
|
1057
|
-
else:
|
1058
|
-
return _false_return(201)
|
1059
1004
|
|
1005
|
+
# constructing all oriented and unoriented triangles
|
1006
|
+
trians = _triangles(dg)
|
1007
|
+
oriented_trians = [trian[0] for trian in trians if trian[1]]
|
1008
|
+
unoriented_trians = [trian[0] for trian in trians if not trian[1]]
|
1009
|
+
|
1010
|
+
oriented_trian_edges = []
|
1011
|
+
for oriented_trian in oriented_trians:
|
1012
|
+
oriented_trian_edges.extend(oriented_trian)
|
1013
|
+
|
1014
|
+
# test that no edge is in more than two oriented triangles
|
1015
|
+
from collections import Counter
|
1016
|
+
edge_count = Counter(oriented_trian_edges)
|
1017
|
+
multiple_trian_edges = []
|
1018
|
+
for edge, count in edge_count.items():
|
1019
|
+
if count > 2:
|
1020
|
+
return _false_return(17)
|
1021
|
+
elif count == 2:
|
1022
|
+
multiple_trian_edges.append(edge)
|
1023
|
+
multiple_trian_edges = list(set(multiple_trian_edges))
|
1024
|
+
|
1025
|
+
# test that there at most three edges appearing in exactly two
|
1026
|
+
# oriented triangles
|
1027
|
+
count = len(multiple_trian_edges)
|
1028
|
+
if count >= 4:
|
1029
|
+
return _false_return(321)
|
1030
|
+
|
1031
|
+
# if two edges appearing in exactly two oriented triangles, test
|
1032
|
+
# that the two edges together determine a unique triangle
|
1033
|
+
if count > 1:
|
1034
|
+
test_triangles = [[tuple(trian) for trian in oriented_trians
|
1035
|
+
if edge in trian]
|
1036
|
+
for edge in multiple_trian_edges]
|
1037
|
+
unique_triangle_set = set.intersection(*map(set, test_triangles))
|
1038
|
+
if len(unique_triangle_set) != 1:
|
1039
|
+
return _false_return(19)
|
1040
|
+
|
1041
|
+
# if a long_cycle had previously been found, this unique oriented triangle is a second long_cycle, a contradiction.
|
1042
|
+
if long_cycle:
|
1043
|
+
return _false_return(20)
|
1044
|
+
|
1045
|
+
unique_triangle = unique_triangle_set.pop()
|
1046
|
+
long_cycle = [unique_triangle, QuiverMutationType(['D', n])]
|
1047
|
+
# if one edge appearing in exactly two oriented triangles, test that it is not a double-edge and then
|
1048
|
+
# test that either the third or fourth vertices (from the oriented triangles) is of degree 2.
|
1049
|
+
# Then initializes the long_cycle as this triangle including the degree 2 vertex, as long as no other long_cycles.
|
1050
|
+
elif count == 1 and not dg.has_multiple_edges() and multiple_trian_edges[0] not in dg.multiple_edges():
|
1051
|
+
multiple_trian_edge = multiple_trian_edges[0]
|
1052
|
+
neighbors = list(set(dg.neighbors(multiple_trian_edge[0])).intersection(dg.neighbors(multiple_trian_edge[1])))
|
1053
|
+
if dg.degree(neighbors[0]) == 2:
|
1054
|
+
unique_triangle = [multiple_trian_edge, (multiple_trian_edge[1], neighbors[0]), (neighbors[0], multiple_trian_edge[0])]
|
1055
|
+
elif dg.degree(neighbors[1]) == 2:
|
1056
|
+
unique_triangle = [multiple_trian_edge, (multiple_trian_edge[1], neighbors[1]), (neighbors[1], multiple_trian_edge[0])]
|
1057
|
+
else:
|
1058
|
+
return _false_return(201)
|
1059
|
+
|
1060
|
+
if long_cycle:
|
1061
|
+
# if a long_cycle had previously been found, then the specified oriented triangle is a second long_cycle, a contradiction.
|
1062
|
+
return _false_return(202)
|
1063
|
+
else:
|
1064
|
+
long_cycle = [unique_triangle, QuiverMutationType(['D', n])]
|
1065
|
+
|
1066
|
+
# there can be at most 1 unoriented triangle and this triangle is the exceptional circle of type A_tilde
|
1067
|
+
if unoriented_trians:
|
1068
|
+
if len(unoriented_trians) == 1:
|
1060
1069
|
if long_cycle:
|
1061
|
-
|
1062
|
-
return _false_return(202)
|
1070
|
+
return _false_return(21)
|
1063
1071
|
else:
|
1064
|
-
long_cycle = [
|
1072
|
+
long_cycle = [unoriented_trians[0], ['A', n - 1, 1]]
|
1073
|
+
else:
|
1074
|
+
return _false_return(22)
|
1065
1075
|
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
long_cycle = [ unoriented_trians[0], ['A',n-1,1] ]
|
1076
|
+
for v in vertices:
|
1077
|
+
w = dict_in_out[v]
|
1078
|
+
if w[2] == 4:
|
1079
|
+
# if a vertex has valency 4 than the 4 neighboring edges must be contained in 2 oriented triangles
|
1080
|
+
if w[0] != 2:
|
1081
|
+
return _false_return(23)
|
1073
1082
|
else:
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1083
|
+
in_neighbors = dg.neighbors_in(v)
|
1084
|
+
out_neighbors = dg.neighbors_out(v)
|
1085
|
+
if len(out_neighbors) == 1:
|
1086
|
+
out_neighbors.extend(out_neighbors)
|
1087
|
+
if len(in_neighbors) == 1:
|
1088
|
+
in_neighbors.extend(in_neighbors)
|
1089
|
+
|
1090
|
+
if (in_neighbors[0], v) not in oriented_trian_edges:
|
1091
|
+
return _false_return(24)
|
1092
|
+
elif (in_neighbors[1], v) not in oriented_trian_edges:
|
1093
|
+
return _false_return(25)
|
1094
|
+
elif (v, out_neighbors[0]) not in oriented_trian_edges:
|
1095
|
+
return _false_return(26)
|
1096
|
+
elif (v, out_neighbors[1]) not in oriented_trian_edges:
|
1097
|
+
return _false_return(27)
|
1098
|
+
|
1099
|
+
# if a vertex has valency 3 than 2 of its neighboring edges must be contained in an oriented triangle and the remaining must not
|
1100
|
+
elif w[2] == 3:
|
1101
|
+
if w[0] == 1:
|
1102
|
+
in_neighbors = dg.neighbors_in(v)
|
1103
|
+
out_neighbors = dg.neighbors_out(v)
|
1104
|
+
if (in_neighbors[0], v) not in oriented_trian_edges:
|
1105
|
+
return _false_return(28)
|
1106
|
+
elif len(out_neighbors) == 1:
|
1107
|
+
if (v, out_neighbors[0]) not in oriented_trian_edges:
|
1108
|
+
return _false_return(29)
|
1082
1109
|
else:
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1110
|
+
if (v, out_neighbors[0]) in oriented_trian_edges and (v, out_neighbors[1]) in oriented_trian_edges:
|
1111
|
+
if not long_cycle:
|
1112
|
+
return _false_return(30)
|
1113
|
+
if not long_cycle[1] == QuiverMutationType(['D', n]):
|
1114
|
+
return _false_return(31)
|
1115
|
+
if (v, out_neighbors[0]) not in long_cycle[0] and (v, out_neighbors[1]) not in long_cycle[0]:
|
1116
|
+
return _false_return(32)
|
1117
|
+
if (v, out_neighbors[0]) not in oriented_trian_edges and (v, out_neighbors[1]) not in oriented_trian_edges:
|
1118
|
+
return _false_return(33)
|
1119
|
+
elif w[0] == 2:
|
1120
|
+
in_neighbors = dg.neighbors_in(v)
|
1121
|
+
out_neighbors = dg.neighbors_out(v)
|
1122
|
+
if (v, out_neighbors[0]) not in oriented_trian_edges:
|
1123
|
+
return _false_return(34)
|
1124
|
+
elif len(in_neighbors) == 1:
|
1090
1125
|
if (in_neighbors[0], v) not in oriented_trian_edges:
|
1091
|
-
return _false_return(
|
1092
|
-
elif (in_neighbors[1], v) not in oriented_trian_edges:
|
1093
|
-
return _false_return(25)
|
1094
|
-
elif (v, out_neighbors[0]) not in oriented_trian_edges:
|
1095
|
-
return _false_return(26)
|
1096
|
-
elif (v, out_neighbors[1]) not in oriented_trian_edges:
|
1097
|
-
return _false_return(27)
|
1098
|
-
|
1099
|
-
# if a vertex has valency 3 than 2 of its neighboring edges must be contained in an oriented triangle and the remaining must not
|
1100
|
-
elif w[2] == 3:
|
1101
|
-
if w[0] == 1:
|
1102
|
-
in_neighbors = dg.neighbors_in( v )
|
1103
|
-
out_neighbors = dg.neighbors_out( v )
|
1104
|
-
if (in_neighbors[0],v) not in oriented_trian_edges:
|
1105
|
-
return _false_return(28)
|
1106
|
-
elif len( out_neighbors ) == 1:
|
1107
|
-
if (v,out_neighbors[0]) not in oriented_trian_edges:
|
1108
|
-
return _false_return(29)
|
1109
|
-
else:
|
1110
|
-
if (v,out_neighbors[0]) in oriented_trian_edges and (v,out_neighbors[1]) in oriented_trian_edges:
|
1111
|
-
if not long_cycle:
|
1112
|
-
return _false_return(30)
|
1113
|
-
if not long_cycle[1] == QuiverMutationType(['D',n]):
|
1114
|
-
return _false_return(31)
|
1115
|
-
if (v, out_neighbors[0]) not in long_cycle[0] and (v, out_neighbors[1]) not in long_cycle[0]:
|
1116
|
-
return _false_return(32)
|
1117
|
-
if (v,out_neighbors[0]) not in oriented_trian_edges and (v,out_neighbors[1]) not in oriented_trian_edges:
|
1118
|
-
return _false_return(33)
|
1119
|
-
elif w[0] == 2:
|
1120
|
-
in_neighbors = dg.neighbors_in( v )
|
1121
|
-
out_neighbors = dg.neighbors_out( v )
|
1122
|
-
if (v, out_neighbors[0]) not in oriented_trian_edges:
|
1123
|
-
return _false_return(34)
|
1124
|
-
elif len( in_neighbors ) == 1:
|
1125
|
-
if (in_neighbors[0],v) not in oriented_trian_edges:
|
1126
|
-
return _false_return(35)
|
1127
|
-
else:
|
1128
|
-
if (in_neighbors[0],v) in oriented_trian_edges and (in_neighbors[1],v) in oriented_trian_edges:
|
1129
|
-
if not long_cycle:
|
1130
|
-
return _false_return(36)
|
1131
|
-
if not long_cycle[1] == QuiverMutationType(['D',n]):
|
1132
|
-
return _false_return(37)
|
1133
|
-
if (in_neighbors[0], v) not in long_cycle[0] and (in_neighbors[1], v) not in long_cycle[0]:
|
1134
|
-
return _false_return(38)
|
1135
|
-
if (in_neighbors[0], v) not in oriented_trian_edges and (in_neighbors[1], v) not in oriented_trian_edges:
|
1136
|
-
return _false_return(39)
|
1126
|
+
return _false_return(35)
|
1137
1127
|
else:
|
1138
|
-
|
1128
|
+
if (in_neighbors[0], v) in oriented_trian_edges and (in_neighbors[1], v) in oriented_trian_edges:
|
1129
|
+
if not long_cycle:
|
1130
|
+
return _false_return(36)
|
1131
|
+
if not long_cycle[1] == QuiverMutationType(['D', n]):
|
1132
|
+
return _false_return(37)
|
1133
|
+
if (in_neighbors[0], v) not in long_cycle[0] and (in_neighbors[1], v) not in long_cycle[0]:
|
1134
|
+
return _false_return(38)
|
1135
|
+
if (in_neighbors[0], v) not in oriented_trian_edges and (in_neighbors[1], v) not in oriented_trian_edges:
|
1136
|
+
return _false_return(39)
|
1137
|
+
else:
|
1138
|
+
return _false_return(40)
|
1139
1139
|
|
1140
1140
|
# there can exist at most one larger oriented or unoriented induced cycle
|
1141
1141
|
# if it is oriented, we are in finite type D, otherwise we are in affine type A
|
@@ -1143,15 +1143,15 @@ def _connected_mutation_type_AAtildeD(dg, ret_conn_vert=False):
|
|
1143
1143
|
# Above code found long_cycles would be an unoriented 2-cycle or an oriented triangle.
|
1144
1144
|
# The method _all_induced_cycles_iter only looks for induced cycles on 4 or more vertices.
|
1145
1145
|
|
1146
|
-
for cycle, is_oriented in _all_induced_cycles_iter(
|
1146
|
+
for cycle, is_oriented in _all_induced_cycles_iter(dg):
|
1147
1147
|
# if there already was a long_cycle and we found another one, then have a contradiction.
|
1148
1148
|
if long_cycle:
|
1149
1149
|
return _false_return(41)
|
1150
1150
|
# otherwise, we obtain cases depending on whether or not the found long_cycle is oriented.
|
1151
1151
|
elif is_oriented:
|
1152
|
-
long_cycle = [
|
1152
|
+
long_cycle = [cycle, QuiverMutationType(['D', n])]
|
1153
1153
|
else:
|
1154
|
-
long_cycle = [
|
1154
|
+
long_cycle = [cycle, ['A', n - 1, 1]]
|
1155
1155
|
# if we haven't found a "long_cycle", we are in finite type A
|
1156
1156
|
if not long_cycle:
|
1157
1157
|
long_cycle = [[], QuiverMutationType(['A', n])]
|
@@ -1161,24 +1161,24 @@ def _connected_mutation_type_AAtildeD(dg, ret_conn_vert=False):
|
|
1161
1161
|
# this is not caught here.
|
1162
1162
|
if ret_conn_vert:
|
1163
1163
|
connecting_vertices = []
|
1164
|
-
o_trian_verts = flatten(
|
1165
|
-
long_cycle_verts = flatten(
|
1164
|
+
o_trian_verts = flatten(oriented_trian_edges)
|
1165
|
+
long_cycle_verts = flatten(long_cycle[0])
|
1166
1166
|
for v in vertices:
|
1167
1167
|
w = dict_in_out[v]
|
1168
1168
|
# if the quiver consists of only one vertex, it is of type A_1 and the vertex is a connecting vertex
|
1169
1169
|
if w[2] == 0:
|
1170
|
-
connecting_vertices.append(
|
1170
|
+
connecting_vertices.append(v)
|
1171
1171
|
# if a vertex is a leaf in a type A quiver, it is a connecting vertex
|
1172
1172
|
elif w[2] == 1:
|
1173
|
-
connecting_vertices.append(
|
1173
|
+
connecting_vertices.append(v)
|
1174
1174
|
# if a vertex is of valence two and contained in an oriented 3-cycle, it is a connecting vertex
|
1175
1175
|
elif w[0] == 1 and w[1] == 1:
|
1176
1176
|
if v in o_trian_verts and v not in long_cycle_verts:
|
1177
|
-
connecting_vertices.append(
|
1177
|
+
connecting_vertices.append(v)
|
1178
1178
|
|
1179
1179
|
# post-parsing 1: if we are in the affine type A case, the two parameters for the non-oriented long cycle are computed
|
1180
|
-
if isinstance(long_cycle[1], list) and len(
|
1181
|
-
tmp = list(
|
1180
|
+
if isinstance(long_cycle[1], list) and len(long_cycle[1]) == 3 and long_cycle[1][0] == 'A' and long_cycle[1][2] == 1:
|
1181
|
+
tmp = list(long_cycle[0])
|
1182
1182
|
e = tmp.pop()
|
1183
1183
|
cycle = [e]
|
1184
1184
|
v = e[1]
|
@@ -1189,52 +1189,51 @@ def _connected_mutation_type_AAtildeD(dg, ret_conn_vert=False):
|
|
1189
1189
|
v = e[1]
|
1190
1190
|
else:
|
1191
1191
|
v = e[0]
|
1192
|
-
tmp.remove(
|
1192
|
+
tmp.remove(e)
|
1193
1193
|
|
1194
|
-
tmp = list(
|
1195
|
-
if len(
|
1194
|
+
tmp = list(cycle)
|
1195
|
+
if len(long_cycle[0]) == 2:
|
1196
1196
|
edge = long_cycle[0][0]
|
1197
|
-
sg = DiGraph(
|
1197
|
+
sg = DiGraph(dg)
|
1198
1198
|
sg. delete_vertices(edge)
|
1199
1199
|
connected_components = sg.connected_components(sort=False)
|
1200
1200
|
cycle = []
|
1201
1201
|
if connected_components:
|
1202
|
-
cycle.append(
|
1202
|
+
cycle.append((edge[0], edge[1], len(connected_components[0]) + 1))
|
1203
1203
|
else:
|
1204
|
-
cycle.append(
|
1204
|
+
cycle.append((edge[0], edge[1], 1))
|
1205
1205
|
else:
|
1206
1206
|
for edge in tmp:
|
1207
|
-
sg = DiGraph(
|
1207
|
+
sg = DiGraph(dg)
|
1208
1208
|
sg. delete_vertices(edge)
|
1209
1209
|
connected_components = sg.connected_components(sort=False)
|
1210
|
-
if len(
|
1211
|
-
#if len(
|
1212
|
-
if len(
|
1210
|
+
if len(connected_components) == 2:
|
1211
|
+
# if len(list_intersection([connected_components[0], list_substract(long_cycle[0], [edge])[0]])) > 0:
|
1212
|
+
if len(set(connected_components[0]).intersection(set(long_cycle[0]).difference([edge]).pop())) > 0:
|
1213
1213
|
cycle.remove(edge)
|
1214
|
-
cycle.append(
|
1214
|
+
cycle.append((edge[0], edge[1], len(connected_components[1]) + 1))
|
1215
1215
|
else:
|
1216
1216
|
cycle.remove(edge)
|
1217
|
-
cycle.append(
|
1217
|
+
cycle.append((edge[0], edge[1], len(connected_components[0]) + 1))
|
1218
1218
|
else:
|
1219
1219
|
cycle.remove(edge)
|
1220
|
-
cycle.append(
|
1220
|
+
cycle.append((edge[0], edge[1], 1))
|
1221
1221
|
r = sum(x[2] for x in cycle)
|
1222
1222
|
r = max(r, n - r)
|
1223
1223
|
if ret_conn_vert:
|
1224
|
-
return [
|
1225
|
-
|
1226
|
-
|
1224
|
+
return [QuiverMutationType(['A', [r, n - r], 1]),
|
1225
|
+
connecting_vertices]
|
1226
|
+
return QuiverMutationType(['A', [r, n - r], 1])
|
1227
1227
|
|
1228
1228
|
# post-parsing 2: if we are in another type, it is returned
|
1229
1229
|
else:
|
1230
1230
|
if ret_conn_vert:
|
1231
|
-
return [
|
1232
|
-
|
1233
|
-
return long_cycle[1]
|
1231
|
+
return [long_cycle[1], connecting_vertices]
|
1232
|
+
return long_cycle[1]
|
1234
1233
|
|
1235
1234
|
|
1236
1235
|
@cached_function
|
1237
|
-
def load_data(n, user=True):
|
1236
|
+
def load_data(n: int, user=True) -> dict:
|
1238
1237
|
r"""
|
1239
1238
|
Load a dict with keys being tuples representing exceptional
|
1240
1239
|
QuiverMutationTypes, and with values being lists or sets
|
@@ -1254,28 +1253,28 @@ def load_data(n, user=True):
|
|
1254
1253
|
|
1255
1254
|
sage: from sage.combinat.cluster_algebra_quiver.mutation_type import load_data
|
1256
1255
|
sage: load_data(2) # random - depends on the data the user has stored
|
1257
|
-
{('G', 2): [('AO', (((0, 1), (1, -3)),)), ('AO', (((0, 1), (3, -1)),))]}
|
1256
|
+
{('G', 2): [('AO', (((0, 1), (1, -3)), )), ('AO', (((0, 1), (3, -1)), ))]}
|
1258
1257
|
|
1259
1258
|
TESTS:
|
1260
1259
|
|
1261
1260
|
We test data from the ``database_mutation_class`` optional package::
|
1262
1261
|
|
1263
1262
|
sage: load_data(2, user=False) # optional - database_mutation_class
|
1264
|
-
{('G', 2): [('AO', (((0, 1), (1, -3)),)), ('AO', (((0, 1), (3, -1)),))]}
|
1263
|
+
{('G', 2): [('AO', (((0, 1), (1, -3)), )), ('AO', (((0, 1), (3, -1)), ))]}
|
1265
1264
|
sage: D = load_data(3, user=False) # optional - database_mutation_class
|
1266
1265
|
sage: sorted(D.items()) # optional - database_mutation_class
|
1267
1266
|
[(('G', 2, -1),
|
1268
|
-
[('BH?', (((1, 2), (1, -3)),)),
|
1269
|
-
('BGO', (((2, 1), (3, -1)),)),
|
1270
|
-
('BW?', (((0, 1), (3, -1)),)),
|
1271
|
-
('BP?', (((0, 1), (1, -3)),)),
|
1267
|
+
[('BH?', (((1, 2), (1, -3)), )),
|
1268
|
+
('BGO', (((2, 1), (3, -1)), )),
|
1269
|
+
('BW?', (((0, 1), (3, -1)), )),
|
1270
|
+
('BP?', (((0, 1), (1, -3)), )),
|
1272
1271
|
('BP_', (((0, 1), (1, -3)), ((2, 0), (3, -1)))),
|
1273
1272
|
('BP_', (((0, 1), (3, -1)), ((1, 2), (1, -3)), ((2, 0), (2, -2))))]),
|
1274
1273
|
(('G', 2, 1),
|
1275
|
-
[('BH?', (((1, 2), (3, -1)),)),
|
1276
|
-
('BGO', (((2, 1), (1, -3)),)),
|
1277
|
-
('BW?', (((0, 1), (1, -3)),)),
|
1278
|
-
('BP?', (((0, 1), (3, -1)),)),
|
1274
|
+
[('BH?', (((1, 2), (3, -1)), )),
|
1275
|
+
('BGO', (((2, 1), (1, -3)), )),
|
1276
|
+
('BW?', (((0, 1), (1, -3)), )),
|
1277
|
+
('BP?', (((0, 1), (3, -1)), )),
|
1279
1278
|
('BKO', (((1, 0), (3, -1)), ((2, 1), (1, -3)))),
|
1280
1279
|
('BP_', (((0, 1), (2, -2)), ((1, 2), (1, -3)), ((2, 0), (3, -1))))])]
|
1281
1280
|
"""
|
@@ -1301,10 +1300,10 @@ def load_data(n, user=True):
|
|
1301
1300
|
return data
|
1302
1301
|
|
1303
1302
|
|
1304
|
-
def _mutation_type_from_data(n, dig6, compute_if_necessary=True):
|
1303
|
+
def _mutation_type_from_data(n: int, dig6, compute_if_necessary=True):
|
1305
1304
|
r"""
|
1306
1305
|
Return the mutation type from the given dig6 data by looking into
|
1307
|
-
the precomputed mutation types
|
1306
|
+
the precomputed mutation types.
|
1308
1307
|
|
1309
1308
|
Attention: it is assumed that dig6 is the dig6 data of the
|
1310
1309
|
canonical form of the given quiver!
|
@@ -1315,10 +1314,10 @@ def _mutation_type_from_data(n, dig6, compute_if_necessary=True):
|
|
1315
1314
|
sage: from sage.combinat.cluster_algebra_quiver.mutation_class import _digraph_to_dig6
|
1316
1315
|
sage: from sage.combinat.cluster_algebra_quiver.mutation_type import _mutation_type_from_data
|
1317
1316
|
sage: from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver
|
1318
|
-
sage: dg = ClusterQuiver(['F',4]).canonical_label().digraph()
|
1319
|
-
sage: dig6 = _digraph_to_dig6(dg,hashable=True); dig6
|
1317
|
+
sage: dg = ClusterQuiver(['F', 4]).canonical_label().digraph()
|
1318
|
+
sage: dig6 = _digraph_to_dig6(dg, hashable=True); dig6
|
1320
1319
|
('CCo?', (((1, 3), (2, -1)),))
|
1321
|
-
sage: _mutation_type_from_data(4,dig6)
|
1320
|
+
sage: _mutation_type_from_data(4, dig6)
|
1322
1321
|
['F', 4]
|
1323
1322
|
"""
|
1324
1323
|
# we try to load the data from a library
|
@@ -1331,8 +1330,8 @@ def _mutation_type_from_data(n, dig6, compute_if_necessary=True):
|
|
1331
1330
|
data = load_data(n)
|
1332
1331
|
# finally, we check if the given quiver is in one of the exceptional mutation classes
|
1333
1332
|
for mutation_type in data:
|
1334
|
-
if dig6 in data[
|
1335
|
-
return QuiverMutationType(
|
1333
|
+
if dig6 in data[mutation_type]:
|
1334
|
+
return QuiverMutationType(mutation_type)
|
1336
1335
|
return 'unknown'
|
1337
1336
|
|
1338
1337
|
|
@@ -1401,17 +1400,17 @@ def _mutation_type_test(n):
|
|
1401
1400
|
from sage.combinat.cluster_algebra_quiver.quiver_mutation_type import _construct_classical_mutation_classes
|
1402
1401
|
from sage.combinat.cluster_algebra_quiver.mutation_class import _dig6_to_digraph
|
1403
1402
|
from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver
|
1404
|
-
data = _construct_classical_mutation_classes(
|
1403
|
+
data = _construct_classical_mutation_classes(n)
|
1405
1404
|
keys = data.keys()
|
1406
1405
|
for mutation_type in sorted(keys, key=str):
|
1407
|
-
mt = QuiverMutationType(
|
1408
|
-
print(all(
|
1406
|
+
mt = QuiverMutationType(mutation_type)
|
1407
|
+
print(all(ClusterQuiver(_dig6_to_digraph(dig6)).mutation_type() == mt for dig6 in data[mutation_type]), mutation_type)
|
1409
1408
|
from sage.combinat.cluster_algebra_quiver.quiver_mutation_type import _construct_exceptional_mutation_classes
|
1410
|
-
data = _construct_exceptional_mutation_classes(
|
1409
|
+
data = _construct_exceptional_mutation_classes(n)
|
1411
1410
|
keys = data.keys()
|
1412
1411
|
for mutation_type in sorted(keys, key=str):
|
1413
|
-
mt = QuiverMutationType(
|
1414
|
-
print(all(
|
1412
|
+
mt = QuiverMutationType(mutation_type)
|
1413
|
+
print(all(ClusterQuiver(_dig6_to_digraph(dig6)).mutation_type() == mt for dig6 in data[mutation_type]), mutation_type)
|
1415
1414
|
|
1416
1415
|
|
1417
1416
|
def _random_tests(mt, k, mut_class=None, nr_mut=5):
|
@@ -1436,7 +1435,7 @@ def _random_tests(mt, k, mut_class=None, nr_mut=5):
|
|
1436
1435
|
TESTS::
|
1437
1436
|
|
1438
1437
|
sage: from sage.combinat.cluster_algebra_quiver.mutation_type import _random_tests
|
1439
|
-
sage: _random_tests(
|
1438
|
+
sage: _random_tests(['A', 3], 1) # needs sage.modules
|
1440
1439
|
testing ['A', 3]
|
1441
1440
|
"""
|
1442
1441
|
from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver
|
@@ -1446,15 +1445,15 @@ def _random_tests(mt, k, mut_class=None, nr_mut=5):
|
|
1446
1445
|
mut_class = ClusterQuiver(mt).mutation_class(data_type='dig6')
|
1447
1446
|
print("testing " + str(mt))
|
1448
1447
|
for dig6 in mut_class:
|
1449
|
-
M_const = _dig6_to_matrix(
|
1450
|
-
nz = [
|
1448
|
+
M_const = _dig6_to_matrix(dig6)
|
1449
|
+
nz = [(i, j) for i, j in M_const.nonzero_positions() if i > j]
|
1451
1450
|
# performing k tests on the matrix M_const
|
1452
1451
|
for i in range(k):
|
1453
|
-
M = copy(
|
1454
|
-
# every pair M[i,j],M[j,i] is possibly changed
|
1452
|
+
M = copy(M_const)
|
1453
|
+
# every pair M[i, j], M[j, i] is possibly changed
|
1455
1454
|
# while the property of being skew-symmetrizable is kept
|
1456
|
-
for i,j in nz:
|
1457
|
-
a,b = M[i,j],M[j,i]
|
1455
|
+
for i, j in nz:
|
1456
|
+
a, b = M[i, j], M[j, i]
|
1458
1457
|
skew_sym = False
|
1459
1458
|
while not skew_sym:
|
1460
1459
|
ran = random.randint(1, 2)
|
@@ -1521,13 +1520,13 @@ def _random_multi_tests(n, k, nr_mut=5):
|
|
1521
1520
|
TESTS::
|
1522
1521
|
|
1523
1522
|
sage: from sage.combinat.cluster_algebra_quiver.mutation_type import _random_multi_tests
|
1524
|
-
sage: _random_multi_tests(2,1) # not tested
|
1523
|
+
sage: _random_multi_tests(2, 1) # not tested
|
1525
1524
|
testing ('A', (1, 1), 1)
|
1526
1525
|
testing ('A', 2)
|
1527
1526
|
testing ('B', 2)
|
1528
1527
|
testing ('BC', 1, 1)
|
1529
1528
|
|
1530
|
-
sage: _random_multi_tests(3,1) # not tested
|
1529
|
+
sage: _random_multi_tests(3, 1) # not tested
|
1531
1530
|
testing ('A', (2, 1), 1)
|
1532
1531
|
testing ('A', 3)
|
1533
1532
|
testing ('B', 3)
|
@@ -1536,7 +1535,7 @@ def _random_multi_tests(n, k, nr_mut=5):
|
|
1536
1535
|
testing ('C', 3)
|
1537
1536
|
testing ('CC', 2, 1)
|
1538
1537
|
|
1539
|
-
sage: _random_multi_tests(4,1) # not tested
|
1538
|
+
sage: _random_multi_tests(4, 1) # not tested
|
1540
1539
|
testing ('A', (2, 2), 1)
|
1541
1540
|
testing ('A', (3, 1), 1)
|
1542
1541
|
testing ('A', 4)
|