summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYurenHao0426 <Blackhao0426@gmail.com>2026-08-29 18:43:05 -0500
committerYurenHao0426 <Blackhao0426@gmail.com>2026-08-29 18:43:05 -0500
commite9f1342fc8e233a4841b7eb3c1363324e90ecda9 (patch)
tree037eff7999f7abf4cd06c4f481c008529a0e45fe
parent28a8e0b249ed1847b38e116e34a1fa0efb467705 (diff)
figure: show SDIL transfer across digital learners
-rw-r--r--experiments/plot_digital_additive_transfer.py325
-rw-r--r--results/digital_additive_transfer.csv19
-rw-r--r--results/digital_additive_transfer.json194
-rw-r--r--results/figs/figure_digital_additive_transfer.pdfbin0 -> 17919 bytes
-rw-r--r--results/figs/figure_digital_additive_transfer.pngbin0 -> 157768 bytes
-rw-r--r--results/figs/figure_digital_additive_transfer.svg964
-rw-r--r--visual-composer/digital-additive-transfer.md33
-rw-r--r--visual-composer/qa-ledger.md14
8 files changed, 1549 insertions, 0 deletions
diff --git a/experiments/plot_digital_additive_transfer.py b/experiments/plot_digital_additive_transfer.py
new file mode 100644
index 0000000..5e12a11
--- /dev/null
+++ b/experiments/plot_digital_additive_transfer.py
@@ -0,0 +1,325 @@
+#!/usr/bin/env python3
+"""Plot SDIL as an additive correction across four digital learners."""
+
+from __future__ import annotations
+
+import argparse
+import csv
+import json
+from pathlib import Path
+
+import matplotlib as mpl
+import matplotlib.pyplot as plt
+import numpy as np
+
+
+COLORS = {
+ "clean": "#222222",
+ "noise": "#CC79A7",
+ "raw": "#D55E00",
+ "constant": "#777777",
+ "sdil": "#0072B2",
+ "oracle": "#009E73",
+}
+
+
+def parse_args() -> argparse.Namespace:
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "--dualprop", type=Path,
+ default=Path("results/contrastive_bias/c1_gate.json"))
+ parser.add_argument(
+ "--ep", type=Path,
+ default=Path("results/ep_bias/c1_gate.json"))
+ parser.add_argument(
+ "--clln", type=Path,
+ default=Path("results/coupled_ladder/p2_confirm_side4.json"))
+ parser.add_argument(
+ "--overclamp", type=Path,
+ default=Path("results/coupled_ladder/p3_overclamp_side4.json"))
+ parser.add_argument(
+ "--output-analysis", type=Path,
+ default=Path("results/digital_additive_transfer.json"))
+ parser.add_argument(
+ "--output-csv", type=Path,
+ default=Path("results/digital_additive_transfer.csv"))
+ parser.add_argument(
+ "--output-figure", type=Path,
+ default=Path("results/figs/figure_digital_additive_transfer"))
+ parser.add_argument("--bootstrap-replicates", type=int, default=20000)
+ parser.add_argument("--bootstrap-seed", type=int, default=20260829)
+ return parser.parse_args()
+
+
+def summarize_values(
+ values: np.ndarray,
+ rng: np.random.Generator,
+ replicates: int,
+) -> dict:
+ values = np.asarray(values, dtype=float)
+ samples = rng.integers(0, len(values), size=(replicates, len(values)))
+ means = np.mean(values[samples], axis=1)
+ return {
+ "mean_accuracy_percent": float(np.mean(values)),
+ "bootstrap_95ci_percent": [
+ float(value) for value in np.percentile(means, (2.5, 97.5))
+ ],
+ "independent_units": len(values),
+ }
+
+
+def task_cluster_accuracies(report: dict, method: str) -> np.ndarray:
+ tasks = sorted({record["task_index"] for record in report["records"]})
+ return np.asarray([
+ 100.0 * np.mean([
+ 1.0 - record["methods"][method]["classification_error"]
+ for record in report["records"]
+ if record["task_index"] == task
+ ])
+ for task in tasks
+ ])
+
+
+def gap_recovered(methods: dict) -> float:
+ clean = methods["clean"]["mean_accuracy_percent"]
+ raw = methods["raw"]["mean_accuracy_percent"]
+ sdil = methods["sdil"]["mean_accuracy_percent"]
+ return float(100.0 * (sdil - raw) / (clean - raw))
+
+
+def build_analysis(
+ dualprop: dict,
+ ep: dict,
+ clln: dict,
+ overclamp: dict,
+ *,
+ replicates: int,
+ seed: int,
+) -> dict:
+ rng = np.random.default_rng(seed)
+ dp_rows = dualprop["rows"]
+ ep_rows = ep["rows"]
+ panels = {
+ "dualprop": {
+ "title": "Dual Propagation · CIFAR-10",
+ "unit": "seed",
+ "methods": {
+ "clean": summarize_values(np.asarray([
+ row["same_path_clean"] for row in dp_rows]), rng, replicates),
+ "raw": summarize_values(np.asarray([
+ row["raw"] for row in dp_rows]), rng, replicates),
+ "sdil": summarize_values(np.asarray([
+ row["innovation"] for row in dp_rows]), rng, replicates),
+ "oracle": summarize_values(np.asarray([
+ row["oracle"] for row in dp_rows]), rng, replicates),
+ },
+ },
+ "ep": {
+ "title": "Equilibrium Propagation · FashionMNIST",
+ "unit": "seed",
+ "methods": {
+ "clean": summarize_values(100.0 * np.asarray([
+ row["clean"] for row in ep_rows]), rng, replicates),
+ "noise": summarize_values(100.0 * np.asarray([
+ row["same_rms_noise"] for row in ep_rows]), rng, replicates),
+ "raw": summarize_values(100.0 * np.asarray([
+ row["raw"] for row in ep_rows]), rng, replicates),
+ "constant": summarize_values(100.0 * np.asarray([
+ row["constant"] for row in ep_rows]), rng, replicates),
+ "sdil": summarize_values(100.0 * np.asarray([
+ row["innovation"] for row in ep_rows]), rng, replicates),
+ "oracle": summarize_values(100.0 * np.asarray([
+ row["oracle"] for row in ep_rows]), rng, replicates),
+ },
+ },
+ "clln": {
+ "title": "Coupled learning · 32 edges",
+ "unit": "task; device draws averaged within task",
+ "methods": {
+ method: summarize_values(
+ task_cluster_accuracies(clln, source), rng, replicates)
+ for method, source in (
+ ("clean", "clean"),
+ ("noise", "matched_noise"),
+ ("raw", "raw"),
+ ("constant", "constant"),
+ ("sdil", "sdil"),
+ )
+ },
+ },
+ "overclamp": {
+ "title": "Overclamped coupled learning · 32 edges",
+ "unit": "task; device draws averaged within task",
+ "methods": {
+ method: summarize_values(
+ task_cluster_accuracies(overclamp, source), rng, replicates)
+ for method, source in (
+ ("clean", "overclamp_clean"),
+ ("raw", "overclamp"),
+ ("sdil", "overclamp_sdil"),
+ )
+ },
+ },
+ }
+ for panel in panels.values():
+ panel["raw_to_clean_gap_recovered_percent"] = gap_recovered(
+ panel["methods"])
+ return {
+ "analysis": "digital_additive_sdil_transfer",
+ "confirmatory_sources": {
+ "dualprop": dualprop["gate"] == "pass",
+ "ep": ep["gate"] == "pass",
+ "clln": bool(clln["confirmatory"]),
+ "overclamp": bool(overclamp["confirmatory"]),
+ },
+ "bootstrap": {
+ "replicates": replicates,
+ "seed": seed,
+ "interval": "percentile 95%",
+ },
+ "panels": panels,
+ }
+
+
+def write_csv(path: Path, analysis: dict) -> None:
+ path.parent.mkdir(parents=True, exist_ok=True)
+ with path.open("w", newline="") as stream:
+ writer = csv.writer(stream)
+ writer.writerow((
+ "panel", "method", "mean_accuracy_percent", "ci_low", "ci_high",
+ "independent_unit", "independent_units", "gap_recovered_percent",
+ ))
+ for panel_name, panel in analysis["panels"].items():
+ for method, summary in panel["methods"].items():
+ writer.writerow((
+ panel_name,
+ method,
+ summary["mean_accuracy_percent"],
+ *summary["bootstrap_95ci_percent"],
+ panel["unit"],
+ summary["independent_units"],
+ panel["raw_to_clean_gap_recovered_percent"],
+ ))
+
+
+def plot(path: Path, analysis: dict) -> None:
+ mpl.rcParams.update({
+ "font.family": "DejaVu Sans",
+ "font.size": 8.5,
+ "axes.labelsize": 9,
+ "axes.titlesize": 9.5,
+ "xtick.labelsize": 7.8,
+ "ytick.labelsize": 8,
+ "axes.spines.top": False,
+ "axes.spines.right": False,
+ "svg.fonttype": "none",
+ "pdf.fonttype": 42,
+ "figure.facecolor": "white",
+ "axes.facecolor": "white",
+ })
+ order = ("dualprop", "ep", "clln", "overclamp")
+ figure, axes = plt.subplots(2, 2, figsize=(8.0, 5.8), sharey=True)
+ for index, (axis, panel_name) in enumerate(zip(axes.ravel(), order)):
+ panel = analysis["panels"][panel_name]
+ methods = list(panel["methods"])
+ summaries = [panel["methods"][method] for method in methods]
+ means = np.asarray([
+ summary["mean_accuracy_percent"] for summary in summaries])
+ intervals = np.asarray([
+ summary["bootstrap_95ci_percent"] for summary in summaries])
+ positions = np.arange(len(methods))
+ axis.bar(
+ positions,
+ means,
+ color=[COLORS[method] for method in methods],
+ width=0.72,
+ edgecolor="white",
+ linewidth=0.4,
+ )
+ axis.errorbar(
+ positions,
+ means,
+ yerr=np.vstack((
+ means - intervals[:, 0], intervals[:, 1] - means,
+ )),
+ fmt="none",
+ ecolor="#222222",
+ elinewidth=0.8,
+ capsize=2.2,
+ )
+ labels = {
+ "clean": "Clean", "noise": "Same-RMS\nnoise", "raw": "Raw",
+ "constant": "Static\ncalibration", "sdil": "SDIL",
+ "oracle": "Oracle",
+ }
+ axis.set_xticks(positions, [labels[method] for method in methods])
+ axis.set_ylim(0.0, 106.0)
+ axis.grid(axis="y", color="#D9D9D9", linewidth=0.55, alpha=0.8)
+ axis.tick_params(length=3)
+ letter = chr(ord("a") + index)
+ recovery = panel["raw_to_clean_gap_recovered_percent"]
+ axis.set_title(
+ f"({letter}) {panel['title']}\n{recovery:.0f}% of clean gap recovered")
+ if index % 2 == 0:
+ axis.set_ylabel("Task accuracy (%)")
+ figure.text(
+ 0.995,
+ 0.005,
+ "Dual Prop/EP: 5 seeds; coupled learners: 40 tasks × 3 device draws",
+ ha="right",
+ va="bottom",
+ fontsize=7,
+ color="#666666",
+ )
+ figure.tight_layout(rect=(0.0, 0.035, 1.0, 1.0), h_pad=2.1, w_pad=1.8)
+ path.parent.mkdir(parents=True, exist_ok=True)
+ figure.savefig(path.with_suffix(".svg"), bbox_inches="tight")
+ figure.savefig(path.with_suffix(".pdf"), bbox_inches="tight")
+ figure.savefig(path.with_suffix(".png"), dpi=240, bbox_inches="tight")
+ plt.close(figure)
+
+
+def main() -> None:
+ args = parse_args()
+ reports = {
+ name: json.loads(path.read_text())
+ for name, path in (
+ ("dualprop", args.dualprop),
+ ("ep", args.ep),
+ ("clln", args.clln),
+ ("overclamp", args.overclamp),
+ )
+ }
+ analysis = build_analysis(
+ **reports,
+ replicates=args.bootstrap_replicates,
+ seed=args.bootstrap_seed,
+ )
+ analysis["sources"] = {
+ "dualprop": str(args.dualprop),
+ "ep": str(args.ep),
+ "clln": str(args.clln),
+ "overclamp": str(args.overclamp),
+ }
+ args.output_analysis.parent.mkdir(parents=True, exist_ok=True)
+ args.output_analysis.write_text(json.dumps(analysis, indent=2) + "\n")
+ write_csv(args.output_csv, analysis)
+ plot(args.output_figure, analysis)
+ print(json.dumps({
+ name: {
+ "gap_recovered_percent": panel[
+ "raw_to_clean_gap_recovered_percent"],
+ "methods": {
+ method: summary["mean_accuracy_percent"]
+ for method, summary in panel["methods"].items()
+ },
+ }
+ for name, panel in analysis["panels"].items()
+ }, indent=2))
+ print(f"wrote {args.output_analysis}")
+ print(f"wrote {args.output_csv}")
+ print(f"wrote {args.output_figure}.svg/.pdf/.png")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/results/digital_additive_transfer.csv b/results/digital_additive_transfer.csv
new file mode 100644
index 0000000..92b019c
--- /dev/null
+++ b/results/digital_additive_transfer.csv
@@ -0,0 +1,19 @@
+panel,method,mean_accuracy_percent,ci_low,ci_high,independent_unit,independent_units,gap_recovered_percent
+dualprop,clean,82.85999908447266,82.56399993896484,83.07999725341797,seed,5,100.08168001179783
+dualprop,raw,9.399999618530273,9.399999618530273,9.399999618530273,seed,5,100.08168001179783
+dualprop,sdil,82.92000122070313,82.6280014038086,83.23600158691406,seed,5,100.08168001179783
+dualprop,oracle,82.96000061035156,82.52799682617187,83.34400329589843,seed,5,100.08168001179783
+ep,clean,76.26,73.55,78.53,seed,5,96.12299465240643
+ep,noise,75.44,71.01,78.76,seed,5,96.12299465240643
+ep,raw,31.379999999999995,18.059999999999995,46.08,seed,5,96.12299465240643
+ep,constant,67.9,60.42,74.46674999999942,seed,5,96.12299465240643
+ep,sdil,74.52000000000001,72.51,76.25999999999999,seed,5,96.12299465240643
+ep,oracle,75.55999999999999,70.6,78.66999999999999,seed,5,96.12299465240643
+clln,clean,100.0,100.0,100.0,task; device draws averaged within task,40,100.0
+clln,noise,99.27083333333334,98.4375,99.89583333333334,task; device draws averaged within task,40,100.0
+clln,raw,65.3125,61.14583333333333,69.375,task; device draws averaged within task,40,100.0
+clln,constant,93.85416666666666,90.41666666666667,96.875,task; device draws averaged within task,40,100.0
+clln,sdil,100.0,100.0,100.0,task; device draws averaged within task,40,100.0
+overclamp,clean,91.875,87.8125,95.3125,task; device draws averaged within task,40,100.41666666666663
+overclamp,raw,66.875,61.25,72.70833333333333,task; device draws averaged within task,40,100.41666666666663
+overclamp,sdil,91.97916666666666,88.125,95.3125,task; device draws averaged within task,40,100.41666666666663
diff --git a/results/digital_additive_transfer.json b/results/digital_additive_transfer.json
new file mode 100644
index 0000000..ccb06f8
--- /dev/null
+++ b/results/digital_additive_transfer.json
@@ -0,0 +1,194 @@
+{
+ "analysis": "digital_additive_sdil_transfer",
+ "confirmatory_sources": {
+ "dualprop": true,
+ "ep": false,
+ "clln": true,
+ "overclamp": true
+ },
+ "bootstrap": {
+ "replicates": 20000,
+ "seed": 20260829,
+ "interval": "percentile 95%"
+ },
+ "panels": {
+ "dualprop": {
+ "title": "Dual Propagation \u00b7 CIFAR-10",
+ "unit": "seed",
+ "methods": {
+ "clean": {
+ "mean_accuracy_percent": 82.85999908447266,
+ "bootstrap_95ci_percent": [
+ 82.56399993896484,
+ 83.07999725341797
+ ],
+ "independent_units": 5
+ },
+ "raw": {
+ "mean_accuracy_percent": 9.399999618530273,
+ "bootstrap_95ci_percent": [
+ 9.399999618530273,
+ 9.399999618530273
+ ],
+ "independent_units": 5
+ },
+ "sdil": {
+ "mean_accuracy_percent": 82.92000122070313,
+ "bootstrap_95ci_percent": [
+ 82.6280014038086,
+ 83.23600158691406
+ ],
+ "independent_units": 5
+ },
+ "oracle": {
+ "mean_accuracy_percent": 82.96000061035156,
+ "bootstrap_95ci_percent": [
+ 82.52799682617187,
+ 83.34400329589843
+ ],
+ "independent_units": 5
+ }
+ },
+ "raw_to_clean_gap_recovered_percent": 100.08168001179783
+ },
+ "ep": {
+ "title": "Equilibrium Propagation \u00b7 FashionMNIST",
+ "unit": "seed",
+ "methods": {
+ "clean": {
+ "mean_accuracy_percent": 76.26,
+ "bootstrap_95ci_percent": [
+ 73.55,
+ 78.53
+ ],
+ "independent_units": 5
+ },
+ "noise": {
+ "mean_accuracy_percent": 75.44,
+ "bootstrap_95ci_percent": [
+ 71.01,
+ 78.76
+ ],
+ "independent_units": 5
+ },
+ "raw": {
+ "mean_accuracy_percent": 31.379999999999995,
+ "bootstrap_95ci_percent": [
+ 18.059999999999995,
+ 46.08
+ ],
+ "independent_units": 5
+ },
+ "constant": {
+ "mean_accuracy_percent": 67.9,
+ "bootstrap_95ci_percent": [
+ 60.42,
+ 74.46674999999942
+ ],
+ "independent_units": 5
+ },
+ "sdil": {
+ "mean_accuracy_percent": 74.52000000000001,
+ "bootstrap_95ci_percent": [
+ 72.51,
+ 76.25999999999999
+ ],
+ "independent_units": 5
+ },
+ "oracle": {
+ "mean_accuracy_percent": 75.55999999999999,
+ "bootstrap_95ci_percent": [
+ 70.6,
+ 78.66999999999999
+ ],
+ "independent_units": 5
+ }
+ },
+ "raw_to_clean_gap_recovered_percent": 96.12299465240643
+ },
+ "clln": {
+ "title": "Coupled learning \u00b7 32 edges",
+ "unit": "task; device draws averaged within task",
+ "methods": {
+ "clean": {
+ "mean_accuracy_percent": 100.0,
+ "bootstrap_95ci_percent": [
+ 100.0,
+ 100.0
+ ],
+ "independent_units": 40
+ },
+ "noise": {
+ "mean_accuracy_percent": 99.27083333333334,
+ "bootstrap_95ci_percent": [
+ 98.4375,
+ 99.89583333333334
+ ],
+ "independent_units": 40
+ },
+ "raw": {
+ "mean_accuracy_percent": 65.3125,
+ "bootstrap_95ci_percent": [
+ 61.14583333333333,
+ 69.375
+ ],
+ "independent_units": 40
+ },
+ "constant": {
+ "mean_accuracy_percent": 93.85416666666666,
+ "bootstrap_95ci_percent": [
+ 90.41666666666667,
+ 96.875
+ ],
+ "independent_units": 40
+ },
+ "sdil": {
+ "mean_accuracy_percent": 100.0,
+ "bootstrap_95ci_percent": [
+ 100.0,
+ 100.0
+ ],
+ "independent_units": 40
+ }
+ },
+ "raw_to_clean_gap_recovered_percent": 100.0
+ },
+ "overclamp": {
+ "title": "Overclamped coupled learning \u00b7 32 edges",
+ "unit": "task; device draws averaged within task",
+ "methods": {
+ "clean": {
+ "mean_accuracy_percent": 91.875,
+ "bootstrap_95ci_percent": [
+ 87.8125,
+ 95.3125
+ ],
+ "independent_units": 40
+ },
+ "raw": {
+ "mean_accuracy_percent": 66.875,
+ "bootstrap_95ci_percent": [
+ 61.25,
+ 72.70833333333333
+ ],
+ "independent_units": 40
+ },
+ "sdil": {
+ "mean_accuracy_percent": 91.97916666666666,
+ "bootstrap_95ci_percent": [
+ 88.125,
+ 95.3125
+ ],
+ "independent_units": 40
+ }
+ },
+ "raw_to_clean_gap_recovered_percent": 100.41666666666663
+ }
+ },
+ "sources": {
+ "dualprop": "results/contrastive_bias/c1_gate.json",
+ "ep": "results/ep_bias/c1_gate.json",
+ "clln": "results/coupled_ladder/p2_confirm_side4.json",
+ "overclamp": "results/coupled_ladder/p3_overclamp_side4.json"
+ }
+}
diff --git a/results/figs/figure_digital_additive_transfer.pdf b/results/figs/figure_digital_additive_transfer.pdf
new file mode 100644
index 0000000..4070813
--- /dev/null
+++ b/results/figs/figure_digital_additive_transfer.pdf
Binary files differ
diff --git a/results/figs/figure_digital_additive_transfer.png b/results/figs/figure_digital_additive_transfer.png
new file mode 100644
index 0000000..78046ca
--- /dev/null
+++ b/results/figs/figure_digital_additive_transfer.png
Binary files differ
diff --git a/results/figs/figure_digital_additive_transfer.svg b/results/figs/figure_digital_additive_transfer.svg
new file mode 100644
index 0000000..014aeb9
--- /dev/null
+++ b/results/figs/figure_digital_additive_transfer.svg
@@ -0,0 +1,964 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="577.840313pt" height="420.588437pt" viewBox="0 0 577.840313 420.588437" xmlns="http://www.w3.org/2000/svg" version="1.1">
+ <metadata>
+ <rdf:RDF xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <cc:Work>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:date>2026-08-29T18:42:31.692423</dc:date>
+ <dc:format>image/svg+xml</dc:format>
+ <dc:creator>
+ <cc:Agent>
+ <dc:title>Matplotlib v3.10.8, https://matplotlib.org/</dc:title>
+ </cc:Agent>
+ </dc:creator>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs>
+ <style type="text/css">*{stroke-linejoin: round; stroke-linecap: butt}</style>
+ </defs>
+ <g id="figure_1">
+ <g id="patch_1">
+ <path d="M 0 420.588437
+L 577.840313 420.588437
+L 577.840313 -0
+L 0 -0
+z
+" style="fill: #ffffff"/>
+ </g>
+ <g id="axes_1">
+ <g id="patch_2">
+ <path d="M 41.680312 168.391437
+L 293.860312 168.391437
+L 293.860312 31.056437
+L 41.680312 31.056437
+z
+" style="fill: #ffffff"/>
+ </g>
+ <g id="patch_3">
+ <path d="M 53.14304 168.391437
+L 97.514887 168.391437
+L 97.514887 61.036928
+L 53.14304 61.036928
+z
+" clip-path="url(#p7944c5556f)" style="fill: #222222; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="patch_4">
+ <path d="M 114.770606 168.391437
+L 159.142453 168.391437
+L 159.142453 156.212674
+L 114.770606 156.212674
+z
+" clip-path="url(#p7944c5556f)" style="fill: #d55e00; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="patch_5">
+ <path d="M 176.398172 168.391437
+L 220.770019 168.391437
+L 220.770019 60.959189
+L 176.398172 60.959189
+z
+" clip-path="url(#p7944c5556f)" style="fill: #0072b2; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="patch_6">
+ <path d="M 238.025738 168.391437
+L 282.397585 168.391437
+L 282.397585 60.907365
+L 238.025738 60.907365
+z
+" clip-path="url(#p7944c5556f)" style="fill: #009e73; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="matplotlib.axis_1">
+ <g id="xtick_1">
+ <g id="line2d_1">
+ <defs>
+ <path id="m278102d266" d="M 0 0
+L 0 3
+" style="stroke: #000000; stroke-width: 0.8"/>
+ </defs>
+ <g>
+ <use xlink:href="#m278102d266" x="75.328964" y="168.391437" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_1">
+ <text style="font-size: 7.8px; font-family: 'DejaVu Sans'; text-anchor: middle" x="75.328964" y="180.818219" transform="rotate(-0 75.328964 180.818219)">Clean</text>
+ </g>
+ </g>
+ <g id="xtick_2">
+ <g id="line2d_2">
+ <g>
+ <use xlink:href="#m278102d266" x="136.95653" y="168.391437" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_2">
+ <text style="font-size: 7.8px; font-family: 'DejaVu Sans'; text-anchor: middle" x="136.95653" y="180.818219" transform="rotate(-0 136.95653 180.818219)">Raw</text>
+ </g>
+ </g>
+ <g id="xtick_3">
+ <g id="line2d_3">
+ <g>
+ <use xlink:href="#m278102d266" x="198.584095" y="168.391437" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_3">
+ <text style="font-size: 7.8px; font-family: 'DejaVu Sans'; text-anchor: middle" x="198.584095" y="180.818219" transform="rotate(-0 198.584095 180.818219)">SDIL</text>
+ </g>
+ </g>
+ <g id="xtick_4">
+ <g id="line2d_4">
+ <g>
+ <use xlink:href="#m278102d266" x="260.211661" y="168.391437" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_4">
+ <text style="font-size: 7.8px; font-family: 'DejaVu Sans'; text-anchor: middle" x="260.211661" y="180.818219" transform="rotate(-0 260.211661 180.818219)">Oracle</text>
+ </g>
+ </g>
+ </g>
+ <g id="matplotlib.axis_2">
+ <g id="ytick_1">
+ <g id="line2d_5">
+ <path d="M 41.680312 168.391437
+L 293.860312 168.391437
+" clip-path="url(#p7944c5556f)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_6">
+ <defs>
+ <path id="m33223f5472" d="M 0 0
+L -3 0
+" style="stroke: #000000; stroke-width: 0.8"/>
+ </defs>
+ <g>
+ <use xlink:href="#m33223f5472" x="41.680312" y="168.391437" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_5">
+ <text style="font-size: 8px; font-family: 'DejaVu Sans'; text-anchor: end" x="35.180312" y="171.430812" transform="rotate(-0 35.180312 171.430812)">0</text>
+ </g>
+ </g>
+ <g id="ytick_2">
+ <g id="line2d_7">
+ <path d="M 41.680312 142.479173
+L 293.860312 142.479173
+" clip-path="url(#p7944c5556f)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_8">
+ <g>
+ <use xlink:href="#m33223f5472" x="41.680312" y="142.479173" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_6">
+ <text style="font-size: 8px; font-family: 'DejaVu Sans'; text-anchor: end" x="35.180312" y="145.518548" transform="rotate(-0 35.180312 145.518548)">20</text>
+ </g>
+ </g>
+ <g id="ytick_3">
+ <g id="line2d_9">
+ <path d="M 41.680312 116.566909
+L 293.860312 116.566909
+" clip-path="url(#p7944c5556f)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_10">
+ <g>
+ <use xlink:href="#m33223f5472" x="41.680312" y="116.566909" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_7">
+ <text style="font-size: 8px; font-family: 'DejaVu Sans'; text-anchor: end" x="35.180312" y="119.606284" transform="rotate(-0 35.180312 119.606284)">40</text>
+ </g>
+ </g>
+ <g id="ytick_4">
+ <g id="line2d_11">
+ <path d="M 41.680312 90.654645
+L 293.860312 90.654645
+" clip-path="url(#p7944c5556f)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_12">
+ <g>
+ <use xlink:href="#m33223f5472" x="41.680312" y="90.654645" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_8">
+ <text style="font-size: 8px; font-family: 'DejaVu Sans'; text-anchor: end" x="35.180312" y="93.69402" transform="rotate(-0 35.180312 93.69402)">60</text>
+ </g>
+ </g>
+ <g id="ytick_5">
+ <g id="line2d_13">
+ <path d="M 41.680312 64.742381
+L 293.860312 64.742381
+" clip-path="url(#p7944c5556f)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_14">
+ <g>
+ <use xlink:href="#m33223f5472" x="41.680312" y="64.742381" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_9">
+ <text style="font-size: 8px; font-family: 'DejaVu Sans'; text-anchor: end" x="35.180312" y="67.781756" transform="rotate(-0 35.180312 67.781756)">80</text>
+ </g>
+ </g>
+ <g id="ytick_6">
+ <g id="line2d_15">
+ <path d="M 41.680312 38.830117
+L 293.860312 38.830117
+" clip-path="url(#p7944c5556f)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_16">
+ <g>
+ <use xlink:href="#m33223f5472" x="41.680312" y="38.830117" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_10">
+ <text style="font-size: 8px; font-family: 'DejaVu Sans'; text-anchor: end" x="35.180312" y="41.869492" transform="rotate(-0 35.180312 41.869492)">100</text>
+ </g>
+ </g>
+ <g id="text_11">
+ <text style="font-size: 9px; font-family: 'DejaVu Sans'; text-anchor: middle" x="14.038594" y="99.723937" transform="rotate(-90 14.038594 99.723937)">Task accuracy (%)</text>
+ </g>
+ </g>
+ <g id="LineCollection_1">
+ <path d="M 75.328964 61.420429
+L 75.328964 60.751896
+" clip-path="url(#p7944c5556f)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ <path d="M 136.95653 156.212674
+L 136.95653 156.212674
+" clip-path="url(#p7944c5556f)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ <path d="M 198.584095 61.337508
+L 198.584095 60.549775
+" clip-path="url(#p7944c5556f)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ <path d="M 260.211661 61.467075
+L 260.211661 60.409846
+" clip-path="url(#p7944c5556f)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ </g>
+ <g id="line2d_17">
+ <defs>
+ <path id="m23b9465fd3" d="M 2.2 0
+L -2.2 -0
+" style="stroke: #222222"/>
+ </defs>
+ <g clip-path="url(#p7944c5556f)">
+ <use xlink:href="#m23b9465fd3" x="75.328964" y="61.420429" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#m23b9465fd3" x="136.95653" y="156.212674" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#m23b9465fd3" x="198.584095" y="61.337508" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#m23b9465fd3" x="260.211661" y="61.467075" style="fill: #1f77b4; stroke: #222222"/>
+ </g>
+ </g>
+ <g id="line2d_18">
+ <g clip-path="url(#p7944c5556f)">
+ <use xlink:href="#m23b9465fd3" x="75.328964" y="60.751896" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#m23b9465fd3" x="136.95653" y="156.212674" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#m23b9465fd3" x="198.584095" y="60.549775" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#m23b9465fd3" x="260.211661" y="60.409846" style="fill: #1f77b4; stroke: #222222"/>
+ </g>
+ </g>
+ <g id="patch_7">
+ <path d="M 41.680312 168.391437
+L 41.680312 31.056437
+" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/>
+ </g>
+ <g id="patch_8">
+ <path d="M 41.680312 168.391437
+L 293.860312 168.391437
+" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/>
+ </g>
+ <g id="text_12">
+ <text style="font-size: 9.5px; font-family: 'DejaVu Sans'" transform="translate(93.087695 14.418516)">(a) Dual Propagation · CIFAR-10</text>
+ <text style="font-size: 9.5px; font-family: 'DejaVu Sans'" transform="translate(98.051445 25.056437)">100% of clean gap recovered</text>
+ </g>
+ </g>
+ <g id="axes_2">
+ <g id="patch_9">
+ <path d="M 312.160312 168.391437
+L 564.340312 168.391437
+L 564.340312 31.056437
+L 312.160312 31.056437
+z
+" style="fill: #ffffff"/>
+ </g>
+ <g id="patch_10">
+ <path d="M 323.62304 168.391437
+L 352.480255 168.391437
+L 352.480255 69.587974
+L 323.62304 69.587974
+z
+" clip-path="url(#p23da3bf74f)" style="fill: #222222; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="patch_11">
+ <path d="M 363.702506 168.391437
+L 392.559721 168.391437
+L 392.559721 70.650377
+L 363.702506 70.650377
+z
+" clip-path="url(#p23da3bf74f)" style="fill: #cc79a7; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="patch_12">
+ <path d="M 403.781972 168.391437
+L 432.639187 168.391437
+L 432.639187 127.735095
+L 403.781972 127.735095
+z
+" clip-path="url(#p23da3bf74f)" style="fill: #d55e00; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="patch_13">
+ <path d="M 443.861438 168.391437
+L 472.718653 168.391437
+L 472.718653 80.419301
+L 443.861438 80.419301
+z
+" clip-path="url(#p23da3bf74f)" style="fill: #777777; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="patch_14">
+ <path d="M 483.940904 168.391437
+L 512.798119 168.391437
+L 512.798119 71.842341
+L 483.940904 71.842341
+z
+" clip-path="url(#p23da3bf74f)" style="fill: #0072b2; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="patch_15">
+ <path d="M 524.02037 168.391437
+L 552.877585 168.391437
+L 552.877585 70.494904
+L 524.02037 70.494904
+z
+" clip-path="url(#p23da3bf74f)" style="fill: #009e73; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="matplotlib.axis_3">
+ <g id="xtick_5">
+ <g id="line2d_19">
+ <g>
+ <use xlink:href="#m278102d266" x="338.051648" y="168.391437" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_13">
+ <text style="font-size: 7.8px; font-family: 'DejaVu Sans'; text-anchor: middle" x="338.051648" y="180.818219" transform="rotate(-0 338.051648 180.818219)">Clean</text>
+ </g>
+ </g>
+ <g id="xtick_6">
+ <g id="line2d_20">
+ <g>
+ <use xlink:href="#m278102d266" x="378.131114" y="168.391437" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_14">
+ <text style="font-size: 7.8px; font-family: 'DejaVu Sans'" transform="translate(357.108895 180.818219)">Same-RMS</text>
+ <text style="font-size: 7.8px; font-family: 'DejaVu Sans'" transform="translate(367.758332 189.552512)">noise</text>
+ </g>
+ </g>
+ <g id="xtick_7">
+ <g id="line2d_21">
+ <g>
+ <use xlink:href="#m278102d266" x="418.21058" y="168.391437" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_15">
+ <text style="font-size: 7.8px; font-family: 'DejaVu Sans'; text-anchor: middle" x="418.21058" y="180.818219" transform="rotate(-0 418.21058 180.818219)">Raw</text>
+ </g>
+ </g>
+ <g id="xtick_8">
+ <g id="line2d_22">
+ <g>
+ <use xlink:href="#m278102d266" x="458.290045" y="168.391437" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_16">
+ <text style="font-size: 7.8px; font-family: 'DejaVu Sans'" transform="translate(447.138483 180.818219)">Static</text>
+ <text style="font-size: 7.8px; font-family: 'DejaVu Sans'" transform="translate(437.649295 189.552512)">calibration</text>
+ </g>
+ </g>
+ <g id="xtick_9">
+ <g id="line2d_23">
+ <g>
+ <use xlink:href="#m278102d266" x="498.369511" y="168.391437" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_17">
+ <text style="font-size: 7.8px; font-family: 'DejaVu Sans'; text-anchor: middle" x="498.369511" y="180.818219" transform="rotate(-0 498.369511 180.818219)">SDIL</text>
+ </g>
+ </g>
+ <g id="xtick_10">
+ <g id="line2d_24">
+ <g>
+ <use xlink:href="#m278102d266" x="538.448977" y="168.391437" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_18">
+ <text style="font-size: 7.8px; font-family: 'DejaVu Sans'; text-anchor: middle" x="538.448977" y="180.818219" transform="rotate(-0 538.448977 180.818219)">Oracle</text>
+ </g>
+ </g>
+ </g>
+ <g id="matplotlib.axis_4">
+ <g id="ytick_7">
+ <g id="line2d_25">
+ <path d="M 312.160312 168.391437
+L 564.340312 168.391437
+" clip-path="url(#p23da3bf74f)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_26">
+ <g>
+ <use xlink:href="#m33223f5472" x="312.160312" y="168.391437" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ </g>
+ <g id="ytick_8">
+ <g id="line2d_27">
+ <path d="M 312.160312 142.479173
+L 564.340312 142.479173
+" clip-path="url(#p23da3bf74f)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_28">
+ <g>
+ <use xlink:href="#m33223f5472" x="312.160312" y="142.479173" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ </g>
+ <g id="ytick_9">
+ <g id="line2d_29">
+ <path d="M 312.160312 116.566909
+L 564.340312 116.566909
+" clip-path="url(#p23da3bf74f)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_30">
+ <g>
+ <use xlink:href="#m33223f5472" x="312.160312" y="116.566909" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ </g>
+ <g id="ytick_10">
+ <g id="line2d_31">
+ <path d="M 312.160312 90.654645
+L 564.340312 90.654645
+" clip-path="url(#p23da3bf74f)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_32">
+ <g>
+ <use xlink:href="#m33223f5472" x="312.160312" y="90.654645" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ </g>
+ <g id="ytick_11">
+ <g id="line2d_33">
+ <path d="M 312.160312 64.742381
+L 564.340312 64.742381
+" clip-path="url(#p23da3bf74f)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_34">
+ <g>
+ <use xlink:href="#m33223f5472" x="312.160312" y="64.742381" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ </g>
+ <g id="ytick_12">
+ <g id="line2d_35">
+ <path d="M 312.160312 38.830117
+L 564.340312 38.830117
+" clip-path="url(#p23da3bf74f)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_36">
+ <g>
+ <use xlink:href="#m33223f5472" x="312.160312" y="38.830117" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ </g>
+ </g>
+ <g id="LineCollection_2">
+ <path d="M 338.051648 73.099086
+L 338.051648 66.646932
+" clip-path="url(#p23da3bf74f)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ <path d="M 378.131114 76.389944
+L 378.131114 66.348941
+" clip-path="url(#p23da3bf74f)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ <path d="M 418.21058 144.992663
+L 418.21058 108.689581
+" clip-path="url(#p23da3bf74f)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ <path d="M 458.290045 90.110487
+L 458.290045 71.911333
+" clip-path="url(#p23da3bf74f)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ <path d="M 498.369511 74.446524
+L 498.369511 69.587974
+" clip-path="url(#p23da3bf74f)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ <path d="M 538.448977 76.921145
+L 538.448977 66.465546
+" clip-path="url(#p23da3bf74f)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ </g>
+ <g id="line2d_37">
+ <g clip-path="url(#p23da3bf74f)">
+ <use xlink:href="#m23b9465fd3" x="338.051648" y="73.099086" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#m23b9465fd3" x="378.131114" y="76.389944" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#m23b9465fd3" x="418.21058" y="144.992663" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#m23b9465fd3" x="458.290045" y="90.110487" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#m23b9465fd3" x="498.369511" y="74.446524" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#m23b9465fd3" x="538.448977" y="76.921145" style="fill: #1f77b4; stroke: #222222"/>
+ </g>
+ </g>
+ <g id="line2d_38">
+ <g clip-path="url(#p23da3bf74f)">
+ <use xlink:href="#m23b9465fd3" x="338.051648" y="66.646932" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#m23b9465fd3" x="378.131114" y="66.348941" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#m23b9465fd3" x="418.21058" y="108.689581" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#m23b9465fd3" x="458.290045" y="71.911333" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#m23b9465fd3" x="498.369511" y="69.587974" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#m23b9465fd3" x="538.448977" y="66.465546" style="fill: #1f77b4; stroke: #222222"/>
+ </g>
+ </g>
+ <g id="patch_16">
+ <path d="M 312.160312 168.391437
+L 312.160312 31.056437
+" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/>
+ </g>
+ <g id="patch_17">
+ <path d="M 312.160312 168.391437
+L 564.340312 168.391437
+" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/>
+ </g>
+ <g id="text_19">
+ <text style="font-size: 9.5px; font-family: 'DejaVu Sans'" transform="translate(335.684453 14.418516)">(b) Equilibrium Propagation · FashionMNIST</text>
+ <text style="font-size: 9.5px; font-family: 'DejaVu Sans'" transform="translate(371.553633 25.056437)">96% of clean gap recovered</text>
+ </g>
+ </g>
+ <g id="axes_3">
+ <g id="patch_18">
+ <path d="M 41.680312 369.628437
+L 293.860312 369.628437
+L 293.860312 232.293437
+L 41.680312 232.293437
+z
+" style="fill: #ffffff"/>
+ </g>
+ <g id="patch_19">
+ <path d="M 53.14304 369.628437
+L 88.114072 369.628437
+L 88.114072 240.067117
+L 53.14304 240.067117
+z
+" clip-path="url(#p8d4a35fc37)" style="fill: #222222; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="patch_20">
+ <path d="M 101.713918 369.628437
+L 136.68495 369.628437
+L 136.68495 241.011835
+L 101.713918 241.011835
+z
+" clip-path="url(#p8d4a35fc37)" style="fill: #cc79a7; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="patch_21">
+ <path d="M 150.284796 369.628437
+L 185.255829 369.628437
+L 185.255829 285.0087
+L 150.284796 285.0087
+z
+" clip-path="url(#p8d4a35fc37)" style="fill: #d55e00; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="patch_22">
+ <path d="M 198.855675 369.628437
+L 233.826707 369.628437
+L 233.826707 248.02974
+L 198.855675 248.02974
+z
+" clip-path="url(#p8d4a35fc37)" style="fill: #777777; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="patch_23">
+ <path d="M 247.426553 369.628437
+L 282.397585 369.628437
+L 282.397585 240.067117
+L 247.426553 240.067117
+z
+" clip-path="url(#p8d4a35fc37)" style="fill: #0072b2; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="matplotlib.axis_5">
+ <g id="xtick_11">
+ <g id="line2d_39">
+ <g>
+ <use xlink:href="#m278102d266" x="70.628556" y="369.628437" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_20">
+ <text style="font-size: 7.8px; font-family: 'DejaVu Sans'; text-anchor: middle" x="70.628556" y="382.055219" transform="rotate(-0 70.628556 382.055219)">Clean</text>
+ </g>
+ </g>
+ <g id="xtick_12">
+ <g id="line2d_40">
+ <g>
+ <use xlink:href="#m278102d266" x="119.199434" y="369.628437" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_21">
+ <text style="font-size: 7.8px; font-family: 'DejaVu Sans'" transform="translate(98.177215 382.055219)">Same-RMS</text>
+ <text style="font-size: 7.8px; font-family: 'DejaVu Sans'" transform="translate(108.826653 390.789512)">noise</text>
+ </g>
+ </g>
+ <g id="xtick_13">
+ <g id="line2d_41">
+ <g>
+ <use xlink:href="#m278102d266" x="167.770312" y="369.628437" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_22">
+ <text style="font-size: 7.8px; font-family: 'DejaVu Sans'; text-anchor: middle" x="167.770312" y="382.055219" transform="rotate(-0 167.770312 382.055219)">Raw</text>
+ </g>
+ </g>
+ <g id="xtick_14">
+ <g id="line2d_42">
+ <g>
+ <use xlink:href="#m278102d266" x="216.341191" y="369.628437" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_23">
+ <text style="font-size: 7.8px; font-family: 'DejaVu Sans'" transform="translate(205.189628 382.055219)">Static</text>
+ <text style="font-size: 7.8px; font-family: 'DejaVu Sans'" transform="translate(195.700441 390.789512)">calibration</text>
+ </g>
+ </g>
+ <g id="xtick_15">
+ <g id="line2d_43">
+ <g>
+ <use xlink:href="#m278102d266" x="264.912069" y="369.628437" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_24">
+ <text style="font-size: 7.8px; font-family: 'DejaVu Sans'; text-anchor: middle" x="264.912069" y="382.055219" transform="rotate(-0 264.912069 382.055219)">SDIL</text>
+ </g>
+ </g>
+ </g>
+ <g id="matplotlib.axis_6">
+ <g id="ytick_13">
+ <g id="line2d_44">
+ <path d="M 41.680312 369.628437
+L 293.860312 369.628437
+" clip-path="url(#p8d4a35fc37)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_45">
+ <g>
+ <use xlink:href="#m33223f5472" x="41.680312" y="369.628437" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_25">
+ <text style="font-size: 8px; font-family: 'DejaVu Sans'; text-anchor: end" x="35.180312" y="372.667812" transform="rotate(-0 35.180312 372.667812)">0</text>
+ </g>
+ </g>
+ <g id="ytick_14">
+ <g id="line2d_46">
+ <path d="M 41.680312 343.716173
+L 293.860312 343.716173
+" clip-path="url(#p8d4a35fc37)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_47">
+ <g>
+ <use xlink:href="#m33223f5472" x="41.680312" y="343.716173" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_26">
+ <text style="font-size: 8px; font-family: 'DejaVu Sans'; text-anchor: end" x="35.180312" y="346.755548" transform="rotate(-0 35.180312 346.755548)">20</text>
+ </g>
+ </g>
+ <g id="ytick_15">
+ <g id="line2d_48">
+ <path d="M 41.680312 317.803909
+L 293.860312 317.803909
+" clip-path="url(#p8d4a35fc37)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_49">
+ <g>
+ <use xlink:href="#m33223f5472" x="41.680312" y="317.803909" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_27">
+ <text style="font-size: 8px; font-family: 'DejaVu Sans'; text-anchor: end" x="35.180312" y="320.843284" transform="rotate(-0 35.180312 320.843284)">40</text>
+ </g>
+ </g>
+ <g id="ytick_16">
+ <g id="line2d_50">
+ <path d="M 41.680312 291.891645
+L 293.860312 291.891645
+" clip-path="url(#p8d4a35fc37)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_51">
+ <g>
+ <use xlink:href="#m33223f5472" x="41.680312" y="291.891645" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_28">
+ <text style="font-size: 8px; font-family: 'DejaVu Sans'; text-anchor: end" x="35.180312" y="294.93102" transform="rotate(-0 35.180312 294.93102)">60</text>
+ </g>
+ </g>
+ <g id="ytick_17">
+ <g id="line2d_52">
+ <path d="M 41.680312 265.979381
+L 293.860312 265.979381
+" clip-path="url(#p8d4a35fc37)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_53">
+ <g>
+ <use xlink:href="#m33223f5472" x="41.680312" y="265.979381" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_29">
+ <text style="font-size: 8px; font-family: 'DejaVu Sans'; text-anchor: end" x="35.180312" y="269.018756" transform="rotate(-0 35.180312 269.018756)">80</text>
+ </g>
+ </g>
+ <g id="ytick_18">
+ <g id="line2d_54">
+ <path d="M 41.680312 240.067117
+L 293.860312 240.067117
+" clip-path="url(#p8d4a35fc37)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_55">
+ <g>
+ <use xlink:href="#m33223f5472" x="41.680312" y="240.067117" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_30">
+ <text style="font-size: 8px; font-family: 'DejaVu Sans'; text-anchor: end" x="35.180312" y="243.106492" transform="rotate(-0 35.180312 243.106492)">100</text>
+ </g>
+ </g>
+ <g id="text_31">
+ <text style="font-size: 9px; font-family: 'DejaVu Sans'; text-anchor: middle" x="14.038594" y="300.960937" transform="rotate(-90 14.038594 300.960937)">Task accuracy (%)</text>
+ </g>
+ </g>
+ <g id="LineCollection_3">
+ <path d="M 70.628556 240.067117
+L 70.628556 240.067117
+" clip-path="url(#p8d4a35fc37)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ <path d="M 119.199434 242.091512
+L 119.199434 240.202076
+" clip-path="url(#p8d4a35fc37)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ <path d="M 167.770312 290.407088
+L 167.770312 279.745271
+" clip-path="url(#p8d4a35fc37)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ <path d="M 216.341191 252.48341
+L 216.341191 244.115908
+" clip-path="url(#p8d4a35fc37)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ <path d="M 264.912069 240.067117
+L 264.912069 240.067117
+" clip-path="url(#p8d4a35fc37)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ </g>
+ <g id="line2d_56">
+ <g clip-path="url(#p8d4a35fc37)">
+ <use xlink:href="#m23b9465fd3" x="70.628556" y="240.067117" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#m23b9465fd3" x="119.199434" y="242.091512" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#m23b9465fd3" x="167.770312" y="290.407088" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#m23b9465fd3" x="216.341191" y="252.48341" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#m23b9465fd3" x="264.912069" y="240.067117" style="fill: #1f77b4; stroke: #222222"/>
+ </g>
+ </g>
+ <g id="line2d_57">
+ <g clip-path="url(#p8d4a35fc37)">
+ <use xlink:href="#m23b9465fd3" x="70.628556" y="240.067117" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#m23b9465fd3" x="119.199434" y="240.202076" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#m23b9465fd3" x="167.770312" y="279.745271" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#m23b9465fd3" x="216.341191" y="244.115908" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#m23b9465fd3" x="264.912069" y="240.067117" style="fill: #1f77b4; stroke: #222222"/>
+ </g>
+ </g>
+ <g id="patch_24">
+ <path d="M 41.680312 369.628437
+L 41.680312 232.293437
+" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/>
+ </g>
+ <g id="patch_25">
+ <path d="M 41.680313 369.628437
+L 293.860312 369.628437
+" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/>
+ </g>
+ <g id="text_32">
+ <text style="font-size: 9.5px; font-family: 'DejaVu Sans'" transform="translate(93.113672 215.655516)">(c) Coupled learning · 32 edges</text>
+ <text style="font-size: 9.5px; font-family: 'DejaVu Sans'" transform="translate(98.051445 226.293437)">100% of clean gap recovered</text>
+ </g>
+ </g>
+ <g id="axes_4">
+ <g id="patch_26">
+ <path d="M 312.160312 369.628437
+L 564.340312 369.628437
+L 564.340312 232.293437
+L 312.160312 232.293437
+z
+" style="fill: #ffffff"/>
+ </g>
+ <g id="patch_27">
+ <path d="M 323.62304 369.628437
+L 384.308067 369.628437
+L 384.308067 250.593974
+L 323.62304 250.593974
+z
+" clip-path="url(#p91f9cd916a)" style="fill: #222222; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="patch_28">
+ <path d="M 407.907799 369.628437
+L 468.592826 369.628437
+L 468.592826 282.984304
+L 407.907799 282.984304
+z
+" clip-path="url(#p91f9cd916a)" style="fill: #d55e00; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="patch_29">
+ <path d="M 492.192558 369.628437
+L 552.877585 369.628437
+L 552.877585 250.459014
+L 492.192558 250.459014
+z
+" clip-path="url(#p91f9cd916a)" style="fill: #0072b2; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="matplotlib.axis_7">
+ <g id="xtick_16">
+ <g id="line2d_58">
+ <g>
+ <use xlink:href="#m278102d266" x="353.965553" y="369.628437" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_33">
+ <text style="font-size: 7.8px; font-family: 'DejaVu Sans'; text-anchor: middle" x="353.965553" y="382.055219" transform="rotate(-0 353.965553 382.055219)">Clean</text>
+ </g>
+ </g>
+ <g id="xtick_17">
+ <g id="line2d_59">
+ <g>
+ <use xlink:href="#m278102d266" x="438.250312" y="369.628437" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_34">
+ <text style="font-size: 7.8px; font-family: 'DejaVu Sans'; text-anchor: middle" x="438.250312" y="382.055219" transform="rotate(-0 438.250312 382.055219)">Raw</text>
+ </g>
+ </g>
+ <g id="xtick_18">
+ <g id="line2d_60">
+ <g>
+ <use xlink:href="#m278102d266" x="522.535072" y="369.628437" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_35">
+ <text style="font-size: 7.8px; font-family: 'DejaVu Sans'; text-anchor: middle" x="522.535072" y="382.055219" transform="rotate(-0 522.535072 382.055219)">SDIL</text>
+ </g>
+ </g>
+ </g>
+ <g id="matplotlib.axis_8">
+ <g id="ytick_19">
+ <g id="line2d_61">
+ <path d="M 312.160312 369.628437
+L 564.340312 369.628437
+" clip-path="url(#p91f9cd916a)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_62">
+ <g>
+ <use xlink:href="#m33223f5472" x="312.160312" y="369.628437" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ </g>
+ <g id="ytick_20">
+ <g id="line2d_63">
+ <path d="M 312.160312 343.716173
+L 564.340312 343.716173
+" clip-path="url(#p91f9cd916a)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_64">
+ <g>
+ <use xlink:href="#m33223f5472" x="312.160312" y="343.716173" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ </g>
+ <g id="ytick_21">
+ <g id="line2d_65">
+ <path d="M 312.160312 317.803909
+L 564.340312 317.803909
+" clip-path="url(#p91f9cd916a)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_66">
+ <g>
+ <use xlink:href="#m33223f5472" x="312.160312" y="317.803909" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ </g>
+ <g id="ytick_22">
+ <g id="line2d_67">
+ <path d="M 312.160312 291.891645
+L 564.340312 291.891645
+" clip-path="url(#p91f9cd916a)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_68">
+ <g>
+ <use xlink:href="#m33223f5472" x="312.160312" y="291.891645" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ </g>
+ <g id="ytick_23">
+ <g id="line2d_69">
+ <path d="M 312.160312 265.979381
+L 564.340312 265.979381
+" clip-path="url(#p91f9cd916a)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_70">
+ <g>
+ <use xlink:href="#m33223f5472" x="312.160312" y="265.979381" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ </g>
+ <g id="ytick_24">
+ <g id="line2d_71">
+ <path d="M 312.160312 240.067117
+L 564.340312 240.067117
+" clip-path="url(#p91f9cd916a)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_72">
+ <g>
+ <use xlink:href="#m33223f5472" x="312.160312" y="240.067117" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ </g>
+ </g>
+ <g id="LineCollection_4">
+ <path d="M 353.965553 255.857403
+L 353.965553 246.140304
+" clip-path="url(#p91f9cd916a)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ <path d="M 438.250312 290.272129
+L 438.250312 275.426561
+" clip-path="url(#p91f9cd916a)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ <path d="M 522.535072 255.452524
+L 522.535072 246.140304
+" clip-path="url(#p91f9cd916a)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ </g>
+ <g id="line2d_73">
+ <g clip-path="url(#p91f9cd916a)">
+ <use xlink:href="#m23b9465fd3" x="353.965553" y="255.857403" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#m23b9465fd3" x="438.250312" y="290.272129" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#m23b9465fd3" x="522.535072" y="255.452524" style="fill: #1f77b4; stroke: #222222"/>
+ </g>
+ </g>
+ <g id="line2d_74">
+ <g clip-path="url(#p91f9cd916a)">
+ <use xlink:href="#m23b9465fd3" x="353.965553" y="246.140304" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#m23b9465fd3" x="438.250312" y="275.426561" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#m23b9465fd3" x="522.535072" y="246.140304" style="fill: #1f77b4; stroke: #222222"/>
+ </g>
+ </g>
+ <g id="patch_30">
+ <path d="M 312.160312 369.628437
+L 312.160312 232.293437
+" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/>
+ </g>
+ <g id="patch_31">
+ <path d="M 312.160312 369.628437
+L 564.340312 369.628437
+" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/>
+ </g>
+ <g id="text_36">
+ <text style="font-size: 9.5px; font-family: 'DejaVu Sans'" transform="translate(330.643516 215.655516)">(d) Overclamped coupled learning · 32 edges</text>
+ <text style="font-size: 9.5px; font-family: 'DejaVu Sans'" transform="translate(368.531445 226.293437)">100% of clean gap recovered</text>
+ </g>
+ </g>
+ <g id="text_37">
+ <text style="font-size: 7px; font-family: 'DejaVu Sans'; text-anchor: end; fill: #666666" x="570.640313" y="411.932656" transform="rotate(-0 570.640313 411.932656)">Dual Prop/EP: 5 seeds; coupled learners: 40 tasks × 3 device draws</text>
+ </g>
+ </g>
+ <defs>
+ <clipPath id="p7944c5556f">
+ <rect x="41.680312" y="31.056437" width="252.18" height="137.335"/>
+ </clipPath>
+ <clipPath id="p23da3bf74f">
+ <rect x="312.160312" y="31.056437" width="252.18" height="137.335"/>
+ </clipPath>
+ <clipPath id="p8d4a35fc37">
+ <rect x="41.680312" y="232.293437" width="252.18" height="137.335"/>
+ </clipPath>
+ <clipPath id="p91f9cd916a">
+ <rect x="312.160312" y="232.293437" width="252.18" height="137.335"/>
+ </clipPath>
+ </defs>
+</svg>
diff --git a/visual-composer/digital-additive-transfer.md b/visual-composer/digital-additive-transfer.md
new file mode 100644
index 0000000..74867f8
--- /dev/null
+++ b/visual-composer/digital-additive-transfer.md
@@ -0,0 +1,33 @@
+# Visual contract: additive digital transfer
+
+- **Artifact:** four-panel quantitative result figure.
+- **Target venue / format:** ICLR two-column paper, full-width figure.
+- **Core claim:** the same local instruction-off subtraction improves four
+ digital local-learning backbones under matched teaching-channel
+ imperfection.
+- **Reviewer question:** is SDIL a correction that transfers across learning
+ rules, or does it work only in one custom network?
+- **Evidence layer:** main digital transfer result.
+- **Source data:**
+ `results/contrastive_bias/c1_gate.json`,
+ `results/ep_bias/c1_gate.json`,
+ `results/coupled_ladder/p2_confirm_side4.json`, and
+ `results/coupled_ladder/p3_overclamp_side4.json`.
+- **Statistics / uncertainty:** Dual Propagation and EP resample five seeds;
+ CLLN panels average three device draws within each of 40 tasks and resample
+ tasks. Error bars are percentile 95% bootstrap intervals.
+- **Figure prototype:** coordinated 2-by-2 bar-chart small multiples.
+- **Panel map:** author-code Dual Propagation, author-code EP, digital coupled
+ learning, and digital overclamped coupled learning.
+- **Exact label inventory:** clean, same-RMS noise, raw, static calibration,
+ SDIL, oracle, task accuracy, clean gap recovered.
+- **Caption role:** define raw-to-clean gap recovery within each panel and
+ state that datasets and absolute clean levels differ across panels.
+- **Manuscript placement:** Part 1, after the algorithm definition.
+- **Output formats:** editable SVG, vector PDF, PNG preview, analysis JSON, and
+ source CSV.
+- **Traceability:** every bar is regenerated by
+ `experiments/plot_digital_additive_transfer.py` from the four JSON sources.
+- **Constraint:** the EP five-seed result beats raw in every seed and recovers
+ 96% of the mean clean gap, while its stricter preregistered gate failed. The
+ manuscript and caption must retain that distinction.
diff --git a/visual-composer/qa-ledger.md b/visual-composer/qa-ledger.md
index c9e66e5..f601f93 100644
--- a/visual-composer/qa-ledger.md
+++ b/visual-composer/qa-ledger.md
@@ -26,3 +26,17 @@ changing the plot grammar.
Rendered inspection: no clipped labels, tick-label collision, legend overlap,
or panel overlap at 2,560-by-765 PNG resolution. SVG text remains editable and
the PDF uses embedded TrueType fonts.
+
+## Additive digital transfer figure
+
+| Issue | Artifact | Severity | Fix | Status |
+|:--|:--|:--|:--|:--|
+| Different datasets invite invalid absolute comparisons | All panels | High | Used separate titled panels and defined recovery relative to each panel's own raw and clean endpoints | Resolved |
+| CLLN device draws are not independent tasks | Panels (c) and (d) | High | Averaged device draws within task before task-level bootstrap | Resolved |
+| EP result could be mistaken for a passed strict gate | Panel (b) | High | Stored gate status in analysis and required the caption/manifest to report the partial-gate boundary | Resolved |
+| Long calibration labels could collide | Panels (b) and (c) | Medium | Used two-line labels and inspected the full-resolution render | Resolved |
+| Color-only distinctions could fail in print | All panels | Low | Every bar has a direct method label | Resolved |
+
+Rendered inspection: no clipped labels, title collision, error-bar clipping,
+or panel overlap at 1,920-by-1,392 PNG resolution. SVG text remains editable
+and the PDF uses embedded TrueType fonts.