.. -*- 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']`` 之一, 因为所有这四个成对不同的结构看起来都是自然的共同父结构,并且没有明显的规范选择。