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