はじめに
この記事は,代数学などの問題を解くのに適した数式処理システム「Magma」を新たに始める方向けの入門記事です.
Magmaには公式ハンドブック(オンライン)や簡単な入門書などの,豊富なドキュメント(その他はこちらを参照)が用意されていますが,いずれも英語で書かれており,初学者(私も含む)にはかなりとっつきにくいことでしょう.
そこで,Magmaを始めるにあたって最も基礎的な部分,特に一般的なプログラミング言語に則した部分を日本語で学習できるようにこの記事にまとめました!
私もMagmaに関しては初心者ですので,ご一緒に学習していきましょう!
なお,こちらの紹介記事も大変参考になるので,是非ご一読ください!本記事も,このサイトを参考にしております.
また,他の数式処理システムも考えている方は,次のサイトが参考になるかと思います.
目次
- Magmaとは
- Step 1. まずは出力
- Step 2. 変数を使おう
- Step 3. 基本的な演算子
- Step 4. 比較と繰り返し
- Step 5. 関数
- Step 6. リスト・数列・集合など
- Step 7. デバッグ・エラー処理・その他構文
- Step 8. 組み込み関数を使ってみよう
- おわりに
- 参考サイト
- 関連リンク
Magmaとは
前述の紹介記事にて,次のように説明されています.
(Magmaは)代数学の領域に特化して,その領域を深くカバーしたシステムなのである.
計算代数の最新の成果を貪欲に取り込むことで,それぞれの分野において,現在知られている最先端のアルゴリズムまでが実装されている.したがって,よほど複雑な計算をしない限り,通常計算できると知られているものの多くはあらかじめコマンドとして用意されていると考えて良い.
Magmaは「線形代数,加群,群論,可換群論(グレブナー基底を含む),環論,代数的整数論,表現論,代数幾何,数論幾何,保形形式,組合せ論,符号理論,暗号理論など」を扱うことができます.一方,「三角関数や指数関数などの関数を含んだ式の簡単化」や「導関数や不定積分を求めること」はできません.説明の通り,「代数学の領域に特化」した数式処理システムなのです.
さて,Magmaを実行するとなると,主に2つの方法が挙げられます.「Magmaを公式サイトからインストールする」,あるいは「公式のサービスMagma Calculatorを利用する」の2つです.
前者でMagmaの実行環境を構築するためには,(確かな情報ではありませんが)ライセンスが必要であり,また有料です.しかしこちらはターミナル上でMagmaを動かすことができ,さらにインタラクティブに実行することができます!もちろんプログラムファイルを読み込んで実行することもできます.
後者の場合ですと,オンラインの接続環境は必要ですが,無料でMagmaを実行させることができます.基本はブラウザ上での実行になりますが,それが面倒という方は私がコンソール上で行えるスクリプトファイルを作成しましたので,よろしければご活用ください.
Step 1. まずは出力
Magmaを実行できる環境は整いましたか?まだの方はとりあえずMagma Calculatorを開きましょう.
これからMagmaの基本的な文法を説明していきます.
マニュアルに詳しく説明がありますので,余裕があったらぜひ読んでみてください.
まずは出力からです.
Magmaをインストールした方はターミナル上で
$ magma
と(単にmagma
です.先頭の$
は不要です.)入力してMagmaを起動してください.(終了は quit;
でできます.)
そうすれば,コマンドの実行が可能になります.Magma Calculatorを使用している方はページ上のテキストボックスにコマンドを入力していってください.
出力は,
print "Hello World!";
// Result: Hello World!
とします.コマンドはprint
で,引数として半角スペースをはさんで必ずダブルクォーテーションで囲んで文字列を指定してください.シングルクォーテーションやバッククォーテーションを使用してはいけません. また,コマンドの末尾に必ずセミコロン;
を記入することを忘れてはいけません.
C言語と同じように,セミコロンはコマンドの終わりを表す役目を持ちます.セミコロンを入れないと,次のような場合にエラーが発生します.
print "Hello"
print "World"
/* Result:
>> print "World"
^
User error: bad syntax
*/
なお,コマンドprint
は省略可能です.つまり,
"Hello World!";
// Result: Hello World!
とするだけで出力が行われます.他にもprintf
とかも使えます.次の「補足」をご覧ください.
補足
- 次のような記法も可能です.
print("Hello World!");
// Result: Hello World!
- 文字列内にダブルクォーテーション記号を使用するときは,
"Hello \"World\"!";
// Result: Hello "World"!
とします.
- 他の様々な型のデータも出力可能です.例えば,数値を出力するなら,
print 2;
// Result: 2
print 3 + 5;
// Result: 8
あるいは
2;
// Result: 2
3 + 5;
// Result: 8
とします.
- 複数の引数をコンマ区切りで渡すことができます.出力は半角スペース区切りとなります.
print "Hello", 2, "World!";
// Result: Hello 2 World!
- 出力の末尾には改行が付されます.
print "Hello";
print 2;
"World!";
/* Result:
Hello
2
World!
*/
-
printf
文などもあります.
printf "%o ", 4;
printf "%-4o", 3;
printf "%o!\n", 1;
printf "%3o", 7;
/* Result:
4 3 1!
7
*/
- コード例にもあるように,コメントアウトはC言語と同様
//
あるいは/* ... */
で行います.今後もこの記事に掲載するコード例には実行結果をコメントアウトして載せていきます.
// 1行のみコメントアウト
/*
行をまたいで
コメントアウト
*/
Step 2. 変数を使おう
他のプログラム言語と同じで,変数を導入することができます.
ただし,代入はPascalなどと同じ:=
を用います.
a := 3;
print a;
a, a * 2;
/* Result:
3
3 6
*/
h := "Hello";
w := "World";
h, w, "!";
// Result: Hello World !
値の更新も同じく:=
を使います.
i := 1; print i; // -> 1
i := i + 1; print i; // -> 2
i := 3 * i; print i; // -> 6
補足
- 変数の型は
Type(変数)
で取得できます.型名は公式サイトのハンドブックを参照するときに有用です!
a := true; Type(a); // -> BoolElt
a := 3; Type(a); // -> RngIntElt
a := 2.4; Type(a); // -> FldReElt
- 変数の消去や定義判定も行えます.
x := 2;
x; // -> 2
assigned x; // -> true
delete x;
assigned x; // -> false
- 実際の変数に格納しないダミー変数
_
への代入が可能です.これは,関数(Step 5を参照)からの複数の返り値を受け取る際に便利です.
_ := 3; // 何もしないのと同じ
f := func< | 1, 2, 3, 4>;
a, _, b, c := f();
a, b, c;
/* Result:
1 3 4
*/
-
where
句を用いると一時的な変数を使うことができます.
n := 6;
F := GF(n, 2) where n := 7;
F, n;
print m + n where m, n := func<|2, 3>();
"";
n, n where n := 0;
n, (n where n := 0);
/* Result:
Finite field of size 7^2
6
5
0 0
6 0
*/
配列も使えます(後の章で詳述)
配列変数は,a := [1, 2, 3];
などとして宣言します.要素は1始まりであることに注意してください!
a := [6, 6, 1, 8, 4, 2];
i := 4;
print a[i];
// Result: 8
なお,a := {1, 2, 3};
とすると,a
は集合を表すようになります(数学チックな書き方!).
a := {1, 2, 3};
b := {4, 6, 4, 4};
print a, b;
/* Result:
{ 1, 2, 3 }
{ 4, 6 }
(改行で区切られます)
*/
Step 3. 基本的な演算子
演算子は他の言語と似たようなものもあれば,大きく異なるようなものもあります.それらを見ていきましょう.
四則演算子ほか
種類 | 記法 |
---|---|
加算 | a + b |
減算 | a - b |
乗算 | a * b |
除算 | a / b |
べき乗 | a ^ b |
ただし除算は「整数 / 整数」の場合に有理数として振舞います.
またオペランドがともに整数ならば,次の演算子div
,mod
も使用可能です.(それぞれC言語の/
,%
,つまり商と余りに相当します.)
参考コード
7 * 3; // -> 21
7.0 * 3; // -> 21.0000000000000000000000000000
7 / 3; // -> 7/3
7.0 / 3.0; // -> 2.33333333333333333333333333333
7.0 / 3; // -> 2.33333333333333333333333333333
7 ^ 3; // -> 343
7.0 ^ 3; // -> 343.000000000000000000000000000
7 ^ 0.5; // -> 2.64575131106459059050161575364
7 div 3; // -> 2
7 mod 3; // -> 1
補足
- 四則演算子と代入を組み合わせた複合代入演算子が使用可能です.
x := 6; x; // -> 6
x +:= 4; x; // -> 10
x /:= 5; x; // -> 2
x ^:= 3; x; // -> 8
比較演算子
基本的に他の演算子は記号ではなく単語で表現します.
記法 | C言語では | 意味 |
---|---|---|
a eq b |
a == b |
a とb が等価(Equal) |
a ne b |
a != b |
a とb が異なる(Not Equal) |
a lt b |
a < b |
a がb より小さい(Less Than) |
a le b |
a <= b |
a がb 以下である(Less than or Equal) |
a gt b |
a > b |
a がb より大きい(Greater Than) |
a ge b |
a >= b |
a がb 以上である(Greater than or Equal) |
a in R |
該当なし |
a がR に属する |
a notin R |
該当なし |
a がR に属さない |
なお,この比較結果(BoolElt
型,"Elt"は"Element"の略?)を変数に格納することができます.
a := 4; b := 5;
res := a eq b;
print res; // -> false
補足
- エラーを考慮する等価比較
cmpeq
,cmpne
もあります.2つのオペランドが比較可能ならeq
,ne
と同じですが,比較不可能の場合でもエラーを生じません.(比較不可能ならcmpeq
はfalse
を返し,cmpne
はtrue
を返します.)
1 eq 1; // -> true
1 cmpeq 1; // -> true
1 cmpeq "a"; // -> false
1 cmpne <4, 5>; // -> true
次の場合はエラーとなります.
1 eq "a";
/* Result:
>> 1 eq "a";
^
Runtime error in 'eq': Bad argument types
Argument types given: RngIntElt, MonStgElt
*/
- 文字列に対しても
eq
やlt
などが適用可能です(辞書順で比較).
"A" eq "A"; // -> true
"A" ne "a"; // -> true
"A" lt "a"; // -> true
"ABC" eq "ABC"; // -> true
"ABDA" gt "ABC"; // -> true
論理・集合演算子
同様に記号ではなく単語で表現します.
- 論理演算子
記法 | C言語では | 意味 |
---|---|---|
not A |
!A |
A でない(否定) |
A and B |
A && B |
A かつB (論理積) |
A or B |
A || B |
A またはB (論理和) |
A xor B |
A ^ B |
A とB の排他的論理和 |
論理演算子はBoolElt
型に対してのみ適用可能です.ビットワイズは兼ね備えていません.
補足
-
and
やor
は短絡評価をサポートしています.例えば,(条件1) and (条件2)
で条件1がfalse
であった場合,条件2は評価されません.(条件2がRuntime error
を起こすようなものであってもエラーは発生しません.)
function a()
print "a";
return false;
end function;
function b()
print "b";
return true;
end function;
print a() and b();
print "";
print b() and a();
/* Result:
a
false
b
a
false
*/
- ただし,無視されるのは
Runtime error
のときであり,User error
の場合は短絡評価で評価されなくてもエラーになります.(ただし,where
句の場合はちょっと挙動が異なるようです.)
function f()
return false, _;
end function;
print "without 'where'";
a, b := f();
a and b eq 1;
print "\nwith 'where'";
a and b eq 1 where a, b := f();
(not a) and b eq 1 where a, b := f();
/* Result:
without 'where'
>> a and b eq 1;
^
User error: Identifier 'b' has not been declared or assigned
with 'where'
false
>> (not a) and b eq 1 where a, b := f();
^
Runtime error: Variable 'b' has not been initialized
*/
- 集合演算子
記法 | 数式 | 意味 |
---|---|---|
S meet T |
$S \cap T$ | 共通部分 |
S join T |
$S \cup T$ | 和集合 |
S diff T |
$S - T$ | 差集合 |
S sdiff T |
$S \bigtriangleup T = (S \cup T) - (S \cap T)$ | 対称差 |
もちろん,これらの演算子に加えて括弧( ... )
も使用できます.
- 文字列の結合
文字列の結合にはcat
演算子や*
演算子が使用できます.
a := "World";
print "Hello " cat a * "!";
// Result: Hello World!
大型演算子
総和$\sum$などの大型演算子に対応する演算子&(演算子)(オペランド)
が定義されています.
数式例 | 記法 | 概要 |
---|---|---|
$\displaystyle \sum_{v \in Values} v$ | &+(配列や集合など) |
総和 |
$\displaystyle \prod_{v \in Values} v$ | &*(配列や集合など) |
総乗 |
$\displaystyle \bigwedge_{B \in Bools} B$ | &and(配列や集合など) |
論理積 |
$\displaystyle \bigvee_{B \in Bools} B$ | &or(配列や集合など) |
論理和 |
$\displaystyle \bigcap_{S \in Sets} S$ | &meet(配列や集合など) |
共通部分 |
$\displaystyle \bigcup_{S \in Sets} S$ | &join(配列や集合など) |
和集合 |
また&cat(配列)
あるいは&*(配列)
とすると文字列を結合することができます.
参考コード
&+[1, 2, 3, 4]; // -> 10
&*[1, 2, 3, 4]; // -> 24
&*{5, 2, 5, 5}; // -> 10
&and[true, true]; // -> true
&or[false, true]; // -> true
&join[{1, 2, 5}, {2, 3, 4}]; // -> { 1, 2, 3, 4, 5 }
&meet[{1, 2, 5}, {2, 3, 4}]; // -> { 2 }
&join{{1, 2, 3}, {3, 4}}; // -> { 1, 2, 3, 4 }
&meet{{1, 2, 3}, {3, 4}}; // -> { 3 }
a := [1, 2, 3, 4]; &+a; // -> 10
&cat["Hello", " ", "World", "!"]; // -> Hello World!
Step 4. 比較と繰り返し
早見表
種類 | 構文 |
---|---|
if 文 |
if 条件式 then 文 else 文 end if; |
三項演算子(select 文) |
(条件式) select 値 else 値; |
case 文 |
case 値: when 値1: when 値2: ... end case; |
for 文1 |
for 変数 := 値 to 値 by 値 do 文 end for; |
for 文2 |
for 変数 in 配列等 do 文 end for; |
for random 文 |
for random 変数 in 配列等 do 文(need "break"!) end for; |
while |
while 条件式 do 文 end while; |
repeat |
repeat 文 until 条件式; |
if
文
if
文はif ... then ... else ... end if;
と記述します.末尾のセミコロンを忘れずに!
res := 3 eq 3;
if res then
print "OK";
else
print "NG";
end if;
// Result: OK
なお,インデントは必ず必要というわけではありませんが,読みやすさのために入れておくとよいでしょう.
三項演算子(select
文)
C言語の三項演算子条件式 ? 値 : 値
のような構文が用意されています.
a := 4;
b := (a gt 2) select "> 2" else "<= 2";
print a, b;
// Result: 4 > 2
case
文
C言語でいうswitch
文が記述できます.ただし,C言語と異なり値が一致するwhen
文を見つけたら対応する文を実行してすぐに終了します(case
文から抜け出します).
v := 2;
case v:
when 0:
"zero";
when 1:
"one";
when 2:
"two";
when 2:
"NG";
else:
"another";
end case;
// Result: two
次のような記法もあります.こちらは,必ずデフォルト値を設定しなければならず,またデフォルト値は必ず最後尾に記入しなければなりません.
x := 2;
a := case<x | 0: "zero", 1: "one", 2: "two", default: "another">;
a;
// Result: two
for
文
for
文はfor ... in ... do ... end for;
またはfor 変数 := 最小値 to 最大値 by 更新幅 do ... end for;
と記述します.
for i in [5, 2, 7] do
printf "%o ", i;
end for;
// Result: 5 2 7
for i in {5, 2, 5, 2, 7} do
printf "%o ", i;
end for;
// Result: 2 5 7
for i := 4 to 7 do
printf "%o ", i;
end for;
// Result: 4 5 6 7
次のように,複数変数の指定も可能です.
for i, j, k in [0, 1] do
printf "%o %o %o, ", i, j, k;
end for;
"";
for i in [0, 1], j in [2, 3], k in [4, 5] do
printf "%o %o %o, ", i, j, k;
end for;
"";
/* Result:
0 0 0, 0 0 1, 0 1 0, 0 1 1, 1 0 0, 1 0 1, 1 1 0, 1 1 1,
0 2 4, 0 2 5, 0 3 4, 0 3 5, 1 2 4, 1 2 5, 1 3 4, 1 3 5,
*/
さらに,for random
文を使用すると,リストから要素をランダムにとるループ文が簡単に作成できます.ただし,無限ループになってしまうので,途中でループから抜け出す break
文が必要です.
n := 0;
for random i in [1, 2, 3, 4, 5] do
printf "%o ", i;
n +:= 1;
if n ge 20 then
break;
end if;
end for;
"";
// Result: 3 1 1 3 3 3 3 4 5 5 3 2 1 3 2 4 3 1 5 2
while
文
while
文はwhile ... do ... end while;
と記述します.while
文の直後の条件式を満たす限り繰り返します(C言語におけるwhile
文と同じです).
i := 1;
while i le 10 do
printf "%o ", i;
i := i + 1;
end while;
"";
// Result: 1 2 3 4 5 6 7 8 9 10
repeat
文
repeat
文はrepeat ... until ...;
と記述します.C言語のdo .. while();
と似た構文ですが,repeat
文の方は条件式を満たすまで繰り返します(条件式を満たした時点で終了).
i := -1;
repeat
print i;
i := i * 3;
until i lt 10;
// Result: -1
Step 5. 関数
関数にはいくつかの書き方があります.
function
構文
一般のプログラム言語における「関数」です.ただし何らかの返り値を要求します.
返り値は複数指定できます(カンマ区切り).
返り値を変数に代入する際は,下記の通り,返り値の順番通りに変数を指定する必要があります.途中の値は使わない,という時は,Step 2の補足で紹介した _
を使うとよいでしょう.
f := function(x)
if x gt 0 then
return x;
else
return -x;
end if;
end function;
f(4); // -> 4
f(-3); // -> 3
f := function()
return 1, 3;
end function;
a, b := f();
a, b; // -> 1 3
補足
- キーワード引数を定義することもできます.
f := function(x, y: key1 := 1, key2 := 2)
return (x * y) + (key1 * key2);
end function;
f(3, 2); // -> 8
f(1, 5: key1 := 2); // -> 9
f(4, 6: key2 := 1, key1 := 5); // -> 29
- 可変長引数を定義することもできます.
f := function(x, args, ...)
return [x + v : v in args];
end function;
f(3); // -> エラー(0個は認めない)
f(1, 5); // -> [ 6 ]
f(1, 2, 3, 4, 5, 6); // -> [ 3, 4, 5, 6, 7 ]
- 複数の値を返すとき,先頭以外の返り値に値を指定しない,ということもできます.(このとき,代入する変数は未定義となります.)
c := 4;
function f()
return 1, 2, _, 3, 4; // _ が値を返さない記号(NULL値を返すようなもの)
end function;
a, b, c, d, e := f();
assigned a; // -> true
assigned c; // -> false (先頭のc := 4;のデータも消去される)
func
文
function
の簡略verです.
複雑な処理なく計算式1個で表現できる場合などに便利な記法です.
また,同様にキーワード引数が使用できます.
f := func<x | 3 * x>;
f(3); // -> 9
g := func<x, y, z | x^3 + y^2 + z>;
g(1, 2, 3); // -> 8
procedure
構文
いわゆるvoid
型返り値の関数,すなわち値を返さない関数はこちらのprocedure
(プロシージャ)を使用します.
proc := procedure(a)
if a lt 0 then
a *:= -1;
end if;
for i := 1 to a do
printf "OK: %o, ", i;
end for;
"";
end procedure;
proc(7);
proc(3);
/* Result:
OK: 1, OK: 2, OK: 3, OK: 4, OK: 5, OK: 6, OK: 7,
OK: 1, OK: 2, OK: 3,
*/
なお,前項function
の補足にて説明したキーワード引数や可変長引数はprocedure
でも使用できます.
補足
procedure
に限っては, いわゆる 参照渡し を実現することができます.参照渡しは,仮引数と実引数のそれぞれの変数名の頭に~
(チルダ)を挿入します.
a := procedure(~b, c)
"b: "; b; b := 1; b;
"c: "; c; c := 1; c;
end procedure;
d := 4; e := 5;
a(~d, e);
d, e;
/* Result:
b:
4
1
c:
5
1
1 5
*/
proc
文
procedure
の簡略verです.
こちらは1文のみの実行に対して有効な書式です.func
と違って返り値を必要としません.
PrintValue := procedure(x)
print x;
end procedure;
f := proc<x | PrintValue(x + 1)>;
f(3);
/* Result:
4
*/
関数名と前方宣言
function
,procedure
は,次のように関数名を後に書くこともできます.
function f(x)
return x+1;
end function;
procedure p(x)
print x-1;
end procedure;
f(3);
p(3);
/*Result:
4
2
*/
また,関数を呼び出す際はすでにその関数が定義されていなければなりません.これは,関数内で別の関数を使用する際も同じです.
function f(x)
p(x+1); // この時点で関数pが定義されていなければならない -> 定義は後ろに記述されてあるのでエラーとなる
return x+1;
end function;
procedure p(x)
print x*2;
end procedure;
f(3);
/* Result:
>> p(x+1);
^
User error: Identifier 'p' has not been declared or assigned
>> f(3);
^
User error: Identifier 'f' has not been declared or assigned
*/
このエラーを回避するために,C言語でいうプロトタイプ宣言にあたるforward
文を実行する必要があります.
forward p; // 追加!関数の外に記述すること.
function f(x)
p(x+1);
return x+1;
end function;
procedure p(x)
print x*2;
end procedure;
f(3);
/* Result:
8
4
*/
型とストラクチャについて
func
,function
,proc
,procedure
はすべてUserProgram
型として扱われます.
また,これらを含むストラクチャはParent(何らかの関数)
として得ることができます.ストラクチャは,演算子 !
によるいわゆる型変換(coerce)を行う際や,後述するタプルの型指定などで使われます.
UserPrograms := Parent(func<x|x>);
function do_func(f, x)
// fの実引数として関数しか認めない(procedureも認めることになる)
f := UserPrograms ! f;
/* あるいは
valid, f := IsCoercible(UserPrograms, f);
if(not valid) then ... end if;
*/
return f(x);
end function;
なお,上記の関数とは異なる関数として,Intrinsic
型の関数(組み込み関数)があります.これは,パッケージという特別なファイルにおいて定義される関数であり,引数や返り値に型を指定してオーバーロードを実現することができます.Magmaにデフォルトで用意されているIsPrime
などの関数はすべてIntrinsic
型として定義されています.
Intrinsic
についても同様で,Parent(何らかのintrinsic関数)
としてストラクチャを得ることができます.
Intrinsics := Parent(IsPrime);
UserProgram
とIntrinsic
の両方の型を受け入れたい場合は,後述の余積(Coproducts)を使用するとよいでしょう.
※両方の型を受け入れるものとして他にもProgram
型というのもありますが,これはパッケージにおいて使用可能なものと思ってください.また,パッケージはユーザ定義のものを用いることもできますが,Magma Calculatorではユーザ定義のパッケージを取り扱えないことに注意してください.
※組み込み関数として定義されていても,例えばIsPrime := func<x | true>;
として上書きすることができます.意図しない上書きを避けたいならば,assert not assigned (関数名)
やassert not IsIntrinsic("関数名");
などとしておくとよいでしょう.
Step 6. リスト・数列・集合など
配列や集合の構造はいくつかの種類・記法があります.それらを見ていきましょう.また,この部分は公式ハンドブックもご参照ください.
基本書式
数列 Sequences
[ 1, 2, 3, 4 ];
Pythonのような基本の記法です.型はSeqEnum
.
もちろん要素に変数を導入できます.
この形式では,要素はすべて同じ型となります.
同じ型となれないものはエラーとなってしまいます.
参考コード
f := procedure(list)
for i in list do
Type(i), i;
end for;
end procedure;
f([1, 2 ,3]); "";
f([1, 2, 3.0]); "";
f([1, 2, "3"]); "";
f([1, 2, true]); "";
/* Result:
RngIntElt 1
RngIntElt 2
RngIntElt 3
FldReElt 1.00000000000000000000000000000
FldReElt 2.00000000000000000000000000000
FldReElt 3.00000000000000000000000000000
FldRatElt 1
FldRatElt 2
FldRatElt 3/7
>> f([1, 2, "3"]); "";
^
User error: Could not find a valid universe for the sequence
>> f([1, 2, true]); "";
^
User error: Could not find a valid universe for the sequence
*/
リスト Lists
[* 1, 2, 3, 4 *];
[* ... *]
で囲む記法です.型はList
.
こちらは異なる型を要素に指定できます.
参考コード
f := procedure(list)
for i in list do
Type(i), i;
end for;
end procedure;
f([* 1, 2 ,3 *]); "";
f([* 1, 2, 3.0 *]); "";
f([* 1, 2, 3/7 *]); "";
f([* 1, 2, "3" *]); "";
f([* 1, 2, true *]); "";
/* Result:
RngIntElt 1
RngIntElt 2
RngIntElt 3
RngIntElt 1
RngIntElt 2
FldReElt 3.00000000000000000000000000000
RngIntElt 1
RngIntElt 2
FldRatElt 3/7
RngIntElt 1
RngIntElt 2
MonStgElt 3
RngIntElt 1
RngIntElt 2
BoolElt true
*/
集合 Sets
重複や順序を無視する通常の集合構造です.型はSetEnum
.
要素はすべて同じ型にされます.
{ 1, 2, 3 };
重複は自動で消去され,順序も自動でソートされます.
{ 1, 4, 3 }; // -> { 1, 3, 4 }
{ 1, 1, 3, 3, 5 } // -> { 1, 3, 5 }
順序付き集合 Indexed Sets
指定された順序を保存する集合構造です.型はSetIndx
.
重複は自動で消去され,一番最初のものだけが残されます.
{@ 5, 2, 3, 7 @};
多重集合 Multisets
値が重複した際に重複した個数を保存する集合構造です.型はSetMulti
.
順序は保存されません.
{* 1^^3, 2, 4^^5, 6 *};
参考コード
a := { 1, 4, 3, 3 };
Type(a), a; // -> SetEnum { 1, 3, 4 }
b := {@ 4, 1, 4, 6, 3 @};
Type(b), b; // -> SetIndx {@ 4, 1, 6, 3 @}
c := {* 5, 3, 1, 5, 7, 3, 2, 6, 7 *};
Type(c), c; // -> SetMulti {* 1, 2, 3^^2, 5^^2, 6, 7^^2 *}
特別な表記
リストや集合の個数
先頭に#
を入れます.
a := [1, 2, 3, 4, 5];
#a; // -> 5
G := {1, 2, 3, 4};
print #G; // -> 4
F := {* 5, 2, 5, 7, 8, 3, 2, 3, 6 *};
#F; // -> 9
区間表記
最小値と最大値を指定する書式です.
[ 3 .. 8 ] // = [ 3, 4, 5, 6, 7, 8 ]
[ 3 .. 8 by 2 ] // = [ 3 .. 7 by 2 ] = [ 3, 5, 7 ]
{ 3 .. 8 } // = { 3, 4, 5, 6, 7, 8 }
{ 3 .. 8 by 2 } // = { 3 .. 7 by 2 } = { 3, 5, 7 }
print
出力だと区間指定の書式[ m .. M by s ]
のまま出力されますが,[]
での添字指定や次の内包表記,in
によるループなどを使うと各要素を取り出せます.
内包表記
Magmaにおける正式名称は知りませんが,Pythonにおける内包表記に近いものが使用できます.定義域と条件を指定して,条件を満たすものだけを抽出します.
F := { 4, 2, 1, 6, 4 };
[ x : x in F | x gt 1 ];
// [ 値 : 左値で使用する変数の定義(in構文) | その変数に関する条件式 ];
// { 値 : 左値で使用する変数の定義(in構文) | その変数に関する条件式 };
参考コード
// 素数を取得
[ x : x in [2 .. 50] | IsPrime(x) ];
// Result: [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47 ]
[[x, y] : x in [2..10], y in [2..10] | x lt y and IsPrime(x) and IsPrime(y)];
/* Result:
[
[ 2, 3 ],
[ 2, 5 ],
[ 3, 5 ],
[ 2, 7 ],
[ 3, 7 ],
[ 5, 7 ]
]
*/
[x + 1 : x in [2..100] | IsPrime(x) and IsPrime(x + 2)];
// Result: [ 4, 6, 12, 18, 30, 42, 60, 72 ]
{* x mod 2 : x in [2..100] | IsPrime(x) *};
// Result: {* 0, 1^^24 *}
条件を設けず,すべての要素を抽出する場合は,|
以降を省略できます.
X := [ 1 .. 6 ];
XX := [ x : x in X ];
X, XX;
/* Result:
[ 1 .. 6 ]
[ 1, 2, 3, 4, 5, 6 ]
*/
配列操作などに関する関数
関数 | 概要 |
---|---|
Universe(S) [S][I][M]
|
S の要素が属する型(ストラクチャ)を取得する |
#S [L][S][I][M]
|
S の要素の個数を取得する |
S cat T [L]
|
2つの配列S ,T を結合 |
Min(S) ,Max(S) [S][I][M]Minimum(S) ,Maximum(S)
|
最小値・最大値を取得 (第2返り値に添字が返される) (演算 lt とeq の両方が定義されている場合に限る) |
Index(S, x) [I][L]
|
配列S から要素x を探してその最小の添字を返す(存在しないときは0を返す) |
Rep(S) [S][I][M]Representative(S)
|
S 内の何らかの要素を返す(どの要素を用いてもよいときに使用.) |
Random(S) [S][I][M]
|
S 内から無作為に選んだ要素を返すrandom{ x : x in F | (条件式) } の書式もある(ただし random は見つかるまで無限ループする!) |
Explode(S) [L]
|
S の要素を関数の複数返り値に変換(配列の各要素を別の変数に代入するときに便利) |
Append(S, x) ~[L]
|
要素x を末尾に追加する. |
Include(S, x) ~[S][I][M]
|
要素が配列内に無いときのみ,要素を末尾に追加する. |
Insert(S, i, x) ~[L]
|
i の添字の手前にx を挿入する( i の値がx になるように挿入). |
Insert(S, k, m, T) ~
|
S の添字k からm までの部分をT に移し替える. |
Exclude(S, x) ~[S][M]
|
S からx を探し,最小の添字のものを削除する. |
Remove(S, i) ~[L]
|
S の添字i の要素を削除する. |
Prune(S) ~[L]
|
S の末尾要素を削除する. |
Reverse(S) ~[L]
|
S を反転させる. |
Rotate(S, p) ~
|
S をp 個分右方向に巡回シフトさせる. |
Sort(S) ~[I]
|
S を昇順ソートする.(演算 lt とeq の両方が定義されている場合に限る) |
Sort(S, f) ~
|
第2引数に比較関数f を指定して昇順ソートするf(x, y) le 0 のときx le y ,f(x, y) eq 0 のときx eq y とされる |
ParallelSort(~S, ~T) |
S を昇順ソートし,ソートにおける並び替えに合わせて T を並び替える.#S eq #T が条件. |
ChangeUniverse(S, V) [S][I][M]
|
S の要素のストラクチャをV に変更する |
CanChangeUniverse(S, V) [S][I][M]
|
ChangeUniverse(S, V) を試みる変更可能なら第1引数に true ,第2引数に変更後の配列.不可能なら false . |
Partition(S, p) |
S をp 個ずつに分割( p は#S の約数とする) |
Partition(S, P) |
S をP[1] 個,P[2] 個,P[3] 個...に分割 |
Setseq(S) [S][I]SetToSequence(S)
|
集合S を配列に直す |
Seqset(S) SequenceToSet(S)
|
配列S を集合に直す |
※:~が付いている関数は,第1引数に渡す配列を参照渡し~S
とすると,その配列に変更を加えます(procedure).一方,値渡しとすると,新たな配列を作成してそれを返します(function).参照渡しの場合だと,配列のコピーを作成しないので,巨大な配列を扱う際などに有効です.
※:記号[L],[S],[I],[M]は,それぞれリスト,集合,順序付き集合,多重集合に対しても有効であることを表します.
また,Boolean型(BoolElt
)の要素からなる同じ長さの配列S
,T
に対しては関数And(S, T)
,Or(S, T)
,Xor(S, T)
が使用できます.これらの関数は,同じ添字の要素同士をそれぞれand
,or
,xor
した配列を返します.And(~S, T)
などとすることで,結果をS
に上書きすることもできます(配列のコピーを作成しない).他にもS
に対してNot(S)
もあり,これは各要素にnot
をかけます.
その他のデータ構造
文字列
文字列も変数に格納して使用することができます.型はMonStgElt
で,ストラクチャはStrings()
で入手できます.
なお,ASCII文字のみ使用可能で,ギリシャ文字や日本語などは使用できないので注意してください.
文字列操作などに関する関数まとめ
関数 | 概要 |
---|---|
s cat t s * t
|
2つの文字列を結合 ( &* も使用可能) |
Sprint(x) |
任意型のx がprint で出力する内容を取得 |
Sprintf(format, x1, x2, ...) |
printf 文のようにフォーマットformat を指定して出力内容を返す |
s ^ n |
文字列s をn 回繰り返す |
s[n] |
文字列s のn 番目の文字を取得 |
#s |
文字数を取得 |
Eltseq(s) ElementToSequence(s)
|
1文字ずつに分割 |
m in s |
文字列m が文字列s に含まれているか |
Index(s, m) Position(s, m)
|
文字列s から文字列m を検索し,最初に見つかった添字を返す |
StringToInteger(s) |
文字列を整数値と解釈して変換する |
StringToIntegerSequence(s) |
半角スペースで区切られた整数値を解釈する |
IntegerToString(n) |
整数値を文字列に変換する |
StringToCode(s) |
1文字目のASCIIコードを返す |
CodeToString(n) |
ASCIIコードから文字に変換 |
Substring(s, n, k) |
文字列s の添字n からk 個文を抽出 |
Split(s, d) |
文字列s をEltseq(d) を区切り文字として分割(キーワード引数 IncludeEmpty := true としない限り空白文字は消去される) |
RegExp(pattern, s) |
pattern に文字列s がマッチするか否か,マッチ部分,グループ 使用できる特殊文字は |*+?^$[] のみ |
タプル Tuples
<1, 2.5, "str", 3/7>
と表現します.型はTup
です.
タプルは配列と似ていますが,各要素の型がストラクチャによって管理されています.
t := <1, 2.5, "str", 3/7>;
Parent(t);
// Cartesian Product<Integer Ring, Real field of precision 30, String structure, Rational Field>
t[1] := 3; // OK
t[1] := "three"; // ストラクチャが異なるのでエラー
タプルへの操作として,Append
,Prune
,Explode
のほか,Flat
,TupleToList
などが使えます.
また,後述のデカルト積によって要素の型を明示的に指定することもできます.
c := car<Integers(), RealField(), Strings()>;
print elt<c | 1, 2.5, "4">; //
print c ! (<a : a in [1..4] | a gt 2> cat <"OK">);
function f(args)
types := car<Integers(), RationalField(), Parent(func<x|x>)>;
args := types ! args;
return args[3](args[1], args[2]);
end function;
print f(<3, 1, func<a, b | a + b>>);
/* Result:
<1, 2.50000000000000000000000000000, "4">
<3, 4.00000000000000000000000000000, "OK">
4
*/
なお,要素型を指定しない場合には内包表記が可能です.
<p : p in [1..100] | IsPrime(p)>;
デカルト積(直積) Cartesian products
ストラクチャの(順序付きの)組という構造を与えることができます.
C := car<Integers()>;
// CartesianProduct関数を使うと配列としてストラクチャを渡せる
C := CartesianProduct(<GF(p) : p in [1..10] | IsPrime(p)>);
// デカルト積自体もストラクチャなので入れ子が可能
C := car<Integers(), car<Integers(), Integers()>>;
余積 Coproducts
ストラクチャの余積(多分ここでは直和と呼んでもよい)を与えます.
プログラミング的観点から言えば,TypeScriptのUnion型 string || number
のようなことが実現できます.
procedure f(args)
IntOrBool := cop<Integers(), Booleans()>;
types := car<IntOrBool, IntOrBool>;
args := types ! args;
a := Retrieve(args[1]); // 元の型に戻す
ai := Index(args[1]); // 何番目の型にマッチしたか
b := Retrieve(args[2]);
bi := Index(args[2]);
print a, Type(a), ai, b, Type(b), bi;
end procedure;
f(<4, 1>); // 4 RngIntElt 1 1 RngIntElt 1
f(<true, 7>); // true BoolElt 2 7 RngIntElt 1
レコード(構造体) Records
C言語でいう構造体に近いものが使用できます.型はRec
です.(recformat
で返される方はRecFrmt
.)
つまり,要素の名前とその型(ストラクチャ)をそれぞれ指定することができます.
recformat
で要素の名前とそのストラクチャ(あるいは型)を指定し,rec
で構造を生成します.
Customer := recformat< name: Strings(), age: Integers(), isVIP: Booleans() >;
// ストラクチャ Strings(), Integers() の代わりに型 MonStgElt, RngIntElt を用いてもよい
alice := rec<Customer | name := "Alice", age := 15, isVIP := true>;
Names(alice);
alice`name;
alice ` age;
n := "name";
if alice`isVIP then
alice``n, "is VIP";
else
alice``n, " is not VIP";
end if;
/* Result:
[ name, age, isVIP ]
Alice
15
Alice is VIP
*/
各要素にアクセスするにはバッククォート `
を使用します.(ドット .
でもアロー ->
でもありません!)
ただし,アクセスする要素名を文字列として渡す場合は,バッククォート2個 ``
を使用します.
連想配列 Associative arrays
連想配列を定義することができます.連想配列に使うキーは同じストラクチャに属さないといけません.
a := AssociativeArray();
a[2] := 5; // ストラクチャが Integers() になる
a["OK"] := 5; // エラー
a[3/5] := 2; // ストラクチャが RationalField() に更新される
// 異なる型でもCoveringStructureが存在すればエラーにならない
print a[2]; // 5
Keys(a); // { 3/5, 2 }
a := AssociativeArray(Strings()); // 初めにストラクチャを指定する
a["a"] := "m";
a["b"] := func<x | x + 1>;
for key -> value in a do
printf "%o -> %o (%o)\n", key, value, Type(value);
end for;
/* Result:
b -> function(x) ... end function (UserProgram)
a -> m (MonStgElt)
*/
写像 Maps
写像を定義することもできます.
写像の定義にはmap
を用い,|
をはさんで左に入力が属するストラクチャと出力のストラクチャを指定し,右に入力に対する出力値を指定します.
例( $f: \mathbb{Z} \rightarrow \mathbb{Q},\ x \mapsto x/7$ を表す)
f := map<Integers() -> RationalField() | x :-> x / 7 >;
f(3); // -> 3/7
補足
- 引数に
SeqEnum
(通常の配列),SetEnum
(通常の集合),SetIndx
(順序付き集合)が使用できます. - 演算子
@
が使用できます.(ただし写像f
が後ろになることに注意!)
f := map<Integers() -> Integers() | x :-> x+1>;
a := [1, 5, -32]; // SeqEnum
b := { 5, 1, 3, 6 }; // SetEnum
c := {@ 5, 1, 3 @}; // SetIndx
f(a);
b @ f;
c @ f;
/* Result:
[ 2, 6, -31 ]
{ 2, 4, 6, 7 }
{@ 6, 2, 4 @}
*/
Step 7. デバッグ・エラー処理・その他構文
assert
文
assert (条件式);
とします.条件式がtrue
のときは何も起きませんが,false
のときはエラーが発生します.
a := 5;
assert a gt 3;
a := 3;
assert a gt 5;
/*
>> assert a gt 5;
^
Runtime error in assert: Assertion failed
*/
assert
以外にも,assert2
,assert3
が用意されています.これらはアサーションレベルが異なり,それぞれ1,2,3です.
デフォルトではアサーションレベル1までをエラーとし,2,3はエラー扱いとしませんが,SetAssertions(2);
とするとレベル2までをエラー扱いに,SetAssertions(3);
とするとすべてをエラーとすることができます.
p := procedure(n)
n, "(", GetAssertions(), ")";
end procedure;
f := procedure()
try assert false; catch e p(1); end try;
try assert2 false; catch e p(2); end try;
try assert3 false; catch e p(3); end try;
end procedure;
f();
SetAssertions(2); f();
SetAssertions(3); f();
/* Result:
1 ( 1 )
1 ( 2 )
2 ( 2 )
1 ( 3 )
2 ( 3 )
3 ( 3 )
*/
なお,エラー扱いとされないとき,条件式は評価されないので,その分だけ実行時間が短縮されます.
// SetAssertions(2);
assert2 forall{a : a in [1..99999999] | a mod 1 eq 0};
// 実行時間の違いに注目!
error
文
error 文1, 文2, ..., 文n;
とすると,Javaのthrow
やpythonのraise
のようにエラーを発生させられます.
また,error if (条件式), 文1, ..., 文n;
とすると,条件式を満たす場合のみにエラーが発生します.
このエラーは,次のtry
によって捕捉されます.
error 2,3,4;
error if 4 gt 3, "ERROR";
/* Result:
Runtime error: 2 3 4
Runtime error: ERROR
*/
try
-catch
文
他のプログラミング言語と同様に,上記のエラー発生を監視し,例外処理を設けることができます.エラーが発生した時点でcatch
の内容に進むので,try
内のその後の処理は実行されません.
a := 4;
try
error "OK";
error "NG";
a := 0;
catch e
print "Error: ", e`Object;
end try;
a;
/* Result:
Error: OK
4
*/
エラーを捕捉してくれるのは Runtime error
(error
文はこちらのエラーに該当)であり, User error
(宣言していない変数を使おうとするなど)は対象外であることに注意してください!
exists
文
存在判断を行う構文が使用できます.
exists(変数){ 式 : 定義 | 条件 }
あるいはexists{ 式 : 定義 | 条件 }
の書式に従います.
条件を満たす値が存在するとき,変数に式の評価値が格納され,true
を返します.
存在しない場合は,false
が返され,変数は未定義の状態になります.(内部でdelete
が実行されている?)
参考コード
- 素体の生成元の存在判定
p := Random([x : x in [2..100] | IsPrime(x)]);
p;
F := GF(p);
function IsGenerator(F, x)
assert x in F;
return x ne 0 and Order(x) eq #F-1;
end function;
exists(g){x : x in F | IsGenerator(F, x)};
g;
/* Result:
89
3
*/
- 式は
x
以外でもOK
exists(t){x + 10 : x in [0..9] | x eq 4};
t; // -> 14
forall
文
全称判断を行う構文が使用できます.
forall(変数){ 式 : 定義 | 条件 }
あるいはforall{ 式 : 定義 | 条件 }
の書式に従います.(exists
と同じ)
すべての要素が条件を満たすときにtrue
を返し,変数は未定義の状態になります.
一方,条件を満たさない要素が存在するときは,その1つに対する式の評価値を変数に格納し,false
を返します.
参考コード
- 2以上の整数$n$に対して$\forall a \in (\mathbb Z / n \mathbb Z)^\ast, a^{\varphi(n)} = 1$を検証(オイラーの定理)
n := Random([2..100]);
n;
F := {x : x in Integers(n) | x ne 0 and GCD(x, n) eq 1 };
forall{x : x in F | x^EulerPhi(n) eq 1};
/* Result:
84
true
*/
- 同様に,式は
x
以外でもOK
forall(t){x + 10 : x in [0..16] | x^16 mod 17 eq 1};
t; // -> 10
Step 8. 組み込み関数を使ってみよう
Magmaには,たくさんの関数が用意されています.
ここでは,いくつかの関数を,公式ハンドブックを見ながら実際に試してみましょう!
基本的に,関数の名前は英語名をそのまま使うことが多いらしいです.ただし,略名が有名な場合(Sqrt
やGCD
など)は,略名を使用することがあります.
また,関数の名前は大文字から始まることが多いです.
使用したい処理の名前を英語に直した後は,公式ハンドブックの索引を使いましょう!
素数判定
「素数」は"Prime"です.「~であるかどうか」「~を持つかどうか」などといったYes/No判定には,よくis
やhas
などが使われます.
ここではIsPrime
という関数が用意されています.
早速索引で調べてみましょう!
次の項目が見つかりました.
- IsPrime
IsPrime(D) : DivSchElt -> BoolElt
IsPrime(I) : OMIdl -> BoolElt
IsPrime(I) : OMIdl -> BoolElt
IsPrime(x) : RngElt -> BoolElt
IsPrime(I) : RngFunOrdIdl -> BoolElt
IsPrime(n) : RngIntElt -> BoolElt
IsPrime(n) : RngIntElt -> BoolElt
IsPrime(I) : RngMPol -> BoolElt
IsPrime(I) : RngMPolRes -> BoolElt
IsPrime(I) : RngOrdIdl -> BoolElt, RngOrdIdl
- RngInt_IsPrime (Example H19E4)
このように,IsPrime
関数と一口に言ってもたくさんのオーバーロードが行われています.
コロンで区切られた左側は,その関数の記法が示され,右側には引数の型(ストラクチャ)と返り値のストラクチャが示されています.
どうやら返り値は,下から2番目の特殊そうなケースを除いて,すべてBoolElt
,すなわち通常のBoolean型のようですね.
また,一番下のMonospaceになっていない項目が,その関数の実行例となっています.実行例にはそれぞれIDが付されており,この実行例は「H19E4」らしいですね.
さて,通常のいわゆるint型はRngIntElt
です.
「整数(Int: Integer)環(Rng: Ring)の要素(Elt: Element)」ということですね.
これを引数にしたものが第6項と第7項にあります.
Webサイトでこちらをクリックすると,該当のページに飛ぶことができます.
試しに第6項の方をクリックしてみましょう.
すると(英語ですが)整数を引数にとるIsPrime
関数の説明文が現れます.
さらに嬉しいことに実行例も載ってますね!この実行例は「H19E4」のIDが付記されているので,上記の「RngInt_IsPrime (Example H19E4)」に該当することが分かります.
「引数は一つ」,「通常の整数型が引数にとれる」ことが分かったので,早速実行してみましょう!コードと実行結果は次の通りです.
IsPrime(97); // -> true
IsPrime(93); // -> false
IsPrime(0); // -> false
IsPrime(1); // -> false
IsPrime(-3); // -> true
最大公約数(GCD)・最小公倍数(LCM)
組み込み関数GCD
,LCM
が用意されています(すべて大文字です).
索引で探していたら,ちょうどよいページが見つかりました.
どうやらGcd
,GCD
,GeatestCommonDivisor
の3通りの書き方があるようですね.それらの違いって一体何なんだろう...
引数は,2つの整数値,または整数値の数列だそうです(集合でもいけるっぽい?多分要素の型がすべて同じであるようなデータ構造ならOK).
早速使ってみましょう!
GCD(4, 6); // -> 2
Gcd(4, 6); // -> 2
GreatestCommonDivisor(4, 6); // -> 2
LCM(4, 6); // -> 12
Lcm(4, 6); // -> 12
LeastCommonMultiple(4, 6); // -> 12
GCD([4, 6, 8, 10]); // -> 2
LeastCommonMultiple({3, 4, 5, 6}); // -> 60
素因数分解
素因数分解は英語で"Factorization in prime numbers"とか"Prime factorization"と言います("Factorization"で「因数分解」).
Magmaでは関数Factorization
が用意されています.
同じく索引で調べてみると...
- Factorization
DistinctDegreeFactorization(f) : RngUPolElt -> [ <RngIntElt, RngUPolElt> ]
EqualDegreeFactorization(f, d, g) : RngUPolElt, RngIntElt, RngUPolElt -> [ RngUPolElt ]
Facint(f) : RngIntEltFact -> RngIntElt
Factorisation(A) : ModAbVar -> List
Factorisation(L) : RngDiffOpElt -> SeqEnum, SeqEnum
Factorization(I) : AlgQuatOrdIdl -> SeqEnum
Factorization(A) : GalRep -> List,GalRep
Factorization(L) : LSer -> SeqEnum[Tup]
Factorization(I) : OMIdl -> SeqEnum
Factorization(I) : OMIdl -> SeqEnum
Factorization(I) : RngFunOrdIdl -> [ <RngFunOrdIdl, RngIntElt> ]
Factorization(n) : RngIntElt -> RngIntEltFact, RngIntElt, SeqEnum
Factorization(f) : RngMPolElt -> [ < RngMPolElt, RngIntElt >], RngElt
Factorization(I) : RngOrdFracIdl -> [<RngOrdIdl, RngIntElt>]
Factorization(n) : RngQuadElt -> SeqEnum, Tup
Factorization(f) : RngUPolElt -> [ < RngUPolElt, RngIntElt > ]
Factorization(f) : RngUPolElt -> [ < RngUPolElt, RngIntElt >], RngElt
Factorization(f) : RngUPolElt[RngLocA] -> SeqEnum, RngElt, Any
Factorization(f) : RngUPolElt[RngSerPow[FldFin]] -> [ < RngUPolElt[RngSerPow], RngIntElt > ], RngSerPowElt
Factorization(f, R) : RngUPolXPadElt, FldXPad -> SeqEnum, RngXPadElt, SeqEnum
FactorizationOverSplittingField(f) : RngUPolElt[FldFin] -> [<RngUPolElt, RngIntElt>], FldFin
FactorizationToInteger(s) : [ <RngIntElt, RngIntElt> ] -> RngIntElt
HasPolynomialFactorization(R) : Rng -> BoolElt
IsUFD(R) : Rng -> BoolElt
PartialFactorization(S) : [ RngIntElt ] -> [ RngIntEltFact ]
SeqFact(s) : SeqEnum -> RngIntEltFact
SquareFreeFactorization(f) : RngUPolElt -> [ < RngUPolElt, RngIntElt > ]
SquarefreeFactorization(n) : RngIntElt -> RngIntElt, RngIntElt
SquarefreeFactorization(f) : RngMPolElt -> [ <RngMPolElt, RngIntElt> ]
SquarefreeFactorization(f) : RngUPolElt -> [ <RngUPolElt, RngIntElt> ]
- AlgAff_Factorization (Example H115E5)
たくさんありますね.ここでは整数の素因数分解を行うこととして,引数がRngIntElt
であるものを探しましょう.
12項目にありました.
Factorization(n) : RngIntElt -> RngIntEltFact, RngIntElt, SeqEnum
どうやら3つの返り値を返すようですね.早速実行してみましょう!
a, b, c := Factorization(96);
a; b; c;
/* Result:
[ <2, 5>, <3, 1> ]
1
>> a; b; c;
^
User error: Identifier 'c' has not been declared or assigned
*/
おや?エラーが出ましたね.マニュアルに戻ってみましょう.
マニュアルを読むと,どうやら3つ目の返り値は素因数分解が完了できなかったときに返されるみたいです.
If the factorization could not be completed, a third sequence is returned, containing composite factors that could not be decomposed with the given values of the parameters; this can only happen if both ECMLimit and MPQSLimit have been set. (Note that the third variable will remain unassigned if the full factorization is found.)
また2つ目の返り値には引数の正負が返され,実際に行われる素因数分解は引数の絶対値に対して行われるそうですね.
ちなみに,1つ目の返り値は2つの値の組の数列になっており,その組のうち1つ目がべき根,2つ目が指数に対応するらしいです.つまり,[ <2, 5>, <3, 1> ]
は$2^5 \times 3^1$に対応しています.
ビット操作
C言語では演算子&
や|
などをでしたが,Magmaでは関数を用いてビット操作を行います.
16進の入力(代入)・出力
a := 0xff; // 0xFF や 0b11111111 も可
Type(a); // -> RngIntElt
a; // -> 256
a : Hex; // -> 0xFF
シフト操作
関数 | 概要 |
---|---|
ShiftLeft(n, b) |
整数n をb ビット左にシフトします(n * 2^b と等価). |
ShiftRight(n, b) |
整数n をb ビット右にシフトします(n div 2^b と等価). |
ModByPowerOf2(n, b) |
n mod 2^b を計算します.n が正なら下位b ビットだけ切り抜く処理と等価です. |
ビットワイズ操作
関数 | 概要 |
---|---|
BitwiseNot(x) |
ビット単位の否定を行います. |
BitwiseAnd(x, y) |
ビット単位の論理積を行います. |
BitwiseOr(x, y) |
ビット単位の論理和を行います. |
BitwiseXor(x, y) |
ビット単位の排他的論理和を行います. |
参考コード
ShiftLeft(6, 2); // -> 24
ShiftRight(15, 2); // -> 3
ModByPowerOf2(65535, 6); // -> 63
BitwiseNot(1); // -> -2 (1 -> 01 -> 10 -> -2)
BitwiseNot(5); // -> -6 (5 -> 0101 -> 1010 -> -6)
BitwiseAnd(0xFFF0, 0xF0FF) : Hex; // -> 0xF0F0
BitwiseOr(0xFFF0, 0xF0FF) : Hex; // -> 0xFFFF
BitwiseXor(0xFFF0, 0xF0FF) : Hex; // -> 0xF0F
時間計測
Magmaでは,時間計測を行う次の関数が用意されています.
Cputime
関数
実行にかかったCPU時間を計測できます.返り値は実数FldReElt
です.
返される値は,セッション開始からの経過時間(秒)を表します.経過時間を引数に渡すことで,その時間からの差分を得ることができます.
t0 := Cputime();
a := [ i: i in [10^1..10^6] | IsPrime(i) ];
t1 := Cputime(t0);
"t0:", t0; // -> t0: 0.010
"Cputime:", t1, "sec"; // -> Cputime: 0.360 sec
Realtime
関数
現在時刻を取得できます.返り値は実数FldReElt
です.
返される値は,グリニッジ標準時刻(GMT)の1970/01/01 00:00:00を基準にした経過時刻(秒)を表します.Cputime
と同様,前に取得した現在時刻の値を引数に渡すことで,その時刻からの差分を得ることができます.
t0 := Realtime();
a := [ i: i in [10^1..10^6] | IsPrime(i) ];
t1 := Realtime(t0);
"t0:", t0; // -> t0: 1661836107.400
"Realtime:", t1, "sec"; // -> Realtime: 0.370 sec
ClockCycles
関数
Magmaのスタートアップ終了時からの経過クロックサイクル数を取得できます.こちらの引数は整数RngIntElt
です.
上の2関数と違って,引数を指定することはできません.
また,このクロックサイクル数は,プロセスのユーザー・システム時間ではなく,実時間に則しています.
プロセッサが機能をサポートせず,クロックサイクル数を取得できない場合,0の値を返します.
t0 := ClockCycles();
a := [ i: i in [10^1..10^6] | IsPrime(i) ];
t1 := ClockCycles();
"t0:", t0; // -> t0: 358638270514
"t1:", t1; // -> t1: 359798462972
"ClockCycles:", t1-t0; // -> ClockCycles: 1160192458
Time
関数
Cputime
,Realtime
の同じように使用しますが,返り値が文字列MonStgElt
であることと,並列処理時の挙動に特徴があります(実時間とCPU時間の短い方を返却し,実時間の場合は[r]
タグが付けられるらしい).
t0:=Time();
t0; // -> 0.02000 1661837326.68000 0
a := [ i: i in [10^1..10^6] | IsPrime(i) ];
t1:=Time(t0);
t1; // -> 0.360
time
文
time (文);
とすると,1文の実行時間を簡単に出力することができます.
SetShowRealTime(true);
とすると,CPU時間だけでなく実時間も出力することができます.(デフォルトはCPU時間のみ.現在の状態はGetShowRealTime()
で取得可能.)
procedure f() a:=[ i: i in [10^1..10^6] | IsPrime(i) ]; end procedure;
"false(default):"; SetShowRealTime(false);
time f();
"\ntrue:"; SetShowRealTime(true);
time f();
/* Result:
false(default):
Time: 0.360
true:
CPU time: 0.360
Real time: 0.360
*/
おわりに
いかがでしたでしょうか?
ここでは,プログラミング言語の側面からMagmaの書き方を取り上げました.
しかしまだまだ基本を触っただけ.
Magmaの真髄である代数処理はこれからです.
さらに難しくなりますが,頑張って学習を進めていきましょう!
また,本記事で紹介したもの以外にも,Magmaにはさらにたくさんの関数や構造があります.
本記事の内容も活用しつつ,あとはマニュアルなどで学習を深めていってください!