piegy 1.1.6__tar.gz → 2.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {piegy-1.1.6/src/piegy.egg-info → piegy-2.0.1}/PKG-INFO +1 -1
- {piegy-1.1.6 → piegy-2.0.1}/pyproject.toml +5 -1
- piegy-2.0.1/src/piegy/C_core/piegyc.so +0 -0
- {piegy-1.1.6 → piegy-2.0.1}/src/piegy/__version__.py +2 -0
- {piegy-1.1.6 → piegy-2.0.1}/src/piegy/data_tools.py +16 -21
- piegy-2.0.1/src/piegy/simulation.py +443 -0
- piegy-1.1.6/src/piegy/simulation.py → piegy-2.0.1/src/piegy/simulation_py.py +9 -356
- {piegy-1.1.6 → piegy-2.0.1}/src/piegy/test_var.py +2 -2
- {piegy-1.1.6 → piegy-2.0.1/src/piegy.egg-info}/PKG-INFO +1 -1
- {piegy-1.1.6 → piegy-2.0.1}/src/piegy.egg-info/SOURCES.txt +2 -0
- {piegy-1.1.6 → piegy-2.0.1}/LICENSE.txt +0 -0
- {piegy-1.1.6 → piegy-2.0.1}/MANIFEST.in +0 -0
- {piegy-1.1.6 → piegy-2.0.1}/README.md +0 -0
- {piegy-1.1.6 → piegy-2.0.1}/setup.cfg +0 -0
- {piegy-1.1.6 → piegy-2.0.1}/src/piegy/__init__.py +0 -0
- {piegy-1.1.6 → piegy-2.0.1}/src/piegy/analysis.py +0 -0
- {piegy-1.1.6 → piegy-2.0.1}/src/piegy/figures.py +0 -0
- {piegy-1.1.6 → piegy-2.0.1}/src/piegy/tools/__init__.py +0 -0
- {piegy-1.1.6 → piegy-2.0.1}/src/piegy/tools/figure_tools.py +0 -0
- {piegy-1.1.6 → piegy-2.0.1}/src/piegy/tools/file_tools.py +0 -0
- {piegy-1.1.6 → piegy-2.0.1}/src/piegy/videos.py +0 -0
- {piegy-1.1.6 → piegy-2.0.1}/src/piegy.egg-info/dependency_links.txt +0 -0
- {piegy-1.1.6 → piegy-2.0.1}/src/piegy.egg-info/requires.txt +0 -0
- {piegy-1.1.6 → piegy-2.0.1}/src/piegy.egg-info/top_level.txt +0 -0
@@ -4,7 +4,7 @@ build-backend = 'setuptools.build_meta'
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = 'piegy'
|
7
|
-
version = '
|
7
|
+
version = '2.0.1'
|
8
8
|
description = 'Payoff-Driven Stochastic Spatial Model for Evolutionary Game Theory'
|
9
9
|
readme = 'README.md'
|
10
10
|
requires-python = '>=3.6'
|
@@ -42,3 +42,7 @@ package-dir = {'' = 'src'}
|
|
42
42
|
[tool.setuptools.packages.find]
|
43
43
|
where = ['src']
|
44
44
|
|
45
|
+
[tool.setuptools.package-data]
|
46
|
+
"piegy" = ["C_core/piegyc.so"]
|
47
|
+
|
48
|
+
|
Binary file
|
@@ -21,4 +21,6 @@ version history:
|
|
21
21
|
1.1.5: update README.
|
22
22
|
1.1.6: change name of variables in model class -- for compatability with the new C core. 1.1.6 is the last verion of v1. From v2 on, the piegy package has C core.
|
23
23
|
|
24
|
+
2.0.0: update simulation core to C-based.
|
25
|
+
2.0.1: re-upload, the C core is not included in package.
|
24
26
|
'''
|
@@ -38,24 +38,19 @@ def save_data(mod, dirs = '', print_msg = True):
|
|
38
38
|
|
39
39
|
data = []
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
inputs2.append(mod.print_pct)
|
55
|
-
inputs2.append(mod.seed)
|
56
|
-
inputs2.append(mod.UV_dtype)
|
57
|
-
inputs2.append(mod.pi_dtype)
|
58
|
-
data.append(inputs2)
|
41
|
+
inputs = []
|
42
|
+
inputs.append(mod.N)
|
43
|
+
inputs.append(mod.M)
|
44
|
+
inputs.append(mod.maxtime)
|
45
|
+
inputs.append(mod.record_itv)
|
46
|
+
inputs.append(mod.sim_time)
|
47
|
+
inputs.append(mod.boundary)
|
48
|
+
inputs.append(mod.I.tolist())
|
49
|
+
inputs.append(mod.X.tolist())
|
50
|
+
inputs.append(mod.P.tolist())
|
51
|
+
inputs.append(mod.print_pct)
|
52
|
+
inputs.append(mod.seed)
|
53
|
+
data.append(inputs)
|
59
54
|
|
60
55
|
# skipped rng
|
61
56
|
|
@@ -112,14 +107,14 @@ def read_data(dirs):
|
|
112
107
|
try:
|
113
108
|
mod = simulation.model(N = data[0][0], M = data[0][1], maxtime = data[0][2], record_itv = data[0][3],
|
114
109
|
sim_time = data[0][4], boundary = data[0][5], I = data[0][6], X = data[0][7], P = data[0][8],
|
115
|
-
print_pct = data[
|
110
|
+
print_pct = data[0][9], seed = data[0][10])
|
116
111
|
except:
|
117
112
|
raise ValueError('Invalid input parameters saved in data')
|
118
113
|
|
119
114
|
# outputs
|
120
115
|
try:
|
121
|
-
mod.set_data(data_empty = False, max_record = data[
|
122
|
-
U = data[
|
116
|
+
mod.set_data(data_empty = False, max_record = data[1][0], compress_itv = data[1][1],
|
117
|
+
U = data[1][2], V = data[1][3], Upi = data[1][4], Vpi = data[1][5])
|
123
118
|
except:
|
124
119
|
raise ValueError('Invalid model results saved in data')
|
125
120
|
|
@@ -0,0 +1,443 @@
|
|
1
|
+
'''
|
2
|
+
Main Module of Stochastic Simulation
|
3
|
+
-------------------------------------------
|
4
|
+
|
5
|
+
Contains all the necessary tools to build a model and run models based on Gillespie Algorithm.
|
6
|
+
|
7
|
+
Classes:
|
8
|
+
- patch: (Private) Simulates a single patch in the N x M space. Assume no spatial structure within a patch.
|
9
|
+
All spatial movements are based on patches.
|
10
|
+
- model: Stores input parameters and data generated during simulation.
|
11
|
+
|
12
|
+
|
13
|
+
Functions:
|
14
|
+
- find_nb_zero_flux: (Private) Return pointers to a patch's neighbors under zero-flux (no-boundary) boundary condition.
|
15
|
+
- find_nb_periodical: (Private) Return pointers to a patch's neighbors under periodical (with-booundary) boundary condition.
|
16
|
+
- find_event: (Private) Pick a random event to happen.
|
17
|
+
- make_signal_zero_flux: (Private) Expand event index (return value of find_event) to a detailed signal, under zero-flux boundary condition.
|
18
|
+
- make_signal_periodical: (Private) Expand event index (return value of find_event) to a detailed signal, under periodical boundary condition.
|
19
|
+
- single_init: (Private) Initialize a single model. Meaning of 'single': see single_test and run.
|
20
|
+
- single_test: (Private) Run a single model.
|
21
|
+
'single' means a single round of model. You can run many single rounds and then take the average --- that's done by <run> function.
|
22
|
+
- run: Run multiple models and then take the average. All the model will use the same parameters.
|
23
|
+
Set a seed for reproducible results.
|
24
|
+
- demo_model: Returns a demo model (a model object).
|
25
|
+
|
26
|
+
NOTE: Only model class and run function are intended for direct usages.
|
27
|
+
'''
|
28
|
+
|
29
|
+
|
30
|
+
import numpy as np
|
31
|
+
import os
|
32
|
+
import ctypes
|
33
|
+
from ctypes import c_size_t, c_uint32, c_int32, c_double, c_bool, c_char_p, c_char
|
34
|
+
import numpy as np
|
35
|
+
from numpy.ctypeslib import ndpointer
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
'''
|
41
|
+
The C core
|
42
|
+
'''
|
43
|
+
|
44
|
+
# path to the C shared libary
|
45
|
+
C_LIB_PATH = os.path.join(os.path.dirname(__file__), 'C_core', 'piegyc.so')
|
46
|
+
|
47
|
+
|
48
|
+
class model_c(ctypes.Structure):
|
49
|
+
'''
|
50
|
+
The C-cored model
|
51
|
+
'''
|
52
|
+
|
53
|
+
_fields_ = [
|
54
|
+
('N', c_size_t),
|
55
|
+
('M', c_size_t),
|
56
|
+
('maxtime', c_double),
|
57
|
+
('record_itv', c_double),
|
58
|
+
('sim_time', c_size_t),
|
59
|
+
('boundary', c_bool),
|
60
|
+
|
61
|
+
('I', ctypes.POINTER(c_uint32)),
|
62
|
+
('X', ctypes.POINTER(c_double)),
|
63
|
+
('P', ctypes.POINTER(c_double)),
|
64
|
+
|
65
|
+
('print_pct', c_int32),
|
66
|
+
('seed', c_int32),
|
67
|
+
|
68
|
+
('data_empty', c_bool),
|
69
|
+
('max_record', c_size_t),
|
70
|
+
('arr_size', c_size_t),
|
71
|
+
('compress_itv', c_uint32),
|
72
|
+
|
73
|
+
('U1d', ctypes.POINTER(c_double)),
|
74
|
+
('V1d', ctypes.POINTER(c_double)),
|
75
|
+
('Upi_1d', ctypes.POINTER(c_double)),
|
76
|
+
('Vpi_1d', ctypes.POINTER(c_double)),
|
77
|
+
]
|
78
|
+
def get_array(self, name):
|
79
|
+
"""Return internal data as NumPy array, e.g. .get_array('U')"""
|
80
|
+
ptr = getattr(self, name)
|
81
|
+
return np.ctypeslib.as_array(ptr, shape=(self.arr_size,))
|
82
|
+
|
83
|
+
lib = ctypes.CDLL(C_LIB_PATH, winmode = 0)
|
84
|
+
lib.mod_init.argtypes = [
|
85
|
+
ctypes.POINTER(model_c), c_size_t, c_size_t,
|
86
|
+
c_double, c_double, c_size_t, c_bool,
|
87
|
+
ndpointer(dtype=np.uint32, flags="C_CONTIGUOUS"),
|
88
|
+
ndpointer(dtype=np.float64, flags="C_CONTIGUOUS"),
|
89
|
+
ndpointer(dtype=np.float64, flags="C_CONTIGUOUS"),
|
90
|
+
c_int32, c_int32
|
91
|
+
]
|
92
|
+
lib.mod_init.restype = c_bool
|
93
|
+
|
94
|
+
lib.run.argtypes = [ctypes.POINTER(model_c), ctypes.POINTER(c_char), c_size_t]
|
95
|
+
lib.run.restype = None
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
'''
|
100
|
+
For access by Python
|
101
|
+
'''
|
102
|
+
|
103
|
+
class model:
|
104
|
+
'''
|
105
|
+
Store model data and input parameters.
|
106
|
+
Initialize a model object to run models.
|
107
|
+
|
108
|
+
Public Class Functions:
|
109
|
+
|
110
|
+
__init__:
|
111
|
+
Create a model object. Also initialize data storage.
|
112
|
+
|
113
|
+
__str__:
|
114
|
+
Print model object in a nice way.
|
115
|
+
|
116
|
+
copy:
|
117
|
+
Return a deep copy of self. Can choose whether to copy data as well. Default is to copy.
|
118
|
+
|
119
|
+
clear_data:
|
120
|
+
clear all data stored, set U, V, Upi, Vpi to zero arrays
|
121
|
+
|
122
|
+
change_maxtime:
|
123
|
+
Changes maxtime of self. Update data storage as well.
|
124
|
+
|
125
|
+
set_seed:
|
126
|
+
Set a new seed.
|
127
|
+
|
128
|
+
compress_data:
|
129
|
+
compress data by only storing average values
|
130
|
+
'''
|
131
|
+
|
132
|
+
def __init__(self, N, M, maxtime, record_itv, sim_time, boundary, I, X, P, print_pct = 25, seed = None):
|
133
|
+
|
134
|
+
self.check_valid_input(N, M, maxtime, record_itv, sim_time, boundary, I, X, P, print_pct)
|
135
|
+
|
136
|
+
self.N = N # int, N x M is spatial dimension
|
137
|
+
self.M = M # int, can't be 1. If want to make 1D space, use N = 1. And this model doesn't work for 1x1 space (causes NaN)
|
138
|
+
self.maxtime = maxtime # float or int, run model for how long time
|
139
|
+
self.record_itv = record_itv # float, record data every record_itv of time
|
140
|
+
self.sim_time = sim_time # int, run this many of rounds (of single_test)
|
141
|
+
self.boundary = boundary # bool, the N x M space have boundary or not (i.e., zero-flux (True) or periodical (False))
|
142
|
+
self.I = np.array(I) # N x M x 2 np.array, initial population. Two init-popu for every patch (U and V)
|
143
|
+
self.X = np.array(X) # N x M x 4 np.array, matrices. The '4' comes from 2x2 matrix flattened to 1D
|
144
|
+
self.P = np.array(P) # N x M x 6 np.array, 'patch variables', i.e., mu1&2, w1&2, kappa1&2
|
145
|
+
self.print_pct = print_pct # int, print how much percent is done, need to be non-zero
|
146
|
+
self.seed = seed # non-negative int, seed for random number generation
|
147
|
+
|
148
|
+
self.init_storage() # initialize storage bins. Put in a separate function because might want to change maxtime
|
149
|
+
# and that doesn't need to initialze the whole object again
|
150
|
+
|
151
|
+
|
152
|
+
def init_storage(self):
|
153
|
+
# initialize storage bins
|
154
|
+
self.data_empty = True # whether data storage bins are empty. model.run will refuse to run (raise error) if not empty.
|
155
|
+
self.max_record = int(self.maxtime / self.record_itv) # int, how many data points to store sin total
|
156
|
+
self.compress_itv = 1 # int, intended to reduce size of data (if not 1). Updated by compress_data function
|
157
|
+
# if set to an int, say 20, mod will take average over every 20 data points and save them as new data.
|
158
|
+
# May be used over and over again to recursively reduce data size.
|
159
|
+
# Default is 1, not to take average.
|
160
|
+
self.U = None # initialized by simulation.run or data_tools.read_data
|
161
|
+
self.V = None
|
162
|
+
self.Upi = None
|
163
|
+
self.Vpi = None
|
164
|
+
|
165
|
+
|
166
|
+
def check_valid_input(self, N, M, maxtime, record_itv, sim_time, boundary, I, X, P, print_pct):
|
167
|
+
# check whether the inputs are valid
|
168
|
+
|
169
|
+
if (N < 1) or (M < 1):
|
170
|
+
raise ValueError('N < 1 or M < 1')
|
171
|
+
if (N == 1) and (M == 1):
|
172
|
+
raise ValueError('Model fails for 1x1 space')
|
173
|
+
if (M == 1):
|
174
|
+
raise ValueError('Please set N = 1 for 1D space.')
|
175
|
+
if maxtime <= 0:
|
176
|
+
raise ValueError('Please set a positive number for maxtime')
|
177
|
+
if record_itv <= 0:
|
178
|
+
raise ValueError('Please set a positive number for record_itv')
|
179
|
+
if sim_time <= 0:
|
180
|
+
raise ValueError('Please set a positive number for sim_time')
|
181
|
+
if type(boundary) != bool:
|
182
|
+
raise TypeError('boundary not a bool. Please use True for zero-flux (with boundary) or False for periodical (no boundary)')
|
183
|
+
|
184
|
+
if (type(I) != list) and (type(I) != np.ndarray):
|
185
|
+
raise TypeError('Please set I as a list or np.ndarray')
|
186
|
+
if np.array(I).shape != (N, M, 2):
|
187
|
+
raise ValueError('Please set I as a N x M x 2 shape list or array. 2 is for init values of U, V at every patch')
|
188
|
+
|
189
|
+
if (type(X) != list) and (type(X) != np.ndarray):
|
190
|
+
raise TypeError('Please set X as a list or np.ndarray')
|
191
|
+
if np.array(X).shape != (N, M, 4):
|
192
|
+
raise ValueError('Please set X as a N x M x 4 shape list or array. 4 is for the flattened 2x2 payoff matrix')
|
193
|
+
|
194
|
+
if (type(P) != list) and (type(P) != np.ndarray):
|
195
|
+
raise TypeError('Please set P as a list or np.ndarray')
|
196
|
+
if np.array(P).shape != (N, M, 6):
|
197
|
+
raise ValueError('Please set P as a N x M x 6 shape list or array. 6 is for mu1, mu2, w1, w2, kappa1, kappa2')
|
198
|
+
|
199
|
+
if print_pct <= 0:
|
200
|
+
raise ValueError('Please use an int > 0 for print_pct or None for not printing progress.')
|
201
|
+
|
202
|
+
|
203
|
+
def check_valid_data(self, data_empty, max_record, compress_itv):
|
204
|
+
# check whether a set of data is valid, used when reading a saved model
|
205
|
+
if type(data_empty) != bool:
|
206
|
+
raise TypeError('data_empty not a bool')
|
207
|
+
|
208
|
+
if type(max_record) != int:
|
209
|
+
raise TypeError('max_record not an int')
|
210
|
+
if max_record < 0:
|
211
|
+
raise ValueError('max_record < 0')
|
212
|
+
|
213
|
+
if type(compress_itv) != int:
|
214
|
+
raise TypeError('compress_itv not an int')
|
215
|
+
if compress_itv < 0:
|
216
|
+
raise ValueError('compress_itv < 0')
|
217
|
+
|
218
|
+
|
219
|
+
def __str__(self):
|
220
|
+
# print this mod in a nice format
|
221
|
+
|
222
|
+
self_str = ''
|
223
|
+
self_str += 'N = ' + str(self.N) + '\n'
|
224
|
+
self_str += 'M = ' + str(self.M) + '\n'
|
225
|
+
self_str += 'maxtime = ' + str(self.maxtime) + '\n'
|
226
|
+
self_str += 'record_itv = ' + str(self.record_itv) + '\n'
|
227
|
+
self_str += 'sim_time = ' + str(self.sim_time) + '\n'
|
228
|
+
self_str += 'boundary = ' + str(self.boundary) + '\n'
|
229
|
+
self_str += 'print_pct = ' + str(self.print_pct) + '\n'
|
230
|
+
self_str += 'seed = ' + str(self.seed) + '\n'
|
231
|
+
self_str += 'data_empty = ' + str(self.data_empty) + '\n'
|
232
|
+
self_str += 'compress_itv = ' + str(self.compress_itv) + '\n'
|
233
|
+
self_str += '\n'
|
234
|
+
|
235
|
+
# check whether I, X, P all same (compare all patches to (0, 0))
|
236
|
+
I_same = True
|
237
|
+
X_same = True
|
238
|
+
P_same = True
|
239
|
+
for i in range(self.N):
|
240
|
+
for j in range(self.M):
|
241
|
+
for k in range(2):
|
242
|
+
if self.I[i][j][k] != self.I[0][0][k]:
|
243
|
+
I_same = False
|
244
|
+
for k in range(4):
|
245
|
+
if self.X[i][j][k] != self.X[0][0][k]:
|
246
|
+
X_same = False
|
247
|
+
for k in range(6):
|
248
|
+
if self.P[i][j][k] != self.P[0][0][k]:
|
249
|
+
P_same = False
|
250
|
+
|
251
|
+
if I_same:
|
252
|
+
self_str += 'I all same: ' + str(self.I[0][0]) + '\n'
|
253
|
+
else:
|
254
|
+
self_str += 'I:\n'
|
255
|
+
for i in range(self.N):
|
256
|
+
for j in range(self.M):
|
257
|
+
self_str += str(self.I[i][j]) + ' '
|
258
|
+
self_str += '\n'
|
259
|
+
self_str += '\n'
|
260
|
+
|
261
|
+
if X_same:
|
262
|
+
self_str += 'X all same: ' + str(self.X[0][0]) + '\n'
|
263
|
+
else:
|
264
|
+
self_str += 'X:\n'
|
265
|
+
for i in range(self.N):
|
266
|
+
for j in range(self.M):
|
267
|
+
self_str += str(self.X[i][j]) + ' '
|
268
|
+
self_str += '\n'
|
269
|
+
self_str += '\n'
|
270
|
+
|
271
|
+
if P_same:
|
272
|
+
self_str += 'P all same: ' + str(self.P[0][0]) + '\n'
|
273
|
+
else:
|
274
|
+
self_str += 'P:\n'
|
275
|
+
for i in range(self.N):
|
276
|
+
for j in range(self.M):
|
277
|
+
self_str += str(self.P[i][j]) + ' '
|
278
|
+
self_str += '\n'
|
279
|
+
|
280
|
+
return self_str
|
281
|
+
|
282
|
+
|
283
|
+
def copy(self, copy_data = True):
|
284
|
+
# return deep copy of self
|
285
|
+
# copy_data decides whether to copy data as well
|
286
|
+
if type(copy_data) != bool:
|
287
|
+
raise TypeError('Please give a bool as argument: whether to copy data or not')
|
288
|
+
|
289
|
+
sim2 = model(N = self.N, M = self.M, maxtime = self.maxtime, record_itv = self.record_itv, sim_time = self.sim_time, boundary = self.boundary,
|
290
|
+
I = np.copy(self.I), X = np.copy(self.X), P = np.copy(self.P),
|
291
|
+
print_pct = self.print_pct, seed = self.seed)
|
292
|
+
|
293
|
+
if copy_data:
|
294
|
+
# copy data as well
|
295
|
+
sim2.set_data(self.data_empty, self.max_record, self.compress_itv, self.U, self.V, self.Upi, self.Vpi)
|
296
|
+
|
297
|
+
return sim2
|
298
|
+
|
299
|
+
|
300
|
+
def calculate_ave(self):
|
301
|
+
# get the average value over sim_time many models
|
302
|
+
if self.sim_time != 1:
|
303
|
+
for i in range(self.N):
|
304
|
+
for j in range(self.M):
|
305
|
+
for t in range(self.max_record):
|
306
|
+
self.U[i][j][t] /= self.sim_time
|
307
|
+
self.V[i][j][t] /= self.sim_time
|
308
|
+
self.Upi[i][j][t] /= self.sim_time
|
309
|
+
self.Vpi[i][j][t] /= self.sim_time
|
310
|
+
|
311
|
+
|
312
|
+
def change_maxtime(self, maxtime):
|
313
|
+
# change maxtime
|
314
|
+
if (type(maxtime) != float) and (type(maxtime) != int):
|
315
|
+
raise TypeError('Please pass in a float or int as the new maxtime.')
|
316
|
+
if maxtime <= 0:
|
317
|
+
raise ValueError('Please use a positive maxtime.')
|
318
|
+
self.maxtime = maxtime
|
319
|
+
self.init_storage()
|
320
|
+
|
321
|
+
|
322
|
+
def set_seed(self, seed):
|
323
|
+
# set seed
|
324
|
+
self.seed = seed
|
325
|
+
|
326
|
+
|
327
|
+
def clear_data(self):
|
328
|
+
# clear all data stored, set all to 0
|
329
|
+
self.init_storage()
|
330
|
+
|
331
|
+
|
332
|
+
def set_data(self, data_empty, max_record, compress_itv, U, V, Upi, Vpi):
|
333
|
+
# set data to the given data values
|
334
|
+
# copies are made
|
335
|
+
self.check_valid_data(data_empty, max_record, compress_itv)
|
336
|
+
|
337
|
+
self.data_empty = data_empty
|
338
|
+
self.max_record = max_record
|
339
|
+
self.compress_itv = compress_itv
|
340
|
+
self.U = np.copy(U)
|
341
|
+
self.V = np.copy(V)
|
342
|
+
self.Upi = np.copy(Upi)
|
343
|
+
self.Vpi = np.copy(Vpi)
|
344
|
+
|
345
|
+
|
346
|
+
def compress_data(self, compress_itv = 5):
|
347
|
+
# compress data by only storing average values
|
348
|
+
if self.data_empty:
|
349
|
+
raise RuntimeError('Model has empty data. Cannot compress')
|
350
|
+
|
351
|
+
if type(compress_itv) != int:
|
352
|
+
raise TypeError('Please use an int as compress_itv')
|
353
|
+
if compress_itv < 1:
|
354
|
+
raise ValueError('Please use record_itv >= 1')
|
355
|
+
if compress_itv == 1:
|
356
|
+
return
|
357
|
+
|
358
|
+
self.compress_itv *= compress_itv # may be reduced over and over again
|
359
|
+
self.max_record = int(self.max_record / compress_itv) # number of data points after reducing
|
360
|
+
|
361
|
+
U_reduced = np.zeros((self.N, self.M, self.max_record), dtype = np.float64)
|
362
|
+
V_reduced = np.zeros((self.N, self.M, self.max_record), dtype = np.float64)
|
363
|
+
Upi_reduced = np.zeros((self.N, self.M, self.max_record), dtype = np.float64)
|
364
|
+
Vpi_reduced = np.zeros((self.N, self.M, self.max_record), dtype = np.float64)
|
365
|
+
|
366
|
+
for i in range(self.N):
|
367
|
+
for j in range(self.M):
|
368
|
+
for k in range(self.max_record):
|
369
|
+
lower = k * compress_itv # upper and lower bound of current record_itv
|
370
|
+
upper = lower + compress_itv
|
371
|
+
U_reduced[i][j][k] = np.mean(self.U[i, j, lower : upper])
|
372
|
+
V_reduced[i][j][k] = np.mean(self.V[i, j, lower : upper])
|
373
|
+
Upi_reduced[i][j][k] = np.mean(self.Upi[i, j, lower : upper])
|
374
|
+
Vpi_reduced[i][j][k] = np.mean(self.Vpi[i, j, lower : upper])
|
375
|
+
|
376
|
+
self.U = U_reduced
|
377
|
+
self.V = V_reduced
|
378
|
+
self.Upi = Upi_reduced
|
379
|
+
self.Vpi = Vpi_reduced
|
380
|
+
|
381
|
+
|
382
|
+
|
383
|
+
def run(mod, message = ""):
|
384
|
+
'''
|
385
|
+
C-cored simulation
|
386
|
+
'''
|
387
|
+
msg_len = len(message)
|
388
|
+
msg_bytes = message.encode('utf-8')
|
389
|
+
msg_buffer = ctypes.create_string_buffer(msg_bytes, msg_len)
|
390
|
+
|
391
|
+
I = np.ascontiguousarray(mod.I.flatten(), dtype = np.uint32)
|
392
|
+
X = np.ascontiguousarray(mod.X.flatten(), dtype = np.float64)
|
393
|
+
P = np.ascontiguousarray(mod.P.flatten(), dtype = np.float64)
|
394
|
+
|
395
|
+
mod_c = model_c()
|
396
|
+
success = lib.mod_init(ctypes.byref(mod_c),
|
397
|
+
mod.N, mod.M, mod.maxtime, mod.record_itv, mod.sim_time, mod.boundary,
|
398
|
+
I, X, P, mod.print_pct, mod.seed)
|
399
|
+
if not success:
|
400
|
+
raise RuntimeError("mod_init failed")
|
401
|
+
|
402
|
+
lib.run(ctypes.byref(mod_c), msg_buffer, msg_len)
|
403
|
+
|
404
|
+
mod.data_empty = False
|
405
|
+
mod.U = mod_c.get_array('U1d').reshape(mod.N, mod.M, mod.max_record)
|
406
|
+
mod.V = mod_c.get_array('V1d').reshape(mod.N, mod.M, mod.max_record)
|
407
|
+
mod.Upi = mod_c.get_array('Upi_1d').reshape(mod.N, mod.M, mod.max_record)
|
408
|
+
mod.Vpi = mod_c.get_array('Vpi_1d').reshape(mod.N, mod.M, mod.max_record)
|
409
|
+
|
410
|
+
|
411
|
+
|
412
|
+
def demo_model():
|
413
|
+
'''
|
414
|
+
Returns a demo model.model object
|
415
|
+
'''
|
416
|
+
|
417
|
+
N = 10 # Number of rows
|
418
|
+
M = 10 # Number of cols
|
419
|
+
maxtime = 300 # how long you want the model to run
|
420
|
+
record_itv = 0.1 # how often to record data.
|
421
|
+
sim_time = 1 # repeat model to reduce randomness
|
422
|
+
boundary = True # boundary condition.
|
423
|
+
|
424
|
+
# initial population for the N x M patches.
|
425
|
+
I = [[[44, 22] for _ in range(M)] for _ in range(N)]
|
426
|
+
|
427
|
+
# flattened payoff matrices, total resource is 0.4, cost of fighting is 0.1
|
428
|
+
X = [[[-0.1, 0.4, 0, 0.2] for _ in range(M)] for _ in range(N)]
|
429
|
+
|
430
|
+
# patch variables
|
431
|
+
P = [[[0.5, 0.5, 200, 200, 0.001, 0.001] for _ in range(M)] for _ in range(N)]
|
432
|
+
|
433
|
+
print_pct = 5 # print progress
|
434
|
+
seed = 36 # seed for random number generation
|
435
|
+
|
436
|
+
# create a model object
|
437
|
+
mod = model(N, M, maxtime, record_itv, sim_time, boundary, I, X, P,
|
438
|
+
print_pct = print_pct, seed = seed)
|
439
|
+
|
440
|
+
return mod
|
441
|
+
|
442
|
+
|
443
|
+
|
@@ -1,47 +1,17 @@
|
|
1
1
|
'''
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
Classes:
|
8
|
-
- patch: (Private) Simulates a single patch in the N x M space. Assume no spatial structure within a patch.
|
9
|
-
All spatial movements are based on patches.
|
10
|
-
- model: Stores input parameters and data generated during simulation.
|
11
|
-
|
12
|
-
|
13
|
-
Functions:
|
14
|
-
- find_nb_zero_flux: (Private) Return pointers to a patch's neighbors under zero-flux (no-boundary) boundary condition.
|
15
|
-
- find_nb_periodical: (Private) Return pointers to a patch's neighbors under periodical (with-booundary) boundary condition.
|
16
|
-
- find_event: (Private) Pick a random event to happen.
|
17
|
-
- make_signal_zero_flux: (Private) Expand event index (return value of find_event) to a detailed signal, under zero-flux boundary condition.
|
18
|
-
- make_signal_periodical: (Private) Expand event index (return value of find_event) to a detailed signal, under periodical boundary condition.
|
19
|
-
- single_init: (Private) Initialize a single model. Meaning of 'single': see single_test and run.
|
20
|
-
- single_test: (Private) Run a single model.
|
21
|
-
'single' means a single round of model. You can run many single rounds and then take the average --- that's done by <run> function.
|
22
|
-
- run: Run multiple models and then take the average. All the model will use the same parameters.
|
23
|
-
Set a seed for reproducible results.
|
24
|
-
- demo_model: Returns a demo model (a model object).
|
25
|
-
|
26
|
-
NOTE: Only model class and run function are intended for direct usages.
|
2
|
+
Functions and class below are for Python-based simulations. Not maintained after v1.1.6 on Jun 26, 2025.
|
3
|
+
But you can still run them by calling:
|
4
|
+
|
5
|
+
>>> run_py(mod)
|
27
6
|
'''
|
28
7
|
|
8
|
+
from . import simulation
|
29
9
|
|
30
10
|
import math
|
31
11
|
import numpy as np
|
32
12
|
from timeit import default_timer as timer
|
33
13
|
|
34
14
|
|
35
|
-
# data type used by model.U and model.V
|
36
|
-
UV_DTYPE = 'float64'
|
37
|
-
|
38
|
-
# data type used by model.Upi and V_pi
|
39
|
-
PI_DTYPE = 'float64'
|
40
|
-
|
41
|
-
# data type for storing rates in single_test an single_init
|
42
|
-
RATES_DTYPE = 'float64'
|
43
|
-
|
44
|
-
|
45
15
|
class patch:
|
46
16
|
'''
|
47
17
|
A single patch in the N x M space.
|
@@ -63,7 +33,7 @@ class patch:
|
|
63
33
|
Set pointers to neighbors of this patch object.
|
64
34
|
|
65
35
|
update_pi:
|
66
|
-
Update Upi,
|
36
|
+
Update Upi, Vpi and payoff rates (payoff rates are the first two numbers in self.pi_death_rates).
|
67
37
|
|
68
38
|
update_k:
|
69
39
|
Update natural death rates (the last two numbers in self.pi_death_rates).
|
@@ -248,291 +218,6 @@ class patch:
|
|
248
218
|
self.V += 1
|
249
219
|
elif self.V > 0:
|
250
220
|
self.V -= 1
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
class model:
|
255
|
-
'''
|
256
|
-
Store model data and input parameters.
|
257
|
-
Initialize a model object to run models.
|
258
|
-
|
259
|
-
Public Class Functions:
|
260
|
-
|
261
|
-
__init__:
|
262
|
-
Create a model object. Also initialize data storage.
|
263
|
-
|
264
|
-
__str__:
|
265
|
-
Print model object in a nice way.
|
266
|
-
|
267
|
-
copy:
|
268
|
-
Return a deep copy of self. Can choose whether to copy data as well. Default is to copy.
|
269
|
-
|
270
|
-
clear_data:
|
271
|
-
clear all data stored, set U, V, Upi, V_pi to zero arrays
|
272
|
-
|
273
|
-
change_maxtime:
|
274
|
-
Changes maxtime of self. Update data storage as well.
|
275
|
-
|
276
|
-
set_seed:
|
277
|
-
Set a new seed.
|
278
|
-
|
279
|
-
compress_data:
|
280
|
-
compress data by only storing average values
|
281
|
-
'''
|
282
|
-
|
283
|
-
def __init__(self, N, M, maxtime, record_itv, sim_time, boundary, I, X, P, print_pct = 25, seed = None, UV_dtype = UV_DTYPE, pi_dtype = PI_DTYPE):
|
284
|
-
|
285
|
-
self.check_valid_input(N, M, maxtime, record_itv, sim_time, boundary, I, X, P, print_pct)
|
286
|
-
|
287
|
-
self.N = N # int, N x M is spatial dimension
|
288
|
-
self.M = M # int, can't be 1. If want to make 1D space, use N = 1. And this model doesn't work for 1x1 space (causes NaN)
|
289
|
-
self.maxtime = maxtime # float or int, run model for how long time
|
290
|
-
self.record_itv = record_itv # float, record data every record_itv of time
|
291
|
-
self.sim_time = sim_time # int, run this many of rounds (of single_test)
|
292
|
-
self.boundary = boundary # bool, the N x M space have boundary or not (i.e., zero-flux (True) or periodical (False))
|
293
|
-
self.I = np.array(I) # N x M x 2 np.array, initial population. Two init-popu for every patch (U and V)
|
294
|
-
self.X = np.array(X) # N x M x 4 np.array, matrices. The '4' comes from 2x2 matrix flattened to 1D
|
295
|
-
self.P = np.array(P) # N x M x 6 np.array, 'patch variables', i.e., mu1&2, w1&2, kappa1&2
|
296
|
-
self.print_pct = print_pct # int, print how much percent is done, need to be non-zero
|
297
|
-
self.seed = seed # non-negative int, seed for random generator
|
298
|
-
self.UV_dtype = UV_dtype # what data type to store population, should be a float format. This value is passed to np.array.
|
299
|
-
# Default is 'float64', use lower accuracy to reduce data size.
|
300
|
-
self.pi_dtype = pi_dtype # what data type to store payoff, should be a float format. This value is passed to np.array.
|
301
|
-
# Default is 'float64'
|
302
|
-
|
303
|
-
self.init_storage() # initialize storage bins. Put in a separate function because might want to change maxtime
|
304
|
-
# and that doesn't need to initialze the whole object again
|
305
|
-
|
306
|
-
|
307
|
-
def init_storage(self):
|
308
|
-
# initialize storage bins
|
309
|
-
self.data_empty = True # whether data storage bins are empty. model.run will refuse to run (raise error) if not empty.
|
310
|
-
self.max_record = int(self.maxtime / self.record_itv) # int, how many data points to store sin total
|
311
|
-
self.compress_itv = 1 # int, intended to reduce size of data (if not 1). Updated by compress_data function
|
312
|
-
# if set to an int, say 20, mod will take average over every 20 data points and save them as new data.
|
313
|
-
# May be used over and over again to recursively reduce data size.
|
314
|
-
# Default is 1, not to take average.
|
315
|
-
self.U = np.zeros((self.N, self.M, self.max_record), dtype = self.UV_dtype) # N x M x max_record np.array, float32, stores population of U in every patch over tiem
|
316
|
-
self.V = np.zeros((self.N, self.M, self.max_record), dtype = self.UV_dtype)
|
317
|
-
self.Upi = np.zeros((self.N, self.M, self.max_record), dtype = self.pi_dtype) # similar to U, but for U's payoff and float 64
|
318
|
-
self.Vpi = np.zeros((self.N, self.M, self.max_record), dtype = self.pi_dtype)
|
319
|
-
|
320
|
-
|
321
|
-
def check_valid_input(self, N, M, maxtime, record_itv, sim_time, boundary, I, X, P, print_pct):
|
322
|
-
# check whether the inputs are valid
|
323
|
-
# seed, UV_dtype, pi_dtype is handled by numpy
|
324
|
-
|
325
|
-
if (N < 1) or (M < 1):
|
326
|
-
raise ValueError('N < 1 or M < 1')
|
327
|
-
if (N == 1) and (M == 1):
|
328
|
-
raise ValueError('Model fails for 1x1 space')
|
329
|
-
if (M == 1):
|
330
|
-
raise ValueError('Please set N = 1 for 1D space.')
|
331
|
-
if maxtime <= 0:
|
332
|
-
raise ValueError('Please set a positive number for maxtime')
|
333
|
-
if record_itv <= 0:
|
334
|
-
raise ValueError('Please set a positive number for record_itv')
|
335
|
-
if sim_time <= 0:
|
336
|
-
raise ValueError('Please set a positive number for sim_time')
|
337
|
-
if type(boundary) != bool:
|
338
|
-
raise TypeError('boundary not a bool. Please use True for zero-flux (with boundary) or False for periodical (no boundary)')
|
339
|
-
|
340
|
-
if (type(I) != list) and (type(I) != np.ndarray):
|
341
|
-
raise TypeError('Please set I as a list or np.ndarray')
|
342
|
-
if np.array(I).shape != (N, M, 2):
|
343
|
-
raise ValueError('Please set I as a N x M x 2 shape list or array. 2 is for init values of U, V at every patch')
|
344
|
-
|
345
|
-
if (type(X) != list) and (type(X) != np.ndarray):
|
346
|
-
raise TypeError('Please set X as a list or np.ndarray')
|
347
|
-
if np.array(X).shape != (N, M, 4):
|
348
|
-
raise ValueError('Please set X as a N x M x 4 shape list or array. 4 is for the flattened 2x2 payoff matrix')
|
349
|
-
|
350
|
-
if (type(P) != list) and (type(P) != np.ndarray):
|
351
|
-
raise TypeError('Please set P as a list or np.ndarray')
|
352
|
-
if np.array(P).shape != (N, M, 6):
|
353
|
-
raise ValueError('Please set P as a N x M x 6 shape list or array. 6 is for mu1, mu2, w1, w2, kappa1, kappa2')
|
354
|
-
|
355
|
-
if print_pct <= 0:
|
356
|
-
raise ValueError('Please use an int > 0 for print_pct or None for not printing progress.')
|
357
|
-
|
358
|
-
|
359
|
-
def check_valid_data(self, data_empty, max_record, compress_itv):
|
360
|
-
# check whether a set of data is valid, used when reading a saved model
|
361
|
-
if type(data_empty) != bool:
|
362
|
-
raise TypeError('data_empty not a bool')
|
363
|
-
|
364
|
-
if type(max_record) != int:
|
365
|
-
raise TypeError('max_record not an int')
|
366
|
-
if max_record < 0:
|
367
|
-
raise ValueError('max_record < 0')
|
368
|
-
|
369
|
-
if type(compress_itv) != int:
|
370
|
-
raise TypeError('compress_itv not an int')
|
371
|
-
if compress_itv < 0:
|
372
|
-
raise ValueError('compress_itv < 0')
|
373
|
-
|
374
|
-
|
375
|
-
def __str__(self):
|
376
|
-
# print this mod in a nice format
|
377
|
-
|
378
|
-
self_str = ''
|
379
|
-
self_str += 'N = ' + str(self.N) + '\n'
|
380
|
-
self_str += 'M = ' + str(self.M) + '\n'
|
381
|
-
self_str += 'maxtime = ' + str(self.maxtime) + '\n'
|
382
|
-
self_str += 'record_itv = ' + str(self.record_itv) + '\n'
|
383
|
-
self_str += 'sim_time = ' + str(self.sim_time) + '\n'
|
384
|
-
self_str += 'boundary = ' + str(self.boundary) + '\n'
|
385
|
-
self_str += 'print_pct = ' + str(self.print_pct) + '\n'
|
386
|
-
self_str += 'seed = ' + str(self.seed) + '\n'
|
387
|
-
self_str += 'UV_dtype = \'' + self.UV_dtype + '\'\n'
|
388
|
-
self_str += 'pi_dtype = \'' + self.pi_dtype + '\'\n'
|
389
|
-
self_str += 'compress_itv = ' + str(self.compress_itv) + '\n'
|
390
|
-
self_str += '\n'
|
391
|
-
|
392
|
-
# check whether I, X, P all same (compare all patches to (0, 0))
|
393
|
-
I_same = True
|
394
|
-
X_same = True
|
395
|
-
P_same = True
|
396
|
-
for i in range(self.N):
|
397
|
-
for j in range(self.M):
|
398
|
-
for k in range(2):
|
399
|
-
if self.I[i][j][k] != self.I[0][0][k]:
|
400
|
-
I_same = False
|
401
|
-
for k in range(4):
|
402
|
-
if self.X[i][j][k] != self.X[0][0][k]:
|
403
|
-
X_same = False
|
404
|
-
for k in range(6):
|
405
|
-
if self.P[i][j][k] != self.P[0][0][k]:
|
406
|
-
P_same = False
|
407
|
-
|
408
|
-
if I_same:
|
409
|
-
self_str += 'I all same: ' + str(self.I[0][0]) + '\n'
|
410
|
-
else:
|
411
|
-
self_str += 'I:\n'
|
412
|
-
for i in range(self.N):
|
413
|
-
for j in range(self.M):
|
414
|
-
self_str += str(self.I[i][j]) + ' '
|
415
|
-
self_str += '\n'
|
416
|
-
self_str += '\n'
|
417
|
-
|
418
|
-
if X_same:
|
419
|
-
self_str += 'X all same: ' + str(self.X[0][0]) + '\n'
|
420
|
-
else:
|
421
|
-
self_str += 'X:\n'
|
422
|
-
for i in range(self.N):
|
423
|
-
for j in range(self.M):
|
424
|
-
self_str += str(self.X[i][j]) + ' '
|
425
|
-
self_str += '\n'
|
426
|
-
self_str += '\n'
|
427
|
-
|
428
|
-
if P_same:
|
429
|
-
self_str += 'P all same: ' + str(self.P[0][0]) + '\n'
|
430
|
-
else:
|
431
|
-
self_str += 'P:\n'
|
432
|
-
for i in range(self.N):
|
433
|
-
for j in range(self.M):
|
434
|
-
self_str += str(self.P[i][j]) + ' '
|
435
|
-
self_str += '\n'
|
436
|
-
|
437
|
-
return self_str
|
438
|
-
|
439
|
-
|
440
|
-
def copy(self, copy_data = True):
|
441
|
-
# return deep copy of self
|
442
|
-
# copy_data decides whether to copy data as well
|
443
|
-
if type(copy_data) != bool:
|
444
|
-
raise TypeError('Please give a bool as argument: whether to copy data or not')
|
445
|
-
|
446
|
-
sim2 = model(N = self.N, M = self.M, maxtime = self.maxtime, record_itv = self.record_itv, sim_time = self.sim_time, boundary = self.boundary,
|
447
|
-
I = np.copy(self.I), X = np.copy(self.X), P = np.copy(self.P),
|
448
|
-
print_pct = self.print_pct, seed = self.seed, UV_dtype = self.UV_dtype, pi_dtype = self.pi_dtype)
|
449
|
-
|
450
|
-
if copy_data:
|
451
|
-
# copy data as well
|
452
|
-
sim2.set_data(self.data_empty, self.max_record, self.compress_itv, self.U, self.V, self.Upi, self.Vpi)
|
453
|
-
|
454
|
-
return sim2
|
455
|
-
|
456
|
-
|
457
|
-
def calculate_ave(self):
|
458
|
-
# get the average value over sim_time many models
|
459
|
-
if self.sim_time != 1:
|
460
|
-
for i in range(self.N):
|
461
|
-
for j in range(self.M):
|
462
|
-
for t in range(self.max_record):
|
463
|
-
self.U[i][j][t] /= self.sim_time
|
464
|
-
self.V[i][j][t] /= self.sim_time
|
465
|
-
self.Upi[i][j][t] /= self.sim_time
|
466
|
-
self.Vpi[i][j][t] /= self.sim_time
|
467
|
-
|
468
|
-
|
469
|
-
def change_maxtime(self, maxtime):
|
470
|
-
# change maxtime
|
471
|
-
if (type(maxtime) != float) and (type(maxtime) != int):
|
472
|
-
raise TypeError('Please pass in a float or int as the new maxtime.')
|
473
|
-
if maxtime <= 0:
|
474
|
-
raise ValueError('Please use a positive maxtime.')
|
475
|
-
self.maxtime = maxtime
|
476
|
-
self.init_storage()
|
477
|
-
|
478
|
-
|
479
|
-
def set_seed(self, seed):
|
480
|
-
# set seed
|
481
|
-
self.seed = seed
|
482
|
-
|
483
|
-
|
484
|
-
def clear_data(self):
|
485
|
-
# clear all data stored, set all to 0
|
486
|
-
self.init_storage()
|
487
|
-
|
488
|
-
|
489
|
-
def set_data(self, data_empty, max_record, compress_itv, U, V, Upi, V_pi):
|
490
|
-
# set data to the given data values
|
491
|
-
# copies are made
|
492
|
-
self.check_valid_data(data_empty, max_record, compress_itv)
|
493
|
-
|
494
|
-
self.data_empty = data_empty
|
495
|
-
self.max_record = max_record
|
496
|
-
self.compress_itv = compress_itv
|
497
|
-
self.U = np.copy(U)
|
498
|
-
self.V = np.copy(V)
|
499
|
-
self.Upi = np.copy(Upi)
|
500
|
-
self.Vpi = np.copy(V_pi)
|
501
|
-
|
502
|
-
|
503
|
-
def compress_data(self, compress_itv = 5):
|
504
|
-
# compress data by only storing average values
|
505
|
-
|
506
|
-
if type(compress_itv) != int:
|
507
|
-
raise TypeError('Please use an int as compress_itv')
|
508
|
-
if compress_itv < 1:
|
509
|
-
raise ValueError('Please use record_itv >= 1')
|
510
|
-
if compress_itv == 1:
|
511
|
-
return
|
512
|
-
|
513
|
-
self.compress_itv *= compress_itv # may be reduced over and over again
|
514
|
-
self.max_record = int(self.max_record / compress_itv) # number of data points after reducing
|
515
|
-
|
516
|
-
U_reduced = np.zeros((self.N, self.M, self.max_record), dtype = self.UV_dtype)
|
517
|
-
V_reduced = np.zeros((self.N, self.M, self.max_record), dtype = self.UV_dtype)
|
518
|
-
Upi_reduced = np.zeros((self.N, self.M, self.max_record), dtype = self.pi_dtype)
|
519
|
-
V_pi_reduced = np.zeros((self.N, self.M, self.max_record), dtype = self.pi_dtype)
|
520
|
-
|
521
|
-
for i in range(self.N):
|
522
|
-
for j in range(self.M):
|
523
|
-
for k in range(self.max_record):
|
524
|
-
lower = k * compress_itv # upper and lower bound of current record_itv
|
525
|
-
upper = lower + compress_itv
|
526
|
-
U_reduced[i][j][k] = np.mean(self.U[i, j, lower : upper])
|
527
|
-
V_reduced[i][j][k] = np.mean(self.V[i, j, lower : upper])
|
528
|
-
Upi_reduced[i][j][k] = np.mean(self.Upi[i, j, lower : upper])
|
529
|
-
V_pi_reduced[i][j][k] = np.mean(self.Vpi[i, j, lower : upper])
|
530
|
-
|
531
|
-
self.U = U_reduced
|
532
|
-
self.V = V_reduced
|
533
|
-
self.Upi = Upi_reduced
|
534
|
-
self.Vpi = V_pi_reduced
|
535
|
-
|
536
221
|
|
537
222
|
|
538
223
|
|
@@ -824,8 +509,8 @@ def single_init(mod, rng):
|
|
824
509
|
#### Initialize Data Storage ####
|
825
510
|
|
826
511
|
world = [[patch(mod.I[i][j][0], mod.I[i][j][1], mod.X[i][j], mod.P[i][j]) for j in range(mod.M)] for i in range(mod.N)] # N x M patches
|
827
|
-
patch_rates = np.zeros((mod.N, mod.M), dtype =
|
828
|
-
sum_rates_by_row = np.zeros((mod.N), dtype =
|
512
|
+
patch_rates = np.zeros((mod.N, mod.M), dtype = np.float64) # every patch's sum-of-12-srates
|
513
|
+
sum_rates_by_row = np.zeros((mod.N), dtype = np.float64) # every row's sum-of-patch, i.e., sum of 12 * M rates in every row.
|
829
514
|
sum_rates = 0 # sum of all N x M x 12 rates
|
830
515
|
|
831
516
|
signal = None
|
@@ -1058,7 +743,7 @@ def single_test(mod, front_info, end_info, update_sum_frequency, rng):
|
|
1058
743
|
|
1059
744
|
|
1060
745
|
|
1061
|
-
def
|
746
|
+
def run_py(mod, predict_runtime = False, message = ''):
|
1062
747
|
'''
|
1063
748
|
Main function. Recursively calls single_test to run many models and then takes the average.
|
1064
749
|
|
@@ -1120,36 +805,4 @@ def run(mod, predict_runtime = False, message = ''):
|
|
1120
805
|
|
1121
806
|
|
1122
807
|
|
1123
|
-
def demo_model():
|
1124
|
-
'''
|
1125
|
-
Returns a demo model.model object
|
1126
|
-
'''
|
1127
|
-
|
1128
|
-
N = 10 # Number of rows
|
1129
|
-
M = 10 # Number of cols
|
1130
|
-
maxtime = 300 # how long you want the model to run
|
1131
|
-
record_itv = 0.1 # how often to record data.
|
1132
|
-
sim_time = 1 # repeat model to reduce randomness
|
1133
|
-
boundary = True # boundary condition.
|
1134
|
-
|
1135
|
-
# initial population for the N x M patches.
|
1136
|
-
I = [[[44, 22] for _ in range(M)] for _ in range(N)]
|
1137
|
-
|
1138
|
-
# flattened payoff matrices, total resource is 0.4, cost of fighting is 0.1
|
1139
|
-
X = [[[-0.1, 0.4, 0, 0.2] for _ in range(M)] for _ in range(N)]
|
1140
|
-
|
1141
|
-
# patch variables
|
1142
|
-
P = [[[0.5, 0.5, 200, 200, 0.001, 0.001] for _ in range(M)] for _ in range(N)]
|
1143
|
-
|
1144
|
-
print_pct = 5 # print progress
|
1145
|
-
seed = 36 # seed for random number generation
|
1146
|
-
UV_dtype = 'float32' # data type for population
|
1147
|
-
pi_dyna = 'float64' # data type for payoff
|
1148
|
-
|
1149
|
-
# create a model object
|
1150
|
-
mod = model(N, M, maxtime, record_itv, sim_time, boundary, I, X, P,
|
1151
|
-
print_pct = print_pct, seed = seed, UV_dtype = UV_dtype, pi_dtype = pi_dyna)
|
1152
|
-
|
1153
|
-
return mod
|
1154
|
-
|
1155
808
|
|
@@ -88,7 +88,7 @@ def test_var1(mod, var, values, dirs, compress_itv = None, predict_runtime = Fal
|
|
88
88
|
sim2.P[i][j][PATCH_VAR_DICT[var]] = values[k]
|
89
89
|
|
90
90
|
try:
|
91
|
-
simulation.run(sim2,
|
91
|
+
simulation.run(sim2, message = current_var_str + ', ')
|
92
92
|
if compress_itv != None:
|
93
93
|
sim2.compress_data(compress_itv)
|
94
94
|
data_t.save_data(sim2, var_dirs[k], print_msg = False)
|
@@ -135,7 +135,7 @@ def test_var2(mod, var1, var2, values1, values2, dirs, compress_itv = None, pred
|
|
135
135
|
sim2.P[i][j][PATCH_VAR_DICT[var2]] = values2[k2]
|
136
136
|
|
137
137
|
try:
|
138
|
-
simulation.run(sim2,
|
138
|
+
simulation.run(sim2, message = current_var_str + ', ')
|
139
139
|
if compress_itv != None:
|
140
140
|
sim2.compress_data(compress_itv)
|
141
141
|
data_t.save_data(sim2, var_dirs[k1][k2], print_msg = False)
|
@@ -8,6 +8,7 @@ src/piegy/analysis.py
|
|
8
8
|
src/piegy/data_tools.py
|
9
9
|
src/piegy/figures.py
|
10
10
|
src/piegy/simulation.py
|
11
|
+
src/piegy/simulation_py.py
|
11
12
|
src/piegy/test_var.py
|
12
13
|
src/piegy/videos.py
|
13
14
|
src/piegy.egg-info/PKG-INFO
|
@@ -15,6 +16,7 @@ src/piegy.egg-info/SOURCES.txt
|
|
15
16
|
src/piegy.egg-info/dependency_links.txt
|
16
17
|
src/piegy.egg-info/requires.txt
|
17
18
|
src/piegy.egg-info/top_level.txt
|
19
|
+
src/piegy/C_core/piegyc.so
|
18
20
|
src/piegy/tools/__init__.py
|
19
21
|
src/piegy/tools/figure_tools.py
|
20
22
|
src/piegy/tools/file_tools.py
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|