-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathgame.py
More file actions
executable file
·379 lines (334 loc) · 11.8 KB
/
game.py
File metadata and controls
executable file
·379 lines (334 loc) · 11.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
# -*- coding: utf-8 -*-
# -*- Author: shaodan(shaodan.cn@gmail.com) -*-
# -*- 2015.07.11 -*-
import numpy as np
class Game(object):
""" base class of game"""
def __init__(self, order=2):
# order=2 Two strategies: 0-Cooperate, 1-Betray
# todo: multi-strategies game
self.order = order
self.population = None
def bind(self, p):
self.population = p
return self
def play(self, node=None, rewire=None):
if node is None:
self.entire_play()
elif rewire is None:
self.fast_play(node)
else:
self.fast_play2(node, rewire)
def entire_play(self):
raise NotImplementedError("Game.entire_play")
def rewire_play(self, rewire):
pass
def fast_play(self, node, rewire=None):
"""
nodes contain all nodes whose strategy or structure changed(no matter initiative or passive)
partly play nodes and their neighbours
"""
raise NotImplementedError("Game.fast_play")
def fast_play2(self, node, rewire):
pass
def node_play(self, node, s=None):
pass
def correct_error(self, node, rewire):
"""
fast_play()会带来10^-16~-15精度的误差,定期修正
"""
self.fast_play(node, rewire)
fit_fast = self.population.fitness.copy()
self.entire_play()
return (fit_fast-self.population.fitness).sum()
class PDG(Game):
""" Prisoner's Dilemma Game"""
def __init__(self, b=None, c=1, r=1, t=1.5, s=0, p=0.1):
super(self.__class__, self).__init__()
if b is not None:
r, s, t, p = b-c, -c, b, 0
# pd condition todo: sd sh games
# assert(t > r > p >= s)
self.payoff = np.array([[(r, r), (s, t)], [(t, s), (p, p)]], dtype=np.double)
self.delta = t-s
def entire_play(self):
fitness = self.population.fitness
strategy = self.population.strategy
fitness.fill(0)
for a, b in self.population.edges:
p = self.payoff[strategy[a]][strategy[b]]
fitness[a] += p[0]
fitness[b] += p[1]
def fast_play(self, node, rewire=None):
strategy = self.population.strategy
fitness = self.population.fitness
# no change
if node < 0:
return
f = 0 # 新节点收益重新计算
s = strategy[node]
s_ = 1 - s
for neigh in self.population.neighbors(node):
p = self.payoff[s][strategy[neigh]]
f += p[0] # 新节点累加
new_payoff = p[1] # 邻居节点计算新的收益
p = self.payoff[s_][strategy[neigh]]
old_payoff = p[1] # 邻居节点计算原来的收益
fitness[neigh] += new_payoff - old_payoff
fitness[node] = f
def fast_play2(self, node, rewire=None):
strategy = self.population.strategy
fitness = self.population.fitness
updated = True
if node < 0:
if rewire is None:
return
updated = False
node = rewire[0]
s = strategy[node]
s_ = 1-s if updated else s
rewired = False
if rewire is not None:
rewired = True
old, new = rewire[1:]
old_p = self.payoff[s_][strategy[old]]
new_p = self.payoff[s][strategy[new]]
fitness[node] += new_p[0]-old_p[0]
fitness[old] -= old_p[1]
fitness[new] += new_p[1]
if not updated:
return
p = 0 # 新节点收益从0计算
for neigh in self.population.neighbors(node):
new_p = self.payoff[s][strategy[neigh]]
p += new_p[0]
if not rewired or neigh != new:
old_p = self.payoff[s_][strategy[neigh]]
fitness[neigh] += new_p[1]-old_p[1]
fitness[node] = p
def rewire_play(self, rewire):
strategy = self.population.strategy
fitness = self.population.fitness
if rewire is None:
return
a, b, c = rewire
# 旧边
p_old = self.payoff[strategy[a]][strategy[b]]
fitness[a] -= p_old[0]
fitness[b] -= p_old[1]
# 新边
p_new = self.payoff[strategy[a]][strategy[c]]
fitness[a] += p_new[0]
fitness[c] += p_new[1]
def fast_play1(self, node_list, edge_list):
strategy = self.population.strategy
fitness = self.population.fitness
# 只用计算新节点和其邻居节点的收益
for edge in edge_list:
a = edge[0]
b = edge[1]
p = self.payoff[strategy[a]][strategy[b]]
if edge[2] == 0: # 旧边断开
fitness[a] -= p[0]
fitness[b] -= p[1]
else: # 新边
fitness[a] += p[0]
fitness[b] += p[1]
for node in node_list:
f = 0 # 新节点收益从0计算
s = strategy[node]
s_ = 1 - s
for neigh in self.population.neighbors(node):
p = self.payoff[s][strategy[neigh]]
f += p[0] # 新节点累加
new_payoff = p[1] # 邻居节点计算新的收益
# 0合作,1背叛
p = self.payoff[s_][strategy[neigh]]
old_payoff = p[1] # 邻居节点计算原来的收益
fitness[neigh] += new_payoff - old_payoff
fitness[node] = f
def node_play(self, node, s=None):
strategy = self.population.strategy
if s is None:
s = strategy[node]
p = 0
for n in self.population.neighbors(node):
p += (self.payoff[s][strategy[n]])[0]
return p
class PGG(Game):
""" Public Goods Game
第一种 每个group投入1
"""
def __init__(self, r=2, fixed=False):
super(PGG, self).__init__()
# 获利倍数
self.r = float(r)
self.fixed = fixed
def entire_play_old(self):
strategy = self.population.strategy
fitness = self.population.fitness
fitness.fill(0)
degree_array = self.population.degree_cache
for node in self.population.nodes():
degree = degree_array[node]
fitness[node] += (strategy[node]-1) * (degree+1)
neighs = list(self.population.neighbors(node))
neighs.append(node)
# b = self.r * (strategy[neighs]==0).sum() / (degree+1)
b = self.r * (len(neighs)-np.count_nonzero(strategy[neighs])) / (degree+1)
for neigh in neighs:
fitness[neigh] += b
def entire_play(self):
strategy = self.population.strategy
fitness = self.population.fitness
degree_array = self.population.degree_cache + 1
cop = 1-strategy
fitness[:] = cop * -degree_array
for node in self.population:
neighs = list(self.population.neighbors(node))
neighs.append(node)
b = self.r * (np.count_nonzero(cop[neighs])) / degree_array[node]
for neigh in neighs:
fitness[neigh] += b
def fast_play(self, node, rewire=None):
strategy = self.population.strategy
fitness = self.population.fitness
if node < 0:
return
s = strategy[node]
d = self.population.degree_cache[node]+1
sign = (1 - 2*s)
sign_r = sign * self.r
# 更新节点作为中心pgg产生的收益增量
delta = sign_r / d
fitness[node] += delta - sign * d
for neigh in self.population.neighbors(node):
delta_neigh = sign_r/(self.population.degree_cache[neigh]+1)
fitness[neigh] += delta + delta_neigh
for nn in self.population.neighbors(neigh):
fitness[nn] += delta_neigh
def fast_play2(self, node, rewire=None):
strategy = self.population.strategy
fitness = self.population.fitness
if node < 0:
if rewire is None:
return
# 2 r
node, old, new = rewire
s = strategy[node]
s_ = s
f = 0
k_old = self.population.degree_cache[old]
for n in self.population.neighbors(old):
f += s/k_old
fitness[node] += f
return
if rewire is None:
# 1 s
return
# 3 r+s
s = strategy[node]
sign = (1 - 2*s)
sign_r = sign * self.r
d = self.population.degree_cache[node]
# 更新节点作为中心pgg产生的收益增量
delta = sign_r/(d+1)
fitness[node] += delta - sign*(d+1)
for neigh in self.population.neighbors(node):
delta_neigh = sign_r/(self.population.degree_cache[neigh]+1)
fitness[neigh] += delta + delta_neigh
for neigh_neigh in self.population.neighbors(neigh):
fitness[neigh_neigh] += delta_neigh
class PGG2(PGG):
""" pgg
第二种 每个group投入1/(k+1)
"""
def entire_play(self):
strategy = self.population.strategy
fitness = self.population.fitness
fitness.fill(0)
degree_array = self.population.degree_cache
# 每个group投入1/(k+1)
inv = (1.0-strategy) / (degree_array+1)
for node in self.population.nodes():
fitness[node] += strategy[node] - 1
neighs = list(self.population.neighbors(node))
neighs.append(node)
b = self.r * inv[neighs].sum() / (degree_array[node]+1)
for neigh in neighs:
fitness[neigh] += b
def fast_play(self, node, rewire=None):
strategy = self.population.strategy
fitness = self.population.fitness
if node < 0:
return
s = strategy[node]
d = self.population.degree_cache[node] + 1
sign = (1 - 2*s)
sign_r = sign * self.r / d
# 更新节点作为中心pgg产生的收益增量
delta = sign_r / d
fitness[node] += delta - sign
for neigh in self.population.neighbors(node):
delta_neigh = sign_r/(self.population.degree_cache[neigh]+1)
fitness[neigh] += delta + delta_neigh
for neigh_neigh in self.population.neighbors(neigh):
fitness[neigh_neigh] += delta_neigh
class RPG(Game):
""" Rational Player Game
"""
def __init__(self, ration):
super(self.__class__, self).__init__()
self.ration = ration
def entire_play(self):
strategy = self.population.strategy
fitness = self.population.fitness
for n in self.population.nodes():
fitness[n] = 1
pass
def fast_play(self, node=None, rewire=None):
pass
# TEST CODE HERE
if __name__ == '__main__':
import networkx as nx
import population as pp
import rule
G = nx.random_regular_graph(4, 1000)
P = pp.Population(G)
# g = PDG().bind(P)
g = PGG(3).bind(P)
u = rule.BirthDeath().bind(P)
# =========test entire_play ===========
# g.entire_play_old()
# fit1 = P.fitness.copy()
# g.entire_play()
# fit2 = P.fitness
# print((fit1 - fit2).sum())
# exit(0)
# =========test fast_play ============
g.play()
i = np.random.randint(1000)
print(i, P.neighbors(i))
case = 0
if case == 0:
g.play()
elif case == 1:
P.strategy[i] = 1-P.strategy[i]
g.play(i)
else:
j = P.random_neighbor(i)
k = np.random.choice(P.nodes_nonadjacent(i))
P.rewire(i, j, k)
if case == 2:
g.play(-1, (i, j, k))
else:
# case 3
P.strategy[i] = 1-P.strategy[i]
g.play(i, (i, j, k))
fit1 = P.fitness.copy()
g.play()
fit2 = P.fitness
print("=======delta=======")
print((fit1-fit2).sum())
print(fit1[i] - fit2[i])
print(P.neighbors(i))