piegy 1.0.0__py3-none-any.whl

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/test_var.py ADDED
@@ -0,0 +1,525 @@
1
+ '''
2
+ Test how certain variables influence simulation result.
3
+
4
+ Public Functions:
5
+
6
+ Test influence of one variable:
7
+ - test_var1: Test how a certain patch variable (mu, w, kappa) influences results.
8
+ Run simulations across different values of that variable and save data.
9
+ - var_UV1: Plot how U, V change across different values of a specified variable.
10
+ - var_pi1: Plot how U_pi, V_pi change across different values of a specified variable.
11
+
12
+
13
+ Test influence of two variables:
14
+ - test_var2: Test how two patch variables influence results.
15
+ Run simulations across different values of the two specified variables and save data.
16
+ - var_UV2: Plot how U, V change across the two variables.
17
+ x-axis is values of the variable 2, y-axis is U or V. Values of variable 1 are shown by multiple curves.
18
+ - var_pi2: Plot how U_pi & V_pi change across the two variables.
19
+
20
+
21
+ Additional tools (can be called directly):
22
+ - get_dirs1: test_var1 returns directories in a certain format based on var and values. This function mimics that format and
23
+ returns a 1D list of directories, where all data are stored.
24
+ This 1D list is intended to pass to var_UV1 and var_pi1 directly.
25
+
26
+ - var_convergence1: Find the simulatoin results of test_var1 that diverge.
27
+
28
+ - get_dirs2: test_var2 returns directories in a certain format based on vars and values. This function mimics that format and
29
+ returns a 2D list of directories, where all data are stored.
30
+ This 2D list is intended to pass to var_UV2 and var_pi2 directly.
31
+ - var_convergence2: Find the simulatoin results of test_var2 that diverge.
32
+ '''
33
+
34
+ from . import model as model
35
+ from .tools import figure_tools as figure_t
36
+ from . import analysis as analysis
37
+ from . import data_tools as data_t
38
+
39
+ import matplotlib.pyplot as plt
40
+ import numpy as np
41
+
42
+
43
+
44
+ # curve type used by var_UV, var2_UV, var_pi, var2_pi
45
+ # can be 'o-', 'x-', ...
46
+ DOTTED_CURVE_TYPE = 'o-'
47
+
48
+ # map patch_var name to index in the patch class (in stochastic_model.py)
49
+ PATCH_VAR_DICT = {'mu1': 0, 'mu2': 1, 'w1': 2, 'w2': 3, 'kappa1': 4, 'kappa2': 5}
50
+
51
+
52
+
53
+ def test_var1(sim, var, values, dirs, compress_itv = None, predict_runtime = False):
54
+ '''
55
+ Test the influence of one patch variable on simulation results.
56
+
57
+ Inputs::
58
+ - sim: a simulation object. All tests will use parameters of sim, except for the variable to test.
59
+ - var: str, which patch variable to test. e.g. var can be 'mu1', 'w2', 'kappa2', ...
60
+ - values: 1D np.array or list, what values of var to test.
61
+ - dirs: str, where to save data.
62
+ - compress_itv: int, whether to reduce data size (if not 1), passed to model.simulation.compress_data function.
63
+ - predict_runtime: bool, whether to predict how much time left for each test.
64
+
65
+ Returns::
66
+ - var_dirs: a 1D list of directories (str) where all the data are stored.
67
+ Directories have form 'mu1=0.1'.
68
+ NOTE: if you accidentally lost this return value, you can retrieve it by get_dirs1.
69
+ '''
70
+
71
+ if var not in PATCH_VAR_DICT.keys():
72
+ raise ValueError('Please enter a valid patch variable: mu1, mu2, w1, w2, kappa1, or kappa2')
73
+
74
+ # var_dirs is where data will be stored
75
+ var_dirs = []
76
+
77
+ for k in range(len(values)):
78
+ sim2 = sim.copy(copy_data = False)
79
+ current_var_str = var + '=' + str(values[k]) # e.g., 'mu1=0.1'
80
+ var_dirs.append(dirs + '/' + current_var_str)
81
+
82
+ for i in range(sim.N):
83
+ for j in range(sim.M):
84
+ sim2.P[i][j][PATCH_VAR_DICT[var]] = values[k]
85
+
86
+ try:
87
+ model.run(sim2, predict_runtime, message = current_var_str + ', ')
88
+ if compress_itv != None:
89
+ sim2.compress_data(compress_itv)
90
+ data_t.save_data(sim2, var_dirs[k], print_msg = False)
91
+ except OverflowError:
92
+ print(current_var_str + ' raised OverflowError, skipped')
93
+ continue
94
+
95
+ return var_dirs
96
+
97
+
98
+
99
+ def test_var2(sim, var1, var2, values1, values2, dirs, compress_itv = None, predict_runtime = False):
100
+ '''
101
+ Two-variable version of test_var1. Test the influence of two varibles on simulation results.
102
+
103
+ Inputs:
104
+ - sim: a simulation object. All tests will use the parameters of sim, except for the two vars to be tested.
105
+ - var1: str, the first variable to test.
106
+ - var2: str, the second variable to test.
107
+ - values1: 1D list or np.array, values for var1.
108
+ - values2: 1D list or np.array, values for var2.
109
+ - dirs, compress_itv, scale_maxtime, predict_runtime: same as in test_var1
110
+
111
+ Returns:
112
+ - var_dirs: 2D list of directories, where all the data are stored.
113
+ Directories have form 'mu1=0.1, mu2=0.2'.
114
+ NOTE: if you accidentally lost this return value, you can retrieve by get_dirs2.
115
+ '''
116
+
117
+ if (var1 not in PATCH_VAR_DICT.keys()) or (var2 not in PATCH_VAR_DICT.keys()):
118
+ raise ValueError('Please enter a valid patch variable: mu1, mu2, w1, w2, kappa1, or kappa2')
119
+
120
+ var_dirs = [[] for k1 in range(len(values1))]
121
+
122
+ for k1 in range(len(values1)):
123
+ for k2 in range(len(values2)):
124
+ sim2 = sim.copy(copy_data = False)
125
+ current_var_str = var1 + '=' + str(values1[k1]) + ', ' + var2 + '=' + str(values2[k2]) # e.g., mu1=0.1, mu2=0.2
126
+ var_dirs[k1].append(dirs + '/' + current_var_str)
127
+
128
+ for i in range(sim.N):
129
+ for j in range(sim.M):
130
+ sim2.P[i][j][PATCH_VAR_DICT[var1]] = values1[k1]
131
+ sim2.P[i][j][PATCH_VAR_DICT[var2]] = values2[k2]
132
+
133
+ try:
134
+ model.run(sim2, predict_runtime, message = current_var_str + ', ')
135
+ if compress_itv != None:
136
+ sim2.compress_data(compress_itv)
137
+ data_t.save_data(sim2, var_dirs[k1][k2], print_msg = False)
138
+ except OverflowError:
139
+ print(current_var_str + ' raise OverflowError, skipped')
140
+ continue
141
+
142
+ return var_dirs
143
+
144
+
145
+
146
+
147
+ def var_UV1(var, values, var_dirs, start = 0.95, end = 1.0, U_color = 'purple', V_color = 'green'):
148
+ '''
149
+ Plot function for test_var1, plot how var influences U, V population.
150
+ Make U, V - var curve in two figures, with y-axis being total population at the end of simulations, x-axis being var's values.
151
+
152
+ Inputs:
153
+ - var: str, which variable was tested.
154
+ - values: 1D list or np.array, which values were tested.
155
+ - var_dirs: return value of test_var1, a 1D list of directories where all data are stored.
156
+ - start, end: floats, give an interval of time over which to take average and make plot.
157
+ For example, start = 0.95, end = 1.0 are to take average over the last 5% time of results;
158
+ essentially plots how var influences equilibrium population.
159
+
160
+ Returns:
161
+ - fig1, fig2: two figures for U, V, respectively.
162
+ '''
163
+
164
+ # average value of U, V over the interval. One entry for one value of var
165
+ U_ave = []
166
+ V_ave = []
167
+
168
+ for k in range(len(var_dirs)):
169
+ try:
170
+ simk = data_t.read_data(var_dirs[k])
171
+ except FileNotFoundError:
172
+ print(var + '=' + str(values[k]) + ' not found, skipped')
173
+ U_ave.append(None)
174
+ V_ave.append(None)
175
+ continue
176
+ start_index = int(simk.max_record * start)
177
+ end_index = int(simk.max_record * end)
178
+ NM = int(simk.N * simk.M)
179
+
180
+ U_ave.append(sum(figure_t.ave_interval_1D(simk.U, start_index, end_index)) / NM)
181
+ V_ave.append(sum(figure_t.ave_interval_1D(simk.V, start_index, end_index)) / NM)
182
+
183
+ #### plot ####
184
+
185
+ fig1, ax1 = plt.subplots()
186
+ ax1.set_xlabel(var)
187
+ ax1.set_ylabel('U')
188
+ ax1.plot(values, U_ave, DOTTED_CURVE_TYPE, color = U_color)
189
+ ax1.title.set_text(figure_t.gen_title(var + ' - U', start, end))
190
+
191
+ fig2, ax2 = plt.subplots()
192
+ ax2.set_xlabel(var)
193
+ ax2.set_ylabel('V')
194
+ ax2.plot(values, V_ave, DOTTED_CURVE_TYPE, color = V_color)
195
+ ax2.title.set_text(figure_t.gen_title(var + ' - V', start, end))
196
+
197
+ return fig1, fig2
198
+
199
+
200
+
201
+
202
+ def var_UV2(var1, var2, values1, values2, var_dirs, start = 0.95, end = 1.0, U_color = 'viridis', V_color = 'viridis', rgb_alpha = 1):
203
+ '''
204
+ Plot function for test_var2, plot how two variables influence U, V population.
205
+ Make U, V - var1, var2 curves. y-axis is population, x-axis is var2's values,
206
+ and var1's values are represented by different curves, one curve corresponds to one value of var1.
207
+
208
+ Inputs:
209
+ - var1: str, the first variable tested.
210
+ - var2: str, the second variable tested.
211
+ - values1: 1D list or np.array, values for var1.
212
+ - values2: 1D list or np.array, values for var2.
213
+ - var_dirs: return value of test_var2, a 2D list of directories where all data are stored.
214
+ - start, end: floats, give an interval of time over which to take average and make plot.
215
+ For example, start = 0.95, end = 1.0 plots the near-end population (equilibrium, if converged).
216
+ - color: str, what colors to use for different value of var1. Uses Matplotlib color maps.
217
+ See available color maps at: https://matplotlib.org/stable/users/explain/colors/colormaps.html
218
+ - rgb_alpha: the alpha value for color. Thr curves might have overlaps, recommend set a small alpha value if so.
219
+
220
+ Returns:
221
+ - fig1, fig2: figures for U, V, respectively.
222
+ '''
223
+
224
+ # average value of U, V over the interval. One entry for one values of var1, var2
225
+ U_ave = [[] for k1 in range(len(var_dirs))]
226
+ V_ave = [[] for k1 in range(len(var_dirs))]
227
+
228
+ for k1 in range(len(var_dirs)):
229
+ for k2 in range(len(var_dirs[k1])):
230
+ try:
231
+ simk = data_t.read_data(var_dirs[k1][k2])
232
+ except FileNotFoundError:
233
+ print(var1 + '=' + str(values1[k1]) + ', ' + var2 + '=' + str(values2[k2]) + ' not found, skipped')
234
+ U_ave[k1].append(None)
235
+ V_ave[k1].append(None)
236
+ continue
237
+ start_index = int(simk.max_record * start)
238
+ end_index = int(simk.max_record * end)
239
+ NM = int(simk.N * simk.M)
240
+
241
+ U_ave[k1].append(sum(figure_t.ave_interval_1D(simk.U, start_index, end_index)) / NM)
242
+ V_ave[k1].append(sum(figure_t.ave_interval_1D(simk.V, start_index, end_index)) / NM)
243
+
244
+ #### plot ####
245
+
246
+ fig1, ax1 = plt.subplots()
247
+ ax1.set_xlabel(var2)
248
+ ax1.set_ylabel('U')
249
+ cmap1 = plt.get_cmap(U_color)
250
+
251
+ for k1 in range(len(values1)):
252
+ label = var1 + '=' + str(values1[k1])
253
+ color_k1 = cmap1(k1 / len(values1))[:3] + (rgb_alpha,)
254
+ ax1.plot(values2, U_ave[k1], DOTTED_CURVE_TYPE, label = label, color = color_k1)
255
+ ax1.title.set_text(figure_t.gen_title(var1 + '&' + var2 + ' - U', start, end))
256
+ ax1.legend(bbox_to_anchor = (1, 1))
257
+
258
+ fig2, ax2 = plt.subplots()
259
+ ax2.set_xlabel(var2)
260
+ ax2.set_ylabel('V')
261
+ cmap2 = plt.get_cmap(V_color)
262
+
263
+ for k1 in range(len(values1)):
264
+ label = var1 + '=' + str(values1[k1])
265
+ color_k1 = cmap2(k1 / len(values1))[:3] + (rgb_alpha,)
266
+ ax2.plot(values2, V_ave[k1], DOTTED_CURVE_TYPE, label = label, color = color_k1)
267
+ ax2.title.set_text(figure_t.gen_title(var1 + '&' + var2 + ' - V', start, end))
268
+ ax2.legend(bbox_to_anchor = (1.2, 1))
269
+
270
+ return fig1, fig2
271
+
272
+
273
+
274
+ def var_pi1(var, values, var_dirs, start = 0.95, end = 1.0, U_color = 'violet', V_color = 'yellowgreen'):
275
+ '''
276
+ Plot function for test_var1. Plot influence of var on U, V's payoff.
277
+ Make U_pi, V_pi - var curves. y-axis is payoff, x-axis is values of var.
278
+
279
+ Inputs:
280
+ - var_dirs: return value of test_var1. A 1D list of directories where all data are stored.
281
+ - var: str, which variable as tested.
282
+ - values: 1D list or np.array, what values were used.
283
+ - start, end: floats, define an interval of time over which to calculate average payoff and make plots.
284
+
285
+ Returns:
286
+ - fig1, fig2: figures for U, V's payoff, respectively.
287
+ '''
288
+
289
+ # take average value of payoff over an interval of time
290
+ U_ave = []
291
+ V_ave = []
292
+
293
+ for k in range(len(var_dirs)):
294
+ try:
295
+ simk = data_t.read_data(var_dirs[k])
296
+ except FileNotFoundError:
297
+ print(var + '=' + str(values[k]) + ' not found, skipped')
298
+ U_ave.append(None)
299
+ V_ave.append(None)
300
+ continue
301
+ start_index = int(simk.max_record * start)
302
+ end_index = int(simk.max_record * end)
303
+ NM = int(simk.N * simk.M)
304
+
305
+ U_ave.append(np.sum(figure_t.ave_interval(simk.U_pi, start_index, end_index)) / NM)
306
+ V_ave.append(np.sum(figure_t.ave_interval(simk.V_pi, start_index, end_index)) / NM)
307
+
308
+ del simk
309
+
310
+ #### plot ####
311
+
312
+ fig1, ax1 = plt.subplots()
313
+ ax1.set_xlabel(var)
314
+ ax1.set_ylabel('U_pi')
315
+ ax1.plot(values, U_ave, DOTTED_CURVE_TYPE, color = U_color)
316
+ ax1.title.set_text(figure_t.gen_title(var + ' - U_pi', start, end))
317
+
318
+ fig2, ax2 = plt.subplots()
319
+ ax2.set_xlabel(var)
320
+ ax2.set_ylabel('V_pi')
321
+ ax2.plot(values, V_ave, DOTTED_CURVE_TYPE, color = V_color)
322
+ ax2.title.set_text(figure_t.gen_title(var + ' - V_pi', start, end))
323
+
324
+ return fig1, fig2
325
+
326
+
327
+
328
+ def var_pi2(var1, var2, values1, values2, var_dirs, start = 0.95, end = 1.0, U_color = 'viridis', V_color = 'viridis', rgb_alpha = 1):
329
+ '''
330
+ Plot function for test_var2. Plot how var1 and var2 influence payoff.
331
+ Make U_pi, V_pi - var2 curves. y-axis is payoff, x-axis is values of var2,
332
+ var1 is represented by different curves. One curve corresponds to one value of var1.
333
+
334
+ Inputs:
335
+ - var_dirs: return value of test_var2. A 2D list of directories where all data are stored.
336
+ - var1, var2: str, what variables were tested.
337
+ - values1, values2: 1D lists or np.array, what values were tested.
338
+ - start, end: floats, define a time inteval over which to make average and make plots.
339
+ - color: str, Matplotlib color maps.
340
+ - rgb_alpha: set alpha value for curves.
341
+
342
+ Returns:
343
+ - fig1, fig2: U, V's payoff figures, respectively.
344
+ '''
345
+
346
+ # take average value of payoff over an interval of time
347
+ U_ave = [[] for k1 in range(len(var_dirs))]
348
+ V_ave = [[] for k1 in range(len(var_dirs))]
349
+
350
+ for k1 in range(len(var_dirs)):
351
+ for k2 in range(len(var_dirs[k1])):
352
+ try:
353
+ simk = data_t.read_data(var_dirs[k1][k2])
354
+ except FileNotFoundError:
355
+ print(var1 + '=' + str(values1[k1]) + ', ' + var2 + '=' + str(values2[k2]) + ' not found, skipped')
356
+ U_ave[k1].append(None)
357
+ V_ave[k1].append(None)
358
+ continue
359
+ start_index = int(simk.max_record * start)
360
+ end_index = int(simk.max_record * end)
361
+ NM = int(simk.N * simk.M)
362
+
363
+ U_ave[k1].append(np.sum(figure_t.ave_interval(simk.U_pi, start_index, end_index)) / NM)
364
+ V_ave[k1].append(np.sum(figure_t.ave_interval(simk.V_pi, start_index, end_index)) / NM)
365
+
366
+ del simk # manually delete this large object
367
+
368
+ #### plot ####
369
+
370
+ fig1, ax1 = plt.subplots()
371
+ ax1.set_xlabel(var2)
372
+ ax1.set_ylabel('U_pi')
373
+ cmap1 = plt.get_cmap(U_color)
374
+
375
+ for k1 in range(len(var_dirs)):
376
+ label = var1 + '=' + str(values1[k1])
377
+ color_k1 = cmap1(k1 / len(values1))[:3] + (rgb_alpha,)
378
+ ax1.plot(values2, U_ave[k1], DOTTED_CURVE_TYPE, label = label, color = color_k1)
379
+ ax1.title.set_text(figure_t.gen_title(var1 + '&' + var2 + ' - U_pi', start, end))
380
+ ax1.legend(bbox_to_anchor = (1, 1))
381
+
382
+ fig2, ax2 = plt.subplots()
383
+ ax2.set_xlabel(var2)
384
+ ax1.set_ylabel('V_pi')
385
+ cmap2 = plt.get_cmap(V_color)
386
+
387
+ for k1 in range(len(var_dirs)):
388
+ label = var1 + '=' + str(values1[k1])
389
+ color_k1 = cmap2(k1 / len(values1))[:3] + (rgb_alpha,)
390
+ ax2.plot(values2, V_ave[k1], DOTTED_CURVE_TYPE, label = label, color = color_k1)
391
+ ax2.title.set_text(figure_t.gen_title(var1 + '&' + var2 + ' - V_pi', start, end))
392
+ ax2.legend(bbox_to_anchor = (1.2, 1))
393
+
394
+ return fig1, fig2
395
+
396
+
397
+
398
+
399
+ def get_dirs1(var, values, dirs):
400
+ '''
401
+ Mimics the return value format of test_var1 and returns a 1D list of directories where test_var1 saved data.
402
+ Intended usage: retrieve the directories if you accidentally lost the return value of test_var1.
403
+
404
+ Inputs:
405
+ - var: what variable was tested.
406
+ - values: what values were testsed.
407
+ - dirs: the directory you passed to test_var1 as 'dirs' parameter.
408
+ Essentially the root directories where data were stored.
409
+
410
+ Returns:
411
+ - var_dirs: a 1D list of directories where data were stored. Has the same format as the return value of test_var1.
412
+ '''
413
+
414
+ var_dirs = []
415
+ values_sorted = sorted(values)
416
+ if dirs[-1] != '/':
417
+ dirs += '/'
418
+
419
+ for val in values_sorted:
420
+ var_dirs.append(dirs + var + '=' + str(val))
421
+
422
+ return var_dirs
423
+
424
+
425
+
426
+
427
+ def var_convergence1(var_dirs, interval = 20, start = 0.8, fluc = 0.07):
428
+ '''
429
+ Find the simulatoin results of test_var1 that diverge.
430
+
431
+ Inputs:
432
+ - var_dirs: Return value of test_var1
433
+ - interval: int. One of the inputs of model_analysis.check_convergence.
434
+ The size of interval to take average over.
435
+ - start: (0,1) float. One of the inputs of model_analysis.check_convergence.
436
+ Convergence is expected to start from at least this point.
437
+ - fluc: (0,1) float. One of the inputs of model_analysis.check_convergence.
438
+ Expect the difference between any two small intervals (a quotient-form difference) should be less than fluc.
439
+
440
+ Returns:
441
+ - diverge_list: A list directories where the simulation data diverge.
442
+ '''
443
+
444
+ diverge_list = []
445
+
446
+ for dirs in var_dirs:
447
+ try:
448
+ sim = data_t.read_data(dirs)
449
+ except FileNotFoundError:
450
+ print(dirs + ' data not found, skipped')
451
+ continue
452
+ if not analysis.check_convergence(sim, interval, start, fluc):
453
+ diverge_list.append(dirs)
454
+
455
+ return diverge_list
456
+
457
+
458
+
459
+
460
+ def get_dirs2(var1, var2, values1, values2, dirs):
461
+ '''
462
+ Mimics the return value format of test_var2 and returns a 2D list of directories where test_var2 saved data.
463
+ Intended usage: retrieve the directories if you accidentally lost the return value of test_var2.
464
+
465
+ Inputs:
466
+ - var1, var2: what variables were tested.
467
+ - values1, values2: what values were testsed.
468
+ - dirs: the directory you passed to test_var2 as 'dirs' parameter.
469
+ Essentially the root directories where data were stored.
470
+
471
+ Returns:
472
+ - var_dirs: a 2D list of directories where data were stored. Has the same format as the return value of test_var2.
473
+ '''
474
+
475
+ var_dirs = [[] for i in range(len(values1))]
476
+ values1_sorted = sorted(values1)
477
+ values2_sorted = sorted(values2)
478
+ if dirs[-1] != '/':
479
+ dirs += '/'
480
+
481
+ for i in range(len(values1)):
482
+ for j in range(len(values2)):
483
+ v1 = values1_sorted[i]
484
+ v2 = values2_sorted[j]
485
+ dirs_ij = dirs + var1 + '=' + str(v1) + ', ' + var2 + '=' + str(v2)
486
+ var_dirs[i].append(dirs_ij)
487
+
488
+ return var_dirs
489
+
490
+
491
+
492
+
493
+ def var_convergence2(var_dirs, interval = 20, start = 0.8, fluc = 0.07):
494
+ '''
495
+ Find the simulatoin results of test_var2 that diverge.
496
+
497
+ Inputs:
498
+ - var_dirs: Return value of test_var2
499
+ - interval: int. One of the inputs of model_analysis.check_convergence.
500
+ The size of interval to take average over.
501
+ - start: (0,1) float. One of the inputs of model_analysis.check_convergence.
502
+ Convergence is expected to start from at least this point.
503
+ - fluc: (0,1) float. One of the inputs of model_analysis.check_convergence.
504
+ Expect the difference between any two small intervals (a quotient-form difference) should be less than fluc.
505
+
506
+ Returns:
507
+ - diverge_list: A list directories where the simulation data diverge.
508
+ '''
509
+
510
+ diverge_list = []
511
+
512
+ for sublist in var_dirs:
513
+ for dirs in sublist:
514
+ try:
515
+ sim = data_t.read_data(dirs)
516
+ except FileNotFoundError:
517
+ print(dirs + ' data not found, skipped')
518
+ continue
519
+ if not analysis.check_convergence(sim, interval, start, fluc):
520
+ diverge_list.append(dirs)
521
+
522
+ return diverge_list
523
+
524
+
525
+
@@ -0,0 +1,15 @@
1
+ '''
2
+ Init file for .tools sub-package.
3
+
4
+ Contains three tool kits:
5
+ - data_tools: intended for both direct usage and private helper module.
6
+ - file_tools: private use.
7
+ - figure_tools: private use.
8
+
9
+
10
+ Last updated: Mar 27, 2025
11
+ '''
12
+
13
+ from . import file_tools
14
+ from . import figure_tools
15
+