【渋滞学】セル・オートマトンの実装
頭痛のひどいRockinWoolです。しかし、時期的にはかなり忙しいのでプログラミングもはかどっています。最近の趣味は公園で散歩をしながら肩をほぐすことですね。さて、ちょっと前に渋滞学という本を読んだのですが、その中にあったセル・オートマトンの実装をずっとやってみたいなと思いながらやっていなかったのでやりました。今回はpybind11を使ってセル・オートマトン部分をC++で作り、作図部分やデータ整理部分をpythonに任せる形にしています。実装はGithubにこっそり上げて置きます。 プログラムの構成 まず、メインプログラムは実験を行ってデータをcsvファイルに出力するmain.pyとcsvを元に作図をするdraw.pyの2つから構成することにしました。このようにすれば、main.pyはpybind11を使って連携するがdraw.pyはピュアpythonの実装で良くなるので便利でした。ざっとフォルダ階層を書くと下のような感じです。 CA.cppはCA.hppで定義された関数をpythonから使用できるようにしていて、細かい実装はcaHistory.cppに書いてあります。余談ですが、CAはCellAutomatonの略、historyとつけているのはセル・オートマトンでどの位置にエージェントがいたかどうかを返すことを明確にしたかったからです。 main.pyの概要 まずは基本となるmain.pyの解説から。難しい処理はC++に任せてしまっているので中身は超単純です。1~100個のエージェントを召喚してセル・オートマトンをして、その情報を保存することを2回繰り返しています。1回目は1個から始めて100個まで増やして混雑具合を見ています。2回目はその逆で100個から減らしていってます。cl.caHistory()でセル・オートマトンの実験をしていますが、その時に計算される混雑の回数congestionはゲッターであるget_congestion()を実装して入手する形になっています。これはpybind11の仕様でメンバ変数を直接参照できないことと、メンバ変数は基本的にprivateにしてゲッターを使ってアクセスするというベストプラクティスに沿った実装をしていることの2点があります。 それでは、このmain.pyで使用されるcaHistory(), get_congestion(), add_agent(), remove_agent()を実装していきましょう。 CA.cppの実装 こちらも中身は単純で、pybindのincludeを行う部分と関数をpython側に共有させる部分の2つから構成されています。書き方が少し独特ですが4つの関数が公開されるのがわかります。 CA.hppの内容 先程の4つの関数のプロトタイプ宣言を行っている部分です。ちなみに前回の記事で書きましたとおり、コンストラクタも作らないといけないのでpublicには5つの関数が登録されています。またprivateにはそれらの関数が使う情報を登録しています。update()関数はセル・オートマトンの具体的動作を規定していて、前のセルにエージェントがいたら止まるなどの挙動を定義しています。これをcaHistory()ではint回呼び出してシミュレートするわけですね。 caHistory.cppの実装 記事のほとんどがこのプログラムの内容で埋まってしまっていますが、実際はかなり単純なプログラムです。ほとんどの分量はupdate()関数の条件文で埋まっているのでcaHistory(), add_agent(), remove_agent(), get_congestion()の本文は2~5行程度になっています。それぞれ与えられた役割が動作するようになっています。 draw.pyによる描画結果 draw.pyの実装は大した内容では無いので割愛します。main.pyを実行して得られたcsvファイルをベースにdraw.pyが書いた図が下記です。 まとめ 今回はpybind11を使ってセル・オートマトンを実装してみました。内容が少しヘビーになってしまったかもしれませんが、興味のある方はコードの改善点などドシドシ送ってください。それでは。