何をやった?
scipy.optimize.minimize
には、とても微妙な仕様? バグ? がありました。
バグに近いと思ったのですが、直すと既に使ってる人が困りそうな内容だったので、Issueを立てて「もし修正するならプルリク出すけどどう?」って聞いてみました。
プルリク出して、ということだったので、出しました。
つい最近、ようやくマージされました。
scipy.optimize.minimize
とは?
何か関数を与えたら、パラメータをいい感じに最適化し、関数が返す値を最小化してくれます。
例えば、$z = 2x^2 + (3y - 2)^2 + xy$という式があったとしましょう。
$z$を最も小さくする$x, y$はなんですか? というのを解いてくれます。
パッと見て分かるのは$x = 0, y = 2/3$のときゼロになるなー、それより小さいのはなるのかなー、程度でしょう。
def f(x, y):
return 2 * x**2 + (3*y - 2)**2 + x*y
print(scipy.optimize.minimize(f, [0., 0.]))
と、関数と適当な初期値([0., 0.]
)を与えるといい感じの値を求めてくれます。
array([-0.16901447, 0.67605677])のとき、 -0.05633802816721355になるらしいです。
何が問題だったのか
scipy.optimize.minimize
には、最小化のためのアルゴリズムは複数用意されていて、method="..."
の引数を与えることで選ぶことができます。
ところで、引数がひとつだけのとき、かつ、"Powell"法を使ったときだけ、少しだけ嫌なことが起こります。
import numpy as np
import scipy.optimize
def fun(x):
return x*x
res = scipy.optimize.minimize(fun, [0.1], method="Nelder-Mead")
print("Nelder-Mead:")
print("Optimized:", res.x, "Shape:", res.x.shape)
res = scipy.optimize.minimize(fun, [0.1], method="COBYLA")
print("COBYLA:")
print("Optimized:", res.x, "Shape:", res.x.shape)
res = scipy.optimize.minimize(fun, [0.1], method="Powell")
print("Powell:")
print("Optimized:", res.x, "Shape:", res.x.shape)
Nelder-Mead:
Optimized: [1.94289029e-16] Shape: (1,)
COBYLA:
Optimized: [0.00010234] Shape: (1,)
Powell:
Optimized: 4.163336342344337e-17 Shape: ()
お分かりいただけただろうか。
返り値のshape
が違うのである。
実はこれ以外にも、ヤコビアンを指定しなければいけないもの、ヘシアンを指定しなければいけないものなども含め、全14種類試したんですが、"Powell"法だけが、このようになっていました。
まぁ、バグっちゃバグなんですが。歴史あるプロジェクトです。今更直すべきかどうか、というのは判断に迷うところかもしれません。
既に、Powellはこうだと信じて使っている人に、影響出ちゃうかもしれないですし。
けど、まぁ、せっかくなので聞いてみることにしました。
やったこと時系列
(2019年1月)
ということで、とりあえずフォークして、自分のリポジトリで直してみました。直せると分かったのでIssueを立てて、直したリンクも貼って「もしこれでよければPR出す」と言ってみました。
https://github.com/scipy/scipy/issues/9715
(2019年8月)
そしたら「バグだから修正した方がいいが、互換性の問題から、修正を入れる関数を変えてほしい」とのことだったので、そのようにしてPRを出しました。
https://github.com/scipy/scipy/pull/10640
(2019年9月)
変更内容はいいけど、付け加えたテストがやや冗長だから直してくれ、とコメントもらったので直して、などとして、
しばらく返事がないまま放置されていました。
(2019年12月)
masterにマージされました。
約1年かかったことになります。開発者も忙しいのと、内容が大したことないので優先度が低かったのもあると思います。
学び・感想
- Scipyにあった形式でIssueを立てたり、コミットメッセージを書いたり、PRを出したりする必要があったので「そもそもどういう風にすることになっているんだ」を調べたり、その形式に合わせたりするのが結構しんどかったです
- 普段からちゃんとコミットメッセージ書いてないのが祟ったのかもしれません
- 時間はかかりましたが、開発者たちもちゃんと対応してくれて優しかったです
- 小さなことであっても、scipyという私もみんなも利用している有名ライブラリの改善に貢献できたので、やってよかったです
- 怖がらずに、ベストを尽くして人に聞く姿勢が大切だなぁ、と改めて思いました
オープンソースは、ただ無料で使えるだけでなく、こういうチャンスにも溢れています。
すごくつまらないことでも「あれ?」と思ったら貢献チャンスかもしれません。
今後も、できそうなことがあればオープンソースに貢献したいです。