| 1 | r""" |
| 2 | Perfect matchings |
| 3 | |
| 4 | A perfect matching of a set `S` is a partition into 2-element sets. If `S`is |
| 5 | the set `{1,..,n}`, it is equivalent to fixpoint-free involutions. These simple |
| 6 | combinatorial objects appear in different domains: |
| 7 | |
| 8 | - combinatorics of orthogonal polynomials (A. de Medicis et X.Viennot, |
| 9 | Moments des q-polynomes de Laguerre et la bijection de Foata-Zeilberger, |
| 10 | Adv. Appl. Math., 15 (1994), 262-304) |
| 11 | |
| 12 | - combinatorics of hyperoctahedral group, double coset algebra and zonal |
| 13 | polynomials (I. G. Macdonald, Symmetric functions and Hall polynomials, |
| 14 | Oxford University Press, second edition, 1995, chapter VII). |
| 15 | |
| 16 | AUTHOR: |
| 17 | |
| 18 | - Valentin Feray, 2010 : initial version |
| 19 | |
| 20 | EXAMPLES: |
| 21 | |
| 22 | Create a perfect matching:: |
| 23 | |
| 24 | sage: m=PerfectMatching([('a','e'),('b','c'),('d','f')]);m |
| 25 | PerfectMatching [('a', 'e'), ('b', 'c'), ('d', 'f')] |
| 26 | |
| 27 | Count its crossings, if the ground set is totally ordered:: |
| 28 | |
| 29 | sage: n=PerfectMatching([3,8,1,7,6,5,4,2]); n |
| 30 | PerfectMatching [(1, 3), (2, 8), (4, 7), (5, 6)] |
| 31 | sage: n.number_of_crossings() |
| 32 | 1 |
| 33 | |
| 34 | List the perfect matchings of a given ground set:: |
| 35 | |
| 36 | sage: PerfectMatchings(4).list() |
| 37 | [PerfectMatching [(4, 1), (3, 2)], PerfectMatching [(4, 2), (3, 1)], PerfectMatching [(4, 3), (2, 1)]] |
| 38 | |
| 39 | #***************************************************************************** |
| 40 | # Copyright (C) 2010 Valentin Feray <feray@labri.fr> |
| 41 | # |
| 42 | # Distributed under the terms of the GNU General Public License (GPL) |
| 43 | # http://www.gnu.org/licenses/ |
| 44 | #***************************************************************************** |
| 45 | |
| 46 | """ |
| 47 | |
| 48 | #from sage.combinat.permutation import Permutation_Class |
| 49 | from sage.structure.unique_representation import UniqueRepresentation |
| 50 | from sage.structure.parent import Parent |
| 51 | from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets |
| 52 | from sage.misc.classcall_metaclass import ClasscallMetaclass |
| 53 | from sage.structure.element_wrapper import ElementWrapper |
| 54 | from sage.structure.element import Element |
| 55 | from sage.misc.cachefunc import cached_method |
| 56 | from sage.rings.integer import Integer |
| 57 | from sage.misc.flatten import flatten |
| 58 | from sage.combinat.permutation import Permutation |
| 59 | from sage.sets.set import Set |
| 60 | from sage.combinat.partition import Partition |
| 61 | from sage.misc.misc_c import prod |
| 62 | from sage.matrix.constructor import Matrix |
| 63 | |
| 64 | class PerfectMatching(ElementWrapper): |
| 65 | r""" |
| 66 | Class of perfect matching. |
| 67 | |
| 68 | An instance of the class can be created from a list of pairs or from a |
| 69 | fixed point-free involution as follows:: |
| 70 | |
| 71 | sage: m=PerfectMatching([('a','e'),('b','c'),('d','f')]);m |
| 72 | PerfectMatching [('a', 'e'), ('b', 'c'), ('d', 'f')] |
| 73 | sage: n=PerfectMatching([3,8,1,7,6,5,4,2]);n |
| 74 | PerfectMatching [(1, 3), (2, 8), (4, 7), (5, 6)] |
| 75 | sage: isinstance(m,PerfectMatching) |
| 76 | True |
| 77 | |
| 78 | The parent, which is the set of perfect matching of the ground set, is |
| 79 | automaticly created:: |
| 80 | |
| 81 | sage: n.parent() |
| 82 | Set of perfect matchings of {1, 2, 3, 4, 5, 6, 7, 8} |
| 83 | |
| 84 | If the ground set is ordered, one can, for example, ask if the matching is |
| 85 | non crossing:: |
| 86 | |
| 87 | sage: PerfectMatching([(1, 4), (2, 3), (5, 6)]).is_non_crossing() |
| 88 | True |
| 89 | |
| 90 | TESTS:: |
| 91 | |
| 92 | sage: m=PerfectMatching([]); m |
| 93 | PerfectMatching [] |
| 94 | sage: m.parent() |
| 95 | Set of perfect matchings of {} |
| 96 | """ |
| 97 | #the data structure of the element is a list (accessible via x.value) |
| 98 | wrapped_class = list |
| 99 | __lt__ = ElementWrapper._lt_by_value |
| 100 | #During the creation of the instance of the class, the function |
| 101 | #__classcall_private__ will be called instead of __init__ directly. |
| 102 | __metaclass__ = ClasscallMetaclass |
| 103 | |
| 104 | @staticmethod |
| 105 | def __classcall_private__(cls,p): |
| 106 | r""" |
| 107 | This function tries to recognize the input (it can be either a list or |
| 108 | a tuple of pairs, or a fix-point free involution given as a list or as |
| 109 | a permutation), constructs the parent (enumerated set of |
| 110 | PerfectMatchings of the ground set) and calls the __init__ function to |
| 111 | construct our object. |
| 112 | |
| 113 | EXAMPLES:: |
| 114 | |
| 115 | sage: m=PerfectMatching([('a','e'),('b','c'),('d','f')]);m |
| 116 | PerfectMatching [('a', 'e'), ('b', 'c'), ('d', 'f')] |
| 117 | sage: isinstance(m,PerfectMatching) |
| 118 | True |
| 119 | sage: n=PerfectMatching([3, 8, 1, 7, 6, 5, 4, 2]);n |
| 120 | PerfectMatching [(1, 3), (2, 8), (4, 7), (5, 6)] |
| 121 | sage: n.parent() |
| 122 | Set of perfect matchings of {1, 2, 3, 4, 5, 6, 7, 8} |
| 123 | sage: PerfectMatching([(1, 4), (2, 3), (5, 6)]).is_non_crossing() |
| 124 | True |
| 125 | |
| 126 | The function checks that the given list or permutation is a valid perfect |
| 127 | matching (i.e. a list of pairs with pairwise disjoint elements or a |
| 128 | fixpoint-free involution) and raises a ValueError otherwise: |
| 129 | |
| 130 | sage: try: |
| 131 | ... m=PerfectMatching([(1, 2, 3), (4, 5)]) |
| 132 | ... except ValueError: |
| 133 | ... "A ValueError has been raised." |
| 134 | 'A ValueError has been raised.' |
| 135 | |
| 136 | If you know your datas are in a good format, use directly |
| 137 | `PerfectMatchings(objects)(data)`. |
| 138 | """ |
| 139 | # we have to extract from the argument p the set of objects of the |
| 140 | # matching and the list of pairs. |
| 141 | # First case: p is a list (resp tuple) of lists (resp tuple). |
| 142 | if (isinstance(p,list) or isinstance(p,tuple)) and ( |
| 143 | all([isinstance(x,list) or isinstance(x,tuple) for x in p])): |
| 144 | objects=Set(flatten(p)) |
| 145 | data=(map(tuple,p)) |
| 146 | #check if the data are correct |
| 147 | if not all([len(t)==2 for t in data]): |
| 148 | raise ValueError, "%s is not a valid perfect matching: all elements of the list must be pairs"%p |
| 149 | if len(objects) < 2*len(data): |
| 150 | raise ValueError, "%s is not a valid perfect matching: there are some repetitions"%p |
| 151 | # Second case: p is a permutation or a list of integers, we have to |
| 152 | # check if it is a fix-point-free involution. |
| 153 | elif ((isinstance(p,list) and |
| 154 | all(map(lambda x: (isinstance(x,Integer) or isinstance(x,int)),p ))) |
| 155 | or isinstance(p,sage.combinat.permutation.Permutation_class)): |
| 156 | p=Permutation(p) |
| 157 | n=len(p) |
| 158 | if not(p.cycle_type()==[2 for i in range(n//2)]): |
| 159 | s="The permutation p (= %s) is not a fixpoint-free involution"%p |
| 160 | raise ValueError,s |
| 161 | objects=Set(range(1,n+1)) |
| 162 | data=p.to_cycles() |
| 163 | # Third case: p is already a perfect matching, we return p directly |
| 164 | elif isinstance(p,PerfectMatching): |
| 165 | return p |
| 166 | else: |
| 167 | raise ValueError, "cannot convert p (= %s) to a PerfectMatching"%p |
| 168 | # Finally, we create the parent and the element using the element |
| 169 | # class of the parent. Note: as this function is private, when we |
| 170 | # create an object via parent.element_class(...), __init__ is directly |
| 171 | # executed and we do not have an infinite loop. |
| 172 | return PerfectMatchings(objects)(data) |
| 173 | |
| 174 | def __init__(self,data,parent): |
| 175 | r""" |
| 176 | See :meth:`__classcall_private__` |
| 177 | |
| 178 | TESTS:: |
| 179 | |
| 180 | sage: m=PerfectMatching([('a','e'),('b','c'),('d','f')]) |
| 181 | sage: TestSuite(m).run() |
| 182 | """ |
| 183 | self.value=data |
| 184 | Element.__init__(self,parent=parent) |
| 185 | |
| 186 | def _repr_(self): |
| 187 | r""" |
| 188 | returns the name of the object |
| 189 | |
| 190 | EXAMPLES:: |
| 191 | |
| 192 | sage: m=PerfectMatching([('a','e'),('b','c'),('d','f')]);m |
| 193 | PerfectMatching [('a', 'e'), ('b', 'c'), ('d', 'f')] |
| 194 | sage: n=PerfectMatching([3,8,1,7,6,5,4,2]);n |
| 195 | PerfectMatching [(1, 3), (2, 8), (4, 7), (5, 6)] |
| 196 | """ |
| 197 | return 'PerfectMatching %s'%self.value |
| 198 | |
| 199 | def __eq__(self,other): |
| 200 | r""" |
| 201 | Compares two perfect matchings |
| 202 | |
| 203 | EXAMPLES:: |
| 204 | |
| 205 | sage: m=PerfectMatching([('a','e'),('b','c'),('d','f')]) |
| 206 | sage: n=PerfectMatching([('c','b'),('d','f'),('e','a')]) |
| 207 | sage: n==m |
| 208 | True |
| 209 | sage: n==PerfectMatching([('a','b'),('d','f'),('e','c')]) |
| 210 | False |
| 211 | |
| 212 | """ |
| 213 | try: |
| 214 | if other.parent() != self.parent(): |
| 215 | return False |
| 216 | except AttributeError: |
| 217 | return False |
| 218 | return Set(map(Set,self.value))==Set(map(Set,other.value)) |
| 219 | |
| 220 | def size(self): |
| 221 | r""" |
| 222 | |
| 223 | Returns the size of the perfect matching ``self``, i.e. the number of |
| 224 | elements in the ground set. |
| 225 | |
| 226 | EXAMPLES:: |
| 227 | |
| 228 | sage: m=PerfectMatching([(-3, 1), (2, 4), (-2, 7)]); m.size() |
| 229 | 6 |
| 230 | """ |
| 231 | return 2*len(self.value) |
| 232 | |
| 233 | def partner(self, x): |
| 234 | r""" |
| 235 | Returns the element in the same pair than ``x`` in the matching ``self``. |
| 236 | |
| 237 | EXAMPLES:: |
| 238 | |
| 239 | sage: m=PerfectMatching([(-3, 1), (2, 4), (-2, 7)]); m.partner(4) |
| 240 | 2 |
| 241 | sage: n=PerfectMatching([('c','b'),('d','f'),('e','a')]) |
| 242 | sage: n.partner('c') |
| 243 | 'b' |
| 244 | """ |
| 245 | for i in range(self.size()): |
| 246 | if self.value[i][0]==x: |
| 247 | return self.value[i][1] |
| 248 | if self.value[i][1]==x: |
| 249 | return self.value[i][0] |
| 250 | raise ValueError,"%s in not an element of the %s"%(x,self) |
| 251 | |
| 252 | def conjugate_by_permutation(self, p): |
| 253 | r""" |
| 254 | Returns the conjugate of the perfect matching ``self`` by the |
| 255 | permutation ``p`` of the ground set. |
| 256 | |
| 257 | EXAMPLE:: |
| 258 | |
| 259 | sage: m=PerfectMatching([(1,4),(2,6),(3,5)]) |
| 260 | sage: m.conjugate_by_permutation(Permutation([4,1,5,6,3,2])) |
| 261 | PerfectMatching [(4, 6), (1, 2), (5, 3)] |
| 262 | |
| 263 | TEST:: |
| 264 | |
| 265 | sage: PerfectMatching([]).conjugate_by_permutation(Permutation([])) |
| 266 | PerfectMatching [] |
| 267 | """ |
| 268 | return self.parent()(map(lambda t:tuple(map(p,t)),self.value)) |
| 269 | |
| 270 | def loops_iterator(self,other=None): |
| 271 | r""" |
| 272 | INPUT: |
| 273 | |
| 274 | - ``other`` -- a perfect matching of the same set of ``self``. |
| 275 | (if the second argument is empty, the method :meth:`an_element` is |
| 276 | called on the parent of the first) |
| 277 | |
| 278 | OUTPUT: |
| 279 | |
| 280 | If we draw the two perfect matchings simultaneously as edges of a |
| 281 | graph, the graph obtained is a union of cycles of even lengths. |
| 282 | The function returns an iterator for these cycles (each cycle is |
| 283 | given as a list). |
| 284 | |
| 285 | EXAMPLES:: |
| 286 | |
| 287 | sage: o=PerfectMatching([(1, 7), (2, 4), (3, 8), (5, 6)]) |
| 288 | sage: p=PerfectMatching([(1, 6), (2, 7), (3, 4), (5, 8)]) |
| 289 | sage: it=o.loops_iterator(p) |
| 290 | sage: it.next() |
| 291 | [1, 7, 2, 4, 3, 8, 5, 6] |
| 292 | sage: it.next() |
| 293 | Traceback (most recent call last): |
| 294 | ... |
| 295 | StopIteration |
| 296 | """ |
| 297 | if other is None: |
| 298 | other=self.parent().an_element() |
| 299 | elif self.parent() <> other.parent(): |
| 300 | s="%s is not a matching of the ground set of %s"%(other,self) |
| 301 | raise ValueError,s |
| 302 | remain=flatten(self.value) |
| 303 | while len(remain)>0: |
| 304 | a=remain.pop(0) |
| 305 | b=self.partner(a) |
| 306 | remain.remove(b) |
| 307 | loop=[a,b] |
| 308 | c=other.partner(b) |
| 309 | while c<>a: |
| 310 | b=self.partner(c) |
| 311 | remain.remove(c) |
| 312 | loop.append(c) |
| 313 | remain.remove(b) |
| 314 | loop.append(b) |
| 315 | c=other.partner(b) |
| 316 | yield loop |
| 317 | |
| 318 | def loops(self,other=None): |
| 319 | r""" |
| 320 | INPUT: |
| 321 | |
| 322 | - ``other`` -- a perfect matching of the same set of ``self``. |
| 323 | (if the second argument is empty, the method :meth:`an_element` is |
| 324 | called on the parent of the first) |
| 325 | |
| 326 | OUTPUT: |
| 327 | |
| 328 | If we draw the two perfect matchings simultaneously as edges of a |
| 329 | graph, the graph obtained is a union of cycles of even lengths. |
| 330 | The function returns the list of these cycles (each cycle is given |
| 331 | as a list). |
| 332 | |
| 333 | EXAMPLES:: |
| 334 | |
| 335 | sage: m=PerfectMatching([('a','e'),('b','c'),('d','f')]) |
| 336 | sage: n=PerfectMatching([('a','b'),('d','f'),('e','c')]) |
| 337 | sage: m.loops(n) |
| 338 | [['a', 'e', 'c', 'b'], ['d', 'f']] |
| 339 | sage: o=PerfectMatching([(1, 7), (2, 4), (3, 8), (5, 6)]) |
| 340 | sage: p=PerfectMatching([(1, 6), (2, 7), (3, 4), (5, 8)]) |
| 341 | sage: o.loops(p) |
| 342 | [[1, 7, 2, 4, 3, 8, 5, 6]] |
| 343 | """ |
| 344 | return list(self.loops_iterator(other)) |
| 345 | |
| 346 | def loop_type(self, other=None): |
| 347 | r""" |
| 348 | INPUT: |
| 349 | |
| 350 | - ``other`` -- a perfect matching of the same set of ``self``. |
| 351 | (if the second argument is empty, the method :meth:`an_element` is |
| 352 | called on the parent of the first) |
| 353 | |
| 354 | OUTPUT: |
| 355 | |
| 356 | If we draw the two perfect matchings simultaneously as edges of a |
| 357 | graph, the graph obtained is a union of cycles of even |
| 358 | lengths. The function returns the ordered list of the semi-length |
| 359 | of these cycles (considered as a partition) |
| 360 | |
| 361 | EXAMPLES:: |
| 362 | |
| 363 | sage: m=PerfectMatching([('a','e'),('b','c'),('d','f')]) |
| 364 | sage: n=PerfectMatching([('a','b'),('d','f'),('e','c')]) |
| 365 | sage: m.loop_type(n) |
| 366 | [2, 1] |
| 367 | |
| 368 | TESTS:: |
| 369 | |
| 370 | sage: m=PerfectMatching([]); m.loop_type() |
| 371 | [] |
| 372 | """ |
| 373 | return Partition(reversed(sorted([len(l)//2 |
| 374 | for l in self.loops_iterator(other)]))) |
| 375 | |
| 376 | def number_of_loops(self,other=None): |
| 377 | r""" |
| 378 | INPUT: |
| 379 | |
| 380 | - ``other`` -- a perfect matching of the same set of ``self``. |
| 381 | (if the second argument is empty, the fonction an_element is |
| 382 | called on the parent of the first) |
| 383 | |
| 384 | OUTPUT: |
| 385 | |
| 386 | If we draw the two perfect matchings simultaneously as edges of a |
| 387 | graph, the graph obtained is a union of cycles of even lengths. |
| 388 | The function returns their numbers. |
| 389 | |
| 390 | EXAMPLES:: |
| 391 | |
| 392 | sage: m=PerfectMatching([('a','e'),('b','c'),('d','f')]) |
| 393 | sage: n=PerfectMatching([('a','b'),('d','f'),('e','c')]) |
| 394 | sage: m.number_of_loops(n) |
| 395 | 2 |
| 396 | """ |
| 397 | c = Integer(0) |
| 398 | one = Integer(1) |
| 399 | for _ in self.loops_iterator(other): |
| 400 | c += one |
| 401 | return c |
| 402 | |
| 403 | def crossings_iterator(self): |
| 404 | r""" |
| 405 | INPUT: |
| 406 | |
| 407 | A perfect matching on an *totally ordered* ground set. |
| 408 | |
| 409 | OUTPUT: |
| 410 | |
| 411 | We place the element of a ground set and draw the perfect matching |
| 412 | by linking the elements of the same pair in the upper |
| 413 | half-plane. This function returns an iterator over the pairs of |
| 414 | crossing lines (as a line correspond to a pair, the iterator |
| 415 | produces pairs of pairs). |
| 416 | |
| 417 | EXAMPLES:: |
| 418 | |
| 419 | sage: n=PerfectMatching([3,8,1,7,6,5,4,2]); n |
| 420 | PerfectMatching [(1, 3), (2, 8), (4, 7), (5, 6)] |
| 421 | sage: it = n.crossings_iterator(); |
| 422 | sage: it.next() |
| 423 | ((1, 3), (2, 8)) |
| 424 | sage: it.next() |
| 425 | Traceback (most recent call last): |
| 426 | ... |
| 427 | StopIteration |
| 428 | """ |
| 429 | x=self.value[:] |
| 430 | if len(x)==0: |
| 431 | return |
| 432 | (i,j)=x.pop(0) |
| 433 | for (a,b) in x: |
| 434 | # if (i<a<j<b) or (i<b<j<a) or (j<a<i<b) or (j<b<i<a) or ( |
| 435 | # a<i<b<j) or (a<j<b<i) or (b<i<a<j) or (b<j<a<i): |
| 436 | labij = sorted([a,b,i,j]) |
| 437 | posij = sorted([labij.index(i), labij.index(j)]) |
| 438 | if posij == [0,2] or posij == [1,3]: |
| 439 | yield ((i,j),(a,b)) |
| 440 | for cr in PerfectMatchings(flatten(x))(x).crossings_iterator(): |
| 441 | yield cr |
| 442 | |
| 443 | def crossings(self): |
| 444 | r""" |
| 445 | INPUT: |
| 446 | |
| 447 | A perfect matching on an *totally ordered* ground set. |
| 448 | |
| 449 | OUTPUT: |
| 450 | |
| 451 | We place the element of a ground set and draw the perfect matching |
| 452 | by linking the elements of the same pair in the upper |
| 453 | half-plane. This function returns the list of the pairs of |
| 454 | crossing lines (as a line correspond to a pair, it returns a list |
| 455 | of pairs of pairs). |
| 456 | |
| 457 | EXAMPLES:: |
| 458 | |
| 459 | sage: n=PerfectMatching([3,8,1,7,6,5,4,2]); n |
| 460 | PerfectMatching [(1, 3), (2, 8), (4, 7), (5, 6)] |
| 461 | sage: n.crossings() |
| 462 | [((1, 3), (2, 8))] |
| 463 | |
| 464 | TESTS:: |
| 465 | |
| 466 | sage: m=PerfectMatching([]); m.crossings() |
| 467 | [] |
| 468 | """ |
| 469 | return list(self.crossings_iterator()) |
| 470 | |
| 471 | def number_of_crossings(self): |
| 472 | r""" |
| 473 | INPUT: |
| 474 | |
| 475 | A perfect matching on an *totally ordered* ground set. |
| 476 | |
| 477 | OUTPUT: |
| 478 | |
| 479 | We place the element of a ground set and draw the perfect matching |
| 480 | by linking the elements of the same pair in the upper |
| 481 | half-plane. This function returns the number the pairs of crossing |
| 482 | lines. |
| 483 | |
| 484 | EXAMPLES:: |
| 485 | |
| 486 | sage: n=PerfectMatching([3,8,1,7,6,5,4,2]); n |
| 487 | PerfectMatching [(1, 3), (2, 8), (4, 7), (5, 6)] |
| 488 | sage: n.number_of_crossings() |
| 489 | 1 |
| 490 | """ |
| 491 | c = Integer(0) |
| 492 | one = Integer(1) |
| 493 | for _ in self.crossings_iterator(): |
| 494 | c += one |
| 495 | return c |
| 496 | |
| 497 | def is_non_crossing(self): |
| 498 | r""" |
| 499 | INPUT: |
| 500 | |
| 501 | A perfect matching on an *totally ordered* ground set. |
| 502 | |
| 503 | OUTPUT: |
| 504 | |
| 505 | We place the element of a ground set and draw the perfect matching |
| 506 | by linking the elements of the same pair in the upper |
| 507 | half-plane. This function returns ``True`` if the picture obtained |
| 508 | this way has no crossings. |
| 509 | |
| 510 | EXAMPLES:: |
| 511 | |
| 512 | sage: n=PerfectMatching([3,8,1,7,6,5,4,2]); n |
| 513 | PerfectMatching [(1, 3), (2, 8), (4, 7), (5, 6)] |
| 514 | sage: n.is_non_crossing() |
| 515 | False |
| 516 | sage: PerfectMatching([(1, 4), (2, 3), (5, 6)]).is_non_crossing() |
| 517 | True |
| 518 | """ |
| 519 | it = self.crossings_iterator() |
| 520 | try: |
| 521 | it.next() |
| 522 | except StopIteration: |
| 523 | return True |
| 524 | else: |
| 525 | return False |
| 526 | |
| 527 | def nestings_iterator(self): |
| 528 | r""" |
| 529 | INPUT: |
| 530 | |
| 531 | A perfect matching on an *totally ordered* ground set. |
| 532 | |
| 533 | OUTPUT: |
| 534 | |
| 535 | We place the element of a ground set and draw the perfect matching |
| 536 | by linking the elements of the same pair in the upper |
| 537 | half-plane. This function returns an iterator over the pairs of |
| 538 | nesting lines (as a line correspond to a pair, the iterator |
| 539 | produces pairs of pairs). |
| 540 | |
| 541 | EXAMPLES:: |
| 542 | |
| 543 | sage: n=PerfectMatching([(1, 6), (2, 7), (3, 5), (4, 8)]) |
| 544 | sage: it = n.nestings_iterator(); |
| 545 | sage: it.next() |
| 546 | ((1, 6), (3, 5)) |
| 547 | sage: it.next() |
| 548 | ((2, 7), (3, 5)) |
| 549 | sage: it.next() |
| 550 | Traceback (most recent call last): |
| 551 | ... |
| 552 | StopIteration |
| 553 | """ |
| 554 | x=self.value[:] |
| 555 | if len(x)==0: |
| 556 | return |
| 557 | (i,j)=x.pop(0) |
| 558 | for (a,b) in x: |
| 559 | # if (i<a<j<b) or (i<b<j<a) or (j<a<i<b) or (j<b<i<a) or ( |
| 560 | # a<i<b<j) or (a<j<b<i) or (b<i<a<j) or (b<j<a<i): |
| 561 | labij = sorted([a,b,i,j]) |
| 562 | posij = sorted([labij.index(i), labij.index(j)]) |
| 563 | if posij == [0,3] or posij == [1,2]: |
| 564 | yield ((i,j),(a,b)) |
| 565 | for nest in PerfectMatchings(flatten(x))(x).nestings_iterator(): |
| 566 | yield nest |
| 567 | |
| 568 | def nestings(self): |
| 569 | r""" |
| 570 | INPUT: |
| 571 | |
| 572 | A perfect matching on an *totally ordered* ground set. |
| 573 | |
| 574 | OUTPUT: |
| 575 | |
| 576 | We place the element of a ground set and draw the perfect matching |
| 577 | by linking the elements of the same pair in the upper |
| 578 | half-plane. This function returns the list of the pairs of |
| 579 | nesting lines (as a line correspond to a pair, it returns a list |
| 580 | of pairs of pairs). |
| 581 | |
| 582 | EXAMPLES:: |
| 583 | |
| 584 | sage: m=PerfectMatching([(1, 6), (2, 7), (3, 5), (4, 8)]) |
| 585 | sage: m.nestings() |
| 586 | [((1, 6), (3, 5)), ((2, 7), (3, 5))] |
| 587 | sage: n=PerfectMatching([3,8,1,7,6,5,4,2]); n |
| 588 | PerfectMatching [(1, 3), (2, 8), (4, 7), (5, 6)] |
| 589 | sage: n.nestings() |
| 590 | [((2, 8), (4, 7)), ((2, 8), (5, 6)), ((4, 7), (5, 6))] |
| 591 | |
| 592 | TESTS:: |
| 593 | |
| 594 | sage: m=PerfectMatching([]); m.nestings() |
| 595 | [] |
| 596 | """ |
| 597 | return list(self.nestings_iterator()) |
| 598 | |
| 599 | def number_of_nestings(self): |
| 600 | r""" |
| 601 | INPUT: |
| 602 | |
| 603 | A perfect matching on an *totally ordered* ground set. |
| 604 | |
| 605 | OUTPUT: |
| 606 | |
| 607 | We place the element of a ground set and draw the perfect matching |
| 608 | by linking the elements of the same pair in the upper |
| 609 | half-plane. This function returns the number the pairs of nesting |
| 610 | lines. |
| 611 | |
| 612 | EXAMPLES:: |
| 613 | |
| 614 | sage: n=PerfectMatching([3,8,1,7,6,5,4,2]); n |
| 615 | PerfectMatching [(1, 3), (2, 8), (4, 7), (5, 6)] |
| 616 | sage: n.number_of_nestings() |
| 617 | 3 |
| 618 | """ |
| 619 | c = Integer(0) |
| 620 | one = Integer(1) |
| 621 | for _ in self.nestings_iterator(): |
| 622 | c += one |
| 623 | return c |
| 624 | |
| 625 | def is_non_nesting(self): |
| 626 | r""" |
| 627 | INPUT: |
| 628 | |
| 629 | A perfect matching on an *totally ordered* ground set. |
| 630 | |
| 631 | OUTPUT: |
| 632 | |
| 633 | We place the element of a ground set and draw the perfect matching |
| 634 | by linking the elements of the same pair in the upper |
| 635 | half-plane. This function returns ``True`` if the picture obtained |
| 636 | this way has no nestings. |
| 637 | |
| 638 | EXAMPLES:: |
| 639 | |
| 640 | sage: n=PerfectMatching([3,8,1,7,6,5,4,2]); n |
| 641 | PerfectMatching [(1, 3), (2, 8), (4, 7), (5, 6)] |
| 642 | sage: n.is_non_nesting() |
| 643 | False |
| 644 | sage: PerfectMatching([(1, 3), (2, 5), (4, 6)]).is_non_nesting() |
| 645 | True |
| 646 | """ |
| 647 | it = self.nestings_iterator() |
| 648 | try: |
| 649 | it.next() |
| 650 | except StopIteration: |
| 651 | return True |
| 652 | else: |
| 653 | return False |
| 654 | |
| 655 | def Weingarten_function(self,d,other=None): |
| 656 | r""" |
| 657 | Returns the Weingarten function of two pairings. This function is |
| 658 | the value of some integrals over the orhtogonal groups `O_N` |
| 659 | |
| 660 | Reference : Benoit Collins, Sho Matsumoto, On some properties of |
| 661 | orthogonal Weingarten functions, arXiv:0903.5143 |
| 662 | With the convention of this article, the function returns |
| 663 | `Wg^{O(d)}(other,self)`. |
| 664 | |
| 665 | EXAMPLES:: |
| 666 | |
| 667 | sage: var('N') |
| 668 | N |
| 669 | sage: m=PerfectMatching([(1,3),(2,4)]) |
| 670 | sage: n=PerfectMatching([(1,2),(3,4)]) |
| 671 | sage: factor(m.Weingarten_function(N,n)) |
| 672 | -1/((N - 1)*(N + 2)*N) |
| 673 | """ |
| 674 | if other is None: |
| 675 | other = self.parent().an_element() |
| 676 | W=self.parent().Weingarten_matrix(d) |
| 677 | return W[other.rank()][self.rank()] |
| 678 | |
| 679 | class PerfectMatchings(UniqueRepresentation,Parent): |
| 680 | r""" |
| 681 | Class of perfect matchings of a ground set. At the creation, the set |
| 682 | can be given as any iterable object. If the argument is an integer `n`, it |
| 683 | will be transformed into `[1 .. n]`:: |
| 684 | |
| 685 | sage: M=PerfectMatchings(6);M |
| 686 | Set of perfect matchings of {1, 2, 3, 4, 5, 6} |
| 687 | sage: PerfectMatchings([-1, -3, 1, 2]) |
| 688 | Set of perfect matchings of {1, 2, -3, -1} |
| 689 | |
| 690 | One can ask for the list, the cardinality or an element of a set of |
| 691 | perfect matching:: |
| 692 | |
| 693 | sage: PerfectMatchings(4).list() |
| 694 | [PerfectMatching [(4, 1), (3, 2)], PerfectMatching [(4, 2), (3, 1)], PerfectMatching [(4, 3), (2, 1)]] |
| 695 | sage: PerfectMatchings(8).cardinality() |
| 696 | 105 |
| 697 | sage: M=PerfectMatchings(('a', 'e', 'b', 'f', 'c', 'd')) |
| 698 | sage: M.an_element() |
| 699 | PerfectMatching [('a', 'e'), ('c', 'd'), ('b', 'f')] |
| 700 | sage: all([PerfectMatchings(i).an_element() in PerfectMatchings(i) |
| 701 | ... for i in range(2,11,2)]) |
| 702 | True |
| 703 | |
| 704 | TESTS:: |
| 705 | |
| 706 | sage: PerfectMatchings(5).list() |
| 707 | [] |
| 708 | """ |
| 709 | |
| 710 | @staticmethod |
| 711 | def _parse_input(objects): |
| 712 | r""" |
| 713 | This function tries to recognize the argument and to transform into a |
| 714 | set. It is not meant to be called manually, but only as the first of |
| 715 | the creation of an enumerated set of ``PerfectMatchings``. |
| 716 | |
| 717 | EXAMPLES:: |
| 718 | |
| 719 | sage: PerfectMatchings._parse_input(4) |
| 720 | {1, 2, 3, 4} |
| 721 | sage: PerfectMatchings._parse_input(['a','b','c','e']) |
| 722 | {'a', 'c', 'b', 'e'} |
| 723 | """ |
| 724 | # if the argument is a python int n, we replace it by the list [1 .. n] |
| 725 | if isinstance(objects,int): |
| 726 | objects=range(1,objects+1) |
| 727 | # same thing if the argument is a sage integer. |
| 728 | elif isinstance(objects,Integer): |
| 729 | objects=range(1,objects+1) |
| 730 | # Finally, if it is iterable, we return the corresponding set. |
| 731 | # Note that it is important to return a hashable object here (in |
| 732 | # particular, NOT A LIST), see comment below. |
| 733 | if not hasattr(objects,'__iter__'): |
| 734 | raise ValueError, "do not know how to construct a set of matchings from %s (it must be iterable)" |
| 735 | return Set(objects) |
| 736 | |
| 737 | @staticmethod |
| 738 | def __classcall__(cls, objects): |
| 739 | r""" |
| 740 | This function is called automatically when the user want to |
| 741 | create an enumerated set of PerfectMatchings. |
| 742 | |
| 743 | EXAMPLES:: |
| 744 | |
| 745 | sage: M=PerfectMatchings(6);M |
| 746 | Set of perfect matchings of {1, 2, 3, 4, 5, 6} |
| 747 | sage: PerfectMatchings([-1, -3, 1, 2]) |
| 748 | Set of perfect matchings of {1, 2, -3, -1} |
| 749 | |
| 750 | If one has already created a set of perfect matchings of the same set, |
| 751 | it does not create a new object, but returns the already existing |
| 752 | one:: |
| 753 | |
| 754 | sage: N=PerfectMatchings((2, 3, 5, 4, 1, 6)) |
| 755 | sage: N is M |
| 756 | True |
| 757 | """ |
| 758 | #we call the constructor of an other class, which will |
| 759 | # - check if the object has already been constructed (so the |
| 760 | # second argument, i.e. the output of _parse_input, must be hashable) |
| 761 | # - look for a place in memory and call the __init__ function |
| 762 | return super(PerfectMatchings, cls).__classcall__( |
| 763 | cls, cls._parse_input(objects)) |
| 764 | |
| 765 | def __init__(self,objects): |
| 766 | r""" |
| 767 | See :meth:`__classcall__` |
| 768 | |
| 769 | TEST:: |
| 770 | |
| 771 | sage: M=PerfectMatchings(6) |
| 772 | sage: TestSuite(M).run() |
| 773 | """ |
| 774 | self._objects=objects |
| 775 | Parent.__init__(self, category = FiniteEnumeratedSets()) |
| 776 | |
| 777 | def _repr_(self): |
| 778 | r""" |
| 779 | Returns a description of ``self``. |
| 780 | |
| 781 | EXAMPLES:: |
| 782 | |
| 783 | sage: PerfectMatchings([-1, -3, 1, 2]) |
| 784 | Set of perfect matchings of {1, 2, -3, -1} |
| 785 | """ |
| 786 | return "Set of perfect matchings of %s"%self._objects |
| 787 | |
| 788 | def __iter__(self): |
| 789 | r""" |
| 790 | Returns an iterator for the elements of ``self``. |
| 791 | |
| 792 | EXAMPLES:: |
| 793 | |
| 794 | sage: PerfectMatchings(4).list() |
| 795 | [PerfectMatching [(4, 1), (3, 2)], PerfectMatching [(4, 2), (3, 1)], PerfectMatching [(4, 3), (2, 1)]] |
| 796 | """ |
| 797 | if len(self._objects) == 0: |
| 798 | yield self([]) |
| 799 | elif len(self._objects) == 1: |
| 800 | pass |
| 801 | else: |
| 802 | l=list(self._objects) |
| 803 | a=l.pop(-1) |
| 804 | for i in range(len(l)): |
| 805 | obj_rest=l[:] |
| 806 | b=obj_rest.pop(i) |
| 807 | for p in PerfectMatchings(obj_rest): |
| 808 | yield self([(a, b)]+p.value) |
| 809 | |
| 810 | def __contains__(self,x): |
| 811 | r""" |
| 812 | Tests if ``x`` is an element of ``self``. |
| 813 | |
| 814 | EXAMPLES:: |
| 815 | |
| 816 | sage: m=PerfectMatching([(1,2),(4,3)]) |
| 817 | sage: m in PerfectMatchings(4) |
| 818 | True |
| 819 | sage: m in PerfectMatchings((0, 1, 2, 3)) |
| 820 | False |
| 821 | sage: all([m in PerfectMatchings(6) for m in PerfectMatchings(6)]) |
| 822 | True |
| 823 | |
| 824 | Note that the class of ``x`` does not need to be ``PerfectMatching``: |
| 825 | if the data defines a perfect matching of the good set, the function |
| 826 | returns ``True``:: |
| 827 | |
| 828 | sage: [(1, 4), (2, 3)] in PerfectMatchings(4) |
| 829 | True |
| 830 | sage: [(1, 3, 6), (2, 4), (5,)] in PerfectMatchings(6) |
| 831 | False |
| 832 | sage: [('a', 'b'), ('a', 'c')] in PerfectMatchings( |
| 833 | ... ('a', 'b', 'c', 'd')) |
| 834 | False |
| 835 | """ |
| 836 | if not isinstance(x,PerfectMatching): |
| 837 | try: |
| 838 | x=PerfectMatching(x) |
| 839 | except ValueError: |
| 840 | return False |
| 841 | if x.parent() is not self: |
| 842 | return False |
| 843 | return True |
| 844 | |
| 845 | def cardinality(self): |
| 846 | r""" |
| 847 | Returns the cardinality of the set of perfect matching ``self``, |
| 848 | that is `1*3*5*...*(2n-1)`, where `2n` is the size of the ground set. |
| 849 | |
| 850 | EXAMPLES:: |
| 851 | |
| 852 | sage: PerfectMatchings(8).cardinality() |
| 853 | 105 |
| 854 | """ |
| 855 | n=len(self._objects) |
| 856 | if n%2==1: |
| 857 | return 0 |
| 858 | else: |
| 859 | return prod(i for i in range(n) if i%2==1) |
| 860 | |
| 861 | def an_element(self): |
| 862 | r""" |
| 863 | Returns an element of self. |
| 864 | |
| 865 | EXAMPLES:: |
| 866 | |
| 867 | sage: M=PerfectMatchings(('a', 'e', 'b', 'f', 'c', 'd')) |
| 868 | sage: M.an_element() |
| 869 | PerfectMatching [('a', 'e'), ('c', 'd'), ('b', 'f')] |
| 870 | sage: all([PerfectMatchings(i).an_element() in PerfectMatchings(i) |
| 871 | ... for i in range(2,11,2)]) |
| 872 | True |
| 873 | """ |
| 874 | n=len(self._objects)//2 |
| 875 | return self([(self._objects[i],self._objects[i+n]) |
| 876 | for i in range(n)]) |
| 877 | |
| 878 | @cached_method |
| 879 | def Weingarten_matrix(self,N): |
| 880 | r""" |
| 881 | Returns the Weingarten matrix corresponding to the set of |
| 882 | PerfectMatchings ``self``. It is a useful theoretical tool to compute |
| 883 | polynomial integral over the orthogonal group `O_N`. |
| 884 | |
| 885 | Reference : Benoit Collins, Sho Matsumoto, On some properties of |
| 886 | orthogonal Weingarten functions, arXiv:0903.5143 |
| 887 | |
| 888 | EXAMPLES:: |
| 889 | |
| 890 | sage: M=PerfectMatchings(4).Weingarten_matrix(var('N')) |
| 891 | sage: N*(N-1)*(N+2)*M.apply_map(factor) |
| 892 | [N + 1 -1 -1] |
| 893 | [ -1 N + 1 -1] |
| 894 | [ -1 -1 N + 1] |
| 895 | """ |
| 896 | G=Matrix([ [N**(p1.number_of_loops(p2)) for p1 in self] |
| 897 | for p2 in self]) |
| 898 | return G**(-1) |
| 899 | |
| 900 | Element=PerfectMatching |