7. run_Game

Note

作成したGameオブジェクトを実際に回すのがrun_Game関数です。この関数には、シミュレーションを回すためのさまざまな機能が備わっています。この章では、はじめにrun_Gameを使用する上でのごく基礎的な方法について紹介したうえで、実際のシミュレーションに生かせるさまざまな機能について紹介します。

7.1 run_Gameの基礎とその仕組み

(1) run_Gameの回し方

# ライブラリを読み込む
library(rABM)

run_ABMの基本的な使い方は、あらかじめ作成しておいたGameオブジェクトを第一引数にし、plan引数に更新に使用するact_FUNのフィールド名を更新したい順番で並べます。times引数には、更新を行う回数を指定します。デフォルトは1回分です。前章でも用いたそれぞれのエージェントが所持金を増やしていくゲームの例で、この動作を確認してみましょう。

# Gフィールドを作成する
money <- 1:5
selected_agent <- 1:5
add_money <- function(b = 1){self$money[self$selected_agent] <- self$money[self$selected_agent] + b}
select_agent <- function(){self$selected_agent <- sample(1:5, size = 2)}
G <- Game(State(money), State(selected_agent), Act(add_money), Act(select_agent))

まずは、(1)ランダムにエージェントを選び、(2)選ばれたエージェントが所持金を増やすというシミュレーションを3回回してみたいと思います(結果省略)。

G2 <- run_Game(G = G, plan = c("select_agent", "add_money"), times = 3)

なお今回の例では、初期設定でselected_agentにすべてのエージェントを選んでいるため、select_agentをかけなければ、すべてのエージェントの所持金が増えることになります。

G3 <- run_Game(G = G, plan = "add_money", times = 3)

また、前章で見たようにact_FUNの引数を変えることで、増えるお金の量を変えることもできます。

G4 <- run_Game(G = G, plan = c("select_agent", "add_money(b = 2)"), times = 3)

このように(1)使用するact_FUN、(2)act_FUNの順序、(3)act_FUNの引数をrun_ABMの時点でさまざまに変えることができます。これにより、新たに一からGameオブジェクトを作りなおすことなく、さまざまなシナリオを検討することができます。

(2) run_Gameの仕組み

ここで、run_Gameの仕組みの内部について簡単に解説します。これを掴むことで、なぜ前述のような動作が可能になるのかを理解できるはずです。

run_Gameの内部では次のような処理を順番に行っています。

  1. Gをdeep copyし新たにG'を内部に生成する。これにより投入されたGameオブジェクトは変化させないようにする。
  2. planにおいてもしもしてされたact_FUNの名前に(引数)が付いていれば、その部分を当該のact_FUNに反映させるよう引数を書き換える。
  3. planに書かれている順序に基づいて、更新関数update_FUNを内部で生成する
  4. 引数nm_stop_FUNが存在する場合には当該のstop_FUNを使用し、指定がない場合にはtimesに指定された回数をもとに内部でstop_FUNを生成する
  5. update_FUNstop_FUNの条件が満たされるまで繰り返し実行する。

このように、run_ABMではユーザーの指定に基づいて、その都度複数の関数を加工・生成することで柔軟な実行を可能にしています。なお、生成されたupdate_FUNは、run_Gameの引数return_update_FUNTRUEにすることで、取得することができます。

7.2 速度を上げるための工夫

前章ではact_FUNの設計を工夫することで実行速度をあげられることを述べましたが、run_ABMにおいても下記の設定を変えることで速度を上げることができます。

(1)verbose引数をFALSEにする

run_Gameを回すと、コンソールにはさまざまな中間メッセージが出てきます。実はこのメッセージの生成そのものは実行を遅くする原因にもなります。verboseFALSEに変更することで、速度を若干上げることができます。

(2) save_log引数をFALSEにする

デフォルトでは、各回の一連の更新が終わるたびに、すべてのStateおよびActive Stateフィールドは$logに保存されます。しかしもしもこのような中間生成物は分析上不要であり、最終時点での実行結果のみを利用するならば、save_logFALSEにすることで劇的に更新速度を上げることができます。

(3) 保存対象を絞る

もっとも、すべてではないが一部のlogは分析上利用したいという場合もあるでしょう。この場合には、保存対象のフィールド名をfields_to_save引数に文字列で指定することで、保存対象を絞ることができます。特に多くのActive Stateが設定されている場合、各回ごとに多くの計算が発生します。保存対象を絞ることで余分なactive stateを呼び出さないことにつながります。

(4) 保存のインターバルを指定する

(3)と組み合わせて、特に長いシミュレーションを行う場合にできる工夫として、すべての時点のlogを取得するのではなく、一定の間隔でのみ保存をすることもできます。すなわち、save_interval引数に1以上の任意の自然数を指定することです。

7.3 その他のさまざまな機能

run_ABMにはこのほかにもさまざまな機能が組み込まれています。以下では特にユーザーにとって有用と思われる機能を紹介します。

(1) seed値

act_FUNがなんらかのランダム性を持つ計算を伴う場合、run_Gameのたびに実行結果が変わります。もしもなんらかの実行結果を再現したい場合には、seedに正の整数値を設定することで、原則的に同じ実行結果を得ることができます。ただしこの機能は、set.seed()seed値を渡すだけであり、この操作で制御できないランダム性を持つような関数の場合には望むような再現ができない可能性があることに注意してください。なおデフォルトでは、実行時点から任意の整数値を生成しています。ランダム生成されたseed値は、アウトプットのGameオブジェクトの$notes$seedで確認できます。

(2) beep

beep引数をTRUEにすることで、シミュレーションが終わった時点で、ビープ音を鳴らすことができます。長いシミュレーションをさせている間に、コーヒーを飲んで休憩したい時などに便利です。

(3) 中間保存

特に長いシミュレーションを行う際に、コンピューターが途中でダウンしてしまった場合それまでの計算がすべて無駄になるという悲劇が起こります。このような事態を避ける保険として、その時点での最新版のG'オブジェクトをRDSファイルとして保存しておくことができます。この機能を利用したい場合には、saveRDS_inbetween引数をTRUEにしてください。なお保存するRDSファイル名はRDS_file_name引数で変更することができます。なお注意点として、RDSファイルの保存そのものにも時間がかかるため、この機能を用いる場合には、実行速度も遅くなるというトレードオフがあります。

なお現状の版では実装されていませんが、近日中に以下の機能についても追加される予定です。

  • 複数のシミュレーションを実行する機能

  • 並列計算をする機能

  • logファイルをいったん外部退避し最後に結合する機能