dima / run_support.py
sparsetrace's picture
Update run_support.py
693c63e verified
Raw
History Blame Contribute Delete
6.32 kB
import numpy as np
import matplotlib.pyplot as plt
from metrics import compare_all_fast_with_curves
COLORS = {
"ALSA": "C0", # blue
"LISA": "C3", # red
}
METRICS = {
"MSE": ["mse", "MSE", "mse_mean"],
"Spectral (JS/KL)": ["spectral_kl", "spectral_js", "spectral_div", "spec_kl", "spectral"],
"ACF-MSE": ["acf_mse", "acf_l2", "acf_err", "acf"],
"MMD$^2$ (RFF)": ["mmd2_rff", "mmd_rff", "mmd2", "mmd"],
}
styles = {
"Truth": dict(color="k", lw=2.3, ls="-"),
"NLSA": dict(color="0.2", lw=2.0, ls="--"),
"ALSA": dict(color=COLORS["ALSA"], lw=2.0, ls="-.", alpha=0.95),
"LISA": dict(color=COLORS["LISA"], lw=2.1, ls=":", alpha=0.95),
}
def standardize_global(F_tX: np.ndarray, eps: float = 1e-12):
mu = F_tX.mean(axis=0, keepdims=True)
sd = F_tX.std(axis=0, keepdims=True) + eps
return (F_tX - mu) / sd, mu, sd
def plot_3d_phase_multi(
bg: np.ndarray,
truth: np.ndarray,
preds: dict[str, np.ndarray],
*,
title: str,
elev: float = 20,
azim: float = 35,
bg_stride: int = 5,
traj_stride: int = 1,
max_bg_points: int = 20000,
styles: dict[str, dict] | None = None,
bg_style: dict | None = None,
axis_names: tuple[str, str, str] = ("x", "y", "z"),
):
"""
bg: (Tbg, 3) background attractor (e.g. train)
truth: (T, 3)
preds: name -> (T, 3)
styles: dict mapping curve name -> matplotlib kwargs
e.g. styles["Truth"] = {...}, styles["NLSA"] = {...}
keys should match preds keys.
"""
bg = np.asarray(bg)
truth = np.asarray(truth)
if bg.ndim != 2 or truth.ndim != 2 or bg.shape[1] != 3 or truth.shape[1] != 3:
raise ValueError(f"bg and truth must be (T,3). Got bg={bg.shape}, truth={truth.shape}")
for k, v in preds.items():
v = np.asarray(v)
if v.ndim != 2 or v.shape[1] != 3:
raise ValueError(f"pred '{k}' must be (T,3). Got {v.shape}")
preds[k] = v
if styles is None:
styles = {}
if bg_style is None:
bg_style = dict(color="0.75", lw=0.8, alpha=0.35)
# subsample background for speed
if bg.shape[0] > max_bg_points:
idx = np.linspace(0, bg.shape[0] - 1, max_bg_points).astype(int)
bgp = bg[idx]
else:
bgp = bg
bgp = bgp[::max(1, int(bg_stride))]
t = truth[::max(1, int(traj_stride))]
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection="3d")
ax.plot(bgp[:, 0], bgp[:, 1], bgp[:, 2], label="Background (train)", **bg_style)
# Truth styling
truth_style = dict(color="k", lw=2.2)
truth_style.update(styles.get("Truth", {}))
ax.plot(t[:, 0], t[:, 1], t[:, 2], label="Truth", **truth_style)
# Predictions styling (fallback to cycle if not provided)
cycle = plt.rcParams["axes.prop_cycle"].by_key().get("color", ["C0", "C1", "C2", "C3", "C4"])
for i, (name, P) in enumerate(preds.items()):
p = P[::max(1, int(traj_stride))]
style = dict(color=cycle[i % len(cycle)], lw=1.8, alpha=0.95)
style.update(styles.get(name, {}))
ax.plot(p[:, 0], p[:, 1], p[:, 2], label=name, **style)
# mark start point
ax.scatter(t[0, 0], t[0, 1], t[0, 2], color=truth_style.get("color", "k"), s=60)
ax.set_title(title)
ax.set_xlabel(axis_names[0])
ax.set_ylabel(axis_names[1])
ax.set_zlabel(axis_names[2])
ax.view_init(elev=elev, azim=azim)
ax.legend(frameon=False, loc="upper left")
plt.tight_layout()
plt.show()
return None
def make_task(F_test: np.ndarray, a_start: int, ell_ctx: int, steps: int):
"""
Returns:
prefix: (ell_ctx, D)
truth : (steps, D)
"""
assert a_start >= ell_ctx
assert a_start + steps <= F_test.shape[0]
prefix = F_test[a_start - ell_ctx: a_start, :]
truth = F_test[a_start: a_start + steps, :]
return prefix, truth
def pick_metric_key(available_keys: list[str], candidates: list[str]) -> str:
"""Pick the first existing key from a list of candidate names."""
for c in candidates:
if c in available_keys:
return c
# fallback: substring match
for c in candidates:
for k in available_keys:
if c.lower() in k.lower():
return k
raise KeyError(f"None of candidates {candidates} found in keys: {available_keys}")
def eval_multistart(
model_name: str,
predictor_fn, # takes prefix -> (steps,D)
F_test: np.ndarray,
*,
starts: np.ndarray,
ell_ctx: int,
steps: int,
burn_in_metrics: int = 0,
mmd_sample: int = 1024,
seed: int = 0,
dt=0.01,
):
"""
Returns:
scalars_mean: dict[str,float]
scalars_std : dict[str,float]
curves_mean : dict[str,np.ndarray]
"""
scalar_keys = None
scalars_acc = {}
curves_acc = {"mse_by_horizon": [], "mse_per_feature": []}
for i, a_start in enumerate(starts):
prefix, truth = make_task(F_test, int(a_start), ell_ctx, steps)
pred = predictor_fn(prefix)
s, c = compare_all_fast_with_curves(
truth, pred,
burn_in=burn_in_metrics,
acf_max_lag=min(200, steps - 2),
mmd_sample=min(mmd_sample, steps),
seed=seed + i,
include_mmd_rff=True,
dt=dt,
)
if scalar_keys is None:
scalar_keys = list(s.keys())
for k in scalar_keys:
scalars_acc[k] = []
for k in scalar_keys:
scalars_acc[k].append(s[k])
curves_acc["mse_by_horizon"].append(c["mse_by_horizon"])
curves_acc["mse_per_feature"].append(c["mse_per_feature"])
scalars_mean = {k: float(np.nanmean(v)) for k, v in scalars_acc.items()}
scalars_std = {k: float(np.nanstd(v)) for k, v in scalars_acc.items()}
curves_mean = {
"mse_by_horizon": np.nanmean(np.stack(curves_acc["mse_by_horizon"], axis=0), axis=0),
"mse_per_feature": np.nanmean(np.stack(curves_acc["mse_per_feature"], axis=0), axis=0),
}
print(f"\n[{model_name}] ℓ={ell_ctx} mean±std over {len(starts)} starts:")
for k in scalars_mean.keys():
print(f" {k:16s}: {scalars_mean[k]:.6g} ± {scalars_std[k]:.3g}")
return scalars_mean, scalars_std, curves_mean