Ticket #715: trac715_one_triple_dict.patch
File trac715_one_triple_dict.patch, 39.3 KB (added by , 8 years ago) |
---|
-
sage/categories/action.pxd
# HG changeset patch # User Simon King <simon.king@uni-jena.de> # Date 1326139789 -3600 # Node ID 04e068d2e37c1a04acf446a7f05412f25951a3b2 # Parent 6120ec4f0db167c65c125f80b2d405ccb49d1da1 #715: Use weak references in the `TripleDict` coercion cache diff --git a/sage/categories/action.pxd b/sage/categories/action.pxd
a b 8 8 cdef S 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 32 32 33 33 import homset 34 34 import sage.structure.element 35 from weakref import ref 35 36 36 37 include "../ext/stdsage.pxi" 37 38 … … 48 49 from groupoid import Groupoid 49 50 Functor.__init__(self, Groupoid(G), category(S)) 50 51 self.G = G 51 self.S = S52 self.S = ref(S) 52 53 self._is_left = is_left 53 54 self.op = op 54 55 … … 61 62 if g in self.G: 62 63 return ActionEndomorphism(self, self.G(g)) 63 64 elif g == self.G: 64 return self. S65 return self.underlying_set() 65 66 else: 66 67 raise TypeError, "%s not an element of %s"%(g, self.G) 67 68 elif len(args) == 2: 68 69 if self._is_left: 69 return self._call_(self.G(args[0]), self. S(args[1]))70 return self._call_(self.G(args[0]), self.underlying_set()(args[1])) 70 71 else: 71 return self._call_(self. S(args[0]), self.G(args[1]))72 return self._call_(self.underlying_set()(args[0]), self.G(args[1])) 72 73 73 74 cpdef _call_(self, a, b): 74 75 raise NotImplementedError, "Action not implemented." … … 91 92 92 93 def __repr__(self): 93 94 side = "Left" if self._is_left else "Right" 94 return "%s %s by %r on %r"%(side, self._repr_name_(), self.G, self.S) 95 return "%s %s by %r on %r"%(side, self._repr_name_(), self.G, 96 self.underlying_set()) 95 97 96 98 def _repr_name_(self): 97 99 return "action" 98 100 99 101 def actor(self): 100 102 return self.G 101 103 104 cdef underlying_set(self): 105 """ 106 The set on which the actor acts (it is not necessarily the codomain of 107 the action). 108 109 NOTE: 110 111 Since this is a cdef'ed method, we can only provide an indirect doctest. 112 113 EXAMPLES:: 114 115 sage: P = QQ['x'] 116 sage: R = (ZZ['x'])['y'] 117 sage: A = R.get_action(P,operator.mul,True) 118 sage: A # indirect doctest 119 Right scalar multiplication by Univariate Polynomial Ring in x over 120 Rational Field on Univariate Polynomial Ring in y over Univariate 121 Polynomial Ring in x over Integer Ring 122 123 In this example, the underlying set is the ring ``R``. This is the same 124 as the left domain, which is different from the codomain of the action:: 125 126 sage: A.codomain() 127 Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Rational Field 128 sage: A.codomain() == R 129 False 130 sage: A.left_domain() is R 131 True 132 133 By trac ticket #715, there is only a weak reference to the underlying 134 set. Hence, the underlying set may be garbage collected, even when the 135 action is still alive. This may result in a runtime error, as follows:: 136 137 sage: from sage.categories.action import Action 138 sage: class P: pass 139 sage: p = P() 140 sage: q = P() 141 sage: A = Action(p,q) 142 sage: A 143 Left action by <__main__.P instance at ...> on <__main__.P instance at ...> 144 sage: del q 145 sage: import gc 146 sage: n = gc.collect() 147 sage: A 148 Traceback (most recent call last): 149 ... 150 RuntimeError: This action acted on a set that became garbage collected 151 152 """ 153 S = self.S() 154 if S is None: 155 raise RuntimeError, "This action acted on a set that became garbage collected" 156 return S 157 102 158 def codomain(self): 103 return self.S159 return self.underlying_set() 104 160 105 161 def domain(self): 106 return self. S162 return self.underlying_set() 107 163 108 164 def left_domain(self): 109 165 if self._is_left: … … 145 201 # We must be in the case that parent(~a) == parent(a) 146 202 # so we can invert in call_c code below. 147 203 if (PY_TYPE_CHECK(G, Group) and G.is_multiplicative()) or G.is_field(): 148 Action.__init__(self, G, action. S, action._is_left)204 Action.__init__(self, G, action.underlying_set(), action._is_left) 149 205 self._action = action 150 206 return 151 207 else: 152 208 K = G._pseudo_fraction_field() 153 Action.__init__(self, K, action. S, action._is_left)209 Action.__init__(self, K, action.underlying_set(), action._is_left) 154 210 self._action = action 155 211 return 156 212 except (AttributeError, NotImplementedError): … … 190 246 right_precomposition = homset.Hom(right_precomposition._codomain, right).natural_map() * right_precomposition 191 247 right = right_precomposition._domain 192 248 if action._is_left: 193 Action.__init__(self, left, action. S, 1)249 Action.__init__(self, left, action.underlying_set(), 1) 194 250 else: 195 Action.__init__(self, right, action. S, 0)251 Action.__init__(self, right, action.underlying_set(), 0) 196 252 self._action = action 197 253 self.left_precomposition = left_precomposition 198 254 self.right_precomposition = right_precomposition … … 230 286 cdef class ActionEndomorphism(Morphism): 231 287 232 288 def __init__(self, Action action, g): 233 Morphism.__init__(self, homset.Hom(action.S, action.S)) 289 Morphism.__init__(self, homset.Hom(action.underlying_set(), 290 action.underlying_set())) 234 291 self._action = action 235 292 self._g = g 236 293 … … 241 298 return self._action._call_(x, self._g) 242 299 243 300 def _repr_(self): 244 return "Action of %s on %s under %s."%(self._g, self._action.S, self._action) 301 return "Action of %s on %s under %s."%(self._g, 302 self._action.underlying_set(), self._action) 245 303 246 304 def __mul__(left, right): 247 305 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 38 38 39 39 def domain(self): 40 40 """ 41 EXAMPLES: 42 sage: A = MatrixSpace(QQ, 2).get_action(MatrixSpace(ZZ['x'], 2)); A 41 EXAMPLES: 42 43 By trac ticket #715, there only is a weak reference on the underlying set, 44 so that it can be garbage collected if only the action itself is explicitly 45 referred to. Hence, we first assign the involved matrix spaces to a 46 variable:: 47 48 sage: MSQ = MatrixSpace(QQ, 2) 49 sage: MSZ = MatrixSpace(ZZ['x'], 2) 50 sage: A = MSQ.get_action(MSZ); A 43 51 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 52 sage: A.actor() 45 53 Full MatrixSpace of 2 by 2 dense matrices over Rational Field … … 48 56 sage: A.codomain() 49 57 Full MatrixSpace of 2 by 2 dense matrices over Univariate Polynomial Ring in x over Rational Field 50 58 """ 51 return self. S59 return self.underlying_set() 52 60 53 61 54 62 cdef class MatrixMatrixAction(MatrixMulAction): 55 63 def __init__(self, G, S): 56 64 """ 57 EXAMPLES: 65 EXAMPLES: 66 67 By trac ticket #715, there only is a weak reference on the underlying set, 68 so that it can be garbage collected if only the action itself is explicitly 69 referred to. Hence, we first assign the involved matrix spaces to a 70 variable:: 71 58 72 sage: R.<x> = ZZ[] 73 sage: MSR = MatrixSpace(R, 3, 3) 74 sage: MSQ = MatrixSpace(QQ, 3, 2) 59 75 sage: from sage.matrix.action import MatrixMatrixAction 60 sage: A = MatrixMatrixAction(M atrixSpace(R, 3, 3), MatrixSpace(QQ, 3, 2)); A76 sage: A = MatrixMatrixAction(MSR, MSQ); A 61 77 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 78 sage: A.codomain() 63 79 Full MatrixSpace of 3 by 2 dense matrices over Univariate Polynomial Ring in x over Rational Field … … 72 88 73 89 def _create_codomain(self, base): 74 90 """ 75 EXAMPLES: 91 EXAMPLES: 92 93 By trac ticket #715, there only is a weak reference on the underlying set, 94 so that it can be garbage collected if only the action itself is explicitly 95 referred to. Hence, we first assign the involved matrix spaces to a 96 variable:: 97 76 98 sage: from sage.matrix.action import MatrixMatrixAction 77 99 sage: R.<x> = ZZ[] 78 sage: A = MatrixMatrixAction(MatrixSpace(R, 3, 3), MatrixSpace(QQ, 3, 2)); A 100 sage: MSR = MatrixSpace(R, 3, 3) 101 sage: MSQ = MatrixSpace(QQ, 3, 2) 102 sage: A = MatrixMatrixAction(MSR, MSQ); A 79 103 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 104 sage: A.codomain() 81 105 Full MatrixSpace of 3 by 2 dense matrices over Univariate Polynomial Ring in x over Rational Field 82 106 """ 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()) 107 if self.G.ncols() != self.underlying_set().nrows(): 108 raise TypeError, "incompatible dimensions %s, %s" % (self.G.ncols(), self.underlying_set().nrows()) 109 return MatrixSpace(base, self.G.nrows(), self.underlying_set().ncols(), 110 sparse = self.G.is_sparse() and self.underlying_set().is_sparse()) 86 111 87 112 cpdef _call_(self, g, s): 88 113 """ 89 114 EXAMPLES: 90 Respects compatible subdivisions: 115 116 Respects compatible subdivisions:: 117 91 118 sage: M = matrix(5, 5, prime_range(100)) 92 119 sage: M.subdivide(2,3); M 93 120 [ 2 3 5| 7 11] … … 112 139 [ 8168|11143] 113 140 [11056|15077] 114 141 115 Note that this is just like block matrix multiplication: 142 Note that this is just like block matrix multiplication:: 143 116 144 sage: M.subdivision(0,0) * N.subdivision(0,0) + M.subdivision(0,1) * N.subdivision(1,0) 117 145 [1048] 118 146 [3056] 119 147 120 148 If the subdivisions aren't compatible, ignore them. 149 :: 150 121 151 sage: N.subdivide(1,1); N 122 152 [ 0| 1] 123 153 [--+--] … … 156 186 cdef class MatrixVectorAction(MatrixMulAction): 157 187 def __init__(self, G, S): 158 188 """ 159 EXAMPLES: 189 EXAMPLES:: 190 160 191 sage: from sage.matrix.action import MatrixVectorAction 161 192 sage: A = MatrixVectorAction(MatrixSpace(QQ, 3, 3), VectorSpace(CDF, 4)); A 162 193 Traceback (most recent call last): … … 169 200 170 201 def _create_codomain(self, base): 171 202 """ 172 EXAMPLES: 203 EXAMPLES:: 204 173 205 sage: from sage.matrix.action import MatrixVectorAction 174 206 sage: A = MatrixVectorAction(MatrixSpace(QQ, 5, 3), VectorSpace(CDF, 3)); A 175 207 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 208 sage: A.codomain() 177 209 Vector space of dimension 5 over Complex Double Field 178 210 """ 179 if self.G.ncols() != self.S.degree(): 180 raise TypeError, "incompatible dimensions %s, %s" % (self.G.ncols(), self.S.degree()) 211 if self.G.ncols() != self.underlying_set().degree(): 212 raise TypeError, "incompatible dimensions %s, %s" % (self.G.ncols(), 213 self.underlying_set().degree()) 181 214 return FreeModule(base, self.G.nrows(), sparse = self.G.is_sparse()) 182 215 183 216 cpdef _call_(self, g, s): … … 198 231 cdef class VectorMatrixAction(MatrixMulAction): 199 232 def __init__(self, G, S): 200 233 """ 201 EXAMPLES: 234 EXAMPLES:: 235 202 236 sage: from sage.matrix.action import VectorMatrixAction 203 237 sage: A = VectorMatrixAction(MatrixSpace(QQ, 5, 3), VectorSpace(CDF, 3)); A 204 238 Traceback (most recent call last): … … 211 245 212 246 def _create_codomain(self, base): 213 247 """ 214 EXAMPLES: 248 EXAMPLES:: 249 215 250 sage: from sage.matrix.action import VectorMatrixAction 216 251 sage: A = VectorMatrixAction(MatrixSpace(QQ, 3, 5), VectorSpace(CDF, 3)); A 217 252 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 253 sage: A.codomain() 219 254 Vector space of dimension 5 over Complex Double Field 220 255 """ 221 if self.G.nrows() != self.S.degree(): 222 raise TypeError, "incompatible dimensions %s, %s" % (self.G.nrows(), self.S.degree()) 256 if self.G.nrows() != self.underlying_set().degree(): 257 raise TypeError, "incompatible dimensions %s, %s" % (self.G.nrows(), 258 self.underlying_set().degree()) 223 259 return FreeModule(base, self.G.ncols(), sparse = self.G.is_sparse()) 224 260 225 261 cpdef _call_(self, s, g): -
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.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 2 2 cdef Py_ssize_t _size 3 3 cdef buckets 4 4 cdef double threshold 5 cdef get(self, k1, k2, k3) 6 cdef set(self, k1, k2, k3, value) 5 cdef TripleDictEraser eraser 6 cdef get(self, object k1, object k2, object k3) 7 cdef set(self, object k1, object k2, object k3, value) 7 8 8 9 cdef class TripleDictIter: 9 10 cdef TripleDict pairs 10 11 cdef buckets, bucket_iter 12 13 cdef class TripleDictEraser: 14 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 # 2011 Simon King <simon.king@uni-jena.de> 3 4 # 4 5 # Distributed under the terms of the GNU General Public License (GPL) 5 6 # … … 9 10 10 11 include "../ext/python_list.pxi" 11 12 13 from sage.misc.constant_function import ConstantFunction 14 from weakref import KeyedRef 15 16 ############################################ 17 # The following code is responsible for 18 # removing dead references from the cache 19 ############################################ 20 21 cdef dict _refcache = {} 22 23 cdef class TripleDictEraser: 24 """ 25 Erases items from a :class:`TripleDict` when a weak reference becomes invalid. 26 27 This is of internal use only. Instances of this class will be passed as a callback 28 function when creating a weak reference. 29 30 EXAMPLES:: 31 32 sage: from sage.structure.coerce_dict import TripleDict 33 sage: class A: pass 34 sage: a = A() 35 sage: T = TripleDict(11) 36 sage: T[a,ZZ,None] = 1 37 sage: T[ZZ,a,1] = 2 38 sage: T[a,a,ZZ] = 3 39 sage: len(T) 40 3 41 sage: del a 42 sage: import gc 43 sage: n = gc.collect() 44 sage: len(T) 45 0 46 47 AUTHOR: 48 49 Simon King (2012-01) 50 """ 51 52 def __init__(self, D): 53 """ 54 INPUT: 55 56 A :class:`TripleDict`. 57 58 EXAMPLES:: 59 60 sage: from sage.structure.coerce_dict import TripleDict, TripleDictEraser 61 sage: D = TripleDict(11) 62 sage: TripleDictEraser(D) 63 <sage.structure.coerce_dict.TripleDictEraser object at ...> 64 65 """ 66 self.D = D 67 def __call__(self, r): 68 """ 69 INPUT: 70 71 A weak reference with key. 72 73 When this is called with a weak reference ``r``, then each item containing ``r`` 74 is removed from the associated :class:`TripleDict`. Normally, this only happens 75 when a weak reference becomes invalid. 76 77 EXAMPLES:: 78 79 sage: from sage.structure.coerce_dict import TripleDict 80 sage: class A: pass 81 sage: a = A() 82 sage: T = TripleDict(11) 83 sage: T[a,ZZ,None] = 1 84 sage: T[ZZ,a,1] = 2 85 sage: T[a,a,ZZ] = 3 86 sage: len(T) 87 3 88 sage: del a 89 sage: import gc 90 sage: n = gc.collect() 91 sage: len(T) # indirect doctest 92 0 93 94 """ 95 # r is a (weak) reference (typically to a parent), 96 # and it knows the stored key of the unique triple r() had been part of. 97 # 98 # We remove that unique triple from self.D 99 cdef Py_ssize_t k1,k2,k3 100 k1,k2,k3 = r.key 101 cdef int h = (k1 + 13*k2 ^ 503*k3) % PyList_GET_SIZE(self.D.buckets) 102 cdef list bucket = <object>PyList_GET_ITEM(self.D.buckets,h) 103 cdef int i 104 for i from 0 <= i < PyList_GET_SIZE(bucket) by 4: 105 if <Py_ssize_t><object>PyList_GET_ITEM(bucket,i)==k1 and \ 106 <Py_ssize_t><object>PyList_GET_ITEM(bucket,i+1)==k2 and \ 107 <Py_ssize_t><object>PyList_GET_ITEM(bucket,i+2)==k3: 108 del bucket[i:i+4] 109 self.D._size -= 1 110 break 111 cdef list L = _refcache[k1,k2,k3] 112 del L[L.index(r)] 12 113 13 114 cdef class TripleDict: 14 115 """ 15 This is a hashtable specifically designed for (read) speed in 16 the coercion model. 116 This is a hashtable specifically designed for (read) speed in 117 the coercion model. 17 118 18 It differs from a python dict in the following important ways: 119 It differs from a python dict in the following important ways: 19 120 20 121 - All keys must be sequence of exactly three elements. All sequence 21 types (tuple, list, etc.) map to the same item. 122 types (tuple, list, etc.) map to the same item. 22 123 - Comparison is done using the 'is' rather than '==' operator. 23 124 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. 125 There are special cdef set/get methods for faster access. 126 It is bare-bones in the sense that not all dictionary methods are 127 implemented. 27 128 28 It is implemented as a list of lists (hereafter called buckets). The bucket 129 It is implemented as a list of lists (hereafter called buckets). The bucket 29 130 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.131 and each bucket is of the form [id(k1), id(k2), id(k3), value, id(k1), id(k2), 132 id(k3), value, ...], on which a linear search is performed. 32 133 33 134 To spread objects evenly, the size should ideally be a prime, and certainly 34 not divisible by 2. 135 not divisible by 2. 35 136 36 137 37 EXAMPLES: 138 EXAMPLES:: 38 139 39 140 sage: from sage.structure.coerce_dict import TripleDict 40 141 sage: L = TripleDict(31) … … 83 184 ... 84 185 KeyError: 'a' 85 186 86 The following illustrates why even sizes are bad. 187 The following illustrates why even sizes are bad:: 188 87 189 sage: L = TripleDict(4, L) 88 190 sage: L.stats() 89 191 (0, 250.25, 1001) 90 192 sage: L.bucket_lens() 91 193 [1001, 0, 0, 0] 92 194 195 Note that this kind of dictionary is also used for caching actions 196 and coerce maps. In previous versions of Sage, the cache was by 197 strong references and resulted in a memory leak in the following 198 example. However, this leak was fixed by trac ticket #715, using 199 weak references:: 93 200 94 AUTHOR: 95 -- Robert Bradshaw, 2007-08 201 sage: K = GF(1<<55,'t') 202 sage: for i in range(50): 203 ... a = K.random_element() 204 ... E = EllipticCurve(j=a) 205 ... P = E.random_point() 206 ... Q = 2*P 207 sage: import gc 208 sage: n = gc.collect() 209 sage: from sage.schemes.generic.homset import SchemeHomsetModule_abelian_variety_coordinates_field 210 sage: LE = [x for x in gc.get_objects() if isinstance(x,SchemeHomsetModule_abelian_variety_coordinates_field)] 211 sage: len(LE) # indirect doctest 212 1 213 214 AUTHOR:: 215 216 - Robert Bradshaw, 2007-08 217 - Simon King, 2012-01 96 218 """ 97 219 98 220 def __init__(self, size, data=None, threshold=0): 99 221 """ 100 222 Create a special dict using triples for keys. 101 223 102 EXAMPLES: 224 EXAMPLES:: 225 103 226 sage: from sage.structure.coerce_dict import TripleDict 104 227 sage: L = TripleDict(31) 105 228 sage: a = 'a'; b = 'b'; c = 'c' … … 111 234 self.threshold = threshold 112 235 self.buckets = [[] for i from 0 <= i < size] 113 236 self._size = 0 237 self.eraser = TripleDictEraser(self) 114 238 if data is not None: 115 for k, v in data.iteritems():116 self [k] = v239 for (k1,k2,k3), v in data.iteritems(): 240 self.set(k1,k2,k3, v) 117 241 118 242 def __len__(self): 119 243 """ 120 244 The number of items in self. 121 245 122 EXAMPLES: 246 EXAMPLES:: 247 123 248 sage: from sage.structure.coerce_dict import TripleDict 124 249 sage: L = TripleDict(37) 125 250 sage: a = 'a'; b = 'b'; c = 'c' … … 136 261 """ 137 262 The distribution of items in buckets. 138 263 139 OUTPUT: 140 (min, avg, max) 264 OUTPUT:: 265 266 - (min, avg, max) 141 267 142 EXAMPLES: 268 EXAMPLES:: 269 143 270 sage: from sage.structure.coerce_dict import TripleDict 144 271 sage: L = TripleDict(37) 145 272 sage: for i in range(100): L[i,i,i] = None … … 171 298 """ 172 299 The distribution of items in buckets. 173 300 174 OUTPUT: 175 A list of how many items are in each bucket. 301 OUTPUT: 176 302 177 EXAMPLES: 303 A list of how many items are in each bucket. 304 305 EXAMPLES:: 306 178 307 sage: from sage.structure.coerce_dict import TripleDict 179 308 sage: L = TripleDict(37) 180 309 sage: for i in range(100): L[i,i,i] = None … … 194 323 """ 195 324 The actual buckets of self, for debugging. 196 325 197 EXAMPLE: 326 EXAMPLE:: 327 198 328 sage: from sage.structure.coerce_dict import TripleDict 199 329 sage: L = TripleDict(3) 200 330 sage: L[0,0,0] = None … … 205 335 206 336 def __getitem__(self, k): 207 337 """ 208 EXAMPLES: 338 EXAMPLES:: 339 209 340 sage: from sage.structure.coerce_dict import TripleDict 210 341 sage: L = TripleDict(31) 211 342 sage: a = 'a'; b = 'b'; c = 'c' … … 213 344 sage: L[a,b,c] 214 345 1 215 346 """ 347 cdef object k1,k2,k3 216 348 try: 217 349 k1, k2, k3 = k 218 350 except (TypeError,ValueError): 219 351 raise KeyError, k 220 352 return self.get(k1, k2, k3) 221 353 222 cdef get(self, k1, k2,k3):354 cdef get(self, object k1, object k2, object k3): 223 355 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 = -h356 #if h < 0: h = -h # shouldn't "%" result in a positive number anyway? 225 357 cdef Py_ssize_t i 226 bucket = <object>PyList_GET_ITEM(self.buckets, h % PyList_GET_SIZE(self.buckets)) 358 cdef list all_buckets = self.buckets 359 cdef list bucket = <object>PyList_GET_ITEM(all_buckets, h % PyList_GET_SIZE(all_buckets)) 360 cdef object tmp 227 361 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) 362 tmp = <object>PyList_GET_ITEM(bucket, i) 363 if <Py_ssize_t>tmp == <Py_ssize_t><void *>k1: 364 tmp = <object>PyList_GET_ITEM(bucket, i+1) 365 if <Py_ssize_t>tmp == <Py_ssize_t><void *>k2: 366 tmp = <object>PyList_GET_ITEM(bucket, i+2) 367 if <Py_ssize_t>tmp == <Py_ssize_t><void *>k3: 368 return <object>PyList_GET_ITEM(bucket, i+3) 232 369 raise KeyError, (k1, k2, k3) 233 370 234 371 def __setitem__(self, k, value): 235 372 """ 236 EXAMPLES: 373 EXAMPLES:: 374 237 375 sage: from sage.structure.coerce_dict import TripleDict 238 376 sage: L = TripleDict(31) 239 377 sage: a = 'a'; b = 'b'; c = 'c' … … 241 379 sage: L[a,b,c] 242 380 -1 243 381 """ 382 cdef object k1,k2,k3 244 383 try: 245 384 k1, k2, k3 = k 246 385 except (TypeError,ValueError): 247 386 raise KeyError, k 248 387 self.set(k1, k2, k3, value) 249 388 250 cdef set(self, k1, k2,k3, value):389 cdef set(self, object k1, object k2, object k3, value): 251 390 if self.threshold and self._size > len(self.buckets) * self.threshold: 252 391 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 392 cdef Py_ssize_t h1 = <Py_ssize_t><void *>k1 393 cdef Py_ssize_t h2 = <Py_ssize_t><void *>k2 394 cdef Py_ssize_t h3 = <Py_ssize_t><void *>k3 395 396 cdef Py_ssize_t h = (h1 + 13*h2 ^ 503*h3) 397 #if h < 0: h = -h # shouldn't "%" return a positive number anyway? 255 398 cdef Py_ssize_t i 256 bucket = <object>PyList_GET_ITEM(self.buckets, h % PyList_GET_SIZE(self.buckets)) 399 cdef list bucket = <object>PyList_GET_ITEM(self.buckets, h % PyList_GET_SIZE(self.buckets)) 400 cdef object tmp 257 401 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] 402 tmp = <object>PyList_GET_ITEM(bucket, i) 403 if <Py_ssize_t>tmp == h1: 404 tmp = <object>PyList_GET_ITEM(bucket, i+1) 405 if <Py_ssize_t>tmp == h2: 406 tmp = <object>PyList_GET_ITEM(bucket, i+2) 407 if <Py_ssize_t>tmp == h3: 408 bucket[i+3] = value 409 return 410 PyList_Append(bucket, h1) 411 PyList_Append(bucket, h2) 412 PyList_Append(bucket, h3) 413 PyList_Append(bucket, value) 414 try: 415 PyList_Append(_refcache.setdefault((h1 , h2, h3), []), 416 KeyedRef(k1,self.eraser,(h1, h2, h3))) 417 except TypeError: 418 PyList_Append(_refcache.setdefault((h1, h2, h3), []), k1) 419 if k2 is not k1: 420 try: 421 PyList_Append(_refcache.setdefault((h1 , h2, h3), []), 422 KeyedRef(k2,self.eraser,(h1, h2, h3))) 423 except TypeError: 424 PyList_Append(_refcache.setdefault((h1, h2, h3), []), k2) 425 if k3 is not k1 and k3 is not k2: 426 try: 427 PyList_Append(_refcache.setdefault((h1 , h2, h3), []), 428 KeyedRef(k3,self.eraser,(h1, h2, h3))) 429 except TypeError: 430 PyList_Append(_refcache.setdefault((h1, h2, h3), []),k3) 264 431 self._size += 1 265 432 266 433 def __delitem__(self, k): 267 434 """ 268 EXAMPLES: 435 EXAMPLES:: 436 269 437 sage: from sage.structure.coerce_dict import TripleDict 270 438 sage: L = TripleDict(31) 271 439 sage: a = 'a'; b = 'b'; c = 'c' … … 274 442 sage: len(L) 275 443 0 276 444 """ 445 cdef object k1,k2,k3 277 446 try: 278 447 k1, k2, k3 = k 279 448 except (TypeError,ValueError): 280 449 raise KeyError, k 281 450 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 = -h451 #if h < 0: h = -h 283 452 cdef Py_ssize_t i 284 bucket = <object>PyList_GET_ITEM(self.buckets, h % PyList_GET_SIZE(self.buckets))453 cdef list bucket = <object>PyList_GET_ITEM(self.buckets, h % PyList_GET_SIZE(self.buckets)) 285 454 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:455 if <Py_ssize_t><object>PyList_GET_ITEM(bucket, i) == <Py_ssize_t><void *>k1 and \ 456 <Py_ssize_t><object>PyList_GET_ITEM(bucket, i+1) == <Py_ssize_t><void *>k2 and \ 457 <Py_ssize_t><object>PyList_GET_ITEM(bucket, i+2) == <Py_ssize_t><void *>k3: 289 458 del bucket[i:i+4] 290 459 self._size -= 1 291 460 return … … 298 467 If the number of buckets is 0 or not given, it resizes self to the 299 468 smallest prime that is at least twice as large as self. 300 469 301 EXAMPLES: 470 EXAMPLES:: 471 302 472 sage: from sage.structure.coerce_dict import TripleDict 303 473 sage: L = TripleDict(8) 304 474 sage: for i in range(100): L[i,i,i] = None … … 312 482 """ 313 483 if buckets == 0: 314 484 buckets = next_odd_prime(2*len(self.buckets)) 315 cdef TripleDict new = TripleDict(buckets, self) 316 self.buckets = new.buckets 317 485 cdef list old_buckets = self.buckets 486 cdef list bucket 487 cdef Py_ssize_t i, h 488 self.buckets = [[] for i from 0 <= i < buckets] 489 cdef Py_ssize_t k1,k2,k3 490 cdef object v 491 for bucket in old_buckets: 492 for i from 0 <= i < PyList_GET_SIZE(bucket) by 4: 493 k1 = <Py_ssize_t><object>PyList_GET_ITEM(bucket,i) 494 k2 = <Py_ssize_t><object>PyList_GET_ITEM(bucket,i+1) 495 k3 = <Py_ssize_t><object>PyList_GET_ITEM(bucket,i+2) 496 v = <object>PyList_GET_ITEM(bucket,i+3) 497 h = (k1 + 13*k2 ^ 503*k3) % buckets # the new hash 498 self.buckets[h] += [k1,k2,k3,v] 499 318 500 def iteritems(self): 319 501 """ 320 EXAMPLES: 502 EXAMPLES:: 503 321 504 sage: from sage.structure.coerce_dict import TripleDict 322 505 sage: L = TripleDict(31) 323 506 sage: L[1,2,3] = None … … 331 514 Note that we don't expect equality as this class concerns itself with 332 515 object identity rather than object equality. 333 516 334 EXAMPLES: 517 EXAMPLES:: 518 335 519 sage: from sage.structure.coerce_dict import TripleDict 336 520 sage: L = TripleDict(31) 337 521 sage: L[1,2,3] = True … … 346 530 cdef class TripleDictIter: 347 531 def __init__(self, pairs): 348 532 """ 349 EXAMPLES: 533 EXAMPLES:: 534 350 535 sage: from sage.structure.coerce_dict import TripleDict, TripleDictIter 351 536 sage: L = TripleDict(31) 352 537 sage: L[1,2,3] = None … … 357 542 self.buckets = iter(self.pairs.buckets) 358 543 def __iter__(self): 359 544 """ 360 EXAMPLES: 545 EXAMPLES:: 546 361 547 sage: from sage.structure.coerce_dict import TripleDict, TripleDictIter 362 548 sage: L = TripleDict(31) 363 549 sage: L[1,2,3] = None … … 367 553 return self 368 554 def __next__(self): 369 555 """ 370 EXAMPLES: 556 EXAMPLES:: 557 371 558 sage: from sage.structure.coerce_dict import TripleDict, TripleDictIter 372 559 sage: L = TripleDict(31) 373 560 sage: L[1,2,3] = None … … 378 565 while self.bucket_iter is None: 379 566 self.bucket_iter = self.buckets.next() 380 567 self.bucket_iter = iter(self.bucket_iter) 568 cdef Py_ssize_t k1,k2,k3 381 569 try: 382 570 k1 = self.bucket_iter.next() 383 571 k2 = self.bucket_iter.next() 384 572 k3 = self.bucket_iter.next() 385 573 value = self.bucket_iter.next() 386 return ((k1, k2, k3), value) 574 return ((<object><PyObject *>k1, <object><PyObject *>k2, 575 <object><PyObject *>k3), value) 387 576 except StopIteration: 388 577 self.bucket_iter = None 389 578 return self.next() -
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 AttributeErrorMessage: 12 13 cdef type cls … … 77 78 # and Parents for which self._rmul_ and/or self._lmul_ 78 79 # do the correct thing. 79 80 # Initialized at ring creation. 80 cdef _action_list81 cdef list _action_list 81 82 # Hashtable of everything we've (possibly recursively) discovered so far. 82 cdef _action_hash83 cdef TripleDict _action_hash 83 84 84 85 # List consisting of Morphisms (from anything to self) 85 86 # 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 802 802 self._coerce_from_list = [] 803 803 self._coerce_from_hash = {} 804 804 self._action_list = [] 805 self._action_hash = {}805 self._action_hash = TripleDict(23) 806 806 self._convert_from_list = [] 807 807 self._convert_from_hash = {} 808 808 self._embedding = None … … 918 918 EXAMPLES:: 919 919 920 920 sage: sorted(QQ._introspect_coerce().items()) 921 [('_action_hash', {...}),921 [('_action_hash', <sage.structure.coerce_dict.TripleDict object at ...>), 922 922 ('_action_list', []), 923 923 ('_coerce_from_hash', {...}), 924 924 ('_coerce_from_list', []), … … 1847 1847 if isinstance(action, Action): 1848 1848 if action.actor() is self: 1849 1849 self._action_list.append(action) 1850 self._action_hash [action.domain(), action.operation(), action.is_left()] = action1850 self._action_hash.set(action.domain(), action.operation(), action.is_left(), action) 1851 1851 elif action.domain() is self: 1852 1852 self._action_list.append(action) 1853 self._action_hash [action.actor(), action.operation(), not action.is_left()] = action1853 self._action_hash.set(action.actor(), action.operation(), not action.is_left(), action) 1854 1854 else: 1855 1855 raise ValueError, "Action must involve self" 1856 1856 else: … … 2386 2386 try: 2387 2387 if self._action_hash is None: # this is because parent.__init__() does not always get called 2388 2388 self.init_coerce() 2389 return self._action_hash [S, op, self_on_left]2389 return self._action_hash.get(S, op, self_on_left) 2390 2390 except KeyError: 2391 2391 pass 2392 2392 … … 2401 2401 # We do NOT add to the list, as this would lead to errors as in 2402 2402 # the example above. 2403 2403 2404 self._action_hash [S, op, self_on_left] = action2404 self._action_hash.set(S, op, self_on_left, action) 2405 2405 return action 2406 2406 2407 2407 -
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: