胁迫¶
预赛¶
强迫到底是什么意思?¶
The primary goal of coercion is to be able to transparently do arithmetic, comparisons, etc. between elements of distinct sets.
作为一个具体的例子,当一个人写下 1 + 1/2 人们希望将操作数作为有理数来执行算术运算,尽管左边是整数。考虑到整数明显而自然地包含在有理数中,这是有意义的。强制系统的目标是促进这一点(以及更复杂的算法),而不必显式地将所有内容映射到同一个领域,同时足够严格,不能解决歧义或接受胡说八道。以下是一些示例:
sage: 1 + 1/2
3/2
sage: R.<x,y> = ZZ[]
sage: R
Multivariate Polynomial Ring in x, y over Integer Ring
sage: parent(x)
Multivariate Polynomial Ring in x, y over Integer Ring
sage: parent(1/3)
Rational Field
sage: x+1/3
x + 1/3
sage: parent(x+1/3)
Multivariate Polynomial Ring in x, y over Rational Field
sage: GF(5)(1) + CC(I)
Traceback (most recent call last):
...
TypeError: unsupported operand parent(s) for +: 'Finite Field of size 5' and 'Complex Field with 53 bits of precision'
父项和元素¶
父项是具体类别中的对象,元素是它们的成员。父母是一流的对象。在Sage中,大多数事情要么是父母,要么是有父母。通常只要你一看到这个词 Parent 人们可以认为 Set 。以下是一些示例:
sage: parent(1)
Integer Ring
sage: parent(1) is ZZ
True
sage: ZZ
Integer Ring
sage: parent(1.50000000000000000000000000000000000)
Real Field with 120 bits of precision
sage: parent(x)
Symbolic Ring
sage: x^sin(x)
x^sin(x)
sage: R.<t> = Qp(5)[]
sage: f = t^3-5; f
(1 + O(5^20))*t^3 + 4*5 + 4*5^2 + 4*5^3 + 4*5^4 + 4*5^5 + 4*5^6 + 4*5^7 + 4*5^8 + 4*5^9 + 4*5^10 + 4*5^11 + 4*5^12 + 4*5^13 + 4*5^14 + 4*5^15 + 4*5^16 + 4*5^17 + 4*5^18 + 4*5^19 + 4*5^20 + O(5^21)
sage: parent(f)
Univariate Polynomial Ring in t over 5-adic Field with capped relative precision 20
sage: f = EllipticCurve('37a').lseries().taylor_series(10); f # abs tol 1e-14
0.997997869801216 + 0.00140712894524925*z - 0.000498127610960097*z^2 + 0.000118835596665956*z^3 - 0.0000215906522442708*z^4 + (3.20363155418421e-6)*z^5 + O(z^6) # 32-bit
0.997997869801216 + 0.00140712894524925*z - 0.000498127610960097*z^2 + 0.000118835596665956*z^3 - 0.0000215906522442708*z^4 + (3.20363155418427e-6)*z^5 + O(z^6) # 64-bit
sage: parent(f)
Power Series Ring in z over Complex Field with 53 bits of precision
父母和类型之间有一个重要的区别:
sage: a = GF(5).random_element()
sage: b = GF(7).random_element()
sage: type(a)
<class 'sage.rings.finite_rings.integer_mod.IntegerMod_int'>
sage: type(b)
<class 'sage.rings.finite_rings.integer_mod.IntegerMod_int'>
sage: type(a) == type(b)
True
sage: parent(a)
Finite Field of size 5
sage: parent(a) == parent(b)
False
但是,非Sage对象并没有真正的父级,但我们仍然希望能够与它们进行推理,因此改用它们的类型::
sage: a = int(10)
sage: parent(a)
<... 'int'>
事实上,在这些情况下,使用了一种特殊类型的父对象“T类的所有Python对象的集合”。
请注意,父母是 not 总是越紧越好。
sage: parent(1/2)
Rational Field
sage: parent(2/1)
Rational Field
父项之间的映射¶
许多家长带着地图往返于其他家长之间。
Sage区分了能够 convert 在不同的父母之间,以及 coerce 在他们之间。转换是显式的,如果可能的话,它会尝试理解目标域中的对象。它是通过调用::
sage: ZZ(5)
5
sage: ZZ(10/5)
2
sage: QQ(10)
10
sage: parent(QQ(10))
Rational Field
sage: a = GF(5)(2); a
2
sage: parent(a)
Finite Field of size 5
sage: parent(ZZ(a))
Integer Ring
sage: GF(71)(1/5)
57
sage: ZZ(1/2)
Traceback (most recent call last):
...
TypeError: no conversion of this rational to integer
转换不需要是规范的(例如,它们可能涉及升降机的选择),甚至不需要在数学上有意义(例如,某种类型的构造)。
sage: ZZ("123")
123
sage: ZZ(GF(5)(14))
4
sage: ZZ['x']([4,3,2,1])
x^3 + 2*x^2 + 3*x + 4
sage: a = Qp(5, 10)(1/3); a
2 + 3*5 + 5^2 + 3*5^3 + 5^4 + 3*5^5 + 5^6 + 3*5^7 + 5^8 + 3*5^9 + O(5^10)
sage: ZZ(a)
6510417
另一方面,Sage有一个概念,即 coercion ,这是父母之间的规范同态(有时是开发人员做出的常规选择)。父母一方对另一方的胁迫 must 被定义在整个领域,并且总是成功的。由于它可能被隐含地援引,它应该是显而易见和自然的(在这个词的数学严谨和口语意义上都是如此)。直到由于不精确的表示而产生的不可避免的舍入问题,这些强制态射都应该是可交换的。特别是,如果存在胁迫映射 A to B 和 B to A ,则它们的组合一定是身份映射。
强迫力可以通过 Parent.has_coerce_map_from()
方法,如果需要,还可以使用 Parent.coerce()
方法:
sage: QQ.has_coerce_map_from(ZZ)
True
sage: QQ.has_coerce_map_from(RR)
False
sage: ZZ['x'].has_coerce_map_from(QQ)
False
sage: ZZ['x'].has_coerce_map_from(ZZ)
True
sage: ZZ['x'].coerce(5)
5
sage: ZZ['x'].coerce(5).parent()
Univariate Polynomial Ring in x over Integer Ring
sage: ZZ['x'].coerce(5/1)
Traceback (most recent call last):
...
TypeError: no canonical coercion from Rational Field to Univariate Polynomial Ring in x over Integer Ring
基本算术规则¶
假设我们要添加两个元素a和b,它们的父项分别是A和B。当我们打字时 a+b
然后
如果A
is
B,调用a._add_(B)如果有一种胁迫 phi: B rightarrow A ,调用._add._( phi (B))
如果有一种胁迫 phi: A rightarrow B ,呼叫 phi (A)._添加_(B)
Look for Z such that there is a coercion phi_A: A rightarrow Z and phi_B: B rightarrow Z, call phi_A (a)._add_( phi_B (b))
这些规则是按顺序计算的;因此,如果在两个方向上都存在强制,则a._addb的父项是A--在这种情况下使用左操作数的父项。
减法、乘法和除法也使用相同的规则。这个逻辑嵌入在一个强制模型对象中,可以获取和查询。
sage: parent(1 + 1/2)
Rational Field
sage: cm = coercion_model; cm
<sage.structure.coerce.CoercionModel object at ...>
sage: cm.explain(ZZ, QQ)
Coercion on left operand via
Natural morphism:
From: Integer Ring
To: Rational Field
Arithmetic performed after coercions.
Result lives in Rational Field
Rational Field
sage: cm.explain(ZZ['x','y'], QQ['x'])
Coercion on left operand via
Coercion map:
From: Multivariate Polynomial Ring in x, y over Integer Ring
To: Multivariate Polynomial Ring in x, y over Rational Field
Coercion on right operand via
Coercion map:
From: Univariate Polynomial Ring in x over Rational Field
To: Multivariate Polynomial Ring in x, y over Rational Field
Arithmetic performed after coercions.
Result lives in Multivariate Polynomial Ring in x, y over Rational Field
Multivariate Polynomial Ring in x, y over Rational Field
强制模型可以直接用于任何二元操作(带两个参数的可调用)。
sage: cm.bin_op(77, 9, gcd)
1
也有 actions 从某种意义上说,一个字段 K 作用于上的模块 K ,或者置换群作用于集合。这些是在上面的步骤1和2之间发现的。
sage: cm.explain(ZZ['x'], ZZ, operator.mul)
Action discovered.
Right scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring
Result lives in Univariate Polynomial Ring in x over Integer Ring
Univariate Polynomial Ring in x over Integer Ring
sage: cm.explain(ZZ['x'], ZZ, operator.truediv)
Action discovered.
Right inverse action by Rational Field on Univariate Polynomial Ring in x over Integer Ring
with precomposition on right by Natural morphism:
From: Integer Ring
To: Rational Field
Result lives in Univariate Polynomial Ring in x over Rational Field
Univariate Polynomial Ring in x over Rational Field
sage: f = QQ.coerce_map_from(ZZ)
sage: f(3).parent()
Rational Field
请注意,由 :issue:`14711` Sage的强制系统使用对域的弱引用的映射。此类地图只应在内部使用,因此应使用副本(除非有人知道自己在做什么):
sage: QQ._internal_coerce_map_from(int)
(map internal to coercion system -- copy before use)
Native morphism:
From: Set of Python objects of class 'int'
To: Rational Field
sage: copy(QQ._internal_coerce_map_from(int))
Native morphism:
From: Set of Python objects of class 'int'
To: Rational Field
请注意,用户可见的方法(不带下划线)自动执行此复制::
sage: copy(QQ.coerce_map_from(int))
Native morphism:
From: Set of Python objects of class 'int'
To: Rational Field
sage: QQ.has_coerce_map_from(RR)
False
sage: QQ['x'].get_action(QQ)
Right scalar multiplication by Rational Field on Univariate Polynomial Ring in x over Rational Field
sage: QQ2 = QQ^2
sage: (QQ2).get_action(QQ)
Right scalar multiplication by Rational Field on Vector space of dimension 2 over Rational Field
sage: QQ['x'].get_action(RR)
Right scalar multiplication by Real Field with 53 bits of precision on Univariate Polynomial Ring in x over Rational Field
如何实施¶
实现的方法¶
元素上的算术:
_add_
,_sub_
,_mul_
,_div_
这就是二进制算术运算符应该实现的地方。不同于Python的
__add__
,两个操作数都是 guaranteed 在这一点上有相同的父母。对父母的胁迫:
_coerce_map_from_
考虑到父母R和S,
R._coerce_map_from_(S)
被调用以确定是否存在强制 phi: S rightarrow R 。注意,该函数是在潜在的共域上调用的。若要指示S对R(自我)没有胁迫,请返回False
或None
。这是默认行为。如果存在胁迫,则返回True
(在这种情况下,使用R._element_constructor_
将被创建)或实际的Morphism
对象,以S为结构域,R为共结构域。针对家长的操作:
_get_action_
或_rmul_
,_lmul_
,_act_on_
,_acted_upon_
Suppose one wants R to act on S. Some examples of this could be R = QQ, S = QQ[x] or R = {rm Gal}(S/QQ) where S is a number field. There are several ways to implement this:
如果 R is the base of S (as in the first example), simply implement
_ Rmul_``和/或 ``_lmul_
关于要素的问题 S. In this caser * s
gets handled ass._rmul_(r)
ands * r
ass._lmul_(r)
. The argument to_ Rmul_``和 ``_lmul_
是 guaranteed 作为…的基础要素 S (如有必要,可事先采取强制措施)。如果 R acts on S, one can define the methods
_ 对的元素执行ACT_ON_
R or_ 对元素作用_对_
S 。在这种情况下r * s
被处理为r._act_on_(s, True)
或s._acted_upon_(r, False)
和s * r
ASr._act_on_(s, False)
或s._acted_upon_(r, True)
。对于传递给这些方法的对象的类型或父级没有约束;引发TypeError
或ValueError
如果传入了错误类型的对象,以指示此处不适合该操作。If either R acts on S or S acts on R, one may implement
R._get_action_
to return an actualAction
object to be used. This is how non-multiplicative actions must be implemented, and is the most powerful and complete way to do things.
应该注意的是,对于第一种工作方式, S 必须是模块元素。这一要求很可能在未来被取消。
Element conversion/construction for Parents: use
_element_constructor_
not__call__
这个
Parent.__call__()
方法调度到_element_constructor_
。当有人写下R(x, ...)
在大多数情况下,这是最终被调用的方法。参见上的文档__call__
方法如下。
家长亦可致电 self._populate_coercion_lists_
方法在它们的 __init__
函数传递任何可调用以供使用,而不是 _element_constructor_
,提供具有自我强制的父母列表(作为实施的替代方案 _coerce_map_from_
),提供特殊的施工方法(如 _integer_
对于ZZ)等。这还允许指定单个强制嵌入 out 自我(而其余的强制函数都指定了映射 into 自我)。的文档字符串中有大量的文档 _populate_coercion_lists_
方法。
示例¶
有时一个简单的例子胜过千言万语。下面是一个设置简单环来处理胁迫的最小示例。(很容易想象更复杂、更强大的本地化,但这会模糊这里提出的主要观点。)
sage: from sage.structure.richcmp import richcmp
sage: from sage.structure.element import Element
sage: class MyLocalization(Parent):
....: def __init__(self, primes):
....: r"""
....: Localization of `\ZZ` away from primes.
....: """
....: Parent.__init__(self, base=ZZ, category=Rings().Commutative())
....: self._primes = primes
....: self._populate_coercion_lists_()
....:
....: def _repr_(self) -> str:
....: """
....: How to print ``self``.
....: """
....: return "%s localized at %s" % (self.base(), self._primes)
....:
....: def _element_constructor_(self, x):
....: """
....: Make sure ``x`` is a valid member of ``self``, and return the constructed element.
....: """
....: if isinstance(x, MyLocalizationElement):
....: x = x._value
....: else:
....: x = QQ(x)
....: for p, e in x.denominator().factor():
....: if p not in self._primes:
....: raise ValueError("not integral at %s" % p)
....: return self.element_class(self, x)
....:
....: def _an_element_(self):
....: return self.element_class(self, 6)
....:
....: def _coerce_map_from_(self, S):
....: """
....: The only things that coerce into this ring are:
....:
....: - the integer ring
....:
....: - other localizations away from fewer primes
....: """
....: if S is ZZ:
....: return True
....: if isinstance(S, MyLocalization):
....: return all(p in self._primes for p in S._primes)
sage: class MyLocalizationElement(Element):
....:
....: def __init__(self, parent, x):
....: Element.__init__(self, parent)
....: self._value = x
....:
....: # We are just printing out this way to make it easy to see what's going on in the examples.
....:
....: def _repr_(self) -> str:
....: return f"LocalElt({self._value})"
....:
....: # Now define addition, subtraction and multiplication of elements.
....: # Note that self and right always have the same parent.
....:
....: def _add_(self, right):
....: return self.parent()(self._value + right._value)
....:
....: def _sub_(self, right):
....: return self.parent()(self._value - right._value)
....:
....: def _mul_(self, right):
....: return self.parent()(self._value * right._value)
....:
....: # The basering was set to ZZ, so c is guaranteed to be in ZZ
....:
....: def _rmul_(self, c):
....: return self.parent()(c * self._value)
....:
....: def _lmul_(self, c):
....: return self.parent()(self._value * c)
....:
....: def _richcmp_(self, other, op):
....: return richcmp(self._value, other._value, op)
sage: MyLocalization.element_class = MyLocalizationElement
非那样做不行。现在我们可以测试一下了:
sage: TestSuite(R).run(skip="_test_pickling")
sage: R = MyLocalization([2]); R
Integer Ring localized at [2]
sage: R(1)
LocalElt(1)
sage: R(1/2)
LocalElt(1/2)
sage: R(1/3)
Traceback (most recent call last):
...
ValueError: not integral at 3
sage: R.coerce(1)
LocalElt(1)
sage: R.coerce(1/4)
Traceback (most recent call last):
...
TypeError: no canonical coercion from Rational Field to Integer Ring localized at [2]
sage: R(1/2) + R(3/4)
LocalElt(5/4)
sage: R(1/2) + 5
LocalElt(11/2)
sage: 5 + R(1/2)
LocalElt(11/2)
sage: R(1/2) + 1/7
Traceback (most recent call last):
...
TypeError: unsupported operand parent(s) for +: 'Integer Ring localized at [2]' and 'Rational Field'
sage: R(3/4) * 7
LocalElt(21/4)
sage: cm = sage.structure.element.get_coercion_model()
sage: cm.explain(R, ZZ, operator.add)
Coercion on right operand via
Coercion map:
From: Integer Ring
To: Integer Ring localized at [2]
Arithmetic performed after coercions.
Result lives in Integer Ring localized at [2]
Integer Ring localized at [2]
sage: cm.explain(R, ZZ, operator.mul)
Coercion on right operand via
Coercion map:
From: Integer Ring
To: Integer Ring localized at [2]
Arithmetic performed after coercions.
Result lives in Integer Ring localized at [2]
Integer Ring localized at [2]
sage: R6 = MyLocalization([2,3]); R6
Integer Ring localized at [2, 3]
sage: R6(1/3) - R(1/2)
LocalElt(-1/6)
sage: parent(R6(1/3) - R(1/2))
Integer Ring localized at [2, 3]
sage: R.has_coerce_map_from(ZZ)
True
sage: R.coerce_map_from(ZZ)
Coercion map:
From: Integer Ring
To: Integer Ring localized at [2]
sage: R6.coerce_map_from(R)
Coercion map:
From: Integer Ring localized at [2]
To: Integer Ring localized at [2, 3]
sage: R6.coerce(R(1/2))
LocalElt(1/2)
sage: cm.explain(R, R6, operator.mul)
Coercion on left operand via
Coercion map:
From: Integer Ring localized at [2]
To: Integer Ring localized at [2, 3]
Arithmetic performed after coercions.
Result lives in Integer Ring localized at [2, 3]
Integer Ring localized at [2, 3]
提供的方法¶
__call__
这为元素构造提供了一致的界面。特别是,如果存在强制,它确保转换始终产生与强制相同的结果。(Sage中的一些指环曾经违反了这一点,因为转换和胁迫的代码被分开编辑。)假设R是父对象,并假设用户键入R(X),其中x具有父对象X。粗略地说,会发生以下情况:
如果X
is
R,返回x(*)如果有一种胁迫 f: X rightarrow R ,返回 f(x)
如果有一种胁迫 f: R rightarrow X ,试着返回 {f^{-1}}(x)
返回
R._element_constructor_(x)
(**)
关键字和额外的参数被传递。所有这些逻辑的结果都被缓存。
(*)除非存在“复制”关键字,如R(x,COPY=FALSE)
(**)从技术上讲,从X到R创建了一个泛态射,它可以使用像这样的魔术方法
_integer_
或由提供的其他数据_populate_coercion_lists_
。coerce
将元素强制为self,如果没有强制映射,则引发类型错误。
coerce_map_from, convert_map_from
返回一个实际的
Morphism
对象以从另一个父级强制/转换为自身。除非直接构造R的元素,R.convert_map_from(S)
将提供一个可调用的Python对象,这是将S的元素转换为R的元素的最快方法。_call_
方法。has_coerce_map_from
退货
True
或False
这取决于是否存在胁迫。R.has_coerce_map_from(S)
是对以下的速记R.coerce_map_from(S) is not None
get_action
这将解开所有
_get_action_, _rmul_, _lmul_, _act_on_, _acted_upon_, ...
方法来提供实际的Action
对象(如果存在)。
发现新父母¶
使用sage/ategory/presout.py中的算法发现新的父代。其基本思想是,Sage中的大多数父对象都是通过各种函数器由更简单的对象构造的。这些都可以通过 construction()
方法,该方法返回一个(更简单的)父对象以及一个可以用来创建自身的函数器。
sage: CC.construction()
(AlgebraicClosureFunctor, Real Field with 53 bits of precision)
sage: RR.construction()
(Completion[+Infinity, prec=53], Rational Field)
sage: QQ.construction()
(FractionField, Integer Ring)
sage: ZZ.construction() # None
sage: Zp(5).construction()
(Completion[5, prec=20], Integer Ring)
sage: QQ.completion(5, 100, {})
5-adic Field with capped relative precision 100
sage: c, R = RR.construction()
sage: a = CC.construction()[0]
sage: a.commutes(c)
False
sage: RR == c(QQ)
True
sage: sage.categories.pushout.construction_tower(Frac(CDF['x']))
[(None,
Fraction Field of Univariate Polynomial Ring in x over Complex Double Field),
(FractionField, Univariate Polynomial Ring in x over Complex Double Field),
(Poly[x], Complex Double Field),
(AlgebraicClosureFunctor, Real Double Field),
(Completion[+Infinity, prec=53], Rational Field),
(FractionField, Integer Ring)]
给定父母R和S,使得从R到S或从S到R都没有胁迫,可以找到一个有胁迫的公共Z R rightarrow Z 和 S rightarrow Z 通过考虑从一个共同祖先到R和S的构造函数器序列,我们然后使用 heuristic 算法将这些构造函数交织在一起,以尝试到达合适的Z(如果存在)。例如::
sage: ZZ['x'].construction()
(Poly[x], Integer Ring)
sage: QQ.construction()
(FractionField, Integer Ring)
sage: sage.categories.pushout.pushout(ZZ['x'], QQ)
Univariate Polynomial Ring in x over Rational Field
sage: sage.categories.pushout.pushout(ZZ['x'], QQ).construction()
(Poly[x], Rational Field)
The common ancestor is Z and our options for Z are mathrm{Frac}(ZZ[x]) or mathrm{Frac}(ZZ)[x]. In Sage we choose the later, treating the fraction field functor as binding "more tightly" than the polynomial functor, as most people agree that QQ[x] is the more natural choice. The same procedure is applied to more complicated Parents, returning a new Parent if one can be unambiguously determined.
sage: sage.categories.pushout.pushout(Frac(ZZ['x,y,z']), QQ['z, t'])
Univariate Polynomial Ring in t over Fraction Field of Multivariate Polynomial Ring in x, y, z over Rational Field