Ticket #8420: trac_8420_features_perfect_matchings_vf.patch

File trac_8420_features_perfect_matchings_vf.patch, 21.1 KB (added by vferay, 12 years ago)
  • sage/combinat/all.py

    # HG changeset patch
    # User Valentin Feray <feray at labri.fr>
    # Date 1268037679 -3600
    # Node ID a17ba6265a37c36301d173ed2fc2b215697646f7
    # Parent  1b7358d42afe78d2e23b291bd34975b28a938462
    #8420 : creation of a class of perfect matchings with several methods
    
    diff --git a/sage/combinat/all.py b/sage/combinat/all.py
    a b from symmetric_group_representations imp 
    1818from yang_baxter_graph import YangBaxterGraph
    1919#from hall_littlewood import HallLittlewood_qp, HallLittlewood_q, HallLittlewood_p
    2020
     21#Permutations
    2122from permutation import Permutation, Permutations, Arrangements, PermutationOptions, CyclicPermutations, CyclicPermutationsOfPartition
    2223
     24#PerfectMatchings
     25from perfect_matching import PerfectMatching, PerfectMatchings
     26
    2327# Integer lists lex
    2428
    2529from integer_list import IntegerListsLex
  • new file sage/combinat/perfect_matching.py

    diff --git a/sage/combinat/perfect_matching.py b/sage/combinat/perfect_matching.py
    new file mode 100644
    - +  
     1#from sage.combinat.permutation import Permutation_Class
     2from sage.structure.unique_representation import UniqueRepresentation
     3from sage.structure.parent import Parent
     4from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
     5from sage.misc.classcall_metaclass import ClasscallMetaclass
     6from sage.structure.element_wrapper import ElementWrapper
     7from sage.structure.element import Element
     8from sage.misc.cachefunc import cached_method
     9from sage.rings.integer import Integer
     10
     11class PerfectMatching(ElementWrapper):
     12    r"""
     13    Class of perfect matching.
     14
     15    An instance of the class can be created from a list of pairs or from a fixed point-free involution as follows::
     16
     17        sage: m=PerfectMatching([('a','e'),('b','c'),('d','f')]);m
     18        PerfectMatching [('a', 'e'), ('b', 'c'), ('d', 'f')]
     19        sage: n=PerfectMatching([3,8,1,7,6,5,4,2]);n
     20        PerfectMatching [(1, 3), (2, 8), (4, 7), (5, 6)]
     21        sage: isinstance(m,PerfectMatching)
     22        True
     23
     24    The parent, which is the set of perfect matching of the ground set, is automaticly created::
     25
     26        sage: n.parent()
     27        Set of perfect matchings of {1, 2, 3, 4, 5, 6, 7, 8}
     28
     29    If the ground set is ordered, one can, for example, ask if the matching is non crossing::
     30
     31        sage: PerfectMatching([(1, 4), (2, 3), (5, 6)]).is_non_crossing()
     32        True
     33    """
     34    #the data structure of the element is a list (accessible via x.value)
     35    wrapper_class = list
     36    __lt__ = ElementWrapper._lt_by_value
     37    #During the creation of the instance of the class, the function __classcall_private__ will be called instead of __init__ directly.
     38    __metaclass__ = ClasscallMetaclass
     39
     40    @staticmethod
     41    def __classcall_private__(cls,p):
     42        from sage.combinat.permutation import Permutation
     43        from sage.misc.flatten import flatten
     44        r"""
     45        This function tries to recognize the input (it can be either a list or a tuple of pairs, or a fix-point free involution given as a list or as a permutation), constructs the parent (enumerated set of PerfectMatchings of the ground set) and calls the __init__ function to construct our object.
     46
     47        EXAMPLES::
     48
     49            sage: m=PerfectMatching([('a','e'),('b','c'),('d','f')]);m
     50            PerfectMatching [('a', 'e'), ('b', 'c'), ('d', 'f')]
     51            sage: isinstance(m,PerfectMatching)
     52            True
     53            sage: n=PerfectMatching([3,8,1,7,6,5,4,2]);n
     54            PerfectMatching [(1, 3), (2, 8), (4, 7), (5, 6)]
     55            sage: n.parent()
     56            Set of perfect matchings of {1, 2, 3, 4, 5, 6, 7, 8}
     57            sage: PerfectMatching([(1, 4), (2, 3), (5, 6)]).is_non_crossing()
     58            True
     59
     60        TESTS::
     61
     62            sage: TestSuite(m).run()
     63        """
     64        #we have to extract from the argument p the set of objects of the matching and the list of pairs
     65        #First case: p is a list (resp tuple) of lists (resp tuple).
     66        if (isinstance(p,list) or isinstance(p,tuple)) and (
     67                all([isinstance(x,list) or isinstance(x,tuple) for x in p])):
     68            objects=flatten(p)
     69            data=(map(tuple,p))
     70        #Second case: p is a permutation or a list of integers, we have to check if it is a fix-point-free involution.
     71        elif ((isinstance(p,list) and all(map(lambda x: (isinstance(x,Integer) or isinstance(x,int)),p )))
     72                or isinstance(p,sage.combinat.permutation.Permutation_class)):
     73            p=Permutation(p)
     74            n=len(p)
     75            if not(p.cycle_type()==[2 for i in range(n//2)]):
     76                raise ValueError, "The permutation p (= %s) is not a fix-point-free involution"%p
     77            objects=range(1,n+1)
     78            data=p.to_cycles()
     79        #Third case: p is already a perfect matching
     80        elif isinstance(p,PerfectMatching):
     81            data=p.value
     82            objects=flatten(data)
     83        else:
     84            raise ValueError, "cannot convert p (= %s) to a PerfectMatching"%p
     85        #Finally, we create the parent and the element using the element class of the parent. Note: as this function is private, when we create an object via parent.element_class(...), __init__ is directly executed and we do not have an infinite loop.
     86        parent=PerfectMatchings(objects)
     87        return parent.element_class(data,parent)     
     88
     89    def __init__(self,data,parent):
     90        r"""
     91        see __classcall_private__?
     92        """
     93        self.value=data
     94        Element.__init__(self,parent=parent)
     95
     96    def _repr_(self):
     97        r"""
     98        returns the name of the object
     99
     100        EXAMPLES::
     101
     102            sage: m=PerfectMatching([('a','e'),('b','c'),('d','f')]);m
     103            PerfectMatching [('a', 'e'), ('b', 'c'), ('d', 'f')]
     104            sage: n=PerfectMatching([3,8,1,7,6,5,4,2]);n
     105            PerfectMatching [(1, 3), (2, 8), (4, 7), (5, 6)]
     106        """
     107        return 'PerfectMatching %s'%self.value
     108
     109    def __eq__(self,other):
     110        from sage.sets.set import Set
     111        r"""
     112        Compares two perfect matchings
     113
     114        EXAMPLES::
     115
     116            sage: m=PerfectMatching([('a','e'),('b','c'),('d','f')])
     117            sage: n=PerfectMatching([('c','b'),('d','f'),('e','a')])
     118            sage: n==m
     119            True
     120            sage: n==PerfectMatching([('a','b'),('d','f'),('e','c')])
     121            False
     122
     123        """
     124        try:
     125            if other.parent() <> self.parent():
     126                return False
     127        except AttributeError:
     128            return False
     129        return Set(map(Set,self.value))==Set(map(Set,other.value))
     130
     131    def size(self):
     132        r"""
     133        returns the size of the perfect matching, i.e. the number of elements in the ground set.
     134       
     135        EXAMPLES::
     136           
     137            sage: m=PerfectMatching([(-3, 1), (2, 4), (-2, 7)]); m.size()
     138            6
     139        """
     140        return 2*len(self.value)
     141
     142    def partner(self,x):
     143        r"""
     144        Returns the element in the same pair than x in the mathching `self`.
     145
     146        EXAMPLES::
     147           
     148            sage: m=PerfectMatching([(-3, 1), (2, 4), (-2, 7)]); m.partner(4)
     149            2
     150            sage: n=PerfectMatching([('c','b'),('d','f'),('e','a')]); n.partner('c')
     151            'b'
     152        """
     153        for i in range(self.size()):
     154            if self.value[i][0]==x:
     155                return self.value[i][1]
     156            if self.value[i][1]==x:
     157                return self.value[i][0]
     158        raise ValueError,"%s in not an element of the %s"%(x,self)
     159
     160    def loop_type(self,other=None):
     161        from sage.combinat.permutation import Permutation
     162        from sage.combinat.partition import Partition
     163        r"""
     164        input:
     165
     166            two perfect matchings of the same set (if the second argument is empty, the fonction an_element is called on the parent of the first)
     167
     168        output:
     169
     170            if we draw the two perfect matchings simultaneously as edges of a graph, the graph obtained is a union of cycles of even lengths. The function returns the ordered list of the semi-length of these cycles (considered as a partition)
     171
     172        EXAMPLES::
     173
     174            sage: m=PerfectMatching([('a','e'),('b','c'),('d','f')])
     175            sage: n=PerfectMatching([('a','b'),('d','f'),('e','c')])
     176            sage: m.loop_type(n)
     177            [2, 1]
     178        """
     179        if other is None:
     180            other=self.parent().an_element()
     181        elif self.parent() <> other.parent():
     182            raise ValueError,"%s is not a matching of the ground set of %s"%(other,self)
     183        n=self.size()
     184        A2N=dict([(self.parent()._objects[i],i+1) for i in range(n)])
     185        permself=Permutation(map(lambda x:tuple(map(lambda a:A2N[a],x)),
     186                self.value))
     187        permother=Permutation(map(lambda x:tuple(map(lambda a:A2N[a],x)),
     188                other.value))
     189        part=(permself*permother).cycle_type()
     190        part_exp=part.to_exp()
     191        l=[]
     192        for i in range(max(part),0,-1):
     193            for j in range(part_exp[i-1]//2):
     194                l.append(i)
     195        return Partition(l)
     196
     197    def number_of_loops(self,other=None):
     198        r"""
     199        input:
     200
     201            two perfect matchings of the same set (if the second argument is empty, the fonction an_element is called on the parent of the first)
     202
     203        output:
     204
     205            if we draw the two perfect matchings simultaneously as edges of a graph, the graph obtained is a union of cycles of even lengths. The function returns their numbers
     206
     207        EXAMPLES::
     208
     209            sage: m=PerfectMatching([('a','e'),('b','c'),('d','f')])
     210            sage: n=PerfectMatching([('a','b'),('d','f'),('e','c')])
     211            sage: m.number_of_loops(n)
     212            2
     213        """
     214        return len(self.loop_type(other))
     215
     216    def crossings(self):
     217        r"""
     218        input:
     219
     220            A perfect matching on an *totally ordered* ground set.
     221
     222        output:
     223
     224            We place the element of a ground set and draw the perfect matching by linking the elements of the same pair in the upper half-plane. This function returns the list of the pairs of crossing lines (as a line correspond to a pair, it returns a list of pairs of pairs).
     225
     226        EXAMPLES::
     227
     228            sage: n=PerfectMatching([3,8,1,7,6,5,4,2]); n
     229            PerfectMatching [(1, 3), (2, 8), (4, 7), (5, 6)]
     230            sage: n.crossings()
     231            [((1, 3), (2, 8))]
     232        """
     233        x=self.value[:]
     234        if len(x)==0:
     235            return []
     236        (i,j)=x.pop(0)
     237        res=PerfectMatching(x).crossings()
     238        for (a,b) in x:
     239            if (i<a<j<b) or (i<b<j<a) or (j<a<i<b) or (j<b<i<a) or (
     240                    a<i<b<j) or (a<j<b<i) or (b<i<a<j) or (b<j<a<i):
     241                res.append(((i,j),(a,b)))
     242        return res
     243
     244    def number_of_crossings(self):
     245        r"""
     246        input:
     247
     248            A perfect matching on an *totally ordered* ground set.
     249
     250        output:
     251
     252            We place the element of a ground set and draw the perfect matching by linking the elements of the same pair in the upper half-plane. This function returns the number the pairs of crossing lines.
     253
     254        EXAMPLES::
     255
     256            sage: n=PerfectMatching([3,8,1,7,6,5,4,2]); n
     257            PerfectMatching [(1, 3), (2, 8), (4, 7), (5, 6)]
     258            sage: n.number_of_crossings()
     259            1
     260        """
     261        x=self.value[:]
     262        if len(x)==0:
     263            return 0
     264        (i,j)=x.pop(0)
     265        res=PerfectMatching(x).number_of_crossings()
     266        for (a,b) in x:
     267            if (i<a<j<b) or (i<b<j<a) or (j<a<i<b) or (j<b<i<a) or (
     268                    a<i<b<j) or (a<j<b<i) or (b<i<a<j) or (b<j<a<i):
     269                res+=1
     270        return res
     271
     272    def is_non_crossing(self):
     273        r"""
     274        input:
     275
     276            A perfect matching on an *totally ordered* ground set.
     277
     278        output:
     279
     280            We place the element of a ground set and draw the perfect matching by linking the elements of the same pair in the upper half-plane. This function returns True if the picture obtained this way has no crossings.
     281
     282        EXAMPLES::
     283
     284            sage: n=PerfectMatching([3,8,1,7,6,5,4,2]); n
     285            PerfectMatching [(1, 3), (2, 8), (4, 7), (5, 6)]
     286            sage: n.is_non_crossing()
     287            False
     288            sage: PerfectMatching([(1, 4), (2, 3), (5, 6)]).is_non_crossing()
     289            True
     290        """
     291        x=self.value[:]
     292        if len(x)==0:
     293            return True
     294        (i,j)=x.pop(0)
     295        for (a,b) in x:
     296            if (i<a<j<b) or (i<b<j<a) or (j<a<i<b) or (j<b<i<a) or (
     297                    a<i<b<j) or (a<j<b<i) or (b<i<a<j) or (b<j<a<i):
     298                return False
     299        return PerfectMatching(x).is_non_crossing()
     300
     301    def Weingarten_function(self,other,N):
     302        r"""
     303        Returns the Weingarten function of two pairings. This function is the value of some integrals over the orhtogonal groups O_N.
     304
     305        EXAMPLES::
     306
     307        sage: var('N')
     308        N
     309        sage: m=PerfectMatching([(1,3),(2,4)])
     310        sage: n=PerfectMatching([(1,2),(3,4)])
     311        sage: factor(m.Weingarten_function(n,N))
     312        -1/((N - 1)*(N + 2)*N)
     313        """
     314        W=self.parent().Weingarten_matrix(N)
     315        return W[self.rank()][other.rank()]
     316
     317class PerfectMatchings(UniqueRepresentation,Parent):
     318    r"""
     319    Class of perfect matchings of a ground set. At the creation, the set can be given as any iterable object. If the argument is an integer n, it will be transformed into [1 .. n]::
     320
     321        sage: M=PerfectMatchings(6);M
     322        Set of perfect matchings of {1, 2, 3, 4, 5, 6}
     323        sage: PerfectMatchings([-1, -3, 1, 2])
     324        Set of perfect matchings of {1, 2, -3, -1}
     325
     326    One can ask for the list, the cardinality or an element of a set of perfect matching::
     327
     328        sage: PerfectMatchings(4).list()
     329        [PerfectMatching [(4, 1), (3, 2)], PerfectMatching [(4, 2), (3, 1)], PerfectMatching [(4, 3), (2, 1)]]
     330        sage: PerfectMatchings(8).cardinality()
     331        105
     332        sage: M=PerfectMatchings(('a', 'e', 'b', 'f', 'c', 'd'))
     333        sage: M.an_element()
     334        PerfectMatching [('a', 'e'), ('c', 'd'), ('b', 'f')]
     335        sage: all([PerfectMatchings(i).an_element() in PerfectMatchings(i) for i in range(2,11,2)])
     336        True
     337    """
     338
     339    @staticmethod
     340    def _parse_input(objects):
     341        from sage.sets.set import Set
     342        r"""
     343        This function tries to recognize the argument and to transform into a set. It is not meant to be called manually, but only as the first of the creation of an enumerated set of PerfectMatchings.
     344       
     345        EXAMPLES::
     346
     347            sage: PerfectMatchings._parse_input(4)
     348            {1, 2, 3, 4}
     349            sage: PerfectMatchings._parse_input(['a','b','c','e'])
     350            {'a', 'c', 'b', 'e'}
     351        """
     352        #if the argument is a python integer n, we replace it by the list [1 .. n]
     353        if isinstance(objects,int):
     354            objects=range(1,objects+1)
     355        #same thing if the argument is a sage integer.
     356        elif isinstance(objects,Integer):
     357            objects=range(1,objects+1)
     358        #Finally, if it is iterable, we return the corresponding set. Note that it is important to return a hashable object here (in particular, NOT A LIST), see comment below.
     359        if not hasattr(objects,'__iter__'):
     360            raise ValueError, "do not know how to construct a set of matchings from %s (it must be iterable)"
     361        return Set(objects)
     362
     363    @staticmethod
     364    def __classcall__(cls, objects):
     365        r"""
     366        This function is called automatically when the user want to create an enumerated set of PerfectMatchings.
     367
     368        EXAMPLES::
     369
     370            sage: M=PerfectMatchings(6);M
     371            Set of perfect matchings of {1, 2, 3, 4, 5, 6}
     372            sage: PerfectMatchings([-1, -3, 1, 2])
     373            Set of perfect matchings of {1, 2, -3, -1}
     374
     375        If one has already created a set of perfect matchings of the same set, it does not create a new object, but returns the already existing one::
     376
     377            sage: N=PerfectMatchings((2, 3, 5, 4, 1, 6))
     378            sage: N is M
     379            True
     380
     381        The class constructor does not check that the perfect matching is correct, one has to use the function __contains__ for that::
     382
     383            sage: m=PerfectMatching([(1,2,3),(4,5)])
     384            sage: isinstance(m,PerfectMatching)
     385            True
     386            sage: m in m.parent()
     387            False
     388
     389        TEST::
     390
     391            sage: TestSuite(M).run()
     392        """
     393        #we call the constructor of an other class, which will
     394        #    - check if the object has already been constructed (so the second argument, i.e. the output of _parse_input, must be hashable)
     395        #    - look for a place in memory and call the __init__ function
     396        return super(PerfectMatchings, cls).__classcall__(
     397                         cls, cls._parse_input(objects))
     398
     399    def __init__(self,objects):
     400        r"""
     401        See __classcall__?
     402        """
     403        self._objects=objects
     404        Parent.__init__(self, category = FiniteEnumeratedSets())
     405
     406    def _repr_(self):
     407        r"""
     408        Returns a description of `self`
     409
     410        EXAMPLES::
     411       
     412            sage: PerfectMatchings([-1, -3, 1, 2])
     413            Set of perfect matchings of {1, 2, -3, -1}
     414        """
     415        return "Set of perfect matchings of %s"%self._objects
     416
     417    def __iter__(self):
     418        r"""
     419        Returns an iterator for the elements of self
     420
     421        EXAMPLES::
     422
     423            sage: PerfectMatchings(4).list()
     424            [PerfectMatching [(4, 1), (3, 2)], PerfectMatching [(4, 2), (3, 1)], PerfectMatching [(4, 3), (2, 1)]]
     425        """
     426        if len(self._objects) == 0:
     427            yield self([])
     428        elif len(self._objects) == 1:
     429            pass
     430        else:
     431            l=list(self._objects)
     432            a=l.pop(-1)
     433            for i in range(len(l)):
     434                obj_rest=l[:]
     435                b=obj_rest.pop(i)
     436                for p in PerfectMatchings(obj_rest):
     437                    yield self([(a,b)]+p.value)
     438
     439
     440    def __contains__(self,x):
     441        from sage.misc.flatten import flatten
     442        from sage.combinat.permutation import Permutations
     443        r"""
     444        Tests if x is an element of self.
     445
     446        EXAMPLES::
     447
     448            sage: m=PerfectMatching([(1,2),(4,3)])
     449            sage: m in PerfectMatchings(4)
     450            True
     451            sage: m in PerfectMatchings((0, 1, 2, 3))
     452            False
     453            sage: all([m in PerfectMatchings(6) for m in PerfectMatchings(6)])
     454            True
     455
     456        Note that the class of x does not need to be PerfectMatching: if the data defines a perfect matching of the good set, the function returns True::
     457
     458            sage: [(1, 4), (2, 3)] in PerfectMatchings(4)
     459            True
     460
     461        The class constructor does not check that the perfect matching is correct, one has to use the function __contains__ for that::
     462
     463            sage: m=PerfectMatching([(1,2,3),(4,5)])
     464            sage: isinstance(m,PerfectMatching)
     465            True
     466            sage: m in m.parent()
     467            False
     468        """
     469        if not isinstance(x,PerfectMatching):
     470            try:
     471                x=PerfectMatching(x)
     472            except ValueError:
     473                return False
     474        if x.parent() is not self:
     475            return False
     476        A2N=dict([(self._objects[i],i+1) for i in range(len(self._objects))])
     477        if not all([len(i)==2 for i in x.value]):
     478            return False
     479        if map(lambda a:A2N[a],flatten(x.value)) not in Permutations(x.size()):
     480            return False
     481        return True
     482 
     483    def cardinality(self):
     484        from sage.misc.misc_c import prod
     485        r"""
     486        Returns the cardinality of the set of perfect matching `self`, that is 1*3*5*...*(2n-1), where 2n is the size of the ground set.
     487
     488        EXAMPLES::
     489
     490            sage: PerfectMatchings(8).cardinality()
     491            105
     492        """
     493        n=len(self._objects)
     494        if n%2==1:
     495            return 0
     496        else:
     497            return prod(i for i in range(n) if i%2==1)
     498
     499    def an_element(self):
     500        r"""
     501        Returns an element of self.
     502
     503        EXAMPLES::
     504
     505            sage: M=PerfectMatchings(('a', 'e', 'b', 'f', 'c', 'd'))
     506            sage: M.an_element()
     507            PerfectMatching [('a', 'e'), ('c', 'd'), ('b', 'f')]
     508            sage: all([PerfectMatchings(i).an_element() in PerfectMatchings(i) for i in range(2,11,2)])
     509            True
     510        """
     511        n=len(self._objects)//2
     512        return PerfectMatching([(self._objects[i],self._objects[i+n]) for i in range(n)])
     513
     514    @cached_method
     515    def Weingarten_matrix(self,N):
     516        from sage.matrix.constructor import Matrix
     517        r"""
     518        Returns the Weingarten matrix corresponding to the set of PerfectMatchings `self`. It is a useful theoretical tool to compute polynomial integral over the orthogonal group O_N.
     519
     520        EXAMPLES::
     521
     522            sage: M=PerfectMatchings(4).Weingarten_matrix(var('N'))
     523            sage: N*(N-1)*(N+2)*M.apply_map(factor)
     524            [N + 1    -1    -1]
     525            [   -1 N + 1    -1]
     526            [   -1    -1 N + 1]
     527        """
     528        G=Matrix([ [N**(p1.number_of_loops(p2)) for p1 in self]
     529            for p2 in self])
     530        return G**(-1)
     531
     532    Element=PerfectMatching
     533