Ticket #13425: trac_13425-mutation_type-gm.patch

File trac_13425-mutation_type-gm.patch, 122.6 KB (added by stumpc5, 7 years ago)
  • sage/combinat/cluster_algebra_quiver/cluster_seed.py

    # HG changeset patch
    # User Christian Stump <christian.stump at gmail.com>
    # Date 1346649016 18000
    # Node ID c27de5fd4ceef69e2511d9558c766e404b15e6ef
    # Parent  ba7dd081702b71b5b52535d36aa601a7fcb56c6f
    Trac 13425: Mutation type checking commands to ClusterSeed and ClusterQuiver
    
    diff --git a/sage/combinat/cluster_algebra_quiver/cluster_seed.py b/sage/combinat/cluster_algebra_quiver/cluster_seed.py
    a b from sage.rings.fraction_field_element i 
    3434from sage.sets.all import Set
    3535from sage.graphs.all import Graph, DiGraph
    3636from sage.combinat.cluster_algebra_quiver.quiver_mutation_type import QuiverMutationType, QuiverMutationType_Irreducible, QuiverMutationType_Reducible
     37from sage.combinat.cluster_algebra_quiver.mutation_type import _connected_mutation_type, is_mutation_finite
    3738from sage.groups.perm_gps.permgroup import PermutationGroup
    3839
    3940class ClusterSeed(SageObject):
    class ClusterSeed(SageObject): 
    170171        if self._mutation_type:
    171172            if type( self._mutation_type ) in [QuiverMutationType_Irreducible,QuiverMutationType_Reducible]:
    172173                name += ' of type ' + str(self._mutation_type)
    173             # the following case will be relevant later on after ticket 13425 (mutation type checking) is applied
     174            # the following case allows description of 'undetermined finite mutation type'
    174175            else:
    175176                name += ' of ' + self._mutation_type
    176177        if self._is_principal:
    class ClusterSeed(SageObject): 
    372373        """
    373374        if k in range(self._n):
    374375            x = self._R.gens()[k]
    375             return ClusterVariable( x.parent(), x.numerator(), x.denominator(), variable_type='cluster variable' )
     376            return ClusterVariable( x.parent(), x.numerator(), x.denominator(), mutation_type=self._mutation_type, variable_type='cluster variable' )
    376377        else:
    377378            raise ValueError("The input is not in an index of a cluster variable.")
    378379
    class ClusterSeed(SageObject): 
    395396        """
    396397        if k in range(self._m):
    397398            x = self._R.gens()[self._n+k]
    398             return ClusterVariable( x.parent(), x.numerator(), x.denominator(), variable_type='frozen variable' )
     399            return ClusterVariable( x.parent(), x.numerator(), x.denominator(), mutation_type=self._mutation_type, variable_type='frozen variable' )
    399400        else:
    400401            raise ValueError("The input is not in an index of a frozen variable.")
    401402
    402     def mutation_type(self):
    403         """
    404         Returns the mutation type of self.
    405 
    406         EXAMPLES::
    407 
    408             sage: ClusterSeed(['A',4]).mutation_type()
    409             ['A', 4]
    410             sage: ClusterSeed(['A',(3,1),1]).mutation_type()
    411             ['A', [1, 3], 1]
    412             sage: ClusterSeed(['C',2]).mutation_type()
    413             ['B', 2]
    414             sage: ClusterSeed(['B',4,1]).mutation_type()
    415             ['BD', 4, 1]
    416         """
    417         return self._mutation_type
    418 
    419403    def n(self):
    420404        r"""
    421405        Returns the number of *exchangeable variables* of ``self``.
    class ClusterSeed(SageObject): 
    462446        if k not in range(self._n):
    463447            raise ValueError("The cluster seed does not have a cluster variable of index %s."%k)
    464448        f = self._cluster[k]
    465         return ClusterVariable( f.parent(), f.numerator(), f.denominator(), variable_type='cluster variable' )
     449        return ClusterVariable( f.parent(), f.numerator(), f.denominator(), mutation_type=self._mutation_type, variable_type='cluster variable' )
    466450
    467451    def cluster(self):
    468452        r"""
    class ClusterSeed(SageObject): 
    10161000        EXAMPLES::
    10171001
    10181002            sage: S = ClusterSeed(['A',[2,3],1])
     1003            sage: S.mutation_type()
     1004            ['A', [2, 3], 1]
    10191005
    10201006            sage: S.reorient([(0,1),(2,3)])
     1007            sage: S.mutation_type()
     1008            ['D', 5]
    10211009
    10221010            sage: S.reorient([(1,0),(2,3)])
     1011            sage: S.mutation_type()
     1012            ['A', [1, 4], 1]
    10231013
    10241014            sage: S.reorient([0,1,2,3,4])
     1015            sage: S.mutation_type()
     1016            ['A', [1, 4], 1]
    10251017        """
    10261018        if not self._quiver:
    10271019            self.quiver()
    class ClusterSeed(SageObject): 
    12631255            dc += ' ' * (5-len(dc))
    12641256            nr = str(len(clusters))
    12651257            nr += ' ' * (10-len(nr))
    1266             print "Depth: %s found: %s Time: %.2f s"%(dc,nr,timer2-timer) 
     1258            print "Depth: %s found: %s Time: %.2f s"%(dc,nr,timer2-timer)
    12671259        while gets_bigger and depth_counter < depth:
    12681260            gets_bigger = False
    12691261            keys = clusters.keys()
    class ClusterSeed(SageObject): 
    13211313
    13221314            sage: A = ClusterSeed(['A',3]).mutation_class()
    13231315        """
    1324         # runs forever without the mutation type recognition patch applied
     1316        if depth is infinity:
     1317            assert self.is_finite(), 'The mutation class can - for infinite types - only be computed up to a given depth'
    13251318        return 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 ) )
    13261319
    13271320    def cluster_class_iter(self, depth=infinity, show_depth=False, up_to_equivalence=True):
    class ClusterSeed(SageObject): 
    13701363            sage: it = S.cluster_class_iter(show_depth=True)
    13711364            sage: for T in it: print T
    13721365            [x0, x1, x2]
    1373             Depth: 0     found: 1          Time: ... s           
     1366            Depth: 0     found: 1          Time: ... s
    13741367            [x0, x1, (x1 + 1)/x2]
    13751368            [x0, (x0*x2 + 1)/x1, x2]
    13761369            [(x1 + 1)/x0, x1, x2]
    class ClusterSeed(SageObject): 
    14551448
    14561449            sage: A = ClusterSeed(['A',3]).cluster_class()
    14571450        """
    1458         # runs forever without the mutation type recognition patch applied
     1451        if depth is infinity:
     1452            assert self.is_finite(), 'The variable class can - for infinite types - only be computed up to a given depth'
     1453
    14591454        return [ c for c in self.cluster_class_iter(depth=depth, show_depth=show_depth, up_to_equivalence=up_to_equivalence) ]
    14601455
    14611456    def b_matrix_class_iter(self, depth=infinity, up_to_equivalence=True):
    class ClusterSeed(SageObject): 
    16051600            sage: A = ClusterSeed(['A',3]).b_matrix_class()
    16061601            sage: A = ClusterSeed(['A',[2,1],1]).b_matrix_class()
    16071602        """
    1608         # runs forever without the mutation type recognition patch applied
     1603        if depth is infinity:
     1604            assert self.is_mutation_finite(), 'The B-matrix class can - for infinite mutation types - only be computed up to a given depth'
     1605
    16091606        return [ M for M in self.b_matrix_class_iter( depth=depth, up_to_equivalence=up_to_equivalence ) ]
    16101607
    16111608    def variable_class_iter(self, depth=infinity, ignore_bipartite_belt=False):
    class ClusterSeed(SageObject): 
    17031700                seed2 = ClusterSeed(seed)
    17041701                for c in seed._cluster:
    17051702                    if c not in var_class:
    1706                         yield ClusterVariable( c.parent(), c.numerator(), c.denominator(), variable_type='cluster variable' )
     1703                        yield ClusterVariable( c.parent(), c.numerator(), c.denominator(), mutation_type=self._mutation_type, variable_type='cluster variable' )
    17071704                var_class = var_class.union( seed._cluster )
    17081705
    17091706                init_cluster = set(seed._cluster)
    class ClusterSeed(SageObject): 
    17161713                    if not end:
    17171714                        for c in seed._cluster:
    17181715                            if c not in var_class:
    1719                                 yield ClusterVariable( c.parent(), c.numerator(), c.denominator(), variable_type='cluster variable' )
     1716                                yield ClusterVariable( c.parent(), c.numerator(), c.denominator(), mutation_type=self._mutation_type, variable_type='cluster variable' )
    17201717                        var_class = var_class.union( seed._cluster )
    17211718                        seed2.mutate(bipartition[1])
    17221719                        seed2.mutate(bipartition[0])
    class ClusterSeed(SageObject): 
    17251722                        if not end:
    17261723                            for c in seed2._cluster:
    17271724                                if c not in var_class:
    1728                                     yield ClusterVariable( c.parent(), c.numerator(), c.denominator(), variable_type='cluster variable' )
     1725                                    yield ClusterVariable( c.parent(), c.numerator(), c.denominator(), mutation_type=self._mutation_type, variable_type='cluster variable' )
    17291726                            var_class = var_class.union(seed2._cluster)
    17301727                return
    17311728            else:
    17321729                for c in seed._cluster:
    17331730                    if c not in var_class:
    1734                         yield ClusterVariable( c.parent(), c.numerator(), c.denominator(), variable_type='cluster variable' )
     1731                        yield ClusterVariable( c.parent(), c.numerator(), c.denominator(), mutation_type=self._mutation_type, variable_type='cluster variable' )
    17351732                var_class = var_class.union(seed._cluster)
    17361733
    17371734    def variable_class(self, depth=infinity, ignore_bipartite_belt=False):
    class ClusterSeed(SageObject): 
    17521749
    17531750            sage: A = ClusterSeed(['A',3]).variable_class()
    17541751        """
    1755         # runs forever without the mutation type recognition patch applied
     1752        if depth is infinity:
     1753            assert self.is_finite(), 'The variable class can - for infinite types - only be computed up to a given depth'
     1754
    17561755        var_iter = self.variable_class_iter( depth=depth, ignore_bipartite_belt=ignore_bipartite_belt )
    17571756        Vs = [ var for var in var_iter ]
    17581757        Vs.sort(cmp=cmp)
    17591758        return Vs
    17601759
     1760    def is_finite( self ):
     1761        r"""
     1762        Returns True if ``self`` is of finite type.
     1763
     1764        EXAMPLES::
     1765
     1766            sage: S = ClusterSeed(['A',3])
     1767            sage: S.is_finite()
     1768            True
     1769
     1770            sage: S = ClusterSeed(['A',[2,2],1])
     1771            sage: S.is_finite()
     1772            False
     1773        """
     1774        mt = self.mutation_type()
     1775        if type(mt) is str:
     1776            return False
     1777        else:
     1778            return mt.is_finite()
     1779
     1780    def is_mutation_finite( self, nr_of_checks=None, return_path=False ):
     1781        r"""
     1782        Returns True if ``self`` is of finite mutation type.
     1783
     1784        INPUT:
     1785
     1786        - ``nr_of_checks`` -- (default: None) number of mutations applied. Standard is 500*(number of vertices of self).
     1787        - ``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.
     1788
     1789        ALGORITHM:
     1790
     1791        - A cluster seed is mutation infinite if and only if every `b_{ij}*b_{ji} > -4`. Thus, we apply random mutations in random directions
     1792
     1793        WARNING:
     1794
     1795        - Uses a non-deterministic method by random mutations in various directions.
     1796        - In theory, it can return a wrong True.
     1797
     1798        EXAMPLES::
     1799
     1800            sage: S = ClusterSeed(['A',10])
     1801            sage: S._mutation_type = None
     1802            sage: S.is_mutation_finite()
     1803            True
     1804
     1805            sage: S = ClusterSeed([(0,1),(1,2),(2,3),(3,4),(4,5),(5,6),(6,7),(7,8),(2,9)])
     1806            sage: S.is_mutation_finite()
     1807            False
     1808        """
     1809        is_finite, path = is_mutation_finite(copy(self._M),nr_of_checks=nr_of_checks)
     1810        if return_path:
     1811            return is_finite, path
     1812        else:
     1813            return is_finite
     1814
     1815    def mutation_type(self):
     1816        r"""
     1817        Returns the mutation_type of each connected component of ``self``, if it can be determined.
     1818        Otherwise, the mutation type of this component is set to be unknown.
     1819
     1820        The mutation types of the components are ordered by vertex labels.
     1821
     1822        WARNING:
     1823
     1824        - All finite types can be detected,
     1825        - All affine types can be detected, EXCEPT affine type D (the algorithm is not yet implemented)
     1826        - All exceptional types can be detected.
     1827
     1828        - Might fail to work if it is used within different Sage processes simultaneously (that happend in the doctesting).
     1829
     1830        EXAMPLES:
     1831
     1832        - finite types::
     1833
     1834            sage: S = ClusterSeed(['A',5])
     1835            sage: S._mutation_type = S._quiver._mutation_type = None
     1836            sage: S.mutation_type()
     1837            ['A', 5]
     1838
     1839            sage: S = ClusterSeed([(0,1),(1,2),(2,3),(3,4)])
     1840            sage: S.mutation_type()
     1841            ['A', 5]
     1842
     1843        - affine types::
     1844
     1845            sage: S = ClusterSeed(['E',8,[1,1]]); S
     1846            A seed for a cluster algebra of rank 10 of type ['E', 8, [1, 1]]
     1847            sage: S._mutation_type = S._quiver._mutation_type = None; S
     1848            A seed for a cluster algebra of rank 10
     1849            sage: S.mutation_type() # long time
     1850            ['E', 8, [1, 1]]
     1851
     1852        - the not yet working affine type D::
     1853
     1854            sage: S = ClusterSeed(['D',4,1])
     1855            sage: S._mutation_type = S._quiver._mutation_type = None
     1856            sage: S.mutation_type()    #indirect doctest
     1857            'undetermined finite mutation type'
     1858
     1859        - the exceptional types::
     1860
     1861            sage: S = ClusterSeed(['X',6])
     1862            sage: S._mutation_type = S._quiver._mutation_type = None
     1863            sage: S.mutation_type() # long time
     1864            ['X', 6]
     1865
     1866        -  infinite types::
     1867
     1868            sage: S = ClusterSeed(['GR',[4,9]])
     1869            sage: S._mutation_type = S._quiver._mutation_type = None
     1870            sage: S.mutation_type()
     1871            'undetermined infinite mutation type'
     1872        """
     1873        if self._mutation_type is None:
     1874            if self._quiver is None:
     1875                self.quiver()
     1876            self._mutation_type = self._quiver.mutation_type()
     1877        return self._mutation_type
     1878
    17611879class ClusterVariable(FractionFieldElement):
    17621880    r"""
    17631881    This class is a thin wrapper for cluster variables in cluster seeds.
    17641882
    17651883    It provides the extra feature to store if a variable is frozen or not.
     1884
     1885    - the associated positive root::
     1886
     1887        sage: S = ClusterSeed(['A',3])
     1888        sage: for T in S.variable_class_iter(): print T, T.almost_positive_root()
     1889        x0 -alpha[1]
     1890        x1 -alpha[2]
     1891        x2 -alpha[3]
     1892        (x1 + 1)/x0 alpha[1]
     1893        (x1^2 + x0*x2 + 2*x1 + 1)/(x0*x1*x2) alpha[1] + alpha[2] + alpha[3]
     1894        (x1 + 1)/x2 alpha[3]
     1895        (x0*x2 + x1 + 1)/(x0*x1) alpha[1] + alpha[2]
     1896        (x0*x2 + 1)/x1 alpha[2]
     1897        (x0*x2 + x1 + 1)/(x1*x2) alpha[2] + alpha[3]
    17661898    """
    1767     def __init__( self, parent, numerator, denominator, coerce=True, reduce=True, variable_type=None ):
     1899    def __init__( self, parent, numerator, denominator, coerce=True, reduce=True, mutation_type=None, variable_type=None ):
    17681900        r"""
    17691901        Initializes a cluster variable in the same way that elements in the field of rational functions are initialized.
    17701902
    class ClusterVariable(FractionFieldEleme 
    17821914            [(x0 + x1 + 1)/(x0*x1), (x1 + 1)/x0, (x0 + 1)/x1, x1, x0]
    17831915        """
    17841916        FractionFieldElement.__init__( self, parent, numerator, denominator, coerce=coerce, reduce=reduce )
     1917        self._mutation_type = mutation_type
    17851918        self._variable_type = variable_type
     1919
     1920    def almost_positive_root( self ):
     1921        r"""
     1922        Returns the *almost positive root* associated to ``self`` if ``self`` is of finite type.
     1923
     1924        EXAMPLES::
     1925
     1926            sage: S = ClusterSeed(['A',3])
     1927            sage: for T in S.variable_class_iter(): print T, T.almost_positive_root()
     1928            x0 -alpha[1]
     1929            x1 -alpha[2]
     1930            x2 -alpha[3]
     1931            (x1 + 1)/x0 alpha[1]
     1932            (x1^2 + x0*x2 + 2*x1 + 1)/(x0*x1*x2) alpha[1] + alpha[2] + alpha[3]
     1933            (x1 + 1)/x2 alpha[3]
     1934            (x0*x2 + x1 + 1)/(x0*x1) alpha[1] + alpha[2]
     1935            (x0*x2 + 1)/x1 alpha[2]
     1936            (x0*x2 + x1 + 1)/(x1*x2) alpha[2] + alpha[3]
     1937        """
     1938        if self._variable_type == 'frozen variable':
     1939            raise ValueError('The variable is frozen.')
     1940        if type(self._mutation_type) is str:
     1941            raise ValueError('The cluster algebra for %s is not of finite type.'%self._repr_())
     1942        else:
     1943            if self._mutation_type is None:
     1944                self._mutation_type = self.parent().mutation_type()
     1945            if self._mutation_type.is_finite():
     1946                from sage.combinat.root_system.root_system import RootSystem
     1947                exec "Phi = RootSystem("+self._mutation_type._repr_()+")"
     1948                Phiplus = Phi.root_lattice().simple_roots()
     1949                if self.denominator() == 1:
     1950                    return -Phiplus[ self.numerator().degrees().index(1) + 1 ]
     1951                else:
     1952                    root = self.denominator().degrees()
     1953                    return sum( [ root[i]*Phiplus[ i+1 ] for i in range(len(root)) ] )
     1954            else:
     1955                raise ValueError('The cluster algebra for %s is not of finite type.'%self._repr_())
  • sage/combinat/cluster_algebra_quiver/mutation_class.py

    diff --git a/sage/combinat/cluster_algebra_quiver/mutation_class.py b/sage/combinat/cluster_algebra_quiver/mutation_class.py
    a b def _digraph_mutate( dg, k, n, m ): 
    7272    EXAMPLES::
    7373
    7474        sage: from sage.combinat.cluster_algebra_quiver.mutation_class import _digraph_mutate
     75        sage: from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver
    7576        sage: dg = ClusterQuiver(['A',4]).digraph()
    7677        sage: dg.edges()
    7778        [(0, 1, (1, -1)), (2, 1, (1, -1)), (2, 3, (1, -1))]
    def _dg_canonical_form( dg, n, m ): 
    154155    EXAMPLES::
    155156
    156157        sage: from sage.combinat.cluster_algebra_quiver.mutation_class import _dg_canonical_form
     158        sage: from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver
    157159        sage: dg = ClusterQuiver(['B',4]).digraph(); dg.edges()
    158160        [(0, 1, (1, -1)), (2, 1, (1, -1)), (2, 3, (1, -2))]
    159161        sage: _dg_canonical_form(dg,4,0); dg.edges()
    def _mutation_class_iter( dg, n, m, dept 
    205207    EXAMPLES::
    206208
    207209        sage: from sage.combinat.cluster_algebra_quiver.mutation_class import _mutation_class_iter
     210        sage: from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver
    208211        sage: dg = ClusterQuiver(['A',[1,2],1]).digraph()
    209212        sage: itt = _mutation_class_iter(dg, 3,0)
    210213        sage: itt.next()[0].edges()
    def _mutation_class_iter( dg, n, m, dept 
    237240        dc += ' ' * (5-len(dc))
    238241        nr = str(len(dig6s))
    239242        nr += ' ' * (10-len(nr))
    240         print "Depth: %s found: %s Time: %.2f s"%(dc,nr,timer2-timer) 
     243        print "Depth: %s found: %s Time: %.2f s"%(dc,nr,timer2-timer)
    241244
    242245    while gets_bigger and depth_counter < depth:
    243246        gets_bigger = False
    def _digraph_to_dig6( dg, hashable=False 
    298301    EXAMPLES::
    299302
    300303        sage: from sage.combinat.cluster_algebra_quiver.mutation_class import _digraph_to_dig6
     304        sage: from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver
    301305        sage: dg = ClusterQuiver(['A',4]).digraph()
    302306        sage: _digraph_to_dig6(dg)
    303307        ('COD?', {})
    def _dig6_to_digraph( dig6 ): 
    323327
    324328        sage: from sage.combinat.cluster_algebra_quiver.mutation_class import _digraph_to_dig6
    325329        sage: from sage.combinat.cluster_algebra_quiver.mutation_class import _dig6_to_digraph
     330        sage: from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver
    326331        sage: dg = ClusterQuiver(['A',4]).digraph()
    327332        sage: data = _digraph_to_dig6(dg)
    328333        sage: _dig6_to_digraph(data)
    def _dig6_to_digraph( dig6 ): 
    341346            dg.set_edge_label( edge[0],edge[1], (1,-1) )
    342347    return dg
    343348
     349def _dig6_to_matrix( dig6 ):
     350    """
     351    Returns the matrix obtained from the dig6 and edge data.
     352
     353    INPUT:
     354
     355    - ``dig6`` -- a pair ``(dig6, edges)`` where ``dig6`` is a string encoding a digraph and ``edges`` is a dict or tuple encoding edges
     356
     357    EXAMPLES::
     358
     359        sage: from sage.combinat.cluster_algebra_quiver.mutation_class import _digraph_to_dig6, _dig6_to_matrix
     360        sage: from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver
     361        sage: dg = ClusterQuiver(['A',4]).digraph()
     362        sage: data = _digraph_to_dig6(dg)
     363        sage: _dig6_to_matrix(data)
     364        [ 0  1  0  0]
     365        [-1  0 -1  0]
     366        [ 0  1  0  1]
     367        [ 0  0 -1  0]
     368    """
     369    dg = _dig6_to_digraph( dig6 )
     370    return _edge_list_to_matrix( dg.edges(), dg.order(), 0 )
     371
    344372def _dg_is_sink_source( dg, v ):
    345373    """
    346374    Returns True iff the digraph dg has a sink or a source at vertex v.
    def _dg_is_sink_source( dg, v ): 
    353381    EXAMPLES::
    354382
    355383        sage: from sage.combinat.cluster_algebra_quiver.mutation_class import _dg_is_sink_source
     384        sage: from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver
    356385        sage: dg = ClusterQuiver(['A',[1,2],1]).digraph()
    357386        sage: _dg_is_sink_source(dg, 0 )
    358387        True
    def _graph_without_edge_labels(dg,vertic 
    372401    EXAMPLES::
    373402
    374403        sage: from sage.combinat.cluster_algebra_quiver.mutation_class import _graph_without_edge_labels
     404        sage: from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver
    375405        sage: dg = ClusterQuiver(['B',4]).digraph(); dg.edges()
    376406        [(0, 1, (1, -1)), (2, 1, (1, -1)), (2, 3, (1, -2))]
    377407        sage: _graph_without_edge_labels(dg,range(3)); dg.edges()
    def _has_two_cycles( dg ): 
    410440        sage: from sage.combinat.cluster_algebra_quiver.mutation_class import _has_two_cycles
    411441        sage: _has_two_cycles( DiGraph([[0,1],[1,0]]))
    412442        True
     443        sage: from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver
    413444        sage: _has_two_cycles( ClusterQuiver(['A',3]).digraph() )
    414445        False
    415446    """
    def _is_valid_digraph_edge_set( edges, f 
    471502            print "The number of frozen variables is larger than the number of vertices."
    472503            return False
    473504
    474         if [ e for e in dg.edges(labels=False) if e[0] >= n] <> []:
     505        if [ e for e in dg.edges(labels=False) if e[0] >= n] != []:
    475506            print "The given digraph or edge list contains edges within the frozen vertices."
    476507            return False
    477508
  • new file sage/combinat/cluster_algebra_quiver/mutation_type.py

    diff --git a/sage/combinat/cluster_algebra_quiver/mutation_type.py b/sage/combinat/cluster_algebra_quiver/mutation_type.py
    new file mode 100644
    - +  
     1r"""
     2This file contains helper functions for detecting the mutation type of a cluster algebra or quiver.
     3
     4For the compendium on the cluster algebra and quiver package see
     5
     6http://arxiv.org/abs/1102.4844
     7
     8AUTHORS:
     9
     10- Gregg Musiker
     11- Christian Stump
     12"""
     13
     14#*****************************************************************************
     15#       Copyright (C) 2011 Gregg Musiker <musiker@math.mit.edu>
     16#                          Christian Stump <christian.stump@univie.ac.at>
     17#
     18#  Distributed under the terms of the GNU General Public License (GPL)
     19#                  http://www.gnu.org/licenses/
     20#*****************************************************************************
     21import time
     22from sage.groups.perm_gps.partn_ref.refinement_graphs import *
     23from sage.graphs.generic_graph import graph_isom_equivalent_non_edge_labeled_graph
     24from copy import copy
     25from sage.misc.all import cached_function
     26from sage.misc.flatten import flatten
     27from sage.rings.all import ZZ, infinity
     28from sage.graphs.all import Graph, DiGraph
     29from sage.matrix.all import matrix
     30from sage.combinat.all import Combinations
     31from sage.combinat.cluster_algebra_quiver.quiver_mutation_type import QuiverMutationType, QuiverMutationType_Irreducible, QuiverMutationType_Reducible
     32
     33def is_mutation_finite(M,nr_of_checks=None):
     34    r"""
     35    Uses a non-deterministic method by random mutations in various directions. Can result in a wrong answer.
     36
     37    .. ATTENTION: This method modifies the input matrix ``M``!
     38
     39    INPUT:
     40
     41    - ``nr_of_checks`` -- (default: None) number of mutations applied. Standard is 500*(number of vertices of self).
     42
     43    ALGORITHM:
     44
     45    A quiver is mutation infinite if and only if every edge label (a,-b) satisfy a*b > 4.
     46    Thus, we apply random mutations in random directions
     47
     48    EXAMPLES::
     49
     50        sage: from sage.combinat.cluster_algebra_quiver.mutation_type import is_mutation_finite
     51
     52        sage: Q = ClusterQuiver(['A',10])
     53        sage: M = Q.b_matrix()
     54        sage: is_mutation_finite(M)
     55        (True, None)
     56
     57        sage: Q = ClusterQuiver([(0,1),(1,2),(2,3),(3,4),(4,5),(5,6),(6,7),(7,8),(2,9)])
     58        sage: M = Q.b_matrix()
     59        sage: is_mutation_finite(M) # random
     60        (False, [9, 6, 9, 8, 9, 4, 0, 4, 5, 2, 1, 0, 1, 0, 7, 1, 9, 2, 5, 7, 8, 6, 3, 0, 2, 5, 4, 2, 6, 9, 2, 7, 3, 5, 3, 7, 9, 5, 9, 0, 2, 7, 9, 2, 4, 2, 1, 6, 9, 4, 3, 5, 0, 8, 2, 9, 5, 3, 7, 0, 1, 8, 3, 7, 2, 7, 3, 4, 8, 0, 4, 9, 5, 2, 8, 4, 8, 1, 7, 8, 9, 1, 5, 0, 8, 7, 4, 8, 9, 8, 0, 7, 4, 7, 1, 2, 8, 6, 1, 3, 9, 3, 9, 1, 3, 2, 4, 9, 5, 1, 2, 9, 4, 8, 5, 3, 4, 6, 8, 9, 2, 5, 9, 4, 6, 2, 1, 4, 9, 6, 0, 9, 8, 0, 4, 7, 9, 2, 1, 6])
     61    """
     62    import random
     63    n, m = M.ncols(), M.nrows()
     64    if nr_of_checks is None:
     65        nr_of_checks = 1000*n
     66    k = 0
     67    path = []
     68    for i in xrange(nr_of_checks):
     69        # this test is done to avoid mutating back in the same direction
     70        k_test = k
     71        while k_test == k:
     72            k = random.randint(0,n-1)
     73        M.mutate(k)
     74        path.append(k)
     75        for i,j in M.nonzero_positions():
     76            if i<j and M[i,j]*M[j,i] < -4:
     77                return False, path
     78    return True, None
     79
     80def _triangles( dg ):
     81    """
     82    Returns a list of all oriented triangles in the digraph ``dg``.
     83
     84    EXAMPLES::
     85
     86        sage: from sage.combinat.cluster_algebra_quiver.mutation_type import _triangles
     87        sage: Q = ClusterQuiver(['A',3])
     88        sage: _triangles(Q.digraph())
     89        []
     90        sage: Q.mutate([0,1])
     91        sage: _triangles(Q.digraph())
     92        [([(2, 0), (0, 1), (1, 2)], True)]
     93        sage: Q2 = ClusterQuiver(['A',[1,2],1])
     94        sage: _triangles(Q2.digraph())
     95        [([(1, 2), (1, 0), (2, 0)], False)]
     96        sage: Q2.mutate(2)
     97        sage: _triangles(Q2.digraph())
     98        [([(1, 0), (0, 2), (2, 1)], True)]
     99    """
     100    E = dg.edges(labels=False)
     101    V = dg.vertices()
     102    trians = []
     103    flat_trians = []
     104    for e in E:
     105        v1, v2 = e
     106        for v in V:
     107            if not v in e:
     108                if (v,v1) in E:
     109                    if (v2,v) in E:
     110                        flat_trian = sorted([v,v1,v2])
     111                        if flat_trian not in flat_trians:
     112                            flat_trians.append( flat_trian )
     113                            trians.append( ( [(v,v1),(v1,v2),(v2,v)], True ) )
     114                    elif (v,v2) in E:
     115                        flat_trian = sorted([v,v1,v2])
     116                        if flat_trian not in flat_trians:
     117                            flat_trians.append( flat_trian )
     118                            trians.append( ( [(v,v1),(v1,v2),(v,v2)], False ) )
     119                if (v1,v) in E:
     120                    if (v2,v) in E:
     121                        flat_trian = sorted([v,v1,v2])
     122                        if flat_trian not in flat_trians:
     123                            flat_trians.append( flat_trian )
     124                            trians.append( ( [(v1,v),(v1,v2),(v2,v)], False ) )
     125                    elif (v,v2) in E:
     126                        flat_trian = sorted([v,v1,v2])
     127                        if flat_trian not in flat_trians:
     128                            flat_trians.append( flat_trian )
     129                            trians.append( ( [(v1,v),(v1,v2),(v,v2)], False ) )
     130    return trians
     131
     132def _all_induced_cycles_iter( dg ):
     133    """
     134    Returns an iterator for all induced oriented cycles of length greater than or equal to 4 in the digraph ``dg``.
     135
     136    EXAMPLES::
     137
     138        sage: from sage.combinat.cluster_algebra_quiver.mutation_type import _all_induced_cycles_iter
     139        sage: Q = ClusterQuiver(['A',[6,0],1]); Q
     140        Quiver on 6 vertices of type ['D', 6]
     141        sage: _all_induced_cycles_iter(Q.digraph()).next()
     142        ([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 0)], True)
     143        sage: Q.mutate(0)
     144        sage: _all_induced_cycles_iter(Q.digraph()).next()
     145        ([(1, 2), (2, 3), (3, 4), (4, 5), (5, 1)], True)
     146        sage: Q2 = ClusterQuiver(['A',[2,3],1])
     147        sage: _all_induced_cycles_iter(Q2.digraph()).next()
     148        ([(1, 0), (1, 2), (3, 2), (3, 4), (4, 0)], False)
     149    """
     150    dg_new = DiGraph(dg)
     151    E = dg_new.edges()
     152    for v1,v2,label in E:
     153        dg_new.add_edge( (v2,v1,label) )
     154    induced_sets = []
     155    cycle_iter = dg_new.all_cycles_iterator(simple=True)
     156    for cycle in cycle_iter:
     157        if len(cycle)>3:
     158            cycle_set = set(cycle)
     159            if not any(cycle_set.issuperset(induced_set) for induced_set in induced_sets):
     160                induced_sets.append( cycle_set )
     161                if len(cycle)>4:
     162                    sg = dg.subgraph( cycle )
     163                    is_oriented = True
     164                    V = sg.vertices()
     165                    while is_oriented and V:
     166                        v = V.pop()
     167                        if not sg.in_degree(v) == 1:
     168                            is_oriented = False
     169                    yield ( sg.edges(labels=False), is_oriented )
     170
     171# a debug function
     172def _false_return(s=False):
     173    """
     174    Returns 'unknown'.  (Written for potential debugging purposes.)
     175
     176    EXAMPLES::
     177
     178        sage: from sage.combinat.cluster_algebra_quiver.mutation_type import _false_return
     179        sage: _false_return()
     180        'unknown'
     181    """
     182#    Uncomment these three lines for debugging purposes.
     183#    if s:
     184#        print 'DEBUG: error %s'%s
     185    return 'unknown'
     186
     187def _reset_dg( dg, vertices, dict_in_out, del_vertices ):
     188    """
     189    Deletes the specified vertices (del_vertices) from the DiGraph dg, and the lists vertices and dict_in_out.
     190
     191    Note that vertices and dict_in_out are the vertices of dg and a dictionary of in- and out-degrees that depend on the digrapu ``dg`` but they are passed through as arguments so the function can change their values.
     192
     193    EXAMPLES::
     194
     195        sage: from sage.combinat.cluster_algebra_quiver.mutation_type import _reset_dg
     196        sage: dg = ClusterQuiver(['A',[2,2],1]).digraph(); dg
     197        Digraph on 4 vertices
     198        sage: vertices = dg.vertices()
     199        sage: dict_in_out = {}
     200        sage: for v in vertices: dict_in_out[v] = (dg.in_degree(v), dg.out_degree(v), dg.degree(v))
     201        sage: _reset_dg(dg,vertices, dict_in_out, [1])
     202        sage: dg
     203        Digraph on 3 vertices
     204        sage: vertices
     205        [0, 2, 3]
     206        sage: dict_in_out
     207        {0: (1, 0, 1), 2: (1, 0, 1), 3: (0, 2, 2)}
     208    """
     209    del_vertices = list(set(del_vertices))
     210    for v in del_vertices:
     211        if v in dg:
     212            dg.delete_vertex(v)
     213        else:
     214            print v
     215            print dg.edges()
     216        vertices.remove(v)
     217        del dict_in_out[v]
     218    for v in vertices:
     219        dict_in_out[v] = (dg.in_degree(v), dg.out_degree(v), dg.degree(v))
     220
     221def _check_special_BC_cases( dg, n, check_letter_list, check_twist_list, hope_letter_list, conn_vert_list=False ):
     222    """
     223    Tests if dg (on at most `n` vertices) is a quiver of type `A` or `D` (as inputed in hope_letter_list) with conn_vert_list (if given)
     224    as connecting vertices.  Since this is supposed to be run on a ``dg`` coming from a larger quiver where vertices
     225    have already been removed (outside of the connecting vertices), this program therefore recognizes the type
     226    of the larger quiver as an `n`-vertex quiver of letter on ``check_letter_list`` and twist on ``check_twist_list``.
     227    This method is utilized in _connected_mutation_type to test for types BC, BB, CC, BD, or CD.
     228
     229    EXAMPLES::
     230
     231        sage: from sage.combinat.cluster_algebra_quiver.mutation_type import _check_special_BC_cases
     232        sage: dg = DiGraph(1)
     233        sage: _check_special_BC_cases(dg, 3, ['BC'], [1], ['A'], [[0]])
     234        ['BC', 2, 1]
     235        sage: dg = DiGraph(2); dg.add_edge([0,1])
     236        sage: _check_special_BC_cases(dg, 4, ['BC'], [1], ['A'], [[0]])
     237        ['BC', 3, 1]
     238        sage: dg = DiGraph(2); dg.add_edge([0,1])
     239        sage: _check_special_BC_cases(dg, 4, ['BB'], [1], ['A'], [[0,1]])
     240        ['BB', 3, 1]
     241        sage: _check_special_BC_cases(dg, 4, ['C', 'CD'], [None, None], ['A', 'D'], [ [], [0] ])
     242        ['C', 4]
     243        sage: dg.add_edges([[1, 2], [1, 3]])
     244        sage: _check_special_BC_cases(dg, 4, ['C', 'CD'], [None, None], ['A', 'D'], [ [], [0] ])
     245        ['CD', 3, 1]
     246    """
     247    # if dg is not connected, mutation type is not recognized.
     248    if not dg.is_connected():
     249        return 'unknown'
     250    # divides into cases depending on whether or not a list 'conn_vert_list' of connecting vertices is given.
     251    if conn_vert_list:
     252        mut_type = _connected_mutation_type_AAtildeD( dg, ret_conn_vert = True )
     253        # when 'conn_vert_list' is given, the output of _connected_mutation_type_AAtildeD is
     254        # either 'unknown' or a pair (mut_type, conn_verts).  Then, it is tested if the vertices can be glued together as desired.
     255        if not mut_type == 'unknown':
     256            mut_type, conn_verts = mut_type
     257    else:
     258        # when conn_vert_list == False, the output of _connected_mutation_type _AAtildeD is simply 'unknown' or the mutation type.
     259        # no 'connecting vertices' need to be computed.
     260        mut_type = _connected_mutation_type_AAtildeD( dg, ret_conn_vert = False )
     261        conn_verts = []
     262    # when the mutation type is recognized, program now tries more specifically to figure out 'letter' and 'twist'
     263    if not mut_type == 'unknown':
     264        for i in range( len( check_letter_list ) ):
     265            check_letter = check_letter_list[i]
     266            check_twist = check_twist_list[i]
     267            hope_letter = hope_letter_list[i]
     268            if conn_vert_list:
     269                conn_vert = set(conn_vert_list[i])
     270            else:
     271                conn_vert = set()
     272            # Now, tries to connect up the quiver components (keeping in mind ['D',3] - ['A',3] equivalence)
     273            if hope_letter == 'D' and mut_type._letter == 'A' and mut_type._rank == 3 and not mut_type._twist:
     274                hope_letter = 'A'
     275                if conn_vert_list: conn_verts = list( set(dg.vertices()).difference(conn_verts) )
     276            if mut_type._letter == hope_letter and not mut_type._twist and conn_vert.issubset(conn_verts):
     277                if len(check_letter)>1:
     278                    check_twist = 1
     279                if check_twist:
     280                    n -= 1
     281                return QuiverMutationType([check_letter, n, check_twist])
     282    return 'unknown'
     283
     284def _connected_mutation_type( dg ):
     285    """
     286    Assuming that ``dg`` is a connected digraph, checks the mutation type of ``dg`` as a valued quiver.
     287
     288    EXAMPLES::
     289
     290        sage: from sage.combinat.cluster_algebra_quiver.mutation_type import _connected_mutation_type
     291        sage: from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver
     292        sage: dg = ClusterQuiver(['A',3]).digraph(); _connected_mutation_type( dg )
     293        ['A', 3]
     294        sage: dg = ClusterQuiver(['D',7]).digraph(); _connected_mutation_type( dg )
     295        ['D', 7]
     296        sage: dg = ClusterQuiver(['BC',4,1]).digraph(); _connected_mutation_type( dg )
     297        ['BC', 4, 1]
     298    """
     299    dg = DiGraph( dg )
     300    # defining some shorthands
     301    n = dg.order()
     302    edges = dg.edges()
     303    vertices = dg.vertices()
     304    # initializing lists of the edges with labels (2,-1) or (1,-2); (4,-1) or (1,-4); or (2,-2), respectively
     305    exc_labels = []
     306    exc_labels41 = []
     307    double_edges = []
     308    letter = None
     309
     310    # replacing higher labels by multiple edges.  Multiple edges and acyclic is a sign that quiver is infinite mutation type with the exception of A_tilde where there might be one multiple edge with multiplicity 2.  Multiple edges is at least a sign that the quiver is of 'undetermined finite mutation type'.
     311    dg.allow_multiple_edges( True )
     312    for edge in edges:
     313        label = edge[2]
     314        if label not in [(1,-1),(2,-2),(1,-2),(2,-1),(4,-1),(1,-4)]:
     315    # _false_return(i) is a simple function that simply returns 'unknown'.  For debugging purposes, it
     316    # can also output 'DEBUG: error i' if desired.
     317    # this command is used many times in this code, something times without the argument i.
     318            return _false_return(2)
     319        elif label == (2,-2):
     320            dg.set_edge_label( edge[0], edge[1], 1 )
     321            dg.add_edge( edge[0], edge[1], 1 )
     322            double_edges.append( edge )
     323            if len( double_edges ) > 1:
     324                return _false_return()
     325        elif label == (1,-1):
     326            dg.set_edge_label( edge[0], edge[1], 1 )
     327        elif label in [(2,-1),(1,-2)]:
     328            exc_labels.append( edge )
     329        elif label in [(1,-4),(4,-1)]:
     330            exc_labels41.append( edge )
     331
     332    # creating a dictionary of in-, out- and total degrees
     333    dict_in_out = {}
     334    for v in vertices:
     335        dict_in_out[v] = (dg.in_degree(v), dg.out_degree(v), dg.degree(v))
     336
     337    if len( exc_labels ) + len( exc_labels41 ) + len( double_edges ) > 4:
     338        return _false_return()
     339
     340    # test for the labels (4,-1) and (1,-4) which can only appear in affine type BC
     341    if exc_labels41:
     342        # tests a two-vertex quiver to see if it is of type ['BC',1,1]
     343        if len(exc_labels41) == 1 and dict_in_out[exc_labels41[0][0]][2] == dict_in_out[exc_labels41[0][1]][2] == 1:
     344            return QuiverMutationType(['BC',1,1])
     345        # test if quiver contains a triangle T with edges [ (0, 1, (2, -1)), (2, 0, (2,-1)), (1, 2, (1, -4)) ] or [ (0, 1, (1, -2)), (2, 0, (1,-2)), (1, 2, (4, -1)) ].
     346        if len(exc_labels41) == 1 and len(exc_labels) == 2:
     347            bool2 = exc_labels41[0][2] == (4,-1) and exc_labels[0][2] == exc_labels[1][2] == (1,-2)
     348            bool3 = exc_labels41[0][2] == (1,-4) and exc_labels[0][2] == exc_labels[1][2] == (2,-1)
     349            if bool2 or bool3:
     350                v1,v2,label = exc_labels41[0]
     351                label1,label2 = exc_labels
     352                # delete the two vertices associated to the edge with label (1,-4) or (4,-1) and test if the rest of the quiver is of type A.
     353                # the third vertex of the triangle T should be a connecting_vertex.
     354                if label1[1] == label2[0] and label2[1] == v1 and v2 == label1[0] and dict_in_out[v1][2] == dict_in_out[v2][2] == 2:
     355                    _reset_dg( dg, vertices, dict_in_out, [v1,v2] )
     356                    return _check_special_BC_cases( dg, n, ['BC'], [1], ['A'], [[label1[1]]] )
     357                elif label1[0] == label2[1] and label1[1] == v1 and v2 == label2[0] and dict_in_out[v1][2] == dict_in_out[v2][2] == 2:
     358                    _reset_dg( dg, vertices, dict_in_out, [v1,v2] )
     359                    return _check_special_BC_cases( dg, n, ['BC'], [1], ['A'], [[label1[0]]] )
     360                else:
     361                    return _false_return()
     362            else:
     363                return _false_return()
     364        else:
     365            return _false_return()
     366
     367    # the program now performs further tests in the case that there are no edges of type (1,-4) nor (4,-1)
     368
     369    # first test for affine type C: if there are 4 exceptional labels, test if both belong to triangles with leaves
     370    if len( exc_labels ) == 4:
     371        exc_labels12 = [ label for label in exc_labels if label[2] == (1,-2) ]
     372        exc_labels21 = [ label for label in exc_labels if label[2] == (2,-1) ]
     373        # check that we have two labels of one kind and one label of the other
     374        if len( exc_labels12 ) != 2 or len( exc_labels21 ) != 2:
     375            return _false_return()
     376
     377        label121 = exc_labels12[0]
     378        label122 = exc_labels12[1]
     379        label211 = exc_labels21[0]
     380        label212 = exc_labels21[1]
     381
     382        # affine type B
     383        if label211[1] == label121[0] and label212[1] == label122[0]:
     384            pass
     385        elif label212[1] == label121[0] and label211[1] == label122[0]:
     386            label211, label212 = label212, label211
     387        # affine type C
     388        elif label121[1] == label211[0] and label122[1] == label212[0]:
     389            pass
     390        elif label122[1] == label211[0] and label121[1] == label212[0]:
     391            label211, label212 = label212, label211
     392        # affine type BC
     393        elif label121[1] == label211[0] and label212[1] == label122[0]:
     394            pass
     395        elif label121[1] == label212[0] and label211[1] == label122[0]:
     396            label211, label212 = label212, label211
     397        elif label122[1] == label211[0] and label212[1] == label121[0]:
     398            label121, label122 = label122, label121
     399        elif label122[1] == label212[0] and label211[1] == label121[0]:
     400            pass
     401        else:
     402            return _false_return()
     403
     404        # tests for which configuration the two (1,-2) and two (2,-1) edges are in.
     405        bool1 = dg.has_edge(label121[1],label211[0],1) and dict_in_out[label211[1]][0] == dict_in_out[label211[1]][1] == 1
     406        bool2 = dg.has_edge(label122[1],label212[0],1) and dict_in_out[label212[1]][0] == dict_in_out[label212[1]][1] == 1
     407        bool12 = not ( label121[1] == label122[1] and label211[0] == label212[0] )
     408        bool3 = dg.has_edge(label211[1],label121[0],1) and dict_in_out[label121[1]][0] == dict_in_out[label121[1]][1] == 1
     409        bool4 = dg.has_edge(label212[1],label122[0],1) and dict_in_out[label122[1]][0] == dict_in_out[label122[1]][1] == 1
     410        bool34 = not ( label211[1] == label212[1] and label121[0] == label122[0] )
     411        bool5 = dg.has_edge(label211[1],label121[0],1) and dict_in_out[label121[1]][0] == dict_in_out[label121[1]][1] == 1
     412        bool6 = dg.has_edge(label122[1],label212[0],1) and dict_in_out[label212[1]][0] == dict_in_out[label212[1]][1] == 1
     413        bool56 = not ( label211[1] == label122[1] and label121[0] == label212[0] )
     414        bool7 = dg.has_edge(label212[1],label122[0],1) and dict_in_out[label122[1]][0] == dict_in_out[label122[1]][1] == 1
     415        bool8 = dg.has_edge(label121[1],label211[0],1) and dict_in_out[label211[1]][0] == dict_in_out[label211[1]][1] == 1
     416        bool78 = not ( label212[1] == label121[1] and label122[0] == label211[0] )
     417
     418        nb1 = len( set(dg.neighbors(label121[1])).intersection(dg.neighbors(label211[0])) ) <= 1
     419        nb2 = len( set(dg.neighbors(label122[1])).intersection(dg.neighbors(label212[0])) ) <= 1
     420        nb3 = len( set(dg.neighbors(label211[1])).intersection(dg.neighbors(label121[0])) ) <= 1
     421        nb4 = len( set(dg.neighbors(label212[1])).intersection(dg.neighbors(label122[0])) ) <= 1
     422
     423        if bool1 and bool2 and bool12 and nb1 and nb2:
     424            v1,v2 = label211[1],label212[1]
     425            _reset_dg( dg, vertices, dict_in_out, [v1,v2] )
     426            return _check_special_BC_cases( dg, n, ['CC'], [1], ['A'] )
     427        if bool3 and bool4 and bool34 and nb3 and nb4:
     428            v1,v2 = label121[1],label122[1]
     429            _reset_dg( dg, vertices, dict_in_out, [v1,v2] )
     430            return _check_special_BC_cases( dg, n, ['BB'], [1], ['A'] )
     431        elif bool5 and bool6 and bool56 and nb2 and nb3:
     432            v1,v2 = label121[1],label212[1]
     433            _reset_dg( dg, vertices, dict_in_out, [v1,v2] )
     434            return _check_special_BC_cases( dg, n, ['BC'], [1], ['A'] )
     435        elif bool7 and bool8 and bool78 and nb1 and nb4:
     436            v1,v2 = label122[1],label211[1]
     437            _reset_dg( dg, vertices, dict_in_out, [v1,v2] )
     438            return _check_special_BC_cases( dg, n, ['BC'], [1], ['A'] )
     439        else:
     440            return _false_return()
     441
     442    # first test for affine type C: if there are three exceptional labels, we must be in both cases below of the same construction
     443    elif len( exc_labels ) == 3:
     444        exc_labels12 = [ label for label in exc_labels if label[2] == (1,-2) ]
     445        exc_labels21 = [ label for label in exc_labels if label[2] == (2,-1) ]
     446        # check that we have two labels of one kind and one label of the other
     447        if exc_labels12 == [] or exc_labels21 == []:
     448            return _false_return()
     449        if len( exc_labels12 ) == 2:
     450            label1,label2 = exc_labels12
     451            label3 = exc_labels21[0]
     452            if dict_in_out[label2[0]][2] == 1 or dict_in_out[label2[1]][2] == 1:
     453                label1, label2 = label2, label1
     454            if dict_in_out[label1[0]][2] == 1:
     455                v = label1[0]
     456                if label2[1] == label3[0] and dict_in_out[label2[1]][2] == 2 and dg.has_edge(label3[1],label2[0],1):
     457                    v1,v2 = label3[1],label2[0]
     458                    _reset_dg( dg, vertices, dict_in_out, [label2[1]] )
     459                    if len( set(dg.neighbors_out(v2)).intersection(dg.neighbors_in(v1)) ) > 0:
     460                        return _false_return()
     461                    elif len( set(dg.neighbors_out(v1)).intersection(dg.neighbors_in(v2)) ) > 0:
     462                        return _check_special_BC_cases( dg, n, ['BC'],[1],['A'],[[v1,v2]] )
     463                    else:
     464                        return _check_special_BC_cases( dg, n, ['BC'],[1],['A'] )
     465                elif label3[1] == label2[0] and dict_in_out[label3[1]][2] == 2 and dg.has_edge(label2[1],label3[0],1):
     466                    v1,v2 = label2[1],label3[0]
     467                    _reset_dg( dg, vertices, dict_in_out, [label3[1]] )
     468                    if len( set(dg.neighbors_out(v2)).intersection(dg.neighbors_in(v1)) ) > 0:
     469                        return _false_return()
     470                    elif len( set(dg.neighbors_out(v1)).intersection(dg.neighbors_in(v2)) ) > 0:
     471                        return _check_special_BC_cases( dg, n, ['CC'],[1],['A'],[[v1,v2]] )
     472                    else:
     473                        return _check_special_BC_cases( dg, n, ['CC'],[1],['A'] )
     474                else:
     475                    return _false_return()
     476            elif dict_in_out[label1[1]][2] == 1:
     477                v = label1[1]
     478                if label3[1] == label2[0] and dict_in_out[label3[1]][2] == 2 and dg.has_edge(label2[1],label3[0],1):
     479                    v1,v2 = label2[1],label3[0]
     480                    _reset_dg( dg, vertices, dict_in_out, [label3[1]] )
     481                    if len( set(dg.neighbors_out(v2)).intersection(dg.neighbors_in(v1)) ) > 0:
     482                        return _false_return()
     483                    elif len( set(dg.neighbors_out(v1)).intersection(dg.neighbors_in(v2)) ) > 0:
     484                        return _check_special_BC_cases( dg, n, ['BC'],[1],['A'],[[v1,v2]] )
     485                    else:
     486                        return _check_special_BC_cases( dg, n, ['BC'],[1],['A'] )
     487                elif label2[1] == label3[0] and dict_in_out[label2[1]][2] == 2 and dg.has_edge(label3[1],label2[0],1):
     488                    v1,v2 = label3[1],label2[0]
     489                    _reset_dg( dg, vertices, dict_in_out, [label2[1]] )
     490                    if len( set(dg.neighbors_out(v2)).intersection(dg.neighbors_in(v1)) ) > 0:
     491                        return _false_return()
     492                    elif len( set(dg.neighbors_out(v1)).intersection(dg.neighbors_in(v2)) ) > 0:
     493                        return _check_special_BC_cases( dg, n, ['BB'],[1],['A'],[[v1,v2]] )
     494                    else:
     495                        return _check_special_BC_cases( dg, n, ['BB'],[1],['A'] )
     496                else:
     497                    return _false_return()
     498            elif label1[1] == label2[1] == label3[0] and dict_in_out[label1[1]][2] == 3 and dg.has_edge(label3[1],label1[0],1) and dg.has_edge(label3[1],label2[0],1) and dict_in_out[label2[0]][2] == dict_in_out[label1[0]][2] == 2:
     499                _reset_dg( dg, vertices, dict_in_out, [label1[1]] )
     500                return _check_special_BC_cases( dg, n, ['BD'],[1],['D'] )
     501            elif label1[0] == label2[0] == label3[1] and dict_in_out[label1[0]][2] == 3 and dg.has_edge(label1[1],label3[0],1) and dg.has_edge(label2[1],label3[0],1) and dict_in_out[label2[1]][2] == dict_in_out[label1[1]][2] == 2:
     502                _reset_dg( dg, vertices, dict_in_out, [label1[0]] )
     503                return _check_special_BC_cases( dg, n, ['CD'],[1],['D'] )
     504            else:
     505                return _false_return()
     506        elif len( exc_labels21 ) == 2:
     507            label1,label2 = exc_labels21
     508            label3 = exc_labels12[0]
     509            if dict_in_out[label2[0]][2] == 1 or dict_in_out[label2[1]][2] == 1:
     510                label1, label2 = label2, label1
     511            if dict_in_out[label1[1]][2] == 1:
     512                v = label1[0]
     513                if label2[1] == label3[0] and dict_in_out[label2[1]][2] == 2 and dg.has_edge(label3[1],label2[0],1):
     514                    v1,v2 = label3[1],label2[0]
     515                    _reset_dg( dg, vertices, dict_in_out, [label2[1]] )
     516                    if len( set(dg.neighbors_out(v2)).intersection(dg.neighbors_in(v1)) ) > 0:
     517                        return _false_return()
     518                    elif len( set(dg.neighbors_out(v1)).intersection(dg.neighbors_in(v2)) ) > 0:
     519                        return _check_special_BC_cases( dg, n, ['CC'],[1],['A'],[[v1,v2]] )
     520                    else:
     521                        return _check_special_BC_cases( dg, n, ['CC'],[1],['A'] )
     522                elif label3[1] == label2[0] and dict_in_out[label3[1]][2] == 2 and dg.has_edge(label2[1],label3[0],1):
     523                    v1,v2 = label2[1], label3[0]
     524                    _reset_dg( dg, vertices, dict_in_out, [label3[1]] )
     525                    if len( set(dg.neighbors_out(v2)).intersection(dg.neighbors_in(v1)) ) > 0:
     526                        return _false_return()
     527                    elif len( set(dg.neighbors_out(v1)).intersection(dg.neighbors_in(v2)) ) > 0:
     528                        return _check_special_BC_cases( dg, n, ['BC'],[1],['A'],[[v1,v2]] )
     529                    else:
     530                        return _check_special_BC_cases( dg, n, ['BC'],[1],['A'] )
     531                else:
     532                    return _false_return()
     533            elif dict_in_out[label1[0]][2] == 1:
     534                v = label1[1]
     535                if label3[1] == label2[0] and dict_in_out[label3[1]][2] == 2 and dg.has_edge(label2[1],label3[0],1):
     536                    v1,v2 = label2[1],label3[0]
     537                    _reset_dg( dg, vertices, dict_in_out, [label3[1]] )
     538                    if len( set(dg.neighbors_out(v2)).intersection(dg.neighbors_in(v1)) ) > 0:
     539                        return _false_return()
     540                    elif len( set(dg.neighbors_out(v1)).intersection(dg.neighbors_in(v2)) ) > 0:
     541                        return _check_special_BC_cases( dg, n, ['BB'],[1],['A'],[[v1,v2]] )
     542                    else:
     543                        return _check_special_BC_cases( dg, n, ['BB'],[1],['A'] )
     544                elif label2[1] == label3[0] and dict_in_out[label2[1]][2] == 2 and dg.has_edge(label3[1],label2[0],1):
     545                    v1,v2 = label3[1],label2[0]
     546                    _reset_dg( dg, vertices, dict_in_out, [label2[1]] )
     547                    if len( set(dg.neighbors_out(v2)).intersection(dg.neighbors_in(v1)) ) > 0:
     548                        return _false_return()
     549                    elif len( set(dg.neighbors_out(v1)).intersection(dg.neighbors_in(v2)) ) > 0:
     550                        return _check_special_BC_cases( dg, n, ['BC'],[1],['A'],[[v1,v2]] )
     551                    else:
     552                        return _check_special_BC_cases( dg, n, ['BC'],[1],['A'] )
     553                else:
     554                    return _false_return()
     555            elif label1[0] == label2[0] == label3[1] and dict_in_out[label1[0]][2] == 3 and dg.has_edge(label1[1],label3[0],1) and dict_in_out[label1[1]][2] == 2 and dg.has_edge(label2[1],label3[0],1) and dict_in_out[label2[1]][2] == 2:
     556                _reset_dg( dg, vertices, dict_in_out, [label3[1]] )
     557                return _check_special_BC_cases( dg, n, ['BD'],[1],['D'] )
     558            elif label1[1] == label2[1] == label3[0] and dict_in_out[label3[0]][2] == 3 and dg.has_edge(label3[1],label1[0],1) and dict_in_out[label1[0]][2] == 2 and dg.has_edge(label3[1],label2[0],1) and dict_in_out[label2[0]][2] == 2:
     559                _reset_dg( dg, vertices, dict_in_out, [label3[0]] )
     560                return _check_special_BC_cases( dg, n, ['CD'],[1],['D'] )
     561            else:
     562                return _false_return()
     563
     564    # first test for finite types B and C: if there are two exceptional labels, they must belong to an oriented triangle and the vertex between must be a leaf
     565    # first test for affine type C: if there are two exceptional labels, they must belong to leaves
     566    # first test for affine type B: if there are two exceptional labels, they must be...
     567    elif len( exc_labels ) == 2:
     568        label1, label2 = exc_labels
     569        if label1[1] == label2[0]:
     570           pass
     571        elif label2[1] == label1[0]:
     572            label1, label2 = label2, label1
     573        else:
     574            # the exceptional case in affine type BC_2 is checked
     575            if label2[2] == (1,-2) and label1[2] == (2,-1):
     576                label1, label2 = label2, label1
     577            if label1[2] == (1,-2) and label2[2] == (2,-1):
     578                if label1[1] == label2[1] and dict_in_out[label1[1]][2] == 2 and dict_in_out[label1[0]][2] == 1 and dict_in_out[label2[0]][2] == 1:
     579                    return QuiverMutationType(['BC',2,1])
     580                elif label1[0] == label2[0] and dict_in_out[label1[0]][2] == 2 and dict_in_out[label1[1]][2] == 1 and dict_in_out[label2[1]][2] == 1:
     581                    return QuiverMutationType(['BC',2,1])
     582            # the cases in affine type B/C are checked where the exceptional labels connect to leaves
     583            v11, v12, label1 = label1
     584            v21, v22, label2 = label2
     585            if dict_in_out[v11][2] == 1:
     586                in_out1 = 'out'
     587            elif dict_in_out[v12][2] == 1:
     588                in_out1 = 'in'
     589            else:
     590                return _false_return()
     591            if dict_in_out[v21][2] == 1:
     592                in_out2 = 'out'
     593            elif dict_in_out[v22][2] == 1:
     594                in_out2 = 'in'
     595            else:
     596                return _false_return()
     597            if label1 == label2:
     598                if in_out1 == in_out2 == 'in':
     599                    if label1 == (1,-2):
     600                        return _check_special_BC_cases( dg, n, ['BB'],[1],['A'] )
     601                    else:
     602                        return _check_special_BC_cases( dg, n, ['CC'],[1],['A'] )
     603                elif in_out1 == in_out2 == 'out':
     604                    if label1 == (1,-2):
     605                        return _check_special_BC_cases( dg, n, ['CC'],[1],['A'] )
     606                    else:
     607                        return _check_special_BC_cases( dg, n, ['BB'],[1],['A'] )
     608                else:
     609                    return _check_special_BC_cases( dg, n, ['BC'],[1],['A'] )
     610            else:
     611                if in_out1 == in_out2:
     612                    return _check_special_BC_cases( dg, n, ['BC'],[1],['A'] )
     613                else:
     614                    if label1 == (1,-2):
     615                        if in_out1 == 'in':
     616                            return _check_special_BC_cases( dg, n, ['BB'],[1],['A'] )
     617                        else:
     618                            return _check_special_BC_cases( dg, n, ['CC'],[1],['A'] )
     619                    else:
     620                        if in_out1 == 'in':
     621                            return _check_special_BC_cases( dg, n, ['CC'],[1],['A'] )
     622                        else:
     623                            return _check_special_BC_cases( dg, n, ['BB'],[1],['A'] )
     624
     625        v1,v,label1 = label1
     626        v,v2,label2 = label2
     627        if dg.has_multiple_edges():
     628            if all( edge == (v2,v1,1) for edge in dg.multiple_edges() ):
     629                if dict_in_out[v2][2] == dict_in_out[v1][2] == 3:
     630                    _reset_dg( dg, vertices, dict_in_out, [v1,v2] )
     631                    if label1 == (1,-2) and label2 == (2,-1):
     632                        return _check_special_BC_cases( dg, n, ['CC'],[1],['A'],[[v]] )
     633                    elif label1 == (2,-1) and label2 == (1,-2):
     634                        return _check_special_BC_cases( dg, n, ['BB'],[1],['A'],[[v]] )
     635                    else:
     636                        return _false_return()
     637                elif dict_in_out[v][0] == dict_in_out[v][1] == 1:
     638                    dg.remove_multiple_edges()
     639                    dg = DiGraph( dg )
     640                    _reset_dg( dg, vertices, dict_in_out, [v] )
     641                    if dict_in_out[v1][0] == dict_in_out[v1][1] == dict_in_out[v2][0] == dict_in_out[v2][1] == 1 and dg.neighbor_out_iterator(v1).next() == dg.neighbor_in_iterator(v2).next():
     642                        if label1 == (2,-1) and label2 == (1,-2):
     643                            return _check_special_BC_cases( dg, n, ['CD'],[1],['A'] )
     644                        elif label1 == (1,-2) and label2 == (2,-1):
     645                            return _check_special_BC_cases( dg, n, ['BD'],[1],['A'] )
     646                    else:
     647                        return _false_return()
     648                else:
     649                    return _false_return()
     650            else:
     651                return _false_return()
     652        elif not dict_in_out[v][0] == 1 or not dict_in_out[v][1] == 1:
     653            return _false_return()
     654        else:
     655            if dg.has_edge(v2,v1,1):
     656                nr_same_neighbors = len( set(dg.neighbors_out(v1)).intersection(dg.neighbors_in(v2)) )
     657                nr_other_neighbors = len( set(dg.neighbors_out(v2)).intersection(dg.neighbors_in(v1)) )
     658                nr_contained_cycles = len([ cycle for cycle, is_oriented in _all_induced_cycles_iter( dg ) if v1 in flatten(cycle) and v2 in flatten(cycle) ] )
     659                if nr_same_neighbors + nr_other_neighbors + nr_contained_cycles > 2:
     660                    return _false_return()
     661                if label1 == (2,-1) and label2 == (1,-2):
     662                    if n == 4 and (nr_same_neighbors == 2 or nr_other_neighbors == 1):
     663                        return QuiverMutationType(['CD',n-1,1])
     664                    # checks for affine A
     665                    if nr_same_neighbors + nr_other_neighbors > 1:
     666                        mt_tmp = _check_special_BC_cases( dg, n, ['C','CD'],[None,None],['A','D'],[[],[v]] )
     667                    else:
     668                        _reset_dg( dg, vertices, dict_in_out, [v] )
     669                        mt_tmp = _check_special_BC_cases( dg, n, ['C','CD'],[None,None],['A','D'] )
     670                    if mt_tmp == 'unknown':
     671                        dg.delete_edges([[v2,v1],[v1,v],[v,v2]])
     672                        dg.add_edges([[v1,v2,1],[v,v1,1],[v2,v,1]])
     673                        if nr_same_neighbors + nr_other_neighbors > 1:
     674                            #_reset_dg( dg, vertices, dict_in_out, [v] )
     675                            return _check_special_BC_cases( dg, n, ['CD'],[None],['D'],[[v]] )
     676                        else:
     677                            return _check_special_BC_cases( dg, n, ['CD'],[None],['D'] )
     678                    else:
     679                        return mt_tmp
     680                elif label1 == (1,-2) and label2 == (2,-1):
     681                    if n == 4 and (nr_same_neighbors == 2 or nr_other_neighbors == 1):
     682                        return QuiverMutationType(['BD',n-1,1])
     683                    # checks for affine A
     684                    if nr_same_neighbors + nr_other_neighbors > 1:
     685                        mt_tmp = _check_special_BC_cases( dg, n, ['B','BD'],[None,None],['A','D'],[[],[v]] )
     686                    else:
     687                        _reset_dg( dg, vertices, dict_in_out, [v] )
     688                        mt_tmp = _check_special_BC_cases( dg, n, ['B','BD'],[None,None],['A','D'] )
     689                    if mt_tmp == 'unknown':
     690                        dg.delete_edges([[v2,v1],[v1,v],[v,v2]])
     691                        dg.add_edges([[v1,v2,1],[v,v1,1],[v2,v,1]])
     692                        if nr_same_neighbors + nr_other_neighbors > 1:
     693                            #_reset_dg( dg, vertices, dict_in_out, [v] )
     694                            return _check_special_BC_cases( dg, n, ['BD'],[None],['D'],[[v]] )
     695                        else:
     696                            return _check_special_BC_cases( dg, n, ['BD'],[None],['D'] )
     697                    else:
     698                        return mt_tmp
     699                else:
     700                    return _false_return()
     701            elif dict_in_out[v1][2] == 1 and dict_in_out[v2][2] == 1:
     702                if label1 == (1,-2) and label2 == (1,-2):
     703                    return _check_special_BC_cases( dg, n, ['BC'],[1],['A'] )
     704                elif label1 == (2,-1) and label2 == (2,-1):
     705                    return _check_special_BC_cases( dg, n, ['BC'],[1],['A'] )
     706                elif label1 == (1,-2) and label2 == (2,-1):
     707                    return _check_special_BC_cases( dg, n, ['CC'],[1],['A'] )
     708                elif label1 == (2,-1) and label2 == (1,-2):
     709                    return _check_special_BC_cases( dg, n, ['BB'],[1],['A'] )
     710                else:
     711                    return _false_return()
     712            elif dict_in_out[v][0] == dict_in_out[v][1] == 1 and dict_in_out[v1][0] == dict_in_out[v1][1] == 1 and dict_in_out[v2][0] == dict_in_out[v2][1] == 1:
     713                _reset_dg( dg, vertices, dict_in_out, [v] )
     714                if n == 4 and ( label1, label2 ) == ( (2,-1), (1,-2) ):
     715                    return _check_special_BC_cases( dg, n, ['CD'],[1],['A'] )
     716                elif n > 4 and ( label1, label2 ) == ( (2,-1), (1,-2) ):
     717                    return _check_special_BC_cases( dg, n, ['CD'],[1],['D'] )
     718                elif n == 4 and ( label1, label2 ) == ( (1,-2), (2,-1) ):
     719                    return _check_special_BC_cases( dg, n, ['BD'],[1],['A'] )
     720                elif n > 4 and ( label1, label2 ) == ( (1,-2), (2,-1) ):
     721                    return _check_special_BC_cases( dg, n, ['BD'],[1],['D'] )
     722                else:
     723                    return _false_return()
     724            else:
     725                return _false_return()
     726
     727    # second tests for finite types B and C: if there is only one exceptional label, it must belong to a leaf
     728    # also tests for affine type B: this exceptional label must belong to a leaf of a type D quiver
     729    elif len( exc_labels ) == 1:
     730        label = exc_labels[0]
     731        v_out = label[0]
     732        v_in  = label[1]
     733        label = label[2]
     734        if label == (1,-2):
     735            if dict_in_out[ v_in ][0] == 1 and dict_in_out[ v_in ][1] == 0:
     736                #_reset_dg( dg, vertices, dict_in_out, [v_in] )
     737                return _check_special_BC_cases( dg, n, ['B','BD'],[None,1],['A','D'],[[v_in],[v_in]] )
     738            elif dict_in_out[ v_out ][0] == 0 and dict_in_out[ v_out ][1] == 1:
     739                #_reset_dg( dg, vertices, dict_in_out, [v_out] )
     740                return _check_special_BC_cases( dg, n, ['C','CD'],[None,1],['A','D'],[[v_out],[v_out]] )
     741            else:
     742                return _false_return()
     743        elif label == (2,-1):
     744            if dict_in_out[ v_out ][0] == 0 and dict_in_out[ v_out ][1] == 1:
     745                #_reset_dg( dg, vertices, dict_in_out, [v_out] )
     746                return _check_special_BC_cases( dg, n, ['B','BD'],[None,1],['A','D'],[[v_out],[v_out]] )
     747            elif dict_in_out[ v_in ][0] == 1 and dict_in_out[ v_in ][1] == 0:
     748                #_reset_dg( dg, vertices, dict_in_out, [v_in] )
     749                return _check_special_BC_cases( dg, n, ['C','CD'],[None,1],['A','D'],[[v_in],[v_in]] )
     750            else:
     751                return _false_return()
     752
     753    # if no edges of type (1,-2) nor (2,-1), then tests for type A, affine A, or D.
     754    return _connected_mutation_type_AAtildeD( dg )
     755
     756def _connected_mutation_type_AAtildeD( dg, ret_conn_vert=False  ):
     757    """
     758    Returns mutation type of ClusterQuiver(dg) for DiGraph dg if it is of type finite A, affine A, or finite D.
     759    For all other types (including affine D), outputs 'unknown'
     760
     761    See http://arxiv.org/pdf/0906.0487.pdf (by Bastian, Prellberg, Rubey, and Stump) and http://arxiv.org/pdf/0810.4789v1.pdf (by Vatne) for theoretical details.
     762
     763    TODO: Improve this algorithm to also recognize affine D.
     764
     765    INPUT:
     766
     767    - ``ret_conn_vert`` (boolean; default:``False``). If ``True, returns 'connecting vertices', technical information that is used in the algorithm.
     768
     769    A brief description of the algorithm::
     770
     771         Looks for a long_cycle (of length >= 4) in the digraph dg.  If there is more than one than the mutation_type is 'unknown'.
     772         Otherwise, checks if each edge of long_cycle connects to a type A quiver.  If so, then ClusterQuiver(dg) is of type D or affine A.
     773
     774         If there is no long_cycle, then checks that there are no multiple edges, all triangles are oriented,
     775         no vertices of valence higher than 4, that vertices of valence 4 are incident to two oriented triangles,
     776         and that a vertex of valence 3 has exactly two incident arrows as part of an oriented triangle.
     777         All these checks ensures that ClusterQuiver(dg) is of type A except for three exceptions that are also checked.
     778
     779    EXAMPLES::
     780
     781        sage: from sage.combinat.cluster_algebra_quiver.mutation_type import _connected_mutation_type_AAtildeD
     782        sage: Q = ClusterQuiver(['A',[7,0],1]); Q.mutate([0,1,4])
     783        sage: _connected_mutation_type_AAtildeD(Q.digraph(),ret_conn_vert=True)
     784        [['D', 7], [0, 4]]
     785
     786        sage: Q2 = ClusterQuiver(['A',[5,2],1]); Q2.mutate([4,5])
     787        sage: _connected_mutation_type_AAtildeD(Q2.digraph() )
     788        ['A', [2, 5], 1]
     789
     790        sage: Q3 = ClusterQuiver(['E',6]); Q3.mutate([5,2,1]);
     791        sage: _connected_mutation_type_AAtildeD(Q3.digraph(),ret_conn_vert=True)
     792        'unknown'
     793    """
     794    # naming the vertices
     795    vertices = dg.vertices()
     796    n = dg.order()
     797
     798    # Test if ClusterQuiver(dg) is of type D_n Type 1, i.e. A_{n-2} plus two leaves
     799    if n > 3:
     800        # check if any vertices have a neighborhood with two leaves.  If so, prune the two leaves and retest on this smaller digraph.
     801        # note that this step is unnecessary for digraphs with fewer than four vertices.
     802        for v in vertices:
     803            dead_neighbors = [ v_n for v_n in dg.neighbors(v) if dg.degree(v_n) == 1 ]
     804            if len( dead_neighbors ) >= 2:
     805                dg_tmp = DiGraph( dg )
     806                dg_tmp.delete_vertices( dead_neighbors[:2] )
     807                type_tmp = _connected_mutation_type_AAtildeD( dg_tmp, ret_conn_vert=True )
     808                if type_tmp == 'unknown':
     809                    return _false_return()
     810                # if smaller digraph is of finite A type with v as a 'connecting vertex', then glueing back the two leaves yields type finite D.
     811                if type_tmp[0].letter() == 'A' and type_tmp[0].is_finite():
     812                    if v in type_tmp[1]:
     813                        type_tmp[1].remove(v)
     814                        if n == 4: type_tmp[1].extend( dead_neighbors[:2] )
     815                        if ret_conn_vert:
     816                            return [ QuiverMutationType( ['D',n] ), type_tmp[1] ]
     817                        else:
     818                            return QuiverMutationType( ['D',n] )
     819                    # note that if v is not a 'connecting vertex' then we make no conclusion either way.
     820                else:
     821                    return _false_return(3)
     822        # Test if ClusterQuiver(dg) is of type D_n Type 2 or 3, i.e. two type A quivers plus a 4-cycle or triangulated square glued together
     823        # at 'connecting vertices'.
     824
     825        # Exception 1 (Type 2 of D_n)
     826        exception_graph1 = DiGraph()
     827        exception_graph1.add_edges([(0,1),(1,2),(2,3),(3,0)])
     828
     829        # Exception 2 (Type 3 of D_n)
     830        exception_graph2 = DiGraph()
     831        exception_graph2.add_edges([(0,1),(1,2),(0,3),(3,2),(2,0)])
     832
     833        # Let c_1 be a pair of 2-valent vertices and c_2 be a pair of two other vertices.
     834        # If together, they make an induced 4-cycle and deleting c_1 yields two connected components,
     835        # then retest of both components.  If still connected after deleting c_1, then return 'unknown'.
     836
     837        # If on the other hand, (c1 and c2) is isomorphic to a triangulated square, then
     838        # delete c1.  This ensures that c2 is an edge of the triangulated square, and we delete
     839        # it irregardless of orientation.  Then check if the digraph has exactly two connected
     840        # components, and again this testing method is rerun on both components.
     841
     842        for c1 in Combinations( [ vertex for vertex in vertices if dg.degree(vertex) == 2], 2 ):
     843            del_vertices = list( vertices )
     844            del_vertices.remove( c1[0] )
     845            del_vertices.remove( c1[1] )
     846            for c2 in Combinations( del_vertices, 2 ):
     847                comb = c1 + c2
     848                sg = dg.subgraph( comb )
     849
     850                # Exception 1 case (4-cycle):
     851                if not (c1[0],c1[1]) in sg.edges(labels=False) and not (c1[1],c1[0]) in sg.edges(labels=False) and sg.is_isomorphic( exception_graph1 ):
     852                    dg_tmp = DiGraph( dg )
     853                    dg_tmp.delete_vertices( c1 )
     854
     855                    components = dg_tmp.connected_components()
     856                    #if not len( components ) == 2:
     857                    if len ( components ) != 2:
     858                        return _false_return(4)
     859                    else:
     860                        dg_tmp1 = dg_tmp.subgraph( components[0] )
     861                        type_tmp1 = _connected_mutation_type_AAtildeD( dg_tmp1, ret_conn_vert=True )
     862                        dg_tmp2 = dg_tmp.subgraph( components[1] )
     863                        type_tmp2 = _connected_mutation_type_AAtildeD( dg_tmp2, ret_conn_vert=True )
     864
     865                        if type_tmp1 == 'unknown' or type_tmp2 == 'unknown':
     866                            return _false_return()
     867
     868                        # Assuming that the two components are recognized, initialize this in a format it can be returned as output
     869                        type_tmp = []
     870                        type_tmp.append( [ type_tmp1[0], type_tmp2[0] ] )
     871                        type_tmp[0].sort()
     872                        type_tmp.append( type_tmp1[1] + type_tmp2[1] )
     873                        type_tmp[1].sort()
     874
     875                        # Need to make sure the two vertices in c2 are both 'connecting vertices'.
     876                        if not set(c2).issubset(type_tmp[1]):
     877                            return _false_return(5)
     878
     879                        if type_tmp[0][0].letter() == 'A' and type_tmp[0][0].is_finite() and type_tmp[0][1].letter() == 'A' and type_tmp[0][1].is_finite():
     880                            if ret_conn_vert:
     881                                type_tmp[1].extend(c1)
     882                                #type_tmp[1].remove(c2[0])
     883                                #type_tmp[1].remove(c2[1])
     884                                return [ QuiverMutationType( ['D',n] ), type_tmp[1] ]
     885                            else:
     886                                return QuiverMutationType( ['D',n] )
     887
     888                # Exception 2 case (triangulated square):
     889                if sg.is_isomorphic( exception_graph2 ):
     890                    dg_tmp = DiGraph( dg )
     891                    dg_tmp.delete_vertices( c1 )
     892                    if tuple( c2 ) in dg_tmp.edges(labels=False):
     893                        dg_tmp.delete_edge( tuple( c2 ) )
     894                    else:
     895                        c2.reverse()
     896                        dg_tmp.delete_edge( tuple( c2 ) )
     897                    components = dg_tmp.connected_components()
     898                    if len ( components ) != 2:
     899                        return _false_return(7)
     900                    else:
     901                        dg_tmp1 = dg_tmp.subgraph( components[0] )
     902                        type_tmp1 = _connected_mutation_type_AAtildeD( dg_tmp1, ret_conn_vert=True )
     903
     904                        if type_tmp1 == 'unknown':
     905                            return _false_return()
     906                        dg_tmp2 = dg_tmp.subgraph( components[1] )
     907                        type_tmp2 = _connected_mutation_type_AAtildeD( dg_tmp2, ret_conn_vert=True )
     908
     909                        # Assuming that the two components are recognized, initialize this in
     910                        # a format it can be returned as output (just as above)
     911                        type_tmp = []
     912                        type_tmp.append( [ type_tmp1[0], type_tmp2[0] ] )
     913                        type_tmp[0].sort()
     914                        type_tmp.append( type_tmp1[1] + type_tmp2[1] )
     915                        type_tmp[1].sort()
     916                        if type_tmp2 == 'unknown':
     917                            return _false_return()
     918                        if not set(c2).issubset(type_tmp[1]) and len( set(type_tmp[1]).intersection(c2) ) == 1:
     919                            return _false_return(5.5)
     920                        if type_tmp[0][0].letter() == 'A' and type_tmp[0][0].is_finite() and type_tmp[0][1].letter() == 'A' and type_tmp[0][1].is_finite():
     921                            if ret_conn_vert:
     922                                type_tmp[1].remove(c2[0])
     923                                type_tmp[1].remove(c2[1])
     924                                #type_tmp[1].extend(c1)
     925                                return [ QuiverMutationType( ['D',n] ), type_tmp[1] ]
     926                            else:
     927                                return QuiverMutationType( ['D',n] )
     928
     929    # The following tests are done regardless of the number of vertices in dg.
     930    # If there are 1, 2, or 3 vertices in dg, we would have skipped above tests and gone directly here.
     931
     932    # Initialize a long_cycle.
     933    long_cycle = False
     934
     935    # test that there is no triple-edge or higher multiplicity and that there is at most one double-edge.
     936    if dg.has_multiple_edges():
     937        multiple_edges = dg.multiple_edges(labels=False)
     938        if len( multiple_edges ) > 2:
     939            return _false_return(14)
     940        elif len( multiple_edges ) ==  2:
     941            # we think of the double-edge as a long_cycle, an unoriented 2-cycle.
     942            long_cycle = [ multiple_edges, ['A',n-1,1] ]
     943
     944    # creating a dictionary of in-, out- and total degrees
     945    dict_in_out = {}
     946    for v in vertices:
     947        dict_in_out[v] = (dg.in_degree(v), dg.out_degree(v), dg.degree(v))
     948
     949    # computing the absolute degree of dg
     950    abs_deg = max( [ x[2] for x in list( dict_in_out.values() ) ] )
     951
     952    edges = dg.edges( labels=False )
     953
     954    # test that no vertex has valency more than 4
     955    if abs_deg > 4:
     956        return _false_return(16)
     957    else:
     958        # constructing all oriented and unoriented triangles
     959        trians = _triangles( dg )
     960        oriented_trians = [ trian[0] for trian in trians if trian[1] ]
     961        unoriented_trians = [ trian[0] for trian in trians if not trian[1] ]
     962
     963        oriented_trian_edges = []
     964        for oriented_trian in oriented_trians:
     965            oriented_trian_edges.extend( oriented_trian )
     966
     967        # test that no edge is in more than two oriented triangles
     968        multiple_trian_edges = []
     969        for edge in oriented_trian_edges:
     970            count = oriented_trian_edges.count(edge)
     971            if count > 2:
     972                return _false_return(17)
     973            elif count == 2:
     974                multiple_trian_edges.append( edge )
     975        multiple_trian_edges = list(set(multiple_trian_edges))
     976
     977        # test that there at most three edges appearing in exactly two oriented triangles
     978        count = len( multiple_trian_edges )
     979        if count >= 4:
     980            return _false_return(321)
     981        # if two edges appearing in exactly two oriented triangles, test that the two edges together
     982        # determine a unique triangle
     983        elif count > 1:
     984            test_triangles = []
     985            for edge in multiple_trian_edges:
     986                test_triangles.append([ tuple(trian) for trian in oriented_trians if edge in trian ])
     987            unique_triangle = set(test_triangles[0]).intersection( *test_triangles[1:] )
     988            if len( unique_triangle ) != 1:
     989                return _false_return(19)
     990            else:
     991                # if a long_cycle had previously been found, this unique oriented triangle is a second long_cycle, a contradiction.
     992                if long_cycle:
     993                    return _false_return(20)
     994                else:
     995                    unique_triangle = unique_triangle.pop()
     996                    long_cycle = [ unique_triangle, QuiverMutationType( ['D',n] ) ]
     997        # if one edge appearing in exactly two oriented triangles, test that it is not a double-edge and then
     998        # test that either the third or fourth vertices (from the oriented triangles) is of degree 2.
     999        # Then initializes the long_cycle as this triangle including the degree 2 vertex, as long as no other long_cycles.
     1000        elif count == 1 and not dg.has_multiple_edges() and not multiple_trian_edges[0] in dg.multiple_edges():
     1001            multiple_trian_edge = multiple_trian_edges[0]
     1002            neighbors = list(set(dg.neighbors( multiple_trian_edge[0] )).intersection(dg.neighbors( multiple_trian_edge[1] )))
     1003            if dg.degree( neighbors[0] ) == 2:
     1004                unique_triangle = [ multiple_trian_edge, ( multiple_trian_edge[1], neighbors[0] ), ( neighbors[0], multiple_trian_edge[0] ) ]
     1005            elif dg.degree( neighbors[1] ) == 2:
     1006                unique_triangle = [ multiple_trian_edge, ( multiple_trian_edge[1], neighbors[1] ), ( neighbors[1], multiple_trian_edge[0] ) ]
     1007            else:
     1008                return _false_return(201)
     1009
     1010            if long_cycle:
     1011                # if a long_cycle had previously been found, then the specified oriented triangle is a second long_cycle, a contradiction.
     1012                return _false_return(202)
     1013            else:
     1014                long_cycle = [ unique_triangle, QuiverMutationType( ['D',n] ) ]
     1015
     1016        # there can be at most 1 unoriented triangle and this triangle is the exceptional circle of type A_tilde
     1017        if unoriented_trians:
     1018            if len(unoriented_trians) == 1:
     1019                if long_cycle:
     1020                    return _false_return(21)
     1021                else:
     1022                    long_cycle = [ unoriented_trians[0], ['A',n-1,1] ]
     1023            else:
     1024                return _false_return(22)
     1025
     1026        for v in vertices:
     1027            w = dict_in_out[v]
     1028            if w[2] == 4:
     1029                # if a vertex has valency 4 than the 4 neighboring edges must be contained in 2 oriented triangles
     1030                if w[0] != 2:
     1031                    return _false_return(23)
     1032                else:
     1033                    in_neighbors = dg.neighbors_in( v )
     1034                    out_neighbors = dg.neighbors_out( v )
     1035                    if len( out_neighbors ) == 1:
     1036                        out_neighbors.extend(out_neighbors)
     1037                    if len( in_neighbors ) == 1:
     1038                        in_neighbors.extend(in_neighbors)
     1039
     1040                    if not (in_neighbors[0],v) in oriented_trian_edges:
     1041                        return _false_return(24)
     1042                    elif not (in_neighbors[1],v) in oriented_trian_edges:
     1043                        return _false_return(25)
     1044                    elif not (v,out_neighbors[0]) in oriented_trian_edges:
     1045                        return _false_return(26)
     1046                    elif not (v,out_neighbors[1]) in oriented_trian_edges:
     1047                        return _false_return(27)
     1048
     1049            # if a vertex has valency 3 than 2 of its neighboring edges must be contained in an oriented triangle and the remaining must not
     1050            elif w[2] == 3:
     1051                if w[0] == 1:
     1052                    in_neighbors = dg.neighbors_in( v )
     1053                    out_neighbors = dg.neighbors_out( v )
     1054                    if (in_neighbors[0],v) not in oriented_trian_edges:
     1055                        return _false_return(28)
     1056                    elif len( out_neighbors ) == 1:
     1057                        if (v,out_neighbors[0]) not in oriented_trian_edges:
     1058                            return _false_return(29)
     1059                    else:
     1060                        if (v,out_neighbors[0]) in oriented_trian_edges and (v,out_neighbors[1]) in oriented_trian_edges:
     1061                            if not long_cycle:
     1062                                return _false_return(30)
     1063                            if not long_cycle[1] == QuiverMutationType(['D',n]):
     1064                                return _false_return(31)
     1065                            if not (v,out_neighbors[0]) in long_cycle[0] and not (v,out_neighbors[1]) in long_cycle[0]:
     1066                                return _false_return(32)
     1067                        if (v,out_neighbors[0]) not in oriented_trian_edges and (v,out_neighbors[1]) not in oriented_trian_edges:
     1068                            return _false_return(33)
     1069                elif w[0] == 2:
     1070                    in_neighbors = dg.neighbors_in( v )
     1071                    out_neighbors = dg.neighbors_out( v )
     1072                    if not (v,out_neighbors[0]) in oriented_trian_edges:
     1073                        return _false_return(34)
     1074                    elif len( in_neighbors ) == 1:
     1075                        if (in_neighbors[0],v) not in oriented_trian_edges:
     1076                            return _false_return(35)
     1077                    else:
     1078                        if (in_neighbors[0],v) in oriented_trian_edges and (in_neighbors[1],v) in oriented_trian_edges:
     1079                            if not long_cycle:
     1080                                return _false_return(36)
     1081                            if not long_cycle[1] == QuiverMutationType(['D',n]):
     1082                                return _false_return(37)
     1083                            if not (in_neighbors[0],v) in long_cycle[0] and not (in_neighbors[1],v) in long_cycle[0]:
     1084                                return _false_return(38)
     1085                        if (in_neighbors[0],v) not in oriented_trian_edges and (in_neighbors[1],v) not in oriented_trian_edges:
     1086                            return _false_return(39)
     1087                else:
     1088                    return _false_return(40)
     1089
     1090    # there can exist at most one larger oriented or unoriented induced cycle
     1091    # if it is oriented, we are in finite type D, otherwise we are in affine type A
     1092
     1093    # Above code found long_cycles would be an unoriented 2-cycle or an oriented triangle.
     1094    # The method _all_induced_cycles_iter only looks for induced cycles on 4 or more vertices.
     1095
     1096    for cycle, is_oriented in _all_induced_cycles_iter( dg ):
     1097        # if there already was a long_cycle and we found another one, then have a contradiction.
     1098        if long_cycle:
     1099            return _false_return(41)
     1100        # otherwise, we obtain cases depending on whether or not the found long_cycle is oriented.
     1101        elif is_oriented:
     1102            long_cycle = [ cycle, QuiverMutationType(['D',n]) ]
     1103        else:
     1104            long_cycle = [ cycle, ['A',n-1,1] ]
     1105    # if we haven't found a "long_cycle", we are in finite type A
     1106    if long_cycle == False:
     1107        long_cycle = [ [], QuiverMutationType(['A',n]) ]
     1108
     1109    # The 'connected vertices' are now computed.
     1110    # Attention: 0-1-2 in type A_3 has connecting vertices 0 and 2, while in type D_3 it has connecting vertex 1;
     1111    # this is not caught here.
     1112    if ret_conn_vert:
     1113        connecting_vertices = []
     1114        o_trian_verts = flatten( oriented_trian_edges )
     1115        long_cycle_verts = flatten( long_cycle[0] )
     1116        for v in vertices:
     1117            w = dict_in_out[v]
     1118            # if the quiver consists of only one vertex, it is of type A_1 and the vertex is a connecting vertex
     1119            if w[2] == 0:
     1120                connecting_vertices.append( v )
     1121            # if a vertex is a leaf in a type A quiver, it is a connecting vertex
     1122            elif w[2] == 1:
     1123                connecting_vertices.append( v )
     1124            # if a vertex is of valence two and contained in an oriented 3-cycle, it is a connecting vertex
     1125            elif w[0] == 1 and w[1] == 1:
     1126                if v in o_trian_verts and not v in long_cycle_verts:
     1127                    connecting_vertices.append( v )
     1128
     1129    # post-parsing 1: if we are in the affine type A case, the two parameters for the non-oriented long cycle are computed
     1130    if type( long_cycle[1] ) == list and len( long_cycle[1] ) == 3 and long_cycle[1][0] == 'A' and long_cycle[1][2] == 1:
     1131        tmp = list( long_cycle[0] )
     1132        e = tmp.pop()
     1133        cycle = [e]
     1134        v = e[1]
     1135        while tmp:
     1136            e = filter( lambda x: v in x, tmp)[0]
     1137            if v == e[0]:
     1138                cycle.append(e)
     1139                v = e[1]
     1140            else:
     1141                v = e[0]
     1142            tmp.remove( e )
     1143
     1144        tmp = list( cycle )
     1145        if len( long_cycle[0] ) == 2:
     1146            edge = long_cycle[0][0]
     1147            sg = DiGraph( dg )
     1148            sg. delete_vertices(edge)
     1149            connected_components = sg.connected_components()
     1150            cycle = []
     1151            if connected_components:
     1152                cycle.append( ( edge[0], edge[1], len( connected_components[0] ) + 1 ) )
     1153            else:
     1154                cycle.append( ( edge[0], edge[1], 1 ) )
     1155        else:
     1156            for edge in tmp:
     1157                sg = DiGraph( dg )
     1158                sg. delete_vertices(edge)
     1159                connected_components = sg.connected_components()
     1160                if len( connected_components ) == 2:
     1161                    #if len( list_intersection( [ connected_components[0], list_substract( long_cycle[0], [edge] )[0] ] ) ) > 0:
     1162                    if len( set(connected_components[0]).intersection( set(long_cycle[0]).difference([edge]).pop() ) ) > 0:
     1163                        cycle.remove(edge)
     1164                        cycle.append( (edge[0],edge[1], len( connected_components[1] ) + 1 ) )
     1165                    else:
     1166                        cycle.remove(edge)
     1167                        cycle.append( (edge[0],edge[1], len( connected_components[0] ) + 1 ) )
     1168                else:
     1169                    cycle.remove(edge)
     1170                    cycle.append( (edge[0],edge[1], 1 ) )
     1171        r = sum ( map( lambda x: x[2], cycle ) )
     1172        r = max ( r, n-r )
     1173        if ret_conn_vert:
     1174            return [ QuiverMutationType( ['A',[r,n-r],1] ), connecting_vertices ]
     1175        else:
     1176            return QuiverMutationType( ['A',[r,n-r],1] )
     1177
     1178    # post-parsing 2: if we are in another type, it is returned
     1179    else:
     1180        if ret_conn_vert:
     1181            return [ long_cycle[1], connecting_vertices ]
     1182        else:
     1183            return long_cycle[1]
     1184
     1185@cached_function
     1186def load_data( n ):
     1187    r"""
     1188    Loads a dict with keys being tuples representing exceptional QuiverMutationTypes,
     1189    and with values being lists or sets containing all mutation equivalent
     1190    quivers as dig6 data.
     1191
     1192    EXAMPLES::
     1193
     1194        sage: from sage.combinat.cluster_algebra_quiver.mutation_type import load_data
     1195        sage: load_data(2) # random
     1196        {('G', 2): [('AO', (((0, 1), (1, -3)),)), ('AO', (((0, 1), (3, -1)),))]}
     1197
     1198    """
     1199    import os.path
     1200    import cPickle
     1201    from sage.misc.misc import SAGE_SHARE
     1202    types_path = os.path.join(SAGE_SHARE, 'cluster_algebra_quiver')
     1203    types_file = types_path+'/mutation_classes_%s.dig6'%n
     1204    if os.path.isfile(types_file):
     1205        f = open(types_file,'r')
     1206        data = cPickle.load(f)
     1207        f.close()
     1208        return data
     1209    else:
     1210        return {}
     1211
     1212def _mutation_type_from_data( n, dig6, compute_if_necessary=True ):
     1213    r"""
     1214    Returns the mutation type from the given dig6 data by looking into the precomputed mutation types
     1215
     1216    Attention: it is assumed that dig6 is the dig6 data of the canonical form of the given quiver!
     1217
     1218    EXAMPLES::
     1219
     1220        sage: from sage.combinat.cluster_algebra_quiver.mutation_class import _digraph_to_dig6, _dg_canonical_form
     1221        sage: from sage.combinat.cluster_algebra_quiver.mutation_type import _mutation_type_from_data
     1222        sage: from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver
     1223        sage: dg = ClusterQuiver(['F',4]).canonical_label().digraph()
     1224        sage: dig6 = _digraph_to_dig6(dg,hashable=True); dig6
     1225        ('CCo?', (((1, 3), (2, -1)),))
     1226        sage: _mutation_type_from_data(4,dig6)
     1227        ['F', 4]
     1228    """
     1229    # we try to load the data from a library
     1230    data = load_data(n)
     1231    # if this didn't work, we construct all exceptional quivers with n vertices
     1232    if compute_if_necessary and data == {}:
     1233        from sage.combinat.cluster_algebra_quiver.quiver_mutation_type import save_quiver_data
     1234        save_quiver_data(n, up_to=False, types='Exceptional', verbose=False)
     1235        load_data.clear_cache()
     1236        data = load_data(n)
     1237    # finally, we check if the given quiver is in one of the exceptional mutation classes
     1238    for mutation_type in data:
     1239        if dig6 in data[ mutation_type ]:
     1240            return QuiverMutationType( mutation_type )
     1241    return 'unknown'
     1242
     1243def _mutation_type_test( n ):
     1244    """
     1245    Tests all quivers (of the given types) of rank n to check that mutation_type() works.
     1246    Affine type D does not return True since this test is not implemented.
     1247
     1248    EXAMPLES::
     1249
     1250        sage: from sage.combinat.cluster_algebra_quiver.mutation_type import _mutation_type_test
     1251
     1252        sage: _mutation_type_test(2) # long time
     1253        True ('A', 2)
     1254        True ('A', (1, 1), 1)
     1255        True ('B', 2)
     1256        True ('BC', 1, 1)
     1257        True ('G', 2)
     1258
     1259        sage: _mutation_type_test(3) # long time
     1260        True ('A', 3)
     1261        True ('A', (2, 1), 1)
     1262        True ('B', 3)
     1263        True ('BB', 2, 1)
     1264        True ('BC', 2, 1)
     1265        True ('C', 3)
     1266        True ('CC', 2, 1)
     1267        True ('G', 2, -1)
     1268        True ('G', 2, 1)
     1269
     1270        sage: _mutation_type_test(4) # long time
     1271        True ('A', 4)
     1272        True ('A', (2, 2), 1)
     1273        True ('A', (3, 1), 1)
     1274        True ('B', 4)
     1275        True ('BB', 3, 1)
     1276        True ('BC', 3, 1)
     1277        True ('BD', 3, 1)
     1278        True ('C', 4)
     1279        True ('CC', 3, 1)
     1280        True ('CD', 3, 1)
     1281        True ('D', 4)
     1282        True ('F', 4)
     1283        True ('G', 2, (1, 1))
     1284        True ('G', 2, (3, 3))
     1285        True ('G', 2, (1, 3))
     1286
     1287        sage: _mutation_type_test(5) # long time
     1288        True ('A', 5)
     1289        True ('A', (3, 2), 1)
     1290        True ('A', (4, 1), 1)
     1291        True ('B', 5)
     1292        True ('BB', 4, 1)
     1293        True ('BC', 4, 1)
     1294        True ('BD', 4, 1)
     1295        True ('C', 5)
     1296        True ('CC', 4, 1)
     1297        True ('CD', 4, 1)
     1298        False ('D', 4, 1)
     1299        True ('D', 5)
     1300        True ('F', 4, 1)
     1301        True ('F', 4, -1)
     1302    """
     1303    from sage.combinat.cluster_algebra_quiver.quiver_mutation_type import _construct_classical_mutation_classes
     1304    from sage.combinat.cluster_algebra_quiver.mutation_class import _dig6_to_matrix, _matrix_to_digraph, _digraph_mutate, _edge_list_to_matrix, _dig6_to_digraph
     1305    from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver
     1306    data = _construct_classical_mutation_classes( n )
     1307    keys = data.keys()
     1308    keys.sort()
     1309    for mutation_type in keys:
     1310        mt = QuiverMutationType( mutation_type )
     1311        print all( ClusterQuiver(_dig6_to_digraph(dig6)).mutation_type() == mt for dig6 in data[mutation_type]), mutation_type
     1312    from sage.combinat.cluster_algebra_quiver.quiver_mutation_type import _construct_exceptional_mutation_classes
     1313    data = _construct_exceptional_mutation_classes( n )
     1314    for mutation_type in data:
     1315        mt = QuiverMutationType( mutation_type )
     1316        print all( ClusterQuiver(_dig6_to_digraph(dig6)).mutation_type() == mt for dig6 in data[mutation_type]), mutation_type
     1317
     1318def _random_tests( mt, k, mut_class=None, nr_mut=5 ):
     1319    """
     1320    Provides random tests to find bugs in the mutation type methods
     1321
     1322    INPUT:
     1323
     1324    - ``mt`` something that can be turned into a QuiverMutationType
     1325    - ``k`` (integer) the number of tests performed for each quiver of rank ``n``
     1326    - ``mut_class`` is given, this mutation class is used
     1327    - ``nr_mut`` (integer, default:5) the number of mutations performed before testing
     1328
     1329    The idea of of this random test is to start with a mutation type and compute is mutation class
     1330    (or have this class given). Now, every quiver in this mutation class is slightly changed
     1331    in order to obtain a matrix of the same type or something very similar.
     1332    Now, the new type is computed and checked if it stays stable for ``nr_mut``'s many mutations.
     1333
     1334    TESTS::
     1335
     1336        sage: from sage.combinat.cluster_algebra_quiver.mutation_type import _random_tests
     1337        sage: _random_tests( ['A',3], 1)
     1338        testing ['A', 3]
     1339    """
     1340    from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver
     1341    from sage.combinat.cluster_algebra_quiver.mutation_class import _dig6_to_matrix, _matrix_to_digraph, _digraph_mutate, _edge_list_to_matrix, _dig6_to_digraph
     1342    import random
     1343    if mut_class is None:
     1344        mut_class = ClusterQuiver(mt).mutation_class(data_type='dig6')
     1345    print "testing " + str( mt )
     1346    for dig6 in mut_class:
     1347        M_const = _dig6_to_matrix( dig6 )
     1348        nz = [ (i,j) for i,j in M_const.nonzero_positions() if i > j ]
     1349        # performing k tests on the matrix M_const
     1350        for i in xrange(k):
     1351            M = copy( M_const )
     1352            # every pair M[i,j],M[j,i] is possibly changed
     1353            # while the property of being skew-symmetrizable is kept
     1354            for i,j in nz:
     1355                a,b = M[i,j],M[j,i]
     1356                skew_sym = False
     1357                while not skew_sym:
     1358                    ran = random.randint(1,2)
     1359                    if ran == 1:
     1360                        M[i,j], M[j,i] = -M[j,i], -M[i,j]
     1361                    elif ran == 2:
     1362                        ran2 = random.randint(1,8)
     1363                        if   ran2 == 1: c,d = 1,-1
     1364                        elif ran2 == 2: c,d = 1,-2
     1365                        elif ran2 == 3: c,d = 2,-1
     1366                        elif ran2 == 4: c,d = 1,-3
     1367                        elif ran2 == 5: c,d = 3,-1
     1368                        elif ran2 == 6: c,d = 2,-2
     1369                        elif ran2 == 7: c,d = 1,-4
     1370                        elif ran2 == 8: c,d = 4,-1
     1371                        M[i,j],M[j,i] = c,d
     1372                    if M.is_skew_symmetrizable( positive=True ):
     1373                        skew_sym = True
     1374                    else:
     1375                        M[i,j],M[j,i] = a,b
     1376            # we now have a new matrix M
     1377            # and a new digraph db
     1378            dg = _matrix_to_digraph( M )
     1379            mt = _connected_mutation_type( dg )
     1380            mut = -1
     1381            # we perform nr_mut many mutations
     1382            for i in xrange(nr_mut):
     1383                # while making sure that we do not mutate back
     1384                mut_tmp = mut
     1385                while mut == mut_tmp:
     1386                    mut = random.randint(0,dg.order()-1)
     1387                dg_new = _digraph_mutate( dg, mut, dg.order(), 0 )
     1388                M = _edge_list_to_matrix(dg.edges(),dg.order(),0)
     1389                M_new = _edge_list_to_matrix(dg_new.edges(),dg_new.order(),0)
     1390                mt_new = _connected_mutation_type( dg_new )
     1391                if not mt == mt_new:
     1392                    print "FOUND ERROR!"
     1393                    M1 = _edge_list_to_matrix( dg.edges(), dg.order(), 0 )
     1394                    print M1
     1395                    print "has mutation type " + str( mt ) + " while it has mutation type " + str(mt_new) + " after mutating at " + str(mut) + ":"
     1396                    M2 = _edge_list_to_matrix( dg_new.edges(), dg.order(), 0 )
     1397                    print M2
     1398                    return dg,dg_new
     1399                else:
     1400                    dg = dg_new
     1401
     1402def _random_multi_tests( n, k, nr_mut=5 ):
     1403    """
     1404    Provides multiple random tests to find bugs in the mutation type methods
     1405
     1406    INPUT:
     1407
     1408    - ``n`` (integer) the rank of the mutation types to test
     1409    - ``k`` (integer) the number of tests performed for each quiver of rank ``n``
     1410    - ``nr_mut`` (integer, default:5) the number of mutations performed before testing
     1411
     1412    TESTS::
     1413
     1414        sage: from sage.combinat.cluster_algebra_quiver.mutation_type import _random_multi_tests
     1415        sage: _random_multi_tests(2,100) # long time
     1416        testing ('A', 2)
     1417        testing ('A', (1, 1), 1)
     1418        testing ('B', 2)
     1419        testing ('BC', 1, 1)
     1420
     1421        sage: _random_multi_tests(3,100) # long time
     1422        testing ('A', 3)
     1423        testing ('A', (2, 1), 1)
     1424        testing ('B', 3)
     1425        testing ('BB', 2, 1)
     1426        testing ('BC', 2, 1)
     1427        testing ('C', 3)
     1428        testing ('CC', 2, 1)
     1429
     1430        sage: _random_multi_tests(4,100) # long time
     1431        testing ('A', 4)
     1432        testing ('A', (2, 2), 1)
     1433        testing ('A', (3, 1), 1)
     1434        testing ('B', 4)
     1435        testing ('BB', 3, 1)
     1436        testing ('BC', 3, 1)
     1437        testing ('BD', 3, 1)
     1438        testing ('C', 4)
     1439        testing ('CC', 3, 1)
     1440        testing ('CD', 3, 1)
     1441        testing ('D', 4)
     1442    """
     1443    from sage.combinat.cluster_algebra_quiver.quiver_mutation_type import _construct_classical_mutation_classes
     1444    mutation_classes = _construct_classical_mutation_classes( n )
     1445    for mutation_type in sorted(mutation_classes):
     1446        _random_tests( mutation_type, k, mut_class=mutation_classes[mutation_type], nr_mut=nr_mut )
  • sage/combinat/cluster_algebra_quiver/quiver.py

    diff --git a/sage/combinat/cluster_algebra_quiver/quiver.py b/sage/combinat/cluster_algebra_quiver/quiver.py
    a b AUTHORS: 
    1313- Gregg Musiker
    1414- Christian Stump
    1515
    16 .. seealso:: For mutation types of combinatorial quivers, see :meth:`~sage.combinat.cluster_algebra_quiver.quiver_mutation_type.QuiverMutationType`.
    1716.. 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`.
    1817"""
    1918
    from sage.misc.all import cached_method 
    3130from sage.rings.all import ZZ, CC, infinity
    3231from sage.graphs.all import Graph, DiGraph
    3332from sage.combinat.cluster_algebra_quiver.quiver_mutation_type import QuiverMutationType, QuiverMutationType_Irreducible, QuiverMutationType_Reducible, _edge_list_to_matrix
    34 from sage.combinat.cluster_algebra_quiver.mutation_class import _principal_part, _digraph_mutate, _matrix_to_digraph, _dg_canonical_form, _mutation_class_iter
     33from sage.combinat.cluster_algebra_quiver.mutation_class import _principal_part, _digraph_mutate, _matrix_to_digraph, _dg_canonical_form, _mutation_class_iter, _digraph_to_dig6, _dig6_to_matrix
     34from sage.combinat.cluster_algebra_quiver.mutation_type import _connected_mutation_type, _mutation_type_from_data, is_mutation_finite
     35
    3536from sage.groups.perm_gps.permgroup import PermutationGroup
    3637
    3738class ClusterQuiver(SageObject):
    class ClusterQuiver(SageObject): 
    7273
    7374            sage: Q = ClusterQuiver(['A', [5,0],1]); Q
    7475            Quiver on 5 vertices of type ['D', 5]
     76            sage: Q.is_finite()
     77            True
    7578            sage: Q.is_acyclic()
    7679            False
    7780
    class ClusterQuiver(SageObject): 
    312315                elif edge[2] in ZZ:
    313316                    dg.set_edge_label( edge[0], edge[1], (edge[2],-edge[2]) )
    314317                    edge = (edge[0],edge[1],(edge[2],-edge[2]))
    315                 elif type(edge[2]) == list and len(edge[2]) <> 2:
     318                elif type(edge[2]) == list and len(edge[2]) != 2:
    316319                    raise ValueError("The input digraph contains an edge with the wrong type of list as a label.")
    317320                elif type(edge[2]) == list and len(edge[2]) == 2:
    318321                    dg.set_edge_label( edge[0], edge[1], (edge[2][0], edge[2][1]))
    class ClusterQuiver(SageObject): 
    373376        """
    374377        name = self._description
    375378        if self._mutation_type:
    376             if type( self._mutation_type ) in [QuiverMutationType_Irreducible,QuiverMutationType_Reducible]:
     379            if type( self._mutation_type ) is str:
     380                name += ' of ' + self._mutation_type
     381            else:
    377382                name += ' of type ' + str(self._mutation_type)
    378             else:
    379                 name += ' of ' + self._mutation_type
    380383        if self._m == 1:
    381384            name += ' with %s frozen vertex'%self._m
    382385        elif self._m > 1:
    class ClusterQuiver(SageObject): 
    601604            self._default_filename = filename
    602605        except AttributeError:
    603606            pass
    604         if filename[-4:] <> '.qmu':
     607        if filename[-4:] != '.qmu':
    605608            filename = filename + '.qmu'
    606609        myfile = open(filename, 'w')
    607610        myfile.write('//Number of points'); myfile.write('\n')
    class ClusterQuiver(SageObject): 
    696699        """
    697700        Returns the mutation type of ``self``.
    698701
     702        Returns the mutation_type of each connected component of self if it can be determined,
     703        otherwise, the mutation type of this component is set to be unknown.
     704
     705        The mutation types of the components are ordered by vertex labels.
     706
     707        If you do many type recognitions, you should consider to save
     708        exceptional mutation types using
     709        `meth`:sage.combinat.cluster_algebra_quiver.quiver_mutation_type.save_exceptional_data
     710
     711        WARNING:
     712
     713        - All finite types can be detected,
     714        - All affine types can be detected, EXCEPT affine type D (the algorithm is not yet implemented)
     715        - All exceptional types can be detected.
     716
    699717        EXAMPLES::
    700718
    701719            sage: ClusterQuiver(['A',4]).mutation_type()
    class ClusterQuiver(SageObject): 
    706724            ['B', 2]
    707725            sage: ClusterQuiver(['B',4,1]).mutation_type()
    708726            ['BD', 4, 1]
     727
     728        - finite types::
     729
     730            sage: Q = ClusterQuiver(['A',5])
     731            sage: Q._mutation_type = None
     732            sage: Q.mutation_type()
     733            ['A', 5]
     734
     735            sage: Q = ClusterQuiver([(0,1),(1,2),(2,3),(3,4)])
     736            sage: Q.mutation_type()
     737            ['A', 5]
     738
     739        - affine types::
     740
     741            sage: Q = ClusterQuiver(['E',8,[1,1]]); Q
     742            Quiver on 10 vertices of type ['E', 8, [1, 1]]
     743            sage: Q._mutation_type = None; Q
     744            Quiver on 10 vertices
     745            sage: Q.mutation_type() # long time
     746            ['E', 8, [1, 1]]
     747
     748        - the not yet working affine type D (unless user has saved small classical quiver data)::
     749
     750            sage: Q = ClusterQuiver(['D',4,1])
     751            sage: Q._mutation_type = None
     752            sage: Q.mutation_type()    #indirect doctest
     753            'undetermined finite mutation type'
     754
     755        - the exceptional types::
     756
     757            sage: Q = ClusterQuiver(['X',6])
     758            sage: Q._mutation_type = None
     759            sage: Q.mutation_type() # long time
     760            ['X', 6]
     761
     762        - examples from page 8 of Keller's article "Cluster algebras, quiver representations
     763        and triangulated categories" (arXiv:0807.1960)::
     764
     765            sage: dg = DiGraph(); dg.add_edges([(9,0),(9,4),(4,6),(6,7),(7,8),(8,3),(3,5),(5,6),(8,1),(2,3)])
     766            sage: ClusterQuiver( dg ).mutation_type() # long time
     767            ['E', 8, [1, 1]]
     768
     769            sage: dg = DiGraph( { 0:[3], 1:[0,4], 2:[0,6], 3:[1,2,7], 4:[3,8], 5:[2], 6:[3,5], 7:[4,6], 8:[7] } )
     770            sage: ClusterQuiver( dg ).mutation_type() # long time
     771            ['E', 8, 1]
     772
     773            sage: dg = DiGraph( { 0:[3,9], 1:[0,4], 2:[0,6], 3:[1,2,7], 4:[3,8], 5:[2], 6:[3,5], 7:[4,6], 8:[7], 9:[1] } )
     774            sage: ClusterQuiver( dg ).mutation_type() # long time
     775            ['E', 8, [1, 1]]
     776
     777        - infinite types::
     778
     779            sage: Q = ClusterQuiver(['GR',[4,9]])
     780            sage: Q._mutation_type = None
     781            sage: Q.mutation_type()
     782            'undetermined infinite mutation type'
     783
     784        - reducible types::
     785            sage: Q = ClusterQuiver([['A', 3], ['B', 3]])
     786            sage: Q._mutation_type = None
     787            sage: Q.mutation_type()
     788            [ ['A', 3], ['B', 3] ]
     789           
     790            sage: Q = ClusterQuiver([['A', 3], ['T', [4,4,4]]])
     791            sage: Q._mutation_type = None
     792            sage: Q.mutation_type()
     793            [['A', 3], 'undetermined infinite mutation type']
     794           
     795            sage: Q = ClusterQuiver([['A', 3], ['B', 3], ['T', [4,4,4]]])
     796            sage: Q._mutation_type = None
     797            sage: Q.mutation_type()
     798            [['A', 3], ['B', 3], 'undetermined infinite mutation type']
     799
     800            sage: Q = ClusterQuiver([[0,1,2],[1,2,2],[2,0,2],[3,4,1],[4,5,1]])
     801            sage: Q.mutation_type()
     802            ['undetermined finite mutation type', ['A', 3]]
     803
     804        TESTS::
     805            sage: Q = ClusterQuiver(matrix([[0, 3], [-1, 0], [1, 0], [0, 1]]))
     806            sage: Q.mutation_type()
     807            ['G', 2]
     808            sage: Q = ClusterQuiver(matrix([[0, -1, -1, 1, 0], [1, 0, 1, 0, 1], [1, -1, 0, -1, 0], [-1, 0, 1, 0, 1], [0, -1, 0, -1, 0], [0, 1, 0, -1, -1], [0, 1, -1, 0, 0]]))
     809            sage: Q.mutation_type()
     810            'undetermined infinite mutation type'
    709811        """
     812        # checking if the mutation type is known already
     813        if self._mutation_type is None:
     814            # checking mutation type only for the principal part
     815            if self._m > 0:
     816                dg = self._digraph.subgraph( range(self._n) )
     817            else:
     818                dg = self._digraph
     819
     820            # checking the type for each connected component
     821            mutation_type = []
     822            connected_components = dg.connected_components()
     823            connected_components.sort()
     824            for component in connected_components:
     825                # constructing the digraph for this component
     826                dg_component = dg.subgraph( component )
     827                dg_component.relabel()
     828                # turning dg_component into a canonical form
     829                iso, orbits = _dg_canonical_form( dg_component, dg_component.num_verts(), 0 )
     830                # turning dg_component into a canonical form
     831                dig6 = _digraph_to_dig6( dg_component, hashable=True )
     832                # and getting the corresponding matrix
     833                M = _dig6_to_matrix(dig6)
     834
     835                # checking if this quiver is mutation infinite
     836                is_finite, path = is_mutation_finite(M)
     837                if is_finite is False:
     838                    mut_type_part = 'undetermined infinite mutation type'
     839                else:
     840                    # checking if this quiver is in the database
     841                    mut_type_part = _mutation_type_from_data( dg_component.order(), dig6, compute_if_necessary=False )
     842                    # checking if the algorithm can determine the mutation type
     843                    if mut_type_part == 'unknown':
     844                        mut_type_part = _connected_mutation_type(dg_component)
     845                    # checking if this quiver is of exceptional type by computing the exceptional mutation classes
     846                    if mut_type_part == 'unknown':
     847                        mut_type_part = _mutation_type_from_data(dg_component.order(), dig6, compute_if_necessary=True)
     848                    if mut_type_part == 'unknown':
     849                        mut_type_part = 'undetermined finite mutation type'
     850                mutation_type.append( mut_type_part )
     851
     852            # the empty quiver case
     853            if len( mutation_type ) == 0:
     854                Warning('Quiver has no vertices')
     855                mutation_type = None
     856            # the connected quiver case
     857            elif len( mutation_type ) == 1:
     858                mutation_type = mutation_type[0]
     859            # the reducible quiver case
     860            elif len( mutation_type ) > 1:
     861                if any( type(mut_type_part) is str for mut_type_part in mutation_type ):
     862                    pass
     863                else:
     864                    mutation_type = QuiverMutationType( mutation_type )
     865            self._mutation_type = mutation_type
    710866        return self._mutation_type
    711867
    712868    def n(self):
    class ClusterQuiver(SageObject): 
    10671223        EXAMPLES::
    10681224
    10691225            sage: Q = ClusterQuiver(['A',(2,3),1])
     1226            sage: Q.mutation_type()
     1227            ['A', [2, 3], 1]
    10701228
    10711229            sage: Q.reorient([(0,1),(1,2),(2,3),(3,4)])
     1230            sage: Q.mutation_type()
     1231            ['D', 5]
    10721232
    10731233            sage: Q.reorient([0,1,2,3,4])
     1234            sage: Q.mutation_type()
     1235            ['A', [1, 4], 1]
    10741236        """
    10751237        if all( 0 <= i and i < self._n + self._m for i in data ) and len( set( data ) ) == self._n+self._m :
    10761238            dg_new = DiGraph()
    class ClusterQuiver(SageObject): 
    11771339            (Quiver on 3 vertices of type ['A', 3], [2, 1, 2, 1])
    11781340            (Quiver on 3 vertices of type ['A', 3], [2, 1, 2, 0])
    11791341
    1180             sage: Q = ClusterQuiver(['A',3]) 
     1342            sage: Q = ClusterQuiver(['A',3])
    11811343            sage: it = Q.mutation_class_iter(data_type='path')
    1182             sage: for T in it: print T 
     1344            sage: for T in it: print T
    11831345            []
    11841346            [1]
    11851347            [0]
    11861348            [0, 1]
    11871349
    1188             sage: Q = ClusterQuiver(['A',3]) 
     1350            sage: Q = ClusterQuiver(['A',3])
    11891351            sage: it = Q.mutation_class_iter(return_paths=True,data_type='matrix')
    11901352            sage: it.next()
    11911353            (
    1192             [ 0  0  1]   
    1193             [ 0  0  1]   
     1354            [ 0  0  1]
     1355            [ 0  0  1]
    11941356            [-1 -1  0], []
    11951357            )
    11961358
    class ClusterQuiver(SageObject): 
    12561418            Quiver on 3 vertices of type ['A', 3]
    12571419
    12581420            sage: Ts = Q.mutation_class(show_depth=True)
    1259             Depth: 0     found: 1          Time: ... s           
     1421            Depth: 0     found: 1          Time: ... s
    12601422            Depth: 1     found: 3          Time: ... s
    12611423            Depth: 2     found: 4          Time: ... s
    12621424
    class ClusterQuiver(SageObject): 
    13041466            sage: Ts = Q.mutation_class(show_depth=True)
    13051467            Depth: 0     found: 1          Time: ... s
    13061468            Depth: 1     found: 3          Time: ... s
    1307             Depth: 2     found: 4          Time: ... s                                   
     1469            Depth: 2     found: 4          Time: ... s
    13081470
    13091471            sage: Ts = Q.mutation_class(show_depth=True, up_to_equivalence=False)
    13101472            Depth: 0     found: 1          Time: ... s
    13111473            Depth: 1     found: 4          Time: ... s
    13121474            Depth: 2     found: 6          Time: ... s
    1313             Depth: 3     found: 10        Time: ... s           
     1475            Depth: 3     found: 10        Time: ... s
    13141476            Depth: 4     found: 14        Time: ... s
    13151477
    13161478        TESTS::
    class ClusterQuiver(SageObject): 
    13211483            sage: all( len(ClusterQuiver(['B',n]).mutation_class()) == ClusterQuiver(['B',n]).mutation_type().class_size() for n in [2..6])
    13221484            True
    13231485        """
    1324         # runs forever without the mutation type recognition patch applied
     1486        if depth is infinity:
     1487            assert self.is_mutation_finite(), 'The mutation class can - for infinite mutation types - only be computed up to a given depth'
    13251488        return [ Q for Q in self.mutation_class_iter( depth=depth, show_depth=show_depth, return_paths=return_paths, data_type=data_type, up_to_equivalence=up_to_equivalence, sink_source=sink_source ) ]
     1489
     1490    def is_finite( self ):
     1491        """
     1492        Returns True if self is of finite type.
     1493
     1494        EXAMPLES::
     1495
     1496        sage: Q = ClusterQuiver(['A',3])
     1497        sage: Q.is_finite()
     1498        True
     1499        sage: Q = ClusterQuiver(['A',[2,2],1])
     1500        sage: Q.is_finite()
     1501        False
     1502        sage: Q = ClusterQuiver([['A',3],['B',3]])
     1503        sage: Q.is_finite()
     1504        True
     1505        sage: Q = ClusterQuiver(['T',[4,4,4]])
     1506        sage: Q.is_finite()
     1507        False
     1508        sage: Q = ClusterQuiver([['A',3],['T',[4,4,4]]])
     1509        sage: Q.is_finite()
     1510        False
     1511        sage: Q = ClusterQuiver([['A',3],['T',[2,2,3]]])
     1512        sage: Q.is_finite()
     1513        True
     1514        sage: Q = ClusterQuiver([['A',3],['D',5]])
     1515        sage: Q.is_finite()
     1516        True
     1517        sage: Q = ClusterQuiver([['A',3],['D',5,1]])
     1518        sage: Q.is_finite()
     1519        False
     1520
     1521        sage: Q = ClusterQuiver([[0,1,2],[1,2,2],[2,0,2]])
     1522        sage: Q.is_finite()
     1523        False
     1524
     1525        sage: Q = ClusterQuiver([[0,1,2],[1,2,2],[2,0,2],[3,4,1],[4,5,1]])
     1526        sage: Q.is_finite()
     1527        False
     1528        """
     1529        mt = self.mutation_type()
     1530        if type(mt) in (str, list) :
     1531            return False
     1532        else:
     1533            return mt.is_finite()
     1534
     1535    def is_mutation_finite( self, nr_of_checks=None, return_path=False ):
     1536        """
     1537        Uses a non-deterministic method by random mutations in various directions. Can result in a wrong answer.
     1538
     1539        INPUT:
     1540
     1541        - ``nr_of_checks`` -- (default: None) number of mutations applied. Standard is 500*(number of vertices of self).
     1542        - ``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.
     1543
     1544        ALGORITHM:
     1545
     1546        A quiver is mutation infinite if and only if every edge label (a,-b) satisfy a*b > 4.
     1547        Thus, we apply random mutations in random directions
     1548
     1549        EXAMPLES::
     1550
     1551            sage: Q = ClusterQuiver(['A',10])
     1552            sage: Q._mutation_type = None
     1553            sage: Q.is_mutation_finite()
     1554            True
     1555
     1556            sage: Q = ClusterQuiver([(0,1),(1,2),(2,3),(3,4),(4,5),(5,6),(6,7),(7,8),(2,9)])
     1557            sage: Q.is_mutation_finite()
     1558            False
     1559        """
     1560        if self._n <= 2:
     1561            is_finite = True
     1562            path = None
     1563        elif not return_path and self._mutation_type == 'undetermined infinite mutation type':
     1564            is_finite = False
     1565        elif type( self._mutation_type ) in [QuiverMutationType_Irreducible, QuiverMutationType_Reducible] and self._mutation_type.is_mutation_finite():
     1566            is_finite = True
     1567            path = None
     1568        elif not return_path and type( self._mutation_type ) in [QuiverMutationType_Irreducible, QuiverMutationType_Reducible] and not self._mutation_type.is_mutation_finite():
     1569            is_finite = False
     1570        else:
     1571            # turning dg_component into a canonical form
     1572            dig6 = _digraph_to_dig6(self.digraph())
     1573            # and getting the corresponding matrix
     1574            M = _dig6_to_matrix(dig6)
     1575
     1576            is_finite, path = is_mutation_finite(M,nr_of_checks=nr_of_checks)
     1577        if return_path:
     1578            return is_finite, path
     1579        else:
     1580            return is_finite
  • sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py

    diff --git a/sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py b/sage/combinat/cluster_algebra_quiver/quiver_mutation_type.py
    a b class QuiverMutationTypeFactory(SageObje 
    9494                else:
    9595                    data = ('D',max(data[1]),None)
    9696            elif data[0] == 'GR' and data[2] == None and type(data[1]) == tuple and len(data[1]) == 2 and data[1][1] > data[1][0]:
    97                 if min(data[1]) > max(data[1])/2 and max(data[1]) <> min(data[1])+1:
     97                if min(data[1]) > max(data[1])/2 and max(data[1]) != min(data[1])+1:
    9898                    data = (data[0],(max(data[1])-min(data[1]),max(data[1])),data[2])
    9999                if min(data[1]) == 2 and max(data[1]) > 3:
    100100                    data = ('A',max(data[1])-3,None)
    class QuiverMutationType_Irreducible(Qui 
    10661066                self._info['simply_laced'] = True
    10671067                self._info['skew_symmetric'] = True
    10681068                self._info['finite'] = True
    1069             elif twist==1 and type(rank) is list and len(rank) == 2 and all( rank[i] in ZZ and rank[i] >= 0 for i in [0,1] ) and rank <> [0,0]:
     1069            elif twist==1 and type(rank) is list and len(rank) == 2 and all( rank[i] in ZZ and rank[i] >= 0 for i in [0,1] ) and rank != [0,0]:
    10701070                if type( rank ) == tuple:
    10711071                    rank = list( rank )
    10721072                    data[1] = rank
    class QuiverMutationType_Reducible(Quive 
    19451945        comps = self.irreducible_components()
    19461946        return QuiverMutationType( [comp.dual() for comp in comps ] )
    19471947
     1948def _construct_classical_mutation_classes(n):
     1949    r"""
     1950    Returns a dict with keys being tuples representing regular QuiverMutationTypes of the given rank, and with values being
     1951    lists or sets containing all mutation equivalent quivers as dig6 data.
     1952
     1953    EXAMPLES::
     1954
     1955    sage: from sage.combinat.cluster_algebra_quiver.quiver_mutation_type import _construct_classical_mutation_classes
     1956    sage: rank_2_classes = _construct_classical_mutation_classes(2) # long time
     1957    sage: for mut_class in sorted(rank_2_classes.keys()): # long time
     1958    ...     print mut_class, rank_2_classes[mut_class]
     1959    ('A', 2) [('AO', ())]
     1960    ('A', (1, 1), 1) [('AO', (((0, 1), (2, -2)),))]
     1961    ('B', 2) [('AO', (((0, 1), (1, -2)),)), ('AO', (((0, 1), (2, -1)),))]
     1962    ('BC', 1, 1) [('AO', (((0, 1), (1, -4)),)), ('AO', (((0, 1), (4, -1)),))]
     1963    """
     1964    from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver
     1965    data = {}
     1966
     1967    # finite A
     1968    data[ ('A',n) ] = ClusterQuiver(['A',n]).mutation_class(data_type='dig6')
     1969    # affine A
     1970    for j in range(1, n/2+1):
     1971        data[ ('A',(n-j,j),1) ] = ClusterQuiver(['A',[n-j,j],1]).mutation_class(data_type='dig6')
     1972    # finite B
     1973    if n > 1:
     1974        data[ ('B',n) ] = ClusterQuiver(['B',n]).mutation_class(data_type='dig6')
     1975    # affine B
     1976    if n > 2:
     1977        data[ ('BB',n-1,1) ] = ClusterQuiver(['BB',n-1,1]).mutation_class(data_type='dig6')
     1978    # finite C
     1979    if n > 2:
     1980        data[ ('C',n) ] = ClusterQuiver(['C',n]).mutation_class(data_type='dig6')
     1981    # affine C
     1982    if n > 1:
     1983        data[ ('BC',n-1,1) ] = ClusterQuiver(['BC',n-1,1]).mutation_class(data_type='dig6')
     1984    # affine CC
     1985    if n > 2:
     1986        data[ ('CC',n-1,1) ] = ClusterQuiver(['CC',n-1,1]).mutation_class(data_type='dig6')
     1987    # affine BD
     1988    if n > 3:
     1989        data[ ('BD',n-1,1) ] = ClusterQuiver(['BD',n-1,1]).mutation_class(data_type='dig6')
     1990    # affine CD
     1991    if n > 3:
     1992        data[ ('CD',n-1,1) ] = ClusterQuiver(['CD',n-1,1]).mutation_class(data_type='dig6')
     1993    # finite D
     1994    if n > 3:
     1995        data[ ('D',n) ] = ClusterQuiver(['D',n]).mutation_class(data_type='dig6')
     1996    # affine D
     1997    if n > 4:
     1998        data[ ('D',n-1,1) ] = ClusterQuiver(['D',n-1,1]).mutation_class(data_type='dig6')
     1999
     2000    return data
     2001
     2002def _construct_exceptional_mutation_classes(n):
     2003    r"""
     2004    Returns a dict with keys being tuples representing exceptional QuiverMutationTypes
     2005    of the given rank, and with values being lists or sets containing all
     2006    mutation equivalent quivers as dig6 data.
     2007
     2008    EXAMPLES::
     2009
     2010    sage: from sage.combinat.cluster_algebra_quiver.quiver_mutation_type import _construct_exceptional_mutation_classes
     2011    sage: rank_3_exceptional = _construct_exceptional_mutation_classes(3) # long time
     2012    sage: for mut_class in sorted(rank_3_exceptional.keys()): # long time
     2013    ...     print mut_class, rank_3_exceptional[mut_class]
     2014    ('G', 2, -1) [('BH?', (((1, 2), (1, -3)),)), ('BGO', (((2, 1), (3, -1)),)), ('BW?', (((0, 1), (3, -1)),)), ('BP?', (((0, 1), (1, -3)),)),
     2015     ('BP_', (((0, 1), (1, -3)), ((2, 0), (3, -1)))), ('BP_', (((0, 1), (3, -1)), ((1, 2), (1, -3)), ((2, 0), (2, -2))))]
     2016    ('G', 2, 1) [('BH?', (((1, 2), (3, -1)),)), ('BGO', (((2, 1), (1, -3)),)), ('BW?', (((0, 1), (1, -3)),)), ('BP?', (((0, 1), (3, -1)),)),
     2017     ('BKO', (((1, 0), (3, -1)), ((2, 1), (1, -3)))), ('BP_', (((0, 1), (2, -2)), ((1, 2), (1, -3)), ((2, 0), (3, -1))))]
     2018    """
     2019    from sage.combinat.cluster_algebra_quiver.quiver import ClusterQuiver
     2020    data = {}
     2021    # finite E
     2022    if n in [6,7,8]:
     2023        data[ ('E',n) ] = ClusterQuiver(['E',n]).mutation_class(data_type='dig6')
     2024    # affine E
     2025    if n in [7,8,9]:
     2026        data[ ('E',n-1,1) ] = ClusterQuiver(['E',n-1,1]).mutation_class(data_type='dig6')
     2027    # elliptic E
     2028    if n in [8,9,10]:
     2029        data[ ('E',n-2,(1,1)) ] = ClusterQuiver(['E',n-2,[1,1]]).mutation_class(data_type='dig6')
     2030    # finite F
     2031    if n == 4:
     2032        data[ ('F',4) ] = ClusterQuiver(['F',4]).mutation_class(data_type='dig6')
     2033    # affine F
     2034    if n == 5:
     2035        data[ ('F',4,1) ] = ClusterQuiver(['F',4,1]).mutation_class(data_type='dig6')
     2036        data[ ('F',4,-1) ] = ClusterQuiver(['F',4,-1]).mutation_class(data_type='dig6')
     2037    # finite G
     2038    if n == 2:
     2039        data[ ('G',2) ] = ClusterQuiver(['G',2]).mutation_class(data_type='dig6')
     2040    # affine G
     2041    if n == 3:
     2042        data[ ('G',2,1) ] = ClusterQuiver(['G',2,1]).mutation_class(data_type='dig6')
     2043        data[ ('G',2,-1) ] = ClusterQuiver(['G',2,-1]).mutation_class(data_type='dig6')
     2044    # elliptic G
     2045    if n == 4:
     2046        data[ ('G',2,(1,3)) ] = ClusterQuiver(['G',2,(1,3)]).mutation_class(data_type='dig6')
     2047        data[ ('G',2,(1,1)) ] = ClusterQuiver(['G',2,(1,1)]).mutation_class(data_type='dig6')
     2048        data[ ('G',2,(3,3)) ] = ClusterQuiver(['G',2,(3,3)]).mutation_class(data_type='dig6')
     2049    # X
     2050    if n in [6,7]:
     2051        data[ ('X',n) ] = ClusterQuiver(['X',n]).mutation_class(data_type='dig6')
     2052    # elliptic F
     2053    if n == 6:
     2054        data[ ('F',4,(1,2)) ] = ClusterQuiver(['F',4,(1,2)]).mutation_class(data_type='dig6')
     2055        data[ ('F',4,(1,1)) ] = ClusterQuiver(['F',4,(1,1)]).mutation_class(data_type='dig6')
     2056        data[ ('F',4,(2,2)) ] = ClusterQuiver(['F',4,(2,2)]).mutation_class(data_type='dig6')
     2057
     2058    return data
     2059
     2060def _save_data_dig6(n, types='ClassicalExceptional', verbose=False):
     2061    """
     2062    Saves all exceptional mutation classes as dig6 data into the file ``exc_classes_n.dig6`` in the folder ``SAGE_SHARE``.
     2063    """
     2064    import os.path
     2065    import cPickle
     2066    data = {}
     2067    possible_types = ['Classical','ClassicalExceptional','Exceptional']
     2068    if types not in possible_types:
     2069        raise ValueError, 'The third input must be either ClassicalExceptional (default), Classical, or Exceptional.'
     2070
     2071    if types in possible_types[:2]:
     2072        data.update(_construct_classical_mutation_classes(n))
     2073    if types in possible_types[1:]:
     2074        data.update(_construct_exceptional_mutation_classes(n))
     2075
     2076    from sage.misc.misc import SAGE_SHARE
     2077    types_path = os.path.join(SAGE_SHARE, 'cluster_algebra_quiver')
     2078    types_file = types_path+'/mutation_classes_%s.dig6'%n
     2079    if not os.path.exists(types_path):
     2080        os.makedirs(types_path)
     2081    f = open(types_file,'w')
     2082    cPickle.dump(data, f)
     2083    f.close()
     2084    if verbose:
     2085        keys = data.keys()
     2086        keys.sort()
     2087        print "\nThe following types are saved to file", types_file,"and will now be used to determine quiver mutation types:"
     2088        print keys
     2089
     2090def save_quiver_data(n, up_to=True, types='ClassicalExceptional', verbose=True):
     2091    r"""
     2092    Saves mutation classes of certain quivers of ranks up to and equal to ``n`` or equal to ``n``
     2093    to ``SAGE_SHARE/cluster_algebra_quiver/mutation_classes_n.dig6.
     2094
     2095    This data will then be used to determine quiver mutation types.
     2096
     2097    INPUT:
     2098
     2099    - ``n``: the rank (or the upper limit on the rank) of the mutation classes that are being saved.
     2100   
     2101    - ``up_to`` -- (default:True) if True, saves data for ranks smaller than or equal to ``n``.
     2102      If False, saves data for rank exactly ``n``.
     2103
     2104    - ``types`` -- (default:'ClassicalExceptional') if all, saves data for both exceptional mutation-finite quivers and for classical quiver.
     2105      The input 'Exceptional' or 'Classical' is also allowed to save only part of this data.
     2106    """
     2107    if up_to is True:
     2108        ranks = range(1,n+1)
     2109    elif up_to is False:
     2110        ranks = [n]
     2111    for i in ranks:
     2112        _save_data_dig6(i,types=types,verbose=verbose)
     2113
    19482114def _bipartite_graph_to_digraph( g ):
    19492115    """
    19502116    Returns a digraph obtained from a bipartite graph g by choosing one