胁迫

预赛

强迫到底是什么意思?

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 BB 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 然后

  1. 如果A is B,调用a._add_(B)

  2. 如果有一种胁迫 phi: B rightarrow A ,调用._add._( phi (B))

  3. 如果有一种胁迫 phi: A rightarrow B ,呼叫 phi (A)._添加_(B)

  4. 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(自我)没有胁迫,请返回 FalseNone 。这是默认行为。如果存在胁迫,则返回 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 case r * s gets handled as s._rmul_(r) and s * r as s._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 AS r._act_on_(s, False)s._acted_upon_(r, True) 。对于传递给这些方法的对象的类型或父级没有约束;引发 TypeErrorValueError 如果传入了错误类型的对象,以指示此处不适合该操作。

    • If either R acts on S or S acts on R, one may implement R._get_action_ to return an actual Action 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。粗略地说,会发生以下情况:

    1. 如果X is R,返回x(*)

    2. 如果有一种胁迫 f: X rightarrow R ,返回 f(x)

    3. 如果有一种胁迫 f: R rightarrow X ,试着返回 {f^{-1}}(x)

    4. 返回 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

    退货 TrueFalse 这取决于是否存在胁迫。 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 ZS 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

模块

索引和表格