piegy 2.3.5__tar.gz → 2.3.7__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.5 → piegy-2.3.7}/MANIFEST.in +1 -0
  2. {piegy-2.3.5/src/piegy.egg-info → piegy-2.3.7}/PKG-INFO +1 -1
  3. {piegy-2.3.5 → piegy-2.3.7}/pyproject.toml +1 -1
  4. {piegy-2.3.5 → piegy-2.3.7}/src/piegy/C_core/Makefile +0 -1
  5. {piegy-2.3.5 → piegy-2.3.7}/src/piegy/C_core/patch.c +8 -4
  6. {piegy-2.3.5 → piegy-2.3.7}/src/piegy/C_core/patch.h +5 -5
  7. {piegy-2.3.5 → piegy-2.3.7}/src/piegy/C_core/sim_funcs.c +116 -108
  8. {piegy-2.3.5 → piegy-2.3.7}/src/piegy/C_core/sim_funcs.h +25 -21
  9. {piegy-2.3.5 → piegy-2.3.7}/src/piegy/__version__.py +3 -1
  10. {piegy-2.3.5 → piegy-2.3.7}/src/piegy/data_tools.py +29 -41
  11. {piegy-2.3.5 → piegy-2.3.7}/src/piegy/figures.py +3 -3
  12. {piegy-2.3.5 → piegy-2.3.7}/src/piegy/simulation.py +9 -4
  13. {piegy-2.3.5 → piegy-2.3.7}/src/piegy/test_var.py +19 -13
  14. {piegy-2.3.5 → piegy-2.3.7}/src/piegy/tools/figure_tools.py +1 -1
  15. {piegy-2.3.5 → piegy-2.3.7}/src/piegy/tools/find_C.py +3 -4
  16. {piegy-2.3.5 → piegy-2.3.7}/src/piegy/videos.py +14 -11
  17. {piegy-2.3.5 → piegy-2.3.7/src/piegy.egg-info}/PKG-INFO +1 -1
  18. {piegy-2.3.5 → piegy-2.3.7}/src/piegy.egg-info/SOURCES.txt +0 -1
  19. piegy-2.3.5/src/piegy/C_core/runner.c +0 -61
  20. {piegy-2.3.5 → piegy-2.3.7}/LICENSE.txt +0 -0
  21. {piegy-2.3.5 → piegy-2.3.7}/README.md +0 -0
  22. {piegy-2.3.5 → piegy-2.3.7}/setup.cfg +0 -0
  23. {piegy-2.3.5 → piegy-2.3.7}/setup.py +0 -0
  24. {piegy-2.3.5 → piegy-2.3.7}/src/piegy/C_core/model.c +0 -0
  25. {piegy-2.3.5 → piegy-2.3.7}/src/piegy/C_core/model.h +0 -0
  26. {piegy-2.3.5 → piegy-2.3.7}/src/piegy/__init__.py +0 -0
  27. {piegy-2.3.5 → piegy-2.3.7}/src/piegy/analysis.py +0 -0
  28. {piegy-2.3.5 → piegy-2.3.7}/src/piegy/build_info.py +0 -0
  29. {piegy-2.3.5 → piegy-2.3.7}/src/piegy/simulation_py.py +0 -0
  30. {piegy-2.3.5 → piegy-2.3.7}/src/piegy/tools/__init__.py +0 -0
  31. {piegy-2.3.5 → piegy-2.3.7}/src/piegy/tools/file_tools.py +0 -0
  32. {piegy-2.3.5 → piegy-2.3.7}/src/piegy.egg-info/dependency_links.txt +0 -0
  33. {piegy-2.3.5 → piegy-2.3.7}/src/piegy.egg-info/requires.txt +0 -0
  34. {piegy-2.3.5 → piegy-2.3.7}/src/piegy.egg-info/top_level.txt +0 -0
@@ -4,3 +4,4 @@ include LICENSE.txt
4
4
  include src/piegy/C_core/*.c
5
5
  include src/piegy/C_core/*.h
6
6
  include src/piegy/C_core/Makefile
7
+ exclude src/piegy/C_core/runner.c
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: piegy
3
- Version: 2.3.5
3
+ Version: 2.3.7
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.5'
7
+ version = '2.3.7'
8
8
  description = 'Payoff-Driven Stochastic Spatial Model for Evolutionary Game Theory'
9
9
  readme = 'README.md'
10
10
  requires-python = '>=3.7'
@@ -4,7 +4,6 @@ CC = gcc
4
4
  #### Common Flags ####
5
5
 
6
6
  ## Flags for Max Speed ##
7
- # standard
8
7
  CFLAGS_COMMON = -O3 -march=native -flto -std=c99 -DNDEBUG -ffp-contract=fast -funroll-loops -fomit-frame-pointer -MMD -MP
9
8
  LDFLAGS_COMMON = -flto
10
9
 
@@ -5,10 +5,10 @@
5
5
 
6
6
  #include "patch.h"
7
7
 
8
- void patch_init(patch_t* p, uint32_t U, uint32_t V, size_t i, size_t j) {
8
+ void patch_init(patch_t* p, uint32_t U, uint32_t V, size_t row, size_t col) {
9
9
  if (p == NULL) return;
10
- p->i = i;
11
- p->j = j;
10
+ p->row = row;
11
+ p->col = col;
12
12
 
13
13
  p->U = U;
14
14
  p->V = V;
@@ -30,14 +30,18 @@ void patch_init(patch_t* p, uint32_t U, uint32_t V, size_t i, size_t j) {
30
30
  }
31
31
 
32
32
 
33
- void set_nb(patch_t* world, size_t* nb_start, size_t ij, size_t NM) {
33
+ void set_nb(patch_t* world, double* P_start, size_t* nb_start, size_t ij, size_t NM) {
34
34
  // nb_start is the where patch ij's neighbor indices start
35
+ size_t num_nb = 0;
35
36
  for (size_t k = 0; k < 4; k++) {
36
37
  if (nb_start[k] != NM) {
37
38
  // neighbor is valid
38
39
  world[ij].nb[k] = &world[nb_start[k]];
40
+ num_nb += 1;
39
41
  } else {
40
42
  world[ij].nb[k] = NULL;
41
43
  }
42
44
  }
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);
43
47
  }
@@ -14,10 +14,10 @@
14
14
 
15
15
 
16
16
  typedef struct patch_t {
17
- size_t i;
18
- size_t j;
17
+ size_t row;
18
+ size_t col;
19
19
 
20
- uint32_t U;
20
+ uint32_t U; // store as double directly to avoid runtime conversion (to double)
21
21
  uint32_t V;
22
22
  double U_pi;
23
23
  double V_pi;
@@ -34,8 +34,8 @@ typedef struct patch_t {
34
34
  } patch_t;
35
35
 
36
36
  // in .c
37
- void patch_init(patch_t* p, uint32_t U, uint32_t V, size_t i, size_t j);
38
- void set_nb(patch_t* world, size_t* nb_start, size_t ij, size_t NM);
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) ;
39
39
 
40
40
  #endif // PATCH_H
41
41
 
@@ -68,75 +68,68 @@ static void find_nb_periodical(size_t* restrict nb, size_t i, size_t j, size_t N
68
68
 
69
69
 
70
70
  // single_init function: initializes world, runs 1 event, returns updated variables
71
- static double single_init(const model_t* mod, patch_t* world, size_t* nb_indices,
72
- double* patch_rates, double* sum_rates_by_row, double* sum_rates_p, signal_t* sig_p, patch_picked_t* picked_p) {
71
+ static double single_init(const model_t* restrict mod, patch_t* restrict world, size_t* restrict nb_indices,
72
+ double* restrict patch_rates, double* restrict sum_rates_by_row, double* restrict sum_rates_p,
73
+ signal_t* restrict sig_p, patch_picked_t* restrict picked_p) {
73
74
 
74
75
  size_t N = mod->N;
75
76
  size_t M = mod->M;
76
77
  size_t NM = N * M;
77
78
  size_t max_record = mod->max_record;
78
- size_t ij = 0; // used to track index i * M + j in double for loops
79
+ size_t ij_out = 0; // used to track index i * M + j in double for loops, "out" means not the "looper" in for loop
79
80
 
80
81
  // init world
81
82
  for (size_t i = 0; i < N; i++) {
82
83
  for (size_t j = 0; j < M; j++) {
83
- patch_init(&world[ij], mod->I[ij * 2], mod->I[ij * 2 + 1], i, j);
84
- ij++;
84
+ patch_init(&world[ij_out], mod->I[ij_out * 2], mod->I[ij_out * 2 + 1], i, j);
85
+ ij_out++;
85
86
  }
86
87
  }
87
88
 
88
89
  // init nb_indices
89
- ij = 0;
90
+ ij_out = 0;
90
91
  if (mod->boundary) {
91
92
  for (size_t i = 0; i < N; i++) {
92
93
  for (size_t j = 0; j < M; j++) {
93
- find_nb_zero_flux(&nb_indices[ij * 4], i, j, N, M, NM);
94
- ij++;
94
+ find_nb_zero_flux(&nb_indices[ij_out * 4], i, j, N, M, NM);
95
+ ij_out++;
95
96
  }
96
97
  }
97
98
  } else {
98
99
  for (size_t i = 0; i < N; i++) {
99
100
  for (size_t j = 0; j < M; j++) {
100
- find_nb_periodical(&nb_indices[ij * 4], i, j, N, M, NM);
101
- ij++;
101
+ find_nb_periodical(&nb_indices[ij_out * 4], i, j, N, M, NM);
102
+ ij_out++;
102
103
  }
103
104
  }
104
105
  }
105
106
 
106
107
 
107
108
  // set nb pointers for patches
108
- ij = 0;
109
- for (size_t i = 0; i < N; i++) {
110
- for (size_t j = 0; j < M; j++) {
111
- set_nb(world, &nb_indices[ij * 4], ij, NM);
112
- ij++;
113
- }
109
+ for (size_t ij = 0; ij < NM; ij++) {
110
+ set_nb(world, &(mod->P[ij * 6]), &nb_indices[ij * 4], ij, NM);
114
111
  }
115
112
 
116
113
  //////// Begin Running ////////
117
114
 
118
115
  // init payoff & natural death rates
119
- ij = 0;
120
- for (size_t i = 0; i < N; i++) {
121
- for (size_t j = 0; j < M; j++) {
122
- update_pi_k(&world[ij], &(mod->X[ij * 4]), &(mod->P[ij * 6]));
123
- ij++;
124
- }
116
+ for (size_t ij = 0; ij < N; ij++) {
117
+ update_pi_k(&world[ij], &(mod->X[ij * 4]), &(mod->P[ij * 6]));
125
118
  }
126
119
 
127
120
  // init migration rates & store patch rates
128
- ij = 0;
121
+ ij_out = 0;
129
122
  for (size_t i = 0; i < N; i++) {
130
123
  for (size_t j = 0; j < M; j++) {
131
- uint8_t mig_result = init_mig(&world[ij], &(mod->P[ij * 6])); // init mig rates for all 4 directions
124
+ uint8_t mig_result = init_mig(&world[ij_out], &(mod->P[ij_out * 6])); // init mig rates for all 4 directions
132
125
  if (mig_result == SIM_OVERFLOW) {
133
126
  return -1 * SIM_OVERFLOW;
134
127
  }
135
- double ij_rates = world[ij].sum_pi_death_rates + world[ij].sum_mig_rates;
136
- patch_rates[ij] = ij_rates;
128
+ double ij_rates = world[ij_out].sum_pi_death_rates + world[ij_out].sum_mig_rates;
129
+ patch_rates[ij_out] = ij_rates;
137
130
  sum_rates_by_row[i] += ij_rates;
138
131
  *sum_rates_p = *sum_rates_p + ij_rates; // can't do *sum_rates_p += ij_rates
139
- ij++;
132
+ ij_out++;
140
133
  }
141
134
  }
142
135
 
@@ -145,6 +138,9 @@ static double single_init(const model_t* mod, patch_t* world, size_t* nb_indices
145
138
  find_patch(picked_p, expected_sum, patch_rates, sum_rates_by_row, *sum_rates_p, N, M);
146
139
  size_t picked_idx = picked_p->i * M + picked_p->j;
147
140
  size_t e0 = find_event(&world[picked_idx], expected_sum - picked_p->current_sum);
141
+ if (picked_idx >= NM || e0 >= 12) {
142
+ return -1 * ACCURACY_ERROR;
143
+ }
148
144
 
149
145
  // make signal
150
146
  if (mod->boundary) {
@@ -172,18 +168,16 @@ static double single_init(const model_t* mod, patch_t* world, size_t* nb_indices
172
168
  // store data
173
169
  if (time > mod->record_itv) {
174
170
  size_t recod_idx = (size_t) (time / mod->record_itv);
175
- ij = 0;
176
- for (size_t i = 0; i < N; i++) {
177
- for (size_t j = 0; j < M; j++) {
178
- for (size_t k = 0; k < recod_idx; k++) {
179
- mod->U1d[ij * max_record + k] += world[ij].U;
180
- mod->V1d[ij * max_record + k] += world[ij].V;
181
- mod->Upi_1d[ij * max_record + k] += world[ij].U_pi;
182
- mod->Vpi_1d[ij * max_record + k] += world[ij].V_pi;
183
- }
184
- ij++;
171
+
172
+ for (size_t ij = 0; ij < NM; ij++) {
173
+ size_t ij_max_record = ij * max_record;
174
+ for (size_t k = 0; k < recod_idx; k++) {
175
+ mod->U1d[ij_max_record + k] += world[ij].U;
176
+ mod->V1d[ij_max_record + k] += world[ij].V;
177
+ mod->Upi_1d[ij_max_record + k] += world[ij].U_pi;
178
+ mod->Vpi_1d[ij_max_record + k] += world[ij].V_pi;
185
179
  }
186
- }
180
+ }
187
181
  }
188
182
 
189
183
  return time;
@@ -200,10 +194,15 @@ static uint8_t single_test(model_t* restrict mod, char* message) {
200
194
  size_t max_record = mod->max_record;
201
195
  double record_itv = mod->record_itv;
202
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
203
 
204
204
  // print progress
205
205
  double one_progress = 0.0;
206
- double current_progress = one_progress;
207
206
  if (mod->print_pct != -1) {
208
207
  one_progress = maxtime * mod->print_pct / 100.0;
209
208
  fprintf(stdout, "\r ");
@@ -212,12 +211,13 @@ static uint8_t single_test(model_t* restrict mod, char* message) {
212
211
  } else {
213
212
  one_progress = 2.0 * maxtime;
214
213
  }
214
+ double current_progress = one_progress;
215
215
 
216
216
  // update sum of rates every 1e5 rounds
217
217
  // many rates are updated each time, rather than re-calculated.
218
- // So need to re-calculate from scratch every some time to reduce numerical errors
218
+ // So need to re-calculate from scratch every some rounds to reduce numerical errors
219
219
  size_t curr_update_sum_round = 0; // current round
220
- size_t update_sum_freq = UPDATE_SUM_FREQ_SM; // the frequency really using, change based on how large values are encountered
220
+ size_t update_sum_freq = UPDATE_SUM_ROUNDS_SM; // recalculate sum every this many rounds
221
221
 
222
222
  // Initialize simulation
223
223
  patch_t* world = (patch_t*) calloc(NM, sizeof(patch_t));
@@ -242,57 +242,59 @@ static uint8_t single_test(model_t* restrict mod, char* message) {
242
242
  fflush(stdout);
243
243
  single_test_free(&world, &nb_indices, &patch_rates, &sum_rates_by_row);
244
244
  return SIM_OVERFLOW;
245
+ } else if (time == -1 * ACCURACY_ERROR) {
246
+ fprintf(stdout, "\nError: accuracy too low at t = 0, simulation stopped\n");
247
+ fflush(stdout);
248
+ single_test_free(&world, &nb_indices, &patch_rates, &sum_rates_by_row);
249
+ return ACCURACY_ERROR;
245
250
  }
246
- size_t record_index = time / mod->record_itv;
251
+ size_t record_index = (size_t) (time / mod->record_itv);
247
252
  double record_time = time - record_index * record_itv;
248
253
 
249
254
 
250
255
  while (time < maxtime) {
251
-
252
- // Print progress
253
- if (time > current_progress) {
254
- uint8_t curr_prog = (uint8_t)(time * 100 / maxtime);
255
- if (curr_prog < 10) {
256
- fprintf(stdout, "\r%s: %d %%", message, (int)(time * 100 / maxtime));
257
- } else {
258
- fprintf(stdout, "\r%s: %d%%", message, (int)(time * 100 / maxtime));
259
- }
260
- fflush(stdout);
261
- //fflush(stdout); // Make sure it prints immediately
262
- current_progress += one_progress;
263
- }
264
256
 
265
- // update sums
257
+ // update sums and print progress
266
258
  curr_update_sum_round++;
267
259
  if (curr_update_sum_round > update_sum_freq) {
268
260
  curr_update_sum_round = 0;
269
- update_sum_freq = UPDATE_SUM_FREQ_LG; // assume can make it larger
270
- size_t ij = 0;
271
261
 
272
- for (size_t i = 0; i < N; i++) {
273
- for (size_t j = 0; j < M; j++) {
274
- double sum_U_weight = 0;
275
- double sum_V_weight = 0;
276
- for (size_t k = 0; k < 4; k++) {
277
- sum_U_weight += world[ij].U_weight[k];
278
- sum_V_weight += world[ij].V_weight[k];
279
- }
280
- world[ij].sum_U_weight = sum_U_weight;
281
- world[ij].sum_V_weight = sum_V_weight;
282
- // patch_rates are updated every time a patch is changed
283
- if (sum_U_weight > ACCURATE_BOUND) {
284
- update_sum_freq = UPDATE_SUM_FREQ_SM; // values too large, put back the small update frequency
285
- }
286
- ij++;
262
+ // Print progress
263
+ if (time > current_progress) {
264
+ uint8_t curr_prog = (uint8_t)(time * 100 / maxtime);
265
+ if (curr_prog < 10) {
266
+ fprintf(stdout, "\r%s: %d %%", message, (int)(time * 100 / maxtime));
267
+ } else {
268
+ fprintf(stdout, "\r%s: %d%%", message, (int)(time * 100 / maxtime));
269
+ }
270
+ fflush(stdout);
271
+ //fflush(stdout); // Make sure it prints immediately
272
+ current_progress += one_progress;
273
+ }
274
+
275
+ // update sum
276
+ update_sum_freq = UPDATE_SUM_ROUNDS_LG; // assume can make it larger
277
+ for (size_t ij = 0; ij < NM; ij++) {
278
+ double sum_U_weight = 0;
279
+ double sum_V_weight = 0;
280
+ for (size_t k = 0; k < 4; k++) {
281
+ sum_U_weight += world[ij].U_weight[k];
282
+ sum_V_weight += world[ij].V_weight[k];
287
283
  }
284
+ if (sum_U_weight > ACCURATE_BOUND || sum_V_weight > ACCURATE_BOUND) {
285
+ update_sum_freq = UPDATE_SUM_ROUNDS_SM; // values too large, put back the small update frequency
286
+ }
287
+ world[ij].sum_U_weight = sum_U_weight;
288
+ world[ij].sum_V_weight = sum_V_weight;
289
+ // patch_rates are updated every time a patch is changed
288
290
  }
289
- ij = 0;
291
+ size_t ij_out = 0;
290
292
  sum_rates = 0;
291
293
  for (size_t i = 0; i < N; i++) {
292
294
  double sum_rates_by_row_i = 0;
293
295
  for (size_t j = 0; j < M; j++) {
294
- sum_rates_by_row_i += patch_rates[ij];
295
- ij++;
296
+ sum_rates_by_row_i += patch_rates[ij_out];
297
+ ij_out++;
296
298
  }
297
299
  sum_rates_by_row[i] = sum_rates_by_row_i;
298
300
  sum_rates += sum_rates_by_row_i;
@@ -307,13 +309,14 @@ static uint8_t single_test(model_t* restrict mod, char* message) {
307
309
  size_t sij1 = signal.ij1;
308
310
  size_t sij2 = signal.ij2;
309
311
  uint8_t rela_loc = signal.rela_loc;
312
+
310
313
  if (rela_loc == NO_MIG) {
311
314
  // if only one
312
315
  sum_rates_by_row[si1] -= patch_rates[sij1];
313
316
  sum_rates -= patch_rates[sij1];
314
317
 
315
- update_pi_k(&world[sij1], &(mod->X[sij1 * 4]), &(mod->P[sij1 * 6]));
316
- update_mig_just_rate(&world[sij1], &(mod->P[sij1 * 6]));
318
+ update_pi_k(&world[sij1], &(X[sij1 * 4]), &(P[sij1 * 6]));
319
+ update_mig_just_rate(&world[sij1], &(P[sij1 * 6]));
317
320
 
318
321
  patch_rates[sij1] = world[sij1].sum_pi_death_rates + world[sij1].sum_mig_rates;
319
322
  sum_rates_by_row[si1] += patch_rates[sij1];
@@ -325,11 +328,11 @@ static uint8_t single_test(model_t* restrict mod, char* message) {
325
328
  sum_rates -= patch_rates[sij1];
326
329
  sum_rates -= patch_rates[sij2];
327
330
 
328
- update_pi_k(&world[sij1], &(mod->X[sij1 * 4]), &(mod->P[sij1 * 6])); // update both patches' payoffs first
329
- update_pi_k(&world[sij2], &(mod->X[sij2 * 4]), &(mod->P[sij2 * 6]));
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]));
330
333
 
331
- if (update_mig_weight_rate(&world[sij1], &(mod->P[sij1 * 6]), rela_loc) == SIM_OVERFLOW ||
332
- update_mig_weight_rate(&world[sij2], &(mod->P[sij2 * 6]), rela_loc ^ 1) == SIM_OVERFLOW) {
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) {
333
336
 
334
337
  fprintf(stdout, "\nError: overflow at t = %f\n", time);
335
338
  fflush(stdout);
@@ -351,7 +354,7 @@ static uint8_t single_test(model_t* restrict mod, char* message) {
351
354
  size_t nb_idx = nb_indices[sij1 * 4 + k];
352
355
  if (nb_idx == NM) { continue; } // invalid neighbor
353
356
  // all neighbors, as long as exists, need to change
354
- if (update_mig_weight_rate(&world[nb_idx], &(mod->P[nb_idx * 6]), k ^ 1) == SIM_OVERFLOW) {
357
+ if (update_mig_weight_rate(&world[nb_idx], &(P[nb_idx * 6]), k ^ 1) == SIM_OVERFLOW) {
355
358
  fprintf(stdout, "\nError: overflow at t = %f\n", time);
356
359
  fflush(stdout);
357
360
  single_test_free(&world, &nb_indices, &patch_rates, &sum_rates_by_row);
@@ -366,7 +369,7 @@ static uint8_t single_test(model_t* restrict mod, char* message) {
366
369
  if (nb_idx == NM) { continue; }
367
370
  if (k != rela_loc) {
368
371
  // nb_idx isn't the second last-changed patch
369
- if (update_mig_weight_rate(&world[nb_idx], &(mod->P[nb_idx * 6]), k ^ 1) == SIM_OVERFLOW) {
372
+ if (update_mig_weight_rate(&world[nb_idx], &(P[nb_idx * 6]), k ^ 1) == SIM_OVERFLOW) {
370
373
  fprintf(stdout, "\nError: overflow at t = %f\n", time);
371
374
  fflush(stdout);
372
375
  single_test_free(&world, &nb_indices, &patch_rates, &sum_rates_by_row);
@@ -380,7 +383,7 @@ static uint8_t single_test(model_t* restrict mod, char* message) {
380
383
  if (nb_idx == NM) { continue; }
381
384
  if (k != (rela_loc ^ 1)) {
382
385
  // nb_idx isn't the first last-changed patch
383
- if (update_mig_weight_rate(&world[nb_idx], &(mod->P[nb_idx * 6]), k ^ 1) == SIM_OVERFLOW) {
386
+ if (update_mig_weight_rate(&world[nb_idx], &(P[nb_idx * 6]), k ^ 1) == SIM_OVERFLOW) {
384
387
  fprintf(stdout, "\nError: overflow at t = %f\n", time);
385
388
  fflush(stdout);
386
389
  single_test_free(&world, &nb_indices, &patch_rates, &sum_rates_by_row);
@@ -396,6 +399,12 @@ static uint8_t single_test(model_t* restrict mod, char* message) {
396
399
  find_patch(&picked, expected_sum, patch_rates, sum_rates_by_row, sum_rates, N, M);
397
400
  size_t picked_idx = picked.i * M + picked.j;
398
401
  uint8_t e0 = find_event(&world[picked_idx], expected_sum - picked.current_sum);
402
+ if (picked_idx >= NM || e0 >= 12) {
403
+ fprintf(stdout, "\nError: accuracy too low at t = %f, simulation stopped\n", time);
404
+ fflush(stdout);
405
+ single_test_free(&world, &nb_indices, &patch_rates, &sum_rates_by_row);
406
+ return ACCURACY_ERROR;
407
+ }
399
408
 
400
409
  // make signal
401
410
  if (boundary) {
@@ -424,16 +433,13 @@ static uint8_t single_test(model_t* restrict mod, char* message) {
424
433
  record_time -= multi_records * record_itv;
425
434
  size_t upper = record_index + multi_records;
426
435
 
427
- size_t ij = 0;
428
- for (size_t i = 0; i < N; i++) {
429
- for (size_t j = 0; j < M; j++) {
430
- for (size_t k = record_index; k < upper; k++) {
431
- mod->U1d[ij * max_record + k] += world[ij].U;
432
- mod->V1d[ij * max_record + k] += world[ij].V;
433
- mod->Upi_1d[ij * max_record + k] += world[ij].U_pi;
434
- mod->Vpi_1d[ij * max_record + k] += world[ij].V_pi;
435
- }
436
- ij++;
436
+ for (size_t ij = 0; ij < NM; ij++) {
437
+ size_t ij_max_record = ij * max_record;
438
+ 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;
437
443
  }
438
444
  }
439
445
  record_index += multi_records;
@@ -441,16 +447,13 @@ static uint8_t single_test(model_t* restrict mod, char* message) {
441
447
 
442
448
  } else {
443
449
  // if already exceeds maxtime
444
- size_t ij = 0;
445
- for (size_t i = 0; i < N; i++) {
446
- for (size_t j = 0; j < M; j++) {
447
- for (size_t k = record_index; k < max_record; k++) {
448
- mod->U1d[ij * max_record + k] += world[ij].U;
449
- mod->V1d[ij * max_record + k] += world[ij].V;
450
- mod->Upi_1d[ij * max_record + k] += world[ij].U_pi;
451
- mod->Vpi_1d[ij * max_record + k] += world[ij].V_pi;
452
- }
453
- ij++;
450
+ for (size_t ij = 0; ij < NM; ij++) {
451
+ size_t ij_max_record = ij * max_record;
452
+ 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;
454
457
  }
455
458
  }
456
459
  }
@@ -483,7 +486,7 @@ static void single_test_free(patch_t** world, size_t** nb_indices, double** patc
483
486
 
484
487
 
485
488
 
486
- uint8_t run(model_t* mod, char* message, size_t msg_len) {
489
+ uint8_t run(model_t* restrict mod, char* message, size_t msg_len) {
487
490
  if (!mod->data_empty) {
488
491
  // this won't happen if called from python, the ``simulation.run`` caller has checked it.
489
492
  fprintf(stdout, "Error: mod has non-empty data\n");
@@ -538,6 +541,9 @@ uint8_t run(model_t* mod, char* message, size_t msg_len) {
538
541
  case SIM_OVERFLOW:
539
542
  // error message is handled by single_test
540
543
  return SIM_OVERFLOW;
544
+ case ACCURACY_ERROR:
545
+ // error message is handled by single_test
546
+ return ACCURACY_ERROR;
541
547
  }
542
548
  }
543
549
 
@@ -545,8 +551,10 @@ uint8_t run(model_t* mod, char* message, size_t msg_len) {
545
551
 
546
552
  double stop = clock();
547
553
 
548
- fprintf(stdout, "\r%sruntime: %.3fs \n", message, (double)(stop - start) / CLOCKS_PER_SEC);
549
- fflush(stdout);
554
+ if (mod->print_pct != -1) {
555
+ fprintf(stdout, "\r%sruntime: %.3fs \n", message, (double)(stop - start) / CLOCKS_PER_SEC);
556
+ fflush(stdout);
557
+ }
550
558
  return SUCCESS;
551
559
  }
552
560
 
@@ -18,8 +18,8 @@
18
18
 
19
19
 
20
20
 
21
- // upper bound for RNG is 2^24 + 1
22
- #define RAND_UPPER_PLUS_2 (double) (0x3fffffffu + 2)
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
@@ -35,19 +35,25 @@
35
35
  #define DATA_NOT_EMPTY 1
36
36
  #define SMALL_MAXTIME 2
37
37
  #define SIM_OVERFLOW 3
38
+ #define ACCURACY_ERROR 4
39
+
40
+
41
+ /**
42
+ * Handling Numerical Errors:
43
+ * half of the rates / values related to rates are calculated on a cumulative basis. i.e., updated over time instead of re-calculating from scratch
44
+ * numerival errors might explode in this case, so need to re-calculate every sometime
45
+ */
38
46
 
39
47
  // where exp(x) is considered overflow
40
- // below the actual bound (709) because the large numbers will be computed with close-to-0 ones (payoff rates), so higher accuracy is needed
48
+ // below the actual bound (709) to preserve accuracy
41
49
  #define EXP_OVERFLOW_BOUND 500
42
- #define ACCURATE_BOUND 10000000000LL
43
-
44
- // how frequent to update rates & sum of rates in single test (recalculate)
45
- #define UPDATE_SUM_FREQ_SM 100
46
- #define UPDATE_SUM_FREQ_LG 10000
47
50
 
51
+ // Compare current rates with this bound
52
+ #define ACCURATE_BOUND 1LL << 33 // about 8.6 * 10^9
48
53
 
49
- static uint64_t pcg_state = 0;
50
- static uint64_t pcg_inc = 0;
54
+ // how frequent to update rates & sum of rates in single test (recalculate)
55
+ #define UPDATE_SUM_ROUNDS_SM 100 // small, more frequent if some rate is larger than ACCURATE_BOUND
56
+ #define UPDATE_SUM_ROUNDS_LG 10000
51
57
 
52
58
 
53
59
 
@@ -90,11 +96,12 @@ typedef struct signal_t {
90
96
  */
91
97
  static void find_nb_zero_flux(size_t* restrict nb, size_t i, size_t j, size_t N, size_t M, size_t NM);
92
98
  static void find_nb_periodical(size_t* restrict nb, size_t i, size_t j, size_t N, size_t M, size_t NM);
93
- static double single_init(const model_t* mod, patch_t* world, size_t* nb_indices,
94
- double* patch_rates, double* sum_rates_by_row, double* sum_rates, signal_t* sig_p, patch_picked_t* picked_p) ;
99
+ static double single_init(const model_t* restrict mod, patch_t* restrict world, size_t* restrict nb_indices,
100
+ double* restrict patch_rates, double* restrict sum_rates_by_row, double* restrict sum_rates_p,
101
+ signal_t* restrict sig_p, patch_picked_t* restrict picked_p);
95
102
  static uint8_t single_test(model_t* restrict mod, char* message);
96
103
  static void single_test_free(patch_t** world, size_t** nb_indices, double** patch_rates, double** sum_rates_by_row);
97
- uint8_t run(model_t* mod, char* message, size_t msg_len);
104
+ uint8_t run(model_t* restrict mod, char* message, size_t msg_len);
98
105
 
99
106
 
100
107
 
@@ -107,7 +114,7 @@ static inline double random01() {
107
114
  uint32_t r1 = rand() & 0x7fff; // RAND_MAX is different across machines, ensure 15 bits
108
115
  uint32_t r2 = rand() & 0x7fff;
109
116
 
110
- double r_combined = (r1 << 15) + r2; // discard the lower 3 bits, which are unstable
117
+ double r_combined = (r1 << 15) + r2;
111
118
  return (r_combined + 1) / RAND_UPPER_PLUS_2;
112
119
  }
113
120
 
@@ -119,8 +126,8 @@ static inline void update_pi_k(patch_t* restrict p, const double* restrict M_sta
119
126
  // M_start: start index of patch (i, j)'s matrix
120
127
  // P_start: start index of p's patch variables, i.e., ij * 6
121
128
 
122
- double U = (double) p->U;
123
- double V = (double) p->V;
129
+ uint32_t U = p->U;
130
+ uint32_t V = p->V;
124
131
  double sum = U + V;
125
132
  double U_ratio = U / sum;
126
133
  double V_ratio = V / sum;
@@ -163,8 +170,8 @@ static inline void update_mig_just_rate(patch_t* restrict p, const double* restr
163
170
  double* p_U_weight = p->U_weight;
164
171
  double* p_V_weight = p->V_weight;
165
172
 
166
- double mu1_U = P_start[0] * (double)p->U;
167
- double mu2_V = P_start[1] * (double)p->V;
173
+ double mu1_U = P_start[0] * p->U;
174
+ double mu2_V = P_start[1] * p->V;
168
175
 
169
176
  double mu1_U_divide_sum = mu1_U / p->sum_U_weight;
170
177
  double mu2_V_divide_sum = mu2_V / p->sum_V_weight;
@@ -347,9 +354,6 @@ static inline void change_popu(patch_t* restrict p, uint8_t s) {
347
354
  p->V -= 1;
348
355
  }
349
356
  return;
350
- //default:
351
- // fprintf(stderr, "Bug: invalid event number in change_popu: %hhu\n", s);
352
- // return;
353
357
  }
354
358
  }
355
359
 
@@ -1,4 +1,4 @@
1
- __version__ = '2.3.4'
1
+ __version__ = '2.3.7'
2
2
 
3
3
  '''
4
4
  version history:
@@ -42,4 +42,6 @@ version history:
42
42
  2.3.3: fix error in calculation of migration rates.
43
43
  2.3.4: change back to the mig & payoff rules in version 2.3.2
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
+ 2.3.6: index error due to reduced accuracy is now explicitly handled.
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.
45
47
  '''
@@ -29,12 +29,8 @@ def save_data(mod, dirs = '', print_msg = True):
29
29
  except AttributeError:
30
30
  raise ValueError('mod is not a model object')
31
31
 
32
- if dirs != '':
33
- # add slash '/'
34
- if dirs[:-1] != '/':
35
- dirs += '/'
36
- if not os.path.exists(dirs):
37
- os.makedirs(dirs)
32
+ if not os.path.exists(dirs):
33
+ os.makedirs(dirs)
38
34
 
39
35
  data = []
40
36
 
@@ -64,14 +60,11 @@ def save_data(mod, dirs = '', print_msg = True):
64
60
  outputs.append(mod.Vpi.tolist())
65
61
  # H&Vpi_total are not saved, will be calculated when reading the data
66
62
  data.append(outputs)
67
-
68
- data_json = json.dumps(data)
69
- data_bytes = data_json.encode('utf-8')
70
- data_dirs = dirs + 'data.json.gz'
71
-
72
- with gzip.open(data_dirs, 'w') as f:
73
- f.write(data_bytes)
74
-
63
+
64
+ data_dirs = os.path.join(dirs, 'data.json.gz')
65
+ with gzip.open(data_dirs, 'wb') as f:
66
+ f.write(json.dumps(data).encode('utf-8'))
67
+
75
68
  if print_msg:
76
69
  print('data saved: ' + data_dirs)
77
70
 
@@ -89,35 +82,30 @@ def read_data(dirs):
89
82
  - mod: a piegy.model.model object read from the data.
90
83
  '''
91
84
 
92
- if dirs != '':
93
- # add slash '/'
94
- if dirs[:-1] != '/':
95
- dirs += '/'
96
- if not os.path.exists(dirs):
97
- raise FileNotFoundError('dirs not found: ' + dirs)
98
-
99
- if not os.path.isfile(dirs + 'data.json.gz'):
85
+ if not os.path.exists(dirs):
86
+ raise FileNotFoundError('dirs not found: ' + dirs)
87
+
88
+ data_dirs = os.path.join(dirs, 'data.json.gz')
89
+ if not os.path.isfile(data_dirs):
100
90
  raise FileNotFoundError('data not found in ' + dirs)
101
91
 
102
- with gzip.open(dirs + 'data.json.gz', 'r') as f:
103
- data_bytes = f.read()
104
- data_json = data_bytes.decode('utf-8')
105
- data = json.loads(data_json)
106
-
107
- # inputs
108
- try:
109
- mod = simulation.model(N = data[0][0], M = data[0][1], maxtime = data[0][2], record_itv = data[0][3],
110
- sim_time = data[0][4], boundary = data[0][5], I = data[0][6], X = data[0][7], P = data[0][8],
111
- print_pct = data[0][9], seed = data[0][10], check_overflow = data[0][11])
112
- except:
113
- raise ValueError('Invalid input parameters saved in data')
114
-
115
- # outputs
116
- try:
117
- mod.set_data(data_empty = False, max_record = data[1][0], compress_itv = data[1][1],
118
- U = data[1][2], V = data[1][3], Upi = data[1][4], Vpi = data[1][5])
119
- except:
120
- raise ValueError('Invalid model results saved in data')
92
+ with gzip.open(data_dirs, 'rb') as f:
93
+ data = json.loads(f.read().decode('utf-8'))
94
+
95
+ # inputs
96
+ try:
97
+ mod = simulation.model(N = data[0][0], M = data[0][1], maxtime = data[0][2], record_itv = data[0][3],
98
+ 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])
100
+ except:
101
+ raise ValueError('Invalid input parameters saved in data')
102
+
103
+ # outputs
104
+ try:
105
+ mod.set_data(data_empty = False, max_record = data[1][0], compress_itv = data[1][1],
106
+ U = data[1][2], V = data[1][3], Upi = data[1][4], Vpi = data[1][5])
107
+ except:
108
+ raise ValueError('Invalid model results saved in data')
121
109
 
122
110
  return mod
123
111
 
@@ -332,15 +332,15 @@ def pi_bar(mod, ax_U = None, ax_V = None, U_color = 'violet', V_color = 'yellowg
332
332
  end_index = int(mod.max_record * end)
333
333
 
334
334
  Upi_ave = figure_t.ave_interval_1D(mod.Upi, start_index, end_index)
335
- V_pi_ave = figure_t.ave_interval_1D(mod.Vpi, start_index, end_index)
335
+ Vpi_ave = figure_t.ave_interval_1D(mod.Vpi, start_index, end_index)
336
336
 
337
337
  U_title = figure_t.gen_title(r'$p_H$', start, end)
338
338
  U_text = figure_t.gen_text(np.mean(Upi_ave), np.std(Upi_ave))
339
339
  V_title = figure_t.gen_title(r'$p_D$', start, end)
340
- V_text = figure_t.gen_text(np.mean(V_pi_ave), np.std(V_pi_ave))
340
+ V_text = figure_t.gen_text(np.mean(Vpi_ave), np.std(Vpi_ave))
341
341
 
342
342
  ax_U = figure_t.bar(Upi_ave, ax_U, U_color, 'Patches', 'Payoff ' + r'$p_H$', U_title, U_text)
343
- ax_V = figure_t.bar(V_pi_ave, ax_V, V_color, 'Patches', 'Payoff ' + r'$p_D$', V_title, V_text)
343
+ ax_V = figure_t.bar(Vpi_ave, ax_V, V_color, 'Patches', 'Payoff ' + r'$p_D$', V_title, V_text)
344
344
 
345
345
  return ax_U, ax_V
346
346
 
@@ -25,7 +25,7 @@ from numpy.ctypeslib import ndpointer
25
25
  # check whether overflow / too large values might be encountered
26
26
  # these values are considered as exponents in exp()
27
27
  EXP_OVERFLOW_BOUND = 709 # where exp(x) almost reaches overflow bound
28
- EXP_TOO_LARGE_BOUND = 88 # where exp(x) reaches 1e34
28
+ EXP_TOO_LARGE_BOUND = 30 # where exp(x) reaches ~10e13 and accuracy goes below 10^-3 (10^-3 isn't accurate any more)
29
29
 
30
30
 
31
31
  # read the C core into LIB
@@ -36,6 +36,7 @@ SIM_SUCCESS = 0
36
36
  SIM_DATA_EMPTY = 1
37
37
  SIM_SMALL_MAXTIME = 2
38
38
  SIM_OVERFLOW = 3
39
+ ACCURACY_ERROR = 4
39
40
 
40
41
 
41
42
  '''
@@ -449,7 +450,11 @@ def run(mod, message = ""):
449
450
  elif result == SIM_OVERFLOW:
450
451
  LIB.mod_free_py(ctypes.byref(mod_c))
451
452
  del mod_c
452
- raise OverflowError('Overflow in simulation')
453
+ raise OverflowError('Overflow in simulation. Possibly due to too large w1, w2, or payoff.')
454
+ elif result == ACCURACY_ERROR:
455
+ LIB.mod_free_py(ctypes.byref(mod_c))
456
+ del mod_c
457
+ raise RuntimeError('Accuracy dropped catastrophically during simulation. Possibly due to too large w1, w2, or payoff.')
453
458
  else:
454
459
  LIB.mod_free_py(ctypes.byref(mod_c))
455
460
  del mod_c
@@ -524,9 +529,9 @@ def check_overflow_func(mod):
524
529
  w1_pi = pi_expected[i][j] * mod.P[i][j][2] # w1 * U_pi
525
530
  w2_pi = pi_expected[i][j] * mod.P[i][j][3] # w2 * V_pi
526
531
  if ((w1_pi > EXP_OVERFLOW_BOUND) or (w2_pi > EXP_OVERFLOW_BOUND)):
527
- print("Warning: might cause overflow. \n\t w1, w2, or payoff matrix values too large")
532
+ print("Warning: might cause overflow in simulation. w1, w2, or payoff matrix values too large")
528
533
  return
529
534
  if ((w1_pi > EXP_TOO_LARGE_BOUND) or (w2_pi > EXP_TOO_LARGE_BOUND)):
530
- print("Warning: might encounter large values > 3e38 in simulation. \n\t w1, w2, or payoff matrix values too large")
535
+ print("Warning: might have low accuracy in simulation. w1, w2, or payoff matrix values too large")
531
536
  return
532
537
 
@@ -92,8 +92,9 @@ def test_var1(mod, var, values, dirs, compress_itv = None):
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)
95
- except OverflowError:
96
- print(current_var_str + ' raised OverflowError, skipped')
95
+ del sim2
96
+ except (OverflowError, RuntimeError):
97
+ print(current_var_str + ' raised error, skipped')
97
98
  continue
98
99
 
99
100
  return var_dirs
@@ -139,8 +140,9 @@ def test_var2(mod, var1, var2, values1, values2, dirs, compress_itv = None):
139
140
  if compress_itv != None:
140
141
  sim2.compress_data(compress_itv)
141
142
  data_t.save_data(sim2, var_dirs[k1][k2], print_msg = False)
142
- except OverflowError:
143
- print(current_var_str + ' raised OverflowError, skipped')
143
+ del sim2
144
+ except (OverflowError, RuntimeError):
145
+ print(current_var_str + ' raised error, skipped')
144
146
  continue
145
147
 
146
148
  return var_dirs
@@ -185,6 +187,7 @@ def var_UV1(var, values, var_dirs, ax_U = None, ax_V = None, start = 0.95, end =
185
187
 
186
188
  U_ave.append(sum(figure_t.ave_interval_1D(simk.U, start_index, end_index)) / NM)
187
189
  V_ave.append(sum(figure_t.ave_interval_1D(simk.V, start_index, end_index)) / NM)
190
+ del simk
188
191
 
189
192
  #### plot ####
190
193
  if ax_U == None:
@@ -253,6 +256,7 @@ def var_UV2(var1, var2, values1, values2, var_dirs, ax_U = None, ax_V = None, va
253
256
 
254
257
  U_ave[k1].append(sum(figure_t.ave_interval_1D(simk.U, start_index, end_index)) / NM)
255
258
  V_ave[k1].append(sum(figure_t.ave_interval_1D(simk.V, start_index, end_index)) / NM)
259
+ del simk
256
260
 
257
261
  U_ave = np.array(U_ave)
258
262
  V_ave = np.array(V_ave)
@@ -340,7 +344,6 @@ def var_pi1(var, values, var_dirs, ax_U = None, ax_V = None, start = 0.95, end =
340
344
 
341
345
  U_ave.append(np.sum(figure_t.ave_interval(simk.Upi, start_index, end_index)) / NM)
342
346
  V_ave.append(np.sum(figure_t.ave_interval(simk.Vpi, start_index, end_index)) / NM)
343
-
344
347
  del simk
345
348
 
346
349
  #### plot ####
@@ -406,8 +409,7 @@ def var_pi2(var1, var2, values1, values2, var_dirs, ax_U = None, ax_V = None, va
406
409
 
407
410
  U_ave[k1].append(np.sum(figure_t.ave_interval(simk.Upi, start_index, end_index)) / NM)
408
411
  V_ave[k1].append(np.sum(figure_t.ave_interval(simk.Vpi, start_index, end_index)) / NM)
409
-
410
- del simk # manually delete this large object
412
+ del simk
411
413
 
412
414
  U_ave = np.array(U_ave)
413
415
  V_ave = np.array(V_ave)
@@ -507,14 +509,16 @@ def var_convergence1(var_dirs, interval = 20, start = 0.8, fluc = 0.07):
507
509
 
508
510
  diverge_list = []
509
511
 
510
- for dirs in var_dirs:
512
+ for k in range(len(var_dirs)):
513
+ dirs = var_dirs[k]
511
514
  try:
512
- mod = data_t.read_data(dirs)
515
+ simk = data_t.read_data(dirs)
513
516
  except FileNotFoundError:
514
517
  print(dirs + ' data not found, skipped')
515
518
  continue
516
- if not analysis.check_convergence(mod, interval, start, fluc):
519
+ if not analysis.check_convergence(simk, interval, start, fluc):
517
520
  diverge_list.append(dirs)
521
+ del simk
518
522
 
519
523
  return diverge_list
520
524
 
@@ -574,14 +578,16 @@ def var_convergence2(var_dirs, interval = 20, start = 0.8, fluc = 0.07):
574
578
  diverge_list = []
575
579
 
576
580
  for sublist in var_dirs:
577
- for dirs in sublist:
581
+ for k in range(len(sublist)):
582
+ dirs = sublist[k]
578
583
  try:
579
- mod = data_t.read_data(dirs)
584
+ simk = data_t.read_data(dirs)
580
585
  except FileNotFoundError:
581
586
  print(dirs + ' data not found, skipped')
582
587
  continue
583
- if not analysis.check_convergence(mod, interval, start, fluc):
588
+ if not analysis.check_convergence(simk, interval, start, fluc):
584
589
  diverge_list.append(dirs)
590
+ del simk
585
591
 
586
592
  return diverge_list
587
593
 
@@ -187,7 +187,7 @@ def ave_interval_1D(data, start_index, end_index):
187
187
  M = len(data[0])
188
188
 
189
189
  if start_index == end_index:
190
- start_index = end_index - 1
190
+ end_index = start_index + 1 # exactly one record
191
191
 
192
192
  data_ave = np.zeros(N * M)
193
193
 
@@ -9,9 +9,8 @@ def find_C():
9
9
  C_core_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'C_core')
10
10
  C_list = os.listdir(C_core_path)
11
11
  for file in C_list:
12
- if file[-3:] == '.so':
13
- return C_core_path + '/' + file
14
- if file[-4:] == '.pyd':
15
- return C_core_path + '/' + file
12
+ if (file[-3:] == '.so') or (file[-4:] == '.pyd') or (file[-4:] == '.dll') or (file[-4:] == '.lib'):
13
+ if 'piegyc' in file:
14
+ return C_core_path + '/' + file
16
15
 
17
16
  raise FileNotFoundError('C computation core not found. You can either compile manully or use the Python core instead. Please see docs.')
@@ -27,7 +27,6 @@ import matplotlib.style as mplstyle
27
27
  import numpy as np
28
28
  import os
29
29
  from cv2 import imread, VideoWriter, VideoWriter_fourcc
30
- from moviepy import ImageSequenceClip
31
30
 
32
31
 
33
32
  # a list of supported figures
@@ -144,7 +143,7 @@ def frame_lim(mod, func, frames):
144
143
  U_ylim = get_max_lim(U_ylist)
145
144
  V_xlim = get_max_lim(V_xlist)
146
145
  V_ylim = get_max_lim(V_ylist)
147
-
146
+
148
147
  return U_xlim, U_ylim, V_xlim, V_ylim
149
148
 
150
149
 
@@ -200,14 +199,15 @@ def make_mp4(video_dir, frame_dir, fps):
200
199
  if (file[-4:] == '.png') and ('frame' in file):
201
200
  frame_path.append(os.path.join(frame_dir, file))
202
201
 
202
+ # setup cv2 video writer
203
203
  first_frame = imread(frame_path[0])
204
204
  height, width, _ = first_frame.shape
205
205
  fourcc = VideoWriter_fourcc(*'mp4v')
206
206
  video_writer = VideoWriter(video_dir, fourcc, fps, (width, height))
207
207
 
208
208
  for file in frame_path:
209
- frame = imread(file)
210
- video_writer.write(frame)
209
+ frame = imread(file)
210
+ video_writer.write(frame)
211
211
  video_writer.release()
212
212
 
213
213
 
@@ -236,9 +236,9 @@ def make_video(mod, func_name = 'UV_hmap', frames = 100, dpi = 200, fps = 30, U_
236
236
  # convert color if invalid colors are given
237
237
  U_color, V_color = convert_color(func_name, U_color, V_color)
238
238
 
239
- # set fast style for faster speed
240
- original_rc = mpl.rcParams.copy()
241
- mplstyle.use('fast')
239
+ # set Agg backend for faster speed
240
+ original_backend = mpl.get_backend()
241
+ mpl.use("Agg")
242
242
 
243
243
  # print progress
244
244
  one_progress = frames / 100
@@ -267,7 +267,7 @@ def make_video(mod, func_name = 'UV_hmap', frames = 100, dpi = 200, fps = 30, U_
267
267
 
268
268
  #### for loop ####
269
269
 
270
- for i in range(frames - 1):
270
+ for i in range(frames):
271
271
  if i > current_progress:
272
272
  print('making frames', round(i / frames * 100), '%', end = '\r')
273
273
  current_progress += one_progress
@@ -277,9 +277,9 @@ def make_video(mod, func_name = 'UV_hmap', frames = 100, dpi = 200, fps = 30, U_
277
277
  fig_U, ax_U = plt.subplots(figsize = figsize)
278
278
  fig_V, ax_V = plt.subplots(figsize = figsize)
279
279
  else:
280
- fig_U, ax_U = plt.subplots(figsize = figsize)#, constrained_layout = True)
281
- fig_V, ax_V = plt.subplots(figsize = figsize)#, constrained_layout = True)
282
-
280
+ fig_U, ax_U = plt.subplots(figsize = figsize)
281
+ fig_V, ax_V = plt.subplots(figsize = figsize)
282
+
283
283
  if 'hmap' in func_name:
284
284
  func(mod, ax_U = ax_U, ax_V = ax_V, U_color = U_color, V_color = V_color, start = i / frames, end = (i + 1) / frames, vrange_U = U_clim, vrange_V = V_clim)
285
285
  else:
@@ -304,6 +304,9 @@ def make_video(mod, func_name = 'UV_hmap', frames = 100, dpi = 200, fps = 30, U_
304
304
  plt.close(fig_V)
305
305
 
306
306
  #### for loop ends ####
307
+
308
+ # reset to original backend
309
+ mpl.use(original_backend)
307
310
 
308
311
  # frames done
309
312
  print('making mp4... ', end = '\r')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: piegy
3
- Version: 2.3.5
3
+ Version: 2.3.7
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,7 +23,6 @@ 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/runner.c
27
26
  src/piegy/C_core/sim_funcs.c
28
27
  src/piegy/C_core/sim_funcs.h
29
28
  src/piegy/tools/__init__.py
@@ -1,61 +0,0 @@
1
- #include <stdbool.h>
2
- #include <stdlib.h>
3
- #include <stdint.h>
4
- #include <stdio.h>
5
-
6
- #include "model.h"
7
- #include "patch.h"
8
- #include "sim_funcs.h"
9
-
10
-
11
- int main() {
12
- size_t N = 1;
13
- size_t M = 100;
14
- double maxtime = 300;
15
- double record_itv = 0.1;
16
- size_t sim_time = 1;
17
- bool boundary = true;
18
- uint32_t I_single[2] = {3, 3};
19
- double X_single[4] = {-1, 4, 0, 2};
20
- double P_single[6] = {0.5, 0.5, 100, 100, 0.001, 0.001};
21
- int32_t print_pct = 1;
22
- int32_t seed = 36; // -1 for None
23
-
24
- uint32_t I[N * M * 2];
25
- double X[N * M * 4];
26
- double P[N * M * 6];
27
-
28
- //printf("sizeof(patch_t) = %zu bytes\n", sizeof(patch_t));
29
- size_t ij = 0;
30
- for (size_t i = 0; i < N; i++) {
31
- for (size_t j = 0; j < M; j++) {
32
- I[ij * 2] = I_single[0];
33
- I[ij * 2 + 1] = I_single[1];
34
-
35
- for (size_t k = 0; k < 4; k++) {
36
- X[ij * 4 + k] = X_single[k];
37
- }
38
- for (size_t k = 0; k < 6; k++) {
39
- P[ij * 6 + k] = P_single[k];
40
- }
41
- ++ij;
42
- }
43
- }
44
-
45
- model_t* mod = malloc(sizeof(model_t));
46
- mod_init(mod, N, M, maxtime, record_itv, sim_time, boundary, I, X, P, print_pct, seed);
47
-
48
- char message[20] = ""; // writable buffer with enough space
49
- uint8_t result = run(mod, message, 20);
50
-
51
- /*for (size_t i = 0; i < mod->max_record; i++) {
52
- fprintf(stdout, "%f ", mod->U1d[i]);
53
- }
54
- fprintf(stdout, "\n");
55
- for (size_t i = 0; i < mod->max_record; i++) {
56
- fprintf(stdout, "%f ", mod->Upi_1d[i]);
57
- }*/
58
- mod_free(mod);
59
- mod = NULL;
60
- }
61
-
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