@@ -174,3 +174,139 @@ with a custom :meth:`~Simulatable.update_timestep` implementation.
174174 plt.ylabel(r'$\o mega$')
175175
176176 plt.show()
177+
178+ Learning Walk Parameters
179+ ------------------------
180+
181+ In the above examples, the diffusion distribution was treated as exactly
182+ known by the model. We can also parameterize this distribution, adding its
183+ parameters to model to be learned as well. :class: `GaussianRandomWalkModel `
184+ is a built in model similar to :class: `RandomWalkModel `. It is more
185+ restrictive in the sense that it is limited to gaussian time-step updates,
186+ but more general in that it has the ability to automatically append a
187+ parameterization of the gaussian time-step distribution, either diagonal or
188+ dense, to the underlying model.
189+
190+ For example suppose that we have a coin whose bias is taking a random walk
191+ in time with an unknown diffusion constant.
192+ To avoid exiting the allowable space of biases, :math: `[0 ,1 ]`,
193+ we transform to inverse-logit space before taking a gaussian step, and
194+ transform back to the probability interval after each step.
195+
196+ .. plot ::
197+
198+ import numpy as np
199+ from scipy.special import expit, logit
200+ from qinfer import (
201+ CoinModel, BinomialModel, GaussianRandomWalkModel,
202+ UniformDistribution, SMCUpdater
203+ )
204+
205+ # Put a random walk on top of a binomial coin model
206+ model = GaussianRandomWalkModel(
207+ BinomialModel(CoinModel()),
208+ model_transformation=(logit, expit)
209+ )
210+
211+ # Generate some data with a true diffusion 0.05
212+ true_sigma_p = 0.05
213+ Nbin = 10
214+ p = expit(logit(0.5) + np.cumsum(true_sigma_p * np.random.normal(size=300)))
215+ data = np.random.binomial(Nbin, 1-p)
216+
217+ # Analyse the data
218+ prior = UniformDistribution([[0.2,0.8],[0,0.1]])
219+ u = SMCUpdater(model, 10000, prior)
220+ ests, stds = np.empty((data.size+1, 2)), np.empty((data.size+1, 2))
221+ ts = np.arange(ests.shape[0])
222+ ests[0,:] = u.est_mean()
223+ for idx in range(data.size):
224+ expparam = np.array([Nbin]).astype(model.expparams_dtype)
225+ u.update(np.array([data[idx]]), expparam)
226+ ests[idx+1,:] = u.est_mean()
227+ stds[idx+1,:] = np.sqrt(np.diag(u.est_covariance_mtx()))
228+
229+ plt.figure(figsize=(10,10))
230+ plt.subplot(2,1,1)
231+ u.plot_posterior_marginal(1)
232+ plt.title('Diffusion parameter posterior')
233+
234+ plt.subplot(2,1,2)
235+ plt.plot(ts, ests[:,0], label='estimated')
236+ plt.fill_between(ts, ests[:,0]-stds[:,0], ests[:,0]+stds[:,0],
237+ alpha=0.2, antialiased=True)
238+ plt.plot(ts[1:], p, '--', label='actual')
239+ plt.legend()
240+ plt.title('Coin bias vs. time')
241+ plt.show()
242+
243+ As a second example, consider a 5-sided die for which the 3rd, 4th and 5th
244+ sides are taking a correlated gaussian random walk, and the other two sides
245+ are constant. We can attempt to learn the six parameters of the cholesky
246+ factorization of the random walk covariance matrix as we track the drift
247+ of the die probabilities.
248+
249+ .. plot ::
250+
251+ import numpy as np
252+ from qinfer.utils import to_simplex, from_simplex, sample_multinomial
253+ from qinfer import (
254+ NDieModel, MultinomialModel, GaussianRandomWalkModel,
255+ UniformDistribution, ConstrainedSumDistribution, SMCUpdater, ProductDistribution
256+ )
257+
258+ # Put a random walk on top of a multinomial die model
259+ randomwalk_idxs = [2,3,4] # only these sides of the die are taking a walk
260+ model = GaussianRandomWalkModel(
261+ MultinomialModel(NDieModel(5)),
262+ model_transformation=(from_simplex, to_simplex),
263+ diagonal=False,
264+ random_walk_idxs = randomwalk_idxs
265+ )
266+
267+ # Generate some data with some true covariance matrix
268+ true_cov = 0.1 * np.random.random(size=(3,3))
269+ true_cov = np.dot(true_cov, true_cov.T)
270+ Nmult = 40
271+ ps = from_simplex(np.array([[0.1,0.2,0.2,0.4,.1]] * 200))
272+ ps[:, randomwalk_idxs] += np.random.multivariate_normal(np.zeros(3), true_cov, size=200).cumsum(axis=0)
273+ ps = to_simplex(ps)
274+ expparam = np.array([(0,Nmult)],dtype=model.expparams_dtype)
275+ data = sample_multinomial(Nmult, ps.T).T
276+
277+ # Analyse the data
278+ prior = ProductDistribution(
279+ ConstrainedSumDistribution(UniformDistribution([[0,1]] * 5)),
280+ UniformDistribution([[0,0.2]] * 6)
281+ )
282+ u = SMCUpdater(model, 10000, prior)
283+ ests, stds = np.empty((data.shape[0]+1, model.n_modelparams)), np.empty((data.shape[0]+1, model.n_modelparams))
284+ ts = np.arange(ests.shape[0])
285+ ests[0,:] = u.est_mean()
286+ for idx in range(data.shape[0]):
287+ expparam = np.array([(0,Nmult)],dtype=model.expparams_dtype)
288+ outcome = np.array([(data[idx],)], dtype=model.domain(expparam)[0].dtype)
289+ u.update(outcome, expparam)
290+ ests[idx+1,:] = u.est_mean()
291+ stds[idx+1,:] = np.sqrt(np.diag(u.est_covariance_mtx()))
292+
293+ true_chol = np.linalg.cholesky(true_cov)
294+ k = 1
295+ plt.figure(figsize=(10,10))
296+ for idx, coord in enumerate(zip(*model._srw_tri_idxs)):
297+ i, j = coord
298+ plt.subplot(3,3,i*3 + j + 1)
299+ u.plot_posterior_marginal(5 + idx)
300+ plt.axvline(true_chol[i,j],color='b')
301+ plt.show()
302+
303+ plt.figure(figsize=(12,10))
304+ color=iter(plt.cm.Vega10(range(5)))
305+ for idx in range(5):
306+ c=next(color)
307+ plt.plot(ps[:, idx], '--', label='$p_{}$ actual'.format(idx), color=c)
308+ plt.plot(ests[:,idx], label='$p_{}$ estimated'.format(idx), color=c)
309+ plt.fill_between(range(len(ests)), ests[:,idx]-stds[:,idx], ests[:,idx]+stds[:,idx],alpha=0.2, color=c, antialiased=True)
310+ plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))
311+ plt.show()
312+
0 commit comments