4. State & Active State

Note

この章であつかうstateactive stateは、エージェントの状態を表現するためのフィールド・カテゴリーです。ABMを自作で作る場合には、通常この二つをはじめに作成します。この章では、Stateについて概観したうえで、特にactive stateについて説明します。active stateはうまく使用すれば、ABMを作成するうえで非常に強力な手法となります。

4.1 State

stateフィールドは、State()でくくることによって定義します。stateフィールドには、関数以外の任意のRオブジェクトを取れます。以下の例は、エージェントの伸長と体重を表現するstateを作成するものです。

library(rABM)
# 10人のエージェント
n <- 10

# 身長(m)と体重(kg)のオブジェクトを作成
height <- rnorm(n = n, mean = 170, sd = 10)/100
weight <- rnorm(n = n, mean = 60, sd = 10)

# Gameに格納する
G <- Game(State(height), State(weight))

ここで、エージェントの数は同じなのだから、heightweightというように、それぞれ別のオブジェクトを作らず、data.frameにまとめればよいのではないかという疑問も出てくるのではないかと思います。実際、stateフィールドにはdata.frameも使用できます。判断はユーザーに任されますが、私自身の考えでは、ABMを回すには、stateは可能な限りvectorで表現されるほうが、他の関数での扱いが容易になります。というのは、たとえば1番目のエージェントの体重にアクセスする場合、ベクトルであればself$weight[1]で直接アクセスできますが、data.frameの場合には、データフレームのさらに対象とする列を指定する必要が出てくるからです(e.g. self$df$weight[1])。このようにアクセスが一段深くなってしまうことは、スクリプトを書くうえでも厄介になり得ます。このことは、次のactive stateについて理解すると、実感がわくでしょう。

4.2 Active State

(1) Active Stateとはなにか

Active stateは、外部からはstateのようにアクセスできますが、定義上は関数を持っています。つまり参照されるたびに、内部で関数が走りその結果が返される仕組みです。

具体例として、Body Mass Index (BMI)について考えてみましょう。BMIは、体重と身長から計算される肥満度を示す指数です。BMIの式は以下の通りです。

\[ BMI = \frac{weight (kg)}{height (m)^2} \]

つまりBMIは身長と体重さえわかれば、自動的に計算が可能な指数です。BMIをactive stateとして実装してみましょう。

# BMIを関数で表現する
bmi <- function(){
  self$weight/self$height^2
}

# bmiをactive stateとして追加する
add_field(G, Active(bmi))

# BMIを見てみる
G$bmi
 [1] 26.50276 21.65032 26.46634 23.13305 24.40561 20.95027 22.73042 17.28472
 [9] 17.09904 11.59473

このようにBMIを関数として表現してあることによって、もしもエージェントの伸長や体重が変化した場合に、それに連動してBMIも変動するという実装が可能になります。つまり、たとえばシミュレーションの更新する対象のフィールドは、身長と体重に絞ればよく、BMIまで更新する必要がなくなるということになります。

(2) Active stateの実行速度に関して

Active stateは非常に便利ですが、特に計算速度を重視する場合には、その仕組みをきちんと理解しておく必要があります。つまりactive stateは参照されるたび(例:G$bmi)新たに計算がしなおされます。もしも実行が非常に重い関数を設定し、かつactive stateを何度も呼ぶと計算負荷がそれだけ大きくなります。つまり更新をする対象の値がごく一部であるならば、毎回計算をし直すよりも、更新する対象の値を直接変化させてしまったほうが実行速度が速くなります。

他方でactive stateそのものはただの関数オブジェクトなので、容量そおのもはさほど大きくありません。大規模だが、ごく単純で実行速度が速いオブジェクトを表現するうえでは、大規模なstateオブジェクトを持つよりも、active stateの方が速くなる場合もあります。

なお、run_Gameで実行された各回のactive stateフィールドは、logには際には、関数ではなく値として保存されます。つまり、その時点でのactive fieldの値が「固められた」状態で保存されることも覚えておいてください。

4.3 フィールドの追加と削除

ABMを作成する際には、Gameオブジェクトに新たにオブジェクトを追加したり、削除したりする操作をよく行います。すでに追加に関しては例として出してありますが、関連する操作について以下改めて例とともにまとめます。

(1) フィールドの追加

x <- 1
y <- 2
z <- 3

# xを追加する
add_field(G, State(x))

# yとzをまとめて追加する
add_field(G, State(y), State(z))

なおすでに存在するフィールドと同じ名前のフィールドを追加することはできません。その場合には、次の例のようにいったんすでに存在しているフィールドを削除してから、新しいフィールドを追加しなおしてください。

(2) フィールドの削除

# xを削除する
remove_field(G, "x")

# yとzをまとめて削除する
remove_field(G, "y", "z")