From 5d5afff674c898f345fc57119cbc2f99fa7fb0bd Mon Sep 17 00:00:00 2001 From: YurenHao0426 Date: Sat, 29 Aug 2026 17:42:16 -0500 Subject: feat: add stable-target and physical-exposure metrics --- experiments/coupled_ladder_scaling.py | 34 +++++++++++++++++++++----- sdil/coupled_ladder.py | 45 ++++++++++++++++++++++++----------- 2 files changed, 59 insertions(+), 20 deletions(-) diff --git a/experiments/coupled_ladder_scaling.py b/experiments/coupled_ladder_scaling.py index ce3509a..7c50e2d 100644 --- a/experiments/coupled_ladder_scaling.py +++ b/experiments/coupled_ladder_scaling.py @@ -160,10 +160,16 @@ def run_job(job: dict) -> dict: "failure_message": str(error), "classification_error": 1.0, "hinge_loss_v2": None, - "reached_zero_error": False, - "restricted_epochs_to_zero_error": job["epochs"], - "restricted_updates_to_zero_error": ( + "reached_stable_zero_error": False, + "restricted_epochs_to_stable_zero_error": job["epochs"], + "restricted_updates_to_stable_zero_error": ( job["epochs"] * len(dataset.labels_v)), + "restricted_edge_updates_to_stable_zero_error": ( + job["epochs"] * len(dataset.labels_v) + * circuit.edge_count), + "restricted_learning_time_to_stable_zero_seconds": ( + job["epochs"] * len(dataset.labels_v) + * job["learning_time_seconds"]), "classification_error_auc": 1.0, "local_updates": None, "neutral_observations": None, @@ -201,10 +207,27 @@ def summarize(records: list[dict], methods: tuple[str, ...]) -> dict: "mean_classification_error_auc": float(np.mean([ value["classification_error_auc"] for value in values ])), - "mean_restricted_epochs_to_zero_error": float(np.mean([ - value["restricted_epochs_to_zero_error"] + "stable_zero_error_fraction": float(np.mean([ + value["reached_stable_zero_error"] for value in values + ])), + "mean_restricted_epochs_to_stable_zero_error": float(np.mean([ + value["restricted_epochs_to_stable_zero_error"] for value in values ])), + "mean_restricted_edge_updates_to_stable_zero_error": ( + float(np.mean([ + value[ + "restricted_edge_updates_to_stable_zero_error" + ] for value in values + ])) + ), + "mean_restricted_learning_time_to_stable_zero_seconds": ( + float(np.mean([ + value[ + "restricted_learning_time_to_stable_zero_seconds" + ] for value in values + ])) + ), "median_wall_seconds": float(np.median([ value["wall_seconds"] for value in values ])), @@ -338,4 +361,3 @@ def main() -> None: if __name__ == "__main__": main() - diff --git a/sdil/coupled_ladder.py b/sdil/coupled_ladder.py index a9b8014..b7c67fb 100644 --- a/sdil/coupled_ladder.py +++ b/sdil/coupled_ladder.py @@ -224,6 +224,7 @@ def train_digital_grid( trace = [{ "epoch": 0, "local_updates": 0, + "cumulative_learning_time_seconds": 0.0, **evaluate_digital_grid(circuit, gates, dataset), }] @@ -296,21 +297,31 @@ def train_digital_grid( trace.append({ "epoch": epoch, "local_updates": local_updates, + "cumulative_learning_time_seconds": float( + cumulative_learning_time), **evaluate_digital_grid(circuit, gates, dataset), }) - zero_records = [ - record for record in trace + stable_zero_index = next(( + index for index, record in enumerate(trace) if record["classification_error"] == 0.0 - ] - if zero_records: - epochs_to_zero = int(zero_records[0]["epoch"]) - updates_to_zero = int(zero_records[0]["local_updates"]) - reached_zero = True + and all( + later["classification_error"] == 0.0 + for later in trace[index:] + ) + ), None) + if stable_zero_index is not None: + stable_record = trace[stable_zero_index] + epochs_to_stable_zero = int(stable_record["epoch"]) + updates_to_stable_zero = int(stable_record["local_updates"]) + exposure_to_stable_zero = float( + stable_record["cumulative_learning_time_seconds"]) + reached_stable_zero = True else: - epochs_to_zero = config.epochs - updates_to_zero = local_updates - reached_zero = False + epochs_to_stable_zero = config.epochs + updates_to_stable_zero = local_updates + exposure_to_stable_zero = float(cumulative_learning_time) + reached_stable_zero = False epoch_axis = np.asarray([record["epoch"] for record in trace]) error_axis = np.asarray([ record["classification_error"] for record in trace]) @@ -322,15 +333,21 @@ def train_digital_grid( "hinge_loss_v2": final["hinge_loss_v2"], "margin_success_fraction": final["margin_success_fraction"], "outputs_v": final["outputs_v"], - "reached_zero_error": reached_zero, - "restricted_epochs_to_zero_error": epochs_to_zero, - "restricted_updates_to_zero_error": updates_to_zero, + "reached_stable_zero_error": reached_stable_zero, + "restricted_epochs_to_stable_zero_error": epochs_to_stable_zero, + "restricted_updates_to_stable_zero_error": updates_to_stable_zero, + "restricted_edge_updates_to_stable_zero_error": int( + updates_to_stable_zero * circuit.edge_count), + "restricted_learning_time_to_stable_zero_seconds": ( + exposure_to_stable_zero), "classification_error_auc": error_auc, "local_updates": local_updates, + "local_edge_updates": int(local_updates * circuit.edge_count), "neutral_observations": neutral_observations, + "neutral_scalar_observations": int( + neutral_observations * circuit.edge_count), "cumulative_learning_time_seconds": float(cumulative_learning_time), "clipped_updates": clipped_updates, "final_gates_v": gates.tolist(), "trace": trace, } - -- cgit v1.2.3