Path: blob/master/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py
8818 views
r"""1ClusterSeed23A *cluster seed* is a pair `(B,\mathbf{x})` with `B` being a *skew-symmetrizable* `(n+m \times n)` *-matrix*4and with `\mathbf{x}` being an `n`-tuple of *independent elements* in the field of rational functions in `n` variables.56For the compendium on the cluster algebra and quiver package see7:arxiv:`1102.4844`.89AUTHORS:1011- Gregg Musiker12- Christian Stump1314.. seealso:: For mutation types of cluster seeds, see :meth:`sage.combinat.cluster_algebra_quiver.quiver_mutation_type.QuiverMutationType`. Cluster seeds are closely related to :meth:`sage.combinat.cluster_algebra_quiver.quiver.ClusterQuiver`.15"""1617#*****************************************************************************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#*****************************************************************************24import time25from sage.structure.sage_object import SageObject26from copy import copy27from sage.rings.all import QQ, infinity28from sage.rings.all import FractionField, PolynomialRing29from sage.rings.fraction_field_element import FractionFieldElement30from sage.sets.all import Set31from sage.combinat.cluster_algebra_quiver.quiver_mutation_type import QuiverMutationType_Irreducible, QuiverMutationType_Reducible32from sage.combinat.cluster_algebra_quiver.mutation_type import is_mutation_finite3334class ClusterSeed(SageObject):35r"""36The *cluster seed* associated to an *exchange matrix*.3738INPUT:3940- ``data`` -- can be any of the following::4142* QuiverMutationType43* str - a string representing a QuiverMutationType or a common quiver type (see Examples)44* ClusterQuiver45* Matrix - a skew-symmetrizable matrix46* DiGraph - must be the input data for a quiver47* List of edges - must be the edge list of a digraph for a quiver4849EXAMPLES::5051sage: S = ClusterSeed(['A',5]); S52A seed for a cluster algebra of rank 5 of type ['A', 5]5354sage: S = ClusterSeed(['A',[2,5],1]); S55A seed for a cluster algebra of rank 7 of type ['A', [2, 5], 1]5657sage: T = ClusterSeed( S ); T58A seed for a cluster algebra of rank 7 of type ['A', [2, 5], 1]5960sage: T = ClusterSeed( S._M ); T61A seed for a cluster algebra of rank 76263sage: T = ClusterSeed( S.quiver()._digraph ); T64A seed for a cluster algebra of rank 76566sage: T = ClusterSeed( S.quiver()._digraph.edges() ); T67A seed for a cluster algebra of rank 76869sage: S = ClusterSeed(['B',2]); S70A seed for a cluster algebra of rank 2 of type ['B', 2]7172sage: S = ClusterSeed(['C',2]); S73A seed for a cluster algebra of rank 2 of type ['B', 2]7475sage: S = ClusterSeed(['A', [5,0],1]); S76A seed for a cluster algebra of rank 5 of type ['D', 5]7778sage: S = ClusterSeed(['GR',[3,7]]); S79A seed for a cluster algebra of rank 6 of type ['E', 6]8081sage: S = ClusterSeed(['F', 4, [2,1]]); S82A seed for a cluster algebra of rank 6 of type ['F', 4, [1, 2]]83"""84def __init__(self, data, frozen=None, is_principal=None):85r"""86TESTS::8788sage: S = ClusterSeed(['A',4])89sage: TestSuite(S).run()90"""91from quiver import ClusterQuiver9293# constructs a cluster seed from a cluster seed94if type(data) is ClusterSeed:95if frozen:96print "The input \'frozen\' is ignored"97self._M = copy( data._M )98self._cluster = copy(data._cluster)99self._n = data._n100self._m = data._m101self._R = data._R102self._quiver = ClusterQuiver( data._quiver ) if data._quiver else None103self._mutation_type = copy( data._mutation_type )104self._description = copy( data._description )105self._is_principal = data._is_principal106107# constructs a cluster seed from a quiver108elif type(data) is ClusterQuiver:109if frozen:110print "The input \'frozen\' is ignored"111112quiver = ClusterQuiver( data )113self._M = copy(quiver._M)114self._n = quiver._n115self._m = quiver._m116self._quiver = quiver117self._mutation_type = quiver._mutation_type118self._description = 'A seed for a cluster algebra of rank %d' %(self._n)119self._R = FractionField(PolynomialRing(QQ,['x%s'%i for i in range(0,self._n)]+['y%s'%i for i in range(0,self._m)]))120self._cluster = list(self._R.gens())121self._is_principal = None122123# in all other cases, we construct the corresponding ClusterQuiver first124else:125quiver = ClusterQuiver( data, frozen=frozen )126self.__init__( quiver )127128if is_principal != None:129self._is_principal = is_principal130131def __eq__(self, other):132r"""133Returns True iff ``self`` represent the same cluster seed as ``other``.134135EXAMPLES::136137sage: S = ClusterSeed(['A',5])138sage: T = S.mutate( 2, inplace=False )139sage: S.__eq__( T )140False141142sage: T.mutate( 2 )143sage: S.__eq__( T )144True145"""146return type( other ) is ClusterSeed and self._M == other._M and self._cluster == other._cluster147148def _repr_(self):149r"""150Returns the description of ``self``.151152EXAMPLES::153154sage: S = ClusterSeed(['A',5])155sage: S._repr_()156"A seed for a cluster algebra of rank 5 of type ['A', 5]"157158sage: S=ClusterSeed(['B',2])159sage: T=S.principal_extension()160sage: T._repr_()161"A seed for a cluster algebra of rank 2 of type ['B', 2] with principal coefficients"162"""163name = self._description164if self._mutation_type:165if type( self._mutation_type ) in [QuiverMutationType_Irreducible,QuiverMutationType_Reducible]:166name += ' of type ' + str(self._mutation_type)167# the following case allows description of 'undetermined finite mutation type'168else:169name += ' of ' + self._mutation_type170if self._is_principal:171name += ' with principal coefficients'172elif self._m == 1:173name += ' with %s frozen variable'%self._m174elif self._m > 1:175name += ' with %s frozen variables'%self._m176return name177178def plot(self, circular=False, mark=None, save_pos=False):179r"""180Returns the plot of the quiver of ``self``.181182INPUT:183184- ``circular`` -- (default:False) if True, the circular plot is chosen, otherwise >>spring<< is used.185- ``mark`` -- (default: None) if set to i, the vertex i is highlighted.186- ``save_pos`` -- (default:False) if True, the positions of the vertices are saved.187188EXAMPLES::189190sage: S = ClusterSeed(['A',5])191sage: pl = S.plot()192sage: pl = S.plot(circular=True)193"""194return self.quiver().plot(circular=circular,mark=mark,save_pos=save_pos)195196def show(self, fig_size=1, circular=False, mark=None, save_pos=False):197r"""198Shows the plot of the quiver of ``self``.199200INPUT:201202- ``fig_size`` -- (default: 1) factor by which the size of the plot is multiplied.203- ``circular`` -- (default: False) if True, the circular plot is chosen, otherwise >>spring<< is used.204- ``mark`` -- (default: None) if set to i, the vertex i is highlighted.205- ``save_pos`` -- (default:False) if True, the positions of the vertices are saved.206207TESTS::208209sage: S = ClusterSeed(['A',5])210sage: S.show() # long time211"""212self.quiver().show(fig_size=fig_size, circular=circular,mark=mark,save_pos=save_pos)213214def interact(self, fig_size=1, circular=True):215r"""216Only in *notebook mode*. Starts an interactive window for cluster seed mutations.217218INPUT:219220- ``fig_size`` -- (default: 1) factor by which the size of the plot is multiplied.221- ``circular`` -- (default: True) if True, the circular plot is chosen, otherwise >>spring<< is used.222223TESTS::224225sage: S = ClusterSeed(['A',4])226sage: S.interact() # long time227'The interactive mode only runs in the Sage notebook.'228"""229from sage.plot.plot import EMBEDDED_MODE230from sagenb.notebook.interact import interact, selector231from sage.misc.all import html,latex232from sage.all import var233234if not EMBEDDED_MODE:235return "The interactive mode only runs in the Sage notebook."236else:237seq = []238sft = [True]239sss = [True]240ssv = [True]241ssm = [True]242ssl = [True]243@interact244def player(k=selector(values=range(self._n),nrows = 1,label='Mutate at: '), show_seq=("Mutation sequence:", True), show_vars=("Cluster variables:", True), show_matrix=("B-Matrix:", True), show_lastmutation=("Show last mutation:", True) ):245ft,ss,sv,sm,sl = sft.pop(), sss.pop(), ssv.pop(), ssm.pop(), ssl.pop()246if ft:247self.show(fig_size=fig_size, circular=circular)248elif show_seq is not ss or show_vars is not sv or show_matrix is not sm or show_lastmutation is not sl:249if seq and show_lastmutation:250self.show(fig_size=fig_size, circular=circular, mark=seq[len(seq)-1])251else:252self.show(fig_size=fig_size, circular=circular )253else:254self.mutate(k)255seq.append(k)256if not show_lastmutation:257self.show(fig_size=fig_size, circular=circular)258else:259self.show(fig_size=fig_size, circular=circular,mark=k)260sft.append(False)261sss.append(show_seq)262ssv.append(show_vars)263ssm.append(show_matrix)264ssl.append(show_lastmutation)265if show_seq: html( "Mutation sequence: $" + str( [ seq[i] for i in xrange(len(seq)) ] ).strip('[]') + "$" )266if show_vars:267html( "Cluster variables:" )268table = "$\\begin{align*}\n"269for i in xrange(self._n):270v = var('v%s'%i)271table += "\t" + latex( v ) + " &= " + latex( self._cluster[i] ) + "\\\\ \\\\\n"272table += "\\end{align*}$"273html( "$ $" )274html( table )275html( "$ $" )276if show_matrix:277html( "B-Matrix:" )278m = self._M279#m = matrix(range(1,self._n+1),sparse=True).stack(m)280m = latex(m)281m = m.split('(')[1].split('\\right')[0]282html( "$ $" )283html( "$\\begin{align*} " + m + "\\end{align*}$" )284#html( "$" + m + "$" )285html( "$ $" )286287def save_image(self, filename, circular=False, mark=None, save_pos=False):288r"""289Saves the plot of the underlying digraph of the quiver of ``self``.290291INPUT:292293- ``filename`` -- the filename the image is saved to.294- ``circular`` -- (default: False) if True, the circular plot is chosen, otherwise >>spring<< is used.295- ``mark`` -- (default: None) if set to i, the vertex i is highlighted.296- ``save_pos`` -- (default:False) if True, the positions of the vertices are saved.297298EXAMPLES::299300sage: S = ClusterSeed(['F',4,[1,2]])301sage: S.save_image(os.path.join(SAGE_TMP, 'sage.png'))302"""303graph_plot = self.plot( circular=circular, mark=mark, save_pos=save_pos)304graph_plot.save( filename=filename )305306def b_matrix(self):307r"""308Returns the `B` *-matrix* of ``self``.309310EXAMPLES::311312sage: ClusterSeed(['A',4]).b_matrix()313[ 0 1 0 0]314[-1 0 -1 0]315[ 0 1 0 1]316[ 0 0 -1 0]317318sage: ClusterSeed(['B',4]).b_matrix()319[ 0 1 0 0]320[-1 0 -1 0]321[ 0 1 0 1]322[ 0 0 -2 0]323324sage: ClusterSeed(['D',4]).b_matrix()325[ 0 1 0 0]326[-1 0 -1 -1]327[ 0 1 0 0]328[ 0 1 0 0]329330sage: ClusterSeed(QuiverMutationType([['A',2],['B',2]])).b_matrix()331[ 0 1 0 0]332[-1 0 0 0]333[ 0 0 0 1]334[ 0 0 -2 0]335"""336return copy( self._M )337338def ground_field(self):339r"""340Returns the *ground field* of the cluster of ``self``.341342EXAMPLES::343344sage: S = ClusterSeed(['A',3])345sage: S.ground_field()346Fraction Field of Multivariate Polynomial Ring in x0, x1, x2 over Rational Field347"""348return self._R349350def x(self,k):351r"""352Returns the `k` *-th initial cluster variable* for the associated cluster seed.353354EXAMPLES::355356sage: S = ClusterSeed(['A',3])357sage: S.mutate([2,1])358sage: S.x(0)359x0360361sage: S.x(1)362x1363364sage: S.x(2)365x2366"""367if k in range(self._n):368x = self._R.gens()[k]369return ClusterVariable( x.parent(), x.numerator(), x.denominator(), mutation_type=self._mutation_type, variable_type='cluster variable' )370else:371raise ValueError("The input is not in an index of a cluster variable.")372373def y(self,k):374r"""375Returns the `k` *-th initial coefficient (frozen variable)* for the associated cluster seed.376377EXAMPLES::378379sage: S = ClusterSeed(['A',3]).principal_extension()380sage: S.mutate([2,1])381sage: S.y(0)382y0383384sage: S.y(1)385y1386387sage: S.y(2)388y2389"""390if k in range(self._m):391x = self._R.gens()[self._n+k]392return ClusterVariable( x.parent(), x.numerator(), x.denominator(), mutation_type=self._mutation_type, variable_type='frozen variable' )393else:394raise ValueError("The input is not in an index of a frozen variable.")395396def n(self):397r"""398Returns the number of *exchangeable variables* of ``self``.399400EXAMPLES::401402sage: S = ClusterSeed(['A',3])403sage: S.n()4043405"""406return self._n407408def m(self):409r"""410Returns the number of *frozen variables* of ``self``.411412EXAMPLES::413414sage: S = ClusterSeed(['A',3])415sage: S.n()4163417418sage: S.m()4190420421sage: S = S.principal_extension()422sage: S.m()4233424"""425return self._m426427def cluster_variable(self,k):428r"""429Returns the `k`-th *cluster variable* of ``self``.430431EXAMPLES::432433sage: S = ClusterSeed(['A',3])434sage: S.mutate([1,2])435436sage: [S.cluster_variable(k) for k in range(3)]437[x0, (x0*x2 + 1)/x1, (x0*x2 + x1 + 1)/(x1*x2)]438"""439if k not in range(self._n):440raise ValueError("The cluster seed does not have a cluster variable of index %s."%k)441f = self._cluster[k]442return ClusterVariable( f.parent(), f.numerator(), f.denominator(), mutation_type=self._mutation_type, variable_type='cluster variable' )443444def cluster(self):445r"""446Returns the *cluster* of ``self``.447448EXAMPLES::449450sage: S = ClusterSeed(['A',3])451sage: S.cluster()452[x0, x1, x2]453454sage: S.mutate(1)455sage: S.cluster()456[x0, (x0*x2 + 1)/x1, x2]457458sage: S.mutate(2)459sage: S.cluster()460[x0, (x0*x2 + 1)/x1, (x0*x2 + x1 + 1)/(x1*x2)]461462sage: S.mutate([2,1])463sage: S.cluster()464[x0, x1, x2]465"""466return [ self.cluster_variable(k) for k in range(self._n) ]467468def f_polynomial(self,k,ignore_coefficients=False):469r"""470Returns the ``k``-th *F-polynomial* of ``self``. It is obtained from the471``k``-th cluster variable by setting all `x_i` to `1`.472473Requires principal coefficients, initialized by using principal_extension(),474or the user can set 'ignore_coefficients=True' to bypass this restriction.475476Warning: this method assumes the sign-coherence conjecture and that the477input seed is sign-coherent (has an exchange matrix with columns of like signs).478Otherwise, computational errors might arise.479480EXAMPLES::481482sage: S = ClusterSeed(['A',3]).principal_extension()483sage: S.mutate([2,1,2])484sage: [S.f_polynomial(k) for k in range(3)]485[1, y1*y2 + y2 + 1, y1 + 1]486487sage: S = ClusterSeed(Matrix([[0,1],[-1,0],[1,0],[-1,1]])); S488A seed for a cluster algebra of rank 2 with 2 frozen variables489sage: T = ClusterSeed(Matrix([[0,1],[-1,0]])).principal_extension(); T490A seed for a cluster algebra of rank 2 with principal coefficients491sage: S.mutate(0)492sage: T.mutate(0)493sage: S.f_polynomials()494Traceback (most recent call last):495...496ValueError: No principal coefficients initialized. Use principal_extension, or ignore_coefficients to ignore this.497sage: S.f_polynomials(ignore_coefficients=True)498[y0 + y1, 1]499sage: T.f_polynomials()500[y0 + 1, 1]501"""502if not (ignore_coefficients or self._is_principal):503raise ValueError("No principal coefficients initialized. Use principal_extension, or ignore_coefficients to ignore this.")504505if k not in range(self._n):506raise ValueError("The cluster seed does not have a cluster variable of index %s."%k)507eval_dict = dict( [ ( self.x(i), 1 ) for i in range(self._n) ] )508return self.cluster_variable(k).subs(eval_dict)509510def f_polynomials(self,ignore_coefficients=False):511r"""512Returns all *F-polynomials* of ``self``. These are obtained from the513cluster variables by setting all `x_i`'s to `1`.514515Requires principal coefficients, initialized by using principal_extension(),516or the user can set 'ignore_coefficients=True' to bypass this restriction.517518Warning: this method assumes the sign-coherence conjecture and that the519input seed is sign-coherent (has an exchange matrix with columns of like signs).520Otherwise, computational errors might arise.521522EXAMPLES::523524sage: S = ClusterSeed(['A',3]).principal_extension()525sage: S.mutate([2,1,2])526sage: S.f_polynomials()527[1, y1*y2 + y2 + 1, y1 + 1]528"""529if not (ignore_coefficients or self._is_principal):530raise ValueError("No principal coefficients initialized. Use principal_extension, or ignore_coefficients to ignore this.")531532return [ self.f_polynomial(k,ignore_coefficients=ignore_coefficients) for k in range(self._n) ]533534def g_vector(self,k,ignore_coefficients=False):535r"""536Returns the ``k``-th *g-vector* of ``self``. This is the degree vector537of the ``k``-th cluster variable after setting all `y_i`'s to `0`.538539Requires principal coefficients, initialized by using principal_extension(),540or the user can set 'ignore_coefficients=True' to bypass this restriction.541542Warning: this method assumes the sign-coherence conjecture and that the543input seed is sign-coherent (has an exchange matrix with columns of like signs).544Otherwise, computational errors might arise.545546EXAMPLES::547548sage: S = ClusterSeed(['A',3]).principal_extension()549sage: S.mutate([2,1,2])550sage: [ S.g_vector(k) for k in range(3) ]551[(1, 0, 0), (0, 0, -1), (0, -1, 0)]552"""553if not (ignore_coefficients or self._is_principal):554raise ValueError("No principal coefficients initialized. Use principal_extension, or ignore_coefficients to ignore this.")555if k not in range(self._n):556raise ValueError("The cluster seed does not have a cluster variable of index %s."%k)557f = self.cluster_variable(k)558eval_dict = dict( [ ( self.y(i), 0 ) for i in range(self._m) ] )559f0 = f.subs(eval_dict)560d1 = f0.numerator().degrees()561d2 = f0.denominator().degrees()562return tuple( d1[i] - d2[i] for i in range(self._n) )563564def g_matrix(self,ignore_coefficients=False):565r"""566Returns the matrix of all *g-vectors* of ``self``. This are the degree vectors567of the cluster variables after setting all `y_i`'s to `0`.568569Requires principal coefficients, initialized by using principal_extension(),570or the user can set 'ignore_coefficients=True' to bypass this restriction.571572Warning: this method assumes the sign-coherence conjecture and that the573input seed is sign-coherent (has an exchange matrix with columns of like signs).574Otherwise, computational errors might arise.575576EXAMPLES::577578sage: S = ClusterSeed(['A',3]).principal_extension()579sage: S.mutate([2,1,2])580sage: S.g_matrix()581[ 1 0 0]582[ 0 0 -1]583[ 0 -1 0]584585sage: S = ClusterSeed(['A',3])586sage: S2 = S.principal_extension()587sage: S.mutate([0,1])588sage: S2.mutate([0,1])589sage: S.g_matrix()590Traceback (most recent call last):591...592ValueError: No principal coefficients initialized. Use593principal_extension, or ignore_coefficients to ignore this.594sage: S.g_matrix(ignore_coefficients=True)595[-1 0 0]596[ 1 0 0]597[ 0 1 1]598sage: S2.g_matrix()599[-1 -1 0]600[ 1 0 0]601[ 0 0 1]602"""603from sage.matrix.all import matrix604if not (ignore_coefficients or self._is_principal):605raise ValueError("No principal coefficients initialized. Use principal_extension, or ignore_coefficients to ignore this.")606return matrix( [ self.g_vector(k,ignore_coefficients=ignore_coefficients) for k in range(self._n) ] ).transpose()607608def c_vector(self,k,ignore_coefficients=False):609r"""610Returns the ``k``-th *c-vector* of ``self``. It is obtained as the611``k``-th column vector of the bottom part of the ``B``-matrix of ``self``.612613Requires principal coefficients, initialized by using principal_extension(),614or the user can set 'ignore_coefficients=True' to bypass this restriction.615616Warning: this method assumes the sign-coherence conjecture and that the617input seed is sign-coherent (has an exchange matrix with columns of like signs).618Otherwise, computational errors might arise.619620EXAMPLES::621622sage: S = ClusterSeed(['A',3]).principal_extension()623sage: S.mutate([2,1,2])624sage: [ S.c_vector(k) for k in range(3) ]625[(1, 0, 0), (0, 0, -1), (0, -1, 0)]626627sage: S = ClusterSeed(Matrix([[0,1],[-1,0],[1,0],[-1,1]])); S628A seed for a cluster algebra of rank 2 with 2 frozen variables629sage: S.c_vector(0)630Traceback (most recent call last):631...632ValueError: No principal coefficients initialized. Use principal_extension, or ignore_coefficients to ignore this.633sage: S.c_vector(0,ignore_coefficients=True)634(1, -1)635"""636if k not in range(self._n):637raise ValueError("The cluster seed does not have a c-vector of index %s."%k)638if not (ignore_coefficients or self._is_principal):639raise ValueError("No principal coefficients initialized. Use principal_extension, or ignore_coefficients to ignore this.")640return tuple( self._M[i,k] for i in range(self._n,self._n+self._m) )641642def c_matrix(self,ignore_coefficients=False):643r"""644Returns all *c-vectors* of ``self``.645646Requires principal coefficients, initialized by using principal_extension(),647or the user can set 'ignore_coefficients=True' to bypass this restriction.648649Warning: this method assumes the sign-coherence conjecture and that the650input seed is sign-coherent (has an exchange matrix with columns of like signs).651Otherwise, computational errors might arise.652653EXAMPLES::654655sage: S = ClusterSeed(['A',3]).principal_extension()656sage: S.mutate([2,1,2])657sage: S.c_matrix()658[ 1 0 0]659[ 0 0 -1]660[ 0 -1 0]661"""662if not (ignore_coefficients or self._is_principal):663raise ValueError("No principal coefficients initialized. Use principal_extension, or ignore_coefficients to ignore this.")664665return self._M.submatrix(self._n,0)666667def coefficient(self,k):668r"""669Returns the *coefficient* of ``self`` at index ``k``.670671EXAMPLES::672673sage: S = ClusterSeed(['A',3]).principal_extension()674sage: S.mutate([2,1,2])675sage: [ S.coefficient(k) for k in range(3) ]676[y0, 1/y2, 1/y1]677"""678from sage.misc.all import prod679if k not in range(self._n):680raise ValueError("The cluster seed does not have a coefficient of index %s."%k)681if self._m == 0:682return self.x(0)**0683#### Note: this special case m = 0 no longer needed except if we want type(answer) to be a cluster variable rather than an integer.684else:685exp = self.c_vector(k,ignore_coefficients=True)686return prod( self.y(i)**exp[i] for i in xrange(self._m) )687688def coefficients(self):689r"""690Returns all *coefficients* of ``self``.691692EXAMPLES::693694sage: S = ClusterSeed(['A',3]).principal_extension()695sage: S.mutate([2,1,2])696sage: S.coefficients()697[y0, 1/y2, 1/y1]698"""699return [ self.coefficient(k) for k in range(self._n) ]700701def quiver(self):702r"""703Returns the *quiver* associated to ``self``.704705EXAMPLES::706707sage: S = ClusterSeed(['A',3])708sage: S.quiver()709Quiver on 3 vertices of type ['A', 3]710"""711from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver712if self._quiver is None:713self._quiver = ClusterQuiver( self._M )714return self._quiver715716def is_acyclic(self):717r"""718Returns True iff self is acyclic (i.e., if the underlying quiver is acyclic).719720EXAMPLES::721722sage: ClusterSeed(['A',4]).is_acyclic()723True724725sage: ClusterSeed(['A',[2,1],1]).is_acyclic()726True727728sage: ClusterSeed([[0,1],[1,2],[2,0]]).is_acyclic()729False730"""731return self.quiver()._digraph.is_directed_acyclic()732733def is_bipartite(self,return_bipartition=False):734r"""735Returns True iff self is bipartite (i.e., if the underlying quiver is bipartite).736737INPUT:738739- return_bipartition -- (default:False) if True, the bipartition is returned in the case of ``self`` being bipartite.740741EXAMPLES::742743sage: ClusterSeed(['A',[3,3],1]).is_bipartite()744True745746sage: ClusterSeed(['A',[4,3],1]).is_bipartite()747False748"""749return self.quiver().is_bipartite(return_bipartition=return_bipartition)750751def mutate(self, sequence, inplace=True):752r"""753Mutates ``self`` at a vertex or a sequence of vertices.754755INPUT:756757- ``sequence`` -- a vertex of self or an iterator of vertices of self.758- ``inplace`` -- (default: True) if False, the result is returned, otherwise ``self`` is modified.759760EXAMPLES::761762sage: S = ClusterSeed(['A',4]); S.b_matrix()763[ 0 1 0 0]764[-1 0 -1 0]765[ 0 1 0 1]766[ 0 0 -1 0]767768sage: S.mutate(0); S.b_matrix()769[ 0 -1 0 0]770[ 1 0 -1 0]771[ 0 1 0 1]772[ 0 0 -1 0]773774sage: T = S.mutate(0, inplace=False); T775A seed for a cluster algebra of rank 4 of type ['A', 4]776777sage: S.mutate(0)778sage: S == T779True780781sage: S.mutate([0,1,0])782sage: S.b_matrix()783[ 0 -1 1 0]784[ 1 0 0 0]785[-1 0 0 1]786[ 0 0 -1 0]787788sage: S = ClusterSeed(QuiverMutationType([['A',1],['A',3]]))789sage: S.b_matrix()790[ 0 0 0 0]791[ 0 0 1 0]792[ 0 -1 0 -1]793[ 0 0 1 0]794795sage: T = S.mutate(0,inplace=False)796sage: S == T797False798"""799if inplace:800seed = self801else:802seed = ClusterSeed( self )803804n, m = seed._n, seed._m805V = range(n)806807if sequence in V:808seq = [sequence]809else:810seq = sequence811if type( seq ) is tuple:812seq = list( seq )813if not type( seq ) is list:814raise ValueError('The quiver can only be mutated at a vertex or at a sequence of vertices')815if not type(inplace) is bool:816raise ValueError('The second parameter must be boolean. To mutate at a sequence of length 2, input it as a list.')817if any( v not in V for v in seq ):818v = filter( lambda v: v not in V, seq )[0]819raise ValueError('The quiver cannot be mutated at the vertex ' + str( v ))820821for k in seq:822M = seed._M823cluster = seed._cluster824mon_p = seed._R(1)825mon_n = seed._R(1)826827for j in range(n+m):828if M[j,k] > 0:829mon_p = mon_p*cluster[j]**M[j,k]830elif M[j,k] < 0:831mon_n = mon_n*cluster[j]**(-M[j,k])832833cluster[k] = (mon_p+mon_n)*cluster[k]**(-1)834seed._M.mutate(k)835#seed._M = _matrix_mutate( seed._M, k )836837seed._quiver = None838if not inplace:839return seed840841def mutation_sequence(self, sequence, show_sequence=False, fig_size=1.2,return_output='seed'):842r"""843Returns the seeds obtained by mutating ``self`` at all vertices in ``sequence``.844845INPUT:846847- ``sequence`` -- an iterable of vertices of self.848- ``show_sequence`` -- (default: False) if True, a png containing the associated quivers is shown.849- ``fig_size`` -- (default: 1.2) factor by which the size of the plot is multiplied.850- ``return_output`` -- (default: 'seed') determines what output is to be returned::851852* if 'seed', outputs all the cluster seeds obtained by the ``sequence`` of mutations.853* if 'matrix', outputs a list of exchange matrices.854* if 'var', outputs a list of new cluster variables obtained at each step.855856EXAMPLES::857858sage: S = ClusterSeed(['A',2])859sage: for T in S.mutation_sequence([0,1,0]):860... print T.b_matrix()861[ 0 -1]862[ 1 0]863[ 0 1]864[-1 0]865[ 0 -1]866[ 1 0]867868sage: S=ClusterSeed(['A',2])869sage: S.mutation_sequence([0,1,0,1],return_output='var')870[(x1 + 1)/x0, (x0 + x1 + 1)/(x0*x1), (x0 + 1)/x1, x0]871"""872seed = ClusterSeed( self )873874new_clust_var = []875seed_sequence = []876877for v in sequence:878seed = seed.mutate(v,inplace=False)879new_clust_var.append( seed._cluster[v])880seed_sequence.append( seed )881882if show_sequence:883self.quiver().mutation_sequence(sequence=sequence, show_sequence=True, fig_size=fig_size )884885if return_output=='seed':886return seed_sequence887elif return_output=='matrix':888return [ seed._M for seed in seed_sequence ]889elif return_output=='var':890return new_clust_var891else:892raise ValueError('The parameter `return_output` can only be `seed`, `matrix`, or `var`.')893894def exchangeable_part(self):895r"""896Returns the restriction to the principal part (i.e. the exchangeable variables) of ``self``.897898EXAMPLES::899900sage: S = ClusterSeed(['A',4])901sage: T = ClusterSeed( S.quiver().digraph().edges(), frozen=1 )902sage: T.quiver().digraph().edges()903[(0, 1, (1, -1)), (2, 1, (1, -1)), (2, 3, (1, -1))]904905sage: T.exchangeable_part().quiver().digraph().edges()906[(0, 1, (1, -1)), (2, 1, (1, -1))]907908sage: S2 = S.principal_extension()909sage: S3 = S2.principal_extension(ignore_coefficients=True)910sage: S2.exchangeable_part() == S3.exchangeable_part()911True912"""913from sage.combinat.cluster_algebra_quiver.mutation_class import _principal_part914eval_dict = dict( [ ( self.y(i), 1 ) for i in xrange(self._m) ] )915seed = ClusterSeed( _principal_part( self._M ) )916seed._cluster = [ self._cluster[k].subs(eval_dict) for k in xrange(self._n) ]917seed._mutation_type = self._mutation_type918return seed919920def principal_extension(self,ignore_coefficients=False):921r"""922Returns the principal extension of self, yielding a 2n-by-n matrix. Raises an error if the input seed has a non-square exchange matrix,923unless 'ignore_coefficients=True' is set. In this case, the method instead adds n frozen variables to any previously frozen variables.924I.e., the seed obtained by adding a frozen variable to every exchangeable variable of ``self``.925926EXAMPLES::927928sage: S = ClusterSeed([[0,1],[1,2],[2,3],[2,4]]); S929A seed for a cluster algebra of rank 5930931sage: T = S.principal_extension(); T932A seed for a cluster algebra of rank 5 with principal coefficients933934sage: T.b_matrix()935[ 0 1 0 0 0]936[-1 0 1 0 0]937[ 0 -1 0 1 1]938[ 0 0 -1 0 0]939[ 0 0 -1 0 0]940[ 1 0 0 0 0]941[ 0 1 0 0 0]942[ 0 0 1 0 0]943[ 0 0 0 1 0]944[ 0 0 0 0 1]945946sage: T2 = T.principal_extension()947Traceback (most recent call last):948...949ValueError: The b-matrix is not square. Use ignore_coefficients to ignore this.950951sage: T2 = T.principal_extension(ignore_coefficients=True); T2.b_matrix()952[ 0 1 0 0 0]953[-1 0 1 0 0]954[ 0 -1 0 1 1]955[ 0 0 -1 0 0]956[ 0 0 -1 0 0]957[ 1 0 0 0 0]958[ 0 1 0 0 0]959[ 0 0 1 0 0]960[ 0 0 0 1 0]961[ 0 0 0 0 1]962[ 1 0 0 0 0]963[ 0 1 0 0 0]964[ 0 0 1 0 0]965[ 0 0 0 1 0]966[ 0 0 0 0 1]967"""968from sage.matrix.all import identity_matrix969if not ignore_coefficients and self._m != 0:970raise ValueError("The b-matrix is not square. Use ignore_coefficients to ignore this.")971M = self._M.stack(identity_matrix(self._n))972is_principal = (self._m == 0)973seed = ClusterSeed( M, is_principal=is_principal )974seed._mutation_type = self._mutation_type975return seed976977def reorient( self, data ):978r"""979Reorients ``self`` with respect to the given total order,980or with respect to an iterator of ordered pairs.981982WARNING:983984- This operation might change the mutation type of ``self``.985- Ignores ordered pairs `(i,j)` for which neither `(i,j)` nor `(j,i)` is an edge of ``self``.986987INPUT:988989- ``data`` -- an iterator defining a total order on ``self.vertices()``, or an iterator of ordered pairs in ``self`` defining the new orientation of these edges.990991EXAMPLES::992993sage: S = ClusterSeed(['A',[2,3],1])994sage: S.mutation_type()995['A', [2, 3], 1]996997sage: S.reorient([(0,1),(2,3)])998sage: S.mutation_type()999['D', 5]10001001sage: S.reorient([(1,0),(2,3)])1002sage: S.mutation_type()1003['A', [1, 4], 1]10041005sage: S.reorient([0,1,2,3,4])1006sage: S.mutation_type()1007['A', [1, 4], 1]1008"""1009if not self._quiver:1010self.quiver()1011self._quiver.reorient( data )1012self._M = self._quiver._M1013self.reset_cluster()1014self._mutation_type = None10151016def set_cluster( self, cluster ):1017r"""1018Sets the cluster for ``self`` to ``cluster``.10191020INPUT:10211022- ``cluster`` -- an iterable defining a cluster for ``self``.10231024EXAMPLES::10251026sage: S = ClusterSeed(['A',3])1027sage: cluster = S.cluster()1028sage: S.mutate([1,2,1])1029sage: S.cluster()1030[x0, (x1 + 1)/x2, (x0*x2 + x1 + 1)/(x1*x2)]10311032sage: S.set_cluster(cluster)1033sage: S.cluster()1034[x0, x1, x2]1035"""1036if not len(cluster) == self._n+self._m:1037raise ValueError('The number of given cluster variables is wrong')1038if any(c not in self._R for c in cluster):1039raise ValueError('The cluster variables are not all contained in %s'%self._R)1040self._cluster = [ self._R(x) for x in cluster ]1041self._is_principal = None10421043def reset_cluster( self ):1044r"""1045Resets the cluster of ``self`` to the initial cluster.10461047EXAMPLES::10481049sage: S = ClusterSeed(['A',3])1050sage: S.mutate([1,2,1])1051sage: S.cluster()1052[x0, (x1 + 1)/x2, (x0*x2 + x1 + 1)/(x1*x2)]10531054sage: S.reset_cluster()1055sage: S.cluster()1056[x0, x1, x2]10571058sage: T = S.principal_extension()1059sage: T.cluster()1060[x0, x1, x2]1061sage: T.mutate([1,2,1])1062sage: T.cluster()1063[x0, (x1*y2 + x0)/x2, (x1*y1*y2 + x0*y1 + x2)/(x1*x2)]10641065sage: T.reset_cluster()1066sage: T.cluster()1067[x0, x1, x2]1068"""1069self.set_cluster(self._R.gens())10701071def reset_coefficients( self ):1072r"""1073Resets the coefficients of ``self`` to the frozen variables but keeps the current cluster.1074Raises an error if the number of frozen variables is different than the number of exchangeable variables.10751076EXAMPLES::10771078sage: S = ClusterSeed(['A',3]).principal_extension()1079sage: S.b_matrix()1080[ 0 1 0]1081[-1 0 -1]1082[ 0 1 0]1083[ 1 0 0]1084[ 0 1 0]1085[ 0 0 1]1086sage: S.mutate([1,2,1])1087sage: S.b_matrix()1088[ 0 1 -1]1089[-1 0 1]1090[ 1 -1 0]1091[ 1 0 0]1092[ 0 1 -1]1093[ 0 0 -1]1094sage: S.reset_coefficients()1095sage: S.b_matrix()1096[ 0 1 -1]1097[-1 0 1]1098[ 1 -1 0]1099[ 1 0 0]1100[ 0 1 0]1101[ 0 0 1]1102"""1103n,m = self._n, self._m1104if not n == m:1105raise ValueError("The numbers of cluster variables and of frozen variables do not coincide.")1106for i in xrange(m):1107for j in xrange(n):1108if i == j:1109self._M[i+n,j] = 11110else:1111self._M[i+n,j] = 01112self._quiver = None1113self._is_principal = None11141115def mutation_class_iter( self, depth=infinity, show_depth=False, return_paths=False, up_to_equivalence=True, only_sink_source=False ):1116r"""1117Returns an iterator for the mutation class of ``self`` with respect to certain constrains.11181119INPUT:11201121- ``depth`` -- (default: infinity) integer or infinity, only seeds with distance at most ``depth`` from ``self`` are returned.1122- ``show_depth`` -- (default: False) if True, the current depth of the mutation is shown while computing.1123- ``return_paths`` -- (default: False) if True, a shortest path of mutations from ``self`` to the given quiver is returned as well.1124- ``up_to_equivalence`` -- (default: True) if True, only one seed up to simultaneous permutation of rows and columns of the exchange matrix is recorded.1125- ``sink_source`` -- (default: False) if True, only mutations at sinks and sources are applied.11261127EXAMPLES:11281129A standard finite type example::11301131sage: S = ClusterSeed(['A',3])1132sage: it = S.mutation_class_iter()1133sage: for T in it: print T1134A seed for a cluster algebra of rank 3 of type ['A', 3]1135A seed for a cluster algebra of rank 3 of type ['A', 3]1136A seed for a cluster algebra of rank 3 of type ['A', 3]1137A seed for a cluster algebra of rank 3 of type ['A', 3]1138A seed for a cluster algebra of rank 3 of type ['A', 3]1139A seed for a cluster algebra of rank 3 of type ['A', 3]1140A seed for a cluster algebra of rank 3 of type ['A', 3]1141A seed for a cluster algebra of rank 3 of type ['A', 3]1142A seed for a cluster algebra of rank 3 of type ['A', 3]1143A seed for a cluster algebra of rank 3 of type ['A', 3]1144A seed for a cluster algebra of rank 3 of type ['A', 3]1145A seed for a cluster algebra of rank 3 of type ['A', 3]1146A seed for a cluster algebra of rank 3 of type ['A', 3]1147A seed for a cluster algebra of rank 3 of type ['A', 3]11481149A finite type example with given depth::11501151sage: it = S.mutation_class_iter(depth=1)1152sage: for T in it: print T1153A seed for a cluster algebra of rank 3 of type ['A', 3]1154A seed for a cluster algebra of rank 3 of type ['A', 3]1155A seed for a cluster algebra of rank 3 of type ['A', 3]1156A seed for a cluster algebra of rank 3 of type ['A', 3]11571158A finite type example where the depth is shown while computing::11591160sage: it = S.mutation_class_iter(show_depth=True)1161sage: for T in it: pass1162Depth: 0 found: 1 Time: ... s1163Depth: 1 found: 4 Time: ... s1164Depth: 2 found: 9 Time: ... s1165Depth: 3 found: 13 Time: ... s1166Depth: 4 found: 14 Time: ... s11671168A finite type example with shortest paths returned::11691170sage: it = S.mutation_class_iter(return_paths=True)1171sage: for T in it: print T1172(A seed for a cluster algebra of rank 3 of type ['A', 3], [])1173(A seed for a cluster algebra of rank 3 of type ['A', 3], [2])1174(A seed for a cluster algebra of rank 3 of type ['A', 3], [1])1175(A seed for a cluster algebra of rank 3 of type ['A', 3], [0])1176(A seed for a cluster algebra of rank 3 of type ['A', 3], [2, 1])1177(A seed for a cluster algebra of rank 3 of type ['A', 3], [0, 2])1178(A seed for a cluster algebra of rank 3 of type ['A', 3], [0, 1])1179(A seed for a cluster algebra of rank 3 of type ['A', 3], [1, 2])1180(A seed for a cluster algebra of rank 3 of type ['A', 3], [1, 0])1181(A seed for a cluster algebra of rank 3 of type ['A', 3], [0, 2, 1])1182(A seed for a cluster algebra of rank 3 of type ['A', 3], [0, 1, 2])1183(A seed for a cluster algebra of rank 3 of type ['A', 3], [2, 1, 0])1184(A seed for a cluster algebra of rank 3 of type ['A', 3], [1, 0, 2])1185(A seed for a cluster algebra of rank 3 of type ['A', 3], [0, 1, 2, 0])11861187Finite type examples not considered up to equivalence::11881189sage: it = S.mutation_class_iter(up_to_equivalence=False)1190sage: len( [ T for T in it ] )11918411921193sage: it = ClusterSeed(['A',2]).mutation_class_iter(return_paths=True,up_to_equivalence=False)1194sage: for T in it: print T1195(A seed for a cluster algebra of rank 2 of type ['A', 2], [])1196(A seed for a cluster algebra of rank 2 of type ['A', 2], [1])1197(A seed for a cluster algebra of rank 2 of type ['A', 2], [0])1198(A seed for a cluster algebra of rank 2 of type ['A', 2], [0, 1])1199(A seed for a cluster algebra of rank 2 of type ['A', 2], [1, 0])1200(A seed for a cluster algebra of rank 2 of type ['A', 2], [1, 0, 1])1201(A seed for a cluster algebra of rank 2 of type ['A', 2], [0, 1, 0])1202(A seed for a cluster algebra of rank 2 of type ['A', 2], [1, 0, 1, 0])1203(A seed for a cluster algebra of rank 2 of type ['A', 2], [0, 1, 0, 1])1204(A seed for a cluster algebra of rank 2 of type ['A', 2], [1, 0, 1, 0, 1])12051206Check that :trac:`14638` is fixed::12071208sage: S = ClusterSeed(['E',6])1209sage: MC = S.mutation_class(depth=7); len(MC)121053412111212Infinite type examples::12131214sage: S = ClusterSeed(['A',[1,1],1])1215sage: it = S.mutation_class_iter()1216sage: it.next()1217A seed for a cluster algebra of rank 2 of type ['A', [1, 1], 1]1218sage: it.next()1219A seed for a cluster algebra of rank 2 of type ['A', [1, 1], 1]1220sage: it.next()1221A seed for a cluster algebra of rank 2 of type ['A', [1, 1], 1]1222sage: it.next()1223A seed for a cluster algebra of rank 2 of type ['A', [1, 1], 1]12241225sage: it = S.mutation_class_iter(depth=3, return_paths=True)1226sage: for T in it: print T1227(A seed for a cluster algebra of rank 2 of type ['A', [1, 1], 1], [])1228(A seed for a cluster algebra of rank 2 of type ['A', [1, 1], 1], [1])1229(A seed for a cluster algebra of rank 2 of type ['A', [1, 1], 1], [0])1230(A seed for a cluster algebra of rank 2 of type ['A', [1, 1], 1], [1, 0])1231(A seed for a cluster algebra of rank 2 of type ['A', [1, 1], 1], [0, 1])1232(A seed for a cluster algebra of rank 2 of type ['A', [1, 1], 1], [1, 0, 1])1233(A seed for a cluster algebra of rank 2 of type ['A', [1, 1], 1], [0, 1, 0])1234"""1235depth_counter = 01236n = self._n1237timer = time.time()1238if return_paths:1239yield (self,[])1240else:1241yield self1242if up_to_equivalence:1243cl = Set( self._cluster )1244else:1245cl = tuple( self._cluster )1246clusters = {}1247clusters[ cl ] = [ self, range(n), [] ]1248gets_bigger = True1249if show_depth:1250timer2 = time.time()1251dc = str(depth_counter)1252dc += ' ' * (5-len(dc))1253nr = str(len(clusters))1254nr += ' ' * (10-len(nr))1255print "Depth: %s found: %s Time: %.2f s"%(dc,nr,timer2-timer)1256while gets_bigger and depth_counter < depth:1257gets_bigger = False1258keys = clusters.keys()1259for key in keys:1260sd = clusters[key]1261while sd[1]:1262i = sd[1].pop()1263if not only_sink_source or all( entry >= 0 for entry in sd[0]._M.row( i ) ) or all( entry <= 0 for entry in sd[0]._M.row( i ) ):1264sd2 = sd[0].mutate( i, inplace=False )1265if up_to_equivalence:1266cl2 = Set(sd2._cluster)1267else:1268cl2 = tuple(sd2._cluster)1269if cl2 in clusters:1270if not up_to_equivalence and i in clusters[cl2][1]:1271clusters[cl2][1].remove(i)1272else:1273gets_bigger = True1274if only_sink_source:1275orbits = range(n)1276else:1277orbits = [ index for index in xrange(n) if index > i or sd2._M[index,i] != 0 ]12781279clusters[ cl2 ] = [ sd2, orbits, clusters[key][2]+[i] ]1280if return_paths:1281yield (sd2,clusters[cl2][2])1282else:1283yield sd21284depth_counter += 11285if show_depth and gets_bigger:1286timer2 = time.time()1287dc = str(depth_counter)1288dc += ' ' * (5-len(dc))1289nr = str(len(clusters))1290nr += ' ' * (10-len(nr))1291print "Depth: %s found: %s Time: %.2f s"%(dc,nr,timer2-timer)12921293def mutation_class( self, depth=infinity, show_depth=False, return_paths=False, up_to_equivalence=True, only_sink_source=False ):1294r"""1295Returns the mutation class of ``self`` with respect to certain constraints.12961297INPUT:12981299- ``depth`` -- (default: infinity) integer, only seeds with distance at most depth from self are returned.1300- ``show_depth`` -- (default: False) if True, the actual depth of the mutation is shown.1301- ``return_paths`` -- (default: False) if True, a shortest path of mutation sequences from self to the given quiver is returned as well.1302- ``up_to_equivalence`` -- (default: True) if True, only seeds up to equivalence are considered.1303- ``sink_source`` -- (default: False) if True, only mutations at sinks and sources are applied.13041305EXAMPLES:13061307- for examples see :meth:`mutation_class_iter`13081309TESTS::13101311sage: A = ClusterSeed(['A',3]).mutation_class()1312"""1313if depth is infinity and not self.is_finite():1314raise ValueError('The mutation class can - for infinite types - only be computed up to a given depth')1315return list( S for S in self.mutation_class_iter( depth=depth, show_depth=show_depth, return_paths=return_paths, up_to_equivalence=up_to_equivalence, only_sink_source=only_sink_source ) )13161317def cluster_class_iter(self, depth=infinity, show_depth=False, up_to_equivalence=True):1318r"""1319Returns an iterator through all clusters in the mutation class of ``self``.13201321INPUT:13221323- ``depth`` -- (default: infinity) integer or infinity, only seeds with distance at most depth from self are returned1324- ``show_depth`` -- (default False) - if True, ignored if depth is set; returns the depth of the mutation class, i.e., the maximal distance from self of an element in the mutation class1325- ``up_to_equivalence`` -- (default: True) if True, only clusters up to equivalence are considered.13261327EXAMPLES:13281329A standard finite type example::13301331sage: S = ClusterSeed(['A',3])1332sage: it = S.cluster_class_iter()1333sage: for T in it: print T1334[x0, x1, x2]1335[x0, x1, (x1 + 1)/x2]1336[x0, (x0*x2 + 1)/x1, x2]1337[(x1 + 1)/x0, x1, x2]1338[x0, (x0*x2 + x1 + 1)/(x1*x2), (x1 + 1)/x2]1339[(x1 + 1)/x0, x1, (x1 + 1)/x2]1340[(x1 + 1)/x0, (x0*x2 + x1 + 1)/(x0*x1), x2]1341[x0, (x0*x2 + 1)/x1, (x0*x2 + x1 + 1)/(x1*x2)]1342[(x0*x2 + x1 + 1)/(x0*x1), (x0*x2 + 1)/x1, x2]1343[(x1 + 1)/x0, (x1^2 + x0*x2 + 2*x1 + 1)/(x0*x1*x2), (x1 + 1)/x2]1344[(x1 + 1)/x0, (x0*x2 + x1 + 1)/(x0*x1), (x1^2 + x0*x2 + 2*x1 + 1)/(x0*x1*x2)]1345[(x1^2 + x0*x2 + 2*x1 + 1)/(x0*x1*x2), (x0*x2 + x1 + 1)/(x1*x2), (x1 + 1)/x2]1346[(x0*x2 + x1 + 1)/(x0*x1), (x0*x2 + 1)/x1, (x0*x2 + x1 + 1)/(x1*x2)]1347[(x0*x2 + x1 + 1)/(x1*x2), (x0*x2 + x1 + 1)/(x0*x1), (x1^2 + x0*x2 + 2*x1 + 1)/(x0*x1*x2)]13481349A finite type example with given depth::13501351sage: it = S.cluster_class_iter(depth=1)1352sage: for T in it: print T1353[x0, x1, x2]1354[x0, x1, (x1 + 1)/x2]1355[x0, (x0*x2 + 1)/x1, x2]1356[(x1 + 1)/x0, x1, x2]13571358A finite type example where the depth is returned while computing::13591360sage: it = S.cluster_class_iter(show_depth=True)1361sage: for T in it: print T1362[x0, x1, x2]1363Depth: 0 found: 1 Time: ... s1364[x0, x1, (x1 + 1)/x2]1365[x0, (x0*x2 + 1)/x1, x2]1366[(x1 + 1)/x0, x1, x2]1367Depth: 1 found: 4 Time: ... s1368[x0, (x0*x2 + x1 + 1)/(x1*x2), (x1 + 1)/x2]1369[(x1 + 1)/x0, x1, (x1 + 1)/x2]1370[(x1 + 1)/x0, (x0*x2 + x1 + 1)/(x0*x1), x2]1371[x0, (x0*x2 + 1)/x1, (x0*x2 + x1 + 1)/(x1*x2)]1372[(x0*x2 + x1 + 1)/(x0*x1), (x0*x2 + 1)/x1, x2]1373Depth: 2 found: 9 Time: ... s1374[(x1 + 1)/x0, (x1^2 + x0*x2 + 2*x1 + 1)/(x0*x1*x2), (x1 + 1)/x2]1375[(x1 + 1)/x0, (x0*x2 + x1 + 1)/(x0*x1), (x1^2 + x0*x2 + 2*x1 + 1)/(x0*x1*x2)]1376[(x1^2 + x0*x2 + 2*x1 + 1)/(x0*x1*x2), (x0*x2 + x1 + 1)/(x1*x2), (x1 + 1)/x2]1377[(x0*x2 + x1 + 1)/(x0*x1), (x0*x2 + 1)/x1, (x0*x2 + x1 + 1)/(x1*x2)]1378Depth: 3 found: 13 Time: ... s1379[(x0*x2 + x1 + 1)/(x1*x2), (x0*x2 + x1 + 1)/(x0*x1), (x1^2 + x0*x2 + 2*x1 + 1)/(x0*x1*x2)]1380Depth: 4 found: 14 Time: ... s13811382Finite type examples not considered up to equivalence::13831384sage: it = S.cluster_class_iter(up_to_equivalence=False)1385sage: len( [ T for T in it ] )13868413871388sage: it = ClusterSeed(['A',2]).cluster_class_iter(up_to_equivalence=False)1389sage: for T in it: print T1390[x0, x1]1391[x0, (x0 + 1)/x1]1392[(x1 + 1)/x0, x1]1393[(x1 + 1)/x0, (x0 + x1 + 1)/(x0*x1)]1394[(x0 + x1 + 1)/(x0*x1), (x0 + 1)/x1]1395[(x0 + x1 + 1)/(x0*x1), (x1 + 1)/x0]1396[(x0 + 1)/x1, (x0 + x1 + 1)/(x0*x1)]1397[x1, (x1 + 1)/x0]1398[(x0 + 1)/x1, x0]1399[x1, x0]14001401Infinite type examples::14021403sage: S = ClusterSeed(['A',[1,1],1])1404sage: it = S.cluster_class_iter()1405sage: it.next()1406[x0, x1]1407sage: it.next()1408[x0, (x0^2 + 1)/x1]1409sage: it.next()1410[(x1^2 + 1)/x0, x1]1411sage: it.next()1412[(x0^4 + 2*x0^2 + x1^2 + 1)/(x0*x1^2), (x0^2 + 1)/x1]1413sage: it.next()1414[(x1^2 + 1)/x0, (x1^4 + x0^2 + 2*x1^2 + 1)/(x0^2*x1)]14151416sage: it = S.cluster_class_iter(depth=3)1417sage: for T in it: print T1418[x0, x1]1419[x0, (x0^2 + 1)/x1]1420[(x1^2 + 1)/x0, x1]1421[(x0^4 + 2*x0^2 + x1^2 + 1)/(x0*x1^2), (x0^2 + 1)/x1]1422[(x1^2 + 1)/x0, (x1^4 + x0^2 + 2*x1^2 + 1)/(x0^2*x1)]1423[(x0^4 + 2*x0^2 + x1^2 + 1)/(x0*x1^2), (x0^6 + 3*x0^4 + 2*x0^2*x1^2 + x1^4 + 3*x0^2 + 2*x1^2 + 1)/(x0^2*x1^3)]1424[(x1^6 + x0^4 + 2*x0^2*x1^2 + 3*x1^4 + 2*x0^2 + 3*x1^2 + 1)/(x0^3*x1^2), (x1^4 + x0^2 + 2*x1^2 + 1)/(x0^2*x1)]1425"""1426mc_iter = self.mutation_class_iter( depth=depth, show_depth=show_depth, up_to_equivalence=up_to_equivalence )1427for c in mc_iter:1428yield c.cluster()14291430def cluster_class(self, depth=infinity, show_depth=False, up_to_equivalence=True):1431r"""1432Returns the cluster class of ``self`` with respect to certain constraints.14331434INPUT:14351436- ``depth`` -- (default: infinity) integer, only seeds with distance at most depth from self are returned1437- ``return_depth`` -- (default False) - if True, ignored if depth is set; returns the depth of the mutation class, i.e., the maximal distance from self of an element in the mutation class1438- ``up_to_equivalence`` -- (default: True) if True, only clusters up to equivalence are considered.14391440EXAMPLES:14411442- for examples see :meth:`cluster_class_iter`14431444TESTS::14451446sage: A = ClusterSeed(['A',3]).cluster_class()1447"""1448if depth is infinity and not self.is_finite():1449raise ValueError('The variable class can - for infinite types - only be computed up to a given depth')14501451return [ c for c in self.cluster_class_iter(depth=depth, show_depth=show_depth, up_to_equivalence=up_to_equivalence) ]14521453def b_matrix_class_iter(self, depth=infinity, up_to_equivalence=True):1454r"""1455Returns an iterator through all `B`-matrices in the mutation class of ``self``.14561457INPUT:14581459- ``depth`` -- (default:infinity) integer or infinity, only seeds with distance at most depth from self are returned1460- ``up_to_equivalence`` -- (default: True) if True, only 'B'-matrices up to equivalence are considered.14611462EXAMPLES:14631464A standard finite type example::14651466sage: S = ClusterSeed(['A',4])1467sage: it = S.b_matrix_class_iter()1468sage: for T in it: print T1469[ 0 0 0 1]1470[ 0 0 1 1]1471[ 0 -1 0 0]1472[-1 -1 0 0]1473[ 0 0 0 1]1474[ 0 0 1 0]1475[ 0 -1 0 1]1476[-1 0 -1 0]1477[ 0 0 1 1]1478[ 0 0 0 -1]1479[-1 0 0 0]1480[-1 1 0 0]1481[ 0 0 0 1]1482[ 0 0 -1 1]1483[ 0 1 0 -1]1484[-1 -1 1 0]1485[ 0 0 0 1]1486[ 0 0 -1 0]1487[ 0 1 0 -1]1488[-1 0 1 0]1489[ 0 0 0 -1]1490[ 0 0 -1 1]1491[ 0 1 0 -1]1492[ 1 -1 1 0]14931494A finite type example with given depth::14951496sage: it = S.b_matrix_class_iter(depth=1)1497sage: for T in it: print T1498[ 0 0 0 1]1499[ 0 0 1 1]1500[ 0 -1 0 0]1501[-1 -1 0 0]1502[ 0 0 0 1]1503[ 0 0 1 0]1504[ 0 -1 0 1]1505[-1 0 -1 0]1506[ 0 0 1 1]1507[ 0 0 0 -1]1508[-1 0 0 0]1509[-1 1 0 0]15101511Finite type example not considered up to equivalence::15121513sage: S = ClusterSeed(['A',3])1514sage: it = S.b_matrix_class_iter(up_to_equivalence=False)1515sage: for T in it: print T1516[ 0 1 0]1517[-1 0 -1]1518[ 0 1 0]1519[ 0 1 0]1520[-1 0 1]1521[ 0 -1 0]1522[ 0 -1 0]1523[ 1 0 1]1524[ 0 -1 0]1525[ 0 -1 0]1526[ 1 0 -1]1527[ 0 1 0]1528[ 0 -1 1]1529[ 1 0 -1]1530[-1 1 0]1531[ 0 1 -1]1532[-1 0 1]1533[ 1 -1 0]1534[ 0 0 1]1535[ 0 0 -1]1536[-1 1 0]1537[ 0 -1 1]1538[ 1 0 0]1539[-1 0 0]1540[ 0 0 -1]1541[ 0 0 1]1542[ 1 -1 0]1543[ 0 1 -1]1544[-1 0 0]1545[ 1 0 0]1546[ 0 1 1]1547[-1 0 0]1548[-1 0 0]1549[ 0 -1 -1]1550[ 1 0 0]1551[ 1 0 0]1552[ 0 0 -1]1553[ 0 0 -1]1554[ 1 1 0]1555[ 0 0 1]1556[ 0 0 1]1557[-1 -1 0]15581559Infinite (but finite mutation) type example::15601561sage: S = ClusterSeed(['A',[1,2],1])1562sage: it = S.b_matrix_class_iter()1563sage: for T in it: print T1564[ 0 1 1]1565[-1 0 1]1566[-1 -1 0]1567[ 0 -2 1]1568[ 2 0 -1]1569[-1 1 0]15701571Infinite mutation type example::15721573sage: S = ClusterSeed(['E',10])1574sage: it = S.b_matrix_class_iter(depth=3)1575sage: len ( [T for T in it] )15762661577"""1578Q = self.quiver()1579for M in Q.mutation_class_iter( depth=depth, up_to_equivalence=up_to_equivalence, data_type='matrix' ):1580yield M15811582def b_matrix_class(self, depth=infinity, up_to_equivalence=True):1583r"""1584Returns all `B`-matrices in the mutation class of ``self``.15851586INPUT:15871588- ``depth`` -- (default:infinity) integer or infinity, only seeds with distance at most depth from self are returned1589- ``up_to_equivalence`` -- (default: True) if True, only 'B'-matrices up to equivalence are considered.15901591EXAMPLES:15921593- for examples see :meth:`b_matrix_class_iter`15941595TESTS::15961597sage: A = ClusterSeed(['A',3]).b_matrix_class()1598sage: A = ClusterSeed(['A',[2,1],1]).b_matrix_class()1599"""1600if depth is infinity and not self.is_mutation_finite():1601raise ValueError('The B-matrix class can - for infinite mutation types - only be computed up to a given depth')16021603return [ M for M in self.b_matrix_class_iter( depth=depth, up_to_equivalence=up_to_equivalence ) ]16041605def variable_class_iter(self, depth=infinity, ignore_bipartite_belt=False):1606r"""1607Returns an iterator for all cluster variables in the mutation class of ``self``.16081609INPUT:16101611- ``depth`` -- (default:infinity) integer, only seeds with distance at most depth from self are returned1612- ``ignore_bipartite_belt`` -- (default:False) if True, the algorithms does not use the bipartite belt16131614EXAMPLES:16151616A standard finite type example::16171618sage: S = ClusterSeed(['A',3])1619sage: it = S.variable_class_iter()1620sage: for T in it: print T1621x01622x11623x21624(x1 + 1)/x01625(x1^2 + x0*x2 + 2*x1 + 1)/(x0*x1*x2)1626(x1 + 1)/x21627(x0*x2 + x1 + 1)/(x0*x1)1628(x0*x2 + 1)/x11629(x0*x2 + x1 + 1)/(x1*x2)16301631Finite type examples with given depth::16321633sage: it = S.variable_class_iter(depth=1)1634sage: for T in it: print T1635Found a bipartite seed - restarting the depth counter at zero and constructing the variable class using its bipartite belt.1636x01637x11638x21639(x1 + 1)/x01640(x1^2 + x0*x2 + 2*x1 + 1)/(x0*x1*x2)1641(x1 + 1)/x21642(x0*x2 + x1 + 1)/(x0*x1)1643(x0*x2 + 1)/x11644(x0*x2 + x1 + 1)/(x1*x2)16451646Note that the notion of *depth* depends on whether a bipartite seed is found or not, or if it is manually ignored::16471648sage: it = S.variable_class_iter(depth=1,ignore_bipartite_belt=True)1649sage: for T in it: print T1650x01651x11652x21653(x1 + 1)/x21654(x0*x2 + 1)/x11655(x1 + 1)/x016561657sage: S.mutate([0,1])1658sage: it2 = S.variable_class_iter(depth=1)1659sage: for T in it2: print T1660(x1 + 1)/x01661(x0*x2 + x1 + 1)/(x0*x1)1662x21663(x1^2 + x0*x2 + 2*x1 + 1)/(x0*x1*x2)1664x11665(x0*x2 + 1)/x116661667Infinite type examples::16681669sage: S = ClusterSeed(['A',[1,1],1])1670sage: it = S.variable_class_iter(depth=2)1671sage: for T in it: print T1672Found a bipartite seed - restarting the depth counter at zero and constructing the variable class using its bipartite belt.1673x01674x11675(x1^2 + 1)/x01676(x1^4 + x0^2 + 2*x1^2 + 1)/(x0^2*x1)1677(x0^4 + 2*x0^2 + x1^2 + 1)/(x0*x1^2)1678(x0^2 + 1)/x11679(x1^6 + x0^4 + 2*x0^2*x1^2 + 3*x1^4 + 2*x0^2 + 3*x1^2 + 1)/(x0^3*x1^2)1680(x1^8 + x0^6 + 2*x0^4*x1^2 + 3*x0^2*x1^4 + 4*x1^6 + 3*x0^4 + 6*x0^2*x1^2 + 6*x1^4 + 3*x0^2 + 4*x1^2 + 1)/(x0^4*x1^3)1681(x0^8 + 4*x0^6 + 3*x0^4*x1^2 + 2*x0^2*x1^4 + x1^6 + 6*x0^4 + 6*x0^2*x1^2 + 3*x1^4 + 4*x0^2 + 3*x1^2 + 1)/(x0^3*x1^4)1682(x0^6 + 3*x0^4 + 2*x0^2*x1^2 + x1^4 + 3*x0^2 + 2*x1^2 + 1)/(x0^2*x1^3)1683"""1684mut_iter = self.mutation_class_iter( depth=depth,show_depth=False )1685var_class = set()16861687for seed in mut_iter:1688if seed is self:1689seed = ClusterSeed(seed)1690if not ignore_bipartite_belt and seed.is_bipartite():1691bipartition = seed.is_bipartite(return_bipartition=True)1692bipartition = (list(bipartition[0]),list(bipartition[1]))1693if depth is not infinity:1694print "Found a bipartite seed - restarting the depth counter at zero and constructing the variable class using its bipartite belt."1695depth_counter = 01696end = False1697seed2 = ClusterSeed(seed)1698for c in seed._cluster:1699if c not in var_class:1700yield ClusterVariable( c.parent(), c.numerator(), c.denominator(), mutation_type=self._mutation_type, variable_type='cluster variable' )1701var_class = var_class.union( seed._cluster )17021703init_cluster = set(seed._cluster)1704while not end and depth_counter < depth:1705depth_counter += 11706seed.mutate(bipartition[0])1707seed.mutate(bipartition[1])1708if set(seed._cluster) in [set(seed2._cluster),init_cluster]:1709end = True1710if not end:1711for c in seed._cluster:1712if c not in var_class:1713yield ClusterVariable( c.parent(), c.numerator(), c.denominator(), mutation_type=self._mutation_type, variable_type='cluster variable' )1714var_class = var_class.union( seed._cluster )1715seed2.mutate(bipartition[1])1716seed2.mutate(bipartition[0])1717if set(seed2._cluster) in [set(seed._cluster),init_cluster]:1718end = True1719if not end:1720for c in seed2._cluster:1721if c not in var_class:1722yield ClusterVariable( c.parent(), c.numerator(), c.denominator(), mutation_type=self._mutation_type, variable_type='cluster variable' )1723var_class = var_class.union(seed2._cluster)1724return1725else:1726for c in seed._cluster:1727if c not in var_class:1728yield ClusterVariable( c.parent(), c.numerator(), c.denominator(), mutation_type=self._mutation_type, variable_type='cluster variable' )1729var_class = var_class.union(seed._cluster)17301731def variable_class(self, depth=infinity, ignore_bipartite_belt=False):1732r"""1733Returns all cluster variables in the mutation class of ``self``.17341735INPUT:17361737- ``depth`` -- (default:infinity) integer, only seeds with distance at most depth from self are returned1738- ``ignore_bipartite_belt`` -- (default:False) if True, the algorithms does not use the bipartite belt173917401741EXAMPLES:17421743- for examples see :meth:`variable_class_iter`17441745TESTS::17461747sage: A = ClusterSeed(['A',3]).variable_class()1748"""1749if depth is infinity and not self.is_finite():1750raise ValueError('The variable class can - for infinite types - only be computed up to a given depth')17511752var_iter = self.variable_class_iter( depth=depth, ignore_bipartite_belt=ignore_bipartite_belt )1753Vs = [ var for var in var_iter ]1754Vs.sort(cmp=cmp)1755return Vs17561757def is_finite( self ):1758r"""1759Returns True if ``self`` is of finite type.17601761EXAMPLES::17621763sage: S = ClusterSeed(['A',3])1764sage: S.is_finite()1765True17661767sage: S = ClusterSeed(['A',[2,2],1])1768sage: S.is_finite()1769False1770"""1771mt = self.mutation_type()1772if type(mt) is str:1773return False1774else:1775return mt.is_finite()17761777def is_mutation_finite( self, nr_of_checks=None, return_path=False ):1778r"""1779Returns True if ``self`` is of finite mutation type.17801781INPUT:17821783- ``nr_of_checks`` -- (default: None) number of mutations applied. Standard is 500*(number of vertices of self).1784- ``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.17851786ALGORITHM:17871788- A cluster seed is mutation infinite if and only if every `b_{ij}*b_{ji} > -4`. Thus, we apply random mutations in random directions17891790WARNING:17911792- Uses a non-deterministic method by random mutations in various directions.1793- In theory, it can return a wrong True.17941795EXAMPLES::17961797sage: S = ClusterSeed(['A',10])1798sage: S._mutation_type = None1799sage: S.is_mutation_finite()1800True18011802sage: S = ClusterSeed([(0,1),(1,2),(2,3),(3,4),(4,5),(5,6),(6,7),(7,8),(2,9)])1803sage: S.is_mutation_finite()1804False1805"""1806is_finite, path = is_mutation_finite(copy(self._M),nr_of_checks=nr_of_checks)1807if return_path:1808return is_finite, path1809else:1810return is_finite18111812def mutation_type(self):1813r"""1814Returns the mutation_type of each connected component of ``self``, if it can be determined.1815Otherwise, the mutation type of this component is set to be unknown.18161817The mutation types of the components are ordered by vertex labels.18181819WARNING:18201821- All finite types can be detected,1822- All affine types can be detected, EXCEPT affine type D (the algorithm is not yet implemented)1823- All exceptional types can be detected.18241825- Might fail to work if it is used within different Sage processes simultaneously (that happend in the doctesting).18261827EXAMPLES:18281829- finite types::18301831sage: S = ClusterSeed(['A',5])1832sage: S._mutation_type = S._quiver._mutation_type = None1833sage: S.mutation_type()1834['A', 5]18351836sage: S = ClusterSeed([(0,1),(1,2),(2,3),(3,4)])1837sage: S.mutation_type()1838['A', 5]18391840- affine types::18411842sage: S = ClusterSeed(['E',8,[1,1]]); S1843A seed for a cluster algebra of rank 10 of type ['E', 8, [1, 1]]1844sage: S._mutation_type = S._quiver._mutation_type = None; S1845A seed for a cluster algebra of rank 101846sage: S.mutation_type() # long time1847['E', 8, [1, 1]]18481849- the not yet working affine type D::18501851sage: S = ClusterSeed(['D',4,1])1852sage: S._mutation_type = S._quiver._mutation_type = None1853sage: S.mutation_type() # todo: not implemented1854['D', 4, 1]18551856- the exceptional types::18571858sage: S = ClusterSeed(['X',6])1859sage: S._mutation_type = S._quiver._mutation_type = None1860sage: S.mutation_type() # long time1861['X', 6]18621863- infinite types::18641865sage: S = ClusterSeed(['GR',[4,9]])1866sage: S._mutation_type = S._quiver._mutation_type = None1867sage: S.mutation_type()1868'undetermined infinite mutation type'1869"""1870if self._mutation_type is None:1871if self._quiver is None:1872self.quiver()1873self._mutation_type = self._quiver.mutation_type()1874return self._mutation_type18751876def greedy(self, a1, a2, method='by_recursion'):1877r"""1878Returns the greedy element `x[a_1,a_2]` assuming that self is rank two.18791880The third input can be 'by_recursion', 'by_combinatorics', or1881'just_numbers' to specify if the user wants the element1882computed by the recurrence, combinatorial formula, or wants to1883set `x_1` and `x_2` to be one.18841885See [LeeLiZe]_ for more details.18861887EXAMPLES::18881889sage: S = ClusterSeed(['R2', [3, 3]])1890sage: S.greedy(4, 4)1891(x0^12 + x1^12 + 4*x0^9 + 4*x1^9 + 6*x0^6 + 4*x0^3*x1^3 + 6*x1^6 + 4*x0^3 + 4*x1^3 + 1)/(x0^4*x1^4)1892sage: S.greedy(4, 4, 'by_combinatorics')1893(x0^12 + x1^12 + 4*x0^9 + 4*x1^9 + 6*x0^6 + 4*x0^3*x1^3 + 6*x1^6 + 4*x0^3 + 4*x1^3 + 1)/(x0^4*x1^4)1894sage: S.greedy(4, 4, 'just_numbers')1895351896sage: S = ClusterSeed(['R2', [2, 2]])1897sage: S.greedy(1, 2)1898(x0^4 + 2*x0^2 + x1^2 + 1)/(x0*x1^2)1899sage: S.greedy(1, 2, 'by_combinatorics')1900(x0^4 + 2*x0^2 + x1^2 + 1)/(x0*x1^2)19011902REFERENCES:19031904.. [LeeLiZe] Lee-Li-Zelevinsky, Greedy elements in rank 21905cluster algebras, :arxiv:`1208.2391`1906"""1907if self.b_matrix().dimensions() == (2, 2):1908b = abs(self.b_matrix()[0, 1])1909c = abs(self.b_matrix()[1, 0])1910if method == 'by_recursion':1911ans = self.x(0)**(-a1)*self.x(1)**(-a2)1912for p in range(max(a2, 0)+1):1913for q in range(max(a1, 0)+1):1914if p != 0 or q != 0:1915ans += self._R(coeff_recurs(p, q, a1, a2, b, c))*self.x(0)**(b*p-a1)*self.x(1)**(c*q-a2)1916return(ans)1917elif method == 'by_combinatorics':1918if b == 0:1919S = ClusterSeed([['A', 1], ['A', 1]])1920else:1921S = ClusterSeed(['R2', [b, b]])1922ans = 01923if a1 >= a2:1924PS = PathSubset(a1, a2)1925elif a1 < a2:1926PS = PathSubset(a2, a1)1927from sage.combinat.subset import Subsets1928for T in Subsets(PS):1929if a1 >= a2:1930if is_LeeLiZel_allowable(T, a1, a2, b, c):1931oddT = set(T).intersection(PathSubset(a1, 0))1932evenT = set(T).symmetric_difference(oddT)1933ans = ans + S.x(0)**(b*len(evenT)) * S.x(1)**(c*len(oddT))1934elif a1 < a2:1935if is_LeeLiZel_allowable(T, a2, a1, b, c):1936oddT = set(T).intersection(PathSubset(a2, 0))1937evenT = set(T).symmetric_difference(oddT)1938ans = ans + S.x(0)**(b*len(oddT)) * S.x(1)**(c*len(evenT))1939ans = ans*S.x(0)**(-a1)*S.x(1)**(-a2)1940return ans1941elif method == 'just_numbers':1942ans = 11943for p in range(max(a2, 0)+1):1944for q in range(max(a1, 0)+1):1945if p != 0 or q != 0:1946ans += coeff_recurs(p, q, a1, a2, b, c)1947return(ans)1948else:1949raise ValueError("The third input should be 'by_recursion', "1950"'by_combinatorics', or 'just_numbers'.")1951else:1952raise ValueError("Greedy elements are only currently "1953"defined for cluster seeds of rank two.")19541955def _bino(n, k):1956"""1957Binomial coefficient which we define as zero for negative n.19581959EXAMPLES::19601961sage: from sage.combinat.cluster_algebra_quiver.cluster_seed import _bino1962sage: _bino(3, 2)196331964sage: _bino(-3, 2)196501966"""1967if n >= 0:1968from sage.rings.arith import binomial1969return binomial(n, k)1970else:1971return 019721973def coeff_recurs(p, q, a1, a2, b, c):1974"""1975Coefficients in Laurent expansion of greedy element, as defined by recursion.19761977EXAMPLES::19781979sage: from sage.combinat.cluster_algebra_quiver.cluster_seed import coeff_recurs1980sage: coeff_recurs(1, 1, 5, 5, 3, 3)1981101982"""1983if p == 0 and q == 0:1984return 11985elif p < 0 or q < 0:1986return 01987else:1988if c*a1*q <= b*a2*p:1989return sum((-1)**(k-1)*coeff_recurs(p-k, q, a1, a2, b, c)*_bino(a2-c*q+k-1, k)1990for k in range(1, p+1))1991else:1992return sum((-1)**(k-1)*coeff_recurs(p, q-k, a1, a2, b, c)*_bino(a1-b*p+k-1, k)1993for k in range(1, q+1))19941995def PathSubset(n,m):1996r"""1997Encodes a *maximal* Dyck path from (0,0) to (n,m) (for n >= m >= 0) as a subset of {0,1,2,..., 2n-1}.1998The encoding is given by indexing horizontal edges by odd numbers and vertical edges by evens.19992000The horizontal between (i,j) and (i+1,j) is indexed by the odd number 2*i+1.2001The vertical between (i,j) and (i,j+1) is indexed by the even number 2*j.20022003EXAMPLES::20042005sage: from sage.combinat.cluster_algebra_quiver.cluster_seed import PathSubset2006sage: PathSubset(4,0)2007set([1, 3, 5, 7])2008sage: PathSubset(4,1)2009set([1, 3, 5, 6, 7])2010sage: PathSubset(4,2)2011set([1, 2, 3, 5, 6, 7])2012sage: PathSubset(4,3)2013set([1, 2, 3, 4, 5, 6, 7])2014sage: PathSubset(4,4)2015set([0, 1, 2, 3, 4, 5, 6, 7])2016"""2017from sage.misc.misc import union2018from sage.functions.other import floor2019S = [ ]2020for i in range(n):2021S = union(S, [2*i+1])2022if m > 0:2023for j in range(n):2024if floor((j+1)*m/n) - floor(j*m/n) == 1:2025S = union(S, [2*j])2026return set(S)20272028def SetToPath(T):2029r"""2030Rearranges the encoding for a *maximal* Dyck path (as a set) so that it is a list in the proper order of the edges.20312032EXAMPLES::20332034sage: from sage.combinat.cluster_algebra_quiver.cluster_seed import PathSubset2035sage: from sage.combinat.cluster_algebra_quiver.cluster_seed import SetToPath2036sage: SetToPath(PathSubset(4,0))2037[1, 3, 5, 7]2038sage: SetToPath(PathSubset(4,1))2039[1, 3, 5, 7, 6]2040sage: SetToPath(PathSubset(4,2))2041[1, 3, 2, 5, 7, 6]2042sage: SetToPath(PathSubset(4,3))2043[1, 3, 2, 5, 4, 7, 6]2044sage: SetToPath(PathSubset(4,4))2045[1, 0, 3, 2, 5, 4, 7, 6]2046"""2047n = (max(T)+1)/22048ans = [1]2049for i in range(n-1):2050if 2*i in T:2051ans.append(2*i)2052ans.append(2*i+3)2053if 2*n-2 in T:2054ans.append(2*n-2)2055return ans20562057def is_LeeLiZel_allowable(T,n,m,b,c):2058"""2059Check if the subset T contributes to the computation of the greedy2060element x[m,n] in the rank two (b,c)-cluster algebra.20612062This uses the conditions of Lee-Li-Zelevinsky's paper [LeeLiZe]_.20632064EXAMPLES::20652066sage: from sage.combinat.cluster_algebra_quiver.cluster_seed import is_LeeLiZel_allowable2067sage: is_LeeLiZel_allowable({1,3,2,5,7,6},4,2,6,6)2068False2069sage: is_LeeLiZel_allowable({1,2,5},3,3,1,1)2070True2071"""2072horiz = set(T).intersection( PathSubset(n, 0))2073vert = set(T).symmetric_difference(horiz)2074if len(horiz) == 0 or len(vert) == 0:2075return True2076else:2077Latt = SetToPath(PathSubset(n, m))2078for u in horiz:2079from sage.combinat.words.word import Word2080from sage.modules.free_module_element import vector2081WW = Word(Latt)2082LattCycled = vector(WW.conjugate(Latt.index(u))).list()2083for v in vert:2084uv_okay = False2085for A in range(LattCycled.index(v)):2086EA = []2087AF = copy(LattCycled)2088for i in range(LattCycled.index(v), len(LattCycled)-1):2089AF.pop()2090AF.reverse()2091for i in range(A+1):2092EA.append(LattCycled[i])2093AF.pop()2094AF.reverse()2095nAF1 = 02096for i in range(len(AF)):2097if AF[i] % 2 == 1:2098nAF1 += 12099nAF2 = 02100for i in range(len(AF)):2101if AF[i] % 2 == 0 and AF[i] in vert:2102nAF2 += 12103nEA2 = 02104for i in range(len(EA)):2105if EA[i] % 2 == 0:2106nEA2 += 12107nEA1 = 02108for i in range(len(EA)):2109if EA[i] % 2 == 1 and EA[i] in horiz:2110nEA1 += 12111if nAF1 == b*nAF2 or nEA2 == c*nEA1:2112uv_okay = True2113if uv_okay == False:2114return False2115return True211621172118class ClusterVariable(FractionFieldElement):2119r"""2120This class is a thin wrapper for cluster variables in cluster seeds.21212122It provides the extra feature to store if a variable is frozen or not.21232124- the associated positive root::21252126sage: S = ClusterSeed(['A',3])2127sage: for T in S.variable_class_iter(): print T, T.almost_positive_root()2128x0 -alpha[1]2129x1 -alpha[2]2130x2 -alpha[3]2131(x1 + 1)/x0 alpha[1]2132(x1^2 + x0*x2 + 2*x1 + 1)/(x0*x1*x2) alpha[1] + alpha[2] + alpha[3]2133(x1 + 1)/x2 alpha[3]2134(x0*x2 + x1 + 1)/(x0*x1) alpha[1] + alpha[2]2135(x0*x2 + 1)/x1 alpha[2]2136(x0*x2 + x1 + 1)/(x1*x2) alpha[2] + alpha[3]2137"""2138def __init__( self, parent, numerator, denominator, coerce=True, reduce=True, mutation_type=None, variable_type=None ):2139r"""2140Initializes a cluster variable in the same way that elements in the field of rational functions are initialized.21412142.. see also:: :class:`Fraction Field of Multivariate Polynomial Ring`21432144TESTS::21452146sage: S = ClusterSeed(['A',2])2147sage: for f in S.cluster():2148... print type(f)2149<class 'sage.combinat.cluster_algebra_quiver.cluster_seed.ClusterVariable'>2150<class 'sage.combinat.cluster_algebra_quiver.cluster_seed.ClusterVariable'>21512152sage: S.variable_class()2153[(x0 + x1 + 1)/(x0*x1), (x1 + 1)/x0, (x0 + 1)/x1, x1, x0]2154"""2155FractionFieldElement.__init__( self, parent, numerator, denominator, coerce=coerce, reduce=reduce )2156self._mutation_type = mutation_type2157self._variable_type = variable_type21582159def almost_positive_root( self ):2160r"""2161Returns the *almost positive root* associated to ``self`` if ``self`` is of finite type.21622163EXAMPLES::21642165sage: S = ClusterSeed(['A',3])2166sage: for T in S.variable_class_iter(): print T, T.almost_positive_root()2167x0 -alpha[1]2168x1 -alpha[2]2169x2 -alpha[3]2170(x1 + 1)/x0 alpha[1]2171(x1^2 + x0*x2 + 2*x1 + 1)/(x0*x1*x2) alpha[1] + alpha[2] + alpha[3]2172(x1 + 1)/x2 alpha[3]2173(x0*x2 + x1 + 1)/(x0*x1) alpha[1] + alpha[2]2174(x0*x2 + 1)/x1 alpha[2]2175(x0*x2 + x1 + 1)/(x1*x2) alpha[2] + alpha[3]2176"""2177if self._variable_type == 'frozen variable':2178raise ValueError('The variable is frozen.')2179if type(self._mutation_type) is str:2180raise ValueError('The cluster algebra for %s is not of finite type.'%self._repr_())2181else:2182if self._mutation_type is None:2183self._mutation_type = self.parent().mutation_type()2184if self._mutation_type.is_finite():2185from sage.combinat.root_system.root_system import RootSystem2186# the import above is used in the line below2187exec "Phi = RootSystem("+self._mutation_type._repr_()+")"2188Phiplus = Phi.root_lattice().simple_roots()2189if self.denominator() == 1:2190return -Phiplus[ self.numerator().degrees().index(1) + 1 ]2191else:2192root = self.denominator().degrees()2193return sum( [ root[i]*Phiplus[ i+1 ] for i in range(len(root)) ] )2194else:2195raise ValueError('The cluster algebra for %s is not of finite type.'%self._repr_())219621972198