Ticket #11521: trac715_two_tripledicts.patch
File trac715_two_tripledicts.patch, 23.9 KB (added by , 9 years ago) |
---|
-
sage/rings/polynomial/polynomial_ring_constructor.py
# HG changeset patch # User Simon King <simon.king@uni-jena.de> # Date 1324507434 -3600 # Node ID e5fd6cdfe1cfca73231f4751c59f0adfd9116321 # Parent 8883449f439a6a078ec685b24f0ddd5b9323fb31 #715: Use weak references for caching coerce maps, actions and polynomial rings. Introduce a second variant of `TripleDict`. 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, TripleDict Iter7 from coerce_dict cimport TripleDict, TripleDictById, TripleDictIter 8 8 9 9 cdef class CoercionModel_cache_maps(CoercionModel): 10 10 # This MUST be a mapping to tuples, where each 11 11 # tuple contains at least two elements that are either 12 12 # None or of type Morphism. 13 cdef TripleDict _coercion_maps13 cdef TripleDictById _coercion_maps 14 14 15 15 # This MUST be a mapping to actions. 16 16 cdef TripleDict _action_maps -
sage/structure/coerce.pyx
diff --git a/sage/structure/coerce.pyx b/sage/structure/coerce.pyx
a b 207 207 # This MUST be a mapping of tuples, where each 208 208 # tuple contains at least two elements that are either 209 209 # None or of type Map. 210 self._coercion_maps = TripleDict (lookup_dict_size, threshold=lookup_dict_threshold)210 self._coercion_maps = TripleDictById(lookup_dict_size, threshold=lookup_dict_threshold) 211 211 # This MUST be a mapping to actions. 212 212 self._action_maps = TripleDict(lookup_dict_size, threshold=lookup_dict_threshold) 213 213 # This is a mapping from Parents to Parents, storing the result of division in the given parent. -
sage/structure/coerce_dict.pxd
diff --git a/sage/structure/coerce_dict.pxd b/sage/structure/coerce_dict.pxd
a b 1 cdef class TripleDictEraser 2 1 3 cdef class TripleDict: 2 4 cdef Py_ssize_t _size 3 cdef buckets5 cdef list buckets 4 6 cdef double threshold 5 cdef get(self, k1, k2, k3) 6 cdef set(self, k1, k2, k3, value) 7 7 cdef TripleDictEraser eraser 8 cdef get(self, object k1, object k2, object k3) 9 cdef set(self, object k1, object k2, object k3, value) 10 11 cdef class TripleDictById(TripleDict): 12 pass 13 8 14 cdef class TripleDictIter: 9 15 cdef TripleDict pairs 10 16 cdef buckets, bucket_iter 17 18 cdef class TripleDictEraser: 19 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 15 ############################################ 16 # The following code is responsible for 17 # removing dead references from the cache 18 ############################################ 19 20 cdef class TripleDictEraser: 21 """ 22 Erases items from a :class:`TripleDict` when a weak reference becomes invalid. 23 24 This is of internal use only. Instances of this class will be passed as a callback 25 function when creating a weak reference. 26 27 EXAMPLES:: 28 29 sage: from sage.structure.coerce_dict import TripleDict 30 sage: class A: pass 31 sage: a = A() 32 sage: T = TripleDict(11) 33 sage: T[a,ZZ,None] = 1 34 sage: T[ZZ,a,1] = 2 35 sage: T[a,a,ZZ] = 3 36 sage: len(T) 37 3 38 sage: del a 39 sage: import gc 40 sage: n = gc.collect() 41 sage: len(T) 42 0 43 44 AUTHOR: 45 46 Simon King (2011-12) 47 """ 48 49 def __init__(self, D): 50 """ 51 INPUT: 52 53 A :class:`TripleDict`. 54 55 EXAMPLES:: 56 57 sage: from sage.structure.coerce_dict import TripleDict, TripleDictEraser 58 sage: D = TripleDict(11) 59 sage: TripleDictEraser(D) 60 <sage.structure.coerce_dict.TripleDictEraser object at ...> 61 62 """ 63 self.D = D 64 def __call__(self, r): 65 """ 66 INPUT: 67 68 A weak reference 69 70 When this is called with a weak reference ``r``, then each item containing ``r`` 71 is removed from the associated :class:`TripleDict`. Normally, this only happens 72 when a weak reference becomes invalid. 73 74 EXAMPLES:: 75 76 sage: from sage.structure.coerce_dict import TripleDict 77 sage: class A: pass 78 sage: a = A() 79 sage: T = TripleDict(11) 80 sage: T[a,ZZ,None] = 1 81 sage: T[ZZ,a,1] = 2 82 sage: T[a,a,ZZ] = 3 83 sage: len(T) 84 3 85 sage: del a 86 sage: import gc 87 sage: n = gc.collect() 88 sage: len(T) # indirect doctest 89 0 90 91 """ 92 # r is a (weak) reference (typically to a parent), 93 # and we remove that parent from the cache self.D 94 # Each weak reference can only occur once in the list. 95 cdef list bucket 96 for bucket in self.D.buckets: 97 for i from 0 <= i < PyList_GET_SIZE(bucket) by 4: 98 if bucket[i] is r or bucket[i+1] is r: 99 del bucket[i:i+4] 100 self.D._size -= 1 101 return 102 103 from weakref import ref 12 104 13 105 cdef class TripleDict: 14 106 """ … … 20 112 - All keys must be sequence of exactly three elements. All sequence 21 113 types (tuple, list, etc.) map to the same item. 22 114 - Comparison is done using the 'is' rather than '==' operator. 23 115 116 By trac ticket #715, it uses weak references on the first two 117 items of the key, if they are weakly referenceable. Otherwise, a 118 :class:`~sage.misc.constant_function.ConstantFunction` is used. 119 24 120 There are special cdef set/get methods for faster access. 25 121 It is bare-bones in the sense that not all dictionary methods are 26 122 implemented. … … 30 126 and each bucket is of the form [k1, k2, k3, value, k1, k2, k3, value, ...] 31 127 on which a linear search is performed. 32 128 33 To spread objects evenly, the size should ideally be a prime, and certainly 34 not divisible by 2. 129 To spread objects evenly, the size should ideally be a prime. 35 130 36 131 37 EXAMPLES: 132 EXAMPLES:: 38 133 39 134 sage: from sage.structure.coerce_dict import TripleDict 40 135 sage: L = TripleDict(31) … … 83 178 ... 84 179 KeyError: 'a' 85 180 86 The following illustrates why even sizes are bad. 87 sage: L = TripleDict(4, L) 88 sage: L.stats() 89 (0, 250.25, 1001) 90 sage: L.bucket_lens() 91 [1001, 0, 0, 0] 181 Here, we show that weak references are indeed in use:: 92 182 183 sage: class A: pass 184 sage: a = A() 185 sage: T = TripleDict(11) 186 sage: T[a,ZZ,None] = 1 187 sage: T[ZZ,a,1] = 2 188 sage: T[a,a,ZZ] = 3 189 sage: len(T) 190 3 191 sage: del a 192 sage: import gc 193 sage: n = gc.collect() 194 sage: len(T) 195 0 93 196 94 AUTHOR: 95 -- Robert Bradshaw, 2007-08 197 We encourage to use objects for the first two constituents of the key 198 that allow for weak references. However, other objects still work. 199 200 AUTHORS: 201 202 - Robert Bradshaw, 2007-08 203 - Simon King, 2011-12 96 204 """ 97 205 98 206 def __init__(self, size, data=None, threshold=0): 99 207 """ 100 208 Create a special dict using triples for keys. 101 209 102 EXAMPLES: 210 EXAMPLES:: 211 103 212 sage: from sage.structure.coerce_dict import TripleDict 104 213 sage: L = TripleDict(31) 105 214 sage: a = 'a'; b = 'b'; c = 'c' … … 111 220 self.threshold = threshold 112 221 self.buckets = [[] for i from 0 <= i < size] 113 222 self._size = 0 223 self.eraser = TripleDictEraser(self) 114 224 if data is not None: 115 225 for k, v in data.iteritems(): 116 226 self[k] = v … … 119 229 """ 120 230 The number of items in self. 121 231 122 EXAMPLES: 232 EXAMPLES:: 233 123 234 sage: from sage.structure.coerce_dict import TripleDict 124 235 sage: L = TripleDict(37) 125 236 sage: a = 'a'; b = 'b'; c = 'c' … … 136 247 """ 137 248 The distribution of items in buckets. 138 249 139 OUTPUT: 140 (min, avg, max) 250 OUTPUT: 251 252 `(min, avg, max)` 141 253 142 EXAMPLES: 254 EXAMPLES:: 255 143 256 sage: from sage.structure.coerce_dict import TripleDict 144 257 sage: L = TripleDict(37) 145 258 sage: for i in range(100): L[i,i,i] = None … … 171 284 """ 172 285 The distribution of items in buckets. 173 286 174 OUTPUT: 175 A list of how many items are in each bucket. 287 OUTPUT: 288 289 A list of how many items are in each bucket. 176 290 177 EXAMPLES: 291 EXAMPLES:: 292 178 293 sage: from sage.structure.coerce_dict import TripleDict 179 294 sage: L = TripleDict(37) 180 295 sage: for i in range(100): L[i,i,i] = None … … 194 309 """ 195 310 The actual buckets of self, for debugging. 196 311 197 EXAMPLE: 312 EXAMPLES:: 313 198 314 sage: from sage.structure.coerce_dict import TripleDict 199 315 sage: L = TripleDict(3) 200 316 sage: L[0,0,0] = None … … 205 321 206 322 def __getitem__(self, k): 207 323 """ 208 EXAMPLES: 324 EXAMPLES:: 325 209 326 sage: from sage.structure.coerce_dict import TripleDict 210 327 sage: L = TripleDict(31) 211 328 sage: a = 'a'; b = 'b'; c = 'c' … … 219 336 raise KeyError, k 220 337 return self.get(k1, k2, k3) 221 338 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)339 cdef get(self, object k1, object k2, object k3): 340 cdef Py_ssize_t h = (hash(k1)+ 13*hash(k2)^ 503*hash(k3)) 224 341 if h < 0: h = -h 225 342 cdef Py_ssize_t i 226 bucket = <object>PyList_GET_ITEM(self.buckets, h % PyList_GET_SIZE(self.buckets))343 cdef list bucket = self.buckets[h % PyList_GET_SIZE(self.buckets)] 227 344 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)345 if bucket[i]() == k1 and \ 346 bucket[i+1]() == k2 and \ 347 bucket[i+2] == k3: 348 return bucket[i+3] 232 349 raise KeyError, (k1, k2, k3) 233 350 234 351 def __setitem__(self, k, value): 235 352 """ 236 EXAMPLES: 353 EXAMPLES:: 354 237 355 sage: from sage.structure.coerce_dict import TripleDict 238 356 sage: L = TripleDict(31) 239 357 sage: a = 'a'; b = 'b'; c = 'c' … … 241 359 sage: L[a,b,c] 242 360 -1 243 361 """ 362 cdef object k1,k2,k3 244 363 try: 245 364 k1, k2, k3 = k 246 365 except (TypeError,ValueError): 247 366 raise KeyError, k 248 367 self.set(k1, k2, k3, value) 249 368 250 cdef set(self, k1, k2,k3, value):369 cdef set(self, object k1, object k2, object k3, value): 251 370 if self.threshold and self._size > len(self.buckets) * self.threshold: 252 371 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)372 cdef Py_ssize_t h = (hash(k1)+ 13*hash(k2)^ 503*hash(k3)) 254 373 if h < 0: h = -h 255 374 cdef Py_ssize_t i 256 bucket = <object>PyList_GET_ITEM(self.buckets, h % PyList_GET_SIZE(self.buckets))375 cdef list bucket = self.buckets[h % PyList_GET_SIZE(self.buckets)] 257 376 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:377 if bucket[i]() == k1 and \ 378 bucket[i+1]() == k2 and \ 379 bucket[i+2] == k3: 261 380 bucket[i+3] = value 262 381 return 263 bucket += [k1, k2, k3, value] 382 try: 383 r1 = ref(k1,self.eraser) 384 except TypeError: 385 r1 = ConstantFunction(k1) 386 try: 387 r2 = ref(k2,self.eraser) 388 except TypeError: 389 r2 = ConstantFunction(k2) 390 bucket += [r1, r2, k3, value] 264 391 self._size += 1 265 392 266 393 def __delitem__(self, k): 267 394 """ 268 EXAMPLES: 395 EXAMPLES:: 396 269 397 sage: from sage.structure.coerce_dict import TripleDict 270 398 sage: L = TripleDict(31) 271 399 sage: a = 'a'; b = 'b'; c = 'c' … … 274 402 sage: len(L) 275 403 0 276 404 """ 405 cdef object k1,k2,k3 277 406 try: 278 407 k1, k2, k3 = k 279 408 except (TypeError,ValueError): 280 409 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)410 cdef Py_ssize_t h = (hash(k1)+ 13*hash(k2)^ 503*hash(k3)) 282 411 if h < 0: h = -h 283 412 cdef Py_ssize_t i 284 bucket = <object>PyList_GET_ITEM(self.buckets, h % PyList_GET_SIZE(self.buckets))413 cdef list bucket = self.buckets[h % PyList_GET_SIZE(self.buckets)] 285 414 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:415 if bucket[i]() == k1 and \ 416 bucket[i+1]() == k2 and \ 417 bucket[i+2] == k3: 289 418 del bucket[i:i+4] 290 419 self._size -= 1 291 420 return … … 298 427 If the number of buckets is 0 or not given, it resizes self to the 299 428 smallest prime that is at least twice as large as self. 300 429 301 EXAMPLES: 430 EXAMPLES:: 431 302 432 sage: from sage.structure.coerce_dict import TripleDict 303 433 sage: L = TripleDict(8) 304 434 sage: for i in range(100): L[i,i,i] = None … … 317 447 318 448 def iteritems(self): 319 449 """ 320 EXAMPLES: 450 EXAMPLES:: 451 321 452 sage: from sage.structure.coerce_dict import TripleDict 322 453 sage: L = TripleDict(31) 323 454 sage: L[1,2,3] = None … … 331 462 Note that we don't expect equality as this class concerns itself with 332 463 object identity rather than object equality. 333 464 334 EXAMPLES: 465 EXAMPLES:: 466 335 467 sage: from sage.structure.coerce_dict import TripleDict 336 468 sage: L = TripleDict(31) 337 469 sage: L[1,2,3] = True … … 342 474 """ 343 475 return TripleDict, (len(self.buckets), dict(self.iteritems()), self.threshold) 344 476 477 cdef class TripleDictById(TripleDict): 478 """ 479 This is a hashtable specifically designed for (read) speed in 480 the coercion model. 481 482 It differs from a python dict in the following important ways: 483 484 - All keys must be sequence of exactly three elements. All sequence 485 types (tuple, list, etc.) map to the same item. 486 - Comparison is done using the 'is' rather than '==' operator. 487 488 By trac ticket #715, it uses weak references on the first two 489 items of the key, if they are weakly referenceable. Otherwise, a 490 :class:`~sage.misc.constant_function.ConstantFunction` is used. 491 492 There are special cdef set/get methods for faster access. 493 It is bare-bones in the sense that not all dictionary methods are 494 implemented. 495 496 It is implemented as a list of lists (hereafter called buckets). The bucket 497 is chosen according to a very simple hash based on the object pointer. 498 and each bucket is of the form [k1, k2, k3, value, k1, k2, k3, value, ...] 499 on which a linear search is performed. 500 501 To spread objects evenly, the size should ideally be a prime, and certainly 502 not divisible by 2. 503 504 EXAMPLES:: 505 506 sage: from sage.structure.coerce_dict import TripleDictById 507 sage: L = TripleDictById(31) 508 sage: a = 'a'; b = 'b'; c = 'c' 509 sage: L[a,b,c] = 1 510 sage: L[a,b,c] 511 1 512 sage: L[c,b,a] = -1 513 sage: list(L.iteritems()) # random order of output. 514 [(('c', 'b', 'a'), -1), (('a', 'b', 'c'), 1)] 515 sage: del L[a,b,c] 516 sage: list(L.iteritems()) 517 [(('c', 'b', 'a'), -1)] 518 sage: len(L) 519 1 520 sage: L.stats() # min, avg, max (bucket length) 521 (0, 0.032258064516129031, 1) 522 sage: L.bucket_lens() # random layout 523 [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 524 sage: for i in range(1000): 525 ... L[i,i,i] = i 526 sage: len(L) 527 1001 528 sage: L.stats() # random 529 (31, 32.29032258064516, 35) 530 sage: L.bucket_lens() # random layout 531 [33, 34, 32, 32, 35, 32, 31, 33, 34, 31, 32, 34, 32, 31, 31, 32, 32, 31, 31, 33, 32, 32, 32, 33, 33, 33, 31, 33, 33, 32, 31] 532 sage: L = TripleDictById(101, L) 533 sage: L.stats() # random 534 (8, 9.9108910891089117, 11) 535 sage: L = TripleDictById(3, L) 536 sage: L.stats() # random 537 (291, 333.66666666666669, 410) 538 sage: L[c,b,a] 539 -1 540 sage: L[a,b,c] 541 Traceback (most recent call last): 542 ... 543 KeyError: ('a', 'b', 'c') 544 sage: L[a] 545 Traceback (most recent call last): 546 ... 547 KeyError: 'a' 548 sage: L[a] = 1 549 Traceback (most recent call last): 550 ... 551 KeyError: 'a' 552 553 Here, we show that weak references are indeed in use:: 554 555 sage: class A: pass 556 sage: a = A() 557 sage: T = TripleDictById(11) 558 sage: T[a,ZZ,None] = 1 559 sage: T[ZZ,a,1] = 2 560 sage: T[a,a,ZZ] = 3 561 sage: len(T) 562 3 563 sage: del a 564 sage: import gc 565 sage: n = gc.collect() 566 sage: len(T) 567 0 568 569 We encourage to use objects for the first two constituents of the key 570 that allow for weak references. However, other objects still work. 571 Note that the keys are tested for identity, not for equality:: 572 573 sage: T[1,1,1] = 1 574 sage: T[1,1,1] 575 Traceback (most recent call last): 576 ... 577 KeyError: (1, 1, 1) 578 sage: x = 1 579 sage: T[x,x,x] = 1 580 sage: T[x,x,x] 581 1 582 583 The following illustrates why even sizes are bad. 584 :: 585 586 sage: L = TripleDictById(4, L) 587 sage: L.stats() 588 (0, 250.25, 1001) 589 sage: L.bucket_lens() 590 [1001, 0, 0, 0] 591 592 AUTHORS: 593 594 - Robert Bradshaw, 2007-08 595 - Simon King, 2011-12 596 597 """ 598 def __reduce__(self): 599 """ 600 TESTS:: 601 602 sage: from sage.structure.coerce_dict import TripleDictById 603 sage: L = TripleDictById(8) 604 sage: for i in range(100): L[i,i,i] = None 605 sage: len(loads(dumps(L))) 606 100 607 608 """ 609 return TripleDictById, (len(self.buckets), dict(self.iteritems()), self.threshold) 610 def resize(self, int buckets=0): 611 """ 612 Changes the number of buckets of self, while preserving the contents. 613 614 If the number of buckets is 0 or not given, it resizes self to the 615 smallest prime that is at least twice as large as self. 616 617 EXAMPLES:: 618 619 sage: from sage.structure.coerce_dict import TripleDictById 620 sage: L = TripleDictById(8) 621 sage: for i in range(100): L[i,i,i] = None 622 sage: L.bucket_lens() # random 623 [50, 0, 0, 0, 50, 0, 0, 0] 624 sage: L.resize(7) 625 sage: L.bucket_lens() # random 626 [15, 14, 14, 14, 14, 15, 14] 627 sage: L.resize() 628 sage: len(L.bucket_lens()) 629 17 630 """ 631 if buckets == 0: 632 buckets = next_odd_prime(2*len(self.buckets)) 633 cdef TripleDictById new = TripleDictById(buckets, self) 634 self.buckets = new.buckets 635 cdef get(self, k1, k2, k3): 636 cdef Py_ssize_t h = (<Py_ssize_t><void *>k1 + 13*<Py_ssize_t><void *>k2 ^ 503*<Py_ssize_t><void *>k3) 637 if h < 0: h = -h 638 cdef Py_ssize_t i 639 cdef list bucket = self.buckets[h % PyList_GET_SIZE(self.buckets)] 640 for i from 0 <= i < PyList_GET_SIZE(bucket) by 4: 641 if bucket[i]() is k1 and \ 642 bucket[i+1]() is k2 and \ 643 bucket[i+2] == k3: 644 return bucket[i+3] 645 raise KeyError, (k1, k2, k3) 646 647 cdef set(self, k1, k2, k3, value): 648 if self.threshold and self._size > len(self.buckets) * self.threshold: 649 self.resize() 650 cdef Py_ssize_t h = (<Py_ssize_t><void *>k1 + 13*<Py_ssize_t><void *>k2 ^ 503*<Py_ssize_t><void *>k3) 651 if h < 0: h = -h 652 cdef Py_ssize_t i 653 cdef list bucket = self.buckets[h % PyList_GET_SIZE(self.buckets)] 654 for i from 0 <= i < PyList_GET_SIZE(bucket) by 4: 655 if bucket[i]() is k1 and \ 656 bucket[i+1]() is k2 and \ 657 bucket[i+2] == k3: 658 bucket[i+3] = value 659 return 660 try: 661 r1 = ref(k1,self.eraser) 662 except TypeError: 663 r1 = ConstantFunction(k1) 664 try: 665 r2 = ref(k2,self.eraser) 666 except TypeError: 667 r2 = ConstantFunction(k2) 668 bucket += [r1, r2, k3, value] 669 self._size += 1 670 def __delitem__(self, k): 671 """ 672 EXAMPLES:: 673 674 sage: from sage.structure.coerce_dict import TripleDictById 675 sage: L = TripleDictById(31) 676 sage: a = 'a'; b = 'b'; c = 15 677 sage: L[a,b,c] = -1 678 sage: del L[a,b,15] 679 Traceback (most recent call last): 680 ... 681 KeyError: ('a', 'b', 15) 682 sage: del L[a,b,c] 683 sage: len(L) 684 0 685 """ 686 try: 687 k1, k2, k3 = k 688 except (TypeError,ValueError): 689 raise KeyError, k 690 cdef Py_ssize_t h = (<Py_ssize_t><void *>k1 + 13*<Py_ssize_t><void *>k2 ^ 503*<Py_ssize_t><void *>k3) 691 if h < 0: h = -h 692 cdef Py_ssize_t i 693 cdef list bucket = self.buckets[h % PyList_GET_SIZE(self.buckets)] 694 for i from 0 <= i < PyList_GET_SIZE(bucket) by 4: 695 if bucket[i]() is k1 and \ 696 bucket[i+1]() is k2 and \ 697 bucket[i+2] is k3: 698 del bucket[i:i+4] 699 self._size -= 1 700 return 701 raise KeyError, k 702 703 704 705 345 706 346 707 cdef class TripleDictIter: 347 708 def __init__(self, pairs): … … 383 744 k2 = self.bucket_iter.next() 384 745 k3 = self.bucket_iter.next() 385 746 value = self.bucket_iter.next() 386 return ((k1 , k2, k3), value)747 return ((k1(), k2(), k3), value) 387 748 except StopIteration: 388 749 self.bucket_iter = None 389 750 return self.next()