piegy 2.3.4__tar.gz → 2.3.6__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.4/src/piegy.egg-info → piegy-2.3.6}/PKG-INFO +1 -1
  2. {piegy-2.3.4 → piegy-2.3.6}/pyproject.toml +1 -1
  3. {piegy-2.3.4 → piegy-2.3.6}/src/piegy/C_core/Makefile +0 -1
  4. {piegy-2.3.4 → piegy-2.3.6}/src/piegy/C_core/runner.c +4 -5
  5. {piegy-2.3.4 → piegy-2.3.6}/src/piegy/C_core/sim_funcs.c +88 -73
  6. {piegy-2.3.4 → piegy-2.3.6}/src/piegy/C_core/sim_funcs.h +13 -21
  7. {piegy-2.3.4 → piegy-2.3.6}/src/piegy/__version__.py +3 -1
  8. {piegy-2.3.4 → piegy-2.3.6}/src/piegy/figures.py +4 -4
  9. {piegy-2.3.4 → piegy-2.3.6}/src/piegy/simulation.py +12 -11
  10. {piegy-2.3.4 → piegy-2.3.6}/src/piegy/test_var.py +4 -4
  11. {piegy-2.3.4 → piegy-2.3.6/src/piegy.egg-info}/PKG-INFO +1 -1
  12. {piegy-2.3.4 → piegy-2.3.6}/LICENSE.txt +0 -0
  13. {piegy-2.3.4 → piegy-2.3.6}/MANIFEST.in +0 -0
  14. {piegy-2.3.4 → piegy-2.3.6}/README.md +0 -0
  15. {piegy-2.3.4 → piegy-2.3.6}/setup.cfg +0 -0
  16. {piegy-2.3.4 → piegy-2.3.6}/setup.py +0 -0
  17. {piegy-2.3.4 → piegy-2.3.6}/src/piegy/C_core/model.c +0 -0
  18. {piegy-2.3.4 → piegy-2.3.6}/src/piegy/C_core/model.h +0 -0
  19. {piegy-2.3.4 → piegy-2.3.6}/src/piegy/C_core/patch.c +0 -0
  20. {piegy-2.3.4 → piegy-2.3.6}/src/piegy/C_core/patch.h +0 -0
  21. {piegy-2.3.4 → piegy-2.3.6}/src/piegy/__init__.py +0 -0
  22. {piegy-2.3.4 → piegy-2.3.6}/src/piegy/analysis.py +0 -0
  23. {piegy-2.3.4 → piegy-2.3.6}/src/piegy/build_info.py +0 -0
  24. {piegy-2.3.4 → piegy-2.3.6}/src/piegy/data_tools.py +0 -0
  25. {piegy-2.3.4 → piegy-2.3.6}/src/piegy/simulation_py.py +0 -0
  26. {piegy-2.3.4 → piegy-2.3.6}/src/piegy/tools/__init__.py +0 -0
  27. {piegy-2.3.4 → piegy-2.3.6}/src/piegy/tools/figure_tools.py +0 -0
  28. {piegy-2.3.4 → piegy-2.3.6}/src/piegy/tools/file_tools.py +0 -0
  29. {piegy-2.3.4 → piegy-2.3.6}/src/piegy/tools/find_C.py +0 -0
  30. {piegy-2.3.4 → piegy-2.3.6}/src/piegy/videos.py +0 -0
  31. {piegy-2.3.4 → piegy-2.3.6}/src/piegy.egg-info/SOURCES.txt +0 -0
  32. {piegy-2.3.4 → piegy-2.3.6}/src/piegy.egg-info/dependency_links.txt +0 -0
  33. {piegy-2.3.4 → piegy-2.3.6}/src/piegy.egg-info/requires.txt +0 -0
  34. {piegy-2.3.4 → piegy-2.3.6}/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.4
3
+ Version: 2.3.6
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.4'
7
+ version = '2.3.6'
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
 
@@ -11,13 +11,13 @@
11
11
  int main() {
12
12
  size_t N = 1;
13
13
  size_t M = 100;
14
- double maxtime = 600;
14
+ double maxtime = 300;
15
15
  double record_itv = 0.1;
16
16
  size_t sim_time = 1;
17
17
  bool boundary = true;
18
- uint32_t I_single[2] = {440, 220};
18
+ uint32_t I_single[2] = {3, 3};
19
19
  double X_single[4] = {-1, 4, 0, 2};
20
- double P_single[6] = {0.5, 0.5, 100, 100, 0.001, 0.001};
20
+ double P_single[6] = {0.5, 0.5, 80, 80, 0.001, 0.001};
21
21
  int32_t print_pct = 1;
22
22
  int32_t seed = 36; // -1 for None
23
23
 
@@ -46,8 +46,7 @@ int main() {
46
46
  mod_init(mod, N, M, maxtime, record_itv, sim_time, boundary, I, X, P, print_pct, seed);
47
47
 
48
48
  char message[20] = ""; // writable buffer with enough space
49
- uint32_t update_sum_freq = 64;
50
- uint8_t result = run(mod, message, 20, update_sum_freq);
49
+ uint8_t result = run(mod, message, 20);
51
50
 
52
51
  /*for (size_t i = 0; i < mod->max_record; i++) {
53
52
  fprintf(stdout, "%f ", mod->U1d[i]);
@@ -68,8 +68,9 @@ 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;
@@ -145,6 +146,9 @@ static double single_init(const model_t* mod, patch_t* world, size_t* nb_indices
145
146
  find_patch(picked_p, expected_sum, patch_rates, sum_rates_by_row, *sum_rates_p, N, M);
146
147
  size_t picked_idx = picked_p->i * M + picked_p->j;
147
148
  size_t e0 = find_event(&world[picked_idx], expected_sum - picked_p->current_sum);
149
+ if (picked_idx >= NM || e0 >= 12) {
150
+ return -1 * ACCURACY_ERROR;
151
+ }
148
152
 
149
153
  // make signal
150
154
  if (mod->boundary) {
@@ -191,7 +195,7 @@ static double single_init(const model_t* mod, patch_t* world, size_t* nb_indices
191
195
 
192
196
 
193
197
 
194
- static uint8_t single_test(model_t* restrict mod, uint32_t update_sum_freq, char* message) {
198
+ static uint8_t single_test(model_t* restrict mod, char* message) {
195
199
  // bring some dimensions to the front
196
200
  size_t N = mod->N;
197
201
  size_t M = mod->M;
@@ -201,9 +205,9 @@ static uint8_t single_test(model_t* restrict mod, uint32_t update_sum_freq, char
201
205
  double record_itv = mod->record_itv;
202
206
  bool boundary = mod->boundary;
203
207
 
204
- double one_time = maxtime / (double)(update_sum_freq > 100 ? update_sum_freq : 100);
208
+ // print progress
205
209
  double one_progress = 0.0;
206
-
210
+ double current_progress = one_progress;
207
211
  if (mod->print_pct != -1) {
208
212
  one_progress = maxtime * mod->print_pct / 100.0;
209
213
  fprintf(stdout, "\r ");
@@ -213,11 +217,11 @@ static uint8_t single_test(model_t* restrict mod, uint32_t update_sum_freq, char
213
217
  one_progress = 2.0 * maxtime;
214
218
  }
215
219
 
216
- double one_update_sum = maxtime / (double) (update_sum_freq + 1);
217
-
218
- double current_time = one_time;
219
- double current_progress = one_progress;
220
- double current_update_sum = one_update_sum;
220
+ // update sum of rates every 1e5 rounds
221
+ // many rates are updated each time, rather than re-calculated.
222
+ // So need to re-calculate from scratch every some rounds to reduce numerical errors
223
+ size_t curr_update_sum_round = 0; // current round
224
+ size_t update_sum_rounds = UPDATE_SUM_ROUNDS_SM; // recalculate sum every this many rounds
221
225
 
222
226
  // Initialize simulation
223
227
  patch_t* world = (patch_t*) calloc(NM, sizeof(patch_t));
@@ -242,54 +246,61 @@ static uint8_t single_test(model_t* restrict mod, uint32_t update_sum_freq, char
242
246
  fflush(stdout);
243
247
  single_test_free(&world, &nb_indices, &patch_rates, &sum_rates_by_row);
244
248
  return SIM_OVERFLOW;
245
- }
249
+ } else if (time == -1 * ACCURACY_ERROR) {
250
+ fprintf(stdout, "\nError: accuracy too low at t = 0, simulation stopped\n");
251
+ fflush(stdout);
252
+ single_test_free(&world, &nb_indices, &patch_rates, &sum_rates_by_row);
253
+ return ACCURACY_ERROR;
254
+ }
246
255
  size_t record_index = time / mod->record_itv;
247
256
  double record_time = time - record_index * record_itv;
248
257
 
249
258
 
250
259
  while (time < maxtime) {
251
260
 
252
- // Print progress and update sums if needed
253
- if (time > current_time) {
254
- current_time += one_time;
255
- if (time > current_progress) {
256
- uint8_t curr_prog = (uint8_t)(time * 100 / maxtime);
257
- if (curr_prog < 10) {
258
- fprintf(stdout, "\r%s: %d %%", message, (int)(time * 100 / maxtime));
259
- } else {
260
- fprintf(stdout, "\r%s: %d%%", message, (int)(time * 100 / maxtime));
261
- }
262
- fflush(stdout);
263
- //fflush(stdout); // Make sure it prints immediately
264
- current_progress += one_progress;
261
+ // Print progress
262
+ if (time > current_progress) {
263
+ uint8_t curr_prog = (uint8_t)(time * 100 / maxtime);
264
+ if (curr_prog < 10) {
265
+ fprintf(stdout, "\r%s: %d %%", message, (int)(time * 100 / maxtime));
266
+ } else {
267
+ fprintf(stdout, "\r%s: %d%%", message, (int)(time * 100 / maxtime));
265
268
  }
266
- if (time > current_update_sum) {
267
- current_update_sum += one_update_sum;
268
-
269
- // recalculate sum
270
- size_t ij = 0;
271
- for (size_t i = 0; i < N; i++) {
272
- for (size_t j = 0; j < M; j++) {
273
- world[ij].sum_U_weight = 0;
274
- world[ij].sum_V_weight = 0;
275
- for (size_t k = 0; k < 4; k++) {
276
- world[ij].sum_U_weight += world[ij].U_weight[k];
277
- world[ij].sum_V_weight += world[ij].V_weight[k];
278
- }
279
- }
269
+ fflush(stdout);
270
+ //fflush(stdout); // Make sure it prints immediately
271
+ current_progress += one_progress;
272
+ }
273
+
274
+ // update sums
275
+ curr_update_sum_round++;
276
+ if (curr_update_sum_round > update_sum_rounds) {
277
+ curr_update_sum_round = 0;
278
+
279
+ update_sum_rounds = UPDATE_SUM_ROUNDS_LG; // assume can make it larger
280
+ for (size_t ij = 0; ij < NM; ij++) {
281
+ double sum_U_weight = 0;
282
+ double sum_V_weight = 0;
283
+ for (size_t k = 0; k < 4; k++) {
284
+ sum_U_weight += world[ij].U_weight[k];
285
+ sum_V_weight += world[ij].V_weight[k];
280
286
  }
281
- ij = 0;
282
- for (size_t i = 0; i < N; i++) {
283
- sum_rates_by_row[i] = 0;
284
- for (size_t j = 0; j < M; j++) {
285
- sum_rates_by_row[i] += patch_rates[ij];
286
- ij++;
287
- }
287
+ if (sum_U_weight > ACCURATE_BOUND || sum_V_weight > ACCURATE_BOUND) {
288
+ update_sum_rounds = UPDATE_SUM_ROUNDS_SM; // values too large, put back the small update frequency
288
289
  }
289
- sum_rates = 0;
290
- for (size_t i = 0; i < N; i++) {
291
- sum_rates += sum_rates_by_row[i];
290
+ world[ij].sum_U_weight = sum_U_weight;
291
+ world[ij].sum_V_weight = sum_V_weight;
292
+ // patch_rates are updated every time a patch is changed
293
+ }
294
+ size_t ij = 0;
295
+ sum_rates = 0;
296
+ for (size_t i = 0; i < N; i++) {
297
+ double sum_rates_by_row_i = 0;
298
+ for (size_t j = 0; j < M; j++) {
299
+ sum_rates_by_row_i += patch_rates[ij];
300
+ ij++;
292
301
  }
302
+ sum_rates_by_row[i] = sum_rates_by_row_i;
303
+ sum_rates += sum_rates_by_row_i;
293
304
  }
294
305
  }
295
306
 
@@ -301,13 +312,14 @@ static uint8_t single_test(model_t* restrict mod, uint32_t update_sum_freq, char
301
312
  size_t sij1 = signal.ij1;
302
313
  size_t sij2 = signal.ij2;
303
314
  uint8_t rela_loc = signal.rela_loc;
315
+
304
316
  if (rela_loc == NO_MIG) {
305
317
  // if only one
306
318
  sum_rates_by_row[si1] -= patch_rates[sij1];
307
319
  sum_rates -= patch_rates[sij1];
308
320
 
309
321
  update_pi_k(&world[sij1], &(mod->X[sij1 * 4]), &(mod->P[sij1 * 6]));
310
- init_mig(&world[sij1], &(mod->P[sij1 * 6]));
322
+ update_mig_just_rate(&world[sij1], &(mod->P[sij1 * 6]));
311
323
 
312
324
  patch_rates[sij1] = world[sij1].sum_pi_death_rates + world[sij1].sum_mig_rates;
313
325
  sum_rates_by_row[si1] += patch_rates[sij1];
@@ -358,7 +370,8 @@ static uint8_t single_test(model_t* restrict mod, uint32_t update_sum_freq, char
358
370
  for (uint8_t k = 0; k < 4; k++) {
359
371
  size_t nb_idx = nb_indices[sij1 * 4 + k];
360
372
  if (nb_idx == NM) { continue; }
361
- if (nb_need_change(nb_idx, sij1, sij2)) {
373
+ if (k != rela_loc) {
374
+ // nb_idx isn't the second last-changed patch
362
375
  if (update_mig_weight_rate(&world[nb_idx], &(mod->P[nb_idx * 6]), k ^ 1) == SIM_OVERFLOW) {
363
376
  fprintf(stdout, "\nError: overflow at t = %f\n", time);
364
377
  fflush(stdout);
@@ -371,7 +384,8 @@ static uint8_t single_test(model_t* restrict mod, uint32_t update_sum_freq, char
371
384
  for (uint8_t k = 0; k < 4; k++) {
372
385
  size_t nb_idx = nb_indices[sij2 * 4 + k];
373
386
  if (nb_idx == NM) { continue; }
374
- if (nb_need_change(nb_idx, sij1, sij2)) {
387
+ if (k != (rela_loc ^ 1)) {
388
+ // nb_idx isn't the first last-changed patch
375
389
  if (update_mig_weight_rate(&world[nb_idx], &(mod->P[nb_idx * 6]), k ^ 1) == SIM_OVERFLOW) {
376
390
  fprintf(stdout, "\nError: overflow at t = %f\n", time);
377
391
  fflush(stdout);
@@ -388,6 +402,12 @@ static uint8_t single_test(model_t* restrict mod, uint32_t update_sum_freq, char
388
402
  find_patch(&picked, expected_sum, patch_rates, sum_rates_by_row, sum_rates, N, M);
389
403
  size_t picked_idx = picked.i * M + picked.j;
390
404
  uint8_t e0 = find_event(&world[picked_idx], expected_sum - picked.current_sum);
405
+ if (picked_idx >= NM || e0 >= 12) {
406
+ fprintf(stdout, "\nError: accuracy too low at t = %f, simulation stopped\n", time);
407
+ fflush(stdout);
408
+ single_test_free(&world, &nb_indices, &patch_rates, &sum_rates_by_row);
409
+ return ACCURACY_ERROR;
410
+ }
391
411
 
392
412
  // make signal
393
413
  if (boundary) {
@@ -416,16 +436,12 @@ static uint8_t single_test(model_t* restrict mod, uint32_t update_sum_freq, char
416
436
  record_time -= multi_records * record_itv;
417
437
  size_t upper = record_index + multi_records;
418
438
 
419
- size_t ij = 0;
420
- for (size_t i = 0; i < N; i++) {
421
- for (size_t j = 0; j < M; j++) {
422
- for (size_t k = record_index; k < upper; k++) {
423
- mod->U1d[ij * max_record + k] += world[ij].U;
424
- mod->V1d[ij * max_record + k] += world[ij].V;
425
- mod->Upi_1d[ij * max_record + k] += world[ij].U_pi;
426
- mod->Vpi_1d[ij * max_record + k] += world[ij].V_pi;
427
- }
428
- ij++;
439
+ for (size_t ij = 0; ij < NM; ij++) {
440
+ for (size_t k = record_index; k < upper; k++) {
441
+ mod->U1d[ij * max_record + k] += world[ij].U;
442
+ mod->V1d[ij * max_record + k] += world[ij].V;
443
+ mod->Upi_1d[ij * max_record + k] += world[ij].U_pi;
444
+ mod->Vpi_1d[ij * max_record + k] += world[ij].V_pi;
429
445
  }
430
446
  }
431
447
  record_index += multi_records;
@@ -433,16 +449,12 @@ static uint8_t single_test(model_t* restrict mod, uint32_t update_sum_freq, char
433
449
 
434
450
  } else {
435
451
  // if already exceeds maxtime
436
- size_t ij = 0;
437
- for (size_t i = 0; i < N; i++) {
438
- for (size_t j = 0; j < M; j++) {
439
- for (size_t k = record_index; k < max_record; k++) {
440
- mod->U1d[ij * max_record + k] += world[ij].U;
441
- mod->V1d[ij * max_record + k] += world[ij].V;
442
- mod->Upi_1d[ij * max_record + k] += world[ij].U_pi;
443
- mod->Vpi_1d[ij * max_record + k] += world[ij].V_pi;
444
- }
445
- ij++;
452
+ for (size_t ij = 0; ij < NM; ij++) {
453
+ for (size_t k = record_index; k < max_record; k++) {
454
+ mod->U1d[ij * max_record + k] += world[ij].U;
455
+ mod->V1d[ij * max_record + k] += world[ij].V;
456
+ mod->Upi_1d[ij * max_record + k] += world[ij].U_pi;
457
+ mod->Vpi_1d[ij * max_record + k] += world[ij].V_pi;
446
458
  }
447
459
  }
448
460
  }
@@ -475,7 +487,7 @@ static void single_test_free(patch_t** world, size_t** nb_indices, double** patc
475
487
 
476
488
 
477
489
 
478
- uint8_t run(model_t* mod, char* message, size_t msg_len, uint32_t update_sum_freq) {
490
+ uint8_t run(model_t* restrict mod, char* message, size_t msg_len) {
479
491
  if (!mod->data_empty) {
480
492
  // this won't happen if called from python, the ``simulation.run`` caller has checked it.
481
493
  fprintf(stdout, "Error: mod has non-empty data\n");
@@ -518,7 +530,7 @@ uint8_t run(model_t* mod, char* message, size_t msg_len, uint32_t update_sum_fre
518
530
  snprintf(end_info, sizeof(end_info), ", ~%.2fs left", pred_runtime);
519
531
  }*/
520
532
 
521
- uint8_t result = single_test(mod, update_sum_freq, curr_msg);
533
+ uint8_t result = single_test(mod, curr_msg);
522
534
 
523
535
  switch (result) {
524
536
  case SUCCESS:
@@ -530,6 +542,9 @@ uint8_t run(model_t* mod, char* message, size_t msg_len, uint32_t update_sum_fre
530
542
  case SIM_OVERFLOW:
531
543
  // error message is handled by single_test
532
544
  return SIM_OVERFLOW;
545
+ case ACCURACY_ERROR:
546
+ // error message is handled by single_test
547
+ return ACCURACY_ERROR;
533
548
  }
534
549
  }
535
550
 
@@ -35,10 +35,16 @@
35
35
  #define DATA_NOT_EMPTY 1
36
36
  #define SMALL_MAXTIME 2
37
37
  #define SIM_OVERFLOW 3
38
+ #define ACCURACY_ERROR 4
38
39
 
39
40
  // 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
41
+ // below the actual bound (709) to preserve accuracy
41
42
  #define EXP_OVERFLOW_BOUND 500
43
+ #define ACCURATE_BOUND 10000000000LL
44
+
45
+ // how frequent to update rates & sum of rates in single test (recalculate)
46
+ #define UPDATE_SUM_ROUNDS_SM 100
47
+ #define UPDATE_SUM_ROUNDS_LG 10000
42
48
 
43
49
 
44
50
  static uint64_t pcg_state = 0;
@@ -85,11 +91,12 @@ typedef struct signal_t {
85
91
  */
86
92
  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);
87
93
  static void find_nb_periodical(size_t* restrict nb, size_t i, size_t j, size_t N, size_t M, size_t NM);
88
- static double single_init(const model_t* mod, patch_t* world, size_t* nb_indices,
89
- double* patch_rates, double* sum_rates_by_row, double* sum_rates, signal_t* sig_p, patch_picked_t* picked_p) ;
90
- static uint8_t single_test(model_t* restrict mod, uint32_t update_sum_frequency, char* message);
94
+ static double single_init(const model_t* restrict mod, patch_t* restrict world, size_t* restrict nb_indices,
95
+ double* restrict patch_rates, double* restrict sum_rates_by_row, double* restrict sum_rates_p,
96
+ signal_t* restrict sig_p, patch_picked_t* restrict picked_p);
97
+ static uint8_t single_test(model_t* restrict mod, char* message);
91
98
  static void single_test_free(patch_t** world, size_t** nb_indices, double** patch_rates, double** sum_rates_by_row);
92
- uint8_t run(model_t* mod, char* message, size_t msg_len, uint32_t update_sum_freq);
99
+ uint8_t run(model_t* restrict mod, char* message, size_t msg_len);
93
100
 
94
101
 
95
102
 
@@ -153,7 +160,6 @@ static inline void update_pi_k(patch_t* restrict p, const double* restrict M_sta
153
160
 
154
161
 
155
162
  static inline void update_mig_just_rate(patch_t* restrict p, const double* restrict P_start) {
156
- // BUGGY - not using
157
163
  // update migration weight for patch p, in location loc. Only rate is updated
158
164
  // used by last-changed patch, when there is only one last-changed patch
159
165
  double* p_U_weight = p->U_weight;
@@ -161,7 +167,6 @@ static inline void update_mig_just_rate(patch_t* restrict p, const double* restr
161
167
 
162
168
  double mu1_U = P_start[0] * (double)p->U;
163
169
  double mu2_V = P_start[1] * (double)p->V;
164
- p->sum_mig_rates = mu1_U + mu2_V;
165
170
 
166
171
  double mu1_U_divide_sum = mu1_U / p->sum_U_weight;
167
172
  double mu2_V_divide_sum = mu2_V / p->sum_V_weight;
@@ -170,6 +175,7 @@ static inline void update_mig_just_rate(patch_t* restrict p, const double* restr
170
175
  p->mig_rates[i] = mu1_U_divide_sum * p_U_weight[i];
171
176
  p->mig_rates[i + 4] = mu2_V_divide_sum * p_V_weight[i];
172
177
  }
178
+ p->sum_mig_rates = mu1_U + mu2_V;
173
179
  }
174
180
 
175
181
 
@@ -245,7 +251,6 @@ static inline uint8_t init_mig(patch_t* restrict p, const double* restrict P_sta
245
251
 
246
252
  for (uint8_t i = 0; i < 4; i++) {
247
253
  patch_t* nbi = p->nb[i];
248
-
249
254
  if (nbi) {
250
255
  // not NULL
251
256
  double w1_Upi = w1 * nbi->U_pi;
@@ -264,7 +269,6 @@ static inline uint8_t init_mig(patch_t* restrict p, const double* restrict P_sta
264
269
  }
265
270
  }
266
271
 
267
-
268
272
  double mu1_U = P_start[0] * (double)p->U;
269
273
  double mu2_V = P_start[1] * (double)p->V;
270
274
  double mu1_U_divide_sum = mu1_U / p->sum_U_weight;
@@ -583,16 +587,4 @@ static inline void make_signal_periodical(size_t N, size_t M, size_t i, size_t j
583
587
  }
584
588
 
585
589
 
586
- static inline bool nb_need_change(size_t nb_idx, size_t sij1, size_t sij2) {
587
- // a nb doesn't need change only if two patches are updated
588
-
589
- // or, can try this 1-line version
590
- // return (nb_idx != sij1) && (nb_idx != sij2);
591
- if ((nb_idx == sij1) || (nb_idx == sij2)) {
592
- return false;
593
- }
594
- return true; // need to change
595
- }
596
-
597
-
598
590
  #endif
@@ -1,4 +1,4 @@
1
- __version__ = '2.3.4'
1
+ __version__ = '2.3.6'
2
2
 
3
3
  '''
4
4
  version history:
@@ -41,4 +41,6 @@ version history:
41
41
  Simplified update-migration functions, improve speed by ~10%. Add -march=native flag to Makefile.
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
+ 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.
44
46
  '''
@@ -530,19 +530,19 @@ def video_fig(mod, ax_list = None, num_grid = 100, U_color = 'Purples', V_color
530
530
 
531
531
 
532
532
  im = ax_list[0, 0].imshow(ave_U, cmap = U_color)
533
- ax_list[0, 0].get_figure().colorbar(im, ax = ax_list[0, 0])
533
+ ax_list[0, 0].get_figure().colorbar(im, ax = ax_list[0, 0], extent = [0, mod.N * mod.M, 0, mod.maxtime], origin='lower', aspect = 'auto')
534
534
  ax_list[0, 0].set_title('Population U over time')
535
535
 
536
536
  im = ax_list[0, 1].imshow(ave_V, cmap = V_color)
537
- ax_list[0, 1].get_figure().colorbar(im, ax = ax_list[0, 1])
537
+ ax_list[0, 1].get_figure().colorbar(im, ax = ax_list[0, 1], extent = [0, mod.N * mod.M, 0, mod.maxtime], origin='lower', aspect = 'auto')
538
538
  ax_list[0, 1].set_title('Population V over time')
539
539
 
540
540
  im = ax_list[1, 0].imshow(ave_Upi, cmap = U_color)
541
- ax_list[1, 0].get_figure().colorbar(im, ax = ax_list[1, 0])
541
+ ax_list[1, 0].get_figure().colorbar(im, ax = ax_list[1, 0], extent = [0, mod.N * mod.M, 0, mod.maxtime], origin='lower', aspect = 'auto')
542
542
  ax_list[1, 0].set_title('Payoff ' + r'$p_H$' + ' over time')
543
543
 
544
544
  im = ax_list[1, 1].imshow(ave_Vpi, cmap = V_color)
545
- ax_list[1, 1].get_figure().colorbar(im, ax = ax_list[1, 1])
545
+ ax_list[1, 1].get_figure().colorbar(im, ax = ax_list[1, 1], extent = [0, mod.N * mod.M, 0, mod.maxtime], origin='lower', aspect = 'auto')
546
546
  ax_list[1, 1].set_title('Payoff ' + r'$p_D$' + ' over time')
547
547
 
548
548
  return ax_list
@@ -25,11 +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 1e20
29
-
30
- # how frequent to update sum in simulation, i.e., accuracy
31
- # the defuault value is 64, increase to 4 times if encounter error
32
- UPDATE_SUM_FREQ = 64
28
+ EXP_TOO_LARGE_BOUND = 30 # where exp(x) reaches ~10e13 and accuracy goes below 10^-3 (10^-3 isn't accurate any more)
33
29
 
34
30
 
35
31
  # read the C core into LIB
@@ -40,6 +36,7 @@ SIM_SUCCESS = 0
40
36
  SIM_DATA_EMPTY = 1
41
37
  SIM_SMALL_MAXTIME = 2
42
38
  SIM_OVERFLOW = 3
39
+ ACCURACY_ERROR = 4
43
40
 
44
41
 
45
42
  '''
@@ -101,7 +98,7 @@ def read_lib():
101
98
  LIB.mod_free_py.argtypes = [ctypes.POINTER(model_c)]
102
99
  LIB.mod_free_py.restype = None
103
100
 
104
- LIB.run.argtypes = [ctypes.POINTER(model_c), ctypes.POINTER(c_char), c_size_t, c_uint32]
101
+ LIB.run.argtypes = [ctypes.POINTER(model_c), ctypes.POINTER(c_char), c_size_t]
105
102
  LIB.run.restype = c_ubyte # which is uint8
106
103
 
107
104
 
@@ -410,7 +407,7 @@ class model:
410
407
 
411
408
 
412
409
 
413
- def run(mod, message = "", update_sum_freq = UPDATE_SUM_FREQ):
410
+ def run(mod, message = ""):
414
411
  '''
415
412
  C-cored simulation
416
413
  '''
@@ -437,7 +434,7 @@ def run(mod, message = "", update_sum_freq = UPDATE_SUM_FREQ):
437
434
  del mod_c
438
435
  raise RuntimeError('Model initialization failed')
439
436
 
440
- result = LIB.run(ctypes.byref(mod_c), msg_buffer, msg_len, update_sum_freq)
437
+ result = LIB.run(ctypes.byref(mod_c), msg_buffer, msg_len)
441
438
 
442
439
  if result == SIM_SUCCESS:
443
440
  mod.set_data(False, mod.max_record, 1, mod_c.get_array('U1d').reshape(mod.N, mod.M, mod.max_record),
@@ -453,7 +450,11 @@ def run(mod, message = "", update_sum_freq = UPDATE_SUM_FREQ):
453
450
  elif result == SIM_OVERFLOW:
454
451
  LIB.mod_free_py(ctypes.byref(mod_c))
455
452
  del mod_c
456
- raise OverflowError('Overflow in simulation')
453
+ raise OverflowError('Overflow in simulation. Possibly due to too large w1, w2, or payoff matrix values.')
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 matrix values.')
457
458
  else:
458
459
  LIB.mod_free_py(ctypes.byref(mod_c))
459
460
  del mod_c
@@ -528,9 +529,9 @@ def check_overflow_func(mod):
528
529
  w1_pi = pi_expected[i][j] * mod.P[i][j][2] # w1 * U_pi
529
530
  w2_pi = pi_expected[i][j] * mod.P[i][j][3] # w2 * V_pi
530
531
  if ((w1_pi > EXP_OVERFLOW_BOUND) or (w2_pi > EXP_OVERFLOW_BOUND)):
531
- print("Warning: might cause overflow. \n\t w1, w2, or payoff matrix values too large")
532
+ print("Warning: might cause overflow in simulation. \n\t w1, w2, or payoff matrix values too large")
532
533
  return
533
534
  if ((w1_pi > EXP_TOO_LARGE_BOUND) or (w2_pi > EXP_TOO_LARGE_BOUND)):
534
- 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. \n\t w1, w2, or payoff matrix values too large")
535
536
  return
536
537
 
@@ -92,8 +92,8 @@ 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
+ except (OverflowError, RuntimeError):
96
+ print(current_var_str + ' raised error, skipped')
97
97
  continue
98
98
 
99
99
  return var_dirs
@@ -139,8 +139,8 @@ def test_var2(mod, var1, var2, values1, values2, dirs, compress_itv = None):
139
139
  if compress_itv != None:
140
140
  sim2.compress_data(compress_itv)
141
141
  data_t.save_data(sim2, var_dirs[k1][k2], print_msg = False)
142
- except OverflowError:
143
- print(current_var_str + ' raised OverflowError, skipped')
142
+ except (OverflowError, RuntimeError):
143
+ print(current_var_str + ' raised error, skipped')
144
144
  continue
145
145
 
146
146
  return var_dirs
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: piegy
3
- Version: 2.3.4
3
+ Version: 2.3.6
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes