Skip to content

Commit 4fd1e4a

Browse files
authored
Merge pull request #124 from ihincks/feature-gaussian-random-walk
New derived model: GaussianRandomWalk
2 parents 4d0aee3 + 31ef01b commit 4fd1e4a

File tree

8 files changed

+649
-13
lines changed

8 files changed

+649
-13
lines changed

doc/source/apiref/derived_models.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,15 @@ additional functionality or changing the behaviors of underlying models.
4141

4242
.. autoclass:: MLEModel
4343
:members:
44+
45+
:class:`RandomWalkModel` - Model for adding fixed random walk to parameters
46+
---------------------------------------------------------------------------
47+
48+
.. autoclass:: RandomWalkModel
49+
:members:
50+
51+
:class:`GaussianRandomWalkModel` - Model for adding gaussian random walk to parameters
52+
--------------------------------------------------------------------------------------
53+
54+
.. autoclass:: GaussianRandomWalkModel
55+
:members:

doc/source/apiref/utils.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,6 @@ Function Reference
3333

3434
.. autofunction:: format_uncertainty
3535

36+
.. autofunction:: to_simplex
37+
38+
.. autofunction:: from_simplex

doc/source/guide/timedep.rst

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,139 @@ with a custom :meth:`~Simulatable.update_timestep` implementation.
174174
plt.ylabel(r'$\omega$')
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

Comments
 (0)