passagemath-objects 10.6.41__cp314-cp314t-macosx_13_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of passagemath-objects might be problematic. Click here for more details.
- passagemath_objects/.dylibs/libgmp.10.dylib +0 -0
- passagemath_objects/__init__.py +3 -0
- passagemath_objects-10.6.41.dist-info/METADATA +115 -0
- passagemath_objects-10.6.41.dist-info/RECORD +280 -0
- passagemath_objects-10.6.41.dist-info/WHEEL +6 -0
- passagemath_objects-10.6.41.dist-info/top_level.txt +3 -0
- sage/all__sagemath_objects.py +37 -0
- sage/arith/all__sagemath_objects.py +5 -0
- sage/arith/long.pxd +411 -0
- sage/arith/numerical_approx.cpython-314t-darwin.so +0 -0
- sage/arith/numerical_approx.pxd +35 -0
- sage/arith/numerical_approx.pyx +75 -0
- sage/arith/power.cpython-314t-darwin.so +0 -0
- sage/arith/power.pxd +31 -0
- sage/arith/power.pyx +127 -0
- sage/categories/action.cpython-314t-darwin.so +0 -0
- sage/categories/action.pxd +29 -0
- sage/categories/action.pyx +641 -0
- sage/categories/algebra_functor.py +745 -0
- sage/categories/all__sagemath_objects.py +33 -0
- sage/categories/basic.py +62 -0
- sage/categories/cartesian_product.py +295 -0
- sage/categories/category.py +3401 -0
- sage/categories/category_cy_helper.cpython-314t-darwin.so +0 -0
- sage/categories/category_cy_helper.pxd +8 -0
- sage/categories/category_cy_helper.pyx +322 -0
- sage/categories/category_singleton.cpython-314t-darwin.so +0 -0
- sage/categories/category_singleton.pxd +3 -0
- sage/categories/category_singleton.pyx +342 -0
- sage/categories/category_types.py +637 -0
- sage/categories/category_with_axiom.py +2876 -0
- sage/categories/covariant_functorial_construction.py +703 -0
- sage/categories/facade_sets.py +228 -0
- sage/categories/functor.cpython-314t-darwin.so +0 -0
- sage/categories/functor.pxd +7 -0
- sage/categories/functor.pyx +691 -0
- sage/categories/homset.py +1338 -0
- sage/categories/homsets.py +364 -0
- sage/categories/isomorphic_objects.py +73 -0
- sage/categories/map.cpython-314t-darwin.so +0 -0
- sage/categories/map.pxd +34 -0
- sage/categories/map.pyx +2112 -0
- sage/categories/morphism.cpython-314t-darwin.so +0 -0
- sage/categories/morphism.pxd +14 -0
- sage/categories/morphism.pyx +895 -0
- sage/categories/objects.py +167 -0
- sage/categories/primer.py +1696 -0
- sage/categories/pushout.py +4834 -0
- sage/categories/quotients.py +64 -0
- sage/categories/realizations.py +200 -0
- sage/categories/sets_cat.py +3228 -0
- sage/categories/sets_with_partial_maps.py +52 -0
- sage/categories/subobjects.py +64 -0
- sage/categories/subquotients.py +21 -0
- sage/categories/with_realizations.py +311 -0
- sage/cpython/__init__.py +19 -0
- sage/cpython/_py2_random.py +619 -0
- sage/cpython/all.py +3 -0
- sage/cpython/atexit.cpython-314t-darwin.so +0 -0
- sage/cpython/atexit.pyx +269 -0
- sage/cpython/builtin_types.cpython-314t-darwin.so +0 -0
- sage/cpython/builtin_types.pyx +7 -0
- sage/cpython/cython_metaclass.cpython-314t-darwin.so +0 -0
- sage/cpython/cython_metaclass.h +117 -0
- sage/cpython/cython_metaclass.pxd +3 -0
- sage/cpython/cython_metaclass.pyx +130 -0
- sage/cpython/debug.cpython-314t-darwin.so +0 -0
- sage/cpython/debug.pyx +302 -0
- sage/cpython/dict_del_by_value.cpython-314t-darwin.so +0 -0
- sage/cpython/dict_del_by_value.pxd +9 -0
- sage/cpython/dict_del_by_value.pyx +191 -0
- sage/cpython/dict_internal.h +245 -0
- sage/cpython/getattr.cpython-314t-darwin.so +0 -0
- sage/cpython/getattr.pxd +9 -0
- sage/cpython/getattr.pyx +439 -0
- sage/cpython/pycore_long.h +97 -0
- sage/cpython/pycore_long.pxd +10 -0
- sage/cpython/python_debug.h +44 -0
- sage/cpython/python_debug.pxd +47 -0
- sage/cpython/pyx_visit.h +13 -0
- sage/cpython/string.cpython-314t-darwin.so +0 -0
- sage/cpython/string.pxd +76 -0
- sage/cpython/string.pyx +34 -0
- sage/cpython/string_impl.h +60 -0
- sage/cpython/type.cpython-314t-darwin.so +0 -0
- sage/cpython/type.pxd +2 -0
- sage/cpython/type.pyx +40 -0
- sage/cpython/wrapperdescr.pxd +67 -0
- sage/ext/all__sagemath_objects.py +3 -0
- sage/ext/ccobject.h +64 -0
- sage/ext/cplusplus.pxd +17 -0
- sage/ext/mod_int.h +30 -0
- sage/ext/mod_int.pxd +24 -0
- sage/ext/stdsage.pxd +39 -0
- sage/groups/all__sagemath_objects.py +1 -0
- sage/groups/group.cpython-314t-darwin.so +0 -0
- sage/groups/group.pxd +14 -0
- sage/groups/group.pyx +322 -0
- sage/groups/old.cpython-314t-darwin.so +0 -0
- sage/groups/old.pxd +14 -0
- sage/groups/old.pyx +219 -0
- sage/libs/all__sagemath_objects.py +3 -0
- sage/libs/gmp/__init__.py +1 -0
- sage/libs/gmp/all.pxd +6 -0
- sage/libs/gmp/binop.pxd +23 -0
- sage/libs/gmp/misc.pxd +8 -0
- sage/libs/gmp/mpf.pxd +88 -0
- sage/libs/gmp/mpn.pxd +57 -0
- sage/libs/gmp/mpq.pxd +57 -0
- sage/libs/gmp/mpz.pxd +202 -0
- sage/libs/gmp/pylong.cpython-314t-darwin.so +0 -0
- sage/libs/gmp/pylong.pxd +12 -0
- sage/libs/gmp/pylong.pyx +150 -0
- sage/libs/gmp/random.pxd +25 -0
- sage/libs/gmp/randomize.pxd +59 -0
- sage/libs/gmp/types.pxd +53 -0
- sage/libs/gmpxx.pxd +19 -0
- sage/misc/abstract_method.py +276 -0
- sage/misc/all__sagemath_objects.py +43 -0
- sage/misc/bindable_class.py +253 -0
- sage/misc/c3_controlled.cpython-314t-darwin.so +0 -0
- sage/misc/c3_controlled.pxd +2 -0
- sage/misc/c3_controlled.pyx +1402 -0
- sage/misc/cachefunc.cpython-314t-darwin.so +0 -0
- sage/misc/cachefunc.pxd +43 -0
- sage/misc/cachefunc.pyx +3781 -0
- sage/misc/call.py +188 -0
- sage/misc/classcall_metaclass.cpython-314t-darwin.so +0 -0
- sage/misc/classcall_metaclass.pxd +14 -0
- sage/misc/classcall_metaclass.pyx +599 -0
- sage/misc/constant_function.cpython-314t-darwin.so +0 -0
- sage/misc/constant_function.pyx +130 -0
- sage/misc/decorators.py +747 -0
- sage/misc/fast_methods.cpython-314t-darwin.so +0 -0
- sage/misc/fast_methods.pxd +20 -0
- sage/misc/fast_methods.pyx +351 -0
- sage/misc/flatten.py +90 -0
- sage/misc/fpickle.cpython-314t-darwin.so +0 -0
- sage/misc/fpickle.pyx +177 -0
- sage/misc/function_mangling.cpython-314t-darwin.so +0 -0
- sage/misc/function_mangling.pxd +11 -0
- sage/misc/function_mangling.pyx +308 -0
- sage/misc/inherit_comparison.cpython-314t-darwin.so +0 -0
- sage/misc/inherit_comparison.pxd +5 -0
- sage/misc/inherit_comparison.pyx +105 -0
- sage/misc/instancedoc.cpython-314t-darwin.so +0 -0
- sage/misc/instancedoc.pyx +331 -0
- sage/misc/lazy_attribute.cpython-314t-darwin.so +0 -0
- sage/misc/lazy_attribute.pyx +607 -0
- sage/misc/lazy_format.py +135 -0
- sage/misc/lazy_import.cpython-314t-darwin.so +0 -0
- sage/misc/lazy_import.pyx +1299 -0
- sage/misc/lazy_import_cache.py +36 -0
- sage/misc/lazy_list.cpython-314t-darwin.so +0 -0
- sage/misc/lazy_list.pxd +19 -0
- sage/misc/lazy_list.pyx +1187 -0
- sage/misc/lazy_string.cpython-314t-darwin.so +0 -0
- sage/misc/lazy_string.pxd +7 -0
- sage/misc/lazy_string.pyx +546 -0
- sage/misc/misc.py +1066 -0
- sage/misc/misc_c.cpython-314t-darwin.so +0 -0
- sage/misc/misc_c.pxd +3 -0
- sage/misc/misc_c.pyx +766 -0
- sage/misc/namespace_package.py +37 -0
- sage/misc/nested_class.cpython-314t-darwin.so +0 -0
- sage/misc/nested_class.pxd +3 -0
- sage/misc/nested_class.pyx +394 -0
- sage/misc/persist.cpython-314t-darwin.so +0 -0
- sage/misc/persist.pyx +1251 -0
- sage/misc/prandom.py +418 -0
- sage/misc/randstate.cpython-314t-darwin.so +0 -0
- sage/misc/randstate.pxd +30 -0
- sage/misc/randstate.pyx +1059 -0
- sage/misc/repr.py +203 -0
- sage/misc/reset.cpython-314t-darwin.so +0 -0
- sage/misc/reset.pyx +196 -0
- sage/misc/sage_ostools.cpython-314t-darwin.so +0 -0
- sage/misc/sage_ostools.pyx +323 -0
- sage/misc/sage_timeit.py +276 -0
- sage/misc/sage_timeit_class.cpython-314t-darwin.so +0 -0
- sage/misc/sage_timeit_class.pyx +120 -0
- sage/misc/sage_unittest.py +637 -0
- sage/misc/sageinspect.py +2768 -0
- sage/misc/session.cpython-314t-darwin.so +0 -0
- sage/misc/session.pyx +392 -0
- sage/misc/superseded.py +557 -0
- sage/misc/test_nested_class.py +228 -0
- sage/misc/timing.py +264 -0
- sage/misc/unknown.py +222 -0
- sage/misc/verbose.py +253 -0
- sage/misc/weak_dict.cpython-314t-darwin.so +0 -0
- sage/misc/weak_dict.pxd +15 -0
- sage/misc/weak_dict.pyx +1231 -0
- sage/modules/all__sagemath_objects.py +1 -0
- sage/modules/module.cpython-314t-darwin.so +0 -0
- sage/modules/module.pxd +5 -0
- sage/modules/module.pyx +329 -0
- sage/rings/all__sagemath_objects.py +3 -0
- sage/rings/integer_fake.h +22 -0
- sage/rings/integer_fake.pxd +55 -0
- sage/sets/all__sagemath_objects.py +3 -0
- sage/sets/pythonclass.cpython-314t-darwin.so +0 -0
- sage/sets/pythonclass.pxd +9 -0
- sage/sets/pythonclass.pyx +247 -0
- sage/structure/__init__.py +4 -0
- sage/structure/all.py +30 -0
- sage/structure/category_object.cpython-314t-darwin.so +0 -0
- sage/structure/category_object.pxd +28 -0
- sage/structure/category_object.pyx +1087 -0
- sage/structure/coerce.cpython-314t-darwin.so +0 -0
- sage/structure/coerce.pxd +44 -0
- sage/structure/coerce.pyx +2107 -0
- sage/structure/coerce_actions.cpython-314t-darwin.so +0 -0
- sage/structure/coerce_actions.pxd +27 -0
- sage/structure/coerce_actions.pyx +988 -0
- sage/structure/coerce_dict.cpython-314t-darwin.so +0 -0
- sage/structure/coerce_dict.pxd +51 -0
- sage/structure/coerce_dict.pyx +1557 -0
- sage/structure/coerce_exceptions.py +23 -0
- sage/structure/coerce_maps.cpython-314t-darwin.so +0 -0
- sage/structure/coerce_maps.pxd +28 -0
- sage/structure/coerce_maps.pyx +718 -0
- sage/structure/debug_options.cpython-314t-darwin.so +0 -0
- sage/structure/debug_options.pxd +6 -0
- sage/structure/debug_options.pyx +54 -0
- sage/structure/dynamic_class.py +541 -0
- sage/structure/element.cpython-314t-darwin.so +0 -0
- sage/structure/element.pxd +272 -0
- sage/structure/element.pyx +4772 -0
- sage/structure/element_wrapper.cpython-314t-darwin.so +0 -0
- sage/structure/element_wrapper.pxd +12 -0
- sage/structure/element_wrapper.pyx +582 -0
- sage/structure/factorization.py +1422 -0
- sage/structure/factorization_integer.py +105 -0
- sage/structure/factory.cpython-314t-darwin.so +0 -0
- sage/structure/factory.pyx +786 -0
- sage/structure/formal_sum.py +489 -0
- sage/structure/gens_py.py +73 -0
- sage/structure/global_options.py +1743 -0
- sage/structure/indexed_generators.py +863 -0
- sage/structure/list_clone.cpython-314t-darwin.so +0 -0
- sage/structure/list_clone.pxd +65 -0
- sage/structure/list_clone.pyx +1867 -0
- sage/structure/list_clone_demo.cpython-314t-darwin.so +0 -0
- sage/structure/list_clone_demo.pyx +248 -0
- sage/structure/list_clone_timings.py +179 -0
- sage/structure/list_clone_timings_cy.cpython-314t-darwin.so +0 -0
- sage/structure/list_clone_timings_cy.pyx +86 -0
- sage/structure/mutability.cpython-314t-darwin.so +0 -0
- sage/structure/mutability.pxd +21 -0
- sage/structure/mutability.pyx +348 -0
- sage/structure/nonexact.py +69 -0
- sage/structure/parent.cpython-314t-darwin.so +0 -0
- sage/structure/parent.pxd +112 -0
- sage/structure/parent.pyx +3093 -0
- sage/structure/parent_base.cpython-314t-darwin.so +0 -0
- sage/structure/parent_base.pxd +13 -0
- sage/structure/parent_base.pyx +44 -0
- sage/structure/parent_gens.cpython-314t-darwin.so +0 -0
- sage/structure/parent_gens.pxd +22 -0
- sage/structure/parent_gens.pyx +377 -0
- sage/structure/parent_old.cpython-314t-darwin.so +0 -0
- sage/structure/parent_old.pxd +25 -0
- sage/structure/parent_old.pyx +294 -0
- sage/structure/proof/__init__.py +1 -0
- sage/structure/proof/all.py +243 -0
- sage/structure/proof/proof.py +300 -0
- sage/structure/richcmp.cpython-314t-darwin.so +0 -0
- sage/structure/richcmp.pxd +213 -0
- sage/structure/richcmp.pyx +495 -0
- sage/structure/sage_object.cpython-314t-darwin.so +0 -0
- sage/structure/sage_object.pxd +3 -0
- sage/structure/sage_object.pyx +988 -0
- sage/structure/sage_object_test.py +19 -0
- sage/structure/sequence.py +937 -0
- sage/structure/set_factories.py +1178 -0
- sage/structure/set_factories_example.py +527 -0
- sage/structure/support_view.py +179 -0
- sage/structure/test_factory.py +56 -0
- sage/structure/unique_representation.py +1359 -0
|
@@ -0,0 +1,4834 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-objects
|
|
2
|
+
"""
|
|
3
|
+
Coercion via construction functors
|
|
4
|
+
"""
|
|
5
|
+
# ****************************************************************************
|
|
6
|
+
# Copyright (C) 2007-2014 Robert Bradshaw
|
|
7
|
+
# 2007-2018 David Roe
|
|
8
|
+
# 2009-2013 Simon King
|
|
9
|
+
# 2010 John Cremona
|
|
10
|
+
# 2010-2011 Mike Hansen
|
|
11
|
+
# 2012 Julian Rueth
|
|
12
|
+
# 2013-2016 Peter Bruin
|
|
13
|
+
# 2014 Wilfried Luebbe
|
|
14
|
+
# 2015 Benjamin Hackl
|
|
15
|
+
# 2015 Daniel Krenn
|
|
16
|
+
# 2016-2020 Frédéric Chapoton
|
|
17
|
+
# 2017 Jori Mäntysalo
|
|
18
|
+
# 2018 Vincent Delecroix
|
|
19
|
+
# 2020 Marc Mezzarobba
|
|
20
|
+
# 2020-2022 Matthias Koeppe
|
|
21
|
+
#
|
|
22
|
+
# This program is free software: you can redistribute it and/or modify
|
|
23
|
+
# it under the terms of the GNU General Public License as published by
|
|
24
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
25
|
+
# (at your option) any later version.
|
|
26
|
+
# https://www.gnu.org/licenses/
|
|
27
|
+
# ****************************************************************************
|
|
28
|
+
|
|
29
|
+
import operator
|
|
30
|
+
try:
|
|
31
|
+
from typing import Self # type: ignore (Python >= 3.11)
|
|
32
|
+
except ImportError:
|
|
33
|
+
from typing_extensions import Self # type: ignore (Python 3.10)
|
|
34
|
+
|
|
35
|
+
from sage.categories.functor import Functor, IdentityFunctor_generic
|
|
36
|
+
from sage.misc.lazy_import import lazy_import
|
|
37
|
+
from sage.structure.coerce_exceptions import CoercionException
|
|
38
|
+
|
|
39
|
+
lazy_import('sage.categories.commutative_additive_groups', 'CommutativeAdditiveGroups')
|
|
40
|
+
lazy_import('sage.categories.commutative_rings', 'CommutativeRings')
|
|
41
|
+
lazy_import('sage.categories.groups', 'Groups')
|
|
42
|
+
lazy_import('sage.categories.objects', 'Objects')
|
|
43
|
+
lazy_import('sage.categories.rings', 'Rings')
|
|
44
|
+
|
|
45
|
+
# TODO, think through the rankings, and override pushout where necessary.
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ConstructionFunctor(Functor):
|
|
49
|
+
"""
|
|
50
|
+
Base class for construction functors.
|
|
51
|
+
|
|
52
|
+
A construction functor is a functorial algebraic construction,
|
|
53
|
+
such as the construction of a matrix ring over a given ring
|
|
54
|
+
or the fraction field of a given ring.
|
|
55
|
+
|
|
56
|
+
In addition to the class :class:`~sage.categories.functor.Functor`,
|
|
57
|
+
construction functors provide rules for combining and merging
|
|
58
|
+
constructions. This is an important part of Sage's coercion model,
|
|
59
|
+
namely the pushout of two constructions: When a polynomial ``p`` in
|
|
60
|
+
a variable ``x`` with integer coefficients is added to a rational
|
|
61
|
+
number ``q``, then Sage finds that the parents ``ZZ['x']`` and
|
|
62
|
+
``QQ`` are obtained from ``ZZ`` by applying a polynomial ring
|
|
63
|
+
construction respectively the fraction field construction. Each
|
|
64
|
+
construction functor has an attribute ``rank``, and the rank of
|
|
65
|
+
the polynomial ring construction is higher than the rank of the
|
|
66
|
+
fraction field construction. This means that the pushout of ``QQ``
|
|
67
|
+
and ``ZZ['x']``, and thus a common parent in which ``p`` and ``q``
|
|
68
|
+
can be added, is ``QQ['x']``, since the construction functor with
|
|
69
|
+
a lower rank is applied first.
|
|
70
|
+
|
|
71
|
+
::
|
|
72
|
+
|
|
73
|
+
sage: F1, R = QQ.construction()
|
|
74
|
+
sage: F1
|
|
75
|
+
FractionField
|
|
76
|
+
sage: R
|
|
77
|
+
Integer Ring
|
|
78
|
+
sage: F2, R = (ZZ['x']).construction()
|
|
79
|
+
sage: F2
|
|
80
|
+
Poly[x]
|
|
81
|
+
sage: R
|
|
82
|
+
Integer Ring
|
|
83
|
+
sage: F3 = F2.pushout(F1)
|
|
84
|
+
sage: F3
|
|
85
|
+
Poly[x](FractionField(...))
|
|
86
|
+
sage: F3(R)
|
|
87
|
+
Univariate Polynomial Ring in x over Rational Field
|
|
88
|
+
sage: from sage.categories.pushout import pushout
|
|
89
|
+
sage: P.<x> = ZZ[]
|
|
90
|
+
sage: pushout(QQ,P)
|
|
91
|
+
Univariate Polynomial Ring in x over Rational Field
|
|
92
|
+
sage: ((x+1) + 1/2).parent()
|
|
93
|
+
Univariate Polynomial Ring in x over Rational Field
|
|
94
|
+
|
|
95
|
+
When composing two construction functors, they are sometimes
|
|
96
|
+
merged into one, as is the case in the Quotient construction::
|
|
97
|
+
|
|
98
|
+
sage: Q15, R = (ZZ.quo(15*ZZ)).construction()
|
|
99
|
+
sage: Q15
|
|
100
|
+
QuotientFunctor
|
|
101
|
+
sage: Q35, R = (ZZ.quo(35*ZZ)).construction()
|
|
102
|
+
sage: Q35
|
|
103
|
+
QuotientFunctor
|
|
104
|
+
sage: Q15.merge(Q35)
|
|
105
|
+
QuotientFunctor
|
|
106
|
+
sage: Q15.merge(Q35)(ZZ)
|
|
107
|
+
Ring of integers modulo 5
|
|
108
|
+
|
|
109
|
+
Functors can not only be applied to objects, but also to morphisms in the
|
|
110
|
+
respective categories. For example::
|
|
111
|
+
|
|
112
|
+
sage: P.<x,y> = ZZ[]
|
|
113
|
+
sage: F = P.construction()[0]; F
|
|
114
|
+
MPoly[x,y]
|
|
115
|
+
sage: A.<a,b> = GF(5)[]
|
|
116
|
+
sage: f = A.hom([a + b, a - b], A)
|
|
117
|
+
sage: F(A)
|
|
118
|
+
Multivariate Polynomial Ring in x, y
|
|
119
|
+
over Multivariate Polynomial Ring in a, b over Finite Field of size 5
|
|
120
|
+
sage: F(f)
|
|
121
|
+
Ring endomorphism of Multivariate Polynomial Ring in x, y
|
|
122
|
+
over Multivariate Polynomial Ring in a, b over Finite Field of size 5
|
|
123
|
+
Defn: Induced from base ring by
|
|
124
|
+
Ring endomorphism of Multivariate Polynomial Ring in a, b
|
|
125
|
+
over Finite Field of size 5
|
|
126
|
+
Defn: a |--> a + b
|
|
127
|
+
b |--> a - b
|
|
128
|
+
sage: F(f)(F(A)(x)*a)
|
|
129
|
+
(a + b)*x
|
|
130
|
+
"""
|
|
131
|
+
def __mul__(self, other):
|
|
132
|
+
"""
|
|
133
|
+
Compose ``self`` and ``other`` to a composite construction
|
|
134
|
+
functor, unless one of them is the identity.
|
|
135
|
+
|
|
136
|
+
.. NOTE::
|
|
137
|
+
|
|
138
|
+
The product is in functorial notation, i.e., when applying the
|
|
139
|
+
product to an object, the second factor is applied first.
|
|
140
|
+
|
|
141
|
+
TESTS::
|
|
142
|
+
|
|
143
|
+
sage: from sage.categories.pushout import IdentityConstructionFunctor
|
|
144
|
+
sage: I = IdentityConstructionFunctor()
|
|
145
|
+
sage: F = QQ.construction()[0]
|
|
146
|
+
sage: P = ZZ['t'].construction()[0]
|
|
147
|
+
sage: F*P
|
|
148
|
+
FractionField(Poly[t](...))
|
|
149
|
+
sage: P*F
|
|
150
|
+
Poly[t](FractionField(...))
|
|
151
|
+
sage: (F*P)(ZZ)
|
|
152
|
+
Fraction Field of Univariate Polynomial Ring in t over Integer Ring
|
|
153
|
+
sage: I*P is P
|
|
154
|
+
True
|
|
155
|
+
sage: F*I is F
|
|
156
|
+
True
|
|
157
|
+
"""
|
|
158
|
+
if not isinstance(self, ConstructionFunctor) and not isinstance(other, ConstructionFunctor):
|
|
159
|
+
raise CoercionException("Non-constructive product")
|
|
160
|
+
if isinstance(other, IdentityConstructionFunctor):
|
|
161
|
+
return self
|
|
162
|
+
if isinstance(self, IdentityConstructionFunctor):
|
|
163
|
+
return other
|
|
164
|
+
return CompositeConstructionFunctor(other, self)
|
|
165
|
+
|
|
166
|
+
def pushout(self, other):
|
|
167
|
+
"""
|
|
168
|
+
Composition of two construction functors, ordered by their ranks.
|
|
169
|
+
|
|
170
|
+
.. NOTE::
|
|
171
|
+
|
|
172
|
+
- This method seems not to be used in the coercion model.
|
|
173
|
+
|
|
174
|
+
- By default, the functor with smaller rank is applied first.
|
|
175
|
+
|
|
176
|
+
TESTS::
|
|
177
|
+
|
|
178
|
+
sage: F = QQ.construction()[0]
|
|
179
|
+
sage: P = ZZ['t'].construction()[0]
|
|
180
|
+
sage: F.pushout(P)
|
|
181
|
+
Poly[t](FractionField(...))
|
|
182
|
+
sage: P.pushout(F)
|
|
183
|
+
Poly[t](FractionField(...))
|
|
184
|
+
"""
|
|
185
|
+
if self.rank > other.rank:
|
|
186
|
+
return self * other
|
|
187
|
+
else:
|
|
188
|
+
return other * self
|
|
189
|
+
|
|
190
|
+
def __eq__(self, other):
|
|
191
|
+
"""
|
|
192
|
+
Equality here means that they are mathematically equivalent, though they may have
|
|
193
|
+
specific implementation data. This method will usually be overloaded in subclasses.
|
|
194
|
+
by default, only the types of the functors are compared. Also see the :meth:`merge` function.
|
|
195
|
+
|
|
196
|
+
TESTS::
|
|
197
|
+
|
|
198
|
+
sage: from sage.categories.pushout import IdentityConstructionFunctor
|
|
199
|
+
sage: I = IdentityConstructionFunctor()
|
|
200
|
+
sage: F = QQ.construction()[0]
|
|
201
|
+
sage: P = ZZ['t'].construction()[0]
|
|
202
|
+
sage: I == F # indirect doctest
|
|
203
|
+
False
|
|
204
|
+
sage: I == I # indirect doctest
|
|
205
|
+
True
|
|
206
|
+
"""
|
|
207
|
+
return type(self) is type(other)
|
|
208
|
+
|
|
209
|
+
def __ne__(self, other):
|
|
210
|
+
"""
|
|
211
|
+
Check whether ``self`` is not equal to ``other``.
|
|
212
|
+
|
|
213
|
+
EXAMPLES::
|
|
214
|
+
|
|
215
|
+
sage: from sage.categories.pushout import IdentityConstructionFunctor
|
|
216
|
+
sage: I = IdentityConstructionFunctor()
|
|
217
|
+
sage: F = QQ.construction()[0]
|
|
218
|
+
sage: P = ZZ['t'].construction()[0]
|
|
219
|
+
sage: I != F # indirect doctest
|
|
220
|
+
True
|
|
221
|
+
sage: I != I # indirect doctest
|
|
222
|
+
False
|
|
223
|
+
"""
|
|
224
|
+
return not (self == other)
|
|
225
|
+
|
|
226
|
+
def __hash__(self):
|
|
227
|
+
"""
|
|
228
|
+
Return the hash of ``self``.
|
|
229
|
+
|
|
230
|
+
EXAMPLES::
|
|
231
|
+
|
|
232
|
+
sage: from sage.categories.pushout import IdentityConstructionFunctor
|
|
233
|
+
sage: I = IdentityConstructionFunctor()
|
|
234
|
+
sage: F = QQ.construction()[0]
|
|
235
|
+
sage: hash(I) == hash(F)
|
|
236
|
+
False
|
|
237
|
+
sage: hash(I) == hash(I)
|
|
238
|
+
True
|
|
239
|
+
"""
|
|
240
|
+
return hash(repr(self))
|
|
241
|
+
|
|
242
|
+
def _repr_(self):
|
|
243
|
+
"""
|
|
244
|
+
.. NOTE::
|
|
245
|
+
|
|
246
|
+
By default, it returns the name of the construction
|
|
247
|
+
functor's class. Usually, this method will be overloaded.
|
|
248
|
+
|
|
249
|
+
TESTS::
|
|
250
|
+
|
|
251
|
+
sage: F = QQ.construction()[0]
|
|
252
|
+
sage: F # indirect doctest
|
|
253
|
+
FractionField
|
|
254
|
+
sage: Q = ZZ.quo(2).construction()[0]
|
|
255
|
+
sage: Q # indirect doctest
|
|
256
|
+
QuotientFunctor
|
|
257
|
+
"""
|
|
258
|
+
s = str(type(self))
|
|
259
|
+
import re
|
|
260
|
+
return re.sub(r"<.*'.*\.([^.]*)'>", "\\1", s)
|
|
261
|
+
|
|
262
|
+
def merge(self, other) -> Self | None:
|
|
263
|
+
"""
|
|
264
|
+
Merge ``self`` with another construction functor, or return ``None``.
|
|
265
|
+
|
|
266
|
+
.. NOTE::
|
|
267
|
+
|
|
268
|
+
The default is to merge only if the two functors coincide. But this
|
|
269
|
+
may be overloaded for subclasses, such as the quotient functor.
|
|
270
|
+
|
|
271
|
+
EXAMPLES::
|
|
272
|
+
|
|
273
|
+
sage: F = QQ.construction()[0]
|
|
274
|
+
sage: P = ZZ['t'].construction()[0]
|
|
275
|
+
sage: F.merge(F)
|
|
276
|
+
FractionField
|
|
277
|
+
sage: F.merge(P)
|
|
278
|
+
sage: P.merge(F)
|
|
279
|
+
sage: P.merge(P)
|
|
280
|
+
Poly[t]
|
|
281
|
+
"""
|
|
282
|
+
if self == other:
|
|
283
|
+
return self
|
|
284
|
+
else:
|
|
285
|
+
return None
|
|
286
|
+
|
|
287
|
+
def commutes(self, other):
|
|
288
|
+
"""
|
|
289
|
+
Determine whether ``self`` commutes with another construction functor.
|
|
290
|
+
|
|
291
|
+
.. NOTE::
|
|
292
|
+
|
|
293
|
+
By default, ``False`` is returned in all cases (even if the two
|
|
294
|
+
functors are the same, since in this case :meth:`merge` will apply
|
|
295
|
+
anyway). So far there is no construction functor that overloads
|
|
296
|
+
this method. Anyway, this method only becomes relevant if two
|
|
297
|
+
construction functors have the same rank.
|
|
298
|
+
|
|
299
|
+
EXAMPLES::
|
|
300
|
+
|
|
301
|
+
sage: F = QQ.construction()[0]
|
|
302
|
+
sage: P = ZZ['t'].construction()[0]
|
|
303
|
+
sage: F.commutes(P)
|
|
304
|
+
False
|
|
305
|
+
sage: P.commutes(F)
|
|
306
|
+
False
|
|
307
|
+
sage: F.commutes(F)
|
|
308
|
+
False
|
|
309
|
+
"""
|
|
310
|
+
return False
|
|
311
|
+
|
|
312
|
+
def expand(self):
|
|
313
|
+
"""
|
|
314
|
+
Decompose ``self`` into a list of construction functors.
|
|
315
|
+
|
|
316
|
+
.. NOTE::
|
|
317
|
+
|
|
318
|
+
The default is to return the list only containing ``self``.
|
|
319
|
+
|
|
320
|
+
EXAMPLES::
|
|
321
|
+
|
|
322
|
+
sage: F = QQ.construction()[0]
|
|
323
|
+
sage: F.expand()
|
|
324
|
+
[FractionField]
|
|
325
|
+
sage: Q = ZZ.quo(2).construction()[0]
|
|
326
|
+
sage: Q.expand()
|
|
327
|
+
[QuotientFunctor]
|
|
328
|
+
sage: P = ZZ['t'].construction()[0]
|
|
329
|
+
sage: FP = F*P
|
|
330
|
+
sage: FP.expand()
|
|
331
|
+
[FractionField, Poly[t]]
|
|
332
|
+
"""
|
|
333
|
+
return [self]
|
|
334
|
+
|
|
335
|
+
# See the pushout() function below for explanation.
|
|
336
|
+
coercion_reversed = False
|
|
337
|
+
|
|
338
|
+
def common_base(self, other_functor, self_bases, other_bases):
|
|
339
|
+
r"""
|
|
340
|
+
This function is called by :func:`pushout` when no common parent
|
|
341
|
+
is found in the construction tower.
|
|
342
|
+
|
|
343
|
+
.. NOTE::
|
|
344
|
+
|
|
345
|
+
The main use is for multivariate construction functors,
|
|
346
|
+
which use this function to implement recursion for
|
|
347
|
+
:func:`pushout`.
|
|
348
|
+
|
|
349
|
+
INPUT:
|
|
350
|
+
|
|
351
|
+
- ``other_functor`` -- a construction functor
|
|
352
|
+
|
|
353
|
+
- ``self_bases`` -- the arguments passed to this functor
|
|
354
|
+
|
|
355
|
+
- ``other_bases`` -- the arguments passed to the functor
|
|
356
|
+
``other_functor``
|
|
357
|
+
|
|
358
|
+
OUTPUT:
|
|
359
|
+
|
|
360
|
+
Nothing, since a
|
|
361
|
+
:class:`~sage.structure.coerce_exceptions.CoercionException`
|
|
362
|
+
is raised.
|
|
363
|
+
|
|
364
|
+
.. NOTE::
|
|
365
|
+
|
|
366
|
+
Overload this function in derived class, see
|
|
367
|
+
e.e. :class:`MultivariateConstructionFunctor`.
|
|
368
|
+
|
|
369
|
+
TESTS::
|
|
370
|
+
|
|
371
|
+
sage: from sage.categories.pushout import pushout
|
|
372
|
+
sage: pushout(QQ, cartesian_product([ZZ])) # indirect doctest
|
|
373
|
+
Traceback (most recent call last):
|
|
374
|
+
...
|
|
375
|
+
CoercionException: No common base ("join") found for
|
|
376
|
+
FractionField(Integer Ring) and The cartesian_product functorial construction(Integer Ring).
|
|
377
|
+
"""
|
|
378
|
+
self._raise_common_base_exception_(
|
|
379
|
+
other_functor, self_bases, other_bases)
|
|
380
|
+
|
|
381
|
+
def _raise_common_base_exception_(self, other_functor,
|
|
382
|
+
self_bases, other_bases,
|
|
383
|
+
reason=None):
|
|
384
|
+
r"""
|
|
385
|
+
Raise a coercion exception.
|
|
386
|
+
|
|
387
|
+
INPUT:
|
|
388
|
+
|
|
389
|
+
- ``other_functor`` -- a functor
|
|
390
|
+
|
|
391
|
+
- ``self_bases`` -- the arguments passed to this functor
|
|
392
|
+
|
|
393
|
+
- ``other_bases`` -- the arguments passed to the functor
|
|
394
|
+
``other_functor``
|
|
395
|
+
|
|
396
|
+
- ``reason`` -- string or ``None`` (default)
|
|
397
|
+
|
|
398
|
+
TESTS::
|
|
399
|
+
|
|
400
|
+
sage: from sage.categories.pushout import pushout
|
|
401
|
+
sage: pushout(QQ, cartesian_product([QQ])) # indirect doctest
|
|
402
|
+
Traceback (most recent call last):
|
|
403
|
+
...
|
|
404
|
+
CoercionException: No common base ("join") found for
|
|
405
|
+
FractionField(Integer Ring) and The cartesian_product functorial construction(Rational Field).
|
|
406
|
+
"""
|
|
407
|
+
if not isinstance(self_bases, (tuple, list)):
|
|
408
|
+
self_bases = (self_bases,)
|
|
409
|
+
if not isinstance(other_bases, (tuple, list)):
|
|
410
|
+
other_bases = (other_bases,)
|
|
411
|
+
if reason is None:
|
|
412
|
+
reason = '.'
|
|
413
|
+
else:
|
|
414
|
+
reason = ': ' + reason + '.'
|
|
415
|
+
raise CoercionException(
|
|
416
|
+
'No common base ("join") found for %s(%s) and %s(%s)%s' %
|
|
417
|
+
(self, ', '.join(str(b) for b in self_bases),
|
|
418
|
+
other_functor, ', '.join(str(b) for b in other_bases),
|
|
419
|
+
reason))
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
class CompositeConstructionFunctor(ConstructionFunctor):
|
|
423
|
+
"""
|
|
424
|
+
A Construction Functor composed by other Construction Functors.
|
|
425
|
+
|
|
426
|
+
INPUT:
|
|
427
|
+
|
|
428
|
+
- ``F1, F2,...`` -- a list of Construction Functors. The result is the
|
|
429
|
+
composition ``F1`` followed by ``F2`` followed by ...
|
|
430
|
+
|
|
431
|
+
EXAMPLES::
|
|
432
|
+
|
|
433
|
+
sage: from sage.categories.pushout import CompositeConstructionFunctor
|
|
434
|
+
sage: F = CompositeConstructionFunctor(QQ.construction()[0], ZZ['x'].construction()[0],
|
|
435
|
+
....: QQ.construction()[0], ZZ['y'].construction()[0])
|
|
436
|
+
sage: F
|
|
437
|
+
Poly[y](FractionField(Poly[x](FractionField(...))))
|
|
438
|
+
sage: F == loads(dumps(F))
|
|
439
|
+
True
|
|
440
|
+
sage: F == CompositeConstructionFunctor(*F.all)
|
|
441
|
+
True
|
|
442
|
+
sage: F(GF(2)['t']) # needs sage.libs.ntl
|
|
443
|
+
Univariate Polynomial Ring in y
|
|
444
|
+
over Fraction Field of Univariate Polynomial Ring in x
|
|
445
|
+
over Fraction Field of Univariate Polynomial Ring in t
|
|
446
|
+
over Finite Field of size 2 (using ...)
|
|
447
|
+
"""
|
|
448
|
+
def __init__(self, *args):
|
|
449
|
+
"""
|
|
450
|
+
TESTS::
|
|
451
|
+
|
|
452
|
+
sage: from sage.categories.pushout import CompositeConstructionFunctor
|
|
453
|
+
sage: F = CompositeConstructionFunctor(QQ.construction()[0], ZZ['x'].construction()[0],
|
|
454
|
+
....: QQ.construction()[0], ZZ['y'].construction()[0])
|
|
455
|
+
sage: F
|
|
456
|
+
Poly[y](FractionField(Poly[x](FractionField(...))))
|
|
457
|
+
sage: F == CompositeConstructionFunctor(*F.all)
|
|
458
|
+
True
|
|
459
|
+
"""
|
|
460
|
+
self.all = []
|
|
461
|
+
for c in args:
|
|
462
|
+
if isinstance(c, list):
|
|
463
|
+
self.all += c
|
|
464
|
+
elif isinstance(c, CompositeConstructionFunctor):
|
|
465
|
+
self.all += c.all
|
|
466
|
+
else:
|
|
467
|
+
self.all.append(c)
|
|
468
|
+
Functor.__init__(self, self.all[0].domain(), self.all[-1].codomain())
|
|
469
|
+
|
|
470
|
+
def _apply_functor_to_morphism(self, f):
|
|
471
|
+
"""
|
|
472
|
+
Apply the functor to an object of ``self``'s domain.
|
|
473
|
+
|
|
474
|
+
TESTS::
|
|
475
|
+
|
|
476
|
+
sage: from sage.categories.pushout import CompositeConstructionFunctor
|
|
477
|
+
sage: F = CompositeConstructionFunctor(QQ.construction()[0],ZZ['x'].construction()[0],QQ.construction()[0],ZZ['y'].construction()[0])
|
|
478
|
+
sage: R.<a,b> = QQ[]
|
|
479
|
+
sage: f = R.hom([a+b, a-b])
|
|
480
|
+
sage: F(f) # indirect doctest
|
|
481
|
+
Ring endomorphism of Univariate Polynomial Ring in y over Fraction Field of Univariate Polynomial Ring in x over Fraction Field of Multivariate Polynomial Ring in a, b over Rational Field
|
|
482
|
+
Defn: Induced from base ring by
|
|
483
|
+
Ring endomorphism of Fraction Field of Univariate Polynomial Ring in x over Fraction Field of Multivariate Polynomial Ring in a, b over Rational Field
|
|
484
|
+
Defn: Induced from base ring by
|
|
485
|
+
Ring endomorphism of Univariate Polynomial Ring in x over Fraction Field of Multivariate Polynomial Ring in a, b over Rational Field
|
|
486
|
+
Defn: Induced from base ring by
|
|
487
|
+
Ring endomorphism of Fraction Field of Multivariate Polynomial Ring in a, b over Rational Field
|
|
488
|
+
Defn: a |--> a + b
|
|
489
|
+
b |--> a - b
|
|
490
|
+
"""
|
|
491
|
+
for c in self.all:
|
|
492
|
+
f = c(f)
|
|
493
|
+
return f
|
|
494
|
+
|
|
495
|
+
def _apply_functor(self, R):
|
|
496
|
+
"""
|
|
497
|
+
Apply the functor to an object of ``self``'s domain.
|
|
498
|
+
|
|
499
|
+
TESTS::
|
|
500
|
+
|
|
501
|
+
sage: from sage.categories.pushout import CompositeConstructionFunctor
|
|
502
|
+
sage: F = CompositeConstructionFunctor(QQ.construction()[0],ZZ['x'].construction()[0],QQ.construction()[0],ZZ['y'].construction()[0])
|
|
503
|
+
sage: R.<a,b> = QQ[]
|
|
504
|
+
sage: F(R) # indirect doctest
|
|
505
|
+
Univariate Polynomial Ring in y over Fraction Field of Univariate Polynomial Ring in x over Fraction Field of Multivariate Polynomial Ring in a, b over Rational Field
|
|
506
|
+
"""
|
|
507
|
+
for c in self.all:
|
|
508
|
+
R = c(R)
|
|
509
|
+
return R
|
|
510
|
+
|
|
511
|
+
def __eq__(self, other):
|
|
512
|
+
"""
|
|
513
|
+
TESTS::
|
|
514
|
+
|
|
515
|
+
sage: from sage.categories.pushout import CompositeConstructionFunctor
|
|
516
|
+
sage: F = CompositeConstructionFunctor(QQ.construction()[0],ZZ['x'].construction()[0],QQ.construction()[0],ZZ['y'].construction()[0])
|
|
517
|
+
sage: F == loads(dumps(F)) # indirect doctest
|
|
518
|
+
True
|
|
519
|
+
"""
|
|
520
|
+
if isinstance(other, CompositeConstructionFunctor):
|
|
521
|
+
return self.all == other.all
|
|
522
|
+
else:
|
|
523
|
+
return type(self) is type(other)
|
|
524
|
+
|
|
525
|
+
def __ne__(self, other):
|
|
526
|
+
"""
|
|
527
|
+
Check whether ``self`` is not equal to ``other``.
|
|
528
|
+
|
|
529
|
+
EXAMPLES::
|
|
530
|
+
|
|
531
|
+
sage: from sage.categories.pushout import CompositeConstructionFunctor
|
|
532
|
+
sage: F = CompositeConstructionFunctor(QQ.construction()[0],ZZ['x'].construction()[0],QQ.construction()[0],ZZ['y'].construction()[0])
|
|
533
|
+
sage: F != loads(dumps(F)) # indirect doctest
|
|
534
|
+
False
|
|
535
|
+
"""
|
|
536
|
+
return not (self == other)
|
|
537
|
+
|
|
538
|
+
__hash__ = ConstructionFunctor.__hash__
|
|
539
|
+
|
|
540
|
+
def __mul__(self, other):
|
|
541
|
+
"""
|
|
542
|
+
Compose construction functors to a composite construction functor, unless one of them is the identity.
|
|
543
|
+
|
|
544
|
+
.. NOTE::
|
|
545
|
+
|
|
546
|
+
The product is in functorial notation, i.e., when applying the product to an object
|
|
547
|
+
then the second factor is applied first.
|
|
548
|
+
|
|
549
|
+
EXAMPLES::
|
|
550
|
+
|
|
551
|
+
sage: from sage.categories.pushout import CompositeConstructionFunctor
|
|
552
|
+
sage: F1 = CompositeConstructionFunctor(QQ.construction()[0],ZZ['x'].construction()[0])
|
|
553
|
+
sage: F2 = CompositeConstructionFunctor(QQ.construction()[0],ZZ['y'].construction()[0])
|
|
554
|
+
sage: F1*F2
|
|
555
|
+
Poly[x](FractionField(Poly[y](FractionField(...))))
|
|
556
|
+
"""
|
|
557
|
+
if isinstance(self, CompositeConstructionFunctor):
|
|
558
|
+
all = [other] + self.all
|
|
559
|
+
elif isinstance(other, IdentityConstructionFunctor):
|
|
560
|
+
return self
|
|
561
|
+
else:
|
|
562
|
+
all = other.all + [self]
|
|
563
|
+
return CompositeConstructionFunctor(*all)
|
|
564
|
+
|
|
565
|
+
def _repr_(self):
|
|
566
|
+
"""
|
|
567
|
+
TESTS::
|
|
568
|
+
|
|
569
|
+
sage: from sage.categories.pushout import CompositeConstructionFunctor
|
|
570
|
+
sage: F = CompositeConstructionFunctor(QQ.construction()[0],ZZ['x'].construction()[0],QQ.construction()[0],ZZ['y'].construction()[0])
|
|
571
|
+
sage: F # indirect doctest
|
|
572
|
+
Poly[y](FractionField(Poly[x](FractionField(...))))
|
|
573
|
+
"""
|
|
574
|
+
s = "..."
|
|
575
|
+
for c in self.all:
|
|
576
|
+
s = "%s(%s)" % (c, s)
|
|
577
|
+
return s
|
|
578
|
+
|
|
579
|
+
def expand(self):
|
|
580
|
+
"""
|
|
581
|
+
Return expansion of a CompositeConstructionFunctor.
|
|
582
|
+
|
|
583
|
+
.. NOTE::
|
|
584
|
+
|
|
585
|
+
The product over the list of components, as returned by
|
|
586
|
+
the ``expand()`` method, is equal to ``self``.
|
|
587
|
+
|
|
588
|
+
EXAMPLES::
|
|
589
|
+
|
|
590
|
+
sage: from sage.categories.pushout import CompositeConstructionFunctor
|
|
591
|
+
sage: F = CompositeConstructionFunctor(QQ.construction()[0],
|
|
592
|
+
....: ZZ['x'].construction()[0],
|
|
593
|
+
....: QQ.construction()[0],
|
|
594
|
+
....: ZZ['y'].construction()[0])
|
|
595
|
+
sage: F
|
|
596
|
+
Poly[y](FractionField(Poly[x](FractionField(...))))
|
|
597
|
+
sage: prod(F.expand()) == F
|
|
598
|
+
True
|
|
599
|
+
"""
|
|
600
|
+
return list(reversed(self.all))
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
class IdentityConstructionFunctor(ConstructionFunctor):
|
|
604
|
+
"""
|
|
605
|
+
A construction functor that is the identity functor.
|
|
606
|
+
|
|
607
|
+
TESTS::
|
|
608
|
+
|
|
609
|
+
sage: from sage.categories.pushout import IdentityConstructionFunctor
|
|
610
|
+
sage: I = IdentityConstructionFunctor()
|
|
611
|
+
sage: I(RR) is RR
|
|
612
|
+
True
|
|
613
|
+
sage: I == loads(dumps(I))
|
|
614
|
+
True
|
|
615
|
+
"""
|
|
616
|
+
rank = -100
|
|
617
|
+
|
|
618
|
+
def __init__(self):
|
|
619
|
+
"""
|
|
620
|
+
TESTS::
|
|
621
|
+
|
|
622
|
+
sage: from sage.categories.pushout import IdentityConstructionFunctor
|
|
623
|
+
sage: I = IdentityConstructionFunctor()
|
|
624
|
+
sage: IdentityFunctor(Sets()) == I
|
|
625
|
+
True
|
|
626
|
+
sage: I(RR) is RR
|
|
627
|
+
True
|
|
628
|
+
"""
|
|
629
|
+
from sage.categories.sets_cat import Sets
|
|
630
|
+
ConstructionFunctor.__init__(self, Sets(), Sets())
|
|
631
|
+
|
|
632
|
+
def _apply_functor(self, x):
|
|
633
|
+
"""
|
|
634
|
+
Return the argument unaltered.
|
|
635
|
+
|
|
636
|
+
TESTS::
|
|
637
|
+
|
|
638
|
+
sage: from sage.categories.pushout import IdentityConstructionFunctor
|
|
639
|
+
sage: I = IdentityConstructionFunctor()
|
|
640
|
+
sage: I(RR) is RR # indirect doctest
|
|
641
|
+
True
|
|
642
|
+
"""
|
|
643
|
+
return x
|
|
644
|
+
|
|
645
|
+
def _apply_functor_to_morphism(self, f):
|
|
646
|
+
"""
|
|
647
|
+
Return the argument unaltered.
|
|
648
|
+
|
|
649
|
+
TESTS::
|
|
650
|
+
|
|
651
|
+
sage: from sage.categories.pushout import IdentityConstructionFunctor
|
|
652
|
+
sage: I = IdentityConstructionFunctor()
|
|
653
|
+
sage: f = ZZ['t'].hom(['x'],QQ['x'])
|
|
654
|
+
sage: I(f) is f # indirect doctest
|
|
655
|
+
True
|
|
656
|
+
"""
|
|
657
|
+
return f
|
|
658
|
+
|
|
659
|
+
def __eq__(self, other):
|
|
660
|
+
"""
|
|
661
|
+
TESTS::
|
|
662
|
+
|
|
663
|
+
sage: from sage.categories.pushout import IdentityConstructionFunctor
|
|
664
|
+
sage: I = IdentityConstructionFunctor()
|
|
665
|
+
sage: I == IdentityFunctor(Sets()) # indirect doctest
|
|
666
|
+
True
|
|
667
|
+
sage: I == QQ.construction()[0]
|
|
668
|
+
False
|
|
669
|
+
"""
|
|
670
|
+
c = (type(self) is type(other))
|
|
671
|
+
if not c:
|
|
672
|
+
if isinstance(other, IdentityFunctor_generic):
|
|
673
|
+
return True
|
|
674
|
+
return c
|
|
675
|
+
|
|
676
|
+
def __ne__(self, other):
|
|
677
|
+
"""
|
|
678
|
+
Check whether ``self`` is not equal to ``other``.
|
|
679
|
+
|
|
680
|
+
EXAMPLES::
|
|
681
|
+
|
|
682
|
+
sage: from sage.categories.pushout import IdentityConstructionFunctor
|
|
683
|
+
sage: I = IdentityConstructionFunctor()
|
|
684
|
+
sage: I != IdentityFunctor(Sets()) # indirect doctest
|
|
685
|
+
False
|
|
686
|
+
sage: I != QQ.construction()[0]
|
|
687
|
+
True
|
|
688
|
+
"""
|
|
689
|
+
return not (self == other)
|
|
690
|
+
|
|
691
|
+
__hash__ = ConstructionFunctor.__hash__
|
|
692
|
+
|
|
693
|
+
def __mul__(self, other):
|
|
694
|
+
"""
|
|
695
|
+
Compose construction functors to a composite construction functor, unless one of them is the identity.
|
|
696
|
+
|
|
697
|
+
.. NOTE::
|
|
698
|
+
|
|
699
|
+
The product is in functorial notation, i.e., when applying the
|
|
700
|
+
product to an object then the second factor is applied first.
|
|
701
|
+
|
|
702
|
+
TESTS::
|
|
703
|
+
|
|
704
|
+
sage: from sage.categories.pushout import IdentityConstructionFunctor
|
|
705
|
+
sage: I = IdentityConstructionFunctor()
|
|
706
|
+
sage: F = QQ.construction()[0]
|
|
707
|
+
sage: P = ZZ['t'].construction()[0]
|
|
708
|
+
sage: I*F is F # indirect doctest
|
|
709
|
+
True
|
|
710
|
+
sage: F*I is F
|
|
711
|
+
True
|
|
712
|
+
sage: I*P is P
|
|
713
|
+
True
|
|
714
|
+
sage: P*I is P
|
|
715
|
+
True
|
|
716
|
+
"""
|
|
717
|
+
if isinstance(self, IdentityConstructionFunctor):
|
|
718
|
+
return other
|
|
719
|
+
else:
|
|
720
|
+
return self
|
|
721
|
+
|
|
722
|
+
|
|
723
|
+
class MultivariateConstructionFunctor(ConstructionFunctor):
|
|
724
|
+
"""
|
|
725
|
+
An abstract base class for functors that take
|
|
726
|
+
multiple inputs (e.g. Cartesian products).
|
|
727
|
+
|
|
728
|
+
TESTS::
|
|
729
|
+
|
|
730
|
+
sage: from sage.categories.pushout import pushout
|
|
731
|
+
sage: A = cartesian_product((QQ['z'], QQ))
|
|
732
|
+
sage: B = cartesian_product((ZZ['t']['z'], QQ))
|
|
733
|
+
sage: pushout(A, B)
|
|
734
|
+
The Cartesian product of (Univariate Polynomial Ring in z over
|
|
735
|
+
Univariate Polynomial Ring in t over Rational Field,
|
|
736
|
+
Rational Field)
|
|
737
|
+
sage: A.construction()
|
|
738
|
+
(The cartesian_product functorial construction,
|
|
739
|
+
(Univariate Polynomial Ring in z over Rational Field, Rational Field))
|
|
740
|
+
sage: pushout(A, B)
|
|
741
|
+
The Cartesian product of (Univariate Polynomial Ring in z over Univariate Polynomial Ring in t over Rational Field, Rational Field)
|
|
742
|
+
"""
|
|
743
|
+
def common_base(self, other_functor, self_bases, other_bases):
|
|
744
|
+
r"""
|
|
745
|
+
This function is called by :func:`pushout` when no common parent
|
|
746
|
+
is found in the construction tower.
|
|
747
|
+
|
|
748
|
+
INPUT:
|
|
749
|
+
|
|
750
|
+
- ``other_functor`` -- a construction functor
|
|
751
|
+
|
|
752
|
+
- ``self_bases`` -- the arguments passed to this functor
|
|
753
|
+
|
|
754
|
+
- ``other_bases`` -- the arguments passed to the functor
|
|
755
|
+
``other_functor``
|
|
756
|
+
|
|
757
|
+
OUTPUT: a parent
|
|
758
|
+
|
|
759
|
+
If no common base is found a :class:`sage.structure.coerce_exceptions.CoercionException`
|
|
760
|
+
is raised.
|
|
761
|
+
|
|
762
|
+
.. NOTE::
|
|
763
|
+
|
|
764
|
+
Overload this function in derived class, see
|
|
765
|
+
e.g. :class:`MultivariateConstructionFunctor`.
|
|
766
|
+
|
|
767
|
+
TESTS::
|
|
768
|
+
|
|
769
|
+
sage: from sage.categories.pushout import pushout
|
|
770
|
+
sage: pushout(cartesian_product([ZZ]), QQ) # indirect doctest
|
|
771
|
+
Traceback (most recent call last):
|
|
772
|
+
...
|
|
773
|
+
CoercionException: No common base ("join") found for
|
|
774
|
+
The cartesian_product functorial construction(Integer Ring) and FractionField(Integer Ring):
|
|
775
|
+
(Multivariate) functors are incompatible.
|
|
776
|
+
sage: pushout(cartesian_product([ZZ]), cartesian_product([ZZ, QQ])) # indirect doctest
|
|
777
|
+
Traceback (most recent call last):
|
|
778
|
+
...
|
|
779
|
+
CoercionException: No common base ("join") found ...
|
|
780
|
+
"""
|
|
781
|
+
if self != other_functor:
|
|
782
|
+
self._raise_common_base_exception_(
|
|
783
|
+
other_functor, self_bases, other_bases,
|
|
784
|
+
'(Multivariate) functors are incompatible')
|
|
785
|
+
if len(self_bases) != len(other_bases):
|
|
786
|
+
self._raise_common_base_exception_(
|
|
787
|
+
other_functor, self_bases, other_bases,
|
|
788
|
+
'Functors need the same number of arguments')
|
|
789
|
+
from sage.structure.element import coercion_model
|
|
790
|
+
Z_bases = tuple(coercion_model.common_parent(S, O)
|
|
791
|
+
for S, O in zip(self_bases, other_bases))
|
|
792
|
+
return self(Z_bases)
|
|
793
|
+
|
|
794
|
+
|
|
795
|
+
class PolynomialFunctor(ConstructionFunctor):
|
|
796
|
+
"""
|
|
797
|
+
Construction functor for univariate polynomial rings.
|
|
798
|
+
|
|
799
|
+
EXAMPLES::
|
|
800
|
+
|
|
801
|
+
sage: P = ZZ['t'].construction()[0]
|
|
802
|
+
sage: P(GF(3))
|
|
803
|
+
Univariate Polynomial Ring in t over Finite Field of size 3
|
|
804
|
+
sage: P == loads(dumps(P))
|
|
805
|
+
True
|
|
806
|
+
sage: R.<x,y> = GF(5)[]
|
|
807
|
+
sage: f = R.hom([x + 2*y, 3*x - y], R)
|
|
808
|
+
sage: P(f)((x+y) * P(R).0)
|
|
809
|
+
(-x + y)*t
|
|
810
|
+
|
|
811
|
+
By :issue:`9944`, the construction functor distinguishes sparse and
|
|
812
|
+
dense polynomial rings. Before, the following example failed::
|
|
813
|
+
|
|
814
|
+
sage: R.<x> = PolynomialRing(GF(5), sparse=True)
|
|
815
|
+
sage: F, B = R.construction()
|
|
816
|
+
sage: F(B) is R
|
|
817
|
+
True
|
|
818
|
+
sage: S.<x> = PolynomialRing(ZZ)
|
|
819
|
+
sage: R.has_coerce_map_from(S)
|
|
820
|
+
False
|
|
821
|
+
sage: S.has_coerce_map_from(R)
|
|
822
|
+
False
|
|
823
|
+
sage: S.0 + R.0
|
|
824
|
+
2*x
|
|
825
|
+
sage: (S.0 + R.0).parent()
|
|
826
|
+
Univariate Polynomial Ring in x over Finite Field of size 5
|
|
827
|
+
sage: (S.0 + R.0).parent().is_sparse()
|
|
828
|
+
False
|
|
829
|
+
"""
|
|
830
|
+
rank = 9
|
|
831
|
+
|
|
832
|
+
def __init__(self, var, multi_variate=False, sparse=False, implementation=None):
|
|
833
|
+
"""
|
|
834
|
+
TESTS::
|
|
835
|
+
|
|
836
|
+
sage: from sage.categories.pushout import PolynomialFunctor
|
|
837
|
+
sage: P = PolynomialFunctor('x')
|
|
838
|
+
sage: P(GF(3))
|
|
839
|
+
Univariate Polynomial Ring in x over Finite Field of size 3
|
|
840
|
+
|
|
841
|
+
There is an optional parameter ``multi_variate``, but
|
|
842
|
+
apparently it is not used::
|
|
843
|
+
|
|
844
|
+
sage: Q = PolynomialFunctor('x',multi_variate=True)
|
|
845
|
+
sage: Q(ZZ)
|
|
846
|
+
Univariate Polynomial Ring in x over Integer Ring
|
|
847
|
+
sage: Q == P
|
|
848
|
+
True
|
|
849
|
+
"""
|
|
850
|
+
from .rings import Rings
|
|
851
|
+
Functor.__init__(self, Rings(), Rings())
|
|
852
|
+
self.var = var
|
|
853
|
+
self.multi_variate = multi_variate
|
|
854
|
+
self.sparse = sparse
|
|
855
|
+
self.implementation = implementation
|
|
856
|
+
|
|
857
|
+
def _apply_functor(self, R):
|
|
858
|
+
"""
|
|
859
|
+
Apply the functor to an object of ``self``'s domain.
|
|
860
|
+
|
|
861
|
+
TESTS::
|
|
862
|
+
|
|
863
|
+
sage: P = ZZ['x'].construction()[0]
|
|
864
|
+
sage: P(GF(3)) # indirect doctest
|
|
865
|
+
Univariate Polynomial Ring in x over Finite Field of size 3
|
|
866
|
+
"""
|
|
867
|
+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
|
|
868
|
+
kwds = {}
|
|
869
|
+
if self.implementation:
|
|
870
|
+
kwds['implementation'] = self.implementation
|
|
871
|
+
return PolynomialRing(R, self.var, sparse=self.sparse, **kwds)
|
|
872
|
+
|
|
873
|
+
def _apply_functor_to_morphism(self, f):
|
|
874
|
+
"""
|
|
875
|
+
Apply the functor ``self`` to the morphism `f`.
|
|
876
|
+
|
|
877
|
+
TESTS::
|
|
878
|
+
|
|
879
|
+
sage: P = ZZ['x'].construction()[0]
|
|
880
|
+
sage: P(ZZ.hom(GF(3))) # indirect doctest
|
|
881
|
+
Ring morphism:
|
|
882
|
+
From: Univariate Polynomial Ring in x over Integer Ring
|
|
883
|
+
To: Univariate Polynomial Ring in x over Finite Field of size 3
|
|
884
|
+
Defn: Induced from base ring by
|
|
885
|
+
Natural morphism:
|
|
886
|
+
From: Integer Ring
|
|
887
|
+
To: Finite Field of size 3
|
|
888
|
+
"""
|
|
889
|
+
from sage.rings.polynomial.polynomial_ring_homomorphism import (
|
|
890
|
+
PolynomialRingHomomorphism_from_base,
|
|
891
|
+
)
|
|
892
|
+
R = self._apply_functor(f.domain())
|
|
893
|
+
S = self._apply_functor(f.codomain())
|
|
894
|
+
return PolynomialRingHomomorphism_from_base(R.Hom(S), f)
|
|
895
|
+
|
|
896
|
+
def __eq__(self, other):
|
|
897
|
+
"""
|
|
898
|
+
TESTS::
|
|
899
|
+
|
|
900
|
+
sage: from sage.categories.pushout import MultiPolynomialFunctor
|
|
901
|
+
sage: Q = MultiPolynomialFunctor(('x',),'lex')
|
|
902
|
+
sage: P = ZZ['x'].construction()[0]
|
|
903
|
+
sage: P
|
|
904
|
+
Poly[x]
|
|
905
|
+
sage: Q
|
|
906
|
+
MPoly[x]
|
|
907
|
+
sage: P == Q
|
|
908
|
+
True
|
|
909
|
+
sage: P == loads(dumps(P))
|
|
910
|
+
True
|
|
911
|
+
sage: P == QQ.construction()[0]
|
|
912
|
+
False
|
|
913
|
+
"""
|
|
914
|
+
if isinstance(other, PolynomialFunctor):
|
|
915
|
+
return self.var == other.var
|
|
916
|
+
elif isinstance(other, MultiPolynomialFunctor):
|
|
917
|
+
return (other == self)
|
|
918
|
+
else:
|
|
919
|
+
return False
|
|
920
|
+
|
|
921
|
+
def __ne__(self, other):
|
|
922
|
+
"""
|
|
923
|
+
Check whether ``self`` is not equal to ``other``.
|
|
924
|
+
|
|
925
|
+
EXAMPLES::
|
|
926
|
+
|
|
927
|
+
sage: from sage.categories.pushout import MultiPolynomialFunctor
|
|
928
|
+
sage: Q = MultiPolynomialFunctor(('x',),'lex')
|
|
929
|
+
sage: P = ZZ['x'].construction()[0]
|
|
930
|
+
sage: P != Q
|
|
931
|
+
False
|
|
932
|
+
sage: P != loads(dumps(P))
|
|
933
|
+
False
|
|
934
|
+
sage: P != QQ.construction()[0]
|
|
935
|
+
True
|
|
936
|
+
"""
|
|
937
|
+
return not (self == other)
|
|
938
|
+
|
|
939
|
+
__hash__ = ConstructionFunctor.__hash__
|
|
940
|
+
|
|
941
|
+
def merge(self, other):
|
|
942
|
+
"""
|
|
943
|
+
Merge ``self`` with another construction functor, or return ``None``.
|
|
944
|
+
|
|
945
|
+
.. NOTE::
|
|
946
|
+
|
|
947
|
+
Internally, the merging is delegated to the merging of
|
|
948
|
+
multipolynomial construction functors. But in effect,
|
|
949
|
+
this does the same as the default implementation, that
|
|
950
|
+
returns ``None`` unless the to-be-merged functors coincide.
|
|
951
|
+
|
|
952
|
+
EXAMPLES::
|
|
953
|
+
|
|
954
|
+
sage: P = ZZ['x'].construction()[0]
|
|
955
|
+
sage: Q = ZZ['y','x'].construction()[0]
|
|
956
|
+
sage: P.merge(Q)
|
|
957
|
+
sage: P.merge(P) is P
|
|
958
|
+
True
|
|
959
|
+
"""
|
|
960
|
+
if isinstance(other, MultiPolynomialFunctor):
|
|
961
|
+
return other.merge(self)
|
|
962
|
+
elif self == other:
|
|
963
|
+
# i.e., they only differ in sparsity
|
|
964
|
+
if not self.sparse:
|
|
965
|
+
return self
|
|
966
|
+
return other
|
|
967
|
+
else:
|
|
968
|
+
return None
|
|
969
|
+
|
|
970
|
+
def _repr_(self):
|
|
971
|
+
"""
|
|
972
|
+
TESTS::
|
|
973
|
+
|
|
974
|
+
sage: P = ZZ['x'].construction()[0]
|
|
975
|
+
sage: P # indirect doctest
|
|
976
|
+
Poly[x]
|
|
977
|
+
"""
|
|
978
|
+
return "Poly[%s]" % self.var
|
|
979
|
+
|
|
980
|
+
|
|
981
|
+
class MultiPolynomialFunctor(ConstructionFunctor):
|
|
982
|
+
"""
|
|
983
|
+
A constructor for multivariate polynomial rings.
|
|
984
|
+
|
|
985
|
+
EXAMPLES::
|
|
986
|
+
|
|
987
|
+
sage: P.<x,y> = ZZ[]
|
|
988
|
+
sage: F = P.construction()[0]; F
|
|
989
|
+
MPoly[x,y]
|
|
990
|
+
sage: A.<a,b> = GF(5)[]
|
|
991
|
+
sage: F(A)
|
|
992
|
+
Multivariate Polynomial Ring in x, y
|
|
993
|
+
over Multivariate Polynomial Ring in a, b over Finite Field of size 5
|
|
994
|
+
sage: f = A.hom([a+b, a-b], A)
|
|
995
|
+
sage: F(f)
|
|
996
|
+
Ring endomorphism of Multivariate Polynomial Ring in x, y
|
|
997
|
+
over Multivariate Polynomial Ring in a, b over Finite Field of size 5
|
|
998
|
+
Defn: Induced from base ring by
|
|
999
|
+
Ring endomorphism of Multivariate Polynomial Ring in a, b over Finite Field of size 5
|
|
1000
|
+
Defn: a |--> a + b
|
|
1001
|
+
b |--> a - b
|
|
1002
|
+
sage: F(f)(F(A)(x)*a)
|
|
1003
|
+
(a + b)*x
|
|
1004
|
+
"""
|
|
1005
|
+
|
|
1006
|
+
rank = 9
|
|
1007
|
+
|
|
1008
|
+
def __init__(self, vars, term_order):
|
|
1009
|
+
"""
|
|
1010
|
+
EXAMPLES::
|
|
1011
|
+
|
|
1012
|
+
sage: F = sage.categories.pushout.MultiPolynomialFunctor(['x','y'], None)
|
|
1013
|
+
sage: F
|
|
1014
|
+
MPoly[x,y]
|
|
1015
|
+
sage: F(ZZ)
|
|
1016
|
+
Multivariate Polynomial Ring in x, y over Integer Ring
|
|
1017
|
+
sage: F(CC) # needs sage.rings.real_mpfr
|
|
1018
|
+
Multivariate Polynomial Ring in x, y over Complex Field with 53 bits of precision
|
|
1019
|
+
"""
|
|
1020
|
+
Functor.__init__(self, Rings(), Rings())
|
|
1021
|
+
self.vars = vars
|
|
1022
|
+
self.term_order = term_order
|
|
1023
|
+
|
|
1024
|
+
def _apply_functor(self, R):
|
|
1025
|
+
"""
|
|
1026
|
+
Apply the functor to an object of ``self``'s domain.
|
|
1027
|
+
|
|
1028
|
+
EXAMPLES::
|
|
1029
|
+
|
|
1030
|
+
sage: R.<x,y,z> = QQ[]
|
|
1031
|
+
sage: F = R.construction()[0]; F
|
|
1032
|
+
MPoly[x,y,z]
|
|
1033
|
+
sage: type(F)
|
|
1034
|
+
<class 'sage.categories.pushout.MultiPolynomialFunctor'>
|
|
1035
|
+
sage: F(ZZ) # indirect doctest
|
|
1036
|
+
Multivariate Polynomial Ring in x, y, z over Integer Ring
|
|
1037
|
+
sage: F(RR) # indirect doctest # needs sage.rings.real_mpfr
|
|
1038
|
+
Multivariate Polynomial Ring in x, y, z over Real Field with 53 bits of precision
|
|
1039
|
+
"""
|
|
1040
|
+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
|
|
1041
|
+
return PolynomialRing(R, self.vars)
|
|
1042
|
+
|
|
1043
|
+
def __eq__(self, other):
|
|
1044
|
+
"""
|
|
1045
|
+
EXAMPLES::
|
|
1046
|
+
|
|
1047
|
+
sage: F = ZZ['x,y,z'].construction()[0]
|
|
1048
|
+
sage: G = QQ['x,y,z'].construction()[0]
|
|
1049
|
+
sage: F == G
|
|
1050
|
+
True
|
|
1051
|
+
sage: G == loads(dumps(G))
|
|
1052
|
+
True
|
|
1053
|
+
sage: G = ZZ['x,y'].construction()[0]
|
|
1054
|
+
sage: F == G
|
|
1055
|
+
False
|
|
1056
|
+
"""
|
|
1057
|
+
if isinstance(other, MultiPolynomialFunctor):
|
|
1058
|
+
return (self.vars == other.vars and
|
|
1059
|
+
self.term_order == other.term_order)
|
|
1060
|
+
elif isinstance(other, PolynomialFunctor):
|
|
1061
|
+
return self.vars == (other.var,)
|
|
1062
|
+
else:
|
|
1063
|
+
return False
|
|
1064
|
+
|
|
1065
|
+
def __ne__(self, other):
|
|
1066
|
+
"""
|
|
1067
|
+
Check whether ``self`` is not equal to ``other``.
|
|
1068
|
+
|
|
1069
|
+
EXAMPLES::
|
|
1070
|
+
|
|
1071
|
+
sage: F = ZZ['x,y,z'].construction()[0]
|
|
1072
|
+
sage: G = QQ['x,y,z'].construction()[0]
|
|
1073
|
+
sage: F != G
|
|
1074
|
+
False
|
|
1075
|
+
sage: G != loads(dumps(G))
|
|
1076
|
+
False
|
|
1077
|
+
sage: G = ZZ['x,y'].construction()[0]
|
|
1078
|
+
sage: F != G
|
|
1079
|
+
True
|
|
1080
|
+
"""
|
|
1081
|
+
return not (self == other)
|
|
1082
|
+
|
|
1083
|
+
__hash__ = ConstructionFunctor.__hash__
|
|
1084
|
+
|
|
1085
|
+
def __mul__(self, other):
|
|
1086
|
+
"""
|
|
1087
|
+
If two MPoly functors are given in a row, form a single MPoly functor
|
|
1088
|
+
with all of the variables.
|
|
1089
|
+
|
|
1090
|
+
EXAMPLES::
|
|
1091
|
+
|
|
1092
|
+
sage: F = sage.categories.pushout.MultiPolynomialFunctor(['x','y'], None)
|
|
1093
|
+
sage: G = sage.categories.pushout.MultiPolynomialFunctor(['t'], None)
|
|
1094
|
+
sage: G*F
|
|
1095
|
+
MPoly[x,y,t]
|
|
1096
|
+
"""
|
|
1097
|
+
if isinstance(other, IdentityConstructionFunctor):
|
|
1098
|
+
return self
|
|
1099
|
+
if isinstance(other, MultiPolynomialFunctor):
|
|
1100
|
+
if self.term_order != other.term_order:
|
|
1101
|
+
raise CoercionException("Incompatible term orders (%s,%s)." % (self.term_order, other.term_order))
|
|
1102
|
+
if set(self.vars).intersection(other.vars):
|
|
1103
|
+
raise CoercionException("Overlapping variables (%s,%s)" % (self.vars, other.vars))
|
|
1104
|
+
return MultiPolynomialFunctor(other.vars + self.vars, self.term_order)
|
|
1105
|
+
elif (isinstance(other, CompositeConstructionFunctor)
|
|
1106
|
+
and isinstance(other.all[-1], MultiPolynomialFunctor)):
|
|
1107
|
+
return CompositeConstructionFunctor(other.all[:-1], self * other.all[-1])
|
|
1108
|
+
else:
|
|
1109
|
+
return CompositeConstructionFunctor(other, self)
|
|
1110
|
+
|
|
1111
|
+
def merge(self, other):
|
|
1112
|
+
"""
|
|
1113
|
+
Merge ``self`` with another construction functor, or return ``None``.
|
|
1114
|
+
|
|
1115
|
+
EXAMPLES::
|
|
1116
|
+
|
|
1117
|
+
sage: F = sage.categories.pushout.MultiPolynomialFunctor(['x','y'], None)
|
|
1118
|
+
sage: G = sage.categories.pushout.MultiPolynomialFunctor(['t'], None)
|
|
1119
|
+
sage: F.merge(G) is None
|
|
1120
|
+
True
|
|
1121
|
+
sage: F.merge(F)
|
|
1122
|
+
MPoly[x,y]
|
|
1123
|
+
"""
|
|
1124
|
+
if self == other:
|
|
1125
|
+
return self
|
|
1126
|
+
else:
|
|
1127
|
+
return None
|
|
1128
|
+
|
|
1129
|
+
def expand(self):
|
|
1130
|
+
"""
|
|
1131
|
+
Decompose ``self`` into a list of construction functors.
|
|
1132
|
+
|
|
1133
|
+
EXAMPLES::
|
|
1134
|
+
|
|
1135
|
+
sage: F = QQ['x,y,z,t'].construction()[0]; F
|
|
1136
|
+
MPoly[x,y,z,t]
|
|
1137
|
+
sage: F.expand()
|
|
1138
|
+
[MPoly[t], MPoly[z], MPoly[y], MPoly[x]]
|
|
1139
|
+
|
|
1140
|
+
Now an actual use case::
|
|
1141
|
+
|
|
1142
|
+
sage: R.<x,y,z> = ZZ[]
|
|
1143
|
+
sage: S.<z,t> = QQ[]
|
|
1144
|
+
sage: x+t
|
|
1145
|
+
x + t
|
|
1146
|
+
sage: parent(x+t)
|
|
1147
|
+
Multivariate Polynomial Ring in x, y, z, t over Rational Field
|
|
1148
|
+
sage: T.<y,s> = QQ[]
|
|
1149
|
+
sage: x + s
|
|
1150
|
+
Traceback (most recent call last):
|
|
1151
|
+
...
|
|
1152
|
+
TypeError: unsupported operand parent(s) for +:
|
|
1153
|
+
'Multivariate Polynomial Ring in x, y, z over Integer Ring' and
|
|
1154
|
+
'Multivariate Polynomial Ring in y, s over Rational Field'
|
|
1155
|
+
sage: R = PolynomialRing(ZZ, 'x', 50)
|
|
1156
|
+
sage: S = PolynomialRing(GF(5), 'x', 20)
|
|
1157
|
+
sage: R.gen(0) + S.gen(0)
|
|
1158
|
+
2*x0
|
|
1159
|
+
"""
|
|
1160
|
+
if len(self.vars) <= 1:
|
|
1161
|
+
return [self]
|
|
1162
|
+
else:
|
|
1163
|
+
return [MultiPolynomialFunctor((x,), self.term_order) for x in reversed(self.vars)]
|
|
1164
|
+
|
|
1165
|
+
def _repr_(self):
|
|
1166
|
+
"""
|
|
1167
|
+
TESTS::
|
|
1168
|
+
|
|
1169
|
+
sage: QQ['x,y,z,t'].construction()[0]
|
|
1170
|
+
MPoly[x,y,z,t]
|
|
1171
|
+
"""
|
|
1172
|
+
return "MPoly[%s]" % ','.join(self.vars)
|
|
1173
|
+
|
|
1174
|
+
|
|
1175
|
+
class InfinitePolynomialFunctor(ConstructionFunctor):
|
|
1176
|
+
r"""
|
|
1177
|
+
A Construction Functor for Infinite Polynomial Rings (see :mod:`~sage.rings.polynomial.infinite_polynomial_ring`).
|
|
1178
|
+
|
|
1179
|
+
AUTHOR:
|
|
1180
|
+
|
|
1181
|
+
-- Simon King
|
|
1182
|
+
|
|
1183
|
+
This construction functor is used to provide uniqueness of infinite polynomial rings as parent structures.
|
|
1184
|
+
As usual, the construction functor allows for constructing pushouts.
|
|
1185
|
+
|
|
1186
|
+
Another purpose is to avoid name conflicts of variables of the to-be-constructed infinite polynomial ring with
|
|
1187
|
+
variables of the base ring, and moreover to keep the internal structure of an Infinite Polynomial Ring as simple
|
|
1188
|
+
as possible: If variables `v_1,...,v_n` of the given base ring generate an *ordered* sub-monoid of the monomials
|
|
1189
|
+
of the ambient Infinite Polynomial Ring, then they are removed from the base ring and merged with the generators
|
|
1190
|
+
of the ambient ring. However, if the orders don't match, an error is raised, since there was a name conflict
|
|
1191
|
+
without merging.
|
|
1192
|
+
|
|
1193
|
+
EXAMPLES::
|
|
1194
|
+
|
|
1195
|
+
sage: A.<a,b> = InfinitePolynomialRing(ZZ['t'])
|
|
1196
|
+
sage: A.construction()
|
|
1197
|
+
[InfPoly{[a,b], "lex", "dense"},
|
|
1198
|
+
Univariate Polynomial Ring in t over Integer Ring]
|
|
1199
|
+
sage: type(_[0])
|
|
1200
|
+
<class 'sage.categories.pushout.InfinitePolynomialFunctor'>
|
|
1201
|
+
sage: B.<x,y,a_3,a_1> = PolynomialRing(QQ, order='lex')
|
|
1202
|
+
sage: B.construction()
|
|
1203
|
+
(MPoly[x,y,a_3,a_1], Rational Field)
|
|
1204
|
+
sage: A.construction()[0] * B.construction()[0]
|
|
1205
|
+
InfPoly{[a,b], "lex", "dense"}(MPoly[x,y](...))
|
|
1206
|
+
|
|
1207
|
+
Apparently the variables `a_1,a_3` of the polynomial ring are merged with the variables
|
|
1208
|
+
`a_0, a_1, a_2, ...` of the infinite polynomial ring; indeed, they form an ordered sub-structure.
|
|
1209
|
+
However, if the polynomial ring was given a different ordering, merging would not be allowed,
|
|
1210
|
+
resulting in a name conflict::
|
|
1211
|
+
|
|
1212
|
+
sage: R = PolynomialRing(QQ, names=['x','y','a_3','a_1'])
|
|
1213
|
+
sage: A.construction()[0] * R.construction()[0]
|
|
1214
|
+
Traceback (most recent call last):
|
|
1215
|
+
...
|
|
1216
|
+
CoercionException: Incompatible term orders lex, degrevlex
|
|
1217
|
+
|
|
1218
|
+
In an infinite polynomial ring with generator `a_\ast`, the variable `a_3` will always be greater
|
|
1219
|
+
than the variable `a_1`. Hence, the orders are incompatible in the next example as well::
|
|
1220
|
+
|
|
1221
|
+
sage: R = PolynomialRing(QQ, names=['x','y','a_1','a_3'], order='lex')
|
|
1222
|
+
sage: A.construction()[0] * R.construction()[0]
|
|
1223
|
+
Traceback (most recent call last):
|
|
1224
|
+
...
|
|
1225
|
+
CoercionException: Overlapping variables (('a', 'b'),['a_1', 'a_3'])
|
|
1226
|
+
are incompatible
|
|
1227
|
+
|
|
1228
|
+
Another requirement is that after merging the order of the remaining variables must be unique.
|
|
1229
|
+
This is not the case in the following example, since it is not clear whether the variables `x,y`
|
|
1230
|
+
should be greater or smaller than the variables `b_\ast`::
|
|
1231
|
+
|
|
1232
|
+
sage: R = PolynomialRing(QQ, names=['a_3','a_1','x','y'], order='lex')
|
|
1233
|
+
sage: A.construction()[0] * R.construction()[0]
|
|
1234
|
+
Traceback (most recent call last):
|
|
1235
|
+
...
|
|
1236
|
+
CoercionException: Overlapping variables (('a', 'b'),['a_3', 'a_1'])
|
|
1237
|
+
are incompatible
|
|
1238
|
+
|
|
1239
|
+
Since the construction functors are actually used to construct infinite polynomial rings, the following
|
|
1240
|
+
result is no surprise::
|
|
1241
|
+
|
|
1242
|
+
sage: C.<a,b> = InfinitePolynomialRing(B); C
|
|
1243
|
+
Infinite polynomial ring in a, b
|
|
1244
|
+
over Multivariate Polynomial Ring in x, y over Rational Field
|
|
1245
|
+
|
|
1246
|
+
There is also an overlap in the next example::
|
|
1247
|
+
|
|
1248
|
+
sage: X.<w,x,y> = InfinitePolynomialRing(ZZ)
|
|
1249
|
+
sage: Y.<x,y,z> = InfinitePolynomialRing(QQ)
|
|
1250
|
+
|
|
1251
|
+
`X` and `Y` have an overlapping generators `x_\ast, y_\ast`. Since the default lexicographic order is
|
|
1252
|
+
used in both rings, it gives rise to isomorphic sub-monoids in both `X` and `Y`. They are merged in the
|
|
1253
|
+
pushout, which also yields a common parent for doing arithmetic::
|
|
1254
|
+
|
|
1255
|
+
sage: P = sage.categories.pushout.pushout(Y,X); P
|
|
1256
|
+
Infinite polynomial ring in w, x, y, z over Rational Field
|
|
1257
|
+
sage: w[2]+z[3]
|
|
1258
|
+
w_2 + z_3
|
|
1259
|
+
sage: _.parent() is P
|
|
1260
|
+
True
|
|
1261
|
+
"""
|
|
1262
|
+
|
|
1263
|
+
# We do provide merging with polynomial rings. However, it seems that it is better
|
|
1264
|
+
# to have a greater rank, since we want to apply InfinitePolynomialFunctor *after*
|
|
1265
|
+
# [Multi]PolynomialFunctor, which have rank 9. But there is the MatrixFunctor, which
|
|
1266
|
+
# has rank 10. So, do fine tuning...
|
|
1267
|
+
rank = 9.5
|
|
1268
|
+
|
|
1269
|
+
def __init__(self, gens, order, implementation):
|
|
1270
|
+
"""
|
|
1271
|
+
TESTS::
|
|
1272
|
+
|
|
1273
|
+
sage: F = sage.categories.pushout.InfinitePolynomialFunctor(['a','b','x'],'degrevlex','sparse'); F # indirect doctest
|
|
1274
|
+
InfPoly{[a,b,x], "degrevlex", "sparse"}
|
|
1275
|
+
sage: F == loads(dumps(F))
|
|
1276
|
+
True
|
|
1277
|
+
"""
|
|
1278
|
+
if not gens:
|
|
1279
|
+
raise ValueError("Infinite Polynomial Rings have at least one generator")
|
|
1280
|
+
ConstructionFunctor.__init__(self, Rings(), Rings())
|
|
1281
|
+
self._gens = tuple(gens)
|
|
1282
|
+
self._order = order
|
|
1283
|
+
self._imple = implementation
|
|
1284
|
+
|
|
1285
|
+
def _apply_functor_to_morphism(self, f):
|
|
1286
|
+
"""
|
|
1287
|
+
Morphisms for infinite polynomial rings are not implemented yet.
|
|
1288
|
+
|
|
1289
|
+
TESTS::
|
|
1290
|
+
|
|
1291
|
+
sage: P.<x,y> = QQ[]
|
|
1292
|
+
sage: R.<alpha> = InfinitePolynomialRing(P)
|
|
1293
|
+
sage: f = P.hom([x+y,x-y],P)
|
|
1294
|
+
sage: R.construction()[0](f) # indirect doctest
|
|
1295
|
+
Traceback (most recent call last):
|
|
1296
|
+
...
|
|
1297
|
+
NotImplementedError: morphisms for infinite polynomial rings are not implemented yet
|
|
1298
|
+
"""
|
|
1299
|
+
raise NotImplementedError("morphisms for infinite polynomial rings are not implemented yet")
|
|
1300
|
+
|
|
1301
|
+
def _apply_functor(self, R):
|
|
1302
|
+
"""
|
|
1303
|
+
Apply the functor to an object of ``self``'s domain.
|
|
1304
|
+
|
|
1305
|
+
TESTS::
|
|
1306
|
+
|
|
1307
|
+
sage: F = sage.categories.pushout.InfinitePolynomialFunctor(['a','b','x'],'degrevlex','sparse'); F
|
|
1308
|
+
InfPoly{[a,b,x], "degrevlex", "sparse"}
|
|
1309
|
+
sage: F(QQ['t']) # indirect doctest
|
|
1310
|
+
Infinite polynomial ring in a, b, x over Univariate Polynomial Ring in t over Rational Field
|
|
1311
|
+
"""
|
|
1312
|
+
from sage.rings.polynomial.infinite_polynomial_ring import (
|
|
1313
|
+
InfinitePolynomialRing,
|
|
1314
|
+
)
|
|
1315
|
+
return InfinitePolynomialRing(R, self._gens, order=self._order, implementation=self._imple)
|
|
1316
|
+
|
|
1317
|
+
def _repr_(self):
|
|
1318
|
+
"""
|
|
1319
|
+
TESTS::
|
|
1320
|
+
|
|
1321
|
+
sage: F = sage.categories.pushout.InfinitePolynomialFunctor(['a','b','x'],'degrevlex','sparse'); F # indirect doctest
|
|
1322
|
+
InfPoly{[a,b,x], "degrevlex", "sparse"}
|
|
1323
|
+
"""
|
|
1324
|
+
return 'InfPoly{[%s], "%s", "%s"}' % (','.join(self._gens), self._order, self._imple)
|
|
1325
|
+
|
|
1326
|
+
def __eq__(self, other):
|
|
1327
|
+
"""
|
|
1328
|
+
TESTS::
|
|
1329
|
+
|
|
1330
|
+
sage: F = sage.categories.pushout.InfinitePolynomialFunctor(['a','b','x'],'degrevlex','sparse'); F # indirect doctest
|
|
1331
|
+
InfPoly{[a,b,x], "degrevlex", "sparse"}
|
|
1332
|
+
sage: F == loads(dumps(F)) # indirect doctest
|
|
1333
|
+
True
|
|
1334
|
+
sage: F == sage.categories.pushout.InfinitePolynomialFunctor(['a','b','x'],'deglex','sparse')
|
|
1335
|
+
False
|
|
1336
|
+
"""
|
|
1337
|
+
if isinstance(other, InfinitePolynomialFunctor):
|
|
1338
|
+
return (self._gens == other._gens and
|
|
1339
|
+
self._order == other._order and
|
|
1340
|
+
self._imple == other._imple)
|
|
1341
|
+
return False
|
|
1342
|
+
|
|
1343
|
+
def __ne__(self, other):
|
|
1344
|
+
"""
|
|
1345
|
+
Check whether ``self`` is not equal to ``other``.
|
|
1346
|
+
|
|
1347
|
+
EXAMPLES::
|
|
1348
|
+
|
|
1349
|
+
sage: F = sage.categories.pushout.InfinitePolynomialFunctor(['a','b','x'],'degrevlex','sparse'); F # indirect doctest
|
|
1350
|
+
InfPoly{[a,b,x], "degrevlex", "sparse"}
|
|
1351
|
+
sage: F != loads(dumps(F)) # indirect doctest
|
|
1352
|
+
False
|
|
1353
|
+
sage: F != sage.categories.pushout.InfinitePolynomialFunctor(['a','b','x'],'deglex','sparse')
|
|
1354
|
+
True
|
|
1355
|
+
"""
|
|
1356
|
+
return not (self == other)
|
|
1357
|
+
|
|
1358
|
+
__hash__ = ConstructionFunctor.__hash__
|
|
1359
|
+
|
|
1360
|
+
def __mul__(self, other):
|
|
1361
|
+
"""
|
|
1362
|
+
Compose construction functors to a composite construction functor, unless one of them is the identity.
|
|
1363
|
+
|
|
1364
|
+
.. NOTE::
|
|
1365
|
+
|
|
1366
|
+
The product is in functorial notation, i.e., when applying the
|
|
1367
|
+
product to an object then the second factor is applied first.
|
|
1368
|
+
|
|
1369
|
+
TESTS::
|
|
1370
|
+
|
|
1371
|
+
sage: F1 = QQ['a','x_2','x_1','y_3','y_2'].construction()[0]; F1
|
|
1372
|
+
MPoly[a,x_2,x_1,y_3,y_2]
|
|
1373
|
+
sage: F2 = InfinitePolynomialRing(QQ, ['x','y'],order='degrevlex').construction()[0]; F2
|
|
1374
|
+
InfPoly{[x,y], "degrevlex", "dense"}
|
|
1375
|
+
sage: F3 = InfinitePolynomialRing(QQ, ['x','y'],order='degrevlex',implementation='sparse').construction()[0]; F3
|
|
1376
|
+
InfPoly{[x,y], "degrevlex", "sparse"}
|
|
1377
|
+
sage: F2*F1
|
|
1378
|
+
InfPoly{[x,y], "degrevlex", "dense"}(Poly[a](...))
|
|
1379
|
+
sage: F3*F1
|
|
1380
|
+
InfPoly{[x,y], "degrevlex", "sparse"}(Poly[a](...))
|
|
1381
|
+
sage: F4 = sage.categories.pushout.FractionField()
|
|
1382
|
+
sage: F2*F4
|
|
1383
|
+
InfPoly{[x,y], "degrevlex", "dense"}(FractionField(...))
|
|
1384
|
+
"""
|
|
1385
|
+
if isinstance(other, IdentityConstructionFunctor):
|
|
1386
|
+
return self
|
|
1387
|
+
if isinstance(other, self.__class__):
|
|
1388
|
+
INT = set(self._gens).intersection(other._gens)
|
|
1389
|
+
if INT:
|
|
1390
|
+
# if there is overlap of generators, it must only be at the ends, so that
|
|
1391
|
+
# the resulting order after the merging is unique
|
|
1392
|
+
if other._gens[-len(INT):] != self._gens[:len(INT)]:
|
|
1393
|
+
raise CoercionException("Overlapping variables (%s,%s) are incompatible" % (self._gens, other._gens))
|
|
1394
|
+
OUTGENS = list(other._gens) + list(self._gens[len(INT):])
|
|
1395
|
+
else:
|
|
1396
|
+
OUTGENS = list(other._gens) + list(self._gens)
|
|
1397
|
+
# the orders must coincide
|
|
1398
|
+
if self._order != other._order:
|
|
1399
|
+
return CompositeConstructionFunctor(other, self)
|
|
1400
|
+
# the implementations must coincide
|
|
1401
|
+
if self._imple != other._imple:
|
|
1402
|
+
return CompositeConstructionFunctor(other, self)
|
|
1403
|
+
return InfinitePolynomialFunctor(OUTGENS, self._order, self._imple)
|
|
1404
|
+
|
|
1405
|
+
# Polynomial Constructor
|
|
1406
|
+
# Idea: We merge into self, if the polynomial functor really provides a substructure,
|
|
1407
|
+
# even respecting the order. Note that, if the pushout is computed, only *one* variable
|
|
1408
|
+
# will occur in the polynomial constructor. Hence, any order is fine, which is exactly
|
|
1409
|
+
# what we need in order to have coercion maps for different orderings.
|
|
1410
|
+
if isinstance(other, (MultiPolynomialFunctor, PolynomialFunctor)):
|
|
1411
|
+
if isinstance(other, MultiPolynomialFunctor):
|
|
1412
|
+
othervars = other.vars
|
|
1413
|
+
else:
|
|
1414
|
+
othervars = [other.var]
|
|
1415
|
+
|
|
1416
|
+
OverlappingVars = []
|
|
1417
|
+
# The variable names of the MultiPolynomialFunctor
|
|
1418
|
+
# that can be interpreted as variables in self
|
|
1419
|
+
|
|
1420
|
+
RemainingVars = list(othervars)
|
|
1421
|
+
IsOverlap = False
|
|
1422
|
+
BadOverlap = False
|
|
1423
|
+
for x in othervars:
|
|
1424
|
+
if x.count('_') == 1:
|
|
1425
|
+
g, n = x.split('_')
|
|
1426
|
+
if n.isdigit():
|
|
1427
|
+
if g.isalnum(): # we can interpret x in any InfinitePolynomialRing
|
|
1428
|
+
if g in self._gens: # we can interpret x in self, hence, we will not use it as a variable anymore.
|
|
1429
|
+
RemainingVars.pop(RemainingVars.index(x))
|
|
1430
|
+
IsOverlap = True # some variables of other can be interpreted in self.
|
|
1431
|
+
if OverlappingVars:
|
|
1432
|
+
# Is OverlappingVars in the right order?
|
|
1433
|
+
g0, n0 = OverlappingVars[-1].split('_')
|
|
1434
|
+
i = self._gens.index(g)
|
|
1435
|
+
i0 = self._gens.index(g0)
|
|
1436
|
+
if i < i0: # wrong order
|
|
1437
|
+
BadOverlap = True
|
|
1438
|
+
if i == i0 and int(n) > int(n0): # wrong order
|
|
1439
|
+
BadOverlap = True
|
|
1440
|
+
OverlappingVars.append(x)
|
|
1441
|
+
elif IsOverlap: # The overlap must be on the right end of the variable list
|
|
1442
|
+
BadOverlap = True
|
|
1443
|
+
elif IsOverlap: # The overlap must be on the right end of the variable list
|
|
1444
|
+
BadOverlap = True
|
|
1445
|
+
elif IsOverlap: # The overlap must be on the right end of the variable list
|
|
1446
|
+
BadOverlap = True
|
|
1447
|
+
elif IsOverlap: # The overlap must be on the right end of the variable list
|
|
1448
|
+
BadOverlap = True
|
|
1449
|
+
|
|
1450
|
+
if BadOverlap: # the overlapping variables appear in the wrong order
|
|
1451
|
+
raise CoercionException("Overlapping variables (%s,%s) are incompatible" % (self._gens, OverlappingVars))
|
|
1452
|
+
if len(OverlappingVars) > 1: # multivariate, hence, the term order matters
|
|
1453
|
+
if other.term_order.name() != self._order:
|
|
1454
|
+
raise CoercionException("Incompatible term orders %s, %s" % (self._order, other.term_order.name()))
|
|
1455
|
+
# ok, the overlap is fine, we will return something.
|
|
1456
|
+
if RemainingVars: # we can only partially merge other into self
|
|
1457
|
+
if len(RemainingVars) > 1:
|
|
1458
|
+
return CompositeConstructionFunctor(MultiPolynomialFunctor(RemainingVars, term_order=other.term_order), self)
|
|
1459
|
+
return CompositeConstructionFunctor(PolynomialFunctor(RemainingVars[0]), self)
|
|
1460
|
+
return self
|
|
1461
|
+
return CompositeConstructionFunctor(other, self)
|
|
1462
|
+
|
|
1463
|
+
def merge(self, other):
|
|
1464
|
+
"""
|
|
1465
|
+
Merge two construction functors of infinite polynomial rings, regardless of monomial order and implementation.
|
|
1466
|
+
|
|
1467
|
+
The purpose is to have a pushout (and thus, arithmetic) even in cases when the parents are isomorphic as
|
|
1468
|
+
rings, but not as ordered rings.
|
|
1469
|
+
|
|
1470
|
+
EXAMPLES::
|
|
1471
|
+
|
|
1472
|
+
sage: X.<x,y> = InfinitePolynomialRing(QQ, implementation='sparse')
|
|
1473
|
+
sage: Y.<x,y> = InfinitePolynomialRing(QQ, order='degrevlex')
|
|
1474
|
+
sage: X.construction()
|
|
1475
|
+
[InfPoly{[x,y], "lex", "sparse"}, Rational Field]
|
|
1476
|
+
sage: Y.construction()
|
|
1477
|
+
[InfPoly{[x,y], "degrevlex", "dense"}, Rational Field]
|
|
1478
|
+
sage: Y.construction()[0].merge(Y.construction()[0])
|
|
1479
|
+
InfPoly{[x,y], "degrevlex", "dense"}
|
|
1480
|
+
sage: y[3] + X(x[2])
|
|
1481
|
+
x_2 + y_3
|
|
1482
|
+
sage: _.parent().construction()
|
|
1483
|
+
[InfPoly{[x,y], "degrevlex", "dense"}, Rational Field]
|
|
1484
|
+
"""
|
|
1485
|
+
# Merging is only done if the ranks of self and other are the same.
|
|
1486
|
+
# It may happen that other is a substructure of self up to the monomial order
|
|
1487
|
+
# and the implementation. And this is when we want to merge, in order to
|
|
1488
|
+
# provide multiplication for rings with different term orderings.
|
|
1489
|
+
if not isinstance(other, InfinitePolynomialFunctor):
|
|
1490
|
+
return None
|
|
1491
|
+
if set(other._gens).issubset(self._gens):
|
|
1492
|
+
return self
|
|
1493
|
+
return None
|
|
1494
|
+
try:
|
|
1495
|
+
OUT = self * other
|
|
1496
|
+
# The following happens if "other" has the same order type etc.
|
|
1497
|
+
if not isinstance(OUT, CompositeConstructionFunctor):
|
|
1498
|
+
return OUT
|
|
1499
|
+
except CoercionException:
|
|
1500
|
+
pass
|
|
1501
|
+
if isinstance(other, InfinitePolynomialFunctor):
|
|
1502
|
+
# We don't require that the orders coincide. This is a difference to self*other
|
|
1503
|
+
# We only merge if other's generators are an ordered subset of self's generators
|
|
1504
|
+
for g in other._gens:
|
|
1505
|
+
if g not in self._gens:
|
|
1506
|
+
return None
|
|
1507
|
+
# The sequence of variables is part of the ordering. It must coincide in both rings
|
|
1508
|
+
Ind = [self._gens.index(g) for g in other._gens]
|
|
1509
|
+
if sorted(Ind) != Ind:
|
|
1510
|
+
return None
|
|
1511
|
+
# OK, other merges into self. Now, choose the default dense implementation,
|
|
1512
|
+
# unless both functors refer to the sparse implementation
|
|
1513
|
+
if self._imple != other._imple:
|
|
1514
|
+
return InfinitePolynomialFunctor(self._gens, self._order, 'dense')
|
|
1515
|
+
return self
|
|
1516
|
+
return None
|
|
1517
|
+
|
|
1518
|
+
def expand(self):
|
|
1519
|
+
"""
|
|
1520
|
+
Decompose the functor `F` into sub-functors, whose product returns `F`.
|
|
1521
|
+
|
|
1522
|
+
EXAMPLES::
|
|
1523
|
+
|
|
1524
|
+
sage: A = InfinitePolynomialRing(QQ, ['x','y'], order='degrevlex')
|
|
1525
|
+
sage: F = A.construction()[0]; F
|
|
1526
|
+
InfPoly{[x,y], "degrevlex", "dense"}
|
|
1527
|
+
sage: F.expand()
|
|
1528
|
+
[InfPoly{[y], "degrevlex", "dense"}, InfPoly{[x], "degrevlex", "dense"}]
|
|
1529
|
+
sage: A = InfinitePolynomialRing(QQ, ['x','y','z'], order='degrevlex')
|
|
1530
|
+
sage: F = A.construction()[0]; F
|
|
1531
|
+
InfPoly{[x,y,z], "degrevlex", "dense"}
|
|
1532
|
+
sage: F.expand()
|
|
1533
|
+
[InfPoly{[z], "degrevlex", "dense"},
|
|
1534
|
+
InfPoly{[y], "degrevlex", "dense"},
|
|
1535
|
+
InfPoly{[x], "degrevlex", "dense"}]
|
|
1536
|
+
sage: prod(F.expand())==F
|
|
1537
|
+
True
|
|
1538
|
+
"""
|
|
1539
|
+
if len(self._gens) == 1:
|
|
1540
|
+
return [self]
|
|
1541
|
+
return [InfinitePolynomialFunctor((x,), self._order, self._imple)
|
|
1542
|
+
for x in reversed(self._gens)]
|
|
1543
|
+
|
|
1544
|
+
|
|
1545
|
+
class MatrixFunctor(ConstructionFunctor):
|
|
1546
|
+
"""
|
|
1547
|
+
A construction functor for matrices over rings.
|
|
1548
|
+
|
|
1549
|
+
EXAMPLES::
|
|
1550
|
+
|
|
1551
|
+
sage: # needs sage.modules
|
|
1552
|
+
sage: MS = MatrixSpace(ZZ, 2, 3)
|
|
1553
|
+
sage: F = MS.construction()[0]; F
|
|
1554
|
+
MatrixFunctor
|
|
1555
|
+
sage: MS = MatrixSpace(ZZ, 2)
|
|
1556
|
+
sage: F = MS.construction()[0]; F
|
|
1557
|
+
MatrixFunctor
|
|
1558
|
+
sage: P.<x,y> = QQ[]
|
|
1559
|
+
sage: R = F(P); R
|
|
1560
|
+
Full MatrixSpace of 2 by 2 dense matrices
|
|
1561
|
+
over Multivariate Polynomial Ring in x, y over Rational Field
|
|
1562
|
+
sage: f = P.hom([x+y, x-y], P); F(f)
|
|
1563
|
+
Ring endomorphism
|
|
1564
|
+
of Full MatrixSpace of 2 by 2 dense matrices
|
|
1565
|
+
over Multivariate Polynomial Ring in x, y over Rational Field
|
|
1566
|
+
Defn: Induced from base ring by
|
|
1567
|
+
Ring endomorphism
|
|
1568
|
+
of Multivariate Polynomial Ring in x, y over Rational Field
|
|
1569
|
+
Defn: x |--> x + y
|
|
1570
|
+
y |--> x - y
|
|
1571
|
+
sage: M = R([x, y, x*y, x + y])
|
|
1572
|
+
sage: F(f)(M)
|
|
1573
|
+
[ x + y x - y]
|
|
1574
|
+
[x^2 - y^2 2*x]
|
|
1575
|
+
"""
|
|
1576
|
+
rank = 10
|
|
1577
|
+
|
|
1578
|
+
def __init__(self, nrows, ncols, is_sparse=False):
|
|
1579
|
+
"""
|
|
1580
|
+
TESTS::
|
|
1581
|
+
|
|
1582
|
+
sage: # needs sage.modules
|
|
1583
|
+
sage: from sage.categories.pushout import MatrixFunctor
|
|
1584
|
+
sage: F = MatrixFunctor(2, 3)
|
|
1585
|
+
sage: F == MatrixSpace(ZZ, 2, 3).construction()[0]
|
|
1586
|
+
True
|
|
1587
|
+
sage: F.codomain()
|
|
1588
|
+
Category of commutative additive groups
|
|
1589
|
+
sage: R = MatrixSpace(ZZ, 2, 2).construction()[0]
|
|
1590
|
+
sage: R.codomain()
|
|
1591
|
+
Category of rings
|
|
1592
|
+
sage: F(ZZ)
|
|
1593
|
+
Full MatrixSpace of 2 by 3 dense matrices over Integer Ring
|
|
1594
|
+
sage: F(ZZ) in F.codomain()
|
|
1595
|
+
True
|
|
1596
|
+
sage: R(GF(2))
|
|
1597
|
+
Full MatrixSpace of 2 by 2 dense matrices over Finite Field of size 2
|
|
1598
|
+
sage: R(GF(2)) in R.codomain()
|
|
1599
|
+
True
|
|
1600
|
+
"""
|
|
1601
|
+
if nrows == ncols:
|
|
1602
|
+
Functor.__init__(self, Rings(), Rings()) # Algebras() takes a base ring
|
|
1603
|
+
else:
|
|
1604
|
+
# Functor.__init__(self, Rings(), MatrixAlgebras()) # takes a base ring
|
|
1605
|
+
Functor.__init__(self, Rings(), CommutativeAdditiveGroups()) # not a nice solution, but the best we can do.
|
|
1606
|
+
self.nrows = nrows
|
|
1607
|
+
self.ncols = ncols
|
|
1608
|
+
self.is_sparse = is_sparse
|
|
1609
|
+
|
|
1610
|
+
def _apply_functor(self, R):
|
|
1611
|
+
"""
|
|
1612
|
+
Apply the functor to an object of ``self``'s domain.
|
|
1613
|
+
|
|
1614
|
+
TESTS:
|
|
1615
|
+
|
|
1616
|
+
The following is a test against a bug discussed at :issue:`8800`::
|
|
1617
|
+
|
|
1618
|
+
sage: F = MatrixSpace(ZZ, 2, 3).construction()[0] # needs sage.modules
|
|
1619
|
+
sage: F(RR) # indirect doctest # needs sage.modules
|
|
1620
|
+
Full MatrixSpace of 2 by 3 dense matrices over Real Field with 53 bits of precision
|
|
1621
|
+
sage: F(RR) in F.codomain() # needs sage.modules
|
|
1622
|
+
True
|
|
1623
|
+
"""
|
|
1624
|
+
from sage.matrix.matrix_space import MatrixSpace
|
|
1625
|
+
return MatrixSpace(R, self.nrows, self.ncols, sparse=self.is_sparse)
|
|
1626
|
+
|
|
1627
|
+
def __eq__(self, other):
|
|
1628
|
+
"""
|
|
1629
|
+
TESTS::
|
|
1630
|
+
|
|
1631
|
+
sage: F = MatrixSpace(ZZ, 2, 3).construction()[0] # needs sage.modules
|
|
1632
|
+
sage: F == loads(dumps(F)) # needs sage.modules
|
|
1633
|
+
True
|
|
1634
|
+
sage: F == MatrixSpace(ZZ, 2, 2).construction()[0] # needs sage.modules
|
|
1635
|
+
False
|
|
1636
|
+
"""
|
|
1637
|
+
if isinstance(other, MatrixFunctor):
|
|
1638
|
+
return (self.nrows == other.nrows and self.ncols == other.ncols)
|
|
1639
|
+
return False
|
|
1640
|
+
|
|
1641
|
+
def __ne__(self, other):
|
|
1642
|
+
"""
|
|
1643
|
+
Check whether ``self`` is not equal to ``other``.
|
|
1644
|
+
|
|
1645
|
+
EXAMPLES::
|
|
1646
|
+
|
|
1647
|
+
sage: F = MatrixSpace(ZZ, 2, 3).construction()[0] # needs sage.modules
|
|
1648
|
+
sage: F != loads(dumps(F)) # needs sage.modules
|
|
1649
|
+
False
|
|
1650
|
+
sage: F != MatrixSpace(ZZ, 2, 2).construction()[0] # needs sage.modules
|
|
1651
|
+
True
|
|
1652
|
+
"""
|
|
1653
|
+
return not (self == other)
|
|
1654
|
+
|
|
1655
|
+
__hash__ = ConstructionFunctor.__hash__
|
|
1656
|
+
|
|
1657
|
+
def merge(self, other):
|
|
1658
|
+
"""
|
|
1659
|
+
Merging is only happening if both functors are matrix functors of the same dimension.
|
|
1660
|
+
|
|
1661
|
+
The result is sparse if and only if both given functors are sparse.
|
|
1662
|
+
|
|
1663
|
+
EXAMPLES::
|
|
1664
|
+
|
|
1665
|
+
sage: # needs sage.modules
|
|
1666
|
+
sage: F1 = MatrixSpace(ZZ, 2, 2).construction()[0]
|
|
1667
|
+
sage: F2 = MatrixSpace(ZZ, 2, 3).construction()[0]
|
|
1668
|
+
sage: F3 = MatrixSpace(ZZ, 2, 2, sparse=True).construction()[0]
|
|
1669
|
+
sage: F1.merge(F2)
|
|
1670
|
+
sage: F1.merge(F3)
|
|
1671
|
+
MatrixFunctor
|
|
1672
|
+
sage: F13 = F1.merge(F3)
|
|
1673
|
+
sage: F13.is_sparse
|
|
1674
|
+
False
|
|
1675
|
+
sage: F1.is_sparse
|
|
1676
|
+
False
|
|
1677
|
+
sage: F3.is_sparse
|
|
1678
|
+
True
|
|
1679
|
+
sage: F3.merge(F3).is_sparse
|
|
1680
|
+
True
|
|
1681
|
+
"""
|
|
1682
|
+
if self != other:
|
|
1683
|
+
return None
|
|
1684
|
+
else:
|
|
1685
|
+
return MatrixFunctor(self.nrows, self.ncols, self.is_sparse and other.is_sparse)
|
|
1686
|
+
|
|
1687
|
+
|
|
1688
|
+
class LaurentPolynomialFunctor(ConstructionFunctor):
|
|
1689
|
+
"""
|
|
1690
|
+
Construction functor for Laurent polynomial rings.
|
|
1691
|
+
|
|
1692
|
+
EXAMPLES::
|
|
1693
|
+
|
|
1694
|
+
sage: L.<t> = LaurentPolynomialRing(ZZ)
|
|
1695
|
+
sage: F = L.construction()[0]
|
|
1696
|
+
sage: F
|
|
1697
|
+
LaurentPolynomialFunctor
|
|
1698
|
+
sage: F(QQ)
|
|
1699
|
+
Univariate Laurent Polynomial Ring in t over Rational Field
|
|
1700
|
+
sage: K.<x> = LaurentPolynomialRing(ZZ)
|
|
1701
|
+
sage: F(K)
|
|
1702
|
+
Univariate Laurent Polynomial Ring in t
|
|
1703
|
+
over Univariate Laurent Polynomial Ring in x over Integer Ring
|
|
1704
|
+
sage: P.<x,y> = ZZ[]
|
|
1705
|
+
sage: f = P.hom([x + 2*y, 3*x - y],P)
|
|
1706
|
+
sage: F(f)
|
|
1707
|
+
Ring endomorphism of Univariate Laurent Polynomial Ring in t
|
|
1708
|
+
over Multivariate Polynomial Ring in x, y over Integer Ring
|
|
1709
|
+
Defn: Induced from base ring by
|
|
1710
|
+
Ring endomorphism of Multivariate Polynomial Ring in x, y over Integer Ring
|
|
1711
|
+
Defn: x |--> x + 2*y
|
|
1712
|
+
y |--> 3*x - y
|
|
1713
|
+
sage: F(f)(x*F(P).gen()^-2 + y*F(P).gen()^3)
|
|
1714
|
+
(x + 2*y)*t^-2 + (3*x - y)*t^3
|
|
1715
|
+
"""
|
|
1716
|
+
rank = 9
|
|
1717
|
+
|
|
1718
|
+
def __init__(self, var, multi_variate=False):
|
|
1719
|
+
"""
|
|
1720
|
+
INPUT:
|
|
1721
|
+
|
|
1722
|
+
- ``var`` -- string or list of strings
|
|
1723
|
+
- ``multi_variate`` -- boolean (default: ``False``); if ``var`` is a
|
|
1724
|
+
string and ``True`` otherwise: If ``True``, application to a Laurent
|
|
1725
|
+
polynomial ring yields a multivariate Laurent polynomial ring.
|
|
1726
|
+
|
|
1727
|
+
TESTS::
|
|
1728
|
+
|
|
1729
|
+
sage: from sage.categories.pushout import LaurentPolynomialFunctor
|
|
1730
|
+
sage: F1 = LaurentPolynomialFunctor('t')
|
|
1731
|
+
sage: F2 = LaurentPolynomialFunctor('s', multi_variate=True)
|
|
1732
|
+
sage: F3 = LaurentPolynomialFunctor(['s','t'])
|
|
1733
|
+
sage: F1(F2(QQ))
|
|
1734
|
+
Univariate Laurent Polynomial Ring in t over
|
|
1735
|
+
Univariate Laurent Polynomial Ring in s over Rational Field
|
|
1736
|
+
sage: F2(F1(QQ)) # needs sage.modules
|
|
1737
|
+
Multivariate Laurent Polynomial Ring in t, s over Rational Field
|
|
1738
|
+
sage: F3(QQ) # needs sage.modules
|
|
1739
|
+
Multivariate Laurent Polynomial Ring in s, t over Rational Field
|
|
1740
|
+
"""
|
|
1741
|
+
Functor.__init__(self, Rings(), Rings())
|
|
1742
|
+
if not isinstance(var, (str, tuple, list)):
|
|
1743
|
+
raise TypeError("variable name or list of variable names expected")
|
|
1744
|
+
self.var = var
|
|
1745
|
+
self.multi_variate = multi_variate or not isinstance(var, str)
|
|
1746
|
+
|
|
1747
|
+
def _apply_functor(self, R):
|
|
1748
|
+
"""
|
|
1749
|
+
Apply the functor to an object of ``self``'s domain.
|
|
1750
|
+
|
|
1751
|
+
TESTS::
|
|
1752
|
+
|
|
1753
|
+
sage: from sage.categories.pushout import LaurentPolynomialFunctor
|
|
1754
|
+
sage: F1 = LaurentPolynomialFunctor('t')
|
|
1755
|
+
sage: F2 = LaurentPolynomialFunctor('s', multi_variate=True)
|
|
1756
|
+
sage: F3 = LaurentPolynomialFunctor(['s','t'])
|
|
1757
|
+
sage: F1(F2(QQ)) # indirect doctest
|
|
1758
|
+
Univariate Laurent Polynomial Ring in t over
|
|
1759
|
+
Univariate Laurent Polynomial Ring in s over Rational Field
|
|
1760
|
+
sage: F2(F1(QQ)) # needs sage.modules
|
|
1761
|
+
Multivariate Laurent Polynomial Ring in t, s over Rational Field
|
|
1762
|
+
sage: F3(QQ) # needs sage.modules
|
|
1763
|
+
Multivariate Laurent Polynomial Ring in s, t over Rational Field
|
|
1764
|
+
"""
|
|
1765
|
+
from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing
|
|
1766
|
+
from sage.rings.polynomial.laurent_polynomial_ring_base import (
|
|
1767
|
+
LaurentPolynomialRing_generic,
|
|
1768
|
+
)
|
|
1769
|
+
if self.multi_variate and isinstance(R, LaurentPolynomialRing_generic):
|
|
1770
|
+
return LaurentPolynomialRing(R.base_ring(), list(R.variable_names()) + [self.var])
|
|
1771
|
+
else:
|
|
1772
|
+
return LaurentPolynomialRing(R, self.var)
|
|
1773
|
+
|
|
1774
|
+
def __eq__(self, other):
|
|
1775
|
+
"""
|
|
1776
|
+
TESTS::
|
|
1777
|
+
|
|
1778
|
+
sage: from sage.categories.pushout import LaurentPolynomialFunctor
|
|
1779
|
+
sage: F1 = LaurentPolynomialFunctor('t')
|
|
1780
|
+
sage: F2 = LaurentPolynomialFunctor('t', multi_variate=True)
|
|
1781
|
+
sage: F3 = LaurentPolynomialFunctor(['s','t'])
|
|
1782
|
+
sage: F1 == F2
|
|
1783
|
+
True
|
|
1784
|
+
sage: F1 == loads(dumps(F1))
|
|
1785
|
+
True
|
|
1786
|
+
sage: F1 == F3
|
|
1787
|
+
False
|
|
1788
|
+
sage: F1 == QQ.construction()[0]
|
|
1789
|
+
False
|
|
1790
|
+
"""
|
|
1791
|
+
if isinstance(other, LaurentPolynomialFunctor):
|
|
1792
|
+
return self.var == other.var
|
|
1793
|
+
return False
|
|
1794
|
+
|
|
1795
|
+
def __ne__(self, other):
|
|
1796
|
+
"""
|
|
1797
|
+
Check whether ``self`` is not equal to ``other``.
|
|
1798
|
+
|
|
1799
|
+
EXAMPLES::
|
|
1800
|
+
|
|
1801
|
+
sage: from sage.categories.pushout import LaurentPolynomialFunctor
|
|
1802
|
+
sage: F1 = LaurentPolynomialFunctor('t')
|
|
1803
|
+
sage: F2 = LaurentPolynomialFunctor('t', multi_variate=True)
|
|
1804
|
+
sage: F3 = LaurentPolynomialFunctor(['s','t'])
|
|
1805
|
+
sage: F1 != F2
|
|
1806
|
+
False
|
|
1807
|
+
sage: F1 != loads(dumps(F1))
|
|
1808
|
+
False
|
|
1809
|
+
sage: F1 != F3
|
|
1810
|
+
True
|
|
1811
|
+
sage: F1 != QQ.construction()[0]
|
|
1812
|
+
True
|
|
1813
|
+
"""
|
|
1814
|
+
return not (self == other)
|
|
1815
|
+
|
|
1816
|
+
__hash__ = ConstructionFunctor.__hash__
|
|
1817
|
+
|
|
1818
|
+
def merge(self, other):
|
|
1819
|
+
"""
|
|
1820
|
+
Two Laurent polynomial construction functors merge if the variable names coincide.
|
|
1821
|
+
|
|
1822
|
+
The result is multivariate if one of the arguments is multivariate.
|
|
1823
|
+
|
|
1824
|
+
EXAMPLES::
|
|
1825
|
+
|
|
1826
|
+
sage: from sage.categories.pushout import LaurentPolynomialFunctor
|
|
1827
|
+
sage: F1 = LaurentPolynomialFunctor('t')
|
|
1828
|
+
sage: F2 = LaurentPolynomialFunctor('t', multi_variate=True)
|
|
1829
|
+
sage: F1.merge(F2)
|
|
1830
|
+
LaurentPolynomialFunctor
|
|
1831
|
+
sage: F1.merge(F2)(LaurentPolynomialRing(GF(2), 'a')) # needs sage.modules
|
|
1832
|
+
Multivariate Laurent Polynomial Ring in a, t over Finite Field of size 2
|
|
1833
|
+
sage: F1.merge(F1)(LaurentPolynomialRing(GF(2), 'a'))
|
|
1834
|
+
Univariate Laurent Polynomial Ring in t over
|
|
1835
|
+
Univariate Laurent Polynomial Ring in a over Finite Field of size 2
|
|
1836
|
+
"""
|
|
1837
|
+
if self == other or isinstance(other, PolynomialFunctor) and self.var == other.var:
|
|
1838
|
+
return LaurentPolynomialFunctor(self.var, (self.multi_variate or other.multi_variate))
|
|
1839
|
+
else:
|
|
1840
|
+
return None
|
|
1841
|
+
|
|
1842
|
+
|
|
1843
|
+
class VectorFunctor(ConstructionFunctor):
|
|
1844
|
+
"""
|
|
1845
|
+
A construction functor for free modules over commutative rings.
|
|
1846
|
+
|
|
1847
|
+
EXAMPLES::
|
|
1848
|
+
|
|
1849
|
+
sage: # needs sage.modules
|
|
1850
|
+
sage: F = (ZZ^3).construction()[0]
|
|
1851
|
+
sage: F
|
|
1852
|
+
VectorFunctor
|
|
1853
|
+
sage: F(GF(2)['t']) # needs sage.libs.ntl
|
|
1854
|
+
Ambient free module of rank 3
|
|
1855
|
+
over the principal ideal domain Univariate Polynomial Ring in t
|
|
1856
|
+
over Finite Field of size 2 (using ...)
|
|
1857
|
+
"""
|
|
1858
|
+
rank = 10 # ranking of functor, not rank of module.
|
|
1859
|
+
# This coincides with the rank of the matrix construction functor, but this is OK since they cannot both be applied in any order
|
|
1860
|
+
|
|
1861
|
+
def __init__(self, n=None, is_sparse=False, inner_product_matrix=None, *,
|
|
1862
|
+
with_basis='standard', basis_keys=None, name_mapping=None, latex_name_mapping=None):
|
|
1863
|
+
"""
|
|
1864
|
+
INPUT:
|
|
1865
|
+
|
|
1866
|
+
- ``n`` -- the rank of the to-be-created modules (nonnegative integer)
|
|
1867
|
+
- ``is_sparse`` -- boolean (default: ``False``); create sparse
|
|
1868
|
+
implementation of modules
|
|
1869
|
+
- ``inner_product_matrix`` -- ``n`` by ``n`` matrix, used to compute inner products in the
|
|
1870
|
+
to-be-created modules
|
|
1871
|
+
- ``name_mapping``, ``latex_name_mapping`` -- Dictionaries from base rings to names
|
|
1872
|
+
- other keywords: see :func:`~sage.modules.free_module.FreeModule`
|
|
1873
|
+
|
|
1874
|
+
TESTS::
|
|
1875
|
+
|
|
1876
|
+
sage: # needs sage.modules
|
|
1877
|
+
sage: from sage.categories.pushout import VectorFunctor
|
|
1878
|
+
sage: F1 = VectorFunctor(3, inner_product_matrix=Matrix(3, 3, range(9)))
|
|
1879
|
+
sage: F1.domain()
|
|
1880
|
+
Category of commutative rings
|
|
1881
|
+
sage: F1.codomain()
|
|
1882
|
+
Category of commutative additive groups
|
|
1883
|
+
sage: M1 = F1(ZZ)
|
|
1884
|
+
sage: M1.is_sparse()
|
|
1885
|
+
False
|
|
1886
|
+
sage: v = M1([3, 2, 1])
|
|
1887
|
+
sage: v * Matrix(3, 3, range(9)) * v.column()
|
|
1888
|
+
(96)
|
|
1889
|
+
sage: v.inner_product(v)
|
|
1890
|
+
96
|
|
1891
|
+
sage: F2 = VectorFunctor(3, is_sparse=True)
|
|
1892
|
+
sage: M2 = F2(QQ); M2; M2.is_sparse()
|
|
1893
|
+
Sparse vector space of dimension 3 over Rational Field
|
|
1894
|
+
True
|
|
1895
|
+
"""
|
|
1896
|
+
# Functor.__init__(self, Rings(), FreeModules()) # FreeModules() takes a base ring
|
|
1897
|
+
# Functor.__init__(self, Objects(), Objects()) # Object() makes no sense, since FreeModule raises an error, e.g., on Set(['a',1]).
|
|
1898
|
+
# FreeModule requires a commutative ring. Thus, we have
|
|
1899
|
+
Functor.__init__(self, CommutativeRings(), CommutativeAdditiveGroups())
|
|
1900
|
+
self.n = n
|
|
1901
|
+
self.is_sparse = is_sparse
|
|
1902
|
+
self.inner_product_matrix = inner_product_matrix
|
|
1903
|
+
self.with_basis = with_basis
|
|
1904
|
+
self.basis_keys = basis_keys
|
|
1905
|
+
if name_mapping is None:
|
|
1906
|
+
name_mapping = {}
|
|
1907
|
+
self.name_mapping = name_mapping
|
|
1908
|
+
if latex_name_mapping is None:
|
|
1909
|
+
latex_name_mapping = {}
|
|
1910
|
+
self.latex_name_mapping = latex_name_mapping
|
|
1911
|
+
|
|
1912
|
+
def _apply_functor(self, R):
|
|
1913
|
+
r"""
|
|
1914
|
+
Apply the functor to an object of ``self``'s domain.
|
|
1915
|
+
|
|
1916
|
+
TESTS::
|
|
1917
|
+
|
|
1918
|
+
sage: # needs sage.modules
|
|
1919
|
+
sage: from sage.categories.pushout import VectorFunctor, pushout
|
|
1920
|
+
sage: F1 = VectorFunctor(3, inner_product_matrix=Matrix(3, 3, range(9)))
|
|
1921
|
+
sage: M1 = F1(ZZ) # indirect doctest
|
|
1922
|
+
sage: M1.is_sparse()
|
|
1923
|
+
False
|
|
1924
|
+
sage: v = M1([3, 2, 1])
|
|
1925
|
+
sage: v * Matrix(3, 3, range(9)) * v.column()
|
|
1926
|
+
(96)
|
|
1927
|
+
sage: v.inner_product(v)
|
|
1928
|
+
96
|
|
1929
|
+
sage: F2 = VectorFunctor(3, is_sparse=True)
|
|
1930
|
+
sage: M2 = F2(QQ); M2; M2.is_sparse()
|
|
1931
|
+
Sparse vector space of dimension 3 over Rational Field
|
|
1932
|
+
True
|
|
1933
|
+
sage: v = M2([3, 2, 1])
|
|
1934
|
+
sage: v.inner_product(v)
|
|
1935
|
+
14
|
|
1936
|
+
|
|
1937
|
+
sage: # needs sage.modules
|
|
1938
|
+
sage: M = FreeModule(ZZ, 4, with_basis=None, name='M')
|
|
1939
|
+
sage: latex(M)
|
|
1940
|
+
M
|
|
1941
|
+
sage: M_QQ = pushout(M, QQ)
|
|
1942
|
+
sage: latex(M_QQ)
|
|
1943
|
+
M \otimes \Bold{Q}
|
|
1944
|
+
"""
|
|
1945
|
+
from sage.modules.free_module import FreeModule
|
|
1946
|
+
name = self.name_mapping.get(R, None)
|
|
1947
|
+
latex_name = self.latex_name_mapping.get(R, None)
|
|
1948
|
+
if name is None:
|
|
1949
|
+
for name in self.name_mapping.values():
|
|
1950
|
+
name = f'{name}_base_ext'
|
|
1951
|
+
break
|
|
1952
|
+
if latex_name is None:
|
|
1953
|
+
from sage.misc.latex import latex
|
|
1954
|
+
for latex_name in self.latex_name_mapping.values():
|
|
1955
|
+
latex_name = fr'{latex_name} \otimes {latex(R)}'
|
|
1956
|
+
break
|
|
1957
|
+
if name is None and latex_name is None:
|
|
1958
|
+
return FreeModule(R, self.n, sparse=self.is_sparse, inner_product_matrix=self.inner_product_matrix,
|
|
1959
|
+
with_basis=self.with_basis, basis_keys=self.basis_keys)
|
|
1960
|
+
return FreeModule(R, self.n, sparse=self.is_sparse, inner_product_matrix=self.inner_product_matrix,
|
|
1961
|
+
with_basis=self.with_basis, basis_keys=self.basis_keys, name=name, latex_name=latex_name)
|
|
1962
|
+
|
|
1963
|
+
def _apply_functor_to_morphism(self, f):
|
|
1964
|
+
"""
|
|
1965
|
+
This is not implemented yet.
|
|
1966
|
+
|
|
1967
|
+
TESTS::
|
|
1968
|
+
|
|
1969
|
+
sage: F = (ZZ^3).construction()[0] # needs sage.modules
|
|
1970
|
+
sage: P.<x,y> = ZZ[]
|
|
1971
|
+
sage: f = P.hom([x + 2*y, 3*x - y], P)
|
|
1972
|
+
sage: F(f) # indirect doctest # needs sage.modules
|
|
1973
|
+
Traceback (most recent call last):
|
|
1974
|
+
...
|
|
1975
|
+
NotImplementedError: Cannot create induced morphisms of free modules yet
|
|
1976
|
+
"""
|
|
1977
|
+
# TODO: Implement this!
|
|
1978
|
+
raise NotImplementedError("Cannot create induced morphisms of free modules yet")
|
|
1979
|
+
|
|
1980
|
+
def __eq__(self, other):
|
|
1981
|
+
"""
|
|
1982
|
+
The rank and the inner product matrix are compared.
|
|
1983
|
+
|
|
1984
|
+
TESTS::
|
|
1985
|
+
|
|
1986
|
+
sage: # needs sage.modules
|
|
1987
|
+
sage: from sage.categories.pushout import VectorFunctor
|
|
1988
|
+
sage: F1 = VectorFunctor(3, inner_product_matrix=Matrix(3, 3, range(9)))
|
|
1989
|
+
sage: F2 = (ZZ^3).construction()[0]
|
|
1990
|
+
sage: F1 == F2
|
|
1991
|
+
False
|
|
1992
|
+
sage: F1(QQ) == F2(QQ)
|
|
1993
|
+
False
|
|
1994
|
+
sage: F1 == loads(dumps(F1))
|
|
1995
|
+
True
|
|
1996
|
+
"""
|
|
1997
|
+
if isinstance(other, VectorFunctor):
|
|
1998
|
+
return (self.n == other.n and
|
|
1999
|
+
self.inner_product_matrix == other.inner_product_matrix and
|
|
2000
|
+
self.with_basis == other.with_basis and
|
|
2001
|
+
self.basis_keys == other.basis_keys and
|
|
2002
|
+
self.name_mapping == other.name_mapping and
|
|
2003
|
+
self.latex_name_mapping == other.latex_name_mapping)
|
|
2004
|
+
return False
|
|
2005
|
+
|
|
2006
|
+
def __ne__(self, other):
|
|
2007
|
+
"""
|
|
2008
|
+
Check whether ``self`` is not equal to ``other``.
|
|
2009
|
+
|
|
2010
|
+
EXAMPLES::
|
|
2011
|
+
|
|
2012
|
+
sage: # needs sage.modules
|
|
2013
|
+
sage: from sage.categories.pushout import VectorFunctor
|
|
2014
|
+
sage: F1 = VectorFunctor(3, inner_product_matrix=Matrix(3, 3, range(9)))
|
|
2015
|
+
sage: F2 = (ZZ^3).construction()[0]
|
|
2016
|
+
sage: F1 != F2
|
|
2017
|
+
True
|
|
2018
|
+
sage: F1(QQ) != F2(QQ)
|
|
2019
|
+
True
|
|
2020
|
+
sage: F1 != loads(dumps(F1))
|
|
2021
|
+
False
|
|
2022
|
+
"""
|
|
2023
|
+
return not (self == other)
|
|
2024
|
+
|
|
2025
|
+
__hash__ = ConstructionFunctor.__hash__
|
|
2026
|
+
|
|
2027
|
+
def merge(self, other):
|
|
2028
|
+
"""
|
|
2029
|
+
Two constructors of free modules merge, if the module ranks and the inner products coincide. If both
|
|
2030
|
+
have explicitly given inner product matrices, they must coincide as well.
|
|
2031
|
+
|
|
2032
|
+
EXAMPLES:
|
|
2033
|
+
|
|
2034
|
+
Two modules without explicitly given inner product allow coercion::
|
|
2035
|
+
|
|
2036
|
+
sage: M1 = QQ^3 # needs sage.modules
|
|
2037
|
+
sage: P.<t> = ZZ[]
|
|
2038
|
+
sage: M2 = FreeModule(P, 3) # needs sage.modules
|
|
2039
|
+
sage: M1([1,1/2,1/3]) + M2([t,t^2+t,3]) # indirect doctest # needs sage.modules
|
|
2040
|
+
(t + 1, t^2 + t + 1/2, 10/3)
|
|
2041
|
+
|
|
2042
|
+
If only one summand has an explicit inner product, the result will be provided
|
|
2043
|
+
with it::
|
|
2044
|
+
|
|
2045
|
+
sage: M3 = FreeModule(P, 3, inner_product_matrix=Matrix(3, 3, range(9))) # needs sage.modules
|
|
2046
|
+
sage: M1([1,1/2,1/3]) + M3([t,t^2+t,3]) # needs sage.modules
|
|
2047
|
+
(t + 1, t^2 + t + 1/2, 10/3)
|
|
2048
|
+
sage: (M1([1,1/2,1/3]) + M3([t,t^2+t,3])).parent().inner_product_matrix() # needs sage.modules
|
|
2049
|
+
[0 1 2]
|
|
2050
|
+
[3 4 5]
|
|
2051
|
+
[6 7 8]
|
|
2052
|
+
|
|
2053
|
+
If both summands have an explicit inner product (even if it is the standard
|
|
2054
|
+
inner product), then the products must coincide. The only difference between
|
|
2055
|
+
``M1`` and ``M4`` in the following example is the fact that the default
|
|
2056
|
+
inner product was *explicitly* requested for ``M4``. It is therefore not
|
|
2057
|
+
possible to coerce with a different inner product::
|
|
2058
|
+
|
|
2059
|
+
sage: # needs sage.modules
|
|
2060
|
+
sage: M4 = FreeModule(QQ, 3, inner_product_matrix=Matrix(3, 3, 1))
|
|
2061
|
+
sage: M4 == M1
|
|
2062
|
+
True
|
|
2063
|
+
sage: M4.inner_product_matrix() == M1.inner_product_matrix()
|
|
2064
|
+
True
|
|
2065
|
+
sage: M4([1,1/2,1/3]) + M3([t,t^2+t,3]) # indirect doctest
|
|
2066
|
+
Traceback (most recent call last):
|
|
2067
|
+
...
|
|
2068
|
+
TypeError: unsupported operand parent(s) for +:
|
|
2069
|
+
'Ambient quadratic space of dimension 3 over Rational Field
|
|
2070
|
+
Inner product matrix:
|
|
2071
|
+
[1 0 0]
|
|
2072
|
+
[0 1 0]
|
|
2073
|
+
[0 0 1]' and
|
|
2074
|
+
'Ambient free quadratic module of rank 3 over the integral domain
|
|
2075
|
+
Univariate Polynomial Ring in t over Integer Ring
|
|
2076
|
+
Inner product matrix:
|
|
2077
|
+
[0 1 2]
|
|
2078
|
+
[3 4 5]
|
|
2079
|
+
[6 7 8]'
|
|
2080
|
+
|
|
2081
|
+
Names are removed when they conflict::
|
|
2082
|
+
|
|
2083
|
+
sage: # needs sage.modules
|
|
2084
|
+
sage: from sage.categories.pushout import VectorFunctor, pushout
|
|
2085
|
+
sage: M_ZZx = FreeModule(ZZ['x'], 4, with_basis=None, name='M_ZZx')
|
|
2086
|
+
sage: N_ZZx = FreeModule(ZZ['x'], 4, with_basis=None, name='N_ZZx')
|
|
2087
|
+
sage: pushout(M_ZZx, QQ)
|
|
2088
|
+
Rank-4 free module M_ZZx_base_ext
|
|
2089
|
+
over the Univariate Polynomial Ring in x over Rational Field
|
|
2090
|
+
sage: pushout(M_ZZx, N_ZZx)
|
|
2091
|
+
Rank-4 free module
|
|
2092
|
+
over the Univariate Polynomial Ring in x over Integer Ring
|
|
2093
|
+
sage: pushout(pushout(M_ZZx, N_ZZx), QQ)
|
|
2094
|
+
Rank-4 free module
|
|
2095
|
+
over the Univariate Polynomial Ring in x over Rational Field
|
|
2096
|
+
"""
|
|
2097
|
+
if not isinstance(other, VectorFunctor):
|
|
2098
|
+
return None
|
|
2099
|
+
|
|
2100
|
+
if self.with_basis != other.with_basis:
|
|
2101
|
+
return None
|
|
2102
|
+
else:
|
|
2103
|
+
with_basis = self.with_basis
|
|
2104
|
+
|
|
2105
|
+
if self.basis_keys != other.basis_keys:
|
|
2106
|
+
# TODO: If both are enumerated families, should we try to take the union of the families?
|
|
2107
|
+
return None
|
|
2108
|
+
else:
|
|
2109
|
+
basis_keys = self.basis_keys
|
|
2110
|
+
|
|
2111
|
+
is_sparse = self.is_sparse and other.is_sparse
|
|
2112
|
+
|
|
2113
|
+
if self.inner_product_matrix is None:
|
|
2114
|
+
inner_product_matrix = other.inner_product_matrix
|
|
2115
|
+
elif other.inner_product_matrix is None:
|
|
2116
|
+
inner_product_matrix = self.inner_product_matrix
|
|
2117
|
+
elif self.inner_product_matrix != other.inner_product_matrix:
|
|
2118
|
+
# At this point, we know that the user wants to take care of the inner product.
|
|
2119
|
+
# So, we only merge if both coincide:
|
|
2120
|
+
return None
|
|
2121
|
+
else:
|
|
2122
|
+
inner_product_matrix = None
|
|
2123
|
+
|
|
2124
|
+
if self.n != other.n:
|
|
2125
|
+
return None
|
|
2126
|
+
else:
|
|
2127
|
+
n = self.n
|
|
2128
|
+
|
|
2129
|
+
name_mapping = {}
|
|
2130
|
+
for base_ring, name in self.name_mapping.items():
|
|
2131
|
+
try:
|
|
2132
|
+
other_name = other.name_mapping[base_ring]
|
|
2133
|
+
except KeyError:
|
|
2134
|
+
name_mapping[base_ring] = name
|
|
2135
|
+
else:
|
|
2136
|
+
if name == other_name:
|
|
2137
|
+
name_mapping[base_ring] = name
|
|
2138
|
+
|
|
2139
|
+
latex_name_mapping = {}
|
|
2140
|
+
for base_ring, latex_name in self.latex_name_mapping.items():
|
|
2141
|
+
try:
|
|
2142
|
+
other_latex_name = other.latex_name_mapping[base_ring]
|
|
2143
|
+
except KeyError:
|
|
2144
|
+
latex_name_mapping[base_ring] = latex_name
|
|
2145
|
+
else:
|
|
2146
|
+
if latex_name == other_latex_name:
|
|
2147
|
+
latex_name_mapping[base_ring] = latex_name
|
|
2148
|
+
|
|
2149
|
+
return VectorFunctor(n, is_sparse, inner_product_matrix,
|
|
2150
|
+
with_basis=with_basis, basis_keys=basis_keys,
|
|
2151
|
+
name_mapping=name_mapping, latex_name_mapping=latex_name_mapping)
|
|
2152
|
+
|
|
2153
|
+
|
|
2154
|
+
class SubspaceFunctor(ConstructionFunctor):
|
|
2155
|
+
"""
|
|
2156
|
+
Constructing a subspace of an ambient free module, given by a basis.
|
|
2157
|
+
|
|
2158
|
+
.. NOTE::
|
|
2159
|
+
|
|
2160
|
+
This construction functor keeps track of the basis. It can only be
|
|
2161
|
+
applied to free modules into which this basis coerces.
|
|
2162
|
+
|
|
2163
|
+
EXAMPLES::
|
|
2164
|
+
|
|
2165
|
+
sage: # needs sage.modules
|
|
2166
|
+
sage: M = ZZ^3
|
|
2167
|
+
sage: S = M.submodule([(1,2,3), (4,5,6)]); S
|
|
2168
|
+
Free module of degree 3 and rank 2 over Integer Ring
|
|
2169
|
+
Echelon basis matrix:
|
|
2170
|
+
[1 2 3]
|
|
2171
|
+
[0 3 6]
|
|
2172
|
+
sage: F = S.construction()[0]
|
|
2173
|
+
sage: F(GF(2)^3)
|
|
2174
|
+
Vector space of degree 3 and dimension 2 over Finite Field of size 2
|
|
2175
|
+
User basis matrix:
|
|
2176
|
+
[1 0 1]
|
|
2177
|
+
[0 1 0]
|
|
2178
|
+
"""
|
|
2179
|
+
rank = 11 # ranking of functor, not rank of module
|
|
2180
|
+
|
|
2181
|
+
# The subspace construction returns an object admitting a coercion
|
|
2182
|
+
# map into the original, not vice versa.
|
|
2183
|
+
coercion_reversed = True
|
|
2184
|
+
|
|
2185
|
+
def __init__(self, basis):
|
|
2186
|
+
"""
|
|
2187
|
+
INPUT:
|
|
2188
|
+
|
|
2189
|
+
- ``basis`` -- list of elements of a free module
|
|
2190
|
+
|
|
2191
|
+
TESTS::
|
|
2192
|
+
|
|
2193
|
+
sage: from sage.categories.pushout import SubspaceFunctor
|
|
2194
|
+
sage: M = ZZ^3 # needs sage.modules
|
|
2195
|
+
sage: F = SubspaceFunctor([M([1,2,3]), M([4,5,6])]) # needs sage.modules
|
|
2196
|
+
sage: F(GF(5)^3) # needs sage.modules
|
|
2197
|
+
Vector space of degree 3 and dimension 2 over Finite Field of size 5
|
|
2198
|
+
User basis matrix:
|
|
2199
|
+
[1 2 3]
|
|
2200
|
+
[4 0 1]
|
|
2201
|
+
"""
|
|
2202
|
+
# Functor.__init__(self, FreeModules(), FreeModules()) # takes a base ring
|
|
2203
|
+
# Functor.__init__(self, Objects(), Objects()) # is too general
|
|
2204
|
+
# It seems that the category of commutative additive groups
|
|
2205
|
+
# currently is the smallest base ring free category that
|
|
2206
|
+
# contains in- and output
|
|
2207
|
+
Functor.__init__(self, CommutativeAdditiveGroups(), CommutativeAdditiveGroups())
|
|
2208
|
+
self.basis = basis
|
|
2209
|
+
|
|
2210
|
+
def _apply_functor(self, ambient):
|
|
2211
|
+
"""
|
|
2212
|
+
Apply the functor to an object of ``self``'s domain.
|
|
2213
|
+
|
|
2214
|
+
TESTS::
|
|
2215
|
+
|
|
2216
|
+
sage: # needs sage.modules
|
|
2217
|
+
sage: M = ZZ^3
|
|
2218
|
+
sage: S = M.submodule([(1,2,3), (4,5,6)]); S
|
|
2219
|
+
Free module of degree 3 and rank 2 over Integer Ring
|
|
2220
|
+
Echelon basis matrix:
|
|
2221
|
+
[1 2 3]
|
|
2222
|
+
[0 3 6]
|
|
2223
|
+
sage: F = S.construction()[0]
|
|
2224
|
+
sage: F(GF(2)^3) # indirect doctest
|
|
2225
|
+
Vector space of degree 3 and dimension 2 over Finite Field of size 2
|
|
2226
|
+
User basis matrix:
|
|
2227
|
+
[1 0 1]
|
|
2228
|
+
[0 1 0]
|
|
2229
|
+
"""
|
|
2230
|
+
return ambient.span_of_basis(self.basis)
|
|
2231
|
+
|
|
2232
|
+
def _apply_functor_to_morphism(self, f):
|
|
2233
|
+
"""
|
|
2234
|
+
This is not implemented yet.
|
|
2235
|
+
|
|
2236
|
+
TESTS::
|
|
2237
|
+
|
|
2238
|
+
sage: # needs sage.modules
|
|
2239
|
+
sage: F = (ZZ^3).span([(1,2,3), (4,5,6)]).construction()[0]
|
|
2240
|
+
sage: P.<x,y> = ZZ[]
|
|
2241
|
+
sage: f = P.hom([x + 2*y, 3*x - y],P)
|
|
2242
|
+
sage: F(f) # indirect doctest
|
|
2243
|
+
Traceback (most recent call last):
|
|
2244
|
+
...
|
|
2245
|
+
NotImplementedError: Cannot create morphisms of free sub-modules yet
|
|
2246
|
+
"""
|
|
2247
|
+
raise NotImplementedError("Cannot create morphisms of free sub-modules yet")
|
|
2248
|
+
|
|
2249
|
+
def __eq__(self, other):
|
|
2250
|
+
"""
|
|
2251
|
+
TESTS::
|
|
2252
|
+
|
|
2253
|
+
sage: # needs sage.modules
|
|
2254
|
+
sage: F1 = (GF(5)^3).span([(1,2,3),(4,5,6)]).construction()[0]
|
|
2255
|
+
sage: F2 = (ZZ^3).span([(1,2,3),(4,5,6)]).construction()[0]
|
|
2256
|
+
sage: F3 = (QQ^3).span([(1,2,3),(4,5,6)]).construction()[0]
|
|
2257
|
+
sage: F4 = (ZZ^3).span([(1,0,-1),(0,1,2)]).construction()[0]
|
|
2258
|
+
sage: F1 == loads(dumps(F1))
|
|
2259
|
+
True
|
|
2260
|
+
|
|
2261
|
+
The ``span`` method automatically transforms the given basis into
|
|
2262
|
+
echelon form. The bases look like that::
|
|
2263
|
+
|
|
2264
|
+
sage: # needs sage.modules
|
|
2265
|
+
sage: F1.basis
|
|
2266
|
+
[(1, 0, 4), (0, 1, 2)]
|
|
2267
|
+
sage: F2.basis
|
|
2268
|
+
[(1, 2, 3), (0, 3, 6)]
|
|
2269
|
+
sage: F3.basis
|
|
2270
|
+
[(1, 0, -1), (0, 1, 2)]
|
|
2271
|
+
sage: F4.basis
|
|
2272
|
+
[(1, 0, -1), (0, 1, 2)]
|
|
2273
|
+
|
|
2274
|
+
|
|
2275
|
+
The basis of ``F2`` is modulo 5 different from the other bases.
|
|
2276
|
+
So, we have::
|
|
2277
|
+
|
|
2278
|
+
sage: F1 != F2 != F3 # needs sage.modules
|
|
2279
|
+
True
|
|
2280
|
+
|
|
2281
|
+
The bases of ``F1``, ``F3`` and ``F4`` are the same modulo 5; however,
|
|
2282
|
+
there is no coercion from ``QQ^3`` to ``GF(5)^3``. Therefore, we have::
|
|
2283
|
+
|
|
2284
|
+
sage: F1 == F3 # needs sage.modules
|
|
2285
|
+
False
|
|
2286
|
+
|
|
2287
|
+
But there are coercions from ``ZZ^3`` to ``QQ^3`` and ``GF(5)^3``, thus::
|
|
2288
|
+
|
|
2289
|
+
sage: F1 == F4 == F3 # needs sage.modules
|
|
2290
|
+
True
|
|
2291
|
+
"""
|
|
2292
|
+
if not isinstance(other, SubspaceFunctor):
|
|
2293
|
+
return False
|
|
2294
|
+
|
|
2295
|
+
# since comparing the basis involves constructing the pushout
|
|
2296
|
+
# of the ambient module, we cannot do:
|
|
2297
|
+
# c = (self.basis == other.basis)
|
|
2298
|
+
# Instead, we only test whether there are coercions.
|
|
2299
|
+
L = self.basis.universe()
|
|
2300
|
+
R = other.basis.universe()
|
|
2301
|
+
c = (L == R)
|
|
2302
|
+
if L.has_coerce_map_from(R):
|
|
2303
|
+
return tuple(self.basis) == tuple(L(x) for x in other.basis)
|
|
2304
|
+
elif R.has_coerce_map_from(L):
|
|
2305
|
+
return tuple(other.basis) == tuple(R(x) for x in self.basis)
|
|
2306
|
+
return c
|
|
2307
|
+
|
|
2308
|
+
def __ne__(self, other):
|
|
2309
|
+
"""
|
|
2310
|
+
Check whether ``self`` is not equal to ``other``.
|
|
2311
|
+
|
|
2312
|
+
EXAMPLES::
|
|
2313
|
+
|
|
2314
|
+
sage: F1 = (GF(5)^3).span([(1,2,3),(4,5,6)]).construction()[0] # needs sage.modules
|
|
2315
|
+
sage: F1 != loads(dumps(F1)) # needs sage.modules
|
|
2316
|
+
False
|
|
2317
|
+
"""
|
|
2318
|
+
return not (self == other)
|
|
2319
|
+
|
|
2320
|
+
__hash__ = ConstructionFunctor.__hash__
|
|
2321
|
+
|
|
2322
|
+
def merge(self, other):
|
|
2323
|
+
"""
|
|
2324
|
+
Two Subspace Functors are merged into a construction functor of the sum of two subspaces.
|
|
2325
|
+
|
|
2326
|
+
EXAMPLES::
|
|
2327
|
+
|
|
2328
|
+
sage: # needs sage.modules
|
|
2329
|
+
sage: M = GF(5)^3
|
|
2330
|
+
sage: S1 = M.submodule([(1,2,3),(4,5,6)])
|
|
2331
|
+
sage: S2 = M.submodule([(2,2,3)])
|
|
2332
|
+
sage: F1 = S1.construction()[0]
|
|
2333
|
+
sage: F2 = S2.construction()[0]
|
|
2334
|
+
sage: F1.merge(F2)
|
|
2335
|
+
SubspaceFunctor
|
|
2336
|
+
sage: F1.merge(F2)(GF(5)^3) == S1 + S2
|
|
2337
|
+
True
|
|
2338
|
+
sage: F1.merge(F2)(GF(5)['t']^3)
|
|
2339
|
+
Free module of degree 3 and rank 3
|
|
2340
|
+
over Univariate Polynomial Ring in t over Finite Field of size 5
|
|
2341
|
+
User basis matrix:
|
|
2342
|
+
[1 0 0]
|
|
2343
|
+
[0 1 0]
|
|
2344
|
+
[0 0 1]
|
|
2345
|
+
|
|
2346
|
+
TESTS::
|
|
2347
|
+
|
|
2348
|
+
sage: # needs sage.modules
|
|
2349
|
+
sage: P.<t> = ZZ[]
|
|
2350
|
+
sage: S1 = (ZZ^3).submodule([(1,2,3), (4,5,6)])
|
|
2351
|
+
sage: S2 = (Frac(P)^3).submodule([(t,t^2,t^3+1), (4*t,0,1)])
|
|
2352
|
+
sage: v = S1([0,3,6]) + S2([2,0,1/(2*t)]); v # indirect doctest
|
|
2353
|
+
(2, 3, (-12*t - 1)/(-2*t))
|
|
2354
|
+
sage: v.parent()
|
|
2355
|
+
Vector space of degree 3 and dimension 3
|
|
2356
|
+
over Fraction Field of Univariate Polynomial Ring in t over Integer Ring
|
|
2357
|
+
User basis matrix:
|
|
2358
|
+
[1 0 0]
|
|
2359
|
+
[0 1 0]
|
|
2360
|
+
[0 0 1]
|
|
2361
|
+
"""
|
|
2362
|
+
if isinstance(other, SubspaceFunctor):
|
|
2363
|
+
# in order to remove linear dependencies, and in
|
|
2364
|
+
# order to test compatibility of the base rings,
|
|
2365
|
+
# we try to construct a sample submodule
|
|
2366
|
+
if not other.basis:
|
|
2367
|
+
return self
|
|
2368
|
+
if not self.basis:
|
|
2369
|
+
return other
|
|
2370
|
+
try:
|
|
2371
|
+
P = pushout(self.basis[0].parent().ambient_module(),
|
|
2372
|
+
other.basis[0].parent().ambient_module())
|
|
2373
|
+
except CoercionException:
|
|
2374
|
+
return None
|
|
2375
|
+
try:
|
|
2376
|
+
# Use span instead of submodule because we want to
|
|
2377
|
+
# allow denominators.
|
|
2378
|
+
submodule = P.span
|
|
2379
|
+
except AttributeError:
|
|
2380
|
+
return None
|
|
2381
|
+
S = submodule(self.basis + other.basis).echelonized_basis()
|
|
2382
|
+
return SubspaceFunctor(S)
|
|
2383
|
+
else:
|
|
2384
|
+
return None
|
|
2385
|
+
|
|
2386
|
+
|
|
2387
|
+
class FractionField(ConstructionFunctor):
|
|
2388
|
+
"""
|
|
2389
|
+
Construction functor for fraction fields.
|
|
2390
|
+
|
|
2391
|
+
EXAMPLES::
|
|
2392
|
+
|
|
2393
|
+
sage: F = QQ.construction()[0]
|
|
2394
|
+
sage: F
|
|
2395
|
+
FractionField
|
|
2396
|
+
sage: F.domain()
|
|
2397
|
+
Category of integral domains
|
|
2398
|
+
sage: F.codomain()
|
|
2399
|
+
Category of fields
|
|
2400
|
+
sage: F(GF(5)) is GF(5)
|
|
2401
|
+
True
|
|
2402
|
+
sage: F(ZZ['t'])
|
|
2403
|
+
Fraction Field of Univariate Polynomial Ring in t over Integer Ring
|
|
2404
|
+
sage: P.<x,y> = QQ[]
|
|
2405
|
+
sage: f = P.hom([x+2*y,3*x-y],P)
|
|
2406
|
+
sage: F(f)
|
|
2407
|
+
Ring endomorphism of
|
|
2408
|
+
Fraction Field of Multivariate Polynomial Ring in x, y over Rational Field
|
|
2409
|
+
Defn: x |--> x + 2*y
|
|
2410
|
+
y |--> 3*x - y
|
|
2411
|
+
sage: F(f)(1/x)
|
|
2412
|
+
1/(x + 2*y)
|
|
2413
|
+
sage: F == loads(dumps(F))
|
|
2414
|
+
True
|
|
2415
|
+
"""
|
|
2416
|
+
rank = 5
|
|
2417
|
+
|
|
2418
|
+
def __init__(self):
|
|
2419
|
+
"""
|
|
2420
|
+
TESTS::
|
|
2421
|
+
|
|
2422
|
+
sage: from sage.categories.pushout import FractionField
|
|
2423
|
+
sage: F = FractionField()
|
|
2424
|
+
sage: F
|
|
2425
|
+
FractionField
|
|
2426
|
+
sage: F(ZZ['t'])
|
|
2427
|
+
Fraction Field of Univariate Polynomial Ring in t over Integer Ring
|
|
2428
|
+
"""
|
|
2429
|
+
from sage.categories.fields import Fields
|
|
2430
|
+
from sage.categories.integral_domains import IntegralDomains
|
|
2431
|
+
Functor.__init__(self, IntegralDomains(), Fields())
|
|
2432
|
+
|
|
2433
|
+
def _apply_functor(self, R):
|
|
2434
|
+
"""
|
|
2435
|
+
Apply the functor to an object of ``self``'s domain.
|
|
2436
|
+
|
|
2437
|
+
TESTS::
|
|
2438
|
+
|
|
2439
|
+
sage: F = QQ.construction()[0]
|
|
2440
|
+
sage: F(GF(5)['t']) # indirect doctest
|
|
2441
|
+
Fraction Field of Univariate Polynomial Ring in t
|
|
2442
|
+
over Finite Field of size 5
|
|
2443
|
+
"""
|
|
2444
|
+
return R.fraction_field()
|
|
2445
|
+
|
|
2446
|
+
|
|
2447
|
+
class CompletionFunctor(ConstructionFunctor):
|
|
2448
|
+
"""
|
|
2449
|
+
Completion of a ring with respect to a given prime (including infinity).
|
|
2450
|
+
|
|
2451
|
+
EXAMPLES::
|
|
2452
|
+
|
|
2453
|
+
sage: # needs sage.rings.padics
|
|
2454
|
+
sage: R = Zp(5)
|
|
2455
|
+
sage: R
|
|
2456
|
+
5-adic Ring with capped relative precision 20
|
|
2457
|
+
sage: F1 = R.construction()[0]
|
|
2458
|
+
sage: F1
|
|
2459
|
+
Completion[5, prec=20]
|
|
2460
|
+
sage: F1(ZZ) is R
|
|
2461
|
+
True
|
|
2462
|
+
sage: F1(QQ)
|
|
2463
|
+
5-adic Field with capped relative precision 20
|
|
2464
|
+
|
|
2465
|
+
sage: F2 = RR.construction()[0]
|
|
2466
|
+
sage: F2
|
|
2467
|
+
Completion[+Infinity, prec=53]
|
|
2468
|
+
sage: F2(QQ) is RR
|
|
2469
|
+
True
|
|
2470
|
+
|
|
2471
|
+
sage: P.<x> = ZZ[]
|
|
2472
|
+
sage: Px = P.completion(x) # currently the only implemented completion of P
|
|
2473
|
+
sage: Px
|
|
2474
|
+
Power Series Ring in x over Integer Ring
|
|
2475
|
+
sage: F3 = Px.construction()[0]
|
|
2476
|
+
sage: F3(GF(3)['x'])
|
|
2477
|
+
Power Series Ring in x over Finite Field of size 3
|
|
2478
|
+
|
|
2479
|
+
TESTS::
|
|
2480
|
+
|
|
2481
|
+
sage: # needs sage.rings.padics
|
|
2482
|
+
sage: R1.<a> = Zp(5, prec=20)[]
|
|
2483
|
+
sage: R2 = Qp(5, prec=40)
|
|
2484
|
+
sage: R2(1) + a
|
|
2485
|
+
(1 + O(5^20))*a + 1 + O(5^40)
|
|
2486
|
+
sage: 1/2 + a
|
|
2487
|
+
(1 + O(5^20))*a + 3 + 2*5 + 2*5^2 + 2*5^3 + 2*5^4 + 2*5^5 + 2*5^6 + 2*5^7 + 2*5^8 + 2*5^9 + 2*5^10 + 2*5^11 + 2*5^12 + 2*5^13 + 2*5^14 + 2*5^15 + 2*5^16 + 2*5^17 + 2*5^18 + 2*5^19 + O(5^20)
|
|
2488
|
+
"""
|
|
2489
|
+
rank = 4
|
|
2490
|
+
_real_types = ['Interval', 'Ball', 'MPFR', 'RDF', 'RLF', 'RR']
|
|
2491
|
+
_dvr_types = [None, 'fixed-mod', 'floating-point', 'capped-abs', 'capped-rel', 'lattice-cap', 'lattice-float', 'relaxed']
|
|
2492
|
+
|
|
2493
|
+
def __init__(self, p, prec, extras=None):
|
|
2494
|
+
"""
|
|
2495
|
+
INPUT:
|
|
2496
|
+
|
|
2497
|
+
- ``p`` -- prime number, the generator of a univariate polynomial ring,
|
|
2498
|
+
or ``+Infinity``
|
|
2499
|
+
|
|
2500
|
+
- ``prec`` -- integer; yielding the precision in bits. Note that
|
|
2501
|
+
if ``p`` is prime then the ``prec`` is the *capped* precision,
|
|
2502
|
+
while it is the *set* precision if ``p`` is ``+Infinity``.
|
|
2503
|
+
In the ``lattice-cap`` precision case, ``prec`` will be a tuple instead.
|
|
2504
|
+
|
|
2505
|
+
- ``extras`` -- dictionary (optional); information on how to print elements, etc.
|
|
2506
|
+
If 'type' is given as a key, the corresponding value should be a string among
|
|
2507
|
+
the following:
|
|
2508
|
+
|
|
2509
|
+
- 'RDF', 'Interval', 'RLF', or 'RR' for completions at infinity
|
|
2510
|
+
|
|
2511
|
+
- 'capped-rel', 'capped-abs', 'fixed-mod', 'lattice-cap' or 'lattice-float'
|
|
2512
|
+
for completions at a finite place or ideal of a DVR.
|
|
2513
|
+
|
|
2514
|
+
TESTS::
|
|
2515
|
+
|
|
2516
|
+
sage: from sage.categories.pushout import CompletionFunctor
|
|
2517
|
+
sage: F1 = CompletionFunctor(5, 100)
|
|
2518
|
+
sage: F1(QQ) # needs sage.rings.padics
|
|
2519
|
+
5-adic Field with capped relative precision 100
|
|
2520
|
+
sage: F1(ZZ) # needs sage.rings.padics
|
|
2521
|
+
5-adic Ring with capped relative precision 100
|
|
2522
|
+
sage: F1.type is None
|
|
2523
|
+
True
|
|
2524
|
+
sage: sorted(F1.extras.items())
|
|
2525
|
+
[]
|
|
2526
|
+
sage: F2 = RR.construction()[0]
|
|
2527
|
+
sage: F2
|
|
2528
|
+
Completion[+Infinity, prec=53]
|
|
2529
|
+
sage: F2.type # needs sage.rings.real_mpfr
|
|
2530
|
+
'MPFR'
|
|
2531
|
+
sage: F2.extras # needs sage.rings.real_mpfr
|
|
2532
|
+
{'rnd': 0, 'sci_not': False}
|
|
2533
|
+
"""
|
|
2534
|
+
Functor.__init__(self, Rings(), Rings())
|
|
2535
|
+
self.p = p
|
|
2536
|
+
self.prec = prec
|
|
2537
|
+
|
|
2538
|
+
if extras is None:
|
|
2539
|
+
self.extras = {}
|
|
2540
|
+
self.type = None
|
|
2541
|
+
else:
|
|
2542
|
+
self.extras = dict(extras)
|
|
2543
|
+
self.type = self.extras.pop('type', None)
|
|
2544
|
+
from sage.rings.infinity import Infinity
|
|
2545
|
+
if self.p == Infinity:
|
|
2546
|
+
if self.type not in self._real_types:
|
|
2547
|
+
raise ValueError("completion type must be one of %s" % (", ".join(self._real_types)))
|
|
2548
|
+
elif self.type not in self._dvr_types:
|
|
2549
|
+
raise ValueError("completion type must be one of %s" % (", ".join(self._dvr_types[1:])))
|
|
2550
|
+
|
|
2551
|
+
def _repr_(self):
|
|
2552
|
+
"""
|
|
2553
|
+
TESTS::
|
|
2554
|
+
|
|
2555
|
+
sage: Zp(7).construction() # indirect doctest # needs sage.rings.padics
|
|
2556
|
+
(Completion[7, prec=20], Integer Ring)
|
|
2557
|
+
|
|
2558
|
+
sage: RR.construction() # indirect doctest
|
|
2559
|
+
(Completion[+Infinity, prec=53], Rational Field)
|
|
2560
|
+
"""
|
|
2561
|
+
return 'Completion[%s, prec=%s]' % (self.p, self.prec)
|
|
2562
|
+
|
|
2563
|
+
def _apply_functor(self, R):
|
|
2564
|
+
"""
|
|
2565
|
+
Apply the functor to an object of ``self``'s domain.
|
|
2566
|
+
|
|
2567
|
+
TESTS::
|
|
2568
|
+
|
|
2569
|
+
sage: # needs sage.rings.padics
|
|
2570
|
+
sage: R = Zp(5)
|
|
2571
|
+
sage: F1 = R.construction()[0]
|
|
2572
|
+
sage: F1(ZZ) is R # indirect doctest
|
|
2573
|
+
True
|
|
2574
|
+
sage: F1(QQ)
|
|
2575
|
+
5-adic Field with capped relative precision 20
|
|
2576
|
+
"""
|
|
2577
|
+
try:
|
|
2578
|
+
if not self.extras:
|
|
2579
|
+
if self.type is None:
|
|
2580
|
+
try:
|
|
2581
|
+
return R.completion(self.p, self.prec)
|
|
2582
|
+
except TypeError:
|
|
2583
|
+
return R.completion(self.p, self.prec, {})
|
|
2584
|
+
else:
|
|
2585
|
+
return R.completion(self.p, self.prec, {'type': self.type})
|
|
2586
|
+
else:
|
|
2587
|
+
extras = self.extras.copy()
|
|
2588
|
+
if self.type is not None:
|
|
2589
|
+
extras['type'] = self.type
|
|
2590
|
+
return R.completion(self.p, self.prec, extras)
|
|
2591
|
+
except (NotImplementedError, AttributeError):
|
|
2592
|
+
if R.construction() is None:
|
|
2593
|
+
raise NotImplementedError("Completion is not implemented for %s" % R.__class__)
|
|
2594
|
+
F, BR = R.construction()
|
|
2595
|
+
M = self.merge(F) or F.merge(self)
|
|
2596
|
+
if M is not None:
|
|
2597
|
+
return M(BR)
|
|
2598
|
+
if self.commutes(F) or F.commutes(self):
|
|
2599
|
+
return F(self(BR))
|
|
2600
|
+
raise NotImplementedError("Don't know how to apply %s to %s" % (repr(self), repr(R)))
|
|
2601
|
+
|
|
2602
|
+
def __eq__(self, other):
|
|
2603
|
+
"""
|
|
2604
|
+
.. NOTE::
|
|
2605
|
+
|
|
2606
|
+
Only the prime used in the completion is relevant to comparison
|
|
2607
|
+
of Completion functors, although the resulting rings also take
|
|
2608
|
+
the precision into account.
|
|
2609
|
+
|
|
2610
|
+
TESTS::
|
|
2611
|
+
|
|
2612
|
+
sage: # needs sage.rings.padics
|
|
2613
|
+
sage: R1 = Zp(5, prec=30)
|
|
2614
|
+
sage: R2 = Zp(5, prec=40)
|
|
2615
|
+
sage: F1 = R1.construction()[0]
|
|
2616
|
+
sage: F2 = R2.construction()[0]
|
|
2617
|
+
sage: F1 == loads(dumps(F1)) # indirect doctest
|
|
2618
|
+
True
|
|
2619
|
+
sage: F1 == F2
|
|
2620
|
+
True
|
|
2621
|
+
sage: F1(QQ) == F2(QQ)
|
|
2622
|
+
False
|
|
2623
|
+
sage: R3 = Zp(7)
|
|
2624
|
+
sage: F3 = R3.construction()[0]
|
|
2625
|
+
sage: F1 == F3
|
|
2626
|
+
False
|
|
2627
|
+
"""
|
|
2628
|
+
if isinstance(other, CompletionFunctor):
|
|
2629
|
+
return self.p == other.p
|
|
2630
|
+
return False
|
|
2631
|
+
|
|
2632
|
+
def __ne__(self, other):
|
|
2633
|
+
"""
|
|
2634
|
+
Check whether ``self`` is not equal to ``other``.
|
|
2635
|
+
|
|
2636
|
+
EXAMPLES::
|
|
2637
|
+
|
|
2638
|
+
sage: # needs sage.rings.padics
|
|
2639
|
+
sage: R1 = Zp(5, prec=30)
|
|
2640
|
+
sage: R2 = Zp(5, prec=40)
|
|
2641
|
+
sage: F1 = R1.construction()[0]
|
|
2642
|
+
sage: F2 = R2.construction()[0]
|
|
2643
|
+
sage: F1 != loads(dumps(F1)) # indirect doctest
|
|
2644
|
+
False
|
|
2645
|
+
sage: F1 != F2
|
|
2646
|
+
False
|
|
2647
|
+
sage: F1(QQ) != F2(QQ)
|
|
2648
|
+
True
|
|
2649
|
+
sage: R3 = Zp(7)
|
|
2650
|
+
sage: F3 = R3.construction()[0]
|
|
2651
|
+
sage: F1 != F3
|
|
2652
|
+
True
|
|
2653
|
+
"""
|
|
2654
|
+
return not (self == other)
|
|
2655
|
+
|
|
2656
|
+
__hash__ = ConstructionFunctor.__hash__
|
|
2657
|
+
|
|
2658
|
+
def merge(self, other):
|
|
2659
|
+
"""
|
|
2660
|
+
Two Completion functors are merged, if they are equal. If the precisions of
|
|
2661
|
+
both functors coincide, then a Completion functor is returned that results
|
|
2662
|
+
from updating the ``extras`` dictionary of ``self`` by ``other.extras``.
|
|
2663
|
+
Otherwise, if the completion is at infinity then merging does not increase
|
|
2664
|
+
the set precision, and if the completion is at a finite prime, merging
|
|
2665
|
+
does not decrease the capped precision.
|
|
2666
|
+
|
|
2667
|
+
EXAMPLES::
|
|
2668
|
+
|
|
2669
|
+
sage: # needs sage.rings.padics
|
|
2670
|
+
sage: R1.<a> = Zp(5, prec=20)[]
|
|
2671
|
+
sage: R2 = Qp(5, prec=40)
|
|
2672
|
+
sage: R2(1) + a # indirect doctest
|
|
2673
|
+
(1 + O(5^20))*a + 1 + O(5^40)
|
|
2674
|
+
sage: R3 = RealField(30)
|
|
2675
|
+
sage: R4 = RealField(50)
|
|
2676
|
+
sage: R3(1) + R4(1) # indirect doctest
|
|
2677
|
+
2.0000000
|
|
2678
|
+
sage: (R3(1) + R4(1)).parent()
|
|
2679
|
+
Real Field with 30 bits of precision
|
|
2680
|
+
|
|
2681
|
+
TESTS:
|
|
2682
|
+
|
|
2683
|
+
We check that :issue:`12353` has been resolved::
|
|
2684
|
+
|
|
2685
|
+
sage: RIF(1) > RR(1) # needs sage.rings.real_interval_field
|
|
2686
|
+
Traceback (most recent call last):
|
|
2687
|
+
...
|
|
2688
|
+
TypeError: unsupported operand parent(s) for >:
|
|
2689
|
+
'Real Interval Field with 53 bits of precision' and 'Real Field with 53 bits of precision'
|
|
2690
|
+
|
|
2691
|
+
We check that various pushouts work::
|
|
2692
|
+
|
|
2693
|
+
sage: # needs sage.rings.real_interval_field sage.rings.real_mpfr
|
|
2694
|
+
sage: R0 = RealIntervalField(30)
|
|
2695
|
+
sage: R1 = RealIntervalField(30, sci_not=True)
|
|
2696
|
+
sage: R2 = RealIntervalField(53)
|
|
2697
|
+
sage: R3 = RealIntervalField(53, sci_not=True)
|
|
2698
|
+
sage: R4 = RealIntervalField(90)
|
|
2699
|
+
sage: R5 = RealIntervalField(90, sci_not=True)
|
|
2700
|
+
sage: R6 = RealField(30)
|
|
2701
|
+
sage: R7 = RealField(30, sci_not=True)
|
|
2702
|
+
sage: R8 = RealField(53, rnd='RNDD')
|
|
2703
|
+
sage: R9 = RealField(53, sci_not=True, rnd='RNDZ')
|
|
2704
|
+
sage: R10 = RealField(53, sci_not=True)
|
|
2705
|
+
sage: R11 = RealField(90, sci_not=True, rnd='RNDZ')
|
|
2706
|
+
sage: Rlist = [R0,R1,R2,R3,R4,R5,R6,R7,R8,R9,R10,R11]
|
|
2707
|
+
sage: from sage.categories.pushout import pushout
|
|
2708
|
+
sage: pushouts = [R0,R0,R0,R1,R0,R1,R0,R1,R0,R1,R1,R1,R1,R1,R1,R1,R1,R1,R1,R1,R1,R1,R1,R1,R0,R1,R2,R2,R2,R3,R0,R1,R2,R3,R3,R3,R1,R1,R3,R3,R3,R3,R1,R1,R3,R3,R3,R3,R0,R1,R2,R3,R4,R4,R0,R1,R2,R3,R3,R5,R1,R1,R3,R3,R5,R5,R1,R1,R3,R3,R3,R5,R0,R1,R0,R1,R0,R1,R6,R6,R6,R7,R7,R7,R1,R1,R1,R1,R1,R1,R7,R7,R7,R7,R7,R7,R0,R1,R2,R3,R2,R3,R6,R7,R8,R9,R10,R9,R1,R1,R3,R3,R3,R3,R7,R7,R9,R9,R10,R9,R1,R1,R3,R3,R3,R3,R7,R7,R10,R10,R10,R10,R1,R1,R3,R3,R5,R5,R7,R7,R9,R9,R10,R11]
|
|
2709
|
+
sage: all(R is S for R, S in zip(pushouts, [pushout(a, b) for a in Rlist for b in Rlist]))
|
|
2710
|
+
True
|
|
2711
|
+
|
|
2712
|
+
::
|
|
2713
|
+
|
|
2714
|
+
sage: # needs sage.rings.padics
|
|
2715
|
+
sage: P0 = ZpFM(5, 10)
|
|
2716
|
+
sage: P1 = ZpFM(5, 20)
|
|
2717
|
+
sage: P2 = ZpCR(5, 10)
|
|
2718
|
+
sage: P3 = ZpCR(5, 20)
|
|
2719
|
+
sage: P4 = ZpCA(5, 10)
|
|
2720
|
+
sage: P5 = ZpCA(5, 20)
|
|
2721
|
+
sage: P6 = Qp(5, 10)
|
|
2722
|
+
sage: P7 = Qp(5, 20)
|
|
2723
|
+
sage: Plist = [P2,P3,P4,P5,P6,P7]
|
|
2724
|
+
sage: from sage.categories.pushout import pushout
|
|
2725
|
+
sage: pushouts = [P2,P3,P4,P5,P6,P7,P3,P3,P5,P5,P7,P7,P4,P5,P4,P5,P6,P7,
|
|
2726
|
+
....: P5,P5,P5,P5,P7,P7,P6,P7,P6,P7,P6,P7,P7,P7,P7,P7,P7,P7]
|
|
2727
|
+
sage: all(P is Q
|
|
2728
|
+
....: for P, Q in zip(pushouts, [pushout(a, b) for a in Plist for b in Plist]))
|
|
2729
|
+
True
|
|
2730
|
+
"""
|
|
2731
|
+
if self == other: # both are Completion functors with the same p
|
|
2732
|
+
from sage.rings.infinity import Infinity
|
|
2733
|
+
if self.p == Infinity:
|
|
2734
|
+
new_prec = min(self.prec, other.prec)
|
|
2735
|
+
new_type = self._real_types[min(self._real_types.index(self.type),
|
|
2736
|
+
self._real_types.index(other.type))]
|
|
2737
|
+
new_scinot = max(self.extras.get('sci_not', 0),
|
|
2738
|
+
other.extras.get('sci_not', 0))
|
|
2739
|
+
new_rnd = min(self.extras.get('rnd', 0),
|
|
2740
|
+
other.extras.get('rnd', 0))
|
|
2741
|
+
return CompletionFunctor(self.p, new_prec,
|
|
2742
|
+
{'type': new_type,
|
|
2743
|
+
'sci_not': new_scinot,
|
|
2744
|
+
'rnd': new_rnd})
|
|
2745
|
+
else:
|
|
2746
|
+
new_type = self._dvr_types[min(self._dvr_types.index(self.type), self._dvr_types.index(other.type))]
|
|
2747
|
+
if new_type in ('fixed-mod', 'floating-point'):
|
|
2748
|
+
if self.type != other.type:
|
|
2749
|
+
return None # no coercion into fixed-mod or floating-point
|
|
2750
|
+
new_prec = min(self.prec, other.prec)
|
|
2751
|
+
else:
|
|
2752
|
+
new_prec = max(self.prec, other.prec) # since elements track their own precision, we don't want to truncate them
|
|
2753
|
+
extras = self.extras.copy()
|
|
2754
|
+
extras.update(other.extras)
|
|
2755
|
+
extras['type'] = new_type
|
|
2756
|
+
return CompletionFunctor(self.p, new_prec, extras)
|
|
2757
|
+
|
|
2758
|
+
# Completion has a lower rank than FractionField
|
|
2759
|
+
# and is thus applied first. However, fact is that
|
|
2760
|
+
# both commute. This is used in the call method,
|
|
2761
|
+
# since some fraction fields have no completion method
|
|
2762
|
+
# implemented.
|
|
2763
|
+
|
|
2764
|
+
def commutes(self, other):
|
|
2765
|
+
"""
|
|
2766
|
+
Completion commutes with fraction fields.
|
|
2767
|
+
|
|
2768
|
+
EXAMPLES::
|
|
2769
|
+
|
|
2770
|
+
sage: F1 = Zp(5).construction()[0] # needs sage.rings.padics
|
|
2771
|
+
sage: F2 = QQ.construction()[0]
|
|
2772
|
+
sage: F1.commutes(F2) # needs sage.rings.padics
|
|
2773
|
+
True
|
|
2774
|
+
|
|
2775
|
+
TESTS:
|
|
2776
|
+
|
|
2777
|
+
The fraction field ``R`` in the example below has no completion
|
|
2778
|
+
method. But completion commutes with the fraction field functor,
|
|
2779
|
+
and so it is tried internally whether applying the construction
|
|
2780
|
+
functors in opposite order works. It does::
|
|
2781
|
+
|
|
2782
|
+
sage: P.<x> = ZZ[]
|
|
2783
|
+
sage: C = P.completion(x).construction()[0]
|
|
2784
|
+
sage: R = FractionField(P)
|
|
2785
|
+
sage: hasattr(R,'completion')
|
|
2786
|
+
False
|
|
2787
|
+
sage: C(R) is Frac(C(P))
|
|
2788
|
+
True
|
|
2789
|
+
sage: F = R.construction()[0]
|
|
2790
|
+
sage: (C*F)(ZZ['x']) is (F*C)(ZZ['x'])
|
|
2791
|
+
True
|
|
2792
|
+
|
|
2793
|
+
The following was fixed in :issue:`15329` (it used to result
|
|
2794
|
+
in an infinite recursion. In :issue:`23218` the construction
|
|
2795
|
+
of `p`-adic fields changed, so there is no longer an
|
|
2796
|
+
Ambiguous base extension error raised)::
|
|
2797
|
+
|
|
2798
|
+
sage: from sage.categories.pushout import pushout
|
|
2799
|
+
sage: pushout(Qp(7), RLF) # needs sage.rings.padics
|
|
2800
|
+
Traceback (most recent call last):
|
|
2801
|
+
...
|
|
2802
|
+
CoercionException: Don't know how to
|
|
2803
|
+
apply Completion[+Infinity, prec=+Infinity]
|
|
2804
|
+
to 7-adic Ring with capped relative precision 20
|
|
2805
|
+
"""
|
|
2806
|
+
return isinstance(other, FractionField)
|
|
2807
|
+
|
|
2808
|
+
|
|
2809
|
+
class QuotientFunctor(ConstructionFunctor):
|
|
2810
|
+
"""
|
|
2811
|
+
Construction functor for quotient rings.
|
|
2812
|
+
|
|
2813
|
+
.. NOTE::
|
|
2814
|
+
|
|
2815
|
+
The functor keeps track of variable names. Optionally, it may
|
|
2816
|
+
keep track of additional properties of the quotient, such as
|
|
2817
|
+
its category or its implementation.
|
|
2818
|
+
|
|
2819
|
+
EXAMPLES::
|
|
2820
|
+
|
|
2821
|
+
sage: P.<x,y> = ZZ[]
|
|
2822
|
+
sage: Q = P.quo([x^2 + y^2] * P)
|
|
2823
|
+
sage: F = Q.construction()[0]
|
|
2824
|
+
sage: F(QQ['x','y'])
|
|
2825
|
+
Quotient of Multivariate Polynomial Ring in x, y over Rational Field
|
|
2826
|
+
by the ideal (x^2 + y^2)
|
|
2827
|
+
sage: F(QQ['x','y']) == QQ['x','y'].quo([x^2 + y^2] * QQ['x','y'])
|
|
2828
|
+
True
|
|
2829
|
+
sage: F(QQ['x','y','z'])
|
|
2830
|
+
Traceback (most recent call last):
|
|
2831
|
+
...
|
|
2832
|
+
CoercionException: Cannot apply this quotient functor to
|
|
2833
|
+
Multivariate Polynomial Ring in x, y, z over Rational Field
|
|
2834
|
+
sage: F(QQ['y','z']) # needs sage.rings.finite_rings
|
|
2835
|
+
Traceback (most recent call last):
|
|
2836
|
+
...
|
|
2837
|
+
TypeError: Could not find a mapping of the passed element to this ring.
|
|
2838
|
+
"""
|
|
2839
|
+
rank = 4.5
|
|
2840
|
+
|
|
2841
|
+
def __init__(self, I, names=None, as_field=False, domain=None,
|
|
2842
|
+
codomain=None, **kwds):
|
|
2843
|
+
"""
|
|
2844
|
+
INPUT:
|
|
2845
|
+
|
|
2846
|
+
- ``I`` -- an ideal (the modulus)
|
|
2847
|
+
- ``names`` -- string or list of strings (optional); the names for the
|
|
2848
|
+
quotient ring generators
|
|
2849
|
+
- ``as_field`` -- boolean (default: ``False``); return the quotient
|
|
2850
|
+
ring as field (if available)
|
|
2851
|
+
- ``domain`` -- category (default: ``Rings()``); the domain of
|
|
2852
|
+
this functor
|
|
2853
|
+
- ``codomain`` -- category (default: ``Rings()``); the codomain
|
|
2854
|
+
of this functor
|
|
2855
|
+
- Further named arguments. In particular, an implementation of the
|
|
2856
|
+
quotient can be suggested here. These named arguments are passed to
|
|
2857
|
+
the quotient construction.
|
|
2858
|
+
|
|
2859
|
+
TESTS::
|
|
2860
|
+
|
|
2861
|
+
sage: from sage.categories.pushout import QuotientFunctor
|
|
2862
|
+
sage: P.<t> = ZZ[]
|
|
2863
|
+
sage: F = QuotientFunctor([5 + t^2] * P)
|
|
2864
|
+
sage: F(P) # needs sage.libs.pari
|
|
2865
|
+
Univariate Quotient Polynomial Ring in tbar
|
|
2866
|
+
over Integer Ring with modulus t^2 + 5
|
|
2867
|
+
sage: F(QQ['t']) # needs sage.libs.pari
|
|
2868
|
+
Univariate Quotient Polynomial Ring in tbar
|
|
2869
|
+
over Rational Field with modulus t^2 + 5
|
|
2870
|
+
sage: F = QuotientFunctor([5 + t^2] * P, names='s')
|
|
2871
|
+
sage: F(P) # needs sage.libs.pari
|
|
2872
|
+
Univariate Quotient Polynomial Ring in s
|
|
2873
|
+
over Integer Ring with modulus t^2 + 5
|
|
2874
|
+
sage: F(QQ['t']) # needs sage.libs.pari
|
|
2875
|
+
Univariate Quotient Polynomial Ring in s
|
|
2876
|
+
over Rational Field with modulus t^2 + 5
|
|
2877
|
+
sage: F = QuotientFunctor([5] * ZZ, as_field=True)
|
|
2878
|
+
sage: F(ZZ)
|
|
2879
|
+
Finite Field of size 5
|
|
2880
|
+
sage: F = QuotientFunctor([5] * ZZ)
|
|
2881
|
+
sage: F(ZZ)
|
|
2882
|
+
Ring of integers modulo 5
|
|
2883
|
+
"""
|
|
2884
|
+
if domain is None:
|
|
2885
|
+
domain = Rings()
|
|
2886
|
+
if codomain is None:
|
|
2887
|
+
codomain = Rings()
|
|
2888
|
+
Functor.__init__(self, domain, codomain)
|
|
2889
|
+
|
|
2890
|
+
self.I = I
|
|
2891
|
+
if names is None:
|
|
2892
|
+
self.names = None
|
|
2893
|
+
elif isinstance(names, str):
|
|
2894
|
+
self.names = (names,)
|
|
2895
|
+
else:
|
|
2896
|
+
self.names = tuple(names)
|
|
2897
|
+
self.as_field = as_field
|
|
2898
|
+
self.kwds = kwds
|
|
2899
|
+
|
|
2900
|
+
def _apply_functor(self, R):
|
|
2901
|
+
"""
|
|
2902
|
+
Apply the functor to an object of ``self``'s domain.
|
|
2903
|
+
|
|
2904
|
+
TESTS::
|
|
2905
|
+
|
|
2906
|
+
sage: P.<x,y> = ZZ[]
|
|
2907
|
+
sage: Q = P.quo([2 + x^2, 3*x + y^2])
|
|
2908
|
+
sage: F = Q.construction()[0]; F
|
|
2909
|
+
QuotientFunctor
|
|
2910
|
+
sage: F(QQ['x','y']) # indirect doctest
|
|
2911
|
+
Quotient of Multivariate Polynomial Ring in x, y over Rational Field
|
|
2912
|
+
by the ideal (x^2 + 2, y^2 + 3*x)
|
|
2913
|
+
|
|
2914
|
+
Note that the ``quo()`` method of a field used to return the
|
|
2915
|
+
integer zero. That strange behaviour was removed in github
|
|
2916
|
+
issue :issue:`9138`. It now returns a trivial quotient ring
|
|
2917
|
+
when applied to a field::
|
|
2918
|
+
|
|
2919
|
+
sage: F = ZZ.quo([5]*ZZ).construction()[0]
|
|
2920
|
+
sage: F(QQ)
|
|
2921
|
+
Ring of integers modulo 1
|
|
2922
|
+
sage: QQ.quo(5)
|
|
2923
|
+
Quotient of Rational Field by the ideal (1)
|
|
2924
|
+
"""
|
|
2925
|
+
I = self.I
|
|
2926
|
+
if not I.is_zero():
|
|
2927
|
+
from sage.categories.fields import Fields
|
|
2928
|
+
if R in Fields():
|
|
2929
|
+
from sage.rings.finite_rings.integer_mod_ring import Integers
|
|
2930
|
+
return Integers(1)
|
|
2931
|
+
if I.ring() != R:
|
|
2932
|
+
if I.ring().has_coerce_map_from(R):
|
|
2933
|
+
R = I.ring()
|
|
2934
|
+
else:
|
|
2935
|
+
R = pushout(R, I.ring().base_ring())
|
|
2936
|
+
I = R.ideal([R.one() * t for t in I.gens()], warn=False)
|
|
2937
|
+
try:
|
|
2938
|
+
Q = R.quo(I, names=self.names, **self.kwds)
|
|
2939
|
+
except IndexError: # That may happen!
|
|
2940
|
+
raise CoercionException("Cannot apply this quotient functor to %s" % R)
|
|
2941
|
+
if self.as_field:
|
|
2942
|
+
try:
|
|
2943
|
+
Q = Q.field()
|
|
2944
|
+
except AttributeError:
|
|
2945
|
+
pass
|
|
2946
|
+
return Q
|
|
2947
|
+
|
|
2948
|
+
def __eq__(self, other):
|
|
2949
|
+
"""
|
|
2950
|
+
The types, domain, codomain, names and moduli are compared.
|
|
2951
|
+
|
|
2952
|
+
TESTS::
|
|
2953
|
+
|
|
2954
|
+
sage: # needs sage.libs.pari
|
|
2955
|
+
sage: P.<x> = QQ[]
|
|
2956
|
+
sage: F = P.quo([(x^2+1)^2*(x^2-3),(x^2+1)^2*(x^5+3)]).construction()[0]
|
|
2957
|
+
sage: F == loads(dumps(F))
|
|
2958
|
+
True
|
|
2959
|
+
sage: P2.<x,y> = QQ[]
|
|
2960
|
+
sage: F == P2.quo([(x^2+1)^2*(x^2-3),(x^2+1)^2*(x^5+3)]).construction()[0]
|
|
2961
|
+
False
|
|
2962
|
+
sage: P3.<x> = ZZ[]
|
|
2963
|
+
sage: F == P3.quo([(x^2+1)^2*(x^2-3),(x^2+1)^2*(x^5+3)]).construction()[0]
|
|
2964
|
+
True
|
|
2965
|
+
"""
|
|
2966
|
+
if not isinstance(other, QuotientFunctor):
|
|
2967
|
+
return False
|
|
2968
|
+
return (type(self) is type(other) and
|
|
2969
|
+
self.domain() == other.domain() and
|
|
2970
|
+
self.codomain() == other.codomain() and
|
|
2971
|
+
self.names == other.names and
|
|
2972
|
+
self.I == other.I)
|
|
2973
|
+
|
|
2974
|
+
def __ne__(self, other):
|
|
2975
|
+
"""
|
|
2976
|
+
Check whether ``self`` is not equal to ``other``.
|
|
2977
|
+
|
|
2978
|
+
EXAMPLES::
|
|
2979
|
+
|
|
2980
|
+
sage: # needs sage.libs.pari
|
|
2981
|
+
sage: P.<x> = QQ[]
|
|
2982
|
+
sage: F = P.quo([(x^2+1)^2*(x^2-3),(x^2+1)^2*(x^5+3)]).construction()[0]
|
|
2983
|
+
sage: F != loads(dumps(F))
|
|
2984
|
+
False
|
|
2985
|
+
sage: P2.<x,y> = QQ[]
|
|
2986
|
+
sage: F != P2.quo([(x^2+1)^2*(x^2-3),(x^2+1)^2*(x^5+3)]).construction()[0]
|
|
2987
|
+
True
|
|
2988
|
+
sage: P3.<x> = ZZ[]
|
|
2989
|
+
sage: F != P3.quo([(x^2+1)^2*(x^2-3),(x^2+1)^2*(x^5+3)]).construction()[0]
|
|
2990
|
+
False
|
|
2991
|
+
"""
|
|
2992
|
+
return not (self == other)
|
|
2993
|
+
|
|
2994
|
+
__hash__ = ConstructionFunctor.__hash__
|
|
2995
|
+
|
|
2996
|
+
def merge(self, other):
|
|
2997
|
+
"""
|
|
2998
|
+
Two quotient functors with coinciding names are merged by taking the gcd
|
|
2999
|
+
of their moduli, the meet of their domains, and the join of their codomains.
|
|
3000
|
+
|
|
3001
|
+
In particular, if one of the functors being merged knows that the quotient
|
|
3002
|
+
is going to be a field, then the merged functor will return fields as
|
|
3003
|
+
well.
|
|
3004
|
+
|
|
3005
|
+
EXAMPLES::
|
|
3006
|
+
|
|
3007
|
+
sage: # needs sage.libs.pari
|
|
3008
|
+
sage: P.<x> = QQ[]
|
|
3009
|
+
sage: Q1 = P.quo([(x^2+1)^2*(x^2-3)])
|
|
3010
|
+
sage: Q2 = P.quo([(x^2+1)^2*(x^5+3)])
|
|
3011
|
+
sage: from sage.categories.pushout import pushout
|
|
3012
|
+
sage: pushout(Q1,Q2) # indirect doctest
|
|
3013
|
+
Univariate Quotient Polynomial Ring in xbar over Rational Field
|
|
3014
|
+
with modulus x^4 + 2*x^2 + 1
|
|
3015
|
+
|
|
3016
|
+
The following was fixed in :issue:`8800`::
|
|
3017
|
+
|
|
3018
|
+
sage: pushout(GF(5), Integers(5)) # needs sage.libs.pari
|
|
3019
|
+
Finite Field of size 5
|
|
3020
|
+
"""
|
|
3021
|
+
if type(self) is not type(other):
|
|
3022
|
+
return None
|
|
3023
|
+
if self.names != other.names:
|
|
3024
|
+
return None
|
|
3025
|
+
if self == other:
|
|
3026
|
+
if self.as_field == other.as_field:
|
|
3027
|
+
# The two functors are *really* equal
|
|
3028
|
+
return self
|
|
3029
|
+
# They are equal up to the additional arguments
|
|
3030
|
+
I = self.I
|
|
3031
|
+
domain = self.domain()
|
|
3032
|
+
codomain = self.codomain()
|
|
3033
|
+
else:
|
|
3034
|
+
try:
|
|
3035
|
+
I = self.I + other.I
|
|
3036
|
+
except (TypeError, NotImplementedError):
|
|
3037
|
+
try:
|
|
3038
|
+
I = self.I.gcd(other.I)
|
|
3039
|
+
except (TypeError, NotImplementedError):
|
|
3040
|
+
return None
|
|
3041
|
+
domain = self.domain().meet([self.domain(), other.domain()])
|
|
3042
|
+
codomain = self.codomain().join([self.codomain(), other.codomain()])
|
|
3043
|
+
# Get the optional arguments:
|
|
3044
|
+
as_field = self.as_field or other.as_field
|
|
3045
|
+
kwds = dict(self.kwds)
|
|
3046
|
+
for k, v in other.kwds.items():
|
|
3047
|
+
if k == 'category':
|
|
3048
|
+
if kwds[k] is not None:
|
|
3049
|
+
kwds[k] = v.join([v, kwds[k]])
|
|
3050
|
+
else:
|
|
3051
|
+
kwds[k] = v
|
|
3052
|
+
continue
|
|
3053
|
+
if k in kwds and kwds[k] is not None and v != kwds[k]:
|
|
3054
|
+
# Don't know what default to choose. Hence: No merge!
|
|
3055
|
+
return None
|
|
3056
|
+
kwds[k] = v
|
|
3057
|
+
if I.is_trivial() and not I.is_zero():
|
|
3058
|
+
# quotient by I would result in the trivial ring/group/...
|
|
3059
|
+
# Rather than create the zero ring, we claim they can't be merged
|
|
3060
|
+
# TODO: Perhaps this should be detected at a higher level...
|
|
3061
|
+
raise TypeError("trivial quotient intersection")
|
|
3062
|
+
# GF(p) has a coercion from Integers(p). Hence, merging should
|
|
3063
|
+
# yield a field if either self or other yields a field.
|
|
3064
|
+
return QuotientFunctor(I, names=self.names, as_field=as_field,
|
|
3065
|
+
domain=domain, codomain=codomain, **kwds)
|
|
3066
|
+
|
|
3067
|
+
|
|
3068
|
+
class AlgebraicExtensionFunctor(ConstructionFunctor):
|
|
3069
|
+
"""
|
|
3070
|
+
Algebraic extension (univariate polynomial ring modulo principal ideal).
|
|
3071
|
+
|
|
3072
|
+
EXAMPLES::
|
|
3073
|
+
|
|
3074
|
+
sage: x = polygen(QQ, 'x')
|
|
3075
|
+
sage: K.<a> = NumberField(x^3 + x^2 + 1) # needs sage.rings.number_field
|
|
3076
|
+
sage: F = K.construction()[0] # needs sage.rings.number_field
|
|
3077
|
+
sage: F(ZZ['t']) # needs sage.rings.number_field
|
|
3078
|
+
Univariate Quotient Polynomial Ring in a
|
|
3079
|
+
over Univariate Polynomial Ring in t over Integer Ring
|
|
3080
|
+
with modulus a^3 + a^2 + 1
|
|
3081
|
+
|
|
3082
|
+
Note that, even if a field is algebraically closed, the algebraic
|
|
3083
|
+
extension will be constructed as the quotient of a univariate
|
|
3084
|
+
polynomial ring::
|
|
3085
|
+
|
|
3086
|
+
sage: F(CC) # needs sage.rings.number_field
|
|
3087
|
+
Univariate Quotient Polynomial Ring in a
|
|
3088
|
+
over Complex Field with 53 bits of precision
|
|
3089
|
+
with modulus a^3 + a^2 + 1.00000000000000
|
|
3090
|
+
sage: F(RR) # needs sage.rings.number_field
|
|
3091
|
+
Univariate Quotient Polynomial Ring in a
|
|
3092
|
+
over Real Field with 53 bits of precision
|
|
3093
|
+
with modulus a^3 + a^2 + 1.00000000000000
|
|
3094
|
+
|
|
3095
|
+
Note that the construction functor of a number field applied to
|
|
3096
|
+
the integers returns an order (not necessarily maximal) of that
|
|
3097
|
+
field, similar to the behaviour of ``ZZ.extension(...)``::
|
|
3098
|
+
|
|
3099
|
+
sage: F(ZZ) # needs sage.rings.number_field
|
|
3100
|
+
Order generated by a in Number Field in a with defining polynomial x^3 + x^2 + 1
|
|
3101
|
+
|
|
3102
|
+
This also holds for non-absolute number fields::
|
|
3103
|
+
|
|
3104
|
+
sage: # needs sage.rings.number_field
|
|
3105
|
+
sage: x = polygen(QQ, 'x')
|
|
3106
|
+
sage: K.<a,b> = NumberField([x^3 + x^2 + 1, x^2 + x + 1])
|
|
3107
|
+
sage: F = K.construction()[0]
|
|
3108
|
+
sage: O = F(ZZ); O
|
|
3109
|
+
Relative Order
|
|
3110
|
+
generated by [(b - 2)*a^2 + (3*b - 1)*a + 3*b + 4, a - b]
|
|
3111
|
+
in Number Field in a with defining polynomial x^3 + x^2 + 1
|
|
3112
|
+
over its base field
|
|
3113
|
+
sage: O.ambient() is K
|
|
3114
|
+
True
|
|
3115
|
+
|
|
3116
|
+
Special cases are made for cyclotomic fields and residue fields::
|
|
3117
|
+
|
|
3118
|
+
sage: # needs sage.rings.number_field
|
|
3119
|
+
sage: C = CyclotomicField(8)
|
|
3120
|
+
sage: F, R = C.construction()
|
|
3121
|
+
sage: F
|
|
3122
|
+
AlgebraicExtensionFunctor
|
|
3123
|
+
sage: R
|
|
3124
|
+
Rational Field
|
|
3125
|
+
sage: F(R)
|
|
3126
|
+
Cyclotomic Field of order 8 and degree 4
|
|
3127
|
+
sage: F(ZZ)
|
|
3128
|
+
Maximal Order generated by zeta8 in Cyclotomic Field of order 8 and degree 4
|
|
3129
|
+
|
|
3130
|
+
::
|
|
3131
|
+
|
|
3132
|
+
sage: # needs sage.rings.number_field
|
|
3133
|
+
sage: K.<z> = CyclotomicField(7)
|
|
3134
|
+
sage: P = K.factor(17)[0][0]
|
|
3135
|
+
sage: k = K.residue_field(P)
|
|
3136
|
+
sage: F, R = k.construction()
|
|
3137
|
+
sage: F
|
|
3138
|
+
AlgebraicExtensionFunctor
|
|
3139
|
+
sage: R
|
|
3140
|
+
Cyclotomic Field of order 7 and degree 6
|
|
3141
|
+
sage: F(R) is k
|
|
3142
|
+
True
|
|
3143
|
+
sage: F(ZZ)
|
|
3144
|
+
Residue field of Integers modulo 17
|
|
3145
|
+
sage: F(CyclotomicField(49))
|
|
3146
|
+
Residue field in zbar of Fractional ideal (17)
|
|
3147
|
+
"""
|
|
3148
|
+
rank = 3
|
|
3149
|
+
|
|
3150
|
+
def __init__(self, polys, names, embeddings=None, structures=None,
|
|
3151
|
+
cyclotomic=None, precs=None, implementations=None,
|
|
3152
|
+
*, residue=None, latex_names=None, **kwds):
|
|
3153
|
+
"""
|
|
3154
|
+
INPUT:
|
|
3155
|
+
|
|
3156
|
+
- ``polys`` -- list of polynomials (or of integers, for
|
|
3157
|
+
finite fields and unramified local extensions)
|
|
3158
|
+
|
|
3159
|
+
- ``names`` -- list of strings of the same length as the
|
|
3160
|
+
list ``polys``
|
|
3161
|
+
|
|
3162
|
+
- ``embeddings`` -- (optional) list of approximate complex
|
|
3163
|
+
values, determining an embedding of the generators into the
|
|
3164
|
+
complex field, or ``None`` for each generator whose
|
|
3165
|
+
embedding is not prescribed.
|
|
3166
|
+
|
|
3167
|
+
- ``structures`` -- (optional) list of structural morphisms of
|
|
3168
|
+
number fields; see
|
|
3169
|
+
:class:`~sage.rings.number_field.structure.NumberFieldStructure`.
|
|
3170
|
+
|
|
3171
|
+
- ``cyclotomic`` -- (optional) integer. If it is provided,
|
|
3172
|
+
application of the functor to the rational field yields a
|
|
3173
|
+
cyclotomic field, rather than just a number field.
|
|
3174
|
+
|
|
3175
|
+
- ``precs`` -- (optional) list of integers. If it is provided,
|
|
3176
|
+
it is used to determine the precision of `p`-adic extensions.
|
|
3177
|
+
|
|
3178
|
+
- ``implementations`` -- (optional) list of strings.
|
|
3179
|
+
If it is provided, it is used to determine an implementation in the
|
|
3180
|
+
`p`-adic case.
|
|
3181
|
+
|
|
3182
|
+
- ``residue`` -- (optional) prime ideal of an order in a number
|
|
3183
|
+
field, determining a residue field. If it is provided,
|
|
3184
|
+
application of the functor to a number field yields the
|
|
3185
|
+
residue field with respect to the given prime ideal
|
|
3186
|
+
(coerced into the number field).
|
|
3187
|
+
|
|
3188
|
+
- ``latex_names`` -- (optional) list of strings of the same length
|
|
3189
|
+
as the list ``polys``
|
|
3190
|
+
|
|
3191
|
+
- ``**kwds`` -- further keywords; when the functor is applied
|
|
3192
|
+
to a ring `R`, these are passed to the ``extension()``
|
|
3193
|
+
method of `R`.
|
|
3194
|
+
|
|
3195
|
+
REMARK:
|
|
3196
|
+
|
|
3197
|
+
Currently, an embedding can only be provided for the last
|
|
3198
|
+
generator, and only when the construction functor is applied
|
|
3199
|
+
to the rational field. There will be no error when constructing
|
|
3200
|
+
the functor, but when applying it.
|
|
3201
|
+
|
|
3202
|
+
TESTS::
|
|
3203
|
+
|
|
3204
|
+
sage: from sage.categories.pushout import AlgebraicExtensionFunctor
|
|
3205
|
+
sage: P.<x> = ZZ[]
|
|
3206
|
+
sage: F1 = AlgebraicExtensionFunctor([x^3 - x^2 + 1], ['a'], [None])
|
|
3207
|
+
sage: F2 = AlgebraicExtensionFunctor([x^3 - x^2 + 1], ['a'], [0])
|
|
3208
|
+
sage: F1 == F2
|
|
3209
|
+
False
|
|
3210
|
+
sage: F1(QQ) # needs sage.rings.number_field
|
|
3211
|
+
Number Field in a with defining polynomial x^3 - x^2 + 1
|
|
3212
|
+
sage: F1(QQ).coerce_embedding() # needs sage.rings.number_field
|
|
3213
|
+
sage: phi = F2(QQ).coerce_embedding().__copy__(); phi # needs sage.rings.number_field
|
|
3214
|
+
Generic morphism:
|
|
3215
|
+
From: Number Field in a with defining polynomial x^3 - x^2 + 1
|
|
3216
|
+
with a = -0.7548776662466928?
|
|
3217
|
+
To: Real Lazy Field
|
|
3218
|
+
Defn: a -> -0.7548776662466928?
|
|
3219
|
+
sage: F1(QQ) == F2(QQ) # needs sage.rings.number_field
|
|
3220
|
+
False
|
|
3221
|
+
sage: F1(GF(5)) # needs sage.libs.pari
|
|
3222
|
+
Univariate Quotient Polynomial Ring in a over Finite Field of size 5
|
|
3223
|
+
with modulus a^3 + 4*a^2 + 1
|
|
3224
|
+
sage: F2(GF(5)) # needs sage.libs.pari
|
|
3225
|
+
Traceback (most recent call last):
|
|
3226
|
+
...
|
|
3227
|
+
NotImplementedError: ring extension with prescribed embedding is not implemented
|
|
3228
|
+
|
|
3229
|
+
When applying a number field constructor to the ring of
|
|
3230
|
+
integers, an order (not necessarily maximal) of that field is
|
|
3231
|
+
returned, similar to the behaviour of ``ZZ.extension``::
|
|
3232
|
+
|
|
3233
|
+
sage: F1(ZZ) # needs sage.rings.number_field
|
|
3234
|
+
Order generated by a in Number Field in a with defining polynomial x^3 - x^2 + 1
|
|
3235
|
+
|
|
3236
|
+
The cyclotomic fields form a special case of number fields
|
|
3237
|
+
with prescribed embeddings::
|
|
3238
|
+
|
|
3239
|
+
sage: # needs sage.rings.number_field
|
|
3240
|
+
sage: C = CyclotomicField(8)
|
|
3241
|
+
sage: F, R = C.construction()
|
|
3242
|
+
sage: F
|
|
3243
|
+
AlgebraicExtensionFunctor
|
|
3244
|
+
sage: R
|
|
3245
|
+
Rational Field
|
|
3246
|
+
sage: F(R)
|
|
3247
|
+
Cyclotomic Field of order 8 and degree 4
|
|
3248
|
+
sage: F(ZZ)
|
|
3249
|
+
Maximal Order generated by zeta8 in Cyclotomic Field of order 8 and degree 4
|
|
3250
|
+
|
|
3251
|
+
The data stored in this construction includes structural
|
|
3252
|
+
morphisms of number fields (see :issue:`20826`)::
|
|
3253
|
+
|
|
3254
|
+
sage: # needs sage.rings.number_field
|
|
3255
|
+
sage: R.<x> = ZZ[]
|
|
3256
|
+
sage: K.<a> = NumberField(x^2 - 3)
|
|
3257
|
+
sage: L0.<b> = K.change_names()
|
|
3258
|
+
sage: L0.structure()
|
|
3259
|
+
(Isomorphism given by variable name change map:
|
|
3260
|
+
From: Number Field in b with defining polynomial x^2 - 3
|
|
3261
|
+
To: Number Field in a with defining polynomial x^2 - 3,
|
|
3262
|
+
Isomorphism given by variable name change map:
|
|
3263
|
+
From: Number Field in a with defining polynomial x^2 - 3
|
|
3264
|
+
To: Number Field in b with defining polynomial x^2 - 3)
|
|
3265
|
+
sage: L1 = (b*x).parent().base_ring()
|
|
3266
|
+
sage: L1 is L0
|
|
3267
|
+
True
|
|
3268
|
+
"""
|
|
3269
|
+
Functor.__init__(self, Rings(), Rings())
|
|
3270
|
+
if not (isinstance(polys, (list, tuple)) and isinstance(names, (list, tuple))):
|
|
3271
|
+
raise ValueError("Arguments must be lists or tuples")
|
|
3272
|
+
n = len(polys)
|
|
3273
|
+
if embeddings is None:
|
|
3274
|
+
embeddings = [None] * n
|
|
3275
|
+
if structures is None:
|
|
3276
|
+
structures = [None] * n
|
|
3277
|
+
if precs is None:
|
|
3278
|
+
precs = [None] * n
|
|
3279
|
+
if implementations is None:
|
|
3280
|
+
implementations = [None] * n
|
|
3281
|
+
if latex_names is None:
|
|
3282
|
+
latex_names = [None] * n
|
|
3283
|
+
if not (len(names) == len(embeddings) == len(structures) == len(latex_names) == n):
|
|
3284
|
+
raise ValueError("All arguments must be of the same length")
|
|
3285
|
+
self.polys = list(polys)
|
|
3286
|
+
self.names = list(names)
|
|
3287
|
+
self.embeddings = list(embeddings)
|
|
3288
|
+
self.structures = list(structures)
|
|
3289
|
+
self.cyclotomic = int(cyclotomic) if cyclotomic is not None else None
|
|
3290
|
+
self.precs = list(precs)
|
|
3291
|
+
self.implementations = list(implementations)
|
|
3292
|
+
self.residue = residue
|
|
3293
|
+
# Normalize latex_names: Use None when latex_name does not override the default.
|
|
3294
|
+
latex_names = list(latex_names)
|
|
3295
|
+
for i, name in enumerate(self.names):
|
|
3296
|
+
if latex_names[i] is not None:
|
|
3297
|
+
from sage.misc.latex import latex_variable_name
|
|
3298
|
+
if latex_names[i] == latex_variable_name(name):
|
|
3299
|
+
latex_names[i] = None
|
|
3300
|
+
self.latex_names = latex_names
|
|
3301
|
+
self.kwds = kwds
|
|
3302
|
+
|
|
3303
|
+
def _apply_functor(self, R):
|
|
3304
|
+
"""
|
|
3305
|
+
Apply the functor to an object of ``self``'s domain.
|
|
3306
|
+
|
|
3307
|
+
TESTS::
|
|
3308
|
+
|
|
3309
|
+
sage: # needs sage.rings.number_field
|
|
3310
|
+
sage: x = polygen(QQ, 'x')
|
|
3311
|
+
sage: K.<a> = NumberField(x^3 + x^2 + 1)
|
|
3312
|
+
sage: F = K.construction()[0]
|
|
3313
|
+
sage: F(ZZ) # indirect doctest
|
|
3314
|
+
Order generated by a in Number Field in a with defining polynomial x^3 + x^2 + 1
|
|
3315
|
+
sage: F(ZZ['t']) # indirect doctest
|
|
3316
|
+
Univariate Quotient Polynomial Ring in a over
|
|
3317
|
+
Univariate Polynomial Ring in t over Integer Ring with modulus a^3 + a^2 + 1
|
|
3318
|
+
sage: F(RR) # indirect doctest
|
|
3319
|
+
Univariate Quotient Polynomial Ring in a over
|
|
3320
|
+
Real Field with 53 bits of precision with modulus a^3 + a^2 + 1.00000000000000
|
|
3321
|
+
|
|
3322
|
+
Check that :issue:`13538` is fixed::
|
|
3323
|
+
|
|
3324
|
+
sage: # needs sage.rings.padics
|
|
3325
|
+
sage: from sage.categories.pushout import AlgebraicExtensionFunctor
|
|
3326
|
+
sage: K = Qp(3, 3)
|
|
3327
|
+
sage: R.<a> = K[]
|
|
3328
|
+
sage: AEF = AlgebraicExtensionFunctor([a^2 - 3], ['a'], [None])
|
|
3329
|
+
sage: AEF(K)
|
|
3330
|
+
3-adic Eisenstein Extension Field in a defined by a^2 - 3
|
|
3331
|
+
"""
|
|
3332
|
+
from sage.rings.integer_ring import ZZ
|
|
3333
|
+
from sage.rings.rational_field import QQ
|
|
3334
|
+
if self.cyclotomic:
|
|
3335
|
+
from sage.rings.number_field.number_field import CyclotomicField
|
|
3336
|
+
if R == QQ:
|
|
3337
|
+
return CyclotomicField(self.cyclotomic)
|
|
3338
|
+
if R == ZZ:
|
|
3339
|
+
return CyclotomicField(self.cyclotomic).maximal_order()
|
|
3340
|
+
elif self.residue is not None:
|
|
3341
|
+
return R.residue_field(R*self.residue, names=tuple(self.names))
|
|
3342
|
+
if len(self.polys) == 1:
|
|
3343
|
+
return R.extension(self.polys[0], names=self.names[0], embedding=self.embeddings[0],
|
|
3344
|
+
structure=self.structures[0], prec=self.precs[0],
|
|
3345
|
+
implementation=self.implementations[0],
|
|
3346
|
+
latex_names=self.latex_names[0], **self.kwds)
|
|
3347
|
+
return R.extension(self.polys, names=self.names, embedding=self.embeddings,
|
|
3348
|
+
structure=self.structures, prec=self.precs,
|
|
3349
|
+
implementation=self.implementations,
|
|
3350
|
+
latex_names=self.latex_names, **self.kwds)
|
|
3351
|
+
|
|
3352
|
+
def __eq__(self, other):
|
|
3353
|
+
"""
|
|
3354
|
+
Check whether ``self`` is equal to ``other``.
|
|
3355
|
+
|
|
3356
|
+
TESTS::
|
|
3357
|
+
|
|
3358
|
+
sage: # needs sage.rings.number_field
|
|
3359
|
+
sage: x = polygen(QQ, 'x')
|
|
3360
|
+
sage: K.<a> = NumberField(x^3 + x^2 + 1)
|
|
3361
|
+
sage: F = K.construction()[0]
|
|
3362
|
+
sage: F == loads(dumps(F))
|
|
3363
|
+
True
|
|
3364
|
+
|
|
3365
|
+
sage: K2.<a> = NumberField(x^3 + x^2 + 1, latex_names='a') # needs sage.rings.number_field
|
|
3366
|
+
sage: F2 = K2.construction()[0] # needs sage.rings.number_field
|
|
3367
|
+
sage: F2 == F # needs sage.rings.number_field
|
|
3368
|
+
True
|
|
3369
|
+
|
|
3370
|
+
sage: K3.<a> = NumberField(x^3 + x^2 + 1, latex_names='alpha') # needs sage.rings.number_field
|
|
3371
|
+
sage: F3 = K3.construction()[0] # needs sage.rings.number_field
|
|
3372
|
+
sage: F3 == F # needs sage.rings.number_field
|
|
3373
|
+
False
|
|
3374
|
+
"""
|
|
3375
|
+
if not isinstance(other, AlgebraicExtensionFunctor):
|
|
3376
|
+
return False
|
|
3377
|
+
|
|
3378
|
+
return (self.polys == other.polys and
|
|
3379
|
+
self.embeddings == other.embeddings and
|
|
3380
|
+
self.structures == other.structures and
|
|
3381
|
+
self.precs == other.precs and
|
|
3382
|
+
self.latex_names == other.latex_names)
|
|
3383
|
+
|
|
3384
|
+
def __ne__(self, other):
|
|
3385
|
+
"""
|
|
3386
|
+
Check whether ``self`` is not equal to ``other``.
|
|
3387
|
+
|
|
3388
|
+
EXAMPLES::
|
|
3389
|
+
|
|
3390
|
+
sage: x = polygen(QQ, 'x')
|
|
3391
|
+
sage: K.<a> = NumberField(x^3 + x^2 + 1) # needs sage.rings.number_field
|
|
3392
|
+
sage: F = K.construction()[0] # needs sage.rings.number_field
|
|
3393
|
+
sage: F != loads(dumps(F)) # needs sage.rings.number_field
|
|
3394
|
+
False
|
|
3395
|
+
"""
|
|
3396
|
+
return not (self == other)
|
|
3397
|
+
|
|
3398
|
+
__hash__ = ConstructionFunctor.__hash__
|
|
3399
|
+
|
|
3400
|
+
def merge(self, other):
|
|
3401
|
+
"""
|
|
3402
|
+
Merging with another :class:`AlgebraicExtensionFunctor`.
|
|
3403
|
+
|
|
3404
|
+
INPUT:
|
|
3405
|
+
|
|
3406
|
+
- ``other`` -- Construction Functor
|
|
3407
|
+
|
|
3408
|
+
OUTPUT:
|
|
3409
|
+
|
|
3410
|
+
- If ``self==other``, ``self`` is returned.
|
|
3411
|
+
- If ``self`` and ``other`` are simple extensions
|
|
3412
|
+
and both provide an embedding, then it is tested
|
|
3413
|
+
whether one of the number fields provided by
|
|
3414
|
+
the functors coerces into the other; the functor
|
|
3415
|
+
associated with the target of the coercion is
|
|
3416
|
+
returned. Otherwise, the construction functor
|
|
3417
|
+
associated with the pushout of the codomains
|
|
3418
|
+
of the two embeddings is returned, provided that
|
|
3419
|
+
it is a number field.
|
|
3420
|
+
- If these two extensions are defined by Conway polynomials
|
|
3421
|
+
over finite fields, merges them into a single extension of
|
|
3422
|
+
degree the lcm of the two degrees.
|
|
3423
|
+
- Otherwise, ``None`` is returned.
|
|
3424
|
+
|
|
3425
|
+
REMARK:
|
|
3426
|
+
|
|
3427
|
+
Algebraic extension with embeddings currently only
|
|
3428
|
+
works when applied to the rational field. This is
|
|
3429
|
+
why we use the admittedly strange rule above for
|
|
3430
|
+
merging.
|
|
3431
|
+
|
|
3432
|
+
EXAMPLES:
|
|
3433
|
+
|
|
3434
|
+
The following demonstrate coercions for finite fields using Conway or
|
|
3435
|
+
pseudo-Conway polynomials::
|
|
3436
|
+
|
|
3437
|
+
sage: k = GF(3^2, prefix='z'); a = k.gen() # needs sage.rings.finite_rings
|
|
3438
|
+
sage: l = GF(3^3, prefix='z'); b = l.gen() # needs sage.rings.finite_rings
|
|
3439
|
+
sage: a + b # indirect doctest # needs sage.rings.finite_rings
|
|
3440
|
+
z6^5 + 2*z6^4 + 2*z6^3 + z6^2 + 2*z6 + 1
|
|
3441
|
+
|
|
3442
|
+
Note that embeddings are compatible in lattices of such finite fields::
|
|
3443
|
+
|
|
3444
|
+
sage: # needs sage.rings.finite_rings
|
|
3445
|
+
sage: m = GF(3^5, prefix='z'); c = m.gen()
|
|
3446
|
+
sage: (a + b) + c == a + (b + c) # indirect doctest
|
|
3447
|
+
True
|
|
3448
|
+
sage: from sage.categories.pushout import pushout
|
|
3449
|
+
sage: n = pushout(k, l)
|
|
3450
|
+
sage: o = pushout(l, m)
|
|
3451
|
+
sage: q = pushout(n, o)
|
|
3452
|
+
sage: q(o(b)) == q(n(b)) # indirect doctest
|
|
3453
|
+
True
|
|
3454
|
+
|
|
3455
|
+
Coercion is also available for number fields::
|
|
3456
|
+
|
|
3457
|
+
sage: # needs sage.rings.number_field
|
|
3458
|
+
sage: P.<x> = QQ[]
|
|
3459
|
+
sage: L.<b> = NumberField(x^8 - x^4 + 1, embedding=CDF.0)
|
|
3460
|
+
sage: M1.<c1> = NumberField(x^2 + x + 1, embedding=b^4 - 1)
|
|
3461
|
+
sage: M2.<c2> = NumberField(x^2 + 1, embedding=-b^6)
|
|
3462
|
+
sage: M1.coerce_map_from(M2)
|
|
3463
|
+
sage: M2.coerce_map_from(M1)
|
|
3464
|
+
sage: c1 + c2; parent(c1 + c2) #indirect doctest
|
|
3465
|
+
-b^6 + b^4 - 1
|
|
3466
|
+
Number Field in b with defining polynomial x^8 - x^4 + 1
|
|
3467
|
+
with b = -0.2588190451025208? + 0.9659258262890683?*I
|
|
3468
|
+
sage: pushout(M1['x'], M2['x']) # needs sage.rings.finite_rings
|
|
3469
|
+
Univariate Polynomial Ring in x
|
|
3470
|
+
over Number Field in b with defining polynomial x^8 - x^4 + 1
|
|
3471
|
+
with b = -0.2588190451025208? + 0.9659258262890683?*I
|
|
3472
|
+
|
|
3473
|
+
In the previous example, the number field ``L`` becomes the pushout
|
|
3474
|
+
of ``M1`` and ``M2`` since both are provided with an embedding into
|
|
3475
|
+
``L``, *and* since ``L`` is a number field. If two number fields
|
|
3476
|
+
are embedded into a field that is not a numberfield, no merging
|
|
3477
|
+
occurs::
|
|
3478
|
+
|
|
3479
|
+
sage: # needs sage.rings.complex_double sage.rings.number_field
|
|
3480
|
+
sage: cbrt2 = CDF(2)^(1/3)
|
|
3481
|
+
sage: zeta3 = CDF.zeta(3)
|
|
3482
|
+
sage: K.<a> = NumberField(x^3 - 2, embedding=cbrt2 * zeta3)
|
|
3483
|
+
sage: L.<b> = NumberField(x^6 - 2, embedding=1.1)
|
|
3484
|
+
sage: L.coerce_map_from(K)
|
|
3485
|
+
sage: K.coerce_map_from(L)
|
|
3486
|
+
sage: pushout(K, L) # needs sage.rings.finite_rings
|
|
3487
|
+
Traceback (most recent call last):
|
|
3488
|
+
...
|
|
3489
|
+
CoercionException: ('Ambiguous Base Extension', Number Field in a with
|
|
3490
|
+
defining polynomial x^3 - 2 with a = -0.6299605249474365? + 1.091123635971722?*I,
|
|
3491
|
+
Number Field in b with defining polynomial x^6 - 2 with b = 1.122462048309373?)
|
|
3492
|
+
"""
|
|
3493
|
+
if isinstance(other, AlgebraicClosureFunctor):
|
|
3494
|
+
return other
|
|
3495
|
+
elif not isinstance(other, AlgebraicExtensionFunctor):
|
|
3496
|
+
return None
|
|
3497
|
+
if self == other:
|
|
3498
|
+
return self
|
|
3499
|
+
# This method is supposed to be used in pushout(),
|
|
3500
|
+
# *after* expanding the functors. Hence, we can
|
|
3501
|
+
# assume that both functors have a single variable.
|
|
3502
|
+
# But for being on the safe side...:
|
|
3503
|
+
if not (len(self.names) == 1 == len(other.names)):
|
|
3504
|
+
return None
|
|
3505
|
+
# We don't accept a forgetful coercion, since, together
|
|
3506
|
+
# with bidirectional coercions between two embedded
|
|
3507
|
+
# number fields, it would yield to contradictions in
|
|
3508
|
+
# the coercion system.
|
|
3509
|
+
# if self.polys==other.polys and self.names==other.names:
|
|
3510
|
+
# # We have a forgetful functor:
|
|
3511
|
+
# if self.embeddings==[None]:
|
|
3512
|
+
# return self
|
|
3513
|
+
# if other.embeddings==[None]:
|
|
3514
|
+
# return other
|
|
3515
|
+
# ... or we may use the given embeddings:
|
|
3516
|
+
if self.embeddings != [None] and other.embeddings != [None]:
|
|
3517
|
+
from sage.rings.rational_field import QQ
|
|
3518
|
+
KS = self(QQ)
|
|
3519
|
+
KO = other(QQ)
|
|
3520
|
+
if KS.has_coerce_map_from(KO):
|
|
3521
|
+
return self
|
|
3522
|
+
if KO.has_coerce_map_from(KS):
|
|
3523
|
+
return other
|
|
3524
|
+
# nothing else helps, hence, we move to the pushout of the codomains of the embeddings
|
|
3525
|
+
try:
|
|
3526
|
+
P = pushout(self.embeddings[0].parent(), other.embeddings[0].parent())
|
|
3527
|
+
from sage.rings.number_field.number_field_base import NumberField
|
|
3528
|
+
if isinstance(P, NumberField):
|
|
3529
|
+
return P.construction()[0]
|
|
3530
|
+
except CoercionException:
|
|
3531
|
+
return None
|
|
3532
|
+
# Finite fields and unramified local extensions may use
|
|
3533
|
+
# integers to encode degrees of extensions.
|
|
3534
|
+
from sage.rings.integer import Integer
|
|
3535
|
+
kwds_self = dict(self.kwds.items())
|
|
3536
|
+
if 'impl' in kwds_self:
|
|
3537
|
+
del kwds_self['impl']
|
|
3538
|
+
kwds_other = dict(other.kwds.items())
|
|
3539
|
+
if 'impl' in kwds_other:
|
|
3540
|
+
del kwds_other['impl']
|
|
3541
|
+
if (isinstance(self.polys[0], Integer)
|
|
3542
|
+
and isinstance(other.polys[0], Integer)
|
|
3543
|
+
and self.embeddings == other.embeddings == [None]
|
|
3544
|
+
and self.structures == other.structures == [None]
|
|
3545
|
+
and kwds_self == kwds_other):
|
|
3546
|
+
return AlgebraicExtensionFunctor([self.polys[0].lcm(other.polys[0])], [None], **kwds_self)
|
|
3547
|
+
|
|
3548
|
+
def __mul__(self, other):
|
|
3549
|
+
"""
|
|
3550
|
+
Compose construction functors to a composite construction functor, unless one of them is the identity.
|
|
3551
|
+
|
|
3552
|
+
.. NOTE::
|
|
3553
|
+
|
|
3554
|
+
The product is in functorial notation, i.e., when applying the
|
|
3555
|
+
product to an object then the second factor is applied first.
|
|
3556
|
+
|
|
3557
|
+
TESTS::
|
|
3558
|
+
|
|
3559
|
+
sage: # needs sage.rings.number_field
|
|
3560
|
+
sage: P.<x> = QQ[]
|
|
3561
|
+
sage: K.<a> = NumberField(x^3 - 5, embedding=0)
|
|
3562
|
+
sage: L.<b> = K.extension(x^2 + a)
|
|
3563
|
+
sage: F, R = L.construction()
|
|
3564
|
+
sage: prod(F.expand())(R) == L #indirect doctest
|
|
3565
|
+
True
|
|
3566
|
+
"""
|
|
3567
|
+
if isinstance(other, IdentityConstructionFunctor):
|
|
3568
|
+
return self
|
|
3569
|
+
if isinstance(other, AlgebraicExtensionFunctor):
|
|
3570
|
+
if set(self.names).intersection(other.names):
|
|
3571
|
+
raise CoercionException("Overlapping names (%s,%s)" % (self.names, other.names))
|
|
3572
|
+
return AlgebraicExtensionFunctor(self.polys + other.polys, self.names + other.names,
|
|
3573
|
+
self.embeddings + other.embeddings,
|
|
3574
|
+
self.structures + other.structures,
|
|
3575
|
+
precs=self.precs + other.precs,
|
|
3576
|
+
implementations=self.implementations + other.implementations,
|
|
3577
|
+
latex_names=self.latex_names + other.latex_names,
|
|
3578
|
+
**self.kwds)
|
|
3579
|
+
elif (isinstance(other, CompositeConstructionFunctor)
|
|
3580
|
+
and isinstance(other.all[-1], AlgebraicExtensionFunctor)):
|
|
3581
|
+
return CompositeConstructionFunctor(other.all[:-1], self * other.all[-1])
|
|
3582
|
+
else:
|
|
3583
|
+
return CompositeConstructionFunctor(other, self)
|
|
3584
|
+
|
|
3585
|
+
def expand(self):
|
|
3586
|
+
"""
|
|
3587
|
+
Decompose the functor `F` into sub-functors, whose product returns `F`.
|
|
3588
|
+
|
|
3589
|
+
EXAMPLES::
|
|
3590
|
+
|
|
3591
|
+
sage: # needs sage.rings.number_field
|
|
3592
|
+
sage: P.<x> = QQ[]
|
|
3593
|
+
sage: K.<a> = NumberField(x^3 - 5, embedding=0)
|
|
3594
|
+
sage: L.<b> = K.extension(x^2 + a)
|
|
3595
|
+
sage: F, R = L.construction()
|
|
3596
|
+
sage: prod(F.expand())(R) == L
|
|
3597
|
+
True
|
|
3598
|
+
sage: K = NumberField([x^2 - 2, x^2 - 3],'a')
|
|
3599
|
+
sage: F, R = K.construction()
|
|
3600
|
+
sage: F
|
|
3601
|
+
AlgebraicExtensionFunctor
|
|
3602
|
+
sage: L = F.expand(); L
|
|
3603
|
+
[AlgebraicExtensionFunctor, AlgebraicExtensionFunctor]
|
|
3604
|
+
sage: L[-1](QQ)
|
|
3605
|
+
Number Field in a1 with defining polynomial x^2 - 3
|
|
3606
|
+
"""
|
|
3607
|
+
n = len(self.polys)
|
|
3608
|
+
if n == 1:
|
|
3609
|
+
return [self]
|
|
3610
|
+
return [AlgebraicExtensionFunctor([self.polys[i]], [self.names[i]], [self.embeddings[i]],
|
|
3611
|
+
[self.structures[i]], precs=[self.precs[i]],
|
|
3612
|
+
implementations=[self.implementations[i]],
|
|
3613
|
+
latex_names=[self.latex_names[i]], **self.kwds)
|
|
3614
|
+
for i in range(n)]
|
|
3615
|
+
|
|
3616
|
+
|
|
3617
|
+
class AlgebraicClosureFunctor(ConstructionFunctor):
|
|
3618
|
+
"""
|
|
3619
|
+
Algebraic Closure.
|
|
3620
|
+
|
|
3621
|
+
EXAMPLES::
|
|
3622
|
+
|
|
3623
|
+
sage: # needs sage.rings.complex_double sage.rings.number_field
|
|
3624
|
+
sage: F = CDF.construction()[0]
|
|
3625
|
+
sage: F(QQ)
|
|
3626
|
+
Algebraic Field
|
|
3627
|
+
sage: F(RR) # needs sage.rings.real_mpfr
|
|
3628
|
+
Complex Field with 53 bits of precision
|
|
3629
|
+
sage: F(F(QQ)) is F(QQ)
|
|
3630
|
+
True
|
|
3631
|
+
"""
|
|
3632
|
+
rank = 3
|
|
3633
|
+
|
|
3634
|
+
def __init__(self):
|
|
3635
|
+
"""
|
|
3636
|
+
TESTS::
|
|
3637
|
+
|
|
3638
|
+
sage: from sage.categories.pushout import AlgebraicClosureFunctor
|
|
3639
|
+
sage: F = AlgebraicClosureFunctor()
|
|
3640
|
+
sage: F(QQ) # needs sage.rings.number_field
|
|
3641
|
+
Algebraic Field
|
|
3642
|
+
sage: F(RR) # needs sage.rings.real_mpfr
|
|
3643
|
+
Complex Field with 53 bits of precision
|
|
3644
|
+
sage: F == loads(dumps(F))
|
|
3645
|
+
True
|
|
3646
|
+
"""
|
|
3647
|
+
Functor.__init__(self, Rings(), Rings())
|
|
3648
|
+
|
|
3649
|
+
def _apply_functor(self, R):
|
|
3650
|
+
"""
|
|
3651
|
+
Apply the functor to an object of ``self``'s domain.
|
|
3652
|
+
|
|
3653
|
+
TESTS::
|
|
3654
|
+
|
|
3655
|
+
sage: F = CDF.construction()[0] # needs sage.rings.complex_double
|
|
3656
|
+
sage: F(QQ) # indirect doctest # needs sage.rings.complex_double sage.rings.number_field
|
|
3657
|
+
Algebraic Field
|
|
3658
|
+
"""
|
|
3659
|
+
try:
|
|
3660
|
+
c = R.construction()
|
|
3661
|
+
if c is not None and c[0] == self:
|
|
3662
|
+
return R
|
|
3663
|
+
except AttributeError:
|
|
3664
|
+
pass
|
|
3665
|
+
return R.algebraic_closure()
|
|
3666
|
+
|
|
3667
|
+
def merge(self, other):
|
|
3668
|
+
"""
|
|
3669
|
+
Mathematically, Algebraic Closure subsumes Algebraic Extension.
|
|
3670
|
+
However, it seems that people do want to work with algebraic
|
|
3671
|
+
extensions of ``RR``. Therefore, we do not merge with algebraic extension.
|
|
3672
|
+
|
|
3673
|
+
TESTS::
|
|
3674
|
+
|
|
3675
|
+
sage: x = polygen(QQ, 'x')
|
|
3676
|
+
sage: K.<a> = NumberField(x^3 + x^2 + 1) # needs sage.rings.number_field
|
|
3677
|
+
sage: CDF.construction()[0].merge(K.construction()[0]) is None # needs sage.rings.number_field
|
|
3678
|
+
True
|
|
3679
|
+
sage: CDF.construction()[0].merge(CDF.construction()[0]) # needs sage.rings.complex_double
|
|
3680
|
+
AlgebraicClosureFunctor
|
|
3681
|
+
"""
|
|
3682
|
+
if self == other:
|
|
3683
|
+
return self
|
|
3684
|
+
return None
|
|
3685
|
+
# Mathematically, Algebraic Closure subsumes Algebraic Extension.
|
|
3686
|
+
# However, it seems that people do want to work with
|
|
3687
|
+
# algebraic extensions of RR (namely RR/poly*RR). So, we don't do:
|
|
3688
|
+
# if isinstance(other,AlgebraicExtensionFunctor):
|
|
3689
|
+
# return self
|
|
3690
|
+
|
|
3691
|
+
|
|
3692
|
+
class PermutationGroupFunctor(ConstructionFunctor):
|
|
3693
|
+
|
|
3694
|
+
rank = 10
|
|
3695
|
+
|
|
3696
|
+
def __init__(self, gens, domain):
|
|
3697
|
+
"""
|
|
3698
|
+
EXAMPLES::
|
|
3699
|
+
|
|
3700
|
+
sage: from sage.categories.pushout import PermutationGroupFunctor
|
|
3701
|
+
sage: PF = PermutationGroupFunctor([PermutationGroupElement([(1,2)])], # needs sage.groups
|
|
3702
|
+
....: [1,2]); PF
|
|
3703
|
+
PermutationGroupFunctor[(1,2)]
|
|
3704
|
+
"""
|
|
3705
|
+
Functor.__init__(self, Groups(), Groups())
|
|
3706
|
+
self._gens = tuple(gens)
|
|
3707
|
+
self._domain = domain
|
|
3708
|
+
|
|
3709
|
+
def _repr_(self):
|
|
3710
|
+
"""
|
|
3711
|
+
EXAMPLES::
|
|
3712
|
+
|
|
3713
|
+
sage: P1 = PermutationGroup([[(1,2)]]) # needs sage.groups
|
|
3714
|
+
sage: PF, P = P1.construction() # needs sage.groups
|
|
3715
|
+
sage: PF # needs sage.groups
|
|
3716
|
+
PermutationGroupFunctor[(1,2)]
|
|
3717
|
+
"""
|
|
3718
|
+
return "PermutationGroupFunctor%s" % list(self.gens())
|
|
3719
|
+
|
|
3720
|
+
def __call__(self, R):
|
|
3721
|
+
"""
|
|
3722
|
+
EXAMPLES::
|
|
3723
|
+
|
|
3724
|
+
sage: P1 = PermutationGroup([[(1,2)]]) # needs sage.groups
|
|
3725
|
+
sage: PF, P = P1.construction() # needs sage.groups
|
|
3726
|
+
sage: PF(P) # needs sage.groups
|
|
3727
|
+
Permutation Group with generators [(1,2)]
|
|
3728
|
+
"""
|
|
3729
|
+
from sage.groups.perm_gps.permgroup import PermutationGroup
|
|
3730
|
+
return PermutationGroup([g for g in (R.gens() + self.gens()) if not g.is_one()],
|
|
3731
|
+
domain=self._domain)
|
|
3732
|
+
|
|
3733
|
+
def gens(self) -> tuple:
|
|
3734
|
+
"""
|
|
3735
|
+
EXAMPLES::
|
|
3736
|
+
|
|
3737
|
+
sage: P1 = PermutationGroup([[(1,2)]]) # needs sage.groups
|
|
3738
|
+
sage: PF, P = P1.construction() # needs sage.groups
|
|
3739
|
+
sage: PF.gens() # needs sage.groups
|
|
3740
|
+
((1,2),)
|
|
3741
|
+
"""
|
|
3742
|
+
return self._gens
|
|
3743
|
+
|
|
3744
|
+
def merge(self, other):
|
|
3745
|
+
"""
|
|
3746
|
+
Merge ``self`` with another construction functor, or return ``None``.
|
|
3747
|
+
|
|
3748
|
+
EXAMPLES::
|
|
3749
|
+
|
|
3750
|
+
sage: # needs sage.groups
|
|
3751
|
+
sage: P1 = PermutationGroup([[(1,2)]])
|
|
3752
|
+
sage: PF1, P = P1.construction()
|
|
3753
|
+
sage: P2 = PermutationGroup([[(1,3)]])
|
|
3754
|
+
sage: PF2, P = P2.construction()
|
|
3755
|
+
sage: PF1.merge(PF2)
|
|
3756
|
+
PermutationGroupFunctor[(1,2), (1,3)]
|
|
3757
|
+
"""
|
|
3758
|
+
if self.__class__ != other.__class__:
|
|
3759
|
+
return None
|
|
3760
|
+
from sage.sets.finite_enumerated_set import FiniteEnumeratedSet
|
|
3761
|
+
|
|
3762
|
+
new_domain = set(self._domain).union(set(other._domain))
|
|
3763
|
+
try:
|
|
3764
|
+
new_domain = FiniteEnumeratedSet(sorted(new_domain))
|
|
3765
|
+
except TypeError:
|
|
3766
|
+
# Sorting the domain will sometimes fail with Python 3.
|
|
3767
|
+
# Fallback (not ideal: find a better solution?)
|
|
3768
|
+
new_domain = FiniteEnumeratedSet(sorted(new_domain, key=str))
|
|
3769
|
+
return PermutationGroupFunctor(self.gens() + other.gens(),
|
|
3770
|
+
new_domain)
|
|
3771
|
+
|
|
3772
|
+
|
|
3773
|
+
class EquivariantSubobjectConstructionFunctor(ConstructionFunctor):
|
|
3774
|
+
r"""
|
|
3775
|
+
Constructor for subobjects invariant or equivariant under given semigroup actions.
|
|
3776
|
+
|
|
3777
|
+
Let `S` be a semigroup that
|
|
3778
|
+
- acts on a parent `X` as `s \cdot x` (``action``, ``side='left'``) or
|
|
3779
|
+
- acts on `X` as `x \cdot s` (``action``, ``side='right'``),
|
|
3780
|
+
and (possibly trivially)
|
|
3781
|
+
- acts on `X` as `s * x` (``other_action``, ``other_side='left'``) or
|
|
3782
|
+
- acts on `X` as `x * s` (``other_action``, ``other_side='right'``).
|
|
3783
|
+
|
|
3784
|
+
The `S`-equivariant subobject is the subobject
|
|
3785
|
+
|
|
3786
|
+
.. MATH::
|
|
3787
|
+
|
|
3788
|
+
X^S := \{x \in X : s \cdot x = s * x,\, \forall s \in S \}
|
|
3789
|
+
|
|
3790
|
+
when ``side = other_side = 'left'`` and mutatis mutandis for the other values
|
|
3791
|
+
of ``side`` and ``other_side``.
|
|
3792
|
+
|
|
3793
|
+
When ``other_action`` is trivial, `X^S` is called the `S`-invariant subobject.
|
|
3794
|
+
|
|
3795
|
+
EXAMPLES:
|
|
3796
|
+
|
|
3797
|
+
Monoterm symmetries of a tensor, here only for matrices: row (index 0),
|
|
3798
|
+
column (index 1); the order of the extra element 2 in a permutation determines
|
|
3799
|
+
whether it is a symmetry or an antisymmetry::
|
|
3800
|
+
|
|
3801
|
+
sage: # needs sage.groups sage.modules
|
|
3802
|
+
sage: GSym01 = PermutationGroup([[(0,1),(2,),(3,)]]); GSym01
|
|
3803
|
+
Permutation Group with generators [(0,1)]
|
|
3804
|
+
sage: GASym01 = PermutationGroup([[(0,1),(2,3)]]); GASym01
|
|
3805
|
+
Permutation Group with generators [(0,1)(2,3)]
|
|
3806
|
+
sage: from sage.categories.action import Action
|
|
3807
|
+
sage: from sage.structure.element import Matrix
|
|
3808
|
+
sage: class TensorIndexAction(Action):
|
|
3809
|
+
....: def _act_(self, g, x):
|
|
3810
|
+
....: if isinstance(x, Matrix):
|
|
3811
|
+
....: if g(0) == 1:
|
|
3812
|
+
....: if g(2) == 2:
|
|
3813
|
+
....: return x.transpose()
|
|
3814
|
+
....: else:
|
|
3815
|
+
....: return -x.transpose()
|
|
3816
|
+
....: else:
|
|
3817
|
+
....: return x
|
|
3818
|
+
....: raise NotImplementedError
|
|
3819
|
+
sage: M = matrix([[1, 2], [3, 4]]); M
|
|
3820
|
+
[1 2]
|
|
3821
|
+
[3 4]
|
|
3822
|
+
sage: GSym01_action = TensorIndexAction(GSym01, M.parent())
|
|
3823
|
+
sage: GASym01_action = TensorIndexAction(GASym01, M.parent())
|
|
3824
|
+
sage: GSym01_action.act(GSym01.0, M)
|
|
3825
|
+
[1 3]
|
|
3826
|
+
[2 4]
|
|
3827
|
+
sage: GASym01_action.act(GASym01.0, M)
|
|
3828
|
+
[-1 -3]
|
|
3829
|
+
[-2 -4]
|
|
3830
|
+
sage: Sym01 = M.parent().invariant_module(GSym01, action=GSym01_action); Sym01
|
|
3831
|
+
(Permutation Group with generators [(0,1)])-invariant submodule
|
|
3832
|
+
of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring
|
|
3833
|
+
sage: list(Sym01.basis())
|
|
3834
|
+
[B[0], B[1], B[2]]
|
|
3835
|
+
sage: list(Sym01.basis().map(Sym01.lift))
|
|
3836
|
+
[
|
|
3837
|
+
[1 0] [0 1] [0 0]
|
|
3838
|
+
[0 0], [1 0], [0 1]
|
|
3839
|
+
]
|
|
3840
|
+
sage: ASym01 = M.parent().invariant_module(GASym01, action=GASym01_action)
|
|
3841
|
+
sage: ASym01
|
|
3842
|
+
(Permutation Group with generators [(0,1)(2,3)])-invariant submodule
|
|
3843
|
+
of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring
|
|
3844
|
+
sage: list(ASym01.basis())
|
|
3845
|
+
[B[0]]
|
|
3846
|
+
sage: list(ASym01.basis().map(ASym01.lift))
|
|
3847
|
+
[
|
|
3848
|
+
[ 0 1]
|
|
3849
|
+
[-1 0]
|
|
3850
|
+
]
|
|
3851
|
+
sage: from sage.categories.pushout import pushout
|
|
3852
|
+
sage: pushout(Sym01, QQ)
|
|
3853
|
+
(Permutation Group with generators [(0,1)])-invariant submodule
|
|
3854
|
+
of Full MatrixSpace of 2 by 2 dense matrices over Rational Field
|
|
3855
|
+
"""
|
|
3856
|
+
def __init__(self, S, action=operator.mul, side='left',
|
|
3857
|
+
other_action=None, other_side='left'):
|
|
3858
|
+
"""
|
|
3859
|
+
EXAMPLES::
|
|
3860
|
+
|
|
3861
|
+
sage: # needs sage.combinat sage.groups sage.modules
|
|
3862
|
+
sage: G = SymmetricGroup(3); G.rename('S3')
|
|
3863
|
+
sage: M = FreeModule(ZZ, [1,2,3], prefix='M'); M.rename('M')
|
|
3864
|
+
sage: action = lambda g, x: M.term(g(x))
|
|
3865
|
+
sage: I = M.invariant_module(G, action_on_basis=action); I
|
|
3866
|
+
(S3)-invariant submodule of M
|
|
3867
|
+
sage: I.construction()
|
|
3868
|
+
(EquivariantSubobjectConstructionFunctor,
|
|
3869
|
+
Representation of S3 indexed by {1, 2, 3} over Integer Ring)
|
|
3870
|
+
"""
|
|
3871
|
+
from sage.categories.sets_cat import Sets
|
|
3872
|
+
super().__init__(Sets(), Sets())
|
|
3873
|
+
self.S = S
|
|
3874
|
+
self.action = action
|
|
3875
|
+
self.side = side
|
|
3876
|
+
self.other_action = other_action
|
|
3877
|
+
self.other_side = other_side
|
|
3878
|
+
|
|
3879
|
+
def _apply_functor(self, X):
|
|
3880
|
+
"""
|
|
3881
|
+
Apply the functor to an object of ``self``'s domain.
|
|
3882
|
+
|
|
3883
|
+
TESTS::
|
|
3884
|
+
|
|
3885
|
+
sage: from sage.categories.pushout import EquivariantSubobjectConstructionFunctor
|
|
3886
|
+
sage: M2 = MatrixSpace(QQ, 2); M2 # needs sage.modules
|
|
3887
|
+
Full MatrixSpace of 2 by 2 dense matrices over Rational Field
|
|
3888
|
+
sage: F = EquivariantSubobjectConstructionFunctor(M2, # needs sage.modules
|
|
3889
|
+
....: operator.mul, 'left',
|
|
3890
|
+
....: operator.mul, 'right'); F
|
|
3891
|
+
EquivariantSubobjectConstructionFunctor
|
|
3892
|
+
sage: F(M2) # needs sage.modules
|
|
3893
|
+
Traceback (most recent call last):
|
|
3894
|
+
...
|
|
3895
|
+
NotImplementedError: non-trivial other_action=<built-in function mul> is not implemented
|
|
3896
|
+
"""
|
|
3897
|
+
other_action = self.other_action
|
|
3898
|
+
if other_action is not None:
|
|
3899
|
+
raise NotImplementedError(f'non-trivial {other_action=} is not implemented')
|
|
3900
|
+
# Currently only implemented for FiniteDimensionalModulesWithBasis
|
|
3901
|
+
return X.invariant_module(self.S, action=self.action, side=self.side)
|
|
3902
|
+
|
|
3903
|
+
|
|
3904
|
+
class BlackBoxConstructionFunctor(ConstructionFunctor):
|
|
3905
|
+
"""
|
|
3906
|
+
Construction functor obtained from any callable object.
|
|
3907
|
+
|
|
3908
|
+
EXAMPLES::
|
|
3909
|
+
|
|
3910
|
+
sage: from sage.categories.pushout import BlackBoxConstructionFunctor
|
|
3911
|
+
|
|
3912
|
+
sage: # needs sage.libs.gap
|
|
3913
|
+
sage: from sage.interfaces.gap import gap
|
|
3914
|
+
sage: FG = BlackBoxConstructionFunctor(gap)
|
|
3915
|
+
sage: FG
|
|
3916
|
+
BlackBoxConstructionFunctor
|
|
3917
|
+
sage: FG(ZZ)
|
|
3918
|
+
Integers
|
|
3919
|
+
sage: FG(ZZ).parent()
|
|
3920
|
+
Gap
|
|
3921
|
+
sage: FG == loads(dumps(FG))
|
|
3922
|
+
True
|
|
3923
|
+
|
|
3924
|
+
sage: FS = BlackBoxConstructionFunctor(singular)
|
|
3925
|
+
sage: FS(QQ['t']) # needs sage.libs.singular
|
|
3926
|
+
polynomial ring, over a field, global ordering
|
|
3927
|
+
// coefficients: QQ...
|
|
3928
|
+
// number of vars : 1
|
|
3929
|
+
// block 1 : ordering lp
|
|
3930
|
+
// : names t
|
|
3931
|
+
// block 2 : ordering C
|
|
3932
|
+
sage: FG == FS # needs sage.libs.gap sage.libs.singular
|
|
3933
|
+
False
|
|
3934
|
+
"""
|
|
3935
|
+
rank = 100
|
|
3936
|
+
|
|
3937
|
+
def __init__(self, box):
|
|
3938
|
+
"""
|
|
3939
|
+
TESTS::
|
|
3940
|
+
|
|
3941
|
+
sage: from sage.categories.pushout import BlackBoxConstructionFunctor
|
|
3942
|
+
sage: FG = BlackBoxConstructionFunctor(gap)
|
|
3943
|
+
sage: FM = BlackBoxConstructionFunctor(maxima) # needs sage.symbolic
|
|
3944
|
+
sage: FM == FG # needs sage.libs.gap sage.symbolic
|
|
3945
|
+
False
|
|
3946
|
+
sage: FM == loads(dumps(FM)) # needs sage.symbolic
|
|
3947
|
+
True
|
|
3948
|
+
"""
|
|
3949
|
+
ConstructionFunctor.__init__(self, Objects(), Objects())
|
|
3950
|
+
if not callable(box):
|
|
3951
|
+
raise TypeError("input must be callable")
|
|
3952
|
+
self.box = box
|
|
3953
|
+
|
|
3954
|
+
def _apply_functor(self, R):
|
|
3955
|
+
"""
|
|
3956
|
+
Apply the functor to an object of ``self``'s domain.
|
|
3957
|
+
|
|
3958
|
+
TESTS::
|
|
3959
|
+
|
|
3960
|
+
sage: from sage.categories.pushout import BlackBoxConstructionFunctor
|
|
3961
|
+
sage: f = lambda x: x^2
|
|
3962
|
+
sage: F = BlackBoxConstructionFunctor(f)
|
|
3963
|
+
sage: F(ZZ) # indirect doctest # needs sage.modules
|
|
3964
|
+
Ambient free module of rank 2 over the principal ideal domain Integer Ring
|
|
3965
|
+
"""
|
|
3966
|
+
return self.box(R)
|
|
3967
|
+
|
|
3968
|
+
def __eq__(self, other):
|
|
3969
|
+
"""
|
|
3970
|
+
TESTS::
|
|
3971
|
+
|
|
3972
|
+
sage: from sage.categories.pushout import BlackBoxConstructionFunctor
|
|
3973
|
+
sage: FG = BlackBoxConstructionFunctor(gap)
|
|
3974
|
+
sage: FM = BlackBoxConstructionFunctor(maxima) # needs sage.symbolic
|
|
3975
|
+
sage: FM == FG # indirect doctest # needs sage.libs.gap sage.symbolic
|
|
3976
|
+
False
|
|
3977
|
+
sage: FM == loads(dumps(FM)) # needs sage.symbolic
|
|
3978
|
+
True
|
|
3979
|
+
"""
|
|
3980
|
+
if not isinstance(other, BlackBoxConstructionFunctor):
|
|
3981
|
+
return False
|
|
3982
|
+
|
|
3983
|
+
return self.box == other.box
|
|
3984
|
+
|
|
3985
|
+
def __ne__(self, other):
|
|
3986
|
+
"""
|
|
3987
|
+
Check whether ``self`` is not equal to ``other``.
|
|
3988
|
+
|
|
3989
|
+
EXAMPLES::
|
|
3990
|
+
|
|
3991
|
+
sage: from sage.categories.pushout import BlackBoxConstructionFunctor
|
|
3992
|
+
sage: FG = BlackBoxConstructionFunctor(gap)
|
|
3993
|
+
sage: FM = BlackBoxConstructionFunctor(maxima) # needs sage.symbolic
|
|
3994
|
+
sage: FM != FG # indirect doctest # needs sage.libs.gap sage.symbolic
|
|
3995
|
+
True
|
|
3996
|
+
sage: FM != loads(dumps(FM)) # needs sage.symbolic
|
|
3997
|
+
False
|
|
3998
|
+
"""
|
|
3999
|
+
return not (self == other)
|
|
4000
|
+
|
|
4001
|
+
__hash__ = ConstructionFunctor.__hash__
|
|
4002
|
+
|
|
4003
|
+
|
|
4004
|
+
def pushout(R, S):
|
|
4005
|
+
r"""
|
|
4006
|
+
Given a pair of objects `R` and `S`, try to construct a
|
|
4007
|
+
reasonable object `Y` and return maps such that
|
|
4008
|
+
canonically `R \leftarrow Y \rightarrow S`.
|
|
4009
|
+
|
|
4010
|
+
ALGORITHM:
|
|
4011
|
+
|
|
4012
|
+
This incorporates the idea of functors discussed at Sage Days 4.
|
|
4013
|
+
Every object `R` can be viewed as an initial object and a series
|
|
4014
|
+
of functors (e.g. polynomial, quotient, extension, completion,
|
|
4015
|
+
vector/matrix, etc.). Call the series of increasingly simple
|
|
4016
|
+
objects (with the associated functors) the "tower" of `R`. The
|
|
4017
|
+
construction method is used to create the tower.
|
|
4018
|
+
|
|
4019
|
+
Given two objects `R` and `S`, try to find a common initial object
|
|
4020
|
+
`Z`. If the towers of `R` and `S` meet, let `Z` be their join.
|
|
4021
|
+
Otherwise, see if the top of one coerces naturally into the other.
|
|
4022
|
+
|
|
4023
|
+
Now we have an initial object and two ordered lists of functors to
|
|
4024
|
+
apply. We wish to merge these in an unambiguous order, popping
|
|
4025
|
+
elements off the top of one or the other tower as we apply them to
|
|
4026
|
+
`Z`.
|
|
4027
|
+
|
|
4028
|
+
- If the functors are of distinct types, there is an absolute
|
|
4029
|
+
ordering given by the rank attribute. Use this.
|
|
4030
|
+
|
|
4031
|
+
- Otherwise:
|
|
4032
|
+
|
|
4033
|
+
- If the tops are equal, we (try to) merge them.
|
|
4034
|
+
|
|
4035
|
+
- If exactly one occurs lower in the other tower, we may
|
|
4036
|
+
unambiguously apply the other (hoping for a later merge).
|
|
4037
|
+
|
|
4038
|
+
- If the tops commute, we can apply either first.
|
|
4039
|
+
|
|
4040
|
+
- Otherwise fail due to ambiguity.
|
|
4041
|
+
|
|
4042
|
+
The algorithm assumes by default that when a construction `F` is
|
|
4043
|
+
applied to an object `X`, the object `F(X)` admits a coercion map
|
|
4044
|
+
from `X`. However, the algorithm can also handle the case where
|
|
4045
|
+
`F(X)` has a coercion map *to* `X` instead. In this case, the
|
|
4046
|
+
attribute ``coercion_reversed`` of the class implementing `F`
|
|
4047
|
+
should be set to ``True``.
|
|
4048
|
+
|
|
4049
|
+
EXAMPLES:
|
|
4050
|
+
|
|
4051
|
+
Here our "towers" are `R = Complete_7(Frac(\ZZ))` and `Frac(Poly_x(\ZZ))`,
|
|
4052
|
+
which give us `Frac(Poly_x(Complete_7(Frac(\ZZ))))`::
|
|
4053
|
+
|
|
4054
|
+
sage: from sage.categories.pushout import pushout
|
|
4055
|
+
sage: pushout(Qp(7), Frac(ZZ['x'])) # needs sage.rings.padics
|
|
4056
|
+
Fraction Field of Univariate Polynomial Ring in x
|
|
4057
|
+
over 7-adic Field with capped relative precision 20
|
|
4058
|
+
|
|
4059
|
+
Note we get the same thing with
|
|
4060
|
+
::
|
|
4061
|
+
|
|
4062
|
+
sage: pushout(Zp(7), Frac(QQ['x'])) # needs sage.rings.padics
|
|
4063
|
+
Fraction Field of Univariate Polynomial Ring in x
|
|
4064
|
+
over 7-adic Field with capped relative precision 20
|
|
4065
|
+
sage: pushout(Zp(7)['x'], Frac(QQ['x'])) # needs sage.rings.padics
|
|
4066
|
+
Fraction Field of Univariate Polynomial Ring in x
|
|
4067
|
+
over 7-adic Field with capped relative precision 20
|
|
4068
|
+
|
|
4069
|
+
Note that polynomial variable ordering must be unambiguously determined.
|
|
4070
|
+
::
|
|
4071
|
+
|
|
4072
|
+
sage: pushout(ZZ['x,y,z'], QQ['w,z,t'])
|
|
4073
|
+
Traceback (most recent call last):
|
|
4074
|
+
...
|
|
4075
|
+
CoercionException: ('Ambiguous Base Extension',
|
|
4076
|
+
Multivariate Polynomial Ring in x, y, z over Integer Ring,
|
|
4077
|
+
Multivariate Polynomial Ring in w, z, t over Rational Field)
|
|
4078
|
+
sage: pushout(ZZ['x,y,z'], QQ['w,x,z,t'])
|
|
4079
|
+
Multivariate Polynomial Ring in w, x, y, z, t over Rational Field
|
|
4080
|
+
|
|
4081
|
+
Some other examples::
|
|
4082
|
+
|
|
4083
|
+
sage: pushout(Zp(7)['y'], Frac(QQ['t'])['x,y,z']) # needs sage.rings.padics
|
|
4084
|
+
Multivariate Polynomial Ring in x, y, z
|
|
4085
|
+
over Fraction Field of Univariate Polynomial Ring in t
|
|
4086
|
+
over 7-adic Field with capped relative precision 20
|
|
4087
|
+
sage: pushout(ZZ['x,y,z'], Frac(ZZ['x'])['y'])
|
|
4088
|
+
Multivariate Polynomial Ring in y, z
|
|
4089
|
+
over Fraction Field of Univariate Polynomial Ring in x over Integer Ring
|
|
4090
|
+
sage: pushout(MatrixSpace(RDF, 2, 2), Frac(ZZ['x'])) # needs sage.modules
|
|
4091
|
+
Full MatrixSpace of 2 by 2 dense matrices
|
|
4092
|
+
over Fraction Field of Univariate Polynomial Ring in x over Real Double Field
|
|
4093
|
+
sage: pushout(ZZ, MatrixSpace(ZZ[['x']], 3, 3)) # needs sage.modules
|
|
4094
|
+
Full MatrixSpace of 3 by 3 dense matrices
|
|
4095
|
+
over Power Series Ring in x over Integer Ring
|
|
4096
|
+
sage: pushout(QQ['x,y'], ZZ[['x']])
|
|
4097
|
+
Univariate Polynomial Ring in y
|
|
4098
|
+
over Power Series Ring in x over Rational Field
|
|
4099
|
+
sage: pushout(Frac(ZZ['x']), QQ[['x']])
|
|
4100
|
+
Laurent Series Ring in x over Rational Field
|
|
4101
|
+
|
|
4102
|
+
A construction with ``coercion_reversed=True`` (currently only
|
|
4103
|
+
the :class:`SubspaceFunctor` construction) is only applied if it
|
|
4104
|
+
leads to a valid coercion::
|
|
4105
|
+
|
|
4106
|
+
sage: # needs sage.modules
|
|
4107
|
+
sage: A = ZZ^2
|
|
4108
|
+
sage: V = span([[1, 2]], QQ)
|
|
4109
|
+
sage: P = sage.categories.pushout.pushout(A, V)
|
|
4110
|
+
sage: P
|
|
4111
|
+
Vector space of dimension 2 over Rational Field
|
|
4112
|
+
sage: P.has_coerce_map_from(A)
|
|
4113
|
+
True
|
|
4114
|
+
|
|
4115
|
+
sage: # needs sage.modules
|
|
4116
|
+
sage: V = (QQ^3).span([[1, 2, 3/4]])
|
|
4117
|
+
sage: A = ZZ^3
|
|
4118
|
+
sage: pushout(A, V)
|
|
4119
|
+
Vector space of dimension 3 over Rational Field
|
|
4120
|
+
sage: B = A.span([[0, 0, 2/3]])
|
|
4121
|
+
sage: pushout(B, V)
|
|
4122
|
+
Vector space of degree 3 and dimension 2 over Rational Field
|
|
4123
|
+
User basis matrix:
|
|
4124
|
+
[1 2 0]
|
|
4125
|
+
[0 0 1]
|
|
4126
|
+
|
|
4127
|
+
Some more tests with ``coercion_reversed=True``::
|
|
4128
|
+
|
|
4129
|
+
sage: from sage.categories.pushout import ConstructionFunctor
|
|
4130
|
+
sage: class EvenPolynomialRing(type(QQ['x'])):
|
|
4131
|
+
....: def __init__(self, base, var):
|
|
4132
|
+
....: super().__init__(base, var)
|
|
4133
|
+
....: self.register_embedding(base[var])
|
|
4134
|
+
....: def __repr__(self):
|
|
4135
|
+
....: return "Even Power " + super().__repr__()
|
|
4136
|
+
....: def construction(self):
|
|
4137
|
+
....: return EvenPolynomialFunctor(), self.base()[self.variable_name()]
|
|
4138
|
+
....: def _coerce_map_from_(self, R):
|
|
4139
|
+
....: return self.base().has_coerce_map_from(R)
|
|
4140
|
+
sage: class EvenPolynomialFunctor(ConstructionFunctor):
|
|
4141
|
+
....: rank = 10
|
|
4142
|
+
....: coercion_reversed = True
|
|
4143
|
+
....: def __init__(self):
|
|
4144
|
+
....: ConstructionFunctor.__init__(self, Rings(), Rings())
|
|
4145
|
+
....: def _apply_functor(self, R):
|
|
4146
|
+
....: return EvenPolynomialRing(R.base(), R.variable_name())
|
|
4147
|
+
sage: pushout(EvenPolynomialRing(QQ, 'x'), ZZ)
|
|
4148
|
+
Even Power Univariate Polynomial Ring in x over Rational Field
|
|
4149
|
+
sage: pushout(EvenPolynomialRing(QQ, 'x'), QQ)
|
|
4150
|
+
Even Power Univariate Polynomial Ring in x over Rational Field
|
|
4151
|
+
sage: pushout(EvenPolynomialRing(QQ, 'x'), RR) # needs sage.rings.real_mpfr
|
|
4152
|
+
Even Power Univariate Polynomial Ring in x over Real Field with 53 bits of precision
|
|
4153
|
+
|
|
4154
|
+
sage: pushout(EvenPolynomialRing(QQ, 'x'), ZZ['x'])
|
|
4155
|
+
Univariate Polynomial Ring in x over Rational Field
|
|
4156
|
+
sage: pushout(EvenPolynomialRing(QQ, 'x'), QQ['x'])
|
|
4157
|
+
Univariate Polynomial Ring in x over Rational Field
|
|
4158
|
+
sage: pushout(EvenPolynomialRing(QQ, 'x'), RR['x']) # needs sage.rings.real_mpfr
|
|
4159
|
+
Univariate Polynomial Ring in x over Real Field with 53 bits of precision
|
|
4160
|
+
|
|
4161
|
+
sage: pushout(EvenPolynomialRing(QQ, 'x'), EvenPolynomialRing(QQ, 'x'))
|
|
4162
|
+
Even Power Univariate Polynomial Ring in x over Rational Field
|
|
4163
|
+
sage: pushout(EvenPolynomialRing(QQ, 'x'), EvenPolynomialRing(RR, 'x')) # needs sage.rings.real_mpfr
|
|
4164
|
+
Even Power Univariate Polynomial Ring in x over Real Field with 53 bits of precision
|
|
4165
|
+
|
|
4166
|
+
sage: pushout(EvenPolynomialRing(QQ, 'x')^2, RR^2) # needs sage.modules sage.rings.real_mpfr
|
|
4167
|
+
Ambient free module of rank 2
|
|
4168
|
+
over the principal ideal domain Even Power Univariate Polynomial Ring in x
|
|
4169
|
+
over Real Field with 53 bits of precision
|
|
4170
|
+
sage: pushout(EvenPolynomialRing(QQ, 'x')^2, RR['x']^2) # needs sage.modules sage.rings.real_mpfr
|
|
4171
|
+
Ambient free module of rank 2
|
|
4172
|
+
over the principal ideal domain Univariate Polynomial Ring in x
|
|
4173
|
+
over Real Field with 53 bits of precision
|
|
4174
|
+
|
|
4175
|
+
Some more tests related to univariate/multivariate
|
|
4176
|
+
constructions. We consider a generalization of polynomial rings,
|
|
4177
|
+
where in addition to the coefficient ring `C` we also specify
|
|
4178
|
+
an additive monoid `E` for the exponents of the indeterminate.
|
|
4179
|
+
In particular, the elements of such a parent are given by
|
|
4180
|
+
|
|
4181
|
+
.. MATH::
|
|
4182
|
+
|
|
4183
|
+
\sum_{i=0}^I c_i X^{e_i}
|
|
4184
|
+
|
|
4185
|
+
with `c_i \in C` and `e_i \in E`. We define
|
|
4186
|
+
::
|
|
4187
|
+
|
|
4188
|
+
sage: class GPolynomialRing(Parent):
|
|
4189
|
+
....: def __init__(self, coefficients, var, exponents):
|
|
4190
|
+
....: self.coefficients = coefficients
|
|
4191
|
+
....: self.var = var
|
|
4192
|
+
....: self.exponents = exponents
|
|
4193
|
+
....: super().__init__(category=Rings())
|
|
4194
|
+
....: def _repr_(self):
|
|
4195
|
+
....: return 'Generalized Polynomial Ring in %s^(%s) over %s' % (
|
|
4196
|
+
....: self.var, self.exponents, self.coefficients)
|
|
4197
|
+
....: def construction(self):
|
|
4198
|
+
....: return GPolynomialFunctor(self.var, self.exponents), self.coefficients
|
|
4199
|
+
....: def _coerce_map_from_(self, R):
|
|
4200
|
+
....: return self.coefficients.has_coerce_map_from(R)
|
|
4201
|
+
|
|
4202
|
+
and
|
|
4203
|
+
::
|
|
4204
|
+
|
|
4205
|
+
sage: class GPolynomialFunctor(ConstructionFunctor):
|
|
4206
|
+
....: rank = 10
|
|
4207
|
+
....: def __init__(self, var, exponents):
|
|
4208
|
+
....: self.var = var
|
|
4209
|
+
....: self.exponents = exponents
|
|
4210
|
+
....: ConstructionFunctor.__init__(self, Rings(), Rings())
|
|
4211
|
+
....: def _repr_(self):
|
|
4212
|
+
....: return 'GPoly[%s^(%s)]' % (self.var, self.exponents)
|
|
4213
|
+
....: def _apply_functor(self, coefficients):
|
|
4214
|
+
....: return GPolynomialRing(coefficients, self.var, self.exponents)
|
|
4215
|
+
....: def merge(self, other):
|
|
4216
|
+
....: if isinstance(other, GPolynomialFunctor) and self.var == other.var:
|
|
4217
|
+
....: exponents = pushout(self.exponents, other.exponents)
|
|
4218
|
+
....: return GPolynomialFunctor(self.var, exponents)
|
|
4219
|
+
|
|
4220
|
+
We can construct a parent now in two different ways::
|
|
4221
|
+
|
|
4222
|
+
sage: GPolynomialRing(QQ, 'X', ZZ)
|
|
4223
|
+
Generalized Polynomial Ring in X^(Integer Ring) over Rational Field
|
|
4224
|
+
sage: GP_ZZ = GPolynomialFunctor('X', ZZ); GP_ZZ
|
|
4225
|
+
GPoly[X^(Integer Ring)]
|
|
4226
|
+
sage: GP_ZZ(QQ)
|
|
4227
|
+
Generalized Polynomial Ring in X^(Integer Ring) over Rational Field
|
|
4228
|
+
|
|
4229
|
+
Since the construction
|
|
4230
|
+
::
|
|
4231
|
+
|
|
4232
|
+
sage: GP_ZZ(QQ).construction()
|
|
4233
|
+
(GPoly[X^(Integer Ring)], Rational Field)
|
|
4234
|
+
|
|
4235
|
+
uses the coefficient ring, we have the usual coercion with respect
|
|
4236
|
+
to this parameter::
|
|
4237
|
+
|
|
4238
|
+
sage: pushout(GP_ZZ(ZZ), GP_ZZ(QQ))
|
|
4239
|
+
Generalized Polynomial Ring in X^(Integer Ring) over Rational Field
|
|
4240
|
+
sage: pushout(GP_ZZ(ZZ['t']), GP_ZZ(QQ))
|
|
4241
|
+
Generalized Polynomial Ring in X^(Integer Ring)
|
|
4242
|
+
over Univariate Polynomial Ring in t over Rational Field
|
|
4243
|
+
sage: pushout(GP_ZZ(ZZ['a,b']), GP_ZZ(ZZ['b,c']))
|
|
4244
|
+
Generalized Polynomial Ring in X^(Integer Ring)
|
|
4245
|
+
over Multivariate Polynomial Ring in a, b, c over Integer Ring
|
|
4246
|
+
sage: pushout(GP_ZZ(ZZ['a,b']), GP_ZZ(QQ['b,c']))
|
|
4247
|
+
Generalized Polynomial Ring in X^(Integer Ring)
|
|
4248
|
+
over Multivariate Polynomial Ring in a, b, c over Rational Field
|
|
4249
|
+
sage: pushout(GP_ZZ(ZZ['a,b']), GP_ZZ(ZZ['c,d']))
|
|
4250
|
+
Traceback (most recent call last):
|
|
4251
|
+
...
|
|
4252
|
+
CoercionException: ('Ambiguous Base Extension', ...)
|
|
4253
|
+
|
|
4254
|
+
::
|
|
4255
|
+
|
|
4256
|
+
sage: GP_QQ = GPolynomialFunctor('X', QQ)
|
|
4257
|
+
sage: pushout(GP_ZZ(ZZ), GP_QQ(ZZ))
|
|
4258
|
+
Generalized Polynomial Ring in X^(Rational Field) over Integer Ring
|
|
4259
|
+
sage: pushout(GP_QQ(ZZ), GP_ZZ(ZZ))
|
|
4260
|
+
Generalized Polynomial Ring in X^(Rational Field) over Integer Ring
|
|
4261
|
+
|
|
4262
|
+
::
|
|
4263
|
+
|
|
4264
|
+
sage: GP_ZZt = GPolynomialFunctor('X', ZZ['t'])
|
|
4265
|
+
sage: pushout(GP_ZZt(ZZ), GP_QQ(ZZ))
|
|
4266
|
+
Generalized Polynomial Ring in X^(Univariate Polynomial Ring in t
|
|
4267
|
+
over Rational Field) over Integer Ring
|
|
4268
|
+
|
|
4269
|
+
::
|
|
4270
|
+
|
|
4271
|
+
sage: pushout(GP_ZZ(ZZ), GP_QQ(QQ))
|
|
4272
|
+
Generalized Polynomial Ring in X^(Rational Field) over Rational Field
|
|
4273
|
+
sage: pushout(GP_ZZ(QQ), GP_QQ(ZZ))
|
|
4274
|
+
Generalized Polynomial Ring in X^(Rational Field) over Rational Field
|
|
4275
|
+
sage: pushout(GP_ZZt(QQ), GP_QQ(ZZ))
|
|
4276
|
+
Generalized Polynomial Ring in X^(Univariate Polynomial Ring in t
|
|
4277
|
+
over Rational Field) over Rational Field
|
|
4278
|
+
sage: pushout(GP_ZZt(ZZ), GP_QQ(QQ))
|
|
4279
|
+
Generalized Polynomial Ring in X^(Univariate Polynomial Ring in t
|
|
4280
|
+
over Rational Field) over Rational Field
|
|
4281
|
+
sage: pushout(GP_ZZt(ZZ['a,b']), GP_QQ(ZZ['c,d']))
|
|
4282
|
+
Traceback (most recent call last):
|
|
4283
|
+
...
|
|
4284
|
+
CoercionException: ('Ambiguous Base Extension', ...)
|
|
4285
|
+
sage: pushout(GP_ZZt(ZZ['a,b']), GP_QQ(ZZ['b,c']))
|
|
4286
|
+
Generalized Polynomial Ring
|
|
4287
|
+
in X^(Univariate Polynomial Ring in t over Rational Field)
|
|
4288
|
+
over Multivariate Polynomial Ring in a, b, c over Integer Ring
|
|
4289
|
+
|
|
4290
|
+
Some tests with Cartesian products::
|
|
4291
|
+
|
|
4292
|
+
sage: from sage.sets.cartesian_product import CartesianProduct
|
|
4293
|
+
sage: A = CartesianProduct((ZZ['x'], QQ['y'], QQ['z']),
|
|
4294
|
+
....: Sets().CartesianProducts())
|
|
4295
|
+
sage: B = CartesianProduct((ZZ['x'], ZZ['y'], ZZ['t']['z']),
|
|
4296
|
+
....: Sets().CartesianProducts())
|
|
4297
|
+
sage: A.construction()
|
|
4298
|
+
(The cartesian_product functorial construction,
|
|
4299
|
+
(Univariate Polynomial Ring in x over Integer Ring,
|
|
4300
|
+
Univariate Polynomial Ring in y over Rational Field,
|
|
4301
|
+
Univariate Polynomial Ring in z over Rational Field))
|
|
4302
|
+
sage: pushout(A, B)
|
|
4303
|
+
The Cartesian product of
|
|
4304
|
+
(Univariate Polynomial Ring in x over Integer Ring,
|
|
4305
|
+
Univariate Polynomial Ring in y over Rational Field,
|
|
4306
|
+
Univariate Polynomial Ring in z over
|
|
4307
|
+
Univariate Polynomial Ring in t over Rational Field)
|
|
4308
|
+
sage: pushout(ZZ, cartesian_product([ZZ, QQ]))
|
|
4309
|
+
Traceback (most recent call last):
|
|
4310
|
+
...
|
|
4311
|
+
CoercionException: 'NoneType' object is not iterable
|
|
4312
|
+
|
|
4313
|
+
::
|
|
4314
|
+
|
|
4315
|
+
sage: from sage.categories.pushout import PolynomialFunctor
|
|
4316
|
+
sage: from sage.sets.cartesian_product import CartesianProduct
|
|
4317
|
+
sage: class CartesianProductPoly(CartesianProduct):
|
|
4318
|
+
....: def __init__(self, polynomial_rings):
|
|
4319
|
+
....: sort = sorted(polynomial_rings,
|
|
4320
|
+
....: key=lambda P: P.variable_name())
|
|
4321
|
+
....: super().__init__(sort, Sets().CartesianProducts())
|
|
4322
|
+
....: def vars(self):
|
|
4323
|
+
....: return tuple(P.variable_name()
|
|
4324
|
+
....: for P in self.cartesian_factors())
|
|
4325
|
+
....: def _pushout_(self, other):
|
|
4326
|
+
....: if isinstance(other, CartesianProductPoly):
|
|
4327
|
+
....: s_vars = self.vars()
|
|
4328
|
+
....: o_vars = other.vars()
|
|
4329
|
+
....: if s_vars == o_vars:
|
|
4330
|
+
....: return
|
|
4331
|
+
....: return pushout(CartesianProductPoly(
|
|
4332
|
+
....: self.cartesian_factors() +
|
|
4333
|
+
....: tuple(f for f in other.cartesian_factors()
|
|
4334
|
+
....: if f.variable_name() not in s_vars)),
|
|
4335
|
+
....: CartesianProductPoly(
|
|
4336
|
+
....: other.cartesian_factors() +
|
|
4337
|
+
....: tuple(f for f in self.cartesian_factors()
|
|
4338
|
+
....: if f.variable_name() not in o_vars)))
|
|
4339
|
+
....: C = other.construction()
|
|
4340
|
+
....: if C is None:
|
|
4341
|
+
....: return
|
|
4342
|
+
....: elif isinstance(C[0], PolynomialFunctor):
|
|
4343
|
+
....: return pushout(self, CartesianProductPoly((other,)))
|
|
4344
|
+
|
|
4345
|
+
::
|
|
4346
|
+
|
|
4347
|
+
sage: pushout(CartesianProductPoly((ZZ['x'],)),
|
|
4348
|
+
....: CartesianProductPoly((ZZ['y'],)))
|
|
4349
|
+
The Cartesian product of
|
|
4350
|
+
(Univariate Polynomial Ring in x over Integer Ring,
|
|
4351
|
+
Univariate Polynomial Ring in y over Integer Ring)
|
|
4352
|
+
sage: pushout(CartesianProductPoly((ZZ['x'], ZZ['y'])),
|
|
4353
|
+
....: CartesianProductPoly((ZZ['x'], ZZ['z'])))
|
|
4354
|
+
The Cartesian product of
|
|
4355
|
+
(Univariate Polynomial Ring in x over Integer Ring,
|
|
4356
|
+
Univariate Polynomial Ring in y over Integer Ring,
|
|
4357
|
+
Univariate Polynomial Ring in z over Integer Ring)
|
|
4358
|
+
sage: pushout(CartesianProductPoly((QQ['a,b']['x'], QQ['y'])), # needs sage.symbolic
|
|
4359
|
+
....: CartesianProductPoly((ZZ['b,c']['x'], SR['z'])))
|
|
4360
|
+
The Cartesian product of
|
|
4361
|
+
(Univariate Polynomial Ring in x over
|
|
4362
|
+
Multivariate Polynomial Ring in a, b, c over Rational Field,
|
|
4363
|
+
Univariate Polynomial Ring in y over Rational Field,
|
|
4364
|
+
Univariate Polynomial Ring in z over Symbolic Ring)
|
|
4365
|
+
|
|
4366
|
+
::
|
|
4367
|
+
|
|
4368
|
+
sage: pushout(CartesianProductPoly((ZZ['x'],)), ZZ['y'])
|
|
4369
|
+
The Cartesian product of
|
|
4370
|
+
(Univariate Polynomial Ring in x over Integer Ring,
|
|
4371
|
+
Univariate Polynomial Ring in y over Integer Ring)
|
|
4372
|
+
sage: pushout(QQ['b,c']['y'], CartesianProductPoly((ZZ['a,b']['x'],)))
|
|
4373
|
+
The Cartesian product of
|
|
4374
|
+
(Univariate Polynomial Ring in x over
|
|
4375
|
+
Multivariate Polynomial Ring in a, b over Integer Ring,
|
|
4376
|
+
Univariate Polynomial Ring in y over
|
|
4377
|
+
Multivariate Polynomial Ring in b, c over Rational Field)
|
|
4378
|
+
|
|
4379
|
+
::
|
|
4380
|
+
|
|
4381
|
+
sage: pushout(CartesianProductPoly((ZZ['x'],)), ZZ)
|
|
4382
|
+
Traceback (most recent call last):
|
|
4383
|
+
...
|
|
4384
|
+
CoercionException: No common base ("join") found for
|
|
4385
|
+
The cartesian_product functorial construction(...) and None(Integer Ring):
|
|
4386
|
+
(Multivariate) functors are incompatible.
|
|
4387
|
+
|
|
4388
|
+
AUTHORS:
|
|
4389
|
+
|
|
4390
|
+
- Robert Bradshaw
|
|
4391
|
+
- Peter Bruin
|
|
4392
|
+
- Simon King
|
|
4393
|
+
- Daniel Krenn
|
|
4394
|
+
- David Roe
|
|
4395
|
+
"""
|
|
4396
|
+
if R is S or R == S:
|
|
4397
|
+
return R
|
|
4398
|
+
|
|
4399
|
+
if hasattr(R, '_pushout_'):
|
|
4400
|
+
P = R._pushout_(S)
|
|
4401
|
+
if P is not None:
|
|
4402
|
+
return P
|
|
4403
|
+
|
|
4404
|
+
if hasattr(S, '_pushout_'):
|
|
4405
|
+
P = S._pushout_(R)
|
|
4406
|
+
if P is not None:
|
|
4407
|
+
return P
|
|
4408
|
+
|
|
4409
|
+
if isinstance(R, type):
|
|
4410
|
+
R = type_to_parent(R)
|
|
4411
|
+
|
|
4412
|
+
if isinstance(S, type):
|
|
4413
|
+
S = type_to_parent(S)
|
|
4414
|
+
|
|
4415
|
+
R_tower = construction_tower(R)
|
|
4416
|
+
S_tower = construction_tower(S)
|
|
4417
|
+
Rs = [c[1] for c in R_tower]
|
|
4418
|
+
Ss = [c[1] for c in S_tower]
|
|
4419
|
+
|
|
4420
|
+
# If there is a multivariate construction functor in the tower, we must chop off the end
|
|
4421
|
+
# because tuples don't have has_coerce_map_from functions and to align with the
|
|
4422
|
+
# modification of Rs and Ss below
|
|
4423
|
+
from sage.structure.parent import Parent
|
|
4424
|
+
if not isinstance(Rs[-1], Parent):
|
|
4425
|
+
Rs = Rs[:-1]
|
|
4426
|
+
if not isinstance(Ss[-1], Parent):
|
|
4427
|
+
Ss = Ss[:-1]
|
|
4428
|
+
|
|
4429
|
+
if R in Ss:
|
|
4430
|
+
if not any(c[0].coercion_reversed for c in S_tower[1:]):
|
|
4431
|
+
return S
|
|
4432
|
+
elif S in Rs:
|
|
4433
|
+
if not any(c[0].coercion_reversed for c in R_tower[1:]):
|
|
4434
|
+
return R
|
|
4435
|
+
|
|
4436
|
+
if Rs[-1] in Ss:
|
|
4437
|
+
Rs, Ss = Ss, Rs
|
|
4438
|
+
R_tower, S_tower = S_tower, R_tower
|
|
4439
|
+
|
|
4440
|
+
# look for join
|
|
4441
|
+
Z = None
|
|
4442
|
+
if Ss[-1] in Rs:
|
|
4443
|
+
if Rs[-1] == Ss[-1]:
|
|
4444
|
+
while Rs and Ss and Rs[-1] == Ss[-1]:
|
|
4445
|
+
Rs.pop()
|
|
4446
|
+
Z = Ss.pop()
|
|
4447
|
+
else:
|
|
4448
|
+
Rs = Rs[:Rs.index(Ss[-1])]
|
|
4449
|
+
Z = Ss.pop()
|
|
4450
|
+
|
|
4451
|
+
# look for topmost coercion
|
|
4452
|
+
elif S.has_coerce_map_from(Rs[-1]):
|
|
4453
|
+
while not Ss[-1].has_coerce_map_from(Rs[-1]):
|
|
4454
|
+
Ss.pop()
|
|
4455
|
+
while Rs and Ss[-1].has_coerce_map_from(Rs[-1]):
|
|
4456
|
+
Rs.pop()
|
|
4457
|
+
Z = Ss.pop()
|
|
4458
|
+
|
|
4459
|
+
elif R.has_coerce_map_from(Ss[-1]):
|
|
4460
|
+
while not Rs[-1].has_coerce_map_from(Ss[-1]):
|
|
4461
|
+
Rs.pop()
|
|
4462
|
+
while Ss and Rs[-1].has_coerce_map_from(Ss[-1]):
|
|
4463
|
+
Ss.pop()
|
|
4464
|
+
Z = Rs.pop()
|
|
4465
|
+
|
|
4466
|
+
if Z is None and R_tower[-1][0] is not None:
|
|
4467
|
+
Z = R_tower[-1][0].common_base(S_tower[-1][0], R_tower[-1][1], S_tower[-1][1])
|
|
4468
|
+
R_tower = expand_tower(R_tower[:len(Rs)])
|
|
4469
|
+
S_tower = expand_tower(S_tower[:len(Ss)])
|
|
4470
|
+
else:
|
|
4471
|
+
# Rc is a list of functors from Z to R and Sc is a list of functors from Z to S
|
|
4472
|
+
R_tower = expand_tower(R_tower[:len(Rs) + 1])
|
|
4473
|
+
S_tower = expand_tower(S_tower[:len(Ss) + 1])
|
|
4474
|
+
Rc = [c[0] for c in R_tower[1:]]
|
|
4475
|
+
Sc = [c[0] for c in S_tower[1:]]
|
|
4476
|
+
|
|
4477
|
+
all = IdentityConstructionFunctor()
|
|
4478
|
+
|
|
4479
|
+
def apply_from(Xc):
|
|
4480
|
+
c = Xc.pop()
|
|
4481
|
+
if c.coercion_reversed:
|
|
4482
|
+
Yc = Sc if Xc is Rc else Rc
|
|
4483
|
+
Y_tower = S_tower if Xc is Rc else R_tower
|
|
4484
|
+
Y_partial = Y_tower[len(Yc)][1]
|
|
4485
|
+
if not (c * all)(Z).has_coerce_map_from(Y_partial):
|
|
4486
|
+
return all
|
|
4487
|
+
return c * all
|
|
4488
|
+
|
|
4489
|
+
try:
|
|
4490
|
+
while Rc or Sc:
|
|
4491
|
+
# if we are out of functors in either tower, there is no ambiguity
|
|
4492
|
+
if not Sc:
|
|
4493
|
+
all = apply_from(Rc)
|
|
4494
|
+
elif not Rc:
|
|
4495
|
+
all = apply_from(Sc)
|
|
4496
|
+
# if one of the functors has lower rank, do it first
|
|
4497
|
+
elif Rc[-1].rank < Sc[-1].rank:
|
|
4498
|
+
all = apply_from(Rc)
|
|
4499
|
+
elif Sc[-1].rank < Rc[-1].rank:
|
|
4500
|
+
all = apply_from(Sc)
|
|
4501
|
+
# the ranks are the same, so things are a bit subtler
|
|
4502
|
+
elif Rc[-1] == Sc[-1]:
|
|
4503
|
+
# If they are indeed the same operation, we only do it once.
|
|
4504
|
+
# The \code{merge} function here takes into account non-mathematical
|
|
4505
|
+
# distinctions (e.g. single vs. multivariate polynomials).
|
|
4506
|
+
cR = Rc.pop()
|
|
4507
|
+
cS = Sc.pop()
|
|
4508
|
+
c = cR.merge(cS) or cS.merge(cR)
|
|
4509
|
+
if c:
|
|
4510
|
+
all = c * all
|
|
4511
|
+
else:
|
|
4512
|
+
raise CoercionException("Incompatible Base Extension %r, %r (on %r, %r)" % (R, S, cR, cS))
|
|
4513
|
+
# Now we look ahead to see if either top functor is
|
|
4514
|
+
# applied later on in the other tower.
|
|
4515
|
+
# If this is the case for exactly one of them, we unambiguously
|
|
4516
|
+
# postpone that operation, but if both then we abort.
|
|
4517
|
+
elif Rc[-1] in Sc:
|
|
4518
|
+
if Sc[-1] in Rc:
|
|
4519
|
+
raise CoercionException("Ambiguous Base Extension", R, S)
|
|
4520
|
+
else:
|
|
4521
|
+
all = apply_from(Sc)
|
|
4522
|
+
elif Sc[-1] in Rc:
|
|
4523
|
+
all = apply_from(Rc)
|
|
4524
|
+
# If, perchance, the two functors commute, then we may do them in any order.
|
|
4525
|
+
elif Rc[-1].commutes(Sc[-1]) or Sc[-1].commutes(Rc[-1]):
|
|
4526
|
+
all = Sc.pop() * Rc.pop() * all
|
|
4527
|
+
else:
|
|
4528
|
+
# try and merge (default merge is failure for unequal functors)
|
|
4529
|
+
cR = Rc.pop()
|
|
4530
|
+
cS = Sc.pop()
|
|
4531
|
+
c = cR.merge(cS) or cS.merge(cR)
|
|
4532
|
+
if c is not None:
|
|
4533
|
+
all = c * all
|
|
4534
|
+
else:
|
|
4535
|
+
# Otherwise, we cannot proceed.
|
|
4536
|
+
raise CoercionException("Ambiguous Base Extension", R, S)
|
|
4537
|
+
|
|
4538
|
+
return all(Z)
|
|
4539
|
+
|
|
4540
|
+
except CoercionException:
|
|
4541
|
+
raise
|
|
4542
|
+
except (TypeError, ValueError, AttributeError, NotImplementedError) as ex:
|
|
4543
|
+
# We do this because we may be trying all kinds of things that don't
|
|
4544
|
+
# make sense, and in this case simply want to return that a pushout
|
|
4545
|
+
# couldn't be found.
|
|
4546
|
+
raise CoercionException(ex)
|
|
4547
|
+
|
|
4548
|
+
|
|
4549
|
+
def pushout_lattice(R, S):
|
|
4550
|
+
r"""
|
|
4551
|
+
Given a pair of objects `R` and `S`, try to construct a
|
|
4552
|
+
reasonable object `Y` and return maps such that
|
|
4553
|
+
canonically `R \leftarrow Y \rightarrow S`.
|
|
4554
|
+
|
|
4555
|
+
ALGORITHM:
|
|
4556
|
+
|
|
4557
|
+
This is based on the model that arose from much discussion at
|
|
4558
|
+
Sage Days 4. Going up the tower of constructions of `R` and `S`
|
|
4559
|
+
(e.g. the reals come from the rationals come from the integers),
|
|
4560
|
+
try to find a common parent, and then try to fill in a lattice
|
|
4561
|
+
with these two towers as sides with the top as the common ancestor
|
|
4562
|
+
and the bottom will be the desired ring.
|
|
4563
|
+
|
|
4564
|
+
See the code for a specific worked-out example.
|
|
4565
|
+
|
|
4566
|
+
EXAMPLES::
|
|
4567
|
+
|
|
4568
|
+
sage: from sage.categories.pushout import pushout_lattice
|
|
4569
|
+
sage: A, B = pushout_lattice(Qp(7), Frac(ZZ['x'])) # needs sage.rings.padics
|
|
4570
|
+
sage: A.codomain() # needs sage.rings.padics
|
|
4571
|
+
Fraction Field of Univariate Polynomial Ring in x
|
|
4572
|
+
over 7-adic Field with capped relative precision 20
|
|
4573
|
+
sage: A.codomain() is B.codomain() # needs sage.rings.padics
|
|
4574
|
+
True
|
|
4575
|
+
sage: A, B = pushout_lattice(ZZ, MatrixSpace(ZZ[['x']], 3, 3)) # needs sage.modules
|
|
4576
|
+
sage: B # needs sage.modules
|
|
4577
|
+
Identity endomorphism of Full MatrixSpace of 3 by 3 dense matrices
|
|
4578
|
+
over Power Series Ring in x over Integer Ring
|
|
4579
|
+
|
|
4580
|
+
AUTHOR:
|
|
4581
|
+
|
|
4582
|
+
- Robert Bradshaw
|
|
4583
|
+
"""
|
|
4584
|
+
R_tower = construction_tower(R)
|
|
4585
|
+
S_tower = construction_tower(S)
|
|
4586
|
+
Rs = [c[1] for c in R_tower]
|
|
4587
|
+
Ss = [c[1] for c in S_tower]
|
|
4588
|
+
|
|
4589
|
+
# look for common ancestor
|
|
4590
|
+
start = None
|
|
4591
|
+
for Z in Rs:
|
|
4592
|
+
if Z in Ss:
|
|
4593
|
+
start = Z
|
|
4594
|
+
if start is None:
|
|
4595
|
+
# Should I test for a map between the tops of the towers?
|
|
4596
|
+
# Or, if they're both not ZZ, is it hopeless?
|
|
4597
|
+
return None
|
|
4598
|
+
|
|
4599
|
+
# truncate at common ancestor
|
|
4600
|
+
R_tower = list(reversed(R_tower[:Rs.index(start) + 1]))
|
|
4601
|
+
S_tower = list(reversed(S_tower[:Ss.index(start) + 1]))
|
|
4602
|
+
Rs = [c[1] for c in R_tower] # the list of objects
|
|
4603
|
+
Ss = [c[1] for c in S_tower]
|
|
4604
|
+
Rc = [c[0] for c in R_tower] # the list of functors
|
|
4605
|
+
Sc = [c[0] for c in S_tower]
|
|
4606
|
+
|
|
4607
|
+
# Here we try and construct a 2-dimensional lattice as follows.
|
|
4608
|
+
# Suppose our towers are Z -> Q -> Qp = R and Z -> Z[t] -> Frac(Z[t]) = S
|
|
4609
|
+
lattice = {}
|
|
4610
|
+
# First we fill in the sides
|
|
4611
|
+
#
|
|
4612
|
+
# Z
|
|
4613
|
+
# / \
|
|
4614
|
+
# Q Z[t]
|
|
4615
|
+
# / \
|
|
4616
|
+
# Qp Frac(Z[t])
|
|
4617
|
+
#
|
|
4618
|
+
for i, Rsi in enumerate(Rs):
|
|
4619
|
+
lattice[i, 0] = Rsi
|
|
4620
|
+
for j, Ssj in enumerate(Ss):
|
|
4621
|
+
lattice[0, j] = Ssj
|
|
4622
|
+
|
|
4623
|
+
# Now we attempt to fill in the center, one (diagonal) row at a time,
|
|
4624
|
+
# one commuting square at a time.
|
|
4625
|
+
#
|
|
4626
|
+
# Z
|
|
4627
|
+
# / \
|
|
4628
|
+
# Q Z[t]
|
|
4629
|
+
# / \ / \
|
|
4630
|
+
# Qp Q[t] Frac(Z[t])
|
|
4631
|
+
# \ /
|
|
4632
|
+
# Qp[t]
|
|
4633
|
+
#
|
|
4634
|
+
# There is always exactly one "correct" path/order in which to apply operations
|
|
4635
|
+
# from the top to the bottom. In our example, this is down the far left side.
|
|
4636
|
+
# We keep track of which that is by clearing out Rc and Sc as we go along.
|
|
4637
|
+
#
|
|
4638
|
+
# Note that when applying the functors in the correct order, base extension
|
|
4639
|
+
# is not needed (though it may occur in the resulting morphisms).
|
|
4640
|
+
#
|
|
4641
|
+
for i in range(len(Rc) - 1):
|
|
4642
|
+
for j in range(len(Sc) - 1):
|
|
4643
|
+
try:
|
|
4644
|
+
if lattice[i, j + 1] == lattice[i + 1, j]:
|
|
4645
|
+
# In this case we have R <- S -> R
|
|
4646
|
+
# We don't want to perform the operation twice
|
|
4647
|
+
# and all subsequent squares will come from objects
|
|
4648
|
+
# where the operation was already performed (either
|
|
4649
|
+
# to the left or right)
|
|
4650
|
+
Rc[i] = Sc[j] = None # IdentityConstructionFunctor()
|
|
4651
|
+
lattice[i + 1, j + 1] = lattice[i, j + 1]
|
|
4652
|
+
elif Rc[i] is None and Sc[j] is None:
|
|
4653
|
+
lattice[i + 1, j + 1] = lattice[i, j + 1]
|
|
4654
|
+
elif Rc[i] is None:
|
|
4655
|
+
lattice[i + 1, j + 1] = Sc[j](lattice[i + 1, j])
|
|
4656
|
+
elif Sc[j] is None:
|
|
4657
|
+
lattice[i + 1, j + 1] = Rc[i](lattice[i, j + 1])
|
|
4658
|
+
# For now, we just look at the rank.
|
|
4659
|
+
# TODO: be more sophisticated and query the functors themselves
|
|
4660
|
+
elif Rc[i].rank < Sc[j].rank:
|
|
4661
|
+
lattice[i + 1, j + 1] = Sc[j](lattice[i + 1, j])
|
|
4662
|
+
Rc[i] = None # force us to use pre-applied Rc[i]
|
|
4663
|
+
else:
|
|
4664
|
+
lattice[i + 1, j + 1] = Rc[i](lattice[i, j + 1])
|
|
4665
|
+
Sc[j] = None # force us to use pre-applied Sc[i]
|
|
4666
|
+
except (AttributeError, NameError):
|
|
4667
|
+
# pp(lattice)
|
|
4668
|
+
for ni in range(100):
|
|
4669
|
+
for nj in range(100):
|
|
4670
|
+
try:
|
|
4671
|
+
R = lattice[ni, nj]
|
|
4672
|
+
print(ni, nj, R)
|
|
4673
|
+
except KeyError:
|
|
4674
|
+
break
|
|
4675
|
+
raise CoercionException("%s does not support %s"
|
|
4676
|
+
% (lattice[ni, nj], 'F'))
|
|
4677
|
+
|
|
4678
|
+
# If we are successful, we should have something that looks like this.
|
|
4679
|
+
#
|
|
4680
|
+
# Z
|
|
4681
|
+
# / \
|
|
4682
|
+
# Q Z[t]
|
|
4683
|
+
# / \ / \
|
|
4684
|
+
# Qp Q[t] Frac(Z[t])
|
|
4685
|
+
# \ / \ /
|
|
4686
|
+
# Qp[t] Frac(Q[t])
|
|
4687
|
+
# \ /
|
|
4688
|
+
# Frac(Qp[t])
|
|
4689
|
+
#
|
|
4690
|
+
R_loc = len(Rs) - 1
|
|
4691
|
+
S_loc = len(Ss) - 1
|
|
4692
|
+
|
|
4693
|
+
# Find the composition coercion morphisms along the bottom left...
|
|
4694
|
+
if S_loc > 0:
|
|
4695
|
+
R_map = lattice[R_loc, 1].coerce_map_from(R)
|
|
4696
|
+
for i in range(1, S_loc):
|
|
4697
|
+
map = lattice[R_loc, i + 1].coerce_map_from(lattice[R_loc, i])
|
|
4698
|
+
# The functor used is implicit here, should it be?
|
|
4699
|
+
R_map = map * R_map
|
|
4700
|
+
else:
|
|
4701
|
+
R_map = R.coerce_map_from(R) # id
|
|
4702
|
+
|
|
4703
|
+
# ... and bottom right
|
|
4704
|
+
if R_loc > 0:
|
|
4705
|
+
S_map = lattice[1, S_loc].coerce_map_from(S)
|
|
4706
|
+
for i in range(1, R_loc):
|
|
4707
|
+
map = lattice[i + 1, S_loc].coerce_map_from(lattice[i, S_loc])
|
|
4708
|
+
S_map = map * S_map
|
|
4709
|
+
else:
|
|
4710
|
+
S_map = S.coerce_map_from(S) # id
|
|
4711
|
+
|
|
4712
|
+
return R_map, S_map
|
|
4713
|
+
|
|
4714
|
+
|
|
4715
|
+
# def pp(lattice):
|
|
4716
|
+
# """
|
|
4717
|
+
# Used in debugging to print the current lattice.
|
|
4718
|
+
# """
|
|
4719
|
+
# for i in range(100):
|
|
4720
|
+
# for j in range(100):
|
|
4721
|
+
# try:
|
|
4722
|
+
# R = lattice[i,j]
|
|
4723
|
+
# print(i, j, R)
|
|
4724
|
+
# except KeyError:
|
|
4725
|
+
# break
|
|
4726
|
+
|
|
4727
|
+
|
|
4728
|
+
def construction_tower(R):
|
|
4729
|
+
"""
|
|
4730
|
+
An auxiliary function that is used in :func:`pushout` and :func:`pushout_lattice`.
|
|
4731
|
+
|
|
4732
|
+
INPUT:
|
|
4733
|
+
|
|
4734
|
+
- ``R`` -- an object
|
|
4735
|
+
|
|
4736
|
+
OUTPUT:
|
|
4737
|
+
|
|
4738
|
+
A constructive description of the object from scratch, by a list of pairs
|
|
4739
|
+
of a construction functor and an object to which the construction functor
|
|
4740
|
+
is to be applied. The first pair is formed by ``None`` and the given object.
|
|
4741
|
+
|
|
4742
|
+
EXAMPLES::
|
|
4743
|
+
|
|
4744
|
+
sage: from sage.categories.pushout import construction_tower
|
|
4745
|
+
sage: construction_tower(MatrixSpace(FractionField(QQ['t']), 2)) # needs sage.modules
|
|
4746
|
+
[(None, Full MatrixSpace of 2 by 2 dense matrices over Fraction Field
|
|
4747
|
+
of Univariate Polynomial Ring in t over Rational Field),
|
|
4748
|
+
(MatrixFunctor, Fraction Field
|
|
4749
|
+
of Univariate Polynomial Ring in t over Rational Field),
|
|
4750
|
+
(FractionField, Univariate Polynomial Ring in t over Rational Field),
|
|
4751
|
+
(Poly[t], Rational Field), (FractionField, Integer Ring)]
|
|
4752
|
+
"""
|
|
4753
|
+
tower = [(None, R)]
|
|
4754
|
+
c = R.construction()
|
|
4755
|
+
from sage.structure.parent import Parent
|
|
4756
|
+
while c is not None:
|
|
4757
|
+
f, R = c
|
|
4758
|
+
if not isinstance(f, ConstructionFunctor):
|
|
4759
|
+
f = BlackBoxConstructionFunctor(f)
|
|
4760
|
+
tower.append((f, R))
|
|
4761
|
+
if not isinstance(R, Parent):
|
|
4762
|
+
break
|
|
4763
|
+
c = R.construction()
|
|
4764
|
+
return tower
|
|
4765
|
+
|
|
4766
|
+
|
|
4767
|
+
def expand_tower(tower):
|
|
4768
|
+
"""
|
|
4769
|
+
An auxiliary function that is used in :func:`pushout`.
|
|
4770
|
+
|
|
4771
|
+
INPUT:
|
|
4772
|
+
|
|
4773
|
+
- ``tower`` -- a construction tower as returned by
|
|
4774
|
+
:func:`construction_tower`
|
|
4775
|
+
|
|
4776
|
+
OUTPUT: a new construction tower with all the construction functors expanded
|
|
4777
|
+
|
|
4778
|
+
EXAMPLES::
|
|
4779
|
+
|
|
4780
|
+
sage: from sage.categories.pushout import construction_tower, expand_tower
|
|
4781
|
+
sage: construction_tower(QQ['x,y,z'])
|
|
4782
|
+
[(None, Multivariate Polynomial Ring in x, y, z over Rational Field),
|
|
4783
|
+
(MPoly[x,y,z], Rational Field),
|
|
4784
|
+
(FractionField, Integer Ring)]
|
|
4785
|
+
sage: expand_tower(construction_tower(QQ['x,y,z']))
|
|
4786
|
+
[(None, Multivariate Polynomial Ring in x, y, z over Rational Field),
|
|
4787
|
+
(MPoly[z], Univariate Polynomial Ring in y
|
|
4788
|
+
over Univariate Polynomial Ring in x over Rational Field),
|
|
4789
|
+
(MPoly[y], Univariate Polynomial Ring in x over Rational Field),
|
|
4790
|
+
(MPoly[x], Rational Field),
|
|
4791
|
+
(FractionField, Integer Ring)]
|
|
4792
|
+
"""
|
|
4793
|
+
new_tower = []
|
|
4794
|
+
for f, R in reversed(tower):
|
|
4795
|
+
if f is None:
|
|
4796
|
+
new_tower.append((f, R))
|
|
4797
|
+
else:
|
|
4798
|
+
fs = f.expand()
|
|
4799
|
+
for ff in reversed(fs[1:]):
|
|
4800
|
+
new_tower.append((ff, R))
|
|
4801
|
+
R = ff(R)
|
|
4802
|
+
new_tower.append((fs[0], R))
|
|
4803
|
+
return list(reversed(new_tower))
|
|
4804
|
+
|
|
4805
|
+
|
|
4806
|
+
def type_to_parent(P):
|
|
4807
|
+
"""
|
|
4808
|
+
An auxiliary function that is used in :func:`pushout`.
|
|
4809
|
+
|
|
4810
|
+
INPUT:
|
|
4811
|
+
|
|
4812
|
+
- ``P`` -- a type
|
|
4813
|
+
|
|
4814
|
+
OUTPUT: a Sage parent structure corresponding to the given type
|
|
4815
|
+
|
|
4816
|
+
TESTS::
|
|
4817
|
+
|
|
4818
|
+
sage: from sage.categories.pushout import type_to_parent
|
|
4819
|
+
sage: type_to_parent(int)
|
|
4820
|
+
Integer Ring
|
|
4821
|
+
sage: type_to_parent(float)
|
|
4822
|
+
Real Double Field
|
|
4823
|
+
sage: type_to_parent(complex) # needs sage.rings.complex_double
|
|
4824
|
+
Complex Double Field
|
|
4825
|
+
sage: type_to_parent(list)
|
|
4826
|
+
Traceback (most recent call last):
|
|
4827
|
+
...
|
|
4828
|
+
TypeError: not a scalar type
|
|
4829
|
+
"""
|
|
4830
|
+
from sage.structure.coerce import py_scalar_parent
|
|
4831
|
+
parent = py_scalar_parent(P)
|
|
4832
|
+
if parent is None:
|
|
4833
|
+
raise TypeError("not a scalar type")
|
|
4834
|
+
return parent
|