def test_logpdf(x, w): prior = Measure() f1, e1 = GP(EQ(), measure=prior), GP(2e-1 * Delta(), measure=prior) f2, e2 = GP(Linear(), measure=prior), GP(1e-1 * Delta(), measure=prior) gpar = GPAR().add_layer(lambda: (f1, e1)).add_layer(lambda: (f2, e2)) # Generate some data. y = gpar.sample(x, w, latent=True) # Compute logpdf. x1 = WeightedUnique(x, w[:, 0]) x2 = WeightedUnique(B.concat(x, y[:, 0:1], axis=1), w[:, 1]) logpdf1 = (f1 + e1)(x1).logpdf(y[:, 0]) logpdf2 = (f2 + e2)(x2).logpdf(y[:, 1]) # Test computation of GPAR. assert gpar.logpdf(x, y, w) == logpdf1 + logpdf2 assert gpar.logpdf(x, y, w, only_last_layer=True) == logpdf2 # Test resuming computation. x_partial, x_ind_partial = gpar.logpdf(x, y, w, return_inputs=True, outputs=[0]) assert gpar.logpdf(x_partial, y, w, x_ind=x_ind_partial, outputs=[1]) == logpdf2 # Test that sampling missing gives a stochastic estimate. y[1, 0] = np.nan all_different( gpar.logpdf(x, y, w, sample_missing=True), gpar.logpdf(x, y, w, sample_missing=True), )
def test_obs(x): prior = Measure() f = GP(EQ(), measure=prior) e = GP(1e-1 * Delta(), measure=prior) # Generate some data. w = B.rand(B.shape(x)[0]) + 1e-2 y = f(x).sample() # Set some observations to be missing. y_missing = y.copy() y_missing[::2] = np.nan # Check dense case. gpar = GPAR() obs = gpar._obs(x, None, y_missing, w, f, e) assert isinstance(obs, Obs) approx( prior.logpdf(obs), (f + e)(WeightedUnique(x[1::2], w[1::2])).logpdf(y[1::2]), atol=1e-6, ) # Check sparse case. gpar = GPAR(x_ind=x) obs = gpar._obs(x, x, y_missing, w, f, e) assert isinstance(obs, SparseObs) approx( prior.logpdf(obs), (f + e)(WeightedUnique(x[1::2], w[1::2])).logpdf(y[1::2]), atol=1e-6, )
def test_conditioning(x, w): prior = Measure() f1, e1 = GP(EQ(), measure=prior), GP(1e-10 * Delta(), measure=prior) f2, e2 = GP(EQ(), measure=prior), GP(2e-10 * Delta(), measure=prior) gpar = GPAR().add_layer(lambda: (f1, e1)).add_layer(lambda: (f2, e2)) # Generate some data. y = B.concat((f1 + e1)(x).sample(), (f2 + e2)(x).sample(), axis=1) # Extract posterior processes. gpar = gpar | (x, y, w) f1_post, e1_post = gpar.layers[0]() f2_post, e2_post = gpar.layers[1]() # Test independence of noises. assert f1_post.measure.kernels[f1_post, e1_post] == ZeroKernel() assert f2_post.measure.kernels[f2_post, e2_post] == ZeroKernel() # Test form of noises. assert e1.mean == e1_post.mean assert e1.kernel == e1_post.kernel assert e2.mean == e2_post.mean assert e2.kernel == e2_post.kernel # Test posteriors. approx(f1_post.mean(x), y[:, 0:1], atol=1e-3) approx(f2_post.mean(B.concat(x, y[:, 0:1], axis=1)), y[:, 1:2], atol=1e-3)
def test_combine(): x1 = B.linspace(0, 2, 10) x2 = B.linspace(2, 4, 10) m = Measure() p1 = GP(EQ(), measure=m) p2 = GP(Matern12(), measure=m) y1 = p1(x1).sample() y2 = p2(x2).sample() # Check the one-argument case. assert_equal_normals(combine(p1(x1, 1)), p1(x1, 1)) fdd_combined, y_combined = combine((p1(x1, 1), B.squeeze(y1))) assert_equal_normals(fdd_combined, p1(x1, 1)) approx(y_combined, y1) # Check the two-argument case. fdd_combined = combine(p1(x1, 1), p2(x2, 2)) assert_equal_normals( fdd_combined, Normal(B.block_diag(p1(x1, 1).var, p2(x2, 2).var)), ) fdd_combined, y_combined = combine((p1(x1, 1), B.squeeze(y1)), (p2(x2, 2), y2)) assert_equal_normals( fdd_combined, Normal(B.block_diag(p1(x1, 1).var, p2(x2, 2).var)), ) approx(y_combined, B.concat(y1, y2, axis=0))
def test_logpdf(): graph = Graph() f1, e1 = GP(EQ(), graph=graph), GP(2e-1 * Delta(), graph=graph) f2, e2 = GP(Linear(), graph=graph), GP(1e-1 * Delta(), graph=graph) gpar = GPAR().add_layer(lambda: (f1, e1)).add_layer(lambda: (f2, e2)) # Sample some data from GPAR. x = B.linspace(0, 2, 10, dtype=torch.float64)[:, None] y = gpar.sample(x, latent=True) # Compute logpdf. logpdf1 = (f1 + e1)(x).logpdf(y[:, 0]) logpdf2 = (f2 + e2)(B.concat([x, y[:, 0:1]], axis=1)).logpdf(y[:, 1]) # Test computation of GPAR. yield eq, gpar.logpdf(x, y), logpdf1 + logpdf2 yield eq, gpar.logpdf(x, y, only_last_layer=True), logpdf2 # Test resuming computation. x_int, x_ind_int = gpar.logpdf(x, y, return_inputs=True, outputs=[0]) yield eq, gpar.logpdf(x_int, y, x_ind=x_ind_int, outputs=[1]), logpdf2 # Test that sampling missing gives a stochastic estimate. y[1, 0] = np.nan yield ge, \ B.abs(gpar.logpdf(x, y, sample_missing=True) - gpar.logpdf(x, y, sample_missing=True)).numpy(), \ 1e-3
def __init__( self, measure: Measure, xs: List[GP], h: AbstractMatrix, noise_obs: B.Numeric, noises_latent: B.Numeric, ): self.measure = measure self.xs = xs self.h = h self.noise_obs = noise_obs self.noises_latent = noises_latent # Create noisy latent processes. xs_noisy = [ x + GP(self.noises_latent[i] * Delta(), measure=self.measure) for i, x in enumerate(xs) ] # Create noiseless observed processes. self.fs = _matmul(self.h, self.xs) # Create observed processes. fs_noisy = _matmul(self.h, xs_noisy) self.ys = [ f + GP(self.noise_obs * Delta(), measure=self.measure) for f in fs_noisy ]
def objective(vs, m, x_data, y_data, locs): """NLML objective. Args: vs (:class:`varz.Vars`): Variable container. m (int): Number of latent processes. x_data (tensor): Time stamps of the observations. y_data (tensor): Observations. locs (tensor): Spatial locations of observations. Returns: scalar: Negative log-marginal likelihood. """ y_proj, _, S, noises_obs = project(vs, m, y_data, locs) xs, noise_obs, noises_latent = model(vs, m) # Add contribution of latent processes. lml = 0 for i, (x, y) in enumerate(zip(xs, y_proj)): e_signal = GP((noise_obs / S[i] + noises_latent[i]) * Delta(), graph=x.graph) lml += (x + e_signal)(x_data).logpdf(y) e_noise = GP(noise_obs / S[i] * Delta(), graph=x.graph) lml -= e_noise(x_data).logpdf(y) # Add regularisation contribution. lml += B.sum(Normal(Diagonal(noises_obs)).logpdf(B.transpose(y_data))) # Return negative the evidence, normalised by the number of data points. n, p = B.shape(y_data) return -lml / (n * p)
def test_dimensionality(): m = Measure() p1 = GP(EQ(), measure=m) p2 = GP(2 * EQ().stretch(2), measure=m) k1 = MultiOutputKernel(m, p1, p2) k2 = MultiOutputKernel(m, p1, p1) assert dimensionality(EQ()) == 1 assert dimensionality(k1) == 2 # Test the unpacking of `Wrapped`s and `Join`s. assert dimensionality(k1 + k2) == 2 assert dimensionality(k1 * k2) == 2 assert dimensionality(k1.periodic(1)) == 2 assert dimensionality(k1.stretch(2)) == 2 # Check that dimensionalities must line up. with pytest.raises(RuntimeError): dimensionality(k1 + EQ()) # Check `PosteriorKernel`. assert dimensionality(PosteriorKernel(EQ(), EQ(), EQ(), None, 0)) == 1 assert dimensionality(PosteriorKernel(k1, k2, k2, None, 0)) == 2 assert dimensionality(PosteriorKernel(k1, k2, ADK(EQ()), None, 0)) == 2 with pytest.raises(RuntimeError): assert dimensionality(PosteriorKernel(k1, k2, EQ(), None, 0)) == 2 # Check `SubspaceKernel`. assert dimensionality(SubspaceKernel(EQ(), EQ(), None, 0)) == 1 assert dimensionality(SubspaceKernel(k1, k2, None, 0)) == 2 assert dimensionality(SubspaceKernel(k1, ADK(EQ()), None, 0)) == 2 with pytest.raises(RuntimeError): assert dimensionality(SubspaceKernel(k1, EQ(), None, 0)) == 2
def test_logpdf(x, w): prior = Measure() f1, noise1 = GP(EQ(), measure=prior), 2e-1 f2, noise2 = GP(Linear(), measure=prior), 1e-1 gpar = GPAR().add_layer(lambda: (f1, noise1)).add_layer(lambda: (f2, noise2)) # Generate some data. y = gpar.sample(x, w, latent=True) # Compute logpdf. x1 = x x2 = B.concat(x, y[:, 0:1], axis=1) logpdf1 = f1(x1, noise1 / w[:, 0]).logpdf(y[:, 0]) logpdf2 = f2(x2, noise2 / w[:, 1]).logpdf(y[:, 1]) # Test computation of GPAR. assert gpar.logpdf(x, y, w) == logpdf1 + logpdf2 assert gpar.logpdf(x, y, w, only_last_layer=True) == logpdf2 # Test resuming computation. x_partial, x_ind_partial = gpar.logpdf(x, y, w, return_inputs=True, outputs=[0]) assert gpar.logpdf(x_partial, y, w, x_ind=x_ind_partial, outputs=[1]) == logpdf2 # Test that sampling missing gives a stochastic estimate. y[1, 0] = np.nan all_different( gpar.logpdf(x, y, w, sample_missing=True), gpar.logpdf(x, y, w, sample_missing=True), )
def test_conditioning(): graph = Graph() f1, e1 = GP(EQ(), graph=graph), GP(1e-8 * Delta(), graph=graph) f2, e2 = GP(EQ(), graph=graph), GP(2e-8 * Delta(), graph=graph) gpar = GPAR().add_layer(lambda: (f1, e1)).add_layer(lambda: (f2, e2)) x = tensor([[1], [2], [3]]) y = tensor([[4, 5], [6, 7], [8, 9]]) gpar = gpar | (x, y) # Extract posterior processes. f1_post, e1_post = gpar.layers[0]() f2_post, e2_post = gpar.layers[1]() # Test independence of noises. assert graph.kernels[f1_post, e1_post] == ZeroKernel() assert graph.kernels[f2_post, e2_post] == ZeroKernel() # Test form of noises. assert e1.mean == e1_post.mean assert e1.kernel == e1_post.kernel assert e2.mean == e2_post.mean assert e2.kernel == e2_post.kernel # Test posteriors. approx(f1_post.mean(x), y[:, 0:1]) approx(f2_post.mean(B.concat(x, y[:, 0:1], axis=1)), y[:, 1:2])
def test_conditioning(): graph = Graph() f1, e1 = GP(EQ(), graph=graph), GP(1e-8 * Delta(), graph=graph) f2, e2 = GP(EQ(), graph=graph), GP(2e-8 * Delta(), graph=graph) gpar = GPAR().add_layer(lambda: (f1, e1)).add_layer(lambda: (f2, e2)) x = array([[1], [2], [3]]) y = array([[4, 5], [6, 7], [8, 9]]) gpar = gpar | (x, y) # Extract posterior processes. f1_post, e1_post = gpar.layers[0]() f2_post, e2_post = gpar.layers[1]() # Test independence of noises. yield eq, graph.kernels[f1_post, e1_post], ZeroKernel() yield eq, graph.kernels[f2_post, e2_post], ZeroKernel() # Test form of noises. yield eq, e1.mean, e1_post.mean yield eq, e1.kernel, e1_post.kernel yield eq, e2.mean, e2_post.mean yield eq, e2.kernel, e2_post.kernel # Test posteriors. yield approx, f1_post.mean(x), y[:, 0:1] yield approx, f2_post.mean(B.concat([x, y[:, 0:1]], axis=1)), y[:, 1:2]
def test_infer_size(): x = B.linspace(0, 2, 5) m = Measure() p1 = GP(EQ(), measure=m) p2 = GP(2 * EQ().stretch(2), measure=m) k = MultiOutputKernel(m, p1, p2) assert infer_size(k, x) == 10 assert infer_size(k, p1(x)) == 5 assert infer_size(k, (x, p1(x))) == 15
def sample(model, t, noise_f): """Sample from a model. Args: model (:class:`gpcm.model.AbstractGPCM`): Model to sample from. t (vector): Time points to sample at. noise_f (vector): Noise for the sample of the function. Should have the same size as `t`. Returns: tuple[vector, ...]: Tuple containing kernel samples, filter samples, and function samples. """ ks, us, fs = [], [], [] # In the below, we look at the third inducing point, because that is the one # determining the value of the filter at zero: the CGPCM adds two extra inducing # points to the left. # Get a smooth sample. u1 = B.ones(model.n_u) while B.abs(u1[2]) > 1e-2: u1 = B.sample(model.compute_K_u())[:, 0] u = GP(model.k_h()) u = u | (u(model.t_u), u1) u1_full = u(t).mean.flatten() # Get a rough sample. u2 = B.zeros(model.n_u) while u2[2] < 0.5: u2 = B.sample(model.compute_K_u())[:, 0] u = GP(model.k_h()) u = u | (u(model.t_u), u2) u2_full = u(t).mean.flatten() with wbml.out.Progress(name="Sampling", total=5) as progress: for c in [0, 0.1, 0.23, 0.33, 0.5]: # Sample kernel. K = model.kernel_approx(t, t, c * u2 + (1 - c) * u1) wbml.out.kv("Sampled variance", K[0, 0]) K = K / K[0, 0] ks.append(K[0, :]) # Store filter. us.append(c * u2_full + (1 - c) * u1_full) # Sample function. f = B.matmul(B.chol(closest_psd(K)), noise_f) fs.append(f) progress() return ks, us, fs
def test_sample(): graph = Graph() x = array([1, 2, 3])[:, None] # Test that it produces random samples. Not sure how to test for # correctness. f1, e1 = GP(EQ(), graph=graph), GP(1e-1 * Delta(), graph=graph) f2, e2 = GP(EQ(), graph=graph), GP(1e-1 * Delta(), graph=graph) gpar = GPAR().add_layer(lambda: (f1, e1)).add_layer(lambda: (f2, e2)) yield ge, B.sum(B.abs(gpar.sample(x) - gpar.sample(x))), 1e-3 yield ge, \ B.sum(B.abs(gpar.sample(x, latent=True) - gpar.sample(x, latent=True))), \ 1e-3 # Test that posterior latent samples are around the data that is # conditioned on. graph = Graph() f1, e1 = GP(EQ(), graph=graph), GP(1e-8 * Delta(), graph=graph) f2, e2 = GP(EQ(), graph=graph), GP(1e-8 * Delta(), graph=graph) gpar = GPAR().add_layer(lambda: (f1, e1)).add_layer(lambda: (f2, e2)) y = gpar.sample(x, latent=True) gpar = gpar | (x, y) yield approx, gpar.sample(x), y, 3 yield approx, gpar.sample(x, latent=True), y, 3
def sample_h(state): state, u = self.approximation.p_u.sample(state) u = B.mm(self.K_u, u) # Transform :math:`\hat u` into :math:`u`. h = GP(self.k_h()) h = h | (h(self.t_u), u) # Condition on sample. state, h = h(t_h).sample(state) # Sample at desired points. return state, B.flatten(h)
def __or__(self, x_y_w): """Condition on data. Args: x (tensor): Inputs. y (tensor): Outputs. w (tensor): Weights. Returns: :class:`.gpar.GPAR`: Updated GPAR model. """ x, y, w = x_y_w gpar, x_ind = self.copy(), self.x_ind for is_last, ((y, w, mask), model) in last( zip(per_output(y, w, keep=self.impute), self.layers)): x = x[mask] # Filter according to mask. f, e = model() # Construct model. obs = self._obs(x, x_ind, y, w, f, e) # Construct observations. # Update with posterior. post = f.measure | obs e_new = GP(e.mean, e.kernel, measure=post) gpar.layers.append(construct_model(post(f), e_new)) # Update inputs. if not is_last: x, x_ind = self._update_inputs(x, x_ind, y, f, obs) return gpar
def __or__(self, x_and_y): """Condition on data. Args: x (tensor): Inputs. y (tensor): Outputs. Returns: :class:`.gpar.GPAR`: Updated GPAR model. """ x, y = x_and_y gpar, x_ind = self.copy(), self.x_ind for is_last, ((y, mask), model) in \ last(zip(per_output(y, self.impute), self.layers)): x = x[mask] # Filter according to mask. f, e = model() # Construct model. obs = self._obs(x, x_ind, y, f, e) # Construct observations. # Update with posterior. f_post = f | obs e_new = GP(e.kernel, e.mean, graph=f.graph) gpar.layers.append(construct_model(f_post, e_new)) # Update inputs. if not is_last: x, x_ind = self._update_inputs(x, x_ind, y, f, obs) return gpar
def test_infer_size(): x = B.linspace(0, 2, 5) m = Measure() p1 = GP(EQ(), measure=m) p2 = GP(2 * EQ().stretch(2), measure=m) k = MultiOutputKernel(m, p1, p2) assert infer_size(k, x) == 10 assert infer_size(k, p1(x)) == 5 assert infer_size(k, (x, p1(x))) == 15 # Check that the dimensionality must be inferrable. assert infer_size(EQ(), x) == 5 with pytest.raises(RuntimeError): infer_size(ADK(EQ()), x)
def test_logpdf(x, w): # Sample some data from a "sensitive" GPAR. reg = GPARRegressor( replace=False, impute=False, nonlinear=True, nonlinear_scale=0.1, linear=True, linear_scale=10.0, noise=1e-2, normalise_y=False, ) y = reg.sample(x, w, p=2, latent=True) # Extract models. gpar = _construct_gpar(reg, reg.vs, B.shape(B.uprank(x))[1], 2) f1, e1 = gpar.layers[0]() f2, e2 = gpar.layers[1]() # Test computation under prior. x1 = x x2 = B.concat(B.uprank(x), y[:, 0:1], axis=1) if w is not None: x1 = WeightedUnique(x1, w[:, 0]) x2 = WeightedUnique(x2, w[:, 1]) logpdf1 = (f1 + e1)(x1).logpdf(y[:, 0]) logpdf2 = (f2 + e2)(x2).logpdf(y[:, 1]) approx(reg.logpdf(x, y, w), logpdf1 + logpdf2, atol=1e-6) # Test computation under posterior. post1 = f1.measure | ((f1 + e1)(x1), y[:, 0]) post2 = f2.measure | ((f2 + e2)(x2), y[:, 1]) e1_post = GP(e1.mean, e1.kernel, measure=post1) e2_post = GP(e2.mean, e2.kernel, measure=post2) logpdf1 = (post1(f1) + e1_post)(x1).logpdf(y[:, 0]) logpdf2 = (post2(f2) + e2_post)(x2).logpdf(y[:, 1]) with pytest.raises(RuntimeError): reg.logpdf(x, y, w, posterior=True) reg.condition(x, y, w) approx(reg.logpdf(x, y, w, posterior=True), logpdf1 + logpdf2, atol=1e-6) # Test that sampling missing gives a stochastic estimate. y[::2, 0] = np.nan all_different( reg.logpdf(x, y, w, sample_missing=True), reg.logpdf(x, y, w, sample_missing=True), )
def test_sample(x, w): prior = Measure() # Test that it produces random samples. f1, noise1 = GP(EQ(), measure=prior), 1e-1 f2, noise2 = GP(EQ(), measure=prior), 2e-1 gpar = GPAR().add_layer(lambda: (f1, noise1)).add_layer(lambda: (f2, noise2)) all_different(gpar.sample(x, w), gpar.sample(x, w)) all_different(gpar.sample(x, w, latent=True), gpar.sample(x, w, latent=True)) # Test that posterior latent samples are around the data that is conditioned on. prior = Measure() f1, noise1 = GP(EQ(), measure=prior), 1e-10 f2, noise2 = GP(EQ(), measure=prior), 2e-10 gpar = GPAR().add_layer(lambda: (f1, noise1)).add_layer(lambda: (f2, noise2)) y = gpar.sample(x, w, latent=True) gpar = gpar | (x, y, w) approx(gpar.sample(x, w), y, atol=1e-3) approx(gpar.sample(x, w, latent=True), y, atol=1e-3)
def test_dimensionality(): m = Measure() p1 = GP(EQ(), measure=m) p2 = GP(2 * EQ().stretch(2), measure=m) k1 = MultiOutputKernel(m, p1, p2) k2 = MultiOutputKernel(m, p1, p1) assert dimensionality(EQ()) == 1 assert dimensionality(k1) == 2 # Test the unpacking of `Wrapped`s and `Join`s. assert dimensionality(k1 + k2) == 2 assert dimensionality(k1 * k2) == 2 assert dimensionality(k1.periodic(1)) == 2 assert dimensionality(k1.stretch(2)) == 2 # Test consistency check. with pytest.raises(RuntimeError): dimensionality(k1 + EQ())
def test_logpdf(): # Sample some data from a "sensitive" GPAR. reg = GPARRegressor(replace=False, impute=False, nonlinear=True, nonlinear_scale=0.1, linear=True, linear_scale=10., noise=1e-4, normalise_y=False) x = np.linspace(0, 5, 10) y = reg.sample(x, p=2, latent=True) # Extract models. gpar = _construct_gpar(reg, reg.vs, 1, 2) f1, e1 = gpar.layers[0]() f2, e2 = gpar.layers[1]() # Test computation under prior. logpdf1 = (f1 + e1)(B.array(x)).logpdf(B.array(y[:, 0])) x_stack = np.concatenate([x[:, None], y[:, 0:1]], axis=1) logpdf2 = (f2 + e2)(B.array(x_stack)).logpdf(B.array(y[:, 1])) yield approx, reg.logpdf(x, y), logpdf1 + logpdf2, 6 # Test computation under posterior. e1_post = GP(e1.kernel, e1.mean, graph=e1.graph) e2_post = GP(e2.kernel, e2.mean, graph=e2.graph) f1_post = f1 | ((f1 + e1)(B.array(x)), B.array(y[:, 0])) f2_post = f2 | ((f2 + e2)(B.array(x_stack)), B.array(y[:, 1])) logpdf1 = (f1_post + e1_post)(B.array(x)).logpdf(B.array(y[:, 0])) logpdf2 = (f2_post + e2_post)(B.array(x_stack)).logpdf(B.array(y[:, 1])) yield raises, RuntimeError, lambda: reg.logpdf(x, y, posterior=True) reg.fit(x, y, iters=0) yield approx, reg.logpdf(x, y, posterior=True), logpdf1 + logpdf2, 6 # Test that sampling missing gives a stochastic estimate. y[::2, 0] = np.nan yield ge, \ np.abs(reg.logpdf(x, y, sample_missing=True) - reg.logpdf(x, y, sample_missing=True)), \ 1e-3
def test_conditioning(x, w): prior = Measure() f1, noise1 = GP(EQ(), measure=prior), 1e-10 f2, noise2 = GP(EQ(), measure=prior), 2e-10 gpar = GPAR().add_layer(lambda: (f1, noise1)).add_layer(lambda: (f2, noise2)) # Generate some data. y = B.concat(f1(x, noise1).sample(), f2(x, noise2).sample(), axis=1) # Extract posterior processes. gpar = gpar | (x, y, w) f1_post, noise1_post = gpar.layers[0]() f2_post, noise2_post = gpar.layers[1]() # Test noises. assert noise1 == noise1_post assert noise2 == noise2_post # Test posteriors. approx(f1_post.mean(x), y[:, 0:1], atol=1e-3) approx(f2_post.mean(B.concat(x, y[:, 0:1], axis=1)), y[:, 1:2], atol=1e-3)
def make_latent_process(i): # Long-term trend: variance = vs.bnd(0.9, name=f'{i}/long_term/var') scale = vs.bnd(2 * 30, name=f'{i}/long_term/scale') kernel = variance * EQ().stretch(scale) # Short-term trend: variance = vs.bnd(0.1, name=f'{i}/short_term/var') scale = vs.bnd(20, name=f'{i}/short_term/scale') kernel += variance * Matern12().stretch(scale) return GP(kernel, graph=g)
def __init__( self, kernels: List[Kernel], h: AbstractMatrix, noise_obs: B.Numeric, noises_latent: B.Numeric, ): measure = Measure() # Create latent processes. xs = [GP(k, measure=measure) for k in kernels] ILMMPP.__init__(self, measure, xs, h, noise_obs, noises_latent)
def predict(vs, m, x_data, y_data, locs, x_pred): """Make predictions. Args: vs (:class:`varz.Vars`): Variable container. m (int): Number of latent processes. x_data (tensor): Time stamps of the observations. y_data (tensor): Observations. locs (tensor): Spatial locations of observations. x_pred (tensor): Time stamps to predict at. Returns: tuple: Tuple containing the predictions for the latent processes and predictions for the observations. """ # Construct model and project data for prediction. xs, noise_obs, noises_latent = model(vs, m) y_proj, H, S, noises_obs = project(vs, m, y_data, locs) L = noise_obs / S + noises_latent # Condition latent processes. xs_posterior = [] for x, noise, y in zip(xs, L, y_proj): e = GP(noise * Delta(), graph=x.graph) xs_posterior.append(x | ((x + e)(x_data), y)) xs = xs_posterior # Extract posterior means and variances of the latent processes. x_means, x_vars = zip(*[(x.mean(x_pred)[:, 0], x.kernel.elwise(x_pred)[:, 0]) for x in xs]) # Construct predictions for latent processes. lat_preds = [ B.to_numpy(mean, mean - 2 * (var + L[i])**.5, mean + 2 * (var + L[i])**.5) for i, (mean, var) in enumerate(zip(x_means, x_vars)) ] # Pull means through mixing matrix. x_means = B.stack(*x_means, axis=0) y_means = B.matmul(H, x_means) # Pull variances through mixing matrix and add noise. x_vars = B.stack(*x_vars, axis=0) y_vars = B.matmul(H**2, x_vars + noises_latent[:, None]) + noise_obs # Construct predictions for observations. obs_preds = [(mean, mean - 2 * var**.5, mean + 2 * var**.5) for mean, var in zip(y_means, y_vars)] return lat_preds, obs_preds
def test_fdd_take(): with Measure(): f1 = GP(1, EQ()) f2 = GP(2, Exp()) f = cross(f1, f2) x = B.linspace(0, 3, 5) # Build an FDD with a very complicated input specification. fdd = f((x, (f2(x), x), f1(x), (f2(x), (f1(x), x)))) n = infer_size(fdd.p.kernel, fdd.x) fdd = f(fdd.x, matrix.Diagonal(B.rand(n))) # Flip a coin for every element. mask = B.randn(n) > 0 taken_fdd = B.take(fdd, mask) approx(taken_fdd.mean, B.take(fdd.mean, mask)) approx(taken_fdd.var, B.submatrix(fdd.var, mask)) approx(taken_fdd.noise, B.submatrix(fdd.noise, mask)) assert isinstance(taken_fdd.noise, matrix.Diagonal) # Test that only masks are supported, for now. with pytest.raises(AssertionError): B.take(fdd, np.array([1, 2]))
def test_obs(): graph = Graph() f = GP(EQ(), graph=graph) e = GP(1e-8 * Delta(), graph=graph) # Check that it produces the correct observations. x = B.linspace(0, 0.1, 10, dtype=torch.float64) y = f(x).sample() # Set some observations to be missing. y_missing = y.clone() y_missing[::2] = np.nan # Check dense case. gpar = GPAR() obs = gpar._obs(x, None, y_missing, f, e) yield eq, type(obs), Obs yield approx, y, (f | obs).mean(x) # Check sparse case. gpar = GPAR(x_ind=x) obs = gpar._obs(x, x, y_missing, f, e) yield eq, type(obs), SparseObs yield approx, y, (f | obs).mean(x)
def test_fdd(): p = GP(1, EQ()) # Test specification without noise. for fdd in [p(1), FDD(p, 1)]: assert isinstance(fdd, FDD) assert identical(fdd.x, 1) assert fdd.p is p assert isinstance(fdd.noise, matrix.Zero) rep = ("<FDD:\n" " process=GP(1, EQ()),\n" " input=1,\n" " noise=<zero matrix: batch=(), shape=(1, 1), dtype=int>>") assert str(fdd) == rep assert repr(fdd) == rep # Check `dtype` and `num_elements`. assert B.dtype(fdd) == int assert num_elements(fdd) == 1 # Test specification with noise. fdd = p(1.0, np.array([1, 2])) assert isinstance(fdd, FDD) assert identical(fdd.x, 1.0) assert fdd.p is p assert isinstance(fdd.noise, matrix.Diagonal) assert str(fdd) == ( "<FDD:\n" " process=GP(1, EQ()),\n" " input=1.0,\n" " noise=<diagonal matrix: batch=(), shape=(2, 2), dtype=int64>>") assert repr(fdd) == ( "<FDD:\n" " process=GP(1, EQ()),\n" " input=1.0,\n" " noise=<diagonal matrix: batch=(), shape=(2, 2), dtype=int64\n" " diag=[1 2]>>") assert B.dtype(fdd) == float assert num_elements(fdd) == 1 # Test construction with `id`. fdd = FDD(5, 1) assert fdd.p is 5 assert identical(fdd.x, 1) assert fdd.noise is None
def test_fdd_properties(): p = GP(1, EQ()) # Sample observations. x = B.linspace(0, 5, 5) y = p(x, 0.1).sample() # Compute posterior. p = p | (p(x, 0.1), y) fdd = p(B.linspace(0, 5, 10), 0.2) mean, var = fdd.mean, fdd.var # Check `var_diag`. fdd = p(B.linspace(0, 5, 10), 0.2) approx(fdd.var_diag, B.diag(var)) # Check `mean_var`. fdd = p(B.linspace(0, 5, 10), 0.2) approx(fdd.mean_var, (mean, var)) # Check `marginals()`. fdd = p(B.linspace(0, 5, 10), 0.2) approx(fdd.marginals(), (B.flatten(mean), B.diag(var)))