数式処理システムSageMathで遊ぶ

代数学について勉強していると、具体的な群の構造やら何やらを知りたくなる時がある。しかし、それらを理論的に求めることができたとしても、実際に計算してみるのは大変骨の折れることだ。例えば、ある群の部分群を全て知りたいとか、ある多項式ガロア群を求めたいとか思っても、実際にはなかなか計算することは難しい。

そこで調べてみたところ、世の中にはこのような代数学に関する計算を行うためのソフトウェアが存在していた。その名もSageMath(以下、sage)である。Sageは数学に関する様々な計算を行えるオープンソースソフトウェアであり、interfaceにはpythonベースの言語を使用する。以下に本家へのリンクを貼っておく。

www.sagemath.org

今回は冒頭で述べた群の部分群を全て求めるという計算と、多項式ガロア群を求める計算を実際に行ってみたので、それについて記載する。

初めに注意しておくと、実はビルドの途中でmakeコマンドがうんともすんとも言わなくなり、やむなくCtrl+Cで強制終了した。ただ、どうもコンソール出力の内容から察するに、コケていたのはドキュメントのビルドだと思われるので、sage自体は問題なく使えている。

準備:基本的な群を求める

群の例で遊んでみるにあたり、基本的な群を自分で構築するのは面倒である。素晴らしいことに、sageでは基本的な群はすぐに取得できるように関数が用意されている。例えば3次対称群が欲しければ以下のコマンドを実行すればよい。

sage: S3 = SymmetricGroup(3)

これで3次対称群が変数S3に代入された。これの中を見るには以下のようにすればよい。

sage: S3
Symmetric group of order 3! as a permutation group
sage: S3.list()
[(), (1,2), (1,2,3), (1,3,2), (2,3), (1,3)]

その他の例として、交代群、二面体群を求めるコマンドも紹介しておく。

sage: A3 = AlternatingGroup(3)  # 3次交代群
sage: A3
Alternating group of order 3!/2 as a permutation group
sage: A3.list()
[(), (1,2,3), (1,3,2)]
sage: D4 = DihedralGroup(4)  # 4次二面体群
sage: D4
Dihedral group of order 8 as a permutation group
sage: D4.list()
[(), (1,4)(2,3), (1,2,3,4), (1,3)(2,4), (1,3), (2,4), (1,4,3,2), (1,2)(3,4)]

群の部分群を全て求める

群の部分群を全て求めるには、subgroups関数を使用する。

sage: S3.subgroups()

[Subgroup of (Symmetric group of order 3! as a permutation group) generated by [()],
 Subgroup of (Symmetric group of order 3! as a permutation group) generated by [(2,3)],
 Subgroup of (Symmetric group of order 3! as a permutation group) generated by [(1,2)],
 Subgroup of (Symmetric group of order 3! as a permutation group) generated by [(1,3)],
 Subgroup of (Symmetric group of order 3! as a permutation group) generated by [(1,2,3)],
 Subgroup of (Symmetric group of order 3! as a permutation group) generated by [(2,3), (1,2,3)]]

ここに表示されていることの意味は、各行の一番右に表示されている元によって生成される部分群が存在するということである。具体的な形を知りたければ、それぞれの部分群に対してlist関数を実行すればよい。

sage: for H in S3.subgroups():  # python風のfor文
....:     H.list()
....:     
[()]
[(), (2,3)]
[(), (1,2)]
[(), (1,3)]
[(), (1,2,3), (1,3,2)]
[(), (2,3), (1,2,3), (1,2), (1,3,2), (1,3)]

これで全ての部分群の具体的な元を求めることができた。

多項式ガロア群を求める

最初の例

さて、こちらはちょっと難しい。正直まだ完全に消化できていないので、分かっているところまで書いてみる。

多項式ガロア群を求めるには、まず多項式を指定し、かつその多項式の根を添加して得られる体を取得しなければならない。これにはNumberField関数を使用する。

sage: K.<alpha> = NumberField(x^2 - 2)

上記コマンドにより、 x^2 - 2という多項式の根である \sqrt{2}を添加して得られる体を取得できた。式中のalphaはどうも根が入る変数のようで、いろいろいじってみるとどういうものか分かる。

sage: alpha
alpha
sage: alpha^2
2

ただし、このalphaの正体が実はよく分かっていない。これについては後述する。

さて、以下のコマンドにより、得られた体がガロア拡大体かどうかが分かる。

sage: K.is_galois()
True

実行結果がTrueと出たので、これはガロア拡大体のようだ。これでやっとガロア群を求めることができる。体Kのガロア群は以下のコマンドで計算できる。

sage: G = K.galois_group()
sage: G
Galois group of Number Field in alpha with defining polynomial x^2 - 2
sage: G.list()
[(), (1,2)]
sage: G.order()  # 位数も計算できる
2

以上により、 x^2 - 2の根を添加した体のガロア群が計算できた。

円分多項式ガロア

次に、 x^5 - 1という円分多項式ガロア群を計算しよう。実際にやってみると、以下のようになる。

sage: K.<alpha> = NumberField(x^5 - 1)
・・・・(大量のエラーメッセージ)・・・・
ValueError: defining polynomial (x^5 - 1) must be irreducible

というわけで、失敗してしまった。エラーメッセージを見ると、多項式はirreducibleでなければならないとある。この単語の意味を調べてみたところ、「既約」ということだった。つまり、引数に与える多項式 \mathbb{Q}上既約でなければならないようだ。

実は、sageを使うと多項式因数分解も簡単に行うことができる。以下のようにすればよい。

sage: x = QQ['x'].0  # xを有理数体上の多項式環の変数にするための記法
sage: f = x^5 - 1
sage: f.factor()
(x - 1) * (x^4 + x^3 + x^2 + x + 1)

これで \mathbb{Q}上既約な2つの多項式が得られたため、これらについて再度ガロア群を求めてみる。ただ、 x - 1の方は根が1であり、 1 \in \mathbb{Q}となるため無視してよい。よって x^4 + x^3 + x^2 + x + 1について計算してみる。

sage: K.<alpha> = NumberField(x^4 + x^3 + x^2 + x + 1)
sage: K
Number Field in alpha with defining polynomial x^4 + x^3 + x^2 + x + 1
sage: alpha
alpha
sage: alpha^2
alpha^2
sage: alpha^3
alpha^3
sage: alpha^4
-alpha^3 - alpha^2 - alpha - 1  # alphaは入力された多項式の根なのでこうなる
sage: K.is_galois()
True    # ガロア拡大体!

どうやらこれはガロア拡大体のようなので、ガロア群を求めてみる。

sage: G = K.galois_group()
sage: G
Galois group of Number Field in alpha with defining polynomial x^4 + x^3 + x^2 + x + 1
sage: G.list()
[(), (1,2,3,4), (1,3)(2,4), (1,4,3,2)]

よって位数4のガロア群が得られた。元々の多項式は円分多項式なので、これは巡回群となっている。しかし、これだとパッとみてそのことがよく分からない。そこで、galois_group関数の引数を変えて実行すると、もう少し分かりやすくなる。

sage: G = K.galois_group(type = 'pari')  # 内部的にpariというソフトウェアの計算エンジンを使用している??
sage: G
Galois group PARI group [4, -1, 1, "C(4) = 4"] of degree 4 of the Number Field in alpha with defining polynomial x^4 + x^3 + x^2 + x + 1

最後の行にC(4)と出力されているが、これはGが4次の巡回群であることを表している。念のため4次の巡回群を出力するコマンドと結果を比較すると、当然ながら一致する。

sage: CyclicPermutationGroup(4).list()
[(), (1,2,3,4), (1,3)(2,4), (1,4,3,2)]

ガロア拡大体にならない例

最後に、ガロア拡大体にならない例について取り上げてみる。入力に使う多項式はおなじみの x^3 - 2である。

sage: K.<alpha> = NumberField(x^3 - 2)
sage: alpha
alpha
sage: alpha^2
alpha^2
sage: alpha^3
2
sage: K.is_galois()
False

is_galois関数の出力がFalseになっており、これはガロア拡大体ではないことが分かる。実際に計算してみると、確かに失敗する。

sage: G = K.galois_group()
・・・・(大量のエラーメッセージ)・・・・
TypeError: You must specify the name of the generator.

最後のエラーを見ると、何やらgeneratorを指定せよとある。Generatorというのは恐らく生成元のことだと思うのだが、これを指定せよというのはどういうことだろうか?

これまでの例では、代数体Kを生成する際、変数としてはalpha1つだけを指定し、これが多項式の根になっていた。最初の例ではガロア拡大 \mathbb{Q}(\sqrt{2})を扱っていたわけだが、これは \mathbb{Q}にalpha =  \sqrt{2}だけを添加すれば得ることができた。また、次の例では \mathbb{Q} \zeta_5を添加することで \mathbb{Q}(\zeta_5)を得ていた。つまり、どちらも元を1つ添加することでガロア拡大体が得られていたので、特に問題はなかった。

しかし、 x^3 - 2の場合、ここからガロア拡大体を得ようとすると、 \mathbb{Q}(2^{\frac{1}{3}}, \zeta_3)となるため、2つの元を添加する必要がある。にも関わらず、最初に指定した変数がalphaだけなので、添加すべきものが足りないと言っているのではないかと思われる。

ここで疑問なのだが、そもそもalphaとは何者なのだろうか?多項式の根というのであれば 2^{\frac{1}{3}}, 2^{\frac{1}{3}} \zeta_3, 2^{\frac{1}{3}} \zeta_3^2の3通りが考えられるわけだが、一体どれなのだろうか?どれでもよいのだろうか?もしくは、どれでもないのだろうか?ここがよく分からない。

ここでいろいろと足掻いてみたところ、以下のようなコマンドが動作することを発見した。

sage: G.<beta> = K.galois_group()  # Generatorが欲しければくれてやる!Betaを持ってけ!
sage: G
Galois group of Galois closure in beta of Number Field in alpha with defining polynomial x^3 - 2
sage: G.list()

[(),
 (1,2,3)(4,5,6),
 (1,3,2)(4,6,5),
 (1,4)(2,6)(3,5),
 (1,5)(2,4)(3,6),
 (1,6)(2,5)(3,4)]

よくよく出力を見ると、"Galois closure"がどうとか書いてある。これはガロア閉包のことだと思われるが、これをどう解釈したらよいのか?さらに調べてみると、galois_closureなる関数があったので、これとの出力を見比べてみた。

sage: L.<beta> = K.galois_closure()
sage: L
Number Field in beta with defining polynomial x^6 + 108
sage: G = L.galois_group()
sage: G
Galois group of Number Field in beta with defining polynomial x^6 + 108
sage: G.list()

[(),
 (1,2,3)(4,5,6),
 (1,3,2)(4,6,5),
 (1,4)(2,6)(3,5),
 (1,5)(2,4)(3,6),
 (1,6)(2,5)(3,4)]

どうやら先ほどの出力と一致しているが、 x^6 + 108という怪しげな多項式が登場した。これは一体何なんだ?わけも分からずbetaをいじくってみると、以下のような結果が得られた。

sage: beta
(1,2,3)(4,5,6)
sage: beta^2
(1,3,2)(4,6,5)
sage: beta^3
()  # betaを3乗したら単位元に戻った

betaを3乗して単位元に戻るというのは、いかにも \zeta_3を彷彿とさせるわけだが、今回はこれ以上分からなかった。

ちなみに、typeをpariに指定すれば、このように苦しまずともガロア群が得られるようだ。

sage: G = K.galois_group(type = 'pari')
sage: G
Galois group PARI group [6, -1, 2, "S3"] of degree 3 of the Number Field in alpha with defining polynomial x^3 - 2

以上によりガロア群は3次対称群であることがわかった。

まとめ

今回はsageの持つ機能のうちほんの一部を触ってみたわけだが、実際には山のように機能があり、大変優れたソフトウェアのようだ。ただし、今の自分の数学力では、まだまだ使いこなすには至らず、無念であった。

教科書で理論を学ぶだけでなく、具体例を考えることの重要性は多くの人が理解しているところだと思うが、このようなツールはそれを助けてくれるという意味で、非常に重要なものだと思う。今後も機会があれば積極的に使っていきたい。