GP/PARI
PARI是一个紧凑的,非常成熟的,高度优化主要关注数论的C语言程序,在Sage中有两种不同的接口可以调用:
gp
- “G o P ARI” 解释器pari
- PARI的C语言库。
例如,下面两种方法做同一件事情。它们看上去相同,但是输出完全不同,而且背后所发生的事情也完全不同。
sage: gp('znprimroot(10007)')
Mod(5, 10007)
sage: pari('znprimroot(10007)')
Mod(5, 10007)
前者,一个独立的GP解释器作为服务器运行,字符串'znprimroot(10007)'
传给它,由GP执行,并将结果赋给一个GP变量(占用GP子进程的内存空间并且不会被释放)。然后变量的值显示出来。后者,没有运行单独的程序,字符串'znprimroot(10007)'
由特定的PARI的C语言库函数执行。结果存储在Python的堆中,当不再使用时会被释放。两个对象的类型不同:
sage: type(gp('znprimroot(10007)'))
<class 'sage.interfaces.gp.GpElement'>
sage: type(pari('znprimroot(10007)'))
<type 'sage.libs.pari.gen.gen'>
那么应该用哪一个?取决于你要做什么。在通常GP/PARI命令行中可以做在GP接口中做的任何事情,因为运行的就是那个程序。特别的,你可以载入复杂的PARI程序并执行。相反,PARI接口(基于C语言库)的限制更多。首先,并没有实现所有的成员函数。第二,很多代码,比如涉及数值积分的,不能直接通过PARI接口执行。也可以说,PARI接口方式比GP接口方式更快,稳定性更好。
(如果GP接口方式执行一个给定的输入行时内存溢出,它会自动将堆栈大小翻番,并重试。这样,如果你没有正确预计所需的内存量,计算也不会崩溃。这是一个不错的技术,但是通常的GP解释器并不提供。对于PARI的C语言库接口,它会立即将已建立的对象复制出堆栈,这样堆栈就不会再增长了。但是每一个对象不能超过100MB,否则建立这个对象的时候,堆栈就会溢出。额外的复制操作会稍微影响性能。)
总之,除了使用不同的高级内存管理策略和Python语言之外,Sage使用PARI的C语言库所提供的功能与GP/PARI解释器所提供的功能接近。
首先我们从Python列表新建一个PARI列表。
sage: v = pari([1,2,3,4,5])
sage: v
[1, 2, 3, 4, 5]
sage: type(v)
<type 'cypari2.gen.Gen'>
每一个PARI对象都是Gen
类型的。 基本对象的PARI类型可由成员函数type
得到。
sage: v.type()
't_VEC'
在PARI中,使用ellinit([1,2,3,4,5])
新建一个椭圆曲线。Sage中是类似的,只是ellinit
是一个方法,可由任何PARI对象调用,如,我们的t_VEC
$v$。
sage: e = v.ellinit()
sage: e.type()
't_VEC'
sage: pari(e)[:13]
[1, 2, 3, 4, 5, 9, 11, 29, 35, -183, -3429, -10351, 6128487/10351]
现在我们有了一个椭圆曲线对象,可以对它进行一些运算。
sage: e.elltors()
[1,[],[]]
sage: e.ellglobalred()
[10351,[1, -1, 0, -1], 1]
sage: f = e.ellchangecurve([1,-1,0,-1])
sage: f[:5]
[1, -1, 0, 4, 3]