m_values = [1, 2, 4, 8, 16, 32, 64]
ms = np.array(m_values, dtype=float)
y_floor = 1e-16
# Orderings computed earlier in the notebook (see fig-ud-ordering-effects).
assert x.shape == (64, 1)
assert x_maxmin.shape == (64, 1)
assert isinstance(x_permuted, list) and len(x_permuted) == 10
assert all(xp.shape == (64, 1) for xp in x_permuted)
def nearest_neighbourhood_sets(x_order: np.ndarray, m: int) -> dict[int, list[int]]:
# For each i, pick the min(m, i) nearest points among {0, ..., i-1}
# under this ordering, using distance in x-space.
n = x_order.shape[0]
x_flat = x_order[:, 0] if x_order.ndim == 2 else x_order
neighbourhood_sets: dict[int, list[int]] = {0: []}
for i in range(1, n):
prev = np.arange(i)
k = min(m, i)
if k == i:
idx = prev
else:
dists = np.abs(x_flat[i] - x_flat[prev])
idx = prev[np.argsort(dists)[:k]]
neighbourhood_sets[i] = np.sort(idx).tolist()
return neighbourhood_sets
# Neighbourhood sets depend on BOTH the ordering AND m.
nh_std_by_m = {m: nearest_neighbourhood_sets(x, m) for m in m_values}
nh_maxmin_by_m = {m: nearest_neighbourhood_sets(x_maxmin, m) for m in m_values}
nh_rand_by_m_list = [{m: nearest_neighbourhood_sets(xr, m) for m in m_values} for xr in x_permuted]
def vecchia_errors_vs_m(
kernel: Matern, x_order: np.ndarray, nh_by_m: dict[int, dict[int, list[int]]]
) -> dict[str, np.ndarray]:
Sigma = kernel(x_order, x_order)
U_exact, D_exact = get_U_and_D_exact(Sigma)
err_U = []
err_D = []
err_cov = []
for m in m_values:
U_m, D_m = get_U_and_D_vecchia(Sigma, nh_by_m[m])
Sigma_m = reconstruct_Sigma_from_U_D(U_m, D_m)
err_U.append(rel_l2_error(U_exact, U_m))
err_D.append(rel_l2_error(D_exact, D_m))
err_cov.append(rel_l2_error(Sigma, Sigma_m))
return {
"U": np.maximum(np.array(err_U, dtype=float), y_floor),
"D": np.maximum(np.array(err_D, dtype=float), y_floor),
"cov": np.maximum(np.array(err_cov, dtype=float), y_floor),
}
kernel_keys = [(0.5, 4), (2.5, 4)]
kernel_names = [str(results[k]["name"]) for k in kernel_keys]
errors_by_kernel: list[dict[str, object]] = []
for k in kernel_keys:
kernel, _name = params_to_kernel[k]
std = vecchia_errors_vs_m(kernel, x, nh_std_by_m)
mm = vecchia_errors_vs_m(kernel, x_maxmin, nh_maxmin_by_m)
rand_samples_U = []
rand_samples_D = []
rand_samples_cov = []
for x_rand, nh_rand_by_m in zip(x_permuted, nh_rand_by_m_list):
e = vecchia_errors_vs_m(kernel, x_rand, nh_rand_by_m)
rand_samples_U.append(e["U"])
rand_samples_D.append(e["D"])
rand_samples_cov.append(e["cov"])
rand_samples_U = np.array(rand_samples_U, dtype=float) # (10, |m|)
rand_samples_D = np.array(rand_samples_D, dtype=float)
rand_samples_cov = np.array(rand_samples_cov, dtype=float)
q_U = np.quantile(rand_samples_U, [0.25, 0.5, 0.75], axis=0, method="linear")
q_D = np.quantile(rand_samples_D, [0.25, 0.5, 0.75], axis=0, method="linear")
q_cov = np.quantile(rand_samples_cov, [0.25, 0.5, 0.75], axis=0, method="linear")
errors_by_kernel.append(
{
"standard": std,
"maxmin": mm,
"random_q": {"U": q_U, "D": q_D, "cov": q_cov}, # rows: q1, q2, q3
}
)
fig, axes = plt.subplots(
2,
3,
constrained_layout=True,
sharex=True,
sharey="col",
)
col_keys = ["U", "D", "cov"]
col_titles = ["U", "D", "∑"]
colors = {"standard": "tab:orange", "maxmin": "tab:green", "random": "tab:blue"}
for r in range(2):
for c, (ckey, ctitle) in enumerate(zip(col_keys, col_titles)):
ax = axes[r, c]
std = errors_by_kernel[r]["standard"][ckey]
mm = errors_by_kernel[r]["maxmin"][ckey]
q = errors_by_kernel[r]["random_q"][ckey]
q1, q2, q3 = q[0], q[1], q[2]
ax.plot(ms, std, marker="o", markersize=3.5, linewidth=1.6, color=colors["standard"], label="standard")
ax.plot(ms, mm, marker="s", markersize=3.5, linewidth=1.6, color=colors["maxmin"], label="maxmin")
ax.fill_between(ms, q1, q3, color=colors["random"], alpha=0.18, linewidth=0)
ax.plot(ms, q2, marker="^", markersize=3.7, linewidth=2.0, color=colors["random"], label="random (median; IQR)")
ax.set_xscale("log")
ax.set_yscale("log")
ax.grid(alpha=0.25, which="both")
ax.set_ylim(bottom=y_floor, top=1.3)
if r == 0:
ax.set_title(ctitle)
if c == 0:
ax.set_ylabel(kernel_names[r])
for ax in axes[-1, :]:
ax.set_xticks(m_values)
ax.set_xticklabels([str(m) for m in m_values])
fig.supylabel("relative L2 error")
fig.supxlabel("m")
axes[0, 2].legend(fontsize=9, loc="best")
plt.show()
plt.close(fig)