graph-energy-matching / src /gem /ot_plotting.py
multimodalart's picture
multimodalart HF Staff
Initial GEM demo
6fe4366 verified
Raw History Blame Contribute Delete
5.09 kB
"""
Plotting helpers for OT energy evaluation and FM diagnostics.
All functions are side-effect free besides writing image files. They raise on
errors, so callers can handle exceptions (keeping compatibility with existing
try/except blocks in scripts).
"""
from __future__ import annotations
from typing import Iterable, Sequence
def _to_numpy1d(x: Iterable) -> "object":
"""Convert a 1D-like iterable (list/torch/tensor/numpy) to a numpy array."""
try:
import numpy as np
except Exception as e: # pragma: no cover
raise RuntimeError("numpy is required for plotting helpers") from e
# Torch tensor support without importing torch at module level
if hasattr(x, "detach") and hasattr(x, "cpu") and hasattr(x, "numpy"):
try:
return x.detach().cpu().numpy()
except Exception:
pass
# Numpy array or array-like
try:
return np.asarray(list(x), dtype=float)
except Exception:
# Best-effort fallback
return np.array(list(x), dtype=float)
def plot_fm_loss(
iterations: Sequence[int],
loss_mean: Sequence[float],
loss_std: Sequence[float],
*,
out_path: str,
show_std: bool = False,
sigma_k: float = 3.0,
dpi: int = 150,
title: str = "FM training loss vs. iteration",
):
import matplotlib.pyplot as plt
its = list(iterations)
lmean = _to_numpy1d(loss_mean)
lstd = _to_numpy1d(loss_std)
fig, ax = plt.subplots(figsize=(7.5, 4.5))
ax.set_title(title)
ax.set_xlabel("iteration")
ax.set_ylabel("loss (mean per selected pair)")
ax.plot(its, lmean, color="C0", label="loss")
if show_std:
ax.fill_between(its, lmean - sigma_k * lstd, lmean + sigma_k * lstd, color="C0", alpha=0.2, label=f"±{sigma_k}σ")
ax.legend(loc="best")
fig.tight_layout()
fig.savefig(out_path, dpi=int(dpi))
plt.close(fig)
def plot_fm_ediff(
iterations: Sequence[int],
ediff_mean: Sequence[float],
ediff_std: Sequence[float],
*,
out_path: str,
sigma_k: float = 3.0,
dpi: int = 150,
title: str = "E_data - E_noise vs. iteration",
):
import matplotlib.pyplot as plt
its = list(iterations)
edmean = _to_numpy1d(ediff_mean)
edstd = _to_numpy1d(ediff_std)
fig, ax = plt.subplots(figsize=(7.5, 4.5))
ax.set_title(title)
ax.set_xlabel("iteration")
ax.set_ylabel("energy difference (mean per selected pair)")
ax.plot(its, edmean, color="C1", label="ΔE mean")
ax.fill_between(its, edmean - sigma_k * edstd, edmean + sigma_k * edstd, color="C1", alpha=0.2, label=f"±{sigma_k}σ")
ax.axhline(0.0, color="k", linewidth=0.8, linestyle=":")
ax.legend(loc="best")
fig.tight_layout()
fig.savefig(out_path, dpi=int(dpi))
plt.close(fig)
def plot_interpolation_energy(
taus: Iterable[float],
energy_means: Sequence[float],
energy_stds: Sequence[float],
*,
mu_noise: float,
sd_noise: float,
mu_data: float,
sd_data: float,
out_path: str,
sigma_k: float = 3.0,
dpi: int = 150,
title: str = "GEM energy vs. interpolation time (t=0 in model)",
):
import matplotlib.pyplot as plt
t_np = _to_numpy1d(taus)
m_np = _to_numpy1d(energy_means)
s_np = _to_numpy1d(energy_stds)
fig, ax = plt.subplots(figsize=(7.5, 4.5))
ax.set_title(title)
ax.set_xlabel("interpolation time (0=noise, 1=data)")
ax.set_ylabel("energy")
ax.plot(t_np, m_np, label="interp mean", color="C0")
ax.fill_between(t_np, m_np - sigma_k * s_np, m_np + sigma_k * s_np, color="C0", alpha=0.2, label=f"interp ±{sigma_k}σ")
# Noise/data reference bands
ax.axhline(mu_noise, color="C1", linestyle="--", label="noise mean")
ax.fill_between([0, 1], mu_noise - sigma_k * sd_noise, mu_noise + sigma_k * sd_noise, color="C1", alpha=0.15, label=f"noise ±{sigma_k}σ")
ax.axhline(mu_data, color="C2", linestyle="--", label="data mean")
ax.fill_between([0, 1], mu_data - sigma_k * sd_data, mu_data + sigma_k * sd_data, color="C2", alpha=0.15, label=f"data ±{sigma_k}σ")
ax.legend(loc="best")
fig.tight_layout()
fig.savefig(out_path, dpi=int(dpi))
plt.close(fig)
def plot_interpolation_grad(
taus: Iterable[float],
grad_means: Sequence[float],
grad_stds: Sequence[float],
*,
out_path: str,
sigma_k: float = 3.0,
dpi: int = 150,
title: str = "||-∇_x E|| vs. interpolation time (t=0 in model)",
):
import matplotlib.pyplot as plt
t_np = _to_numpy1d(taus)
gm_np = _to_numpy1d(grad_means)
gs_np = _to_numpy1d(grad_stds)
fig, ax = plt.subplots(figsize=(7.5, 4.5))
ax.set_title(title)
ax.set_xlabel("interpolation time (0=noise, 1=data)")
ax.set_ylabel("gradient strength (mean per node/edge)")
ax.plot(t_np, gm_np, label="||-∇E|| mean", color="C3")
ax.fill_between(t_np, gm_np - sigma_k * gs_np, gm_np + sigma_k * gs_np, color="C3", alpha=0.2, label=f"±{sigma_k}σ")
ax.legend(loc="best")
fig.tight_layout()
fig.savefig(out_path, dpi=int(dpi))
plt.close(fig)