pari-gpで代数体関連の計算をする

# pari-gpとは

pari-gpは，数論に特化した計算ライブラリであるpariと，それをコマンドラインから対話的に使うためのプログラミング言語GP, ならびにその処理系gpの総称です．pariライブラリは，C/C++で書いたプログラムにリンクして使うことが想定されています（他の言語へのバインディングもあります）．gpは，プログラミング言語としてはPerlなどのスクリプト言語に似ていますが，独自のものです．

開発は主にボルドー大学の計算機数論グループによって行われています．Henri Cohen氏らによって始められ，現在はKarim Belabas氏らが統括しています．pari-gpのライセンスはGPLです． 公式ウェブ： http://pari.math.u-bordeaux.fr/

## pari-gpの始め方

一番手っ取り早いのは，公式ウェブにある「ブラウザ版」でしょう．CのソースコードをemscriptenでJavascriptにしたものが動きます：http://pari.math.u-bordeaux.fr/gp.html

LinuxなどUnix互換環境なら，パッケージとして配布されています．適宜探してください．MacでもHomebrewで提供されています．Windows版は，開発元でバイナリを配布しています．

ソースコードをtar ballでダウンロードして，コンパイルするのも簡単です．事前に，pari-gpが依存するいくつかのコンポーネントを導入する必要があるでしょう．

## pari-gpで何ができるか

数論（もしくは整数論）に関連する様々な計算が可能です．本稿で扱う代数体の計算のほか，任意多倍長の整数，有理数， $p$進数のほか，有限体や有理数体上の楕円曲線，ゼータ関数，さまざまな数値積分など．最近は，モジュラー形式や半単純代数の計算の実装が進められています．

## pari-gpで代数体

本稿では，有限次代数体の計算例をいくつか解説します．pari-gpに限らず，コンピュータで数学的な対象を計算する際には，計算の対象を有限のデータで記述しなければなりません．

代数体は，有理数体$\mathbb{Q}$上の多項式環$\mathbb{Q}[x]$を既約多項式$f(x)\in\mathbb{Q}[x]$で割った剰余環として与えます．
以下，gpのセッションを記述する際には， ``gp>`` がプロンプトとします．

### 代数体の定義

```shell-session
bash$ gp \\ banner省略
gp> nf = bnfinit(x^2+23); 
```

これで，nfという変数に，$x^2+23$が定義する代数体の様々なデータが格納されました．GPの変数には型の指定がありません．（値には型があります）． ``bnfinit()`` は，代数体の様々なデータを計算して，それを返す関数です．行末のセミコロンは，計算結果を画面に表示することを抑制します．（ためしに，同じ入力でセミコロンを省いて試してみてください．「様々なデータ」が出力されます）．上の例で，変数に格納されたのは虚2次体$\mathbb{Q}(\sqrt{-23})$です．

### 類数とイデアル類群

```shell-session
(16:12) gp> nf.no
%2 = 3
(16:12) gp> nf.clgp
%3 = [3, [3], [[2, 0; 0, 1]]] 
```

C言語の構造体のメンバーにアクセスするように， ``.no`` などの記法が使えます． ``.no`` では，変数に格納されている代数体の類数が，同様に， ``.clgp`` ではイデアル類群が返ります．類数は正の整数なので見たままですが，イデアル類群については出力の読み方を知らないと意味不明でしょう．

上の例の場合，出力されているのは「ベクトル型」の値です（``[]``で囲まれている）．GPのベクトル型の各要素には，``[]``でアクセスできます．ベクトルの添え字は1始まりです．

第1成分が位数（つまりイデアル類群の位数は3で，類数の表示と整合します）．``nf[1]``としても，``nf.no``としても同じですが，類数である事から覚えやすいのは後者でしょう．

第2成分は，有限アーベル群の構造（この場合，位数3の巡回群）を示すベクトルです．

最後第3成分の ``[[2, 0; 0, 1]]`` は，2行2列の行列1つからなるベクトルです．``[2, 0; 0, 1]``は，1行が``[2, 0]``, 2行が``[0, 1]``の行列です．この行列は，イデアル類群の生成元（この場合は1つ）の代表元であるイデアルを，$\mathbb{Z}$格子だと思ったときの生成元を表します．nfの整数底は``nf.zk``で，また，nfのイデアル類群の生成系（``nf.clgp``の3番目の要素）は``nf.gen``で取得できるので，

```shell-session
gp> nf.zk*nf.gen[1]
%83 = [2, 1/2*x - 1/2]
```

以上で，イデアル類群の生成元であるイデアル類の代表であるイデアルを，$\mathbb{Z}$格子だと思ったときの生成元2つが得られました．

### 代数体での素数の分解

上で見たイデアル類群の生成元を代表するイデアルは，2の上にあるものです．これを確認しましょう．

まず，有理素数を代数体で素イデアル分解する関数が``idealprimedec()``です．$\mathbb{Q}(\sqrt{-23})$の例で，2の素イデアル分解を求めると

```shell-session
gp> idealprimedec(nf, 2)
%84 = [[2, [0, 1]~, 1, 1, [1, -6; 1, 0]], [2, [1, 1]~, 1, 1, [0, -6; 1, -1]]]
gp> kronecker(-23, 2)
%85 = 1
```
出力を見ると，2は2つの素イデアルの積であることが読み取れます．なお，ベクトルの閉じ括弧の次にチルダがついているのは，そのベクトルが縦ベクトルである事を表します（より一般に，行列にチルダをつけることができ，転置を意味します）． ``kronecker()``は，Kronecker記号で，値が1である事からも2が分解することが分かります．

その一方を``pr2``という変数に格納し，3乗すると単項イデアルになるのか確認してみる．イデアルの冪乗には，``idealpow()``という関数を使います．引数は，第一引数が考えている代数体，第二引数が冪乗したいイデアル，第三引数が冪指数です：


```shell-session
gp> pr2 = idealprimedec(nf, 2)[1];
gp> idealpow(nf, pr2, 2)
%87 = 
[4 2]

[0 1]

gp> idealpow(nf, pr2, 3)
%88 = 
[8 2]

[0 1]
```

最後の結果を見ても，単項イデアルである事は読み取れないですね．イデアルが単項であるかどうかを確認するのには，``bnfisprincipal()``という関数を使います．第一引数は考えている代数体，第二引数が単項か否かを判定したいイデアルです：

```shell-session
gp> bnfisprincipal(nf, idealpow(nf, pr2, 3))
%89 = [[0]~, [2, 1]~]
```

この関数の返値は，``[v, a]``で，vはイデアル類群の生成元に対する指数，aはこのイデアルと，vで表されるイデアルとのずれを表す単項イデアルの生成元です．上の表示から，pr2の3乗は単項イデアル（イデアル類群の生成元，今の場合は1つ，の0乗になっています），また，``nf.zk * [2,1]~``を計算して得られる``1/2*x + 3/2``が，件の単項イデアルの生成元である．つまり，単項イデアルか否かを知りたいだけなら，返値の第一成分がゼロベクトルであるか否かをみればよいのです．

イデアルの積を計算するには，``idealmul()``という関数を使います． 第一引数は考えている代数体，第二，第三引数が，かけ算したいイデアルたちです．例として，2の上にあるもう一つのイデアルをpr2cとして，積を取り，その積が単項である事を確認します：

```shell-session

gp> pr2c = idealprimedec(nf, 2)[2];
gp> idealmul(nf, pr2, pr2c)
%99 = 
[2 0]

[0 2]
gp> bnfisprincipal(nf, idealmul(nf, pr2, pr2c))
%100 = [[0]~, [2, 0]~]
```

## まとめ

pari-gpは非常に高機能ですが，そのぶん，何をどうすれば自分のやりたい計算ができるのか把握しづらいかもしれません．pari-gpの公式ドキュメント http://pari.math.u-bordeaux.fr/doc.html とくにユーザーズマニュアル（Users' guide）やチュートリアル http://pari.math.u-bordeaux.fr/tutorials.html に目を通し，あとは前者のユーザーズマニュアルを手元に置いて，頻繁に参照するとよいでしょう．

別の機会に，代数体での計算をもう少し補足したり（相対代数体の計算や類体論などの話題），楕円曲線やモジュラー形式の計算の話題に触れたいと思います．また，今回はpari-gpの機能を呼び出して電卓がわりに計算する例に終始しましたが，GPのプログラミング言語としての側面にもいずれ触れたいと思います．