父母、皈依和胁迫

这一节可能看起来比前一节更技术性,但我们认为,为了有效和高效地使用Sage中的环和其他代数结构,理解双亲和胁迫的含义是很重要的。

请注意,我们试图解释概念,但我们在这里没有展示如何实现它们。面向实现的教程可作为 Sage thematic tutorial

元素

如果想要在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()

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 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

而当 parents 是唯一的,平等的 elements 在《圣贤》中,父母的身份并不一定是相同的。这与部分(尽管不是全部)整数的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

因此,从代数的角度来看, the parent of an element is more important than its type.

转换与胁迫

在某些情况下,可以将一个父结构的元素转换为不同父结构的元素。这种转换可以是显式的,也可以是隐式的(这称为 coercion )。

读者可能知道其中的概念 type conversiontype coercion 来自例如C编程语言。有一些概念是 conversioncoercion 在塞奇也是如此。但《Sage》中的观念主要集中在 parents ,而不是类型。所以,请不要混淆C中的类型转换和Sage中的转换!

我们在这里给出一个相当简短的说明。有关实施的详细说明和信息,请参阅参考手册中关于胁迫的部分和 thematic tutorial

对于使用以下元素进行算术的可能性,有两种极端的观点 different 戒指:

  • 不同的环是不同的世界,将不同环的元素相加或相乘是没有任何意义的;甚至 1 + 1/2 没有意义,因为第一个被加数是一个整数,第二个是有理数。

  • 如果一个元素 r1 一环中的 R1 能以某种方式在另一个环中被解释 R2 ,则涉及的所有算术运算 r1 以及任何元素 R2 是被允许的。乘法单位存在于所有的域和许多环中,并且它们都应该相等。

Sage倾向于妥协。如果 P1P2 是父结构,并且 p1 是一个元素, P1 ,则用户可以明确地要求解释 p1 在……里面 P2 。这可能不是在所有情况下都有意义,也不是为的所有元素定义的 P1 ,并由用户来确保它有意义。我们将其称为 conversion

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

然而,一个 implicit (或自动)转换只有在可以完成时才会发生 thoroughlyconsistently 。在这一点上,数学上的严谨是至关重要的。

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

  1. 来自中国的胁迫 P1P2 必须由结构保持映射(例如,环同态)给出。这还不够 some 元素: P1 可以映射到 P2 ,并且映射必须遵守的代数结构 P1

  2. 这些强制映射的选择必须一致:如果 P3 是第三个母体结构,那么所选择的胁迫的组成来自 P1P2 有了来自于 P2P3 必须与所选择的来自 P1P3 。特别是,如果有来自 P1P2P2P1 ,组成必须是的身份地图 P1

因此,尽管可以将每个元素 GF(2) vt.进入,进入 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: R3 = ZZ['z','x']
sage: R3.has_coerce_map_from(R1)
False
sage: R3(x)
z
sage: R3(y)
x

但这样的位置保持转换并不符合强制:通过从 ZZ['x','y']ZZ['y','x'] 具有位置保持的地图,从 ZZ['y','x']ZZ['a','b'] ,映射的结果既不是名称保持,也不是位置保持,这违反了一致性。

如果存在强制,它将被用来比较不同环的元素或进行算术。这通常很方便,但用户应该意识到扩展 == -跨越不同父母的边界的关系很容易导致过度。例如,虽然 == 被认为是关于元素的等价关系 one Ring,如果是这样,情况就不一定是这样 different 戒指也牵涉其中。例如, 1 在……里面 ZZ 和在有限域中被认为是相等的,因为存在从整数到任何有限域的正则胁迫。然而,一般而言,在两个不同的有限场之间不存在胁迫。因此,我们有

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

同样,我们有

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

一致性条件的另一个结果是强制只能来自精确的环(例如,有理数 QQ )到不精确的环(例如,具有固定精度的实数 RR ),但不是反过来。究其原因,是胁迫的成分来自 QQRR 转换为从 RRQQ 应该是上面的身份 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

比较两个父项的元素时 P1P2 ,这两个环之间可能没有强制,但存在父母的规范选择 P3 这样两个人都能 P1P2 强行进入 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试图选择一种 canonical 共同的父母似乎是最自然的 (QQ['x'] 在我们的示例中)。如果几个潜在的共同父母看起来同样自然,Sage会这样做的 not 随机选择其中之一,以获得可靠的结果。中解释了该选择所基于的机制 thematic tutorial

在下面的示例中,不会发生对共同父级的强制:

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