-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgrowth.cpp
317 lines (291 loc) · 12.2 KB
/
growth.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
#include <RcppArmadilloExtensions/sample.h>
#include <Rcpp/stats/random/rbinom.h>
#include <Rcpp.h>
using namespace Rcpp;
using namespace std;
// [[Rcpp::depends(RcppArmadillo)]]
//' pick_new_bugs
//'
//' Sampling function for growth_one_group(), growth()
//' @param arr is a vector of positions. If the abundance vector of interest has
//' 5 members, x must be [1 2 3 4 5].
//' @export
// [[Rcpp::export]]
NumericVector pick_new_bugs(NumericVector arr,
int size,
bool replace,
NumericVector prob) {
NumericVector pos = RcppArmadillo::sample(arr, size, replace, prob);
return (pos);
}
//' growth_one_group
//'
//' This Rcpp function simulates the growth of a community of organisms over a
//' specified time step. It takes the current community abundances, a fixed
//' growth step, and an optional interactions matrix as inputs, and returns
//' an updated abundances vector that reflects the growth of the community. The
//' function calculates the probability of growth for each organism based on
//' their current abundance and any specified interactions between organisms. The
//' function then randomly selects organisms to grow based on these probabilities
//' and increases their abundance by growth_step (1 by default).
//' The function takes four arguments:
//' @param this_timestep A numeric vector representing the current abundance of
//' each organism in the population.
//' @param growth_step An integer representing the growth step for organisms in
//' the community, 1 by default.
//' @param interactions An optional numeric matrix representing the interaction
//' between organisms in the population. This argument is set to R_NilValue by
//' default.
//' @export
// [[Rcpp::export]]
NumericVector growth_one_group(NumericVector this_timestep,
double growth_step,
Rcpp::Nullable<Rcpp::NumericMatrix> interactions = R_NilValue) {
// We will sample the growth positions from here
NumericVector arr(this_timestep.size());
for(int x = 0; x < this_timestep.size(); ++x)
arr[x] = x;
// Account for interactions if present
NumericVector prob = this_timestep/sum((this_timestep)); // abs abundances
if (interactions.isNotNull()) {
prob = wrap(as<arma::vec>(prob) + (as<arma::mat>(interactions.get()) * as<arma::vec>(prob)));
for (int i = 0; i < prob.size(); i++) { // no negative probabilities allowed
if (prob[i] < 0) {
prob[i] = 0;
}
}
}
// Grow (loop: as many times as "step" indicates)
NumericVector new_bugs = pick_new_bugs(arr, growth_step, true, prob);
int bug;
for (std::size_t i = 0; i < new_bugs.size(); i++) {
bug = new_bugs[i];
this_timestep[bug] = (this_timestep[(bug)] + 1.0);
}
return(this_timestep);
}
// [[Rcpp::depends(RcppArmadillo)]]
//' growth
//'
//' This function simulates growth in a community by looking at the carrying
//' capacities of the group they belong to. It takes a named vector,
//' carrying_capacities.
//'
//' Growth is RANDOM: all species have the opportunity to grow each
//' time this function is called, but whether they will is random and depends on
//' their abundance. Their growth probability varies depending not only on their
//' abundances but also on their carrying capacity. Growth is not logistic.
//'
//' As opposed to growth_one_group(), the growth rate (probability of being
//' picked for growth) is determined for each individual by the carrying
//' capacity of its group(\[growth_step * group's % of total carrying capacity]).
//' An optionally passed interactions matrix has an effect as well. Every group
//' will have growth_step (by default 1) of its present individuals grow in each
//' run, and they can be from the same ASV/OTU/species or not. Also, growth step
//' is proportional to the carrying capacity of its group. This is to avoid group
//' extinction and also to ensure growth has a similar, proportional rate for
//' each group so all groups reach their CC at the same time.
//'`
//' @export
// [[Rcpp::export]]
NumericVector growth(NumericVector x,
NumericVector carrying_capacities,
int growth_step,
Rcpp::Nullable<Rcpp::NumericMatrix> interactions = R_NilValue) { // named vector
// Set variables
double size = carrying_capacities.length();
NumericVector prob = x/sum((x));
CharacterVector names = carrying_capacities.attr("names");
CharacterVector groups = unique(names);
NumericVector group_ccs = carrying_capacities[groups];
NumericVector arr(x.size());
for (int i = 0; i < x.size(); ++i) {
arr[i] = i;
}
// Account for interactions if present
if (interactions.isNotNull()) {
prob = wrap(as<arma::vec>(prob) + (as<arma::mat>(interactions.get()) * as<arma::vec>(prob)));
for (int i = 0; i < prob.size(); i++) { // no negative probabilities allowed
if (prob[i] < 0) {
prob[i] = 0;
}
}
}
// Stop if all probabilities become 0
if (sum(prob)==0) {
Rf_error("After accounting for interactions, all probabilities became 0. No growth possible. Check the interaction matrix.");
}
// Growth per group
for (String group : groups) {
// indexing vector for this group
LogicalVector g(size); // size is size of x! groups.size() is the # of groups
for (int i = 0; i < size; i++) {
g[i] = (names[i] == group);
}
// (only bugs from this group can grow)
// (also check magnitude of growth_step)
NumericVector group_prob(size);
double sum_group = 0;
for (int i = 0; i < size; i++) {
if (g[i]) {
group_prob[i] = prob[i];
sum_group += x[i];
} else {
group_prob[i] = 0;
}
}
sum_group = trunc(sum_group);
int step = min(static_cast<int>(growth_step), static_cast<int>(sum_group)); // TODO
// Stop if no one can grow in this group
if (sum(group_prob)==0) {
continue;
}
// Grow
NumericVector new_bugs = pick_new_bugs(arr, step, true, group_prob);
int bug;
for (int i = 0; i < new_bugs.size(); i++) { // growth_step times
bug = new_bugs[i];
x[bug] += (1.0 * (group_ccs[group]/sum(group_ccs))); // % of total CC
}
}
return x;
}
//' growth_log
//'
//' This function simulates growth in a community by looking at the carrying
//' capacities of the group they belong to. It takes a named vector,
//' carrying_capacities.
//'
//' Growth is RANDOM but logistic: all species have the opportunity to grow each
//' time this function is called, but whether they will is random and depends on
//' their abundance (random). Their growth probability varies depending not
//' only on their abundances but also on their carrying capacity.
//'
//' As opposed to growth_one_group(), the growth rate is given by a logistic
//' function and not growth_step. Another difference is that growing species are
//' not chosen one by one by sampling, but with a binomial function. That means
//' that the number of different species and the number of individuals that can
//' grow in each iteration of this function is not limited.
//'
//' The difference with growth() is that growth is logistic in this function and
//' there is not growth_step here.
//'
//' If the carrying capacity for a group was surpassed before starting the
//' growth cycle, the species of that group will die at a proportionate rate,
//' while the others may grow.
//'
//' @export
// [[Rcpp::export]]
NumericVector growth_log(NumericVector x,
NumericVector carrying_capacities,
Rcpp::Nullable<Rcpp::NumericMatrix> interactions = R_NilValue) { // named vector
// Set variables
double size = carrying_capacities.length();
NumericVector newx = x;
NumericVector prob = x/sum((x));
CharacterVector names = carrying_capacities.attr("names");
CharacterVector groups = unique(names);
// Get new abundance for each bug, group by group.
// This is were we apply the probabilities: within each group, each species
// might or might not grow.
for (String group : groups) {
// indexing vector for this group
LogicalVector g(size);
for (int i = 0; i < size; i++) {
g[i] = (names[i] == group);
}
// sum_group => total abundance of the group
double sum_group = 0.0;
for (int i = 0; i < size; i++) {
if (g[i]) {
sum_group += x[i];
}
}
// Account for interactions if present
if (interactions.isNotNull()) {
prob = wrap(as<arma::vec>(prob) + (as<arma::mat>(interactions.get()) * as<arma::vec>(prob)));
for (int i = 0; i < prob.size(); i++) { // no negative probabilities allowed
if (prob[i] < 0) {
prob[i] = 0;
}
}
}
// Here, we randomly determine whether each species in the group will grow
for (int i = 0; i < size; i++) {
if (g[i]) {
if (rbinom(1, 1, prob[i])[0] == 1) { // more likely to grow or die if more abundant <- nothing if 0
double growth_step(2 * (1 - sum_group / carrying_capacities[i]));
newx[i] += max(NumericVector(1, growth_step));
}
}
}
}
return newx;
}
//' check_step
//'
//' Checks if a given growth_step is ok for running a growth() function and adjusts
//' it accordingly if it's not (for example not allowing for it to cause too big
//' of a growth, surpassing total wanted final community abundance).
//' @param is_growth_step_a_perc Boolean: if false, growth_step is taken as a fixed
//' value, so the step will always be the same. If true, it is taken to indicate
//' a percentage - the step will be changed proportionally to the community size.
//' If growth_step is 0.02, 2% of the members in the community will grow the next
//' iteration
//' @export
// [[Rcpp::export]]
int check_step(NumericVector this_timestep,
int abun_total,
double growth_step,
bool is_growth_step_a_perc = false) {
int step = growth_step;
if (is_growth_step_a_perc) {
// Apply a percentage first if necessary
if ((growth_step > 1) || (growth_step < 0)) {
Rf_error("If growth_step is a percentage, it has to be a value between 0 (0% of the members of the community will grow) and 1 (100% of members will grow, the total abundance will duplicate every iteration).");
} else {
step = trunc(growth_step * sum(this_timestep));
step = max(1, step); // never less than 1 !!
}
}
// Check magnitude of step
if (trunc((sum(this_timestep)) + growth_step) > abun_total) { // trunc: this_timestep might not have whole numbers.
// Avoid growing too much (when step>1)
step = (abun_total - sum(this_timestep));
} else if (sum(this_timestep) < growth_step) {
// Ensure there are enough bugs to grow with that step
int half = trunc(sum(this_timestep)/2);
step = std::max(half, 1);
}
return(step);
}
//' full_growth
//'
//' This function consists in a loop that runs growth(...)() functions as many
//' times as needed to reach a given population size.
//' @export
// [[Rcpp::export]]
NumericVector full_growth(NumericVector this_timestep,
int abun_total,
int growth_step,
bool is_growth_step_a_perc = false,
String func = "growth",
Rcpp::Nullable<Rcpp::NumericMatrix> interactions = R_NilValue,
Rcpp::Nullable<Rcpp::NumericVector> carrying_capacities = R_NilValue) {
if (func == "growth_one_group") {
while (sum(this_timestep) < abun_total) {
int step = check_step(this_timestep, abun_total, growth_step, is_growth_step_a_perc);
this_timestep = growth_one_group(this_timestep, step, interactions.get());
}
} else if (func == "growth" && carrying_capacities.isNotNull()) {
while (sum(this_timestep) < abun_total) {
int step = check_step(this_timestep, abun_total, growth_step, is_growth_step_a_perc);
this_timestep = growth(this_timestep, carrying_capacities.get(), step, interactions.get());
}
} else if (func == "growth_log" && carrying_capacities.isNotNull()) {
while (round(sum(this_timestep)) < abun_total) {
this_timestep = growth_log(this_timestep, carrying_capacities.get(), interactions.get());
}
}
return(this_timestep);
}