import matplotlib.pyplot as plt from typing import Dict, List, Tuple def plot_telemetry( metrics_log: Dict[str, List[float]], k_floor: float = 0.5, c_floor: float = 0.3, s_floor: float = 0.5, ) -> Tuple[plt.Figure, List[plt.Axes]]: """Plot K, C, S metrics over time with cluster transitions. Args: metrics_log: Dictionary with keys ``negentropy``, ``lz_complexity``, ``symbiosis_score`` and optional ``clusters`` listing cluster assignments per step. k_floor: Threshold for negentropy (K). c_floor: Threshold for LZ complexity (C). s_floor: Threshold for symbiosis score (S). Returns: (figure, axes) tuple for further customization or saving. """ steps = list(range(len(metrics_log.get("negentropy", [])))) fig, axes = plt.subplots(3, 1, sharex=True, figsize=(10, 6)) metrics = [ ("negentropy", k_floor, "K"), ("lz_complexity", c_floor, "C"), ("symbiosis_score", s_floor, "S"), ] for ax, (key, floor, label) in zip(axes, metrics): values = metrics_log.get(key, []) ax.plot(steps, values, label=label) ax.axhline(floor, color="r", linestyle="--", linewidth=1) violations = [i for i, v in enumerate(values) if v < floor] if violations: ax.scatter( [steps[i] for i in violations], [values[i] for i in violations], color="r", zorder=5, label="violation", ) ax.set_ylabel(label) ax.legend(loc="upper right") clusters = metrics_log.get("clusters") if clusters is not None: prev = clusters[0] for t, c in enumerate(clusters): if t > 0 and c != prev: for ax in axes: ax.axvline(t, color="gray", linestyle=":", alpha=0.5) prev = c axes[-1].set_xlabel("step") plt.tight_layout() return fig, axes