胁迫

准备工作

什么是强迫?

强制的主要目标是能够透明地在不同集合的元素之间进行算术、比较等。

作为一个具体的例子 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中的大多数事物不是父母就是有父母。通常只要你看到这个词 起源 一个人可以思考 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)
<type 'sage.rings.finite_rings.integer_mod.IntegerMod_int'>
sage: type(b)
<type '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区分 转换 在不同的父母之间,以及 胁迫 他们之间。转换是显式的,如果可能的话,它会尝试理解目标域中的对象。通过调用调用:

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有一个概念 强制 ,这是父母之间的规范态射(偶尔由开发人员进行常规选择)。从父母一方到另一方的胁迫 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. 如果 is B、 打电话给a

  2. 如果有胁迫 phi: B rightarrow A ,呼叫a.U add_( phi (b) )

  3. 如果有胁迫 phi: A rightarrow B ,呼叫 phi (a) 加上(b)

  4. 寻找 Z 以至于有一种胁迫 phi_A: A rightarrow Zphi_B: B rightarrow Z ,呼叫 phi_A (a) .u添加_( phi_B (b) )

这些规则是按顺序计算的;因此,如果在两个方向上都有强制,则a..u add_b的父级是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

强制模型可以直接用于任何二进制操作(callable接受两个参数)。

sage: cm.bin_op(77, 9, gcd)
1

也有 行动 从某种意义上说 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

请注意 :trac:`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__ ,两个操作数都是 放心 在这一点上有相同的父母。

  • 强迫父母: _coerce_map_from_

    给两个父母R和S, R._coerce_map_from_(S) 调用以确定是否存在强制 phi: S rightarrow R . 请注意,函数是在潜在的密码域上调用的。若要指示不存在从S到R(self)的强制,请返回 FalseNone . 这是默认行为。如果有胁迫,就回去 True (在这种情况下,态射使用 R._element_constructor_ 将被创建)或 Morphism 对象,其中S为域,R为codomain。

  • 家长行动: _get_action__rmul__lmul__act_on__acted_upon_

    假设有人想让R作用于S R = QQS = QQ[x]R = {{rm Gal}}(S/QQ) 在哪里? S 是一个数字域。有几种方法可以实现这一点:

    • 如果 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 _ ``_lmul_放心 成为 S (如有必要,可事先采取强制措施)。

    • 如果 R acts on S, one can define the methods _ 对元素采取行动 `R` or ``_ 作用于 `S` . 在这种情况下 ``r * s 处理为 r._act_on_(s, True)s._acted_upon_(r, False)s * r 作为 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_ 这也允许指定一个单一的强制嵌入 out (而其余的强制函数都指定映射 into 自我)。的docstring中有大量文档 _populate_coercion_lists_ 方法。

例子

有时一个简单的例子胜过千言万语。下面是设置一个处理强制的简单环的最小示例。(很容易想象出更复杂、更强大的本地化,但这会掩盖本文的主要观点。)

class Localization(Ring):
   def __init__(self, primes):
       """
       Localization of `\ZZ` away from primes.
       """
       Ring.__init__(self, base=ZZ)
       self._primes = primes
       self._populate_coercion_lists_()

   def _repr_(self):
       """
       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, LocalizationElement):
           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 LocalizationElement(self, x)

   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
       elif isinstance(S, Localization):
           return all(p in self._primes for p in S._primes)


class LocalizationElement(RingElement):

   def __init__(self, parent, x):
       RingElement.__init__(self, parent)
       self._value = x


   # We're just printing out this way to make it easy to see what's going on in the examples.

   def _repr_(self):
       return "LocalElt(%s)" % self._value

   # Now define addition, subtraction, and multiplication of elements.
   # Note that left and right always have the same parent.

   def _add_(left, right):
       return LocalizationElement(left.parent(), left._value + right._value)

   def _sub_(left, right):
       return LocalizationElement(left.parent(), left._value - right._value)

   def _mul_(left, right):
       return LocalizationElement(left.parent(), left._value * right._value)

   # The basering was set to ZZ, so c is guaranteed to be in ZZ

   def _rmul_(self, c):
       return LocalizationElement(self.parent(), c * self._value)

   def _lmul_(self, c):
       return LocalizationElement(self.parent(), self._value * c)

就这些了。现在我们可以测试一下:

sage: R = Localization([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 (click to the left for traceback)
...
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: R.get_action(ZZ)
Right scalar multiplication by Integer Ring on Integer Ring localized at [2]
sage: cm = sage.structure.element.get_coercion_model()
sage: cm.explain(R, ZZ, operator.add)
Coercion on right operand via
   Conversion 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)
Action discovered.
   Right scalar multiplication by Integer Ring on Integer Ring localized at [2]
Result lives in Integer Ring localized at [2]
Integer Ring localized at [2]

sage: R6 = Localization([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)
Conversion map:
 From: Integer Ring
 To:   Integer Ring localized at [2]

sage: R6.coerce_map_from(R)
Conversion 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
   Conversion 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中,由于转换和强制的代码分别进行了编辑,所以在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) (**)

    关键字和额外的参数被传递。所有这些逻辑的结果都被缓存。

    (*)除非有“copy”关键字,如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/category中的算法来发现新的父代/推出.py. 其基本思想是Sage中的大多数父类都是通过各种函子从更简单的对象中构造出来的。这些可以通过 construction() 方法,该方法返回一个(更简单)父级以及一个可以创建self的函子。

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的构造函子的序列,我们使用a 启发式的 交错这些构造器以尝试到达合适的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)

共同的祖先是 Z 我们对Z的选择是 mathrm{{Frac}}(ZZ[x])mathrm{{Frac}}(ZZ)[x] . 在Sage中,我们选择后者,将分数域函子视为比多项式函子“更紧密”的结合,正如大多数人所同意的那样 QQ[x] 是比较自然的选择。同样的过程也适用于更复杂的父对象,如果可以明确地确定一个新的父对象,则返回一个新的父对象。

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