距離$d$の確率への依存性$g(d) = e^{-d}$
In [1]:
%matplotlib inline
from Tkinter import *
import matplotlib.pyplot as plt
import numpy as np
import collections
import operator
def accumulate(iterable, func=operator.add):
'Return running totals'
# accumulate([1,2,3,4,5]) --> 1 3 6 10 15
# accumulate([1,2,3,4,5], operator.mul) --> 1 2 6 24 120
it = iter(iterable)
total = next(it)
yield total
for element in it:
total = func(total, element)
yield total
class Person(object):
def __init__(self, ideas_num=10, place=(0., 0.), **kwargs):
# 意見は0~1の間の値を一様に取りうる
self.ideas = list(np.random.random(ideas_num))
# 発言者の実際の位置が2次元の座標として表せる
self.place = place
# その他の特徴量
for (k, v) in kwargs.items():
setattr(self, k, v)
def distance(self, p):
# 人pと自分との間の距離(ユークリッド距離)
d = np.sqrt((self.place[0]-p.place[0])**2 + (self.place[1]-p.place[1])**2)
return d
class meeting(object):
def __init__(self, N):
# 会議の参加人数
self.N = N
# 意見の時系列
self.ideas = []
# 発言者の時系列
self.speaker = []
# 時刻
self.k = 0
self.K = 100
# 張られたリンク(時刻, 時刻)のタプルで表現する
self.links = []
# リンクの数(各時刻)
self.l = [0]
# リンクの数(累計)
self.L = [0]
def g(self, x):
# 発言者の物理的距離に対する関数
return np.exp(-x)
def p(self, i):
# 参加者の中で話せる人のみを対象に
_N = []
for k in range(1, self.N+1):
if len(self.members[k].ideas):
_N.append(k)
# それらの人たちに対し、関数gによる重み付けの確率を付与
w = []
for n in _N:
d = self.members[n].distance(i)
w.append(self.g(d))
w = np.array(w)
sum_ = np.sum(w)
_p = list(w/sum_)
p = list(accumulate(_p))
rn = np.random.rand()
nm = 0
while True:
if p[nm] > rn:
break
else:
nm += 1
# その確率で選ばれた人の名前を返す
j = _N[nm]
return j
def q(self, j):
# 発言者jが選ばれた時、持っている意見から等確率で意見を取り出す
x_j = self.members[j]
return np.random.rand() #x_j.ideas.pop()
def distance(self, x, y):
# 意見の近さを絶対値で表現
d = np.abs(x - y)
if d == 0:
return self.radius + 1
else:
return d
def connect(self):
l = 0
for i, v in enumerate(self.ideas[:-1]):
# k番目の意見と意見が近い時、それらノードの間にリンクを形成する
if self.distance(v, self.ideas[self.k]) < self.radius:
self.links.append((i, self.k))
l += 1
return l
def check_agreement(self):
# 合意チェック 参加人数Nによる関数
def L(N):
return N**2
#if self.l[-1] > L(self.N):
if self.k > self.K:
return True
else:
return False
def check_ideas(self):
for k in range(1, self.N+1):
if len(self.members[k].ideas):
return True
return False
def f_L(self):
# リンクから会議の評価
# 単純に会議終了時に得られたリンクの数を返す
return self.L[-1]
def f_T(self):
# 会議に必要な時間の評価
# 単純に必要な時間kを返す
return self.k
def f(self):
# f_Lとf_Tを使った評価関数f
return self.f_L() - self.f_T()
def end(self):
# 会議の通常終了、各定義量の計算や受け渡しなどはここで
plt.ioff()
plt.show()
# ネットワーク図を描画
link_s = [(a, b) for a, b in zip(self.speaker[:-1], self.speaker[1:])]
counter_links = collections.Counter(link_s)
for link, lw in counter_links.items():
ix = self.members[link[0]].place[0]
iy = self.members[link[0]].place[1]
jx = self.members[link[1]].place[0]
jy = self.members[link[1]].place[1]
_x, _y = ((ix+jx)/2, (iy+jy)/2)
if link[0] == link[1]:
continue
elif link[0] < link[1]:
color = 'black'
va = 'bottom'
else:
color = 'red'
va = 'top'
plt.plot([ix, jx], [iy, jy], color=color, lw=lw*4/self.k+1)
plt.text(_x, _y, '(%d,%d)' % (link[0], link[1]),
color=color, va=va)
counter = collections.Counter(self.speaker)
for key, i in self.members.items():
x = i.place[0]
y = i.place[1]
size = counter[key] * 30
plt.scatter(x, y, s=size)
plt.text(x, y, str(key), color='green')
plt.show()
# 各時刻に追加されたリンク数のグラフ
r = self.radius
k = np.arange(self.k + 1)
y = (-r**2 + 2*r)*k
delta = np.sqrt((-r**4 + 4*r**3 - 5*r**2 + 2*r)*k)
y1 = y + delta
y2 = y - delta
plt.fill_between(k, y1, y2, facecolor='green', alpha=0.2)
plt.plot(k, self.l)
plt.plot(k, y)
plt.xlabel(r"Time: $k$")
plt.ylabel(r"A number of edges for each time: $l$")
plt.show()
# リンク数の累積グラフ
plt.plot(k, self.L)
plt.plot(k, (-self.radius**2 + 2*self.radius)*k**2/2.)
plt.xlabel(r"Time: $k$")
plt.ylabel(r"A number of edges: $L$")
plt.show()
# 時系列で発言者の表示
# print 'self.speaker:', self.speaker
# 評価関数を通した結果
# print 'self.f', self.f()
def end2(self):
# 会議の異常終了(発言者が発言できなくなる)
pass
def init(self):
x = [i.place[0] for i in self.members.values()]
y = [i.place[1] for i in self.members.values()]
plt.scatter(x, y)
plt.ion()
plt.draw()
def callback(self):
# print 'speaker:', self.speaker[-1]
# print 'link:', self.l[-1]
ix = self.members[self.speaker[-2]].place[0]
iy = self.members[self.speaker[-2]].place[1]
jx = self.members[self.speaker[-1]].place[0]
jy = self.members[self.speaker[-1]].place[1]
plt.plot([ix, jx], [iy, jy])
plt.text((ix+jx)/2, (iy+jy)/2, '%d:(%d,%d)'
% (self.k, self.speaker[-2], self.speaker[-1]))
plt.draw()
def progress(self):
self.init()
# はじめに1が発言するとする
self.ideas.append(self.q(1))
self.speaker.append(1)
while True:
j = self.p(self.members[self.speaker[-1]])
self.ideas.append(self.q(j))
self.speaker.append(j)
self.k += 1
self.l.append(self.connect())
self.L.append(len(self.links))
self.callback()
if self.check_agreement():
print "\nnormal end"
self.end()
break
if not self.check_ideas():
print "\nno one can speak"
self.end2()
break
class Main:
def __init__(self, radius=0.3):
N = 6
self.app = meeting(N)
self.app.radius = radius
window = Window(N, main=self.app)
window.display()
class Window(object):
def __init__(self, N, main):
self.root = Tk()
self.main = main
self.width = 640
self.height = 480
self.canvas = Canvas(self.root, width=self.width, height=self.height)
self.var = StringVar()
self.oval(self.canvas, N)
self.canvas.bind('<Motion>', self.pointer)
self.canvas.pack()
label = Label(self.root, textvariable=self.var, font='Ubuntu 9')
label.pack(side='left')
b1 = Button(self.root, text='start', command=self.b1_clicked)
b1.pack(side='right')
b2 = Button(self.root, text='save', command=self.b2_clicked)
b2.pack(side='right')
def oval(self, canvas, N=6):
self.members = dict()
deg = np.linspace(0., 360., N, endpoint=False)
radius = 20
self.r = int((min(self.height, self.width)/2-radius)*0.9)
self.centerx = int(self.width/2)
self.centery = int(self.height/2)
for n in range(1, N+1):
rad = np.radians(deg[n-1])
self.members[n] = Oval(canvas, n,
self.centerx+self.r*np.cos(rad),
self.centery+self.r*np.sin(rad),
radius, self.var)
def pointer(self, event):
self.var.set("(%d,%d)" % (event.x, event.y))
def b1_clicked(self):
self.main.members = dict()
for n in range(1, self.main.N+1):
x = (self.members[n].x-self.centerx)/float(self.r)
y = (self.members[n].y-self.centery)/float(self.r)
self.main.members[n] = Person(place=(x, y))
self.main.progress()
self.root.destroy()
def b2_clicked(self):
import tkFileDialog
import os
fTyp = [('eps file', '*.eps'), ('all files', '*')]
filename = tkFileDialog.asksaveasfilename(filetypes=fTyp,
initialdir=os.getcwd(),
initialfile='figure_1.eps')
if filename is None:
return
try:
self.canvas.postscript(file=filename)
except TclError:
print """
TclError: Cannot save the figure.
Canvas Window must be alive for save."""
return 1
def display(self):
self.root.mainloop()
class Oval:
def __init__(self, canvas, id, x, y, r, var):
self.c = canvas
self.x = x
self.y = y
self.var = var
self.tag = str(id)
self.c.create_oval(x-r, y-r, x+r, y+r, outline='', fill='#069', tags=self.tag)
self.c.tag_bind(self.tag, '<Button-1>', self.pressed)
self.c.tag_bind(self.tag, '<Button1-Motion>', self.dragging)
def pressed(self, event):
self.x = event.x
self.y = event.y
def dragging(self, event):
self.c.move(self.tag, event.x - self.x, event.y - self.y)
self.x = event.x
self.y = event.y
In [2]:
main = Main(radius=1/3.)