编程¶
加载和附加Sage文件¶
接下来我们将演示如何将单独文件中编写的程序加载到Sage中。创建一个名为 example.sage
with the following content:
print("Hello World")
print(2^3)
你可以读入并执行 example.sage
使用文件 load
命令。
sage: load("example.sage")
Hello World
8
也可以使用将Sage文件附加到正在运行的会话 attach
命令:
sage: attach("example.sage")
Hello World
8
如果你改变了 example.sage
在Sage中输入一个空白行(即 return
),则 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中,只要有新行来生成新的块(在文件中这是不必要的)。然而,将此类代码输入Sage的最佳方法是将其保存到一个文件中并使用 attach
,如上所述。
创建编译代码¶
速度在数学计算中是至关重要的。虽然Python是一种非常方便的高级语言,但是如果在编译语言中使用静态类型实现某些计算,那么它们可以比Python快几个数量级。如果Sage是完全用Python编写的,那么它的某些方面就太慢了。为了解决这个问题,Sage支持Python的编译“版本” ([Cyt] 和 [Pyr]) . Cython同时类似于Python和C。大多数Python结构,包括列表理解、条件表达式、类似代码的 +=
允许;您还可以导入在其他Python模块中编写的代码。此外,您可以声明任意C变量,并且可以直接进行任意C库调用。生成的代码被转换成C语言并使用C编译器进行编译。
为了使您自己编译的Sage代码,给文件一个 .spyx
扩展(而不是 .sage
). 如果您使用的是命令行界面,那么您可以像使用解释代码一样附加和加载编译的代码(目前,notebook接口不支持附加和加载Cython代码)。实际的编译是在“幕后”完成的,而不必做任何明确的操作。编译的共享对象库存储在 $HOME/.sage/temp/hostname/pid/spyx
. 当您退出Sage时,这些文件将被删除。
没有Sage preparsing应用于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.c
和 test.spyx
在包含以下内容的同一目录中:
纯C代码: test.c
int add_one(int n) {
return n + 1;
}
赛顿密码: 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
如果有额外的库 foo
需要编译从Cython文件生成的C代码时,添加行 clib foo
到Cython的源头。类似地,一个附加的C文件 bar
可以在编译中加入声明 cfile bar
.
独立的Python/Sage脚本¶
以下独立的Sage脚本因子为整数、多项式等:
#!/usr/bin/env sage
import sys
from sage.all import *
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
一定在你的路上。如果调用上述脚本 factor
,以下是用法示例:
$ ./factor 2006
2 * 17 * 59
数据类型¶
Sage中的每个对象都有一个明确的类型。Python有许多基本的内置类型,Sage库还添加了更多的类型。一些内置的Python类型包括字符串、列表、元组、int和float,如图所示:
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'>
除此之外,Sage还添加了许多其他类型。E、 g.,向量空间:
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'>
只能调用某些函数 V
. 在其他的数学软件系统中,这些将被称为使用“函数”符号 foo(V,...)
. 在Sage中,某些函数附加在 V
,并且使用像java或C++那样的面向对象语法,例如 V.foo(...)
. 这有助于防止全局命名空间被成千上万个函数污染,并意味着许多具有不同行为的不同函数可以命名为foo,而不必使用参数(或case语句)的类型检查来决定调用哪个函数。另外,如果重用函数的名称,该函数仍然可用(例如,如果调用某个函数 zeta
,然后要计算Riemann-Zeta函数的值为0.5,仍然可以输入 s=.5; s.zeta()
)
sage: zeta = -1
sage: s=.5; s.zeta()
-1.46035450880959
在一些非常常见的情况下,为了方便起见,还支持通常的函数表示法,因为使用面向对象的表示法时,数学表达式可能看起来很混乱。这里有一些例子。
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
列出的所有成员函数 \(A\) ,使用制表符完成。只是打字 A.
,然后键入 [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
(当索引到列表中时,如果索引不是Python int就可以了!)一个聪明的整数(或有理数,或任何带有 __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
这个 range
函数创建Python int的列表(不是Sage整数):
sage: range(1, 15) # py2
[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]
有关如何使用列表理解创建列表的更多信息,请参见 [PyT].
列表切片是一个很好的特性。如果 L
是一张单子 L[m:n]
返回的子列表 L
从 \(m^{{th}}\) 元素并在 \((n-1)^{{st}}\) 元素,如下所示。
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]
元组类似于列表,只是它们是不可变的,这意味着一旦它们被创建,它们就不能被更改。
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
序列是第三种面向列表的Sage类型。与列表和元组不同,Sequence不是内置的Python类型。默认情况下,序列是可变的,但是使用 Sequence
类方法 set_immutable
,可以将其设置为不可变的,如下例所示。一个序列的所有元素都有一个共同的父元素,称为序列宇宙。
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])
<type '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.
序列源于列表,可用于任何可以使用列表的位置:
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'>
另一个例子是,向量空间的基础是不可变的序列,因为不改变它们很重要。
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
辞典¶
字典(有时也称为关联数组)是“散列”对象(例如字符串、数字和元组)的映射;请参阅Python文档http://docs.python.org/tut/node7.html以及http://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
第三个键说明字典的索引可以很复杂,例如整数环。
您可以将上述字典转换为具有相同数据的列表:
sage: 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]
如最后一个输出所示,字典是无序的。
集合¶
Python有一个内置的set类型。它提供的主要功能是快速查找元素是否在集合中,以及标准的集合论运算。
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}
Sage也有自己的set类型(在某些情况下)使用内置的Python set类型实现,但是有一些额外的Sage相关功能。使用创建Sage集 Set(...)
. 例如,
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
遍历器¶
迭代器是Python的新成员,在数学应用中特别有用。这里有几个示例;请参见 [PyT] 更多细节。我们在非负整数的平方上做一个迭代器 \(10000000\) .
sage: v = (n^2 for n in xrange(10000000)) # py2
sage: v = (n^2 for n in range(10000000)) # py3
sage: next(v)
0
sage: next(v)
1
sage: 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
某些环,例如有限域和整数都有与其相关联的迭代器:
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)
循环、函数、控制语句和比较¶
我们已经看到了一些 for
循环。在Python中,一个 for
循环具有缩进结构,例如
>>> for i in range(5):
... print(i)
...
0
1
2
3
4
注意for语句末尾的冒号(在GAP或Maple中没有“do”或“od”),在循环的“body”前面有缩进,即 print(i)
. 这个缩进很重要。在Sage中,当你点击时,缩进会自动为你输入 enter
在“:”之后,如下图所示。
sage: for i in range(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
请记住缩进是如何确定块结构的 if
, for
和 while
声明:
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
当然,这不是一个有效的Legendre符号实现!它旨在说明Python/Sage编程的各个方面。Sage附带的函数{kronecker}通过对PARI的C-library调用有效地计算Legendre符号。
最后,我们注意到比较,例如 ==
, !=
, <=
, >=
, >
, <
,如果可能,数字之间会自动将两个数字转换为同一类型:
sage: 2 < 3.1; 3.1 <= 1
True
False
sage: 2/3 < 3/2; 3/2 < 3/1
True
True
使用bool表示符号不等式:
sage: x < x + 1
x < x + 1
sage: bool(x < x + 1)
True
当在Sage中比较不同类型的对象时,在大多数情况下Sage会尝试找到两个对象对共同父对象的规范强制(请参见 父母、皈依和强迫 更多细节)。如果成功,则在强制对象之间执行比较;如果不成功,则认为对象不相等。用于测试两个变量是否引用同一个对象使用 is
. 正如我们在本例中看到的,Python int 1
是唯一的,但是Sage整数 1
不是:
sage: 1 is 2/2
False
sage: int(1) is int(2)/int(2) # py2
True
sage: 1 is 1
False
sage: 1 == 2/2
True
在下面两行中,第一个等式是 False
因为没有标准态射 \(\QQ\to \GF{{5}}\) ,因此没有标准的方法来比较 \(1\) 在里面 \(\GF{{5}}\) 到 \(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
警告:在Sage中的比较比在Magma中的限制更大,它声明 \(1 \in \GF{{5}}\) 等于 \(1 \in \QQ\) .
sage: magma('GF(5)!1 eq Rationals()!1') # optional - magma
true
分析¶
作者:Martin Albrecht(malb@informatik.uni-bremen.de)
“过早的优化是万恶之源。”—唐纳德·克努斯
有时,检查代码中的瓶颈以了解哪些部分占用了最多的计算时间,这可以很好地了解哪些部分需要优化。Python和Sage提供了几个评测选项(这个过程被称为)。
最简单的用法是 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)])
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)
...
在这里 ncalls
是通话次数, tottime
是在给定函数中花费的总时间(不包括调用子函数的时间), percall
是的商 tottime
除以 ncalls
. cumtime
是此函数和所有子函数(即从调用到退出)所花费的总时间, percall
是的商 cumtime
除以原始调用,以及 filename:lineno(function)
提供每个函数的相应数据。这里的经验法则是:清单中的函数越高,它的成本就越高。因此,对优化问题更感兴趣。
As usual, prun?
provides details on how to use the profiler and
understand the output.
也可以将分析数据写入对象,以便进行更仔细的检查:
sage: %prun -r A*A
sage: stats = _
sage: stats?
注:进入 stats = prun -r A\*A
显示语法错误消息,因为prun是IPython shell命令,而不是常规函数。
为了更好地以图形方式表示分析数据,可以使用hotshot profiler,一个名为 hotshot2cachetree
程序呢 kcachegrind
(仅限Unix)。与hotshot profiler相同的示例:
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)
sage: prof.run("A*A")
<hotshot.Profile instance at 0x414c11ec>
sage: prof.close()
这将生成一个文件 pythongrind.prof
在当前工作目录中。现在可以将其转换为cachegrind格式进行可视化。
在系统外壳上,键入
$ hotshot2calltree -o cachegrind.out.42 pythongrind.prof
输出文件 cachegrind.out.42
现在可以用 kcachegrind
. 请注意命名约定 cachegrind.out.XX
需要服从。