Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sage
Path: blob/develop/src/doc/zh/tutorial/tour_coercion.rst
4086 views
.. -*- coding: utf-8 -*-

.. _section-coercion:

======================
父结构、转换与强制转换
======================

这一节可能比前一节更技术化,但为了有效且高效地使用 Sage 中的环和其他代数结构,
理解父结构和强制转换的意义非常重要。

请注意,我们在这里只解释概念,不展示具体实现。
面向实现的教程可以参见
`Sage thematic tutorial <http://doc.sagemath.org/html/en/thematic_tutorials/coercion_and_categories.html>`_ 。

元素
----

如果想在 Python 中实现一个环,第一步是创建一个类来表示该环的元素 ``X``,
并为其提供必要的双下划线方法,例如 ``__add__``, ``__sub__``, ``__mul__``,
同时确保环公理成立。

由于 Python 是一种强类型(但动态类型)语言,可能会想到为每个环实现一个 Python 类。
毕竟,Python 有整数类型 ``<int>`` 和实数类型 ``<float>`` 等等。
但这种方法很快就会失败:环的数量是无限的,无法实现无限多个类。

相反,可以创建一个类层次结构来实现常见的代数结构元素,例如群、环、斜域、交换环、域、代数等等。

但这意味着不同环的元素可以具有相同的类型。

::

    sage: P.<x,y> = GF(3)[]
    sage: Q.<a,b> = GF(4,'z')[]
    sage: type(x)==type(a)
    True

另一方面,也可以有不同的 Python 类来实现相同的数学结构(例如稠密矩阵与稀疏矩阵)

::

    sage: P.<a> = PolynomialRing(ZZ)
    sage: Q.<b> = PolynomialRing(ZZ, sparse=True)
    sage: R.<c> = PolynomialRing(ZZ, implementation='NTL')
    sage: type(a); type(b); type(c)
    <class 'sage.rings.polynomial.polynomial_integer_dense_flint.Polynomial_integer_dense_flint'>
    <class 'sage.rings.polynomial.polynomial_ring.PolynomialRing_integral_domain_with_category.element_class'>
    <class 'sage.rings.polynomial.polynomial_integer_dense_ntl.Polynomial_integer_dense_ntl'>

这带来了两个问题:一方面,如果两个元素是相同类的实例,可以预期它们的 ``__add__`` 方法能够相加;
但如果这些元素属于非常不同的环,则不希望如此。另一方面,如果两个元素属于同一环的不同实现,想要相加,
但如果它们属于不同的 Python 类,这并不容易实现。

解决这些问题的方法称为“强制转换”,将在下面解释。

然而,每个元素都必须知道它属于哪个父结构。这可以通过 ``parent()`` 方法获得:

.. link

::

    sage: a.parent(); b.parent(); c.parent()
    Univariate Polynomial Ring in a over Integer Ring
    Sparse Univariate Polynomial Ring in b over Integer Ring
    Univariate Polynomial Ring in c over Integer Ring (using NTL)


父结构与范畴
------------

与代数结构元素的 Python 类层次结构类似,Sage 也提供包含这些元素的代数结构的类。
在 Sage 中包含元素的结构称为“父结构”,并且有一个基类。
大致上与数学概念的层次结构一致,有一系列类,例如集合、环、域等等:

::

    sage: isinstance(QQ,Field)
    True
    sage: isinstance(QQ, Ring)
    True
    sage: isinstance(ZZ,Field)
    False
    sage: isinstance(ZZ, Ring)
    True

在代数中,共享相同代数结构的对象被归类到所谓的“范畴”中。
因此,Sage 中类层次结构与范畴层次结构之间有一个粗略的类比。
然而,不应过分强调 Python 类与范畴的类比。毕竟,数学范畴也在 Sage 中实现:

::

    sage: Rings()
    Category of rings
    sage: ZZ.category()
    Join of Category of Dedekind domains
        and Category of euclidean domains
        and Category of noetherian rings
        and Category of infinite enumerated sets
        and Category of metric spaces
    sage: ZZ.category().is_subcategory(Rings())
    True
    sage: ZZ in Rings()
    True
    sage: ZZ in Fields()
    False
    sage: QQ in Fields()
    True

虽然 Sage 的类层次结构集中在实现细节上,但 Sage 的范畴框架更集中在数学结构上。
可以在范畴中实现不依赖具体实现的通用方法和测试。

Sage 中的父结构应该是唯一的 Python 对象。
例如,一旦创建了一个具有特定基环和特定生成器列表的多项式环,结果将被缓存:

::

    sage: RR['x','y'] is RR['x','y']
    True


类型与父结构
------------
类型 ``RingElement`` 并不完全对应于数学概念中的环元素。
例如,虽然方阵属于一个环,但它们不是 ``RingElement`` 的实例:

::

    sage: M = Matrix(ZZ,2,2); M
    [0 0]
    [0 0]
    sage: isinstance(M, RingElement)
    False

虽然在 Sage 中 *父结构* 是唯一的,但在一个父结构中的相等元素不一定是相同的。
这与 Python 对某些(虽然不是全部)整数的行为形成对比:

::

    sage: int(1) is int(1) # Python int
    True
    sage: int(-15) is int(-15)
    False
    sage: 1 is 1           # Sage Integer
    False

不同环的元素通常不是通过它们的类型区分,而是通过它们的父结构区分:

::

    sage: a = GF(2)(1)
    sage: b = GF(5)(1)
    sage: type(a) is type(b)
    True
    sage: parent(a)
    Finite Field of size 2
    sage: parent(b)
    Finite Field of size 5

因此,从代数的角度来看,**元素的父结构比它的类型更重要。**

转换与强制转换
--------------

在某些情况下,可以将一个父结构的元素转换为另一个父结构的元素。
这样的转换可以是显式的也可以是隐式的(被称为 *强制转换*)。

读者可能知道例如 C 语言中的 *类型转换* 和 *类型强制转换* 的概念。
Sage 中也有转换和强制转换的概念。但 Sage 中的概念集中在 *父结构* 上,而不是类型上。
所以请不要将 C 语言中的类型转换与 Sage 中的转换混淆!

我们在这里给出一个相当简短的说明。
详细描述和实现信息,请参阅参考手册中的强制转换章节以及
`thematic tutorial <http://doc.sagemath.org/html/en/thematic_tutorials/coercion_and_categories.html>`_.

关于在 *不同* 环的元素上进行算术运算的可能性,有两种极端观点:

* 不同的环是不同的世界,对不同环的元素进行加法或乘法没有任何意义;
  即使 ``1 + 1/2`` 也没有意义,因为第一个加数是整数,第二个是有理数。

或者

* 如果一个环 ``R1`` 的元素 ``r1`` 可以以某种方式在另一个环 ``R2`` 中解释,
  那么所有涉及 ``r1`` 和任意 ``R2`` 元素的算术运算都是允许的。
  乘法单位存在于所有域和许多环,它们应该都是相等的。

Sage 选择了一种折衷方案。如果 ``P1`` 和 ``P2`` 是父结构,``p1`` 是 ``P1`` 的元素,
那么用户可以显式请求将 ``p1`` 在 ``P2`` 中解释。这在所有情况下可能没有意义,
或者对于 ``P1`` 的所有元素都没有定义,用户需要确保其合理性。我们称之为 **转换**:

::

    sage: a = GF(2)(1)
    sage: b = GF(5)(1)
    sage: GF(5)(a) == b
    True
    sage: GF(2)(b) == a
    True

然而,只有当这种转换可以彻底和一致地完成时,才会发生 *隐式* (或自动)转换。
数学的严谨性在这一点上至关重要。

这种隐式转换称为 **强制转换**。如果定义了强制转换,那么它必须与转换一致。
定义强制转换需要满足两个条件:

#. 从 ``P1`` 到 ``P2`` 的强制转换必须由结构保持映射给出(例如环同态)。
   仅仅一些 ``P1`` 的元素可以映射到 ``P2`` 是不够的,映射必须尊重 ``P1`` 的代数结构。
#. 这些强制转换映射的选择必须一致:如果 ``P3`` 是第三个父结构,
   那么从 ``P1`` 到 ``P2`` 的选定强制转换与从 ``P2`` 到 ``P3`` 的强制转换的组合
   必须与从 ``P1`` 到 ``P3`` 的选定强制转换一致。特别是,
   如果存在从 ``P1`` 到 ``P2`` 和从 ``P2`` 到 ``P1`` 的强制转换,则组合必须是 ``P1`` 的恒等映射。

因此,尽管可以将 ``GF(2)`` 的每个元素转换为 ``GF(5)``,但不能强制转换,
因为 ``GF(2)`` 和 ``GF(5)`` 之间没有环同态。

一致性方面更难解释。我们用多元多项式环来说明。在应用中,保留名称的强制转换最有意义。因此,我们有:

::

    sage: R1.<x,y> = ZZ[]
    sage: R2 = ZZ['y','x']
    sage: R2.has_coerce_map_from(R1)
    True
    sage: R2(x)
    x
    sage: R2(y)
    y
    sage: R2.coerce(y)
    y

如果没有保留名称的环同态,则不定义强制转换。然而,转换可能仍然是可能的,即通过根据生成器列表中的位置映射环生成器:

.. link

::

    sage: R3 = ZZ['z','x']
    sage: R3.has_coerce_map_from(R1)
    False
    sage: R3(x)
    z
    sage: R3(y)
    x
    sage: R3.coerce(y)
    Traceback (most recent call last):
    ...
    TypeError: no canonical coercion
    from Multivariate Polynomial Ring in x, y over Integer Ring
    to Multivariate Polynomial Ring in z, x over Integer Ring

但这种保留位置的转换不符合强制转换:通过组合从 ``ZZ['x','y']`` 到 ``ZZ['y','x']`` 的保留名称映射
与从 ``ZZ['y','x']`` 到 ``ZZ['a','b']`` 的保留位置映射,将得到一个既不保留名称也不保留位置的映射,违反了一致性。

如果存在强制转换,它将用于比较不同环的元素或进行算术运算。这通常很方便,
但用户应该意识将 ``==`` 关系扩展到不同父结构的边界可能很容易导致过度使用。
例如,虽然 ``==`` 应该是 **同一** 环元素上的等价关系,但如果涉及 *不同* 环,则不一定如此。
例如,``ZZ`` 和有限域中的 ``1`` 被认为是相等的,因为从整数到任何有限域都有一个规范的强制转换。
然而,通常两个不同的有限域之间没有强制转换。因此我们有:

.. link

::

    sage: GF(5)(1) == 1
    True
    sage: 1 == GF(2)(1)
    True
    sage: GF(5)(1) == GF(2)(1)
    False
    sage: GF(5)(1) != GF(2)(1)
    True

同理,我们有:

.. link

::

    sage: R3(R1.1) == R3.1
    True
    sage: R1.1 == R3.1
    False
    sage: R1.1 != R3.1
    True


一致性条件的另一个结果是强制转换只能从精确环(例如有理数 ``QQ``)到不精确环(例如具有固定精度的实数 ``RR``),而不能反过来。
原因是从 ``QQ`` 到 ``RR`` 的强制转换与从 ``RR`` 到 ``QQ`` 的转换的组合应该是 ``QQ`` 上的恒等映射。
但这是不可能的,因为在 ``RR`` 中一些不同的有理数可能被视为相等,如下例所示:

::

    sage: RR(1/10^200+1/10^100) == RR(1/10^100)
    True
    sage: 1/10^200+1/10^100 == 1/10^100
    False


当比较两个父结构 ``P1`` 和 ``P2`` 的元素时,可能没有两个环之间的强制转换,
但有一个规范的父结构 ``P3`` 可选,使得 ``P1`` 和 ``P2`` 都强制转换到 ``P3``。
在这种情况下,也会发生强制转换。一个典型用例是有理数和具有整数系数的多项式之和,产生具有有理系数的多项式:

::

    sage: P1.<x> = ZZ[]
    sage: p = 2*x+3
    sage: q = 1/2
    sage: parent(p)
    Univariate Polynomial Ring in x over Integer Ring
    sage: parent(p+q)
    Univariate Polynomial Ring in x over Rational Field

注意,原则上结果在 ``ZZ['x']`` 的分数域中也有意义。
然而,Sage 会尝试选择一个 *规范的* 共同父结构,使得看起来最自然(在我们的例子中是 ``QQ['x']``)。
如果几个潜在的共同父结构看起来同样自然,为了获得可靠的结果,Sage *不会* 随机选择其中一个。
该选择所基于的机制在
`thematic tutorial <http://doc.sagemath.org/html/en/thematic_tutorials/coercion_and_categories.html>`_
中进行了解释。

以下示例不会发生强制转换到共同父结构:

::

    sage: R.<x> = QQ[]
    sage: S.<y> = QQ[]
    sage: x+y
    Traceback (most recent call last):
    ...
    TypeError: unsupported operand parent(s) for +: 'Univariate Polynomial Ring in x over Rational Field' and 'Univariate Polynomial Ring in y over Rational Field'

原因是 Sage 不会选择潜在候选结构 ``QQ['x']['y']``, ``QQ['y']['x']``, ``QQ['x','y']`` 或 ``QQ['y','x']`` 之一,
因为所有这四个成对不同的结构看起来都是自然的共同父结构,并且没有明显的规范选择。