piegy 2.1.8__cp37-cp37m-win32.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,561 @@
1
+ /**
2
+ * This .c defines the simulation functions in piegy.simulation
3
+ */
4
+
5
+ #include <stdbool.h>
6
+ #include <time.h>
7
+
8
+ #include "sim_funcs.h"
9
+ #include "patch.h"
10
+ #include "model.h"
11
+
12
+
13
+
14
+ 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) {
15
+ // Up
16
+ if (i != 0) {
17
+ nb[0] = (i - 1) * M + j;
18
+ } else {
19
+ nb[0] = NM; // N * M for nb doesn't exist
20
+ }
21
+
22
+ // Down
23
+ if (i != N - 1) {
24
+ nb[1] = (i + 1) * M + j;
25
+ } else {
26
+ nb[1] = NM;
27
+ }
28
+
29
+ // Left
30
+ if (j != 0) {
31
+ nb[2] = i * M + j - 1;
32
+ } else {
33
+ nb[2] = NM;
34
+ }
35
+
36
+ // Right
37
+ if (j != M - 1) {
38
+ nb[3] = i * M + j + 1;
39
+ } else {
40
+ nb[3] = NM;
41
+ }
42
+ }
43
+
44
+
45
+ static void find_nb_periodical(size_t* restrict nb, size_t i, size_t j, size_t N, size_t M, size_t NM) {
46
+ // up
47
+ if (N != 1) {
48
+ nb[0] = (i != 0) ? (i - 1) * M + j : (N - 1) * M + j;
49
+ } else {
50
+ nb[0] = NM;
51
+ }
52
+
53
+ // down
54
+ if (N != 1) {
55
+ nb[1] = (i != N - 1) ? (i + 1) * M + j : j;
56
+ } else {
57
+ nb[1] = NM;
58
+ }
59
+
60
+ // We explicitly asked for M > 1
61
+ // left
62
+ nb[2] = (j != 0) ? i * M + j - 1 : i * M + M - 1;
63
+
64
+ // right
65
+ nb[3] = (j != M - 1) ? i * M + j + 1 : i * M;
66
+ }
67
+
68
+
69
+
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) {
73
+
74
+ size_t N = mod->N;
75
+ size_t M = mod->M;
76
+ size_t NM = N * M;
77
+ size_t max_record = mod->max_record;
78
+ size_t ij = 0; // used to track index i * M + j in double for loops
79
+
80
+ // init world
81
+ for (size_t i = 0; i < N; i++) {
82
+ 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++;
85
+ }
86
+ }
87
+
88
+ // init nb_indices
89
+ ij = 0;
90
+ if (mod->boundary) {
91
+ for (size_t i = 0; i < N; i++) {
92
+ for (size_t j = 0; j < M; j++) {
93
+ find_nb_zero_flux(&nb_indices[ij * 4], i, j, N, M, NM);
94
+ ij++;
95
+ }
96
+ }
97
+ } else {
98
+ for (size_t i = 0; i < N; i++) {
99
+ for (size_t j = 0; j < M; j++) {
100
+ find_nb_periodical(&nb_indices[ij * 4], i, j, N, M, NM);
101
+ ij++;
102
+ }
103
+ }
104
+ }
105
+
106
+
107
+ // 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
+ }
114
+ }
115
+
116
+ //////// Begin Running ////////
117
+
118
+ // 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 + 4]));
123
+ ij++;
124
+ }
125
+ }
126
+
127
+ // init migration rates & store patch rates
128
+ ij = 0;
129
+ for (size_t i = 0; i < N; i++) {
130
+ for (size_t j = 0; j < M; j++) {
131
+ update_mig_all(&world[ij], &(mod->P[ij * 6])); // init mig rates for all 4 directions
132
+ double ij_rates = world[ij].sum_pi_death_rates + world[ij].sum_mig_rates;
133
+ patch_rates[ij] = ij_rates;
134
+ sum_rates_by_row[i] += ij_rates;
135
+ *sum_rates_p = *sum_rates_p + ij_rates; // can't do *sum_rates_p += ij_rates
136
+ ij++;
137
+ }
138
+ }
139
+
140
+ // pick the first random event
141
+ double expected_sum = random01() * *sum_rates_p;
142
+ find_patch(picked_p, expected_sum, patch_rates, sum_rates_by_row, *sum_rates_p, N, M);
143
+ size_t picked_idx = picked_p->i * M + picked_p->j;
144
+ size_t e0 = find_event(&world[picked_idx], expected_sum - picked_p->current_sum);
145
+
146
+ // make signal
147
+ if (mod->boundary) {
148
+ make_signal_zero_flux(picked_p->i, picked_p->j, e0, sig_p);
149
+ } else {
150
+ make_signal_periodical(N, M, picked_p->i, picked_p->j, e0, sig_p);
151
+ }
152
+ sig_p->ij1 = sig_p->i1 * M + sig_p->j1;
153
+ sig_p->ij2 = sig_p->i2 * M + sig_p->j2;
154
+
155
+ // update patch based on signal
156
+ change_popu(&world[sig_p->ij1], sig_p->e1);
157
+ if (!sig_p->only_first) {
158
+ change_popu(&world[sig_p->ij2], sig_p->e2);
159
+ }
160
+
161
+ // time increment
162
+ double time = (1.0 / *sum_rates_p) * log(1.0 / random01());
163
+
164
+ if (time > mod->maxtime) {
165
+ // maxtime too small
166
+ return -1;
167
+ }
168
+
169
+ // store data
170
+ if (time > mod->record_itv) {
171
+ size_t recod_idx = (size_t) (time / mod->record_itv);
172
+ ij = 0;
173
+ for (size_t i = 0; i < N; i++) {
174
+ for (size_t j = 0; j < M; j++) {
175
+ for (size_t k = 0; k < recod_idx; k++) {
176
+ mod->U1d[ij * max_record + k] += world[ij].U;
177
+ mod->V1d[ij * max_record + k] += world[ij].V;
178
+ mod->Upi_1d[ij * max_record + k] += world[ij].U_pi;
179
+ mod->Vpi_1d[ij * max_record + k] += world[ij].V_pi;
180
+ }
181
+ ij++;
182
+ }
183
+ }
184
+ }
185
+
186
+ return time;
187
+ }
188
+
189
+
190
+
191
+ static void single_test(model_t* restrict mod, uint32_t update_sum_frequency, uint32_t* result, char* message) {
192
+ // bring some dimensions to the front
193
+ size_t N = mod->N;
194
+ size_t M = mod->M;
195
+ size_t NM = N * M;
196
+ double maxtime = mod->maxtime;
197
+ size_t max_record = mod->max_record;
198
+ double record_itv = mod->record_itv;
199
+ bool boundary = mod->boundary;
200
+
201
+ double one_time = maxtime / (double)(update_sum_frequency > 100 ? update_sum_frequency : 100);
202
+ double one_progress = 0.0;
203
+
204
+ if (mod->print_pct != -1) {
205
+ one_progress = maxtime * mod->print_pct / 100.0;
206
+ fprintf(stdout, "\r ");
207
+ fprintf(stdout, "\r%s: 0 %%", message);
208
+ fflush(stdout);
209
+ } else {
210
+ one_progress = 2.0 * maxtime;
211
+ }
212
+
213
+ double one_update_sum = maxtime / (double) (update_sum_frequency + 1);
214
+
215
+ double current_time = one_time;
216
+ double current_progress = one_progress;
217
+ double current_update_sum = one_update_sum;
218
+
219
+ // Initialize simulation
220
+ patch_t* world = (patch_t*) calloc(NM * SIZEOF_PATCH_T, sizeof(size_t));
221
+ size_t* nb_indices = (size_t*) calloc(NM * 4, sizeof(size_t));
222
+
223
+ double* patch_rates = (double*) calloc(NM, sizeof(double));
224
+ double* sum_rates_by_row = (double*) calloc(N, sizeof(double));
225
+ double sum_rates = 0.0;
226
+
227
+ signal_t signal;
228
+ patch_picked_t picked;
229
+
230
+ double time = single_init(mod, world, nb_indices, patch_rates, sum_rates_by_row, &sum_rates, &signal, &picked);
231
+ if (time == -1) {
232
+ // time too small
233
+ *result = SMALL_MAXTIME;
234
+ single_test_free(world, nb_indices, patch_rates, sum_rates_by_row);
235
+ world = NULL;
236
+ nb_indices = NULL;
237
+ patch_rates = NULL;
238
+ sum_rates_by_row = NULL;
239
+ return;
240
+ }
241
+ size_t record_index = time / mod->record_itv;
242
+ double record_time = time - record_index * record_itv;
243
+
244
+
245
+ while (time < maxtime) {
246
+
247
+ // Print progress and update sums if needed
248
+ if (time > current_time) {
249
+ current_time += one_time;
250
+ if (time > current_progress) {
251
+ uint8_t curr_prog = (uint8_t)(time * 100 / maxtime);
252
+ if (curr_prog < 10) {
253
+ fprintf(stdout, "\r%s: %d %%", message, (int)(time * 100 / maxtime));
254
+ } else {
255
+ fprintf(stdout, "\r%s: %d%%", message, (int)(time * 100 / maxtime));
256
+ }
257
+ fflush(stdout);
258
+ //fflush(stdout); // Make sure it prints immediately
259
+ current_progress += one_progress;
260
+ }
261
+ if (time > current_update_sum) {
262
+ current_update_sum += one_update_sum;
263
+
264
+ // recalculate sum
265
+ size_t ij = 0;
266
+ for (size_t i = 0; i < N; i++) {
267
+ for (size_t j = 0; j < M; j++) {
268
+ world[ij].sum_U_weight = 0;
269
+ world[ij].sum_V_weight = 0;
270
+ for (size_t k = 0; k < 4; k++) {
271
+ world[ij].sum_U_weight += world[ij].U_weight[k];
272
+ world[ij].sum_V_weight += world[ij].V_weight[k];
273
+ }
274
+ }
275
+ }
276
+ ij = 0;
277
+ for (size_t i = 0; i < N; i++) {
278
+ sum_rates_by_row[i] = 0;
279
+ for (size_t j = 0; j < M; j++) {
280
+ sum_rates_by_row[i] += patch_rates[ij];
281
+ ij++;
282
+ }
283
+ }
284
+ sum_rates = 0;
285
+ for (size_t i = 0; i < N; i++) {
286
+ sum_rates += sum_rates_by_row[i];
287
+ }
288
+ }
289
+ // check overflow
290
+ size_t ij = 0;
291
+ for (size_t i = 0; i < N; i++) {
292
+ for (size_t j = 0; j < M; j++) {
293
+ if ((mod->P[ij * 6 + 2] * world[ij].U_pi > EXP_OVERFLOW_BOUND)
294
+ || (mod->P[ij * 6 + 3] * world[ij].V_pi > EXP_OVERFLOW_BOUND)) {
295
+ fprintf(stdout, "\nError: overflow at time about %f, patch (%zu, %zu). Simulation stopped.\n", time, i, j);
296
+ fflush(stdout);
297
+ *result = OVERFLOW;
298
+ single_test_free(world, nb_indices, patch_rates, sum_rates_by_row);
299
+ world = NULL;
300
+ nb_indices = NULL;
301
+ patch_rates = NULL;
302
+ sum_rates_by_row = NULL;
303
+ return;
304
+ }
305
+ ij += 1;
306
+ }
307
+ }
308
+ }
309
+
310
+ // update last-changed patches
311
+ // subtract old rates first, then update patch, then add new rates
312
+ // and split into cases whether there are two or one last-changed patches (because need to update all payoffs first and then mig rates)
313
+ size_t si1 = signal.i1;
314
+ size_t si2 = signal.i2;
315
+ size_t sij1 = signal.ij1;
316
+ size_t sij2 = signal.ij2;
317
+ bool only_first = signal.only_first;
318
+ if (only_first) {
319
+ // if only one
320
+ double picked_rate = world[sij1].sum_pi_death_rates + world[sij1].sum_mig_rates;
321
+ sum_rates_by_row[si1] -= picked_rate;
322
+ sum_rates -= picked_rate;
323
+
324
+ update_pi_k(&world[sij1], &(mod->X[sij1 * 4]), &(mod->P[sij1 * 6 + 4]));
325
+ update_mig_all(&world[sij1], &(mod->P[sij1 * 6]));
326
+
327
+ picked_rate = world[sij1].sum_pi_death_rates + world[sij1].sum_mig_rates;
328
+ patch_rates[sij1] = picked_rate;
329
+ sum_rates_by_row[si1] += picked_rate;
330
+ sum_rates += picked_rate;
331
+ } else {
332
+ // two
333
+ double picked_rate1 = world[sij1].sum_pi_death_rates + world[sij1].sum_mig_rates;
334
+ double picked_rate2 = world[sij2].sum_pi_death_rates + world[sij2].sum_mig_rates;
335
+ sum_rates_by_row[si1] -= picked_rate1;
336
+ sum_rates_by_row[si2] -= picked_rate2;
337
+ sum_rates -= picked_rate1;
338
+ sum_rates -= picked_rate2;
339
+
340
+ update_pi_k(&world[sij1], &(mod->X[sij1 * 4]), &(mod->P[sij1 * 6 + 4])); // update both patches' payoffs first
341
+ update_pi_k(&world[sij2], &(mod->X[sij2 * 4]), &(mod->P[sij2 * 6 + 4]));
342
+ update_mig_all(&world[sij1], &(mod->P[sij1 * 6])); // and then mig rates
343
+ update_mig_all(&world[sij2], &(mod->P[sij2 * 6]));
344
+
345
+ picked_rate1 = world[sij1].sum_pi_death_rates + world[sij1].sum_mig_rates;
346
+ picked_rate2 = world[sij2].sum_pi_death_rates + world[sij2].sum_mig_rates;
347
+
348
+ patch_rates[sij1] = picked_rate1;
349
+ patch_rates[sij2] = picked_rate2;
350
+ sum_rates_by_row[si1] += picked_rate1;
351
+ sum_rates_by_row[si2] += picked_rate2;
352
+ sum_rates += picked_rate1;
353
+ sum_rates += picked_rate2;
354
+ }
355
+
356
+ // update neighbors of last-changed patches
357
+ if (only_first) {
358
+ for (uint8_t k = 0; k < 4; k++) {
359
+ size_t nb_idx = nb_indices[sij1 * 4 + k];
360
+ if (nb_idx == NM) { continue; } // invalid neighbor
361
+ // all neighbors, as long as exists, need to change
362
+ update_mig_all(&world[nb_idx], &(mod->P[nb_idx * 6]));
363
+ // sum_mig_rate is not changed
364
+ }
365
+ } else {
366
+ // the first patch
367
+ for (uint8_t k = 0; k < 4; k++) {
368
+ size_t nb_idx = nb_indices[sij1 * 4 + k];
369
+ if (nb_idx == NM) { continue; }
370
+ if (nb_need_change(nb_idx, sij1, sij2)) {
371
+ update_mig_all(&world[nb_idx], &(mod->P[nb_idx * 6]));
372
+ }
373
+ }
374
+ // the second patch
375
+ for (uint8_t k = 0; k < 4; k++) {
376
+ size_t nb_idx = nb_indices[sij2 * 4 + k];
377
+ if (nb_idx == NM) { continue; }
378
+ if (nb_need_change(nb_idx, sij1, sij2)) {
379
+ update_mig_all(&world[nb_idx], &(mod->P[nb_idx * 6]));
380
+ }
381
+ }
382
+ }
383
+
384
+
385
+ // pick a random event
386
+ double expected_sum = random01() * sum_rates;
387
+ find_patch(&picked, expected_sum, patch_rates, sum_rates_by_row, sum_rates, N, M);
388
+ size_t picked_idx = picked.i * M + picked.j;
389
+ uint8_t e0 = find_event(&world[picked_idx], expected_sum - picked.current_sum);
390
+
391
+ // make signal
392
+ if (boundary) {
393
+ make_signal_zero_flux(picked.i, picked.j, e0, &signal);
394
+ } else {
395
+ make_signal_periodical(N, M, picked.i, picked.j, e0, &signal);
396
+ }
397
+ signal.ij1 = signal.i1 * M + signal.j1;
398
+ signal.ij2 = signal.i2 * M + signal.j2;
399
+
400
+ // let the event happenn
401
+ change_popu(&world[signal.ij1], signal.e1);
402
+ if (!signal.only_first) {
403
+ change_popu(&world[signal.ij2], signal.e2);
404
+ }
405
+
406
+ // increase time
407
+ double dt = (1.0 / sum_rates) * log(1.0 / random01());
408
+ time += dt;
409
+ record_time += dt;
410
+
411
+ // record data
412
+ if (time < maxtime) {
413
+ if (record_time > record_itv) {
414
+ size_t multi_records = record_time / record_itv;
415
+ record_time -= multi_records * record_itv;
416
+ size_t upper = record_index + multi_records;
417
+
418
+ size_t ij = 0;
419
+ for (size_t i = 0; i < N; i++) {
420
+ for (size_t j = 0; j < M; j++) {
421
+ for (size_t k = record_index; k < upper; k++) {
422
+ mod->U1d[ij * max_record + k] += world[ij].U;
423
+ mod->V1d[ij * max_record + k] += world[ij].V;
424
+ mod->Upi_1d[ij * max_record + k] += world[ij].U_pi;
425
+ mod->Vpi_1d[ij * max_record + k] += world[ij].V_pi;
426
+ }
427
+ ij++;
428
+ }
429
+ }
430
+ record_index += multi_records;
431
+ }
432
+
433
+ } else {
434
+ // if already exceeds maxtime
435
+ size_t ij = 0;
436
+ for (size_t i = 0; i < N; i++) {
437
+ for (size_t j = 0; j < M; j++) {
438
+ for (size_t k = record_index; k < max_record; k++) {
439
+ mod->U1d[ij * max_record + k] += world[ij].U;
440
+ mod->V1d[ij * max_record + k] += world[ij].V;
441
+ mod->Upi_1d[ij * max_record + k] += world[ij].U_pi;
442
+ mod->Vpi_1d[ij * max_record + k] += world[ij].V_pi;
443
+ }
444
+ ij++;
445
+ }
446
+ }
447
+ }
448
+ }
449
+
450
+ //////// End of while loop ////////
451
+
452
+ /*if (mod->print_pct != -1) {
453
+ fprintf(stdout, "\r%s: 100%%", message);
454
+ fflush(stdout);
455
+ }*/
456
+
457
+ single_test_free(world, nb_indices, patch_rates, sum_rates_by_row);
458
+ world = NULL;
459
+ nb_indices = NULL;
460
+ patch_rates = NULL;
461
+ sum_rates_by_row = NULL;
462
+
463
+ *result = SUCCESS;
464
+ }
465
+
466
+
467
+
468
+ static void single_test_free(patch_t* world, size_t* nb_indices, double* patch_rates, double* sum_rates_by_row) {
469
+ free(world);
470
+ free(nb_indices);
471
+ free(patch_rates);
472
+ free(sum_rates_by_row);
473
+ }
474
+
475
+
476
+
477
+ void run(model_t* mod, char* message, size_t msg_len) {
478
+ if (!mod->data_empty) {
479
+ fprintf(stdout, "Error: mod has non-empty data\n");
480
+ fflush(stdout);
481
+ return; // Or handle error as you see fit
482
+ }
483
+
484
+ double start = clock();
485
+
486
+ mod->data_empty = false;
487
+
488
+ // initialize random
489
+ if (mod->seed != -1) {
490
+ srand((uint32_t) mod->seed);
491
+ } else {
492
+ srand(time(NULL));
493
+ }
494
+
495
+ if (mod->seed == -1){
496
+ srand(time(NULL));
497
+ } else {
498
+ srand(mod->seed);
499
+ }
500
+
501
+ if (mod->print_pct == 0) {
502
+ mod->print_pct = 5; // default print_pct
503
+ }
504
+
505
+ uint32_t update_sum_frequency = 16;
506
+ size_t i = 0;
507
+
508
+ while (i < mod->sim_time) {
509
+ char curr_msg[20 + msg_len]; // message for current round
510
+ strcpy(curr_msg, message);
511
+ strcat(curr_msg, "round ");
512
+ snprintf(curr_msg + strlen(curr_msg), sizeof(curr_msg) - strlen(curr_msg), "%zu", i);
513
+
514
+ /*if (predict_runtime && i > 0) {
515
+ double time_elapsed = timer() - start;
516
+ double pred_runtime = time_elapsed / i * (mod->sim_time - i);
517
+ snprintf(end_info, sizeof(end_info), ", ~%.2fs left", pred_runtime);
518
+ }*/
519
+
520
+ uint32_t result = GENERAL_FAILURE;
521
+ single_test(mod, update_sum_frequency, &result, curr_msg);
522
+
523
+ switch (result) {
524
+ case SUCCESS:
525
+ i++;
526
+ break;
527
+ case SMALL_MAXTIME:
528
+ fprintf(stdout, "Error: maxtime too small.\n");
529
+ fflush(stdout);
530
+ return;
531
+ case GENERAL_FAILURE:
532
+ // Possibly Numerical issue, increase precision
533
+ update_sum_frequency *= 4;
534
+ if (update_sum_frequency > 1e5) {
535
+ fprintf(stdout, "Error: input model not handable.\n");
536
+ fflush(stdout);
537
+ return;
538
+ }
539
+ fprintf(stdout, "Possible numerical issue at round %zu. Trying higher precision now.\n", i);
540
+ fflush(stdout);
541
+ break;
542
+ case OVERFLOW:
543
+ // error message is handled by single_test
544
+ return;
545
+ default:
546
+ fprintf(stdout, "Error: unkown behavior.\n");
547
+ fflush(stdout);
548
+ return;
549
+ }
550
+ }
551
+
552
+ calculate_ave(mod);
553
+
554
+ double stop = clock();
555
+
556
+ fprintf(stdout, "\r%sruntime: %.3fs \n", message, (double)(stop - start) / CLOCKS_PER_SEC);
557
+ fflush(stdout);
558
+ }
559
+
560
+
561
+