Ticket #11521: trac11521_new_homset_cache.patch

File trac11521_new_homset_cache.patch, 11.3 KB (added by SimonKing, 8 years ago)

Use weak references for the keys of the homset cache. If weak references are not supported, then raise an error, pointing out that category objects should be CategoryObjects.

  • sage/categories/homset.py

    # HG changeset patch
    # User Simon King <simon.king@uni-jena.de>
    # Date 1324923418 -3600
    # Node ID 10a4774934582e834dd882128d60df651fc3f476
    # Parent  9ca30e8d246cd318261c3e9f46bd351d119fdff5
    #11521: Use a more sophisticated way of using weak references in the homset cache
    If an object of a category does not support weak references, then the error message
    suggests to use CategoryObject for implementing category objects.
    
    diff --git a/sage/categories/homset.py b/sage/categories/homset.py
    a b  
    1010- William Stein (2006-01-14): Changed from Homspace to Homset.
    1111
    1212- Nicolas M. Thiery (2008-12-): Updated for the new category framework
     13
     14- Simon King (2011-12): Use a weak cache for homsets
    1315"""
    1416
    1517#*****************************************************************************
     
    3032import weakref
    3133
    3234from sage.categories.category import Category
     35from sage.structure.category_object import CategoryObject
    3336import morphism
    3437from sage.structure.parent import Parent, Set_generic
    3538from sage.misc.lazy_attribute import lazy_attribute
    3639from sage.misc.cachefunc import cached_function
    3740import types
    3841
    39 _cache = {}
     42_cache = weakref.WeakKeyDictionary()
    4043def Hom(X, Y, category=None):
    4144    """
    4245    Create the space of homomorphisms from X to Y in the category ``category``.
     
    7275        sage: Hom(FreeModule(QQ,1), FreeModule(ZZ,1))
    7376        Set of Morphisms from Vector space of dimension 1 over Rational Field to Ambient free module of rank 1 over the principal ideal domain Integer Ring in Category of vector spaces over Rational Field
    7477
     78    Here, we test against a memory leak that has been fixed at #11521 by using
     79    a weak cache::
     80
     81        sage: for p in prime_range(10^5):
     82        ...    K = GF(p)
     83        ...    a = K(0)
     84        sage: import gc
     85        sage: gc.collect()       # random
     86        624
     87        sage: from sage.rings.finite_rings.finite_field_prime_modn import FiniteField_prime_modn as FF
     88        sage: L = [x for x in gc.get_objects() if isinstance(x, FF)]
     89        sage: len(L), L[0], L[len(L)-1]
     90        (2, Finite Field of size 2, Finite Field of size 99991)
     91
     92
    7593    To illustrate the choice of the category, we consider the
    7694    following parents as running examples::
    7795
     
    155173    # However it breaks somehow the coercion (see e.g. sage -t sage.rings.real_mpfr)
    156174    # To be investigated.
    157175    global _cache
    158     key = (X,Y,category)
    159     if _cache.has_key(key):
    160         H = _cache[key]()
    161         # What is this test for? Why does the cache ever contain a 0 value?
    162         # This actually occurs: see e.g. sage -t  sage-combinat/sage/categories/modules_with_basis.py
    163         if H:
    164             # Are domain or codomain breaking the unique parent condition?
    165             if H.domain() is X and H.codomain() is Y:
    166                 return H
     176    try:
     177        cache2 = _cache[X]
     178    except KeyError:
     179        cache2 = _cache[X] = weakref.WeakKeyDictionary()
     180    except TypeError:
     181        raise TypeError, "Objects of categories must be instances of %s, but %s isn't"%(CategoryObject,X)
    167182
    168183    try:
     184        cache3 = cache2[Y]
     185    except KeyError:
     186        # It would not be reasonable to use a WeakValueDictionary here,
     187        # since the homset has a reference to the key anyway. Hence, if
     188        # the value could be garbage collected, then the key could be
     189        # garbage collected as well.
     190        cache3 = cache2[Y] = {}
     191    except TypeError:
     192        raise TypeError, "Objects of categories must be instances of %s, but %s isn't"%(CategoryObject,Y)
     193
     194    catkey = weakref.ref(category) if category is not None else None
     195    try:
     196        H = cache3[catkey]
     197    except KeyError:
     198        H = None
     199    # What is this test for? Why does the cache ever contain a 0 value?
     200    # This actually occurs: see e.g. sage -t  sage-combinat/sage/categories/modules_with_basis.py
     201    if H:
     202        # Are domain or codomain breaking the unique parent condition?
     203        if H.domain() is X and H.codomain() is Y:
     204            return H
     205
     206    try:
     207        # apparently _Hom_ is supposed to be cached.
    169208        return X._Hom_(Y, category)
    170209    except (AttributeError, TypeError):
    171210        pass
     
    182221    else:
    183222        raise TypeError, "Argument category (= %s) must be a category."%category
    184223    # Now, as the category may have changed, we try to find the hom set in the cache, again:
    185     key = (X,Y,category)
    186     if _cache.has_key(key):
    187         H = _cache[key]()
    188         if H:
    189             # Are domain or codomain breaking the unique parent condition?
    190             if H.domain() is X and H.codomain() is Y:
    191                 return H
     224    catkey = weakref.ref(category) if category is not None else None
     225    try:
     226        H = cache3[catkey]
     227    except KeyError:
     228        pass
     229
     230    if H:
     231        # Are domain or codomain breaking the unique parent condition?
     232        if H.domain() is X and H.codomain() is Y:
     233            return H
    192234
    193235    # coercing would be incredibly annoying, since the domain and codomain
    194236    # are totally different objects
     
    201243    H = category.hom_category().parent_class(X, Y, category = category)
    202244           
    203245    ##_cache[key] = weakref.ref(H)
    204     _cache[(X, Y, category)] = weakref.ref(H)
     246    cache3[catkey] = H
    205247    return H
    206248
    207249def hom(X, Y, f):
  • sage/rings/polynomial/multi_polynomial_ring.py

    diff --git a/sage/rings/polynomial/multi_polynomial_ring.py b/sage/rings/polynomial/multi_polynomial_ring.py
    a b  
    7979from sage.rings.polynomial.polydict import PolyDict, ETuple
    8080from sage.rings.polynomial.term_order import TermOrder
    8181
     82from sage.structure.parent import Parent
     83
    8284from sage.interfaces.all import is_SingularElement
    8385from sage.interfaces.all import macaulay2 as macaulay2_default
    8486from sage.interfaces.macaulay2 import is_Macaulay2Element
     
    137139    def __init__(self, base_ring, n, names, order):
    138140        from sage.rings.polynomial.polynomial_singular_interface import can_convert_to_singular
    139141        order = TermOrder(order,n)
    140         MPolynomialRing_generic.__init__(self, base_ring, n, names, order)
     142        Parent.__init__(self, base=base_ring)
    141143        # Construct the generators
    142144        v = [0 for _ in xrange(n)]
    143145        one = base_ring(1);
     
    148150            self._gens.append(C(self, {tuple(v):one}))
    149151            v[i] = 0
    150152        self._gens = tuple(self._gens)
     153        MPolynomialRing_generic.__init__(self, base_ring, n, names, order)
    151154        self._zero_tuple = tuple(v)
    152155        self._has_singular = can_convert_to_singular(self)
    153156        # This polynomial ring should belong to Algebras(base_ring).
  • sage/structure/category_object.pyx

    diff --git a/sage/structure/category_object.pyx b/sage/structure/category_object.pyx
    a b  
    583583            sage: R.Hom(QQ)
    584584            Set of Homomorphisms from Multivariate Polynomial Ring in x, y over Rational Field to Rational Field
    585585
    586         Homspaces are defined for very general Sage objects, even elements of familiar rings.
     586        Homspaces are defined for very general Sage objects. However, since trac
     587        ticket #11521, it is enforced that objects of categories support weak
     588        references (which is the case if they are instances of
     589        :class:`~sage.structure.category_object.CategoryObject`.
     590
     591        ::
    587592       
    588         ::
    589 
    590593            sage: n = 5; Hom(n,7)
    591             Set of Morphisms from 5 to 7 in Category of elements of Integer Ring
    592             sage: z=(2/3); Hom(z,8/1)
    593             Set of Morphisms from 2/3 to 8 in Category of elements of Rational Field
     594            Traceback (most recent call last):
     595            ...
     596            TypeError: Objects of categories must be instances of <type 'sage.structure.category_object.CategoryObject'>, but 5 isn't
     597            sage: Hom(ZZ,8/1)
     598            Traceback (most recent call last):
     599            ...
     600            TypeError: Objects of categories must be instances of <type 'sage.structure.category_object.CategoryObject'>, but 8 isn't
    594601
    595602        This example illustrates the optional third argument::
    596603
  • sage/structure/parent.pyx

    diff --git a/sage/structure/parent.pyx b/sage/structure/parent.pyx
    a b  
    14151415            sage: R.Hom(QQ)
    14161416            Set of Homomorphisms from Multivariate Polynomial Ring in x, y over Rational Field to Rational Field
    14171417
    1418         Homspaces are defined for very general Sage objects, even elements of familiar rings.
     1418        Homspaces are defined for very general Sage objects. However, since trac
     1419        ticket #11521, it is enforced that objects of categories support weak
     1420        references (which is the case if they are instances of
     1421        :class:`~sage.structure.category_object.CategoryObject`.
    14191422
    14201423        ::
    14211424       
    14221425            sage: n = 5; Hom(n,7)
    1423             Set of Morphisms from 5 to 7 in Category of elements of Integer Ring
     1426            Traceback (most recent call last):
     1427            ...
     1428            TypeError: Objects of categories must be instances of <type 'sage.structure.category_object.CategoryObject'>, but 5 isn't
    14241429            sage: z=(2/3); Hom(z,8/1)
    1425             Set of Morphisms from 2/3 to 8 in Category of elements of Rational Field
     1430            Traceback (most recent call last):
     1431            ...
     1432            TypeError: Objects of categories must be instances of <type 'sage.structure.category_object.CategoryObject'>, but 2/3 isn't
    14261433
    14271434        This example illustrates the optional third argument::
    14281435       
  • sage/structure/parent_base.pyx

    diff --git a/sage/structure/parent_base.pyx b/sage/structure/parent_base.pyx
    a b  
    9999        homomorphisms from self to codomain in the category cat.  The
    100100        default category is \code{self.category()}.
    101101
    102         EXAMPLES:
     102        EXAMPLES::
     103
    103104            sage: R.<x,y> = PolynomialRing(QQ, 2)
    104105            sage: R.Hom(QQ)
    105106            Set of Homomorphisms from Multivariate Polynomial Ring in x, y over Rational Field to Rational Field
    106107
    107         Homspaces are defined for very general \sage objects, even elements of familiar rings.
     108        Homspaces are defined for very general Sage objects. However, since trac
     109        ticket #11521, it is enforced that objects of categories support weak
     110        references (which is the case if they are instances of
     111        :class:`~sage.structure.category_object.CategoryObject`.
     112
     113        ::
     114       
    108115            sage: n = 5; Hom(n,7)
    109             Set of Morphisms from 5 to 7 in Category of elements of Integer Ring
     116            Traceback (most recent call last):
     117            ...
     118            TypeError: Objects of categories must be instances of <type 'sage.structure.category_object.CategoryObject'>, but 5 isn't
    110119            sage: z=(2/3); Hom(z,8/1)
    111             Set of Morphisms from 2/3 to 8 in Category of elements of Rational Field
     120            Traceback (most recent call last):
     121            ...
     122            TypeError: Objects of categories must be instances of <type 'sage.structure.category_object.CategoryObject'>, but 2/3 isn't
    112123
    113         This example illustrates the optional third argument:
     124        This example illustrates the optional third argument::
     125
    114126            sage: QQ.Hom(ZZ, Sets())
    115127            Set of Morphisms from Rational Field to Integer Ring in Category of sets
    116128        """