といった C 言語標準の数学関数が ex 型に拡張されているが、ex 型 を返すためには引数に ex 型を与える必要がある。つまり例えば、 公開コピー誌 ex eq=1+sqrt(ex(4)); GiNaC ライブラリの使い方 cout << eq <<endl; というふうに ex 型のコンストラクタで数値を与える。std::string 暗黒通信団 型で与えた式を使えるというのは大きく、例えば std::cin あたりか ら読んだ文字列を str() メンバで std::string にすれば、すぐさま GiNaC は C 用の数式処理ライブラリである。ライセンスは 電卓のできあがりだ。パーサを書いたことある人ならばこれだけで ++ GPL なのでフリーである。もとは素粒子物理学の人々が作ったも もかなり感動モノである。 の*1 なので物理で使うような機能もあるが、Maple *2 の代替となるこ GiNaC は文字変数も扱える。以下の例は √ ab+0.3b を定義してか とを目的に開発されているというあたり、特に物理のことは考えなく ら b = 2 のときの値を出力する。この例以降、ヘッダと namespace ても単なる数式処理ツールとして十分に使える。本稿は GiNaC に感 と int main() は省略する。 動した筆者が、日本語文献のほとんどない状況を悲しみ、普及と啓蒙 symbol a("a"), b("b"); のために書いたものである。ただしテンソルやクリフォード代数や、 ex eq=sqrt(a*b)+0.3*b; その他の物理に特化した機能については書かない。また行列について cout << eq.subs(b==2) <<endl; は GiNaC 固有の行列実装よりも Eigen などの汎用テンプレートラ symbol で文字変数を定義し、ex で式を定義し、ex の各メンバ関数 イブラリと組み合わせたほうが自由度が高いので、そちらを参照され で微分したり数値代入したりするというのが GiNaC における定番の たい。Ubuntu/Debian 系であれば標準で提供されており「apt-get 使い方である。次節からそれぞれについて詳述する。ちなみに、本稿 install libginac-dev」だけでインストールできる。このライブ にでてくるクラスは以下である。他にも式を構成するための様々な ラリを利用するときのコンパイルオプションは g++、clang++ であ オブジェクトがあるがそれは割愛する*3 。GiNaC を使うには知って れば-lginac -lcln である。両方つけなくてはいけない。また、完 おいたほうがよい。 全な (英語の) マニュアルは HTML と PS と PDF で提供されてお • numeric: 数値 り http://www.ginac.de/tutorial/から取得できる。特に PDF • symbol: 変数 マニュアルは http://www.ginac.de/tutorial.pdf である。 • lst: リスト • ex: 式 1 基本的な使い方 ● 2 数値 ● 文字列を動的に解釈して計算するコードを書くには、古くから yacc などのパーサジェネレータが使われてきた。しかし数式に限ってい えば、GiNaC で std::string 型で与えた式をそのまま計算するこ 1 定数 ◆ とができる。以下はそのサンプルである。 定数は正確に言えば constant(定数クラス)なのだが、この型は意 #include <iostream> 識しなくてもよい。以下の三つの定数が定義されている。 #include <string> • Pi 円周率 #include <ginac/ginac.h> • Catalan カタラン定数 ∼ 0.91596559 using namespace std; • Euler オイラー定数 using namespace GiNaC; int main(){ 2 numeric 型 ◆ string st("2+3*sqrt(4)"); 数値の設定にはいくつかの方法がある。 ex sample(st, 0); cout << sample <<endl; • 厳密な整数の定義:numeric p=2; return 0; • 有理数の定義:numeric p(2,3); ちなみに numeric p(3/2) では先に 3/2 が int 型として評 } 価されてしまうのでダメだ。この場合は numeric(1)/2 なら 式は ex という型で表現する。sqrt のほか log や exp や sin や cos 適切に設定できる。 *1 チュートリアルにはエルミート多項式の計算が出ていたりするが、民間人には難しすぎると思う。 有料の数式処理ソフト。Mathematica みたいなもの。 *3 多項式を構成するための、加算や乗算や比較演算子や累乗の結果を格納するオブジェクト、組み込みの数学関数もオブジェクトとして実装されている。 *2 1 • 浮動小数点:numeric p(2.71828); • real(z) 実数分を取り出す。 • 文字列からの浮動小数点:numeric p="3.14159"; • imag(z) 虚数部分を取り出す。 • csgn(z) 複素数かどうか。返り値は int 型。 p("1.08410151E-2") といった指数表記も使える。 • 複素数の設定:numeric p=2-3*I; • step(x) 階級化。返り値は numeric 型。 整数計算精度は、例えば Digits = 60; と指定すれば 60 桁で処理さ • numer(z) 有理数の分子を取り出す。 れる。これはグローバル変数である。数値を出力するには cout << • denom(z) 有理数の分母を取り出す。 evalf(p) <<endl; か、cout << p.evalf() <<endl; とする。 • Li2(z) 対数積分関数 • zeta(z) リーマンのζ関数 通常の基本型数値への変換は以下のメンバ関数を使う。なお、虚数 • tgamma(z) ガンマ関数 部は無視される。 • numeric::to int(); • lgamma(z) 対数ガンマ関数 • numeric::to long(); • psi(z) ψ 関数 • numeric::to double(); • psi(n, z) ψ 関数 • factorial(n) 階乗 (n!) 実際の使用例をあげておく。まずはまどろっこしい例。 ostringstream tmp; • doublefactorial(n) n!! siki.subs(la==x).print(print_csrc_double(tmp,0),0); • binomial(n, k) 二項係数 double data=atof(tmp.str().c_str()); • bernoulli(n) ベルヌーイ数 • fibonacci(n) フィボナッチ吸う 次はいくぶんエレガントな例である。 symbol x("x"); • irem(a, b) 剰余 ex e = series(sin(x/Pi), x==0, 6);//6 次まで展開 • iquo(a, b) 整数商を返す。 ex f = evalf(e.subs(x == 0.1));//0.1 のときの数値 • gcd(a, b) 最大公約数を返す。 if (is_a<numeric>(f)){//数値チェックが必要 • lcm(a, b) 最小公倍数を返す。 numeric 型の数値についているメソッドは次の通りである。 double d = ex_to<numeric>(f).to_double(); • .is zero() } • .is positive() 実数値かつ 0 以上かどうか numeric 型には次の関数が定義されている。意味が C 言語と同じ • .is negative() 実数値かつ 0 未満かどうか ものはコメントを割愛する。 • pow(a, b) • .is integer() 実数整数かどうか • sin(z) • .is pos integer() • cos(z) • .is nonneg integer() • tan(z) • .is even() • asin(z) • .is odd() • acos(z) • .is prime() • atan(z) • .is rational() • atan(y, x) • .is real() • sinh(z) • .is cinteger() 複素整数かどうか • cosh(z) • .is crational() 複素有理数かどうか • tanh(z) • asinh(z) 3 記号変数 ● • acosh(z) 変数の宣言は symbol である。式の中で使うシンボルはコンストラ • atanh(z) クタで文字列として与える。つまり symbol X("x")l と宣言すると、 • exp(z) 式中の x という記号が変数と見なされる。もしも単に symbol X と • log(z) だけ宣言すると、文字列は symbolXXX という名前が指定されたもの • abs(z) として内部的に勝手に割り振られる。逆に配列などで名前なしで記 • mod(a, b) 号だけを定義することもできる。 • sqrt(z) 実は GiNaC は出力を LATEX 形式にすることができるが、その際 • isqrt(n) 整数平方根 LATEX 用に使う文字列をコンストラクタの第二引数に与えることが • inverse(z) 1/z を返す。 2 できる。つまり symbol a("a","\alpha") とすれば、数式入力中で } は「a」を使い、印刷出力では「α」にしたりすることができる。小学 これを使って式を std::cout に渡すことができる。 校算数の課題を作るには symbol x("x", " std::copy(e.begin(), e.end(), Box") という変換もよい。 std::ostream_iterator<ex>(cout, "\n")); symbol 宣言で作られた変数は(数値と同様に)複素数として扱わ ex 型は std::vector に入れることもできる。しかし式の大小関 れる。もし実数として扱う変数を作るのなら realsymbol x("x") 係が定義されていないので std::set や std::map に入れることは とする。さらに正の数として定義するなら possymbol x("x") で できない。式表現において累乗は使えるが、2*x^2 は (2x)2 と解釈 ある。 される。 式について様々な処理をする関数群が用意されている。例えば偏 微分 (diff) や有理化 (normal)、式の簡約 (simplify_indexed) な 4 リスト ● どである。Mathematica とまではいかないが、これらを組み合わせ lst 型は複数のデータをまとめて一つにする型である。ex 型や てある程度の数式処理系を作ることができる。簡約化などを使えば、 symbol 型を含めることができる。イテレータを使って逐次処理をす 例えば乱数をふって問題を出し、答えを計算することで正解かどうか ることもできる。 自動評価するような教育ソフトも作れるだろう。 よく使う例としては、複数の変数をまとめて ex に与えるために 使う。 2 拡張された数学関数 ◆ string st("2*a+3*sqrt(4)*b"); ex sample2(st, lst(a,b)); • abs(x) cout << sample2 <<endl; lst は動的に構成することができる。以下は lst 操作のための基本 • step(x) step function 的なメンバ関数である。 • csgn(x) 複素数かどうか • 最後尾に追加:append() • conjugate(x) 複素共役 無名の記号を追加するには append(symbol("x")) などと • real part(x) 実数部 する。 • imag part(x) 虚数部 • 前に追加:.prepend() • sqrt(x) • 最初の要素を削除:.remove first() • sin(x) • 最後の要素を削除:.remove last() • cos(x) • 全クリア:.remove all() • tan(x) • ソート:.sort() • asin(x) • ユニーク:.unique() • acos(x) • 要素数を返す:.nops() • atan(x) • 要素の中身を返す:.op() • atan2(y, x) • sinh(x) 例えば 2 番目の要素の中身を返すなら l.op(2) である。 • 要素に中身を設定:.let op() • cosh(x) 例 え ば l.let op(1)=7 と い う ふ う に 使 う 。ち な み に • tanh(x) l[1]=42 というふうに直接アクセスすることもできる。 • asinh(x) • acosh(x) • atanh(x) 5 式とその処理 ● • exp(x) • log(x) 1 全般 ◆ • eta(x,y) イータ関数 log(xy) − log(x) − log(y) • Li2(x) 対数積分関数 式は ex 型である。式は数値や変数や関数から成る。こうした式の • Li(m, x) 多重対数関数 各要素は lst 同様にイテレータで順に参照することができる。 • G(a, y) 多重対数関数 for(auto i = e.begin();i!=e.end();++i){ • S(n, p, x) ニールセンの一般化多重対数関数 cout << *i << endl; • H(m, x) 調和多重対数関数 3 • zeta(m) リーマンの ζ 関数 次は通常関数による expand と collect() による整列の例である。 • zeta(m, s) オイラー和 ex e=expand((sin(x)+sin(y))*(1+p+q)*(1+d)); • zetaderiv(n, x) 微分 ζ 関数 collect(e,{p,q}); • tgamma(x) ガンマ関数 collect(e,find(e,sin(\$1))); • lgamma(x) 対数ガンマ関数 collect_common_factors(a*x+a*y); • beta(x, y) β 関数 collect_common_factors( • psi(x) ψ 関数 a*(b*(a+c)*x+b*((a+c)*x+(a+c)*y)*y) • factorial(n) 階乗 (n!) ); • binomial(n, k) 二項係数 degree() は最大次数を返し、ldegree() は最小次数*4 を返す。式 • Order(x) 多項式展開のオーダー項 p の次数 n の項の係数は coeff(p, int n) で求める。次の例はシ ンプルで分かりやすい。 symbol x("x"), y("y"); 3 代入と零点計算 ◆ ex ptmp=4*pow(x,3)*y+5*x*pow(y,2) 代入は p.subs(f,x==2) などとする。複数の数値を設定するには +3*y-pow(x+y,2)+2*pow(y+2,2)-8; lst で与える。 ex p=ptmp.expand(); ex e=x*y+x; for(int i=p.ldegree(x);i<=p.degree(x);++i){ cout << e2.subs(lst(x==-2,y==4))<<endl; cout <<p.coeff(x,i)<< endl; subs に よ る 複 数 の 値 設 定 は パ ラ レ ル に 動 く の で subs(lst(x==y,y==x)) というのは記号 x と y を交 換すること 5 微分と積分と級数展開 ◆ になる。一般的に記号置換にこの方法が使える。 GiNaC の微分は基本的に偏微分である。 記号的に式を解くには、例えば lsolve(1+x*3==5,x) と書く。数 値的に解くには fsolve(x==cos(x),x,0,2) と書く。連立方程式を symbol x("x"), y("y"), z("z"); 解くには lst で式と変数を与える。 ex p=pow(x,5)+pow(x,2)+y; symbol a("a"), b("b"), x("x"), y("y"); cout << p.diff(x,2) << endl;//20*x^3+2 lst eqns = a*x+b*y==3, x-y==b; cout << p.diff(y) << endl;// 1 lst vars = x, y; cout << p.diff(z) << endl;// 0 ちなみに積分は integral(x, 0, 1, x*x) などとするが、多項式 cout << lsolve(eqns, vars) << endl; の積分しかできない。 //x==(3+b^2)/(b+a),y==(3-b*a)/(b+a) 級 数 展 開 は series() を 使 う 。例 え ば ex gamma = 1/sqrt(1 中カッコでまとめれば lsolve(a*x+b*y==3,x-y==b,x,y) と直接書 - pow(v/c,2)) と い う 式 を v いてもよい。 = 0 で 6 項まで展開 す る た め に ex gamma.series(v==0, 6) と す る 。あ る い は series(gamma,v==0, 6) で も よ い 。ど ち ら に せ よ 結 果 4 展開と並べ替え ◆ は 1+(1/2*c^(-2))*v^2+(3/8*c^(-4))*v^4+Order(v^6) と な expand() と collect() が 基 本 で あ る 。expand() に は オ プ シ る 。オ ー ダ ー 項 は 通 常 邪 魔 な の で series to poly() を ョンが 2 つある。expand options::expand transcendental と 使 っ て オ ー ダ ー 項 を 削 除 す る 。す な わ ち 例 え ば ex a = expand options::expand function args である。実例を見るの series to poly(atan(x).series(x,3)) などとする。 がよい。 ex e=exp(pow(x+y,2)); 6 整数論的処理 ◆ cout << e.expand() << endl; //exp((x+y)^2) cout << e.expand( • 因数分解 expand_options::expand_transcendental factor(e) とする。expand してからでないとまともに動か ) << endl; //exp((x+y)^2) ない。 cout << e.expand( • 無平方分解 expand_options::expand_function_args sqrfree(e, lst(x,y)) ) << endl; //exp(2*x*y+x^2+y^2) *4 次数 0 は 0 である。 4 な ど と す る 。sqrfree(e, lst(y,x)) とすれば結果は変わる。 for(uint i=0;i<mdim;i++){ • 最小公倍数、最大公約数 for(uint j=0;j<mdim;j++){ gcd(e1, e2) および lcm(e1, e2) である。 m(i,j)=ex("0",vals); • 終結式 }} ex e1 = x+pow(y,2), e2 = 2*pow(x,3)-1, r; //cout <<"Sylvester’s metrix"<<endl; ex r=resultant(e1, e2, x); //1+2*y^6 for(uint i=0;i<dim-1;i++){ r = resultant(e1, e2, y); for(uint j=i;j<dim+1+i;j++){ //1-4*x^3+4*x^6 • 多項式の割り算 ostringstream tmp2; tmp2 << "a" << dim-j+i; a = b × quo(a, b, x) + rem(a, b, x) という関数で求 m(i,j)=ex(tmp2.str(),vals); める。割り切れるかどうかの判定は divide(a, b, q) で }} bool 値が返る。 for(uint i=0;i<dim;i++){ for(uint j=i;j<dim+i;j++){ 7 行列 ◆ ostringstream tmp2; GiNaC にも固有の行列実装があるが、実際には Eigen などの汎用テ tmp2 << dim-j+i << "*a" << dim-j+i; ンプレートライブラリに入れたほうが統一的に扱える。本稿は Eigen m(i+dim-1,j)=ex(tmp2.str(),vals); の解説ではないので実例のみを紹介する。以下は Sylvester 行列を計 }} 算することで n 次関数の判別式を求める完全なプログラムである。n ex det(m.determinant()); は 4 か 5 くらいまでにしておかないと指数爆発する。 cout << det << endl; #include <ginac/ginac.h> return 0; #include <eigen3/Eigen/Core> } #include <eigen3/Eigen/Eigen> #include <iostream> #include <fstream> 6 出力 ● #include <sstream> 通常の出力は std::cout で良い。std::ostream の出力なので任意 のファイルに振り向けることもできる。注意点としては pow(x,2) は x^2 と表示される点である。 using namespace std; GiNaC では LATEX 形式、数値表現、内部表現の出力もできる。 using namespace Eigen; • cout << latex:出力を LATEX 形式に変更する。string 型 using namespace GiNaC; を ex 型を経由して LATEX 形式に出力すれば、GiNaC を C++ における LATEX コンバータとして使える。 int main(int ac, char *av[]){ • cout << tree:内部表現形式に変更する。デバッグ時など、 if(ac<2){cout << "./a.out dim\n";return -1;} 中で何が行われているかを見るには良い。 uint dim=atoi(av[1]); • cout << csrc float:出力形式を float 型に。 uint mdim=2*dim-1; • cout << csrc double:出力形式を double 型に。 lst vals; • cout << csrc cl N:出力形式を CLN の整数型に。 for(uint i=0;i<=dim;i++){ • cout << dflt:出力形式を元に戻す。 ostringstream tmp1; tmp1 << "a" << i; GiNaC ライブラリの使い方 vals.append(symbol(tmp1.str())); 2016 年 4 月 1 日 PDF 版発行 著 者 シ 発行者 星野 香奈 (ほしの かな) 発行所 同人集合 暗黒通信団(http://ankokudan.org/) 〒277-8691 千葉県柏局私書箱 54 号 D 係 頒 価 0 円 } Matrix<ex, Dynamic, Dynamic> m; ∑ ∞ ·`· m.resize(mdim, mdim); ミスがあれば指摘下さい。 c ⃝Copyright 2016 暗黒通信団 5 Printed in Japan
© Copyright 2024 Paperzz