SageMath 基本代数

archive time: 2024-09-05

这个算是 SageMath 的强项,不过还是不太方便

常见的环

我们在 SageMath 中进行代数运算的时候,例如微积分,或者后面会提到的向量和矩阵运算, 有时必须要指定一个环才能够计算出想要的结果,所以,了解 SageMath 中有哪些常见的环是非常有必要的

环简介

那么,什么是环呢?

环,英文是 Ring,是数学上的一个结构,这个结构满足如下性质:

  • 是一个集合,并且在这个集合上定义有两种运算,
  • 在集合中, 有么元,记为
  • 在集合中,每个元素 都有对应的 逆元,记为
  • 在集合中, 满足交换律,即对于集合元素 ,满足
  • 在集合中, 满足结合律,即对于集合元素 ,满足
  • 在集合中, 有么元,记为
  • 在集合中, 满足结合律,即对于集合元素 ,满足
  • 在集合中, 满足左分配律,即对于集合元素 ,满足
  • 在集合中, 满足右分配律,即对于集合元素 ,满足

我们可以来验证一下,一个最常见的环就是整数环 ,在 SageMath 中表示为 ZZ

# 定义整数环,并且定义整数环上的三个元素,a,b 和 c
R = PolynomialRing(ZZ, "a, b, c")
(a, b, c) = (R.gen(0), R.gen(1), R.gen(2))
# 定义有加法和乘法
_ = a + b
_ = a * b
# 加法有么元
zero = R(0)
assert(a == zero + a == a + zero)
# 加法有逆元
assert(zero == a - a)
# 加法有交换律
assert(a + b == b + a)
# 加法有结合律
assert((a + b) + c == a + (b + c))
# 乘法有么元
one = R(1)
assert(a == a * one == one * a)
# 乘法有结合律
assert((a * b) * c == a * (b * c))
# 乘法有左分配律
assert(a * (b + c) == a * b + a * c)
# 乘法有右分配律
assert((a + b) * c == a * c + b * c)

此外还有例如有理数环 对应 QQ,实数环 对应 RR,以及复数环 对应 CC

但是对于这些常见的环,例如有理数环 ,我们有个更加熟悉的名称,那就是有理数域,这又是怎么回事呢?

域,对应英文 Field,也是数学上的一种结构,并且是 环 的一种特殊情况,域在满足环的所有特点的同时还满足:

  • 在集合上,除了 么元,即 ,外的任一元素 都有对应 逆元,记作:
  • 在集合上, 满足交换律,即对于集合元素 ,满足
# 定义实数域元素,其中 a 和 b 属于实数域
a, b = var("a, b", domain=RR)
# 除了 0 以外的元素 a 有逆元 1/a
one = RR(1)
assert(one == a / a)
# 乘法有交换律
assert(a * b == b * a)

所以并不存在整数域,因为整数没有除法,并且因为环的定义更加灵活,所以在 SageMath 中更加常用的是环

环的应用

下面的例子展现了在不同的环下运算结果的差异:

ratioRing.<t> = PolynomialRing(QQ)
realRing.<z> = PolynomialRing(RR)
tuple(map(lambda x: factor(x^2 - 2), (t, z)))
# => (t^2 - 2, (z - 1.41421356237310) * (z + 1.41421356237310))

可以发现,在有理数域上的变量 t,因为不包含无理数 ,所以无法对 进行因式分解,而实数 z 就可以

对于某些预定义名称,如果不小心覆盖了其定义,可以使用 reset() 函数复原,例如 reset("ZZ")

要判断某一个数或者变量是否属于某一个环或者域,我们可以通过 in 这个操作符来查看:

(t in ratioRing, z in ratioRing, 2 in ratioRing, sqrt(2) in ratioRing)
# => (True, False, True, False)

我们还可以通过 parent() 方法查看某一变量的归属, 并且在遇到冲突时,例如一个有理数和实数的运算, 那么 SageMath 会自动使用类型转换,尽可能的符合运算的定义,例如:

((2/3).parent(), (sqrt(2)).parent(), (2/3 + sqrt(2)).parent())

除了常见的这些环以外,SageMath 还支持很多数学结构,例如有限域以及矩阵环,下面是一些例子:

# 3 个元素的有限域
GF(3)
# 5 进数整数环
Zp(5)
# sqrt(3) 属于代数域
assert(sqrt(3) in QQbar)

线性代数

常见示例

SageMath 基于 Numpy 和 Scipy 等数学工具,自然是支持线性代数的, 包括矩阵,向量以及特征多项式等等,下面是一些示例:

A = Matrix([[1, 2, 3], [3, 2, 1], [1, 1, 1]])
w = vector([1, 1, -4])
(w * A, A * w, kernel(A))
# =>
# ((0, 0, 0),
#  (-9, 1, -2),
#  Free module of degree 3 and rank 1 over Integer Ring
#  Echelon basis matrix:
#  [ 1  1 -4])

其中 kernel() 函数返回对应矩阵的 左核,即

对于可以求解的矩阵,SageMath 会返回对应方程组的解,否则会返回一个错误:

A = Matrix([[1, 2, 3], [3, 2, 1], [1, 1, 1]])
Y = vector([0, -4, -1])
X = A.solve_right(Y) # A . X = Y
X
# => (-2, 1, 0)
# A.solve_right(w) 会返回错误

一般的特征值和特征向量可以通过 eigenvalues()eigenvectors_right() 求得:

A = matrix([[0, 4], [-1, 0]])
(A.eigenvalues(), A.eigenvectors_right())
# => ([-2*I, 2*I],
#     [(-2*I, [(1, -0.50000000000000000?*I)], 1),
#      (2*I, [(1, 0.50000000000000000?*I)], 1)])

eigenvectors_left() 求解的是 , 而 eigenvectors_right() 则是

矩阵定义

矩阵也可以在定义时指定所属的环或者域,例如:

AZ = matrix(ZZ, [[2, 0], [0, 1]])
AQ = matrix(QQ, [[2, 0], [0, 1]])

不同的定义计算结果也会有所不同,例如化为阶梯式:

(AZ.echelon_form(), AQ.echelon_form())
# =>
# (
# [2 0]  [1 0]
# [0 1], [0 1]
# )

通常为了能够计算,并且计算结果,会将矩阵定义在 RDF 或者 CDF 上,即 实/复双精度域

矩阵空间

我们可以通过 MatrixSpace() 来定义矩阵空间,即所有满足某条件的矩阵构成的集合,例如

M = MatrixSpace(QQ, 3)
M
# => Full MatrixSpace of 3 by 3 dense matrices over Rational Field

如果想要定义非方阵,那么可以用 MatrixSpace(R, a, b) 来定义 R 上的 ab 列的矩阵空间

我们还可以通过 basis() 函数查看矩阵空间的标准基:

B = M.basis()
(len(B), B[0, 1])
# =>
# (
#    [0 1 0]
#    [0 0 0]
# 9, [0 0 0]
# )

对于一个矩阵空间 M,我们可以通过给定元素来构造一个矩阵:

A = M(range(2, 11))
A
# =>
# [ 2  3  4]
# [ 5  6  7]
# [ 8  9 10]

我们有很多种方式来展示矩阵,例如通过 rows() 或者 columns() 来获取矩阵所有的的行或者列

这非常有用,例如可以用矩阵的行来 张(span) 出一个向量空间:

V = VectorSpace(QQ, 3)
S = V.subspace(A.rows())
S
# =>
# Vector space of degree 3 and dimension 2 over Rational Field
# Basis matrix:
# [ 1  0 -1]
# [ 0  1  2]

稀疏线性代数

注意到上述矩阵空间都是 dense matrices,也就是 稠密的, 但是现实中很多数据构成的矩阵并没有那么稠密,而是非常空的,或者称为 稀疏的

我们可以用相似的方式来运算稀疏线性代数, SageMath 的稀疏线性代数基于 PIDs,也就是主理想整数环

M = MatrixSpace(QQ, 8, sparse=True)
A = M.random_element(density=0.2)
(A, A.echelon_form())
# =>
# (
# [    0     0 -6/23     0    -3     0     3  -1/7]
# [    0  -1/3     0     0     0     0     0     0]
# [    0     0     0     0     0     0     0     0]
# [    0 -23/3     0     0     0     0     0     1]
# [    0    -3     0     0     0     0     0     0]
# [    0     0     0     0     0     0     0     0]
# [    3     0     0     0     0     0     0     0]
# [    0     0     0    15     1     1     0     0],
#
# [    1     0     0     0     0     0     0     0]
# [    0     1     0     0     0     0     0     0]
# [    0     0     1     0  23/2     0 -23/2     0]
# [    0     0     0     1  1/15  1/15     0     0]
# [    0     0     0     0     0     0     0     1]
# [    0     0     0     0     0     0     0     0]
# [    0     0     0     0     0     0     0     0]
# [    0     0     0     0     0     0     0     0]
# )

后记

SageMath 由于集成了很多代数方面的工具, 所以在一些数论和代数相关的方面, SageMath 比起一些付费的 CAS 还要全面, 但是由于一些命名和使用方式和主流的名称有所区别, 使用起来还需要多注意,特别是多个 CAS 混用的时候