Path: blob/master/src/sage/combinat/combinatorial_map.py
8815 views
"""1Combinatorial maps2"""3#*****************************************************************************4# Copyright (C) 2011 Christian Stump <christian.stump at gmail.com>5#6# Distributed under the terms of the GNU General Public License (GPL)7# http://www.gnu.org/licenses/8#*****************************************************************************910def combinatorial_map(f=None, order=None, name=None):11r"""12Combinatorial maps1314We call a method a *combinatorial map* if it is a map between two15combinatorial sets.1617INPUT:1819- ``f`` -- (default: ``None``, if combinatorial_map is used as a decorator) a function20- ``name`` -- (default: ``None``) the name for nicer outputs on combinatorial maps21- ``order`` -- (default: ``None``) the order of the combinatorial map, if it is known. Is not used, but might be helpful later2223OUTPUT:2425- A combinatorial map. This is an instance of the :class:`CombinatorialMap`2627The decorator :obj:`combinatorial_map` can be used to declare28methods as combinatorial maps.2930EXAMPLES::3132sage: p = Permutation([1,3,2,4])33sage: p.left_tableau34Combinatorial map: Robinson-Schensted insertion tableau3536We define a class illustrating the use of the decorator37:obj:`combinatorial_map` with the various arguments::3839sage: from sage.combinat.combinatorial_map import combinatorial_map40sage: class MyPermutation(object):41...42... @combinatorial_map()43... def reverse(self):44... '''45... Reverse the permutation46... '''47... pass48...49... @combinatorial_map(order=2)50... def inverse(self):51... '''52... The inverse of the permutation53... '''54... pass55...56... @combinatorial_map(name='descent set of permutation')57... def descent_set(self):58... '''59... The descent set of the permutation60... '''61... pass62...63... def major_index(self):64... '''65... The major index of the permutation66... '''67... pass68sage: MyPermutation.reverse69Combinatorial map: reverse70sage: MyPermutation.descent_set71Combinatorial map: descent set of permutation72sage: MyPermutation.inverse73Combinatorial map: inverse7475One can determine all the combinatorial maps associated with a given object76as follows::7778sage: from sage.combinat.combinatorial_map import combinatorial_maps_in_class79sage: X = combinatorial_maps_in_class(MyPermutation); X # random80[Combinatorial map: reverse,81Combinatorial map: descent set of permutation,82Combinatorial map: inverse]8384The method ``major_index`` defined about is not a combinatorial map::8586sage: MyPermutation.major_index87<unbound method MyPermutation.major_index>8889But one can define a function that turns ``major_index`` into a combinatorial map::9091sage: def major_index(p):92... return p.major_index()93...94sage: major_index95<function major_index at ...>96sage: combinatorial_map(major_index)97Combinatorial map: major_index9899"""100if f is None:101return lambda f: CombinatorialMap(f, order=order, name=name)102else:103return CombinatorialMap(f, order=order, name=name)104105class CombinatorialMap(object):106r"""107This is a wrapper class for methods that are *combinatorial maps*.108109For further details and doctests, see :func:`combinatorial_map`.110"""111def __init__(self, f, order=None, name=None):112"""113Constructor for combinatorial maps114115EXAMPLES::116117sage: from sage.combinat.combinatorial_map import combinatorial_map118sage: def f(x):119... "doc of f"120... return x121...122sage: x = combinatorial_map(f); x123Combinatorial map: f124sage: x.__doc__125'doc of f'126sage: x.__name__127'f'128sage: x.__module__129'__main__'130"""131import types132if not isinstance(f, types.FunctionType):133raise ValueError, "Only plain functions are supported"134self._f = f135self._order = order136self._name = name137if hasattr(f, "func_doc"):138self.__doc__ = f.func_doc139if hasattr(f, "func_name"):140self.__name__ = f.func_name141else:142self.__name__ = "..."143if hasattr(f, "__module__"):144self.__module__ = f.__module__145146def __repr__(self):147"""148EXAMPLES::149150sage: p = Permutation([1,3,2,4])151sage: p.left_tableau.__repr__()152'Combinatorial map: Robinson-Schensted insertion tableau'153"""154return "Combinatorial map: %s" %self.name()155156def _sage_src_lines_(self):157"""158Returns the source code location for the wrapped function.159160EXAMPLES::161162sage: p = Permutation([1,3,2,4])163sage: cm = p.left_tableau; cm164Combinatorial map: Robinson-Schensted insertion tableau165sage: (src, lines) = cm._sage_src_lines_()166sage: src[0]167" @combinatorial_map(name='Robinson-Schensted insertion tableau')\n"168sage: lines # random1692653170"""171from sage.misc.sageinspect import sage_getsourcelines172return sage_getsourcelines(self._f)173174def __get__(self, inst, cls=None):175"""176Bounds the method of self to the given instance.177178EXAMPLES::179180sage: p = Permutation([1,3,2,4])181sage: p.left_tableau #indirect doctest182Combinatorial map: Robinson-Schensted insertion tableau183"""184self._inst = inst185return self186187def __call__(self, *args, **kwds):188"""189Calls the combinatorial map.190191EXAMPLES::192193sage: p = Permutation([1,3,2,4])194sage: cm = type(p).left_tableau; cm195Combinatorial map: Robinson-Schensted insertion tableau196sage: cm(p)197[[1, 2, 4], [3]]198sage: cm(Permutation([4,3,2,1]))199[[1], [2], [3], [4]]200"""201if self._inst is not None:202return self._f(self._inst, *args, **kwds)203else:204return self._f(*args, **kwds)205206def unbounded_map(self):207r"""208Return the unbounded version of ``self``.209210You can use this method to return a function which takes as input211an element in the domain of the combinatorial map.212See the example below.213214EXAMPLES::215216sage: from sage.combinat.permutation import Permutation217sage: pi = Permutation([1,3,2])218sage: f = pi.reverse219sage: F = f.unbounded_map()220sage: F(pi)221[2, 3, 1]222"""223return self._f224225def order(self):226"""227Returns the order of ``self``, or ``None`` if the order is not known.228229EXAMPLES::230231sage: from sage.combinat.combinatorial_map import combinatorial_map232sage: class CombinatorialClass:233... @combinatorial_map(order=2)234... def to_self_1(): pass235... @combinatorial_map()236... def to_self_2(): pass237sage: CombinatorialClass.to_self_1.order()2382239sage: CombinatorialClass.to_self_2.order() is None240True241"""242return self._order243244def name(self):245"""246Returns the name of a combinatorial map.247This is used for the string representation of ``self``.248249EXAMPLES::250251sage: from sage.combinat.combinatorial_map import combinatorial_map252sage: class CombinatorialClass:253... @combinatorial_map(name='map1')254... def to_self_1(): pass255... @combinatorial_map()256... def to_self_2(): pass257sage: CombinatorialClass.to_self_1.name()258'map1'259sage: CombinatorialClass.to_self_2.name()260'to_self_2'261"""262if self._name is not None:263return self._name264else:265return self._f.__name__266267def combinatorial_maps_in_class(cls):268"""269Returns the combinatorial maps of the class as a list of combinatorial maps.270271EXAMPLES::272273sage: from sage.combinat.combinatorial_map import combinatorial_maps_in_class274sage: p = Permutation([1,3,2,4])275sage: cmaps = combinatorial_maps_in_class(p)276sage: cmaps # random277[Combinatorial map: Robinson-Schensted insertion tableau,278Combinatorial map: Robinson-Schensted recording tableau,279Combinatorial map: Robinson-Schensted tableau shape,280Combinatorial map: complement,281Combinatorial map: descent composition,282Combinatorial map: inverse, ...]283sage: p.left_tableau in cmaps284True285sage: p.right_tableau in cmaps286True287sage: p.complement in cmaps288True289"""290result = set()291for method in dir(cls):292entry = getattr(cls, method)293if isinstance(entry, CombinatorialMap):294result.add(entry)295return list(result)296297298