父母、皈依和胁迫¶
这一节可能看起来比前一节更技术性,但我们认为,为了有效和高效地使用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 conversion 和 type coercion 来自例如C编程语言。有一些概念是 conversion 和 coercion 在塞奇也是如此。但《Sage》中的观念主要集中在 parents ,而不是类型。所以,请不要混淆C中的类型转换和Sage中的转换!
我们在这里给出一个相当简短的说明。有关实施的详细说明和信息,请参阅参考手册中关于胁迫的部分和 thematic tutorial 。
对于使用以下元素进行算术的可能性,有两种极端的观点 different 戒指:
不同的环是不同的世界,将不同环的元素相加或相乘是没有任何意义的;甚至
1 + 1/2
没有意义,因为第一个被加数是一个整数,第二个是有理数。
或
如果一个元素
r1
一环中的R1
能以某种方式在另一个环中被解释R2
,则涉及的所有算术运算r1
以及任何元素R2
是被允许的。乘法单位存在于所有的域和许多环中,并且它们都应该相等。
Sage倾向于妥协。如果 P1
和 P2
是父结构,并且 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 (或自动)转换只有在可以完成时才会发生 thoroughly 和 consistently 。在这一点上,数学上的严谨是至关重要的。
这种隐式转换称为 coercion 。如果定义了强制,那么它必须与转换一致。要定义胁迫,必须满足两个条件:
来自中国的胁迫
P1
至P2
必须由结构保持映射(例如,环同态)给出。这还不够 some 元素:P1
可以映射到P2
,并且映射必须遵守的代数结构P1
。这些强制映射的选择必须一致:如果
P3
是第三个母体结构,那么所选择的胁迫的组成来自P1
至P2
有了来自于P2
至P3
必须与所选择的来自P1
至P3
。特别是,如果有来自P1
至P2
和P2
至P1
,组成必须是的身份地图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
),但不是反过来。究其原因,是胁迫的成分来自 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试图选择一种 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']
,因为所有这四个成对不同的结构看起来都是自然的共同父母,没有明显的规范选择。