# Ticket #15349: trac_15349-lazy-family-values-dg.patch

File trac_15349-lazy-family-values-dg.patch, 16.3 KB (added by darij, 9 years ago)
• ## sage/sets/family.py

# HG changeset patch
# User darij grinberg <darijgrinberg@gmail.com>
# Date 1383462578 25200
# Node ID 839c49fe7d4c206d35358dd201f335b8e3885801
# Parent  dc029471c9063cc44bc9aa05ffa40b9ca97da8c1
trac #15349: value() for lazy families

diff --git a/sage/sets/family.py b/sage/sets/family.py
 a from sage.categories.enumerated_sets imp from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.sets.finite_enumerated_set import FiniteEnumeratedSet from sage.misc.lazy_import import lazy_import from sage.rings.integer import Integer from sage.misc.misc import AttrCallObject from warnings import warn name_warn_message = "The keyword name for family has never been used and will be removed shortly. Please update your code." def Family(indices, function = None, hidden_keys = [], hidden_function = None, lazy = False, name=None): def Family(indices, function = None, hidden_keys = [], hidden_function = None, lazy = False): r""" A Family is an associative container which models a family (f_i)_{i \in I}. Then, f[i] returns the element of the family indexed by i. Whenever available, set and combinatorial class operations (counting, iteration, listing) on the family are induced from those of the index set. A Family is an associative container which models a family (f_i)_{i \in I}. Then, f[i] returns the element of the family f indexed by i. Whenever available, set and combinatorial class operations (counting, iteration, listing) on the family are induced from those of the index set. There are several available implementations (classes) for different usages; Family serves as a factory, and will create instances of the appropriate classes depending on its arguments. usages; Family serves as a factory, and will create instances of the appropriate classes depending on its arguments. EXAMPLES: In its simplest form, a list l or a tuple by itself is considered as the family (l[i]_{i \in I}) where I is the range 0\dots,len(l). So Family(l) returns the corresponding family:: In its simplest form, a list l or a tuple by itself is considered as the family (l[i]_{i \in I}) where I is the range 0,1,\dots,len(l)-1. So Family(l) returns the corresponding family:: sage: f = Family([1,2,3]) sage: f def Family(indices, function = None, hid sage: f Family (3, 5, 7) A family can also be constructed from a dictionary t. The resulting family is very close to t, except that the elements of the family are the values of t. Here, we define the family (f_i)_{i \in \{3,4,7\}} with f_3='a', f_4='b', and f_7='d':: A family can also be constructed from a dictionary t. The resulting family is very close to t, except that the elements of the family are the values of t. Here, we define the family (f_i)_{i \in \{3,4,7\}} with f_3='a', f_4='b', and f_7='d':: sage: f = Family({3: 'a', 4: 'b', 7: 'd'}) sage: f def Family(indices, function = None, hid Note that the lazy keyword parameter is only needed to force laziness. Usually it is automatically set to a correct default value (ie: False for finite data structures and True for enumerated sets:: False for finite data structures and True for enumerated sets):: sage: f == Family(ZZ, lambda i: 2*i) True Beware that for those kind of families len(f) is not supposed to work. As a replacement, use the .cardinality() method:: Beware that for those kinds of families len(f) is not supposed to work. As a replacement, use the .cardinality() method:: sage: f = Family(Permutations(3), attrcall("to_lehmer_code")) sage: list(f) def Family(indices, function = None, hid 6 Caveat: Only certain families with lazy behavior can be pickled. In particular, only functions that work with Sage's pickle_function and unpickle_function (in sage.misc.fpickle) will correctly particular, only functions that work with Sage's pickle_function and unpickle_function (in sage.misc.fpickle) will correctly unpickle. The following two work:: sage: f = Family(Permutations(3), lambda p: p.to_lehmer_code()); f def Family(indices, function = None, hid ValueError: Cannot pickle code objects from closures Finally, it can occasionally be useful to add some hidden elements in a family, which are accessible as f[i], but do not appear in the keys or the container operations:: to a family, which are accessible as f[i], but do not appear in the keys or the container operations:: sage: f = Family([3,4,7], lambda i: 2*i, hidden_keys=[2]) sage: f def Family(indices, function = None, hid called:: sage: def compute_value(i): ...       print('computing 2*'+str(i)) ...       return 2*i ....:       print('computing 2*'+str(i)) ....:       return 2*i sage: f = Family([3,4,7], compute_value, hidden_keys=[2]) computing 2*3 computing 2*4 def Family(indices, function = None, hid sage: len(f) 3 Family accept finite and infinite EnumeratedSets as input:: Family accepts finite and infinite EnumeratedSets as input:: sage: f = Family(FiniteEnumeratedSet([1,2,3])) sage: f def Family(indices, function = None, hid ['cc', 'aa', 'bb'] """ if name is not None: warn(name_warn_message) assert(type(hidden_keys) == list) assert(isinstance(lazy, bool)) if hidden_keys == []: if hidden_function is not None: raise ValueError, "hidden_function keyword only makes sense together with hidden_keys keyword !" raise ValueError("hidden_function keyword only makes sense together with hidden_keys keyword !") elif function is None: if lazy: raise ValueError, "lazy keyword only makes sense together with function keyword !" raise ValueError("lazy keyword only makes sense together with function keyword !") if isinstance(indices, dict): return FiniteFamily(indices) if isinstance(indices, (list, tuple) ): def Family(indices, function = None, hid return LazyFamily(indices, function) else: if lazy: raise ValueError, "lazy keyword is incompatible with hidden keys !" raise ValueError("lazy keyword is incompatible with hidden keys !") if hidden_function is None: hidden_function = function return FiniteFamilyWithHiddenKeys(dict([(i, function(i)) for i in indices]), def Family(indices, function = None, hid class AbstractFamily(Parent): """ The abstract class for family The abstract class for family. Any family belongs to a class which inherits from AbstractFamily. """ class AbstractFamily(Parent): """ return [] def zip(self, f, other, name = None): def zip(self, f, other): """ Given two families with same index set I (and same hidden keys if relevant), returns the family class AbstractFamily(Parent): """ assert(self.keys() == other.keys()) assert(self.hidden_keys() == other.hidden_keys()) if name is not None: warn(name_warn_message) return Family(self.keys(), lambda i: f(self[i],other[i]), hidden_keys = self.hidden_keys()) def map(self, f, name = None): def map(self, f): """ Returns the family ( f(\mathtt{self}[i]) )_{i \in I}, where I is the index set of self. class AbstractFamily(Parent): sage: list(g) ['a1', 'b1', 'd1'] """ if name is not None: warn(name_warn_message) return Family(self.keys(), lambda i: f(self[i]), hidden_keys = self.hidden_keys()) # temporary; tested by TestSuite. class AbstractFamily(Parent): class FiniteFamily(AbstractFamily): r""" A FiniteFamily is an associative container which models a finite family (f_i)_{i \in I}. Its elements f_i are therefore its values. Instances should be created via the Family factory, which see for further examples and tests. A FiniteFamily is an associative container which models a finite family (f_i)_{i \in I}. Its elements f_i are therefore its values. Instances should be created via the Family factory, which should be consulted for further examples and tests. EXAMPLES: We define the family (f_i)_{i \in \{3,4,7\}} with f_3=a, f_4=b, and f_7=d:: EXAMPLES: We define the family (f_i)_{i \in \{3,4,7\}} with f_3='a', f_4='b', and f_7='d':: sage: from sage.sets.family import FiniteFamily sage: f = FiniteFamily({3: 'a', 4: 'b', 7: 'd'}) class FiniteFamily(AbstractFamily): sage: f.keys() [3, 4, 7] However f behaves as a container for the f_i's:: However f behaves as a container for the f_i's:: sage: list(f) ['a', 'b', 'd'] class FiniteFamily(AbstractFamily): def values(self): """ Returns the elements of this family Return the list of the elements of this family. EXAMPLES:: class FiniteFamily(AbstractFamily): class FiniteFamilyWithHiddenKeys(FiniteFamily): r""" A close variant of FiniteFamily where the family contains some A close variant of FiniteFamily where the family contains some hidden keys whose corresponding values are computed lazily (and remembered). Instances should be created via the Family factory, which see for examples and tests. remembered). Instances should be created via the Family factory, which should also be consulted for examples and tests. Caveat: Only instances of this class whose functions are compatible with sage.misc.fpickle can be pickled. with sage.misc.fpickle can be pickled. """ def __init__(self, dictionary, hidden_keys, hidden_function): """ class FiniteFamilyWithHiddenKeys(FiniteF class LazyFamily(AbstractFamily): r""" A LazyFamily(I, f) is an associative container which models the LazyFamily(I, f) is an associative container which models the (possibly infinite) family (f(i))_{i \in I}. Instances should be created via the Family factory, which see for examples and tests. Instances should be created via the Family factory, whose documentation also provides examples and tests. """ def __init__(self, set, function, name=None): def __init__(self, set, function): """ TESTS:: class LazyFamily(AbstractFamily): category = EnumeratedSets() Parent.__init__(self, category = category) if name is not None: warn(name_warn_message) from copy import copy self.set = copy(set) self.function = function class LazyFamily(AbstractFamily): def __eq__(self, other): """ WARNING: Since there is no way to compare function, we only compare their name. WARNING: Since there is no way to compare functions, we only compare their names. TESTS:: class LazyFamily(AbstractFamily): sage: f == g True """ from sage.misc.fpickle import pickle_function if not isinstance(other, self.__class__): return False if not self.set == other.set: class LazyFamily(AbstractFamily): def keys(self): """ Returns self's keys. Returns self's keys. EXAMPLES:: class LazyFamily(AbstractFamily): """ return self.set def values(self, lazy_list=False): """ Return the elements of this family, either as a list (if the optional keyword lazy_list is set to False, which it is by default), or as a lazy list (if that keyword is set to True). This will misbehave when self is infinite, unless the optional keyword lazy_list is set to True, in which case a lazy list will be returned. EXAMPLES:: sage: from sage.sets.family import LazyFamily sage: f = LazyFamily(["c", "a", "b"], lambda x: x+x) sage: f.values() ['cc', 'aa', 'bb'] sage: f.values(lazy_list=True) lazy list ['cc', 'aa', 'bb'] sage: from itertools import count sage: f = LazyFamily(count(), lambda x: x**2) sage: f.values(lazy_list=True) lazy list [0, 1, 4, ...] """ if not lazy_list: return [ self.function(key) for key in self.set ] else: from sage.misc.lazy_list import lazy_list return lazy_list( self.function(key) for key in self.set ) def cardinality(self): """ Return the number of elements in self. Return the number of elements in self. EXAMPLES:: class LazyFamily(AbstractFamily): class TrivialFamily(AbstractFamily): r""" TrivialFamily(c) turn the container c into a family indexed by the set {0, \dots, len(c)}. The container c can be either a list or a tuple. TrivialFamily(c) turns the container c into a family indexed by the set \{0, 1, \dots, \mathrm{len}(c) - 1\}. The container c can be either a list or a tuple. Instances should be created via the Family factory, which see for examples and tests. Instances should be created via the Family factory, which should also be consulted for examples and tests. """ def __init__(self, enumeration): """ class TrivialFamily(AbstractFamily): Family (3, 4, 7) sage: f = TrivialFamily([3,4,7]); f Family (3, 4, 7) sage: TrivialFamily([3,4,7])[2] 7 sage: TestSuite(f).run() """ Parent.__init__(self, category = FiniteEnumeratedSets()) class TrivialFamily(AbstractFamily): def keys(self): """ Returns self's keys. Returns self's keys. EXAMPLES:: class TrivialFamily(AbstractFamily): def cardinality(self): """ Return the number of elements in self. Return the number of elements in self. EXAMPLES:: from sage.rings.infinity import Infinity class EnumeratedFamily(LazyFamily): r""" EnumeratedFamily(c) turn the enumerated set c into a family indexed by the set {0,\dots, c.cardinality()}. EnumeratedFamily(c) turns the enumerated set c into a family indexed by the set \{0, 1, \dots, c.\mathrm{cardinality()}-1 \}. Instances should be created via the Family factory, which see for examples and tests. Instances should be created via the Family factory; see the latter's documentation for examples and tests. """ def __init__(self, enumset): """ class EnumeratedFamily(LazyFamily): def keys(self): """ Returns self's keys. Returns self's keys. EXAMPLES:: class EnumeratedFamily(LazyFamily): def cardinality(self): """ Return the number of elements in self. Return the number of elements in self`. EXAMPLES::