15. SymPy in Julia: 数式処理
Photo by Jeswin Thomas on Unsplash
数式処理 超入門
Julia ではいくつかの数式処理システムを利用できる. その中でもっともよく使われているものが SymPy package だろう.
この package の使い方だが,幸い,package 自身にドキュメントの素になる資料がついてくる. この資料を julia でコンパイルして html にすると web browser で読めるようになるのだが,面倒だろうから, 読めるようにした
package に付属する SymPyCore document
を用意した. この資料は入門用資料として結構よくできているので,本来はこれを一通り読むのが望ましいなあ.できれば読んでおこう.
はじめに
そして,ここではなるべく簡単に SymPy の使い方を紹介しよう. このパッケージは,VIORAS環境だと既にインストール済みだ.
SymPy パッケージのインストール (VIORAS 環境では不要!)
個人環境等で SymPy が未インストールの場合は下記のようにしてインストールしておこう.
1using Pkg
2Pkg.add("SymPy")
さて,このパッケージの利用宣言をしよう.
1using SymPy # 数式処理パッケージ
そして,SymPy のポイントを先に説明しよう. そのポイントとは,
SymPy では 「数式処理の対象となる変数を先に陽に指定する」 という仕組み
である,とでもいうものである. 言い換えると,「指定すると特別な変数」,「指定していない変数は,いつもどおり Julia の普通の変数」であるのだ. そのあたりを例で示そう.
まず,数式処理と関係なく,いつものように普通に Julia で 関数を定義してみよう.
1p(x) = x^2 + 3x + 2
ここで出てくる変数 x
は関数定義のために仮に使われたもので特別なものではないので,
1p(x)
と入力すると(それまでに x
を使っていなければ) UndefVarError: x not defined
なんてエラーが出たりする.
また,これまでの知識通り,関数 p
の入力を数字に置き換えたり,関数 p
をプロットできたりする.
例えば
例えば
1p(3)
とすると計算結果として 20
が出てくるし,
1using Plots
2plot(p)
とすると
というプロットが普通に得られる.
しかし,次のように @syms
という命令を使って数式処理の対象変数を指定1してこれを使うとなると話が変わってくる.
1# t という名前の変数を数式処理の対象に!
2@syms t
まず,この変数を使うと違いが発生することを確認しよう.次の入力をしてみるとすぐわかる.
1eq = p(t)
すると出力が $t^2 + 3t + 2$ という数式で出てきて,「おや,これまでとなにか違うぞ」と感じるわけだ.
何が起きているか少し解説すると,これは数式処理対象として指定した変数 t
を使っているので,
(p(x)
は Julia の関数だが ) p(t)
は Julia の関数ではなく,数式として扱われる ことによる違いが見えているのだ.
まあもう少し書くと,ここでは,「関数は計算手順を書いたもの」であるが「数式は(特殊な)文字列」だ,と思っておけば良い.
まあこれで eq
が数式であることが認識できたので,この数式に対して以下のように,実際に数式処理を施してみよう.
1# 因数分解
2factor( eq )
$(t+1)(t+2)$
1# 展開
2eq2 = expand( eq * (t^2-1) )
$t^4 + 3t^3 + t^2 -3t -2$
1# 微分
2diff( eq, t )
$2t+3$
1# 不定積分
2integrate( eq, t )
$\displaystyle \frac{t^3}{3}+\frac{3t^2}{2}+2t$
1# 定積分
2integrate( eq, (t, -2, PI) )
$\displaystyle \frac{2}{3}+2\pi+\frac{\pi^3}{3}+\frac{3\pi^2}{2}$
SymPy package には特別な定数記号がいくつかある.
PI
(アルファベット大文字の P と I)は円周率を表す記号であり,oo
(アルファベットの o を2つ書く)は無限大を表す記号である.
1# 変数が複数の関数も問題ない.
2@syms u v
3
4eq3 = p(v) * (u^2 - 1) * exp(u/v)
$(u^2-1)(v^2+3v+2)e^{u/v}$
このように,数式に単になにか変形を加えるというのであれば本当に簡単だ.
数式を関数に戻す/変換するには?
上の例のように,関数から数式を作るのは単に「特別な変数を代入」するだけでよいので簡単だった.
では逆はどうだろう?
せっかく数式処理して得た結果の数式を関数に直して計算に使いたい,というのは自然な要求だ.
それは簡単で,たとえば上の数式 eq2
を 変数 t
をもらって t の4次式に基づいた計算値を返す関数に変換するには,
1eval(Meta.parse("p4(t) = $eq2"))
とすれば良い2.
これで数式 eq2
を変換した関数 p4(t)
を定義したことになる(ただしこの p4(t) の中の t は SymPy と関係なく,単なる普通の変数扱い).
これで得られた p4(t)
は確かに上で求めた 4次多項式の値を計算する関数になっている.
複数の入力変数があるときでも同様で,たとえば上の数式 eq3
に対しては
1eval(Meta.parse("q(u,v) = $eq3"))
とすれば,関数 q(u,v)
を定義したことになる.
ちなみに,関数に変換するたびに毎回 eval(Meta.parse...
と書くのは大変だという人は,using SymPy
の直後にいつも
1toFunc(str) = eval(Meta.parse(str))
という感じの定義をつけておけば良い.
こうしておけば,上の p4(t)
を定義するにも
1toFunc("p4(t) = $eq2")
と書けばよい.少しだけ楽ができるし,構文を覚えておかなくても良いね.
さて,変換した結果は通常の関数だから,たとえば p4
を使って t = 3 の時のこの4次多項式の値は
1p4(3)
160
と普通に計算できる.
これが普通の関数であることは,次のように通常の関数と比較することでも確認できる. まず,この4次多項式を普通の関数で作る.
1p5(x) = x^4 + 3x^3 + x^2 -3x -2
そして,同じ入力値に対してのそれぞれの動作を @code_lowered
マクロで比較してみれば良い.
1@code_lowered p4(3)
CodeInfo(
1 ─ %1 = Main.:^
│ %2 = Core.apply_type(Base.Val, 4)
│ %3 = (%2)()
│ %4 = Base.literal_pow(%1, t, %3)
│ %5 = Main.:^
│ %6 = Core.apply_type(Base.Val, 3)
│ %7 = (%6)()
│ %8 = Base.literal_pow(%5, t, %7)
│ %9 = 3 * %8
│ %10 = Main.:^
│ %11 = Core.apply_type(Base.Val, 2)
│ %12 = (%11)()
│ %13 = Base.literal_pow(%10, t, %12)
│ %14 = %4 + %9 + %13
│ %15 = 3 * t
│ %16 = %14 - %15
│ %17 = %16 - 2
└── return %17
)
1@code_lowered p5(3)
CodeInfo(
1 ─ %1 = Main.:^
│ %2 = Core.apply_type(Base.Val, 4)
│ %3 = (%2)()
│ %4 = Base.literal_pow(%1, x, %3)
│ %5 = Main.:^
│ %6 = Core.apply_type(Base.Val, 3)
│ %7 = (%6)()
│ %8 = Base.literal_pow(%5, x, %7)
│ %9 = 3 * %8
│ %10 = Main.:^
│ %11 = Core.apply_type(Base.Val, 2)
│ %12 = (%11)()
│ %13 = Base.literal_pow(%10, x, %12)
│ %14 = %4 + %9 + %13
│ %15 = 3 * x
│ %16 = %14 - %15
│ %17 = %16 - 2
└── return %17
)
この2つの結果を比べると,入力変数名が異なるほかは全く同じであることがわかるだろう.安心だね.
Plot
SymPy の対象である数式は,実はある程度そのまま plot できる.
文法は少しだけいつもと異なるので, SymPy package Plotting を眺めておくと良い. この資料に載っている以上に細かい plot をしたければ,上に書いてあるように,いったん普通の関数に変換してから plot すると良いだろう.
レポート
下記要領でレポートを出してみよう.
- e-mail にて,
- 題名を 2023-numerical-analysis-report-15 として,
- 教官宛(アドレスは web の "TOP" を見よう)に,
- 自分の学籍番号と名前を必ず書き込んで,
- 内容はテキストでも良いし,pdf などの電子ファイルを添付しても良いので,
下記の内容を実行して,結果や解析,感想等をレポートとして提出しよう.
注意
近年はセキュリティ上の懸念から,実行形式のプログラムなどをメールに添付するとそのメールそのものの受信を受信側サーバが拒絶したりする.
そういうことを避けるため,レポートをメールで提出するときは添付ファイルにそういった懸念のあるファイルが無いようにしよう.
まあ要するに,レポートは pdf ファイルにして,それをメールに添付して送るのが良い ということだと思っておこう.
- この授業のこれまでで手計算が必要だった箇所を探し出し,2例ほど上の SymPy を使う形で数式処理してみよう.
そして,その結果と自分の手計算の結果を比較しよう.
- 自分の持っている微分積分学,線形代数学の教科書を開き,手計算でこれまで計算してきた計算を 2例ほど上の SymPy を使う形で数式処理してみよう.
そして,その結果と自分の手計算の結果を比較しよう.