def test_obs(x): prior = Measure() f = GP(EQ(), measure=prior) noise = 0.1 # Generate some data. w = B.rand(B.shape(x)[0]) + 1e-2 y = f(x, 0.1).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, noise) assert isinstance(obs, Obs) approx( prior.logpdf(obs), f(x[1::2], noise / 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, noise) assert isinstance(obs, SparseObs) approx( prior.logpdf(obs), f(x[1::2], noise / w[1::2]).logpdf(y[1::2]), atol=1e-6, )
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(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_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_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_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_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_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 __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 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_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_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 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_update_inputs(): prior = Measure() f = GP(EQ(), measure=prior) x = np.array([[1], [2], [3]]) y = np.array([[4], [5], [6]], dtype=float) res = B.concat(x, y, axis=1) x_ind = np.array([[6], [7]]) res_ind = np.array([[6, 0], [7, 0]]) # Check vanilla case. gpar = GPAR(x_ind=x_ind) approx(gpar._update_inputs(x, x_ind, y, f, None), (res, res_ind)) # Check imputation with prior. gpar = GPAR(impute=True, x_ind=x_ind) this_y = y.copy() this_y[1] = np.nan this_res = res.copy() this_res[1, 1] = 0 approx(gpar._update_inputs(x, x_ind, this_y, f, None), (this_res, res_ind)) # Check replacing with prior. gpar = GPAR(replace=True, x_ind=x_ind) this_y = y.copy() this_y[1] = np.nan this_res = res.copy() this_res[0, 1] = 0 this_res[1, 1] = np.nan this_res[2, 1] = 0 approx(gpar._update_inputs(x, x_ind, this_y, f, None), (this_res, res_ind)) # Check imputation and replacing with prior. gpar = GPAR(impute=True, replace=True, x_ind=x_ind) this_res = res.copy() this_res[:, 1] = 0 approx(gpar._update_inputs(x, x_ind, y, f, None), (this_res, res_ind)) # Construct observations and update result for inducing points. obs = Obs(f(np.array([1, 2, 3, 6, 7])), np.array([9, 10, 11, 12, 13])) res_ind = np.array([[6, 12], [7, 13]]) # Check imputation with posterior. gpar = GPAR(impute=True, x_ind=x_ind) this_y = y.copy() this_y[1] = np.nan this_res = res.copy() this_res[1, 1] = 10 approx(gpar._update_inputs(x, x_ind, this_y, f, obs), (this_res, res_ind)) # Check replacing with posterior. gpar = GPAR(replace=True, x_ind=x_ind) this_y = y.copy() this_y[1] = np.nan this_res = res.copy() this_res[0, 1] = 9 this_res[1, 1] = np.nan this_res[2, 1] = 11 approx(gpar._update_inputs(x, x_ind, this_y, f, obs), (this_res, res_ind)) # Check imputation and replacing with posterior. gpar = GPAR(impute=True, replace=True, x_ind=x_ind) this_res = res.copy() this_res[0, 1] = 9 this_res[1, 1] = 10 this_res[2, 1] = 11 approx(gpar._update_inputs(x, x_ind, y, f, obs), (this_res, res_ind))
import matplotlib.pyplot as plt from wbml.plot import tweak from stheno import B, Measure, GP, EQ # Define points to predict at. x = B.linspace(0, 10, 100) # Construct a prior. prior = Measure() f1 = GP(3, EQ(), measure=prior) f2 = GP(3, EQ(), measure=prior) # Compute the approximate product. f_prod = f1 * f2 # Sample two functions. s1, s2 = prior.sample(f1(x), f2(x)) # Predict. post = prior | ((f1(x), s1), (f2(x), s2)) mean, lower, upper = post(f_prod(x)).marginals() # Plot result. plt.plot(x, s1, label="Sample 1", style="train") plt.plot(x, s2, label="Sample 2", style="train", ls="--") plt.plot(x, s1 * s2, label="True product", style="test") plt.plot(x, mean, label="Approximate posterior", style="pred") plt.fill_between(x, lower, upper, style="pred") tweak()
for j in range(n): ps[i] += A[i, j] * self.ps[j] return VGP(ps) # Define points to predict at. x = B.linspace(0, 10, 100) x_obs = B.linspace(0, 10, 10) # Model parameters: m = 2 p = 4 H = B.randn(p, m) # Construct latent functions. prior = Measure() us = VGP([GP(EQ(), measure=prior) for _ in range(m)]) fs = us.lmatmul(H) # Construct noise. e = VGP([GP(0.5 * Delta(), measure=prior) for _ in range(p)]) # Construct observation model. ys = e + fs # Sample a true, underlying function and observations. samples = prior.sample(*(p(x) for p in fs.ps), *(p(x_obs) for p in ys.ps)) fs_true, ys_obs = samples[:p], samples[p:] # Compute the posterior and make predictions. post = prior | (*((p(x_obs), y_obs) for p, y_obs in zip(ys.ps, ys_obs)), )
# Setup experiment. n = 881 # Add last one for `linspace`. noise = 0.1 t = B.linspace(-44, 44, n) t_plot = B.linspace(44, 44, 500) # Setup true model and GPCM models. kernel = EQ() window = 2 scale = 1 n_u = 40 n_z = 88 # Sample data. m = Measure() gp_f = GP(kernel, measure=m) gp_y = gp_f + GP(noise * Delta(), measure=m) truth, y = map(B.flatten, m.sample(gp_f(t_plot), gp_y(t))) # Remove region [-8.8, 8.8]. inds = ~((t >= -8.8) & (t <= 8.8)) t = t[inds] y = y[inds] def comparative_kernel(vs_): return vs_.pos(1) * kernel.stretch(vs_.pos(1.0)) + vs_.pos(noise) * Delta() run(
import matplotlib.pyplot as plt from wbml.plot import tweak from stheno import Measure, GP, EQ, RQ, Linear, Delta, Exp, B B.epsilon = 1e-10 # Define points to predict at. x = B.linspace(0, 10, 200) x_obs = B.linspace(0, 7, 50) # Construct a latent function consisting of four different components. prior = Measure() f_smooth = GP(EQ(), measure=prior) f_wiggly = GP(RQ(1e-1).stretch(0.5), measure=prior) f_periodic = GP(EQ().periodic(1.0), measure=prior) f_linear = GP(Linear(), measure=prior) f = f_smooth + f_wiggly + f_periodic + 0.2 * f_linear # Let the observation noise consist of a bit of exponential noise. e_indep = GP(Delta(), measure=prior) e_exp = GP(Exp(), measure=prior) e = e_indep + 0.3 * e_exp # Sum the latent function and observation noise to get a model for the observations. y = f + 0.5 * e # Sample a true, underlying function and observations. (
import matplotlib.pyplot as plt from wbml.plot import tweak from stheno import Measure, GP, EQ, RQ, Linear, Delta, Exp, B B.epsilon = 1e-10 # Define points to predict at. x = B.linspace(0, 10, 200) x_obs = B.linspace(0, 7, 50) with Measure() as prior: # Construct a latent function consisting of four different components. f_smooth = GP(EQ()) f_wiggly = GP(RQ(1e-1).stretch(0.5)) f_periodic = GP(EQ().periodic(1.0)) f_linear = GP(Linear()) f = f_smooth + f_wiggly + f_periodic + 0.2 * f_linear # Let the observation noise consist of a bit of exponential noise. e_indep = GP(Delta()) e_exp = GP(Exp()) e = e_indep + 0.3 * e_exp # Sum the latent function and observation noise to get a model for the observations. y = f + 0.5 * e # Sample a true, underlying function and observations. ( f_true_smooth, f_true_wiggly,
import matplotlib.pyplot as plt from wbml.plot import tweak from stheno import B, Measure, GP, EQ, Delta # Define points to predict at. x = B.linspace(0, 10, 100) x_obs = B.linspace(0, 10, 20) # Constuct a prior: prior = Measure() w = lambda x: B.exp(-(x**2) / 0.5) # Window b = [(w * GP(EQ(), measure=prior)).shift(xi) for xi in x_obs] # Weighted basis funs f = sum(b) # Latent function e = GP(Delta(), measure=prior) # Noise y = f + 0.2 * e # Observation model # Sample a true, underlying function and observations. f_true, y_obs = prior.sample(f(x), y(x_obs)) # Condition on the observations to make predictions. post = prior | (y(x_obs), y_obs) # Plot result. for i, bi in enumerate(b): mean, lower, upper = post(bi(x)).marginals() kw_args = {"label": "Basis functions"} if i == 0 else {} plt.plot(x, mean, style="pred2", **kw_args) plt.plot(x, f_true, label="True", style="test") plt.scatter(x_obs, y_obs, label="Observations", style="train", s=20)
import matplotlib.pyplot as plt import wbml.out as out from wbml.plot import tweak from stheno import B, Measure, GP, Delta # Define points to predict at. x = B.linspace(0, 10, 200) x_obs = B.linspace(0, 10, 10) # Construct the model. prior = Measure() slope = GP(1, measure=prior) intercept = GP(5, measure=prior) f = slope * (lambda x: x) + intercept e = 0.2 * GP(Delta(), measure=prior) # Noise model y = f + e # Observation model # Sample a slope, intercept, underlying function, and observations. true_slope, true_intercept, f_true, y_obs = prior.sample( slope(0), intercept(0), f(x), y(x_obs)) # Condition on the observations to make predictions. post = prior | (y(x_obs), y_obs) mean, lower, upper = post(f(x)).marginals() out.kv("True slope", true_slope[0, 0]) out.kv("Predicted slope", post(slope(0)).mean[0, 0]) out.kv("True intercept", true_intercept[0, 0])