Ticket #715: trac715_one_triple_dict.patch

File trac715_one_triple_dict.patch, 39.3 KB (added by SimonKing, 8 years ago)

Drop the distinction of TripleDict versus TripleDictById. Use the memory addresses as dictionary keys

  • 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  
    88    cdef S
    99    cdef bint _is_left
    1010    cdef op
     11    cdef underlying_set(self)
    1112    cpdef _call_(self, a, b)
    1213   
    1314   
  • sage/categories/action.pyx

    diff --git a/sage/categories/action.pyx b/sage/categories/action.pyx
    a b  
    3232
    3333import homset
    3434import sage.structure.element
     35from weakref import ref
    3536
    3637include "../ext/stdsage.pxi"
    3738
     
    4849        from groupoid import Groupoid
    4950        Functor.__init__(self, Groupoid(G), category(S))
    5051        self.G = G
    51         self.S = S
     52        self.S = ref(S)
    5253        self._is_left = is_left
    5354        self.op = op
    5455       
     
    6162            if g in self.G:
    6263                return ActionEndomorphism(self, self.G(g))
    6364            elif g == self.G:
    64                 return self.S
     65                return self.underlying_set()
    6566            else:
    6667                raise TypeError, "%s not an element of %s"%(g, self.G)
    6768        elif len(args) == 2:
    6869            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]))
    7071            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]))
    7273           
    7374    cpdef _call_(self, a, b):
    7475        raise NotImplementedError, "Action not implemented."
     
    9192       
    9293    def __repr__(self):
    9394        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())
    9597       
    9698    def _repr_name_(self):
    9799        return "action"
    98100       
    99101    def actor(self):
    100102        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
    102158    def codomain(self):
    103         return self.S
     159       return self.underlying_set()
    104160       
    105161    def domain(self):
    106         return self.S
     162        return self.underlying_set()
    107163       
    108164    def left_domain(self):
    109165        if self._is_left:
     
    145201            # We must be in the case that parent(~a) == parent(a)
    146202            # so we can invert in call_c code below.
    147203            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)
    149205                self._action = action
    150206                return
    151207            else:
    152208                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)
    154210                self._action = action
    155211                return
    156212        except (AttributeError, NotImplementedError):
     
    190246              right_precomposition = homset.Hom(right_precomposition._codomain, right).natural_map() * right_precomposition
    191247            right = right_precomposition._domain
    192248        if action._is_left:
    193             Action.__init__(self, left, action.S, 1)
     249            Action.__init__(self, left, action.underlying_set(), 1)
    194250        else:
    195             Action.__init__(self, right, action.S, 0)
     251            Action.__init__(self, right, action.underlying_set(), 0)
    196252        self._action = action
    197253        self.left_precomposition = left_precomposition
    198254        self.right_precomposition = right_precomposition
     
    230286cdef class ActionEndomorphism(Morphism):
    231287   
    232288    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()))
    234291        self._action = action
    235292        self._g = g
    236293       
     
    241298            return self._action._call_(x, self._g)
    242299               
    243300    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)
    245303       
    246304    def __mul__(left, right):
    247305        cdef ActionEndomorphism left_c, right_c
  • sage/categories/functor.pxd

    diff --git a/sage/categories/functor.pxd b/sage/categories/functor.pxd
    a b  
    11from sage.structure.sage_object cimport SageObject
    22
    33cdef class Functor(SageObject):
     4    cdef __weakref__
    45    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  
    3838       
    3939    def domain(self):
    4040        """
    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
    4351            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
    4452            sage: A.actor()
    4553            Full MatrixSpace of 2 by 2 dense matrices over Rational Field
     
    4856            sage: A.codomain()
    4957            Full MatrixSpace of 2 by 2 dense matrices over Univariate Polynomial Ring in x over Rational Field
    5058        """
    51         return self.S
     59        return self.underlying_set()
    5260
    5361
    5462cdef class MatrixMatrixAction(MatrixMulAction):
    5563    def __init__(self, G, S):
    5664        """
    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
    5872            sage: R.<x> = ZZ[]
     73            sage: MSR = MatrixSpace(R, 3, 3)
     74            sage: MSQ = MatrixSpace(QQ, 3, 2)
    5975            sage: from sage.matrix.action import MatrixMatrixAction
    60             sage: A = MatrixMatrixAction(MatrixSpace(R, 3, 3), MatrixSpace(QQ, 3, 2)); A
     76            sage: A = MatrixMatrixAction(MSR, MSQ); A
    6177            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
    6278            sage: A.codomain()
    6379            Full MatrixSpace of 3 by 2 dense matrices over Univariate Polynomial Ring in x over Rational Field
     
    7288       
    7389    def _create_codomain(self, base):
    7490        """
    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
    7698            sage: from sage.matrix.action import MatrixMatrixAction
    7799            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
    79103            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
    80104            sage: A.codomain()
    81105            Full MatrixSpace of 3 by 2 dense matrices over Univariate Polynomial Ring in x over Rational Field
    82106        """
    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())
    86111       
    87112    cpdef _call_(self, g, s):
    88113        """
    89114        EXAMPLES:
    90         Respects compatible subdivisions:
     115
     116        Respects compatible subdivisions::
     117
    91118            sage: M = matrix(5, 5, prime_range(100))
    92119            sage: M.subdivide(2,3); M
    93120            [ 2  3  5| 7 11]
     
    112139            [ 8168|11143]
    113140            [11056|15077]
    114141
    115         Note that this is just like block matrix multiplication:
     142        Note that this is just like block matrix multiplication::
     143
    116144            sage: M.subdivision(0,0) * N.subdivision(0,0) + M.subdivision(0,1) * N.subdivision(1,0)
    117145            [1048]
    118146            [3056]
    119147           
    120148        If the subdivisions aren't compatible, ignore them.
     149        ::
     150
    121151            sage: N.subdivide(1,1); N
    122152            [ 0| 1]
    123153            [--+--]
     
    156186cdef class MatrixVectorAction(MatrixMulAction):
    157187    def __init__(self, G, S):
    158188        """
    159         EXAMPLES:
     189        EXAMPLES::
     190
    160191            sage: from sage.matrix.action import MatrixVectorAction
    161192            sage: A = MatrixVectorAction(MatrixSpace(QQ, 3, 3), VectorSpace(CDF, 4)); A
    162193            Traceback (most recent call last):
     
    169200       
    170201    def _create_codomain(self, base):
    171202        """
    172         EXAMPLES:
     203        EXAMPLES::
     204
    173205            sage: from sage.matrix.action import MatrixVectorAction
    174206            sage: A = MatrixVectorAction(MatrixSpace(QQ, 5, 3), VectorSpace(CDF, 3)); A
    175207            Left action by Full MatrixSpace of 5 by 3 dense matrices over Rational Field on Vector space of dimension 3 over Complex Double Field
    176208            sage: A.codomain()
    177209            Vector space of dimension 5 over Complex Double Field
    178210        """
    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())
    181214        return FreeModule(base, self.G.nrows(), sparse = self.G.is_sparse())
    182215       
    183216    cpdef _call_(self, g, s):
     
    198231cdef class VectorMatrixAction(MatrixMulAction):
    199232    def __init__(self, G, S):
    200233        """
    201         EXAMPLES:
     234        EXAMPLES::
     235
    202236            sage: from sage.matrix.action import VectorMatrixAction
    203237            sage: A = VectorMatrixAction(MatrixSpace(QQ, 5, 3), VectorSpace(CDF, 3)); A
    204238            Traceback (most recent call last):
     
    211245       
    212246    def _create_codomain(self, base):
    213247        """
    214         EXAMPLES:
     248        EXAMPLES::
     249
    215250            sage: from sage.matrix.action import VectorMatrixAction
    216251            sage: A = VectorMatrixAction(MatrixSpace(QQ, 3, 5), VectorSpace(CDF, 3)); A
    217252            Right action by Full MatrixSpace of 3 by 5 dense matrices over Rational Field on Vector space of dimension 3 over Complex Double Field
    218253            sage: A.codomain()
    219254            Vector space of dimension 5 over Complex Double Field
    220255        """
    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())
    223259        return FreeModule(base, self.G.ncols(), sparse = self.G.is_sparse())
    224260       
    225261    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  
    5353from sage.categories.commutative_rings import CommutativeRings
    5454_CommutativeRings = CommutativeRings()
    5555
    56 _cache = {}
     56import weakref
     57_cache = weakref.WeakValueDictionary()
    5758
    5859def PolynomialRing(base_ring, arg1=None, arg2=None,
    5960                   sparse=False, order='degrevlex',
  • sage/structure/coerce.pyx

    diff --git a/sage/structure/coerce.pyx b/sage/structure/coerce.pyx
    a b  
    12071207        try:
    12081208            return self._action_maps.get(R, S, op)
    12091209        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
    12141215           
    12151216    cpdef verify_action(self, action, R, S, op, bint fix=True):
    12161217        r"""
  • sage/structure/coerce_actions.pyx

    diff --git a/sage/structure/coerce_actions.pyx b/sage/structure/coerce_actions.pyx
    a b  
    100100            Multivariate Polynomial Ring in x, y, z over Rational Field
    101101        """
    102102        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())))
    104105        return self._codomain
    105106
    106107
     
    332333        """
    333334        if self.extended_base is not None:
    334335            return self.extended_base
    335         return self.S
     336        return self.underlying_set()
    336337       
    337338    def domain(self):
    338339        """
     
    345346            sage: A.domain()
    346347            Multivariate Polynomial Ring in x, y, z over Integer Ring
    347348        """
    348         return self.S
     349        return self.underlying_set()
    349350
    350351
    351352
  • sage/structure/coerce_dict.pxd

    diff --git a/sage/structure/coerce_dict.pxd b/sage/structure/coerce_dict.pxd
    a b  
    22    cdef Py_ssize_t _size
    33    cdef buckets
    44    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)
    78   
    89cdef class TripleDictIter:
    910    cdef TripleDict pairs
    1011    cdef buckets, bucket_iter
     12
     13cdef 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  
    11#*****************************************************************************
    22#       Copyright (C) 2007 Robert Bradshaw <robertwb@math.washington.edu>
     3#                     2011 Simon King <simon.king@uni-jena.de>
    34#
    45#  Distributed under the terms of the GNU General Public License (GPL)
    56#
     
    910
    1011include "../ext/python_list.pxi"
    1112
     13from sage.misc.constant_function import ConstantFunction
     14from weakref import KeyedRef
     15
     16############################################
     17# The following code is responsible for
     18# removing dead references from the cache
     19############################################
     20
     21cdef dict _refcache = {}
     22
     23cdef 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)]
    12113
    13114cdef class TripleDict:
    14115    """
    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.
    17118   
    18     It differs from a python dict in the following important ways: 
     119    It differs from a python dict in the following important ways:
    19120   
    20121       - 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.
    22123       - Comparison is done using the 'is' rather than '==' operator.
    23124       
    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.
    27128   
    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
    29130    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.
    32133   
    33134    To spread objects evenly, the size should ideally be a prime, and certainly
    34     not divisible by 2. 
     135    not divisible by 2.
    35136   
    36137   
    37     EXAMPLES: 
     138    EXAMPLES::
    38139   
    39140        sage: from sage.structure.coerce_dict import TripleDict
    40141        sage: L = TripleDict(31)
     
    83184        ...
    84185        KeyError: 'a'
    85186       
    86     The following illustrates why even sizes are bad.
     187    The following illustrates why even sizes are bad::
     188
    87189        sage: L = TripleDict(4, L)
    88190        sage: L.stats()
    89191        (0, 250.25, 1001)
    90192        sage: L.bucket_lens()
    91193        [1001, 0, 0, 0]
    92194
     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::
    93200
    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
    96218    """
    97219   
    98220    def __init__(self, size, data=None, threshold=0):
    99221        """
    100222        Create a special dict using triples for keys.
    101223       
    102         EXAMPLES:
     224        EXAMPLES::
     225
    103226            sage: from sage.structure.coerce_dict import TripleDict
    104227            sage: L = TripleDict(31)
    105228            sage: a = 'a'; b = 'b'; c = 'c'
     
    111234        self.threshold = threshold
    112235        self.buckets = [[] for i from 0 <= i <  size]
    113236        self._size = 0
     237        self.eraser = TripleDictEraser(self)
    114238        if data is not None:
    115             for k, v in data.iteritems():
    116                 self[k] = v
     239            for (k1,k2,k3), v in data.iteritems():
     240                self.set(k1,k2,k3, v)
    117241               
    118242    def __len__(self):
    119243        """
    120244        The number of items in self.
    121245       
    122         EXAMPLES:
     246        EXAMPLES::
     247
    123248            sage: from sage.structure.coerce_dict import TripleDict
    124249            sage: L = TripleDict(37)
    125250            sage: a = 'a'; b = 'b'; c = 'c'
     
    136261        """
    137262        The distribution of items in buckets.
    138263       
    139         OUTPUT:
    140             (min, avg, max)
     264        OUTPUT::
     265
     266        - (min, avg, max)
    141267       
    142         EXAMPLES:
     268        EXAMPLES::
     269
    143270            sage: from sage.structure.coerce_dict import TripleDict
    144271            sage: L = TripleDict(37)
    145272            sage: for i in range(100): L[i,i,i] = None
     
    171298        """
    172299        The distribution of items in buckets.
    173300       
    174         OUTPUT:
    175             A list of how many items are in each bucket.
     301        OUTPUT:
    176302       
    177         EXAMPLES:
     303        A list of how many items are in each bucket.
     304       
     305        EXAMPLES::
     306
    178307            sage: from sage.structure.coerce_dict import TripleDict
    179308            sage: L = TripleDict(37)
    180309            sage: for i in range(100): L[i,i,i] = None
     
    194323        """
    195324        The actual buckets of self, for debugging.
    196325       
    197         EXAMPLE:
     326        EXAMPLE::
     327
    198328            sage: from sage.structure.coerce_dict import TripleDict
    199329            sage: L = TripleDict(3)
    200330            sage: L[0,0,0] = None
     
    205335       
    206336    def __getitem__(self, k):
    207337        """
    208         EXAMPLES:
     338        EXAMPLES::
     339
    209340            sage: from sage.structure.coerce_dict import TripleDict
    210341            sage: L = TripleDict(31)
    211342            sage: a = 'a'; b = 'b'; c = 'c'
     
    213344            sage: L[a,b,c]
    214345            1
    215346        """
     347        cdef object k1,k2,k3
    216348        try:
    217349            k1, k2, k3 = k
    218350        except (TypeError,ValueError):
    219351            raise KeyError, k
    220352        return self.get(k1, k2, k3)
    221353           
    222     cdef get(self, k1, k2, k3):
     354    cdef get(self, object k1, object k2, object k3):
    223355        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
     356        #if h < 0: h = -h  # shouldn't "%" result in a positive number anyway?
    225357        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
    227361        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)
    232369        raise KeyError, (k1, k2, k3)
    233370       
    234371    def __setitem__(self, k, value):
    235372        """
    236         EXAMPLES:
     373        EXAMPLES::
     374
    237375            sage: from sage.structure.coerce_dict import TripleDict
    238376            sage: L = TripleDict(31)
    239377            sage: a = 'a'; b = 'b'; c = 'c'
     
    241379            sage: L[a,b,c]
    242380            -1
    243381        """
     382        cdef object k1,k2,k3
    244383        try:
    245384            k1, k2, k3 = k
    246385        except (TypeError,ValueError):
    247386            raise KeyError, k
    248387        self.set(k1, k2, k3, value)
    249388           
    250     cdef set(self, k1, k2, k3, value):
     389    cdef set(self, object k1, object k2, object k3, value):
    251390        if self.threshold and self._size > len(self.buckets) * self.threshold:
    252391            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?
    255398        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
    257401        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)
    264431        self._size += 1
    265432           
    266433    def __delitem__(self, k):
    267434        """
    268         EXAMPLES:
     435        EXAMPLES::
     436
    269437            sage: from sage.structure.coerce_dict import TripleDict
    270438            sage: L = TripleDict(31)
    271439            sage: a = 'a'; b = 'b'; c = 'c'
     
    274442            sage: len(L)
    275443            0
    276444        """
     445        cdef object k1,k2,k3
    277446        try:
    278447            k1, k2, k3 = k
    279448        except (TypeError,ValueError):
    280449            raise KeyError, k
    281450        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
     451        #if h < 0: h = -h
    283452        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))
    285454        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:
    289458                del bucket[i:i+4]
    290459                self._size -= 1
    291460                return
     
    298467        If the number of buckets is 0 or not given, it resizes self to the
    299468        smallest prime that is at least twice as large as self.
    300469       
    301         EXAMPLES:
     470        EXAMPLES::
     471
    302472            sage: from sage.structure.coerce_dict import TripleDict
    303473            sage: L = TripleDict(8)
    304474            sage: for i in range(100): L[i,i,i] = None
     
    312482        """
    313483        if buckets == 0:
    314484            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
    318500    def iteritems(self):
    319501        """
    320         EXAMPLES:
     502        EXAMPLES::
     503
    321504            sage: from sage.structure.coerce_dict import TripleDict
    322505            sage: L = TripleDict(31)
    323506            sage: L[1,2,3] = None
     
    331514        Note that we don't expect equality as this class concerns itself with
    332515        object identity rather than object equality.
    333516
    334         EXAMPLES:
     517        EXAMPLES::
     518
    335519            sage: from sage.structure.coerce_dict import TripleDict
    336520            sage: L = TripleDict(31)
    337521            sage: L[1,2,3] = True
     
    346530cdef class TripleDictIter:
    347531    def __init__(self, pairs):
    348532        """
    349         EXAMPLES:
     533        EXAMPLES::
     534
    350535            sage: from sage.structure.coerce_dict import TripleDict, TripleDictIter
    351536            sage: L = TripleDict(31)
    352537            sage: L[1,2,3] = None
     
    357542        self.buckets = iter(self.pairs.buckets)
    358543    def __iter__(self):
    359544        """
    360         EXAMPLES:
     545        EXAMPLES::
     546
    361547            sage: from sage.structure.coerce_dict import TripleDict, TripleDictIter
    362548            sage: L = TripleDict(31)
    363549            sage: L[1,2,3] = None
     
    367553        return self
    368554    def __next__(self):
    369555        """
    370         EXAMPLES:
     556        EXAMPLES::
     557
    371558            sage: from sage.structure.coerce_dict import TripleDict, TripleDictIter
    372559            sage: L = TripleDict(31)
    373560            sage: L[1,2,3] = None
     
    378565        while self.bucket_iter is None:
    379566            self.bucket_iter = self.buckets.next()
    380567        self.bucket_iter = iter(self.bucket_iter)
     568        cdef Py_ssize_t k1,k2,k3
    381569        try:
    382570            k1 = self.bucket_iter.next()
    383571            k2 = self.bucket_iter.next()
    384572            k3 = self.bucket_iter.next()
    385573            value = self.bucket_iter.next()
    386             return ((k1, k2, k3), value)
     574            return ((<object><PyObject *>k1, <object><PyObject *>k2,
     575                     <object><PyObject *>k3), value)
    387576        except StopIteration:
    388577            self.bucket_iter = None
    389578            return self.next()
  • sage/structure/parent.pxd

    diff --git a/sage/structure/parent.pxd b/sage/structure/parent.pxd
    a b  
    77###############################################################################
    88
    99cimport sage.structure.category_object
     10from sage.structure.coerce_dict cimport TripleDict
    1011
    1112cdef class AttributeErrorMessage:
    1213    cdef type cls
     
    7778    # and Parents for which self._rmul_ and/or self._lmul_
    7879    # do the correct thing.
    7980    # Initialized at ring creation.
    80     cdef _action_list
     81    cdef list _action_list
    8182    # Hashtable of everything we've (possibly recursively) discovered so far.
    82     cdef _action_hash
     83    cdef TripleDict _action_hash
    8384
    8485    # List consisting of Morphisms (from anything to self)
    8586    # 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  
    802802            self._coerce_from_list = []
    803803            self._coerce_from_hash = {}
    804804            self._action_list = []
    805             self._action_hash = {}
     805            self._action_hash = TripleDict(23)
    806806            self._convert_from_list = []
    807807            self._convert_from_hash = {}
    808808            self._embedding = None
     
    918918        EXAMPLES::
    919919       
    920920            sage: sorted(QQ._introspect_coerce().items())
    921             [('_action_hash', {...}),
     921            [('_action_hash', <sage.structure.coerce_dict.TripleDict object at ...>),
    922922             ('_action_list', []),
    923923             ('_coerce_from_hash', {...}),
    924924             ('_coerce_from_list', []),
     
    18471847        if isinstance(action, Action):
    18481848            if action.actor() is self:
    18491849                self._action_list.append(action)
    1850                 self._action_hash[action.domain(), action.operation(), action.is_left()] = action
     1850                self._action_hash.set(action.domain(), action.operation(), action.is_left(), action)
    18511851            elif action.domain() is self:
    18521852                self._action_list.append(action)
    1853                 self._action_hash[action.actor(), action.operation(), not action.is_left()] = action
     1853                self._action_hash.set(action.actor(), action.operation(), not action.is_left(), action)
    18541854            else:
    18551855                raise ValueError, "Action must involve self"
    18561856        else:
     
    23862386        try:
    23872387            if self._action_hash is None: # this is because parent.__init__() does not always get called
    23882388                self.init_coerce()
    2389             return self._action_hash[S, op, self_on_left]
     2389            return self._action_hash.get(S, op, self_on_left)
    23902390        except KeyError:
    23912391            pass
    23922392
     
    24012401            # We do NOT add to the list, as this would lead to errors as in
    24022402            # the example above.
    24032403
    2404         self._action_hash[S, op, self_on_left] = action
     2404        self._action_hash.set(S, op, self_on_left, action)
    24052405        return action
    24062406       
    24072407
  • sage/structure/parent_old.pyx

    diff --git a/sage/structure/parent_old.pyx b/sage/structure/parent_old.pyx
    a b  
    3030import operator
    3131from parent import Set_PythonType, Set_PythonType_class
    3232from coerce import py_scalar_parent
     33from sage.structure.coerce_dict import TripleDict
    3334
    3435include '../ext/python_object.pxi'
    3536include '../ext/python_bool.pxi'
     
    6667        self._coerce_from_list = list(coerce_from)
    6768        self._coerce_from_hash = {}
    6869        self._action_list = list(actions)
    69         self._action_hash = {}
     70        self._action_hash = TripleDict(23)
    7071       
    7172        cdef parent.Parent other
    7273        for mor in embeddings: