Path: blob/master/src/sage/combinat/cluster_algebra_quiver/quiver.py
8818 views
r"""1Quiver23A *quiver* is an oriented graphs without loops, two-cycles, or multiple edges. The edges are labelled by pairs `(i,-j)`4such 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.56For the compendium on the cluster algebra and quiver package see78http://arxiv.org/abs/1102.4844.910AUTHORS:1112- Gregg Musiker13- Christian Stump1415.. 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`.16"""17#*****************************************************************************18# Copyright (C) 2011 Gregg Musiker <[email protected]>19# Christian Stump <[email protected]>20#21# Distributed under the terms of the GNU General Public License (GPL)22# http://www.gnu.org/licenses/23#*****************************************************************************24from sage.structure.sage_object import SageObject25from copy import copy26from sage.structure.unique_representation import UniqueRepresentation27from sage.misc.all import cached_method28from sage.rings.all import ZZ, CC, infinity29from sage.graphs.all import Graph, DiGraph30from sage.combinat.cluster_algebra_quiver.quiver_mutation_type import QuiverMutationType, QuiverMutationType_Irreducible, QuiverMutationType_Reducible, _edge_list_to_matrix31from 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_matrix32from sage.combinat.cluster_algebra_quiver.mutation_type import _connected_mutation_type, _mutation_type_from_data, is_mutation_finite3334from sage.groups.perm_gps.permgroup import PermutationGroup3536class ClusterQuiver(SageObject):37"""38The *quiver* associated to an *exchange matrix*.3940INPUT:4142- ``data`` -- can be any of the following::4344* QuiverMutationType45* str - a string representing a QuiverMutationType or a common quiver type (see Examples)46* ClusterQuiver47* Matrix - a skew-symmetrizable matrix48* DiGraph - must be the input data for a quiver49* List of edges - must be the edge list of a digraph for a quiver5051- ``frozen`` -- (default:``None``) sets the number of frozen variables if the input type is a DiGraph, it is ignored otherwise.5253EXAMPLES:5455from a QuiverMutationType::5657sage: Q = ClusterQuiver(['A',5]); Q58Quiver on 5 vertices of type ['A', 5]5960sage: Q = ClusterQuiver(['B',2]); Q61Quiver on 2 vertices of type ['B', 2]62sage: Q2 = ClusterQuiver(['C',2]); Q263Quiver on 2 vertices of type ['B', 2]64sage: MT = Q.mutation_type(); MT.standard_quiver() == Q65True66sage: MT = Q2.mutation_type(); MT.standard_quiver() == Q267False6869sage: Q = ClusterQuiver(['A',[2,5],1]); Q70Quiver on 7 vertices of type ['A', [2, 5], 1]7172sage: Q = ClusterQuiver(['A', [5,0],1]); Q73Quiver on 5 vertices of type ['D', 5]74sage: Q.is_finite()75True76sage: Q.is_acyclic()77False7879sage: Q = ClusterQuiver(['F', 4, [2,1]]); Q80Quiver on 6 vertices of type ['F', 4, [1, 2]]81sage: MT = Q.mutation_type(); MT.standard_quiver() == Q82False83sage: dg = Q.digraph(); Q.mutate([2,1,4,0,5,3])84sage: dg2 = Q.digraph(); dg2.is_isomorphic(dg,edge_labels=True)85False86sage: dg2.is_isomorphic(MT.standard_quiver().digraph(),edge_labels=True)87True8889sage: Q = ClusterQuiver(['G',2, (3,1)]); Q90Quiver on 4 vertices of type ['G', 2, [1, 3]]91sage: MT = Q.mutation_type(); MT.standard_quiver() == Q92False9394sage: Q = ClusterQuiver(['GR',[3,6]]); Q95Quiver on 4 vertices of type ['D', 4]96sage: MT = Q.mutation_type(); MT.standard_quiver() == Q97False9899sage: Q = ClusterQuiver(['GR',[3,7]]); Q100Quiver on 6 vertices of type ['E', 6]101102sage: Q = ClusterQuiver(['TR',2]); Q103Quiver on 3 vertices of type ['A', 3]104sage: MT = Q.mutation_type(); MT.standard_quiver() == Q105False106sage: Q.mutate([1,0]); MT.standard_quiver() == Q107True108109sage: Q = ClusterQuiver(['TR',3]); Q110Quiver on 6 vertices of type ['D', 6]111sage: MT = Q.mutation_type(); MT.standard_quiver() == Q112False113114from a ClusterQuiver::115116sage: Q = ClusterQuiver(['A',[2,5],1]); Q117Quiver on 7 vertices of type ['A', [2, 5], 1]118sage: T = ClusterQuiver( Q ); T119Quiver on 7 vertices of type ['A', [2, 5], 1]120121from a Matrix::122123sage: Q = ClusterQuiver(['A',[2,5],1]); Q124Quiver on 7 vertices of type ['A', [2, 5], 1]125sage: T = ClusterQuiver( Q._M ); T126Quiver on 7 vertices127128sage: Q = ClusterQuiver( matrix([[0,1,-1],[-1,0,1],[1,-1,0],[1,2,3]]) ); Q129Quiver on 4 vertices with 1 frozen vertex130131sage: Q = ClusterQuiver( matrix([]) ); Q132Quiver without vertices133134from a DiGraph::135136sage: Q = ClusterQuiver(['A',[2,5],1]); Q137Quiver on 7 vertices of type ['A', [2, 5], 1]138sage: T = ClusterQuiver( Q._digraph ); T139Quiver on 7 vertices140141sage: Q = ClusterQuiver( DiGraph([[1,2],[2,3],[3,4],[4,1]]) ); Q142Quiver on 4 vertices143144from a List of edges::145146sage: Q = ClusterQuiver(['A',[2,5],1]); Q147Quiver on 7 vertices of type ['A', [2, 5], 1]148sage: T = ClusterQuiver( Q._digraph.edges() ); T149Quiver on 7 vertices150151sage: Q = ClusterQuiver( [[1,2],[2,3],[3,4],[4,1]] ); Q152Quiver on 4 vertices153154TESTS::155156sage: Q = ClusterQuiver(DiGraph([[1,1]]))157Traceback (most recent call last):158...159ValueError: The input DiGraph contains a loop160161sage: Q = ClusterQuiver([[1,1]])162Traceback (most recent call last):163...164ValueError: The input DiGraph contains a loop165166sage: Q = ClusterQuiver(DiGraph([[1, 0],[0,1]]))167Traceback (most recent call last):168...169ValueError: The input DiGraph contains two-cycles170171sage: Q = ClusterQuiver('whatever')172Traceback (most recent call last):173...174ValueError: The input data was not recognized.175"""176def __init__( self, data, frozen=None ):177"""178TESTS::179180sage: Q = ClusterQuiver(['A',4])181sage: TestSuite(Q).run()182"""183from cluster_seed import ClusterSeed184from sage.matrix.matrix import Matrix185186# constructs a quiver from a mutation type187if type( data ) in [QuiverMutationType_Irreducible,QuiverMutationType_Reducible]:188if frozen is not None:189print 'The input data is a quiver, therefore the additional parameter frozen is ignored.'190191mutation_type = data192self.__init__( mutation_type.standard_quiver() )193194# constructs a quiver from string representing a mutation type or a common quiver type (see Examples)195# 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.196elif 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) ):197if frozen is not None:198print 'The input data is a quiver, therefore the additional parameter frozen is ignored.'199mutation_type = QuiverMutationType( data )200201# 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.202if len(data) == 2 and type( data[0] ) is str:203if data[0] == 'TR' or data[0] == 'GR' or (data[0] == 'C' and data[1] == 2):204if data[1] in ZZ:205quiv = ClusterQuiver( QuiverMutationType_Irreducible( data[0], data[1] )._digraph )206quiv._mutation_type = mutation_type207self.__init__( quiv )208elif type( data[1]) is list:209quiv = ClusterQuiver( QuiverMutationType_Irreducible( data[0], tuple(data[1]) )._digraph )210quiv._mutation_type = mutation_type211self.__init__( quiv )212else:213self.__init__( mutation_type.standard_quiver() )214elif len(data) == 3 and type( data[0] ) is str:215if (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]):216quiv = ClusterQuiver( QuiverMutationType_Irreducible( data[0], data[1], tuple(data[2]) )._digraph )217quiv._mutation_type = mutation_type218self.__init__( quiv )219elif (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) ):220quiv = ClusterQuiver( QuiverMutationType_Irreducible( data[0], data[1], data[2] )._digraph )221quiv._mutation_type = mutation_type222self.__init__( quiv )223elif data[0] == 'A' and type(data[1]) is list and data[2] == 1:224if len(data[1]) == 2 and min(data[1]) == 0:225quiv = ClusterQuiver( QuiverMutationType_Irreducible( data[0], tuple(data[1]), data[2] )._digraph )226quiv._mutation_type = mutation_type227self.__init__( quiv )228else:229self.__init__( mutation_type.standard_quiver() )230231elif data[0] == 'A' and type(data[1]) is tuple and data[2] == 1:232if len(data[1]) == 2 and min(data[1]) == 0:233quiv = ClusterQuiver( QuiverMutationType_Irreducible( data[0], data[1], data[2] )._digraph )234quiv._mutation_type = mutation_type235self.__init__( quiv )236else:237self.__init__( mutation_type.standard_quiver() )238239else:240self.__init__( mutation_type.standard_quiver() )241else:242self.__init__( mutation_type.standard_quiver() )243244# constructs a quiver from a cluster seed245elif type(data) is ClusterSeed:246self.__init__( data.quiver() )247248# constructs a quiver from a quiver249elif type(data) is ClusterQuiver:250if frozen is not None:251print 'The input data is a quiver, therefore the additional parameter frozen is ignored.'252253self._M = copy(data._M)254self._n = data._n255self._m = data._m256self._digraph = copy( data._digraph )257self._mutation_type = data._mutation_type258self._description = data._description259260# constructs a quiver from a matrix261elif isinstance(data, Matrix):262if not _principal_part(data).is_skew_symmetrizable( positive=True ):263raise ValueError('The principal part of the matrix data must be skew-symmetrizable.')264if frozen is not None:265print 'The input data is a matrix, therefore the additional parameter frozen is ignored.'266267self._M = copy(data).sparse_matrix()268self._n = n = self._M.ncols()269self._m = m = self._M.nrows() - self._n270self._digraph = _matrix_to_digraph( self._M )271self._mutation_type = None272if n+m == 0:273self._description = 'Quiver without vertices'274elif n+m == 1:275self._description = 'Quiver on %d vertex' %(n+m)276else:277self._description = 'Quiver on %d vertices' %(n+m)278279# constructs a quiver from a digraph280elif type(data) is DiGraph:281if frozen is None:282frozen = 0283elif not ZZ(frozen) == frozen:284raise ValueError("The optional argument frozen (=%s) must be an integer."%frozen)285m = self._m = frozen286n = self._n = data.order() - m287dg = copy( data )288edges = data.edges(labels=False)289if any( (a,a) in edges for a in data.vertices() ):290raise ValueError("The input DiGraph contains a loop")291if any( (b,a) in edges for (a,b) in edges ):292raise ValueError("The input DiGraph contains two-cycles")293if not set(dg.vertices()) == set(range(n+m)):294dg.relabel()295if dg.has_multiple_edges():296multi_edges = {}297for v1,v2,label in dg.multiple_edges():298if label not in ZZ:299raise ValueError("The input DiGraph contains multiple edges labeled by non-integers")300elif (v1,v2) in multi_edges:301multi_edges[(v1,v2)] += label302else:303multi_edges[(v1,v2)] = label304dg.delete_edge(v1,v2)305dg.add_edges( [ (v1,v2,multi_edges[(v1,v2)]) for v1,v2 in multi_edges ] )306for edge in dg.edge_iterator():307if edge[0] >= n and edge[1] >= n:308raise ValueError("The input digraph contains edges within the frozen vertices")309if edge[2] is None:310dg.set_edge_label( edge[0], edge[1], (1,-1) )311edge = (edge[0],edge[1],(1,-1))312elif edge[2] in ZZ:313dg.set_edge_label( edge[0], edge[1], (edge[2],-edge[2]) )314edge = (edge[0],edge[1],(edge[2],-edge[2]))315elif type(edge[2]) == list and len(edge[2]) != 2:316raise ValueError("The input digraph contains an edge with the wrong type of list as a label.")317elif type(edge[2]) == list and len(edge[2]) == 2:318dg.set_edge_label( edge[0], edge[1], (edge[2][0], edge[2][1]))319edge = (edge[0],edge[1],(edge[2][0],edge[2][1]))320elif ( edge[0] >= n or edge[1] >= n ) and not edge[2][0] == - edge[2][1]:321raise ValueError("The input digraph contains an edge to or from a frozen vertex which is not skew-symmetric.")322if edge[2][0] < 0:323raise ValueError("The input digraph contains an edge of the form (a,-b) with negative a.")324325M = _edge_list_to_matrix( dg.edge_iterator(), n, m )326if not _principal_part(M).is_skew_symmetrizable( positive=True ):327raise ValueError("The input digraph must be skew-symmetrizable")328self._digraph = dg329self._M = M330if n+m == 0:331self._description = 'Quiver without vertices'332elif n+m == 1:333self._description = 'Quiver on %d vertex' %(n+m)334else:335self._description = 'Quiver on %d vertices' %(n+m)336self._mutation_type = None337338# if data is a list of edges, the appropriate digraph is constructed.339340elif isinstance(data,list) and all(isinstance(x,(list,tuple)) for x in data ):341dg = DiGraph( data )342self.__init__(data=dg, frozen=frozen )343344# otherwise, an error is raised345else:346raise ValueError("The input data was not recognized.")347348def __eq__(self, other):349"""350Returns ``True`` if ``self`` and ``other`` represent the same quiver.351352EXAMPLES::353354sage: Q = ClusterQuiver(['A',5])355sage: T = Q.mutate( 2, inplace=False )356sage: Q.__eq__( T )357False358sage: T.mutate( 2 )359sage: Q.__eq__( T )360True361"""362return type( other ) is ClusterQuiver and self._M == other._M363364def _repr_(self):365"""366Returns the description of ``self``.367368EXAMPLES::369370sage: Q = ClusterQuiver(['A',5])371sage: Q._repr_()372"Quiver on 5 vertices of type ['A', 5]"373"""374name = self._description375if self._mutation_type:376if type( self._mutation_type ) is str:377name += ' of ' + self._mutation_type378else:379name += ' of type ' + str(self._mutation_type)380if self._m == 1:381name += ' with %s frozen vertex'%self._m382elif self._m > 1:383name += ' with %s frozen vertices'%self._m384return name385386def plot(self, circular=True, center=(0,0), directed=True, mark=None, save_pos=False):387"""388Returns the plot of the underlying digraph of ``self``.389390INPUT:391392- ``circular`` -- (default:True) if True, the circular plot is chosen, otherwise >>spring<< is used.393- ``center`` -- (default:(0,0)) sets the center of the circular plot, otherwise it is ignored.394- ``directed`` -- (default: True) if True, the directed version is shown, otherwise the undirected.395- ``mark`` -- (default: None) if set to i, the vertex i is highlighted.396- ``save_pos`` -- (default:False) if True, the positions of the vertices are saved.397398EXAMPLES::399400sage: Q = ClusterQuiver(['A',5])401sage: pl = Q.plot()402sage: pl = Q.plot(circular=True)403"""404from sage.plot.colors import rainbow405from sage.graphs.graph_generators import GraphGenerators406from sage.all import e,pi,I407graphs = GraphGenerators()408# returns positions for graph vertices on two concentric cycles with radius 1 and 2409def _graphs_concentric_circles(n,m):410g1 = graphs.CycleGraph(n).get_pos()411g2 = graphs.CycleGraph(m).get_pos()412for i in g2:413z = CC(g2[i])*e**(-pi*I/(2*m))414g2[i] = (z.real_part(),z.imag_part())415for i in range(m):416g1[n+i] = [2*g2[i][0], 2*g2[i][1]]417return g1418419n, m = self._n, self._m420colors = rainbow(11)421color_dict = { colors[0]:[], colors[1]:[], colors[6]:[], colors[5]:[] }422if directed:423dg = DiGraph( self._digraph )424else:425dg = Graph( self._digraph )426for edge in dg.edges():427v1,v2,(a,b) = edge428if v1 < n and v2 < n:429if (a,b) == (1,-1):430color_dict[ colors[0] ].append((v1,v2))431else:432color_dict[ colors[6] ].append((v1,v2))433else:434if (a,b) == (1,-1):435color_dict[ colors[1] ].append((v1,v2))436else:437color_dict[ colors[5] ].append((v1,v2))438if a == -b:439if a == 1:440dg.set_edge_label( v1,v2,'' )441else:442dg.set_edge_label( v1,v2,a )443if mark is not None:444if mark < n:445partition = (range(mark)+range(mark+1,n),range(n,n+m),[mark])446elif mark > n:447partition = (range(n),range(n,mark)+range(mark+1,n+m),[mark])448else:449raise ValueError("The given mark is not a vertex of self.")450else:451partition = (range(n),range(n,n+m),[])452vertex_color_dict = {}453vertex_color_dict[ colors[0] ] = partition[0]454vertex_color_dict[ colors[6] ] = partition[1]455vertex_color_dict[ colors[4] ] = partition[2]456457options = {458'graph_border' : True,459'edge_colors': color_dict,460'vertex_colors': vertex_color_dict,461'edge_labels' : True,462}463if circular:464pp = _graphs_concentric_circles( n, m )465for v in pp:466pp[v] = (pp[v][0]+center[0],pp[v][1]+center[1])467options[ 'pos' ] = pp468return dg.plot( **options )469470def show(self, fig_size=1, circular=False, directed=True, mark=None, save_pos=False):471"""472Shows the plot of the underlying digraph of ``self``.473474INPUT:475476- ``fig_size`` -- (default: 1) factor by which the size of the plot is multiplied.477- ``circular`` -- (default: False) if True, the circular plot is chosen, otherwise >>spring<< is used.478- ``directed`` -- (default: True) if True, the directed version is shown, otherwise the undirected.479- ``mark`` -- (default: None) if set to i, the vertex i is highlighted.480- ``save_pos`` -- (default:False) if True, the positions of the vertices are saved.481482TESTS::483484sage: Q = ClusterQuiver(['A',5])485sage: Q.show() # long time486"""487n, m = self._n, self._m488plot = self.plot( circular=circular, directed=directed, mark=mark, save_pos=save_pos )489if circular:490plot.show( figsize=[fig_size*3*(n+m)/4+1,fig_size*3*(n+m)/4+1] )491else:492plot.show( figsize=[fig_size*n+1,fig_size*n+1] )493494def interact(self, fig_size=1, circular=True):495"""496Only in notebook mode. Starts an interactive window for cluster seed mutations.497498INPUT:499500- ``fig_size`` -- (default: 1) factor by which the size of the plot is multiplied.501- ``circular`` -- (default: False) if True, the circular plot is chosen, otherwise >>spring<< is used.502503TESTS::504505sage: Q = ClusterQuiver(['A',4])506sage: Q.interact() # long time507'The interactive mode only runs in the Sage notebook.'508"""509from sage.plot.plot import EMBEDDED_MODE510from sagenb.notebook.interact import interact, selector511from sage.misc.all import html,latex512513if not EMBEDDED_MODE:514return "The interactive mode only runs in the Sage notebook."515else:516seq = []517sft = [True]518sss = [True]519ssm = [True]520ssl = [True]521@interact522def 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) ):523ft,ss,sm,sl = sft.pop(), sss.pop(), ssm.pop(), ssl.pop()524if ft:525self.show(fig_size=fig_size, circular=circular)526elif show_seq is not ss or show_matrix is not sm or show_lastmutation is not sl:527if seq and show_lastmutation:528self.show(fig_size=fig_size, circular=circular, mark=seq[len(seq)-1])529else:530self.show(fig_size=fig_size, circular=circular )531else:532self.mutate(k)533seq.append(k)534if not show_lastmutation:535self.show(fig_size=fig_size, circular=circular)536else:537self.show(fig_size=fig_size, circular=circular,mark=k)538sft.append(False)539sss.append(show_seq)540ssm.append(show_matrix)541ssl.append(show_lastmutation)542if show_seq: html( "Mutation sequence: $" + str( [ seq[i] for i in xrange(len(seq)) ] ).strip('[]') + "$" )543if show_matrix:544html( "B-Matrix:" )545m = self._M546#m = matrix(range(1,self._n+1),sparse=True).stack(m)547m = latex(m)548m = m.split('(')[1].split('\\right')[0]549html( "$ $" )550html( "$\\begin{align*} " + m + "\\end{align*}$" )551#html( "$" + m + "$" )552html( "$ $" )553554def save_image(self,filename,circular=False):555"""556Saves the plot of the underlying digraph of ``self``.557558INPUT:559560- ``filename`` -- the filename the image is saved to.561- ``circular`` -- (default: False) if True, the circular plot is chosen, otherwise >>spring<< is used.562563EXAMPLES::564565sage: Q = ClusterQuiver(['F',4,[1,2]])566sage: Q.save_image(os.path.join(SAGE_TMP, 'sage.png'))567"""568graph_plot = self.plot( circular=circular )569graph_plot.save( filename=filename )570571def qmu_save(self,filename=None):572"""573Saves a .qmu file of ``self`` that can then be opened in Bernhard Keller's Quiver Applet.574575INPUT:576577- ``filename`` -- the filename the image is saved to.578579If a filename is not specified, the default name is from_sage.qmu in the current sage directory.580581EXAMPLES::582583sage: Q = ClusterQuiver(['F',4,[1,2]])584sage: Q.qmu_save(os.path.join(SAGE_TMP, 'sage.qmu'))585586Make sure we can save quivers with `m != n` frozen variables, see :trac:`14851`::587588sage: S=ClusterSeed(['A',3])589sage: T1=S.principal_extension()590sage: T2=T1.principal_extension(ignore_coefficients=True)591sage: Q=T2.quiver()592sage: Q.qmu_save(os.path.join(SAGE_TMP, 'sage.qmu'))593"""594M = self.b_matrix()595if self.m() > 0:596from sage.matrix.constructor import Matrix597from sage.matrix.constructor import block_matrix598M1 = M.matrix_from_rows(range(self.n()))599M2 = M.matrix_from_rows(range(self.n(),self.n()+self.m()))600M3 = Matrix(self.m(),self.m())601M = block_matrix([[M1,-M2.transpose()],[M2,M3]])602dg = self.digraph()603dg.plot(save_pos=True)604PP = dg.get_pos()605m = M.ncols()606if filename is None:607filename = 'from_sage.qmu'608try:609self._default_filename = filename610except AttributeError:611pass612if filename[-4:] != '.qmu':613filename = filename + '.qmu'614myfile = open(filename, 'w')615myfile.write('//Number of points'); myfile.write('\n')616myfile.write(str(m)); myfile.write('\n')617myfile.write('//Vertex radius'); myfile.write('\n')618myfile.write(str(9)); myfile.write('\n')619myfile.write('//Labels shown'); myfile.write('\n')620myfile.write(str(1)); myfile.write('\n')621myfile.write('//Matrix'); myfile.write('\n')622myfile.write(str(m)); myfile.write(' '); myfile.write(str(m)); myfile.write('\n')623for i in range(m):624for j in range(m):625myfile.write(str(M[i,j])); myfile.write(' ')626myfile.write('\n')627myfile.write('//Points'); myfile.write('\n')628for i in range(m):629myfile.write(str(9)); myfile.write(' '); myfile.write(str(100*PP[i][0])); myfile.write(' ');630myfile.write(str(100*PP[i][1]));631if i > self.n()-1:632myfile.write(' '); myfile.write(str(1))633myfile.write('\n')634myfile.write('//Historycounter'); myfile.write('\n')635myfile.write(str(-1)); myfile.write('\n')636myfile.write('//History'); myfile.write('\n'); myfile.write('\n')637myfile.write('//Cluster is null');638myfile.close()639640def b_matrix(self):641"""642Returns the b-matrix of ``self``.643644EXAMPLES::645646sage: ClusterQuiver(['A',4]).b_matrix()647[ 0 1 0 0]648[-1 0 -1 0]649[ 0 1 0 1]650[ 0 0 -1 0]651652sage: ClusterQuiver(['B',4]).b_matrix()653[ 0 1 0 0]654[-1 0 -1 0]655[ 0 1 0 1]656[ 0 0 -2 0]657658sage: ClusterQuiver(['D',4]).b_matrix()659[ 0 1 0 0]660[-1 0 -1 -1]661[ 0 1 0 0]662[ 0 1 0 0]663664sage: ClusterQuiver(QuiverMutationType([['A',2],['B',2]])).b_matrix()665[ 0 1 0 0]666[-1 0 0 0]667[ 0 0 0 1]668[ 0 0 -2 0]669"""670return copy( self._M )671672def digraph(self):673"""674Returns the underlying digraph of ``self``.675676EXAMPLES::677678sage: ClusterQuiver(['A',1]).digraph()679Digraph on 1 vertex680sage: ClusterQuiver(['A',1]).digraph().vertices()681[0]682sage: ClusterQuiver(['A',1]).digraph().edges()683[]684685sage: ClusterQuiver(['A',4]).digraph()686Digraph on 4 vertices687sage: ClusterQuiver(['A',4]).digraph().edges()688[(0, 1, (1, -1)), (2, 1, (1, -1)), (2, 3, (1, -1))]689690sage: ClusterQuiver(['B',4]).digraph()691Digraph on 4 vertices692sage: ClusterQuiver(['A',4]).digraph().edges()693[(0, 1, (1, -1)), (2, 1, (1, -1)), (2, 3, (1, -1))]694695sage: ClusterQuiver(QuiverMutationType([['A',2],['B',2]])).digraph()696Digraph on 4 vertices697698sage: ClusterQuiver(QuiverMutationType([['A',2],['B',2]])).digraph().edges()699[(0, 1, (1, -1)), (2, 3, (1, -2))]700"""701return copy( self._digraph )702703def mutation_type(self):704"""705Returns the mutation type of ``self``.706707Returns the mutation_type of each connected component of self if it can be determined,708otherwise, the mutation type of this component is set to be unknown.709710The mutation types of the components are ordered by vertex labels.711712If you do many type recognitions, you should consider to save713exceptional mutation types using714`meth`:sage.combinat.cluster_algebra_quiver.quiver_mutation_type.save_exceptional_data715716WARNING:717718- All finite types can be detected,719- All affine types can be detected, EXCEPT affine type D (the algorithm is not yet implemented)720- All exceptional types can be detected.721722EXAMPLES::723724sage: ClusterQuiver(['A',4]).mutation_type()725['A', 4]726sage: ClusterQuiver(['A',(3,1),1]).mutation_type()727['A', [1, 3], 1]728sage: ClusterQuiver(['C',2]).mutation_type()729['B', 2]730sage: ClusterQuiver(['B',4,1]).mutation_type()731['BD', 4, 1]732733finite types::734735sage: Q = ClusterQuiver(['A',5])736sage: Q._mutation_type = None737sage: Q.mutation_type()738['A', 5]739740sage: Q = ClusterQuiver([(0,1),(1,2),(2,3),(3,4)])741sage: Q.mutation_type()742['A', 5]743744affine types::745746sage: Q = ClusterQuiver(['E',8,[1,1]]); Q747Quiver on 10 vertices of type ['E', 8, [1, 1]]748sage: Q._mutation_type = None; Q749Quiver on 10 vertices750sage: Q.mutation_type() # long time751['E', 8, [1, 1]]752753the not yet working affine type D (unless user has saved small classical quiver data)::754755sage: Q = ClusterQuiver(['D',4,1])756sage: Q._mutation_type = None757sage: Q.mutation_type() # todo: not implemented758['D', 4, 1]759760the exceptional types::761762sage: Q = ClusterQuiver(['X',6])763sage: Q._mutation_type = None764sage: Q.mutation_type() # long time765['X', 6]766767examples from page 8 of Keller's article "Cluster algebras, quiver representations768and triangulated categories" (arXiv:0807.1960)::769770sage: 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)])771sage: ClusterQuiver( dg ).mutation_type() # long time772['E', 8, [1, 1]]773774sage: 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] } )775sage: ClusterQuiver( dg ).mutation_type() # long time776['E', 8, 1]777778sage: 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] } )779sage: ClusterQuiver( dg ).mutation_type() # long time780['E', 8, [1, 1]]781782infinite types::783784sage: Q = ClusterQuiver(['GR',[4,9]])785sage: Q._mutation_type = None786sage: Q.mutation_type()787'undetermined infinite mutation type'788789reducible types::790791sage: Q = ClusterQuiver([['A', 3], ['B', 3]])792sage: Q._mutation_type = None793sage: Q.mutation_type()794[ ['A', 3], ['B', 3] ]795796sage: Q = ClusterQuiver([['A', 3], ['T', [4,4,4]]])797sage: Q._mutation_type = None798sage: Q.mutation_type()799[['A', 3], 'undetermined infinite mutation type']800801sage: Q = ClusterQuiver([['A', 3], ['B', 3], ['T', [4,4,4]]])802sage: Q._mutation_type = None803sage: Q.mutation_type()804[['A', 3], ['B', 3], 'undetermined infinite mutation type']805806sage: Q = ClusterQuiver([[0,1,2],[1,2,2],[2,0,2],[3,4,1],[4,5,1]])807sage: Q.mutation_type()808['undetermined finite mutation type', ['A', 3]]809810TESTS::811812sage: Q = ClusterQuiver(matrix([[0, 3], [-1, 0], [1, 0], [0, 1]]))813sage: Q.mutation_type()814['G', 2]815sage: 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]]))816sage: Q.mutation_type()817'undetermined infinite mutation type'818"""819# checking if the mutation type is known already820if self._mutation_type is None:821# checking mutation type only for the principal part822if self._m > 0:823dg = self._digraph.subgraph( range(self._n) )824else:825dg = self._digraph826827# checking the type for each connected component828mutation_type = []829connected_components = dg.connected_components()830connected_components.sort()831for component in connected_components:832# constructing the digraph for this component833dg_component = dg.subgraph( component )834dg_component.relabel()835# turning dg_component into a canonical form836iso, orbits = _dg_canonical_form( dg_component, dg_component.num_verts(), 0 )837# turning dg_component into a canonical form838dig6 = _digraph_to_dig6( dg_component, hashable=True )839# and getting the corresponding matrix840M = _dig6_to_matrix(dig6)841842# checking if this quiver is mutation infinite843is_finite, path = is_mutation_finite(M)844if is_finite is False:845mut_type_part = 'undetermined infinite mutation type'846else:847# checking if this quiver is in the database848mut_type_part = _mutation_type_from_data( dg_component.order(), dig6, compute_if_necessary=False )849# checking if the algorithm can determine the mutation type850if mut_type_part == 'unknown':851mut_type_part = _connected_mutation_type(dg_component)852# checking if this quiver is of exceptional type by computing the exceptional mutation classes853if mut_type_part == 'unknown':854mut_type_part = _mutation_type_from_data(dg_component.order(), dig6, compute_if_necessary=True)855if mut_type_part == 'unknown':856mut_type_part = 'undetermined finite mutation type'857mutation_type.append( mut_type_part )858859# the empty quiver case860if len( mutation_type ) == 0:861Warning('Quiver has no vertices')862mutation_type = None863# the connected quiver case864elif len( mutation_type ) == 1:865mutation_type = mutation_type[0]866# the reducible quiver case867elif len( mutation_type ) > 1:868if any( type(mut_type_part) is str for mut_type_part in mutation_type ):869pass870else:871mutation_type = QuiverMutationType( mutation_type )872self._mutation_type = mutation_type873return self._mutation_type874875def n(self):876"""877Returns the number of free vertices of ``self``.878879EXAMPLES::880881sage: ClusterQuiver(['A',4]).n()8824883sage: ClusterQuiver(['A',(3,1),1]).n()8844885sage: ClusterQuiver(['B',4]).n()8864887sage: ClusterQuiver(['B',4,1]).n()8885889"""890return self._n891892def m(self):893"""894Returns the number of frozen vertices of ``self``.895896EXAMPLES::897898sage: Q = ClusterQuiver(['A',4])899sage: Q.m()9000901902sage: T = ClusterQuiver( Q.digraph().edges(), frozen=1 )903sage: T.n()9043905sage: T.m()9061907"""908return self._m909910def canonical_label( self, certify=False ):911"""912Returns the canonical labelling of ``self``, see sage.graphs.graph.GenericGraph.canonical_label.913914INPUT:915916- ``certify`` -- (default: False) if True, the dictionary from ``self.vertices()`` to the vertices of the returned quiver is returned as well.917918EXAMPLES::919920sage: Q = ClusterQuiver(['A',4]); Q.digraph().edges()921[(0, 1, (1, -1)), (2, 1, (1, -1)), (2, 3, (1, -1))]922923sage: T = Q.canonical_label(); T.digraph().edges()924[(0, 3, (1, -1)), (1, 2, (1, -1)), (1, 3, (1, -1))]925926sage: T,iso = Q.canonical_label(certify=True); T.digraph().edges(); iso927[(0, 3, (1, -1)), (1, 2, (1, -1)), (1, 3, (1, -1))]928{0: 0, 1: 3, 2: 1, 3: 2}929930sage: Q = ClusterQuiver(QuiverMutationType([['B',2],['A',1]])); Q931Quiver on 3 vertices of type [ ['B', 2], ['A', 1] ]932933sage: Q.canonical_label()934Quiver on 3 vertices of type [ ['A', 1], ['B', 2] ]935936sage: Q.canonical_label(certify=True)937(Quiver on 3 vertices of type [ ['A', 1], ['B', 2] ], {0: 1, 1: 2, 2: 0})938"""939n = self._n940m = self._m941942# computing the canonical form respecting the frozen variables943dg = copy( self._digraph )944iso, orbits = _dg_canonical_form( dg, n, m )945Q = ClusterQuiver( dg )946# getting the new ordering for the mutation type if necessary947if self._mutation_type:948if dg.is_connected():949Q._mutation_type = self._mutation_type950else:951CC = sorted( self._digraph.connected_components() )952CC_new = sorted( zip( [ sorted( [ iso[i] for i in L ] ) for L in CC ], range( len( CC ) ) ) )953comp_iso = [ L[1] for L in CC_new ]954Q._mutation_type = []955for i in range( len( CC_new ) ):956Q._mutation_type.append( copy( self._mutation_type.irreducible_components()[ comp_iso[i] ] ) )957Q._mutation_type = QuiverMutationType( Q._mutation_type )958if certify:959return Q, iso960else:961return Q962963def is_acyclic(self):964"""965Returns true if ``self`` is acyclic.966967EXAMPLES::968969sage: ClusterQuiver(['A',4]).is_acyclic()970True971972sage: ClusterQuiver(['A',[2,1],1]).is_acyclic()973True974975sage: ClusterQuiver([[0,1],[1,2],[2,0]]).is_acyclic()976False977"""978return self._digraph.is_directed_acyclic()979980def is_bipartite(self,return_bipartition=False):981"""982Returns true if ``self`` is bipartite.983984EXAMPLES::985986sage: ClusterQuiver(['A',[3,3],1]).is_bipartite()987True988989sage: ClusterQuiver(['A',[4,3],1]).is_bipartite()990False991"""992dg = copy( self._digraph )993dg.delete_vertices( range(self._n,self._n+self._m) )994innie = dg.in_degree()995outie = dg.out_degree()996is_bip = sum( [ innie[i]*outie[i] for i in range(len(innie)) ] ) == 0997if not is_bip:998return False999else:1000if not return_bipartition:1001return True1002else:1003g = dg.to_undirected()1004return g.bipartite_sets()10051006def exchangeable_part(self):1007"""1008Returns the restriction to the principal part (i.e. exchangeable part) of ``self``, the subquiver obtained by deleting the frozen vertices of ``self``.10091010EXAMPLES::10111012sage: Q = ClusterQuiver(['A',4])1013sage: T = ClusterQuiver( Q.digraph().edges(), frozen=1 )1014sage: T.digraph().edges()1015[(0, 1, (1, -1)), (2, 1, (1, -1)), (2, 3, (1, -1))]10161017sage: T.exchangeable_part().digraph().edges()1018[(0, 1, (1, -1)), (2, 1, (1, -1))]10191020sage: Q2 = Q.principal_extension()1021sage: Q3 = Q2.principal_extension()1022sage: Q2.exchangeable_part() == Q3.exchangeable_part()1023True1024"""1025dg = DiGraph( self._digraph )1026dg.delete_vertices( range(self._n,self._n+self._m) )1027Q = ClusterQuiver( dg )1028Q._mutation_type = self._mutation_type1029return Q10301031def principal_extension(self, inplace=False):1032"""1033Returns 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``.10341035EXAMPLES::10361037sage: Q = ClusterQuiver(['A',2]); Q1038Quiver on 2 vertices of type ['A', 2]1039sage: T = Q.principal_extension(); T1040Quiver on 4 vertices of type ['A', 2] with 2 frozen vertices1041sage: T2 = T.principal_extension(); T21042Quiver on 6 vertices of type ['A', 2] with 4 frozen vertices1043sage: Q.digraph().edges()1044[(0, 1, (1, -1))]1045sage: T.digraph().edges()1046[(0, 1, (1, -1)), (2, 0, (1, -1)), (3, 1, (1, -1))]1047sage: T2.digraph().edges()1048[(0, 1, (1, -1)), (2, 0, (1, -1)), (3, 1, (1, -1)), (4, 0, (1, -1)), (5, 1, (1, -1))]1049"""1050dg = DiGraph( self._digraph )1051dg.add_edges( [(self._n+self._m+i,i) for i in range(self._n)] )1052Q = ClusterQuiver( dg, frozen=self._m+self._n )1053Q._mutation_type = self._mutation_type1054if inplace:1055self.__init__(Q)1056else:1057return Q10581059def mutate(self, data, inplace=True):1060"""1061Mutates ``self`` at a sequence of vertices.10621063INPUT:10641065- ``sequence`` -- a vertex of ``self`` or an iterator of vertices of ``self``.1066- ``inplace`` -- (default: True) if False, the result is returned, otherwise ``self`` is modified.10671068EXAMPLES::10691070sage: Q = ClusterQuiver(['A',4]); Q.b_matrix()1071[ 0 1 0 0]1072[-1 0 -1 0]1073[ 0 1 0 1]1074[ 0 0 -1 0]10751076sage: Q.mutate(0); Q.b_matrix()1077[ 0 -1 0 0]1078[ 1 0 -1 0]1079[ 0 1 0 1]1080[ 0 0 -1 0]10811082sage: T = Q.mutate(0, inplace=False); T1083Quiver on 4 vertices of type ['A', 4]10841085sage: Q.mutate(0)1086sage: Q == T1087True10881089sage: Q.mutate([0,1,0])1090sage: Q.b_matrix()1091[ 0 -1 1 0]1092[ 1 0 0 0]1093[-1 0 0 1]1094[ 0 0 -1 0]10951096sage: Q = ClusterQuiver(QuiverMutationType([['A',1],['A',3]]))1097sage: Q.b_matrix()1098[ 0 0 0 0]1099[ 0 0 1 0]1100[ 0 -1 0 -1]1101[ 0 0 1 0]11021103sage: T = Q.mutate(0,inplace=False)1104sage: Q == T1105True11061107TESTS::11081109sage: Q = ClusterQuiver(['A',4]); Q.mutate(0,1)1110Traceback (most recent call last):1111...1112ValueError: The second parameter must be boolean. To mutate at a sequence of length 2, input it as a list.11131114sage: Q = ClusterQuiver(['A',4]); Q.mutate(0,0)1115Traceback (most recent call last):1116...1117ValueError: The second parameter must be boolean. To mutate at a sequence of length 2, input it as a list.1118"""1119n = self._n1120m = self._m1121dg = self._digraph1122V = range(n)11231124if data in V:1125seq = [data]1126else:1127seq = data1128if type( seq ) is tuple:1129seq = list( seq )1130if not type( seq ) is list:1131raise ValueError('The quiver can only be mutated at a vertex or at a sequence of vertices')1132if not type(inplace) is bool:1133raise ValueError('The second parameter must be boolean. To mutate at a sequence of length 2, input it as a list.')1134if any( v not in V for v in seq ):1135v = filter( lambda v: v not in V, seq )[0]1136raise ValueError('The quiver cannot be mutated at the vertex %s'%v)11371138for v in seq:1139dg = _digraph_mutate( dg, v, n, m )1140M = _edge_list_to_matrix( dg.edge_iterator(), n, m )1141if inplace:1142self._M = M1143self._digraph = dg1144else:1145Q = ClusterQuiver( M )1146Q._mutation_type = self._mutation_type1147return Q11481149def mutation_sequence(self, sequence, show_sequence=False, fig_size=1.2 ):1150"""1151Returns a list containing the sequence of quivers obtained from ``self`` by a sequence of mutations on vertices.11521153INPUT:11541155- ``sequence`` -- a list or tuple of vertices of ``self``.1156- ``show_sequence`` -- (default: False) if True, a png containing the mutation sequence is shown.1157- ``fig_size`` -- (default: 1.2) factor by which the size of the sequence is expanded.11581159EXAMPLES::11601161sage: Q = ClusterQuiver(['A',4])1162sage: seq = Q.mutation_sequence([0,1]); seq1163[Quiver on 4 vertices of type ['A', 4], Quiver on 4 vertices of type ['A', 4], Quiver on 4 vertices of type ['A', 4]]1164sage: [T.b_matrix() for T in seq]1165[1166[ 0 1 0 0] [ 0 -1 0 0] [ 0 1 -1 0]1167[-1 0 -1 0] [ 1 0 -1 0] [-1 0 1 0]1168[ 0 1 0 1] [ 0 1 0 1] [ 1 -1 0 1]1169[ 0 0 -1 0], [ 0 0 -1 0], [ 0 0 -1 0]1170]1171"""1172from sage.plot.plot import Graphics1173from sage.plot.text import text1174n = self._n1175m = self._m1176if m == 0:1177width_factor = 31178fig_size = fig_size*2*n/31179else:1180width_factor = 61181fig_size = fig_size*4*n/31182V = range(n)11831184if type( sequence ) is tuple:1185sequence = list( sequence )1186if not type( sequence ) is list:1187raise ValueError('The quiver can only be mutated at a vertex or at a sequence of vertices')1188if any( v not in V for v in sequence ):1189v = filter( lambda v: v not in V, sequence )[0]1190raise ValueError('The quiver can only be mutated at the vertex %s'%v )11911192quiver = copy( self )1193quiver_sequence = []1194quiver_sequence.append( copy( quiver ) )11951196for v in sequence:1197quiver.mutate( v )1198quiver_sequence.append( copy( quiver ) )11991200if show_sequence:1201def _plot_arrow( v, k, center=(0,0) ):1202return text("$\longleftrightarrow$",(center[0],center[1]), fontsize=25) + text("$\mu_"+str(v)+"$",(center[0],center[1]+0.15), fontsize=15) \1203+ text("$"+str(k)+"$",(center[0],center[1]-0.2), fontsize=15)1204plot_sequence = [ quiver_sequence[i].plot( circular=True, center=(i*width_factor,0) ) for i in range(len(quiver_sequence)) ]1205arrow_sequence = [ _plot_arrow( sequence[i],i+1,center=((i+0.5)*width_factor,0) ) for i in range(len(sequence)) ]1206sequence = []1207for i in xrange( len( plot_sequence ) ):1208if i < len( arrow_sequence ):1209sequence.append( plot_sequence[i] + arrow_sequence[i] )1210else:1211sequence.append( plot_sequence[i] )1212plot_obj = Graphics()1213for elem in sequence:1214plot_obj += elem1215plot_obj.show(axes=False, figsize=[fig_size*len(quiver_sequence),fig_size])1216return quiver_sequence12171218def reorient( self, data ):1219"""1220Reorients ``self`` with respect to the given total order, or with respect to an iterator of edges in ``self`` to be reverted.12211222WARNING::12231224This operation might change the mutation type of ``self``.12251226INPUT:12271228- ``data`` -- an iterator defining a total order on ``self.vertices()``, or an iterator of edges in ``self`` to be reoriented.12291230EXAMPLES::12311232sage: Q = ClusterQuiver(['A',(2,3),1])1233sage: Q.mutation_type()1234['A', [2, 3], 1]12351236sage: Q.reorient([(0,1),(1,2),(2,3),(3,4)])1237sage: Q.mutation_type()1238['D', 5]12391240sage: Q.reorient([0,1,2,3,4])1241sage: Q.mutation_type()1242['A', [1, 4], 1]1243"""1244if all( 0 <= i and i < self._n + self._m for i in data ) and len( set( data ) ) == self._n+self._m :1245dg_new = DiGraph()1246for edge in self._digraph.edges():1247if data.index( edge[0] ) < data.index( edge[1] ):1248dg_new.add_edge( edge[0],edge[1],edge[2] )1249else:1250dg_new.add_edge( edge[1],edge[0],edge[2] )1251self._digraph = dg_new1252self._M = _edge_list_to_matrix( dg_new.edges(), self._n, self._m )1253self._mutation_type = None1254elif all( type(edge) in [list,tuple] and len(edge)==2 for edge in data ):1255edges = self._digraph.edges(labels=False)1256for edge in data:1257if (edge[1],edge[0]) in edges:1258label = self._digraph.edge_label(edge[1],edge[0])1259self._digraph.delete_edge(edge[1],edge[0])1260self._digraph.add_edge(edge[0],edge[1],label)1261self._M = _edge_list_to_matrix( self._digraph.edges(), self._n, self._m )1262self._mutation_type = None1263else:1264raise ValueError('The order is no total order on the vertices of the quiver or a list of edges to be oriented.')12651266def mutation_class_iter( self, depth=infinity, show_depth=False, return_paths=False, data_type="quiver", up_to_equivalence=True, sink_source=False ):1267"""1268Returns an iterator for the mutation class of self together with certain constrains.12691270INPUT:12711272- ``depth`` -- (default: infinity) integer, only quivers with distance at most depth from self are returned.1273- ``show_depth`` -- (default: False) if True, the actual depth of the mutation is shown.1274- ``return_paths`` -- (default: False) if True, a shortest path of mutation sequences from self to the given quiver is returned as well.1275- ``data_type`` -- (default: "quiver") can be one of the following::12761277* "quiver"1278* "matrix"1279* "digraph"1280* "dig6"1281* "path"12821283- ``up_to_equivalence`` -- (default: True) if True, only one quiver for each graph-isomorphism class is recorded.1284- ``sink_source`` -- (default: False) if True, only mutations at sinks and sources are applied.12851286EXAMPLES::12871288sage: Q = ClusterQuiver(['A',3])1289sage: it = Q.mutation_class_iter()1290sage: for T in it: print T1291Quiver on 3 vertices of type ['A', 3]1292Quiver on 3 vertices of type ['A', 3]1293Quiver on 3 vertices of type ['A', 3]1294Quiver on 3 vertices of type ['A', 3]12951296sage: it = Q.mutation_class_iter(depth=1)1297sage: for T in it: print T1298Quiver on 3 vertices of type ['A', 3]1299Quiver on 3 vertices of type ['A', 3]1300Quiver on 3 vertices of type ['A', 3]13011302sage: it = Q.mutation_class_iter(show_depth=True)1303sage: for T in it: pass1304Depth: 0 found: 1 Time: ... s1305Depth: 1 found: 3 Time: ... s1306Depth: 2 found: 4 Time: ... s13071308sage: it = Q.mutation_class_iter(return_paths=True)1309sage: for T in it: print T1310(Quiver on 3 vertices of type ['A', 3], [])1311(Quiver on 3 vertices of type ['A', 3], [1])1312(Quiver on 3 vertices of type ['A', 3], [0])1313(Quiver on 3 vertices of type ['A', 3], [0, 1])13141315sage: it = Q.mutation_class_iter(up_to_equivalence=False)1316sage: for T in it: print T1317Quiver on 3 vertices of type ['A', 3]1318Quiver on 3 vertices of type ['A', 3]1319Quiver on 3 vertices of type ['A', 3]1320Quiver on 3 vertices of type ['A', 3]1321Quiver on 3 vertices of type ['A', 3]1322Quiver on 3 vertices of type ['A', 3]1323Quiver on 3 vertices of type ['A', 3]1324Quiver on 3 vertices of type ['A', 3]1325Quiver on 3 vertices of type ['A', 3]1326Quiver on 3 vertices of type ['A', 3]1327Quiver on 3 vertices of type ['A', 3]1328Quiver on 3 vertices of type ['A', 3]1329Quiver on 3 vertices of type ['A', 3]1330Quiver on 3 vertices of type ['A', 3]13311332sage: it = Q.mutation_class_iter(return_paths=True,up_to_equivalence=False)1333sage: for T in it: print T1334(Quiver on 3 vertices of type ['A', 3], [])1335(Quiver on 3 vertices of type ['A', 3], [2])1336(Quiver on 3 vertices of type ['A', 3], [1])1337(Quiver on 3 vertices of type ['A', 3], [0])1338(Quiver on 3 vertices of type ['A', 3], [2, 1])1339(Quiver on 3 vertices of type ['A', 3], [0, 1])1340(Quiver on 3 vertices of type ['A', 3], [0, 1, 2])1341(Quiver on 3 vertices of type ['A', 3], [0, 1, 0])1342(Quiver on 3 vertices of type ['A', 3], [2, 1, 2])1343(Quiver on 3 vertices of type ['A', 3], [2, 1, 0])1344(Quiver on 3 vertices of type ['A', 3], [2, 1, 0, 2])1345(Quiver on 3 vertices of type ['A', 3], [2, 1, 0, 1])1346(Quiver on 3 vertices of type ['A', 3], [2, 1, 2, 1])1347(Quiver on 3 vertices of type ['A', 3], [2, 1, 2, 0])13481349sage: Q = ClusterQuiver(['A',3])1350sage: it = Q.mutation_class_iter(data_type='path')1351sage: for T in it: print T1352[]1353[1]1354[0]1355[0, 1]13561357sage: Q = ClusterQuiver(['A',3])1358sage: it = Q.mutation_class_iter(return_paths=True,data_type='matrix')1359sage: it.next()1360(1361[ 0 0 1]1362[ 0 0 1]1363[-1 -1 0], []1364)13651366"""1367if data_type == 'path':1368return_paths = False1369if data_type == "dig6":1370return_dig6 = True1371else:1372return_dig6 = False1373dg = DiGraph( self._digraph )1374MC_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 )1375for data in MC_iter:1376if data_type == "quiver":1377next_element = ClusterQuiver( data[0], frozen=self._m )1378next_element._mutation_type = self._mutation_type1379elif data_type == "matrix":1380next_element = ClusterQuiver( data[0], frozen=self._m )._M1381elif data_type == "digraph":1382next_element = data[0]1383elif data_type == "dig6":1384next_element = data[0]1385elif data_type == "path":1386next_element = data[1]1387else:1388raise ValueError("The parameter for data_type was not recognized.")1389if return_paths:1390yield ( next_element, data[1] )1391else:1392yield next_element13931394def mutation_class( self, depth=infinity, show_depth=False, return_paths=False, data_type="quiver", up_to_equivalence=True, sink_source=False ):1395"""1396Returns the mutation class of self together with certain constrains.13971398INPUT:13991400- ``depth`` -- (default: infinity) integer, only seeds with distance at most depth from self are returned.1401- ``show_depth`` -- (default: False) if True, the actual depth of the mutation is shown.1402- ``return_paths`` -- (default: False) if True, a shortest path of mutation sequences from self to the given quiver is returned as well.1403- ``data_type`` -- (default: "quiver") can be one of the following::14041405* "quiver" -- the quiver is returned1406* "dig6" -- the dig6-data is returned1407* "path" -- shortest paths of mutation sequences from self are returned14081409- ``sink_source`` -- (default: False) if True, only mutations at sinks and sources are applied.14101411EXAMPLES::14121413sage: Q = ClusterQuiver(['A',3])1414sage: Ts = Q.mutation_class()1415sage: for T in Ts: print T1416Quiver on 3 vertices of type ['A', 3]1417Quiver on 3 vertices of type ['A', 3]1418Quiver on 3 vertices of type ['A', 3]1419Quiver on 3 vertices of type ['A', 3]14201421sage: Ts = Q.mutation_class(depth=1)1422sage: for T in Ts: print T1423Quiver on 3 vertices of type ['A', 3]1424Quiver on 3 vertices of type ['A', 3]1425Quiver on 3 vertices of type ['A', 3]14261427sage: Ts = Q.mutation_class(show_depth=True)1428Depth: 0 found: 1 Time: ... s1429Depth: 1 found: 3 Time: ... s1430Depth: 2 found: 4 Time: ... s14311432sage: Ts = Q.mutation_class(return_paths=True)1433sage: for T in Ts: print T1434(Quiver on 3 vertices of type ['A', 3], [])1435(Quiver on 3 vertices of type ['A', 3], [1])1436(Quiver on 3 vertices of type ['A', 3], [0])1437(Quiver on 3 vertices of type ['A', 3], [0, 1])14381439sage: Ts = Q.mutation_class(up_to_equivalence=False)1440sage: for T in Ts: print T1441Quiver on 3 vertices of type ['A', 3]1442Quiver on 3 vertices of type ['A', 3]1443Quiver on 3 vertices of type ['A', 3]1444Quiver on 3 vertices of type ['A', 3]1445Quiver on 3 vertices of type ['A', 3]1446Quiver on 3 vertices of type ['A', 3]1447Quiver on 3 vertices of type ['A', 3]1448Quiver on 3 vertices of type ['A', 3]1449Quiver on 3 vertices of type ['A', 3]1450Quiver on 3 vertices of type ['A', 3]1451Quiver on 3 vertices of type ['A', 3]1452Quiver on 3 vertices of type ['A', 3]1453Quiver on 3 vertices of type ['A', 3]1454Quiver on 3 vertices of type ['A', 3]14551456sage: Ts = Q.mutation_class(return_paths=True,up_to_equivalence=False)1457sage: for T in Ts: print T1458(Quiver on 3 vertices of type ['A', 3], [])1459(Quiver on 3 vertices of type ['A', 3], [2])1460(Quiver on 3 vertices of type ['A', 3], [1])1461(Quiver on 3 vertices of type ['A', 3], [0])1462(Quiver on 3 vertices of type ['A', 3], [2, 1])1463(Quiver on 3 vertices of type ['A', 3], [0, 1])1464(Quiver on 3 vertices of type ['A', 3], [0, 1, 2])1465(Quiver on 3 vertices of type ['A', 3], [0, 1, 0])1466(Quiver on 3 vertices of type ['A', 3], [2, 1, 2])1467(Quiver on 3 vertices of type ['A', 3], [2, 1, 0])1468(Quiver on 3 vertices of type ['A', 3], [2, 1, 0, 2])1469(Quiver on 3 vertices of type ['A', 3], [2, 1, 0, 1])1470(Quiver on 3 vertices of type ['A', 3], [2, 1, 2, 1])1471(Quiver on 3 vertices of type ['A', 3], [2, 1, 2, 0])14721473sage: Ts = Q.mutation_class(show_depth=True)1474Depth: 0 found: 1 Time: ... s1475Depth: 1 found: 3 Time: ... s1476Depth: 2 found: 4 Time: ... s14771478sage: Ts = Q.mutation_class(show_depth=True, up_to_equivalence=False)1479Depth: 0 found: 1 Time: ... s1480Depth: 1 found: 4 Time: ... s1481Depth: 2 found: 6 Time: ... s1482Depth: 3 found: 10 Time: ... s1483Depth: 4 found: 14 Time: ... s14841485TESTS::14861487sage: all( len(ClusterQuiver(['A',n]).mutation_class()) == ClusterQuiver(['A',n]).mutation_type().class_size() for n in [2..6])1488True14891490sage: all( len(ClusterQuiver(['B',n]).mutation_class()) == ClusterQuiver(['B',n]).mutation_type().class_size() for n in [2..6])1491True1492"""1493if depth is infinity and not self.is_mutation_finite():1494raise ValueError('The mutation class can - for infinite mutation types - only be computed up to a given depth')1495return [ 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 ) ]14961497def is_finite( self ):1498"""1499Returns True if self is of finite type.15001501EXAMPLES::15021503sage: Q = ClusterQuiver(['A',3])1504sage: Q.is_finite()1505True1506sage: Q = ClusterQuiver(['A',[2,2],1])1507sage: Q.is_finite()1508False1509sage: Q = ClusterQuiver([['A',3],['B',3]])1510sage: Q.is_finite()1511True1512sage: Q = ClusterQuiver(['T',[4,4,4]])1513sage: Q.is_finite()1514False1515sage: Q = ClusterQuiver([['A',3],['T',[4,4,4]]])1516sage: Q.is_finite()1517False1518sage: Q = ClusterQuiver([['A',3],['T',[2,2,3]]])1519sage: Q.is_finite()1520True1521sage: Q = ClusterQuiver([['A',3],['D',5]])1522sage: Q.is_finite()1523True1524sage: Q = ClusterQuiver([['A',3],['D',5,1]])1525sage: Q.is_finite()1526False15271528sage: Q = ClusterQuiver([[0,1,2],[1,2,2],[2,0,2]])1529sage: Q.is_finite()1530False15311532sage: Q = ClusterQuiver([[0,1,2],[1,2,2],[2,0,2],[3,4,1],[4,5,1]])1533sage: Q.is_finite()1534False1535"""1536mt = self.mutation_type()1537if type( mt ) in [QuiverMutationType_Irreducible, QuiverMutationType_Reducible] and mt.is_finite():1538return True1539else:1540return False15411542def is_mutation_finite( self, nr_of_checks=None, return_path=False ):1543"""1544Uses a non-deterministic method by random mutations in various directions. Can result in a wrong answer.15451546INPUT:15471548- ``nr_of_checks`` -- (default: None) number of mutations applied. Standard is 500*(number of vertices of self).1549- ``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.15501551ALGORITHM:15521553A quiver is mutation infinite if and only if every edge label (a,-b) satisfy a*b > 4.1554Thus, we apply random mutations in random directions15551556EXAMPLES::15571558sage: Q = ClusterQuiver(['A',10])1559sage: Q._mutation_type = None1560sage: Q.is_mutation_finite()1561True15621563sage: Q = ClusterQuiver([(0,1),(1,2),(2,3),(3,4),(4,5),(5,6),(6,7),(7,8),(2,9)])1564sage: Q.is_mutation_finite()1565False1566"""1567if self._n <= 2:1568is_finite = True1569path = None1570elif not return_path and self._mutation_type == 'undetermined infinite mutation type':1571is_finite = False1572elif type( self._mutation_type ) in [QuiverMutationType_Irreducible, QuiverMutationType_Reducible] and self._mutation_type.is_mutation_finite():1573is_finite = True1574path = None1575elif not return_path and type( self._mutation_type ) in [QuiverMutationType_Irreducible, QuiverMutationType_Reducible] and not self._mutation_type.is_mutation_finite():1576is_finite = False1577else:1578# turning dg_component into a canonical form1579dig6 = _digraph_to_dig6(self.digraph())1580# and getting the corresponding matrix1581M = _dig6_to_matrix(dig6)15821583is_finite, path = is_mutation_finite(M,nr_of_checks=nr_of_checks)1584if return_path:1585return is_finite, path1586else:1587return is_finite158815891590