Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/structure/factory.pyx
8814 views
1
r"""
2
Factory for cached representations
3
4
.. SEEALSO::
5
6
:mod:`sage.structure.unique_representation`
7
8
Using a :class:`UniqueFactory` is one way of implementing a *cached
9
representation behaviour*. In spite of its name, using a
10
:class:`UniqueFactory` is not enough to ensure the *unique representation
11
behaviour*. See :mod:`~sage.structure.unique_representation` for a
12
detailed explanation.
13
14
With a :class:`UniqueFactory`, one can preprocess the given arguments. There
15
is special support for specifying a subset of the arguments that serve as the
16
unique key, so that still *all* given arguments are used to create a new
17
instance, but only the specified subset is used to look up in the
18
cache. Typically, this is used to construct objects that accept an optional
19
``check=[True|False]`` argument, but whose result should be unique
20
regardless of said optional argument. (This use case should be handled with
21
care, though: Any checking which isn't done in the ``create_key`` or
22
``create_key_and_extra_args`` method will be done only when a new object is
23
generated, but not when a cached object is retrieved from cache.
24
Consequently, if the factory is once called with ``check=False``, a
25
subsequent call with ``check=True`` cannot be expected to perform all checks
26
unless these checks are all in the ``create_key`` or
27
``create_key_and_extra_args`` method.)
28
29
For a class derived from
30
:class:`~sage.structure.unique_representation.CachedRepresentation`, argument
31
preprocessing can be obtained by providing a custom static ``__classcall__``
32
or ``__classcall_private__`` method, but this seems less transparent. When
33
argument preprocessing is not needed or the preprocess is not very
34
sophisticated, then generally
35
:class:`~sage.structure.unique_representation.CachedRepresentation` is much
36
easier to use than a factory.
37
38
AUTHORS:
39
40
- Robert Bradshaw (2008), initial version.
41
- Simon King (2013), extended documentation.
42
43
"""
44
45
#*****************************************************************************
46
# Copyright (C) 2008 Robert Bradshaw <[email protected]>
47
#
48
# Distributed under the terms of the GNU General Public License (GPL)
49
#
50
# This code is distributed in the hope that it will be useful,
51
# but WITHOUT ANY WARRANTY; without even the implied warranty of
52
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
53
# General Public License for more details.
54
#
55
# The full text of the GPL is available at:
56
#
57
# http://www.gnu.org/licenses/
58
#******************************************************************************
59
60
import types, copy_reg
61
62
from sage_object cimport SageObject
63
64
cdef sage_version
65
from sage.version import version as sage_version
66
67
sage_version = sage_version.split('.')
68
for i in range(len(sage_version)):
69
try:
70
sage_version[i] = int(sage_version[i])
71
except ValueError:
72
pass
73
sage_version = tuple(sage_version)
74
75
import sage.misc.weak_dict
76
77
cdef class UniqueFactory(SageObject):
78
"""
79
This class is intended to make it easy to cache objects.
80
81
It is based on the idea that the object is uniquely defined by a set of
82
defining data (the key). There is also the possibility of some
83
non-defining data (extra args) which will be used in initial creation,
84
but not affect the caching.
85
86
.. WARNING::
87
88
This class only provides *cached representation behaviour*. Hence,
89
using :class:`UniqueFactory`, it is still possible to create distinct
90
objects that evaluate equal. Unique representation behaviour can be
91
added, for example, by additionally inheriting from
92
:class:`sage.misc.fast_methods.WithEqualityById`.
93
94
The objects created are cached (using weakrefs) based on their key and
95
returned directly rather than re-created if requested again. Pickling is
96
taken care of by the factory, and will return the same object for the same
97
version of Sage, and distinct (but hopefully equal) objects for different
98
versions of Sage.
99
100
.. WARNING::
101
102
The objects returned by a :class:`UniqueFactory` must be instances of
103
new style classes (hence, they must be instances of :class:`object`)
104
that must not only allow a weak reference, but must accept general
105
attribute assignment. Otherwise, pickling won't work.
106
107
USAGE:
108
109
A *unique factory* provides a way to create objects from parameters
110
(the type of these objects can depend on the parameters, and is often
111
determined only at runtime) and to cache them by a certain key
112
derived from these parameters, so that when the factory is being
113
called again with the same parameters (or just with parameters which
114
yield the same key), the object is being returned from cache rather
115
than constructed anew.
116
117
An implementation of a unique factory consists of a factory class and
118
an instance of this factory class.
119
120
The factory class has to be a class inheriting from ``UniqueFactory``.
121
Typically it only needs to implement :meth:`create_key` (a method that
122
creates a key from the given parameters, under which key the object
123
will be stored in the cache) and :meth:`create_object` (a method that
124
returns the actual object from the key). Sometimes, one would also
125
implement :meth:`create_key_and_extra_args` (this differs from
126
:meth:`create_key` in allowing to also create some additional
127
arguments from the given parameters, which arguments then get passed
128
to :meth:`create_object` and thus can have an effect on the initial
129
creation of the object, but do *not* affect the key) or
130
:meth:`other_keys`. Other methods are not supposed to be overloaded.
131
132
The factory class itself cannot be called to create objects. Instead,
133
an instance of the factory class has to be created first. For
134
technical reasons, this instance has to be provided with a name that
135
allows Sage to find its definition. Specifically, the name of the
136
factory instance (or the full path to it, if it is not in the global
137
namespace) has to be passed to the factory class as a string variable.
138
So, if our factory class has been called ``A`` and is located in
139
``sage/spam/battletoads.py``, then we need to define an instance (say,
140
``B``) of ``A`` by writing ``B = A("sage.spam.battletoads.B")``
141
(or ``B = A("B")`` if this ``B`` will be imported into global
142
namespace). This instance can then be used to create objects (by
143
calling ``B(*parameters)``).
144
145
Notice that the objects created by the factory don't inherit from the
146
factory class. They *do* know about the factory that created them (this
147
information, along with the keys under which this factory caches them,
148
is stored in the ``_factory_data`` attributes of the objects), but not
149
via inheritance.
150
151
EXAMPLES:
152
153
The below examples are rather artificial and illustrate particular
154
aspects. For a "real-life" usage case of ``UniqueFactory``, see
155
the finite field factory in :mod:`sage.rings.finite_rings.constructor`.
156
157
In many cases, a factory class is implemented by providing the two
158
methods :meth:`create_key` and :meth:`create_object`. In our example,
159
we want to demonstrate how to use "extra arguments" to choose a specific
160
implementation, with preference given to an instance found in the cache,
161
even if its implementation is different. Hence, we implement
162
:meth:`create_key_and_extra_args` rather than :meth:`create_key`, putting
163
the chosen implementation into the extra arguments. Then, in the
164
:meth:`create_object` method, we create and return instances of the
165
specified implementation.
166
::
167
168
sage: from sage.structure.factory import UniqueFactory
169
sage: class MyFactory(UniqueFactory):
170
....: def create_key_and_extra_args(self, *args, **kwds):
171
....: return args, {'impl':kwds.get('impl', None)}
172
....: def create_object(self, version, key, **extra_args):
173
....: impl = extra_args['impl']
174
....: if impl=='C':
175
....: return C(*key)
176
....: if impl=='D':
177
....: return D(*key)
178
....: return E(*key)
179
....:
180
181
Now we can create a factory instance. It is supposed to be found under the
182
name ``"F"`` in the ``"__main__"`` module. Note that in an interactive
183
session, ``F`` would automatically be in the ``__main__`` module. Hence,
184
the second and third of the following four lines are only needed in
185
doctests. ::
186
187
sage: F = MyFactory("__main__.F")
188
sage: import __main__
189
sage: __main__.F = F
190
sage: loads(dumps(F)) is F
191
True
192
193
Now we create three classes ``C``, ``D`` and ``E``. The first is a Cython
194
extension-type class that does not allow weak references nor attribute
195
assignment. The second is a Python class that is not derived from
196
:class:`object`. The third allows attribute assignment and is derived
197
from :class:`object`. ::
198
199
sage: cython("cdef class C: pass")
200
sage: class D:
201
....: def __init__(self, *args):
202
....: self.t = args
203
....: def __repr__(self):
204
....: return "D%s"%repr(self.t)
205
....:
206
sage: class E(D, object): pass
207
208
Again, being in a doctest, we need to put the class ``D`` into the
209
``__main__`` module, so that Python can find it::
210
211
sage: import __main__
212
sage: __main__.D = D
213
214
It is impossible to create an instance of ``C`` with our factory, since it
215
does not allow weak references::
216
217
sage: F(1, impl='C')
218
Traceback (most recent call last):
219
...
220
TypeError: cannot create weak reference to '....C' object
221
222
Let us try again, with a Cython class that does allow weak
223
references. Now, creation of an instance using the factory works::
224
225
sage: cython('''cdef class C:
226
....: cdef __weakref__
227
....: ''')
228
....:
229
sage: c = F(1, impl='C')
230
sage: isinstance(c, C)
231
True
232
233
The cache is used when calling the factory again---even if it is suggested
234
to use a different implementation. This is because the implementation is
235
only considered an "extra argument" that does not count for the key.
236
::
237
238
sage: c is F(1, impl='C') is F(1, impl="D") is F(1)
239
True
240
241
However, pickling and unpickling does not use the cache. This is because
242
the factory has tried to assign an attribute to the instance that provides
243
information on the key used to create the instance, but failed::
244
245
sage: loads(dumps(c)) is c
246
False
247
sage: hasattr(c, '_factory_data')
248
False
249
250
We have already seen that our factory will only take the requested
251
implementation into account if the arguments used as key have not been
252
used yet. So, we use other arguments to create an instance of class
253
``D``::
254
255
sage: d = F(2, impl='D')
256
sage: isinstance(d, D)
257
True
258
259
The factory only knows about the pickling protocol used by new style
260
classes. Hence, again, pickling and unpickling fails to use the cache,
261
even though the "factory data" are now available::
262
263
sage: loads(dumps(d)) is d
264
False
265
sage: d._factory_data
266
(<class '__main__.MyFactory'>, (...), (2,), {'impl': 'D'})
267
268
Only when we have a new style class that can be weak referenced and allows
269
for attribute assignment, everything works::
270
271
sage: e = F(3)
272
sage: isinstance(e, E)
273
True
274
sage: loads(dumps(e)) is e
275
True
276
sage: e._factory_data
277
(<class '__main__.MyFactory'>, (...), (3,), {'impl': None})
278
279
"""
280
281
cdef readonly _name
282
cdef readonly _cache
283
284
def __init__(self, name):
285
"""
286
INPUT:
287
288
- ``name`` -- string. A name in the global namespace referring
289
to self or a fully qualified path name to self, which is
290
used to locate the factory on unpickling.
291
292
EXAMPLES::
293
294
sage: from sage.structure.factory import UniqueFactory
295
sage: fake_factory = UniqueFactory('ZZ')
296
sage: loads(dumps(fake_factory))
297
Integer Ring
298
sage: fake_factory = UniqueFactory('sage.rings.all.QQ')
299
sage: loads(dumps(fake_factory))
300
Rational Field
301
"""
302
self._name = name
303
self._cache = sage.misc.weak_dict.WeakValueDictionary()
304
305
def __reduce__(self):
306
"""
307
EXAMPLES::
308
309
sage: A = FiniteField(127)
310
sage: A is loads(dumps(A)) # indirect doctest
311
True
312
sage: B = FiniteField(3^3,'b')
313
sage: B is loads(dumps(B))
314
True
315
sage: C = FiniteField(2^16,'c')
316
sage: C is loads(dumps(C))
317
True
318
sage: D = FiniteField(3^20,'d')
319
sage: D is loads(dumps(D))
320
True
321
322
TESTS::
323
324
sage: loads(dumps(FiniteField)) is FiniteField
325
True
326
sage: from sage.structure.test_factory import test_factory
327
sage: loads(dumps(test_factory)) is test_factory
328
True
329
"""
330
return lookup_global, (self._name,)
331
332
def __call__(self, *args, **kwds):
333
"""
334
This is the method invoked to create objects. It first creates a key
335
from the given parameters, then if an object with that key already
336
exists returns it, and otherwise creates one and stores a weak reference
337
to it in its dictionary.
338
339
Do not override this method. Instead, override ``create_key`` and
340
``create_object`` and put the docstring in the body of the class.
341
342
EXAMPLES::
343
344
sage: from sage.structure.test_factory import test_factory
345
sage: _ = test_factory(1,2,3); _
346
Making object (1, 2, 3)
347
<sage.structure.test_factory.A instance at ...>
348
349
It already created one, so don't re-create::
350
351
sage: test_factory(1,2,3)
352
<sage.structure.test_factory.A instance at ...>
353
sage: test_factory(1,2,3) is test_factory(1,2,3)
354
True
355
356
Of course, with a different key, a new object will be created::
357
358
sage: test_factory(1,2,3) is test_factory(1,2,4)
359
Making object (1, 2, 4)
360
False
361
"""
362
key, kwds = self.create_key_and_extra_args(*args, **kwds)
363
version = self.get_version(sage_version)
364
return self.get_object(version, key, kwds)
365
366
cpdef get_object(self, version, key, extra_args):
367
"""
368
Returns the object corresponding to ``key``, creating it with
369
``extra_args`` if necessary (for example, it isn't in the cache
370
or it is unpickling from an older version of Sage).
371
372
EXAMPLES::
373
374
sage: from sage.structure.test_factory import test_factory
375
sage: a = test_factory.get_object(3.0, 'a', {}); a
376
Making object a
377
<sage.structure.test_factory.A instance at ...>
378
sage: test_factory.get_object(3.0, 'a', {}) is test_factory.get_object(3.0, 'a', {})
379
True
380
sage: test_factory.get_object(3.0, 'a', {}) is test_factory.get_object(3.1, 'a', {})
381
Making object a
382
False
383
sage: test_factory.get_object(3.0, 'a', {}) is test_factory.get_object(3.0, 'b', {})
384
Making object b
385
False
386
"""
387
try:
388
return self._cache[version, key]
389
except KeyError:
390
pass
391
obj = self.create_object(version, key, **extra_args)
392
self._cache[version, key] = obj
393
try:
394
other_keys = self.other_keys(key, obj)
395
for key in other_keys:
396
try:
397
obj = self._cache[version, key]
398
break
399
except KeyError:
400
pass
401
for key in other_keys:
402
self._cache[version, key] = obj
403
obj._factory_data = self, version, key, extra_args
404
if obj.__class__.__reduce__.__objclass__ is object:
405
# replace the generic object __reduce__ to use this one
406
obj.__reduce_ex__ = types.MethodType(generic_factory_reduce, obj)
407
except AttributeError:
408
pass
409
return obj
410
411
cpdef get_version(self, sage_version):
412
"""
413
This is provided to allow more or less granular control over
414
pickle versioning. Objects pickled in the same version of Sage
415
will unpickle to the same rather than simply equal objects. This
416
can provide significant gains as arithmetic must be performed on
417
objects with identical parents. However, if there has been an
418
incompatible change (e.g. in element representation) we want the
419
version number to change so coercion is forced between the two
420
parents.
421
422
Defaults to the Sage version that is passed in, but coarser
423
granularity can be provided.
424
425
EXAMPLES::
426
427
sage: from sage.structure.test_factory import test_factory
428
sage: test_factory.get_version((3,1,0))
429
(3, 1, 0)
430
"""
431
return sage_version
432
433
def create_key_and_extra_args(self, *args, **kwds):
434
r"""
435
Return a tuple containing the key (uniquely defining data)
436
and any extra arguments (empty by default).
437
438
Defaults to :meth:`create_key`.
439
440
EXAMPLES::
441
442
sage: from sage.structure.test_factory import test_factory
443
sage: test_factory.create_key_and_extra_args(1, 2, key=5)
444
((1, 2), {})
445
sage: GF.create_key_and_extra_args(3, foo='value')
446
((3, None, None, None, "{'foo': 'value'}", 3, 1, True), {'foo': 'value'})
447
"""
448
return self.create_key(*args, **kwds), {}
449
450
def create_key(self, *args, **kwds):
451
"""
452
Given the parameters (arguments and keywords), create a key
453
that uniquely determines this object.
454
455
EXAMPLES::
456
457
sage: from sage.structure.test_factory import test_factory
458
sage: test_factory.create_key(1, 2, key=5)
459
(1, 2)
460
"""
461
raise NotImplementedError
462
463
def create_object(self, version, key, **extra_args):
464
"""
465
Create the object from the key and extra arguments. This is only
466
called if the object was not found in the cache.
467
468
EXAMPLES::
469
470
sage: from sage.structure.test_factory import test_factory
471
sage: test_factory.create_object(0, (1,2,3))
472
Making object (1, 2, 3)
473
<sage.structure.test_factory.A instance at ...>
474
sage: test_factory('a')
475
Making object ('a',)
476
<sage.structure.test_factory.A instance at ...>
477
sage: test_factory('a') # NOT called again
478
<sage.structure.test_factory.A instance at ...>
479
"""
480
raise NotImplementedError
481
482
cpdef other_keys(self, key, obj):
483
"""
484
Sometimes during object creation, certain defaults are chosen which
485
may result in a new (more specific) key. This allows the more specific
486
key to be regarded as equivalent to the original key returned by
487
:meth:`create_key` for the purpose of lookup in the cache, and is used
488
for pickling.
489
490
EXAMPLES:
491
492
We use the ``GF`` factory to build the finite field with `27`
493
elements and generator `k`::
494
495
sage: key, _ = GF.create_key_and_extra_args(27, 'k'); key
496
(27, ('k',), x^3 + 2*x + 1, None, '{}', 3, 3, True)
497
sage: K = GF.create_object(0, key); K
498
Finite Field in k of size 3^3
499
sage: GF.other_keys(key, K)
500
[(27, ('k',), x^3 + 2*x + 1, None, '{}', 3, 3, True),
501
(27, ('k',), x^3 + 2*x + 1, 'givaro', '{}', 3, 3, True)]
502
503
sage: K = GF(7^40, 'a')
504
sage: loads(dumps(K)) is K
505
True
506
"""
507
return []
508
509
cpdef reduce_data(self, obj):
510
"""
511
The results of this function can be returned from
512
:meth:`__reduce__`. This is here so the factory internals can
513
change without having to re-write :meth:`__reduce__` methods
514
that use it.
515
516
EXAMPLE::
517
518
sage: V = FreeModule(ZZ, 5)
519
sage: factory, data = FreeModule.reduce_data(V)
520
sage: factory(*data)
521
Ambient free module of rank 5 over the principal ideal domain Integer Ring
522
sage: factory(*data) is V
523
True
524
525
sage: from sage.structure.test_factory import test_factory
526
sage: a = test_factory(1, 2)
527
Making object (1, 2)
528
sage: test_factory.reduce_data(a)
529
(<built-in function generic_factory_unpickle>,
530
(<class 'sage.structure.test_factory.UniqueFactoryTester'>,
531
(...),
532
(1, 2),
533
{}))
534
535
Note that the ellipsis ``(...)`` here stands for the Sage
536
version.
537
"""
538
return generic_factory_unpickle, obj._factory_data
539
540
541
def generic_factory_unpickle(UniqueFactory factory, *args):
542
"""
543
Method used for unpickling the object.
544
545
The unpickling mechanism needs a plain Python function to call.
546
It takes a factory as the first argument, passes the rest of the
547
arguments onto the factory's :meth:`UniqueFactory.get_object` method.
548
549
EXAMPLES::
550
551
sage: V = FreeModule(ZZ, 5)
552
sage: func, data = FreeModule.reduce_data(V)
553
sage: func is sage.structure.factory.generic_factory_unpickle
554
True
555
sage: sage.structure.factory.generic_factory_unpickle(*data) is V
556
True
557
"""
558
return factory.get_object(*args)
559
560
def generic_factory_reduce(self, proto):
561
"""
562
Used to provide a ``__reduce__`` method if one does not already exist.
563
564
EXAMPLES::
565
566
sage: V = QQ^6
567
sage: sage.structure.factory.generic_factory_reduce(V, 1) == V.__reduce_ex__(1)
568
True
569
"""
570
if self._factory_data is None:
571
raise NotImplementedError, "__reduce__ not implemented for %s" % type(self)
572
else:
573
return self._factory_data[0].reduce_data(self)
574
575
def lookup_global(name):
576
"""
577
Used in unpickling the factory itself.
578
579
EXAMPLES::
580
581
sage: from sage.structure.factory import lookup_global
582
sage: lookup_global('ZZ')
583
Integer Ring
584
sage: lookup_global('sage.rings.all.ZZ')
585
Integer Ring
586
"""
587
if '.' in name:
588
module, name = name.rsplit('.', 1)
589
all = __import__(module, fromlist=[name])
590
else:
591
import sage.all as all
592
return getattr(all, name)
593
594
595
# To make the pickle jar happy:
596
from sage.structure.test_factory import test_factory
597
598