编程

加载和附加 Sage 文件

接下来我们说明如何将写在单独文件中的程序加载到 Sage 中。 创建一个名为 example.sage 的文件,并写入以下内容:

print("Hello World")
print(2^3)

你可以使用 load 命令读取并执行 example.sage 文件。

sage: load("example.sage")
Hello World
8
>>> from sage.all import *
>>> load("example.sage")
Hello World
8

你也可以使用 attach 命令将 Sage 文件附加到运行的会话中:

sage: attach("example.sage")
Hello World
8
>>> from sage.all import *
>>> attach("example.sage")
Hello World
8

现在如果你修改 example.sage 文件并在 Sage 中输入一个空行(即按下回车键), 那么 example.sage 的内容将会自动重新加载到 Sage 中。

特别是,attach 命令会在文件更改时自动重新加载文件,这在调试代码时非常方便, 而 load 命令仅加载文件一次。

当 Sage 加载 example.sage 时,它会将其转换为 Python,然后由 Python 解释器执行。 此转换非常简单;它主要是将整型字面量包装在 Integer() 中, 将浮点型字面量包装在 RealNumber() 中, 将 ^ 替换为 **,并将例如 R.2 替换为 R.gen(2)。 转换后的 example.sage 版本包含在与 example.sage 相同的目录中, 名为 example.sage.py。该文件包含以下代码:

print("Hello World")
print(Integer(2)**Integer(3))

整型字面量被包装,^ 被替换为 **。 (在 Python 中,^ 表示“异或”,而 ** 表示“幂运算”。)

(这种预解析由 sage/misc/interpreter.py 模块实现。)

只要有换行符来创建新块(在文件中则无需如此),你就可以将多行缩进代码粘贴到 Sage 中。 然而,更好的方式是将这些代码保存到文件中,并如上所述使用 attach 命令来加载。

创建编译代码

速度在数学计算中至关重要,因为更快的计算可以大大提高效率。 尽管 Python 是一种非常方便的高级语言,但如果使用静态类型的编译型语言实现某些计算, 其速度可以比用 Python 实现快几个数量级。如果 Sage 完全用 Python 编写, 那么在某些方面速度会过于缓慢。为了应对这种情况,Sage 支持一种编译“版本”的 Python,称为 Cython ([Cyt][Pyr])。 Cython 类似于 Python 和 C 语言。大多数 Python 结构,包括列表推导式、条件表达式、 类似 += 这样的代码都支持;你还可以导入其他 Python 模块中编写的代码。 此外,你还可以声明任意的 C 变量,并直接调用任意的 C 库函数。生成的代码会转换为 C,并使用 C 编译器进行编译。

为了创建你自己的编译 Sage 代码,请将文件命名为 .spyx 扩展名(而非 .sage)。 如果使用命令行界面,你可以像处理解释代码一样附加和加载编译代码(目前,Notebook 界面不支持附加和加载 Cython 代码)。 实际编译是在“后台”完成的,你无需进行任何显式操作。编译后的共享对象库存储在 $HOME/.sage/temp/hostname/pid/spyx 中。 这些文件将在退出 Sage 时删除。

Sage 预解析不适用于 spyx 文件,例如,1/3 在 spyx 文件中结果为 0, 而不是有理数 \(1/3\)。如果 foo 是 Sage 库中的一个函数,要想在 spyx 文件中使用它, 请导入 sage.all 并使用 sage.all.foo

import sage.all
def foo(n):
    return sage.all.factorial(n)

访问单独文件中的 C 函数

访问定义在单独 *.c 文件中的 C 函数也很容易。 以下是一个示例。在同一目录下创建文件 test.ctest.spyx,内容如下:

纯 C 代码:test.c

int add_one(int n) {
  return n + 1;
}

Cython 代码:test.spyx:

cdef extern from "test.c":
    int add_one(int n)

def test(n):
    return add_one(n)

然后进行以下操作:

sage: attach("test.spyx")
Compiling (...)/test.spyx...
sage: test(10)
11
>>> from sage.all import *
>>> attach("test.spyx")
Compiling (...)/test.spyx...
>>> test(Integer(10))
11

如果需要额外的库 foo 来编译从 Cython 文件生成的 C 代码, 在 Cython 源代码中添加 clib foo。 类似地,可以使用声明 cfile bar 将额外的 C 文件 bar 包含在编译中。

独立 Python/Sage 脚本

以下独立 Sage 脚本可以分解整数、多项式等:

#!/usr/bin/env sage

import sys

if len(sys.argv) != 2:
    print("Usage: %s <n>" % sys.argv[0])
    print("Outputs the prime factorization of n.")
    sys.exit(1)

print(factor(sage_eval(sys.argv[1])))

为了使用此脚本,SAGE_ROOT 必须包含在 PATH 中。如果将上述脚本命名为 factor,则以下是使用示例:

$ ./factor 2006
2 * 17 * 59

数据类型

在 Sage 中,每个对象都有一个明确的类型。Python 有各种基本内置类型, 而 Sage 库还增加了更多类型。Python 内置类型包括字符串、列表、元组、整型和浮点型等,如下所示:

sage: s = "sage"; type(s)
<... 'str'>
sage: s = 'sage'; type(s)      # you can use either single or double quotes
<... 'str'>
sage: s = [1,2,3,4]; type(s)
<... 'list'>
sage: s = (1,2,3,4); type(s)
<... 'tuple'>
sage: s = int(2006); type(s)
<... 'int'>
sage: s = float(2006); type(s)
<... 'float'>
>>> from sage.all import *
>>> s = "sage"; type(s)
<... 'str'>
>>> s = 'sage'; type(s)      # you can use either single or double quotes
<... 'str'>
>>> s = [Integer(1),Integer(2),Integer(3),Integer(4)]; type(s)
<... 'list'>
>>> s = (Integer(1),Integer(2),Integer(3),Integer(4)); type(s)
<... 'tuple'>
>>> s = int(Integer(2006)); type(s)
<... 'int'>
>>> s = float(Integer(2006)); type(s)
<... 'float'>

除此之外,Sage 还添加了许多其他类型。例如,向量空间:

sage: V = VectorSpace(QQ, 1000000); V
Vector space of dimension 1000000 over Rational Field
sage: type(V)
<class 'sage.modules.free_module.FreeModule_ambient_field_with_category'>
>>> from sage.all import *
>>> V = VectorSpace(QQ, Integer(1000000)); V
Vector space of dimension 1000000 over Rational Field
>>> type(V)
<class 'sage.modules.free_module.FreeModule_ambient_field_with_category'>

只有某些函数可以在 V 上调用。 在其他数学软件系统中,这些函数可以使用“函数”符号 foo(V,...) 来调用。 在 Sage 中,某些函数附加到 V 类型(或类),并使用与 Java 或 C++ 类似的面向对象语法调用, 例如 V.foo(...)。这种方式有助于保持全局命名空间的整洁,并允许名称相同但行为不同的函数存在, 而无需通过参数类型检查(或 case 语句)来决定调用哪个函数。此外,如果你重复使用函数名,该函数仍然可用 (例如,如果你调用某个函数 zeta,那么要计算 Riemann-Zeta 函数在 0.5 处的值, 可以输入 s=.5; s.zeta())。

sage: zeta = -1
sage: s=.5; s.zeta()
-1.46035450880959
>>> from sage.all import *
>>> zeta = -Integer(1)
>>> s=RealNumber('.5'); s.zeta()
-1.46035450880959

在某些非常常见的情况下,为了方便起见, 同时避免使用面向对象符号可能导致数学表达式看起来令人困惑, Sage 也支持常规的函数符号。这里有一些例子。

sage: n = 2; n.sqrt()
sqrt(2)
sage: sqrt(2)
sqrt(2)
sage: V = VectorSpace(QQ,2)
sage: V.basis()
    [(1, 0), (0, 1)]
sage: basis(V)
    [(1, 0), (0, 1)]
sage: M = MatrixSpace(GF(7), 2); M
Full MatrixSpace of 2 by 2 dense matrices over Finite Field of size 7
sage: A = M([1,2,3,4]); A
[1 2]
[3 4]
sage: A.charpoly('x')
x^2 + 2*x + 5
sage: charpoly(A, 'x')
x^2 + 2*x + 5
>>> from sage.all import *
>>> n = Integer(2); n.sqrt()
sqrt(2)
>>> sqrt(Integer(2))
sqrt(2)
>>> V = VectorSpace(QQ,Integer(2))
>>> V.basis()
    [(1, 0), (0, 1)]
>>> basis(V)
    [(1, 0), (0, 1)]
>>> M = MatrixSpace(GF(Integer(7)), Integer(2)); M
Full MatrixSpace of 2 by 2 dense matrices over Finite Field of size 7
>>> A = M([Integer(1),Integer(2),Integer(3),Integer(4)]); A
[1 2]
[3 4]
>>> A.charpoly('x')
x^2 + 2*x + 5
>>> charpoly(A, 'x')
x^2 + 2*x + 5

要列出 \(A\) 的所有成员函数,请使用 tab 补全功能。 只需输入 A.,然后在键盘上按 [tab] 键即可, 如 反向搜索与 Tab 补全 中所述。

列表、元组和序列

列表数据类型具有存储任意类型元素的功能。 和 C、C++ 等语言类似(但与大多数标准的计算机代数系统不同),列表元素的索引是从 \(0\) 开始的:

sage: v = [2, 3, 5, 'x', SymmetricGroup(3)]; v
[2, 3, 5, 'x', Symmetric group of order 3! as a permutation group]
sage: type(v)
<... 'list'>
sage: v[0]
2
sage: v[2]
5
>>> from sage.all import *
>>> v = [Integer(2), Integer(3), Integer(5), 'x', SymmetricGroup(Integer(3))]; v
[2, 3, 5, 'x', Symmetric group of order 3! as a permutation group]
>>> type(v)
<... 'list'>
>>> v[Integer(0)]
2
>>> v[Integer(2)]
5

(在列表中进行索引时,不一定需要使用 Python 的整型作为索引!) Sage 整数(或有理数,或任何具有 __index__ 方法的对象)都可以正常使用。

sage: v = [1,2,3]
sage: v[2]
3
sage: n = 2      # Sage Integer
sage: v[n]       # Perfectly OK!
3
sage: v[int(n)]  # Also OK.
3
>>> from sage.all import *
>>> v = [Integer(1),Integer(2),Integer(3)]
>>> v[Integer(2)]
3
>>> n = Integer(2)      # Sage Integer
>>> v[n]       # Perfectly OK!
3
>>> v[int(n)]  # Also OK.
3

range 函数创建一个包含 Python 整型(而不是 Sage 整数)元素的列表:

sage: list(range(1, 15))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
>>> from sage.all import *
>>> list(range(Integer(1), Integer(15)))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]

该函数在使用列表推导式构造列表时非常有用:

sage: L = [factor(n) for n in range(1, 15)]
sage: L
[1, 2, 3, 2^2, 5, 2 * 3, 7, 2^3, 3^2, 2 * 5, 11, 2^2 * 3, 13, 2 * 7]
sage: L[12]
13
sage: type(L[12])
<class 'sage.structure.factorization_integer.IntegerFactorization'>
sage: [factor(n) for n in range(1, 15) if is_odd(n)]
[1, 3, 5, 7, 3^2, 11, 13]
>>> from sage.all import *
>>> L = [factor(n) for n in range(Integer(1), Integer(15))]
>>> L
[1, 2, 3, 2^2, 5, 2 * 3, 7, 2^3, 3^2, 2 * 5, 11, 2^2 * 3, 13, 2 * 7]
>>> L[Integer(12)]
13
>>> type(L[Integer(12)])
<class 'sage.structure.factorization_integer.IntegerFactorization'>
>>> [factor(n) for n in range(Integer(1), Integer(15)) if is_odd(n)]
[1, 3, 5, 7, 3^2, 11, 13]

有关如何使用列表推导式创建列表的更多内容,请参考 [PyT]

列表切片是一个非常好的功能。如果 L 是一个列表,那么 L[m:n] 返回 从第 \(m\) 个元素开始到第 \((n-1)\) 个元素结束的子列表,如下所示:

sage: L = [factor(n) for n in range(1, 20)]
sage: L[4:9]
[5, 2 * 3, 7, 2^3, 3^2]
sage: L[:4]
[1, 2, 3, 2^2]
sage: L[14:4]
[]
sage: L[14:]
[3 * 5, 2^4, 17, 2 * 3^2, 19]
>>> from sage.all import *
>>> L = [factor(n) for n in range(Integer(1), Integer(20))]
>>> L[Integer(4):Integer(9)]
[5, 2 * 3, 7, 2^3, 3^2]
>>> L[:Integer(4)]
[1, 2, 3, 2^2]
>>> L[Integer(14):Integer(4)]
[]
>>> L[Integer(14):]
[3 * 5, 2^4, 17, 2 * 3^2, 19]

元组与列表类似,只不过它是不可变的,一旦创建便不能更改。

sage: v = (1,2,3,4); v
(1, 2, 3, 4)
sage: type(v)
<... 'tuple'>
sage: v[1] = 5
Traceback (most recent call last):
...
TypeError: 'tuple' object does not support item assignment
>>> from sage.all import *
>>> v = (Integer(1),Integer(2),Integer(3),Integer(4)); v
(1, 2, 3, 4)
>>> type(v)
<... 'tuple'>
>>> v[Integer(1)] = Integer(5)
Traceback (most recent call last):
...
TypeError: 'tuple' object does not support item assignment

序列是第三种面向列表的 Sage 类型。与列表和元组不同,序列不是 Python 内置类型。 默认情况下,序列是可变的,但可以使用 Sequence 类方法 set_immutable 将其设置为不可变, 如以下例子所示。序列的所有元素都属于同一个父对象,称为序列的领域 (universe)。

sage: v = Sequence([1,2,3,4/5])
sage: v
[1, 2, 3, 4/5]
sage: type(v)
<class 'sage.structure.sequence.Sequence_generic'>
sage: type(v[1])
<class 'sage.rings.rational.Rational'>
sage: v.universe()
Rational Field
sage: v.is_immutable()
False
sage: v.set_immutable()
sage: v[0] = 3
Traceback (most recent call last):
...
ValueError: object is immutable; please change a copy instead.
>>> from sage.all import *
>>> v = Sequence([Integer(1),Integer(2),Integer(3),Integer(4)/Integer(5)])
>>> v
[1, 2, 3, 4/5]
>>> type(v)
<class 'sage.structure.sequence.Sequence_generic'>
>>> type(v[Integer(1)])
<class 'sage.rings.rational.Rational'>
>>> v.universe()
Rational Field
>>> v.is_immutable()
False
>>> v.set_immutable()
>>> v[Integer(0)] = Integer(3)
Traceback (most recent call last):
...
ValueError: object is immutable; please change a copy instead.

序列派生自列表,可以在任何需要列表的地方使用:

sage: v = Sequence([1,2,3,4/5])
sage: isinstance(v, list)
True
sage: list(v)
[1, 2, 3, 4/5]
sage: type(list(v))
<... 'list'>
>>> from sage.all import *
>>> v = Sequence([Integer(1),Integer(2),Integer(3),Integer(4)/Integer(5)])
>>> isinstance(v, list)
True
>>> list(v)
[1, 2, 3, 4/5]
>>> type(list(v))
<... 'list'>

另一个例子是,向量空间的基是不可变序列,因为不能改变它们至关重要。

sage: V = QQ^3; B = V.basis(); B
[(1, 0, 0), (0, 1, 0), (0, 0, 1)]
sage: type(B)
<class 'sage.structure.sequence.Sequence_generic'>
sage: B[0] = B[1]
Traceback (most recent call last):
...
ValueError: object is immutable; please change a copy instead.
sage: B.universe()
Vector space of dimension 3 over Rational Field
>>> from sage.all import *
>>> V = QQ**Integer(3); B = V.basis(); B
[(1, 0, 0), (0, 1, 0), (0, 0, 1)]
>>> type(B)
<class 'sage.structure.sequence.Sequence_generic'>
>>> B[Integer(0)] = B[Integer(1)]
Traceback (most recent call last):
...
ValueError: object is immutable; please change a copy instead.
>>> B.universe()
Vector space of dimension 3 over Rational Field

字典

字典(有时也被称为关联数组)是从“可哈希”对象(例如字符串、数字和元组等;详情请参见 Python 文档 http://docs.python.org/tut/node7.htmlhttp://docs.python.org/lib/typesmapping.html ) 到任意对象的映射。

sage: d = {1:5, 'sage':17, ZZ:GF(7)}
sage: type(d)
<... 'dict'>
sage: list(d.keys())
[1, 'sage', Integer Ring]
sage: d['sage']
17
sage: d[ZZ]
Finite Field of size 7
sage: d[1]
5
>>> from sage.all import *
>>> d = {Integer(1):Integer(5), 'sage':Integer(17), ZZ:GF(Integer(7))}
>>> type(d)
<... 'dict'>
>>> list(d.keys())
[1, 'sage', Integer Ring]
>>> d['sage']
17
>>> d[ZZ]
Finite Field of size 7
>>> d[Integer(1)]
5

第三个键说明字典的索引可以很复杂,例如整数环。

你可以将上述字典转换为具有相同数据的列表:

sage: list(d.items())
[(1, 5), ('sage', 17), (Integer Ring, Finite Field of size 7)]
>>> from sage.all import *
>>> list(d.items())
[(1, 5), ('sage', 17), (Integer Ring, Finite Field of size 7)]

一种常见用法是遍历字典中的键值对:

sage: d = {2:4, 3:9, 4:16}
sage: [a*b for a, b in d.items()]
[8, 27, 64]
>>> from sage.all import *
>>> d = {Integer(2):Integer(4), Integer(3):Integer(9), Integer(4):Integer(16)}
>>> [a*b for a, b in d.items()]
[8, 27, 64]

正如最后的输出所示,字典是无序的。

集合

Python 有内建的集合类型。它提供的主要功能是快速查找元素是否在集合中,以及标准集合论运算。

sage: X = set([1,19,'a']);   Y = set([1,1,1, 2/3])
sage: X   # random sort order
{1, 19, 'a'}
sage: X == set(['a', 1, 1, 19])
True
sage: Y
{2/3, 1}
sage: 'a' in X
True
sage: 'a' in Y
False
sage: X.intersection(Y)
{1}
>>> from sage.all import *
>>> X = set([Integer(1),Integer(19),'a']);   Y = set([Integer(1),Integer(1),Integer(1), Integer(2)/Integer(3)])
>>> X   # random sort order
{1, 19, 'a'}
>>> X == set(['a', Integer(1), Integer(1), Integer(19)])
True
>>> Y
{2/3, 1}
>>> 'a' in X
True
>>> 'a' in Y
False
>>> X.intersection(Y)
{1}

Sage 也有自己的集合类型(在某些情况下使用 Python 内建集合类型实现), 但具有一些与 Sage 相关的额外功能。使用 Set(...) 来创建 Sage 集合。例如:

sage: X = Set([1,19,'a']);   Y = Set([1,1,1, 2/3])
sage: X   # random sort order
{'a', 1, 19}
sage: X == Set(['a', 1, 1, 19])
True
sage: Y
{1, 2/3}
sage: X.intersection(Y)
{1}
sage: print(latex(Y))
\left\{1, \frac{2}{3}\right\}
sage: Set(ZZ)
Set of elements of Integer Ring
>>> from sage.all import *
>>> X = Set([Integer(1),Integer(19),'a']);   Y = Set([Integer(1),Integer(1),Integer(1), Integer(2)/Integer(3)])
>>> X   # random sort order
{'a', 1, 19}
>>> X == Set(['a', Integer(1), Integer(1), Integer(19)])
True
>>> Y
{1, 2/3}
>>> X.intersection(Y)
{1}
>>> print(latex(Y))
\left\{1, \frac{2}{3}\right\}
>>> Set(ZZ)
Set of elements of Integer Ring

迭代器

迭代器是 Python 最近添加的功能,在数学应用中特别有用。 这里有几个例子;详情请参见 [PyT] 。我们创建一个非负整数平方的迭代器,上限为 \(10000000\)

sage: v = (n^2 for n in range(10000000))
sage: next(v)
0
sage: next(v)
1
sage: next(v)
4
>>> from sage.all import *
>>> v = (n**Integer(2) for n in range(Integer(10000000)))
>>> next(v)
0
>>> next(v)
1
>>> next(v)
4

我们创建一个 \(4p+1\) 形式的素数迭代器,其中 \(p\) 也是素数,并查看前几个值。

sage: w = (4*p + 1 for p in Primes() if is_prime(4*p+1))
sage: w         # in the next line, 0xb0853d6c is a random 0x number
<generator object at 0xb0853d6c>
sage: next(w)
13
sage: next(w)
29
sage: next(w)
53
>>> from sage.all import *
>>> w = (Integer(4)*p + Integer(1) for p in Primes() if is_prime(Integer(4)*p+Integer(1)))
>>> w         # in the next line, 0xb0853d6c is a random 0x number
<generator object at 0xb0853d6c>
>>> next(w)
13
>>> next(w)
29
>>> next(w)
53

某些环,例如有限域和整数环有与之关联的迭代器:

sage: [x for x in GF(7)]
[0, 1, 2, 3, 4, 5, 6]
sage: W = ((x,y) for x in ZZ for y in ZZ)
sage: next(W)
(0, 0)
sage: next(W)
(0, 1)
sage: next(W)
(0, -1)
>>> from sage.all import *
>>> [x for x in GF(Integer(7))]
[0, 1, 2, 3, 4, 5, 6]
>>> W = ((x,y) for x in ZZ for y in ZZ)
>>> next(W)
(0, 0)
>>> next(W)
(0, 1)
>>> next(W)
(0, -1)

循环、函数、控制语句和比较

我们已经看过了一些常见的 for 循环用法示例。在 Python 中,for 循环具有缩进结构,例如:

>>> for i in range(5):
...     print(i)
...
0
1
2
3
4

请注意 for 语句末尾的冒号(不像 GAP 或 Maple 中有 "do" 或 "od"), 以及循环体(即 print(i))前的缩进。这个缩进非常重要。 在 Sage 中,当你在 ":" 后按下 enter 时,会自动添加缩进,如下所示。

sage: for i in range(5):
....:     print(i)  # now hit enter twice
....:
0
1
2
3
4
>>> from sage.all import *
>>> for i in range(Integer(5)):
...     print(i)  # now hit enter twice
....:
0
1
2
3
4

符号 = 用于赋值。 符号 == 用于检查相等:

sage: for i in range(15):
....:     if gcd(i,15) == 1:
....:         print(i)
....:
1
2
4
7
8
11
13
14
>>> from sage.all import *
>>> for i in range(Integer(15)):
...     if gcd(i,Integer(15)) == Integer(1):
...         print(i)
....:
1
2
4
7
8
11
13
14

请牢记缩进如何决定 if, forwhile 语句的块结构:

sage: def legendre(a,p):
....:     is_sqr_modp=-1
....:     for i in range(p):
....:         if a % p == i^2 % p:
....:             is_sqr_modp=1
....:     return is_sqr_modp

sage: legendre(2,7)
1
sage: legendre(3,7)
-1
>>> from sage.all import *
>>> def legendre(a,p):
...     is_sqr_modp=-Integer(1)
...     for i in range(p):
...         if a % p == i**Integer(2) % p:
...             is_sqr_modp=Integer(1)
...     return is_sqr_modp

>>> legendre(Integer(2),Integer(7))
1
>>> legendre(Integer(3),Integer(7))
-1

当然,这不是勒让德符号 (Legendre symbol) 的高效实现! 它只是为了说明 Python/Sage 编程的各个方面。Sage 附带的函数 {kronecker}, 可以通过调用 PARI 的 C 库高效地计算勒让德符号。

最后,我们注意到数字之间的比较,如 ==, !=, <=, >=, >, <, 会自动将两个数字转换为相同类型(如果可能的话):

sage: 2 < 3.1; 3.1 <= 1
True
False
sage: 2/3 < 3/2;   3/2 < 3/1
True
True
>>> from sage.all import *
>>> Integer(2) < RealNumber('3.1'); RealNumber('3.1') <= Integer(1)
True
False
>>> Integer(2)/Integer(3) < Integer(3)/Integer(2);   Integer(3)/Integer(2) < Integer(3)/Integer(1)
True
True

使用 bool 来判断符号不等式:

sage: x < x + 1
x < x + 1
sage: bool(x < x + 1)
True
>>> from sage.all import *
>>> x < x + Integer(1)
x < x + 1
>>> bool(x < x + Integer(1))
True

在比较不同类型的对象时,在大多数情况下,Sage 会尝试找到两者的共同复结构 (参见 父结构、转换与强制转换 了解更多细节)。 如果成功,比较将在强制转换的对象之间进行;如果不成功,则认为对象不相等。 要测试两个变量是否引用同一个对象,请使用 is。 在下面这个示例中我们将看到,Python 整型 1 是唯一的,而 Sage 整型 1 则不是:

sage: 1 is 2/2
False
sage: 1 is 1
False
sage: 1 == 2/2
True
>>> from sage.all import *
>>> Integer(1) is Integer(2)/Integer(2)
False
>>> Integer(1) is Integer(1)
False
>>> Integer(1) == Integer(2)/Integer(2)
True

在以下两行代码中,第一个等式为 False,因为没有从 \(\QQ \to \GF{5}\) 的标准同态, 因此无法将 \(\GF{5}\) 中的 \(1\)\(1 \in \QQ\) 进行比较。 相反,由于存在从 \(\ZZ \to \GF{5}\) 的标准映射,因此第二个比较为 True。 需要注意的是,顺序不影响结果。

sage: GF(5)(1) == QQ(1); QQ(1) == GF(5)(1)
False
False
sage: GF(5)(1) == ZZ(1); ZZ(1) == GF(5)(1)
True
True
sage: ZZ(1) == QQ(1)
True
>>> from sage.all import *
>>> GF(Integer(5))(Integer(1)) == QQ(Integer(1)); QQ(Integer(1)) == GF(Integer(5))(Integer(1))
False
False
>>> GF(Integer(5))(Integer(1)) == ZZ(Integer(1)); ZZ(Integer(1)) == GF(Integer(5))(Integer(1))
True
True
>>> ZZ(Integer(1)) == QQ(Integer(1))
True

警告: Sage 中的比较比 Magma 更严格,Magma 会声明 \(1 \in \GF{5}\) 等于 \(1 \in \QQ\)

sage: magma('GF(5)!1 eq Rationals()!1')            # optional - magma
true
>>> from sage.all import *
>>> magma('GF(5)!1 eq Rationals()!1')            # optional - magma
true

性能分析

“过早优化乃万恶之源。” - Donald Knuth

节作者: Martin Albrecht <malb@informatik.uni-bremen.de>

有时检查代码中的瓶颈有助于了解哪些部分占用最多的计算时间; 这可以很好地了解哪些部分需要优化。 Python(以及 Sage)提供了几种性能分析工具和方法, 这个过程称为性能分析。

最简单的方式是使用交互式 shell 中的 prun 命令。 它会返回一个总结,描述哪些函数花了多少计算时间。 例如,要分析有限域上的矩阵乘法(版本 1.0 当前很慢!),可以这样做:

sage: k,a = GF(2**8, 'a').objgen()
sage: A = Matrix(k,10,10,[k.random_element() for _ in range(10*10)])
>>> from sage.all import *
>>> k,a = GF(Integer(2)**Integer(8), 'a').objgen()
>>> A = Matrix(k,Integer(10),Integer(10),[k.random_element() for _ in range(Integer(10)*Integer(10))])
sage: %prun B = A*A
       32893 function calls in 1.100 CPU seconds

Ordered by: internal time

ncalls tottime percall cumtime percall filename:lineno(function)
 12127  0.160   0.000   0.160  0.000 :0(isinstance)
  2000  0.150   0.000   0.280  0.000 matrix.py:2235(__getitem__)
  1000  0.120   0.000   0.370  0.000 finite_field_element.py:392(__mul__)
  1903  0.120   0.000   0.200  0.000 finite_field_element.py:47(__init__)
  1900  0.090   0.000   0.220  0.000 finite_field_element.py:376(__compat)
   900  0.080   0.000   0.260  0.000 finite_field_element.py:380(__add__)
     1  0.070   0.070   1.100  1.100 matrix.py:864(__mul__)
  2105  0.070   0.000   0.070  0.000 matrix.py:282(ncols)
  ...
>>> from sage.all import *
>>> %prun B = A*A
       32893 function calls in 1.100 CPU seconds

Ordered by: internal time

ncalls tottime percall cumtime percall filename:lineno(function)
 12127  0.160   0.000   0.160  0.000 :0(isinstance)
  2000  0.150   0.000   0.280  0.000 matrix.py:2235(__getitem__)
  1000  0.120   0.000   0.370  0.000 finite_field_element.py:392(__mul__)
  1903  0.120   0.000   0.200  0.000 finite_field_element.py:47(__init__)
  1900  0.090   0.000   0.220  0.000 finite_field_element.py:376(__compat)
   900  0.080   0.000   0.260  0.000 finite_field_element.py:380(__add__)
     1  0.070   0.070   1.100  1.100 matrix.py:864(__mul__)
  2105  0.070   0.000   0.070  0.000 matrix.py:282(ncols)
  ...

这里 ncalls 是调用次数,tottime 是给定函数花费的总时间(不包括调用子函数的时间), percalltottime 除以 ncalls 的商。 cumtime 是该函数及所有子函数花费的总时间(即,从调用到退出), percallcumtime 除以原始调用次数的商, filename:lineno(function) 提供了每个函数的相关数据。 性能分析中的经验法则是:列表越靠前的函数,其代价越高,因而更需要进行优化。

与以往一样,prun? 命令提供了使用性能分析器和理解输出详细信息的帮助。

性能分析数据还可以保存到一个对象中,以便进行更详细的检查:

sage: %prun -r A*A
sage: stats = _
sage: stats?
>>> from sage.all import *
>>> %prun -r A*A
>>> stats = _
>>> stats?

注意:输入 stats = prun -r A\*A 会显示语法错误消息, 因为 prun 是 IPython shell 命令而不是常规函数。

为了更好地以图形化方式呈现分析数据,你可以使用 hotshot 分析器, hotshot2cachetree 脚本,以及 kcachegrind 程序(仅限 Unix)。 以下是使用 hotshot 分析器的示例:

sage: k,a = GF(2**8, 'a').objgen()
sage: A = Matrix(k,10,10,[k.random_element() for _ in range(10*10)])
sage: import hotshot
sage: filename = "pythongrind.prof"
sage: prof = hotshot.Profile(filename, lineevents=1)
>>> from sage.all import *
>>> k,a = GF(Integer(2)**Integer(8), 'a').objgen()
>>> A = Matrix(k,Integer(10),Integer(10),[k.random_element() for _ in range(Integer(10)*Integer(10))])
>>> import hotshot
>>> filename = "pythongrind.prof"
>>> prof = hotshot.Profile(filename, lineevents=Integer(1))
sage: prof.run("A*A")
<hotshot.Profile instance at 0x414c11ec>
sage: prof.close()
>>> from sage.all import *
>>> prof.run("A*A")
<hotshot.Profile instance at 0x414c11ec>
>>> prof.close()

这会在当前工作目录中生成一个 pythongrind.prof 文件。 现在可以将其转换为 cachegrind 格式进行可视化展示。

在系统终端中,输入

$ hotshot2calltree -o cachegrind.out.42 pythongrind.prof

现在,输出文件 cachegrind.out.42 可以用 kcachegrind 查看。 请注意,需要遵守命名约定 cachegrind.out.XX