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,2107 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-objects
|
|
2
|
+
r"""
|
|
3
|
+
The coercion model
|
|
4
|
+
|
|
5
|
+
The coercion model manages how elements of one parent get related to elements
|
|
6
|
+
of another. For example, the integer 2 can canonically be viewed as an element
|
|
7
|
+
of the rational numbers. (The parent of a non-element is its Python type.)
|
|
8
|
+
|
|
9
|
+
::
|
|
10
|
+
|
|
11
|
+
sage: ZZ(2).parent()
|
|
12
|
+
Integer Ring
|
|
13
|
+
sage: QQ(2).parent()
|
|
14
|
+
Rational Field
|
|
15
|
+
|
|
16
|
+
The most prominent role of the coercion model is to make sense of binary
|
|
17
|
+
operations between elements that have distinct parents. It does this by
|
|
18
|
+
finding a parent where both elements make sense, and doing the operation
|
|
19
|
+
there. For example::
|
|
20
|
+
|
|
21
|
+
sage: a = 1/2; a.parent()
|
|
22
|
+
Rational Field
|
|
23
|
+
sage: b = ZZ['x'].gen(); b.parent()
|
|
24
|
+
Univariate Polynomial Ring in x over Integer Ring
|
|
25
|
+
sage: a + b
|
|
26
|
+
x + 1/2
|
|
27
|
+
sage: (a + b).parent()
|
|
28
|
+
Univariate Polynomial Ring in x over Rational Field
|
|
29
|
+
|
|
30
|
+
If there is a coercion (see below) from one of the parents to the other,
|
|
31
|
+
the operation is always performed in the codomain of that coercion. Otherwise
|
|
32
|
+
a reasonable attempt to create a new parent with coercion maps from both
|
|
33
|
+
original parents is made. The results of these discoveries are cached.
|
|
34
|
+
On failure, a :exc:`TypeError` is always raised.
|
|
35
|
+
|
|
36
|
+
Some arithmetic operations (such as multiplication) can indicate an action
|
|
37
|
+
rather than arithmetic in a common parent. For example::
|
|
38
|
+
|
|
39
|
+
sage: # needs database_cremona_mini_ellcurve sage.schemes
|
|
40
|
+
sage: E = EllipticCurve('37a')
|
|
41
|
+
sage: P = E(0,0)
|
|
42
|
+
sage: 5*P
|
|
43
|
+
(1/4 : -5/8 : 1)
|
|
44
|
+
|
|
45
|
+
where there is action of `\ZZ` on the points of `E` given by the additive
|
|
46
|
+
group law. Parents can specify how they act on or are acted upon by other
|
|
47
|
+
parents.
|
|
48
|
+
|
|
49
|
+
There are two kinds of ways to get from one parent to another, coercions and
|
|
50
|
+
conversions.
|
|
51
|
+
|
|
52
|
+
Coercions are canonical (possibly modulo a finite number of
|
|
53
|
+
deterministic choices) morphisms, and the set of all coercions between
|
|
54
|
+
all parents forms a commuting diagram (modulo possibly rounding
|
|
55
|
+
issues). `\ZZ \rightarrow \QQ` is an example of a
|
|
56
|
+
coercion. These are invoked implicitly by the coercion model.
|
|
57
|
+
|
|
58
|
+
Conversions try to construct an element out of their input if at all possible.
|
|
59
|
+
Examples include sections of coercions, creating an element from a string or
|
|
60
|
+
list, etc. and may fail on some inputs of a given type while succeeding on
|
|
61
|
+
others (i.e. they may not be defined on the whole domain). Conversions are
|
|
62
|
+
always explicitly invoked, and never used by the coercion model to resolve
|
|
63
|
+
binary operations.
|
|
64
|
+
|
|
65
|
+
For more information on how to specify coercions, conversions, and actions,
|
|
66
|
+
see the documentation for :class:`Parent`.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
# ****************************************************************************
|
|
70
|
+
# Copyright (C) 2007 Robert Bradshaw <robertwb@math.washington.edu>
|
|
71
|
+
#
|
|
72
|
+
# This program is free software: you can redistribute it and/or modify
|
|
73
|
+
# it under the terms of the GNU General Public License as published by
|
|
74
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
75
|
+
# (at your option) any later version.
|
|
76
|
+
# https://www.gnu.org/licenses/
|
|
77
|
+
# ****************************************************************************
|
|
78
|
+
|
|
79
|
+
from cpython.object cimport (PyTypeObject, PyObject_CallObject,
|
|
80
|
+
PyObject_RichCompare, Py_TYPE,
|
|
81
|
+
Py_EQ, Py_NE, Py_LT, Py_LE, Py_GT)
|
|
82
|
+
from cpython.weakref cimport PyWeakref_GET_OBJECT, PyWeakref_NewRef
|
|
83
|
+
from libc.string cimport strncmp
|
|
84
|
+
cimport gmpy2
|
|
85
|
+
|
|
86
|
+
cdef mul, truediv
|
|
87
|
+
from operator import mul, truediv
|
|
88
|
+
|
|
89
|
+
from sage.structure.richcmp cimport rich_to_bool, revop
|
|
90
|
+
from sage.structure.sage_object cimport SageObject
|
|
91
|
+
from sage.structure.parent cimport Parent_richcmp_element_without_coercion
|
|
92
|
+
from sage.structure.element cimport bin_op_exception, parent, Element
|
|
93
|
+
from sage.structure.coerce_exceptions import CoercionException
|
|
94
|
+
from sage.rings.integer_fake cimport is_Integer
|
|
95
|
+
from sage.categories.map cimport Map
|
|
96
|
+
from sage.categories.morphism import IdentityMorphism
|
|
97
|
+
from sage.categories.action cimport Action, PrecomposedAction
|
|
98
|
+
from sage.sets.pythonclass cimport Set_PythonType
|
|
99
|
+
|
|
100
|
+
import traceback
|
|
101
|
+
|
|
102
|
+
from fractions import Fraction
|
|
103
|
+
cdef type FractionType = <type>Fraction
|
|
104
|
+
|
|
105
|
+
cpdef py_scalar_parent(py_type):
|
|
106
|
+
"""
|
|
107
|
+
Return the Sage equivalent of the given python type, if one exists.
|
|
108
|
+
If there is no equivalent, return ``None``.
|
|
109
|
+
|
|
110
|
+
EXAMPLES::
|
|
111
|
+
|
|
112
|
+
sage: from sage.structure.coerce import py_scalar_parent
|
|
113
|
+
sage: py_scalar_parent(int)
|
|
114
|
+
Integer Ring
|
|
115
|
+
sage: py_scalar_parent(float)
|
|
116
|
+
Real Double Field
|
|
117
|
+
sage: py_scalar_parent(complex) # needs sage.rings.complex_double
|
|
118
|
+
Complex Double Field
|
|
119
|
+
sage: py_scalar_parent(bool)
|
|
120
|
+
Integer Ring
|
|
121
|
+
sage: py_scalar_parent(dict),
|
|
122
|
+
(None,)
|
|
123
|
+
|
|
124
|
+
sage: import fractions
|
|
125
|
+
sage: py_scalar_parent(fractions.Fraction)
|
|
126
|
+
Rational Field
|
|
127
|
+
|
|
128
|
+
sage: # needs numpy
|
|
129
|
+
sage: import numpy
|
|
130
|
+
sage: py_scalar_parent(numpy.int16)
|
|
131
|
+
Integer Ring
|
|
132
|
+
sage: py_scalar_parent(numpy.int32)
|
|
133
|
+
Integer Ring
|
|
134
|
+
sage: py_scalar_parent(numpy.uint64)
|
|
135
|
+
Integer Ring
|
|
136
|
+
sage: py_scalar_parent(numpy.double)
|
|
137
|
+
Real Double Field
|
|
138
|
+
|
|
139
|
+
sage: import gmpy2
|
|
140
|
+
sage: py_scalar_parent(gmpy2.mpz)
|
|
141
|
+
Integer Ring
|
|
142
|
+
sage: py_scalar_parent(gmpy2.mpq)
|
|
143
|
+
Rational Field
|
|
144
|
+
sage: py_scalar_parent(gmpy2.mpfr)
|
|
145
|
+
Real Double Field
|
|
146
|
+
sage: py_scalar_parent(gmpy2.mpc) # needs sage.rings.complex_double
|
|
147
|
+
Complex Double Field
|
|
148
|
+
|
|
149
|
+
sage: # needs mpmath
|
|
150
|
+
sage: import mpmath
|
|
151
|
+
sage: py_scalar_parent(mpmath.mpf)
|
|
152
|
+
Real Double Field
|
|
153
|
+
sage: py_scalar_parent(mpmath.mpc) # needs sage.rings.complex_double
|
|
154
|
+
Complex Double Field
|
|
155
|
+
"""
|
|
156
|
+
if issubclass(py_type, int):
|
|
157
|
+
import sage.rings.integer_ring
|
|
158
|
+
return sage.rings.integer_ring.ZZ
|
|
159
|
+
if py_type is FractionType:
|
|
160
|
+
import sage.rings.rational_field
|
|
161
|
+
return sage.rings.rational_field.QQ
|
|
162
|
+
if issubclass(py_type, float):
|
|
163
|
+
import sage.rings.real_double
|
|
164
|
+
return sage.rings.real_double.RDF
|
|
165
|
+
if issubclass(py_type, complex):
|
|
166
|
+
import sage.rings.complex_double
|
|
167
|
+
return sage.rings.complex_double.CDF
|
|
168
|
+
if is_numpy_type(py_type):
|
|
169
|
+
import numpy
|
|
170
|
+
if issubclass(py_type, numpy.integer):
|
|
171
|
+
import sage.rings.integer_ring
|
|
172
|
+
return sage.rings.integer_ring.ZZ
|
|
173
|
+
if issubclass(py_type, numpy.floating):
|
|
174
|
+
import sage.rings.real_double
|
|
175
|
+
return sage.rings.real_double.RDF
|
|
176
|
+
if issubclass(py_type, numpy.complexfloating):
|
|
177
|
+
import sage.rings.complex_double
|
|
178
|
+
return sage.rings.complex_double.CDF
|
|
179
|
+
return None
|
|
180
|
+
if issubclass(py_type, gmpy2.mpz):
|
|
181
|
+
import sage.rings.integer_ring
|
|
182
|
+
return sage.rings.integer_ring.ZZ
|
|
183
|
+
if issubclass(py_type, gmpy2.mpq):
|
|
184
|
+
import sage.rings.rational_field
|
|
185
|
+
return sage.rings.rational_field.QQ
|
|
186
|
+
if issubclass(py_type, gmpy2.mpfr):
|
|
187
|
+
import sage.rings.real_double
|
|
188
|
+
return sage.rings.real_double.RDF
|
|
189
|
+
if issubclass(py_type, gmpy2.mpc):
|
|
190
|
+
import sage.rings.complex_double
|
|
191
|
+
return sage.rings.complex_double.CDF
|
|
192
|
+
if is_mpmath_type(py_type):
|
|
193
|
+
import mpmath
|
|
194
|
+
if issubclass(py_type, mpmath.mpf):
|
|
195
|
+
from sage.rings.real_double import RDF
|
|
196
|
+
return RDF
|
|
197
|
+
if issubclass(py_type, mpmath.mpc):
|
|
198
|
+
from sage.rings.complex_double import CDF
|
|
199
|
+
return CDF
|
|
200
|
+
return None
|
|
201
|
+
return None
|
|
202
|
+
|
|
203
|
+
cpdef py_scalar_to_element(x):
|
|
204
|
+
"""
|
|
205
|
+
Convert ``x`` to a Sage :class:`~sage.structure.element.Element` if possible.
|
|
206
|
+
|
|
207
|
+
If ``x`` was already an :class:`~sage.structure.element.Element` or if there is no obvious
|
|
208
|
+
conversion possible, just return ``x`` itself.
|
|
209
|
+
|
|
210
|
+
EXAMPLES::
|
|
211
|
+
|
|
212
|
+
sage: from sage.structure.coerce import py_scalar_to_element
|
|
213
|
+
sage: x = py_scalar_to_element(42)
|
|
214
|
+
sage: x, parent(x)
|
|
215
|
+
(42, Integer Ring)
|
|
216
|
+
sage: x = py_scalar_to_element(int(42))
|
|
217
|
+
sage: x, parent(x)
|
|
218
|
+
(42, Integer Ring)
|
|
219
|
+
sage: x = py_scalar_to_element(float(42))
|
|
220
|
+
sage: x, parent(x)
|
|
221
|
+
(42.0, Real Double Field)
|
|
222
|
+
sage: x = py_scalar_to_element(complex(42)) # needs sage.rings.complex_double
|
|
223
|
+
sage: x, parent(x) # needs sage.rings.complex_double
|
|
224
|
+
(42.0, Complex Double Field)
|
|
225
|
+
sage: py_scalar_to_element('hello')
|
|
226
|
+
'hello'
|
|
227
|
+
|
|
228
|
+
sage: from fractions import Fraction
|
|
229
|
+
sage: f = Fraction(int(2^100), int(3^100))
|
|
230
|
+
sage: py_scalar_to_element(f)
|
|
231
|
+
1267650600228229401496703205376/515377520732011331036461129765621272702107522001
|
|
232
|
+
|
|
233
|
+
Note that bools are converted to 0 or 1::
|
|
234
|
+
|
|
235
|
+
sage: py_scalar_to_element(False), py_scalar_to_element(True)
|
|
236
|
+
(0, 1)
|
|
237
|
+
|
|
238
|
+
Test gmpy2's types::
|
|
239
|
+
|
|
240
|
+
sage: import gmpy2
|
|
241
|
+
sage: x = py_scalar_to_element(gmpy2.mpz(42))
|
|
242
|
+
sage: x, parent(x)
|
|
243
|
+
(42, Integer Ring)
|
|
244
|
+
sage: x = py_scalar_to_element(gmpy2.mpq('3/4'))
|
|
245
|
+
sage: x, parent(x)
|
|
246
|
+
(3/4, Rational Field)
|
|
247
|
+
sage: x = py_scalar_to_element(gmpy2.mpfr(42.57))
|
|
248
|
+
sage: x, parent(x)
|
|
249
|
+
(42.57, Real Double Field)
|
|
250
|
+
sage: x = py_scalar_to_element(gmpy2.mpc(int(42), int(42))) # needs sage.rings.complex_double
|
|
251
|
+
sage: x, parent(x) # needs sage.rings.complex_double
|
|
252
|
+
(42.0 + 42.0*I, Complex Double Field)
|
|
253
|
+
|
|
254
|
+
Test compatibility with :func:`py_scalar_parent`::
|
|
255
|
+
|
|
256
|
+
sage: from sage.structure.coerce import py_scalar_parent
|
|
257
|
+
sage: elt = [True, int(42), float(42), complex(42)]
|
|
258
|
+
sage: for x in elt: # needs sage.rings.complex_double
|
|
259
|
+
....: assert py_scalar_parent(type(x)) == py_scalar_to_element(x).parent()
|
|
260
|
+
|
|
261
|
+
sage: import numpy # needs numpy
|
|
262
|
+
sage: elt = [numpy.int8('-12'), numpy.uint8('143'), # needs numpy sage.symbolic
|
|
263
|
+
....: numpy.int16('-33'), numpy.uint16('122'),
|
|
264
|
+
....: numpy.int32('-19'), numpy.uint32('44'),
|
|
265
|
+
....: numpy.int64('-3'), numpy.uint64('552'),
|
|
266
|
+
....: numpy.float16('-1.23'), numpy.float32('-2.22'),
|
|
267
|
+
....: numpy.float64('-3.412'), numpy.complex64(1.2+I),
|
|
268
|
+
....: numpy.complex128(-2+I)]
|
|
269
|
+
sage: for x in elt: # needs numpy sage.symbolic
|
|
270
|
+
....: assert py_scalar_parent(type(x)) == py_scalar_to_element(x).parent()
|
|
271
|
+
|
|
272
|
+
sage: elt = [gmpy2.mpz(42), gmpy2.mpq('3/4'),
|
|
273
|
+
....: gmpy2.mpfr(42.57), gmpy2.mpc(int(42), int(42))]
|
|
274
|
+
sage: for x in elt: # needs sage.rings.complex_double
|
|
275
|
+
....: assert py_scalar_parent(type(x)) == py_scalar_to_element(x).parent()
|
|
276
|
+
"""
|
|
277
|
+
if isinstance(x, Element):
|
|
278
|
+
return x
|
|
279
|
+
elif isinstance(x, int):
|
|
280
|
+
from sage.rings.integer import Integer
|
|
281
|
+
return Integer(x)
|
|
282
|
+
elif type(x) is FractionType:
|
|
283
|
+
from sage.rings.rational import Rational
|
|
284
|
+
return Rational(x)
|
|
285
|
+
elif isinstance(x, float):
|
|
286
|
+
from sage.rings.real_double import RDF
|
|
287
|
+
return RDF(x)
|
|
288
|
+
elif isinstance(x, complex):
|
|
289
|
+
from sage.rings.complex_double import CDF
|
|
290
|
+
return CDF(x)
|
|
291
|
+
elif is_numpy_type(type(x)):
|
|
292
|
+
import numpy
|
|
293
|
+
if isinstance(x, numpy.integer):
|
|
294
|
+
from sage.rings.integer import Integer
|
|
295
|
+
return Integer(x)
|
|
296
|
+
elif isinstance(x, numpy.floating):
|
|
297
|
+
from sage.rings.real_double import RDF
|
|
298
|
+
return RDF(x)
|
|
299
|
+
elif isinstance(x, numpy.complexfloating):
|
|
300
|
+
from sage.rings.complex_double import CDF
|
|
301
|
+
return CDF(x)
|
|
302
|
+
else:
|
|
303
|
+
return x
|
|
304
|
+
elif type(x) is gmpy2.mpz:
|
|
305
|
+
from sage.rings.integer import Integer
|
|
306
|
+
return Integer(x)
|
|
307
|
+
elif type(x) is gmpy2.mpq:
|
|
308
|
+
from sage.rings.rational import Rational
|
|
309
|
+
return Rational(x)
|
|
310
|
+
elif type(x) is gmpy2.mpfr:
|
|
311
|
+
from sage.rings.real_double import RDF
|
|
312
|
+
return RDF(x)
|
|
313
|
+
elif type(x) is gmpy2.mpc:
|
|
314
|
+
from sage.rings.complex_double import CDF
|
|
315
|
+
return CDF(x)
|
|
316
|
+
else:
|
|
317
|
+
return x
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
cpdef bint parent_is_integers(P) except -1:
|
|
321
|
+
"""
|
|
322
|
+
Check whether the type or parent represents the ring of integers.
|
|
323
|
+
|
|
324
|
+
EXAMPLES::
|
|
325
|
+
|
|
326
|
+
sage: from sage.structure.coerce import parent_is_integers
|
|
327
|
+
sage: parent_is_integers(int)
|
|
328
|
+
True
|
|
329
|
+
sage: parent_is_integers(float)
|
|
330
|
+
False
|
|
331
|
+
sage: parent_is_integers(bool)
|
|
332
|
+
True
|
|
333
|
+
sage: parent_is_integers(dict)
|
|
334
|
+
False
|
|
335
|
+
|
|
336
|
+
sage: import numpy # needs numpy
|
|
337
|
+
sage: parent_is_integers(numpy.int16) # needs numpy
|
|
338
|
+
True
|
|
339
|
+
sage: parent_is_integers(numpy.uint64) # needs numpy
|
|
340
|
+
True
|
|
341
|
+
sage: parent_is_integers(float)
|
|
342
|
+
False
|
|
343
|
+
|
|
344
|
+
sage: import gmpy2
|
|
345
|
+
sage: parent_is_integers(gmpy2.mpz)
|
|
346
|
+
True
|
|
347
|
+
sage: parent_is_integers(gmpy2.mpq)
|
|
348
|
+
False
|
|
349
|
+
|
|
350
|
+
Ensure (:issue:`27893`) is fixed::
|
|
351
|
+
|
|
352
|
+
sage: K.<f> = QQ[]
|
|
353
|
+
sage: gmpy2.mpz(2) * f
|
|
354
|
+
2*f
|
|
355
|
+
"""
|
|
356
|
+
if isinstance(P, type):
|
|
357
|
+
if issubclass(P, int):
|
|
358
|
+
return True
|
|
359
|
+
elif is_numpy_type(P):
|
|
360
|
+
from numpy import integer
|
|
361
|
+
return issubclass(P, integer)
|
|
362
|
+
elif issubclass(P, gmpy2.mpz):
|
|
363
|
+
return True
|
|
364
|
+
else:
|
|
365
|
+
return False
|
|
366
|
+
else:
|
|
367
|
+
from sage.rings.integer_ring import ZZ
|
|
368
|
+
return P is ZZ
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def parent_is_numerical(P):
|
|
372
|
+
r"""
|
|
373
|
+
Test if elements of the parent or type ``P`` can be numerically evaluated
|
|
374
|
+
as complex numbers (in a canonical way).
|
|
375
|
+
|
|
376
|
+
EXAMPLES::
|
|
377
|
+
|
|
378
|
+
sage: from sage.structure.coerce import parent_is_numerical
|
|
379
|
+
sage: import gmpy2
|
|
380
|
+
sage: [parent_is_numerical(R) for R in [QQ, int, complex, gmpy2.mpc]] # needs sage.rings.complex_double
|
|
381
|
+
[True, True, True, True]
|
|
382
|
+
sage: [parent_is_numerical(R) for R in [RR, CC]] # needs sage.rings.real_mpfr
|
|
383
|
+
[True, True]
|
|
384
|
+
sage: parent_is_numerical(QuadraticField(-1)) # needs sage.rings.number_field
|
|
385
|
+
True
|
|
386
|
+
sage: import numpy; parent_is_numerical(numpy.complexfloating) # needs numpy
|
|
387
|
+
True
|
|
388
|
+
sage: parent_is_numerical(SR) # needs sage.symbolic
|
|
389
|
+
False
|
|
390
|
+
sage: [parent_is_numerical(R) for R in [QQ['x'], QQ[['x']], str]]
|
|
391
|
+
[False, False, False]
|
|
392
|
+
sage: [parent_is_numerical(R) for R in [RIF, RBF, CIF, CBF]] # needs sage.libs.flint
|
|
393
|
+
[False, False, False, False]
|
|
394
|
+
"""
|
|
395
|
+
if not isinstance(P, Parent):
|
|
396
|
+
P = py_scalar_parent(P)
|
|
397
|
+
if P is None:
|
|
398
|
+
return False
|
|
399
|
+
return P._is_numerical()
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def parent_is_real_numerical(P):
|
|
403
|
+
r"""
|
|
404
|
+
Test if elements of the parent or type ``P`` can be numerically evaluated
|
|
405
|
+
as real numbers (in a canonical way).
|
|
406
|
+
|
|
407
|
+
EXAMPLES::
|
|
408
|
+
|
|
409
|
+
sage: from sage.structure.coerce import parent_is_real_numerical
|
|
410
|
+
sage: import gmpy2
|
|
411
|
+
sage: [parent_is_real_numerical(R) for R in [RR, QQ, ZZ, RLF, int, float, gmpy2.mpq]]
|
|
412
|
+
[True, True, True, True, True, True, True]
|
|
413
|
+
sage: parent_is_real_numerical(QuadraticField(2)) # needs sage.rings.number_field
|
|
414
|
+
True
|
|
415
|
+
sage: import numpy; parent_is_real_numerical(numpy.integer) # needs numpy
|
|
416
|
+
True
|
|
417
|
+
sage: parent_is_real_numerical(QuadraticField(-1)) # needs sage.rings.number_field
|
|
418
|
+
False
|
|
419
|
+
sage: [parent_is_real_numerical(R) # needs numpy
|
|
420
|
+
....: for R in [CC, complex, gmpy2.mpc, numpy.complexfloating]]
|
|
421
|
+
[False, False, False, False]
|
|
422
|
+
sage: [parent_is_real_numerical(R) for R in [QQ['x'], QQ[['x']], str]]
|
|
423
|
+
[False, False, False]
|
|
424
|
+
sage: parent_is_real_numerical(SR) # needs sage.symbolic
|
|
425
|
+
False
|
|
426
|
+
sage: [parent_is_real_numerical(R) for R in [RIF, RBF, CIF, CBF]] # needs sage.libs.flint
|
|
427
|
+
[False, False, False, False]
|
|
428
|
+
"""
|
|
429
|
+
if not isinstance(P, Parent):
|
|
430
|
+
P = py_scalar_parent(P)
|
|
431
|
+
if P is None:
|
|
432
|
+
return False
|
|
433
|
+
return P._is_real_numerical()
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
cpdef bint is_numpy_type(t) noexcept:
|
|
437
|
+
"""
|
|
438
|
+
Return ``True`` if and only if `t` is a type whose name starts
|
|
439
|
+
with ``numpy.``
|
|
440
|
+
|
|
441
|
+
EXAMPLES::
|
|
442
|
+
|
|
443
|
+
sage: from sage.structure.coerce import is_numpy_type
|
|
444
|
+
|
|
445
|
+
sage: # needs numpy
|
|
446
|
+
sage: import numpy
|
|
447
|
+
sage: is_numpy_type(numpy.int16)
|
|
448
|
+
True
|
|
449
|
+
sage: is_numpy_type(numpy.floating)
|
|
450
|
+
True
|
|
451
|
+
sage: is_numpy_type(numpy.ndarray)
|
|
452
|
+
True
|
|
453
|
+
sage: is_numpy_type(numpy.matrix)
|
|
454
|
+
True
|
|
455
|
+
|
|
456
|
+
sage: is_numpy_type(int)
|
|
457
|
+
False
|
|
458
|
+
sage: is_numpy_type(Integer)
|
|
459
|
+
False
|
|
460
|
+
sage: is_numpy_type(Sudoku) # needs sage.combinat
|
|
461
|
+
False
|
|
462
|
+
sage: is_numpy_type(None)
|
|
463
|
+
False
|
|
464
|
+
|
|
465
|
+
TESTS:
|
|
466
|
+
|
|
467
|
+
This used to crash Sage (:issue:`20715`)::
|
|
468
|
+
|
|
469
|
+
sage: is_numpy_type(object)
|
|
470
|
+
False
|
|
471
|
+
sage: 1 + object()
|
|
472
|
+
Traceback (most recent call last):
|
|
473
|
+
...
|
|
474
|
+
TypeError: unsupported operand parent(s) for +: 'Integer Ring' and
|
|
475
|
+
'<class 'object'>'
|
|
476
|
+
"""
|
|
477
|
+
if not isinstance(t, type):
|
|
478
|
+
return False
|
|
479
|
+
cdef PyTypeObject* T = <PyTypeObject*>t
|
|
480
|
+
if strncmp(T.tp_name, "numpy.", 6) == 0:
|
|
481
|
+
return True
|
|
482
|
+
# Check base type. This is needed to detect numpy.matrix.
|
|
483
|
+
if T.tp_base != NULL and strncmp(T.tp_base.tp_name, "numpy.", 6) == 0:
|
|
484
|
+
return True
|
|
485
|
+
return False
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
cpdef bint is_mpmath_type(t) noexcept:
|
|
489
|
+
r"""
|
|
490
|
+
Check whether the type ``t`` is a type whose name starts with ``mpmath.``
|
|
491
|
+
|
|
492
|
+
EXAMPLES::
|
|
493
|
+
|
|
494
|
+
sage: # needs mpmath
|
|
495
|
+
sage: from sage.structure.coerce import is_mpmath_type
|
|
496
|
+
sage: is_mpmath_type(int)
|
|
497
|
+
False
|
|
498
|
+
sage: import mpmath
|
|
499
|
+
sage: is_mpmath_type(mpmath.mpc(2))
|
|
500
|
+
False
|
|
501
|
+
sage: is_mpmath_type(type(mpmath.mpc(2)))
|
|
502
|
+
True
|
|
503
|
+
sage: is_mpmath_type(type(mpmath.mpf(2)))
|
|
504
|
+
True
|
|
505
|
+
"""
|
|
506
|
+
return isinstance(t, type) and \
|
|
507
|
+
t.__module__.startswith("mpmath.")
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
cdef class CoercionModel:
|
|
511
|
+
"""
|
|
512
|
+
See also :mod:`sage.categories.pushout`
|
|
513
|
+
|
|
514
|
+
EXAMPLES::
|
|
515
|
+
|
|
516
|
+
sage: f = ZZ['t', 'x'].0 + QQ['x'].0 + CyclotomicField(13).gen(); f # needs sage.rings.number_field
|
|
517
|
+
t + x + zeta13
|
|
518
|
+
sage: f.parent() # needs sage.rings.number_field
|
|
519
|
+
Multivariate Polynomial Ring in t, x
|
|
520
|
+
over Cyclotomic Field of order 13 and degree 12
|
|
521
|
+
sage: ZZ['x','y'].0 + ~Frac(QQ['y']).0
|
|
522
|
+
(x*y + 1)/y
|
|
523
|
+
sage: MatrixSpace(ZZ['x'], 2, 2)(2) + ~Frac(QQ['x']).0 # needs sage.modules
|
|
524
|
+
[(2*x + 1)/x 0]
|
|
525
|
+
[ 0 (2*x + 1)/x]
|
|
526
|
+
sage: f = ZZ['x,y,z'].0 + QQ['w,x,z,a'].0; f
|
|
527
|
+
w + x
|
|
528
|
+
sage: f.parent()
|
|
529
|
+
Multivariate Polynomial Ring in w, x, y, z, a over Rational Field
|
|
530
|
+
sage: ZZ['x,y,z'].0 + ZZ['w,x,z,a'].1
|
|
531
|
+
2*x
|
|
532
|
+
|
|
533
|
+
TESTS:
|
|
534
|
+
|
|
535
|
+
Check that :issue:`8426` is fixed (see also :issue:`18076`)::
|
|
536
|
+
|
|
537
|
+
sage: import numpy # needs numpy
|
|
538
|
+
sage: if int(numpy.version.short_version[0]) > 1: # needs numpy
|
|
539
|
+
....: __ = numpy.set_printoptions(legacy="1.25")
|
|
540
|
+
|
|
541
|
+
sage: # needs sage.rings.real_mpfr
|
|
542
|
+
sage: x = polygen(RR)
|
|
543
|
+
sage: numpy.float32('1.5') * x # needs numpy
|
|
544
|
+
1.50000000000000*x
|
|
545
|
+
sage: x * numpy.float32('1.5') # needs numpy
|
|
546
|
+
1.50000000000000*x
|
|
547
|
+
sage: p = x**3 + 2*x - 1
|
|
548
|
+
sage: p(float('1.2'))
|
|
549
|
+
3.12800000000000
|
|
550
|
+
sage: p(int('2'))
|
|
551
|
+
11.0000000000000
|
|
552
|
+
|
|
553
|
+
This used to fail (see :issue:`18076`)::
|
|
554
|
+
|
|
555
|
+
sage: 1/3 + numpy.int8('12') # needs numpy
|
|
556
|
+
37/3
|
|
557
|
+
sage: -2/3 + numpy.int16('-2') # needs numpy
|
|
558
|
+
-8/3
|
|
559
|
+
sage: 2/5 + numpy.uint8('2') # needs numpy
|
|
560
|
+
12/5
|
|
561
|
+
|
|
562
|
+
The numpy types do not interact well with the Sage coercion framework. More
|
|
563
|
+
precisely, if a numpy type is the first operand in a binary operation then
|
|
564
|
+
this operation is done in numpy. The result is hence a numpy type::
|
|
565
|
+
|
|
566
|
+
sage: numpy.uint8('2') + 3 # needs numpy
|
|
567
|
+
5
|
|
568
|
+
sage: type(_) # needs numpy
|
|
569
|
+
<class 'numpy.int32'> # 32-bit
|
|
570
|
+
<class 'numpy.int64'> # 64-bit
|
|
571
|
+
|
|
572
|
+
sage: numpy.int8('12') + 1/3 # needs numpy
|
|
573
|
+
12.333333333333334
|
|
574
|
+
sage: type(_) # needs numpy
|
|
575
|
+
<class 'numpy.float64'>
|
|
576
|
+
|
|
577
|
+
AUTHOR:
|
|
578
|
+
|
|
579
|
+
- Robert Bradshaw
|
|
580
|
+
"""
|
|
581
|
+
def __init__(self):
|
|
582
|
+
"""
|
|
583
|
+
EXAMPLES::
|
|
584
|
+
|
|
585
|
+
sage: from sage.structure.coerce import CoercionModel
|
|
586
|
+
sage: cm = CoercionModel()
|
|
587
|
+
sage: x = polygen(ZZ, 'x')
|
|
588
|
+
sage: K = NumberField(x^2 - 2, 'a') # needs sage.rings.number_field
|
|
589
|
+
sage: A = cm.get_action(ZZ, K, operator.mul) # needs sage.rings.number_field
|
|
590
|
+
sage: f, g = cm.coercion_maps(QQ, int)
|
|
591
|
+
sage: f, g = cm.coercion_maps(ZZ, int)
|
|
592
|
+
"""
|
|
593
|
+
self.reset_cache()
|
|
594
|
+
|
|
595
|
+
def reset_cache(self):
|
|
596
|
+
"""
|
|
597
|
+
Clear the coercion cache.
|
|
598
|
+
|
|
599
|
+
This should have no impact on the result of arithmetic operations, as
|
|
600
|
+
the exact same coercions and actions will be re-discovered when needed.
|
|
601
|
+
|
|
602
|
+
It may be useful for debugging, and may also free some memory.
|
|
603
|
+
|
|
604
|
+
EXAMPLES::
|
|
605
|
+
|
|
606
|
+
sage: cm = sage.structure.element.get_coercion_model()
|
|
607
|
+
sage: len(cm.get_cache()[0]) # random
|
|
608
|
+
42
|
|
609
|
+
sage: cm.reset_cache()
|
|
610
|
+
sage: cm.get_cache()
|
|
611
|
+
({}, {})
|
|
612
|
+
"""
|
|
613
|
+
# This MUST be a mapping of tuples, where each
|
|
614
|
+
# tuple contains at least two elements that are either
|
|
615
|
+
# None or of type Map.
|
|
616
|
+
self._coercion_maps = TripleDict()
|
|
617
|
+
# This MUST be a mapping to actions.
|
|
618
|
+
self._action_maps = TripleDict()
|
|
619
|
+
# This is a mapping from Parents to Parents, storing the result of division in the given parent.
|
|
620
|
+
self._division_parents = TripleDict()
|
|
621
|
+
|
|
622
|
+
def get_cache(self):
|
|
623
|
+
"""
|
|
624
|
+
This returns the current cache of coercion maps and actions, primarily
|
|
625
|
+
useful for debugging and introspection.
|
|
626
|
+
|
|
627
|
+
EXAMPLES::
|
|
628
|
+
|
|
629
|
+
sage: cm = sage.structure.element.get_coercion_model()
|
|
630
|
+
sage: cm.canonical_coercion(1, 2/3)
|
|
631
|
+
(1, 2/3)
|
|
632
|
+
sage: maps, actions = cm.get_cache()
|
|
633
|
+
|
|
634
|
+
Now let us see what happens when we do a binary operations with
|
|
635
|
+
an integer and a rational::
|
|
636
|
+
|
|
637
|
+
sage: left_morphism_ref, right_morphism_ref = maps[ZZ, QQ]
|
|
638
|
+
|
|
639
|
+
Note that by :issue:`14058` the coercion model only stores a weak
|
|
640
|
+
reference to the coercion maps in this case::
|
|
641
|
+
|
|
642
|
+
sage: left_morphism_ref
|
|
643
|
+
<weakref at ...; to 'sage.rings.rational.Z_to_Q' at ...>
|
|
644
|
+
|
|
645
|
+
Moreover, the weakly referenced coercion map uses only a weak
|
|
646
|
+
reference to the codomain::
|
|
647
|
+
|
|
648
|
+
sage: left_morphism_ref()
|
|
649
|
+
(map internal to coercion system -- copy before use)
|
|
650
|
+
Natural morphism:
|
|
651
|
+
From: Integer Ring
|
|
652
|
+
To: Rational Field
|
|
653
|
+
|
|
654
|
+
To get an actual valid map, we simply copy the weakly referenced
|
|
655
|
+
coercion map::
|
|
656
|
+
|
|
657
|
+
sage: print(copy(left_morphism_ref()))
|
|
658
|
+
Natural morphism:
|
|
659
|
+
From: Integer Ring
|
|
660
|
+
To: Rational Field
|
|
661
|
+
sage: print(right_morphism_ref)
|
|
662
|
+
None
|
|
663
|
+
|
|
664
|
+
We can see that it coerces the left operand from an integer to a
|
|
665
|
+
rational, and doesn't do anything to the right.
|
|
666
|
+
|
|
667
|
+
Now for some actions::
|
|
668
|
+
|
|
669
|
+
sage: R.<x> = ZZ['x']
|
|
670
|
+
sage: 1/2 * x
|
|
671
|
+
1/2*x
|
|
672
|
+
sage: maps, actions = cm.get_cache()
|
|
673
|
+
sage: act = actions[QQ, R, operator.mul]; act
|
|
674
|
+
Left scalar multiplication by Rational Field
|
|
675
|
+
on Univariate Polynomial Ring in x over Integer Ring
|
|
676
|
+
sage: act.actor()
|
|
677
|
+
Rational Field
|
|
678
|
+
sage: act.domain()
|
|
679
|
+
Univariate Polynomial Ring in x over Integer Ring
|
|
680
|
+
sage: act.codomain()
|
|
681
|
+
Univariate Polynomial Ring in x over Rational Field
|
|
682
|
+
sage: act(1/5, x+10)
|
|
683
|
+
1/5*x + 2
|
|
684
|
+
"""
|
|
685
|
+
d1 = {(S, R): mors for (S, R, op), mors in self._coercion_maps.items()}
|
|
686
|
+
d2 = self._action_maps.copy()
|
|
687
|
+
return d1, d2
|
|
688
|
+
|
|
689
|
+
def record_exceptions(self, bint value=True):
|
|
690
|
+
r"""
|
|
691
|
+
Enables (or disables) recording of the exceptions suppressed during
|
|
692
|
+
arithmetic.
|
|
693
|
+
|
|
694
|
+
Each time that record_exceptions is called (either enabling or disabling
|
|
695
|
+
the record), the exception_stack is cleared.
|
|
696
|
+
|
|
697
|
+
TESTS::
|
|
698
|
+
|
|
699
|
+
sage: cm = sage.structure.element.get_coercion_model()
|
|
700
|
+
sage: cm.record_exceptions()
|
|
701
|
+
sage: cm._test_exception_stack()
|
|
702
|
+
sage: cm.exception_stack()
|
|
703
|
+
['Traceback (most recent call last):\n File "...coerce.pyx", line ...TypeError: just a test']
|
|
704
|
+
sage: cm.record_exceptions(False)
|
|
705
|
+
sage: cm._test_exception_stack()
|
|
706
|
+
sage: cm.exception_stack()
|
|
707
|
+
[]
|
|
708
|
+
"""
|
|
709
|
+
self._record_exceptions = value
|
|
710
|
+
self._exceptions_cleared = True
|
|
711
|
+
self._exception_stack = []
|
|
712
|
+
|
|
713
|
+
cpdef _record_exception(self):
|
|
714
|
+
r"""
|
|
715
|
+
Push the last exception that occurred onto the stack for later reference,
|
|
716
|
+
for internal use.
|
|
717
|
+
|
|
718
|
+
If the stack has not yet been flagged as cleared, we clear it now (rather
|
|
719
|
+
than wasting time to do so for successful operations).
|
|
720
|
+
|
|
721
|
+
TESTS::
|
|
722
|
+
|
|
723
|
+
sage: cm = sage.structure.element.get_coercion_model()
|
|
724
|
+
sage: cm.record_exceptions()
|
|
725
|
+
sage: 1 + 1/2 + 2 # make sure there aren't any errors hanging around
|
|
726
|
+
7/2
|
|
727
|
+
sage: cm.exception_stack()
|
|
728
|
+
[]
|
|
729
|
+
sage: cm._test_exception_stack()
|
|
730
|
+
sage: cm.exception_stack()
|
|
731
|
+
['Traceback (most recent call last):\n File "...coerce.pyx", line ...TypeError: just a test']
|
|
732
|
+
|
|
733
|
+
The function _test_exception_stack is executing the following code::
|
|
734
|
+
|
|
735
|
+
try:
|
|
736
|
+
raise TypeError("just a test")
|
|
737
|
+
except TypeError:
|
|
738
|
+
cm._record_exception()
|
|
739
|
+
"""
|
|
740
|
+
if not self._record_exceptions:
|
|
741
|
+
return
|
|
742
|
+
if not self._exceptions_cleared:
|
|
743
|
+
self._exception_stack = []
|
|
744
|
+
self._exceptions_cleared = True
|
|
745
|
+
self._exception_stack.append(traceback.format_exc().strip())
|
|
746
|
+
|
|
747
|
+
def _test_exception_stack(self):
|
|
748
|
+
r"""
|
|
749
|
+
A function to test the exception stack.
|
|
750
|
+
|
|
751
|
+
EXAMPLES::
|
|
752
|
+
|
|
753
|
+
sage: cm = sage.structure.element.get_coercion_model()
|
|
754
|
+
sage: cm.record_exceptions()
|
|
755
|
+
sage: 1 + 1/11 # make sure there aren't any errors hanging around
|
|
756
|
+
12/11
|
|
757
|
+
sage: cm.exception_stack()
|
|
758
|
+
[]
|
|
759
|
+
sage: cm._test_exception_stack()
|
|
760
|
+
sage: cm.exception_stack()
|
|
761
|
+
['Traceback (most recent call last):\n File "...coerce.pyx", line ...TypeError: just a test']
|
|
762
|
+
"""
|
|
763
|
+
try:
|
|
764
|
+
raise TypeError("just a test")
|
|
765
|
+
except TypeError:
|
|
766
|
+
self._record_exception()
|
|
767
|
+
|
|
768
|
+
def exception_stack(self):
|
|
769
|
+
r"""
|
|
770
|
+
Return the list of exceptions that were caught in the course of
|
|
771
|
+
executing the last binary operation. Useful for diagnosis when
|
|
772
|
+
user-defined maps or actions raise exceptions that are caught in
|
|
773
|
+
the course of coercion detection.
|
|
774
|
+
|
|
775
|
+
If all went well, this should be the empty list. If things aren't
|
|
776
|
+
happening as you expect, this is a good place to check. See also
|
|
777
|
+
:func:`coercion_traceback`.
|
|
778
|
+
|
|
779
|
+
EXAMPLES::
|
|
780
|
+
|
|
781
|
+
sage: cm = sage.structure.element.get_coercion_model()
|
|
782
|
+
sage: cm.record_exceptions()
|
|
783
|
+
sage: 1/2 + 2
|
|
784
|
+
5/2
|
|
785
|
+
sage: cm.exception_stack()
|
|
786
|
+
[]
|
|
787
|
+
sage: 1/2 + GF(3)(2)
|
|
788
|
+
Traceback (most recent call last):
|
|
789
|
+
...
|
|
790
|
+
TypeError: unsupported operand parent(s) for +:
|
|
791
|
+
'Rational Field' and 'Finite Field of size 3'
|
|
792
|
+
|
|
793
|
+
Now see what the actual problem was::
|
|
794
|
+
|
|
795
|
+
sage: import traceback
|
|
796
|
+
sage: cm.exception_stack()
|
|
797
|
+
['Traceback (most recent call last):...', 'Traceback (most recent call last):...']
|
|
798
|
+
sage: print(cm.exception_stack()[-1])
|
|
799
|
+
Traceback (most recent call last):
|
|
800
|
+
...
|
|
801
|
+
TypeError: no common canonical parent for objects with parents:
|
|
802
|
+
'Rational Field' and 'Finite Field of size 3'
|
|
803
|
+
|
|
804
|
+
This is typically accessed via the :func:`coercion_traceback` function.
|
|
805
|
+
|
|
806
|
+
::
|
|
807
|
+
|
|
808
|
+
sage: coercion_traceback()
|
|
809
|
+
Traceback (most recent call last):
|
|
810
|
+
...
|
|
811
|
+
TypeError: no common canonical parent for objects with parents:
|
|
812
|
+
'Rational Field' and 'Finite Field of size 3'
|
|
813
|
+
"""
|
|
814
|
+
if not self._exceptions_cleared:
|
|
815
|
+
self._exception_stack = []
|
|
816
|
+
self._exceptions_cleared = True
|
|
817
|
+
return self._exception_stack
|
|
818
|
+
|
|
819
|
+
def explain(self, xp, yp, op=mul, int verbosity=2):
|
|
820
|
+
"""
|
|
821
|
+
This function can be used to understand what coercions will happen
|
|
822
|
+
for an arithmetic operation between xp and yp (which may be either
|
|
823
|
+
elements or parents). If the parent of the result can be determined
|
|
824
|
+
then it will be returned.
|
|
825
|
+
|
|
826
|
+
EXAMPLES::
|
|
827
|
+
|
|
828
|
+
sage: cm = sage.structure.element.get_coercion_model()
|
|
829
|
+
|
|
830
|
+
sage: cm.explain(ZZ, ZZ)
|
|
831
|
+
Identical parents, arithmetic performed immediately.
|
|
832
|
+
Result lives in Integer Ring
|
|
833
|
+
Integer Ring
|
|
834
|
+
|
|
835
|
+
sage: cm.explain(QQ, int)
|
|
836
|
+
Coercion on right operand via
|
|
837
|
+
Native morphism:
|
|
838
|
+
From: Set of Python objects of class 'int'
|
|
839
|
+
To: Rational Field
|
|
840
|
+
Arithmetic performed after coercions.
|
|
841
|
+
Result lives in Rational Field
|
|
842
|
+
Rational Field
|
|
843
|
+
|
|
844
|
+
sage: R = ZZ['x']
|
|
845
|
+
sage: cm.explain(R, QQ)
|
|
846
|
+
Action discovered.
|
|
847
|
+
Right scalar multiplication by Rational Field
|
|
848
|
+
on Univariate Polynomial Ring in x over Integer Ring
|
|
849
|
+
Result lives in Univariate Polynomial Ring in x over Rational Field
|
|
850
|
+
Univariate Polynomial Ring in x over Rational Field
|
|
851
|
+
|
|
852
|
+
sage: cm.explain(ZZ['x'], QQ, operator.add)
|
|
853
|
+
Coercion on left operand via
|
|
854
|
+
Ring morphism:
|
|
855
|
+
From: Univariate Polynomial Ring in x over Integer Ring
|
|
856
|
+
To: Univariate Polynomial Ring in x over Rational Field
|
|
857
|
+
Defn: Induced from base ring by
|
|
858
|
+
Natural morphism:
|
|
859
|
+
From: Integer Ring
|
|
860
|
+
To: Rational Field
|
|
861
|
+
Coercion on right operand via
|
|
862
|
+
Polynomial base injection morphism:
|
|
863
|
+
From: Rational Field
|
|
864
|
+
To: Univariate Polynomial Ring in x over Rational Field
|
|
865
|
+
Arithmetic performed after coercions.
|
|
866
|
+
Result lives in Univariate Polynomial Ring in x over Rational Field
|
|
867
|
+
Univariate Polynomial Ring in x over Rational Field
|
|
868
|
+
|
|
869
|
+
Sometimes with non-sage types there is not enough information to deduce
|
|
870
|
+
what will actually happen::
|
|
871
|
+
|
|
872
|
+
sage: R100 = RealField(100) # needs sage.rings.real_mpfr
|
|
873
|
+
sage: cm.explain(R100, float, operator.add) # needs sage.rings.real_mpfr
|
|
874
|
+
Right operand is numeric, will attempt coercion in both directions.
|
|
875
|
+
Unknown result parent.
|
|
876
|
+
sage: parent(R100(1) + float(1)) # needs sage.rings.real_mpfr
|
|
877
|
+
<class 'float'>
|
|
878
|
+
sage: cm.explain(QQ, float, operator.add)
|
|
879
|
+
Right operand is numeric, will attempt coercion in both directions.
|
|
880
|
+
Unknown result parent.
|
|
881
|
+
sage: parent(QQ(1) + float(1))
|
|
882
|
+
<class 'float'>
|
|
883
|
+
|
|
884
|
+
Special care is taken to deal with division::
|
|
885
|
+
|
|
886
|
+
sage: cm.explain(ZZ, ZZ, operator.truediv)
|
|
887
|
+
Identical parents, arithmetic performed immediately.
|
|
888
|
+
Result lives in Rational Field
|
|
889
|
+
Rational Field
|
|
890
|
+
|
|
891
|
+
sage: ZZx = ZZ['x']
|
|
892
|
+
sage: QQx = QQ['x']
|
|
893
|
+
sage: cm.explain(ZZx, QQx, operator.truediv)
|
|
894
|
+
Coercion on left operand via
|
|
895
|
+
Ring morphism:
|
|
896
|
+
From: Univariate Polynomial Ring in x over Integer Ring
|
|
897
|
+
To: Univariate Polynomial Ring in x over Rational Field
|
|
898
|
+
Defn: Induced from base ring by
|
|
899
|
+
Natural morphism:
|
|
900
|
+
From: Integer Ring
|
|
901
|
+
To: Rational Field
|
|
902
|
+
Arithmetic performed after coercions.
|
|
903
|
+
Result lives in Fraction Field of Univariate Polynomial Ring in x over Rational Field
|
|
904
|
+
Fraction Field of Univariate Polynomial Ring in x over Rational Field
|
|
905
|
+
|
|
906
|
+
sage: cm.explain(int, ZZ, operator.truediv)
|
|
907
|
+
Coercion on left operand via
|
|
908
|
+
Native morphism:
|
|
909
|
+
From: Set of Python objects of class 'int'
|
|
910
|
+
To: Integer Ring
|
|
911
|
+
Arithmetic performed after coercions.
|
|
912
|
+
Result lives in Rational Field
|
|
913
|
+
Rational Field
|
|
914
|
+
|
|
915
|
+
sage: cm.explain(ZZx, ZZ, operator.truediv)
|
|
916
|
+
Action discovered.
|
|
917
|
+
Right inverse action by Rational Field
|
|
918
|
+
on Univariate Polynomial Ring in x over Integer Ring
|
|
919
|
+
with precomposition on right by Natural morphism:
|
|
920
|
+
From: Integer Ring
|
|
921
|
+
To: Rational Field
|
|
922
|
+
Result lives in Univariate Polynomial Ring in x over Rational Field
|
|
923
|
+
Univariate Polynomial Ring in x over Rational Field
|
|
924
|
+
|
|
925
|
+
.. NOTE::
|
|
926
|
+
|
|
927
|
+
This function is accurate only in so far as :meth:`analyse` is kept
|
|
928
|
+
in sync with the :meth:`bin_op` and
|
|
929
|
+
:meth:`canonical_coercion` which are kept separate for
|
|
930
|
+
maximal efficiency.
|
|
931
|
+
"""
|
|
932
|
+
all, res = self.analyse(xp, yp, op)
|
|
933
|
+
indent = " " * 4
|
|
934
|
+
if verbosity >= 2:
|
|
935
|
+
print("\n".join(s if isinstance(s, str)
|
|
936
|
+
else (indent + repr(s).replace("\n", "\n" + indent))
|
|
937
|
+
for s in all))
|
|
938
|
+
elif verbosity >= 1:
|
|
939
|
+
print("\n".join(s for s in all if isinstance(s, str)))
|
|
940
|
+
if verbosity >= 1:
|
|
941
|
+
if res is None:
|
|
942
|
+
print("Unknown result parent.")
|
|
943
|
+
else:
|
|
944
|
+
print("Result lives in {}".format(res))
|
|
945
|
+
return res
|
|
946
|
+
|
|
947
|
+
cpdef analyse(self, xp, yp, op=mul):
|
|
948
|
+
"""
|
|
949
|
+
Emulate the process of doing arithmetic between xp and yp, returning
|
|
950
|
+
a list of steps and the parent that the result will live in.
|
|
951
|
+
|
|
952
|
+
The :meth:`explain` method is easier to use, but if one wants access to
|
|
953
|
+
the actual morphism and action objects (rather than their string
|
|
954
|
+
representations), then this is the function to use.
|
|
955
|
+
|
|
956
|
+
EXAMPLES::
|
|
957
|
+
|
|
958
|
+
sage: cm = sage.structure.element.get_coercion_model()
|
|
959
|
+
sage: GF7 = GF(7)
|
|
960
|
+
sage: steps, res = cm.analyse(GF7, ZZ)
|
|
961
|
+
sage: steps
|
|
962
|
+
['Coercion on right operand via',
|
|
963
|
+
Natural morphism:
|
|
964
|
+
From: Integer Ring
|
|
965
|
+
To: Finite Field of size 7,
|
|
966
|
+
'Arithmetic performed after coercions.']
|
|
967
|
+
sage: res
|
|
968
|
+
Finite Field of size 7
|
|
969
|
+
sage: f = steps[1]; type(f)
|
|
970
|
+
<class 'sage.rings.finite_rings.integer_mod.Integer_to_IntegerMod'>
|
|
971
|
+
sage: f(100)
|
|
972
|
+
2
|
|
973
|
+
"""
|
|
974
|
+
self._exceptions_cleared = False
|
|
975
|
+
res = None
|
|
976
|
+
if not isinstance(xp, type) and not isinstance(xp, Parent):
|
|
977
|
+
xp = parent(xp)
|
|
978
|
+
if not isinstance(yp, type) and not isinstance(yp, Parent):
|
|
979
|
+
yp = parent(yp)
|
|
980
|
+
|
|
981
|
+
all = []
|
|
982
|
+
if xp is yp:
|
|
983
|
+
all.append("Identical parents, arithmetic performed immediately." % xp)
|
|
984
|
+
if op is truediv and isinstance(xp, Parent):
|
|
985
|
+
xp = self.division_parent(xp)
|
|
986
|
+
return all, xp
|
|
987
|
+
if xp == yp:
|
|
988
|
+
all.append("Equal but distinct parents.")
|
|
989
|
+
|
|
990
|
+
action = self.get_action(xp, yp, op)
|
|
991
|
+
if action is not None:
|
|
992
|
+
all.append("Action discovered.")
|
|
993
|
+
all.append(action)
|
|
994
|
+
return all, action.codomain()
|
|
995
|
+
|
|
996
|
+
homs = self.discover_coercion(xp, yp)
|
|
997
|
+
if homs is not None:
|
|
998
|
+
x_mor, y_mor = homs
|
|
999
|
+
if x_mor is not None:
|
|
1000
|
+
x_mor = x_mor.__copy__()
|
|
1001
|
+
all.append("Coercion on left operand via")
|
|
1002
|
+
all.append(x_mor)
|
|
1003
|
+
res = x_mor.codomain()
|
|
1004
|
+
if y_mor is not None:
|
|
1005
|
+
y_mor = y_mor.__copy__()
|
|
1006
|
+
all.append("Coercion on right operand via")
|
|
1007
|
+
all.append(y_mor)
|
|
1008
|
+
if res is not None and res is not y_mor.codomain():
|
|
1009
|
+
raise RuntimeError("BUG in coercion model: codomains not equal", x_mor, y_mor)
|
|
1010
|
+
res = y_mor.codomain()
|
|
1011
|
+
all.append("Arithmetic performed after coercions.")
|
|
1012
|
+
if op is truediv and isinstance(res, Parent):
|
|
1013
|
+
res = self.division_parent(res)
|
|
1014
|
+
return all, res
|
|
1015
|
+
|
|
1016
|
+
if isinstance(yp, Parent) and xp in [int, float, complex, bool]:
|
|
1017
|
+
mor = yp._internal_coerce_map_from(xp)
|
|
1018
|
+
if mor is not None:
|
|
1019
|
+
mor = mor.__copy__()
|
|
1020
|
+
all.append("Coercion on numeric left operand via")
|
|
1021
|
+
all.append(mor)
|
|
1022
|
+
if op is truediv and isinstance(yp, Parent):
|
|
1023
|
+
yp = self.division_parent(yp)
|
|
1024
|
+
return all, yp
|
|
1025
|
+
all.append("Left operand is numeric, will attempt coercion in both directions.")
|
|
1026
|
+
elif type(xp) is type:
|
|
1027
|
+
all.append("Left operand is not Sage element, will try _sage_.")
|
|
1028
|
+
|
|
1029
|
+
if isinstance(xp, Parent) and yp in [int, float, complex, bool]:
|
|
1030
|
+
mor = xp._internal_coerce_map_from(yp)
|
|
1031
|
+
if mor is not None:
|
|
1032
|
+
mor = mor.__copy__()
|
|
1033
|
+
all.append("Coercion on numeric right operand via")
|
|
1034
|
+
all.append(mor)
|
|
1035
|
+
if op is truediv and isinstance(xp, Parent):
|
|
1036
|
+
xp = self.division_parent(xp)
|
|
1037
|
+
return all, xp
|
|
1038
|
+
all.append("Right operand is numeric, will attempt coercion in both directions.")
|
|
1039
|
+
elif type(yp) is type:
|
|
1040
|
+
all.append("Right operand is not Sage element, will try _sage_.")
|
|
1041
|
+
|
|
1042
|
+
return all, None
|
|
1043
|
+
|
|
1044
|
+
def common_parent(self, *args):
|
|
1045
|
+
"""
|
|
1046
|
+
Compute a common parent for all the inputs.
|
|
1047
|
+
|
|
1048
|
+
It's essentially an `n`-ary canonical coercion except it can
|
|
1049
|
+
operate on parents rather than just elements.
|
|
1050
|
+
|
|
1051
|
+
INPUT:
|
|
1052
|
+
|
|
1053
|
+
- ``args`` -- set of elements and/or parents
|
|
1054
|
+
|
|
1055
|
+
OUTPUT:
|
|
1056
|
+
|
|
1057
|
+
A :class:`Parent` into which each input should coerce, or raises a
|
|
1058
|
+
:exc:`TypeError` if no such :class:`Parent` can be found.
|
|
1059
|
+
|
|
1060
|
+
EXAMPLES::
|
|
1061
|
+
|
|
1062
|
+
sage: cm = sage.structure.element.get_coercion_model()
|
|
1063
|
+
sage: cm.common_parent(ZZ, QQ)
|
|
1064
|
+
Rational Field
|
|
1065
|
+
sage: cm.common_parent(ZZ, QQ, RR) # needs sage.rings.real_mpfr
|
|
1066
|
+
Real Field with 53 bits of precision
|
|
1067
|
+
sage: ZZT = ZZ[['T']]
|
|
1068
|
+
sage: QQT = QQ['T']
|
|
1069
|
+
sage: cm.common_parent(ZZT, QQT, RDF)
|
|
1070
|
+
Power Series Ring in T over Real Double Field
|
|
1071
|
+
sage: cm.common_parent(4r, 5r)
|
|
1072
|
+
<class 'int'>
|
|
1073
|
+
sage: cm.common_parent(int, float, ZZ)
|
|
1074
|
+
<class 'float'>
|
|
1075
|
+
sage: real_fields = [RealField(prec) for prec in [10,20..100]] # needs sage.rings.real_mpfr
|
|
1076
|
+
sage: cm.common_parent(*real_fields) # needs sage.rings.real_mpfr
|
|
1077
|
+
Real Field with 10 bits of precision
|
|
1078
|
+
|
|
1079
|
+
There are some cases where the ordering does matter, but if a parent
|
|
1080
|
+
can be found it is always the same::
|
|
1081
|
+
|
|
1082
|
+
sage: QQxy = QQ['x,y']
|
|
1083
|
+
sage: QQyz = QQ['y,z']
|
|
1084
|
+
sage: cm.common_parent(QQxy, QQyz) == cm.common_parent(QQyz, QQxy)
|
|
1085
|
+
True
|
|
1086
|
+
sage: QQzt = QQ['z,t']
|
|
1087
|
+
sage: cm.common_parent(QQxy, QQyz, QQzt)
|
|
1088
|
+
Multivariate Polynomial Ring in x, y, z, t over Rational Field
|
|
1089
|
+
sage: cm.common_parent(QQxy, QQzt, QQyz)
|
|
1090
|
+
Traceback (most recent call last):
|
|
1091
|
+
...
|
|
1092
|
+
TypeError: no common canonical parent for objects with parents:
|
|
1093
|
+
'Multivariate Polynomial Ring in x, y over Rational Field' and
|
|
1094
|
+
'Multivariate Polynomial Ring in z, t over Rational Field'
|
|
1095
|
+
"""
|
|
1096
|
+
base = None
|
|
1097
|
+
for x in args:
|
|
1098
|
+
if not isinstance(x, Parent) and not isinstance(x, type):
|
|
1099
|
+
x = parent(x)
|
|
1100
|
+
if base is None:
|
|
1101
|
+
base = x
|
|
1102
|
+
if isinstance(base, Parent) and (<Parent>base).has_coerce_map_from(x):
|
|
1103
|
+
continue
|
|
1104
|
+
elif isinstance(x, Parent) and (<Parent>x).has_coerce_map_from(base):
|
|
1105
|
+
base = x
|
|
1106
|
+
else:
|
|
1107
|
+
a = base.an_element() if isinstance(base, Parent) else base(1)
|
|
1108
|
+
b = x.an_element() if isinstance(x, Parent) else x(1)
|
|
1109
|
+
base = parent(self.canonical_coercion(a, b)[0])
|
|
1110
|
+
return base
|
|
1111
|
+
|
|
1112
|
+
cpdef division_parent(self, Parent P):
|
|
1113
|
+
r"""
|
|
1114
|
+
Deduces where the result of division in ``P`` lies by
|
|
1115
|
+
calculating the inverse of ``P.one()`` or ``P.an_element()``.
|
|
1116
|
+
|
|
1117
|
+
The result is cached.
|
|
1118
|
+
|
|
1119
|
+
EXAMPLES::
|
|
1120
|
+
|
|
1121
|
+
sage: cm = sage.structure.element.get_coercion_model()
|
|
1122
|
+
sage: cm.division_parent(ZZ)
|
|
1123
|
+
Rational Field
|
|
1124
|
+
sage: cm.division_parent(QQ)
|
|
1125
|
+
Rational Field
|
|
1126
|
+
sage: ZZx = ZZ['x']
|
|
1127
|
+
sage: cm.division_parent(ZZx)
|
|
1128
|
+
Fraction Field of Univariate Polynomial Ring in x over Integer Ring
|
|
1129
|
+
sage: K = GF(41)
|
|
1130
|
+
sage: cm.division_parent(K)
|
|
1131
|
+
Finite Field of size 41
|
|
1132
|
+
sage: Zmod100 = Integers(100)
|
|
1133
|
+
sage: cm.division_parent(Zmod100)
|
|
1134
|
+
Ring of integers modulo 100
|
|
1135
|
+
sage: S5 = SymmetricGroup(5) # needs sage.groups
|
|
1136
|
+
sage: cm.division_parent(S5) # needs sage.groups
|
|
1137
|
+
Symmetric group of order 5! as a permutation group
|
|
1138
|
+
"""
|
|
1139
|
+
try:
|
|
1140
|
+
return self._division_parents.get(P, None, None)
|
|
1141
|
+
except KeyError:
|
|
1142
|
+
pass
|
|
1143
|
+
try:
|
|
1144
|
+
ret = parent(~P.one())
|
|
1145
|
+
except Exception:
|
|
1146
|
+
self._record_exception()
|
|
1147
|
+
ret = parent(~P.an_element())
|
|
1148
|
+
self._division_parents.set(P, None, None, ret)
|
|
1149
|
+
return ret
|
|
1150
|
+
|
|
1151
|
+
cpdef bin_op(self, x, y, op):
|
|
1152
|
+
"""
|
|
1153
|
+
Execute the operation ``op`` on `x` and `y`.
|
|
1154
|
+
|
|
1155
|
+
It first looks for an action
|
|
1156
|
+
corresponding to ``op``, and failing that, it tries to coerce `x` and `y`
|
|
1157
|
+
into a common parent and calls ``op`` on them.
|
|
1158
|
+
|
|
1159
|
+
If it cannot make sense of the operation, a :exc:`TypeError` is raised.
|
|
1160
|
+
|
|
1161
|
+
INPUT:
|
|
1162
|
+
|
|
1163
|
+
- ``x`` -- the left operand
|
|
1164
|
+
|
|
1165
|
+
- ``y`` -- the right operand
|
|
1166
|
+
|
|
1167
|
+
- ``op`` -- a python function taking 2 arguments
|
|
1168
|
+
|
|
1169
|
+
.. NOTE::
|
|
1170
|
+
|
|
1171
|
+
``op`` is often an arithmetic operation, but need not be so.
|
|
1172
|
+
|
|
1173
|
+
EXAMPLES::
|
|
1174
|
+
|
|
1175
|
+
sage: cm = sage.structure.element.get_coercion_model()
|
|
1176
|
+
sage: cm.bin_op(1/2, 5, operator.mul)
|
|
1177
|
+
5/2
|
|
1178
|
+
|
|
1179
|
+
The operator can be any callable::
|
|
1180
|
+
|
|
1181
|
+
sage: R.<x> = ZZ['x']
|
|
1182
|
+
sage: cm.bin_op(x^2 - 1, x + 1, gcd)
|
|
1183
|
+
x + 1
|
|
1184
|
+
|
|
1185
|
+
Actions are detected and performed::
|
|
1186
|
+
|
|
1187
|
+
sage: M = matrix(ZZ, 2, 2, range(4)) # needs sage.modules
|
|
1188
|
+
sage: V = vector(ZZ, [5,7]) # needs sage.modules
|
|
1189
|
+
sage: cm.bin_op(M, V, operator.mul) # needs sage.modules
|
|
1190
|
+
(7, 31)
|
|
1191
|
+
|
|
1192
|
+
TESTS::
|
|
1193
|
+
|
|
1194
|
+
sage: class Foo():
|
|
1195
|
+
....: def __rmul__(self, left):
|
|
1196
|
+
....: return 'hello'
|
|
1197
|
+
sage: H = Foo()
|
|
1198
|
+
sage: print(int(3)*H)
|
|
1199
|
+
hello
|
|
1200
|
+
sage: print(Integer(3)*H)
|
|
1201
|
+
hello
|
|
1202
|
+
sage: print(H*3)
|
|
1203
|
+
Traceback (most recent call last):
|
|
1204
|
+
...
|
|
1205
|
+
TypeError: unsupported operand parent(s) for *: '<class '__main__.Foo'>' and 'Integer Ring'
|
|
1206
|
+
|
|
1207
|
+
sage: class Nonsense():
|
|
1208
|
+
....: def __init__(self, s):
|
|
1209
|
+
....: self.s = s
|
|
1210
|
+
....: def __repr__(self):
|
|
1211
|
+
....: return self.s
|
|
1212
|
+
....: def __mul__(self, x):
|
|
1213
|
+
....: return Nonsense(self.s + chr(x%256))
|
|
1214
|
+
....: __add__ = __mul__
|
|
1215
|
+
....: def __rmul__(self, x):
|
|
1216
|
+
....: return Nonsense(chr(x%256) + self.s)
|
|
1217
|
+
....: __radd__ = __rmul__
|
|
1218
|
+
sage: a = Nonsense('blahblah')
|
|
1219
|
+
sage: a*80
|
|
1220
|
+
blahblahP
|
|
1221
|
+
sage: 80*a
|
|
1222
|
+
Pblahblah
|
|
1223
|
+
sage: a+80
|
|
1224
|
+
blahblahP
|
|
1225
|
+
sage: 80+a
|
|
1226
|
+
Pblahblah
|
|
1227
|
+
"""
|
|
1228
|
+
self._exceptions_cleared = False
|
|
1229
|
+
|
|
1230
|
+
# If parents are equal, we can just call op()
|
|
1231
|
+
xp = parent(x)
|
|
1232
|
+
yp = parent(y)
|
|
1233
|
+
if xp is yp:
|
|
1234
|
+
return op(x,y)
|
|
1235
|
+
|
|
1236
|
+
# Actions take preference over common-parent coercions
|
|
1237
|
+
try:
|
|
1238
|
+
action = self._action_maps.get(xp, yp, op)
|
|
1239
|
+
except KeyError:
|
|
1240
|
+
action = self.get_action(xp, yp, op, x, y)
|
|
1241
|
+
if action is not None:
|
|
1242
|
+
if (<Action>action)._is_left:
|
|
1243
|
+
return (<Action>action)._act_(x, y)
|
|
1244
|
+
else:
|
|
1245
|
+
return (<Action>action)._act_(y, x)
|
|
1246
|
+
|
|
1247
|
+
# Now coerce to a common parent and do the operation there
|
|
1248
|
+
try:
|
|
1249
|
+
xy = self.canonical_coercion(x, y)
|
|
1250
|
+
except TypeError:
|
|
1251
|
+
self._record_exception()
|
|
1252
|
+
else:
|
|
1253
|
+
return PyObject_CallObject(op, xy)
|
|
1254
|
+
|
|
1255
|
+
if op is mul:
|
|
1256
|
+
# elements may also act on non-elements
|
|
1257
|
+
# (e.g. sequences or parents)
|
|
1258
|
+
if not isinstance(y, Element) or not isinstance(x, Element):
|
|
1259
|
+
try:
|
|
1260
|
+
if hasattr(x, '_act_on_'):
|
|
1261
|
+
res = x._act_on_(y, True)
|
|
1262
|
+
if res is not None: return res
|
|
1263
|
+
except CoercionException:
|
|
1264
|
+
self._record_exception()
|
|
1265
|
+
|
|
1266
|
+
try:
|
|
1267
|
+
if hasattr(x, '_acted_upon_'):
|
|
1268
|
+
res = x._acted_upon_(y, True)
|
|
1269
|
+
if res is not None: return res
|
|
1270
|
+
except CoercionException:
|
|
1271
|
+
self._record_exception()
|
|
1272
|
+
|
|
1273
|
+
try:
|
|
1274
|
+
if hasattr(y, '_act_on_'):
|
|
1275
|
+
res = y._act_on_(x, False)
|
|
1276
|
+
if res is not None: return res
|
|
1277
|
+
except CoercionException:
|
|
1278
|
+
self._record_exception()
|
|
1279
|
+
|
|
1280
|
+
try:
|
|
1281
|
+
if hasattr(y, '_acted_upon_'):
|
|
1282
|
+
res = y._acted_upon_(x, False)
|
|
1283
|
+
if res is not None: return res
|
|
1284
|
+
except CoercionException:
|
|
1285
|
+
self._record_exception()
|
|
1286
|
+
|
|
1287
|
+
if not isinstance(y, Element):
|
|
1288
|
+
op_name = op.__name__
|
|
1289
|
+
mul_method = getattr(y, '__r%s__' % op_name, None)
|
|
1290
|
+
if mul_method is not None:
|
|
1291
|
+
res = mul_method(x)
|
|
1292
|
+
if res is not None and res is not NotImplemented:
|
|
1293
|
+
return res
|
|
1294
|
+
|
|
1295
|
+
# We should really include the underlying error.
|
|
1296
|
+
# This causes so much headache.
|
|
1297
|
+
raise bin_op_exception(op, x, y)
|
|
1298
|
+
|
|
1299
|
+
cpdef canonical_coercion(self, x, y):
|
|
1300
|
+
r"""
|
|
1301
|
+
Given two elements `x` and `y`, with parents `S` and `R` respectively,
|
|
1302
|
+
find a common parent `Z` such that there are coercions
|
|
1303
|
+
`f: S \to Z` and `g: R \to Z` and return `f(x), g(y)`,
|
|
1304
|
+
which will have the same parent.
|
|
1305
|
+
|
|
1306
|
+
Raises a type error if no such `Z` can be found.
|
|
1307
|
+
|
|
1308
|
+
EXAMPLES::
|
|
1309
|
+
|
|
1310
|
+
sage: cm = sage.structure.element.get_coercion_model()
|
|
1311
|
+
sage: cm.canonical_coercion(mod(2, 10), 17)
|
|
1312
|
+
(2, 7)
|
|
1313
|
+
|
|
1314
|
+
sage: # needs sage.modules
|
|
1315
|
+
sage: x, y = cm.canonical_coercion(1/2, matrix(ZZ, 2, 2, range(4)))
|
|
1316
|
+
sage: x
|
|
1317
|
+
[1/2 0]
|
|
1318
|
+
[ 0 1/2]
|
|
1319
|
+
sage: y
|
|
1320
|
+
[0 1]
|
|
1321
|
+
[2 3]
|
|
1322
|
+
sage: parent(x) is parent(y)
|
|
1323
|
+
True
|
|
1324
|
+
|
|
1325
|
+
There is some support for non-Sage datatypes as well::
|
|
1326
|
+
|
|
1327
|
+
sage: x, y = cm.canonical_coercion(int(5), 10)
|
|
1328
|
+
sage: type(x), type(y)
|
|
1329
|
+
(<class 'sage.rings.integer.Integer'>, <class 'sage.rings.integer.Integer'>)
|
|
1330
|
+
|
|
1331
|
+
sage: x, y = cm.canonical_coercion(int(5), complex(3))
|
|
1332
|
+
sage: type(x), type(y)
|
|
1333
|
+
(<class 'complex'>, <class 'complex'>)
|
|
1334
|
+
|
|
1335
|
+
sage: class MyClass:
|
|
1336
|
+
....: def _sage_(self):
|
|
1337
|
+
....: return 13
|
|
1338
|
+
sage: a, b = cm.canonical_coercion(MyClass(), 1/3)
|
|
1339
|
+
sage: a, b
|
|
1340
|
+
(13, 1/3)
|
|
1341
|
+
sage: type(a)
|
|
1342
|
+
<class 'sage.rings.rational.Rational'>
|
|
1343
|
+
|
|
1344
|
+
We also make an exception for 0, even if `\ZZ` does not map in::
|
|
1345
|
+
|
|
1346
|
+
sage: canonical_coercion(vector([1, 2, 3]), 0) # needs sage.modules
|
|
1347
|
+
((1, 2, 3), (0, 0, 0))
|
|
1348
|
+
sage: canonical_coercion(GF(5)(0), float(0))
|
|
1349
|
+
(0, 0)
|
|
1350
|
+
"""
|
|
1351
|
+
xp = parent(x)
|
|
1352
|
+
yp = parent(y)
|
|
1353
|
+
if xp is yp:
|
|
1354
|
+
return x,y
|
|
1355
|
+
|
|
1356
|
+
cdef Element x_elt, y_elt
|
|
1357
|
+
coercions = self.coercion_maps(xp, yp)
|
|
1358
|
+
if coercions is not None:
|
|
1359
|
+
x_map, y_map = coercions
|
|
1360
|
+
if x_map is not None:
|
|
1361
|
+
x_elt = (<Map>x_map)._call_(x)
|
|
1362
|
+
else:
|
|
1363
|
+
x_elt = x
|
|
1364
|
+
if y_map is not None:
|
|
1365
|
+
y_elt = (<Map>y_map)._call_(y)
|
|
1366
|
+
else:
|
|
1367
|
+
y_elt = y
|
|
1368
|
+
if x_elt is None:
|
|
1369
|
+
raise RuntimeError("BUG in map, returned None %s %s %s" % (x, type(x_map), x_map))
|
|
1370
|
+
elif y_elt is None:
|
|
1371
|
+
raise RuntimeError("BUG in map, returned None %s %s %s" % (y, type(y_map), y_map))
|
|
1372
|
+
if x_elt._parent is y_elt._parent:
|
|
1373
|
+
# We must verify this as otherwise we are prone to
|
|
1374
|
+
# getting into an infinite loop in c, and the above
|
|
1375
|
+
# maps may be written by (imperfect) users.
|
|
1376
|
+
return x_elt,y_elt
|
|
1377
|
+
elif x_elt._parent == y_elt._parent:
|
|
1378
|
+
# TODO: Non-uniqueness of parents strikes again!
|
|
1379
|
+
y_elt = parent(x_elt)(y_elt)
|
|
1380
|
+
if x_elt._parent is y_elt._parent:
|
|
1381
|
+
return x_elt,y_elt
|
|
1382
|
+
self._coercion_error(x, x_map, x_elt, y, y_map, y_elt)
|
|
1383
|
+
|
|
1384
|
+
cdef bint x_numeric = isinstance(x, (int, float, complex))
|
|
1385
|
+
cdef bint y_numeric = isinstance(y, (int, float, complex))
|
|
1386
|
+
|
|
1387
|
+
if not x_numeric and is_numpy_type(type(x)):
|
|
1388
|
+
import numpy
|
|
1389
|
+
x_numeric = isinstance(x, numpy.number)
|
|
1390
|
+
if not y_numeric and is_numpy_type(type(y)):
|
|
1391
|
+
import numpy
|
|
1392
|
+
y_numeric = isinstance(y, numpy.number)
|
|
1393
|
+
|
|
1394
|
+
if x_numeric and y_numeric:
|
|
1395
|
+
ty = type(x + y)
|
|
1396
|
+
return ty(x), ty(y)
|
|
1397
|
+
|
|
1398
|
+
# Now handle the native python + sage object cases
|
|
1399
|
+
# that were not taken care of above.
|
|
1400
|
+
elif x_numeric:
|
|
1401
|
+
try:
|
|
1402
|
+
sage_parent = py_scalar_parent(type(x))
|
|
1403
|
+
if sage_parent is None or sage_parent.has_coerce_map_from(yp):
|
|
1404
|
+
return x, x.__class__(y)
|
|
1405
|
+
else:
|
|
1406
|
+
return self.canonical_coercion(sage_parent(x), y)
|
|
1407
|
+
except (TypeError, ValueError):
|
|
1408
|
+
self._record_exception()
|
|
1409
|
+
|
|
1410
|
+
elif y_numeric:
|
|
1411
|
+
try:
|
|
1412
|
+
sage_parent = py_scalar_parent(type(y))
|
|
1413
|
+
if sage_parent is None or sage_parent.has_coerce_map_from(xp):
|
|
1414
|
+
return y.__class__(x), y
|
|
1415
|
+
else:
|
|
1416
|
+
return self.canonical_coercion(x, sage_parent(y))
|
|
1417
|
+
except (TypeError, ValueError):
|
|
1418
|
+
self._record_exception()
|
|
1419
|
+
|
|
1420
|
+
# See if the non-objects define a _sage_ method.
|
|
1421
|
+
if not isinstance(x, SageObject) or not isinstance(y, SageObject):
|
|
1422
|
+
try:
|
|
1423
|
+
x = x._sage_()
|
|
1424
|
+
y = y._sage_()
|
|
1425
|
+
except AttributeError:
|
|
1426
|
+
self._record_exception()
|
|
1427
|
+
else:
|
|
1428
|
+
return self.canonical_coercion(x, y)
|
|
1429
|
+
|
|
1430
|
+
# Allow coercion of 0 even if no coercion from Z
|
|
1431
|
+
if (x_numeric or is_Integer(x)) and not x and type(yp) is not type:
|
|
1432
|
+
try:
|
|
1433
|
+
return yp(0), y
|
|
1434
|
+
except Exception:
|
|
1435
|
+
self._record_exception()
|
|
1436
|
+
|
|
1437
|
+
if (y_numeric or is_Integer(y)) and not y and type(xp) is not type:
|
|
1438
|
+
try:
|
|
1439
|
+
return x, xp(0)
|
|
1440
|
+
except Exception:
|
|
1441
|
+
self._record_exception()
|
|
1442
|
+
|
|
1443
|
+
raise TypeError("no common canonical parent for objects with parents: '%s' and '%s'" % (xp, yp))
|
|
1444
|
+
|
|
1445
|
+
cpdef coercion_maps(self, R, S):
|
|
1446
|
+
r"""
|
|
1447
|
+
Give two parents `R` and `S`, return a pair of coercion maps
|
|
1448
|
+
`f: R \rightarrow Z` and `g: S \rightarrow Z` , if such a `Z`
|
|
1449
|
+
can be found.
|
|
1450
|
+
|
|
1451
|
+
In the (common) case that `R=Z` or `S=Z` then ``None`` is returned
|
|
1452
|
+
for `f` or `g` respectively rather than constructing (and subsequently
|
|
1453
|
+
calling) the identity morphism.
|
|
1454
|
+
|
|
1455
|
+
If no suitable `f, g` can be found, a single ``None`` is returned.
|
|
1456
|
+
This result is cached.
|
|
1457
|
+
|
|
1458
|
+
.. NOTE::
|
|
1459
|
+
|
|
1460
|
+
By :issue:`14711`, coerce maps should be copied when using them
|
|
1461
|
+
outside of the coercion system, because they may become defunct
|
|
1462
|
+
by garbage collection.
|
|
1463
|
+
|
|
1464
|
+
EXAMPLES::
|
|
1465
|
+
|
|
1466
|
+
sage: cm = sage.structure.element.get_coercion_model()
|
|
1467
|
+
sage: f, g = cm.coercion_maps(ZZ, QQ)
|
|
1468
|
+
sage: print(copy(f))
|
|
1469
|
+
Natural morphism:
|
|
1470
|
+
From: Integer Ring
|
|
1471
|
+
To: Rational Field
|
|
1472
|
+
sage: print(g)
|
|
1473
|
+
None
|
|
1474
|
+
|
|
1475
|
+
sage: ZZx = ZZ['x']
|
|
1476
|
+
sage: f, g = cm.coercion_maps(ZZx, QQ)
|
|
1477
|
+
sage: print(f)
|
|
1478
|
+
(map internal to coercion system -- copy before use)
|
|
1479
|
+
Ring morphism:
|
|
1480
|
+
From: Univariate Polynomial Ring in x over Integer Ring
|
|
1481
|
+
To: Univariate Polynomial Ring in x over Rational Field
|
|
1482
|
+
sage: print(g)
|
|
1483
|
+
(map internal to coercion system -- copy before use)
|
|
1484
|
+
Polynomial base injection morphism:
|
|
1485
|
+
From: Rational Field
|
|
1486
|
+
To: Univariate Polynomial Ring in x over Rational Field
|
|
1487
|
+
|
|
1488
|
+
sage: K = GF(7)
|
|
1489
|
+
sage: cm.coercion_maps(QQ, K) is None
|
|
1490
|
+
True
|
|
1491
|
+
|
|
1492
|
+
Note that to break symmetry, if there is a coercion map in both
|
|
1493
|
+
directions, the parent on the left is used::
|
|
1494
|
+
|
|
1495
|
+
sage: # needs sage.modules
|
|
1496
|
+
sage: V = QQ^3
|
|
1497
|
+
sage: W = V.__class__(QQ, 3)
|
|
1498
|
+
sage: V == W
|
|
1499
|
+
True
|
|
1500
|
+
sage: V is W
|
|
1501
|
+
False
|
|
1502
|
+
sage: cm = sage.structure.element.get_coercion_model()
|
|
1503
|
+
sage: cm.coercion_maps(V, W)
|
|
1504
|
+
(None,
|
|
1505
|
+
(map internal to coercion system -- copy before use)
|
|
1506
|
+
Coercion map:
|
|
1507
|
+
From: Vector space of dimension 3 over Rational Field
|
|
1508
|
+
To: Vector space of dimension 3 over Rational Field)
|
|
1509
|
+
sage: cm.coercion_maps(W, V)
|
|
1510
|
+
(None,
|
|
1511
|
+
(map internal to coercion system -- copy before use)
|
|
1512
|
+
Coercion map:
|
|
1513
|
+
From: Vector space of dimension 3 over Rational Field
|
|
1514
|
+
To: Vector space of dimension 3 over Rational Field)
|
|
1515
|
+
sage: v = V([1,2,3])
|
|
1516
|
+
sage: w = W([1,2,3])
|
|
1517
|
+
sage: parent(v + w) is V
|
|
1518
|
+
True
|
|
1519
|
+
sage: parent(w + v) is W
|
|
1520
|
+
True
|
|
1521
|
+
|
|
1522
|
+
TESTS:
|
|
1523
|
+
|
|
1524
|
+
We check that with :issue:`14058`, parents are still eligible for
|
|
1525
|
+
garbage collection after being involved in binary operations::
|
|
1526
|
+
|
|
1527
|
+
sage: # needs sage.libs.pari
|
|
1528
|
+
sage: import gc
|
|
1529
|
+
sage: T = type(GF(2))
|
|
1530
|
+
sage: gc.collect() # random
|
|
1531
|
+
852
|
|
1532
|
+
sage: N0 = len(list(o for o in gc.get_objects() if type(o) is T))
|
|
1533
|
+
sage: L = [ZZ(1) + GF(p)(1) for p in prime_range(2, 50)]
|
|
1534
|
+
sage: N1 = len(list(o for o in gc.get_objects() if type(o) is T))
|
|
1535
|
+
sage: N1 > N0
|
|
1536
|
+
True
|
|
1537
|
+
sage: del L
|
|
1538
|
+
sage: gc.collect() # random
|
|
1539
|
+
3939
|
|
1540
|
+
sage: N2 = len(list(o for o in gc.get_objects() if type(o) is T))
|
|
1541
|
+
sage: N2 - N0
|
|
1542
|
+
0
|
|
1543
|
+
"""
|
|
1544
|
+
try:
|
|
1545
|
+
refs = self._coercion_maps.get(R, S, None)
|
|
1546
|
+
if refs is None:
|
|
1547
|
+
return None
|
|
1548
|
+
R_map_ref, S_map_ref = refs
|
|
1549
|
+
if R_map_ref is None:
|
|
1550
|
+
S_map = <object>PyWeakref_GET_OBJECT(S_map_ref)
|
|
1551
|
+
if S_map is not None:
|
|
1552
|
+
return None, S_map
|
|
1553
|
+
elif S_map_ref is None:
|
|
1554
|
+
R_map = <object>PyWeakref_GET_OBJECT(R_map_ref)
|
|
1555
|
+
if R_map is not None:
|
|
1556
|
+
return R_map, None
|
|
1557
|
+
else:
|
|
1558
|
+
R_map = <object>PyWeakref_GET_OBJECT(R_map_ref)
|
|
1559
|
+
S_map = <object>PyWeakref_GET_OBJECT(S_map_ref)
|
|
1560
|
+
if R_map is not None and S_map is not None:
|
|
1561
|
+
return R_map, S_map
|
|
1562
|
+
except KeyError:
|
|
1563
|
+
pass
|
|
1564
|
+
homs = self.discover_coercion(R, S)
|
|
1565
|
+
if 0:
|
|
1566
|
+
# This breaks too many things that are going to change
|
|
1567
|
+
# in the new coercion model anyways.
|
|
1568
|
+
# COERCE TODO: Enable it then.
|
|
1569
|
+
homs = self.verify_coercion_maps(R, S, homs)
|
|
1570
|
+
else:
|
|
1571
|
+
if homs is not None:
|
|
1572
|
+
x_map, y_map = homs
|
|
1573
|
+
if x_map is not None and not isinstance(x_map, Map):
|
|
1574
|
+
raise RuntimeError("BUG in coercion model: coerce_map_from must return a Map")
|
|
1575
|
+
if y_map is not None and not isinstance(y_map, Map):
|
|
1576
|
+
raise RuntimeError("BUG in coercion model: coerce_map_from must return a Map")
|
|
1577
|
+
if homs is None:
|
|
1578
|
+
refs = None
|
|
1579
|
+
swap = None
|
|
1580
|
+
else:
|
|
1581
|
+
R_map, S_map = homs
|
|
1582
|
+
R_map_ref = None if R_map is None else PyWeakref_NewRef(R_map, None)
|
|
1583
|
+
S_map_ref = None if S_map is None else PyWeakref_NewRef(S_map, None)
|
|
1584
|
+
refs = R_map_ref, S_map_ref
|
|
1585
|
+
if R_map is None and isinstance(S, Parent) and (<Parent>S).has_coerce_map_from(R):
|
|
1586
|
+
swap = None, PyWeakref_NewRef((<Parent>S).coerce_map_from(R), None)
|
|
1587
|
+
else:
|
|
1588
|
+
swap = S_map_ref, R_map_ref
|
|
1589
|
+
self._coercion_maps.set(R, S, None, refs)
|
|
1590
|
+
self._coercion_maps.set(S, R, None, swap)
|
|
1591
|
+
return homs
|
|
1592
|
+
|
|
1593
|
+
cpdef verify_coercion_maps(self, R, S, homs, bint fix=False):
|
|
1594
|
+
"""
|
|
1595
|
+
Make sure this is a valid pair of homomorphisms from `R` and `S` to a common parent.
|
|
1596
|
+
This function is used to protect the user against buggy parents.
|
|
1597
|
+
|
|
1598
|
+
EXAMPLES::
|
|
1599
|
+
|
|
1600
|
+
sage: cm = sage.structure.element.get_coercion_model()
|
|
1601
|
+
sage: homs = QQ.coerce_map_from(ZZ), None
|
|
1602
|
+
sage: cm.verify_coercion_maps(ZZ, QQ, homs) == homs
|
|
1603
|
+
True
|
|
1604
|
+
sage: homs = QQ.coerce_map_from(ZZ), RR.coerce_map_from(QQ)
|
|
1605
|
+
sage: cm.verify_coercion_maps(ZZ, QQ, homs) == homs
|
|
1606
|
+
Traceback (most recent call last):
|
|
1607
|
+
...
|
|
1608
|
+
RuntimeError: ('BUG in coercion model, codomains must be identical',
|
|
1609
|
+
Natural morphism:
|
|
1610
|
+
From: Integer Ring
|
|
1611
|
+
To: Rational Field,
|
|
1612
|
+
Generic map:
|
|
1613
|
+
From: Rational Field
|
|
1614
|
+
To: Real Field with 53 bits of precision)
|
|
1615
|
+
"""
|
|
1616
|
+
if homs is None:
|
|
1617
|
+
return None
|
|
1618
|
+
cdef Map R_map, S_map
|
|
1619
|
+
R_map, S_map = homs
|
|
1620
|
+
if isinstance(R, type):
|
|
1621
|
+
R = Set_PythonType(R)
|
|
1622
|
+
elif isinstance(S, type):
|
|
1623
|
+
S = Set_PythonType(S)
|
|
1624
|
+
if R_map is None:
|
|
1625
|
+
R_map = IdentityMorphism(R)
|
|
1626
|
+
elif S_map is None:
|
|
1627
|
+
S_map = IdentityMorphism(S)
|
|
1628
|
+
# Make sure the domains are correct
|
|
1629
|
+
if R_map.domain() is not R:
|
|
1630
|
+
if fix:
|
|
1631
|
+
connecting = R_map.domain()._internal_coerce_map_from(R)
|
|
1632
|
+
if connecting is not None:
|
|
1633
|
+
R_map = R_map * connecting
|
|
1634
|
+
if R_map.domain() is not R:
|
|
1635
|
+
raise RuntimeError("BUG in coercion model, left domain must be original parent", R, R_map)
|
|
1636
|
+
if S_map is not None and S_map.domain() is not S:
|
|
1637
|
+
if fix:
|
|
1638
|
+
connecting = S_map.domain()._internal_coerce_map_from(S)
|
|
1639
|
+
if connecting is not None:
|
|
1640
|
+
S_map = S_map * connecting
|
|
1641
|
+
if S_map.domain() is not S:
|
|
1642
|
+
raise RuntimeError("BUG in coercion model, right domain must be original parent", S, S_map)
|
|
1643
|
+
# Make sure the codomains are correct
|
|
1644
|
+
if R_map.codomain() is not S_map.codomain():
|
|
1645
|
+
if fix:
|
|
1646
|
+
connecting = R_map.codomain()._internal_coerce_map_from(S_map.codomain())
|
|
1647
|
+
if connecting is not None:
|
|
1648
|
+
S_map = connecting * S_map
|
|
1649
|
+
else:
|
|
1650
|
+
connecting = S_map.codomain()._internal_coerce_map_from(R_map.codomain())
|
|
1651
|
+
if connecting is not None:
|
|
1652
|
+
R_map = connecting * R_map
|
|
1653
|
+
if R_map.codomain() is not S_map.codomain():
|
|
1654
|
+
raise RuntimeError("BUG in coercion model, codomains must be identical", R_map, S_map)
|
|
1655
|
+
if isinstance(R_map, IdentityMorphism):
|
|
1656
|
+
R_map = None
|
|
1657
|
+
elif isinstance(S_map, IdentityMorphism):
|
|
1658
|
+
S_map = None
|
|
1659
|
+
return R_map, S_map
|
|
1660
|
+
|
|
1661
|
+
cpdef discover_coercion(self, R, S):
|
|
1662
|
+
"""
|
|
1663
|
+
This actually implements the finding of coercion maps as described in
|
|
1664
|
+
the :meth:`coercion_maps` method.
|
|
1665
|
+
|
|
1666
|
+
EXAMPLES::
|
|
1667
|
+
|
|
1668
|
+
sage: cm = sage.structure.element.get_coercion_model()
|
|
1669
|
+
|
|
1670
|
+
If R is S, then two identity morphisms suffice::
|
|
1671
|
+
|
|
1672
|
+
sage: cm.discover_coercion(SR, SR) # needs sage.symbolic
|
|
1673
|
+
(None, None)
|
|
1674
|
+
|
|
1675
|
+
If there is a coercion map either direction, use that::
|
|
1676
|
+
|
|
1677
|
+
sage: cm.discover_coercion(ZZ, QQ)
|
|
1678
|
+
((map internal to coercion system -- copy before use)
|
|
1679
|
+
Natural morphism:
|
|
1680
|
+
From: Integer Ring
|
|
1681
|
+
To: Rational Field, None)
|
|
1682
|
+
sage: cm.discover_coercion(RR, QQ) # needs sage.rings.real_mpfr
|
|
1683
|
+
(None, (map internal to coercion system -- copy before use)
|
|
1684
|
+
Generic map:
|
|
1685
|
+
From: Rational Field
|
|
1686
|
+
To: Real Field with 53 bits of precision)
|
|
1687
|
+
|
|
1688
|
+
Otherwise, try and compute an appropriate cover::
|
|
1689
|
+
|
|
1690
|
+
sage: ZZxy = ZZ['x,y']
|
|
1691
|
+
sage: cm.discover_coercion(ZZxy, RDF)
|
|
1692
|
+
((map internal to coercion system -- copy before use)
|
|
1693
|
+
Call morphism:
|
|
1694
|
+
From: Multivariate Polynomial Ring in x, y over Integer Ring
|
|
1695
|
+
To: Multivariate Polynomial Ring in x, y over Real Double Field,
|
|
1696
|
+
(map internal to coercion system -- copy before use)
|
|
1697
|
+
Polynomial base injection morphism:
|
|
1698
|
+
From: Real Double Field
|
|
1699
|
+
To: Multivariate Polynomial Ring in x, y over Real Double Field)
|
|
1700
|
+
|
|
1701
|
+
Sometimes there is a reasonable "cover," but no canonical coercion::
|
|
1702
|
+
|
|
1703
|
+
sage: sage.categories.pushout.pushout(QQ, QQ^3) # needs sage.modules
|
|
1704
|
+
Vector space of dimension 3 over Rational Field
|
|
1705
|
+
sage: print(cm.discover_coercion(QQ, QQ^3)) # needs sage.modules
|
|
1706
|
+
None
|
|
1707
|
+
"""
|
|
1708
|
+
if R is S:
|
|
1709
|
+
return None, None
|
|
1710
|
+
|
|
1711
|
+
# See if there is a natural coercion from R to S
|
|
1712
|
+
if isinstance(R, Parent):
|
|
1713
|
+
mor = (<Parent>R)._internal_coerce_map_from(S)
|
|
1714
|
+
if mor is not None:
|
|
1715
|
+
return None, mor
|
|
1716
|
+
|
|
1717
|
+
# See if there is a natural coercion from S to R
|
|
1718
|
+
if isinstance(S, Parent):
|
|
1719
|
+
mor = (<Parent>S)._internal_coerce_map_from(R)
|
|
1720
|
+
if mor is not None:
|
|
1721
|
+
return mor, None
|
|
1722
|
+
|
|
1723
|
+
# Try base extending
|
|
1724
|
+
if isinstance(R, Parent) and isinstance(S, Parent):
|
|
1725
|
+
from sage.categories.pushout import pushout
|
|
1726
|
+
try:
|
|
1727
|
+
Z = pushout(R, S)
|
|
1728
|
+
coerce_R = Z._internal_coerce_map_from(R)
|
|
1729
|
+
coerce_S = Z._internal_coerce_map_from(S)
|
|
1730
|
+
if coerce_R is None:
|
|
1731
|
+
raise TypeError("No coercion from %s to pushout %s" % (R, Z))
|
|
1732
|
+
if coerce_S is None:
|
|
1733
|
+
raise TypeError("No coercion from %s to pushout %s" % (S, Z))
|
|
1734
|
+
return coerce_R, coerce_S
|
|
1735
|
+
except Exception:
|
|
1736
|
+
self._record_exception()
|
|
1737
|
+
|
|
1738
|
+
return None
|
|
1739
|
+
|
|
1740
|
+
cpdef get_action(self, R, S, op=mul, r=None, s=None):
|
|
1741
|
+
"""
|
|
1742
|
+
Get the action of R on S or S on R associated to the operation op.
|
|
1743
|
+
|
|
1744
|
+
EXAMPLES::
|
|
1745
|
+
|
|
1746
|
+
sage: cm = sage.structure.element.get_coercion_model()
|
|
1747
|
+
sage: ZZx = ZZ['x']
|
|
1748
|
+
sage: cm.get_action(ZZx, ZZ, operator.mul)
|
|
1749
|
+
Right scalar multiplication by Integer Ring
|
|
1750
|
+
on Univariate Polynomial Ring in x over Integer Ring
|
|
1751
|
+
sage: cm.get_action(ZZx, QQ, operator.mul)
|
|
1752
|
+
Right scalar multiplication by Rational Field
|
|
1753
|
+
on Univariate Polynomial Ring in x over Integer Ring
|
|
1754
|
+
sage: QQx = QQ['x']
|
|
1755
|
+
sage: cm.get_action(QQx, int, operator.mul)
|
|
1756
|
+
Right scalar multiplication by Integer Ring
|
|
1757
|
+
on Univariate Polynomial Ring in x over Rational Field
|
|
1758
|
+
with precomposition on right by Native morphism:
|
|
1759
|
+
From: Set of Python objects of class 'int'
|
|
1760
|
+
To: Integer Ring
|
|
1761
|
+
|
|
1762
|
+
sage: A = cm.get_action(QQx, ZZ, operator.truediv); A
|
|
1763
|
+
Right inverse action by Rational Field
|
|
1764
|
+
on Univariate Polynomial Ring in x over Rational Field
|
|
1765
|
+
with precomposition on right by Natural morphism:
|
|
1766
|
+
From: Integer Ring
|
|
1767
|
+
To: Rational Field
|
|
1768
|
+
sage: x = QQx.gen()
|
|
1769
|
+
sage: A(x+10, 5)
|
|
1770
|
+
1/5*x + 2
|
|
1771
|
+
"""
|
|
1772
|
+
try:
|
|
1773
|
+
return self._action_maps.get(R, S, op)
|
|
1774
|
+
except KeyError:
|
|
1775
|
+
pass
|
|
1776
|
+
action = self.discover_action(R, S, op, r, s)
|
|
1777
|
+
action = self.verify_action(action, R, S, op)
|
|
1778
|
+
self._action_maps.set(R, S, op, action)
|
|
1779
|
+
return action
|
|
1780
|
+
|
|
1781
|
+
cpdef verify_action(self, action, R, S, op, bint fix=True):
|
|
1782
|
+
r"""
|
|
1783
|
+
Verify that ``action`` takes an element of R on the left and S
|
|
1784
|
+
on the right, raising an error if not.
|
|
1785
|
+
|
|
1786
|
+
This is used for consistency checking in the coercion model.
|
|
1787
|
+
|
|
1788
|
+
EXAMPLES::
|
|
1789
|
+
|
|
1790
|
+
sage: R.<x> = ZZ['x']
|
|
1791
|
+
sage: cm = sage.structure.element.get_coercion_model()
|
|
1792
|
+
sage: cm.verify_action(R.get_action(QQ), R, QQ, operator.mul)
|
|
1793
|
+
Right scalar multiplication by Rational Field
|
|
1794
|
+
on Univariate Polynomial Ring in x over Integer Ring
|
|
1795
|
+
sage: cm.verify_action(R.get_action(QQ), RDF, R, operator.mul)
|
|
1796
|
+
Traceback (most recent call last):
|
|
1797
|
+
...
|
|
1798
|
+
RuntimeError: There is a BUG in the coercion model:
|
|
1799
|
+
Action found for R <built-in function mul> S does not have the correct domains
|
|
1800
|
+
R = Real Double Field
|
|
1801
|
+
S = Univariate Polynomial Ring in x over Integer Ring
|
|
1802
|
+
(should be Univariate Polynomial Ring in x over Integer Ring, Rational Field)
|
|
1803
|
+
action = Right scalar multiplication by Rational Field
|
|
1804
|
+
on Univariate Polynomial Ring in x over Integer Ring
|
|
1805
|
+
(<class 'sage.structure.coerce_actions.RightModuleAction'>)
|
|
1806
|
+
"""
|
|
1807
|
+
if action is None:
|
|
1808
|
+
return action
|
|
1809
|
+
cdef bint ok = True
|
|
1810
|
+
try:
|
|
1811
|
+
if action.left_domain() is not R:
|
|
1812
|
+
ok &= isinstance(R, type) and action.left_domain()._type is R
|
|
1813
|
+
if action.right_domain() is not S:
|
|
1814
|
+
ok &= isinstance(S, type) and action.right_domain()._type is S
|
|
1815
|
+
except AttributeError:
|
|
1816
|
+
ok = False
|
|
1817
|
+
if not ok:
|
|
1818
|
+
if isinstance(R, type):
|
|
1819
|
+
R = Set_PythonType(R)
|
|
1820
|
+
if isinstance(S, type):
|
|
1821
|
+
S = Set_PythonType(S)
|
|
1822
|
+
|
|
1823
|
+
# Non-unique parents
|
|
1824
|
+
if fix and action.left_domain() is not R and action.left_domain() == R:
|
|
1825
|
+
action = PrecomposedAction(action, action.left_domain()._internal_coerce_map_from(R), None)
|
|
1826
|
+
if fix and action.right_domain() is not S and action.right_domain() == S:
|
|
1827
|
+
action = PrecomposedAction(action, None, action.right_domain()._internal_coerce_map_from(S))
|
|
1828
|
+
|
|
1829
|
+
if action.left_domain() is not R or action.right_domain() is not S:
|
|
1830
|
+
raise RuntimeError("""There is a BUG in the coercion model:
|
|
1831
|
+
Action found for R %s S does not have the correct domains
|
|
1832
|
+
R = %s
|
|
1833
|
+
S = %s
|
|
1834
|
+
(should be %s, %s)
|
|
1835
|
+
action = %s (%s)
|
|
1836
|
+
""" % (op, R, S, action.left_domain(), action.right_domain(), action, type(action)))
|
|
1837
|
+
|
|
1838
|
+
return action
|
|
1839
|
+
|
|
1840
|
+
cpdef discover_action(self, R, S, op, r=None, s=None):
|
|
1841
|
+
"""
|
|
1842
|
+
INPUT:
|
|
1843
|
+
|
|
1844
|
+
- ``R`` -- the left :class:`Parent` (or type)
|
|
1845
|
+
- ``S`` -- the right :class:`Parent` (or type)
|
|
1846
|
+
- ``op`` -- the operand, typically an element of the :mod:`operator` module
|
|
1847
|
+
- ``r`` -- (optional) element of `R`
|
|
1848
|
+
- ``s`` -- (optional) element of `S`
|
|
1849
|
+
|
|
1850
|
+
OUTPUT: an action `A` such that `s` ``op`` `r` is given by `A(s,r)`
|
|
1851
|
+
|
|
1852
|
+
The steps taken are illustrated below.
|
|
1853
|
+
|
|
1854
|
+
EXAMPLES::
|
|
1855
|
+
|
|
1856
|
+
sage: P.<x> = ZZ['x']
|
|
1857
|
+
sage: P.get_action(ZZ)
|
|
1858
|
+
Right scalar multiplication by Integer Ring on
|
|
1859
|
+
Univariate Polynomial Ring in x over Integer Ring
|
|
1860
|
+
sage: ZZ.get_action(P) is None
|
|
1861
|
+
True
|
|
1862
|
+
sage: cm = sage.structure.element.get_coercion_model()
|
|
1863
|
+
|
|
1864
|
+
If `R` or `S` is a :class:`Parent`, ask it for an action by/on `R`::
|
|
1865
|
+
|
|
1866
|
+
sage: cm.discover_action(ZZ, P, operator.mul)
|
|
1867
|
+
Left scalar multiplication by Integer Ring on
|
|
1868
|
+
Univariate Polynomial Ring in x over Integer Ring
|
|
1869
|
+
|
|
1870
|
+
If `R` or `S` a type, recursively call :meth:`get_action`
|
|
1871
|
+
with the Sage versions of `R` and/or `S`::
|
|
1872
|
+
|
|
1873
|
+
sage: cm.discover_action(P, int, operator.mul)
|
|
1874
|
+
Right scalar multiplication by Integer Ring on
|
|
1875
|
+
Univariate Polynomial Ring in x over Integer Ring
|
|
1876
|
+
with precomposition on right by Native morphism:
|
|
1877
|
+
From: Set of Python objects of class 'int'
|
|
1878
|
+
To: Integer Ring
|
|
1879
|
+
|
|
1880
|
+
If ``op`` is division, look for action on ``right`` by inverse::
|
|
1881
|
+
|
|
1882
|
+
sage: cm.discover_action(P, ZZ, operator.truediv)
|
|
1883
|
+
Right inverse action by Rational Field on
|
|
1884
|
+
Univariate Polynomial Ring in x over Integer Ring
|
|
1885
|
+
with precomposition on right by Natural morphism:
|
|
1886
|
+
From: Integer Ring
|
|
1887
|
+
To: Rational Field
|
|
1888
|
+
|
|
1889
|
+
Check that :issue:`17740` is fixed::
|
|
1890
|
+
|
|
1891
|
+
sage: R = GF(5)['x']
|
|
1892
|
+
sage: cm.discover_action(R, ZZ, operator.truediv)
|
|
1893
|
+
Right inverse action by Finite Field of size 5
|
|
1894
|
+
on Univariate Polynomial Ring in x over Finite Field of size 5
|
|
1895
|
+
with precomposition on right by Natural morphism:
|
|
1896
|
+
From: Integer Ring
|
|
1897
|
+
To: Finite Field of size 5
|
|
1898
|
+
sage: cm.bin_op(R.gen(), 7, operator.truediv).parent()
|
|
1899
|
+
Univariate Polynomial Ring in x over Finite Field of size 5
|
|
1900
|
+
|
|
1901
|
+
Check that :issue:`18221` is fixed::
|
|
1902
|
+
|
|
1903
|
+
sage: # needs sage.combinat sage.modules
|
|
1904
|
+
sage: F.<x> = FreeAlgebra(QQ)
|
|
1905
|
+
sage: x / 2
|
|
1906
|
+
1/2*x
|
|
1907
|
+
sage: cm.discover_action(F, ZZ, operator.truediv)
|
|
1908
|
+
Right inverse action by Rational Field on
|
|
1909
|
+
Free Algebra on 1 generator (x,) over Rational Field
|
|
1910
|
+
with precomposition on right by Natural morphism:
|
|
1911
|
+
From: Integer Ring
|
|
1912
|
+
To: Rational Field
|
|
1913
|
+
"""
|
|
1914
|
+
if isinstance(R, Parent):
|
|
1915
|
+
action = (<Parent>R).get_action(S, op, True, r, s)
|
|
1916
|
+
if action is not None:
|
|
1917
|
+
return action
|
|
1918
|
+
|
|
1919
|
+
if isinstance(S, Parent):
|
|
1920
|
+
action = (<Parent>S).get_action(R, op, False, s, r)
|
|
1921
|
+
if action is not None:
|
|
1922
|
+
return action
|
|
1923
|
+
|
|
1924
|
+
if type(R) is type:
|
|
1925
|
+
sageR = py_scalar_parent(R)
|
|
1926
|
+
if sageR is not None:
|
|
1927
|
+
action = self.get_action(sageR, S, op, s=s)
|
|
1928
|
+
if action is not None:
|
|
1929
|
+
return PrecomposedAction(action, sageR._internal_coerce_map_from(R), None)
|
|
1930
|
+
|
|
1931
|
+
if type(S) is type:
|
|
1932
|
+
sageS = py_scalar_parent(S)
|
|
1933
|
+
if sageS is not None:
|
|
1934
|
+
action = self.get_action(R, sageS, op, r=r)
|
|
1935
|
+
if action is not None:
|
|
1936
|
+
return PrecomposedAction(action, None, sageS._internal_coerce_map_from(S))
|
|
1937
|
+
|
|
1938
|
+
if op is truediv:
|
|
1939
|
+
# Division on right is the same acting on right by inverse, if it is so defined.
|
|
1940
|
+
right_mul = None
|
|
1941
|
+
try:
|
|
1942
|
+
right_mul = self.get_action(R, S, mul)
|
|
1943
|
+
except NotImplementedError:
|
|
1944
|
+
self._record_exception()
|
|
1945
|
+
|
|
1946
|
+
if right_mul is not None and not right_mul.is_left():
|
|
1947
|
+
try:
|
|
1948
|
+
action = ~right_mul
|
|
1949
|
+
if action.right_domain() != S:
|
|
1950
|
+
action = PrecomposedAction(action, None,
|
|
1951
|
+
action.right_domain()._internal_coerce_map_from(S))
|
|
1952
|
+
return action
|
|
1953
|
+
except TypeError: # action may not be invertible
|
|
1954
|
+
self._record_exception()
|
|
1955
|
+
|
|
1956
|
+
# It's possible an action is defined on the fraction field itself.
|
|
1957
|
+
try:
|
|
1958
|
+
K = S._pseudo_fraction_field()
|
|
1959
|
+
except AttributeError:
|
|
1960
|
+
pass
|
|
1961
|
+
else:
|
|
1962
|
+
if K is not S:
|
|
1963
|
+
try:
|
|
1964
|
+
right_mul = self.get_action(R, K, mul)
|
|
1965
|
+
except NotImplementedError:
|
|
1966
|
+
self._record_exception()
|
|
1967
|
+
|
|
1968
|
+
if right_mul is not None and not right_mul.is_left():
|
|
1969
|
+
try:
|
|
1970
|
+
return PrecomposedAction(~right_mul, None, K.coerce_map_from(S))
|
|
1971
|
+
except TypeError: # action may not be invertible
|
|
1972
|
+
self._record_exception()
|
|
1973
|
+
|
|
1974
|
+
return None
|
|
1975
|
+
|
|
1976
|
+
cpdef richcmp(self, x, y, int op):
|
|
1977
|
+
"""
|
|
1978
|
+
Given two arbitrary objects ``x`` and ``y``, coerce them to
|
|
1979
|
+
a common parent and compare them using rich comparison operator
|
|
1980
|
+
``op``.
|
|
1981
|
+
|
|
1982
|
+
EXAMPLES::
|
|
1983
|
+
|
|
1984
|
+
sage: from sage.structure.element import get_coercion_model
|
|
1985
|
+
sage: from sage.structure.richcmp import op_LT, op_LE, op_EQ, op_NE, op_GT, op_GE
|
|
1986
|
+
sage: richcmp = get_coercion_model().richcmp
|
|
1987
|
+
sage: richcmp(None, None, op_EQ)
|
|
1988
|
+
True
|
|
1989
|
+
sage: richcmp(None, 1, op_LT)
|
|
1990
|
+
True
|
|
1991
|
+
sage: richcmp("hello", None, op_LE)
|
|
1992
|
+
False
|
|
1993
|
+
sage: richcmp(-1, 1, op_GE)
|
|
1994
|
+
False
|
|
1995
|
+
sage: richcmp(int(1), float(2), op_GE)
|
|
1996
|
+
False
|
|
1997
|
+
|
|
1998
|
+
If there is no coercion, we only support ``==`` and ``!=``::
|
|
1999
|
+
|
|
2000
|
+
sage: x = QQ.one(); y = GF(2).one()
|
|
2001
|
+
sage: richcmp(x, y, op_EQ)
|
|
2002
|
+
False
|
|
2003
|
+
sage: richcmp(x, y, op_NE)
|
|
2004
|
+
True
|
|
2005
|
+
sage: richcmp(x, y, op_GT)
|
|
2006
|
+
Traceback (most recent call last):
|
|
2007
|
+
...
|
|
2008
|
+
TypeError: unsupported operand parent(s) for >:
|
|
2009
|
+
'Rational Field' and 'Finite Field of size 2'
|
|
2010
|
+
|
|
2011
|
+
We support non-Sage types with the usual Python convention::
|
|
2012
|
+
|
|
2013
|
+
sage: class AlwaysEqual():
|
|
2014
|
+
....: def __eq__(self, other):
|
|
2015
|
+
....: return True
|
|
2016
|
+
sage: x = AlwaysEqual()
|
|
2017
|
+
sage: x == 1
|
|
2018
|
+
True
|
|
2019
|
+
sage: 1 == x
|
|
2020
|
+
True
|
|
2021
|
+
"""
|
|
2022
|
+
# Some very special cases
|
|
2023
|
+
if x is None or x is Ellipsis:
|
|
2024
|
+
return rich_to_bool(op, 0 if x is y else -1)
|
|
2025
|
+
if y is None or y is Ellipsis:
|
|
2026
|
+
return rich_to_bool(op, 0 if x is y else 1)
|
|
2027
|
+
|
|
2028
|
+
cdef bint y_is_Element = isinstance(y, Element)
|
|
2029
|
+
|
|
2030
|
+
# Check for manual __richcmp__ override (only on y since
|
|
2031
|
+
# x.__richcmp__ would already have been called)
|
|
2032
|
+
if y_is_Element:
|
|
2033
|
+
if (<Element>y)._parent.get_flag(Parent_richcmp_element_without_coercion):
|
|
2034
|
+
return Py_TYPE(y).tp_richcompare(y, x, revop(op))
|
|
2035
|
+
|
|
2036
|
+
# Coerce to a common parent
|
|
2037
|
+
try:
|
|
2038
|
+
x, y = self.canonical_coercion(x, y)
|
|
2039
|
+
except (TypeError, NotImplementedError):
|
|
2040
|
+
pass
|
|
2041
|
+
else:
|
|
2042
|
+
# The common parent should not be one which explicitly
|
|
2043
|
+
# asked to *not* use coercion for comparisons.
|
|
2044
|
+
assert not (isinstance(x, Element) and
|
|
2045
|
+
(<Element>x)._parent.get_flag(Parent_richcmp_element_without_coercion))
|
|
2046
|
+
return PyObject_RichCompare(x, y, op)
|
|
2047
|
+
|
|
2048
|
+
# Comparing with coercion didn't work, try something else.
|
|
2049
|
+
|
|
2050
|
+
# Try y.__richcmp__(x, revop) where revop is the reversed
|
|
2051
|
+
# operation (<= becomes >=).
|
|
2052
|
+
# This only makes sense when y is not a Sage Element, otherwise
|
|
2053
|
+
# we would end up trying the same coercion again.
|
|
2054
|
+
if not y_is_Element and Py_TYPE(y).tp_richcompare:
|
|
2055
|
+
res = Py_TYPE(y).tp_richcompare(y, x, revop(op))
|
|
2056
|
+
if res is not NotImplemented:
|
|
2057
|
+
return res
|
|
2058
|
+
|
|
2059
|
+
# At this point, we have 2 objects which cannot be coerced to
|
|
2060
|
+
# a common parent. So we assume that they are not equal.
|
|
2061
|
+
if op == Py_EQ:
|
|
2062
|
+
return False
|
|
2063
|
+
if op == Py_NE:
|
|
2064
|
+
return True
|
|
2065
|
+
|
|
2066
|
+
# It does not make sense to compare x and y with an inequality,
|
|
2067
|
+
# so we raise an exception.
|
|
2068
|
+
if op == Py_LT:
|
|
2069
|
+
raise bin_op_exception('<', x, y)
|
|
2070
|
+
elif op == Py_LE:
|
|
2071
|
+
raise bin_op_exception('<=', x, y)
|
|
2072
|
+
elif op == Py_GT:
|
|
2073
|
+
raise bin_op_exception('>', x, y)
|
|
2074
|
+
else:
|
|
2075
|
+
raise bin_op_exception('>=', x, y)
|
|
2076
|
+
|
|
2077
|
+
def _coercion_error(self, x, x_map, x_elt, y, y_map, y_elt):
|
|
2078
|
+
"""
|
|
2079
|
+
This function is only called when someone has incorrectly implemented
|
|
2080
|
+
a user-defined part of the coercion system (usually, a morphism).
|
|
2081
|
+
|
|
2082
|
+
EXAMPLES::
|
|
2083
|
+
|
|
2084
|
+
sage: cm = sage.structure.element.get_coercion_model()
|
|
2085
|
+
sage: cm._coercion_error('a', 'f', 'f(a)', 'b', 'g', 'g(b)')
|
|
2086
|
+
Traceback (most recent call last):
|
|
2087
|
+
...
|
|
2088
|
+
RuntimeError: There is a bug in the coercion code in Sage.
|
|
2089
|
+
Both x (='f(a)') and y (='g(b)') are supposed to have identical parents but they don't.
|
|
2090
|
+
In fact, x has parent '<class 'str'>'
|
|
2091
|
+
whereas y has parent '<class 'str'>'
|
|
2092
|
+
Original elements 'a' (parent <class 'str'>) and 'b' (parent <class 'str'>) and maps
|
|
2093
|
+
<class 'str'> 'f'
|
|
2094
|
+
<class 'str'> 'g'
|
|
2095
|
+
"""
|
|
2096
|
+
raise RuntimeError("""There is a bug in the coercion code in Sage.
|
|
2097
|
+
Both x (=%r) and y (=%r) are supposed to have identical parents but they don't.
|
|
2098
|
+
In fact, x has parent '%s'
|
|
2099
|
+
whereas y has parent '%s'
|
|
2100
|
+
Original elements %r (parent %s) and %r (parent %s) and maps
|
|
2101
|
+
%s %r
|
|
2102
|
+
%s %r""" % (x_elt, y_elt, parent(x_elt), parent(y_elt),
|
|
2103
|
+
x, parent(x), y, parent(y),
|
|
2104
|
+
type(x_map), x_map, type(y_map), y_map))
|
|
2105
|
+
|
|
2106
|
+
|
|
2107
|
+
coercion_model = CoercionModel()
|