piegy 2.3.7__tar.gz → 2.3.9__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.
Files changed (34) hide show
  1. {piegy-2.3.7/src/piegy.egg-info → piegy-2.3.9}/PKG-INFO +1 -1
  2. {piegy-2.3.7 → piegy-2.3.9}/pyproject.toml +1 -1
  3. {piegy-2.3.7 → piegy-2.3.9}/src/piegy/C_core/model.c +8 -9
  4. {piegy-2.3.7 → piegy-2.3.9}/src/piegy/C_core/patch.c +17 -14
  5. {piegy-2.3.7 → piegy-2.3.9}/src/piegy/C_core/patch.h +6 -3
  6. piegy-2.3.9/src/piegy/C_core/random.h +77 -0
  7. {piegy-2.3.7 → piegy-2.3.9}/src/piegy/C_core/sim_funcs.c +76 -73
  8. {piegy-2.3.7 → piegy-2.3.9}/src/piegy/C_core/sim_funcs.h +34 -54
  9. {piegy-2.3.7 → piegy-2.3.9}/src/piegy/__init__.py +2 -2
  10. {piegy-2.3.7 → piegy-2.3.9}/src/piegy/__version__.py +3 -1
  11. {piegy-2.3.7 → piegy-2.3.9}/src/piegy/data_tools.py +22 -13
  12. {piegy-2.3.7 → piegy-2.3.9}/src/piegy/simulation.py +7 -16
  13. {piegy-2.3.7 → piegy-2.3.9}/src/piegy/simulation_py.py +17 -9
  14. {piegy-2.3.7 → piegy-2.3.9}/src/piegy/test_var.py +8 -8
  15. {piegy-2.3.7 → piegy-2.3.9/src/piegy.egg-info}/PKG-INFO +1 -1
  16. {piegy-2.3.7 → piegy-2.3.9}/src/piegy.egg-info/SOURCES.txt +1 -0
  17. {piegy-2.3.7 → piegy-2.3.9}/LICENSE.txt +0 -0
  18. {piegy-2.3.7 → piegy-2.3.9}/MANIFEST.in +0 -0
  19. {piegy-2.3.7 → piegy-2.3.9}/README.md +0 -0
  20. {piegy-2.3.7 → piegy-2.3.9}/setup.cfg +0 -0
  21. {piegy-2.3.7 → piegy-2.3.9}/setup.py +0 -0
  22. {piegy-2.3.7 → piegy-2.3.9}/src/piegy/C_core/Makefile +0 -0
  23. {piegy-2.3.7 → piegy-2.3.9}/src/piegy/C_core/model.h +0 -0
  24. {piegy-2.3.7 → piegy-2.3.9}/src/piegy/analysis.py +0 -0
  25. {piegy-2.3.7 → piegy-2.3.9}/src/piegy/build_info.py +0 -0
  26. {piegy-2.3.7 → piegy-2.3.9}/src/piegy/figures.py +0 -0
  27. {piegy-2.3.7 → piegy-2.3.9}/src/piegy/tools/__init__.py +0 -0
  28. {piegy-2.3.7 → piegy-2.3.9}/src/piegy/tools/figure_tools.py +0 -0
  29. {piegy-2.3.7 → piegy-2.3.9}/src/piegy/tools/file_tools.py +0 -0
  30. {piegy-2.3.7 → piegy-2.3.9}/src/piegy/tools/find_C.py +0 -0
  31. {piegy-2.3.7 → piegy-2.3.9}/src/piegy/videos.py +0 -0
  32. {piegy-2.3.7 → piegy-2.3.9}/src/piegy.egg-info/dependency_links.txt +0 -0
  33. {piegy-2.3.7 → piegy-2.3.9}/src/piegy.egg-info/requires.txt +0 -0
  34. {piegy-2.3.7 → piegy-2.3.9}/src/piegy.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: piegy
3
- Version: 2.3.7
3
+ Version: 2.3.9
4
4
  Summary: Payoff-Driven Stochastic Spatial Model for Evolutionary Game Theory
5
5
  Author-email: Chenning Xu <cxu7@caltech.edu>
6
6
  License: BSD 3-Clause License
@@ -4,7 +4,7 @@ build-backend = 'setuptools.build_meta'
4
4
 
5
5
  [project]
6
6
  name = 'piegy'
7
- version = '2.3.7'
7
+ version = '2.3.9'
8
8
  description = 'Payoff-Driven Stochastic Spatial Model for Evolutionary Game Theory'
9
9
  readme = 'README.md'
10
10
  requires-python = '>=3.7'
@@ -79,17 +79,16 @@ void mod_free(model_t* mod) {
79
79
 
80
80
  void mod_free_py(model_t* mod) {
81
81
  // free function for python
82
-
82
+ // the same as mod_free except for not having free(mod)
83
83
  if (!mod) return;
84
- if (mod->I) { free(mod->I); }
85
- if (mod->X) { free(mod->X); }
86
- if (mod->P) { free(mod->P); }
87
- if (mod->U1d) { free(mod->U1d); }
88
- if (mod->V1d) { free(mod->V1d); }
89
- if (mod->Upi_1d) { free(mod->Upi_1d); }
90
- if (mod->Vpi_1d) { free(mod->Vpi_1d); }
91
- // if (mod) { free(mod); }
92
84
 
85
+ free(mod->I);
86
+ free(mod->X);
87
+ free(mod->P);
88
+ free(mod->U1d);
89
+ free(mod->V1d);
90
+ free(mod->Upi_1d);
91
+ free(mod->Vpi_1d);
93
92
  mod->I = NULL;
94
93
  mod->X = mod->P = mod->U1d = mod->V1d = mod->Upi_1d = mod->Vpi_1d = NULL;
95
94
  }
@@ -5,32 +5,35 @@
5
5
 
6
6
  #include "patch.h"
7
7
 
8
- void patch_init(patch_t* p, uint32_t U, uint32_t V, size_t row, size_t col) {
8
+ void patch_init(patch_t* p, uint32_t U, uint32_t V, size_t row, size_t col, double* X_start, double* P_start) {
9
9
  if (p == NULL) return;
10
10
  p->row = row;
11
11
  p->col = col;
12
12
 
13
13
  p->U = U;
14
14
  p->V = V;
15
- p->U_pi = 0.0;
16
- p->V_pi = 0.0;
15
+ p->U_pi = 0;
16
+ p->V_pi = 0;
17
+
18
+ memcpy(p->X, X_start, 4 * sizeof(double));
19
+ memcpy(p->P, P_start, 6 * sizeof(double));
17
20
 
18
21
  for (size_t i = 0; i < 4; i++) {
19
- p->U_weight[i] = 0.0;
20
- p->V_weight[i] = 0.0;
21
- p->pi_death_rates[i] = 0.0;
22
+ p->U_weight[i] = 0;
23
+ p->V_weight[i] = 0;
24
+ p->pi_death_rates[i] = 0;
22
25
  }
23
26
  for (size_t i = 0; i < 8; i++) {
24
- p->mig_rates[i] = 0.0;
27
+ p->mig_rates[i] = 0;
25
28
  }
26
- p->sum_U_weight = 0.0;
27
- p->sum_V_weight = 0.0;
28
- p->sum_pi_death_rates = 0.0;
29
- p->sum_mig_rates = 0.0;
29
+ p->sum_U_weight = 0;
30
+ p->sum_V_weight = 0;
31
+ p->sum_pi_death_rates = 0;
32
+ p->sum_mig_rates = 0;
30
33
  }
31
34
 
32
35
 
33
- void set_nb(patch_t* world, double* P_start, size_t* nb_start, size_t ij, size_t NM) {
36
+ void set_nb(patch_t* world, size_t* nb_start, size_t ij, size_t NM) {
34
37
  // nb_start is the where patch ij's neighbor indices start
35
38
  size_t num_nb = 0;
36
39
  for (size_t k = 0; k < 4; k++) {
@@ -42,6 +45,6 @@ void set_nb(patch_t* world, double* P_start, size_t* nb_start, size_t ij, size_t
42
45
  world[ij].nb[k] = NULL;
43
46
  }
44
47
  }
45
- P_start[0] *= (0.25 * num_nb); // scale total migration rates by how many neighbors the patch has
46
- P_start[1] *= (0.25 * num_nb);
48
+ world[ij].P[0] *= (0.25 * num_nb);
49
+ world[ij].P[1] *= (0.25 * num_nb);
47
50
  }
@@ -10,7 +10,7 @@
10
10
  #include <stdio.h>
11
11
  #include <stdint.h>
12
12
  #include <stdbool.h>
13
-
13
+ #include <string.h>
14
14
 
15
15
 
16
16
  typedef struct patch_t {
@@ -22,6 +22,9 @@ typedef struct patch_t {
22
22
  double U_pi;
23
23
  double V_pi;
24
24
 
25
+ double X[4]; // a copy of matrix and patch variables (mu, w, kappa)
26
+ double P[6];
27
+
25
28
  struct patch_t* nb[4];
26
29
  double U_weight[4]; // stores migration weight of each of the 4 neighbors
27
30
  double V_weight[4];
@@ -34,8 +37,8 @@ typedef struct patch_t {
34
37
  } patch_t;
35
38
 
36
39
  // in .c
37
- void patch_init(patch_t* p, uint32_t U, uint32_t V, size_t row, size_t col);
38
- void set_nb(patch_t* world, double* P_start, size_t* nb_start, size_t ij, size_t NM) ;
40
+ void patch_init(patch_t* p, uint32_t U, uint32_t V, size_t row, size_t col, double* X_start, double* P_start);
41
+ void set_nb(patch_t* world, size_t* nb_start, size_t ij, size_t NM) ;
39
42
 
40
43
  #endif // PATCH_H
41
44
 
@@ -0,0 +1,77 @@
1
+ /**
2
+ * The Random Number Generation module
3
+ * Returns a random double in (0, 1) with 2^-53 resolution, 0 and 1 not included
4
+ *
5
+ * First initialize xoshiro256+ states with Splitmix64
6
+ * Then use xoshiro256+ for random number generation
7
+ *
8
+ * Referrence:
9
+ * xoshiro256++ code: https://prng.di.unimi.it
10
+ * Splitmix64 code: https://rosettacode.org/wiki/Pseudo-random_numbers/Splitmix64
11
+ *
12
+ */
13
+
14
+ #include <stdint.h>
15
+
16
+
17
+ // resolution of random01 is 2^-53
18
+ // which is (about) the IEEE 754 double precision
19
+ #define MAX_53BIT (uint64_t) ((1ULL << 53) - 1)
20
+ #define RAND_DENOM 1.0 / ((double) (1ULL << 53))
21
+ // xor_state
22
+ static uint64_t xor_state[4];
23
+ static uint64_t splitmix64_state;
24
+
25
+
26
+
27
+ static inline uint64_t Splitmix64_rand() {
28
+ // used to seed PCG initial states
29
+ splitmix64_state += 0x9e3779b97f4a7c15ULL;
30
+ uint64_t z = splitmix64_state;
31
+ z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;
32
+ z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;
33
+ return z ^ (z >> 31);
34
+ }
35
+
36
+
37
+ static inline uint64_t rot23(const uint64_t x) {
38
+ // 23 and 64 - 23
39
+ // Not used here, part of xoshiro256++
40
+ return (x << 23) | (x >> 41);
41
+ }
42
+
43
+
44
+ static inline uint64_t rot45(const uint64_t x) {
45
+ // 45 and 64 - 45
46
+ return (x << 45) | (x >> 19);
47
+ }
48
+
49
+
50
+ static inline double random01() {
51
+ // return a random number in (0, 1) with 2^-53 resolution. 0 and 1 not included.
52
+
53
+ const uint64_t result = xor_state[0] + xor_state[3];
54
+ const uint64_t t = xor_state[1] << 17;
55
+
56
+ xor_state[2] ^= xor_state[0];
57
+ xor_state[3] ^= xor_state[1];
58
+ xor_state[1] ^= xor_state[2];
59
+ xor_state[0] ^= xor_state[3];
60
+
61
+ xor_state[2] ^= t;
62
+ xor_state[3] = rot45(xor_state[3]);
63
+
64
+ return ((double) (result & MAX_53BIT) + 0.5) * RAND_DENOM;
65
+ }
66
+
67
+
68
+ static inline void rand_init(const uint64_t seed) {
69
+ // initialize PCG random
70
+ splitmix64_state = seed;
71
+ for (int i = 0; i < 4; i++) {
72
+ xor_state[i] = Splitmix64_rand();
73
+ }
74
+ (void) random01();
75
+ }
76
+
77
+
@@ -81,7 +81,7 @@ static double single_init(const model_t* restrict mod, patch_t* restrict world,
81
81
  // init world
82
82
  for (size_t i = 0; i < N; i++) {
83
83
  for (size_t j = 0; j < M; j++) {
84
- patch_init(&world[ij_out], mod->I[ij_out * 2], mod->I[ij_out * 2 + 1], i, j);
84
+ patch_init(&world[ij_out], mod->I[ij_out * 2], mod->I[ij_out * 2 + 1], i, j, &(mod->X[ij_out * 4]), &(mod->P[ij_out * 6]));
85
85
  ij_out++;
86
86
  }
87
87
  }
@@ -107,21 +107,21 @@ static double single_init(const model_t* restrict mod, patch_t* restrict world,
107
107
 
108
108
  // set nb pointers for patches
109
109
  for (size_t ij = 0; ij < NM; ij++) {
110
- set_nb(world, &(mod->P[ij * 6]), &nb_indices[ij * 4], ij, NM);
110
+ set_nb(world, &nb_indices[ij * 4], ij, NM);
111
111
  }
112
112
 
113
113
  //////// Begin Running ////////
114
114
 
115
115
  // init payoff & natural death rates
116
116
  for (size_t ij = 0; ij < N; ij++) {
117
- update_pi_k(&world[ij], &(mod->X[ij * 4]), &(mod->P[ij * 6]));
117
+ update_pi_k(&world[ij]);
118
118
  }
119
119
 
120
120
  // init migration rates & store patch rates
121
121
  ij_out = 0;
122
122
  for (size_t i = 0; i < N; i++) {
123
123
  for (size_t j = 0; j < M; j++) {
124
- uint8_t mig_result = init_mig(&world[ij_out], &(mod->P[ij_out * 6])); // init mig rates for all 4 directions
124
+ uint8_t mig_result = init_mig(&world[ij_out]); // init mig rates for all 4 directions
125
125
  if (mig_result == SIM_OVERFLOW) {
126
126
  return -1 * SIM_OVERFLOW;
127
127
  }
@@ -144,7 +144,7 @@ static double single_init(const model_t* restrict mod, patch_t* restrict world,
144
144
 
145
145
  // make signal
146
146
  if (mod->boundary) {
147
- make_signal_zero_flux(picked_p->i, picked_p->j, e0, sig_p);
147
+ make_signal_zero_flux(N, M, picked_p->i, picked_p->j, e0, sig_p);
148
148
  } else {
149
149
  make_signal_periodical(N, M, picked_p->i, picked_p->j, e0, sig_p);
150
150
  }
@@ -164,7 +164,7 @@ static double single_init(const model_t* restrict mod, patch_t* restrict world,
164
164
  // maxtime too small
165
165
  return -1 * SMALL_MAXTIME;
166
166
  }
167
-
167
+
168
168
  // store data
169
169
  if (time > mod->record_itv) {
170
170
  size_t recod_idx = (size_t) (time / mod->record_itv);
@@ -194,24 +194,10 @@ static uint8_t single_test(model_t* restrict mod, char* message) {
194
194
  size_t max_record = mod->max_record;
195
195
  double record_itv = mod->record_itv;
196
196
  bool boundary = mod->boundary;
197
- double* X = mod->X;
198
- double* P = mod->P;
199
- double* U1d = mod->U1d;
200
- double* V1d = mod->V1d;
201
- double* Upi_1d = mod->Upi_1d;
202
- double* Vpi_1d = mod->Vpi_1d;
203
-
204
- // print progress
205
- double one_progress = 0.0;
206
- if (mod->print_pct != -1) {
207
- one_progress = maxtime * mod->print_pct / 100.0;
208
- fprintf(stdout, "\r ");
209
- fprintf(stdout, "\r%s: 0 %%", message);
210
- fflush(stdout);
211
- } else {
212
- one_progress = 2.0 * maxtime;
213
- }
214
- double current_progress = one_progress;
197
+ double* mod_U1d = mod->U1d;
198
+ double* mod_V1d = mod->V1d;
199
+ double* mod_Upi_1d = mod->Upi_1d;
200
+ double* mod_Vpi_1d = mod->Vpi_1d;
215
201
 
216
202
  // update sum of rates every 1e5 rounds
217
203
  // many rates are updated each time, rather than re-calculated.
@@ -219,10 +205,17 @@ static uint8_t single_test(model_t* restrict mod, char* message) {
219
205
  size_t curr_update_sum_round = 0; // current round
220
206
  size_t update_sum_freq = UPDATE_SUM_ROUNDS_SM; // recalculate sum every this many rounds
221
207
 
222
- // Initialize simulation
208
+ // set make_signal function based on boundary conditions
209
+ void (*make_signal)(size_t, size_t, size_t, size_t, uint8_t, signal_t*);
210
+ if (mod->boundary) {
211
+ make_signal = &make_signal_zero_flux;
212
+ } else {
213
+ make_signal = &make_signal_periodical;
214
+ }
215
+
216
+ // core containers
223
217
  patch_t* world = (patch_t*) calloc(NM, sizeof(patch_t));
224
218
  size_t* nb_indices = (size_t*) calloc(NM * 4, sizeof(size_t));
225
-
226
219
  double* patch_rates = (double*) calloc(NM, sizeof(double));
227
220
  double* sum_rates_by_row = (double*) calloc(N, sizeof(double));
228
221
  double sum_rates = 0;
@@ -230,6 +223,17 @@ static uint8_t single_test(model_t* restrict mod, char* message) {
230
223
  signal_t signal;
231
224
  patch_picked_t picked;
232
225
 
226
+ // print progress
227
+ double one_progress = one_progress = 2.0 * maxtime;
228
+ if (mod->print_pct != -1) {
229
+ one_progress = maxtime * mod->print_pct / 100.0;
230
+ fprintf(stdout, "\r ");
231
+ fprintf(stdout, "\r%s: 0 %%", message);
232
+ fflush(stdout);
233
+ }
234
+ double current_progress = one_progress;
235
+
236
+ // Call single_init. Initialize rates and run for 1 event
233
237
  double time = single_init(mod, world, nb_indices, patch_rates, sum_rates_by_row, &sum_rates, &signal, &picked);
234
238
  if (time == -1 * SMALL_MAXTIME) {
235
239
  // time too small
@@ -251,9 +255,10 @@ static uint8_t single_test(model_t* restrict mod, char* message) {
251
255
  size_t record_index = (size_t) (time / mod->record_itv);
252
256
  double record_time = time - record_index * record_itv;
253
257
 
258
+ //////// while loop ////////
254
259
 
255
260
  while (time < maxtime) {
256
-
261
+
257
262
  // update sums and print progress
258
263
  curr_update_sum_round++;
259
264
  if (curr_update_sum_round > update_sum_freq) {
@@ -264,11 +269,14 @@ static uint8_t single_test(model_t* restrict mod, char* message) {
264
269
  uint8_t curr_prog = (uint8_t)(time * 100 / maxtime);
265
270
  if (curr_prog < 10) {
266
271
  fprintf(stdout, "\r%s: %d %%", message, (int)(time * 100 / maxtime));
272
+ fflush(stdout);
267
273
  } else {
268
274
  fprintf(stdout, "\r%s: %d%%", message, (int)(time * 100 / maxtime));
275
+ fflush(stdout);
269
276
  }
270
- fflush(stdout);
271
- //fflush(stdout); // Make sure it prints immediately
277
+ //fprintf(stdout, "\n99: %d, %d, %f, %f\n100: %d, %d, %f, %f\n",
278
+ //world[98].U, world[98].V, world[98].mig_rates[3], world[98].mig_rates[7], world[99].U, world[99].V, world[99].mig_rates[2], world[99].mig_rates[6]);
279
+ //fflush(stdout);
272
280
  current_progress += one_progress;
273
281
  }
274
282
 
@@ -315,8 +323,8 @@ static uint8_t single_test(model_t* restrict mod, char* message) {
315
323
  sum_rates_by_row[si1] -= patch_rates[sij1];
316
324
  sum_rates -= patch_rates[sij1];
317
325
 
318
- update_pi_k(&world[sij1], &(X[sij1 * 4]), &(P[sij1 * 6]));
319
- update_mig_just_rate(&world[sij1], &(P[sij1 * 6]));
326
+ update_pi_k(&world[sij1]);
327
+ update_mig_just_rate(&world[sij1]);
320
328
 
321
329
  patch_rates[sij1] = world[sij1].sum_pi_death_rates + world[sij1].sum_mig_rates;
322
330
  sum_rates_by_row[si1] += patch_rates[sij1];
@@ -328,11 +336,11 @@ static uint8_t single_test(model_t* restrict mod, char* message) {
328
336
  sum_rates -= patch_rates[sij1];
329
337
  sum_rates -= patch_rates[sij2];
330
338
 
331
- update_pi_k(&world[sij1], &(X[sij1 * 4]), &(P[sij1 * 6])); // update both patches' payoffs first
332
- update_pi_k(&world[sij2], &(X[sij2 * 4]), &(P[sij2 * 6]));
339
+ update_pi_k(&world[sij1]); // update both patches' payoffs first
340
+ update_pi_k(&world[sij2]);
333
341
 
334
- if (update_mig_weight_rate(&world[sij1], &(P[sij1 * 6]), rela_loc) == SIM_OVERFLOW ||
335
- update_mig_weight_rate(&world[sij2], &(P[sij2 * 6]), rela_loc ^ 1) == SIM_OVERFLOW) {
342
+ if (update_mig_weight_rate(&world[sij1], rela_loc) == SIM_OVERFLOW ||
343
+ update_mig_weight_rate(&world[sij2], rela_loc ^ 1) == SIM_OVERFLOW) {
336
344
 
337
345
  fprintf(stdout, "\nError: overflow at t = %f\n", time);
338
346
  fflush(stdout);
@@ -354,7 +362,7 @@ static uint8_t single_test(model_t* restrict mod, char* message) {
354
362
  size_t nb_idx = nb_indices[sij1 * 4 + k];
355
363
  if (nb_idx == NM) { continue; } // invalid neighbor
356
364
  // all neighbors, as long as exists, need to change
357
- if (update_mig_weight_rate(&world[nb_idx], &(P[nb_idx * 6]), k ^ 1) == SIM_OVERFLOW) {
365
+ if (update_mig_weight_rate(&world[nb_idx], k ^ 1) == SIM_OVERFLOW) {
358
366
  fprintf(stdout, "\nError: overflow at t = %f\n", time);
359
367
  fflush(stdout);
360
368
  single_test_free(&world, &nb_indices, &patch_rates, &sum_rates_by_row);
@@ -369,7 +377,7 @@ static uint8_t single_test(model_t* restrict mod, char* message) {
369
377
  if (nb_idx == NM) { continue; }
370
378
  if (k != rela_loc) {
371
379
  // nb_idx isn't the second last-changed patch
372
- if (update_mig_weight_rate(&world[nb_idx], &(P[nb_idx * 6]), k ^ 1) == SIM_OVERFLOW) {
380
+ if (update_mig_weight_rate(&world[nb_idx], k ^ 1) == SIM_OVERFLOW) {
373
381
  fprintf(stdout, "\nError: overflow at t = %f\n", time);
374
382
  fflush(stdout);
375
383
  single_test_free(&world, &nb_indices, &patch_rates, &sum_rates_by_row);
@@ -383,7 +391,7 @@ static uint8_t single_test(model_t* restrict mod, char* message) {
383
391
  if (nb_idx == NM) { continue; }
384
392
  if (k != (rela_loc ^ 1)) {
385
393
  // nb_idx isn't the first last-changed patch
386
- if (update_mig_weight_rate(&world[nb_idx], &(P[nb_idx * 6]), k ^ 1) == SIM_OVERFLOW) {
394
+ if (update_mig_weight_rate(&world[nb_idx], k ^ 1) == SIM_OVERFLOW) {
387
395
  fprintf(stdout, "\nError: overflow at t = %f\n", time);
388
396
  fflush(stdout);
389
397
  single_test_free(&world, &nb_indices, &patch_rates, &sum_rates_by_row);
@@ -393,7 +401,6 @@ static uint8_t single_test(model_t* restrict mod, char* message) {
393
401
  }
394
402
  }
395
403
 
396
-
397
404
  // pick a random event
398
405
  double expected_sum = random01() * sum_rates;
399
406
  find_patch(&picked, expected_sum, patch_rates, sum_rates_by_row, sum_rates, N, M);
@@ -407,13 +414,9 @@ static uint8_t single_test(model_t* restrict mod, char* message) {
407
414
  }
408
415
 
409
416
  // make signal
410
- if (boundary) {
411
- make_signal_zero_flux(picked.i, picked.j, e0, &signal);
412
- } else {
413
- make_signal_periodical(N, M, picked.i, picked.j, e0, &signal);
414
- }
417
+ (*make_signal)(N, M, picked.i, picked.j, e0, &signal);
415
418
  signal.ij1 = signal.i1 * M + signal.j1;
416
- signal.ij2 = signal.i2 * M + signal.j2;
419
+ signal.ij2 = signal.i2 * M + signal.j2;
417
420
 
418
421
  // let the event happenn
419
422
  change_popu(&world[signal.ij1], signal.e1);
@@ -436,10 +439,10 @@ static uint8_t single_test(model_t* restrict mod, char* message) {
436
439
  for (size_t ij = 0; ij < NM; ij++) {
437
440
  size_t ij_max_record = ij * max_record;
438
441
  for (size_t k = record_index; k < upper; k++) {
439
- U1d[ij_max_record + k] += world[ij].U;
440
- V1d[ij_max_record + k] += world[ij].V;
441
- Upi_1d[ij_max_record + k] += world[ij].U_pi;
442
- Vpi_1d[ij_max_record + k] += world[ij].V_pi;
442
+ mod_U1d[ij_max_record + k] += world[ij].U;
443
+ mod_V1d[ij_max_record + k] += world[ij].V;
444
+ mod_Upi_1d[ij_max_record + k] += world[ij].U_pi;
445
+ mod_Vpi_1d[ij_max_record + k] += world[ij].V_pi;
443
446
  }
444
447
  }
445
448
  record_index += multi_records;
@@ -450,10 +453,10 @@ static uint8_t single_test(model_t* restrict mod, char* message) {
450
453
  for (size_t ij = 0; ij < NM; ij++) {
451
454
  size_t ij_max_record = ij * max_record;
452
455
  for (size_t k = record_index; k < max_record; k++) {
453
- U1d[ij_max_record + k] += world[ij].U;
454
- V1d[ij_max_record + k] += world[ij].V;
455
- Upi_1d[ij_max_record + k] += world[ij].U_pi;
456
- Vpi_1d[ij_max_record + k] += world[ij].V_pi;
456
+ mod_U1d[ij_max_record + k] += world[ij].U;
457
+ mod_V1d[ij_max_record + k] += world[ij].V;
458
+ mod_Upi_1d[ij_max_record + k] += world[ij].U_pi;
459
+ mod_Vpi_1d[ij_max_record + k] += world[ij].V_pi;
457
460
  }
458
461
  }
459
462
  }
@@ -500,40 +503,40 @@ uint8_t run(model_t* restrict mod, char* message, size_t msg_len) {
500
503
 
501
504
  // initialize random
502
505
  if (mod->seed != -1) {
503
- srand((uint32_t) mod->seed);
504
- } else {
505
- srand(time(NULL));
506
- }
507
-
508
- if (mod->seed == -1){
509
- srand(time(NULL));
506
+ rand_init((uint64_t) mod->seed);
510
507
  } else {
511
- srand(mod->seed);
508
+ rand_init((uint64_t) time(NULL));
512
509
  }
513
510
 
514
511
  if (mod->print_pct == 0) {
515
512
  mod->print_pct = 5; // default print_pct
513
+ }
514
+ size_t print_round = 0; // print every some round if print_pct == x * 100, set to 0 for not printing
515
+ if (mod->print_pct >= 100) {
516
+ print_round = mod->print_pct / 100; // print progress every some round
517
+ mod->print_pct = -1; // not printing progress in single_test
516
518
  }
517
519
 
518
- size_t i = 0;
520
+ size_t round = 0;
519
521
 
520
- while (i < mod->sim_time) {
522
+ while (round < mod->sim_time) {
521
523
  char curr_msg[100 + msg_len]; // message for current round
522
524
  strcpy(curr_msg, message);
523
525
  strcat(curr_msg, "round ");
524
- snprintf(curr_msg + strlen(curr_msg), sizeof(curr_msg) - strlen(curr_msg), "%zu", i);
526
+ snprintf(curr_msg + strlen(curr_msg), sizeof(curr_msg) - strlen(curr_msg), "%zu", round);
525
527
 
526
- /*if (predict_runtime && i > 0) {
527
- double time_elapsed = timer() - start;
528
- double pred_runtime = time_elapsed / i * (mod->sim_time - i);
529
- snprintf(end_info, sizeof(end_info), ", ~%.2fs left", pred_runtime);
530
- }*/
528
+ if ((print_round != 0) && (round % print_round == 0)) {
529
+ // only printing the round number
530
+ // add "!= 0" because round 0 could trigger printing
531
+ fprintf(stdout, "\r%s", curr_msg);
532
+ fflush(stdout);
533
+ }
531
534
 
532
535
  uint8_t result = single_test(mod, curr_msg);
533
536
 
534
537
  switch (result) {
535
538
  case SUCCESS:
536
- i++;
539
+ round++;
537
540
  break;
538
541
  case SMALL_MAXTIME:
539
542
  // error message is handled by single_test
@@ -549,9 +552,9 @@ uint8_t run(model_t* restrict mod, char* message, size_t msg_len) {
549
552
 
550
553
  calculate_ave(mod);
551
554
 
552
- double stop = clock();
553
-
554
- if (mod->print_pct != -1) {
555
+ if ((mod->print_pct != -1) || (print_round != 0)) {
556
+ // print runtime if the original mod->print_pct != -1
557
+ double stop = clock();
555
558
  fprintf(stdout, "\r%sruntime: %.3fs \n", message, (double)(stop - start) / CLOCKS_PER_SEC);
556
559
  fflush(stdout);
557
560
  }
@@ -15,11 +15,11 @@
15
15
 
16
16
  #include "patch.h"
17
17
  #include "model.h"
18
+ #include "random.h"
19
+
18
20
 
19
21
 
20
22
 
21
- // upper bound for RNG is 2^30 + 1
22
- #define RAND_UPPER_PLUS_2 (double) ((1LL << 30) + 1)
23
23
 
24
24
  // directions of migration
25
25
  // so that 3 - mig_dir = reflection of it. Used to track neighbors
@@ -55,8 +55,6 @@
55
55
  #define UPDATE_SUM_ROUNDS_SM 100 // small, more frequent if some rate is larger than ACCURATE_BOUND
56
56
  #define UPDATE_SUM_ROUNDS_LG 10000
57
57
 
58
-
59
-
60
58
 
61
59
  /**
62
60
  * index of patch in find_patch
@@ -105,42 +103,27 @@ uint8_t run(model_t* restrict mod, char* message, size_t msg_len);
105
103
 
106
104
 
107
105
 
108
- /**
109
- * Inline Functions
110
- */
111
-
112
- static inline double random01() {
113
- // generate a 24 bit random int, then convert to a (0, 1) ranged double
114
- uint32_t r1 = rand() & 0x7fff; // RAND_MAX is different across machines, ensure 15 bits
115
- uint32_t r2 = rand() & 0x7fff;
116
-
117
- double r_combined = (r1 << 15) + r2;
118
- return (r_combined + 1) / RAND_UPPER_PLUS_2;
119
- }
120
-
121
106
  /**
122
107
  * patch functions
123
108
  */
124
109
 
125
- static inline void update_pi_k(patch_t* restrict p, const double* restrict M_start, const double* restrict P_start) {
126
- // M_start: start index of patch (i, j)'s matrix
127
- // P_start: start index of p's patch variables, i.e., ij * 6
128
-
110
+ static inline void update_pi_k(patch_t* restrict p) {
111
+ // update payoff and carrying capacity rates
129
112
  uint32_t U = p->U;
130
113
  uint32_t V = p->V;
131
114
  double sum = U + V;
132
- double U_ratio = U / sum;
133
- double V_ratio = V / sum;
134
115
 
135
116
  if (sum > 0) {
117
+ double U_ratio = U / sum;
118
+ double V_ratio = V / sum;
136
119
  if (U > 0) {
137
- p->U_pi = U_ratio * M_start[0] + V_ratio * M_start[1];
120
+ p->U_pi = U_ratio * p->X[0] + V_ratio * p->X[1];
138
121
  } else {
139
122
  p->U_pi = 0.0;
140
123
  }
141
124
 
142
125
  if (V > 0) {
143
- p->V_pi = U_ratio * M_start[2] + V_ratio * M_start[3];
126
+ p->V_pi = U_ratio * p->X[2] + V_ratio * p->X[3];
144
127
  } else {
145
128
  p->V_pi = 0.0;
146
129
  }
@@ -153,8 +136,8 @@ static inline void update_pi_k(patch_t* restrict p, const double* restrict M_sta
153
136
  p->pi_death_rates[0] = fabs(U * p->U_pi);
154
137
  p->pi_death_rates[1] = fabs(V * p->V_pi);
155
138
 
156
- p->pi_death_rates[2] = P_start[4] * U * sum;
157
- p->pi_death_rates[3] = P_start[5] * V * sum;
139
+ p->pi_death_rates[2] = p->P[4] * U * sum;
140
+ p->pi_death_rates[3] = p->P[5] * V * sum;
158
141
 
159
142
  p->sum_pi_death_rates = 0.0;
160
143
  for (size_t i = 0; i < 4; i++) {
@@ -164,14 +147,14 @@ static inline void update_pi_k(patch_t* restrict p, const double* restrict M_sta
164
147
 
165
148
 
166
149
 
167
- static inline void update_mig_just_rate(patch_t* restrict p, const double* restrict P_start) {
150
+ static inline void update_mig_just_rate(patch_t* restrict p) {
168
151
  // update migration weight for patch p, in location loc. Only rate is updated
169
152
  // used by last-changed patch, when there is only one last-changed patch
170
153
  double* p_U_weight = p->U_weight;
171
154
  double* p_V_weight = p->V_weight;
172
155
 
173
- double mu1_U = P_start[0] * p->U;
174
- double mu2_V = P_start[1] * p->V;
156
+ double mu1_U = p->P[0] * p->U;
157
+ double mu2_V = p->P[1] * p->V;
175
158
 
176
159
  double mu1_U_divide_sum = mu1_U / p->sum_U_weight;
177
160
  double mu2_V_divide_sum = mu2_V / p->sum_V_weight;
@@ -184,7 +167,7 @@ static inline void update_mig_just_rate(patch_t* restrict p, const double* restr
184
167
  }
185
168
 
186
169
 
187
- static inline uint8_t update_mig_weight_rate(patch_t* restrict p, const double* P_start, uint8_t loc) {
170
+ static inline uint8_t update_mig_weight_rate(patch_t* restrict p, const uint8_t loc) {
188
171
  // update migration weight as well as rates, in one direction
189
172
  // used by neighbors of last-changed patches
190
173
  // also used by last-changed patches themselve, when there are two patch changed, to update mig rates of in each other's direction
@@ -196,8 +179,8 @@ static inline uint8_t update_mig_weight_rate(patch_t* restrict p, const double*
196
179
  p->sum_U_weight -= p_U_weight[loc];
197
180
  p->sum_V_weight -= p_V_weight[loc];
198
181
 
199
- double w1_Upi = P_start[2] * nbi->U_pi;
200
- double w2_Vpi = P_start[3] * nbi->V_pi;
182
+ double w1_Upi = p->P[2] * nbi->U_pi;
183
+ double w2_Vpi = p->P[3] * nbi->V_pi;
201
184
  if (w1_Upi > EXP_OVERFLOW_BOUND) {
202
185
  return SIM_OVERFLOW;
203
186
  }
@@ -226,8 +209,8 @@ static inline uint8_t update_mig_weight_rate(patch_t* restrict p, const double*
226
209
  p->sum_U_weight += p_U_weight[loc];
227
210
  p->sum_V_weight += p_V_weight[loc];
228
211
 
229
- double mu1_U = P_start[0] * (double)p->U;
230
- double mu2_V = P_start[1] * (double)p->V;
212
+ double mu1_U = p->P[0] * p->U;
213
+ double mu2_V = p->P[1] * p->V;
231
214
  double mu1_U_divide_sum = mu1_U / p->sum_U_weight;
232
215
  double mu2_V_divide_sum = mu2_V / p->sum_V_weight;
233
216
 
@@ -242,7 +225,7 @@ static inline uint8_t update_mig_weight_rate(patch_t* restrict p, const double*
242
225
 
243
226
 
244
227
 
245
- static inline uint8_t init_mig(patch_t* restrict p, const double* restrict P_start) {
228
+ static inline uint8_t init_mig(patch_t* restrict p) {
246
229
  // update migration rate for all directions
247
230
 
248
231
  double* p_U_weight = p->U_weight;
@@ -251,8 +234,8 @@ static inline uint8_t init_mig(patch_t* restrict p, const double* restrict P_sta
251
234
  p->sum_U_weight = 0.0;
252
235
  p->sum_V_weight = 0.0;
253
236
 
254
- double w1 = P_start[2];
255
- double w2 = P_start[3];
237
+ double w1 = p->P[2];
238
+ double w2 = p->P[3];
256
239
 
257
240
  for (uint8_t i = 0; i < 4; i++) {
258
241
  patch_t* nbi = p->nb[i];
@@ -274,8 +257,8 @@ static inline uint8_t init_mig(patch_t* restrict p, const double* restrict P_sta
274
257
  }
275
258
  }
276
259
 
277
- double mu1_U = P_start[0] * (double)p->U;
278
- double mu2_V = P_start[1] * (double)p->V;
260
+ double mu1_U = p->P[0] * p->U;
261
+ double mu2_V = p->P[1] * p->V;
279
262
  double mu1_U_divide_sum = mu1_U / p->sum_U_weight;
280
263
  double mu2_V_divide_sum = mu2_V / p->sum_V_weight;
281
264
 
@@ -290,7 +273,7 @@ static inline uint8_t init_mig(patch_t* restrict p, const double* restrict P_sta
290
273
 
291
274
 
292
275
 
293
- static inline uint8_t find_event(const patch_t* restrict p, double expected_sum) {
276
+ static inline uint8_t find_event(const patch_t* restrict p, const double expected_sum) {
294
277
  size_t event = 0;
295
278
  double current_sum;
296
279
 
@@ -320,7 +303,7 @@ static inline uint8_t find_event(const patch_t* restrict p, double expected_sum)
320
303
  }
321
304
 
322
305
 
323
- static inline void change_popu(patch_t* restrict p, uint8_t s) {
306
+ static inline void change_popu(patch_t* restrict p, const uint8_t s) {
324
307
  switch (s) {
325
308
  case 0:
326
309
  // Migration IN for U
@@ -362,11 +345,12 @@ static inline void change_popu(patch_t* restrict p, uint8_t s) {
362
345
  * Main Simulation Functions
363
346
  */
364
347
 
365
- static inline void find_patch(patch_picked_t* restrict picked, double expected_sum,
366
- const double* restrict patch_rates, const double* restrict sum_rates_by_row, double sum_rates, size_t N, size_t M) {
348
+ static inline void find_patch(patch_picked_t* restrict picked, const double expected_sum,
349
+ const double* restrict patch_rates, const double* restrict sum_rates_by_row, const double sum_rates, const size_t N, const size_t M) {
367
350
  double current_sum = 0;
368
351
  size_t row = 0;
369
352
  size_t col = 0;
353
+ size_t row_M = 0;
370
354
 
371
355
  // Find row
372
356
  if (N != 1) {
@@ -388,10 +372,9 @@ static inline void find_patch(patch_picked_t* restrict picked, double expected_s
388
372
  }
389
373
  row++;
390
374
  }
375
+ row_M = row * M;
391
376
  }
392
377
 
393
- size_t row_M = row * M;
394
-
395
378
  // Find col in that row
396
379
  if ((expected_sum - current_sum) < sum_rates_by_row[row] * 0.5) {
397
380
  col = 0;
@@ -418,7 +401,10 @@ static inline void find_patch(patch_picked_t* restrict picked, double expected_s
418
401
 
419
402
 
420
403
 
421
- static inline void make_signal_zero_flux(size_t i, size_t j, uint8_t e, signal_t* restrict signal) {
404
+ static inline void make_signal_zero_flux(const size_t N, const size_t M, const size_t i, const size_t j, const uint8_t e, signal_t* restrict signal) {
405
+ // pass in N & M as well to match the param set of make_signal_periodical
406
+ (void) N;
407
+ (void) M;
422
408
  // this is always the case for the first one
423
409
  signal->i1 = i;
424
410
  signal->j1 = j;
@@ -496,15 +482,12 @@ static inline void make_signal_zero_flux(size_t i, size_t j, uint8_t e, signal_t
496
482
  signal->e2 = 3;
497
483
  signal->rela_loc = MIG_RIGHT;
498
484
  return;
499
- //default:
500
- // fprintf(stderr, "Bug: invalid case in make_signal_zf, i, j, e: %zu, %zu, %hhu\n", i, j, e);
501
- // return;
502
485
  }
503
486
  }
504
487
 
505
488
 
506
489
 
507
- static inline void make_signal_periodical(size_t N, size_t M, size_t i, size_t j, uint8_t e, signal_t* restrict signal) {
490
+ static inline void make_signal_periodical(const size_t N, const size_t M, const size_t i, const size_t j, const uint8_t e, signal_t* restrict signal) {
508
491
  // this is always the case for the first one
509
492
  signal->i1 = i;
510
493
  signal->j1 = j;
@@ -582,9 +565,6 @@ static inline void make_signal_periodical(size_t N, size_t M, size_t i, size_t j
582
565
  signal->e2 = 3;
583
566
  signal->rela_loc = MIG_RIGHT;
584
567
  return;
585
- //default:
586
- // fprintf(stderr, "Bug: invalid case in make_signal_pr, i, j, e: %zu, %zu, %hhu\n", i, j, e);
587
- // return;
588
568
  }
589
569
  }
590
570
 
@@ -23,7 +23,7 @@ from .build_info import build_info
23
23
 
24
24
  from .simulation import model, run, demo_model, UV_expected_val, check_overflow_func
25
25
  from .videos import make_video, SUPPORTED_FIGURES
26
- from .data_tools import save_data, read_data
26
+ from .data_tools import save, load
27
27
 
28
28
  from .analysis import rounds_expected, scale_maxtime, check_convergence, combine_sim
29
29
 
@@ -38,7 +38,7 @@ simulation_memebers = ['model', 'run', 'demo_model']
38
38
 
39
39
  videos_members = ['make_video', 'SUPPORTED_FIGURES']
40
40
 
41
- data_members = ['save_data', 'read_data']
41
+ data_members = ['save', 'load']
42
42
 
43
43
  analysis_members = ['expected_rounds', 'scale_maxtime', 'check_convergence', 'combine_mod']
44
44
 
@@ -1,4 +1,4 @@
1
- __version__ = '2.3.7'
1
+ __version__ = '2.3.9'
2
2
 
3
3
  '''
4
4
  version history:
@@ -44,4 +44,6 @@ version history:
44
44
  2.3.5: improved accuracy for simulation, now can better handle large mig rates. Numerical errors are now being checked and reduced automatically based on how large the values are.
45
45
  2.3.6: index error due to reduced accuracy is now explicitly handled.
46
46
  2.3.7: update migration rules on the boundary. mu values are scaled based on how many neighbors a patch has. Minor debugging in several modules.
47
+ 2.3.8: bug fix for 2.3.7 update.
48
+ 2.3.9: now use xoshiro256+ as RNG.
47
49
  '''
@@ -2,8 +2,8 @@
2
2
  Stores and reads a model object.
3
3
 
4
4
  Functions:
5
- - save_data: save a model object.
6
- - read_data: read a model object.
5
+ - save: save a model object.
6
+ - load: load a model object.
7
7
  '''
8
8
 
9
9
 
@@ -12,9 +12,10 @@ from . import simulation
12
12
  import json
13
13
  import gzip
14
14
  import os
15
+ import numpy as np
15
16
 
16
17
 
17
- def save_data(mod, dirs = '', print_msg = True):
18
+ def save(mod, dirs = '', print_msg = True):
18
19
  '''
19
20
  Saves a model object. Data will be stored at dirs/data.json.gz
20
21
 
@@ -46,7 +47,6 @@ def save_data(mod, dirs = '', print_msg = True):
46
47
  inputs.append(mod.P.tolist())
47
48
  inputs.append(mod.print_pct)
48
49
  inputs.append(mod.seed)
49
- inputs.append(mod.check_overflow)
50
50
  data.append(inputs)
51
51
 
52
52
  # skipped rng
@@ -54,11 +54,17 @@ def save_data(mod, dirs = '', print_msg = True):
54
54
  outputs = []
55
55
  outputs.append(mod.max_record)
56
56
  outputs.append(mod.compress_itv)
57
- outputs.append(mod.U.tolist())
58
- outputs.append(mod.V.tolist())
59
- outputs.append(mod.Upi.tolist())
60
- outputs.append(mod.Vpi.tolist())
61
- # H&Vpi_total are not saved, will be calculated when reading the data
57
+ if not mod.data_empty:
58
+ outputs.append(mod.U.tolist())
59
+ outputs.append(mod.V.tolist())
60
+ outputs.append(mod.Upi.tolist())
61
+ outputs.append(mod.Vpi.tolist())
62
+ else:
63
+ outputs.append(None)
64
+ outputs.append(None)
65
+ outputs.append(None)
66
+ outputs.append(None)
67
+
62
68
  data.append(outputs)
63
69
 
64
70
  data_dirs = os.path.join(dirs, 'data.json.gz')
@@ -70,7 +76,7 @@ def save_data(mod, dirs = '', print_msg = True):
70
76
 
71
77
 
72
78
 
73
- def read_data(dirs):
79
+ def load(dirs):
74
80
  '''
75
81
  Reads and returns a model object.
76
82
 
@@ -96,16 +102,19 @@ def read_data(dirs):
96
102
  try:
97
103
  mod = simulation.model(N = data[0][0], M = data[0][1], maxtime = data[0][2], record_itv = data[0][3],
98
104
  sim_time = data[0][4], boundary = data[0][5], I = data[0][6], X = data[0][7], P = data[0][8],
99
- print_pct = data[0][9], seed = data[0][10], check_overflow = data[0][11])
105
+ print_pct = data[0][9], seed = data[0][10], check_overflow = False)
100
106
  except:
101
- raise ValueError('Invalid input parameters saved in data')
107
+ raise ValueError('Invalid input parameters stored in data')
102
108
 
103
109
  # outputs
104
110
  try:
105
111
  mod.set_data(data_empty = False, max_record = data[1][0], compress_itv = data[1][1],
106
112
  U = data[1][2], V = data[1][3], Upi = data[1][4], Vpi = data[1][5])
113
+ if (mod.U is None) or (isinstance(mod.U, np.ndarray) and mod.U.shape == () and mod.U.item() is None):
114
+ # if data is None
115
+ mod.data_empty = True
107
116
  except:
108
- raise ValueError('Invalid model results saved in data')
117
+ raise ValueError('Invalid simulation results stored in data')
109
118
 
110
119
  return mod
111
120
 
@@ -164,22 +164,11 @@ class model:
164
164
  if check_overflow:
165
165
  check_overflow_func(self)
166
166
 
167
- self.init_storage() # initialize storage bins. Put in a separate function because might want to change maxtime
168
- # and that doesn't need to initialze the whole object again
167
+ # initialize storage bins.
168
+ self.set_data(data_empty = True, max_record = int(maxtime / record_itv), compress_itv = 1,
169
+ U = None, V = None, Upi = None, Vpi = None)
169
170
 
170
171
 
171
- def init_storage(self):
172
- # initialize storage bins
173
- self.data_empty = True # whether data storage bins are empty. model.run will refuse to run (raise error) if not empty.
174
- self.max_record = int(self.maxtime / self.record_itv) # int, how many data points to store sin total
175
- self.compress_itv = 1 # int, intended to reduce size of data (if not 1). Updated by compress_data function
176
- # if set to an int, say 20, mod will take average over every 20 data points and save them as new data.
177
- # May be used over and over again to recursively reduce data size.
178
- # Default is 1, not to take average.
179
- self.U = None # initialized by simulation.run or data_tools.read_data
180
- self.V = None
181
- self.Upi = None
182
- self.Vpi = None
183
172
 
184
173
 
185
174
  def check_valid_input(self, N, M, maxtime, record_itv, sim_time, boundary, I, X, P, print_pct, seed, check_overflow):
@@ -319,6 +308,8 @@ class model:
319
308
 
320
309
  if copy_data:
321
310
  # copy data as well
311
+ if self.data_empty:
312
+ print("Warning: model has empty data")
322
313
  sim2.set_data(self.data_empty, self.max_record, self.compress_itv, self.U, self.V, self.Upi, self.Vpi)
323
314
 
324
315
  return sim2
@@ -426,10 +417,10 @@ def run(mod, message = ""):
426
417
  P = np.ascontiguousarray(mod.P.flatten(), dtype = np.float64)
427
418
 
428
419
  mod_c = model_c()
429
- success = LIB.mod_init(ctypes.byref(mod_c),
420
+ init_sucess = LIB.mod_init(ctypes.byref(mod_c),
430
421
  mod.N, mod.M, mod.maxtime, mod.record_itv, mod.sim_time, mod.boundary,
431
422
  I, X, P, mod.print_pct, mod.seed)
432
- if not success:
423
+ if not init_sucess:
433
424
  LIB.mod_free_py(ctypes.byref(mod_c))
434
425
  del mod_c
435
426
  raise RuntimeError('Model initialization failed')
@@ -93,7 +93,13 @@ class patch:
93
93
  def set_nb_pointers(self, nb):
94
94
  # nb is a list of pointers (point to patches)
95
95
  # nb is passed from the model class
96
+ nb_count = 0
97
+ for nb_i in nb:
98
+ if nb_i != None:
99
+ nb_count += 1
96
100
  self.nb = nb
101
+ self.mu1 *= (0.25 * nb_count)
102
+ self.mu2 *= (0.25 * nb_count)
97
103
 
98
104
 
99
105
  def update_pi_k(self):
@@ -101,19 +107,21 @@ class patch:
101
107
 
102
108
  U = self.U # bring the values to front
103
109
  V = self.V
104
- sum_minus_1 = U + V - 1 # this value is used several times
110
+ sum_UV = U + V # this value is used several times
105
111
 
106
- if sum_minus_1 > 0:
112
+ if sum_UV > 0:
107
113
  # interaction happens only if there is more than 1 individual
114
+ U_ratio = U / sum_UV
115
+ V_ratio = V / sum_UV
108
116
 
109
117
  if U != 0:
110
118
  # no payoff if U == 0
111
- self.Upi = (U - 1) / sum_minus_1 * self.matrix[0] + V / sum_minus_1 * self.matrix[1]
119
+ self.Upi = U_ratio * self.matrix[0] + V_ratio * self.matrix[1]
112
120
  else:
113
121
  self.Upi = 0
114
122
 
115
123
  if V != 0:
116
- self.Vpi = U / sum_minus_1 * self.matrix[2] + (V - 1) / sum_minus_1 * self.matrix[3]
124
+ self.Vpi = U_ratio * self.matrix[2] + V_ratio * self.matrix[3]
117
125
  else:
118
126
  self.Vpi = 0
119
127
 
@@ -127,8 +135,8 @@ class patch:
127
135
  self.pi_death_rates[1] = abs(V * self.Vpi)
128
136
 
129
137
  # update natural death rates
130
- self.pi_death_rates[2] = self.kappa1 * U * (sum_minus_1 + 1)
131
- self.pi_death_rates[3] = self.kappa2 * V * (sum_minus_1 + 1)
138
+ self.pi_death_rates[2] = self.kappa1 * U * sum_UV
139
+ self.pi_death_rates[3] = self.kappa2 * V * sum_UV
132
140
 
133
141
  # update sum of rates
134
142
  self.sum_pi_death_rates = sum(self.pi_death_rates)
@@ -143,8 +151,8 @@ class patch:
143
151
 
144
152
  for i in range(4):
145
153
  if self.nb[i] != None:
146
- U_weight[i] = 1 + pow(math.e, self.w1 * self.nb[i].Upi)
147
- V_weight[i] = 1 + pow(math.e, self.w2 * self.nb[i].Vpi)
154
+ U_weight[i] = pow(math.e, self.w1 * self.nb[i].Upi)
155
+ V_weight[i] = pow(math.e, self.w2 * self.nb[i].Vpi)
148
156
 
149
157
  mu1_U = self.mu1 * self.U
150
158
  mu2_V = self.mu2 * self.V
@@ -746,7 +754,7 @@ def single_test(mod, front_info, end_info, update_sum_frequency, rng):
746
754
 
747
755
 
748
756
 
749
- def run_py(mod, predict_runtime = False, message = ''):
757
+ def run(mod, predict_runtime = False, message = ''):
750
758
  '''
751
759
  Main function. Recursively calls single_test to run many models and then takes the average.
752
760
 
@@ -91,7 +91,7 @@ def test_var1(mod, var, values, dirs, compress_itv = None):
91
91
  simulation.run(sim2, message = current_var_str + ', ')
92
92
  if compress_itv != None:
93
93
  sim2.compress_data(compress_itv)
94
- data_t.save_data(sim2, var_dirs[k], print_msg = False)
94
+ data_t.save(sim2, var_dirs[k], print_msg = False)
95
95
  del sim2
96
96
  except (OverflowError, RuntimeError):
97
97
  print(current_var_str + ' raised error, skipped')
@@ -139,7 +139,7 @@ def test_var2(mod, var1, var2, values1, values2, dirs, compress_itv = None):
139
139
  simulation.run(sim2, message = current_var_str + ', ')
140
140
  if compress_itv != None:
141
141
  sim2.compress_data(compress_itv)
142
- data_t.save_data(sim2, var_dirs[k1][k2], print_msg = False)
142
+ data_t.save(sim2, var_dirs[k1][k2], print_msg = False)
143
143
  del sim2
144
144
  except (OverflowError, RuntimeError):
145
145
  print(current_var_str + ' raised error, skipped')
@@ -175,7 +175,7 @@ def var_UV1(var, values, var_dirs, ax_U = None, ax_V = None, start = 0.95, end =
175
175
 
176
176
  for k in range(len(var_dirs)):
177
177
  try:
178
- simk = data_t.read_data(var_dirs[k])
178
+ simk = data_t.load(var_dirs[k])
179
179
  except FileNotFoundError:
180
180
  print(var + '=' + str(values[k]) + ' not found, skipped')
181
181
  U_ave.append(None)
@@ -244,7 +244,7 @@ def var_UV2(var1, var2, values1, values2, var_dirs, ax_U = None, ax_V = None, va
244
244
  for k1 in range(len(var_dirs)):
245
245
  for k2 in range(len(var_dirs[k1])):
246
246
  try:
247
- simk = data_t.read_data(var_dirs[k1][k2])
247
+ simk = data_t.load(var_dirs[k1][k2])
248
248
  except FileNotFoundError:
249
249
  print(var1 + '=' + str(values1[k1]) + ', ' + var2 + '=' + str(values2[k2]) + ' not found, skipped')
250
250
  U_ave[k1].append(None)
@@ -332,7 +332,7 @@ def var_pi1(var, values, var_dirs, ax_U = None, ax_V = None, start = 0.95, end =
332
332
 
333
333
  for k in range(len(var_dirs)):
334
334
  try:
335
- simk = data_t.read_data(var_dirs[k])
335
+ simk = data_t.load(var_dirs[k])
336
336
  except FileNotFoundError:
337
337
  print(var + '=' + str(values[k]) + ' not found, skipped')
338
338
  U_ave.append(None)
@@ -397,7 +397,7 @@ def var_pi2(var1, var2, values1, values2, var_dirs, ax_U = None, ax_V = None, va
397
397
  for k1 in range(len(var_dirs)):
398
398
  for k2 in range(len(var_dirs[k1])):
399
399
  try:
400
- simk = data_t.read_data(var_dirs[k1][k2])
400
+ simk = data_t.load(var_dirs[k1][k2])
401
401
  except FileNotFoundError:
402
402
  print(var1 + '=' + str(values1[k1]) + ', ' + var2 + '=' + str(values2[k2]) + ' not found, skipped')
403
403
  U_ave[k1].append(None)
@@ -512,7 +512,7 @@ def var_convergence1(var_dirs, interval = 20, start = 0.8, fluc = 0.07):
512
512
  for k in range(len(var_dirs)):
513
513
  dirs = var_dirs[k]
514
514
  try:
515
- simk = data_t.read_data(dirs)
515
+ simk = data_t.load(dirs)
516
516
  except FileNotFoundError:
517
517
  print(dirs + ' data not found, skipped')
518
518
  continue
@@ -581,7 +581,7 @@ def var_convergence2(var_dirs, interval = 20, start = 0.8, fluc = 0.07):
581
581
  for k in range(len(sublist)):
582
582
  dirs = sublist[k]
583
583
  try:
584
- simk = data_t.read_data(dirs)
584
+ simk = data_t.load(dirs)
585
585
  except FileNotFoundError:
586
586
  print(dirs + ' data not found, skipped')
587
587
  continue
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: piegy
3
- Version: 2.3.7
3
+ Version: 2.3.9
4
4
  Summary: Payoff-Driven Stochastic Spatial Model for Evolutionary Game Theory
5
5
  Author-email: Chenning Xu <cxu7@caltech.edu>
6
6
  License: BSD 3-Clause License
@@ -23,6 +23,7 @@ src/piegy/C_core/model.c
23
23
  src/piegy/C_core/model.h
24
24
  src/piegy/C_core/patch.c
25
25
  src/piegy/C_core/patch.h
26
+ src/piegy/C_core/random.h
26
27
  src/piegy/C_core/sim_funcs.c
27
28
  src/piegy/C_core/sim_funcs.h
28
29
  src/piegy/tools/__init__.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