@@ -74,8 +74,10 @@ def lotka_volterra(r1, r2, r3, prey_init=50, predator_init=100, sigma=0., n_obs=
74
74
75
75
n_full = 20000
76
76
stock = np .empty ((batch_size , n_full , 2 ), dtype = np .int32 )
77
- stock [:, 0 , 0 ] = prey_init
78
- stock [:, 0 , 1 ] = predator_init
77
+ # As we use approximate continuous priors for prey_init and
78
+ # predator_init, we'll round them down to closest integers
79
+ stock [:, 0 , 0 ] = np .floor (prey_init )
80
+ stock [:, 0 , 1 ] = np .floor (predator_init )
79
81
stoichiometry = np .array ([[1 , 0 ], [- 1 , 1 ], [0 , - 1 ], [0 , 0 ]], dtype = np .int32 )
80
82
times = np .empty ((batch_size , n_full ))
81
83
times [:, 0 ] = 0
@@ -141,9 +143,11 @@ def lotka_volterra(r1, r2, r3, prey_init=50, predator_init=100, sigma=0., n_obs=
141
143
return stock_out
142
144
143
145
144
- def get_model (n_obs = 50 , true_params = None , seed_obs = None , ** kwargs ):
146
+ def get_model (n_obs = 50 , true_params = None , observation_noise = False , seed_obs = None , ** kwargs ):
145
147
"""Return a complete Lotka-Volterra model in inference task.
146
148
149
+ Including observation noise to system is optional.
150
+
147
151
Parameters
148
152
----------
149
153
n_obs : int, optional
@@ -152,6 +156,8 @@ def get_model(n_obs=50, true_params=None, seed_obs=None, **kwargs):
152
156
Parameters with which the observed data is generated.
153
157
seed_obs : int, optional
154
158
Seed for the observed data generation.
159
+ observation_noise : bool, optional
160
+ Whether or not add normal noise to observations.
155
161
156
162
Returns
157
163
-------
@@ -160,34 +166,117 @@ def get_model(n_obs=50, true_params=None, seed_obs=None, **kwargs):
160
166
"""
161
167
logger = logging .getLogger ()
162
168
if true_params is None :
163
- true_params = [1.0 , 0.005 , 0.6 , 50 , 100 , 10. ]
169
+ if observation_noise :
170
+ true_params = [1.0 , 0.005 , 0.6 , 50 , 100 , 10. ]
171
+ else :
172
+ true_params = [1.0 , 0.005 , 0.6 , 50 , 100 , 0. ]
173
+ else :
174
+ if observation_noise :
175
+ if len (true_params ) != 6 :
176
+ raise ValueError (
177
+ "Option observation_noise = True."
178
+ " Provide six input parameters."
179
+ )
180
+ else :
181
+ if len (true_params ) != 5 :
182
+ raise ValueError (
183
+ "Option observation_noise = False."
184
+ " Provide five input parameters."
185
+ )
186
+ true_params = true_params + [0 ]
164
187
165
188
kwargs ['n_obs' ] = n_obs
166
189
y_obs = lotka_volterra (* true_params , random_state = np .random .RandomState (seed_obs ), ** kwargs )
167
190
168
191
m = elfi .ElfiModel ()
169
192
sim_fn = partial (lotka_volterra , ** kwargs )
170
- priors = []
171
- sumstats = []
193
+ priors = [
194
+ elfi .Prior (ExpUniform , - 6. , 2. , model = m , name = 'r1' ),
195
+ elfi .Prior (ExpUniform , - 6. , 2. , model = m , name = 'r2' ), # easily kills populations
196
+ elfi .Prior (ExpUniform , - 6. , 2. , model = m , name = 'r3' ),
197
+ elfi .Prior ('normal' , 50 , np .sqrt (50 ), model = m , name = 'prey0' ),
198
+ elfi .Prior ('normal' , 100 , np .sqrt (100 ), model = m , name = 'predator0' )
199
+ ]
172
200
173
- priors .append (elfi .Prior (ExpUniform , - 6. , 2. , model = m , name = 'r1' ))
174
- priors .append (elfi .Prior (ExpUniform , - 6. , 2. , model = m , name = 'r2' )) # easily kills populations
175
- priors .append (elfi .Prior (ExpUniform , - 6. , 2. , model = m , name = 'r3' ))
176
- priors .append (elfi .Prior ('poisson' , 50 , model = m , name = 'prey0' ))
177
- priors .append (elfi .Prior ('poisson' , 100 , model = m , name = 'predator0' ))
178
- priors .append (elfi .Prior (ExpUniform , np .log (0.5 ), np .log (50 ), model = m , name = 'sigma' ))
201
+ if observation_noise :
202
+ priors .append (elfi .Prior (ExpUniform , np .log (0.5 ), np .log (50 ), model = m , name = 'sigma' ))
179
203
180
204
elfi .Simulator (sim_fn , * priors , observed = y_obs , name = 'LV' )
181
- sumstats .append (elfi .Summary (partial (pick_stock , species = 0 ), m ['LV' ], name = 'prey' ))
182
- sumstats .append (elfi .Summary (partial (pick_stock , species = 1 ), m ['LV' ], name = 'predator' ))
183
- elfi .Distance ('sqeuclidean' , * sumstats , name = 'd' )
205
+
206
+ sumstats = [
207
+ elfi .Summary (partial (stock_mean , species = 0 ), m ['LV' ], name = 'prey_mean' ),
208
+ elfi .Summary (partial (stock_mean , species = 1 ), m ['LV' ], name = 'pred_mean' ),
209
+ elfi .Summary (partial (stock_log_variance , species = 0 ), m ['LV' ], name = 'prey_log_var' ),
210
+ elfi .Summary (partial (stock_log_variance , species = 1 ), m ['LV' ], name = 'pred_log_var' ),
211
+ elfi .Summary (partial (stock_autocorr , species = 0 , lag = 1 ), m ['LV' ], name = 'prey_autocorr_1' ),
212
+ elfi .Summary (partial (stock_autocorr , species = 1 , lag = 1 ), m ['LV' ], name = 'pred_autocorr_1' ),
213
+ elfi .Summary (partial (stock_autocorr , species = 0 , lag = 2 ), m ['LV' ], name = 'prey_autocorr_2' ),
214
+ elfi .Summary (partial (stock_autocorr , species = 1 , lag = 2 ), m ['LV' ], name = 'pred_autocorr_2' ),
215
+ elfi .Summary (stock_crosscorr , m ['LV' ], name = 'crosscorr' )
216
+ ]
217
+
218
+ elfi .Distance ('euclidean' , * sumstats , name = 'd' )
184
219
185
220
logger .info ("Generated %i observations with true parameters r1: %.1f, r2: %.3f, r3: %.1f, "
186
221
"prey0: %i, predator0: %i, sigma: %.1f." , n_obs , * true_params )
187
222
188
223
return m
189
224
190
225
226
+ def stock_mean (stock , species = 0 , mu = 0 , std = 1 ):
227
+ """Calculate the mean of the trajectory by species."""
228
+ stock = np .atleast_2d (stock [:, :, species ])
229
+ mu_x = np .mean (stock , axis = 1 )
230
+
231
+ return (mu_x - mu ) / std
232
+
233
+
234
+ def stock_log_variance (stock , species = 0 , mu = 0 , std = 1 ):
235
+ """Calculate the log variance of the trajectory by species."""
236
+ stock = np .atleast_2d (stock [:, :, species ])
237
+ var_x = np .var (stock , axis = 1 , ddof = 1 )
238
+ log_x = np .log (var_x + 1 )
239
+
240
+ return (log_x - mu ) / std
241
+
242
+
243
+ def stock_autocorr (stock , species = 0 , lag = 1 , mu = 0 , std = 1 ):
244
+ """Calculate the autocorrelation of lag n of the trajectory by species."""
245
+ stock = np .atleast_2d (stock [:, :, species ])
246
+ n_obs = stock .shape [1 ]
247
+
248
+ mu_x = np .mean (stock , axis = 1 , keepdims = True )
249
+ std_x = np .std (stock , axis = 1 , ddof = 1 , keepdims = True )
250
+ sx = ((stock - np .repeat (mu_x , n_obs , axis = 1 )) / np .repeat (std_x , n_obs , axis = 1 ))
251
+ sx_t = sx [:, lag :]
252
+ sx_s = sx [:, :- lag ]
253
+
254
+ C = np .sum (sx_t * sx_s , axis = 1 ) / (n_obs - 1 )
255
+
256
+ return (C - mu ) / std
257
+
258
+
259
+ def stock_crosscorr (stock , mu = 0 , std = 1 ):
260
+ """Calculate the cross correlation of the species trajectories."""
261
+ n_obs = stock .shape [1 ]
262
+
263
+ x_preys = stock [:, :, 0 ] # preys
264
+ x_preds = stock [:, :, 1 ] # predators
265
+
266
+ mu_preys = np .mean (x_preys , axis = 1 , keepdims = True )
267
+ mu_preds = np .mean (x_preds , axis = 1 , keepdims = True )
268
+ std_preys = np .std (x_preys , axis = 1 , keepdims = True )
269
+ std_preds = np .std (x_preds , axis = 1 , keepdims = True )
270
+ s_preys = ((x_preys - np .repeat (mu_preys , n_obs , axis = 1 ))
271
+ / np .repeat (std_preys , n_obs , axis = 1 ))
272
+ s_preds = ((x_preds - np .repeat (mu_preds , n_obs , axis = 1 ))
273
+ / np .repeat (std_preds , n_obs , axis = 1 ))
274
+
275
+ C = np .sum (s_preys * s_preds , axis = 1 ) / (n_obs - 1 )
276
+
277
+ return (C - mu ) / std
278
+
279
+
191
280
class ExpUniform (elfi .Distribution ):
192
281
r"""Prior distribution for parameter.
193
282
@@ -235,20 +324,3 @@ def pdf(cls, x, a, b):
235
324
p = np .where ((x < np .exp (a )) | (x > np .exp (b )), 0 , np .reciprocal (x ))
236
325
p /= (b - a ) # normalize
237
326
return p
238
-
239
-
240
- def pick_stock (stock , species ):
241
- """Return the stock for single species.
242
-
243
- Parameters
244
- ----------
245
- stock : np.array
246
- species : int
247
- 0 for prey, 1 for predator.
248
-
249
- Returns
250
- -------
251
- np.array
252
-
253
- """
254
- return stock [:, :, species ]
0 commit comments