Ticket #5538: family_interface-cleanup-fh-final.patch

File family_interface-cleanup-fh-final.patch, 14.7 KB (added by hivert, 13 years ago)

Cleanup of the interface.

  • sage/sets/family.py

    # HG changeset patch
    # User Florent Hivert <Florent.Hivert@univ-rouen.fr>
    # Date 1239728129 -7200
    # Node ID 6cba5f9cf81a69d9bb098e33f475999b139798fe
    # Parent  99c724f3204d823337ad900c157b939c4227f120
    Cleanup of the interface of Family:
     - added a keyword lazy for forcing lazy family
     - removed the keyword name which was redundant with rename of SageObjects.
    
    diff -r 99c724f3204d -r 6cba5f9cf81a sage/sets/family.py
    a b  
    1919from sage.combinat.combinat import CombinatorialClass
    2020from sage.combinat.finite_class import FiniteCombinatorialClass
    2121from sage.rings.integer import Integer
    22 
    23 def Family(indices, function = None, name = None, hidden_keys = [], hidden_function = None):
     22from sage.misc.misc import AttrCallObject
     23from warnings import warn
     24name_warn_message = "The keyword name for family has never been used and will be removed shortly. Please update your code."
     25 
     26def Family(indices, function = None, hidden_keys = [], hidden_function = None, lazy = False, name=None):
    2427    r"""
    2528    A Family is an associative container which models a family
    2629    `(f_i)_{i \in I}`. Then, f[i] returns the element of the family
     
    8790        sage: len(f)
    8891        3
    8992   
    90     By default, if the index set is a list or a tuple, all images are
    91     computed right away, and stored in an internal dictionary::
     93    By default, all images are computed right away, and stored in an internal
     94    dictionary::
    9295
    9396        sage: f = Family((3,4,7), lambda i: 2*i)
    9497        sage: f
     
    98101    hashable. One can ask instead for the images `f(i)` to be computed
    99102    lazily, when needed::
    100103   
    101         sage: f = LazyFamily([3,4,7], lambda i: 2r*i)
     104        sage: f = Family([3,4,7], lambda i: 2r*i, lazy=True)
    102105        sage: f
    103         Lazy family (f(i))_{i in [3, 4, 7]}
     106        Lazy family (<lambda>(i))_{i in [3, 4, 7]}
    104107        sage: f[7]
    105108        14
    106109        sage: list(f)
     
    110113   
    111114    This allows in particular for modeling infinite families::
    112115   
    113         sage: f = Family(ZZ, lambda i: 2r*i)
     116        sage: f = Family(ZZ, lambda i: 2r*i, lazy=True)
    114117        sage: f
    115         Lazy family (f(i))_{i in Integer Ring}
     118        Lazy family (<lambda>(i))_{i in Integer Ring}
    116119        sage: f.keys()
    117120        Integer Ring
    118121        sage: f[1]
     
    122125        sage: i = iter(f)
    123126        sage: i.next(), i.next(), i.next(), i.next(), i.next()
    124127        (0, 2, -2, 4, -4)
     128
     129    Note that the ``lazy`` keyword parameter is only needed to force
     130    laziness. Usually it is automatically set to a correct default value (ie:
     131    ``False`` for finite data structures and true for CombinatorialClasses::
     132
     133        sage: f == Family(ZZ, lambda i: 2r*i)
     134        True
    125135   
    126136    Beware that for those kind of families len(f) is not supposed to
    127137    work. As a replacement, use the .cardinality() method::
    128138   
    129        sage: f = LazyFamily(Permutations(3), attrcall("to_lehmer_code"))
     139       sage: f = Family(Permutations(3), attrcall("to_lehmer_code"))
    130140       sage: list(f)
    131141       [[0, 0, 0], [0, 1, 0], [1, 0, 0], [1, 1, 0], [2, 0, 0], [2, 1, 0]]
    132142       sage: f.cardinality()
    133143       6
    134 
     144       
    135145    Caveat: Only certain families with lazy behavior can be pickled. In
    136146    particular, only functions that work with Sage's pickle_function
    137147    and unpickle_function (in sage.misc.fpickle) will correctly
    138148    unpickle. The following two work::
    139149
    140        sage: loads(dumps(LazyFamily(Permutations(3), lambda p: p.to_lehmer_code())))
    141        Lazy family (f(i))_{i in Standard permutations of 3}
     150       sage: f = Family(Permutations(3), lambda p: p.to_lehmer_code()); f
     151       Lazy family (<lambda>(i))_{i in Standard permutations of 3}
     152       sage: f == loads(dumps(f))
     153       True
     154       
     155       sage: f = Family(Permutations(3), attrcall("to_lehmer_code")); f
     156       Lazy family (i.to_lehmer_code())_{i in Standard permutations of 3}
     157       sage: f == loads(dumps(f))
     158       True
    142159
    143        sage: loads(dumps(LazyFamily(Permutations(3), attrcall("to_lehmer_code"))))
    144        Lazy family (f(i))_{i in Standard permutations of 3}
    145 
    146     But this one dont::   
     160    But this one don't::   
    147161
    148162       sage: def plus_n(n): return lambda x: x+n
    149        sage: loads(dumps(LazyFamily([1,2,3], plus_n(3))))
     163       sage: f = Family([1,2,3], plus_n(3), lazy=True); f
     164       Lazy family (<lambda>(i))_{i in [1, 2, 3]}
     165       sage: f == loads(dumps(f))
    150166       Traceback (most recent call last):
    151167       ...
    152168       ValueError: Cannot pickle code objects from closures
    153169       
    154170    Finally, it can occasionally be useful to add some hidden elements
    155171    in a family, which are accessible as f[i], but do not appear in the
    156     keys or the container operations.
    157    
    158     ::
     172    keys or the container operations::
    159173   
    160174        sage: f = Family([3,4,7], lambda i: 2*i, hidden_keys=[2])
    161175        sage: f
     
    261275        'b'
    262276        sage: loads(dumps(f)) == f
    263277        True
    264    
     278
     279    ::
     280
     281        sage: f = Family({1:'a', 2:'b', 3:'c'}, lazy=True)
     282        Traceback (most recent call last):
     283        ValueError: lazy keyword only makes sense together with function keyword !
     284       
    265285    ::
    266286   
    267287        sage: f = Family(range(1,27), lambda i: chr(i+96))
     
    292312        sage: f == g
    293313        True
    294314    """
     315   
     316    if name is not None:
     317        warn(name_warn_message)
    295318    assert(type(hidden_keys) == list)
    296     if function is None and hidden_keys == []:
    297         if isinstance(indices, dict):
    298             return FiniteFamily(indices)
    299         if isinstance(indices, (list, tuple) ):
    300             return TrivialFamily(indices)
    301         if isinstance(indices, (FiniteCombinatorialClass,
    302                                 FiniteFamily, LazyFamily, TrivialFamily) ):
    303             return indices
     319    assert(isinstance(lazy, bool))
     320
     321    if hidden_keys == []:
     322        if hidden_function is not None:
     323                raise ValueError, "hidden_function keyword only makes sense together with hidden_keys keyword !"           
     324        elif function is None:
     325            if lazy:
     326                raise ValueError, "lazy keyword only makes sense together with function keyword !"
     327            if isinstance(indices, dict):
     328                return FiniteFamily(indices)
     329            if isinstance(indices, (list, tuple) ):
     330                return TrivialFamily(indices)
     331            if isinstance(indices, (FiniteCombinatorialClass,
     332                                    FiniteFamily, LazyFamily, TrivialFamily) ):
     333                return indices
     334        elif ( isinstance(indices, (list, tuple, FiniteCombinatorialClass) )
     335               and not lazy):
     336            return FiniteFamily(dict([(i, function(i)) for i in indices]),
     337                                keys = indices)
     338        else:
     339            return LazyFamily(indices, function)           
    304340    else:
    305         if isinstance(indices, (list, tuple, FiniteCombinatorialClass) ):
    306             if not hidden_keys == []:
    307                 if hidden_function is None:
    308                     hidden_function = function
    309                 return FiniteFamilyWithHiddenKeys(dict([(i, function(i)) for i in indices]),
    310                                                   hidden_keys, hidden_function)
    311             else:
    312                 return FiniteFamily(dict([(i, function(i)) for i in indices]), keys = indices)
    313         elif hidden_keys == [] and hidden_function is None:
    314             return LazyFamily(indices, function)
     341        if lazy:
     342            raise ValueError, "lazy keyword is incompatible with hidden keys !"
     343        if hidden_function is None:
     344            hidden_function = function
     345        return FiniteFamilyWithHiddenKeys(dict([(i, function(i)) for i in indices]),
     346                                          hidden_keys, hidden_function)
     347
    315348    raise NotImplementedError
    316349
    317350class AbstractFamily(CombinatorialClass):
     
    350383        """
    351384        assert(self.keys() == other.keys())
    352385        assert(self.hidden_keys() == other.hidden_keys())
    353         return Family(self.keys(), lambda i: f(self[i],other[i]), hidden_keys = self.hidden_keys(), name = name)
     386        if name is not None:
     387            warn(name_warn_message)
     388        return Family(self.keys(), lambda i: f(self[i],other[i]), hidden_keys = self.hidden_keys())
    354389
    355390    def map(self, f, name = None):
    356391        """
     
    366401            sage: list(g)
    367402            ['a1', 'b1', 'd1']
    368403        """
    369         return Family(self.keys(), lambda i: f(self[i]), hidden_keys = self.hidden_keys(), name = name)
     404        if name is not None:
     405            warn(name_warn_message)
     406        return Family(self.keys(), lambda i: f(self[i]), hidden_keys = self.hidden_keys())
    370407
    371408class FiniteFamily(AbstractFamily):
    372409    r"""
     
    423460        self.keys = dictionary.keys
    424461        self.values = dictionary.values
    425462
    426     def __repr__(self):
     463    def _repr_(self):
    427464        """
    428465        EXAMPLES::
    429466       
     
    607644            [3, 4, 7]
    608645            sage: f[3]
    609646            6
    610            
    611             sage: f = LazyFamily(Permutations(3), lambda p: p.to_lehmer_code())
    612             sage: f == loads(dumps(f))
    613             True
    614 
    615             sage: f = LazyFamily(Permutations(3), attrcall("to_lehmer_code"))
    616             sage: f == loads(dumps(f))
    617             True
    618647        """
    619648        hidden_function = d['hidden_function']
    620649        if isinstance(hidden_function, str):
     
    633662    Instances should be created via the Family factory, which see for
    634663    examples and tests.
    635664    """
    636     def __init__(self, set, function, name = "f"):
     665    def __init__(self, set, function, name=None):
    637666        """
    638667        TESTS::
    639668       
    640669            sage: f = LazyFamily([3,4,7], lambda i: 2r*i); f
    641             Lazy family (f(i))_{i in [3, 4, 7]}
     670            Lazy family (<lambda>(i))_{i in [3, 4, 7]}
    642671            sage: f == loads(dumps(f))
    643672            True
    644673
     
    648677            sage: f = LazyFamily(l, lambda i: 2r*i);
    649678            sage: l[1] = 18
    650679            sage: f
    651             Lazy family (f(i))_{i in [3, 4, 7]}
     680            Lazy family (<lambda>(i))_{i in [3, 4, 7]}
    652681           
    653682        """
     683        if name is not None:
     684            warn(name_warn_message)       
    654685        from copy import copy
    655686        self.set = copy(set)
    656         self.name = name
    657687        self.function = function
    658688
    659     def __repr__(self):
     689    def _repr_(self):
    660690        """
    661691        EXAMPLES::
    662692       
     693            sage: def fun(i): 2*i
     694            sage: f = LazyFamily([3,4,7], fun); f
     695            Lazy family (fun(i))_{i in [3, 4, 7]}
     696
     697            sage: f = Family(Permutations(3), attrcall("to_lehmer_code"), lazy=True); f
     698            Lazy family (i.to_lehmer_code())_{i in Standard permutations of 3}
     699
    663700            sage: f = LazyFamily([3,4,7], lambda i: 2*i); f
    664             Lazy family (f(i))_{i in [3, 4, 7]}
     701            Lazy family (<lambda>(i))_{i in [3, 4, 7]}
    665702        """
    666         return "Lazy family (%s(i))_{i in %s}"%(self.name,self.set)
     703        if isinstance(self.function, type(lambda x:1)):
     704            name = self.function.__name__
     705#            if name == "<lambda>":
     706#                name = "f"
     707            name = name+"(i)"
     708        else:
     709            name = self.function.__repr__()
     710            if isinstance(self.function, AttrCallObject):
     711                name = "i"+name[1:]
     712            else:
     713                name = name+"(i)"
     714        return "Lazy family (%s)_{i in %s}"%(name,self.set)
    667715
    668716    def keys(self):
    669717        """
     
    726774            sage: d = f.__getstate__()
    727775            sage: d['set']
    728776            [3, 4, 7]
     777
     778            sage: f = LazyFamily(Permutations(3), lambda p: p.to_lehmer_code())
     779            sage: f == loads(dumps(f))
     780            True
     781
     782            sage: f = LazyFamily(Permutations(3), attrcall("to_lehmer_code"))
     783            sage: f == loads(dumps(f))
     784            True           
    729785        """
    730786        f = self.function
    731787        # This should be done once for all by registering
     
    735791            f = pickle_function(f)
    736792       
    737793        return {'set': self.set,
    738                 'name': self.name,
    739794                'function': f}
    740795
    741796    def __setstate__(self, d):
     
    757812            from sage.misc.fpickle import unpickle_function
    758813            function = unpickle_function(function)
    759814           
    760         self.__init__(d['set'], function, d['name'])
     815        self.__init__(d['set'], function)
    761816
    762817
    763818class TrivialFamily(AbstractFamily):
    764819    r"""
    765820    ``TrivialFamily(c)`` turn the container c into a family indexed by
    766     the set `{0,\dots, len(c)}`. The container `c` can be either a list or a
     821    the set `{0, \dots, len(c)}`. The container `c` can be either a list or a
    767822    tuple.
    768823   
    769824    Instances should be created via the Family factory, which see for
    770825    examples and tests.
    771826    """
    772     def __init__(self, set):
     827    def __init__(self, enumeration):
    773828        """
    774829        EXAMPLES::
    775830       
     
    780835            sage: f == loads(dumps(f))
    781836            True
    782837        """
    783         self.set = tuple(set)
     838        self._enumeration = tuple(enumeration)
    784839
    785     def __repr__(self):
     840    def _repr_(self):
    786841        """
    787842        EXAMPLES::
    788843       
    789844            sage: f = TrivialFamily([3,4,7]); f
    790845            Family (3, 4, 7)
    791846        """
    792         return "Family %s"%((self.set),)
     847        return "Family %s"%((self._enumeration),)
    793848
    794849    def keys(self):
    795850        """
     
    801856            sage: f.keys()
    802857            [0, 1, 2]
    803858        """
    804         return range(len(self.set))
     859        return range(len(self._enumeration))
    805860
    806861    def cardinality(self):
    807862        """
     
    813868            sage: f.cardinality()
    814869            3
    815870        """
    816         return Integer(len(self.set))
     871        return Integer(len(self._enumeration))
    817872   
    818873    def __iter__(self):
    819874        """
     
    823878            sage: [i for i in f]
    824879            [3, 4, 7]
    825880        """
    826         for i in self.set:
    827             yield i
     881        return iter(self._enumeration)
    828882
    829883    def __getitem__(self, i):
    830884        """       
     
    834888            sage: f[1]
    835889            4       
    836890        """
    837         return self.set[i]
     891        return self._enumeration[i]
    838892
    839893    def __getstate__(self):
    840894        """
     
    842896       
    843897            sage: f = TrivialFamily([3,4,7])
    844898            sage: f.__getstate__()
    845             {'set': (3, 4, 7)}
     899            {'_enumeration': (3, 4, 7)}
    846900        """
    847         return {'set': self.set}
     901        return {'_enumeration': self._enumeration}
    848902
    849903    def __setstate__(self, state):
    850904        """
    851905        TESTS::
    852906       
    853907            sage: f = TrivialFamily([3,4,7])
    854             sage: f.__setstate__({'set': (2, 4, 8)})
     908            sage: f.__setstate__({'_enumeration': (2, 4, 8)})
    855909            sage: f
    856910            Family (2, 4, 8)
    857911        """
    858         self.__init__(state['set'])
     912        self.__init__(state['_enumeration'])