# HG changeset patch
# User Simon King <simon.king@unijena.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


10  10   William Stein (20060114): Changed from Homspace to Homset. 
11  11  
12  12   Nicolas M. Thiery (200812): Updated for the new category framework 
 13  
 14   Simon King (201112): Use a weak cache for homsets 
13  15  """ 
14  16  
15  17  #***************************************************************************** 
… 
… 

30  32  import weakref 
31  33  
32  34  from sage.categories.category import Category 
 35  from sage.structure.category_object import CategoryObject 
33  36  import morphism 
34  37  from sage.structure.parent import Parent, Set_generic 
35  38  from sage.misc.lazy_attribute import lazy_attribute 
36  39  from sage.misc.cachefunc import cached_function 
37  40  import types 
38  41  
39   _cache = {} 
 42  _cache = weakref.WeakKeyDictionary() 
40  43  def Hom(X, Y, category=None): 
41  44  """ 
42  45  Create the space of homomorphisms from X to Y in the category ``category``. 
… 
… 

72  75  sage: Hom(FreeModule(QQ,1), FreeModule(ZZ,1)) 
73  76  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 
74  77  
 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  
75  93  To illustrate the choice of the category, we consider the 
76  94  following parents as running examples:: 
77  95  
… 
… 

155  173  # However it breaks somehow the coercion (see e.g. sage t sage.rings.real_mpfr) 
156  174  # To be investigated. 
157  175  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 sagecombinat/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) 
167  182  
168  183  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 sagecombinat/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. 
169  208  return X._Hom_(Y, category) 
170  209  except (AttributeError, TypeError): 
171  210  pass 
… 
… 

182  221  else: 
183  222  raise TypeError, "Argument category (= %s) must be a category."%category 
184  223  # 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 
192  234  
193  235  # coercing would be incredibly annoying, since the domain and codomain 
194  236  # are totally different objects 
… 
… 

201  243  H = category.hom_category().parent_class(X, Y, category = category) 
202  244  
203  245  ##_cache[key] = weakref.ref(H) 
204   _cache[(X, Y, category)] = weakref.ref(H) 
 246  cache3[catkey] = H 
205  247  return H 
206  248  
207  249  def hom(X, Y, f): 
diff git a/sage/rings/polynomial/multi_polynomial_ring.py b/sage/rings/polynomial/multi_polynomial_ring.py
a

b


79  79  from sage.rings.polynomial.polydict import PolyDict, ETuple 
80  80  from sage.rings.polynomial.term_order import TermOrder 
81  81  
 82  from sage.structure.parent import Parent 
 83  
82  84  from sage.interfaces.all import is_SingularElement 
83  85  from sage.interfaces.all import macaulay2 as macaulay2_default 
84  86  from sage.interfaces.macaulay2 import is_Macaulay2Element 
… 
… 

137  139  def __init__(self, base_ring, n, names, order): 
138  140  from sage.rings.polynomial.polynomial_singular_interface import can_convert_to_singular 
139  141  order = TermOrder(order,n) 
140   MPolynomialRing_generic.__init__(self, base_ring, n, names, order) 
 142  Parent.__init__(self, base=base_ring) 
141  143  # Construct the generators 
142  144  v = [0 for _ in xrange(n)] 
143  145  one = base_ring(1); 
… 
… 

148  150  self._gens.append(C(self, {tuple(v):one})) 
149  151  v[i] = 0 
150  152  self._gens = tuple(self._gens) 
 153  MPolynomialRing_generic.__init__(self, base_ring, n, names, order) 
151  154  self._zero_tuple = tuple(v) 
152  155  self._has_singular = can_convert_to_singular(self) 
153  156  # This polynomial ring should belong to Algebras(base_ring). 
diff git a/sage/structure/category_object.pyx b/sage/structure/category_object.pyx
a

b


583  583  sage: R.Hom(QQ) 
584  584  Set of Homomorphisms from Multivariate Polynomial Ring in x, y over Rational Field to Rational Field 
585  585  
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  :: 
587  592  
588   :: 
589   
590  593  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 
594  601  
595  602  This example illustrates the optional third argument:: 
596  603  
diff git a/sage/structure/parent.pyx b/sage/structure/parent.pyx
a

b


1415  1415  sage: R.Hom(QQ) 
1416  1416  Set of Homomorphisms from Multivariate Polynomial Ring in x, y over Rational Field to Rational Field 
1417  1417  
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`. 
1419  1422  
1420  1423  :: 
1421  1424  
1422  1425  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 
1424  1429  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 
1426  1433  
1427  1434  This example illustrates the optional third argument:: 
1428  1435  
diff git a/sage/structure/parent_base.pyx b/sage/structure/parent_base.pyx
a

b


99  99  homomorphisms from self to codomain in the category cat. The 
100  100  default category is \code{self.category()}. 
101  101  
102   EXAMPLES: 
 102  EXAMPLES:: 
 103  
103  104  sage: R.<x,y> = PolynomialRing(QQ, 2) 
104  105  sage: R.Hom(QQ) 
105  106  Set of Homomorphisms from Multivariate Polynomial Ring in x, y over Rational Field to Rational Field 
106  107  
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  
108  115  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 
110  119  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 
112  123  
113   This example illustrates the optional third argument: 
 124  This example illustrates the optional third argument:: 
 125  
114  126  sage: QQ.Hom(ZZ, Sets()) 
115  127  Set of Morphisms from Rational Field to Integer Ring in Category of sets 
116  128  """ 