Ticket #715: 715_all.patch
File 715_all.patch, 54.4 KB (added by , 9 years ago) |
---|
-
doc/en/reference/structure.rst
# HG changeset patch # User Simon King <simon.king@uni-jena.de> # Date 1326139789 -3600 # Node ID de9f19a8dedc0863676041f555f746809ac45db5 # Parent c95c1ffa8aff73e6055bf82919815d93bf66d1a8 Use weak references in the `TripleDict` coercion cache The C % operator can return negative results, thus, we use an unsigned type for the hash diff --git a/doc/en/reference/structure.rst b/doc/en/reference/structure.rst
a b 9 9 sage/structure/parent_old 10 10 sage/structure/parent_base 11 11 sage/structure/parent_gens 12 sage/structure/coerce_dict 12 13 sage/structure/formal_sum 13 14 sage/structure/factorization 14 15 sage/structure/element -
sage/categories/action.pxd
diff --git a/sage/categories/action.pxd b/sage/categories/action.pxd
a b 5 5 6 6 cdef class Action(Functor): 7 7 cdef G 8 cdef S8 cdef US 9 9 cdef bint _is_left 10 10 cdef op 11 cdef underlying_set(self) 11 12 cpdef _call_(self, a, b) 12 13 13 14 -
sage/categories/action.pyx
diff --git a/sage/categories/action.pyx b/sage/categories/action.pyx
a b 1 1 r""" 2 Group, ring, etc. actions on objects. 2 Group, ring, etc. actions on objects. 3 3 4 The terminology and notation used is suggestive of groups 5 acting on sets, but this framework can be used for modules, 6 algebras, etc. 4 The terminology and notation used is suggestive of groups acting on sets, 5 but this framework can be used for modules, algebras, etc. 7 6 8 A group action $G \times S \rightarrow S$ is a functor from $G$ to Sets. 7 A group action $G \times S \rightarrow S$ is a functor from $G$ to Sets. 9 8 10 AUTHORS: 11 -- Robert Bradshaw: initial version 9 .. WARNING:: 10 11 An :class:`Action` object only keeps a weak reference to the underlying set 12 which is acted upon. This decision was made in :trac:`715` in order to 13 allow garbage collection within the coercion framework (this is where 14 actions are mainly used) and avoid memory leaks. 15 16 :: 17 18 sage: from sage.categories.action import Action 19 sage: class P: pass 20 sage: A = Action(P(),P()) 21 sage: import gc 22 sage: _ = gc.collect() 23 sage: A 24 Traceback (most recent call last): 25 ... 26 RuntimeError: This action acted on a set that became garbage collected 27 28 To avoid garbage collection of the underlying set, it is sufficient to 29 create a strong reference to it before the action is created. 30 31 :: 32 33 sage: _ = gc.collect() 34 sage: from sage.categories.action import Action 35 sage: class P: pass 36 sage: q = P() 37 sage: A = Action(P(),q) 38 sage: gc.collect() 39 0 40 sage: A 41 Left action by <__main__.P instance at ...> on <__main__.P instance at ...> 42 43 AUTHOR: 44 45 - Robert Bradshaw: initial version 12 46 """ 13 47 14 48 #***************************************************************************** … … 32 66 33 67 import homset 34 68 import sage.structure.element 69 from weakref import ref 35 70 36 71 include "../ext/stdsage.pxi" 37 72 … … 48 83 from groupoid import Groupoid 49 84 Functor.__init__(self, Groupoid(G), category(S)) 50 85 self.G = G 51 self. S = S86 self.US = ref(S) 52 87 self._is_left = is_left 53 88 self.op = op 54 89 … … 61 96 if g in self.G: 62 97 return ActionEndomorphism(self, self.G(g)) 63 98 elif g == self.G: 64 return self. S99 return self.underlying_set() 65 100 else: 66 101 raise TypeError, "%s not an element of %s"%(g, self.G) 67 102 elif len(args) == 2: 68 103 if self._is_left: 69 return self._call_(self.G(args[0]), self. S(args[1]))104 return self._call_(self.G(args[0]), self.underlying_set()(args[1])) 70 105 else: 71 return self._call_(self. S(args[0]), self.G(args[1]))106 return self._call_(self.underlying_set()(args[0]), self.G(args[1])) 72 107 73 108 cpdef _call_(self, a, b): 74 109 raise NotImplementedError, "Action not implemented." … … 91 126 92 127 def __repr__(self): 93 128 side = "Left" if self._is_left else "Right" 94 return "%s %s by %r on %r"%(side, self._repr_name_(), self.G, self.S) 129 return "%s %s by %r on %r"%(side, self._repr_name_(), self.G, 130 self.underlying_set()) 95 131 96 132 def _repr_name_(self): 97 133 return "action" 98 134 99 135 def actor(self): 100 136 return self.G 101 137 138 cdef underlying_set(self): 139 """ 140 The set on which the actor acts (it is not necessarily the codomain of 141 the action). 142 143 NOTE: 144 145 Since this is a cdef'ed method, we can only provide an indirect doctest. 146 147 EXAMPLES:: 148 149 sage: P = QQ['x'] 150 sage: R = (ZZ['x'])['y'] 151 sage: A = R.get_action(P,operator.mul,True) 152 sage: A # indirect doctest 153 Right scalar multiplication by Univariate Polynomial Ring in x over 154 Rational Field on Univariate Polynomial Ring in y over Univariate 155 Polynomial Ring in x over Integer Ring 156 157 In this example, the underlying set is the ring ``R``. This is the same 158 as the left domain, which is different from the codomain of the action:: 159 160 sage: A.codomain() 161 Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Rational Field 162 sage: A.codomain() == R 163 False 164 sage: A.left_domain() is R 165 True 166 167 By :trac:`715`, there is only a weak reference to the underlying set. 168 Hence, the underlying set may be garbage collected, even when the 169 action is still alive. This may result in a runtime error, as follows:: 170 171 sage: from sage.categories.action import Action 172 sage: class P: pass 173 sage: p = P() 174 sage: q = P() 175 sage: A = Action(p,q) 176 sage: A 177 Left action by <__main__.P instance at ...> on <__main__.P instance at ...> 178 sage: del q 179 sage: import gc 180 sage: _ = gc.collect() 181 sage: A 182 Traceback (most recent call last): 183 ... 184 RuntimeError: This action acted on a set that became garbage collected 185 186 """ 187 S = self.US() 188 if S is None: 189 raise RuntimeError, "This action acted on a set that became garbage collected" 190 return S 191 102 192 def codomain(self): 103 return self.S193 return self.underlying_set() 104 194 105 195 def domain(self): 106 return self. S196 return self.underlying_set() 107 197 108 198 def left_domain(self): 109 199 if self._is_left: … … 145 235 # We must be in the case that parent(~a) == parent(a) 146 236 # so we can invert in call_c code below. 147 237 if (PY_TYPE_CHECK(G, Group) and G.is_multiplicative()) or G.is_field(): 148 Action.__init__(self, G, action. S, action._is_left)238 Action.__init__(self, G, action.underlying_set(), action._is_left) 149 239 self._action = action 150 240 return 151 241 else: 152 242 K = G._pseudo_fraction_field() 153 Action.__init__(self, K, action. S, action._is_left)243 Action.__init__(self, K, action.underlying_set(), action._is_left) 154 244 self._action = action 155 245 return 156 246 except (AttributeError, NotImplementedError): … … 190 280 right_precomposition = homset.Hom(right_precomposition._codomain, right).natural_map() * right_precomposition 191 281 right = right_precomposition._domain 192 282 if action._is_left: 193 Action.__init__(self, left, action. S, 1)283 Action.__init__(self, left, action.underlying_set(), 1) 194 284 else: 195 Action.__init__(self, right, action. S, 0)285 Action.__init__(self, right, action.underlying_set(), 0) 196 286 self._action = action 197 287 self.left_precomposition = left_precomposition 198 288 self.right_precomposition = right_precomposition … … 230 320 cdef class ActionEndomorphism(Morphism): 231 321 232 322 def __init__(self, Action action, g): 233 Morphism.__init__(self, homset.Hom(action.S, action.S)) 323 Morphism.__init__(self, homset.Hom(action.underlying_set(), 324 action.underlying_set())) 234 325 self._action = action 235 326 self._g = g 236 327 … … 241 332 return self._action._call_(x, self._g) 242 333 243 334 def _repr_(self): 244 return "Action of %s on %s under %s."%(self._g, self._action.S, self._action) 335 return "Action of %s on %s under %s."%(self._g, 336 self._action.underlying_set(), self._action) 245 337 246 338 def __mul__(left, right): 247 339 cdef ActionEndomorphism left_c, right_c -
sage/categories/functor.pxd
diff --git a/sage/categories/functor.pxd b/sage/categories/functor.pxd
a b 1 1 from sage.structure.sage_object cimport SageObject 2 2 3 3 cdef class Functor(SageObject): 4 cdef __weakref__ 4 5 cdef object __domain 5 cdef object __codomain 6 No newline at end of file 6 cdef object __codomain -
sage/matrix/action.pyx
diff --git a/sage/matrix/action.pyx b/sage/matrix/action.pyx
a b 1 1 """ 2 These are the actions used by the coercion model for matrix and vector multiplications. 2 These are the actions used by the coercion model for matrix and vector 3 multiplications. 3 4 4 AUTHORS: 5 -- Robert Bradshaw (2007-09): Initial version. 5 .. WARNING:: 6 7 The class :class:`MatrixMulAction` and its descendants extends the class 8 :class:`Action`. As a cosnequence objects from these classes only keep weak 9 references to the underlying sets which are acted upon. This decision was 10 made in :trac:`715` in order to allow garbage collection within the coercion 11 framework, where actions are mainly used, and avoid memory leaks. 12 13 To ensure that the underlying set of such an object does not get garbage 14 collected, it is sufficient to explicitely create a strong reference to it 15 before creating the action. 16 17 :: 18 19 sage: MSQ = MatrixSpace(QQ, 2) 20 sage: MSZ = MatrixSpace(ZZ['x'], 2) 21 sage: A = MSQ.get_action(MSZ) 22 sage: A 23 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 24 sage: import gc 25 sage: _ = gc.collect() 26 sage: A 27 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 28 29 .. NOTE:: 30 31 The :func:`MatrixSpace` function caches the objects it creates. Therefore, 32 the underlying set ``MSZ`` in the above example will not be garbage 33 collected, even if it is not strongly ref'ed. Nonetheless, there is no 34 guarantee that the set that is acted upon will always be cached in such a 35 way, so that following the above example is good practice. 36 37 AUTHOR: 38 39 - Robert Bradshaw (2007-09): Initial version. 6 40 """ 7 41 8 42 #***************************************************************************** … … 38 72 39 73 def domain(self): 40 74 """ 41 EXAMPLES: 42 sage: A = MatrixSpace(QQ, 2).get_action(MatrixSpace(ZZ['x'], 2)); A 75 EXAMPLES: 76 77 By :trac:`715`, there only is a weak reference on the underlying set, 78 so that it can be garbage collected if only the action itself is 79 explicitly referred to. Hence, we first assign the involved matrix 80 spaces to a variable:: 81 82 sage: MSQ = MatrixSpace(QQ, 2) 83 sage: MSZ = MatrixSpace(ZZ['x'], 2) 84 sage: A = MSQ.get_action(MSZ); A 43 85 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 44 86 sage: A.actor() 45 87 Full MatrixSpace of 2 by 2 dense matrices over Rational Field … … 47 89 Full MatrixSpace of 2 by 2 dense matrices over Univariate Polynomial Ring in x over Integer Ring 48 90 sage: A.codomain() 49 91 Full MatrixSpace of 2 by 2 dense matrices over Univariate Polynomial Ring in x over Rational Field 92 93 .. NOTE:: 94 95 The :func:`MatrixSpace` function caches the object it creates. 96 Therefore, the underlying set ``MSZ`` in the above example will not 97 be garbage collected, even if it is not strongly ref'ed. 98 Nonetheless, there is no guarantee that the set that is acted upon 99 will always be cached in such a way, so that following the above 100 example is good practice. 101 50 102 """ 51 return self. S103 return self.underlying_set() 52 104 53 105 54 106 cdef class MatrixMatrixAction(MatrixMulAction): 55 107 def __init__(self, G, S): 56 108 """ 57 EXAMPLES: 109 EXAMPLES: 110 111 By :trac:`715`, there only is a weak reference on the underlying set, 112 so that it can be garbage collected if only the action itself is 113 explicitly referred to. Hence, we first assign the involved matrix 114 spaces to a variable:: 115 58 116 sage: R.<x> = ZZ[] 117 sage: MSR = MatrixSpace(R, 3, 3) 118 sage: MSQ = MatrixSpace(QQ, 3, 2) 59 119 sage: from sage.matrix.action import MatrixMatrixAction 60 sage: A = MatrixMatrixAction(M atrixSpace(R, 3, 3), MatrixSpace(QQ, 3, 2)); A120 sage: A = MatrixMatrixAction(MSR, MSQ); A 61 121 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 62 122 sage: A.codomain() 63 123 Full MatrixSpace of 3 by 2 dense matrices over Univariate Polynomial Ring in x over Rational Field … … 65 125 [ 0 x] 66 126 [2*x 3*x] 67 127 [4*x 5*x] 128 129 .. NOTE:: 130 131 The :func:`MatrixSpace` function caches the object it creates. 132 Therefore, the underlying set ``MSZ`` in the above example will not 133 be garbage collected, even if it is not strongly ref'ed. 134 Nonetheless, there is no guarantee that the set that is acted upon 135 will always be cached in such a way, so that following the above 136 example is good practice. 137 68 138 """ 69 139 if not is_MatrixSpace(S): 70 140 raise TypeError, "Not a matrix space: %s" % S … … 72 142 73 143 def _create_codomain(self, base): 74 144 """ 75 EXAMPLES: 145 EXAMPLES: 146 147 By :trac:`715`, there only is a weak reference on the underlying set, 148 so that it can be garbage collected if only the action itself is 149 explicitly referred to. Hence, we first assign the involved matrix 150 spaces to a variable:: 151 76 152 sage: from sage.matrix.action import MatrixMatrixAction 77 153 sage: R.<x> = ZZ[] 78 sage: A = MatrixMatrixAction(MatrixSpace(R, 3, 3), MatrixSpace(QQ, 3, 2)); A 154 sage: MSR = MatrixSpace(R, 3, 3) 155 sage: MSQ = MatrixSpace(QQ, 3, 2) 156 sage: A = MatrixMatrixAction(MSR, MSQ); A 79 157 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 80 158 sage: A.codomain() 81 159 Full MatrixSpace of 3 by 2 dense matrices over Univariate Polynomial Ring in x over Rational Field 160 161 .. NOTE:: 162 163 The :func:`MatrixSpace` function caches the object it creates. 164 Therefore, the underlying set ``MSZ`` in the above example will not 165 be garbage collected, even if it is not strongly ref'ed. 166 Nonetheless, there is no guarantee that the set that is acted upon 167 will always be cached in such a way, so that following the above 168 example is good practice. 169 82 170 """ 83 if self.G.ncols() != self.S.nrows(): 84 raise TypeError, "incompatible dimensions %s, %s" % (self.G.ncols(), self.S.nrows()) 85 return MatrixSpace(base, self.G.nrows(), self.S.ncols(), sparse = self.G.is_sparse() and self.S.is_sparse()) 171 if self.G.ncols() != self.underlying_set().nrows(): 172 raise TypeError, "incompatible dimensions %s, %s" % (self.G.ncols(), self.underlying_set().nrows()) 173 return MatrixSpace(base, self.G.nrows(), self.underlying_set().ncols(), 174 sparse = self.G.is_sparse() and self.underlying_set().is_sparse()) 86 175 87 176 cpdef _call_(self, g, s): 88 177 """ 89 178 EXAMPLES: 90 Respects compatible subdivisions: 179 180 Respects compatible subdivisions:: 181 91 182 sage: M = matrix(5, 5, prime_range(100)) 92 183 sage: M.subdivide(2,3); M 93 184 [ 2 3 5| 7 11] … … 112 203 [ 8168|11143] 113 204 [11056|15077] 114 205 115 Note that this is just like block matrix multiplication: 206 Note that this is just like block matrix multiplication:: 207 116 208 sage: M.subdivision(0,0) * N.subdivision(0,0) + M.subdivision(0,1) * N.subdivision(1,0) 117 209 [1048] 118 210 [3056] 119 211 120 212 If the subdivisions aren't compatible, ignore them. 213 :: 214 121 215 sage: N.subdivide(1,1); N 122 216 [ 0| 1] 123 217 [--+--] … … 156 250 cdef class MatrixVectorAction(MatrixMulAction): 157 251 def __init__(self, G, S): 158 252 """ 159 EXAMPLES: 253 EXAMPLES:: 254 160 255 sage: from sage.matrix.action import MatrixVectorAction 161 256 sage: A = MatrixVectorAction(MatrixSpace(QQ, 3, 3), VectorSpace(CDF, 4)); A 162 257 Traceback (most recent call last): … … 169 264 170 265 def _create_codomain(self, base): 171 266 """ 172 EXAMPLES: 267 EXAMPLES:: 268 173 269 sage: from sage.matrix.action import MatrixVectorAction 174 270 sage: A = MatrixVectorAction(MatrixSpace(QQ, 5, 3), VectorSpace(CDF, 3)); A 175 271 Left action by Full MatrixSpace of 5 by 3 dense matrices over Rational Field on Vector space of dimension 3 over Complex Double Field 176 272 sage: A.codomain() 177 273 Vector space of dimension 5 over Complex Double Field 178 274 """ 179 if self.G.ncols() != self.S.degree(): 180 raise TypeError, "incompatible dimensions %s, %s" % (self.G.ncols(), self.S.degree()) 275 if self.G.ncols() != self.underlying_set().degree(): 276 raise TypeError, "incompatible dimensions %s, %s" % (self.G.ncols(), 277 self.underlying_set().degree()) 181 278 return FreeModule(base, self.G.nrows(), sparse = self.G.is_sparse()) 182 279 183 280 cpdef _call_(self, g, s): … … 198 295 cdef class VectorMatrixAction(MatrixMulAction): 199 296 def __init__(self, G, S): 200 297 """ 201 EXAMPLES: 298 EXAMPLES:: 299 202 300 sage: from sage.matrix.action import VectorMatrixAction 203 301 sage: A = VectorMatrixAction(MatrixSpace(QQ, 5, 3), VectorSpace(CDF, 3)); A 204 302 Traceback (most recent call last): … … 211 309 212 310 def _create_codomain(self, base): 213 311 """ 214 EXAMPLES: 312 EXAMPLES:: 313 215 314 sage: from sage.matrix.action import VectorMatrixAction 216 315 sage: A = VectorMatrixAction(MatrixSpace(QQ, 3, 5), VectorSpace(CDF, 3)); A 217 316 Right action by Full MatrixSpace of 3 by 5 dense matrices over Rational Field on Vector space of dimension 3 over Complex Double Field 218 317 sage: A.codomain() 219 318 Vector space of dimension 5 over Complex Double Field 220 319 """ 221 if self.G.nrows() != self.S.degree(): 222 raise TypeError, "incompatible dimensions %s, %s" % (self.G.nrows(), self.S.degree()) 320 if self.G.nrows() != self.underlying_set().degree(): 321 raise TypeError, "incompatible dimensions %s, %s" % (self.G.nrows(), 322 self.underlying_set().degree()) 223 323 return FreeModule(base, self.G.ncols(), sparse = self.G.is_sparse()) 224 324 225 325 cpdef _call_(self, s, g): -
sage/rings/polynomial/multi_polynomial_libsingular.pyx
diff --git a/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/sage/rings/polynomial/multi_polynomial_libsingular.pyx
a b 243 243 import sage.libs.pari.gen 244 244 import polynomial_element 245 245 246 permstore=[] 246 247 cdef class MPolynomialRing_libsingular(MPolynomialRing_generic): 247 248 248 249 def __cinit__(self): … … 364 365 from sage.rings.polynomial.polynomial_element import PolynomialBaseringInjection 365 366 base_inject = PolynomialBaseringInjection(base_ring, self) 366 367 self.register_coercion(base_inject) 368 #permanently store a reference to this ring until deallocation works reliably 369 permstore.append(self) 367 370 368 371 def __dealloc__(self): 369 372 r""" -
sage/rings/polynomial/polynomial_ring_constructor.py
diff --git a/sage/rings/polynomial/polynomial_ring_constructor.py b/sage/rings/polynomial/polynomial_ring_constructor.py
a b 53 53 from sage.categories.commutative_rings import CommutativeRings 54 54 _CommutativeRings = CommutativeRings() 55 55 56 _cache = {} 56 import weakref 57 _cache = weakref.WeakValueDictionary() 57 58 58 59 def PolynomialRing(base_ring, arg1=None, arg2=None, 59 60 sparse=False, order='degrevlex', -
sage/structure/coerce.pxd
diff --git a/sage/structure/coerce.pxd b/sage/structure/coerce.pxd
a b 4 4 from parent cimport Parent 5 5 from sage.categories.action cimport Action 6 6 7 from coerce_dict cimport TripleDict , TripleDictIter7 from coerce_dict cimport TripleDict 8 8 9 9 cdef class CoercionModel_cache_maps(CoercionModel): 10 10 # This MUST be a mapping to tuples, where each -
sage/structure/coerce.pyx
diff --git a/sage/structure/coerce.pyx b/sage/structure/coerce.pyx
a b 1207 1207 try: 1208 1208 return self._action_maps.get(R, S, op) 1209 1209 except KeyError: 1210 action = self.discover_action(R, S, op) 1211 action = self.verify_action(action, R, S, op) 1212 self._action_maps.set(R, S, op, action) 1213 return action 1210 pass 1211 action = self.discover_action(R, S, op) 1212 action = self.verify_action(action, R, S, op) 1213 self._action_maps.set(R, S, op, action) 1214 return action 1214 1215 1215 1216 cpdef verify_action(self, action, R, S, op, bint fix=True): 1216 1217 r""" -
sage/structure/coerce_actions.pyx
diff --git a/sage/structure/coerce_actions.pyx b/sage/structure/coerce_actions.pyx
a b 100 100 Multivariate Polynomial Ring in x, y, z over Rational Field 101 101 """ 102 102 if self._codomain is None: 103 self._codomain = parent_c(self.act(an_element(self.G), an_element(self.S))) 103 self._codomain = parent_c(self.act(an_element(self.G), 104 an_element(self.underlying_set()))) 104 105 return self._codomain 105 106 106 107 … … 332 333 """ 333 334 if self.extended_base is not None: 334 335 return self.extended_base 335 return self. S336 return self.underlying_set() 336 337 337 338 def domain(self): 338 339 """ … … 345 346 sage: A.domain() 346 347 Multivariate Polynomial Ring in x, y, z over Integer Ring 347 348 """ 348 return self. S349 return self.underlying_set() 349 350 350 351 351 352 -
sage/structure/coerce_dict.pxd
diff --git a/sage/structure/coerce_dict.pxd b/sage/structure/coerce_dict.pxd
a b 1 1 cdef class TripleDict: 2 2 cdef Py_ssize_t _size 3 3 cdef buckets 4 cdef dict _refcache 4 5 cdef double threshold 5 cdef get(self, k1, k2, k3) 6 cdef set(self, k1, k2, k3, value) 6 cdef TripleDictEraser eraser 7 cdef get(self, object k1, object k2, object k3) 8 cdef set(self, object k1, object k2, object k3, value) 7 9 8 cdef class TripleDictIter: 9 cdef TripleDict pairs 10 cdef buckets, bucket_iter 10 cdef class TripleDictEraser: 11 cdef TripleDict D -
sage/structure/coerce_dict.pyx
diff --git a/sage/structure/coerce_dict.pyx b/sage/structure/coerce_dict.pyx
a b 1 1 #***************************************************************************** 2 2 # Copyright (C) 2007 Robert Bradshaw <robertwb@math.washington.edu> 3 # 2012 Simon King <simon.king@uni-jena.de> 3 4 # 4 5 # Distributed under the terms of the GNU General Public License (GPL) 5 6 # 6 7 # http://www.gnu.org/licenses/ 7 8 #***************************************************************************** 9 """ 10 Containers for storing coercion data 8 11 12 This module provides :class:`TripleDict`. It is a structure similar to 13 ``WeakKeyDictionary`` in Python's weakref module, and is optimized for lookup 14 speed. Keys consist of a triple (k1,k2,k3) and are looked up by identity 15 rather than equality. The keys are stored by weakrefs if possible. If any 16 one of the components k1, k2, k3 gets garbage collected, then the entry is 17 removed from the :class:`TripleDict`. 9 18 19 Key components that do not allow for weakrefs are stored via a normal 20 refcounted reference. That means that any entry stored using a triple 21 (k1,k2,k3) so that none of the k1,k2,k3 allows a weak reference behaves 22 as an entry in a normal dictionary: Its existence in :class:`TripleDict` 23 prevents it from being garbage collected. 24 25 That container currently is used to store coercion and conversion maps 26 between two parents (:trac:`715`) and to store homsets of pairs of objects 27 of a category (:trac:`11521`). In both cases, it is essential that the parent 28 structures remain garbage collectable, it is essential that the data access 29 is faster than with a usual ``WeakKeyDictionary``, and we enforce the "unique 30 parent condition" in Sage (parent structures should be identical if they are 31 equal). 32 """ 10 33 include "../ext/python_list.pxi" 11 34 35 from weakref import KeyedRef 36 37 ############################################ 38 # The following code is responsible for 39 # removing dead references from the cache 40 ############################################ 41 42 cdef class TripleDictEraser: 43 """ 44 Erases items from a :class:`TripleDict` when a weak reference becomes 45 invalid. 46 47 This is of internal use only. Instances of this class will be passed as a 48 callback function when creating a weak reference. 49 50 EXAMPLES:: 51 52 sage: from sage.structure.coerce_dict import TripleDict 53 sage: class A: pass 54 sage: a = A() 55 sage: T = TripleDict(11) 56 sage: T[a,ZZ,None] = 1 57 sage: T[ZZ,a,1] = 2 58 sage: T[a,a,ZZ] = 3 59 sage: len(T) 60 3 61 sage: del a 62 sage: import gc 63 sage: n = gc.collect() 64 sage: len(T) 65 0 66 67 AUTHOR: 68 69 - Simon King (2012-01) 70 """ 71 72 def __init__(self, D): 73 """ 74 INPUT: 75 76 A :class:`TripleDict`. 77 78 EXAMPLES:: 79 80 sage: from sage.structure.coerce_dict import TripleDict, TripleDictEraser 81 sage: D = TripleDict(11) 82 sage: TripleDictEraser(D) 83 <sage.structure.coerce_dict.TripleDictEraser object at ...> 84 85 """ 86 self.D = D 87 88 def __call__(self, r): 89 """ 90 INPUT: 91 92 A weak reference with key. 93 94 When this is called with a weak reference ``r``, then each item 95 containing ``r`` is removed from the associated :class:`TripleDict`. 96 Normally, this only happens when a weak reference becomes invalid. 97 98 EXAMPLES:: 99 100 sage: from sage.structure.coerce_dict import TripleDict 101 sage: class A: pass 102 sage: a = A() 103 sage: T = TripleDict(11) 104 sage: T[a,ZZ,None] = 1 105 sage: T[ZZ,a,1] = 2 106 sage: T[a,a,ZZ] = 3 107 sage: len(T) 108 3 109 sage: del a 110 sage: import gc 111 sage: n = gc.collect() 112 sage: len(T) # indirect doctest 113 0 114 """ 115 # r is a (weak) reference (typically to a parent), and it knows the 116 # stored key of the unique triple r() had been part of. 117 # We remove that unique triple from self.D 118 cdef size_t k1,k2,k3 119 k1,k2,k3 = r.key 120 cdef size_t h = (k1 + 13*k2 ^ 503*k3) 121 cdef list bucket = <object>PyList_GET_ITEM(self.D.buckets, h % PyList_GET_SIZE(self.D.buckets)) 122 cdef int i 123 for i from 0 <= i < PyList_GET_SIZE(bucket) by 4: 124 if <size_t><object>PyList_GET_ITEM(bucket, i)==k1 and \ 125 <size_t><object>PyList_GET_ITEM(bucket, i+1)==k2 and \ 126 <size_t><object>PyList_GET_ITEM(bucket, i+2)==k3: 127 del bucket[i:i+4] 128 self.D._size -= 1 129 break 130 try: 131 self.D._refcache.__delitem__((k1,k2,k3)) 132 except KeyError: 133 pass 12 134 13 135 cdef class TripleDict: 14 136 """ 15 This is a hashtable specifically designed for (read) speed in 16 the coercion model. 17 18 It differs from a python dict in the following important ways: 19 137 This is a hashtable specifically designed for (read) speed in 138 the coercion model. 139 140 It differs from a python dict in the following important ways: 141 20 142 - All keys must be sequence of exactly three elements. All sequence 21 types (tuple, list, etc.) map to the same item. 143 types (tuple, list, etc.) map to the same item. 22 144 - Comparison is done using the 'is' rather than '==' operator. 23 24 There are special cdef set/get methods for faster access. 25 It is bare-bones in the sense that not all dictionary methods are 26 implemented. 27 28 It is implemented as a list of lists (hereafter called buckets). The bucket 29 is chosen according to a very simple hash based on the object pointer .30 and each bucket is of the form [ k1, k2, k3, value, k1, k2, k3, value, ...]31 on which a linear search is performed.32 145 146 There are special cdef set/get methods for faster access. 147 It is bare-bones in the sense that not all dictionary methods are 148 implemented. 149 150 It is implemented as a list of lists (hereafter called buckets). The bucket 151 is chosen according to a very simple hash based on the object pointer, 152 and each bucket is of the form [id(k1), id(k2), id(k3), value, id(k1), 153 id(k2), id(k3), value, ...], on which a linear search is performed. 154 33 155 To spread objects evenly, the size should ideally be a prime, and certainly 34 not divisible by 2. 35 36 37 EXAMPLES: 38 156 not divisible by 2. 157 158 EXAMPLES:: 159 39 160 sage: from sage.structure.coerce_dict import TripleDict 40 161 sage: L = TripleDict(31) 41 162 sage: a = 'a'; b = 'b'; c = 'c' … … 82 203 Traceback (most recent call last): 83 204 ... 84 205 KeyError: 'a' 85 86 The following illustrates why even sizes are bad. 87 sage: L = TripleDict(4, L) 206 207 The following illustrates why even sizes are bad (setting the threshold 208 zero, so that no beneficial resizing happens):: 209 210 sage: L = TripleDict(4, L, threshold=0) 88 211 sage: L.stats() 89 212 (0, 250.25, 1001) 90 213 sage: L.bucket_lens() 91 214 [1001, 0, 0, 0] 92 215 216 Note that this kind of dictionary is also used for caching actions 217 and coerce maps. In previous versions of Sage, the cache was by 218 strong references and resulted in a memory leak in the following 219 example. However, this leak was fixed by trac ticket :trac:`715`, 220 using weak references:: 93 221 94 AUTHOR: 95 -- Robert Bradshaw, 2007-08 222 sage: K = GF(1<<55,'t') 223 sage: for i in range(50): 224 ... a = K.random_element() 225 ... E = EllipticCurve(j=a) 226 ... P = E.random_point() 227 ... Q = 2*P 228 sage: import gc 229 sage: n = gc.collect() 230 sage: from sage.schemes.elliptic_curves.ell_finite_field import EllipticCurve_finite_field 231 sage: LE = [x for x in gc.get_objects() if isinstance(x, EllipticCurve_finite_field)] 232 sage: len(LE) # indirect doctest 233 1 234 235 ..NOTE:: 236 237 The index `h` corresponding to the key [k1, k2, k3] is computed as a 238 value of unsigned type size_t as follows: 239 240 ..MATH:: 241 242 h = id(k1) + 13*id(k2) \oplus 503 id(k3) 243 244 Indeed, although the PyList_GetItem function and corresponding 245 PyList_GET_ITEM macro take a value of signed type Py_ssize_t as input 246 for the index, they do not accept negative inputs as the higher level 247 Python functions. Moreover, the above formula can overflow so that `h` 248 might be considered as negative. Even though this value is taken 249 modulo the size of the buckets' list before accessing the corresponding 250 item, the Cython "%" operator behaves for values of type size_t and 251 Py_ssize_t like the C "%" operator, rather than like the Python "%" 252 operator as it does for values of type int. That is, it returns a 253 result of the same sign as its input. Therefore, if `h` was defined as 254 a signed value, we might access the list at a negative index and raise 255 a segfault (and this has been observed on 32 bits systems, see 256 :trac:`715` for details). 257 258 AUTHORS: 259 260 - Robert Bradshaw, 2007-08 261 262 - Simon King, 2012-01 96 263 """ 97 98 def __init__(self, size, data=None, threshold=0 ):264 265 def __init__(self, size, data=None, threshold=0.7): 99 266 """ 100 Create a special dict using triples for keys. 101 102 EXAMPLES: 267 Create a special dict using triples for keys. 268 269 EXAMPLES:: 270 103 271 sage: from sage.structure.coerce_dict import TripleDict 104 272 sage: L = TripleDict(31) 105 273 sage: a = 'a'; b = 'b'; c = 'c' … … 111 279 self.threshold = threshold 112 280 self.buckets = [[] for i from 0 <= i < size] 113 281 self._size = 0 282 self.eraser = TripleDictEraser(self) 283 self._refcache = {} 114 284 if data is not None: 115 for k, v in data.iteritems():116 self [k] = v117 285 for (k1,k2,k3), v in data.iteritems(): 286 self.set(k1,k2,k3, v) 287 118 288 def __len__(self): 119 289 """ 120 290 The number of items in self. 121 122 EXAMPLES: 291 292 EXAMPLES:: 293 123 294 sage: from sage.structure.coerce_dict import TripleDict 124 295 sage: L = TripleDict(37) 125 296 sage: a = 'a'; b = 'b'; c = 'c' … … 131 302 3 132 303 """ 133 304 return self._size 134 305 135 306 def stats(self): 136 307 """ 137 The distribution of items in buckets. 138 139 OUTPUT: 140 (min, avg, max) 141 142 EXAMPLES: 308 The distribution of items in buckets. 309 310 OUTPUT: 311 312 - (min, avg, max) 313 314 EXAMPLES:: 315 143 316 sage: from sage.structure.coerce_dict import TripleDict 144 317 sage: L = TripleDict(37) 145 318 sage: for i in range(100): L[i,i,i] = None … … 150 323 sage: for i in range(100): L[i,i,i] = None 151 324 sage: L.stats() # random 152 325 (0, 0.03325573661456601, 1) 153 154 sage: L = TripleDict(1) 326 327 In order to have a test that isn't random, we use parameters 328 that should not be used in real applications:: 329 330 sage: L = TripleDict(1, threshold=0) 155 331 sage: for i in range(100): L[i,i,i] = None 156 332 sage: L.stats() 157 333 (100, 100.0, 100) … … 166 342 else: 167 343 min = 0 168 344 return min, 1.0*size/len(self.buckets), max 169 345 170 346 def bucket_lens(self): 171 347 """ 172 The distribution of items in buckets. 173 174 OUTPUT: 175 A list of how many items are in each bucket. 176 177 EXAMPLES: 348 The distribution of items in buckets. 349 350 OUTPUT: 351 352 A list of how many items are in each bucket. 353 354 EXAMPLES:: 355 178 356 sage: from sage.structure.coerce_dict import TripleDict 179 sage: L = TripleDict(37 )357 sage: L = TripleDict(37, threshold=0) 180 358 sage: for i in range(100): L[i,i,i] = None 181 359 sage: L.bucket_lens() # random 182 360 [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] 183 361 sage: sum(L.bucket_lens()) 184 362 100 185 186 sage: L = TripleDict(1) 363 364 In order to have a test that isn't random, we use parameters 365 that should not be used in real applications:: 366 367 sage: L = TripleDict(1, threshold=0) 187 368 sage: for i in range(100): L[i,i,i] = None 188 369 sage: L.bucket_lens() 189 370 [100] 190 371 """ 191 372 return [len(self.buckets[i])/4 for i from 0 <= i < len(self.buckets)] 192 373 193 374 def _get_buckets(self): 194 375 """ 195 The actual buckets of self, for debugging. 196 197 EXAMPLE: 376 The actual buckets of self, for debugging. 377 378 EXAMPLES:: 379 198 380 sage: from sage.structure.coerce_dict import TripleDict 199 381 sage: L = TripleDict(3) 200 382 sage: L[0,0,0] = None … … 202 384 [[0, 0, 0, None], [], []] 203 385 """ 204 386 return self.buckets 205 387 206 388 def __getitem__(self, k): 207 389 """ 208 EXAMPLES: 390 EXAMPLES:: 391 209 392 sage: from sage.structure.coerce_dict import TripleDict 210 393 sage: L = TripleDict(31) 211 394 sage: a = 'a'; b = 'b'; c = 'c' … … 213 396 sage: L[a,b,c] 214 397 1 215 398 """ 399 cdef object k1,k2,k3 216 400 try: 217 401 k1, k2, k3 = k 218 402 except (TypeError,ValueError): 219 403 raise KeyError, k 220 404 return self.get(k1, k2, k3) 221 222 cdef get(self, k1, k2, k3): 223 cdef Py_ssize_t h = (<Py_ssize_t><void *>k1 + 13*<Py_ssize_t><void *>k2 ^ 503*<Py_ssize_t><void *>k3) 224 if h < 0: h = -h 405 406 cdef get(self, object k1, object k2, object k3): 407 cdef size_t h1,h2,h3 408 h1 = <size_t><void *>k1 409 h2 = <size_t><void *>k2 410 h3 = <size_t><void *>k3 411 cdef object r1,r2,r3 412 try: 413 r1,r2,r3 = self._refcache[h1,h2,h3] 414 except KeyError: 415 raise KeyError, (k1,k2,k3) 416 if (isinstance(r1,KeyedRef) and r1() is None) or \ 417 (isinstance(r2,KeyedRef) and r2() is None) or \ 418 (isinstance(r3,KeyedRef) and r3() is None): 419 raise KeyError, (k1,k2,k3) 420 cdef size_t h = (h1 + 13*h2 ^ 503*h3) 225 421 cdef Py_ssize_t i 226 bucket = <object>PyList_GET_ITEM(self.buckets, h % PyList_GET_SIZE(self.buckets)) 422 cdef list all_buckets = self.buckets 423 cdef list bucket = <object>PyList_GET_ITEM(all_buckets, h % PyList_GET_SIZE(all_buckets)) 424 cdef object tmp 227 425 for i from 0 <= i < PyList_GET_SIZE(bucket) by 4: 228 if PyList_GET_ITEM(bucket, i) == <PyObject*>k1 and \ 229 PyList_GET_ITEM(bucket, i+1) == <PyObject*>k2 and \ 230 PyList_GET_ITEM(bucket, i+2) == <PyObject*>k3: 231 return <object>PyList_GET_ITEM(bucket, i+3) 426 tmp = <object>PyList_GET_ITEM(bucket, i) 427 if <size_t>tmp == <size_t><void *>k1: 428 tmp = <object>PyList_GET_ITEM(bucket, i+1) 429 if <size_t>tmp == <size_t><void *>k2: 430 tmp = <object>PyList_GET_ITEM(bucket, i+2) 431 if <size_t>tmp == <size_t><void *>k3: 432 return <object>PyList_GET_ITEM(bucket, i+3) 232 433 raise KeyError, (k1, k2, k3) 233 434 234 435 def __setitem__(self, k, value): 235 436 """ 236 EXAMPLES: 437 EXAMPLES:: 438 237 439 sage: from sage.structure.coerce_dict import TripleDict 238 440 sage: L = TripleDict(31) 239 441 sage: a = 'a'; b = 'b'; c = 'c' … … 241 443 sage: L[a,b,c] 242 444 -1 243 445 """ 446 cdef object k1,k2,k3 244 447 try: 245 448 k1, k2, k3 = k 246 449 except (TypeError,ValueError): 247 450 raise KeyError, k 248 451 self.set(k1, k2, k3, value) 249 250 cdef set(self, k1, k2,k3, value):452 453 cdef set(self, object k1, object k2, object k3, value): 251 454 if self.threshold and self._size > len(self.buckets) * self.threshold: 252 455 self.resize() 253 cdef Py_ssize_t h = (<Py_ssize_t><void *>k1 + 13*<Py_ssize_t><void *>k2 ^ 503*<Py_ssize_t><void *>k3) 254 if h < 0: h = -h 456 cdef size_t h1 = <size_t><void *>k1 457 cdef size_t h2 = <size_t><void *>k2 458 cdef size_t h3 = <size_t><void *>k3 459 cdef size_t h = (h1 + 13*h2 ^ 503*h3) 255 460 cdef Py_ssize_t i 256 bucket = <object>PyList_GET_ITEM(self.buckets, h % PyList_GET_SIZE(self.buckets)) 461 cdef list bucket = <object>PyList_GET_ITEM(self.buckets, h % PyList_GET_SIZE(self.buckets)) 462 cdef object tmp 257 463 for i from 0 <= i < PyList_GET_SIZE(bucket) by 4: 258 if PyList_GET_ITEM(bucket, i) == <PyObject*>k1 and \ 259 PyList_GET_ITEM(bucket, i+1) == <PyObject*>k2 and \ 260 PyList_GET_ITEM(bucket, i+2) == <PyObject*>k3: 261 bucket[i+3] = value 262 return 263 bucket += [k1, k2, k3, value] 464 tmp = <object>PyList_GET_ITEM(bucket, i) 465 if <size_t>tmp == h1: 466 tmp = <object>PyList_GET_ITEM(bucket, i+1) 467 if <size_t>tmp == h2: 468 tmp = <object>PyList_GET_ITEM(bucket, i+2) 469 if <size_t>tmp == h3: 470 # Test whether the old references are still active 471 r1,r2,r3 = <tuple>(self._refcache[h1,h2,h3]) 472 if (isinstance(r1,KeyedRef) and r1() is None) or \ 473 (isinstance(r2,KeyedRef) and r2() is None) or \ 474 (isinstance(r3,KeyedRef) and r3() is None): 475 del bucket [i:i+4] 476 self._size -= 1 477 break 478 bucket[i+3] = value 479 return 480 PyList_Append(bucket, h1) 481 PyList_Append(bucket, h2) 482 PyList_Append(bucket, h3) 483 PyList_Append(bucket, value) 484 try: 485 ref1 = KeyedRef(k1,self.eraser,(h1, h2, h3)) 486 except TypeError: 487 ref1 = k1 488 if k2 is not k1: 489 try: 490 ref2 = KeyedRef(k2,self.eraser,(h1, h2, h3)) 491 except TypeError: 492 ref2 = k2 493 else: 494 ref2 = None 495 if k3 is not k2 or k3 is not k1: 496 try: 497 ref3 = KeyedRef(k3,self.eraser,(h1, h2, h3)) 498 except TypeError: 499 ref3 = k3 500 else: 501 ref3 = None 502 self._refcache[h1,h2,h3] = (ref1,ref2,ref3) 264 503 self._size += 1 265 504 266 505 def __delitem__(self, k): 267 506 """ 268 EXAMPLES: 507 EXAMPLES:: 508 269 509 sage: from sage.structure.coerce_dict import TripleDict 270 510 sage: L = TripleDict(31) 271 511 sage: a = 'a'; b = 'b'; c = 'c' … … 274 514 sage: len(L) 275 515 0 276 516 """ 517 cdef object k1,k2,k3 277 518 try: 278 519 k1, k2, k3 = k 279 520 except (TypeError,ValueError): 280 521 raise KeyError, k 281 cdef Py_ssize_t h = (<Py_ssize_t><void *>k1 + 13*<Py_ssize_t><void *>k2 ^ 503*<Py_ssize_t><void *>k3) 282 if h < 0: h = -h 522 try: 523 r1,r2,r3 = self._refcache[<size_t><void *>k1,<size_t><void *>k2,<size_t><void *>k3] 524 except KeyError: 525 raise KeyError, k 526 if (isinstance(r1,KeyedRef) and r1() is None) or \ 527 (isinstance(r2,KeyedRef) and r2() is None) or \ 528 (isinstance(r3,KeyedRef) and r3() is None): 529 raise KeyError, k 530 try: 531 del self._refcache[<size_t><void *>k1,<size_t><void *>k2,<size_t><void *>k3] 532 except KeyError: 533 # This is to cope with a potential racing condition - if garbage 534 # collection and weakref callback happens right between the 535 # "if (isinstance(r1,..." and the "del", then the previously 536 # existing entry might already be gone. 537 raise KeyError, k 538 cdef size_t h = (<size_t><void *>k1 + 13*<size_t><void *>k2 ^ 503*<size_t><void *>k3) 283 539 cdef Py_ssize_t i 284 bucket = <object>PyList_GET_ITEM(self.buckets, h % PyList_GET_SIZE(self.buckets))540 cdef list bucket = <object>PyList_GET_ITEM(self.buckets, h % PyList_GET_SIZE(self.buckets)) 285 541 for i from 0 <= i < PyList_GET_SIZE(bucket) by 4: 286 if PyList_GET_ITEM(bucket, i) == <PyObject*>k1 and \287 PyList_GET_ITEM(bucket, i+1) == <PyObject*>k2 and \288 PyList_GET_ITEM(bucket, i+2) == <PyObject*>k3:542 if <size_t><object>PyList_GET_ITEM(bucket, i) == <size_t><void *>k1 and \ 543 <size_t><object>PyList_GET_ITEM(bucket, i+1) == <size_t><void *>k2 and \ 544 <size_t><object>PyList_GET_ITEM(bucket, i+2) == <size_t><void *>k3: 289 545 del bucket[i:i+4] 290 546 self._size -= 1 291 547 return 292 548 raise KeyError, k 293 549 294 550 def resize(self, int buckets=0): 295 551 """ 296 Changes the number of buckets of self, while preserving the contents. 297 298 If the number of buckets is 0 or not given, it resizes self to the 299 smallest prime that is at least twice as large as self. 300 301 EXAMPLES: 552 Changes the number of buckets of self, while preserving the contents. 553 554 If the number of buckets is 0 or not given, it resizes self to the 555 smallest prime that is at least twice as large as self. 556 557 EXAMPLES:: 558 302 559 sage: from sage.structure.coerce_dict import TripleDict 303 560 sage: L = TripleDict(8) 304 561 sage: for i in range(100): L[i,i,i] = None … … 312 569 """ 313 570 if buckets == 0: 314 571 buckets = next_odd_prime(2*len(self.buckets)) 315 cdef TripleDict new = TripleDict(buckets, self) 316 self.buckets = new.buckets 317 572 cdef list old_buckets = self.buckets 573 cdef list bucket 574 cdef Py_ssize_t i 575 cdef size_t h 576 self.buckets = [[] for i from 0 <= i < buckets] 577 cdef size_t k1,k2,k3 578 cdef object v 579 for bucket in old_buckets: 580 for i from 0 <= i < PyList_GET_SIZE(bucket) by 4: 581 k1 = <size_t><object>PyList_GET_ITEM(bucket, i) 582 k2 = <size_t><object>PyList_GET_ITEM(bucket, i+1) 583 k3 = <size_t><object>PyList_GET_ITEM(bucket, i+2) 584 v = <object>PyList_GET_ITEM(bucket, i+3) 585 h = (k1 + 13*k2 ^ 503*k3) 586 self.buckets[h % buckets] += [k1,k2,k3,v] 587 318 588 def iteritems(self): 319 589 """ 320 EXAMPLES: 590 EXAMPLES:: 591 321 592 sage: from sage.structure.coerce_dict import TripleDict 322 593 sage: L = TripleDict(31) 323 594 sage: L[1,2,3] = None 324 595 sage: list(L.iteritems()) 325 596 [((1, 2, 3), None)] 326 597 """ 327 return TripleDictIter(self) 328 598 cdef list bucket 599 cdef size_t i, h1,h2,h3 600 # We test whether the references are still valid. 601 # However, we must not delete them, since we are 602 # iterating. 603 for bucket in self.buckets: 604 for i from 0<=i<len(bucket) by 4: 605 h1,h2,h3 = bucket[i:i+3] 606 try: 607 r1,r2,r3 = self._refcache[h1,h2,h3] 608 except KeyError: 609 # That can only happen under a race condition. 610 # Anyway, it means the item is not there. 611 continue 612 if isinstance(r1, KeyedRef): 613 r1 = r1() 614 if r1 is None: 615 continue 616 if isinstance(r2, KeyedRef): 617 r2 = r2() 618 if r2 is None: 619 continue 620 if isinstance(r3, KeyedRef): 621 r3 = r3() 622 if r3 is None: 623 continue 624 yield (r1,r2,r3), <object>PyList_GET_ITEM(bucket,i+3) 625 329 626 def __reduce__(self): 330 627 """ 331 Note that we don't expect equality as this class concerns itself with 332 object identity rather than object equality. 628 Note that we don't expect equality as this class concerns itself with 629 object identity rather than object equality. 333 630 334 EXAMPLES: 631 EXAMPLES:: 632 335 633 sage: from sage.structure.coerce_dict import TripleDict 336 634 sage: L = TripleDict(31) 337 635 sage: L[1,2,3] = True … … 341 639 [((1, 2, 3), True)] 342 640 """ 343 641 return TripleDict, (len(self.buckets), dict(self.iteritems()), self.threshold) 344 345 346 cdef class TripleDictIter:347 def __init__(self, pairs):348 """349 EXAMPLES:350 sage: from sage.structure.coerce_dict import TripleDict, TripleDictIter351 sage: L = TripleDict(31)352 sage: L[1,2,3] = None353 sage: L.iteritems().next()354 ((1, 2, 3), None)355 """356 self.pairs = pairs357 self.buckets = iter(self.pairs.buckets)358 def __iter__(self):359 """360 EXAMPLES:361 sage: from sage.structure.coerce_dict import TripleDict, TripleDictIter362 sage: L = TripleDict(31)363 sage: L[1,2,3] = None364 sage: iter(L.iteritems()).next()365 ((1, 2, 3), None)366 """367 return self368 def __next__(self):369 """370 EXAMPLES:371 sage: from sage.structure.coerce_dict import TripleDict, TripleDictIter372 sage: L = TripleDict(31)373 sage: L[1,2,3] = None374 sage: L[3,2,1] = None375 sage: sorted(L.iteritems())376 [((1, 2, 3), None), ((3, 2, 1), None)]377 """378 while self.bucket_iter is None:379 self.bucket_iter = self.buckets.next()380 self.bucket_iter = iter(self.bucket_iter)381 try:382 k1 = self.bucket_iter.next()383 k2 = self.bucket_iter.next()384 k3 = self.bucket_iter.next()385 value = self.bucket_iter.next()386 return ((k1, k2, k3), value)387 except StopIteration:388 self.bucket_iter = None389 return self.next()390 391 392 642 393 643 cdef long next_odd_prime(long n): 394 644 if n % 2 == 0: -
sage/structure/parent.pxd
diff --git a/sage/structure/parent.pxd b/sage/structure/parent.pxd
a b 7 7 ############################################################################### 8 8 9 9 cimport sage.structure.category_object 10 from sage.structure.coerce_dict cimport TripleDict 10 11 11 12 cdef class Parent(category_object.CategoryObject): 12 13 … … 73 74 # and Parents for which self._rmul_ and/or self._lmul_ 74 75 # do the correct thing. 75 76 # Initialized at ring creation. 76 cdef _action_list77 cdef list _action_list 77 78 # Hashtable of everything we've (possibly recursively) discovered so far. 78 cdef _action_hash79 cdef TripleDict _action_hash 79 80 80 81 # List consisting of Morphisms (from anything to self) 81 82 # and Parents for which the __call__ method of self -
sage/structure/parent.pyx
diff --git a/sage/structure/parent.pyx b/sage/structure/parent.pyx
a b 546 546 self._coerce_from_list = [] 547 547 self._coerce_from_hash = {} 548 548 self._action_list = [] 549 self._action_hash = {}549 self._action_hash = TripleDict(23) 550 550 self._convert_from_list = [] 551 551 self._convert_from_hash = {} 552 552 self._embedding = None … … 664 664 EXAMPLES:: 665 665 666 666 sage: sorted(QQ._introspect_coerce().items()) 667 [('_action_hash', {...}),667 [('_action_hash', <sage.structure.coerce_dict.TripleDict object at ...>), 668 668 ('_action_list', []), 669 669 ('_coerce_from_hash', {...}), 670 670 ('_coerce_from_list', []), … … 1606 1606 if isinstance(action, Action): 1607 1607 if action.actor() is self: 1608 1608 self._action_list.append(action) 1609 self._action_hash [action.domain(), action.operation(), action.is_left()] = action1609 self._action_hash.set(action.domain(), action.operation(), action.is_left(), action) 1610 1610 elif action.domain() is self: 1611 1611 self._action_list.append(action) 1612 self._action_hash [action.actor(), action.operation(), not action.is_left()] = action1612 self._action_hash.set(action.actor(), action.operation(), not action.is_left(), action) 1613 1613 else: 1614 1614 raise ValueError("Action must involve self") 1615 1615 else: … … 2219 2219 try: 2220 2220 if self._action_hash is None: # this is because parent.__init__() does not always get called 2221 2221 self.init_coerce() 2222 return self._action_hash [S, op, self_on_left]2222 return self._action_hash.get(S, op, self_on_left) 2223 2223 except KeyError: 2224 2224 pass 2225 2225 … … 2234 2234 # We do NOT add to the list, as this would lead to errors as in 2235 2235 # the example above. 2236 2236 2237 self._action_hash [S, op, self_on_left] = action2237 self._action_hash.set(S, op, self_on_left, action) 2238 2238 return action 2239 2239 2240 2240 -
sage/structure/parent_old.pyx
diff --git a/sage/structure/parent_old.pyx b/sage/structure/parent_old.pyx
a b 30 30 import operator 31 31 from parent import Set_PythonType, Set_PythonType_class 32 32 from coerce import py_scalar_parent 33 from sage.structure.coerce_dict import TripleDict 33 34 34 35 include '../ext/python_object.pxi' 35 36 include '../ext/python_bool.pxi' … … 66 67 self._coerce_from_list = list(coerce_from) 67 68 self._coerce_from_hash = {} 68 69 self._action_list = list(actions) 69 self._action_hash = {}70 self._action_hash = TripleDict(23) 70 71 71 72 cdef parent.Parent other 72 73 for mor in embeddings: