Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/combinat/cluster_algebra_quiver/quiver.py
8818 views
1
r"""
2
Quiver
3
4
A *quiver* is an oriented graphs without loops, two-cycles, or multiple edges. The edges are labelled by pairs `(i,-j)`
5
such that the matrix `M = (m_{ab})` with `m_{ab} = i, m_{ba} = -j` for an edge `(i,-j)` between vertices `a` and `b` is skew-symmetrizable.
6
7
For the compendium on the cluster algebra and quiver package see
8
9
http://arxiv.org/abs/1102.4844.
10
11
AUTHORS:
12
13
- Gregg Musiker
14
- Christian Stump
15
16
.. seealso:: For mutation types of combinatorial quivers, see :meth:`~sage.combinat.cluster_algebra_quiver.quiver_mutation_type.QuiverMutationType`. Cluster seeds are closely related to :meth:`~sage.combinat.cluster_algebra_quiver.cluster_seed.ClusterSeed`.
17
"""
18
#*****************************************************************************
19
# Copyright (C) 2011 Gregg Musiker <[email protected]>
20
# Christian Stump <[email protected]>
21
#
22
# Distributed under the terms of the GNU General Public License (GPL)
23
# http://www.gnu.org/licenses/
24
#*****************************************************************************
25
from sage.structure.sage_object import SageObject
26
from copy import copy
27
from sage.structure.unique_representation import UniqueRepresentation
28
from sage.misc.all import cached_method
29
from sage.rings.all import ZZ, CC, infinity
30
from sage.graphs.all import Graph, DiGraph
31
from sage.combinat.cluster_algebra_quiver.quiver_mutation_type import QuiverMutationType, QuiverMutationType_Irreducible, QuiverMutationType_Reducible, _edge_list_to_matrix
32
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
33
from sage.combinat.cluster_algebra_quiver.mutation_type import _connected_mutation_type, _mutation_type_from_data, is_mutation_finite
34
35
from sage.groups.perm_gps.permgroup import PermutationGroup
36
37
class ClusterQuiver(SageObject):
38
"""
39
The *quiver* associated to an *exchange matrix*.
40
41
INPUT:
42
43
- ``data`` -- can be any of the following::
44
45
* QuiverMutationType
46
* str - a string representing a QuiverMutationType or a common quiver type (see Examples)
47
* ClusterQuiver
48
* Matrix - a skew-symmetrizable matrix
49
* DiGraph - must be the input data for a quiver
50
* List of edges - must be the edge list of a digraph for a quiver
51
52
- ``frozen`` -- (default:``None``) sets the number of frozen variables if the input type is a DiGraph, it is ignored otherwise.
53
54
EXAMPLES:
55
56
from a QuiverMutationType::
57
58
sage: Q = ClusterQuiver(['A',5]); Q
59
Quiver on 5 vertices of type ['A', 5]
60
61
sage: Q = ClusterQuiver(['B',2]); Q
62
Quiver on 2 vertices of type ['B', 2]
63
sage: Q2 = ClusterQuiver(['C',2]); Q2
64
Quiver on 2 vertices of type ['B', 2]
65
sage: MT = Q.mutation_type(); MT.standard_quiver() == Q
66
True
67
sage: MT = Q2.mutation_type(); MT.standard_quiver() == Q2
68
False
69
70
sage: Q = ClusterQuiver(['A',[2,5],1]); Q
71
Quiver on 7 vertices of type ['A', [2, 5], 1]
72
73
sage: Q = ClusterQuiver(['A', [5,0],1]); Q
74
Quiver on 5 vertices of type ['D', 5]
75
sage: Q.is_finite()
76
True
77
sage: Q.is_acyclic()
78
False
79
80
sage: Q = ClusterQuiver(['F', 4, [2,1]]); Q
81
Quiver on 6 vertices of type ['F', 4, [1, 2]]
82
sage: MT = Q.mutation_type(); MT.standard_quiver() == Q
83
False
84
sage: dg = Q.digraph(); Q.mutate([2,1,4,0,5,3])
85
sage: dg2 = Q.digraph(); dg2.is_isomorphic(dg,edge_labels=True)
86
False
87
sage: dg2.is_isomorphic(MT.standard_quiver().digraph(),edge_labels=True)
88
True
89
90
sage: Q = ClusterQuiver(['G',2, (3,1)]); Q
91
Quiver on 4 vertices of type ['G', 2, [1, 3]]
92
sage: MT = Q.mutation_type(); MT.standard_quiver() == Q
93
False
94
95
sage: Q = ClusterQuiver(['GR',[3,6]]); Q
96
Quiver on 4 vertices of type ['D', 4]
97
sage: MT = Q.mutation_type(); MT.standard_quiver() == Q
98
False
99
100
sage: Q = ClusterQuiver(['GR',[3,7]]); Q
101
Quiver on 6 vertices of type ['E', 6]
102
103
sage: Q = ClusterQuiver(['TR',2]); Q
104
Quiver on 3 vertices of type ['A', 3]
105
sage: MT = Q.mutation_type(); MT.standard_quiver() == Q
106
False
107
sage: Q.mutate([1,0]); MT.standard_quiver() == Q
108
True
109
110
sage: Q = ClusterQuiver(['TR',3]); Q
111
Quiver on 6 vertices of type ['D', 6]
112
sage: MT = Q.mutation_type(); MT.standard_quiver() == Q
113
False
114
115
from a ClusterQuiver::
116
117
sage: Q = ClusterQuiver(['A',[2,5],1]); Q
118
Quiver on 7 vertices of type ['A', [2, 5], 1]
119
sage: T = ClusterQuiver( Q ); T
120
Quiver on 7 vertices of type ['A', [2, 5], 1]
121
122
from a Matrix::
123
124
sage: Q = ClusterQuiver(['A',[2,5],1]); Q
125
Quiver on 7 vertices of type ['A', [2, 5], 1]
126
sage: T = ClusterQuiver( Q._M ); T
127
Quiver on 7 vertices
128
129
sage: Q = ClusterQuiver( matrix([[0,1,-1],[-1,0,1],[1,-1,0],[1,2,3]]) ); Q
130
Quiver on 4 vertices with 1 frozen vertex
131
132
sage: Q = ClusterQuiver( matrix([]) ); Q
133
Quiver without vertices
134
135
from a DiGraph::
136
137
sage: Q = ClusterQuiver(['A',[2,5],1]); Q
138
Quiver on 7 vertices of type ['A', [2, 5], 1]
139
sage: T = ClusterQuiver( Q._digraph ); T
140
Quiver on 7 vertices
141
142
sage: Q = ClusterQuiver( DiGraph([[1,2],[2,3],[3,4],[4,1]]) ); Q
143
Quiver on 4 vertices
144
145
from a List of edges::
146
147
sage: Q = ClusterQuiver(['A',[2,5],1]); Q
148
Quiver on 7 vertices of type ['A', [2, 5], 1]
149
sage: T = ClusterQuiver( Q._digraph.edges() ); T
150
Quiver on 7 vertices
151
152
sage: Q = ClusterQuiver( [[1,2],[2,3],[3,4],[4,1]] ); Q
153
Quiver on 4 vertices
154
155
TESTS::
156
157
sage: Q = ClusterQuiver(DiGraph([[1,1]]))
158
Traceback (most recent call last):
159
...
160
ValueError: The input DiGraph contains a loop
161
162
sage: Q = ClusterQuiver([[1,1]])
163
Traceback (most recent call last):
164
...
165
ValueError: The input DiGraph contains a loop
166
167
sage: Q = ClusterQuiver(DiGraph([[1, 0],[0,1]]))
168
Traceback (most recent call last):
169
...
170
ValueError: The input DiGraph contains two-cycles
171
172
sage: Q = ClusterQuiver('whatever')
173
Traceback (most recent call last):
174
...
175
ValueError: The input data was not recognized.
176
"""
177
def __init__( self, data, frozen=None ):
178
"""
179
TESTS::
180
181
sage: Q = ClusterQuiver(['A',4])
182
sage: TestSuite(Q).run()
183
"""
184
from cluster_seed import ClusterSeed
185
from sage.matrix.matrix import Matrix
186
187
# constructs a quiver from a mutation type
188
if type( data ) in [QuiverMutationType_Irreducible,QuiverMutationType_Reducible]:
189
if frozen is not None:
190
print 'The input data is a quiver, therefore the additional parameter frozen is ignored.'
191
192
mutation_type = data
193
self.__init__( mutation_type.standard_quiver() )
194
195
# constructs a quiver from string representing a mutation type or a common quiver type (see Examples)
196
# NOTE: for now, any string representing a *reducible type* is coerced into the standard quiver, but there is now more flexibility in how to input a connected (irreducible) quiver.
197
elif type( data ) in [list,tuple] and ( type( data[0] ) is str or all(type( comp ) in [list,tuple] and type( comp[0] ) is str for comp in data) ):
198
if frozen is not None:
199
print 'The input data is a quiver, therefore the additional parameter frozen is ignored.'
200
mutation_type = QuiverMutationType( data )
201
202
# The command QuiverMutationType_Irreducible (which is not imported globally) already creates the desired digraph as long as we bypass the mutation type checking of QuiverMutationType and format the input appropriately. Thus we handle several special cases this way.
203
if len(data) == 2 and type( data[0] ) is str:
204
if data[0] == 'TR' or data[0] == 'GR' or (data[0] == 'C' and data[1] == 2):
205
if data[1] in ZZ:
206
quiv = ClusterQuiver( QuiverMutationType_Irreducible( data[0], data[1] )._digraph )
207
quiv._mutation_type = mutation_type
208
self.__init__( quiv )
209
elif type( data[1]) is list:
210
quiv = ClusterQuiver( QuiverMutationType_Irreducible( data[0], tuple(data[1]) )._digraph )
211
quiv._mutation_type = mutation_type
212
self.__init__( quiv )
213
else:
214
self.__init__( mutation_type.standard_quiver() )
215
elif len(data) == 3 and type( data[0] ) is str:
216
if (data[0] == 'F' and data[1] == 4 and data[2] == [2,1]) or (data[0] == 'G' and data[1] == 2 and data[2] == [3,1]):
217
quiv = ClusterQuiver( QuiverMutationType_Irreducible( data[0], data[1], tuple(data[2]) )._digraph )
218
quiv._mutation_type = mutation_type
219
self.__init__( quiv )
220
elif (data[0] == 'F' and data[1] == 4 and data[2] == (2,1) ) or (data[0] == 'G' and data[1] == 2 and data[2] == (3,1) ):
221
quiv = ClusterQuiver( QuiverMutationType_Irreducible( data[0], data[1], data[2] )._digraph )
222
quiv._mutation_type = mutation_type
223
self.__init__( quiv )
224
elif data[0] == 'A' and type(data[1]) is list and data[2] == 1:
225
if len(data[1]) == 2 and min(data[1]) == 0:
226
quiv = ClusterQuiver( QuiverMutationType_Irreducible( data[0], tuple(data[1]), data[2] )._digraph )
227
quiv._mutation_type = mutation_type
228
self.__init__( quiv )
229
else:
230
self.__init__( mutation_type.standard_quiver() )
231
232
elif data[0] == 'A' and type(data[1]) is tuple and data[2] == 1:
233
if len(data[1]) == 2 and min(data[1]) == 0:
234
quiv = ClusterQuiver( QuiverMutationType_Irreducible( data[0], data[1], data[2] )._digraph )
235
quiv._mutation_type = mutation_type
236
self.__init__( quiv )
237
else:
238
self.__init__( mutation_type.standard_quiver() )
239
240
else:
241
self.__init__( mutation_type.standard_quiver() )
242
else:
243
self.__init__( mutation_type.standard_quiver() )
244
245
# constructs a quiver from a cluster seed
246
elif type(data) is ClusterSeed:
247
self.__init__( data.quiver() )
248
249
# constructs a quiver from a quiver
250
elif type(data) is ClusterQuiver:
251
if frozen is not None:
252
print 'The input data is a quiver, therefore the additional parameter frozen is ignored.'
253
254
self._M = copy(data._M)
255
self._n = data._n
256
self._m = data._m
257
self._digraph = copy( data._digraph )
258
self._mutation_type = data._mutation_type
259
self._description = data._description
260
261
# constructs a quiver from a matrix
262
elif isinstance(data, Matrix):
263
if not _principal_part(data).is_skew_symmetrizable( positive=True ):
264
raise ValueError('The principal part of the matrix data must be skew-symmetrizable.')
265
if frozen is not None:
266
print 'The input data is a matrix, therefore the additional parameter frozen is ignored.'
267
268
self._M = copy(data).sparse_matrix()
269
self._n = n = self._M.ncols()
270
self._m = m = self._M.nrows() - self._n
271
self._digraph = _matrix_to_digraph( self._M )
272
self._mutation_type = None
273
if n+m == 0:
274
self._description = 'Quiver without vertices'
275
elif n+m == 1:
276
self._description = 'Quiver on %d vertex' %(n+m)
277
else:
278
self._description = 'Quiver on %d vertices' %(n+m)
279
280
# constructs a quiver from a digraph
281
elif type(data) is DiGraph:
282
if frozen is None:
283
frozen = 0
284
elif not ZZ(frozen) == frozen:
285
raise ValueError("The optional argument frozen (=%s) must be an integer."%frozen)
286
m = self._m = frozen
287
n = self._n = data.order() - m
288
dg = copy( data )
289
edges = data.edges(labels=False)
290
if any( (a,a) in edges for a in data.vertices() ):
291
raise ValueError("The input DiGraph contains a loop")
292
if any( (b,a) in edges for (a,b) in edges ):
293
raise ValueError("The input DiGraph contains two-cycles")
294
if not set(dg.vertices()) == set(range(n+m)):
295
dg.relabel()
296
if dg.has_multiple_edges():
297
multi_edges = {}
298
for v1,v2,label in dg.multiple_edges():
299
if label not in ZZ:
300
raise ValueError("The input DiGraph contains multiple edges labeled by non-integers")
301
elif (v1,v2) in multi_edges:
302
multi_edges[(v1,v2)] += label
303
else:
304
multi_edges[(v1,v2)] = label
305
dg.delete_edge(v1,v2)
306
dg.add_edges( [ (v1,v2,multi_edges[(v1,v2)]) for v1,v2 in multi_edges ] )
307
for edge in dg.edge_iterator():
308
if edge[0] >= n and edge[1] >= n:
309
raise ValueError("The input digraph contains edges within the frozen vertices")
310
if edge[2] is None:
311
dg.set_edge_label( edge[0], edge[1], (1,-1) )
312
edge = (edge[0],edge[1],(1,-1))
313
elif edge[2] in ZZ:
314
dg.set_edge_label( edge[0], edge[1], (edge[2],-edge[2]) )
315
edge = (edge[0],edge[1],(edge[2],-edge[2]))
316
elif type(edge[2]) == list and len(edge[2]) != 2:
317
raise ValueError("The input digraph contains an edge with the wrong type of list as a label.")
318
elif type(edge[2]) == list and len(edge[2]) == 2:
319
dg.set_edge_label( edge[0], edge[1], (edge[2][0], edge[2][1]))
320
edge = (edge[0],edge[1],(edge[2][0],edge[2][1]))
321
elif ( edge[0] >= n or edge[1] >= n ) and not edge[2][0] == - edge[2][1]:
322
raise ValueError("The input digraph contains an edge to or from a frozen vertex which is not skew-symmetric.")
323
if edge[2][0] < 0:
324
raise ValueError("The input digraph contains an edge of the form (a,-b) with negative a.")
325
326
M = _edge_list_to_matrix( dg.edge_iterator(), n, m )
327
if not _principal_part(M).is_skew_symmetrizable( positive=True ):
328
raise ValueError("The input digraph must be skew-symmetrizable")
329
self._digraph = dg
330
self._M = M
331
if n+m == 0:
332
self._description = 'Quiver without vertices'
333
elif n+m == 1:
334
self._description = 'Quiver on %d vertex' %(n+m)
335
else:
336
self._description = 'Quiver on %d vertices' %(n+m)
337
self._mutation_type = None
338
339
# if data is a list of edges, the appropriate digraph is constructed.
340
341
elif isinstance(data,list) and all(isinstance(x,(list,tuple)) for x in data ):
342
dg = DiGraph( data )
343
self.__init__(data=dg, frozen=frozen )
344
345
# otherwise, an error is raised
346
else:
347
raise ValueError("The input data was not recognized.")
348
349
def __eq__(self, other):
350
"""
351
Returns ``True`` if ``self`` and ``other`` represent the same quiver.
352
353
EXAMPLES::
354
355
sage: Q = ClusterQuiver(['A',5])
356
sage: T = Q.mutate( 2, inplace=False )
357
sage: Q.__eq__( T )
358
False
359
sage: T.mutate( 2 )
360
sage: Q.__eq__( T )
361
True
362
"""
363
return type( other ) is ClusterQuiver and self._M == other._M
364
365
def _repr_(self):
366
"""
367
Returns the description of ``self``.
368
369
EXAMPLES::
370
371
sage: Q = ClusterQuiver(['A',5])
372
sage: Q._repr_()
373
"Quiver on 5 vertices of type ['A', 5]"
374
"""
375
name = self._description
376
if self._mutation_type:
377
if type( self._mutation_type ) is str:
378
name += ' of ' + self._mutation_type
379
else:
380
name += ' of type ' + str(self._mutation_type)
381
if self._m == 1:
382
name += ' with %s frozen vertex'%self._m
383
elif self._m > 1:
384
name += ' with %s frozen vertices'%self._m
385
return name
386
387
def plot(self, circular=True, center=(0,0), directed=True, mark=None, save_pos=False):
388
"""
389
Returns the plot of the underlying digraph of ``self``.
390
391
INPUT:
392
393
- ``circular`` -- (default:True) if True, the circular plot is chosen, otherwise >>spring<< is used.
394
- ``center`` -- (default:(0,0)) sets the center of the circular plot, otherwise it is ignored.
395
- ``directed`` -- (default: True) if True, the directed version is shown, otherwise the undirected.
396
- ``mark`` -- (default: None) if set to i, the vertex i is highlighted.
397
- ``save_pos`` -- (default:False) if True, the positions of the vertices are saved.
398
399
EXAMPLES::
400
401
sage: Q = ClusterQuiver(['A',5])
402
sage: pl = Q.plot()
403
sage: pl = Q.plot(circular=True)
404
"""
405
from sage.plot.colors import rainbow
406
from sage.graphs.graph_generators import GraphGenerators
407
from sage.all import e,pi,I
408
graphs = GraphGenerators()
409
# returns positions for graph vertices on two concentric cycles with radius 1 and 2
410
def _graphs_concentric_circles(n,m):
411
g1 = graphs.CycleGraph(n).get_pos()
412
g2 = graphs.CycleGraph(m).get_pos()
413
for i in g2:
414
z = CC(g2[i])*e**(-pi*I/(2*m))
415
g2[i] = (z.real_part(),z.imag_part())
416
for i in range(m):
417
g1[n+i] = [2*g2[i][0], 2*g2[i][1]]
418
return g1
419
420
n, m = self._n, self._m
421
colors = rainbow(11)
422
color_dict = { colors[0]:[], colors[1]:[], colors[6]:[], colors[5]:[] }
423
if directed:
424
dg = DiGraph( self._digraph )
425
else:
426
dg = Graph( self._digraph )
427
for edge in dg.edges():
428
v1,v2,(a,b) = edge
429
if v1 < n and v2 < n:
430
if (a,b) == (1,-1):
431
color_dict[ colors[0] ].append((v1,v2))
432
else:
433
color_dict[ colors[6] ].append((v1,v2))
434
else:
435
if (a,b) == (1,-1):
436
color_dict[ colors[1] ].append((v1,v2))
437
else:
438
color_dict[ colors[5] ].append((v1,v2))
439
if a == -b:
440
if a == 1:
441
dg.set_edge_label( v1,v2,'' )
442
else:
443
dg.set_edge_label( v1,v2,a )
444
if mark is not None:
445
if mark < n:
446
partition = (range(mark)+range(mark+1,n),range(n,n+m),[mark])
447
elif mark > n:
448
partition = (range(n),range(n,mark)+range(mark+1,n+m),[mark])
449
else:
450
raise ValueError("The given mark is not a vertex of self.")
451
else:
452
partition = (range(n),range(n,n+m),[])
453
vertex_color_dict = {}
454
vertex_color_dict[ colors[0] ] = partition[0]
455
vertex_color_dict[ colors[6] ] = partition[1]
456
vertex_color_dict[ colors[4] ] = partition[2]
457
458
options = {
459
'graph_border' : True,
460
'edge_colors': color_dict,
461
'vertex_colors': vertex_color_dict,
462
'edge_labels' : True,
463
}
464
if circular:
465
pp = _graphs_concentric_circles( n, m )
466
for v in pp:
467
pp[v] = (pp[v][0]+center[0],pp[v][1]+center[1])
468
options[ 'pos' ] = pp
469
return dg.plot( **options )
470
471
def show(self, fig_size=1, circular=False, directed=True, mark=None, save_pos=False):
472
"""
473
Shows the plot of the underlying digraph of ``self``.
474
475
INPUT:
476
477
- ``fig_size`` -- (default: 1) factor by which the size of the plot is multiplied.
478
- ``circular`` -- (default: False) if True, the circular plot is chosen, otherwise >>spring<< is used.
479
- ``directed`` -- (default: True) if True, the directed version is shown, otherwise the undirected.
480
- ``mark`` -- (default: None) if set to i, the vertex i is highlighted.
481
- ``save_pos`` -- (default:False) if True, the positions of the vertices are saved.
482
483
TESTS::
484
485
sage: Q = ClusterQuiver(['A',5])
486
sage: Q.show() # long time
487
"""
488
n, m = self._n, self._m
489
plot = self.plot( circular=circular, directed=directed, mark=mark, save_pos=save_pos )
490
if circular:
491
plot.show( figsize=[fig_size*3*(n+m)/4+1,fig_size*3*(n+m)/4+1] )
492
else:
493
plot.show( figsize=[fig_size*n+1,fig_size*n+1] )
494
495
def interact(self, fig_size=1, circular=True):
496
"""
497
Only in notebook mode. Starts an interactive window for cluster seed mutations.
498
499
INPUT:
500
501
- ``fig_size`` -- (default: 1) factor by which the size of the plot is multiplied.
502
- ``circular`` -- (default: False) if True, the circular plot is chosen, otherwise >>spring<< is used.
503
504
TESTS::
505
506
sage: Q = ClusterQuiver(['A',4])
507
sage: Q.interact() # long time
508
'The interactive mode only runs in the Sage notebook.'
509
"""
510
from sage.plot.plot import EMBEDDED_MODE
511
from sagenb.notebook.interact import interact, selector
512
from sage.misc.all import html,latex
513
514
if not EMBEDDED_MODE:
515
return "The interactive mode only runs in the Sage notebook."
516
else:
517
seq = []
518
sft = [True]
519
sss = [True]
520
ssm = [True]
521
ssl = [True]
522
@interact
523
def player(k=selector(values=range(self._n),nrows = 1,label='Mutate at: '), show_seq=("Mutation sequence:", True), show_matrix=("B-Matrix:", True), show_lastmutation=("Show last mutation:", True) ):
524
ft,ss,sm,sl = sft.pop(), sss.pop(), ssm.pop(), ssl.pop()
525
if ft:
526
self.show(fig_size=fig_size, circular=circular)
527
elif show_seq is not ss or show_matrix is not sm or show_lastmutation is not sl:
528
if seq and show_lastmutation:
529
self.show(fig_size=fig_size, circular=circular, mark=seq[len(seq)-1])
530
else:
531
self.show(fig_size=fig_size, circular=circular )
532
else:
533
self.mutate(k)
534
seq.append(k)
535
if not show_lastmutation:
536
self.show(fig_size=fig_size, circular=circular)
537
else:
538
self.show(fig_size=fig_size, circular=circular,mark=k)
539
sft.append(False)
540
sss.append(show_seq)
541
ssm.append(show_matrix)
542
ssl.append(show_lastmutation)
543
if show_seq: html( "Mutation sequence: $" + str( [ seq[i] for i in xrange(len(seq)) ] ).strip('[]') + "$" )
544
if show_matrix:
545
html( "B-Matrix:" )
546
m = self._M
547
#m = matrix(range(1,self._n+1),sparse=True).stack(m)
548
m = latex(m)
549
m = m.split('(')[1].split('\\right')[0]
550
html( "$ $" )
551
html( "$\\begin{align*} " + m + "\\end{align*}$" )
552
#html( "$" + m + "$" )
553
html( "$ $" )
554
555
def save_image(self,filename,circular=False):
556
"""
557
Saves the plot of the underlying digraph of ``self``.
558
559
INPUT:
560
561
- ``filename`` -- the filename the image is saved to.
562
- ``circular`` -- (default: False) if True, the circular plot is chosen, otherwise >>spring<< is used.
563
564
EXAMPLES::
565
566
sage: Q = ClusterQuiver(['F',4,[1,2]])
567
sage: Q.save_image(os.path.join(SAGE_TMP, 'sage.png'))
568
"""
569
graph_plot = self.plot( circular=circular )
570
graph_plot.save( filename=filename )
571
572
def qmu_save(self,filename=None):
573
"""
574
Saves a .qmu file of ``self`` that can then be opened in Bernhard Keller's Quiver Applet.
575
576
INPUT:
577
578
- ``filename`` -- the filename the image is saved to.
579
580
If a filename is not specified, the default name is from_sage.qmu in the current sage directory.
581
582
EXAMPLES::
583
584
sage: Q = ClusterQuiver(['F',4,[1,2]])
585
sage: Q.qmu_save(os.path.join(SAGE_TMP, 'sage.qmu'))
586
587
Make sure we can save quivers with `m != n` frozen variables, see :trac:`14851`::
588
589
sage: S=ClusterSeed(['A',3])
590
sage: T1=S.principal_extension()
591
sage: T2=T1.principal_extension(ignore_coefficients=True)
592
sage: Q=T2.quiver()
593
sage: Q.qmu_save(os.path.join(SAGE_TMP, 'sage.qmu'))
594
"""
595
M = self.b_matrix()
596
if self.m() > 0:
597
from sage.matrix.constructor import Matrix
598
from sage.matrix.constructor import block_matrix
599
M1 = M.matrix_from_rows(range(self.n()))
600
M2 = M.matrix_from_rows(range(self.n(),self.n()+self.m()))
601
M3 = Matrix(self.m(),self.m())
602
M = block_matrix([[M1,-M2.transpose()],[M2,M3]])
603
dg = self.digraph()
604
dg.plot(save_pos=True)
605
PP = dg.get_pos()
606
m = M.ncols()
607
if filename is None:
608
filename = 'from_sage.qmu'
609
try:
610
self._default_filename = filename
611
except AttributeError:
612
pass
613
if filename[-4:] != '.qmu':
614
filename = filename + '.qmu'
615
myfile = open(filename, 'w')
616
myfile.write('//Number of points'); myfile.write('\n')
617
myfile.write(str(m)); myfile.write('\n')
618
myfile.write('//Vertex radius'); myfile.write('\n')
619
myfile.write(str(9)); myfile.write('\n')
620
myfile.write('//Labels shown'); myfile.write('\n')
621
myfile.write(str(1)); myfile.write('\n')
622
myfile.write('//Matrix'); myfile.write('\n')
623
myfile.write(str(m)); myfile.write(' '); myfile.write(str(m)); myfile.write('\n')
624
for i in range(m):
625
for j in range(m):
626
myfile.write(str(M[i,j])); myfile.write(' ')
627
myfile.write('\n')
628
myfile.write('//Points'); myfile.write('\n')
629
for i in range(m):
630
myfile.write(str(9)); myfile.write(' '); myfile.write(str(100*PP[i][0])); myfile.write(' ');
631
myfile.write(str(100*PP[i][1]));
632
if i > self.n()-1:
633
myfile.write(' '); myfile.write(str(1))
634
myfile.write('\n')
635
myfile.write('//Historycounter'); myfile.write('\n')
636
myfile.write(str(-1)); myfile.write('\n')
637
myfile.write('//History'); myfile.write('\n'); myfile.write('\n')
638
myfile.write('//Cluster is null');
639
myfile.close()
640
641
def b_matrix(self):
642
"""
643
Returns the b-matrix of ``self``.
644
645
EXAMPLES::
646
647
sage: ClusterQuiver(['A',4]).b_matrix()
648
[ 0 1 0 0]
649
[-1 0 -1 0]
650
[ 0 1 0 1]
651
[ 0 0 -1 0]
652
653
sage: ClusterQuiver(['B',4]).b_matrix()
654
[ 0 1 0 0]
655
[-1 0 -1 0]
656
[ 0 1 0 1]
657
[ 0 0 -2 0]
658
659
sage: ClusterQuiver(['D',4]).b_matrix()
660
[ 0 1 0 0]
661
[-1 0 -1 -1]
662
[ 0 1 0 0]
663
[ 0 1 0 0]
664
665
sage: ClusterQuiver(QuiverMutationType([['A',2],['B',2]])).b_matrix()
666
[ 0 1 0 0]
667
[-1 0 0 0]
668
[ 0 0 0 1]
669
[ 0 0 -2 0]
670
"""
671
return copy( self._M )
672
673
def digraph(self):
674
"""
675
Returns the underlying digraph of ``self``.
676
677
EXAMPLES::
678
679
sage: ClusterQuiver(['A',1]).digraph()
680
Digraph on 1 vertex
681
sage: ClusterQuiver(['A',1]).digraph().vertices()
682
[0]
683
sage: ClusterQuiver(['A',1]).digraph().edges()
684
[]
685
686
sage: ClusterQuiver(['A',4]).digraph()
687
Digraph on 4 vertices
688
sage: ClusterQuiver(['A',4]).digraph().edges()
689
[(0, 1, (1, -1)), (2, 1, (1, -1)), (2, 3, (1, -1))]
690
691
sage: ClusterQuiver(['B',4]).digraph()
692
Digraph on 4 vertices
693
sage: ClusterQuiver(['A',4]).digraph().edges()
694
[(0, 1, (1, -1)), (2, 1, (1, -1)), (2, 3, (1, -1))]
695
696
sage: ClusterQuiver(QuiverMutationType([['A',2],['B',2]])).digraph()
697
Digraph on 4 vertices
698
699
sage: ClusterQuiver(QuiverMutationType([['A',2],['B',2]])).digraph().edges()
700
[(0, 1, (1, -1)), (2, 3, (1, -2))]
701
"""
702
return copy( self._digraph )
703
704
def mutation_type(self):
705
"""
706
Returns the mutation type of ``self``.
707
708
Returns the mutation_type of each connected component of self if it can be determined,
709
otherwise, the mutation type of this component is set to be unknown.
710
711
The mutation types of the components are ordered by vertex labels.
712
713
If you do many type recognitions, you should consider to save
714
exceptional mutation types using
715
`meth`:sage.combinat.cluster_algebra_quiver.quiver_mutation_type.save_exceptional_data
716
717
WARNING:
718
719
- All finite types can be detected,
720
- All affine types can be detected, EXCEPT affine type D (the algorithm is not yet implemented)
721
- All exceptional types can be detected.
722
723
EXAMPLES::
724
725
sage: ClusterQuiver(['A',4]).mutation_type()
726
['A', 4]
727
sage: ClusterQuiver(['A',(3,1),1]).mutation_type()
728
['A', [1, 3], 1]
729
sage: ClusterQuiver(['C',2]).mutation_type()
730
['B', 2]
731
sage: ClusterQuiver(['B',4,1]).mutation_type()
732
['BD', 4, 1]
733
734
finite types::
735
736
sage: Q = ClusterQuiver(['A',5])
737
sage: Q._mutation_type = None
738
sage: Q.mutation_type()
739
['A', 5]
740
741
sage: Q = ClusterQuiver([(0,1),(1,2),(2,3),(3,4)])
742
sage: Q.mutation_type()
743
['A', 5]
744
745
affine types::
746
747
sage: Q = ClusterQuiver(['E',8,[1,1]]); Q
748
Quiver on 10 vertices of type ['E', 8, [1, 1]]
749
sage: Q._mutation_type = None; Q
750
Quiver on 10 vertices
751
sage: Q.mutation_type() # long time
752
['E', 8, [1, 1]]
753
754
the not yet working affine type D (unless user has saved small classical quiver data)::
755
756
sage: Q = ClusterQuiver(['D',4,1])
757
sage: Q._mutation_type = None
758
sage: Q.mutation_type() # todo: not implemented
759
['D', 4, 1]
760
761
the exceptional types::
762
763
sage: Q = ClusterQuiver(['X',6])
764
sage: Q._mutation_type = None
765
sage: Q.mutation_type() # long time
766
['X', 6]
767
768
examples from page 8 of Keller's article "Cluster algebras, quiver representations
769
and triangulated categories" (arXiv:0807.1960)::
770
771
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)])
772
sage: ClusterQuiver( dg ).mutation_type() # long time
773
['E', 8, [1, 1]]
774
775
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] } )
776
sage: ClusterQuiver( dg ).mutation_type() # long time
777
['E', 8, 1]
778
779
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] } )
780
sage: ClusterQuiver( dg ).mutation_type() # long time
781
['E', 8, [1, 1]]
782
783
infinite types::
784
785
sage: Q = ClusterQuiver(['GR',[4,9]])
786
sage: Q._mutation_type = None
787
sage: Q.mutation_type()
788
'undetermined infinite mutation type'
789
790
reducible types::
791
792
sage: Q = ClusterQuiver([['A', 3], ['B', 3]])
793
sage: Q._mutation_type = None
794
sage: Q.mutation_type()
795
[ ['A', 3], ['B', 3] ]
796
797
sage: Q = ClusterQuiver([['A', 3], ['T', [4,4,4]]])
798
sage: Q._mutation_type = None
799
sage: Q.mutation_type()
800
[['A', 3], 'undetermined infinite mutation type']
801
802
sage: Q = ClusterQuiver([['A', 3], ['B', 3], ['T', [4,4,4]]])
803
sage: Q._mutation_type = None
804
sage: Q.mutation_type()
805
[['A', 3], ['B', 3], 'undetermined infinite mutation type']
806
807
sage: Q = ClusterQuiver([[0,1,2],[1,2,2],[2,0,2],[3,4,1],[4,5,1]])
808
sage: Q.mutation_type()
809
['undetermined finite mutation type', ['A', 3]]
810
811
TESTS::
812
813
sage: Q = ClusterQuiver(matrix([[0, 3], [-1, 0], [1, 0], [0, 1]]))
814
sage: Q.mutation_type()
815
['G', 2]
816
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]]))
817
sage: Q.mutation_type()
818
'undetermined infinite mutation type'
819
"""
820
# checking if the mutation type is known already
821
if self._mutation_type is None:
822
# checking mutation type only for the principal part
823
if self._m > 0:
824
dg = self._digraph.subgraph( range(self._n) )
825
else:
826
dg = self._digraph
827
828
# checking the type for each connected component
829
mutation_type = []
830
connected_components = dg.connected_components()
831
connected_components.sort()
832
for component in connected_components:
833
# constructing the digraph for this component
834
dg_component = dg.subgraph( component )
835
dg_component.relabel()
836
# turning dg_component into a canonical form
837
iso, orbits = _dg_canonical_form( dg_component, dg_component.num_verts(), 0 )
838
# turning dg_component into a canonical form
839
dig6 = _digraph_to_dig6( dg_component, hashable=True )
840
# and getting the corresponding matrix
841
M = _dig6_to_matrix(dig6)
842
843
# checking if this quiver is mutation infinite
844
is_finite, path = is_mutation_finite(M)
845
if is_finite is False:
846
mut_type_part = 'undetermined infinite mutation type'
847
else:
848
# checking if this quiver is in the database
849
mut_type_part = _mutation_type_from_data( dg_component.order(), dig6, compute_if_necessary=False )
850
# checking if the algorithm can determine the mutation type
851
if mut_type_part == 'unknown':
852
mut_type_part = _connected_mutation_type(dg_component)
853
# checking if this quiver is of exceptional type by computing the exceptional mutation classes
854
if mut_type_part == 'unknown':
855
mut_type_part = _mutation_type_from_data(dg_component.order(), dig6, compute_if_necessary=True)
856
if mut_type_part == 'unknown':
857
mut_type_part = 'undetermined finite mutation type'
858
mutation_type.append( mut_type_part )
859
860
# the empty quiver case
861
if len( mutation_type ) == 0:
862
Warning('Quiver has no vertices')
863
mutation_type = None
864
# the connected quiver case
865
elif len( mutation_type ) == 1:
866
mutation_type = mutation_type[0]
867
# the reducible quiver case
868
elif len( mutation_type ) > 1:
869
if any( type(mut_type_part) is str for mut_type_part in mutation_type ):
870
pass
871
else:
872
mutation_type = QuiverMutationType( mutation_type )
873
self._mutation_type = mutation_type
874
return self._mutation_type
875
876
def n(self):
877
"""
878
Returns the number of free vertices of ``self``.
879
880
EXAMPLES::
881
882
sage: ClusterQuiver(['A',4]).n()
883
4
884
sage: ClusterQuiver(['A',(3,1),1]).n()
885
4
886
sage: ClusterQuiver(['B',4]).n()
887
4
888
sage: ClusterQuiver(['B',4,1]).n()
889
5
890
"""
891
return self._n
892
893
def m(self):
894
"""
895
Returns the number of frozen vertices of ``self``.
896
897
EXAMPLES::
898
899
sage: Q = ClusterQuiver(['A',4])
900
sage: Q.m()
901
0
902
903
sage: T = ClusterQuiver( Q.digraph().edges(), frozen=1 )
904
sage: T.n()
905
3
906
sage: T.m()
907
1
908
"""
909
return self._m
910
911
def canonical_label( self, certify=False ):
912
"""
913
Returns the canonical labelling of ``self``, see sage.graphs.graph.GenericGraph.canonical_label.
914
915
INPUT:
916
917
- ``certify`` -- (default: False) if True, the dictionary from ``self.vertices()`` to the vertices of the returned quiver is returned as well.
918
919
EXAMPLES::
920
921
sage: Q = ClusterQuiver(['A',4]); Q.digraph().edges()
922
[(0, 1, (1, -1)), (2, 1, (1, -1)), (2, 3, (1, -1))]
923
924
sage: T = Q.canonical_label(); T.digraph().edges()
925
[(0, 3, (1, -1)), (1, 2, (1, -1)), (1, 3, (1, -1))]
926
927
sage: T,iso = Q.canonical_label(certify=True); T.digraph().edges(); iso
928
[(0, 3, (1, -1)), (1, 2, (1, -1)), (1, 3, (1, -1))]
929
{0: 0, 1: 3, 2: 1, 3: 2}
930
931
sage: Q = ClusterQuiver(QuiverMutationType([['B',2],['A',1]])); Q
932
Quiver on 3 vertices of type [ ['B', 2], ['A', 1] ]
933
934
sage: Q.canonical_label()
935
Quiver on 3 vertices of type [ ['A', 1], ['B', 2] ]
936
937
sage: Q.canonical_label(certify=True)
938
(Quiver on 3 vertices of type [ ['A', 1], ['B', 2] ], {0: 1, 1: 2, 2: 0})
939
"""
940
n = self._n
941
m = self._m
942
943
# computing the canonical form respecting the frozen variables
944
dg = copy( self._digraph )
945
iso, orbits = _dg_canonical_form( dg, n, m )
946
Q = ClusterQuiver( dg )
947
# getting the new ordering for the mutation type if necessary
948
if self._mutation_type:
949
if dg.is_connected():
950
Q._mutation_type = self._mutation_type
951
else:
952
CC = sorted( self._digraph.connected_components() )
953
CC_new = sorted( zip( [ sorted( [ iso[i] for i in L ] ) for L in CC ], range( len( CC ) ) ) )
954
comp_iso = [ L[1] for L in CC_new ]
955
Q._mutation_type = []
956
for i in range( len( CC_new ) ):
957
Q._mutation_type.append( copy( self._mutation_type.irreducible_components()[ comp_iso[i] ] ) )
958
Q._mutation_type = QuiverMutationType( Q._mutation_type )
959
if certify:
960
return Q, iso
961
else:
962
return Q
963
964
def is_acyclic(self):
965
"""
966
Returns true if ``self`` is acyclic.
967
968
EXAMPLES::
969
970
sage: ClusterQuiver(['A',4]).is_acyclic()
971
True
972
973
sage: ClusterQuiver(['A',[2,1],1]).is_acyclic()
974
True
975
976
sage: ClusterQuiver([[0,1],[1,2],[2,0]]).is_acyclic()
977
False
978
"""
979
return self._digraph.is_directed_acyclic()
980
981
def is_bipartite(self,return_bipartition=False):
982
"""
983
Returns true if ``self`` is bipartite.
984
985
EXAMPLES::
986
987
sage: ClusterQuiver(['A',[3,3],1]).is_bipartite()
988
True
989
990
sage: ClusterQuiver(['A',[4,3],1]).is_bipartite()
991
False
992
"""
993
dg = copy( self._digraph )
994
dg.delete_vertices( range(self._n,self._n+self._m) )
995
innie = dg.in_degree()
996
outie = dg.out_degree()
997
is_bip = sum( [ innie[i]*outie[i] for i in range(len(innie)) ] ) == 0
998
if not is_bip:
999
return False
1000
else:
1001
if not return_bipartition:
1002
return True
1003
else:
1004
g = dg.to_undirected()
1005
return g.bipartite_sets()
1006
1007
def exchangeable_part(self):
1008
"""
1009
Returns the restriction to the principal part (i.e. exchangeable part) of ``self``, the subquiver obtained by deleting the frozen vertices of ``self``.
1010
1011
EXAMPLES::
1012
1013
sage: Q = ClusterQuiver(['A',4])
1014
sage: T = ClusterQuiver( Q.digraph().edges(), frozen=1 )
1015
sage: T.digraph().edges()
1016
[(0, 1, (1, -1)), (2, 1, (1, -1)), (2, 3, (1, -1))]
1017
1018
sage: T.exchangeable_part().digraph().edges()
1019
[(0, 1, (1, -1)), (2, 1, (1, -1))]
1020
1021
sage: Q2 = Q.principal_extension()
1022
sage: Q3 = Q2.principal_extension()
1023
sage: Q2.exchangeable_part() == Q3.exchangeable_part()
1024
True
1025
"""
1026
dg = DiGraph( self._digraph )
1027
dg.delete_vertices( range(self._n,self._n+self._m) )
1028
Q = ClusterQuiver( dg )
1029
Q._mutation_type = self._mutation_type
1030
return Q
1031
1032
def principal_extension(self, inplace=False):
1033
"""
1034
Returns the principal extension of ``self``, adding n frozen vertices to any previously frozen vertices. I.e., the quiver obtained by adding an outgoing edge to every mutable vertex of ``self``.
1035
1036
EXAMPLES::
1037
1038
sage: Q = ClusterQuiver(['A',2]); Q
1039
Quiver on 2 vertices of type ['A', 2]
1040
sage: T = Q.principal_extension(); T
1041
Quiver on 4 vertices of type ['A', 2] with 2 frozen vertices
1042
sage: T2 = T.principal_extension(); T2
1043
Quiver on 6 vertices of type ['A', 2] with 4 frozen vertices
1044
sage: Q.digraph().edges()
1045
[(0, 1, (1, -1))]
1046
sage: T.digraph().edges()
1047
[(0, 1, (1, -1)), (2, 0, (1, -1)), (3, 1, (1, -1))]
1048
sage: T2.digraph().edges()
1049
[(0, 1, (1, -1)), (2, 0, (1, -1)), (3, 1, (1, -1)), (4, 0, (1, -1)), (5, 1, (1, -1))]
1050
"""
1051
dg = DiGraph( self._digraph )
1052
dg.add_edges( [(self._n+self._m+i,i) for i in range(self._n)] )
1053
Q = ClusterQuiver( dg, frozen=self._m+self._n )
1054
Q._mutation_type = self._mutation_type
1055
if inplace:
1056
self.__init__(Q)
1057
else:
1058
return Q
1059
1060
def mutate(self, data, inplace=True):
1061
"""
1062
Mutates ``self`` at a sequence of vertices.
1063
1064
INPUT:
1065
1066
- ``sequence`` -- a vertex of ``self`` or an iterator of vertices of ``self``.
1067
- ``inplace`` -- (default: True) if False, the result is returned, otherwise ``self`` is modified.
1068
1069
EXAMPLES::
1070
1071
sage: Q = ClusterQuiver(['A',4]); Q.b_matrix()
1072
[ 0 1 0 0]
1073
[-1 0 -1 0]
1074
[ 0 1 0 1]
1075
[ 0 0 -1 0]
1076
1077
sage: Q.mutate(0); Q.b_matrix()
1078
[ 0 -1 0 0]
1079
[ 1 0 -1 0]
1080
[ 0 1 0 1]
1081
[ 0 0 -1 0]
1082
1083
sage: T = Q.mutate(0, inplace=False); T
1084
Quiver on 4 vertices of type ['A', 4]
1085
1086
sage: Q.mutate(0)
1087
sage: Q == T
1088
True
1089
1090
sage: Q.mutate([0,1,0])
1091
sage: Q.b_matrix()
1092
[ 0 -1 1 0]
1093
[ 1 0 0 0]
1094
[-1 0 0 1]
1095
[ 0 0 -1 0]
1096
1097
sage: Q = ClusterQuiver(QuiverMutationType([['A',1],['A',3]]))
1098
sage: Q.b_matrix()
1099
[ 0 0 0 0]
1100
[ 0 0 1 0]
1101
[ 0 -1 0 -1]
1102
[ 0 0 1 0]
1103
1104
sage: T = Q.mutate(0,inplace=False)
1105
sage: Q == T
1106
True
1107
1108
TESTS::
1109
1110
sage: Q = ClusterQuiver(['A',4]); Q.mutate(0,1)
1111
Traceback (most recent call last):
1112
...
1113
ValueError: The second parameter must be boolean. To mutate at a sequence of length 2, input it as a list.
1114
1115
sage: Q = ClusterQuiver(['A',4]); Q.mutate(0,0)
1116
Traceback (most recent call last):
1117
...
1118
ValueError: The second parameter must be boolean. To mutate at a sequence of length 2, input it as a list.
1119
"""
1120
n = self._n
1121
m = self._m
1122
dg = self._digraph
1123
V = range(n)
1124
1125
if data in V:
1126
seq = [data]
1127
else:
1128
seq = data
1129
if type( seq ) is tuple:
1130
seq = list( seq )
1131
if not type( seq ) is list:
1132
raise ValueError('The quiver can only be mutated at a vertex or at a sequence of vertices')
1133
if not type(inplace) is bool:
1134
raise ValueError('The second parameter must be boolean. To mutate at a sequence of length 2, input it as a list.')
1135
if any( v not in V for v in seq ):
1136
v = filter( lambda v: v not in V, seq )[0]
1137
raise ValueError('The quiver cannot be mutated at the vertex %s'%v)
1138
1139
for v in seq:
1140
dg = _digraph_mutate( dg, v, n, m )
1141
M = _edge_list_to_matrix( dg.edge_iterator(), n, m )
1142
if inplace:
1143
self._M = M
1144
self._digraph = dg
1145
else:
1146
Q = ClusterQuiver( M )
1147
Q._mutation_type = self._mutation_type
1148
return Q
1149
1150
def mutation_sequence(self, sequence, show_sequence=False, fig_size=1.2 ):
1151
"""
1152
Returns a list containing the sequence of quivers obtained from ``self`` by a sequence of mutations on vertices.
1153
1154
INPUT:
1155
1156
- ``sequence`` -- a list or tuple of vertices of ``self``.
1157
- ``show_sequence`` -- (default: False) if True, a png containing the mutation sequence is shown.
1158
- ``fig_size`` -- (default: 1.2) factor by which the size of the sequence is expanded.
1159
1160
EXAMPLES::
1161
1162
sage: Q = ClusterQuiver(['A',4])
1163
sage: seq = Q.mutation_sequence([0,1]); seq
1164
[Quiver on 4 vertices of type ['A', 4], Quiver on 4 vertices of type ['A', 4], Quiver on 4 vertices of type ['A', 4]]
1165
sage: [T.b_matrix() for T in seq]
1166
[
1167
[ 0 1 0 0] [ 0 -1 0 0] [ 0 1 -1 0]
1168
[-1 0 -1 0] [ 1 0 -1 0] [-1 0 1 0]
1169
[ 0 1 0 1] [ 0 1 0 1] [ 1 -1 0 1]
1170
[ 0 0 -1 0], [ 0 0 -1 0], [ 0 0 -1 0]
1171
]
1172
"""
1173
from sage.plot.plot import Graphics
1174
from sage.plot.text import text
1175
n = self._n
1176
m = self._m
1177
if m == 0:
1178
width_factor = 3
1179
fig_size = fig_size*2*n/3
1180
else:
1181
width_factor = 6
1182
fig_size = fig_size*4*n/3
1183
V = range(n)
1184
1185
if type( sequence ) is tuple:
1186
sequence = list( sequence )
1187
if not type( sequence ) is list:
1188
raise ValueError('The quiver can only be mutated at a vertex or at a sequence of vertices')
1189
if any( v not in V for v in sequence ):
1190
v = filter( lambda v: v not in V, sequence )[0]
1191
raise ValueError('The quiver can only be mutated at the vertex %s'%v )
1192
1193
quiver = copy( self )
1194
quiver_sequence = []
1195
quiver_sequence.append( copy( quiver ) )
1196
1197
for v in sequence:
1198
quiver.mutate( v )
1199
quiver_sequence.append( copy( quiver ) )
1200
1201
if show_sequence:
1202
def _plot_arrow( v, k, center=(0,0) ):
1203
return text("$\longleftrightarrow$",(center[0],center[1]), fontsize=25) + text("$\mu_"+str(v)+"$",(center[0],center[1]+0.15), fontsize=15) \
1204
+ text("$"+str(k)+"$",(center[0],center[1]-0.2), fontsize=15)
1205
plot_sequence = [ quiver_sequence[i].plot( circular=True, center=(i*width_factor,0) ) for i in range(len(quiver_sequence)) ]
1206
arrow_sequence = [ _plot_arrow( sequence[i],i+1,center=((i+0.5)*width_factor,0) ) for i in range(len(sequence)) ]
1207
sequence = []
1208
for i in xrange( len( plot_sequence ) ):
1209
if i < len( arrow_sequence ):
1210
sequence.append( plot_sequence[i] + arrow_sequence[i] )
1211
else:
1212
sequence.append( plot_sequence[i] )
1213
plot_obj = Graphics()
1214
for elem in sequence:
1215
plot_obj += elem
1216
plot_obj.show(axes=False, figsize=[fig_size*len(quiver_sequence),fig_size])
1217
return quiver_sequence
1218
1219
def reorient( self, data ):
1220
"""
1221
Reorients ``self`` with respect to the given total order, or with respect to an iterator of edges in ``self`` to be reverted.
1222
1223
WARNING::
1224
1225
This operation might change the mutation type of ``self``.
1226
1227
INPUT:
1228
1229
- ``data`` -- an iterator defining a total order on ``self.vertices()``, or an iterator of edges in ``self`` to be reoriented.
1230
1231
EXAMPLES::
1232
1233
sage: Q = ClusterQuiver(['A',(2,3),1])
1234
sage: Q.mutation_type()
1235
['A', [2, 3], 1]
1236
1237
sage: Q.reorient([(0,1),(1,2),(2,3),(3,4)])
1238
sage: Q.mutation_type()
1239
['D', 5]
1240
1241
sage: Q.reorient([0,1,2,3,4])
1242
sage: Q.mutation_type()
1243
['A', [1, 4], 1]
1244
"""
1245
if all( 0 <= i and i < self._n + self._m for i in data ) and len( set( data ) ) == self._n+self._m :
1246
dg_new = DiGraph()
1247
for edge in self._digraph.edges():
1248
if data.index( edge[0] ) < data.index( edge[1] ):
1249
dg_new.add_edge( edge[0],edge[1],edge[2] )
1250
else:
1251
dg_new.add_edge( edge[1],edge[0],edge[2] )
1252
self._digraph = dg_new
1253
self._M = _edge_list_to_matrix( dg_new.edges(), self._n, self._m )
1254
self._mutation_type = None
1255
elif all( type(edge) in [list,tuple] and len(edge)==2 for edge in data ):
1256
edges = self._digraph.edges(labels=False)
1257
for edge in data:
1258
if (edge[1],edge[0]) in edges:
1259
label = self._digraph.edge_label(edge[1],edge[0])
1260
self._digraph.delete_edge(edge[1],edge[0])
1261
self._digraph.add_edge(edge[0],edge[1],label)
1262
self._M = _edge_list_to_matrix( self._digraph.edges(), self._n, self._m )
1263
self._mutation_type = None
1264
else:
1265
raise ValueError('The order is no total order on the vertices of the quiver or a list of edges to be oriented.')
1266
1267
def mutation_class_iter( self, depth=infinity, show_depth=False, return_paths=False, data_type="quiver", up_to_equivalence=True, sink_source=False ):
1268
"""
1269
Returns an iterator for the mutation class of self together with certain constrains.
1270
1271
INPUT:
1272
1273
- ``depth`` -- (default: infinity) integer, only quivers with distance at most depth from self are returned.
1274
- ``show_depth`` -- (default: False) if True, the actual depth of the mutation is shown.
1275
- ``return_paths`` -- (default: False) if True, a shortest path of mutation sequences from self to the given quiver is returned as well.
1276
- ``data_type`` -- (default: "quiver") can be one of the following::
1277
1278
* "quiver"
1279
* "matrix"
1280
* "digraph"
1281
* "dig6"
1282
* "path"
1283
1284
- ``up_to_equivalence`` -- (default: True) if True, only one quiver for each graph-isomorphism class is recorded.
1285
- ``sink_source`` -- (default: False) if True, only mutations at sinks and sources are applied.
1286
1287
EXAMPLES::
1288
1289
sage: Q = ClusterQuiver(['A',3])
1290
sage: it = Q.mutation_class_iter()
1291
sage: for T in it: print T
1292
Quiver on 3 vertices of type ['A', 3]
1293
Quiver on 3 vertices of type ['A', 3]
1294
Quiver on 3 vertices of type ['A', 3]
1295
Quiver on 3 vertices of type ['A', 3]
1296
1297
sage: it = Q.mutation_class_iter(depth=1)
1298
sage: for T in it: print T
1299
Quiver on 3 vertices of type ['A', 3]
1300
Quiver on 3 vertices of type ['A', 3]
1301
Quiver on 3 vertices of type ['A', 3]
1302
1303
sage: it = Q.mutation_class_iter(show_depth=True)
1304
sage: for T in it: pass
1305
Depth: 0 found: 1 Time: ... s
1306
Depth: 1 found: 3 Time: ... s
1307
Depth: 2 found: 4 Time: ... s
1308
1309
sage: it = Q.mutation_class_iter(return_paths=True)
1310
sage: for T in it: print T
1311
(Quiver on 3 vertices of type ['A', 3], [])
1312
(Quiver on 3 vertices of type ['A', 3], [1])
1313
(Quiver on 3 vertices of type ['A', 3], [0])
1314
(Quiver on 3 vertices of type ['A', 3], [0, 1])
1315
1316
sage: it = Q.mutation_class_iter(up_to_equivalence=False)
1317
sage: for T in it: print T
1318
Quiver on 3 vertices of type ['A', 3]
1319
Quiver on 3 vertices of type ['A', 3]
1320
Quiver on 3 vertices of type ['A', 3]
1321
Quiver on 3 vertices of type ['A', 3]
1322
Quiver on 3 vertices of type ['A', 3]
1323
Quiver on 3 vertices of type ['A', 3]
1324
Quiver on 3 vertices of type ['A', 3]
1325
Quiver on 3 vertices of type ['A', 3]
1326
Quiver on 3 vertices of type ['A', 3]
1327
Quiver on 3 vertices of type ['A', 3]
1328
Quiver on 3 vertices of type ['A', 3]
1329
Quiver on 3 vertices of type ['A', 3]
1330
Quiver on 3 vertices of type ['A', 3]
1331
Quiver on 3 vertices of type ['A', 3]
1332
1333
sage: it = Q.mutation_class_iter(return_paths=True,up_to_equivalence=False)
1334
sage: for T in it: print T
1335
(Quiver on 3 vertices of type ['A', 3], [])
1336
(Quiver on 3 vertices of type ['A', 3], [2])
1337
(Quiver on 3 vertices of type ['A', 3], [1])
1338
(Quiver on 3 vertices of type ['A', 3], [0])
1339
(Quiver on 3 vertices of type ['A', 3], [2, 1])
1340
(Quiver on 3 vertices of type ['A', 3], [0, 1])
1341
(Quiver on 3 vertices of type ['A', 3], [0, 1, 2])
1342
(Quiver on 3 vertices of type ['A', 3], [0, 1, 0])
1343
(Quiver on 3 vertices of type ['A', 3], [2, 1, 2])
1344
(Quiver on 3 vertices of type ['A', 3], [2, 1, 0])
1345
(Quiver on 3 vertices of type ['A', 3], [2, 1, 0, 2])
1346
(Quiver on 3 vertices of type ['A', 3], [2, 1, 0, 1])
1347
(Quiver on 3 vertices of type ['A', 3], [2, 1, 2, 1])
1348
(Quiver on 3 vertices of type ['A', 3], [2, 1, 2, 0])
1349
1350
sage: Q = ClusterQuiver(['A',3])
1351
sage: it = Q.mutation_class_iter(data_type='path')
1352
sage: for T in it: print T
1353
[]
1354
[1]
1355
[0]
1356
[0, 1]
1357
1358
sage: Q = ClusterQuiver(['A',3])
1359
sage: it = Q.mutation_class_iter(return_paths=True,data_type='matrix')
1360
sage: it.next()
1361
(
1362
[ 0 0 1]
1363
[ 0 0 1]
1364
[-1 -1 0], []
1365
)
1366
1367
"""
1368
if data_type == 'path':
1369
return_paths = False
1370
if data_type == "dig6":
1371
return_dig6 = True
1372
else:
1373
return_dig6 = False
1374
dg = DiGraph( self._digraph )
1375
MC_iter = _mutation_class_iter( dg, self._n, self._m, depth=depth, return_dig6=return_dig6, show_depth=show_depth, up_to_equivalence=up_to_equivalence, sink_source=sink_source )
1376
for data in MC_iter:
1377
if data_type == "quiver":
1378
next_element = ClusterQuiver( data[0], frozen=self._m )
1379
next_element._mutation_type = self._mutation_type
1380
elif data_type == "matrix":
1381
next_element = ClusterQuiver( data[0], frozen=self._m )._M
1382
elif data_type == "digraph":
1383
next_element = data[0]
1384
elif data_type == "dig6":
1385
next_element = data[0]
1386
elif data_type == "path":
1387
next_element = data[1]
1388
else:
1389
raise ValueError("The parameter for data_type was not recognized.")
1390
if return_paths:
1391
yield ( next_element, data[1] )
1392
else:
1393
yield next_element
1394
1395
def mutation_class( self, depth=infinity, show_depth=False, return_paths=False, data_type="quiver", up_to_equivalence=True, sink_source=False ):
1396
"""
1397
Returns the mutation class of self together with certain constrains.
1398
1399
INPUT:
1400
1401
- ``depth`` -- (default: infinity) integer, only seeds with distance at most depth from self are returned.
1402
- ``show_depth`` -- (default: False) if True, the actual depth of the mutation is shown.
1403
- ``return_paths`` -- (default: False) if True, a shortest path of mutation sequences from self to the given quiver is returned as well.
1404
- ``data_type`` -- (default: "quiver") can be one of the following::
1405
1406
* "quiver" -- the quiver is returned
1407
* "dig6" -- the dig6-data is returned
1408
* "path" -- shortest paths of mutation sequences from self are returned
1409
1410
- ``sink_source`` -- (default: False) if True, only mutations at sinks and sources are applied.
1411
1412
EXAMPLES::
1413
1414
sage: Q = ClusterQuiver(['A',3])
1415
sage: Ts = Q.mutation_class()
1416
sage: for T in Ts: print T
1417
Quiver on 3 vertices of type ['A', 3]
1418
Quiver on 3 vertices of type ['A', 3]
1419
Quiver on 3 vertices of type ['A', 3]
1420
Quiver on 3 vertices of type ['A', 3]
1421
1422
sage: Ts = Q.mutation_class(depth=1)
1423
sage: for T in Ts: print T
1424
Quiver on 3 vertices of type ['A', 3]
1425
Quiver on 3 vertices of type ['A', 3]
1426
Quiver on 3 vertices of type ['A', 3]
1427
1428
sage: Ts = Q.mutation_class(show_depth=True)
1429
Depth: 0 found: 1 Time: ... s
1430
Depth: 1 found: 3 Time: ... s
1431
Depth: 2 found: 4 Time: ... s
1432
1433
sage: Ts = Q.mutation_class(return_paths=True)
1434
sage: for T in Ts: print T
1435
(Quiver on 3 vertices of type ['A', 3], [])
1436
(Quiver on 3 vertices of type ['A', 3], [1])
1437
(Quiver on 3 vertices of type ['A', 3], [0])
1438
(Quiver on 3 vertices of type ['A', 3], [0, 1])
1439
1440
sage: Ts = Q.mutation_class(up_to_equivalence=False)
1441
sage: for T in Ts: print T
1442
Quiver on 3 vertices of type ['A', 3]
1443
Quiver on 3 vertices of type ['A', 3]
1444
Quiver on 3 vertices of type ['A', 3]
1445
Quiver on 3 vertices of type ['A', 3]
1446
Quiver on 3 vertices of type ['A', 3]
1447
Quiver on 3 vertices of type ['A', 3]
1448
Quiver on 3 vertices of type ['A', 3]
1449
Quiver on 3 vertices of type ['A', 3]
1450
Quiver on 3 vertices of type ['A', 3]
1451
Quiver on 3 vertices of type ['A', 3]
1452
Quiver on 3 vertices of type ['A', 3]
1453
Quiver on 3 vertices of type ['A', 3]
1454
Quiver on 3 vertices of type ['A', 3]
1455
Quiver on 3 vertices of type ['A', 3]
1456
1457
sage: Ts = Q.mutation_class(return_paths=True,up_to_equivalence=False)
1458
sage: for T in Ts: print T
1459
(Quiver on 3 vertices of type ['A', 3], [])
1460
(Quiver on 3 vertices of type ['A', 3], [2])
1461
(Quiver on 3 vertices of type ['A', 3], [1])
1462
(Quiver on 3 vertices of type ['A', 3], [0])
1463
(Quiver on 3 vertices of type ['A', 3], [2, 1])
1464
(Quiver on 3 vertices of type ['A', 3], [0, 1])
1465
(Quiver on 3 vertices of type ['A', 3], [0, 1, 2])
1466
(Quiver on 3 vertices of type ['A', 3], [0, 1, 0])
1467
(Quiver on 3 vertices of type ['A', 3], [2, 1, 2])
1468
(Quiver on 3 vertices of type ['A', 3], [2, 1, 0])
1469
(Quiver on 3 vertices of type ['A', 3], [2, 1, 0, 2])
1470
(Quiver on 3 vertices of type ['A', 3], [2, 1, 0, 1])
1471
(Quiver on 3 vertices of type ['A', 3], [2, 1, 2, 1])
1472
(Quiver on 3 vertices of type ['A', 3], [2, 1, 2, 0])
1473
1474
sage: Ts = Q.mutation_class(show_depth=True)
1475
Depth: 0 found: 1 Time: ... s
1476
Depth: 1 found: 3 Time: ... s
1477
Depth: 2 found: 4 Time: ... s
1478
1479
sage: Ts = Q.mutation_class(show_depth=True, up_to_equivalence=False)
1480
Depth: 0 found: 1 Time: ... s
1481
Depth: 1 found: 4 Time: ... s
1482
Depth: 2 found: 6 Time: ... s
1483
Depth: 3 found: 10 Time: ... s
1484
Depth: 4 found: 14 Time: ... s
1485
1486
TESTS::
1487
1488
sage: all( len(ClusterQuiver(['A',n]).mutation_class()) == ClusterQuiver(['A',n]).mutation_type().class_size() for n in [2..6])
1489
True
1490
1491
sage: all( len(ClusterQuiver(['B',n]).mutation_class()) == ClusterQuiver(['B',n]).mutation_type().class_size() for n in [2..6])
1492
True
1493
"""
1494
if depth is infinity and not self.is_mutation_finite():
1495
raise ValueError('The mutation class can - for infinite mutation types - only be computed up to a given depth')
1496
return [ Q for Q in self.mutation_class_iter( depth=depth, show_depth=show_depth, return_paths=return_paths, data_type=data_type, up_to_equivalence=up_to_equivalence, sink_source=sink_source ) ]
1497
1498
def is_finite( self ):
1499
"""
1500
Returns True if self is of finite type.
1501
1502
EXAMPLES::
1503
1504
sage: Q = ClusterQuiver(['A',3])
1505
sage: Q.is_finite()
1506
True
1507
sage: Q = ClusterQuiver(['A',[2,2],1])
1508
sage: Q.is_finite()
1509
False
1510
sage: Q = ClusterQuiver([['A',3],['B',3]])
1511
sage: Q.is_finite()
1512
True
1513
sage: Q = ClusterQuiver(['T',[4,4,4]])
1514
sage: Q.is_finite()
1515
False
1516
sage: Q = ClusterQuiver([['A',3],['T',[4,4,4]]])
1517
sage: Q.is_finite()
1518
False
1519
sage: Q = ClusterQuiver([['A',3],['T',[2,2,3]]])
1520
sage: Q.is_finite()
1521
True
1522
sage: Q = ClusterQuiver([['A',3],['D',5]])
1523
sage: Q.is_finite()
1524
True
1525
sage: Q = ClusterQuiver([['A',3],['D',5,1]])
1526
sage: Q.is_finite()
1527
False
1528
1529
sage: Q = ClusterQuiver([[0,1,2],[1,2,2],[2,0,2]])
1530
sage: Q.is_finite()
1531
False
1532
1533
sage: Q = ClusterQuiver([[0,1,2],[1,2,2],[2,0,2],[3,4,1],[4,5,1]])
1534
sage: Q.is_finite()
1535
False
1536
"""
1537
mt = self.mutation_type()
1538
if type( mt ) in [QuiverMutationType_Irreducible, QuiverMutationType_Reducible] and mt.is_finite():
1539
return True
1540
else:
1541
return False
1542
1543
def is_mutation_finite( self, nr_of_checks=None, return_path=False ):
1544
"""
1545
Uses a non-deterministic method by random mutations in various directions. Can result in a wrong answer.
1546
1547
INPUT:
1548
1549
- ``nr_of_checks`` -- (default: None) number of mutations applied. Standard is 500*(number of vertices of self).
1550
- ``return_path`` -- (default: False) if True, in case of self not being mutation finite, a path from self to a quiver with an edge label (a,-b) and a*b > 4 is returned.
1551
1552
ALGORITHM:
1553
1554
A quiver is mutation infinite if and only if every edge label (a,-b) satisfy a*b > 4.
1555
Thus, we apply random mutations in random directions
1556
1557
EXAMPLES::
1558
1559
sage: Q = ClusterQuiver(['A',10])
1560
sage: Q._mutation_type = None
1561
sage: Q.is_mutation_finite()
1562
True
1563
1564
sage: Q = ClusterQuiver([(0,1),(1,2),(2,3),(3,4),(4,5),(5,6),(6,7),(7,8),(2,9)])
1565
sage: Q.is_mutation_finite()
1566
False
1567
"""
1568
if self._n <= 2:
1569
is_finite = True
1570
path = None
1571
elif not return_path and self._mutation_type == 'undetermined infinite mutation type':
1572
is_finite = False
1573
elif type( self._mutation_type ) in [QuiverMutationType_Irreducible, QuiverMutationType_Reducible] and self._mutation_type.is_mutation_finite():
1574
is_finite = True
1575
path = None
1576
elif not return_path and type( self._mutation_type ) in [QuiverMutationType_Irreducible, QuiverMutationType_Reducible] and not self._mutation_type.is_mutation_finite():
1577
is_finite = False
1578
else:
1579
# turning dg_component into a canonical form
1580
dig6 = _digraph_to_dig6(self.digraph())
1581
# and getting the corresponding matrix
1582
M = _dig6_to_matrix(dig6)
1583
1584
is_finite, path = is_mutation_finite(M,nr_of_checks=nr_of_checks)
1585
if return_path:
1586
return is_finite, path
1587
else:
1588
return is_finite
1589
1590