# source:sage/schemes/generic/morphism.py@5498:dd7f96fc2561

Revision 5498:dd7f96fc2561, 16.1 KB checked in by Robert Bradshaw <robertwb@…>, 6 years ago (diff)

fix obscure pyrex errors

Line
1"""
2Scheme morphism
3
4AUTHORS:
5    -- David Kohel, William Stein
6    -- William Stein (2006-02-11): fixed bug where P(0,0,0) was allowed as a projective point.
7"""
8
9#*****************************************************************************
10#  Copyright (C) 2006 David Kohel <kohel@maths.usyd.edu.au>
11#  Copyright (C) 2006 William Stein <wstein@gmail.com>
14#*****************************************************************************
15
16from sage.structure.element   import AdditiveGroupElement, RingElement, Element
17from sage.structure.sequence  import Sequence
18
19from sage.categories.morphism import Morphism
20from sage.categories.homset   import Homset
21
22from sage.rings.all           import is_RingHomomorphism, is_CommutativeRing, Integer
23
24from point                    import is_SchemeTopologicalPoint
25
26import scheme
27
28import spec
29
30def is_SchemeMorphism(f):
31    from sage.schemes.elliptic_curves.ell_point import EllipticCurvePoint_field # TODO: fix circular ref.
32    return isinstance(f, (SchemeMorphism, EllipticCurvePoint_field));
33
34
35class PyMorphism(Element):
36    # Double inheritance from both Morphism and AdditiveGroupElement seems to mess up the ModuleElement pyrex vtab, which is really bad!
37    def __init__(self, parent):
38        if not isinstance(parent, Homset):
39            raise TypeError, "parent (=%s) must be a Homspace"%parent
40        Element.__init__(self, parent)
41        self._domain = parent.domain()
42        self._codomain = parent.codomain()
43
44    def _repr_type(self):
45        return "Generic"
46
47    def _repr_defn(self):
48        return ""
49
50    def _repr_(self):
51        if self.is_endomorphism():
52            s = "%s endomorphism of %s"%(self._repr_type(), self.domain())
53        else:
54            s = "%s morphism:"%self._repr_type()
55            s += "\n  From: %s"%self.domain()
56            s += "\n  To:   %s"%self.codomain()
57        d = self._repr_defn()
58        if d != '':
59            s += "\n  Defn: %s"%('\n        '.join(self._repr_defn().split('\n')))
60        return s
61
62    def domain(self):
63        return self._domain
64
65    def codomain(self):
66        return self.parent().codomain()
67
68    def category(self):
69        return self.parent().category()
70
71    def is_endomorphism(self):
72        return self.parent().is_endomorphism_set()
73
74    def _composition_(self, right, homset):
75        return FormalCompositeMorphism(homset, right, self)
76
77    def __pow__(self, n, dummy):
78        if not self.is_endomorphism():
79            raise TypeError, "self must be an endomorphism."
80        # todo -- what about the case n=0 -- need to specify the identity map somehow.
81        import sage.rings.arith as arith
82        return arith.generic_power(self, n)
83
84
85
86class SchemeMorphism(PyMorphism):
87    """
88    A scheme morphism
89    """
90    def __init__(self, parent):
91        PyMorphism.__init__(self, parent)
92
93    def _repr_type(self):
94        return "Scheme"
95
96    def glue_along_domains(self, other):
97        r"""
98        Assuming that self and other are open immersions with the same
99        domain, return scheme obtained by gluing along the images.
100
101        EXAMPLES:
102        We construct a scheme isomorphic to the projective line over
103        \$\Spec(\Q)\$ by gluing two copies of \$\A^1\$ minus a point.
104            sage: R.<x,y> = MPolynomialRing(QQ, 2)
105            sage: S.<xbar, ybar> = R.quotient(x*y - 1)
106            sage: Rx = PolynomialRing(QQ, 'x')
107            sage: i1 = Rx.hom([xbar])
108            sage: Ry = PolynomialRing(QQ, 'y')
109            sage: i2 = Ry.hom([ybar])
110            sage: Sch = Schemes()
111            sage: f1 = Sch(i1)
112            sage: f2 = Sch(i2)
113
114        Now f1 and f2 have the same domain, which is a \$\A^1\$ minus a point.
115        We glue along the domain:
116            sage: P1 = f1.glue_along_domains(f2)
117            sage: P1
118            Scheme obtained by gluing X and Y along U, where
119              X: Spectrum of Univariate Polynomial Ring in x over Rational Field
120              Y: Spectrum of Univariate Polynomial Ring in y over Rational Field
121              U: Spectrum of Quotient of Polynomial Ring in x, y over Rational Field by the ideal (x*y - 1)
122
123            sage: a, b = P1.gluing_maps()
124            sage: a
125            Affine Scheme morphism:
126             From: Spectrum of Quotient of Polynomial Ring in x, y over Rational Field by the ideal (x*y - 1)
127              To:   Spectrum of Univariate Polynomial Ring in x over Rational Field
128              Defn: Ring morphism:
129                      From: Univariate Polynomial Ring in x over Rational Field
130                      To:   Quotient of Polynomial Ring in x, y over Rational Field by the ideal (x*y - 1)
131                      Defn: x |--> xbar
132            sage: b
133            Affine Scheme morphism:
134              From: Spectrum of Quotient of Polynomial Ring in x, y over Rational Field by the ideal (x*y - 1)
135              To:   Spectrum of Univariate Polynomial Ring in y over Rational Field
136              Defn: Ring morphism:
137                      From: Univariate Polynomial Ring in y over Rational Field
138                      To:   Quotient of Polynomial Ring in x, y over Rational Field by the ideal (x*y - 1)
139                      Defn: y |--> ybar
140        """
141        import glue
142        return glue.GluedScheme(self, other)
143
144class SchemeMorphism_id(SchemeMorphism):
145    """
146    Return the identity morphism from X to itself.
147
148    EXAMPLES:
149        sage: X = Spec(ZZ)
150        sage: X.identity_morphism()
151        Scheme endomorphism of Spectrum of Integer Ring
152          Defn: Identity map
153    """
154    def __init__(self, X):
155        SchemeMorphism.__init__(self, X.Hom(X))
156
157    def _repr_defn(self):
158        return "Identity map"
159
160
161class SchemeMorphism_structure_map(SchemeMorphism):
162    def __init__(self, parent):
163        """
164        INPUT:
165            parent -- homset with codomain equal to the base scheme of the domain.
166        """
167        SchemeMorphism.__init__(self, parent)
168        if self.domain().base_scheme() != self.codomain():
169            raise ValueError, "parent must have codomain equal the base scheme of domain."
170
171    def _repr_defn(self):
172        return "Structure map"
173
174
175class SchemeMorphism_spec(SchemeMorphism):
176    """
177    A morphism of spectrums of rings
178
179    EXAMPLES:
180        sage: R.<x> = PolynomialRing(QQ)
181        sage: phi = R.hom([QQ(7)]); phi
182        Ring morphism:
183          From: Univariate Polynomial Ring in x over Rational Field
184          To:   Rational Field
185          Defn: x |--> 7
186
187        sage: X = Spec(QQ); Y = Spec(R)
188        sage: f = X.hom(phi); f
189        Affine Scheme morphism:
190          From: Spectrum of Rational Field
191          To:   Spectrum of Univariate Polynomial Ring in x over Rational Field
192          Defn: Ring morphism:
193                  From: Univariate Polynomial Ring in x over Rational Field
194                  To:   Rational Field
195                  Defn: x |--> 7
196
197        sage: f.ring_homomorphism()
198        Ring morphism:
199          From: Univariate Polynomial Ring in x over Rational Field
200          To:   Rational Field
201          Defn: x |--> 7
202    """
203    def __init__(self, parent, phi, check=True):
204        SchemeMorphism.__init__(self, parent)
205        if check:
206            if not is_RingHomomorphism(phi):
207                raise TypeError, "phi (=%s) must be a ring homomorphism"%phi
208            if phi.domain() != parent.codomain().coordinate_ring():
209                raise TypeError, "phi (=%s) must have domain %s"%(phi,
210                                                   parent.codomain().coordinate_ring())
211            if phi.codomain() != parent.domain().coordinate_ring():
212                raise TypeError, "phi (=%s) must have codomain %s"%(phi,
213                                                 parent.domain().coordinate_ring())
214        self.__ring_homomorphism = phi
215
216    def __call__(self, P):
217        if not is_SchemeTopologicalPoint(P) and P in self.domain():
218            raise TypeError, "P (=%s) must be a topological scheme point of %s"%(P, self)
219        S = self.ring_homomorphism().inverse_image(P.prime_ideal())
220        return self.codomain()(S)
221
222    def _repr_type(self):
223        return "Affine Scheme"
224
225    def _repr_defn(self):
226        return repr(self.ring_homomorphism())
227
228
229    def ring_homomorphism(self):
230        return self.__ring_homomorphism
231
232
233############################################################################
234# Morphisms between schemes given on points
235# The _affine and _projective below refer to the CODOMAIN.
236# The domain can be either affine or projective irregardless
237# of the class
238############################################################################
239
240class SchemeMorphism_on_points(SchemeMorphism):
241    """
242    A morphism of schemes determined by rational functions that define
243    what the morphism does on points in the ambient space.
244    """
245    def __call__(self, x):
246        if not isinstance(x, SchemeMorphism_coordinates):
247            raise TypeError, "x (=%s) must be a projective point given by coordinates"%x
248        P = [f(x._coords) for f in self.defining_polynomials()]
249        return self.codomain()(P)
250
251
252    def _repr_defn(self):
253        i = self.domain().ambient_space()._repr_generic_point()
254        o = self.codomain().ambient_space()._repr_generic_point(self.defining_polynomials())
255        return "Defined on coordinates by sending %s to\n%s"%(i,o)
256
257
258class SchemeMorphism_on_points_affine_space(SchemeMorphism_on_points):
259    """
260    A morphism of schemes determined by rational functions that define
261    what the morphism does on points in the ambient affine space.
262    """
263    def __init__(self, parent, polys, check=True):
264        if check:
265            if not isinstance(polys, (list, tuple)):
266                raise TypeError, "polys (=%s) must be a list or tuple"%polys
267            polys = Sequence(polys)
268            if len(polys) != parent.codomain().dimension():
269                raise ValueError, "there must be %s polynomials but instead received %s"%(
270                    parent.codomain().dimension(), polys)
271            polys.set_immutable()
272            # Todo: check that map is well defined (how?)
273        self.__polys = polys
274        SchemeMorphism_on_points.__init__(self, parent)
275
276    def defining_polynomials(self):
277        return self.__polys
278
279
280class SchemeMorphism_on_points_projective_space(SchemeMorphism_on_points):
281    """
282    A morphism of schemes determined by rational functions that define
283    what the morphism does on points in the ambient projective space.
284    """
285
286    def __init__(self, parent, polys, check=True):
287        if check:
288            if not isinstance(polys, (list, tuple)):
289                raise TypeError, "polys (=%s) must be a list or tuple"%polys
290            polys = Sequence(polys)
291            if len(polys) != parent.codomain().ambient_space().ngens():
292                raise ValueError, "there must be %s polynomials"%parent.codomain().ambient_space().ngens()
293            polys.set_immutable()
294        self.__polys = polys
295        SchemeMorphism_on_points.__init__(self, parent)
296
297    def defining_polynomials(self):
298        return self.__polys
299
300
301############################################################################
302# Rational points on schemes, which we view as morphisms determined
303# by coordinates.
304############################################################################
305
306class SchemeMorphism_coordinates(SchemeMorphism):
307    def _repr_(self):
308        return self.codomain().ambient_space()._repr_generic_point(self._coords)
309
310    def _latex_(self):
311        return self.codomain().ambient_space()._latex_generic_point(self._coords)
312
313    def __getitem__(self, n):
314        return self._coords[n]
315
316    def __list__(self):
317        return list(self._coords)
318
319    def __tuple__(self):
320        return self._coords
321
322    def __cmp__(self, other):
323        if not isinstance(other, SchemeMorphism_coordinates):
324            try:
325                other = self.codomain().ambient_space()(other)
326            except TypeError:
327                return -1
328        return cmp(self._coords, other._coords)
329
330    def scheme(self):
331        return self.codomain()
332
333class SchemeMorphism_affine_coordinates(SchemeMorphism_coordinates):
334    """
335    A morphism determined by giving coordinates in a ring.
336
337    INPUT:
338        X -- a subscheme of an ambient affine space over a ring R.
339        v -- a list or tuple of coordinates in R
340
341    EXAMPLES:
342        sage: A = AffineSpace(2, QQ)
343        sage: A(1,2)
344        (1, 2)
345    """
346    def __init__(self, X, v, check=True):
347        if scheme.is_Scheme(X):
348            X = X(X.base_ring())
349        SchemeMorphism.__init__(self, X)
350        if check:
351            # Verify that there are the right number of coords
352            d = X.codomain().ambient_space().ngens()
353            if len(v) != d:
354                raise TypeError, \
355                      "Argument v (=%s) must have %s coordinates."%(v, d)
356            if is_SchemeMorphism(v):
357                v = list(v)
358            if not isinstance(v,(list,tuple)):
359                raise TypeError, \
360                      "Argument v (= %s) must be a scheme point, list, or tuple."%str(v)
361            # Make sure the coordinates all lie in the appropriate ring
362            v = Sequence(v, X.value_ring())
363            # Verify that the point satisfies the equations of X.
364            X.codomain()._check_satisfies_equations(v)
365        self._coords = v
366
367
368class SchemeMorphism_projective_coordinates_ring(SchemeMorphism_coordinates):
369    """
370    A morphism determined by giving coordinates in a ring (how?).
371
372    """
373    def __init__(self, X, v, check=True):
374        raise NotImplementedError
375
376
377class SchemeMorphism_projective_coordinates_field(SchemeMorphism_projective_coordinates_ring):
378    """
379    A morphism determined by giving coordinates in a field.
380
381    INPUT:
382        X -- a subscheme of an ambient projective space over a field K
383        v -- a list or tuple of coordinates in K
384
385    EXAMPLES:
386        sage: P = ProjectiveSpace(3, RR)
387        sage: P(2,3,4,5)
388        (0.400000000000000 : 0.600000000000000 : 0.800000000000000 : 1.00000000000000)
389
390        sage: P = ProjectiveSpace(3, QQ)
391        sage: P(0,0,0,0)
392        Traceback (most recent call last):
393        ...
394        ValueError: [0, 0, 0, 0] does not define a valid point since all entries are 0
395    """
396    def __init__(self, X, v, check=True):
397        if scheme.is_Scheme(X):
398            X = X(X.base_ring())
399        SchemeMorphism.__init__(self, X)
400        if check:
401            from sage.schemes.elliptic_curves.ell_point import EllipticCurvePoint_field # TODO: fix circular ref.
402            d = X.codomain().ambient_space().ngens()
403            if is_SchemeMorphism(v) or isinstance(v, EllipticCurvePoint_field):
404                v = list(v)
405            if not isinstance(v,(list,tuple)):
406                raise TypeError, \
407                      "Argument v (= %s) must be a scheme point, list, or tuple."%str(v)
408            if len(v) != d and len(v) != d-1:
409                raise TypeError, "v (=%s) must have %s components"%(v, d)
410            #v = Sequence(v, X.base_ring())
411            v = Sequence(v, X.value_ring())
412            if len(v) == d-1:     # very common special case
413                v.append(1)
414
415            n = len(v)
416            all_zero = True
417            for i in range(n):
418                if v[n-1-i]:
419                    all_zero = False
420                    c = v[n-1-i]
421                    if c == 1:
422                        break
423                    for j in range(n-i):
424                        v[j] /= c
425                    break
426            if all_zero:
427                raise ValueError, "%s does not define a valid point since all entries are 0"%repr(v)
428
429            X.codomain()._check_satisfies_equations(v)
430
431        self._coords = v
432