1. 人のネットワーク上の情報伝搬モデルを作成する¶
このチュートリアルではネットワークのモデルを作成します。
ネットワークとは、ものごとの関係性を示したシステムのことを指します。ある場所で一緒になった他人が実は知り合いの知り合いだった…という経験は誰にでもあるでしょう。世間は一見巨大かつ複雑に見えて、案外狭いものです。こうした人同士の繋がりを分析するのが、社会的なネットワーク分析です。現在ではインターネット、食物連鎖、さらには感染症の流行といった様々な現象への分析が適用されています。
他方で、人以外の繋がりを幾何学的に分析するのが、理工的なネットワーク分析です。ネットワーク分析の基礎にはグラフ理論があり、研究の歴史はこちらの方が長いといえます。現在は脳科学や物理学、都市の交通網などに適用されています。
ネットワークについては、MASコミュニティのこちらのページも参考にしてください。
本節では、人同士の繋がりを想定したネットワーク上における情報伝搬モデルを作成し、社会的ネットワークの性質の一端に触れます。次節では実際の都市空間への適用を想定した、道路ネットワーク上での移動モデルを作成します。
それでは始めましょう。
1.1. エージェントのネットワークを定義する¶
1.1.1. ネットワークの考え方¶
たとえば、下図のようなネットワークを考えましょう。数字の入った丸が人を、線が人同士の繋がりを表しています。たとえば0さんと1さんは線で繋がっているので、2人は知り合いであるという想定です。
ちなみにネットワーク科学の用語では、この図における人をノード、繋がりをリンク(またはエッジ)と呼ぶことが多いです。この例ではノードは人ですが、たとえば道路ネットワークではノードは交差点になります。
artisocでこれを表現するには、ノードをエージェント、リンクをエージェントの持つエージェント集合型変数として定義します。下図のように、たとえばエージェント2が持つエージェント集合型変数には、エージェント0, 3, 4が格納されています。こうすることで、エージェント2がエージェント0, 3, 4と繋がっていることが表現できます。
1.1.2. ネットワークの作成¶
それではモデルを作成していきましょう。まずは下準備として、下記の手順で作成していきます。
「新規モデルの作成」から新しくモデルの画面を開き、モデルには「情報伝搬モデル」と名前を付けます。
次にルール画面を表示し、モデルツリーのUniverseの下に空間を追加します。空間名は「city」、他はデフォルトのままにしておきましょう。
cityの下には「person」という名前でエージェントを追加します。エージェントには「color」と「link」の変数を追加してください。後ほど、繋がっているエージェントをこの「link」に格納します。
ユニバース変数「num」を作成します。
次に出力設定をします。マップ出力を追加し、空間には「まち」という名前でcityを選択します。マップ要素リストには、「人」という名前でpersonを選択します。エージェントの表示色は変数指定の「color」です。
また、コントロールパネルに「人数」という名前でnumを表示させましょう。種類はスライドバー、値の型は整数、範囲は1~50、初期値は20にします。
ここから、ネットワークを作成するためのルールを書いていきます。見やすくするため、人を円周上に均等配置しましょう。
def univ_init(self):
# マップの中心の座標の取得
cx = get_width_space(Universe.city) / 2
cy = get_height_space(Universe.city) / 2
# 円周の半径の定義
r = 0.4 * get_width_space(Universe.city)
# Universe.numの数だけ、personを生成し、円周上に均等配置する
for i in range(Universe.num):
p = create_agt(Universe.city.person)
theta = radians(360 * i / Universe.num)
p.x = cx + r * cos(theta)
p.y = cy + r * sin(theta)
円周のサイズはマップサイズより少し小さく(ここでは半径がマップの0.4倍)します。ここでget_width_space(Universe.city)
はcityの横幅を、get_height_space(Universe.city)
はcityの縦幅を取得することができます。
次にpersonのlink属性を初期化します。
def agt_init(self):
self.link = set()
link属性には元のエージェントと繋がりのある他のエージェントを格納します。従って、linkはエージェント集合型である必要があります。ここではself.link = set()
とすることで、linkが集合型変数であることを宣言しています。(何もしないとき、変数の初期値は整数の0なので注意しましょう)
最後に、for文を用いて各エージェントのlinkに繋がりのある人をランダムに追加します。ここで、全ての他人と繋がっては面白くないので、Universe変数link_rateを作りましょう。コントロールパネルには「リンク確率」という名前で表示させます。種類はスライドバー、値の型は実数、範囲は0~1、初期値は0.1、目盛り間隔は0.01にします。univ_initに次のようなルールを書き足すと、他のエージェントと繋がる確率をlink_rateで統制することができます。
def univ_init(self):
cx = get_width_space(Universe.city) / 2
cy = get_height_space(Universe.city) / 2
r = 0.4 * get_width_space(Universe.city)
for i in range(Universe.num):
p = create_agt(Universe.city.person)
theta = radians(360 * i / Universe.num)
p.x = cx + r * cos(theta)
p.y = cy + r * sin(theta)
# ここから追記
people = make_agtset()
for p1 in people:
for p2 in people:
if rand() < Universe.link_rate:
p1.link.add(p2)
p2.link.add(p1)
追記部分のルールを説明します。
まず、people
という名前で全てのpersonを集めたエージェント集合を作ります。
people
の中からp1
という人を順に選択します。
さらにp1
は、自分とリンクを作成する可能性のある人、p2
を順に選択します。
Universe.link_rate
の確率を満たした場合のみ、p1
とp2
はお互いに相手を自分の属性link
に追加します。p1.link.add(p2)
とp2.link.add(p1)
がそれを行うルールです。集合型変数.add(要素) と書くことで、集合型変数に要素を追加できます。
※ルールについて補足すると、厳密には、上記のルールだと少しおかしな状況も許容していることになります。例えばp1とp2が同じエージェントである場合。自分と繋がりのあるエージェントとして自分自身を選ぶのは、少しおかしいですね。またそうでなくても、p1がp2を、p2がp1を追加した場合。ペア同士で2重にリンクを作成することになってしまいます。しかしこれから見るように、これらの状況はマップにネットワークを表示する上で特に問題を起こしません。今回は気にせず進めましょう。
最後に、マップ上にネットワークを表示する方法について説明をしていきます。
先ほど出力したマップの出力設定を開いてください。さらにマップ要素リストにある「人」の設定画面を開き、「エージェント間に線を引く」の欄を見つけます。ここで「対象の変数」に「link」を選択し、「OK」を押します。
これでネットワークの作成は完成です。モデルを保存後、実行してエージェント間にネットワークが作成されていることを確認してください。
1.2. ネットワーク上での情報の伝搬を表現する¶
1.2.1. 情報が伝わるルールを定義する¶
次に、情報の伝搬を定義します。最初に、3人が情報を知っているというルールをuniv_initに追加します。マップ上では、情報を知っている人を赤、知らない人を青と表示させます。
def univ_init(self):
cx = get_width_space(Universe.city) / 2
cy = get_height_space(Universe.city) / 2
r = 0.4 * get_width_space(Universe.city)
for i in range(Universe.num):
p = create_agt(Universe.city.person)
theta = radians(360 * i / Universe.num)
p.x = cx + r * cos(theta)
p.y = cy + r * sin(theta)
# ここから追記
if i < 3:
p.color = COLOR_RED # 最初の3人は、情報を知っている
else:
p.color = COLOR_BLUE # それ以外の人は、情報を知らない
# 追記ここまで
people = make_agtset()
for p1 in people:
for p2 in people:
if rand() < Universe.link_rate:
p1.link.add(p2)
p2.link.add(p1)
また、毎ステップ任意の確率(ここでは0.1)で自分と繋がっている人に情報を伝えるルールを、agt_stepに書きましょう。
def agt_step(self):
if self.color == COLOR_RED:
for p in self.link:
if rand() < 0.1:
p.color = COLOR_RED
モデルを実行すると、初めは青(情報を知らない人)の多かったマップが、リンク先の人に情報を教える(青から赤に変える)ことで、赤く染められていく様子が確認できると思います。(実行後すぐに赤くなる場合は、ディレイ(モデル実行速度)を200程度にすると、変化の様子がよく見られます。)
1.2.2. 情報の伝わり方を可視化する¶
前節までに基本的なモデルの構築は完了しましたが、さらに情報を知っている人の数をグラフに出力しましょう。モデルの情報を可視化することで、新たな発見や間違いに気づくことが出来ます。
モデルツリーに、新たなUniverse変数num_REDを作ります。この変数に、情報を知っている人の数(つまり赤の数)を記録させましょう。毎ステップ変数の初期化と赤の数え上げを行うため、univ_step_beginとuniv_step_endには以下のようなルールを書き込んでください。
def univ_step_begin(self):
# ステップの最初にカウントを初期化
Universe.num_RED = 0
def univ_step_end(self):
# ステップの最後に数をカウント
people = make_agtset()
for p1 in people:
if p1.color == COLOR_RED:
Universe.num_RED += 1
num_REDは、毎ステップuniv_step_beginで0に初期化する必要があります。このルールがないと、次のステップで前ステップに数え上げた赤の数を余分に加算してしまうからです。
ルールを書き込んだら、出力画面から出力設定を行います。時系列グラフを追加し、時系列グラフ要素リストにUniverse.num_REDを登録しましょう。(その他のラベル設定等は、自分の好みの名前を付けてください。)
再生ボタンを押すと、ステップごとに情報を知っている人の数が増えていく様子が折れ線グラフとして確認できます。
このように、ユニバース変数を利用して集計・グラフ表示をすることで、モデルの状態の変化を可視化することができます。様々な場面で使えるテクニックですので、覚えておきましょう。
1.3. 参考¶
この章のサンプルモデルは次の通りです。
1.4. 練習問題¶
これで、4-1のチュートリアルは終了です。最後に、より深くモデルを理解するための練習問題を用意しました。興味のある方は、取り組んでみてください。
リンク確率と伝わる速度の関係を調べる
リンク確率Universe.link_rateを変化させたときの、情報伝搬の速度を調べてみましょう。全員に伝わるまでのステップ数は、リンク確率とどのような関係にあるでしょうか。
情報伝播の確率と伝わる速度の関係を調べる
本節のモデルでは、自分と繋がっている人に情報を伝える確率を0.1としました。この確率をUniverse変数とし、スライドバーで操作できるようにしましょう。そして、この確率と情報伝播の速度の関係を調べてみましょう。
同期のルールを入れる
agt_stepで情報を伝えるルールについて、チュートリアル3で学んだ同期問題を考えてみましょう。
本節のモデルの「自分が情報を知っているとき、繋がっている人に情報を伝える」というルールは、チュートリアル3.2の森林火災モデルでの「自分が燃えているとき、隣り合う木を燃やす」というルールと対応します。したがって、チュートリアル3で扱った同期問題がここでも発生します。つまり、エージェントのルールが実行される順番によって、情報の伝わり方が変わってしまうのです。
これを防ぐため、チュートリアル3と同様に、エージェントの行動を同期させるルールを導入してみましょう。
また、エージェントの行動を同期させるべきなのはどのような場合か、考えてみましょう。