# Ticket #715: trac_715-reviewer-take2.patch

File trac_715-reviewer-take2.patch, 19.3 KB (added by jpflori, 8 years ago)
• ## sage/categories/action.pyx

# HG changeset patch
# User Jean-Pierre Flori <jean-pierre.flor@ssi.gouv.fr>
# Date 1334221874 -7200
# Node ID 3d76af023be692183fcaa41b18875402a36a2f75
# Parent  75769cbc372eb71c07067d54c3bf4c8e57cb2c04
#715: Reviewer patch

diff --git a/sage/categories/action.pyx b/sage/categories/action.pyx
 a r""" Group, ring, etc. actions on objects. Group, ring, etc. actions on objects. The terminology and notation used is suggestive of groups acting on sets, but this framework can be used for modules, algebras, etc. The terminology and notation used is suggestive of groups acting on sets, but this framework can be used for modules, algebras, etc. A group action $G \times S \rightarrow S$ is a functor from $G$ to Sets. A group action $G \times S \rightarrow S$ is a functor from $G$ to Sets. AUTHORS: -- Robert Bradshaw: initial version .. WARNING:: An :class:Action object only keeps a weak reference to the underlying set which is acted upon. This decision was made in :trac:715 in order to allow garbage collection within the coercion framework (this is where actions are mainly used) and avoid memory leaks. :: sage: from sage.categories.action import Action sage: class P: pass sage: A = Action(P(),P()) sage: import gc sage: _ = gc.collect() sage: A Traceback (most recent call last): ... RuntimeError: This action acted on a set that became garbage collected To avoid garbage collection of the underlying set, it is sufficient to create a strong reference to it before the action is created. :: sage: _ = gc.collect() sage: from sage.categories.action import Action sage: class P: pass sage: q = P() sage: A = Action(P(),q) sage: gc.collect() 0 sage: A Left action by <__main__.P instance at ...> on <__main__.P instance at ...> AUTHOR: - Robert Bradshaw: initial version """ #***************************************************************************** sage: A.left_domain() is R True By trac ticket #715, there is only a weak reference to the underlying set. Hence, the underlying set may be garbage collected, even when the By :trac:715, there is only a weak reference to the underlying set. Hence, the underlying set may be garbage collected, even when the action is still alive. This may result in a runtime error, as follows:: sage: from sage.categories.action import Action Left action by <__main__.P instance at ...> on <__main__.P instance at ...> sage: del q sage: import gc sage: n = gc.collect() sage: _ = gc.collect() sage: A Traceback (most recent call last): ...
• ## sage/matrix/action.pyx

diff --git a/sage/matrix/action.pyx b/sage/matrix/action.pyx
 a """ These are the actions used by the coercion model for matrix and vector multiplications. These are the actions used by the coercion model for matrix and vector multiplications. AUTHORS: -- Robert Bradshaw (2007-09): Initial version. .. WARNING:: The class :class:MatrixMulAction and its descendants extends the class :class:Action. As a cosnequence objects from these classes only keep weak references to the underlying sets which are acted upon. This decision was made in :trac:715 in order to allow garbage collection within the coercion framework, where actions are mainly used, and avoid memory leaks. To ensure that the underlying set of such an object does not get garbage collected, it is sufficient to explicitely create a strong reference to it before creating the action. :: sage: MSQ = MatrixSpace(QQ, 2) sage: MSZ = MatrixSpace(ZZ['x'], 2) sage: A = MSQ.get_action(MSZ) sage: A Left action by Full MatrixSpace of 2 by 2 dense matrices over Rational Field on Full MatrixSpace of 2 by 2 dense matrices over Univariate Polynomial Ring in x over Integer Ring sage: import gc sage: _ = gc.collect() sage: A Left action by Full MatrixSpace of 2 by 2 dense matrices over Rational Field on Full MatrixSpace of 2 by 2 dense matrices over Univariate Polynomial Ring in x over Integer Ring .. NOTE:: The :func:MatrixSpace function caches the objects it creates. Therefore, the underlying set MSZ in the above example will not be garbage collected, even if it is not strongly ref'ed. Nonetheless, there is no guarantee that the set that is acted upon will always be cached in such a way, so that following the above example is good practice. AUTHOR: - Robert Bradshaw (2007-09): Initial version. """ #***************************************************************************** """ EXAMPLES: By trac ticket #715, there only is a weak reference on the underlying set, so that it can be garbage collected if only the action itself is explicitly referred to. Hence, we first assign the involved matrix spaces to a variable:: By :trac:715, there only is a weak reference on the underlying set, so that it can be garbage collected if only the action itself is explicitly referred to. Hence, we first assign the involved matrix spaces to a variable:: sage: MSQ = MatrixSpace(QQ, 2) sage: MSZ = MatrixSpace(ZZ['x'], 2) Full MatrixSpace of 2 by 2 dense matrices over Univariate Polynomial Ring in x over Integer Ring sage: A.codomain() Full MatrixSpace of 2 by 2 dense matrices over Univariate Polynomial Ring in x over Rational Field .. NOTE:: The :func:MatrixSpace function caches the object it creates. Therefore, the underlying set MSZ in the above example will not be garbage collected, even if it is not strongly ref'ed. Nonetheless, there is no guarantee that the set that is acted upon will always be cached in such a way, so that following the above example is good practice. """ return self.underlying_set() """ EXAMPLES: By trac ticket #715, there only is a weak reference on the underlying set, so that it can be garbage collected if only the action itself is explicitly referred to. Hence, we first assign the involved matrix spaces to a variable:: By :trac:715, there only is a weak reference on the underlying set, so that it can be garbage collected if only the action itself is explicitly referred to. Hence, we first assign the involved matrix spaces to a variable:: sage: R. = ZZ[] sage: MSR = MatrixSpace(R, 3, 3) [  0   x] [2*x 3*x] [4*x 5*x] .. NOTE:: The :func:MatrixSpace function caches the object it creates. Therefore, the underlying set MSZ in the above example will not be garbage collected, even if it is not strongly ref'ed. Nonetheless, there is no guarantee that the set that is acted upon will always be cached in such a way, so that following the above example is good practice. """ if not is_MatrixSpace(S): raise TypeError, "Not a matrix space: %s" % S """ EXAMPLES: By trac ticket #715, there only is a weak reference on the underlying set, so that it can be garbage collected if only the action itself is explicitly referred to. Hence, we first assign the involved matrix spaces to a variable:: By :trac:715, there only is a weak reference on the underlying set, so that it can be garbage collected if only the action itself is explicitly referred to. Hence, we first assign the involved matrix spaces to a variable:: sage: from sage.matrix.action import MatrixMatrixAction sage: R. = ZZ[] Left action by Full MatrixSpace of 3 by 3 dense matrices over Univariate Polynomial Ring in x over Integer Ring on Full MatrixSpace of 3 by 2 dense matrices over Rational Field sage: A.codomain() Full MatrixSpace of 3 by 2 dense matrices over Univariate Polynomial Ring in x over Rational Field .. NOTE:: The :func:MatrixSpace function caches the object it creates. Therefore, the underlying set MSZ in the above example will not be garbage collected, even if it is not strongly ref'ed. Nonetheless, there is no guarantee that the set that is acted upon will always be cached in such a way, so that following the above example is good practice. """ if self.G.ncols() != self.underlying_set().nrows(): raise TypeError, "incompatible dimensions %s, %s" % (self.G.ncols(),  self.underlying_set().nrows())
• ## sage/structure/coerce_dict.pyx

diff --git a/sage/structure/coerce_dict.pyx b/sage/structure/coerce_dict.pyx
 a cdef class TripleDictEraser: """ Erases items from a :class:TripleDict when a weak reference becomes invalid. Erases items from a :class:TripleDict when a weak reference becomes invalid. This is of internal use only. Instances of this class will be passed as a callback function when creating a weak reference. This is of internal use only. Instances of this class will be passed as a callback function when creating a weak reference. EXAMPLES:: AUTHOR: Simon King (2012-01) - Simon King (2012-01) """ def __init__(self, D): """ self.D = D def __call__(self, r): """ INPUT: A weak reference with key. When this is called with a weak reference r, then each item containing r is removed from the associated :class:TripleDict. Normally, this only happens when a weak reference becomes invalid. When this is called with a weak reference r, then each item containing r is removed from the associated :class:TripleDict. Normally, this only happens when a weak reference becomes invalid. EXAMPLES:: sage: n = gc.collect() sage: len(T)    # indirect doctest 0 """ # r is a (weak) reference (typically to a parent), # and it knows the stored key of the unique triple r() had been part of. """ This is a hashtable specifically designed for (read) speed in the coercion model. It differs from a python dict in the following important ways: - All keys must be sequence of exactly three elements. All sequence types (tuple, list, etc.) map to the same item. - Comparison is done using the 'is' rather than '==' operator. There are special cdef set/get methods for faster access. It is bare-bones in the sense that not all dictionary methods are implemented. It is implemented as a list of lists (hereafter called buckets). The bucket is chosen according to a very simple hash based on the object pointer. and each bucket is of the form [id(k1), id(k2), id(k3), value, id(k1), id(k2), id(k3), value, ...], on which a linear search is performed. is chosen according to a very simple hash based on the object pointer, and each bucket is of the form [id(k1), id(k2), id(k3), value, id(k1), id(k2), id(k3), value, ...], on which a linear search is performed. To spread objects evenly, the size should ideally be a prime, and certainly not divisible by 2. EXAMPLES:: sage: from sage.structure.coerce_dict import TripleDict sage: L = TripleDict(31) sage: a = 'a'; b = 'b'; c = 'c' Traceback (most recent call last): ... KeyError: 'a' The following illustrates why even sizes are bad:: sage: L = TripleDict(4, L) Note that this kind of dictionary is also used for caching actions and coerce maps. In previous versions of Sage, the cache was by strong references and resulted in a memory leak in the following example. However, this leak was fixed by trac ticket #715, using weak references:: example. However, this leak was fixed by trac ticket :trac:715, using weak references:: sage: K = GF(1<<55,'t') sage: for i in range(50): sage: len(LE)    # indirect doctest 1 AUTHOR:: AUTHORS: - Robert Bradshaw, 2007-08 - Simon King, 2012-01 """ def __init__(self, size, data=None, threshold=0): """ Create a special dict using triples for keys. Create a special dict using triples for keys. EXAMPLES:: sage: from sage.structure.coerce_dict import TripleDict if data is not None: for (k1,k2,k3), v in data.iteritems(): self.set(k1,k2,k3, v) def __len__(self): """ The number of items in self. EXAMPLES:: sage: from sage.structure.coerce_dict import TripleDict 3 """ return self._size def stats(self): """ The distribution of items in buckets. OUTPUT:: The distribution of items in buckets. OUTPUT: - (min, avg, max) EXAMPLES:: sage: from sage.structure.coerce_dict import TripleDict sage: for i in range(100): L[i,i,i] = None sage: L.stats() # random (0, 0.03325573661456601, 1) sage: L = TripleDict(1) sage: for i in range(100): L[i,i,i] = None sage: L.stats() else: min = 0 return min, 1.0*size/len(self.buckets), max def bucket_lens(self): """ The distribution of items in buckets. The distribution of items in buckets. OUTPUT: A list of how many items are in each bucket. A list of how many items are in each bucket. EXAMPLES:: sage: from sage.structure.coerce_dict import TripleDict [3, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 3, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4] sage: sum(L.bucket_lens()) 100 sage: L = TripleDict(1) sage: for i in range(100): L[i,i,i] = None sage: L.bucket_lens() [100] """ return [len(self.buckets[i])/4 for i from 0 <= i < len(self.buckets)] def _get_buckets(self): """ The actual buckets of self, for debugging. EXAMPLE:: The actual buckets of self, for debugging. EXAMPLES:: sage: from sage.structure.coerce_dict import TripleDict sage: L = TripleDict(3) [[0, 0, 0, None], [], []] """ return self.buckets def __getitem__(self, k): """ EXAMPLES:: except (TypeError,ValueError): raise KeyError, k return self.get(k1, k2, k3) cdef get(self, object k1, object k2, object k3): cdef Py_ssize_t h = (k1 + 13*k2 ^ 503*k3) #if h < 0: h = -h  # shouldn't "%" result in a positive number anyway? if tmp == k3: return