読者です 読者をやめる 読者になる 読者になる

歩いたら休め

If the implementation is easy to explain, it may be a good idea.

【ABM】鳥海先生のメタ規範ゲームの話を聞いて、実装しようとしてみた(いくつかわからない点があるけど)

Python numpy 社会シミュレーション

@zaoriku0さんから鳥海不二夫先生がされていた発表の話を聞きました。SNSが流行るかどうかの必要条件を、ゲーム理論を使って説明できないか、という趣旨の研究のようです。

社会シミュレーションの先駆者のAxelrodの論文が基になってるようです。

language-and-engineering.hatenablog.jp

作りかけですが、詳しいこと(S_tの算出方法とか)はAxelrodの論文読まないと分からないので、とりあえず作りかけのやつ載せときます(数値検証もできないですし)。動くかどうかも怪しいかも…。

11/5追記:ちょっとプログラム修正しました

import numpy as np
from numpy import random as rnd
from matplotlib import pyplot as plt

# エージェントの数
N = 20
# シミュレーションの試行回数
T = 10000
# 利得を表したリストの初期値は全部0
benefit_list = np.zeros(N)

class Agent(object):
    # グローバル変数として利得を更新する(絶対良くない実装だけど)
    # global benefit_list
    
    T = 3
    H = -1
    E = -9
    P = -2
    E_d = -9
    P_d = -2
    
    # 協調率B_i
    B_i = rnd.uniform(0,1)
    # 裏切り者ユーザーへの懲罰率V_i
    V_j = rnd.uniform(0,1)
    # 裏切り者を見逃したユーザーへの懲罰率V_k
    V_k = rnd.uniform(0,1)
    
    def __init__(self):
        # 初期値では、協力(1)か裏切り(0)を1/2で選ぶ
        self.strategy = rnd.binomial(n=1, p=0.5)
    
    # エージェントの番号をセットする
    def set_agent_number(self, i):
        self.num = i
    
    # 裏切るか協力するか決断する
    def make_strategy(self):
        # 協力する
        if rnd.uniform(0,1) < self.B_i:
            self.strategy = 1
            #自分と他者に0のbenefitを与えるだけなのでコメントアウト
            #self._get_benefit(0)
            #self._give_benefit_to_others(0)
        # 裏切る
        else:
            self.strategy = 0
            self._get_benefit(self.T)
            self._give_benefit_to_others(self.H)
        
    # 裏切り者ユーザーへの懲罰
    def punish_discooperative_user(self, i):
        # 協力する(懲罰する)
        if rnd.uniform(0,1) < self.V_j:
            self.strategy = 1
            self._get_benefit(self.P)
            self._give_benefit_to_discooperator(self.E, i)
        # 裏切る(懲罰しない、見逃す)
        else:
            self.strategy = 0
            #自分と他者に0のbenefitを与えるだけなのでコメントアウト
            #self._get_benefit(0)
            #self._give_benefit_to_others(0)
    
    # 裏切り者を見逃したユーザーへ懲罰を与える
    def punish_overlook_user(self, j):
        # 協力する(懲罰する)
        if rnd.uniform(0,1) < self.V_k:
            self.strategy = 1
            self._get_benefit(self.P_d)
            self._give_benefit_to_discooperator(self.E_d, j)
        # 裏切る(懲罰しない、見逃す)
        else:
            self.strategy = 0
            #自分と他者に0のbenefitを与えるだけなのでコメントアウト
            #self._get_benefit(0)
            #self._give_benefit_to_others(0)

    # 周囲を真似て進化する(本当は遺伝的アルゴリズムを使ってるらしいんですが、とりあえず)
    def evolution(self, B_i, V_j):
        if rnd.uniform(0,1) < 0.01:
            self.B_i = rnd.uniform(0,1)
            self.V_j = rnd.uniform(0,1)
        else:
            self.B_i = B_i
            self.V_j = V_j
    
    # 利得を得る(またはコストを払う)
    def _get_benefit(self, his_benefit):
        global benefit_list
        benefit_list[self.num] += his_benefit
    
    # 自分以外の全員のエージェントに利得を与える
    def _give_benefit_to_others(self, others_benefit):
        global benefit_list
        benefit_list[self.num] -= others_benefit #自分からは一旦引き算する
        benefit_list += others_benefit
        
    # 非協力者を攻撃する
    def _give_benefit_to_discooperator(self, benefit, discooperator_num):
        global benefit_list
        benefit_list[discooperator_num] += benefit

# 一回の試行の処理
def one_game_trial(agents):
    agentnums = list(range(N))
    rnd.shuffle(agentnums)

    # 1人目が協力するか裏切るかを決める
    i = agentnums[0]
    agents[i].make_strategy()
    # もし協力者なら何もせずにこの試行を終了
    if agents[i].strategy == 1:
        return None

    # 2人目が裏切り者を罰するかを決める
    j = agentnums[1]
    agents[j].punish_discooperative_user(i)
    # もし協力者なら何もせずにこの試行を終了
    if agents[j].strategy == 1:
        return None
    
    # 3人目が裏切り者を見逃した人を罰するかを決める
    k = agentnums[2]
    agents[k].punish_overlook_user(j)

# エージェントを一人を選んで、進化させる
def evolution_turn(agents):
    # 利得が最大のエージェントを見つける(注:np.argmaxはインデックスの小さい値を優先する)
    maxindex = np.argmax(benefit_list)
    a = agents[maxindex]

    # 進化させる
    i = rnd.randint(N)
    agents[i].evolution(a.B_i, a.V_j)
    
# メイン処理
def main():
    # エージェントのインスタンスの入ったリストを作る
    agents = [Agent() for x in range(N)]
    for i, a in enumerate(agents):
        a.set_agent_number(i) 

    # 試行回数の分だけ処理を実行する
    tmp = []
    for t in range(T):
        one_game_trial(agents)
        # 一回の試行につき一人を進化させる実装でいいのかな…?
        evolution_turn(agents)
        tmp.append(np.sum([a.strategy for a in agents]))
    
    return tmp

# プログラム実行時の処理
if __name__ == '__main__':
    plt.plot(main())
    plt.show()

グラフが出てきます。縦軸が協調の戦略をとっている人数、横軸がステップ数です。 f:id:takeshi0406:20151105071502p:plain

グラフの形が全く違うので、プログラムのどこかに間違いが潜んでいます。

前のプログラム。グローバル変数の扱いとかいろいろ間違ってました。

import numpy as np
from numpy import random as rnd

class Agent(object):
    # グローバル変数として利得を更新する(絶対良くない実装だけど)
    global benefit_list
    
    T = 3
    H = -1
    E = -9
    P = -2
    E_d = -9
    P_d = -2
    
    # この辺の数字は適当です
    # 協調率B_i
    B_i = 0.5
    # 裏切り者ユーザーへの懲罰率V_i
    V_j = 0.1
    # 裏切り者を見逃したユーザーへの懲罰率V_k
    V_k = 0.2
    # 発見率
    S_t = 0.5
    
    def __init__(self):
        # 初期値では、協力(1)か裏切り(0)を1/2で選ぶ
        self.strategy = rnd.binomial(n=1, p=0.5)
    
    # エージェントの番号をセットする
    def set_agent_number(self, i):
        self.num = i
    
    # 裏切るか協力するか決断する
    def make_strategy(self):
        # 裏切る
        if S_t < B_i:
            self.strategy = 0
            self._get_benefit(T)
            self._give_benefit_to_others(H)
        # 協力する
        else:
            self.strategy = 1
            #自分と他者に0のbenefitを与えるだけなのでコメントアウト
            #self._get_benefit(0)
            #self._give_benefit_to_others(0)
        
    # 裏切り者ユーザーへの懲罰
    def punish_discooperative_user(self, i):
        # 裏切る(懲罰しない、見逃す)
        if S_t < V_j: #TODO:: S_tの計算方法がわからない(周囲にいる協力者の割合?)
            self.strategy = 0
            #自分と他者に0のbenefitを与えるだけなのでコメントアウト
            #self._get_benefit(0)
            #self._give_benefit_to_others(0)
        # 協力する(懲罰する)
        else:
            self.strategy = 1
            self._get_benefit(P)
            self._give_benefit_to_discooperator(E, i)
    
    # 裏切り者を見逃したユーザーへ懲罰を与える
    def punish_overlook_user(self, j):
        if S_t < V_k:
            self.strategy = 0
            #自分と他者に0のbenefitを与えるだけなのでコメントアウト
            #self._get_benefit(0)
            #self._give_benefit_to_others(0)
        else:
            self.strategy = 1
            self._get_benefit(P_d)
            self._give_benefit_to_discooperator(E_d, j)
    
    # 利得を得る(またはコストを払う)
    def _get_benefit(self, his_benefit):
        benefit_list[self.num] += his_benefit
    
    # 自分以外の全員のエージェントに利得を与える
    def _give_benefit_to_others(self, others_benefit):
        benefit_list[self.num] -= others_benefit #自分からは一旦引き算する
        benefit_list += others_benefit
        
    # 非協力者を攻撃する
    def _give_benefit_to_discooperator(self, benefit, discooperator_num):
        benefit_list[discooperator_num] += benefit

# 一回の試行の処理
def one_trial():
    agentnums = list(range(N))
    rnd.shuffle(agentnums)

    # 1人目が協力するか裏切るかを決める
    i = agentnums[0]
    agents[i].make_strategy()
    # もし協力者なら何もせずにこの試行を終了
    if agent[i].strategy == 1:
        return

    # 2人目が裏切り者を罰するかを決める
    j = agentnums[1]
    agent[j].punish_discooperative_user(i)
    # もし協力者なら何もせずにこの試行を終了
    if agent[j].strategy == 1:
        return
    
    # 3人目が裏切り者を見逃した人を罰するかを決める
    k = agentnums[2]
    agent[k].punish_overlook_user(j)

# メイン処理
def main():
    # エージェントの数
    N = 10
    # シミュレーションの試行回数
    T = 1000
    
    # 利得を表したリストの初期値は全部0
    benefit_list = np.zeros(N)
    
    # エージェントのインスタンスの入ったリストを作る
    agents = [Agent() for x in range(N)]
    for i, a in enumerate(agents):
        a.set_agent_number(i) 

    # 試行回数の分だけ処理を実行する
    for t in range(T):
        one_trial()

# プログラム実行時の処理
if __name__ == '__main__':
    main()