passagemath-graphs 10.5.43__cp39-cp39-musllinux_1_2_aarch64.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.
Files changed (258) hide show
  1. passagemath_graphs-10.5.43.dist-info/METADATA +293 -0
  2. passagemath_graphs-10.5.43.dist-info/RECORD +258 -0
  3. passagemath_graphs-10.5.43.dist-info/WHEEL +5 -0
  4. passagemath_graphs-10.5.43.dist-info/top_level.txt +2 -0
  5. passagemath_graphs.libs/libgcc_s-69c45f16.so.1 +0 -0
  6. passagemath_graphs.libs/libgmp-8e78bd9b.so.10.5.0 +0 -0
  7. passagemath_graphs.libs/libstdc++-1f1a71be.so.6.0.33 +0 -0
  8. sage/all__sagemath_graphs.py +39 -0
  9. sage/combinat/abstract_tree.py +2552 -0
  10. sage/combinat/all__sagemath_graphs.py +34 -0
  11. sage/combinat/binary_tree.py +5306 -0
  12. sage/combinat/cluster_algebra_quiver/all.py +22 -0
  13. sage/combinat/cluster_algebra_quiver/cluster_seed.py +5208 -0
  14. sage/combinat/cluster_algebra_quiver/interact.py +125 -0
  15. sage/combinat/cluster_algebra_quiver/mutation_class.py +625 -0
  16. sage/combinat/cluster_algebra_quiver/mutation_type.py +1556 -0
  17. sage/combinat/cluster_algebra_quiver/quiver.py +2262 -0
  18. sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py +2468 -0
  19. sage/combinat/designs/MOLS_handbook_data.py +570 -0
  20. sage/combinat/designs/all.py +58 -0
  21. sage/combinat/designs/bibd.py +1655 -0
  22. sage/combinat/designs/block_design.py +1071 -0
  23. sage/combinat/designs/covering_array.py +269 -0
  24. sage/combinat/designs/covering_design.py +534 -0
  25. sage/combinat/designs/database.py +5614 -0
  26. sage/combinat/designs/design_catalog.py +122 -0
  27. sage/combinat/designs/designs_pyx.cpython-39-aarch64-linux-gnu.so +0 -0
  28. sage/combinat/designs/designs_pyx.pxd +21 -0
  29. sage/combinat/designs/designs_pyx.pyx +993 -0
  30. sage/combinat/designs/difference_family.py +3951 -0
  31. sage/combinat/designs/difference_matrices.py +279 -0
  32. sage/combinat/designs/evenly_distributed_sets.cpython-39-aarch64-linux-gnu.so +0 -0
  33. sage/combinat/designs/evenly_distributed_sets.pyx +661 -0
  34. sage/combinat/designs/ext_rep.py +1064 -0
  35. sage/combinat/designs/gen_quadrangles_with_spread.cpython-39-aarch64-linux-gnu.so +0 -0
  36. sage/combinat/designs/gen_quadrangles_with_spread.pyx +339 -0
  37. sage/combinat/designs/group_divisible_designs.py +361 -0
  38. sage/combinat/designs/incidence_structures.py +2357 -0
  39. sage/combinat/designs/latin_squares.py +548 -0
  40. sage/combinat/designs/orthogonal_arrays.py +2243 -0
  41. sage/combinat/designs/orthogonal_arrays_build_recursive.py +1780 -0
  42. sage/combinat/designs/orthogonal_arrays_find_recursive.cpython-39-aarch64-linux-gnu.so +0 -0
  43. sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +966 -0
  44. sage/combinat/designs/resolvable_bibd.py +781 -0
  45. sage/combinat/designs/steiner_quadruple_systems.py +1306 -0
  46. sage/combinat/designs/subhypergraph_search.cpython-39-aarch64-linux-gnu.so +0 -0
  47. sage/combinat/designs/subhypergraph_search.pyx +530 -0
  48. sage/combinat/designs/twographs.py +306 -0
  49. sage/combinat/finite_state_machine.py +14874 -0
  50. sage/combinat/finite_state_machine_generators.py +2006 -0
  51. sage/combinat/graph_path.py +448 -0
  52. sage/combinat/interval_posets.py +3908 -0
  53. sage/combinat/nu_tamari_lattice.py +269 -0
  54. sage/combinat/ordered_tree.py +1446 -0
  55. sage/combinat/posets/all.py +46 -0
  56. sage/combinat/posets/cartesian_product.py +493 -0
  57. sage/combinat/posets/d_complete.py +182 -0
  58. sage/combinat/posets/elements.py +273 -0
  59. sage/combinat/posets/forest.py +30 -0
  60. sage/combinat/posets/hasse_cython.cpython-39-aarch64-linux-gnu.so +0 -0
  61. sage/combinat/posets/hasse_cython.pyx +174 -0
  62. sage/combinat/posets/hasse_diagram.py +3678 -0
  63. sage/combinat/posets/incidence_algebras.py +796 -0
  64. sage/combinat/posets/lattices.py +5119 -0
  65. sage/combinat/posets/linear_extension_iterator.cpython-39-aarch64-linux-gnu.so +0 -0
  66. sage/combinat/posets/linear_extension_iterator.pyx +292 -0
  67. sage/combinat/posets/linear_extensions.py +1039 -0
  68. sage/combinat/posets/mobile.py +275 -0
  69. sage/combinat/posets/moebius_algebra.py +776 -0
  70. sage/combinat/posets/poset_examples.py +2131 -0
  71. sage/combinat/posets/posets.py +9169 -0
  72. sage/combinat/rooted_tree.py +1070 -0
  73. sage/combinat/shard_order.py +239 -0
  74. sage/combinat/tamari_lattices.py +384 -0
  75. sage/combinat/yang_baxter_graph.py +923 -0
  76. sage/databases/all__sagemath_graphs.py +1 -0
  77. sage/databases/knotinfo_db.py +1230 -0
  78. sage/ext_data/all__sagemath_graphs.py +1 -0
  79. sage/ext_data/graphs/graph_plot_js.html +330 -0
  80. sage/ext_data/kenzo/CP2.txt +45 -0
  81. sage/ext_data/kenzo/CP3.txt +349 -0
  82. sage/ext_data/kenzo/CP4.txt +4774 -0
  83. sage/ext_data/kenzo/README.txt +49 -0
  84. sage/ext_data/kenzo/S4.txt +20 -0
  85. sage/graphs/all.py +42 -0
  86. sage/graphs/asteroidal_triples.cpython-39-aarch64-linux-gnu.so +0 -0
  87. sage/graphs/asteroidal_triples.pyx +299 -0
  88. sage/graphs/base/all.py +1 -0
  89. sage/graphs/base/boost_graph.cpython-39-aarch64-linux-gnu.so +0 -0
  90. sage/graphs/base/boost_graph.pxd +106 -0
  91. sage/graphs/base/boost_graph.pyx +3045 -0
  92. sage/graphs/base/c_graph.cpython-39-aarch64-linux-gnu.so +0 -0
  93. sage/graphs/base/c_graph.pxd +106 -0
  94. sage/graphs/base/c_graph.pyx +5096 -0
  95. sage/graphs/base/dense_graph.cpython-39-aarch64-linux-gnu.so +0 -0
  96. sage/graphs/base/dense_graph.pxd +26 -0
  97. sage/graphs/base/dense_graph.pyx +757 -0
  98. sage/graphs/base/graph_backends.cpython-39-aarch64-linux-gnu.so +0 -0
  99. sage/graphs/base/graph_backends.pxd +5 -0
  100. sage/graphs/base/graph_backends.pyx +797 -0
  101. sage/graphs/base/overview.py +85 -0
  102. sage/graphs/base/sparse_graph.cpython-39-aarch64-linux-gnu.so +0 -0
  103. sage/graphs/base/sparse_graph.pxd +90 -0
  104. sage/graphs/base/sparse_graph.pyx +1653 -0
  105. sage/graphs/base/static_dense_graph.cpython-39-aarch64-linux-gnu.so +0 -0
  106. sage/graphs/base/static_dense_graph.pxd +5 -0
  107. sage/graphs/base/static_dense_graph.pyx +1032 -0
  108. sage/graphs/base/static_sparse_backend.cpython-39-aarch64-linux-gnu.so +0 -0
  109. sage/graphs/base/static_sparse_backend.pxd +27 -0
  110. sage/graphs/base/static_sparse_backend.pyx +1580 -0
  111. sage/graphs/base/static_sparse_graph.cpython-39-aarch64-linux-gnu.so +0 -0
  112. sage/graphs/base/static_sparse_graph.pxd +37 -0
  113. sage/graphs/base/static_sparse_graph.pyx +1304 -0
  114. sage/graphs/bipartite_graph.py +2709 -0
  115. sage/graphs/centrality.cpython-39-aarch64-linux-gnu.so +0 -0
  116. sage/graphs/centrality.pyx +965 -0
  117. sage/graphs/cographs.py +519 -0
  118. sage/graphs/comparability.cpython-39-aarch64-linux-gnu.so +0 -0
  119. sage/graphs/comparability.pyx +813 -0
  120. sage/graphs/connectivity.cpython-39-aarch64-linux-gnu.so +0 -0
  121. sage/graphs/connectivity.pxd +157 -0
  122. sage/graphs/connectivity.pyx +4813 -0
  123. sage/graphs/convexity_properties.cpython-39-aarch64-linux-gnu.so +0 -0
  124. sage/graphs/convexity_properties.pxd +16 -0
  125. sage/graphs/convexity_properties.pyx +827 -0
  126. sage/graphs/digraph.py +4410 -0
  127. sage/graphs/digraph_generators.py +1921 -0
  128. sage/graphs/distances_all_pairs.cpython-39-aarch64-linux-gnu.so +0 -0
  129. sage/graphs/distances_all_pairs.pxd +12 -0
  130. sage/graphs/distances_all_pairs.pyx +2938 -0
  131. sage/graphs/domination.py +1363 -0
  132. sage/graphs/dot2tex_utils.py +100 -0
  133. sage/graphs/edge_connectivity.cpython-39-aarch64-linux-gnu.so +0 -0
  134. sage/graphs/edge_connectivity.pyx +1215 -0
  135. sage/graphs/generators/all.py +1 -0
  136. sage/graphs/generators/basic.py +1769 -0
  137. sage/graphs/generators/chessboard.py +538 -0
  138. sage/graphs/generators/classical_geometries.py +1611 -0
  139. sage/graphs/generators/degree_sequence.py +235 -0
  140. sage/graphs/generators/distance_regular.cpython-39-aarch64-linux-gnu.so +0 -0
  141. sage/graphs/generators/distance_regular.pyx +2846 -0
  142. sage/graphs/generators/families.py +4749 -0
  143. sage/graphs/generators/intersection.py +565 -0
  144. sage/graphs/generators/platonic_solids.py +262 -0
  145. sage/graphs/generators/random.py +2623 -0
  146. sage/graphs/generators/smallgraphs.py +5741 -0
  147. sage/graphs/generators/world_map.py +724 -0
  148. sage/graphs/generic_graph.py +26395 -0
  149. sage/graphs/generic_graph_pyx.cpython-39-aarch64-linux-gnu.so +0 -0
  150. sage/graphs/generic_graph_pyx.pxd +34 -0
  151. sage/graphs/generic_graph_pyx.pyx +1626 -0
  152. sage/graphs/genus.cpython-39-aarch64-linux-gnu.so +0 -0
  153. sage/graphs/genus.pyx +623 -0
  154. sage/graphs/graph.py +9362 -0
  155. sage/graphs/graph_coloring.cpython-39-aarch64-linux-gnu.so +0 -0
  156. sage/graphs/graph_coloring.pyx +2284 -0
  157. sage/graphs/graph_database.py +1122 -0
  158. sage/graphs/graph_decompositions/all.py +1 -0
  159. sage/graphs/graph_decompositions/bandwidth.cpython-39-aarch64-linux-gnu.so +0 -0
  160. sage/graphs/graph_decompositions/bandwidth.pyx +428 -0
  161. sage/graphs/graph_decompositions/clique_separators.cpython-39-aarch64-linux-gnu.so +0 -0
  162. sage/graphs/graph_decompositions/clique_separators.pyx +595 -0
  163. sage/graphs/graph_decompositions/cutwidth.cpython-39-aarch64-linux-gnu.so +0 -0
  164. sage/graphs/graph_decompositions/cutwidth.pyx +753 -0
  165. sage/graphs/graph_decompositions/fast_digraph.cpython-39-aarch64-linux-gnu.so +0 -0
  166. sage/graphs/graph_decompositions/fast_digraph.pxd +13 -0
  167. sage/graphs/graph_decompositions/fast_digraph.pyx +212 -0
  168. sage/graphs/graph_decompositions/graph_products.cpython-39-aarch64-linux-gnu.so +0 -0
  169. sage/graphs/graph_decompositions/graph_products.pyx +462 -0
  170. sage/graphs/graph_decompositions/modular_decomposition.cpython-39-aarch64-linux-gnu.so +0 -0
  171. sage/graphs/graph_decompositions/modular_decomposition.pxd +27 -0
  172. sage/graphs/graph_decompositions/modular_decomposition.pyx +1536 -0
  173. sage/graphs/graph_decompositions/slice_decomposition.cpython-39-aarch64-linux-gnu.so +0 -0
  174. sage/graphs/graph_decompositions/slice_decomposition.pxd +18 -0
  175. sage/graphs/graph_decompositions/slice_decomposition.pyx +1080 -0
  176. sage/graphs/graph_decompositions/tree_decomposition.cpython-39-aarch64-linux-gnu.so +0 -0
  177. sage/graphs/graph_decompositions/tree_decomposition.pxd +17 -0
  178. sage/graphs/graph_decompositions/tree_decomposition.pyx +1996 -0
  179. sage/graphs/graph_decompositions/vertex_separation.cpython-39-aarch64-linux-gnu.so +0 -0
  180. sage/graphs/graph_decompositions/vertex_separation.pxd +5 -0
  181. sage/graphs/graph_decompositions/vertex_separation.pyx +1963 -0
  182. sage/graphs/graph_editor.py +82 -0
  183. sage/graphs/graph_generators.py +3301 -0
  184. sage/graphs/graph_generators_pyx.cpython-39-aarch64-linux-gnu.so +0 -0
  185. sage/graphs/graph_generators_pyx.pyx +95 -0
  186. sage/graphs/graph_input.py +812 -0
  187. sage/graphs/graph_latex.py +2064 -0
  188. sage/graphs/graph_list.py +367 -0
  189. sage/graphs/graph_plot.py +1749 -0
  190. sage/graphs/graph_plot_js.py +338 -0
  191. sage/graphs/hyperbolicity.cpython-39-aarch64-linux-gnu.so +0 -0
  192. sage/graphs/hyperbolicity.pyx +1702 -0
  193. sage/graphs/hypergraph_generators.py +364 -0
  194. sage/graphs/independent_sets.cpython-39-aarch64-linux-gnu.so +0 -0
  195. sage/graphs/independent_sets.pxd +13 -0
  196. sage/graphs/independent_sets.pyx +402 -0
  197. sage/graphs/isgci.py +1033 -0
  198. sage/graphs/isoperimetric_inequalities.cpython-39-aarch64-linux-gnu.so +0 -0
  199. sage/graphs/isoperimetric_inequalities.pyx +453 -0
  200. sage/graphs/line_graph.cpython-39-aarch64-linux-gnu.so +0 -0
  201. sage/graphs/line_graph.pyx +627 -0
  202. sage/graphs/lovasz_theta.py +77 -0
  203. sage/graphs/matching.py +1633 -0
  204. sage/graphs/matching_covered_graph.py +3566 -0
  205. sage/graphs/orientations.py +1504 -0
  206. sage/graphs/partial_cube.py +459 -0
  207. sage/graphs/path_enumeration.cpython-39-aarch64-linux-gnu.so +0 -0
  208. sage/graphs/path_enumeration.pyx +2040 -0
  209. sage/graphs/pq_trees.py +1129 -0
  210. sage/graphs/print_graphs.py +201 -0
  211. sage/graphs/schnyder.py +865 -0
  212. sage/graphs/spanning_tree.cpython-39-aarch64-linux-gnu.so +0 -0
  213. sage/graphs/spanning_tree.pyx +1457 -0
  214. sage/graphs/strongly_regular_db.cpython-39-aarch64-linux-gnu.so +0 -0
  215. sage/graphs/strongly_regular_db.pyx +3340 -0
  216. sage/graphs/traversals.cpython-39-aarch64-linux-gnu.so +0 -0
  217. sage/graphs/traversals.pxd +9 -0
  218. sage/graphs/traversals.pyx +1871 -0
  219. sage/graphs/trees.cpython-39-aarch64-linux-gnu.so +0 -0
  220. sage/graphs/trees.pxd +15 -0
  221. sage/graphs/trees.pyx +310 -0
  222. sage/graphs/tutte_polynomial.py +713 -0
  223. sage/graphs/views.cpython-39-aarch64-linux-gnu.so +0 -0
  224. sage/graphs/views.pyx +794 -0
  225. sage/graphs/weakly_chordal.cpython-39-aarch64-linux-gnu.so +0 -0
  226. sage/graphs/weakly_chordal.pyx +562 -0
  227. sage/groups/all__sagemath_graphs.py +1 -0
  228. sage/groups/perm_gps/all__sagemath_graphs.py +1 -0
  229. sage/groups/perm_gps/partn_ref/all__sagemath_graphs.py +1 -0
  230. sage/groups/perm_gps/partn_ref/refinement_graphs.cpython-39-aarch64-linux-gnu.so +0 -0
  231. sage/groups/perm_gps/partn_ref/refinement_graphs.pxd +38 -0
  232. sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +1666 -0
  233. sage/knots/all.py +6 -0
  234. sage/knots/free_knotinfo_monoid.py +507 -0
  235. sage/knots/gauss_code.py +291 -0
  236. sage/knots/knot.py +682 -0
  237. sage/knots/knot_table.py +284 -0
  238. sage/knots/knotinfo.py +2880 -0
  239. sage/knots/link.py +4682 -0
  240. sage/sandpiles/all.py +13 -0
  241. sage/sandpiles/examples.py +225 -0
  242. sage/sandpiles/sandpile.py +6365 -0
  243. sage/topology/all.py +22 -0
  244. sage/topology/cell_complex.py +1214 -0
  245. sage/topology/cubical_complex.py +1977 -0
  246. sage/topology/delta_complex.py +1806 -0
  247. sage/topology/filtered_simplicial_complex.py +744 -0
  248. sage/topology/moment_angle_complex.py +823 -0
  249. sage/topology/simplicial_complex.py +5161 -0
  250. sage/topology/simplicial_complex_catalog.py +86 -0
  251. sage/topology/simplicial_complex_examples.py +1692 -0
  252. sage/topology/simplicial_complex_homset.py +205 -0
  253. sage/topology/simplicial_complex_morphism.py +836 -0
  254. sage/topology/simplicial_set.py +4102 -0
  255. sage/topology/simplicial_set_catalog.py +55 -0
  256. sage/topology/simplicial_set_constructions.py +2954 -0
  257. sage/topology/simplicial_set_examples.py +865 -0
  258. sage/topology/simplicial_set_morphism.py +1464 -0
@@ -0,0 +1,2262 @@
1
+ # sage_setup: distribution = sagemath-graphs
2
+ # sage.doctest: needs sage.graphs sage.modules
3
+ r"""
4
+ Quiver
5
+
6
+ A *quiver* is an oriented graph without loops, two-cycles, or multiple
7
+ edges. The edges are labelled by pairs `(i,-j)` (with `i` and `j` being
8
+ positive integers) such that the matrix `M = (m_{ab})` with
9
+ `m_{ab} = i, m_{ba} = -j` for an edge `(i,-j)` between vertices
10
+ `a` and `b` is skew-symmetrizable.
11
+
12
+ .. WARNING::
13
+
14
+ This is not the standard definition of a quiver. Normally, in
15
+ cluster algebra theory, a quiver is defined as an oriented graph
16
+ without loops and two-cycles but with multiple edges allowed; the
17
+ edges are unlabelled. This notion of quivers, however, can be seen
18
+ as a particular case of our notion of quivers. Namely, if we have
19
+ a quiver (in the regular sense of this word) with (precisely)
20
+ `i` edges from `a` to `b`, then we represent it by a quiver
21
+ (in our sense of this word) with an edge from `a` to `b` labelled
22
+ by the pair `(i,-i)`.
23
+
24
+ For the compendium on the cluster algebra and quiver package see [MS2011]_
25
+
26
+ AUTHORS:
27
+
28
+ - Gregg Musiker
29
+ - Christian Stump
30
+
31
+ .. SEEALSO:: For mutation types of combinatorial quivers, see :meth:`~sage.combinat.cluster_algebra_quiver.quiver_mutation_type.QuiverMutationType`. Cluster seeds are closely related to :meth:`~sage.combinat.cluster_algebra_quiver.cluster_seed.ClusterSeed`.
32
+ """
33
+ # ****************************************************************************
34
+ # Copyright (C) 2011 Gregg Musiker <musiker@math.mit.edu>
35
+ # Christian Stump <christian.stump@univie.ac.at>
36
+ #
37
+ # Distributed under the terms of the GNU General Public License (GPL)
38
+ # https://www.gnu.org/licenses/
39
+ # ****************************************************************************
40
+ from copy import copy
41
+ from itertools import product
42
+
43
+ from sage.structure.sage_object import SageObject
44
+ from sage.rings.integer_ring import ZZ
45
+ from sage.rings.infinity import infinity
46
+ from sage.graphs.digraph import DiGraph
47
+ from sage.graphs.graph import Graph
48
+ from sage.graphs.views import EdgesView
49
+ from sage.arith.misc import gcd
50
+ from sage.misc.misc_c import prod
51
+ from sage.misc.lazy_import import lazy_import
52
+ from sage.rings.rational_field import QQ
53
+ from sage.rings.polynomial.polynomial_ring import polygen
54
+ from sage.combinat.cluster_algebra_quiver.quiver_mutation_type import QuiverMutationType, QuiverMutationType_Irreducible, QuiverMutationType_Reducible, _edge_list_to_matrix
55
+ from sage.combinat.cluster_algebra_quiver.mutation_class import _principal_part, _digraph_mutate, _matrix_to_digraph, _dg_canonical_form, _mutation_class_iter, _digraph_to_dig6, _dig6_to_matrix
56
+ from sage.combinat.cluster_algebra_quiver.mutation_type import _connected_mutation_type, _mutation_type_from_data, is_mutation_finite
57
+
58
+ from sage.combinat.cluster_algebra_quiver.interact import cluster_interact
59
+
60
+ lazy_import('sage.modules.free_module_element', 'vector')
61
+ lazy_import('sage.matrix.constructor', 'matrix')
62
+
63
+
64
+ class ClusterQuiver(SageObject):
65
+ """
66
+ The *quiver* associated to an *exchange matrix*.
67
+
68
+ INPUT:
69
+
70
+ - ``data`` -- can be any of the following::
71
+
72
+ * :class:`QuiverMutationType`
73
+ * :class:`str` -- string representing a :class:`QuiverMutationType` or a common quiver type (see Examples)
74
+ * :class:`ClusterQuiver`
75
+ * :class:`Matrix` -- a skew-symmetrizable matrix
76
+ * :class:`DiGraph` -- must be the input data for a quiver
77
+ * List of edges -- must be the edge list of a digraph for a quiver
78
+
79
+ - ``frozen`` -- (default: ``None``) sets the list of frozen variables
80
+ if the input type is a :class:`DiGraph`, it is ignored otherwise
81
+
82
+ - ``user_labels`` -- (default: ``None``) sets the names of the labels for
83
+ the vertices of the quiver if the input type is not a :class:`DiGraph`;
84
+ otherwise it is ignored
85
+
86
+ EXAMPLES:
87
+
88
+ From a :class:`QuiverMutationType`::
89
+
90
+ sage: Q = ClusterQuiver(['A',5]); Q
91
+ Quiver on 5 vertices of type ['A', 5]
92
+
93
+ sage: Q = ClusterQuiver(['B',2]); Q
94
+ Quiver on 2 vertices of type ['B', 2]
95
+ sage: Q2 = ClusterQuiver(['C',2]); Q2
96
+ Quiver on 2 vertices of type ['B', 2]
97
+ sage: MT = Q.mutation_type(); MT.standard_quiver() == Q
98
+ True
99
+ sage: MT = Q2.mutation_type(); MT.standard_quiver() == Q2
100
+ False
101
+
102
+ sage: Q = ClusterQuiver(['A',[2,5],1]); Q
103
+ Quiver on 7 vertices of type ['A', [2, 5], 1]
104
+
105
+ sage: Q = ClusterQuiver(['A', [5,0],1]); Q
106
+ Quiver on 5 vertices of type ['D', 5]
107
+ sage: Q.is_finite()
108
+ True
109
+ sage: Q.is_acyclic()
110
+ False
111
+
112
+ sage: Q = ClusterQuiver(['F', 4, [2,1]]); Q
113
+ Quiver on 6 vertices of type ['F', 4, [1, 2]]
114
+ sage: MT = Q.mutation_type(); MT.standard_quiver() == Q
115
+ False
116
+ sage: dg = Q.digraph(); Q.mutate([2,1,4,0,5,3])
117
+ sage: dg2 = Q.digraph(); dg2.is_isomorphic(dg,edge_labels=True)
118
+ False
119
+ sage: dg2.is_isomorphic(MT.standard_quiver().digraph(),edge_labels=True)
120
+ True
121
+
122
+ sage: Q = ClusterQuiver(['G',2, (3,1)]); Q
123
+ Quiver on 4 vertices of type ['G', 2, [1, 3]]
124
+ sage: MT = Q.mutation_type(); MT.standard_quiver() == Q
125
+ False
126
+
127
+ sage: Q = ClusterQuiver(['GR',[3,6]]); Q
128
+ Quiver on 4 vertices of type ['D', 4]
129
+ sage: MT = Q.mutation_type(); MT.standard_quiver() == Q
130
+ False
131
+
132
+ sage: Q = ClusterQuiver(['GR',[3,7]]); Q
133
+ Quiver on 6 vertices of type ['E', 6]
134
+
135
+ sage: Q = ClusterQuiver(['TR',2]); Q
136
+ Quiver on 3 vertices of type ['A', 3]
137
+ sage: MT = Q.mutation_type(); MT.standard_quiver() == Q
138
+ False
139
+ sage: Q.mutate([1,0]); MT.standard_quiver() == Q
140
+ True
141
+
142
+ sage: Q = ClusterQuiver(['TR',3]); Q
143
+ Quiver on 6 vertices of type ['D', 6]
144
+ sage: MT = Q.mutation_type(); MT.standard_quiver() == Q
145
+ False
146
+
147
+ From a :class:`ClusterQuiver`::
148
+
149
+ sage: Q = ClusterQuiver(['A',[2,5],1]); Q
150
+ Quiver on 7 vertices of type ['A', [2, 5], 1]
151
+ sage: T = ClusterQuiver( Q ); T
152
+ Quiver on 7 vertices of type ['A', [2, 5], 1]
153
+
154
+ From a Matrix::
155
+
156
+ sage: Q = ClusterQuiver(['A',[2,5],1]); Q
157
+ Quiver on 7 vertices of type ['A', [2, 5], 1]
158
+ sage: T = ClusterQuiver( Q._M ); T
159
+ Quiver on 7 vertices
160
+
161
+ sage: Q = ClusterQuiver( matrix([[0,1,-1],[-1,0,1],[1,-1,0],[1,2,3]]) ); Q
162
+ Quiver on 4 vertices with 1 frozen vertex
163
+
164
+ sage: Q = ClusterQuiver( matrix([]) ); Q
165
+ Quiver without vertices
166
+
167
+ From a DiGraph::
168
+
169
+ sage: Q = ClusterQuiver(['A',[2,5],1]); Q
170
+ Quiver on 7 vertices of type ['A', [2, 5], 1]
171
+ sage: T = ClusterQuiver( Q._digraph ); T
172
+ Quiver on 7 vertices
173
+
174
+ sage: Q = ClusterQuiver( DiGraph([[1,2],[2,3],[3,4],[4,1]]) ); Q
175
+ Quiver on 4 vertices
176
+
177
+ sage: Q = ClusterQuiver(DiGraph([['a', 'b'], ['b', 'c'], ['c', 'd'], ['d', 'e']]),
178
+ ....: frozen=['c']); Q
179
+ Quiver on 5 vertices with 1 frozen vertex
180
+ sage: Q.mutation_type()
181
+ [ ['A', 2], ['A', 2] ]
182
+ sage: Q
183
+ Quiver on 5 vertices of type [ ['A', 2], ['A', 2] ] with 1 frozen vertex
184
+
185
+ From a List of edges::
186
+
187
+ sage: Q = ClusterQuiver(['A',[2,5],1]); Q
188
+ Quiver on 7 vertices of type ['A', [2, 5], 1]
189
+ sage: T = ClusterQuiver( Q._digraph.edges(sort=True) ); T
190
+ Quiver on 7 vertices
191
+
192
+ sage: Q = ClusterQuiver([[1, 2], [2, 3], [3, 4], [4, 1]]); Q
193
+ Quiver on 4 vertices
194
+
195
+ TESTS::
196
+
197
+ sage: Q = ClusterQuiver(DiGraph([[1,1]]))
198
+ Traceback (most recent call last):
199
+ ...
200
+ ValueError: cannot add edge from 1 to 1 in graph without loops
201
+
202
+ sage: Q = ClusterQuiver([[1,1]])
203
+ Traceback (most recent call last):
204
+ ...
205
+ ValueError: cannot add edge from 1 to 1 in graph without loops
206
+
207
+ sage: Q = ClusterQuiver(DiGraph([[1, 0],[0,1]]))
208
+ Traceback (most recent call last):
209
+ ...
210
+ ValueError: the input DiGraph contains two-cycles
211
+
212
+ sage: Q = ClusterQuiver('whatever')
213
+ Traceback (most recent call last):
214
+ ...
215
+ ValueError: the input data was not recognized
216
+ """
217
+
218
+ def __init__(self, data, frozen=None, user_labels=None):
219
+ """
220
+ TESTS::
221
+
222
+ sage: Q = ClusterQuiver(['A',4])
223
+ sage: TestSuite(Q).run()
224
+ """
225
+ from sage.combinat.cluster_algebra_quiver.cluster_seed import ClusterSeed
226
+ from sage.structure.element import Matrix
227
+
228
+ if isinstance(user_labels, list):
229
+ user_labels = [tuple(x) if isinstance(x, list) else x for x in user_labels]
230
+ elif isinstance(user_labels, dict):
231
+ values = [tuple(user_labels[x]) if isinstance(user_labels[x], list) else user_labels[x] for x in user_labels]
232
+ user_labels = dict(zip(user_labels.keys(),
233
+ values))
234
+
235
+ # constructs a quiver from a mutation type
236
+ if isinstance(data, (QuiverMutationType_Irreducible,
237
+ QuiverMutationType_Reducible)):
238
+ if frozen is not None:
239
+ print('The input specifies a mutation type, so the'
240
+ ' additional parameter frozen is ignored.'
241
+ ' Use set_frozen to freeze vertices.')
242
+
243
+ mutation_type = data
244
+ self.__init__( mutation_type.standard_quiver() )
245
+ if user_labels:
246
+ self.relabel(user_labels)
247
+ self._nlist = list(user_labels)
248
+
249
+ # constructs a quiver from string representing a mutation type
250
+ # or a common quiver type (see Examples)
251
+ # NOTE: for now, any string representing a *reducible type* is
252
+ # coerced into the standard quiver, but there is now more flexibility
253
+ # in how to input a connected (irreducible) quiver.
254
+ elif (isinstance(data, (list,tuple))
255
+ and (isinstance(data[0], str) or all(isinstance(comp, (list,tuple))
256
+ and isinstance(comp[0], str) for comp in data))):
257
+ if frozen is not None:
258
+ print('The input specifies a mutation type, so the additional'
259
+ ' parameter frozen is ignored. Use set_frozen to freeze vertices.')
260
+ mutation_type = QuiverMutationType(data)
261
+
262
+ # The command QuiverMutationType_Irreducible (which is not imported
263
+ # globally) already creates the desired digraph as long as we
264
+ # bypass the mutation type checking of QuiverMutationType and
265
+ # format the input appropriately. Thus we handle several
266
+ # special cases this way.
267
+ if len(data) == 2 and isinstance(data[0], str):
268
+ if data[0] == 'TR' or data[0] == 'GR' or (data[0] == 'C' and data[1] == 2):
269
+ if data[1] in ZZ:
270
+ quiv = ClusterQuiver( QuiverMutationType_Irreducible( data[0], data[1] )._digraph )
271
+ quiv._mutation_type = mutation_type
272
+ self.__init__( quiv )
273
+ elif isinstance(data[1], list):
274
+ quiv = ClusterQuiver( QuiverMutationType_Irreducible( data[0], tuple(data[1]) )._digraph )
275
+ quiv._mutation_type = mutation_type
276
+ self.__init__( quiv )
277
+ else:
278
+ self.__init__( mutation_type.standard_quiver() )
279
+ elif len(data) == 3 and isinstance(data[0], str):
280
+ if (data[0] == 'F' and data[1] == 4 and data[2] == [2,1]) or (data[0] == 'G' and data[1] == 2 and data[2] == [3,1]):
281
+ quiv = ClusterQuiver( QuiverMutationType_Irreducible( data[0], data[1], tuple(data[2]) )._digraph )
282
+ quiv._mutation_type = mutation_type
283
+ self.__init__( quiv )
284
+ elif (data[0] == 'F' and data[1] == 4 and data[2] == (2,1) ) or (data[0] == 'G' and data[1] == 2 and data[2] == (3,1) ):
285
+ quiv = ClusterQuiver( QuiverMutationType_Irreducible( data[0], data[1], data[2] )._digraph )
286
+ quiv._mutation_type = mutation_type
287
+ self.__init__( quiv )
288
+ elif data[0] == 'A' and isinstance(data[1], list) and data[2] == 1:
289
+ if len(data[1]) == 2 and min(data[1]) == 0:
290
+ quiv = ClusterQuiver( QuiverMutationType_Irreducible( data[0], tuple(data[1]), data[2] )._digraph )
291
+ quiv._mutation_type = mutation_type
292
+ self.__init__( quiv )
293
+ else:
294
+ self.__init__( mutation_type.standard_quiver() )
295
+
296
+ elif data[0] == 'A' and isinstance(data[1], tuple) and data[2] == 1:
297
+ if len(data[1]) == 2 and min(data[1]) == 0:
298
+ quiv = ClusterQuiver( QuiverMutationType_Irreducible( data[0], data[1], data[2] )._digraph )
299
+ quiv._mutation_type = mutation_type
300
+ self.__init__( quiv )
301
+ else:
302
+ self.__init__( mutation_type.standard_quiver() )
303
+
304
+ else:
305
+ self.__init__( mutation_type.standard_quiver() )
306
+ else:
307
+ self.__init__( mutation_type.standard_quiver() )
308
+
309
+ if user_labels:
310
+ if isinstance(user_labels, dict):
311
+ self._nlist = list(user_labels.keys())
312
+ else:
313
+ self._nlist = user_labels
314
+
315
+ self.relabel(self._nlist)
316
+
317
+ # constructs a quiver from a cluster seed
318
+ elif isinstance(data, ClusterSeed):
319
+ self.__init__( data.quiver() )
320
+
321
+ # constructs a quiver from a quiver
322
+ elif isinstance(data, ClusterQuiver):
323
+ if frozen is not None:
324
+ print('The input data is a quiver, therefore the additional'
325
+ ' parameter frozen is ignored. Use set_frozen to freeze vertices.')
326
+
327
+ self._M = copy(data._M)
328
+ self._M.set_immutable()
329
+ self._n = data._n
330
+ self._m = data._m
331
+ self._mlist = list(data._mlist)
332
+ self._nlist = list(data._nlist)
333
+ self._digraph = copy( data._digraph )
334
+ self._vertex_dictionary = data._vertex_dictionary
335
+ self._mutation_type = data._mutation_type
336
+ self._description = data._description
337
+
338
+ # constructs a quiver from a matrix
339
+ elif isinstance(data, Matrix):
340
+ if not _principal_part(data).is_skew_symmetrizable( positive=True ):
341
+ raise ValueError('The principal part of the matrix data must be skew-symmetrizable.')
342
+
343
+ if frozen is not None:
344
+ print('The input data is a matrix, therefore the additional parameter frozen is ignored. Frozen vertices read off accordingly if the matrix is not square.')
345
+
346
+ self._M = copy(data).sparse_matrix()
347
+ self._M.set_immutable()
348
+ self._n = n = self._M.ncols()
349
+ self._m = m = self._M.nrows() - self._n
350
+ self._digraph = _matrix_to_digraph( self._M )
351
+ self._vertex_dictionary = {}
352
+ self._mutation_type = None
353
+
354
+ if user_labels:
355
+ if isinstance(user_labels, dict):
356
+ self._nlist = list(user_labels.keys())[0:n]
357
+ self._mlist = list(user_labels.keys())[n:n+m]
358
+ elif isinstance(user_labels, list):
359
+ self._nlist = user_labels[0:n]
360
+ self._mlist = user_labels[n:n+m]
361
+ self._digraph.relabel(self._nlist + self._mlist)
362
+ else:
363
+ self._mlist = list(range(n,n+m))
364
+ self._nlist = list(range(n))
365
+ if n+m == 0:
366
+ self._description = 'Quiver without vertices'
367
+ elif n+m == 1:
368
+ self._description = 'Quiver on 1 vertex'
369
+ else:
370
+ self._description = 'Quiver on %d vertices' % (n+m)
371
+
372
+ # constructs a quiver from a digraph
373
+ elif isinstance(data, DiGraph):
374
+ if frozen is None:
375
+ frozen = []
376
+ if not isinstance(frozen, (list, tuple)):
377
+ raise ValueError("'frozen' must be a list of vertices")
378
+ frozen = set(frozen)
379
+ if not frozen.issubset(data.vertex_iterator()):
380
+ raise ValueError("frozen elements must be vertices")
381
+
382
+ mlist = self._mlist = list(frozen)
383
+ m = self._m = len(mlist)
384
+
385
+ try:
386
+ nlist = sorted(x for x in data if x not in frozen)
387
+ except TypeError:
388
+ nlist = sorted([x for x in data if x not in frozen], key=str)
389
+ self._nlist = nlist
390
+ n = self._n = len(nlist)
391
+ labelDict = {x: i for i, x in enumerate(nlist + mlist)}
392
+
393
+ dg = copy(data)
394
+ if data.has_loops():
395
+ raise ValueError("the input DiGraph contains a loop")
396
+
397
+ edges = set(data.edge_iterator(labels=False))
398
+ if any((b, a) in edges for (a, b) in edges):
399
+ raise ValueError("the input DiGraph contains two-cycles")
400
+
401
+ dg_labelling = False
402
+ if not set(dg.vertex_iterator()) == set(range(n + m)):
403
+ # relabelling to integers
404
+ # frozen vertices must be preserved
405
+ dg_labelling = nlist + mlist
406
+ dg.relabel(labelDict)
407
+
408
+ multiple_edges = dg.multiple_edges()
409
+ if multiple_edges:
410
+ multi_edges = {}
411
+ for v1, v2, label in multiple_edges:
412
+ if label not in ZZ:
413
+ raise ValueError("the input DiGraph contains multiple"
414
+ " edges labeled by non-integers")
415
+ elif (v1, v2) in multi_edges:
416
+ multi_edges[(v1, v2)] += label
417
+ else:
418
+ multi_edges[(v1, v2)] = label
419
+ dg.delete_edge(v1, v2)
420
+ dg.add_edges([(v1, v2, multi_edges[(v1,v2)])
421
+ for v1, v2 in multi_edges])
422
+
423
+ for edge in dg.edge_iterator():
424
+ if edge[0] >= n and edge[1] >= n:
425
+ raise ValueError("the input digraph contains edges"
426
+ " within the frozen vertices")
427
+ if edge[2] is None:
428
+ dg.set_edge_label( edge[0], edge[1], (1,-1) )
429
+ edge = (edge[0],edge[1],(1,-1))
430
+ elif edge[2] in ZZ:
431
+ dg.set_edge_label( edge[0], edge[1], (edge[2],-edge[2]) )
432
+ edge = (edge[0],edge[1],(edge[2],-edge[2]))
433
+ elif isinstance(edge[2], list) and len(edge[2]) != 2:
434
+ raise ValueError("the input digraph contains an edge with"
435
+ " the wrong type of list as a label")
436
+ elif isinstance(edge[2], list) and len(edge[2]) == 2:
437
+ dg.set_edge_label( edge[0], edge[1], (edge[2][0], edge[2][1]))
438
+ edge = (edge[0],edge[1],(edge[2][0],edge[2][1]))
439
+ elif ( edge[0] >= n or edge[1] >= n ) and not edge[2][0] == - edge[2][1]:
440
+ raise ValueError("the input digraph contains an edge to or"
441
+ " from a frozen vertex which is not skew-symmetric")
442
+ if edge[2][0] < 0:
443
+ raise ValueError("the input digraph contains an edge of the"
444
+ " form (a,-b) with negative a")
445
+
446
+ M = _edge_list_to_matrix( dg.edge_iterator(), list(range(n)),
447
+ list(range(n, n + m)) )
448
+ if not _principal_part(M).is_skew_symmetrizable(positive=True):
449
+ raise ValueError("the input digraph must be skew-symmetrizable")
450
+
451
+ self._digraph = dg
452
+ self._vertex_dictionary = {}
453
+ if dg_labelling is not False:
454
+ self.relabel(dg_labelling) # relabelling back
455
+
456
+ self._M = M
457
+ self._M.set_immutable()
458
+ if n + m == 0:
459
+ self._description = 'Quiver without vertices'
460
+ elif n + m == 1:
461
+ self._description = 'Quiver on %d vertex' % (n+m)
462
+ else:
463
+ self._description = 'Quiver on %d vertices' % (n+m)
464
+ self._mutation_type = None
465
+
466
+ # if data is a list of edges, the appropriate digraph is constructed.
467
+
468
+ elif isinstance(data, (list, EdgesView)) and all(isinstance(x, (list,tuple)) for x in data):
469
+ dg = DiGraph(data)
470
+ self.__init__(data=dg, frozen=frozen)
471
+
472
+ # otherwise, an error is raised
473
+ else:
474
+ raise ValueError("the input data was not recognized")
475
+
476
+ # stopgap for bugs arising from coefficients
477
+ if self._m:
478
+ from sage.misc.stopgap import stopgap
479
+ stopgap("Having frozen nodes is known to produce wrong answers", 22381)
480
+
481
+ def __eq__(self, other):
482
+ """
483
+ Return ``True`` if ``self`` and ``other`` represent the same quiver.
484
+
485
+ EXAMPLES::
486
+
487
+ sage: Q = ClusterQuiver(['A',5])
488
+ sage: T = Q.mutate( 2, inplace=False )
489
+ sage: Q.__eq__( T )
490
+ False
491
+ sage: T.mutate( 2 )
492
+ sage: Q.__eq__( T )
493
+ True
494
+ """
495
+ return isinstance(other, ClusterQuiver) and self._M == other._M
496
+
497
+ def __hash__(self):
498
+ """
499
+ Return a hash of ``self``.
500
+
501
+ EXAMPLES::
502
+
503
+ sage: Q = ClusterQuiver(['A',5])
504
+ sage: hash(Q) # indirect doctest
505
+ 7654921743699262111 # 64-bit
506
+ -1264862561 # 32-bit
507
+ """
508
+ return hash(self._M)
509
+
510
+ def _repr_(self):
511
+ """
512
+ Return the description of ``self``.
513
+
514
+ EXAMPLES::
515
+
516
+ sage: Q = ClusterQuiver(['A',5])
517
+ sage: Q._repr_()
518
+ "Quiver on 5 vertices of type ['A', 5]"
519
+ """
520
+ name = self._description
521
+ if self._mutation_type:
522
+ if isinstance(self._mutation_type, str):
523
+ name += ' of ' + self._mutation_type
524
+ else:
525
+ name += ' of type ' + str(self._mutation_type)
526
+ if self._m == 1:
527
+ name += ' with %s frozen vertex' % self._m
528
+ elif self._m > 1:
529
+ name += ' with %s frozen vertices' % self._m
530
+ return name
531
+
532
+ def plot(self, circular=True, center=(0, 0), directed=True, mark=None,
533
+ save_pos=False, greens=[]):
534
+ """
535
+ Return the plot of the underlying digraph of ``self``.
536
+
537
+ INPUT:
538
+
539
+ - ``circular`` -- boolean (default: ``True``); if ``True``, the
540
+ circular plot is chosen, otherwise >>spring<< is used
541
+ - ``center`` -- (default: (0,0)) sets the center of the circular plot,
542
+ otherwise it is ignored
543
+ - ``directed`` -- boolean (default: ``True``); if ``True``, the
544
+ directed version is shown, otherwise the undirected
545
+ - ``mark`` -- (default: ``None``) if set to i, the vertex i is
546
+ highlighted
547
+ - ``save_pos`` -- boolean (default: ``False``); if ``True``, the
548
+ positions of the vertices are saved
549
+ - ``greens`` -- (default: ``[]``) if set to a list, will display the green
550
+ vertices as green
551
+
552
+ EXAMPLES::
553
+
554
+ sage: Q = ClusterQuiver(['A',5])
555
+ sage: Q.plot() # needs sage.plot sage.symbolic
556
+ Graphics object consisting of 15 graphics primitives
557
+ sage: Q.plot(circular=True) # needs sage.plot sage.symbolic
558
+ Graphics object consisting of 15 graphics primitives
559
+ sage: Q.plot(circular=True, mark=1) # needs sage.plot sage.symbolic
560
+ Graphics object consisting of 15 graphics primitives
561
+ """
562
+ from sage.plot.colors import rainbow
563
+ from sage.graphs.graph_generators import GraphGenerators
564
+ from sage.symbolic.constants import e, pi
565
+ from sage.rings.cc import CC
566
+ from sage.rings.imaginary_unit import I
567
+ graphs = GraphGenerators()
568
+ # returns positions for graph vertices on two concentric cycles with radius 1 and 2
569
+
570
+ def _graphs_concentric_circles(n, m):
571
+ g1 = graphs.CycleGraph(n).get_pos()
572
+ g2 = graphs.CycleGraph(m).get_pos()
573
+ for i in g2:
574
+ z = CC(g2[i])*e**(-pi*I/(2*m))
575
+ g2[i] = (z.real_part(),z.imag_part())
576
+ for i in range(m):
577
+ g1[n+i] = [2*g2[i][0], 2*g2[i][1]]
578
+ return g1
579
+
580
+ n, m = self._n, self._m
581
+ # So that we don't remove elements of these lists later
582
+ nlist = copy(self._nlist)
583
+ mlist = copy(self._mlist)
584
+ colors = rainbow(11)
585
+ color_dict = { colors[0]:[], colors[1]:[], colors[6]:[], colors[5]:[] }
586
+
587
+ # Set up our graph. If it's directed we have a digraph, else just a normal graph
588
+ if directed:
589
+ dg = DiGraph( self._digraph )
590
+ else:
591
+ dg = Graph( self._digraph )
592
+
593
+ # For each edge in our graph we assign a color
594
+ for edge in dg.edges(sort=True):
595
+ v1,v2,(a,b) = edge
596
+
597
+ if v1 in nlist and v2 in nlist:
598
+ if (a,b) == (1,-1):
599
+ color_dict[ colors[0] ].append((v1,v2))
600
+ else:
601
+ color_dict[ colors[6] ].append((v1,v2))
602
+ else:
603
+ if (a,b) == (1,-1):
604
+ color_dict[ colors[1] ].append((v1,v2))
605
+ else:
606
+ color_dict[ colors[5] ].append((v1,v2))
607
+ if a == -b:
608
+ if a == 1:
609
+ dg.set_edge_label(v1, v2, '')
610
+ else:
611
+ dg.set_edge_label(v1, v2, a)
612
+
613
+ # If a mark is given, then we set that mark apart from the rest
614
+ # The mark is assumed to be a vertex
615
+ if mark is not None:
616
+
617
+ if mark in nlist:
618
+ nlist.remove(mark)
619
+ partition = (nlist, mlist, [mark])
620
+ elif mark in mlist:
621
+ mlist.remove(mark)
622
+ partition = (nlist, mlist, [mark])
623
+ else:
624
+ raise ValueError("The given mark is not a vertex of self.")
625
+ else:
626
+
627
+ # Partition out the green vertices
628
+ for i in greens:
629
+ if i in nlist:
630
+ nlist.remove(i)
631
+ else:
632
+ mlist.remove(i)
633
+ partition = (nlist, mlist, greens)
634
+
635
+ vertex_color_dict = {'tomato': partition[0],
636
+ 'lightblue': partition[1],
637
+ 'lightgreen': partition[2]}
638
+
639
+ options = {
640
+ 'graph_border': True,
641
+ 'edge_colors': color_dict,
642
+ 'vertex_colors': vertex_color_dict,
643
+ 'edge_labels': True,
644
+ 'vertex_labels': True,
645
+ }
646
+ if circular:
647
+ pp = _graphs_concentric_circles( n, m )
648
+ options['pos'] = {}
649
+ for v in pp:
650
+ # If we're using vertex dictionary set that as key
651
+ if v in self._vertex_dictionary:
652
+ vkey = self._vertex_dictionary[v]
653
+ else:
654
+ vkey = v
655
+ options['pos'][vkey] = (pp[v][0] + center[0], pp[v][1] + center[1])
656
+
657
+ return dg.plot( **options )
658
+
659
+ def show(self, fig_size=1, circular=False, directed=True, mark=None, save_pos=False, greens=[]):
660
+ """
661
+ Show the plot of the underlying digraph of ``self``.
662
+
663
+ INPUT:
664
+
665
+ - ``fig_size`` -- (default: 1) factor by which the size of the plot
666
+ is multiplied
667
+ - ``circular`` -- boolean (default: ``False``); if ``True``, the
668
+ circular plot is chosen, otherwise >>spring<< is used
669
+ - ``directed`` -- boolean (default: ``True``); if ``True``, the directed
670
+ version is shown, otherwise the undirected
671
+ - ``mark`` -- boolean (default: ``None``); if set to i, the vertex i is
672
+ highlighted
673
+ - ``save_pos`` -- boolean (default: ``False``); if ``True``, the
674
+ positions of the vertices are saved
675
+ - ``greens`` -- (default: ``[]``) if set to a list, will display the
676
+ green vertices as green
677
+
678
+ TESTS::
679
+
680
+ sage: Q = ClusterQuiver(['A',5])
681
+ sage: Q.show() # long time
682
+ """
683
+ n, m = self._n, self._m
684
+ plot = self.plot( circular=circular, directed=directed, mark=mark, save_pos=save_pos, greens=greens)
685
+ if circular:
686
+ plot.show( figsize=[fig_size*3*(n+m)/4+1,fig_size*3*(n+m)/4+1] )
687
+ else:
688
+ plot.show( figsize=[fig_size*n+1,fig_size*n+1] )
689
+
690
+ def interact(self, fig_size=1, circular=True):
691
+ r"""
692
+ Start an interactive window for cluster quiver mutations.
693
+
694
+ Only in *Jupyter notebook mode*.
695
+
696
+ INPUT:
697
+
698
+ - ``fig_size`` -- (default: 1) factor by which the size of the
699
+ plot is multiplied
700
+
701
+ - ``circular`` -- boolean (default: ``True``); if ``True``, the
702
+ circular plot is chosen, otherwise >>spring<< is used
703
+
704
+ TESTS::
705
+
706
+ sage: S = ClusterQuiver(['A',4])
707
+ sage: S.interact() # needs sage.plot sage.symbolic
708
+ ...VBox(children=...
709
+ """
710
+ return cluster_interact(self, fig_size, circular, kind='quiver')
711
+
712
+ def save_image(self, filename, circular=False):
713
+ """
714
+ Save the plot of the underlying digraph of ``self``.
715
+
716
+ INPUT:
717
+
718
+ - ``filename`` -- the filename the image is saved to
719
+ - ``circular`` -- boolean (default: ``False``); if ``True``, the
720
+ circular plot is chosen, otherwise >>spring<< is used
721
+
722
+ EXAMPLES::
723
+
724
+ sage: Q = ClusterQuiver(['F',4,[1,2]])
725
+ sage: import tempfile
726
+ sage: with tempfile.NamedTemporaryFile(suffix='.png') as f: # needs sage.plot sage.symbolic
727
+ ....: Q.save_image(f.name)
728
+ """
729
+ graph_plot = self.plot(circular=circular)
730
+ graph_plot.save(filename=filename)
731
+
732
+ def qmu_save(self, filename=None):
733
+ """
734
+ Save ``self`` in a ``.qmu`` file.
735
+
736
+ This file can then be opened in Bernhard Keller's Quiver Applet.
737
+
738
+ See https://webusers.imj-prg.fr/~bernhard.keller/quivermutation/
739
+
740
+ INPUT:
741
+
742
+ - ``filename`` -- the filename the image is saved to
743
+
744
+ If a filename is not specified, the default name is
745
+ ``from_sage.qmu`` in the current sage directory.
746
+
747
+ EXAMPLES::
748
+
749
+ sage: Q = ClusterQuiver(['F',4,[1,2]])
750
+ sage: import tempfile
751
+ sage: with tempfile.NamedTemporaryFile(suffix='.qmu') as f: # needs sage.plot sage.symbolic
752
+ ....: Q.qmu_save(f.name)
753
+
754
+ Make sure we can save quivers with `m != n` frozen variables, see :issue:`14851`::
755
+
756
+ sage: S = ClusterSeed(['A',3])
757
+ sage: T1 = S.principal_extension()
758
+ sage: Q = T1.quiver()
759
+ sage: import tempfile
760
+ sage: with tempfile.NamedTemporaryFile(suffix='.qmu') as f: # needs sage.plot sage.symbolic
761
+ ....: Q.qmu_save(f.name)
762
+ """
763
+ M = self.b_matrix()
764
+ if self.m():
765
+ from sage.matrix.constructor import matrix
766
+ from sage.matrix.constructor import block_matrix
767
+ M1 = M.matrix_from_rows(list(range(self.n())))
768
+ M2 = M.matrix_from_rows(list(range(self.n(), self.n() + self.m())))
769
+ M3 = matrix(self.m(), self.m())
770
+ M = block_matrix([[M1, -M2.transpose()], [M2, M3]])
771
+ dg = self.digraph()
772
+ dg.plot(save_pos=True)
773
+ PP = dg.get_pos()
774
+ m = M.ncols()
775
+
776
+ if filename is None:
777
+ filename = 'from_sage.qmu'
778
+ try:
779
+ self._default_filename = filename
780
+ except AttributeError:
781
+ pass
782
+ if filename[-4:] != '.qmu':
783
+ filename += '.qmu'
784
+
785
+ string = []
786
+ string.append('//Number of points')
787
+ string.append(str(m))
788
+ string.append('//Vertex radius')
789
+ string.append('9')
790
+ string.append('//Labels shown')
791
+ string.append('1')
792
+ string.append('//Matrix')
793
+ string.append(str(m) + ' ' + str(m))
794
+ string.extend(' '.join(str(M[i, j]) for j in range(m))
795
+ for i in range(m))
796
+ string.append('//Points')
797
+
798
+ for i in range(m):
799
+ x, y = PP[i]
800
+ txt = '9 ' + str(100 * x) + ' ' + str(100 * y)
801
+ if i >= self.n():
802
+ txt += ' 1'
803
+ string.append(txt)
804
+
805
+ string.append('//Historycounter')
806
+ string.append('-1')
807
+ string.append('//History')
808
+ string.append('')
809
+ string.append('//Cluster is null')
810
+
811
+ string = '\n'.join(string)
812
+
813
+ with open(filename, 'w') as myfile:
814
+ myfile.write(string)
815
+
816
+ def b_matrix(self):
817
+ """
818
+ Return the b-matrix of ``self``.
819
+
820
+ EXAMPLES::
821
+
822
+ sage: ClusterQuiver(['A',4]).b_matrix()
823
+ [ 0 1 0 0]
824
+ [-1 0 -1 0]
825
+ [ 0 1 0 1]
826
+ [ 0 0 -1 0]
827
+
828
+ sage: ClusterQuiver(['B',4]).b_matrix()
829
+ [ 0 1 0 0]
830
+ [-1 0 -1 0]
831
+ [ 0 1 0 1]
832
+ [ 0 0 -2 0]
833
+
834
+ sage: ClusterQuiver(['D',4]).b_matrix()
835
+ [ 0 1 0 0]
836
+ [-1 0 -1 -1]
837
+ [ 0 1 0 0]
838
+ [ 0 1 0 0]
839
+
840
+ sage: ClusterQuiver(QuiverMutationType([['A',2],['B',2]])).b_matrix()
841
+ [ 0 1 0 0]
842
+ [-1 0 0 0]
843
+ [ 0 0 0 1]
844
+ [ 0 0 -2 0]
845
+ """
846
+ return copy(self._M)
847
+
848
+ def digraph(self):
849
+ """
850
+ Return the underlying digraph of ``self``.
851
+
852
+ EXAMPLES::
853
+
854
+ sage: ClusterQuiver(['A',1]).digraph()
855
+ Digraph on 1 vertex
856
+ sage: list(ClusterQuiver(['A',1]).digraph())
857
+ [0]
858
+ sage: ClusterQuiver(['A',1]).digraph().edges(sort=True)
859
+ []
860
+
861
+ sage: ClusterQuiver(['A',4]).digraph()
862
+ Digraph on 4 vertices
863
+ sage: ClusterQuiver(['A',4]).digraph().edges(sort=True)
864
+ [(0, 1, (1, -1)), (2, 1, (1, -1)), (2, 3, (1, -1))]
865
+
866
+ sage: ClusterQuiver(['B',4]).digraph()
867
+ Digraph on 4 vertices
868
+ sage: ClusterQuiver(['A',4]).digraph().edges(sort=True)
869
+ [(0, 1, (1, -1)), (2, 1, (1, -1)), (2, 3, (1, -1))]
870
+
871
+ sage: ClusterQuiver(QuiverMutationType([['A',2],['B',2]])).digraph()
872
+ Digraph on 4 vertices
873
+
874
+ sage: ClusterQuiver(QuiverMutationType([['A',2],['B',2]])).digraph().edges(sort=True)
875
+ [(0, 1, (1, -1)), (2, 3, (1, -2))]
876
+
877
+ sage: ClusterQuiver(['C', 4], user_labels = ['x', 'y', 'z', 'w']).digraph().edges(sort=True)
878
+ [('x', 'y', (1, -1)), ('z', 'w', (2, -1)), ('z', 'y', (1, -1))]
879
+ """
880
+ return copy(self._digraph)
881
+
882
+ def mutation_type(self):
883
+ """
884
+ Return the mutation type of ``self``.
885
+
886
+ Return the mutation_type of each connected component of ``self`` if it
887
+ can be determined, otherwise, the mutation type of this component is
888
+ set to be unknown.
889
+
890
+ The mutation types of the components are ordered by vertex labels.
891
+
892
+ If you do many type recognitions, you should consider to save
893
+ exceptional mutation types using
894
+ :meth:`~sage.combinat.cluster_algebra_quiver.quiver_mutation_type.save_quiver_data`
895
+
896
+ WARNING:
897
+
898
+ - All finite types can be detected,
899
+ - All affine types can be detected, EXCEPT affine type D (the algorithm is not yet implemented)
900
+ - All exceptional types can be detected.
901
+
902
+ EXAMPLES::
903
+
904
+ sage: ClusterQuiver(['A',4]).mutation_type()
905
+ ['A', 4]
906
+ sage: ClusterQuiver(['A',(3,1),1]).mutation_type()
907
+ ['A', [1, 3], 1]
908
+ sage: ClusterQuiver(['C',2]).mutation_type()
909
+ ['B', 2]
910
+ sage: ClusterQuiver(['B',4,1]).mutation_type()
911
+ ['BD', 4, 1]
912
+
913
+ finite types::
914
+
915
+ sage: Q = ClusterQuiver(['A',5])
916
+ sage: Q._mutation_type = None
917
+ sage: Q.mutation_type()
918
+ ['A', 5]
919
+
920
+ sage: Q = ClusterQuiver([(0,1),(1,2),(2,3),(3,4)])
921
+ sage: Q.mutation_type()
922
+ ['A', 5]
923
+
924
+ sage: Q = ClusterQuiver(DiGraph([['a', 'b'], ['c', 'b'], ['c', 'd'], ['e', 'd']]),
925
+ ....: frozen=['c'])
926
+ sage: Q.mutation_type()
927
+ [ ['A', 2], ['A', 2] ]
928
+
929
+ affine types::
930
+
931
+ sage: Q = ClusterQuiver(['E',8,[1,1]]); Q
932
+ Quiver on 10 vertices of type ['E', 8, [1, 1]]
933
+ sage: Q._mutation_type = None; Q
934
+ Quiver on 10 vertices
935
+ sage: Q.mutation_type() # long time
936
+ ['E', 8, [1, 1]]
937
+
938
+ the not yet working affine type D (unless user has saved small classical quiver data)::
939
+
940
+ sage: Q = ClusterQuiver(['D',4,1])
941
+ sage: Q._mutation_type = None
942
+ sage: Q.mutation_type() # todo: not implemented
943
+ ['D', 4, 1]
944
+
945
+ the exceptional types::
946
+
947
+ sage: Q = ClusterQuiver(['X',6])
948
+ sage: Q._mutation_type = None
949
+ sage: Q.mutation_type() # long time
950
+ ['X', 6]
951
+
952
+ examples from page 8 of [Ke2008]_::
953
+
954
+ sage: dg = DiGraph(); dg.add_edges([(9,0),(9,4),(4,6),(6,7),(7,8),(8,3),(3,5),(5,6),(8,1),(2,3)])
955
+ sage: ClusterQuiver( dg ).mutation_type() # long time
956
+ ['E', 8, [1, 1]]
957
+
958
+ sage: dg = DiGraph( { 0:[3], 1:[0,4], 2:[0,6], 3:[1,2,7], 4:[3,8], 5:[2], 6:[3,5], 7:[4,6], 8:[7] } )
959
+ sage: ClusterQuiver( dg ).mutation_type() # long time
960
+ ['E', 8, 1]
961
+
962
+ sage: dg = DiGraph( { 0:[3,9], 1:[0,4], 2:[0,6], 3:[1,2,7], 4:[3,8], 5:[2], 6:[3,5], 7:[4,6], 8:[7], 9:[1] } )
963
+ sage: ClusterQuiver( dg ).mutation_type() # long time
964
+ ['E', 8, [1, 1]]
965
+
966
+ infinite types::
967
+
968
+ sage: Q = ClusterQuiver(['GR',[4,9]])
969
+ sage: Q._mutation_type = None
970
+ sage: Q.mutation_type()
971
+ 'undetermined infinite mutation type'
972
+
973
+ reducible types::
974
+
975
+ sage: Q = ClusterQuiver([['A', 3], ['B', 3]])
976
+ sage: Q._mutation_type = None
977
+ sage: Q.mutation_type()
978
+ [ ['A', 3], ['B', 3] ]
979
+
980
+ sage: Q = ClusterQuiver([['A', 3], ['T', [4,4,4]]])
981
+ sage: Q._mutation_type = None
982
+ sage: Q.mutation_type()
983
+ [['A', 3], 'undetermined infinite mutation type']
984
+
985
+ sage: Q = ClusterQuiver([['A', 3], ['B', 3], ['T', [4,4,4]]])
986
+ sage: Q._mutation_type = None
987
+ sage: Q.mutation_type()
988
+ [['A', 3], ['B', 3], 'undetermined infinite mutation type']
989
+
990
+ sage: Q = ClusterQuiver([[0,1,2],[1,2,2],[2,0,2],[3,4,1],[4,5,1]])
991
+ sage: Q.mutation_type()
992
+ ['undetermined finite mutation type', ['A', 3]]
993
+
994
+ TESTS::
995
+
996
+ sage: Q = ClusterQuiver(matrix([[0, 3], [-1, 0], [1, 0], [0, 1]]))
997
+ sage: Q.mutation_type()
998
+ ['G', 2]
999
+ sage: Q = ClusterQuiver(matrix([[0, -1, -1, 1, 0], [1, 0, 1, 0, 1], [1, -1, 0, -1, 0], [-1, 0, 1, 0, 1], [0, -1, 0, -1, 0], [0, 1, 0, -1, -1], [0, 1, -1, 0, 0]]))
1000
+ sage: Q.mutation_type()
1001
+ 'undetermined infinite mutation type'
1002
+ """
1003
+ # checking if the mutation type is known already
1004
+ if self._mutation_type is None:
1005
+ # checking mutation type only for the principal part
1006
+ if self._m > 0:
1007
+ dg = self._digraph.subgraph(self._nlist)
1008
+ else:
1009
+ dg = self._digraph
1010
+
1011
+ # checking the type for each connected component
1012
+ mutation_type = []
1013
+ connected_components = sorted(dg.connected_components(sort=False))
1014
+ for component in connected_components:
1015
+ # constructing the digraph for this component
1016
+ dg_component = dg.subgraph( component )
1017
+ dg_component.relabel()
1018
+ # turning dg_component into a canonical form
1019
+ _dg_canonical_form(dg_component)
1020
+ # turning dg_component into a canonical form
1021
+ dig6 = _digraph_to_dig6(dg_component, hashable=True)
1022
+ # and getting the corresponding matrix
1023
+ M = _dig6_to_matrix(dig6)
1024
+
1025
+ # checking if this quiver is mutation infinite
1026
+ is_finite, path = is_mutation_finite(M)
1027
+ if is_finite is False:
1028
+ mut_type_part = 'undetermined infinite mutation type'
1029
+ else:
1030
+ # checking if this quiver is in the database
1031
+ mut_type_part = _mutation_type_from_data( dg_component.order(), dig6, compute_if_necessary=False )
1032
+ # checking if the algorithm can determine the mutation type
1033
+ if mut_type_part == 'unknown':
1034
+ mut_type_part = _connected_mutation_type(dg_component)
1035
+ # checking if this quiver is of exceptional type by computing the exceptional mutation classes
1036
+ if mut_type_part == 'unknown':
1037
+ mut_type_part = _mutation_type_from_data(dg_component.order(), dig6, compute_if_necessary=True)
1038
+ if mut_type_part == 'unknown':
1039
+ mut_type_part = 'undetermined finite mutation type'
1040
+ mutation_type.append( mut_type_part )
1041
+
1042
+ # the empty quiver case
1043
+ if len(mutation_type) == 0:
1044
+ mutation_type = None
1045
+ # the connected quiver case
1046
+ elif len(mutation_type) == 1:
1047
+ mutation_type = mutation_type[0]
1048
+ # the reducible quiver case
1049
+ elif len(mutation_type) > 1:
1050
+ if any(isinstance(mut_type_part, str)
1051
+ for mut_type_part in mutation_type):
1052
+ pass
1053
+ else:
1054
+ mutation_type = QuiverMutationType(mutation_type)
1055
+ self._mutation_type = mutation_type
1056
+ return self._mutation_type
1057
+
1058
+ def n(self):
1059
+ """
1060
+ Return the number of free vertices of ``self``.
1061
+
1062
+ EXAMPLES::
1063
+
1064
+ sage: ClusterQuiver(['A',4]).n()
1065
+ 4
1066
+ sage: ClusterQuiver(['A',(3,1),1]).n()
1067
+ 4
1068
+ sage: ClusterQuiver(['B',4]).n()
1069
+ 4
1070
+ sage: ClusterQuiver(['B',4,1]).n()
1071
+ 5
1072
+ """
1073
+ return self._n
1074
+
1075
+ def m(self):
1076
+ """
1077
+ Return the number of frozen vertices of ``self``.
1078
+
1079
+ EXAMPLES::
1080
+
1081
+ sage: Q = ClusterQuiver(['A',4])
1082
+ sage: Q.m()
1083
+ 0
1084
+
1085
+ sage: T = ClusterQuiver(Q.digraph().edges(sort=True), frozen=[3])
1086
+ sage: T.n()
1087
+ 3
1088
+ sage: T.m()
1089
+ 1
1090
+ """
1091
+ return self._m
1092
+
1093
+ def free_vertices(self):
1094
+ """
1095
+ Return the list of free vertices of ``self``.
1096
+
1097
+ EXAMPLES::
1098
+
1099
+ sage: Q = ClusterQuiver(DiGraph([['a', 'b'], ['c', 'b'], ['c', 'd'], ['e', 'd']]),
1100
+ ....: frozen=['b', 'd'])
1101
+ sage: Q.free_vertices()
1102
+ ['a', 'c', 'e']
1103
+ """
1104
+ return self._nlist
1105
+
1106
+ def frozen_vertices(self):
1107
+ """
1108
+ Return the list of frozen vertices of ``self``.
1109
+
1110
+ EXAMPLES::
1111
+
1112
+ sage: Q = ClusterQuiver(DiGraph([['a', 'b'], ['c', 'b'], ['c', 'd'], ['e', 'd']]),
1113
+ ....: frozen=['b', 'd'])
1114
+ sage: sorted(Q.frozen_vertices())
1115
+ ['b', 'd']
1116
+ """
1117
+ return self._mlist
1118
+
1119
+ def canonical_label(self, certificate=False):
1120
+ """
1121
+ Return the canonical labelling of ``self``.
1122
+
1123
+ See :meth:`sage.graphs.generic_graph.GenericGraph.canonical_label`.
1124
+
1125
+ INPUT:
1126
+
1127
+ - ``certificate`` -- boolean (default: ``False``); if ``True``, the
1128
+ dictionary from ``self.vertices()`` to the vertices of the returned
1129
+ quiver is returned as well
1130
+
1131
+ EXAMPLES::
1132
+
1133
+ sage: Q = ClusterQuiver(['A',4]); Q.digraph().edges(sort=True)
1134
+ [(0, 1, (1, -1)), (2, 1, (1, -1)), (2, 3, (1, -1))]
1135
+
1136
+ sage: T = Q.canonical_label(); T.digraph().edges(sort=True)
1137
+ [(0, 3, (1, -1)), (1, 2, (1, -1)), (1, 3, (1, -1))]
1138
+
1139
+ sage: T, iso = Q.canonical_label(certificate=True)
1140
+ sage: T.digraph().edges(sort=True); iso
1141
+ [(0, 3, (1, -1)), (1, 2, (1, -1)), (1, 3, (1, -1))]
1142
+ {0: 0, 1: 3, 2: 1, 3: 2}
1143
+
1144
+ sage: Q = ClusterQuiver(QuiverMutationType([['B',2],['A',1]])); Q
1145
+ Quiver on 3 vertices of type [ ['B', 2], ['A', 1] ]
1146
+
1147
+ sage: Q.canonical_label()
1148
+ Quiver on 3 vertices of type [ ['A', 1], ['B', 2] ]
1149
+
1150
+ sage: Q.canonical_label(certificate=True)
1151
+ (Quiver on 3 vertices of type [ ['A', 1], ['B', 2] ], {0: 1, 1: 2, 2: 0})
1152
+ """
1153
+ # computing the canonical form respecting the frozen variables
1154
+ dg = copy(self._digraph)
1155
+ iso, _ = _dg_canonical_form(dg, frozen=self._mlist)
1156
+ frozen = [iso[i] for i in self._mlist]
1157
+ Q = ClusterQuiver(dg, frozen=frozen)
1158
+ # getting the new ordering for the mutation type if necessary
1159
+ if self._mutation_type:
1160
+ if dg.is_connected():
1161
+ Q._mutation_type = self._mutation_type
1162
+ else:
1163
+ CC = sorted(self._digraph.connected_components(sort=False))
1164
+ CC_new = sorted(zip([sorted(iso[i] for i in L) for L in CC],
1165
+ range(len(CC))))
1166
+ comp_iso = [L[1] for L in CC_new]
1167
+ Q._mutation_type = []
1168
+ for i in range( len( CC_new ) ):
1169
+ Q._mutation_type.append( copy( self._mutation_type.irreducible_components()[ comp_iso[i] ] ) )
1170
+ Q._mutation_type = QuiverMutationType( Q._mutation_type )
1171
+ if certificate:
1172
+ return Q, iso
1173
+ else:
1174
+ return Q
1175
+
1176
+ def is_acyclic(self):
1177
+ """
1178
+ Return true if ``self`` is acyclic.
1179
+
1180
+ EXAMPLES::
1181
+
1182
+ sage: ClusterQuiver(['A',4]).is_acyclic()
1183
+ True
1184
+
1185
+ sage: ClusterQuiver(['A',[2,1],1]).is_acyclic()
1186
+ True
1187
+
1188
+ sage: ClusterQuiver([[0,1],[1,2],[2,0]]).is_acyclic()
1189
+ False
1190
+ """
1191
+ return self._digraph.is_directed_acyclic()
1192
+
1193
+ def is_bipartite(self, return_bipartition=False):
1194
+ """
1195
+ Return ``True`` if ``self`` is bipartite.
1196
+
1197
+ EXAMPLES::
1198
+
1199
+ sage: ClusterQuiver(['A',[3,3],1]).is_bipartite()
1200
+ True
1201
+
1202
+ sage: ClusterQuiver(['A',[4,3],1]).is_bipartite()
1203
+ False
1204
+ """
1205
+ dg = copy(self._digraph)
1206
+ dg.delete_vertices(list(range(self._n, self._n + self._m)))
1207
+ if any(dg.in_degree(i) and dg.out_degree(i) for i in dg):
1208
+ return False
1209
+ if not return_bipartition:
1210
+ return True
1211
+ return dg.to_undirected().bipartite_sets()
1212
+
1213
+ def exchangeable_part(self):
1214
+ """
1215
+ Return the restriction to the principal part (i.e. exchangeable part) of ``self``, the subquiver obtained by deleting the frozen vertices of ``self``.
1216
+
1217
+ EXAMPLES::
1218
+
1219
+ sage: Q = ClusterQuiver(['A',4])
1220
+ sage: T = ClusterQuiver(Q.digraph().edges(sort=True), frozen=[3])
1221
+ sage: T.digraph().edges(sort=True)
1222
+ [(0, 1, (1, -1)), (2, 1, (1, -1)), (2, 3, (1, -1))]
1223
+
1224
+ sage: T.exchangeable_part().digraph().edges(sort=True)
1225
+ [(0, 1, (1, -1)), (2, 1, (1, -1))]
1226
+
1227
+ sage: Q2 = Q.principal_extension()
1228
+ sage: Q3 = Q2.principal_extension()
1229
+ sage: Q2.exchangeable_part() == Q3.exchangeable_part()
1230
+ True
1231
+ """
1232
+ dg = DiGraph( self._digraph )
1233
+ dg.delete_vertices(list(range(self._n, self._n + self._m)))
1234
+ Q = ClusterQuiver( dg )
1235
+ Q._mutation_type = self._mutation_type
1236
+ return Q
1237
+
1238
+ def principal_extension(self, inplace=False):
1239
+ """
1240
+ Return the principal extension of ``self``, adding n frozen vertices to any previously frozen vertices. I.e., the quiver obtained by adding an outgoing edge to every mutable vertex of ``self``.
1241
+
1242
+ EXAMPLES::
1243
+
1244
+ sage: Q = ClusterQuiver(['A',2]); Q
1245
+ Quiver on 2 vertices of type ['A', 2]
1246
+ sage: T = Q.principal_extension(); T
1247
+ Quiver on 4 vertices of type ['A', 2] with 2 frozen vertices
1248
+ sage: T2 = T.principal_extension(); T2
1249
+ Quiver on 6 vertices of type ['A', 2] with 4 frozen vertices
1250
+ sage: Q.digraph().edges(sort=True)
1251
+ [(0, 1, (1, -1))]
1252
+ sage: T.digraph().edges(sort=True)
1253
+ [(0, 1, (1, -1)), (2, 0, (1, -1)), (3, 1, (1, -1))]
1254
+ sage: T2.digraph().edges(sort=True)
1255
+ [(0, 1, (1, -1)), (2, 0, (1, -1)), (3, 1, (1, -1)), (4, 0, (1, -1)), (5, 1, (1, -1))]
1256
+ """
1257
+ dg = DiGraph(self._digraph)
1258
+ dg.add_edges( [(self._n+self._m+i,i) for i in range(self._n)] )
1259
+ Q = ClusterQuiver( dg, frozen=list(range(self._n, self._n+self._m+self._n)) )
1260
+ Q._mutation_type = self._mutation_type
1261
+ if inplace:
1262
+ self.__init__(Q)
1263
+ else:
1264
+ return Q
1265
+
1266
+ def first_sink(self):
1267
+ r"""
1268
+ Return the first vertex of ``self`` that is a sink.
1269
+
1270
+ EXAMPLES::
1271
+
1272
+ sage: Q = ClusterQuiver(['A',5])
1273
+ sage: Q.mutate([1,2,4,3,2])
1274
+ sage: Q.first_sink()
1275
+ 0
1276
+ """
1277
+ sinks = self.digraph().sinks()
1278
+
1279
+ if sinks:
1280
+ return sinks[0]
1281
+ return None
1282
+
1283
+ def sinks(self):
1284
+ r"""
1285
+ Return all vertices of ``self`` that are sinks.
1286
+
1287
+ EXAMPLES::
1288
+
1289
+ sage: Q = ClusterQuiver(['A',5])
1290
+ sage: Q.mutate([1,2,4,3,2])
1291
+ sage: Q.sinks()
1292
+ [0, 2]
1293
+
1294
+ sage: Q = ClusterQuiver(['A',5])
1295
+ sage: Q.mutate([2,1,3,4,2])
1296
+ sage: Q.sinks()
1297
+ [3]
1298
+ """
1299
+ return self.digraph().sinks()
1300
+
1301
+ def first_source(self):
1302
+ r"""
1303
+ Return the first vertex of ``self`` that is a source.
1304
+
1305
+ EXAMPLES::
1306
+
1307
+ sage: Q = ClusterQuiver(['A',5])
1308
+ sage: Q.mutate([2,1,3,4,2])
1309
+ sage: Q.first_source()
1310
+ 1
1311
+ """
1312
+ sources = self.digraph().sources()
1313
+
1314
+ if sources:
1315
+ return sources[0]
1316
+ return None
1317
+
1318
+ def sources(self):
1319
+ r"""
1320
+ Return all vertices of ``self`` that are sources.
1321
+
1322
+ EXAMPLES::
1323
+
1324
+ sage: Q = ClusterQuiver(['A',5])
1325
+ sage: Q.mutate([1,2,4,3,2])
1326
+ sage: Q.sources()
1327
+ []
1328
+
1329
+ sage: Q = ClusterQuiver(['A',5])
1330
+ sage: Q.mutate([2,1,3,4,2])
1331
+ sage: Q.sources()
1332
+ [1]
1333
+ """
1334
+ return self.digraph().sources()
1335
+
1336
+ def mutate(self, data, inplace=True):
1337
+ """
1338
+ Mutates ``self`` at a sequence of vertices.
1339
+
1340
+ INPUT:
1341
+
1342
+ - ``sequence`` -- a vertex of ``self``, an iterator of vertices of
1343
+ ``self``, a function which takes in the ClusterQuiver and returns a
1344
+ vertex or an iterator of vertices, or a string of the parameter
1345
+ wanting to be called on ClusterQuiver that will return a vertex or
1346
+ an iterator of vertices
1347
+ - ``inplace`` -- boolean (default: ``True``); if ``False``, the result
1348
+ is returned, otherwise ``self`` is modified
1349
+
1350
+ EXAMPLES::
1351
+
1352
+ sage: Q = ClusterQuiver(['A',4]); Q.b_matrix()
1353
+ [ 0 1 0 0]
1354
+ [-1 0 -1 0]
1355
+ [ 0 1 0 1]
1356
+ [ 0 0 -1 0]
1357
+
1358
+ sage: Q.mutate(0); Q.b_matrix()
1359
+ [ 0 -1 0 0]
1360
+ [ 1 0 -1 0]
1361
+ [ 0 1 0 1]
1362
+ [ 0 0 -1 0]
1363
+
1364
+ sage: T = Q.mutate(0, inplace=False); T
1365
+ Quiver on 4 vertices of type ['A', 4]
1366
+
1367
+ sage: Q.mutate(0)
1368
+ sage: Q == T
1369
+ True
1370
+
1371
+ sage: Q.mutate([0,1,0])
1372
+ sage: Q.b_matrix()
1373
+ [ 0 -1 1 0]
1374
+ [ 1 0 0 0]
1375
+ [-1 0 0 1]
1376
+ [ 0 0 -1 0]
1377
+
1378
+ sage: Q = ClusterQuiver(QuiverMutationType([['A',1],['A',3]]))
1379
+ sage: Q.b_matrix()
1380
+ [ 0 0 0 0]
1381
+ [ 0 0 1 0]
1382
+ [ 0 -1 0 -1]
1383
+ [ 0 0 1 0]
1384
+
1385
+ sage: T = Q.mutate(0,inplace=False)
1386
+ sage: Q == T
1387
+ True
1388
+
1389
+ sage: Q = ClusterQuiver(['A',3]); Q.b_matrix()
1390
+ [ 0 1 0]
1391
+ [-1 0 -1]
1392
+ [ 0 1 0]
1393
+ sage: Q.mutate('first_sink'); Q.b_matrix()
1394
+ [ 0 -1 0]
1395
+ [ 1 0 1]
1396
+ [ 0 -1 0]
1397
+ sage: Q.mutate('first_source'); Q.b_matrix()
1398
+ [ 0 1 0]
1399
+ [-1 0 -1]
1400
+ [ 0 1 0]
1401
+
1402
+ sage: dg = DiGraph()
1403
+ sage: dg.add_vertices(['a','b','c','d','e'])
1404
+ sage: dg.add_edges([['a','b'], ['b','c'], ['c','d'], ['d','e']])
1405
+ sage: Q2 = ClusterQuiver(dg, frozen=['c']); Q2.b_matrix()
1406
+ [ 0 1 0 0]
1407
+ [-1 0 0 0]
1408
+ [ 0 0 0 1]
1409
+ [ 0 0 -1 0]
1410
+ [ 0 -1 1 0]
1411
+ sage: Q2.mutate('a'); Q2.b_matrix()
1412
+ [ 0 -1 0 0]
1413
+ [ 1 0 0 0]
1414
+ [ 0 0 0 1]
1415
+ [ 0 0 -1 0]
1416
+ [ 0 -1 1 0]
1417
+
1418
+ sage: dg = DiGraph([['a', 'b'], ['b', 'c']], format='list_of_edges')
1419
+ sage: Q = ClusterQuiver(dg);Q
1420
+ Quiver on 3 vertices
1421
+ sage: Q.mutate(['a','b'],inplace=False).digraph().edges(sort=True)
1422
+ [('a', 'b', (1, -1)), ('c', 'b', (1, -1))]
1423
+
1424
+ TESTS::
1425
+
1426
+ sage: Q = ClusterQuiver(['A',4]); Q.mutate(0,1)
1427
+ Traceback (most recent call last):
1428
+ ...
1429
+ ValueError: The second parameter must be boolean. To mutate at a sequence of length 2, input it as a list.
1430
+
1431
+ sage: Q = ClusterQuiver(['A',4]); Q.mutate(0,0)
1432
+ Traceback (most recent call last):
1433
+ ...
1434
+ ValueError: The second parameter must be boolean. To mutate at a sequence of length 2, input it as a list.
1435
+ """
1436
+ dg = self._digraph
1437
+ V = nlist = self._nlist
1438
+ mlist = self._mlist
1439
+
1440
+ # If we get a string which is not a cluster variable, execute as a function
1441
+ if isinstance(data, str):
1442
+ if data not in V:
1443
+ data = getattr(self, data)()
1444
+
1445
+ # If we get a function, execute it
1446
+ if callable(data):
1447
+ # function should return either integer or sequence
1448
+ data = data(self)
1449
+
1450
+ if data is None:
1451
+ raise ValueError('Not mutating: No vertices given.')
1452
+
1453
+ if data in V:
1454
+ seq = [data]
1455
+ else:
1456
+ seq = data
1457
+ if isinstance(seq, tuple):
1458
+ seq = list( seq )
1459
+ if not isinstance(seq, list):
1460
+ raise ValueError('The quiver can only be mutated at a vertex or at a sequence of vertices')
1461
+ if not isinstance(inplace, bool):
1462
+ raise ValueError('The second parameter must be boolean. To mutate at a sequence of length 2, input it as a list.')
1463
+ if any(v not in V for v in seq):
1464
+ v = next(v for v in seq if v not in V)
1465
+ raise ValueError('The quiver cannot be mutated at the vertex %s' % v)
1466
+
1467
+ for v in seq:
1468
+ dg = _digraph_mutate(dg, v, frozen=mlist)
1469
+
1470
+ if inplace:
1471
+ self._M = _edge_list_to_matrix(dg.edge_iterator(), nlist, mlist)
1472
+ self._M.set_immutable()
1473
+ self._digraph = dg
1474
+ else:
1475
+ Q = ClusterQuiver(dg, frozen=mlist)
1476
+ Q._mutation_type = self._mutation_type
1477
+ return Q
1478
+
1479
+ def mutation_sequence(self, sequence, show_sequence=False, fig_size=1.2 ):
1480
+ """
1481
+ Return a list containing the sequence of quivers obtained from ``self``
1482
+ by a sequence of mutations on vertices.
1483
+
1484
+ INPUT:
1485
+
1486
+ - ``sequence`` -- list or tuple of vertices of ``self``
1487
+ - ``show_sequence`` -- boolean (default: ``False``); if ``True``, a png
1488
+ containing the mutation sequence is shown
1489
+ - ``fig_size`` -- (default: 1.2) factor by which the size of the
1490
+ sequence is expanded
1491
+
1492
+ EXAMPLES::
1493
+
1494
+ sage: Q = ClusterQuiver(['A',4])
1495
+ sage: seq = Q.mutation_sequence([0,1]); seq
1496
+ [Quiver on 4 vertices of type ['A', 4],
1497
+ Quiver on 4 vertices of type ['A', 4],
1498
+ Quiver on 4 vertices of type ['A', 4]]
1499
+ sage: [T.b_matrix() for T in seq]
1500
+ [
1501
+ [ 0 1 0 0] [ 0 -1 0 0] [ 0 1 -1 0]
1502
+ [-1 0 -1 0] [ 1 0 -1 0] [-1 0 1 0]
1503
+ [ 0 1 0 1] [ 0 1 0 1] [ 1 -1 0 1]
1504
+ [ 0 0 -1 0], [ 0 0 -1 0], [ 0 0 -1 0]
1505
+ ]
1506
+ """
1507
+ n = self._n
1508
+ m = self._m
1509
+ if m == 0:
1510
+ width_factor = 3
1511
+ fig_size = fig_size*2*n/3
1512
+ else:
1513
+ width_factor = 6
1514
+ fig_size = fig_size*4*n/3
1515
+ V = list(range(n))
1516
+
1517
+ if isinstance(sequence, tuple):
1518
+ sequence = list( sequence )
1519
+ if not isinstance(sequence, list):
1520
+ raise ValueError('The quiver can only be mutated at a vertex or at a sequence of vertices')
1521
+ if any(v not in V for v in sequence):
1522
+ v = next(v for v in sequence if v not in V)
1523
+ raise ValueError('The quiver can only be mutated at the vertex %s' % v )
1524
+
1525
+ quiver = copy( self )
1526
+ quiver_sequence = []
1527
+ quiver_sequence.append( copy( quiver ) )
1528
+
1529
+ for v in sequence:
1530
+ quiver.mutate( v )
1531
+ quiver_sequence.append( copy( quiver ) )
1532
+
1533
+ if show_sequence:
1534
+ from sage.plot.plot import Graphics
1535
+ from sage.plot.text import text
1536
+
1537
+ def _plot_arrow( v, k, center=(0, 0) ):
1538
+ return text(r"$\longleftrightarrow$",(center[0],center[1]), fontsize=25) + text(r"$\mu_"+str(v)+"$",(center[0],center[1]+0.15), fontsize=15) \
1539
+ + text("$"+str(k)+"$",(center[0],center[1]-0.2), fontsize=15)
1540
+
1541
+ plot_sequence = [ quiver_sequence[i].plot( circular=True, center=(i*width_factor,0) ) for i in range(len(quiver_sequence)) ]
1542
+ arrow_sequence = [ _plot_arrow( sequence[i],i+1,center=((i+0.5)*width_factor,0) ) for i in range(len(sequence)) ]
1543
+ sequence = []
1544
+ for i in range( len( plot_sequence ) ):
1545
+ if i < len( arrow_sequence ):
1546
+ sequence.append( plot_sequence[i] + arrow_sequence[i] )
1547
+ else:
1548
+ sequence.append( plot_sequence[i] )
1549
+ plot_obj = Graphics()
1550
+ for elem in sequence:
1551
+ plot_obj += elem
1552
+ plot_obj.show(axes=False, figsize=[fig_size*len(quiver_sequence),fig_size])
1553
+ return quiver_sequence
1554
+
1555
+ def reorient(self, data):
1556
+ """
1557
+ Reorient ``self`` with respect to the given total order, or
1558
+ with respect to an iterator of edges in ``self`` to be
1559
+ reverted.
1560
+
1561
+ .. WARNING::
1562
+
1563
+ This operation might change the mutation type of ``self``.
1564
+
1565
+ INPUT:
1566
+
1567
+ - ``data`` -- an iterator defining a total order on
1568
+ ``self.vertices()``, or an iterator of edges in ``self`` to
1569
+ be reoriented.
1570
+
1571
+ EXAMPLES::
1572
+
1573
+ sage: Q = ClusterQuiver(['A',(2,3),1])
1574
+ sage: Q.mutation_type()
1575
+ ['A', [2, 3], 1]
1576
+
1577
+ sage: Q.reorient([(0,1),(1,2),(2,3),(3,4)])
1578
+ sage: Q.mutation_type()
1579
+ ['D', 5]
1580
+
1581
+ sage: Q.reorient([0,1,2,3,4])
1582
+ sage: Q.mutation_type()
1583
+ ['A', [1, 4], 1]
1584
+
1585
+ TESTS::
1586
+
1587
+ sage: Q = ClusterQuiver(['A',2])
1588
+ sage: Q.reorient([])
1589
+ Traceback (most recent call last):
1590
+ ...
1591
+ ValueError: empty input
1592
+ sage: Q.reorient([3,4])
1593
+ Traceback (most recent call last):
1594
+ ...
1595
+ ValueError: not a total order on the vertices of the quiver or
1596
+ a list of edges to be oriented
1597
+ """
1598
+ if not data:
1599
+ raise ValueError('empty input')
1600
+ first = data[0]
1601
+
1602
+ if set(data) == set(range(self._n + self._m)):
1603
+ dg_new = DiGraph()
1604
+ for edge in self._digraph.edges(sort=True):
1605
+ if data.index(edge[0]) < data.index(edge[1]):
1606
+ dg_new.add_edge(edge[0], edge[1], edge[2])
1607
+ else:
1608
+ dg_new.add_edge(edge[1], edge[0], edge[2])
1609
+ self._digraph = dg_new
1610
+ self._M = _edge_list_to_matrix(dg_new.edges(sort=True),
1611
+ self._nlist, self._mlist)
1612
+ self._M.set_immutable()
1613
+ self._mutation_type = None
1614
+ elif isinstance(first, (list, tuple)) and len(first) == 2:
1615
+ edges = self._digraph.edges(sort=True, labels=False)
1616
+ for edge in data:
1617
+ if (edge[1], edge[0]) in edges:
1618
+ label = self._digraph.edge_label(edge[1], edge[0])
1619
+ self._digraph.delete_edge(edge[1], edge[0])
1620
+ self._digraph.add_edge(edge[0], edge[1], label)
1621
+ self._M = _edge_list_to_matrix(self._digraph.edges(sort=True),
1622
+ self._nlist, self._mlist)
1623
+ self._M.set_immutable()
1624
+ self._mutation_type = None
1625
+ else:
1626
+ raise ValueError('not a total order on the vertices of the quiver or a list of edges to be oriented')
1627
+
1628
+ def mutation_class_iter(self, depth=infinity, show_depth=False,
1629
+ return_paths=False, data_type='quiver',
1630
+ up_to_equivalence=True, sink_source=False):
1631
+ """
1632
+ Return an iterator for the mutation class of ``self`` together with certain constraints.
1633
+
1634
+ INPUT:
1635
+
1636
+ - ``depth`` -- integer (default: infinity); only quivers with distance
1637
+ at most depth from ``self`` are returned.
1638
+ - ``show_depth`` -- boolean (default: ``False``); if ``True``, the
1639
+ actual depth of the mutation is shown
1640
+ - ``return_paths`` -- boolean (default: ``False``); if ``True``, a
1641
+ shortest path of mutation sequences from ``self`` to the given quiver
1642
+ is returned as well
1643
+ - ``data_type`` -- (default: ``'quiver'``) can be one of the following::
1644
+
1645
+ * "quiver"
1646
+ * "matrix"
1647
+ * "digraph"
1648
+ * "dig6"
1649
+ * "path"
1650
+
1651
+ - ``up_to_equivalence`` -- boolean (default: ``True``); if ``True``,
1652
+ only one quiver for each graph-isomorphism class is recorded
1653
+ - ``sink_source`` -- boolean (default: ``False``); if ``True``, only
1654
+ mutations at sinks and sources are applied
1655
+
1656
+ EXAMPLES::
1657
+
1658
+ sage: Q = ClusterQuiver(['A',3])
1659
+ sage: it = Q.mutation_class_iter()
1660
+ sage: for T in it: print(T)
1661
+ Quiver on 3 vertices of type ['A', 3]
1662
+ Quiver on 3 vertices of type ['A', 3]
1663
+ Quiver on 3 vertices of type ['A', 3]
1664
+ Quiver on 3 vertices of type ['A', 3]
1665
+
1666
+ sage: it = Q.mutation_class_iter(depth=1)
1667
+ sage: for T in it: print(T)
1668
+ Quiver on 3 vertices of type ['A', 3]
1669
+ Quiver on 3 vertices of type ['A', 3]
1670
+ Quiver on 3 vertices of type ['A', 3]
1671
+
1672
+ sage: it = Q.mutation_class_iter(show_depth=True)
1673
+ sage: for T in it: pass
1674
+ Depth: 0 found: 1 Time: ... s
1675
+ Depth: 1 found: 3 Time: ... s
1676
+ Depth: 2 found: 4 Time: ... s
1677
+
1678
+ sage: it = Q.mutation_class_iter(return_paths=True)
1679
+ sage: for T in it: print(T)
1680
+ (Quiver on 3 vertices of type ['A', 3], [])
1681
+ (Quiver on 3 vertices of type ['A', 3], [1])
1682
+ (Quiver on 3 vertices of type ['A', 3], [0])
1683
+ (Quiver on 3 vertices of type ['A', 3], [0, 1])
1684
+
1685
+ sage: it = Q.mutation_class_iter(up_to_equivalence=False)
1686
+ sage: for T in it: print(T)
1687
+ Quiver on 3 vertices of type ['A', 3]
1688
+ Quiver on 3 vertices of type ['A', 3]
1689
+ Quiver on 3 vertices of type ['A', 3]
1690
+ Quiver on 3 vertices of type ['A', 3]
1691
+ Quiver on 3 vertices of type ['A', 3]
1692
+ Quiver on 3 vertices of type ['A', 3]
1693
+ Quiver on 3 vertices of type ['A', 3]
1694
+ Quiver on 3 vertices of type ['A', 3]
1695
+ Quiver on 3 vertices of type ['A', 3]
1696
+ Quiver on 3 vertices of type ['A', 3]
1697
+ Quiver on 3 vertices of type ['A', 3]
1698
+ Quiver on 3 vertices of type ['A', 3]
1699
+ Quiver on 3 vertices of type ['A', 3]
1700
+ Quiver on 3 vertices of type ['A', 3]
1701
+
1702
+ sage: it = Q.mutation_class_iter(return_paths=True,up_to_equivalence=False)
1703
+ sage: mutation_class = list(it)
1704
+ sage: len(mutation_class)
1705
+ 14
1706
+ sage: mutation_class[0]
1707
+ (Quiver on 3 vertices of type ['A', 3], [])
1708
+
1709
+ sage: Q = ClusterQuiver(['A',3])
1710
+ sage: it = Q.mutation_class_iter(data_type='path')
1711
+ sage: for T in it: print(T)
1712
+ []
1713
+ [1]
1714
+ [0]
1715
+ [0, 1]
1716
+
1717
+ sage: Q = ClusterQuiver(['A',3])
1718
+ sage: it = Q.mutation_class_iter(return_paths=True,data_type='matrix')
1719
+ sage: next(it)
1720
+ (
1721
+ [ 0 0 1]
1722
+ [ 0 0 1]
1723
+ [-1 -1 0], []
1724
+ )
1725
+
1726
+ sage: dg = DiGraph([['a', 'b'], ['b', 'c']], format='list_of_edges')
1727
+ sage: S = ClusterQuiver(dg, frozen=['b'])
1728
+ sage: S.mutation_class()
1729
+ [Quiver on 3 vertices with 1 frozen vertex,
1730
+ Quiver on 3 vertices with 1 frozen vertex,
1731
+ Quiver on 3 vertices with 1 frozen vertex]
1732
+ """
1733
+ if data_type == 'path':
1734
+ return_paths = False
1735
+ if data_type == "dig6":
1736
+ return_dig6 = True
1737
+ else:
1738
+ return_dig6 = False
1739
+
1740
+ # jump to the standard labelling convention
1741
+ dg = ClusterQuiver(self._M).digraph()
1742
+ frozen = list(range(self._n, self._n + self._m))
1743
+
1744
+ MC_iter = _mutation_class_iter(dg, self._n, self._m, depth=depth,
1745
+ return_dig6=return_dig6,
1746
+ show_depth=show_depth,
1747
+ up_to_equivalence=up_to_equivalence,
1748
+ sink_source=sink_source)
1749
+ for data in MC_iter:
1750
+ if data_type == "quiver":
1751
+ next_element = ClusterQuiver(data[0], frozen=frozen)
1752
+ next_element._mutation_type = self._mutation_type
1753
+ elif data_type == "matrix":
1754
+ next_element = ClusterQuiver(data[0], frozen=frozen)._M
1755
+ elif data_type == "digraph":
1756
+ next_element = data[0]
1757
+ elif data_type == "dig6":
1758
+ next_element = data[0]
1759
+ elif data_type == "path":
1760
+ next_element = data[1]
1761
+ else:
1762
+ raise ValueError("the parameter for data_type was "
1763
+ "not recognized")
1764
+ if return_paths:
1765
+ yield (next_element, data[1])
1766
+ else:
1767
+ yield next_element
1768
+
1769
+ def mutation_class(self, depth=infinity, show_depth=False, return_paths=False,
1770
+ data_type='quiver', up_to_equivalence=True, sink_source=False):
1771
+ """
1772
+ Return the mutation class of ``self`` together with certain constraints.
1773
+
1774
+ INPUT:
1775
+
1776
+ - ``depth`` -- (default: ``infinity``) integer, only seeds with
1777
+ distance at most depth from ``self`` are returned
1778
+ - ``show_depth`` -- boolean (default: ``False``); if ``True``, the
1779
+ actual depth of the mutation is shown
1780
+ - ``return_paths`` -- boolean (default: ``False``); if ``True``, a
1781
+ shortest path of mutation sequences from ``self`` to the given quiver is
1782
+ returned as well
1783
+ - ``data_type`` -- (default: ``'quiver'``) can be one of
1784
+ the following:
1785
+
1786
+ * ``'quiver'`` -- the quiver is returned
1787
+ * ``'dig6'`` -- the dig6-data is returned
1788
+ * ``'path'`` -- shortest paths of mutation sequences from
1789
+ ``self`` are returned
1790
+
1791
+ - ``sink_source`` -- boolean (default: ``False``); if ``True``, only
1792
+ mutations at sinks and sources are applied
1793
+
1794
+ EXAMPLES::
1795
+
1796
+ sage: Q = ClusterQuiver(['A',3])
1797
+ sage: Ts = Q.mutation_class()
1798
+ sage: for T in Ts: print(T)
1799
+ Quiver on 3 vertices of type ['A', 3]
1800
+ Quiver on 3 vertices of type ['A', 3]
1801
+ Quiver on 3 vertices of type ['A', 3]
1802
+ Quiver on 3 vertices of type ['A', 3]
1803
+
1804
+ sage: Ts = Q.mutation_class(depth=1)
1805
+ sage: for T in Ts: print(T)
1806
+ Quiver on 3 vertices of type ['A', 3]
1807
+ Quiver on 3 vertices of type ['A', 3]
1808
+ Quiver on 3 vertices of type ['A', 3]
1809
+
1810
+ sage: Ts = Q.mutation_class(show_depth=True)
1811
+ Depth: 0 found: 1 Time: ... s
1812
+ Depth: 1 found: 3 Time: ... s
1813
+ Depth: 2 found: 4 Time: ... s
1814
+
1815
+ sage: Ts = Q.mutation_class(return_paths=True)
1816
+ sage: for T in Ts: print(T)
1817
+ (Quiver on 3 vertices of type ['A', 3], [])
1818
+ (Quiver on 3 vertices of type ['A', 3], [1])
1819
+ (Quiver on 3 vertices of type ['A', 3], [0])
1820
+ (Quiver on 3 vertices of type ['A', 3], [0, 1])
1821
+
1822
+ sage: Ts = Q.mutation_class(up_to_equivalence=False)
1823
+ sage: for T in Ts: print(T)
1824
+ Quiver on 3 vertices of type ['A', 3]
1825
+ Quiver on 3 vertices of type ['A', 3]
1826
+ Quiver on 3 vertices of type ['A', 3]
1827
+ Quiver on 3 vertices of type ['A', 3]
1828
+ Quiver on 3 vertices of type ['A', 3]
1829
+ Quiver on 3 vertices of type ['A', 3]
1830
+ Quiver on 3 vertices of type ['A', 3]
1831
+ Quiver on 3 vertices of type ['A', 3]
1832
+ Quiver on 3 vertices of type ['A', 3]
1833
+ Quiver on 3 vertices of type ['A', 3]
1834
+ Quiver on 3 vertices of type ['A', 3]
1835
+ Quiver on 3 vertices of type ['A', 3]
1836
+ Quiver on 3 vertices of type ['A', 3]
1837
+ Quiver on 3 vertices of type ['A', 3]
1838
+
1839
+ sage: Ts = Q.mutation_class(return_paths=True,up_to_equivalence=False)
1840
+ sage: len(Ts)
1841
+ 14
1842
+ sage: Ts[0]
1843
+ (Quiver on 3 vertices of type ['A', 3], [])
1844
+
1845
+ sage: Ts = Q.mutation_class(show_depth=True)
1846
+ Depth: 0 found: 1 Time: ... s
1847
+ Depth: 1 found: 3 Time: ... s
1848
+ Depth: 2 found: 4 Time: ... s
1849
+
1850
+ sage: Ts = Q.mutation_class(show_depth=True, up_to_equivalence=False)
1851
+ Depth: 0 found: 1 Time: ... s
1852
+ Depth: 1 found: 4 Time: ... s
1853
+ Depth: 2 found: 6 Time: ... s
1854
+ Depth: 3 found: 10 Time: ... s
1855
+ Depth: 4 found: 14 Time: ... s
1856
+
1857
+ TESTS::
1858
+
1859
+ sage: all(len(ClusterQuiver(['A',n]).mutation_class())
1860
+ ....: == ClusterQuiver(['A',n]).mutation_type().class_size()
1861
+ ....: for n in [2..6])
1862
+ True
1863
+
1864
+ sage: all(len(ClusterQuiver(['B',n]).mutation_class())
1865
+ ....: == ClusterQuiver(['B',n]).mutation_type().class_size()
1866
+ ....: for n in [2..6])
1867
+ True
1868
+ """
1869
+ if depth is infinity and not self.is_mutation_finite():
1870
+ raise ValueError('the mutation class can - for infinite mutation'
1871
+ ' types - only be computed up to a given depth')
1872
+ return list(self.mutation_class_iter(depth=depth, show_depth=show_depth,
1873
+ return_paths=return_paths,
1874
+ data_type=data_type,
1875
+ up_to_equivalence=up_to_equivalence,
1876
+ sink_source=sink_source))
1877
+
1878
+ def is_finite(self):
1879
+ """
1880
+ Return ``True`` if ``self`` is of finite type.
1881
+
1882
+ EXAMPLES::
1883
+
1884
+ sage: Q = ClusterQuiver(['A',3])
1885
+ sage: Q.is_finite()
1886
+ True
1887
+ sage: Q = ClusterQuiver(['A',[2,2],1])
1888
+ sage: Q.is_finite()
1889
+ False
1890
+ sage: Q = ClusterQuiver([['A',3],['B',3]])
1891
+ sage: Q.is_finite()
1892
+ True
1893
+ sage: Q = ClusterQuiver(['T',[4,4,4]])
1894
+ sage: Q.is_finite()
1895
+ False
1896
+ sage: Q = ClusterQuiver([['A',3],['T',[4,4,4]]])
1897
+ sage: Q.is_finite()
1898
+ False
1899
+ sage: Q = ClusterQuiver([['A',3],['T',[2,2,3]]])
1900
+ sage: Q.is_finite()
1901
+ True
1902
+ sage: Q = ClusterQuiver([['A',3],['D',5]])
1903
+ sage: Q.is_finite()
1904
+ True
1905
+ sage: Q = ClusterQuiver([['A',3],['D',5,1]])
1906
+ sage: Q.is_finite()
1907
+ False
1908
+
1909
+ sage: Q = ClusterQuiver([[0,1,2],[1,2,2],[2,0,2]])
1910
+ sage: Q.is_finite()
1911
+ False
1912
+
1913
+ sage: Q = ClusterQuiver([[0,1,2],[1,2,2],[2,0,2],[3,4,1],[4,5,1]])
1914
+ sage: Q.is_finite()
1915
+ False
1916
+ """
1917
+ mt = self.mutation_type()
1918
+ return (type(mt) in [QuiverMutationType_Irreducible,
1919
+ QuiverMutationType_Reducible] and mt.is_finite())
1920
+
1921
+ def is_mutation_finite( self, nr_of_checks=None, return_path=False ):
1922
+ """
1923
+ Uses a non-deterministic method by random mutations in various
1924
+ directions. Can result in a wrong answer.
1925
+
1926
+ INPUT:
1927
+
1928
+ - ``nr_of_checks`` -- (default: ``None``) number of mutations applied;
1929
+ Standard is 500*(number of vertices of ``self``)
1930
+ - ``return_path`` -- (default: ``False``) if ``True``, in case of
1931
+ ``self`` not being mutation finite, a path from ``self`` to a quiver
1932
+ with an edge label `(a,-b)` and `a*b > 4` is returned
1933
+
1934
+ ALGORITHM:
1935
+
1936
+ A quiver is mutation infinite if and only if every edge label (a,-b) satisfy a*b > 4.
1937
+ Thus, we apply random mutations in random directions
1938
+
1939
+ EXAMPLES::
1940
+
1941
+ sage: Q = ClusterQuiver(['A',10])
1942
+ sage: Q._mutation_type = None
1943
+ sage: Q.is_mutation_finite()
1944
+ True
1945
+
1946
+ sage: Q = ClusterQuiver([(0,1),(1,2),(2,3),(3,4),(4,5),(5,6),(6,7),(7,8),(2,9)])
1947
+ sage: Q.is_mutation_finite()
1948
+ False
1949
+ """
1950
+ if self._n <= 2:
1951
+ is_finite = True
1952
+ path = None
1953
+ elif not return_path and self._mutation_type == 'undetermined infinite mutation type':
1954
+ is_finite = False
1955
+ elif (isinstance(self._mutation_type, (QuiverMutationType_Irreducible,
1956
+ QuiverMutationType_Reducible))
1957
+ and self._mutation_type.is_mutation_finite()):
1958
+ is_finite = True
1959
+ path = None
1960
+ elif (not return_path and isinstance(self._mutation_type,
1961
+ (QuiverMutationType_Irreducible,
1962
+ QuiverMutationType_Reducible))
1963
+ and not self._mutation_type.is_mutation_finite()):
1964
+ is_finite = False
1965
+ else:
1966
+ # turning dg_component into a canonical form
1967
+ dig6 = _digraph_to_dig6(self.digraph())
1968
+ # and getting the corresponding matrix
1969
+ M = _dig6_to_matrix(dig6)
1970
+
1971
+ is_finite, path = is_mutation_finite(M, nr_of_checks=nr_of_checks)
1972
+ if return_path:
1973
+ return is_finite, path
1974
+ else:
1975
+ return is_finite
1976
+
1977
+ def number_of_edges(self):
1978
+ r"""
1979
+ Return the total number of edges on the quiver.
1980
+
1981
+ Note: This only works with non-valued quivers. If used on a
1982
+ non-valued quiver then the positive value is taken to be the number of edges added
1983
+
1984
+ OUTPUT: integer of the number of edges
1985
+
1986
+ EXAMPLES::
1987
+
1988
+ sage: S = ClusterQuiver(['A',4]); S.number_of_edges()
1989
+ 3
1990
+
1991
+ sage: S = ClusterQuiver(['B',4]); S.number_of_edges()
1992
+ 3
1993
+ """
1994
+ digraph_edges = self.digraph().edges(sort=True)
1995
+
1996
+ total_edges = 0
1997
+ for edge in digraph_edges:
1998
+ total_edges += edge[2][0]
1999
+
2000
+ return total_edges
2001
+
2002
+ def relabel(self, relabelling, inplace=True):
2003
+ r"""
2004
+ Return the quiver after doing a relabelling.
2005
+
2006
+ Will relabel the vertices of the quiver.
2007
+
2008
+ INPUT:
2009
+
2010
+ - ``relabelling`` -- dictionary of labels to move around
2011
+ - ``inplace`` -- boolean (default: ``True``); if ``True``, will return
2012
+ a duplicate of the quiver
2013
+
2014
+ EXAMPLES::
2015
+
2016
+ sage: S = ClusterQuiver(['A',4]).relabel({1:'5',2:'go'})
2017
+ """
2018
+ if inplace:
2019
+ quiver = self
2020
+ else:
2021
+ quiver = ClusterQuiver(self)
2022
+
2023
+ # Instantiation variables
2024
+ old_vertices = list(quiver.digraph())
2025
+ digraph_labels = {}
2026
+ dict_labels = {}
2027
+
2028
+ # Organize labels noting that for:
2029
+ # _digraph: { old_vertex: new_vertex}
2030
+ # _vertex_dictionary: {num: new_vertex}
2031
+ if isinstance(relabelling, list):
2032
+ digraph_labels = {old_vertices[i]: relabelling[i] for i in range(len(relabelling))}
2033
+ dict_labels = {range(len(relabelling))[i]: relabelling[i] for i in range(len(relabelling))}
2034
+ elif isinstance(relabelling, dict):
2035
+ # need to make sure we map correctly
2036
+ for key in relabelling:
2037
+ val = relabelling[key]
2038
+
2039
+ if key in old_vertices:
2040
+ # If the key is in the old vertices, use that mapping
2041
+ digraph_labels[key] = val
2042
+ # And place it in the right order for our dictionary
2043
+ loc = [i for i, x in enumerate(old_vertices)
2044
+ if x == key][0]
2045
+ dict_labels[loc] = val
2046
+ elif isinstance(key, int) and len(old_vertices) > key:
2047
+ # If the key is an integer, grab that particular vertex
2048
+ digraph_labels[old_vertices[key]] = val
2049
+ # And copy it over to our dictionary
2050
+ dict_labels[key] = val
2051
+
2052
+ quiver._digraph.relabel(digraph_labels)
2053
+ quiver._vertex_dictionary = dict_labels
2054
+ return quiver
2055
+
2056
+ def poincare_semistable(self, theta, d):
2057
+ r"""
2058
+ Return the Poincaré polynomial of the moduli space of semi-stable
2059
+ representations of dimension vector `d`.
2060
+
2061
+ INPUT:
2062
+
2063
+ - ``theta`` -- stability weight, as list or vector of rationals
2064
+ - ``d`` -- dimension vector, as list or vector of coprime integers
2065
+
2066
+ The semi-stability is taken with respect to the slope function
2067
+
2068
+ .. MATH::
2069
+
2070
+ \mu(d) = \theta(d) / \operatorname{dim}(d)
2071
+
2072
+ where `d` is a dimension vector.
2073
+
2074
+ This uses the matrix-inversion algorithm from [Rei2002]_.
2075
+
2076
+ EXAMPLES::
2077
+
2078
+ sage: Q = ClusterQuiver(['A',2])
2079
+ sage: Q.poincare_semistable([1,0],[1,0])
2080
+ 1
2081
+ sage: Q.poincare_semistable([1,0],[1,1])
2082
+ 1
2083
+
2084
+ sage: K2 = ClusterQuiver(matrix([[0,2],[-2,0]]))
2085
+ sage: theta = (1, 0)
2086
+ sage: K2.poincare_semistable(theta, [1,0])
2087
+ 1
2088
+ sage: K2.poincare_semistable(theta, [1,1])
2089
+ v^2 + 1
2090
+ sage: K2.poincare_semistable(theta, [1,2])
2091
+ 1
2092
+ sage: K2.poincare_semistable(theta, [1,3])
2093
+ 0
2094
+
2095
+ sage: K3 = ClusterQuiver(matrix([[0,3],[-3,0]]))
2096
+ sage: theta = (1, 0)
2097
+ sage: K3.poincare_semistable(theta, (2,3))
2098
+ v^12 + v^10 + 3*v^8 + 3*v^6 + 3*v^4 + v^2 + 1
2099
+ sage: K3.poincare_semistable(theta, (3,4))(1)
2100
+ 68
2101
+
2102
+ TESTS::
2103
+
2104
+ sage: Q = ClusterQuiver(['A',2])
2105
+ sage: Q.poincare_semistable([1,0],[2,2])
2106
+ Traceback (most recent call last):
2107
+ ...
2108
+ ValueError: dimension vector d is not coprime
2109
+
2110
+ sage: Q = ClusterQuiver(['A',3])
2111
+ sage: Q.poincare_semistable([1,1,0],[2,3,4])
2112
+ 0
2113
+
2114
+ REFERENCES:
2115
+
2116
+ .. [Rei2002] Markus Reineke, *The Harder-Narasimhan system in quantum
2117
+ groups and cohomology of quiver moduli*, :arxiv:`math/0204059`
2118
+ """
2119
+ if gcd([x for x in d if x]) != 1:
2120
+ raise ValueError("dimension vector d is not coprime")
2121
+ d = vector(ZZ, d)
2122
+ theta = vector(theta)
2123
+
2124
+ n = self.n()
2125
+ b_mat = self.b_matrix()
2126
+ Eu = matrix(ZZ, n, n,
2127
+ lambda i, j: -b_mat[i, j] if b_mat[i, j] > 0 else 0)
2128
+ Eu = 1 + Eu
2129
+ edges = list(self.digraph().edges(sort=True, labels=False))
2130
+
2131
+ mu_d = theta.dot_product(d) / sum(d)
2132
+
2133
+ Li = [0 * d]
2134
+ it = (vector(e) for e in product(*[range(d_i + 1)
2135
+ for d_i in d]))
2136
+ Li += [e for e in it if e.dot_product(theta) > mu_d * sum(e)]
2137
+ Li.append(d)
2138
+ N = len(Li) - 1
2139
+
2140
+ q = polygen(QQ, 'v') # q stands for v**2 until the last line
2141
+
2142
+ def cardinal_RG(d):
2143
+ cardinal_G = prod(q**d_i - q**k for d_i in d for k in range(d_i))
2144
+ cardinal_R = prod(q**(b_mat[i, j] * d[i] * d[j])
2145
+ for i, j in edges)
2146
+ return cardinal_R / cardinal_G
2147
+
2148
+ Reineke_submat = matrix(q.parent().fraction_field(), N, N)
2149
+
2150
+ for i, e in enumerate(Li[:-1]):
2151
+ for j, f in enumerate(Li[1:]):
2152
+ if e == f:
2153
+ Reineke_submat[i, j] = 1
2154
+ continue
2155
+ f_e = f - e
2156
+ if all(x >= 0 for x in f_e):
2157
+ power = (-f_e) * Eu * e
2158
+ Reineke_submat[i, j] = q**power * cardinal_RG(f_e)
2159
+
2160
+ poly = (-1)**N * ((1 - q) * Reineke_submat.det()).numerator()
2161
+ return poly(q**2) # replacing q by v**2
2162
+
2163
+ def d_vector_fan(self):
2164
+ r"""
2165
+ Return the d-vector fan associated with the quiver.
2166
+
2167
+ It is the fan whose maximal cones are generated by the
2168
+ d-matrices of the clusters.
2169
+
2170
+ This is a complete simplicial fan (and even smooth when the
2171
+ initial quiver is acyclic). It only makes sense for quivers of
2172
+ finite type.
2173
+
2174
+ EXAMPLES::
2175
+
2176
+ sage: # needs sage.geometry.polyhedron sage.libs.singular
2177
+ sage: Fd = ClusterQuiver([[1,2]]).d_vector_fan(); Fd
2178
+ Rational polyhedral fan in 2-d lattice N
2179
+ sage: Fd.ngenerating_cones()
2180
+ 5
2181
+ sage: Fd = ClusterQuiver([[1,2],[2,3]]).d_vector_fan(); Fd
2182
+ Rational polyhedral fan in 3-d lattice N
2183
+ sage: Fd.ngenerating_cones()
2184
+ 14
2185
+ sage: Fd.is_smooth()
2186
+ True
2187
+ sage: Fd = ClusterQuiver([[1,2],[2,3],[3,1]]).d_vector_fan(); Fd
2188
+ Rational polyhedral fan in 3-d lattice N
2189
+ sage: Fd.ngenerating_cones()
2190
+ 14
2191
+ sage: Fd.is_smooth()
2192
+ False
2193
+
2194
+ TESTS::
2195
+
2196
+ sage: ClusterQuiver(['A',[2,2],1]).d_vector_fan()
2197
+ Traceback (most recent call last):
2198
+ ...
2199
+ ValueError: only makes sense for quivers of finite type
2200
+ """
2201
+ if not self.is_finite():
2202
+ raise ValueError('only makes sense for quivers of finite type')
2203
+
2204
+ from .cluster_seed import ClusterSeed
2205
+ from sage.geometry.fan import Fan
2206
+ from sage.geometry.cone import Cone
2207
+
2208
+ seed = ClusterSeed(self)
2209
+ return Fan([Cone(s.d_matrix().columns())
2210
+ for s in seed.mutation_class()])
2211
+
2212
+ def g_vector_fan(self):
2213
+ r"""
2214
+ Return the g-vector fan associated with the quiver.
2215
+
2216
+ It is the fan whose maximal cones are generated by the
2217
+ g-matrices of the clusters.
2218
+
2219
+ This is a complete simplicial fan. It is only supported for
2220
+ quivers of finite type.
2221
+
2222
+ EXAMPLES::
2223
+
2224
+ sage: # needs sage.geometry.polyhedron
2225
+ sage: Fg = ClusterQuiver([[1,2]]).g_vector_fan(); Fg
2226
+ Rational polyhedral fan in 2-d lattice N
2227
+ sage: Fg.ngenerating_cones()
2228
+ 5
2229
+
2230
+ sage: # needs sage.geometry.polyhedron
2231
+ sage: Fg = ClusterQuiver([[1,2],[2,3]]).g_vector_fan(); Fg
2232
+ Rational polyhedral fan in 3-d lattice N
2233
+ sage: Fg.ngenerating_cones()
2234
+ 14
2235
+ sage: Fg.is_smooth()
2236
+ True
2237
+
2238
+ sage: # needs sage.geometry.polyhedron
2239
+ sage: Fg = ClusterQuiver([[1,2],[2,3],[3,1]]).g_vector_fan(); Fg
2240
+ Rational polyhedral fan in 3-d lattice N
2241
+ sage: Fg.ngenerating_cones()
2242
+ 14
2243
+ sage: Fg.is_smooth()
2244
+ True
2245
+
2246
+ TESTS::
2247
+
2248
+ sage: # needs sage.geometry.polyhedron
2249
+ sage: ClusterQuiver(['A',[2,2],1]).g_vector_fan()
2250
+ Traceback (most recent call last):
2251
+ ...
2252
+ ValueError: only supported for quivers of finite type
2253
+ """
2254
+ from .cluster_seed import ClusterSeed
2255
+ from sage.geometry.fan import Fan
2256
+ from sage.geometry.cone import Cone
2257
+
2258
+ if not (self.is_finite()):
2259
+ raise ValueError('only supported for quivers of finite type')
2260
+ seed = ClusterSeed(self).principal_extension()
2261
+ return Fan([Cone(s.g_matrix().columns())
2262
+ for s in seed.mutation_class()])