Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
241818 views
1
from sage.categories.functor import Functor
2
from sage.categories.basic import *
3
4
from sage.structure.parent import CoercionException
5
6
# TODO, think through the rankings, and override pushout where necessary.
7
8
class ConstructionFunctor(Functor):
9
10
def __mul__(self, other):
11
if not isinstance(self, ConstructionFunctor) and not isinstance(other, ConstructionFunctor):
12
raise CoercionException, "Non-constructive product"
13
return CompositConstructionFunctor(other, self)
14
15
def pushout(self, other):
16
if self.rank > other.rank:
17
return self * other
18
else:
19
return other * self
20
21
def __cmp__(self, other):
22
"""
23
Equality here means that they are mathematically equivalent, though they may have specific implementation data.
24
See the \code{merge} function.
25
"""
26
return cmp(type(self), type(other))
27
28
def __str__(self):
29
s = str(type(self))
30
import re
31
return re.sub("<.*'.*\.([^.]*)'>", "\\1", s)
32
33
def __repr__(self):
34
return str(self)
35
36
def merge(self, other):
37
if self == other:
38
return self
39
else:
40
return None
41
42
def commutes(self, other):
43
return False
44
45
def expand(self):
46
return [self]
47
48
49
class CompositConstructionFunctor(ConstructionFunctor):
50
"""
51
A Construction Functor composed by other Construction Functors
52
53
INPUT:
54
55
``F1,F2,...``: A list of Construction Functors. The result is the
56
composition ``F1`` followed by ``F2`` followed by ...
57
58
EXAMPLES::
59
60
sage: from sage.categories.pushout import CompositConstructionFunctor
61
sage: F = CompositConstructionFunctor(QQ.construction()[0],ZZ['x'].construction()[0],QQ.construction()[0],ZZ['y'].construction()[0])
62
sage: F
63
Poly[y](FractionField(Poly[x](FractionField(...))))
64
sage: F == CompositConstructionFunctor(*F.all)
65
True
66
sage: F(GF(2)['t'])
67
Univariate Polynomial Ring in y over Fraction Field of Univariate Polynomial Ring in x over Fraction Field of Univariate Polynomial Ring in t over Finite Field of size 2 (using NTL)
68
69
"""
70
71
def __init__(self, *args):
72
"""
73
TESTS::
74
75
sage: from sage.categories.pushout import CompositConstructionFunctor
76
sage: F = CompositConstructionFunctor(QQ.construction()[0],ZZ['x'].construction()[0],QQ.construction()[0],ZZ['y'].construction()[0])
77
sage: F
78
Poly[y](FractionField(Poly[x](FractionField(...))))
79
sage: F == CompositConstructionFunctor(*F.all)
80
True
81
82
"""
83
self.all = []
84
for c in args:
85
if isinstance(c, list):
86
self.all += c
87
elif isinstance(c, CompositConstructionFunctor):
88
self.all += c.all
89
else:
90
self.all.append(c)
91
Functor.__init__(self, self.all[0].domain(), self.all[-1].codomain())
92
93
def __call__(self, R):
94
for c in self.all:
95
R = c(R)
96
return R
97
98
def __cmp__(self, other):
99
if isinstance(other, CompositConstructionFunctor):
100
return cmp(self.all, other.all)
101
else:
102
return cmp(type(self), type(other))
103
104
def __mul__(self, other):
105
"""
106
Convention: ``(F1*F2)(X) == F1(F2(X))``.
107
108
EXAMPLES::
109
110
sage: from sage.categories.pushout import CompositConstructionFunctor
111
sage: F1 = CompositConstructionFunctor(QQ.construction()[0],ZZ['x'].construction()[0])
112
sage: F2 = CompositConstructionFunctor(QQ.construction()[0],ZZ['y'].construction()[0])
113
sage: F1*F2
114
Poly[x](FractionField(Poly[y](FractionField(...))))
115
116
"""
117
if isinstance(self, CompositConstructionFunctor):
118
all = [other] + self.all
119
else:
120
all = other.all + [self]
121
return CompositConstructionFunctor(*all)
122
123
def __str__(self):
124
s = "..."
125
for c in self.all:
126
s = "%s(%s)" % (c,s)
127
return s
128
129
def expand(self):
130
"""
131
Return expansion of a CompositConstructionFunctor.
132
133
NOTE:
134
135
The product over the list of components, as returned by
136
the ``expand()`` method, is equal to ``self``.
137
138
EXAMPLES::
139
140
sage: from sage.categories.pushout import CompositConstructionFunctor
141
sage: F = CompositConstructionFunctor(QQ.construction()[0],ZZ['x'].construction()[0],QQ.construction()[0],ZZ['y'].construction()[0])
142
sage: F
143
Poly[y](FractionField(Poly[x](FractionField(...))))
144
sage: prod(F.expand()) == F
145
True
146
147
"""
148
return list(reversed(self.all))
149
150
151
class IdentityConstructionFunctor(ConstructionFunctor):
152
153
rank = -100
154
155
def __init__(self):
156
Functor.__init__(self, Sets(), Sets())
157
def __call__(self, R):
158
return R
159
def __mul__(self, other):
160
if isinstance(self, IdentityConstructionFunctor):
161
return other
162
else:
163
return self
164
165
166
167
168
class PolynomialFunctor(ConstructionFunctor):
169
170
rank = 9
171
172
def __init__(self, var, multi_variate=False):
173
from rings import Rings
174
Functor.__init__(self, Rings(), Rings())
175
self.var = var
176
self.multi_variate = multi_variate
177
178
def __call__(self, R):
179
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
180
return PolynomialRing(R, self.var)
181
182
def __cmp__(self, other):
183
c = cmp(type(self), type(other))
184
if c == 0:
185
c = cmp(self.var, other.var)
186
elif isinstance(other, MultiPolynomialFunctor):
187
return -cmp(other, self)
188
return c
189
190
def merge(self, other):
191
if isinstance(other, MultiPolynomialFunctor):
192
return other.merge(self)
193
elif self == other:
194
return self
195
else:
196
return None
197
198
def __str__(self):
199
return "Poly[%s]" % self.var
200
201
class MultiPolynomialFunctor(ConstructionFunctor):
202
"""
203
A constructor for multivariate polynomial rings.
204
"""
205
206
rank = 9
207
208
def __init__(self, vars, term_order):
209
"""
210
EXAMPLES:
211
sage: F = sage.categories.pushout.MultiPolynomialFunctor(['x','y'], None)
212
sage: F
213
MPoly[x,y]
214
sage: F(ZZ)
215
Multivariate Polynomial Ring in x, y over Integer Ring
216
sage: F(CC)
217
Multivariate Polynomial Ring in x, y over Complex Field with 53 bits of precision
218
"""
219
Functor.__init__(self, Rings(), Rings())
220
self.vars = vars
221
self.term_order = term_order
222
223
def __call__(self, R):
224
"""
225
EXAMPLES:
226
sage: R.<x,y,z> = QQ[]
227
sage: F = R.construction()[0]; F
228
MPoly[x,y,z]
229
sage: type(F)
230
<class 'sage.categories.pushout.MultiPolynomialFunctor'>
231
sage: F(ZZ)
232
Multivariate Polynomial Ring in x, y, z over Integer Ring
233
sage: F(RR)
234
Multivariate Polynomial Ring in x, y, z over Real Field with 53 bits of precision
235
"""
236
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
237
return PolynomialRing(R, self.vars)
238
239
def __cmp__(self, other):
240
"""
241
EXAMPLES:
242
sage: F = ZZ['x,y,z'].construction()[0]
243
sage: G = QQ['x,y,z'].construction()[0]
244
sage: F == G
245
True
246
sage: G = ZZ['x,y'].construction()[0]
247
sage: F == G
248
False
249
"""
250
c = cmp(type(self), type(other))
251
if c == 0:
252
c = cmp(self.vars, other.vars) or cmp(self.term_order, other.term_order)
253
elif isinstance(other, PolynomialFunctor):
254
c = cmp(self.vars, (other.var,))
255
return c
256
257
def __mul__(self, other):
258
"""
259
If two MPoly functors are given in a row, form a single MPoly functor
260
with all of the variables.
261
262
EXAMPLES:
263
sage: F = sage.categories.pushout.MultiPolynomialFunctor(['x','y'], None)
264
sage: G = sage.categories.pushout.MultiPolynomialFunctor(['t'], None)
265
sage: G*F
266
MPoly[x,y,t]
267
"""
268
if isinstance(other, MultiPolynomialFunctor):
269
if self.term_order != other.term_order:
270
raise CoercionException, "Incompatible term orders (%s,%s)." % (self.term_order, other.term_order)
271
if set(self.vars).intersection(other.vars):
272
raise CoercionException, "Overlapping variables (%s,%s)" % (self.vars, other.vars)
273
return MultiPolynomialFunctor(other.vars + self.vars, self.term_order)
274
elif isinstance(other, CompositConstructionFunctor) \
275
and isinstance(other.all[-1], MultiPolynomialFunctor):
276
return CompositConstructionFunctor(other.all[:-1], self * other.all[-1])
277
else:
278
return CompositConstructionFunctor(other, self)
279
280
def merge(self, other):
281
"""
282
EXAMPLES:
283
sage: F = sage.categories.pushout.MultiPolynomialFunctor(['x','y'], None)
284
sage: G = sage.categories.pushout.MultiPolynomialFunctor(['t'], None)
285
sage: F.merge(G) is None
286
True
287
sage: F.merge(F)
288
MPoly[x,y]
289
"""
290
if self == other:
291
return self
292
else:
293
return None
294
295
def expand(self):
296
"""
297
EXAMPLES:
298
sage: F = QQ['x,y,z,t'].construction()[0]; F
299
MPoly[x,y,z,t]
300
sage: F.expand()
301
[MPoly[t], MPoly[z], MPoly[y], MPoly[x]]
302
303
Now an actual use case:
304
sage: R.<x,y,z> = ZZ[]
305
sage: S.<z,t> = QQ[]
306
sage: x+t
307
x + t
308
sage: parent(x+t)
309
Multivariate Polynomial Ring in x, y, z, t over Rational Field
310
sage: T.<y,s> = QQ[]
311
sage: x + s
312
Traceback (most recent call last):
313
...
314
TypeError: unsupported operand parent(s) for '+': 'Multivariate Polynomial Ring in x, y, z over Integer Ring' and 'Multivariate Polynomial Ring in y, s over Rational Field'
315
sage: R = PolynomialRing(ZZ, 'x', 500)
316
sage: S = PolynomialRing(GF(5), 'x', 200)
317
sage: R.gen(0) + S.gen(0)
318
2*x0
319
"""
320
if len(self.vars) <= 1:
321
return [self]
322
else:
323
return [MultiPolynomialFunctor((x,), self.term_order) for x in reversed(self.vars)]
324
325
def __str__(self):
326
"""
327
EXAMPLES:
328
sage: QQ['x,y,z,t'].construction()[0]
329
MPoly[x,y,z,t]
330
"""
331
return "MPoly[%s]" % ','.join(self.vars)
332
333
334
335
class InfinitePolynomialFunctor(ConstructionFunctor):
336
"""
337
A Construction Functor for Infinite Polynomial Rings (see :mod:`~sage.rings.polynomial.infinite_polynomial_ring`)
338
339
AUTHOR:
340
-- Simon King
341
342
This construction functor is used to provide uniqueness of infinite polynomial rings as parent structures.
343
As usual, the construction functor allows for constructing pushouts.
344
345
Another purpose is to avoid name conflicts of variables of the to-be-constructed infinite polynomial ring with
346
variables of the base ring, and moreover to keep the internal structure of an Infinite Polynomial Ring as simple
347
as possible: If variables `v_1,...,v_n` of the given base ring generate an *ordered* sub-monoid of the monomials
348
of the ambient Infinite Polynomial Ring, then they are removed from the base ring and merged with the generators
349
of the ambient ring. However, if the orders don't match, an error is raised, since there was a name conflict
350
without merging.
351
352
EXAMPLES::
353
354
sage: A.<a,b> = InfinitePolynomialRing(ZZ['t'])
355
sage: A.construction()
356
[InfPoly{[a,b], "lex", "dense"},
357
Univariate Polynomial Ring in t over Integer Ring]
358
sage: type(_[0])
359
<class 'sage.categories.pushout.InfinitePolynomialFunctor'>
360
sage: B.<x,y,a_3,a_1> = PolynomialRing(QQ, order='lex')
361
sage: B.construction()
362
(MPoly[x,y,a_3,a_1], Rational Field)
363
sage: A.construction()[0]*B.construction()[0]
364
InfPoly{[a,b], "lex", "dense"}(MPoly[x,y](...))
365
366
Apparently the variables `a_1,a_3` of the polynomial ring are merged with the variables
367
`a_0, a_1, a_2, ...` of the infinite polynomial ring; indeed, they form an ordered sub-structure.
368
However, if the polynomial ring was given a different ordering, merging would not be allowed,
369
resulting in a name conflict::
370
371
sage: A.construction()[0]*PolynomialRing(QQ,names=['x','y','a_3','a_1']).construction()[0]
372
Traceback (most recent call last):
373
...
374
CoercionException: Incompatible term orders lex, degrevlex
375
376
In an infinite polynomial ring with generator `a_\\ast`, the variable `a_3` will always be greater
377
than the variable `a_1`. Hence, the orders are incompatible in the next example as well::
378
379
sage: A.construction()[0]*PolynomialRing(QQ,names=['x','y','a_1','a_3'], order='lex').construction()[0]
380
Traceback (most recent call last):
381
...
382
CoercionException: Overlapping variables (('a', 'b'),['a_1', 'a_3']) are incompatible
383
384
Another requirement is that after merging the order of the remaining variables must be unique.
385
This is not the case in the following example, since it is not clear whether the variables `x,y`
386
should be greater or smaller than the variables `b_\\ast`::
387
388
sage: A.construction()[0]*PolynomialRing(QQ,names=['a_3','a_1','x','y'], order='lex').construction()[0]
389
Traceback (most recent call last):
390
...
391
CoercionException: Overlapping variables (('a', 'b'),['a_3', 'a_1']) are incompatible
392
393
Since the construction functors are actually used to construct infinite polynomial rings, the following
394
result is no surprise:
395
396
sage: C.<a,b> = InfinitePolynomialRing(B); C
397
Infinite polynomial ring in a, b over Multivariate Polynomial Ring in x, y over Rational Field
398
399
There is also an overlap in the next example::
400
401
sage: X.<w,x,y> = InfinitePolynomialRing(ZZ)
402
sage: Y.<x,y,z> = InfinitePolynomialRing(QQ)
403
404
`X` and `Y` have an overlapping generators `x_\\ast, y_\\ast`. Since the default lexicographic order is
405
used in both rings, it gives rise to isomorphic sub-monoids in both `X` and `Y`. They are merged in the
406
pushout, which also yields a common parent for doing arithmetic.
407
408
sage: P = sage.categories.pushout.pushout(Y,X); P
409
Infinite polynomial ring in w, x, y, z over Rational Field
410
sage: w[2]+z[3]
411
w_2 + z_3
412
sage: _.parent() is P
413
True
414
415
"""
416
417
# We do provide merging with polynomial rings. However, it seems that it is better
418
# to have a greater rank, since we want to apply InfinitePolynomialFunctor *after*
419
# [Multi]PolynomialFunktor, which have rank 9. But there is the MatrixFunctor, which
420
# has rank 10. So, do fine tuning...
421
rank = 9.5
422
423
def __init__(self, gens, order, implementation):
424
"""
425
TEST::
426
427
sage: F = sage.categories.pushout.InfinitePolynomialFunctor(['a','b','x'],'degrevlex','sparse'); F # indirect doc test
428
InfPoly{[a,b,x], "degrevlex", "sparse"}
429
sage: F == loads(dumps(F))
430
True
431
432
"""
433
if len(gens)<1:
434
raise ValueError, "Infinite Polynomial Rings have at least one generator"
435
ConstructionFunctor.__init__(self, Rings(), Rings())
436
self._gens = tuple(gens)
437
self._order = order
438
self._imple = implementation
439
440
def __call__(self, R):
441
"""
442
TEST::
443
444
sage: F = sage.categories.pushout.InfinitePolynomialFunctor(['a','b','x'],'degrevlex','sparse'); F
445
InfPoly{[a,b,x], "degrevlex", "sparse"}
446
sage: F(QQ['t']) # indirect doc test
447
Infinite polynomial ring in a, b, x over Univariate Polynomial Ring in t over Rational Field
448
449
"""
450
from sage.rings.polynomial.infinite_polynomial_ring import InfinitePolynomialRing
451
return InfinitePolynomialRing(R, self._gens, order=self._order, implementation=self._imple)
452
453
def __str__(self):
454
"""
455
TEST::
456
457
sage: F = sage.categories.pushout.InfinitePolynomialFunctor(['a','b','x'],'degrevlex','sparse'); F # indirect doc test
458
InfPoly{[a,b,x], "degrevlex", "sparse"}
459
460
"""
461
return 'InfPoly{[%s], "%s", "%s"}'%(','.join(self._gens), self._order, self._imple)
462
463
def __cmp__(self, other):
464
"""
465
TEST::
466
467
sage: F = sage.categories.pushout.InfinitePolynomialFunctor(['a','b','x'],'degrevlex','sparse'); F # indirect doc test
468
InfPoly{[a,b,x], "degrevlex", "sparse"}
469
sage: F == loads(dumps(F)) # indirect doc test
470
True
471
sage: F == sage.categories.pushout.InfinitePolynomialFunctor(['a','b','x'],'deglex','sparse')
472
False
473
474
"""
475
c = cmp(type(self), type(other))
476
if c == 0:
477
c = cmp(self._gens, other._gens) or cmp(self._order, other._order) or cmp(self._imple, other._imple)
478
return c
479
480
def __mul__(self, other):
481
"""
482
TESTS::
483
484
sage: F1 = QQ['a','x_2','x_1','y_3','y_2'].construction()[0]; F1
485
MPoly[a,x_2,x_1,y_3,y_2]
486
sage: F2 = InfinitePolynomialRing(QQ, ['x','y'],order='degrevlex').construction()[0]; F2
487
InfPoly{[x,y], "degrevlex", "dense"}
488
sage: F3 = InfinitePolynomialRing(QQ, ['x','y'],order='degrevlex',implementation='sparse').construction()[0]; F3
489
InfPoly{[x,y], "degrevlex", "sparse"}
490
sage: F2*F1
491
InfPoly{[x,y], "degrevlex", "dense"}(Poly[a](...))
492
sage: F3*F1
493
InfPoly{[x,y], "degrevlex", "sparse"}(Poly[a](...))
494
sage: F4 = sage.categories.pushout.FractionField()
495
sage: F2*F4
496
InfPoly{[x,y], "degrevlex", "dense"}(FractionField(...))
497
498
"""
499
if isinstance(other, self.__class__): #
500
INT = set(self._gens).intersection(other._gens)
501
if INT:
502
# if there is overlap of generators, it must only be at the ends, so that
503
# the resulting order after the merging is unique
504
if other._gens[-len(INT):] != self._gens[:len(INT)]:
505
raise CoercionException, "Overlapping variables (%s,%s) are incompatible" % (self._gens, other._gens)
506
OUTGENS = list(other._gens) + list(self._gens[len(INT):])
507
else:
508
OUTGENS = list(other._gens) + list(self._gens)
509
# the orders must coincide
510
if self._order != other._order:
511
return CompositConstructionFunctor(other, self)
512
# the implementations must coincide
513
if self._imple != other._imple:
514
return CompositConstructionFunctor(other, self)
515
return InfinitePolynomialFunctor(OUTGENS, self._order, self._imple)
516
517
# Polynomial Constructor
518
# Idea: We merge into self, if the polynomial functor really provides a substructure,
519
# even respecting the order. Note that, if the pushout is computed, only *one* variable
520
# will occur in the polynomial constructor. Hence, any order is fine, which is exactly
521
# what we need in order to have coercion maps for different orderings.
522
if isinstance(other, MultiPolynomialFunctor) or isinstance(other, PolynomialFunctor):
523
if isinstance(other, MultiPolynomialFunctor):
524
othervars = other.vars
525
else:
526
othervars = [other.var]
527
OverlappingGens = [] ## Generator names of variable names of the MultiPolynomialFunctor
528
## that can be interpreted as variables in self
529
OverlappingVars = [] ## The variable names of the MultiPolynomialFunctor
530
## that can be interpreted as variables in self
531
RemainingVars = [x for x in othervars]
532
IsOverlap = False
533
BadOverlap = False
534
for x in othervars:
535
if x.count('_') == 1:
536
g,n = x.split('_')
537
if n.isdigit():
538
if g.isalnum(): # we can interprete x in any InfinitePolynomialRing
539
if g in self._gens: # we can interprete x in self, hence, we will not use it as a variable anymore.
540
RemainingVars.pop(RemainingVars.index(x))
541
IsOverlap = True # some variables of other can be interpreted in self.
542
if OverlappingVars:
543
# Is OverlappingVars in the right order?
544
g0,n0 = OverlappingVars[-1].split('_')
545
i = self._gens.index(g)
546
i0 = self._gens.index(g0)
547
if i<i0: # wrong order
548
BadOverlap = True
549
if i==i0 and int(n)>int(n0): # wrong order
550
BadOverlap = True
551
OverlappingVars.append(x)
552
else:
553
if IsOverlap: # The overlap must be on the right end of the variable list
554
BadOverlap = True
555
else:
556
if IsOverlap: # The overlap must be on the right end of the variable list
557
BadOverlap = True
558
else:
559
if IsOverlap: # The overlap must be on the right end of the variable list
560
BadOverlap = True
561
else:
562
if IsOverlap: # The overlap must be on the right end of the variable list
563
BadOverlap = True
564
565
if BadOverlap: # the overlapping variables appear in the wrong order
566
raise CoercionException, "Overlapping variables (%s,%s) are incompatible" % (self._gens, OverlappingVars)
567
if len(OverlappingVars)>1: # multivariate, hence, the term order matters
568
if other.term_order.name()!=self._order:
569
raise CoercionException, "Incompatible term orders %s, %s" % (self._order, other.term_order.name())
570
# ok, the overlap is fine, we will return something.
571
if RemainingVars: # we can only partially merge other into self
572
if len(RemainingVars)>1:
573
return CompositConstructionFunctor(MultiPolynomialFunctor(RemainingVars,term_order=other.term_order), self)
574
return CompositConstructionFunctor(PolynomialFunctor(RemainingVars[0]), self)
575
return self
576
return CompositConstructionFunctor(other, self)
577
578
def merge(self,other):
579
"""
580
Merge two construction functors of infinite polynomial rings, regardless of monomial order and implementation.
581
582
The purpose is to have a pushout (and thus, arithmetic) even in cases when the parents are isomorphic as
583
rings, but not as ordered rings.
584
585
EXAMPLES::
586
587
sage: X.<x,y> = InfinitePolynomialRing(QQ,implementation='sparse')
588
sage: Y.<x,y> = InfinitePolynomialRing(QQ,order='degrevlex')
589
sage: X.construction()
590
[InfPoly{[x,y], "lex", "sparse"}, Rational Field]
591
sage: Y.construction()
592
[InfPoly{[x,y], "degrevlex", "dense"}, Rational Field]
593
sage: Y.construction()[0].merge(Y.construction()[0])
594
InfPoly{[x,y], "degrevlex", "dense"}
595
sage: y[3] + X(x[2])
596
x_2 + y_3
597
sage: _.parent().construction()
598
[InfPoly{[x,y], "degrevlex", "dense"}, Rational Field]
599
600
"""
601
# Merging is only done if the ranks of self and other are the same.
602
# It may happen that other is a substructure of self up to the monomial order
603
# and the implementation. And this is when we want to merge, in order to
604
# provide multiplication for rings with different term orderings.
605
if not isinstance(other, InfinitePolynomialFunctor):
606
return None
607
if set(other._gens).issubset(self._gens):
608
return self
609
return None
610
try:
611
OUT = self*other
612
# The following happens if "other" has the same order type etc.
613
if not isinstance(OUT, CompositConstructionFunctor):
614
return OUT
615
except CoercionException:
616
pass
617
if isinstance(other,InfinitePolynomialFunctor):
618
# We don't require that the orders coincide. This is a difference to self*other
619
# We only merge if other's generators are an ordered subset of self's generators
620
for g in other._gens:
621
if g not in self._gens:
622
return None
623
# The sequence of variables is part of the ordering. It must coincide in both rings
624
Ind = [self._gens.index(g) for g in other._gens]
625
if sorted(Ind)!=Ind:
626
return None
627
# OK, other merges into self. Now, chose the default dense implementation,
628
# unless both functors refer to the sparse implementation
629
if self._imple != other._imple:
630
return InfinitePolynomialFunctor(self._gens, self._order, 'dense')
631
return self
632
## if isinstance(other, PolynomialFunctor) or isinstance(other, MultiPolynomialFunctor):
633
## # For merging, we don't care about the orders
634
## ## if isinstance(other, MultiPolynomialFunctor) and self._order!=other.term_order.name():
635
## ## return None
636
## # We only merge if other's variables can all be interpreted in self.
637
## if isinstance(other, PolynomialFunctor):
638
## if other.var.count('_')!=1:
639
## return None
640
## g,n = other.var.split('_')
641
## if not ((g in self._gens) and n.isdigit()):
642
## return None
643
## # other merges into self!
644
## return self
645
## # Now, other is MultiPolynomial
646
## for v in other.vars:
647
## if v.count('_')!=1:
648
## return None
649
## g,n = v.split('_')
650
## if not ((g in self._gens) and n.isdigit()):
651
## return None
652
## # other merges into self!
653
## return self
654
return None
655
656
def expand(self):
657
"""
658
Decompose the functor `F` into sub-functors, whose product returns `F`
659
660
EXAMPLES::
661
662
sage: F = InfinitePolynomialRing(QQ, ['x','y'],order='degrevlex').construction()[0]; F
663
InfPoly{[x,y], "degrevlex", "dense"}
664
sage: F.expand()
665
[InfPoly{[y], "degrevlex", "dense"}, InfPoly{[x], "degrevlex", "dense"}]
666
sage: F = InfinitePolynomialRing(QQ, ['x','y','z'],order='degrevlex').construction()[0]; F
667
InfPoly{[x,y,z], "degrevlex", "dense"}
668
sage: F.expand()
669
[InfPoly{[z], "degrevlex", "dense"},
670
InfPoly{[y], "degrevlex", "dense"},
671
InfPoly{[x], "degrevlex", "dense"}]
672
sage: prod(F.expand())==F
673
True
674
675
"""
676
if len(self._gens)==1:
677
return [self]
678
return [InfinitePolynomialFunctor((x,), self._order, self._imple) for x in reversed(self._gens)]
679
680
681
class SiegelModularFormsAlgebraFunctor(ConstructionFunctor):
682
# this is needed to make the coercion system happy
683
# DO NOT REMOVE
684
rank = 9
685
686
def __init__(self, group, weights, degree, default_prec):
687
r"""
688
Initialize the functor.
689
690
EXAMPLES::
691
692
sage: from sage.categories.pushout import SiegelModularFormsAlgebraFunctor
693
sage: F = SiegelModularFormsAlgebraFunctor('Sp(4,Z)', 'even', 2, 101)
694
sage: F.rank
695
9
696
"""
697
from sage.categories.all import Rings
698
Functor.__init__(self, Rings(), Rings())
699
self._group = group
700
self._weights = weights
701
self._degree = degree
702
self._default_prec = default_prec
703
704
def __call__(self, R):
705
r"""
706
Apply the functor ``self`` to the ring ``R``.
707
708
EXAMPLES::
709
710
sage: from sage.categories.pushout import SiegelModularFormsAlgebraFunctor
711
sage: F = SiegelModularFormsAlgebraFunctor('Sp(4,Z)', 'all', 2, 41)
712
sage: F(QQ)
713
Algebra of Siegel modular forms of degree 2 and all weights on Sp(4,Z) over Rational Field
714
715
TESTS::
716
717
sage: F(3)
718
Traceback (most recent call last):
719
...
720
TypeError: The coefficient ring must be a ring
721
"""
722
from sage.modular.siegel.all import SiegelModularFormsAlgebra
723
return SiegelModularFormsAlgebra(coeff_ring=R, group=self._group, weights=self._weights, degree=self._degree, default_prec=self._default_prec)
724
725
def __cmp__(self, other):
726
r"""
727
Compare the functor ``self`` and the object ``other``.
728
729
EXAMPLES::
730
731
sage: from sage.categories.pushout import SiegelModularFormsAlgebraFunctor
732
sage: F = SiegelModularFormsAlgebraFunctor('Sp(4,Z)', 'even', 2, 101)
733
sage: G = SiegelModularFormsAlgebraFunctor('Sp(4,Z)', 'even', 2, 101)
734
sage: F == G
735
True
736
sage: F == SiegelModularFormsAlgebraFunctor('Sp(4,Z)', 'all', 2, 41)
737
False
738
sage: F == SiegelModularFormsAlgebraFunctor('Sp(4,Z)', 'even', 2, 61)
739
True
740
"""
741
c = cmp(type(self), type(other))
742
if c == 0:
743
# default precision is an implementation detail, not a
744
# mathematical property, so it is ignored in comparison
745
c = cmp(self._group, other._group) or cmp(self._weights, other._weights) or cmp(self._degree, other._degree)
746
return c
747
748
def merge(self, other):
749
r"""
750
Merge the two functors ``self`` and ``other``.
751
752
EXAMPLES::
753
754
sage: from sage.categories.pushout import SiegelModularFormsAlgebraFunctor
755
sage: F = SiegelModularFormsAlgebraFunctor('Sp(4,Z)', 'even', 2, 101)
756
sage: G = SiegelModularFormsAlgebraFunctor('Gamma0(2)', 'all', 2, 61)
757
sage: F == G
758
False
759
sage: F.merge(G) is None
760
True
761
sage: G = SiegelModularFormsAlgebraFunctor('Sp(4,Z)', 'even', 2, 61)
762
sage: F.merge(G) is G
763
True
764
"""
765
if not isinstance(other, SiegelModularFormsAlgebraFunctor):
766
return None
767
if (self._group == other._group) and (self._weights == other._weights) and (self._degree == other._degree):
768
if self._default_prec <= other._default_prec:
769
return self
770
else: # self._default_prec > other._default_prec
771
return other
772
else:
773
return None
774
775
def __str__(self):
776
r"""
777
Return a string describing the functor ``self``.
778
779
EXAMPLES::
780
781
sage: from sage.categories.pushout import SiegelModularFormsAlgebraFunctor
782
sage: F = SiegelModularFormsAlgebraFunctor('Gamma0(4)', 'even', 2, 41)
783
sage: F
784
SMFAlg{"Gamma0(4)", "even", 2, 41}
785
"""
786
return 'SMFAlg{"%s", "%s", %s, %s}' %(self._group, self._weights, self._degree, self._default_prec)
787
788
789
class MatrixFunctor(ConstructionFunctor):
790
791
rank = 10
792
793
def __init__(self, nrows, ncols, is_sparse=False):
794
# if nrows == ncols:
795
# Functor.__init__(self, Rings(), RingModules()) # takes a base ring
796
# else:
797
# Functor.__init__(self, Rings(), MatrixAlgebras()) # takes a base ring
798
Functor.__init__(self, Rings(), Rings())
799
self.nrows = nrows
800
self.ncols = ncols
801
self.is_sparse = is_sparse
802
def __call__(self, R):
803
from sage.matrix.matrix_space import MatrixSpace
804
return MatrixSpace(R, self.nrows, self.ncols, sparse=self.is_sparse)
805
def __cmp__(self, other):
806
c = cmp(type(self), type(other))
807
if c == 0:
808
c = cmp((self.nrows, self.ncols), (other.nrows, other.ncols))
809
return c
810
def merge(self, other):
811
if self != other:
812
return None
813
else:
814
return MatrixFunctor(self.nrows, self.ncols, self.is_sparse and other.is_sparse)
815
816
class LaurentPolynomialFunctor(ConstructionFunctor):
817
818
rank = 9
819
820
def __init__(self, var, multi_variate=False):
821
Functor.__init__(self, Rings(), Rings())
822
self.var = var
823
self.multi_variate = multi_variate
824
def __call__(self, R):
825
from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing, is_LaurentPolynomialRing
826
if self.multi_variate and is_LaurentPolynomialRing(R):
827
return LaurentPolynomialRing(R.base_ring(), (list(R.variable_names()) + [self.var]))
828
else:
829
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
830
return PolynomialRing(R, self.var)
831
def __cmp__(self, other):
832
c = cmp(type(self), type(other))
833
if c == 0:
834
c = cmp(self.var, other.var)
835
return c
836
def merge(self, other):
837
if self == other or isinstance(other, PolynomialFunctor) and self.var == other.var:
838
return LaurentPolynomialFunctor(self.var, (self.multi_variate or other.multi_variate))
839
else:
840
return None
841
842
843
class VectorFunctor(ConstructionFunctor):
844
845
rank = 10 # ranking of functor, not rank of module
846
847
def __init__(self, n, is_sparse=False, inner_product_matrix=None):
848
# Functor.__init__(self, Rings(), FreeModules()) # takes a base ring
849
Functor.__init__(self, Objects(), Objects())
850
self.n = n
851
self.is_sparse = is_sparse
852
self.inner_product_matrix = inner_product_matrix
853
def __call__(self, R):
854
from sage.modules.free_module import FreeModule
855
return FreeModule(R, self.n, sparse=self.is_sparse, inner_product_matrix=self.inner_product_matrix)
856
def __cmp__(self, other):
857
c = cmp(type(self), type(other))
858
if c == 0:
859
c = cmp(self.n, other.n)
860
return c
861
def merge(self, other):
862
if self != other:
863
return None
864
else:
865
return VectorFunctor(self.n, self.is_sparse and other.is_sparse)
866
867
868
class SubspaceFunctor(ConstructionFunctor):
869
rank = 11 # ranking of functor, not rank of module
870
def __init__(self, basis):
871
# Functor.__init__(self, FreeModules(), FreeModules()) # takes a base ring
872
Functor.__init__(self, Objects(), Objects())
873
self.basis = basis
874
def __call__(self, ambient):
875
return ambient.span_of_basis(self.basis)
876
def __cmp__(self, other):
877
c = cmp(type(self), type(other))
878
if c == 0:
879
c = cmp(self.basis, other.basis)
880
return c
881
def merge(self, other):
882
if isinstance(other, SubspaceFunctor):
883
return SubspaceFunctor(self.basis + other.basis) # TODO: remove linear dependencies
884
else:
885
return None
886
887
888
class FractionField(ConstructionFunctor):
889
890
rank = 5
891
892
def __init__(self):
893
Functor.__init__(self, Rings(), Fields())
894
def __call__(self, R):
895
return R.fraction_field()
896
897
898
class LocalizationFunctor(ConstructionFunctor):
899
900
rank = 6
901
902
def __init__(self, t):
903
Functor.__init__(self, Rings(), Rings())
904
self.t = t
905
def __call__(self, R):
906
return R.localize(t)
907
def __cmp__(self, other):
908
c = cmp(type(self), type(other))
909
if c == 0:
910
c = cmp(self.t, other.t)
911
return c
912
913
914
class CompletionFunctor(ConstructionFunctor):
915
916
rank = 4
917
918
def __init__(self, p, prec, extras=None):
919
Functor.__init__(self, Rings(), Rings())
920
self.p = p
921
self.prec = prec
922
self.extras = extras
923
def __call__(self, R):
924
return R.completion(self.p, self.prec, self.extras)
925
def __cmp__(self, other):
926
c = cmp(type(self), type(other))
927
if c == 0:
928
c = cmp(self.p, other.p)
929
return c
930
def merge(self, other):
931
if self.p == other.p:
932
if self.prec == other.prec:
933
extras = self.extras.copy()
934
extras.update(other.extras)
935
return CompletionFunctor(self.p, self.prec, extras)
936
elif self.prec < other.prec:
937
return self
938
else: # self.prec > other.prec
939
return other
940
else:
941
return None
942
943
944
class QuotientFunctor(ConstructionFunctor):
945
946
rank = 7
947
948
def __init__(self, I, as_field=False):
949
Functor.__init__(self, Rings(), Rings()) # much more general...
950
self.I = I
951
self.as_field = as_field
952
def __call__(self, R):
953
I = self.I
954
if I.ring() != R:
955
I.base_extend(R)
956
Q = R.quo(I)
957
if self.as_field and hasattr(Q, 'field'):
958
Q = Q.field()
959
return Q
960
def __cmp__(self, other):
961
c = cmp(type(self), type(other))
962
if c == 0:
963
c = cmp(self.I, other.I)
964
return c
965
def merge(self, other):
966
if self == other:
967
return self
968
try:
969
gcd = self.I + other.I
970
except (TypeError, NotImplementedError):
971
return None
972
if gcd.is_trivial() and not gcd.is_zero():
973
# quotient by gcd would result in the trivial ring/group/...
974
# Rather than create the zero ring, we claim they can't be merged
975
# TODO: Perhaps this should be detected at a higher level...
976
raise TypeError, "Trivial quotient intersection."
977
return QuotientFunctor(gcd)
978
979
class AlgebraicExtensionFunctor(ConstructionFunctor):
980
981
rank = 3
982
983
def __init__(self, poly, name, elt=None, embedding=None):
984
Functor.__init__(self, Rings(), Rings())
985
self.poly = poly
986
self.name = name
987
self.elt = elt
988
self.embedding = embedding
989
def __call__(self, R):
990
return R.extension(self.poly, self.name, embedding=self.embedding)
991
def __cmp__(self, other):
992
c = cmp(type(self), type(other))
993
if c == 0:
994
c = cmp(self.poly, other.poly)
995
if c == 0:
996
c = cmp(self.embedding, other.embedding)
997
return c
998
999
class AlgebraicClosureFunctor(ConstructionFunctor):
1000
1001
rank = 3
1002
1003
def __init__(self):
1004
Functor.__init__(self, Rings(), Rings())
1005
def __call__(self, R):
1006
return R.algebraic_closure()
1007
def merge(self, other):
1008
# Algebraic Closure subsumes Algebraic Extension
1009
return self
1010
1011
class PermutationGroupFunctor(ConstructionFunctor):
1012
1013
rank = 10
1014
1015
def __init__(self, gens):
1016
"""
1017
EXAMPLES::
1018
1019
sage: from sage.categories.pushout import PermutationGroupFunctor
1020
sage: PF = PermutationGroupFunctor([PermutationGroupElement([(1,2)])]); PF
1021
PermutationGroupFunctor[(1,2)]
1022
"""
1023
Functor.__init__(self, Groups(), Groups())
1024
self._gens = gens
1025
1026
def __repr__(self):
1027
"""
1028
EXAMPLES::
1029
1030
sage: P1 = PermutationGroup([[(1,2)]])
1031
sage: PF, P = P1.construction()
1032
sage: PF
1033
PermutationGroupFunctor[(1,2)]
1034
"""
1035
return "PermutationGroupFunctor%s"%self.gens()
1036
1037
def __call__(self, R):
1038
"""
1039
EXAMPLES::
1040
1041
sage: P1 = PermutationGroup([[(1,2)]])
1042
sage: PF, P = P1.construction()
1043
sage: PF(P)
1044
Permutation Group with generators [(1,2)]
1045
"""
1046
from sage.groups.perm_gps.permgroup import PermutationGroup
1047
return PermutationGroup([g for g in (R.gens() + self.gens()) if not g.is_one()])
1048
1049
def gens(self):
1050
"""
1051
EXAMPLES::
1052
1053
sage: P1 = PermutationGroup([[(1,2)]])
1054
sage: PF, P = P1.construction()
1055
sage: PF.gens()
1056
[(1,2)]
1057
"""
1058
return self._gens
1059
1060
def merge(self, other):
1061
"""
1062
EXAMPLES::
1063
1064
sage: P1 = PermutationGroup([[(1,2)]])
1065
sage: PF1, P = P1.construction()
1066
sage: P2 = PermutationGroup([[(1,3)]])
1067
sage: PF2, P = P2.construction()
1068
sage: PF1.merge(PF2)
1069
PermutationGroupFunctor[(1,2), (1,3)]
1070
"""
1071
if self.__class__ != other.__class__:
1072
return None
1073
return PermutationGroupFunctor(self.gens() + other.gens())
1074
1075
def BlackBoxConstructionFunctor(ConstructionFunctor):
1076
1077
rank = 100
1078
1079
def __init__(self, box):
1080
if not callable(box):
1081
raise TypeError, "input must be callable"
1082
self.box = box
1083
def __call__(self, R):
1084
return box(R)
1085
def __cmp__(self, other):
1086
return self.box == other.box
1087
1088
1089
def pushout(R, S):
1090
"""
1091
Given a pair of Objects R and S, try and construct a
1092
reasonable object $Y$ and return maps such that
1093
canonically $R \leftarrow Y \rightarrow S$.
1094
1095
ALGORITHM:
1096
This incorporates the idea of functors discussed Sage Days 4.
1097
Every object $R$ can be viewed as an initial object and
1098
a series of functors (e.g. polynomial, quotient, extension,
1099
completion, vector/matrix, etc.). Call the series of
1100
increasingly-simple rings (with the associated functors)
1101
the "tower" of $R$. The \code{construction} method is used to
1102
create the tower.
1103
1104
Given two objects $R$ and $S$, try and find a common initial
1105
object $Z$. If the towers of $R$ and $S$ meet, let $Z$ be their
1106
join. Otherwise, see if the top of one coerces naturally into
1107
the other.
1108
1109
Now we have an initial object and two \emph{ordered} lists of
1110
functors to apply. We wish to merge these in an unambiguous order,
1111
popping elements off the top of one or the other tower as we
1112
apply them to $Z$.
1113
1114
- If the functors are distinct types, there is an absolute ordering
1115
given by the rank attribute. Use this.
1116
- Otherwise:
1117
- If the tops are equal, we (try to) merge them.
1118
- If \emph{exactly} one occurs lower in the other tower
1119
we may unambiguously apply the other (hoping for a later merge).
1120
- If the tops commute, we can apply either first.
1121
- Otherwise fail due to ambiguity.
1122
1123
EXAMPLES:
1124
Here our "towers" are $R = Complete_7(Frac(\Z)$ and $Frac(Poly_x(\Z))$, which give us $Frac(Poly_x(Complete_7(Frac(\Z)))$
1125
sage: from sage.categories.pushout import pushout
1126
sage: pushout(Qp(7), Frac(ZZ['x']))
1127
Fraction Field of Univariate Polynomial Ring in x over 7-adic Field with capped relative precision 20
1128
1129
Note we get the same thing with
1130
sage: pushout(Zp(7), Frac(QQ['x']))
1131
Fraction Field of Univariate Polynomial Ring in x over 7-adic Field with capped relative precision 20
1132
sage: pushout(Zp(7)['x'], Frac(QQ['x']))
1133
Fraction Field of Univariate Polynomial Ring in x over 7-adic Field with capped relative precision 20
1134
1135
Note that polynomial variable ordering must be unambiguously determined.
1136
sage: pushout(ZZ['x,y,z'], QQ['w,z,t'])
1137
Traceback (most recent call last):
1138
...
1139
CoercionException: ('Ambiguous Base Extension', Multivariate Polynomial Ring in x, y, z over Integer Ring, Multivariate Polynomial Ring in w, z, t over Rational Field)
1140
sage: pushout(ZZ['x,y,z'], QQ['w,x,z,t'])
1141
Multivariate Polynomial Ring in w, x, y, z, t over Rational Field
1142
1143
Some other examples
1144
sage: pushout(Zp(7)['y'], Frac(QQ['t'])['x,y,z'])
1145
Multivariate Polynomial Ring in x, y, z over Fraction Field of Univariate Polynomial Ring in t over 7-adic Field with capped relative precision 20
1146
sage: pushout(ZZ['x,y,z'], Frac(ZZ['x'])['y'])
1147
Multivariate Polynomial Ring in y, z over Fraction Field of Univariate Polynomial Ring in x over Integer Ring
1148
sage: pushout(MatrixSpace(RDF, 2, 2), Frac(ZZ['x']))
1149
Full MatrixSpace of 2 by 2 dense matrices over Fraction Field of Univariate Polynomial Ring in x over Real Double Field
1150
sage: pushout(ZZ, MatrixSpace(ZZ[['x']], 3, 3))
1151
Full MatrixSpace of 3 by 3 dense matrices over Power Series Ring in x over Integer Ring
1152
sage: pushout(QQ['x,y'], ZZ[['x']])
1153
Univariate Polynomial Ring in y over Power Series Ring in x over Rational Field
1154
sage: pushout(Frac(ZZ['x']), QQ[['x']])
1155
Laurent Series Ring in x over Rational Field
1156
1157
AUTHORS:
1158
-- Robert Bradshaw
1159
"""
1160
if R is S or R == S:
1161
return R
1162
1163
if isinstance(R, type):
1164
R = type_to_parent(R)
1165
1166
if isinstance(S, type):
1167
S = type_to_parent(S)
1168
1169
R_tower = construction_tower(R)
1170
S_tower = construction_tower(S)
1171
Rs = [c[1] for c in R_tower]
1172
Ss = [c[1] for c in S_tower]
1173
1174
if R in Ss:
1175
return S
1176
elif S in Rs:
1177
return R
1178
1179
if R_tower[-1][1] in Ss:
1180
Rs, Ss = Ss, Rs
1181
R_tower, S_tower = S_tower, R_tower
1182
1183
# look for join
1184
if Ss[-1] in Rs:
1185
if Rs[-1] == Ss[-1]:
1186
while Rs and Ss and Rs[-1] == Ss[-1]:
1187
Rs.pop()
1188
Z = Ss.pop()
1189
else:
1190
Rs = Rs[:Rs.index(Ss[-1])]
1191
Z = Ss.pop()
1192
1193
# look for topmost coercion
1194
elif S.has_coerce_map_from(Rs[-1]):
1195
while not Ss[-1].has_coerce_map_from(Rs[-1]):
1196
Ss.pop()
1197
while len(Rs) > 0 and Ss[-1].has_coerce_map_from(Rs[-1]):
1198
Rs.pop()
1199
Z = Ss.pop()
1200
1201
elif R.has_coerce_map_from(Ss[-1]):
1202
while not Rs[-1].has_coerce_map_from(Ss[-1]):
1203
Rs.pop()
1204
while len(Ss) > 0 and Rs[-1].has_coerce_map_from(Ss[-1]):
1205
Ss.pop()
1206
Z = Rs.pop()
1207
1208
else:
1209
raise CoercionException, "No common base"
1210
1211
# Rc is a list of functors from Z to R and Sc is a list of functors from Z to S
1212
Rc = [c[0] for c in R_tower[1:len(Rs)+1]]
1213
Sc = [c[0] for c in S_tower[1:len(Ss)+1]]
1214
1215
Rc = sum([c.expand() for c in Rc], [])
1216
Sc = sum([c.expand() for c in Sc], [])
1217
1218
all = IdentityConstructionFunctor()
1219
1220
try:
1221
1222
while len(Rc) > 0 or len(Sc) > 0:
1223
# print Z
1224
# if we are out of functors in either tower, there is no ambiguity
1225
if len(Sc) == 0:
1226
all = Rc.pop() * all
1227
elif len(Rc) == 0:
1228
all = Sc.pop() * all
1229
# if one of the functors has lower rank, do it first
1230
elif Rc[-1].rank < Sc[-1].rank:
1231
all = Rc.pop() * all
1232
elif Sc[-1].rank < Rc[-1].rank:
1233
all = Sc.pop() * all
1234
else:
1235
# the ranks are the same, so things are a bit subtler
1236
if Rc[-1] == Sc[-1]:
1237
# If they are indeed the same operation, we only do it once.
1238
# The \code{merge} function here takes into account non-mathematical
1239
# distinctions (e.g. single vs. multivariate polynomials).
1240
cR = Rc.pop()
1241
cS = Sc.pop()
1242
c = cR.merge(cS) or cS.merge(cR)
1243
if c:
1244
all = c * all
1245
else:
1246
raise CoercionException, "Incompatible Base Extension %r, %r (on %r, %r)" % (R, S, cR, cS)
1247
else:
1248
# Now we look ahead to see if either top functor is
1249
# applied later on in the other tower.
1250
# If this is the case for exactly one of them, we unambiguously
1251
# postpone that operation, but if both then we abort.
1252
if Rc[-1] in Sc:
1253
if Sc[-1] in Rc:
1254
raise CoercionException, ("Ambiguous Base Extension", R, S)
1255
else:
1256
all = Sc.pop() * all
1257
elif Sc[-1] in Rc:
1258
all = Rc.pop() * all
1259
# If, perchance, the two functors commute, then we may do them in any order.
1260
elif Rc[-1].commutes(Sc[-1]):
1261
all = Sc.pop() * Rc.pop() * all
1262
else:
1263
# try and merge (default merge is failure for unequal functors)
1264
cR = Rc.pop()
1265
cS = Sc.pop()
1266
c = cR.merge(cS) or cS.merge(cR)
1267
if c is not None:
1268
all = c * all
1269
else:
1270
# Otherwise, we cannot proceed.
1271
raise CoercionException, ("Ambiguous Base Extension", R, S)
1272
1273
return all(Z)
1274
1275
except CoercionException:
1276
raise
1277
except (TypeError, ValueError, AttributeError, NotImplementedError), ex:
1278
# We do this because we may be trying all kinds of things that don't
1279
# make sense, and in this case simply want to return that a pushout
1280
# couldn't be found.
1281
raise CoercionException(ex)
1282
1283
1284
1285
def pushout_lattice(R, S):
1286
"""
1287
Given a pair of Objects R and S, try and construct a
1288
reasonable object $Y$ and return maps such that
1289
canonically $R \leftarrow Y \rightarrow S$.
1290
1291
ALGORITHM:
1292
This is based on the model that arose from much discussion at Sage Days 4.
1293
Going up the tower of constructions of $R$ and $S$ (e.g. the reals
1294
come from the rationals come from the integers) try and find a
1295
common parent, and then try and fill in a lattice with these
1296
two towers as sides with the top as the common ancestor and
1297
the bottom will be the desired ring.
1298
1299
See the code for a specific worked-out example.
1300
1301
EXAMPLES:
1302
sage: from sage.categories.pushout import pushout_lattice
1303
sage: A, B = pushout_lattice(Qp(7), Frac(ZZ['x']))
1304
sage: A.codomain()
1305
Fraction Field of Univariate Polynomial Ring in x over 7-adic Field with capped relative precision 20
1306
sage: A.codomain() is B.codomain()
1307
True
1308
sage: A, B = pushout_lattice(ZZ, MatrixSpace(ZZ[['x']], 3, 3))
1309
sage: B
1310
Identity endomorphism of Full MatrixSpace of 3 by 3 dense matrices over Power Series Ring in x over Integer Ring
1311
1312
AUTHOR:
1313
-- Robert Bradshaw
1314
"""
1315
R_tower = construction_tower(R)
1316
S_tower = construction_tower(S)
1317
Rs = [c[1] for c in R_tower]
1318
Ss = [c[1] for c in S_tower]
1319
1320
# look for common ancestor
1321
start = None
1322
for Z in Rs:
1323
if Z in Ss:
1324
start = Z
1325
if start is None:
1326
# Should I test for a map between the tops of the towers?
1327
# Or, if they're both not ZZ, is it hopeless?
1328
return None
1329
1330
# truncate at common ancestor
1331
R_tower = list(reversed(R_tower[:Rs.index(start)+1]))
1332
S_tower = list(reversed(S_tower[:Ss.index(start)+1]))
1333
Rs = [c[1] for c in R_tower] # the list of objects
1334
Ss = [c[1] for c in S_tower]
1335
Rc = [c[0] for c in R_tower] # the list of functors
1336
Sc = [c[0] for c in S_tower]
1337
1338
# Here we try and construct a 2-dimensional lattice as follows.
1339
# Suppose our towers are Z -> Q -> Qp = R and Z -> Z[t] -> Frac(Z[t]) = S
1340
lattice = {}
1341
# First we fill in the sides
1342
#
1343
# Z
1344
# / \
1345
# Q Z[t]
1346
# / \
1347
# Qp Frac(Z[t])
1348
#
1349
for i in range(len(Rs)):
1350
lattice[i,0] = Rs[i]
1351
for j in range(len(Ss)):
1352
lattice[0,j] = Ss[j]
1353
1354
# Now we attempt to fill in the center, one (diagonal) row at a time,
1355
# one commuting square at a time.
1356
#
1357
# Z
1358
# / \
1359
# Q Z[t]
1360
# / \ / \
1361
# Qp Q[t] Frac(Z[t])
1362
# \ /
1363
# Qp[t]
1364
#
1365
# There is always exactly one "correct" path/order in which to apply operations
1366
# from the top to the bottom. In our example, this is down the far left side.
1367
# We keep track of which that is by clearing out Rc and Sc as we go along.
1368
#
1369
# Note that when applying the functors in the correct order, base extension
1370
# is not needed (though it may occur in the resulting morphisms).
1371
#
1372
for i in range(len(Rc)-1):
1373
for j in range(len(Sc)-1):
1374
try:
1375
if lattice[i,j+1] == lattice[i+1,j]:
1376
# In this case we have R <- S -> R
1377
# We don't want to perform the operation twice
1378
# and all subsequent squares will come from objects
1379
# where the operation was already performed (either
1380
# to the left or right)
1381
Rc[i] = Sc[j] = None # IdentityConstructionFunctor()
1382
lattice[i+1,j+1] = lattice[i,j+1]
1383
elif Rc[i] is None and Sc[j] is None:
1384
lattice[i+1,j+1] = lattice[i,j+1]
1385
elif Rc[i] is None:
1386
lattice[i+1,j+1] = Sc[j](lattice[i+1,j])
1387
elif Sc[j] is None:
1388
lattice[i+1,j+1] = Rc[i](lattice[i,j+1])
1389
else:
1390
# For now, we just look at the rank.
1391
# TODO: be more sophisticated and query the functors themselves
1392
if Rc[i].rank < Sc[j].rank:
1393
lattice[i+1,j+1] = Sc[j](lattice[i+1,j])
1394
Rc[i] = None # force us to use pre-applied Rc[i]
1395
else:
1396
lattice[i+1,j+1] = Rc[i](lattice[i,j+1])
1397
Sc[j] = None # force us to use pre-applied Sc[i]
1398
except (AttributeError, NameError):
1399
print i, j
1400
pp(lattice)
1401
raise CoercionException, "%s does not support %s" % (lattice[i,j], 'F')
1402
1403
# If we are successful, we should have something that looks like this.
1404
#
1405
# Z
1406
# / \
1407
# Q Z[t]
1408
# / \ / \
1409
# Qp Q[t] Frac(Z[t])
1410
# \ / \ /
1411
# Qp[t] Frac(Q[t])
1412
# \ /
1413
# Frac(Qp[t])
1414
#
1415
R_loc = len(Rs)-1
1416
S_loc = len(Ss)-1
1417
1418
# Find the composition coercion morphisms along the bottom left...
1419
if S_loc > 0:
1420
R_map = lattice[R_loc,1].coerce_map_from(R)
1421
for i in range(1, S_loc):
1422
map = lattice[R_loc, i+1].coerce_map_from(lattice[R_loc, i]) # The functor used is implicit here, should it be?
1423
R_map = map * R_map
1424
else:
1425
R_map = R.coerce_map_from(R) # id
1426
1427
# ... and bottom right
1428
if R_loc > 0:
1429
S_map = lattice[1, S_loc].coerce_map_from(S)
1430
for i in range(1, R_loc):
1431
map = lattice[i+1, S_loc].coerce_map_from(lattice[i, S_loc])
1432
S_map = map * S_map
1433
else:
1434
S_map = S.coerce_map_from(S) # id
1435
1436
return R_map, S_map
1437
1438
1439
def pp(lattice):
1440
"""
1441
Used in debugging to print the current lattice.
1442
"""
1443
for i in range(100):
1444
for j in range(100):
1445
try:
1446
R = lattice[i,j]
1447
print i, j, R
1448
except KeyError:
1449
break
1450
1451
def construction_tower(R):
1452
tower = [(None, R)]
1453
c = R.construction()
1454
while c is not None:
1455
f, R = c
1456
if not isinstance(f, ConstructionFunctor):
1457
f = BlackBoxConstructionFunctor(f)
1458
tower.append((f,R))
1459
c = R.construction()
1460
return tower
1461
1462
1463
1464
def type_to_parent(P):
1465
import sage.rings.all
1466
if P in [int, long]:
1467
return sage.rings.all.ZZ
1468
elif P is float:
1469
return sage.rings.all.RDF
1470
elif P is complex:
1471
return sage.rings.all.CDF
1472
else:
1473
raise TypeError, "Not a scalar type."
1474
1475