0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Magma 入門

Last updated at Posted at 2022-02-03

はじめに

この記事は,代数学などの問題を解くのに適した数式処理システム「Magma」を新たに始める方向けの入門記事です.

Magmaには公式ハンドブック(オンライン)や簡単な入門書などの,豊富なドキュメント(その他はこちらを参照)が用意されていますが,いずれも英語で書かれており,初学者(私も含む)にはかなりとっつきにくいことでしょう.
そこで,Magmaを始めるにあたって最も基礎的な部分,特に一般的なプログラミング言語に則した部分を日本語で学習できるようにこの記事にまとめました!
私もMagmaに関しては初心者ですので,ご一緒に学習していきましょう!

なお,こちらの紹介記事も大変参考になるので,是非ご一読ください!本記事も,このサイトを参考にしております.

また,他の数式処理システムも考えている方は,次のサイトが参考になるかと思います.

目次

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

ただし除算は「整数 / 整数」の場合に有理数として振舞います.
またオペランドがともに整数ならば,次の演算子divmodも使用可能です.(それぞれ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 abが等価(Equal)
a ne b a != b abが異なる(Not Equal)
a lt b a < b abより小さい(Less Than)
a le b a <= b ab以下である(Less than or Equal)
a gt b a > b abより大きい(Greater Than)
a ge b a >= b ab以上である(Greater than or Equal)
a in R 該当なし aRに属する
a notin R 該当なし aRに属さない

なお,この比較結果(BoolElt型,"Elt"は"Element"の略?)を変数に格納することができます.

a := 4; b := 5;
res := a eq b;
print res; // -> false
補足
  • エラーを考慮する等価比較cmpeqcmpneもあります.2つのオペランドが比較可能ならeqneと同じですが,比較不可能の場合でもエラーを生じません.(比較不可能ならcmpeqfalseを返し,cmpnetrueを返します.)
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
*/
  • 文字列に対してもeqltなどが適用可能です(辞書順で比較).
"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 ABの排他的論理和

論理演算子はBoolElt型に対してのみ適用可能です.ビットワイズは兼ね備えていません.

補足
  • andor短絡評価をサポートしています.例えば,(条件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
*/

関数名と前方宣言

functionprocedureは,次のように関数名を後に書くこともできます.

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
*/

型とストラクチャについて

funcfunctionprocprocedureはすべて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);

UserProgramIntrinsicの両方の型を受け入れたい場合は,後述の余積(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つの配列STを結合
Min(S)Max(S)[S][I][M]
Minimum(S)Maximum(S)
最小値・最大値を取得
(第2返り値に添字が返される)
(演算lteqの両方が定義されている場合に限る)
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)~ Sp個分右方向に巡回シフトさせる.
Sort(S)~[I] Sを昇順ソートする.
(演算lteqの両方が定義されている場合に限る)
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) Sp個ずつに分割
p#Sの約数とする)
Partition(S, P) SP[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)の要素からなる同じ長さの配列STに対しては関数And(S, T)Or(S, T)Xor(S, T)が使用できます.これらの関数は,同じ添字の要素同士をそれぞれandorxorした配列を返します.And(~S, T)などとすることで,結果をSに上書きすることもできます(配列のコピーを作成しない).他にもSに対してNot(S)もあり,これは各要素にnotをかけます.

その他のデータ構造

文字列

文字列も変数に格納して使用することができます.型はMonStgEltで,ストラクチャはStrings()で入手できます.

なお,ASCII文字のみ使用可能で,ギリシャ文字や日本語などは使用できないので注意してください.

文字列操作などに関する関数まとめ
関数 概要
s cat t
s * t
2つの文字列を結合
&*も使用可能)
Sprint(x) 任意型のxprintで出力する内容を取得
Sprintf(format, x1, x2, ...) printf文のようにフォーマットformat
指定して出力内容を返す
s ^ n 文字列sn回繰り返す
s[n] 文字列sn番目の文字を取得
#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) 文字列sEltseq(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"; // ストラクチャが異なるのでエラー

タプルへの操作として,AppendPruneExplodeのほか,FlatTupleToListなどが使えます.

また,後述のデカルト積によって要素の型を明示的に指定することもできます.

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以外にも,assert2assert3が用意されています.これらはアサーションレベルが異なり,それぞれ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 errorerror文はこちらのエラーに該当)であり, 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には,たくさんの関数が用意されています.
ここでは,いくつかの関数を,公式ハンドブックを見ながら実際に試してみましょう!

基本的に,関数の名前は英語名をそのまま使うことが多いらしいです.ただし,略名が有名な場合(SqrtGCDなど)は,略名を使用することがあります.
また,関数の名前は大文字から始まることが多いです.
使用したい処理の名前を英語に直した後は,公式ハンドブックの索引を使いましょう!

素数判定

「素数」は"Prime"です.「~であるかどうか」「~を持つかどうか」などといったYes/No判定には,よくishasなどが使われます.
ここでは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)

組み込み関数GCDLCMが用意されています(すべて大文字です).

索引で探していたら,ちょうどよいページが見つかりました.

どうやらGcdGCDGeatestCommonDivisorの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) 整数nbビット左にシフトします(n * 2^bと等価).
ShiftRight(n, b) 整数nbビット右にシフトします(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関数

CputimeRealtimeの同じように使用しますが,返り値が文字列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にはさらにたくさんの関数や構造があります.

本記事の内容も活用しつつ,あとはマニュアルなどで学習を深めていってください!

参考サイト

関連リンク

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?