群論と対称性
第 2 回 GAP を使う - 電卓のように

GAP (Groups, Algorithms, Programming, 外部サイト) は無料で利用できる高性能な数学ソフトウェアで、特に代数学関係の計算に強い。 GAP には多くの組み込み関数が用意されておリ、単にそれを呼び出して使うだけでも十分利用価値がある。 ここでは GAP を組み込み関数を呼び出して使う方法について解説する。 この方法以外に GAP では自分でプログラムを書くことができる。 プログラミングについては第 3 回で解説する。

GAP のマニュアルは 1000 ページを超え、 pdf ファイルが /usr/share/gap/doc/ にある。 (GAP の本家からダウンロードしてインストールした場合には html ファイルもあるが、 Knoppix DVD には pdf ファイルしかないようである。 ここにコピーを置いたので見るといいだろう。) もちろん、ここではそのすべてを説明するわけではない。 日本語の参考文献は多くはないが

などに日本語の解説がある。

GAP は常に厳密計算を行うので、通常の方法では近似計算などはできない。 また扱える数にも制限が強い。 近似計算やより柔軟な数の扱いが必要なときには、あとで学ぶ maxima の方が適しているであろう。


GAP の起動

GAP を起動するには左下のメニューから「Math」→「GAP」とすればよい。 しかしこの方法だと作業ディレクトリの指定ができず、データの読み書きを USB メモリから行うには不便である。 以下の方法を推奨する。


簡単な計算 - 四則演算、整数計算

GAP では計算式の後に必ず ; (セミコロン) をつける。 セミコロンがない場合には計算式が終了していないと判断され、更なる入力を待つ状態になる。 言い換えれば、長い計算や命令はセミコロンを付けずに複数の行に分けて書くことができる。 式中のスペースは無視されるので、見やすいように適当に挿入して構わない。 また計算結果を出力する必要がない場合には ;; (セミコロン2つ) をつければ、 計算だけをして出力はされない。

課題. コマンド Gcdex について、何をする関数であるかをヘルプまたはマニュアルで調べ、実際に試してみよ。


エラーへの対処

GAP におけるエラーでよくあるのは、以下のようなものである。

gap> a;                           # 未定義の変数を参照した (変数については後述する)
Variable: 'a' must have a value

gap> Gcm(2, 4);                   # 未定義の関数を利用しようとした
Variable: 'Gcm' must have a value

gap> Gcd(2);                      # 定義済みの関数であるが、引数の数や型があっていない
Error, usage: Gcd( [<R>,] <r1>, <r2>... ) called from
<function>( <arguments> ) called from read-eval-loop
Entering break read-eval-print loop ...
you can 'quit;' to quit to outer loop, or
you can 'return;' to continue
brk> quit;
gap> 
エラーで停止し「brk>」を表示している場合には、ここでエラーの原因などを調べることができるが、 よく分からない時には「quit;」で抜けることができる。 次もよくあるミスである。
gap> 1 + 1                        # ; (セミコロン) の付け忘れ。
> ;                               # 次の行に ; を入力すればよい。
2
誤ったプログラムで無限ループに陥ったときや、時間のかかりすぎるコマンドを実行してしまったときには、 Ctrl+c でプログラムを停止することができ、GAP のプロンプトに戻る。 しかし、場合によっては停止処理に時間がかかり、GAP のプロンプトがなかなか表示されない。 計算を中止してでもプログラムを停止したければ Ctrl+c を繰り返し押す。 これによって GAP 自体が強制終了され、コマンドラインに戻ることになる。


変数の利用

GAP を電卓のように使うとは言っても、計算結果をその後の計算に利用することぐらいはしたいであろう。 このためには変数を用意して、そこに計算結果を代入しておけばよい。 簡単な例を見る。

gap> a := 2^3;
8
gap> b := 3^2;
9
gap> a + b;
17
gap> c = 1;
Variable: 'c' must have a value

gap> a = 1;
false
変数への代入は = ではなく := で行う。 = は代入ではなく、左辺と右辺が等しいかを true または false で返す。 変数名は、アルファベットの大文字、小文字、数字、 および _ (アンダースコア)で任意の長さを持つことができる。 アルファベットの大文字と小文字は厳密に区別される。 しかし特定の文字列は GAP が利用するために予約されており、利用することができない。 例えば、true (真)、false (偽)、E (1 のべき根), ER (平方根), Z (有限体の単位元) などは利用できない。 これ以外にも GAP が利用している変数や関数を上書きしてしまうと、思わぬ不具合が生じることがあるので注意しよう。 GAP の予約語や組込み関数の多くはアルファベットの大文字で始まるので、 自分の利用する変数名はアルファベットの小文字で始めるようにするとほとんど問題は起きない。

変数への代入の際に

gap> a := 2;; b := 3;;
gap> a := a + b;
5
のように、 a の値を使って a に値を代入することも許される。

GAP では直前の計算結果を last という変数に自動的に代入する。 last は上書き可能で、自分で代入することもできるが、次の計算が行われるとその結果が代入されてしまう。

gap> last := 12;
12
gap> last;
12
gap> 1+2;
3
gap> last;
3
last という名前の変数は直前の計算結果を利用するときにだけ使えると思っていた方がよいであろう。


ファイルの入出力

GAP を電卓のように利用するときであっても、 データがある程度大きいときにはそれをコマンドラインから入力するのは面倒である。 ここでは GAP に入力するデータを予めファイルに記述しておいて、それを GAP に読み込ませる方法を説明する。 これは、同じデータを繰り返し用いたいときなどにも有効な手段である。 また計算結果などをファイルに書き出す方法も説明する。

GAP にデータを読み込ませるのは簡単である。 まずコマンドラインから入力する内容を記述したファイルを用意する。 この際、説明などのコメントを記述しておくこともできる。 GAP では行の # 以降をコメントとして無視する。 例えば以下の内容のファイルを matrix.gap という名前で用意する。

mat :=
[[1,2,3],
 [4,5,6],
 [7,8,9]];       # 行列
vec := [1,2,3];  # ベクトル
これを GAP に読み込ませるには、GAP のコマンドラインから
gap> Read("matrix.gap");
gap> vec * mat;
[ 30, 36, 42 ]
とする。読み込むファイルが GAP を実行中のディレクトリにないときには、 ファイルを絶対パス、または相対パスで指定する。 もっと簡単な方法は、入力したい内容をエディタからコピーしてコマンドラインに貼り付けることである。 また GAP 起動時に読み込ませたいのであれば
knoppix@Microknoppix:/media/sdc1/gap$ gap matrix.gap
のようにコマンドラインから GAP のオプションとして読み込ませたいファイルを指定すればよい。 この方法では複数のファイルを読み込ませることができる。

課題. 上記の方法で GAP にファイルを読み込ませ、vec と mat にきちんと代入されていることを確認しなさい。

次に計算結果をファイルに保存する方法を説明する。 もっとも簡単な方法は画面に表示される内容をすべてファイルに保存する方法である。

gap> LogTo("logfile");
gap> 1 + 2;
3
gap> LogTo();
LogTo に保存するファイル名を指定すると保存が開始される。 このあと LogTo() とするまでの間、画面に表示されるものと同じ内容がすべてファイルに書き出される。 とりあえず、すべてを保存しておき、あとで必要な部分を整理する場合に便利であろう。

課題. LogTo を試してみなさい。

次に説明する方法は必要なデータのみをその出力形式まで指定して保存する。 工夫すれば出力を LaTeX の形式で保存するなど、いろいろなことができる。

gap> a := 4;; b := 6;; 
gap> Print("Gcd(", a, ", ", b, ") = ", Gcd(a, b), "\n");
Gcd(4, 6) = 2
gap> PrintTo("logfile", "Gcd(", a, ", ", b, ") = ", Gcd(a, b), "\n");
gap> AppendTo("logfile", 1 + 2, "\n");
Print は複数の値を , (カンマ) で区切って指定でき、画面にそれを出力する。 文字列は " " でくくって指定する。 上の例では "Gcd(" を文字列として出力させた後、変数 a の値、 文字列 ", " などを指定している。 最後の "\n" は改行を表す特殊記号である。 PrintTo は Print と同じ書式で、出力をはじめの引数に指定したファイルに対して行う。 この例では logfile に書き出す。 PrintTo は指定した名前のファイルが存在するとき、すでに書き込んである内容をすべて消して書き込んでしまうので注意が必要である。 すでに書いてある内容を消さずに、出力を書き足すときには AppendTo を使う。

課題. PrintTo と AppendTo を試してみなさい。


整数以外の計算

ベクトルと行列

ベクトルは [1,0,1] のように [] でくくって記述する。 すなわち、スカラーを成分とするリストを行列と見る。 加法やスカラー倍は以下のように自然な方法で記述できる。

gap> [0,1,0] + [1,2,3];
[ 1, 3, 3 ]
gap> 2 * [2,3,4];
[ 4, 6, 8 ]
gap> [1,2] + [1,0,0];
[ 2, 2, 0 ]
三つ目の例のように次元の異なるベクトルの加法も許される。 行列は 2 次元配列、すなわち行ベクトルを成分とするリスト、で表される。
gap> A := [[1,0],[0,1]];;
gap> A^2;
[ [ 1, 0 ], [ 0, 1 ] ]
gap> A^(-1);
[ [ 1, 0 ], [ 0, 1 ] ]
gap> B := [[1,1],[1,1]];; 
gap> B^(-1);             
fail
加法やスカラー倍はベクトルと同様である。 また行列の積や累乗も自然に計算できる。 逆行列は Inverse(A) として求めることもできるが A^(-1) でもよい。 正則でない行列 B に対しては B^(-1) は fail となる。

行列の固有値や固有ベクトルは以下のように求めることができる。

gap> A := [[3,2,0],[2,3,0],[0,0,5]];;
gap> Eigenvalues(Rationals, A);      
[ 5, 1 ]
gap> Eigenvectors(Rationals, A);     
[ [ 1, 1, 0 ], [ 0, 0, 1 ], [ 1, -1, 0 ] ]
ただし、どの数体の範囲で求めるのかを指定する必要がある。 上の例では Rationals (有理数体) の範囲で求めている。 また固有値の重複は求められない。 固有値と固有ベクトルの順番も対応するとは限らない。 有理数体以外の体の指定については、あとで説明する。

多項式

多項式を扱うには、まず変数を用意しないといけない。 その際、どのような数体を係数とするのかも指定する。

gap> x := Indeterminate(Rationals, "x");
x
これで多項式を扱うための準備ができたことになる。 複数の変数が必要ならば、これを繰り返す。
gap> y := Indeterminate(Rationals, "y");
y
gap> (x + y)^3;
x^3+3*x^2*y+3*x*y^2+y^3
多項式への値の代入、多項式の因数分解は以下の通りである。
gap> f := x^2 + x*y + y^2; 
y^2+y*x+x^2
gap> Value(f, [x], [2]);
y^2+2*y+4
gap> Value(f, [x,y], [2,1]);
7
gap> Factors(x^2-1);
[ x-1, x+1 ]
多項式環は以下のように定義する。 x と y はすでに変数として用意されているものとする。
gap> R := PolynomialRing(Rationals, [x,y]);    
Rationals[x,y]

代数的数

GAP では円分体 (cyclotomic field) に含まれる数以外はうまく扱えない。 例えば 2 の 3 乗根は円分体に含まれないので扱いにくい。 円周率などの超越数も扱えない。 GAP でよく用いられる数は

である。 ER(a) は E(n) の多項式として表示される。 もちろん、これらの数の多項式で書ける数はすべて扱うことが出来る。
gap> ER(-2)^2;
-2
gap> ER(-2);
E(8)+E(8)^3
有理数体 Rationals に 1 の n 乗根 E(n) を添加した体 (円分体) は CF(n) で表される。
gap> M := [[0,1],[2,0]];
[ [ 0, 1 ], [ 2, 0 ] ]
gap> Eigenvalues(Rationals, M);
[  ]
gap> Eigenvalues(CF(8), M);    
[ E(8)-E(8)^3, -E(8)+E(8)^3 ]
平方根 E(a) を扱うために CF(n) として n をいくつにすればよいかは、 それほど簡単ではない。 これを知るためには
gap> DefaultField(ER(3));
CF(12)
などとすればよい。

有限体

q 個の元からなる有限体 (finite field, Galois field) は GF(q) と表される。 GF(q) の乗法群は巡回群となるので、その生成元の一つを Z(q) で表す。 GF(q) の 0 でない元は Z(q)^i と表すことができるので、GAP はこの形で表示する。 また 0 は有理数としての 0 特別するため 0*Z(q) と表される。 GF(q) を有理整数環上の加群と見ることができるので、GF(q) の元に整数をかけることは許される。 また q が q' の約数であるとき、自然に GF(q) は GF(q') の部分体とみなされる。

gap> Z(4)+Z(4)^2;
Z(2)^0
gap> Z(4)-Z(4);
0*Z(2)
gap> 3*Z(4);
Z(2^2)
gap> Z(4) in GF(16);
true
有限体の元を成分とする行列なども問題なく扱うことが出来る。
gap> M := [[1,1],[1,0]] * Z(4)^0;
[ [ Z(2)^0, Z(2)^0 ], [ Z(2)^0, 0*Z(2) ] ]
gap> Eigenvalues(GF(2), M);
[  ]
gap> Eigenvalues(GF(4), M);
[ Z(2^2), Z(2^2)^2 ]


パッケージの利用

GAP はそれ自身で多くの機能をもっているが、もちろん何でも出来るわけではない。 そこで多くの人が GAP 上で特定の用途に用いるためのプログラムを作成し、パッケージとして公開している。 パッケージを利用することによって GAP の利用できる範囲は大きく膨らむ。 しかしながら、今回利用している Knoppix/Math の環境では DVD から起動しているため、 新たなパッケージをインストールこることはできない。 パッケージを利用したければ Knoppix をハードディスクにインストールしたり、 あるいは UBUNTU などの Linux ディストリビューションに GAP をインストールするなどする必要がある。 GAP は MS Windows にもインストールすることができるが、いくつかのパッケージは Windows 上では利用できない。


戻る

Akihide Hanaki (Shinshu University)