父母、皈依和强迫

这一节似乎比前一节更具技术性,但我们相信理解父项和强制的含义对于有效和高效地使用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)
<type 'sage.rings.polynomial.polynomial_integer_dense_flint.Polynomial_integer_dense_flint'>
<class 'sage.rings.polynomial.polynomial_ring.PolynomialRing_integral_domain_with_category.element_class'>
<type '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 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

同时 父母 是独一无二的,平等的 元素 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 .

关于用 不同的 环:

  • 不同的环是不同的世界,对不同环的元素进行加法或乘法是没有意义的;甚至 1 + 1/2 没有意义,因为第一个和是整数,第二个是有理数。

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

Sage赞成妥协。如果 P1P2 是父结构和 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

但是,一个 隐性的 (或自动)转换只有在可以进行时才会发生 彻底一贯地 . 在这一点上,数学的严谨是必不可少的。

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

  1. 来自 P1P2 必须由结构保持映射给出(例如,环同态)。这还不够 some 要素 P1 可以映射到 P2 ,并且映射必须尊重 P1 .

  2. 这些强制映射的选择必须一致:如果 P3 是第三个父结构,则从中选择强制成分 P1P2 在胁迫下 P2P3 必须与所选择的胁迫一致 P1P3 . 特别是如果 P1P2P2P1 ,组成必须是 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: 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'] ,则映射将导致既不保留名称也不保留位置,这违反了一致性。

如果有强制,它将被用来比较不同环的元素或做算术。这通常很方便,但是用户应该知道扩展 == -不同父母之间的关系很容易导致过度。例如,while == 应该是元素的等价关系 one 环,如果 不同的 涉及环。例如, 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 )但不是相反。究其原因,是胁迫的构成 QQRRRRQQ 应该是身份证上的 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试图选择 典范的 共同的父母似乎是最自然的 (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'] ,因为这四个成对不同的结构看起来都是自然的共同父母,没有明显的典型选择。