summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYurenHao0426 <Blackhao0426@gmail.com>2026-08-29 18:34:49 -0500
committerYurenHao0426 <Blackhao0426@gmail.com>2026-08-29 18:34:49 -0500
commit3562fd83ef8fc7f1ebede7571301224f2b5368c7 (patch)
tree7a9613117068ba2e9da6ae85c4eaf98376a12955
parent2509cbf0eed13ab1ab0eaf83a65b920384988d80 (diff)
figure: consolidate hardware-realistic CLLN evidence
-rw-r--r--experiments/plot_physical_hardware_evidence.py423
-rw-r--r--results/figs/figure_physical_hardware_evidence.pdfbin0 -> 19174 bytes
-rw-r--r--results/figs/figure_physical_hardware_evidence.pngbin0 -> 152686 bytes
-rw-r--r--results/figs/figure_physical_hardware_evidence.svg1082
-rw-r--r--results/physical_bias/p10_hardware_evidence_analysis.json308
-rw-r--r--results/physical_bias/p10_hardware_evidence_source.csv25
-rw-r--r--visual-composer/physical-hardware-evidence.md40
-rw-r--r--visual-composer/qa-ledger.md13
8 files changed, 1891 insertions, 0 deletions
diff --git a/experiments/plot_physical_hardware_evidence.py b/experiments/plot_physical_hardware_evidence.py
new file mode 100644
index 0000000..6a38b60
--- /dev/null
+++ b/experiments/plot_physical_hardware_evidence.py
@@ -0,0 +1,423 @@
+#!/usr/bin/env python3
+"""Build the hardware-realistic CLLN evidence figure from frozen results."""
+
+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
+
+
+METHODS = (
+ ("clean", "Clean", "#222222"),
+ ("raw", "Raw", "#D55E00"),
+ ("constant", "Static\ncalibration", "#777777"),
+ ("overclamp", "Overclamp", "#E69F00"),
+ ("sdil", "SDIL", "#0072B2"),
+ ("overclamp_sdil", "Overclamp\n+ SDIL", "#56B4E9"),
+)
+
+COMBINED_CONDITIONS = (
+ ("ideal_cds", "Ideal\nCDS"),
+ ("common_pedestal_10", "Common\npedestal"),
+ ("sample_noise_1", "Sample\nnoise"),
+ ("refresh_every_4", "Refresh\nevery 4"),
+ ("combined_mild_refresh4", "Mild\ncombined"),
+ ("combined_strong", "Strong\ncombined"),
+ ("overclamp_plus_combined_mild", "Overclamp\n+ mild"),
+)
+
+
+def parse_args() -> argparse.Namespace:
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "--physical",
+ type=Path,
+ default=Path(
+ "results/physical_bias/p5_full_grid_bias_crossover.json"),
+ )
+ parser.add_argument(
+ "--sampler",
+ type=Path,
+ default=Path(
+ "results/physical_bias/p9_grid_correlated_autozero.json"),
+ )
+ parser.add_argument(
+ "--spice",
+ type=Path,
+ default=Path(
+ "results/physical_bias/p8_spice_autozero_primitive.json"),
+ )
+ parser.add_argument(
+ "--output-analysis",
+ type=Path,
+ default=Path(
+ "results/physical_bias/p10_hardware_evidence_analysis.json"),
+ )
+ parser.add_argument(
+ "--output-csv",
+ type=Path,
+ default=Path(
+ "results/physical_bias/p10_hardware_evidence_source.csv"),
+ )
+ parser.add_argument(
+ "--output-figure",
+ type=Path,
+ default=Path("results/figs/figure_physical_hardware_evidence"),
+ )
+ parser.add_argument("--bootstrap-replicates", type=int, default=20000)
+ parser.add_argument("--bootstrap-seed", type=int, default=20260829)
+ return parser.parse_args()
+
+
+def clustered_summary(
+ records: list[dict],
+ value,
+ *,
+ rng: np.random.Generator,
+ replicates: int,
+) -> dict:
+ tasks = sorted({record["task_index"] for record in records})
+ task_means = np.asarray([
+ np.mean([value(record) for record in records
+ if record["task_index"] == task])
+ for task in tasks
+ ])
+ samples = rng.integers(
+ 0, len(task_means), size=(replicates, len(task_means)))
+ bootstrap = np.mean(task_means[samples], axis=1)
+ return {
+ "mean": float(np.mean(task_means)),
+ "task_bootstrap_95ci": [
+ float(bound) for bound in np.percentile(bootstrap, (2.5, 97.5))
+ ],
+ "task_clusters": len(tasks),
+ "trials": len(records),
+ }
+
+
+def physical_method_summaries(
+ report: dict, rng: np.random.Generator, replicates: int
+) -> dict:
+ output = {}
+ for method, _, _ in METHODS:
+ output[method] = clustered_summary(
+ report["records"],
+ lambda record, name=method: record["methods"][name][
+ "classification_error"],
+ rng=rng,
+ replicates=replicates,
+ )
+ return output
+
+
+def sampler_condition_summary(
+ report: dict,
+ condition: str,
+ rng: np.random.Generator,
+ replicates: int,
+) -> dict:
+ selected = [
+ record for record in report["records"]
+ if record["condition"] == condition
+ ]
+ summary = clustered_summary(
+ selected,
+ lambda record: record["classification_error"],
+ rng=rng,
+ replicates=replicates,
+ )
+ summary["zero_error_fraction"] = float(np.mean([
+ record["classification_error"] == 0.0 for record in selected
+ ]))
+ summary["solver_failure_fraction"] = float(np.mean([
+ record["status"] != "completed" for record in selected
+ ]))
+ finite_rmse = [
+ record["applied_rate_rmse_v_per_s"] for record in selected
+ if record["applied_rate_rmse_v_per_s"] is not None
+ ]
+ summary["median_applied_rate_rmse_v_per_s"] = float(np.median([
+ value for value in finite_rmse
+ ]))
+ return summary
+
+
+def build_analysis(
+ physical: dict,
+ sampler: dict,
+ spice: dict,
+ *,
+ replicates: int,
+ seed: int,
+) -> dict:
+ rng = np.random.default_rng(seed)
+ sampler_conditions = {
+ condition["name"] for condition in sampler["protocol"]["conditions"]
+ }
+ required_conditions = {
+ name for name, _ in COMBINED_CONDITIONS
+ } | {
+ f"pedestal_mismatch_{value:g}"
+ for value in (0.01, 0.025, 0.05, 0.1, 0.25, 0.5)
+ } | {
+ f"gain_mismatch_{value:g}"
+ for value in (0.001, 0.005, 0.01, 0.025, 0.05)
+ }
+ missing = required_conditions - sampler_conditions
+ if missing:
+ raise ValueError(f"sampler report is missing {sorted(missing)}")
+ condition_summaries = {
+ condition: sampler_condition_summary(
+ sampler, condition, rng, replicates)
+ for condition in sorted(required_conditions)
+ }
+ return {
+ "analysis": "physical_clln_hardware_evidence_figure",
+ "confirmatory": False,
+ "bootstrap": {
+ "unit": "task; four device draws averaged within task",
+ "task_clusters": 40,
+ "replicates": replicates,
+ "seed": seed,
+ "interval": "percentile 95%",
+ },
+ "physical_methods": physical_method_summaries(
+ physical, rng, replicates),
+ "sampler_conditions": condition_summaries,
+ "spice_primitive": {
+ "publication_evidence": spice["publication_evidence"],
+ "scope": spice["scope"],
+ "configuration_count": spice["configuration_count"],
+ "fraction_below_1_percent_error_at_start": spice[
+ "fraction_below_1_percent_error_at_start"],
+ "fraction_below_1_percent_error_at_end": spice[
+ "fraction_below_1_percent_error_at_end"],
+ "reference_configuration": spice["reference_configuration"],
+ },
+ }
+
+
+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", "series", "condition", "x", "x_unit", "mean_error",
+ "ci_low", "ci_high", "zero_error_fraction",
+ ))
+ for method, label, _ in METHODS:
+ summary = analysis["physical_methods"][method]
+ writer.writerow((
+ "a", "physical_method", label.replace("\n", " "), "", "",
+ summary["mean"], *summary["task_bootstrap_95ci"], "",
+ ))
+ for family, values in (
+ ("pedestal mismatch", (0.01, 0.025, 0.05, 0.1, 0.25, 0.5)),
+ ("gain mismatch", (0.001, 0.005, 0.01, 0.025, 0.05)),
+ ):
+ prefix = family.replace(" ", "_")
+ for value in values:
+ summary = analysis["sampler_conditions"][f"{prefix}_{value:g}"]
+ writer.writerow((
+ "b", family, f"{prefix}_{value:g}",
+ summary["median_applied_rate_rmse_v_per_s"], "V/s",
+ summary["mean"], *summary["task_bootstrap_95ci"],
+ summary["zero_error_fraction"],
+ ))
+ for condition, label in COMBINED_CONDITIONS:
+ summary = analysis["sampler_conditions"][condition]
+ writer.writerow((
+ "c", "sampling condition", label.replace("\n", " "), "", "",
+ summary["mean"], *summary["task_bootstrap_95ci"],
+ summary["zero_error_fraction"],
+ ))
+
+
+def errorbar(axis, x, summaries, **kwargs) -> None:
+ means = np.asarray([summary["mean"] for summary in summaries]) * 100.0
+ intervals = np.asarray([
+ summary["task_bootstrap_95ci"] for summary in summaries
+ ]) * 100.0
+ axis.errorbar(
+ x,
+ means,
+ yerr=np.vstack((means - intervals[:, 0], intervals[:, 1] - means)),
+ capsize=2.2,
+ **kwargs,
+ )
+
+
+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,
+ "legend.fontsize": 7.5,
+ "xtick.labelsize": 7.7,
+ "ytick.labelsize": 8,
+ "axes.spines.top": False,
+ "axes.spines.right": False,
+ "svg.fonttype": "none",
+ "pdf.fonttype": 42,
+ "figure.facecolor": "white",
+ "axes.facecolor": "white",
+ })
+ figure, axes = plt.subplots(1, 3, figsize=(10.7, 3.15))
+
+ method_summaries = [
+ analysis["physical_methods"][method] for method, _, _ in METHODS
+ ]
+ method_means = np.asarray([
+ summary["mean"] for summary in method_summaries]) * 100.0
+ method_intervals = np.asarray([
+ summary["task_bootstrap_95ci"] for summary in method_summaries
+ ]) * 100.0
+ positions = np.arange(len(METHODS))[::-1]
+ axes[0].barh(
+ positions,
+ method_means,
+ color=[color for _, _, color in METHODS],
+ height=0.70,
+ edgecolor="white",
+ linewidth=0.4,
+ )
+ axes[0].errorbar(
+ method_means,
+ positions,
+ xerr=np.vstack((
+ method_means - method_intervals[:, 0],
+ method_intervals[:, 1] - method_means,
+ )),
+ fmt="none",
+ ecolor="#222222",
+ elinewidth=0.8,
+ capsize=2.2,
+ )
+ axes[0].set_yticks(
+ positions, [label.replace("\n", " ") for _, label, _ in METHODS])
+ axes[0].set_xlabel("Final classification error (%)")
+ axes[0].set_title("(a) Nonlinear CLLN with component errors")
+
+ sweep_specs = (
+ ("pedestal_mismatch", (0.01, 0.025, 0.05, 0.1, 0.25, 0.5),
+ "Pedestal mismatch", "#D55E00", "o"),
+ ("gain_mismatch", (0.001, 0.005, 0.01, 0.025, 0.05),
+ "Gain mismatch", "#0072B2", "D"),
+ )
+ for prefix, values, label, color, marker in sweep_specs:
+ summaries = [
+ analysis["sampler_conditions"][f"{prefix}_{value:g}"]
+ for value in values
+ ]
+ x = np.asarray([
+ summary["median_applied_rate_rmse_v_per_s"]
+ for summary in summaries
+ ])
+ errorbar(
+ axes[1], x, summaries, color=color, marker=marker,
+ markersize=4.5, linewidth=1.35, label=label,
+ )
+ axes[1].set_xscale("log")
+ axes[1].set_xlabel("Residual sampling error (V/s, RMSE)")
+ axes[1].set_ylabel("Final classification error (%)")
+ axes[1].set_title("(b) Local sample-path mismatch")
+ axes[1].legend(frameon=False, loc="upper left")
+
+ combined = [
+ analysis["sampler_conditions"][condition]
+ for condition, _ in COMBINED_CONDITIONS
+ ]
+ combined_means = np.asarray([
+ summary["mean"] for summary in combined]) * 100.0
+ combined_intervals = np.asarray([
+ summary["task_bootstrap_95ci"] for summary in combined
+ ]) * 100.0
+ positions = np.arange(len(COMBINED_CONDITIONS))[::-1]
+ axes[2].barh(
+ positions,
+ combined_means,
+ color="#0072B2",
+ height=0.70,
+ edgecolor="white",
+ linewidth=0.4,
+ )
+ axes[2].errorbar(
+ combined_means,
+ positions,
+ xerr=np.vstack((
+ combined_means - combined_intervals[:, 0],
+ combined_intervals[:, 1] - combined_means,
+ )),
+ fmt="none",
+ ecolor="#222222",
+ elinewidth=0.8,
+ capsize=2.2,
+ )
+ axes[2].set_yticks(
+ positions,
+ [label.replace("\n", " ") for _, label in COMBINED_CONDITIONS],
+ )
+ axes[2].set_xlabel("Final classification error (%)")
+ axes[2].set_title("(c) Nonideal local sampling")
+
+ axes[0].set_xlim(-0.8, 32.0)
+ axes[1].set_ylim(-0.8, 32.0)
+ axes[2].set_xlim(-0.8, 32.0)
+ for index, axis in enumerate(axes):
+ axis.grid(
+ axis="y" if index == 1 else "x",
+ color="#D9D9D9", linewidth=0.55, alpha=0.8)
+ axis.tick_params(length=3)
+ figure.text(
+ 0.995,
+ 0.005,
+ "40 tasks × 4 device draws; error bars are task-bootstrap 95% intervals",
+ ha="right",
+ va="bottom",
+ fontsize=7,
+ color="#666666",
+ )
+ figure.tight_layout(rect=(0.0, 0.065, 1.0, 1.0), w_pad=2.0)
+ 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()
+ physical = json.loads(args.physical.read_text())
+ sampler = json.loads(args.sampler.read_text())
+ spice = json.loads(args.spice.read_text())
+ analysis = build_analysis(
+ physical,
+ sampler,
+ spice,
+ replicates=args.bootstrap_replicates,
+ seed=args.bootstrap_seed,
+ )
+ analysis["sources"] = [
+ str(args.physical), str(args.sampler), str(args.spice)]
+ 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({
+ "physical_methods": analysis["physical_methods"],
+ "combined_strong": analysis["sampler_conditions"]["combined_strong"],
+ }, 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/figs/figure_physical_hardware_evidence.pdf b/results/figs/figure_physical_hardware_evidence.pdf
new file mode 100644
index 0000000..6e3aeac
--- /dev/null
+++ b/results/figs/figure_physical_hardware_evidence.pdf
Binary files differ
diff --git a/results/figs/figure_physical_hardware_evidence.png b/results/figs/figure_physical_hardware_evidence.png
new file mode 100644
index 0000000..d78f8ea
--- /dev/null
+++ b/results/figs/figure_physical_hardware_evidence.png
Binary files differ
diff --git a/results/figs/figure_physical_hardware_evidence.svg b/results/figs/figure_physical_hardware_evidence.svg
new file mode 100644
index 0000000..9efa4a8
--- /dev/null
+++ b/results/figs/figure_physical_hardware_evidence.svg
@@ -0,0 +1,1082 @@
+<?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="771.54175pt" height="230.904516pt" viewBox="0 0 771.54175 230.904516" 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:34:08.451813</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 230.904516
+L 771.54175 230.904516
+L 771.54175 0
+L 0 0
+z
+" style="fill: #ffffff"/>
+ </g>
+ <g id="axes_1">
+ <g id="patch_2">
+ <path d="M 87.00375 171.120516
+L 246.560417 171.120516
+L 246.560417 20.418516
+L 87.00375 20.418516
+z
+" style="fill: #ffffff"/>
+ </g>
+ <g id="patch_3">
+ <path d="M 90.895376 44.093391
+L 90.895376 44.093391
+L 90.895376 27.268607
+L 90.895376 27.268607
+z
+" clip-path="url(#pf3ff1facc5)" style="fill: #222222; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="patch_4">
+ <path d="M 90.895376 68.128798
+L 216.689147 68.128798
+L 216.689147 51.304013
+L 90.895376 51.304013
+z
+" clip-path="url(#pf3ff1facc5)" style="fill: #d55e00; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="patch_5">
+ <path d="M 90.895376 92.164205
+L 109.137373 92.164205
+L 109.137373 75.33942
+L 90.895376 75.33942
+z
+" clip-path="url(#pf3ff1facc5)" style="fill: #777777; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="patch_6">
+ <path d="M 90.895376 116.199611
+L 114.077914 116.199611
+L 114.077914 99.374827
+L 90.895376 99.374827
+z
+" clip-path="url(#pf3ff1facc5)" style="fill: #e69f00; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="patch_7">
+ <path d="M 90.895376 140.235018
+L 90.895376 140.235018
+L 90.895376 123.410233
+L 90.895376 123.410233
+z
+" clip-path="url(#pf3ff1facc5)" style="fill: #0072b2; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="patch_8">
+ <path d="M 90.895376 164.270425
+L 90.895376 164.270425
+L 90.895376 147.44564
+L 90.895376 147.44564
+z
+" clip-path="url(#pf3ff1facc5)" style="fill: #56b4e9; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="matplotlib.axis_1">
+ <g id="xtick_1">
+ <g id="line2d_1">
+ <path d="M 90.895376 171.120516
+L 90.895376 20.418516
+" clip-path="url(#pf3ff1facc5)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_2">
+ <defs>
+ <path id="m5974431c5e" d="M 0 0
+L 0 3
+" style="stroke: #000000; stroke-width: 0.8"/>
+ </defs>
+ <g>
+ <use xlink:href="#m5974431c5e" x="90.895376" y="171.120516" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_1">
+ <text style="font-size: 7.7px; font-family: 'DejaVu Sans'; text-anchor: middle" x="90.895376" y="183.471313" transform="rotate(-0 90.895376 183.471313)">0</text>
+ </g>
+ </g>
+ <g id="xtick_2">
+ <g id="line2d_3">
+ <path d="M 139.540701 171.120516
+L 139.540701 20.418516
+" clip-path="url(#pf3ff1facc5)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_4">
+ <g>
+ <use xlink:href="#m5974431c5e" x="139.540701" y="171.120516" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_2">
+ <text style="font-size: 7.7px; font-family: 'DejaVu Sans'; text-anchor: middle" x="139.540701" y="183.471313" transform="rotate(-0 139.540701 183.471313)">10</text>
+ </g>
+ </g>
+ <g id="xtick_3">
+ <g id="line2d_5">
+ <path d="M 188.186026 171.120516
+L 188.186026 20.418516
+" clip-path="url(#pf3ff1facc5)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_6">
+ <g>
+ <use xlink:href="#m5974431c5e" x="188.186026" y="171.120516" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_3">
+ <text style="font-size: 7.7px; font-family: 'DejaVu Sans'; text-anchor: middle" x="188.186026" y="183.471313" transform="rotate(-0 188.186026 183.471313)">20</text>
+ </g>
+ </g>
+ <g id="xtick_4">
+ <g id="line2d_7">
+ <path d="M 236.831352 171.120516
+L 236.831352 20.418516
+" clip-path="url(#pf3ff1facc5)" 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="#m5974431c5e" x="236.831352" y="171.120516" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_4">
+ <text style="font-size: 7.7px; font-family: 'DejaVu Sans'; text-anchor: middle" x="236.831352" y="183.471313" transform="rotate(-0 236.831352 183.471313)">30</text>
+ </g>
+ </g>
+ <g id="text_5">
+ <text style="font-size: 9px; font-family: 'DejaVu Sans'; text-anchor: middle" x="166.782083" y="195.911266" transform="rotate(-0 166.782083 195.911266)">Final classification error (%)</text>
+ </g>
+ </g>
+ <g id="matplotlib.axis_2">
+ <g id="ytick_1">
+ <g id="line2d_9">
+ <defs>
+ <path id="m2ade93fa71" d="M 0 0
+L -3 0
+" style="stroke: #000000; stroke-width: 0.8"/>
+ </defs>
+ <g>
+ <use xlink:href="#m2ade93fa71" x="87.00375" y="35.680999" 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="80.50375" y="38.720374" transform="rotate(-0 80.50375 38.720374)">Clean</text>
+ </g>
+ </g>
+ <g id="ytick_2">
+ <g id="line2d_10">
+ <g>
+ <use xlink:href="#m2ade93fa71" x="87.00375" y="59.716406" 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="80.50375" y="62.755781" transform="rotate(-0 80.50375 62.755781)">Raw</text>
+ </g>
+ </g>
+ <g id="ytick_3">
+ <g id="line2d_11">
+ <g>
+ <use xlink:href="#m2ade93fa71" x="87.00375" y="83.751812" 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="80.50375" y="86.791187" transform="rotate(-0 80.50375 86.791187)">Static calibration</text>
+ </g>
+ </g>
+ <g id="ytick_4">
+ <g id="line2d_12">
+ <g>
+ <use xlink:href="#m2ade93fa71" x="87.00375" y="107.787219" 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="80.50375" y="110.826594" transform="rotate(-0 80.50375 110.826594)">Overclamp</text>
+ </g>
+ </g>
+ <g id="ytick_5">
+ <g id="line2d_13">
+ <g>
+ <use xlink:href="#m2ade93fa71" x="87.00375" y="131.822626" 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="80.50375" y="134.862001" transform="rotate(-0 80.50375 134.862001)">SDIL</text>
+ </g>
+ </g>
+ <g id="ytick_6">
+ <g id="line2d_14">
+ <g>
+ <use xlink:href="#m2ade93fa71" x="87.00375" y="155.858032" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_11">
+ <text style="font-size: 8px; font-family: 'DejaVu Sans'; text-anchor: end" x="80.50375" y="158.897407" transform="rotate(-0 80.50375 158.897407)">Overclamp + SDIL</text>
+ </g>
+ </g>
+ </g>
+ <g id="LineCollection_1">
+ <path d="M 90.895376 35.680999
+L 90.895376 35.680999
+" clip-path="url(#pf3ff1facc5)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ <path d="M 199.207233 59.716406
+L 234.931144 59.716406
+" clip-path="url(#pf3ff1facc5)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ <path d="M 100.016374 83.751812
+L 119.398496 83.751812
+" clip-path="url(#pf3ff1facc5)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ <path d="M 100.016374 107.787219
+L 131.179786 107.787219
+" clip-path="url(#pf3ff1facc5)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ <path d="M 90.895376 131.822626
+L 90.895376 131.822626
+" clip-path="url(#pf3ff1facc5)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ <path d="M 90.895376 155.858032
+L 90.895376 155.858032
+" clip-path="url(#pf3ff1facc5)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ </g>
+ <g id="line2d_15">
+ <defs>
+ <path id="mc120802246" d="M 0 2.2
+L 0 -2.2
+" style="stroke: #222222"/>
+ </defs>
+ <g clip-path="url(#pf3ff1facc5)">
+ <use xlink:href="#mc120802246" x="90.895376" y="35.680999" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#mc120802246" x="199.207233" y="59.716406" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#mc120802246" x="100.016374" y="83.751812" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#mc120802246" x="100.016374" y="107.787219" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#mc120802246" x="90.895376" y="131.822626" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#mc120802246" x="90.895376" y="155.858032" style="fill: #1f77b4; stroke: #222222"/>
+ </g>
+ </g>
+ <g id="line2d_16">
+ <g clip-path="url(#pf3ff1facc5)">
+ <use xlink:href="#mc120802246" x="90.895376" y="35.680999" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#mc120802246" x="234.931144" y="59.716406" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#mc120802246" x="119.398496" y="83.751812" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#mc120802246" x="131.179786" y="107.787219" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#mc120802246" x="90.895376" y="131.822626" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#mc120802246" x="90.895376" y="155.858032" style="fill: #1f77b4; stroke: #222222"/>
+ </g>
+ </g>
+ <g id="patch_9">
+ <path d="M 87.00375 171.120516
+L 87.00375 20.418516
+" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/>
+ </g>
+ <g id="patch_10">
+ <path d="M 87.00375 171.120516
+L 246.560417 171.120516
+" 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'; text-anchor: middle" x="166.782083" y="14.418516" transform="rotate(-0 166.782083 14.418516)">(a) Nonlinear CLLN with component errors</text>
+ </g>
+ </g>
+ <g id="axes_2">
+ <g id="patch_11">
+ <path d="M 343.230417 171.120516
+L 502.787083 171.120516
+L 502.787083 20.418516
+L 343.230417 20.418516
+z
+" style="fill: #ffffff"/>
+ </g>
+ <g id="matplotlib.axis_3">
+ <g id="xtick_5">
+ <g id="line2d_17">
+ <g>
+ <use xlink:href="#m5974431c5e" x="391.550804" y="171.120516" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_13">
+ <!-- $\mathdefault{10^{-2}}$ -->
+ <g transform="translate(382.503304 183.471313)">
+ <text>
+ <tspan x="0" y="-0.78125" style="font-size: 7.7px; font-family: 'DejaVu Sans'">1</tspan>
+ <tspan x="4.930786" y="-0.78125" style="font-size: 7.7px; font-family: 'DejaVu Sans'">0</tspan>
+ <tspan x="9.938135" y="-3.84375" style="font-size: 5.39px; font-family: 'DejaVu Sans'">−</tspan>
+ <tspan x="14.483691" y="-3.84375" style="font-size: 5.39px; font-family: 'DejaVu Sans'">2</tspan>
+ </text>
+ </g>
+ </g>
+ </g>
+ <g id="xtick_6">
+ <g id="line2d_18">
+ <g>
+ <use xlink:href="#m5974431c5e" x="452.749266" y="171.120516" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_14">
+ <!-- $\mathdefault{10^{-1}}$ -->
+ <g transform="translate(443.701766 183.471313)">
+ <text>
+ <tspan x="0" y="-0.857813" style="font-size: 7.7px; font-family: 'DejaVu Sans'">1</tspan>
+ <tspan x="4.930786" y="-0.857813" style="font-size: 7.7px; font-family: 'DejaVu Sans'">0</tspan>
+ <tspan x="9.938135" y="-3.920313" style="font-size: 5.39px; font-family: 'DejaVu Sans'">−</tspan>
+ <tspan x="14.483691" y="-3.920313" style="font-size: 5.39px; font-family: 'DejaVu Sans'">1</tspan>
+ </text>
+ </g>
+ </g>
+ </g>
+ <g id="xtick_7">
+ <g id="line2d_19">
+ <defs>
+ <path id="m1f6c4797ec" d="M 0 0
+L 0 2
+" style="stroke: #000000; stroke-width: 0.6"/>
+ </defs>
+ <g>
+ <use xlink:href="#m1f6c4797ec" x="348.774916" y="171.120516" style="stroke: #000000; stroke-width: 0.6"/>
+ </g>
+ </g>
+ </g>
+ <g id="xtick_8">
+ <g id="line2d_20">
+ <g>
+ <use xlink:href="#m1f6c4797ec" x="359.55143" y="171.120516" style="stroke: #000000; stroke-width: 0.6"/>
+ </g>
+ </g>
+ </g>
+ <g id="xtick_9">
+ <g id="line2d_21">
+ <g>
+ <use xlink:href="#m1f6c4797ec" x="367.197488" y="171.120516" style="stroke: #000000; stroke-width: 0.6"/>
+ </g>
+ </g>
+ </g>
+ <g id="xtick_10">
+ <g id="line2d_22">
+ <g>
+ <use xlink:href="#m1f6c4797ec" x="373.128232" y="171.120516" style="stroke: #000000; stroke-width: 0.6"/>
+ </g>
+ </g>
+ </g>
+ <g id="xtick_11">
+ <g id="line2d_23">
+ <g>
+ <use xlink:href="#m1f6c4797ec" x="377.974002" y="171.120516" style="stroke: #000000; stroke-width: 0.6"/>
+ </g>
+ </g>
+ </g>
+ <g id="xtick_12">
+ <g id="line2d_24">
+ <g>
+ <use xlink:href="#m1f6c4797ec" x="382.071043" y="171.120516" style="stroke: #000000; stroke-width: 0.6"/>
+ </g>
+ </g>
+ </g>
+ <g id="xtick_13">
+ <g id="line2d_25">
+ <g>
+ <use xlink:href="#m1f6c4797ec" x="385.620061" y="171.120516" style="stroke: #000000; stroke-width: 0.6"/>
+ </g>
+ </g>
+ </g>
+ <g id="xtick_14">
+ <g id="line2d_26">
+ <g>
+ <use xlink:href="#m1f6c4797ec" x="388.750516" y="171.120516" style="stroke: #000000; stroke-width: 0.6"/>
+ </g>
+ </g>
+ </g>
+ <g id="xtick_15">
+ <g id="line2d_27">
+ <g>
+ <use xlink:href="#m1f6c4797ec" x="409.973377" y="171.120516" style="stroke: #000000; stroke-width: 0.6"/>
+ </g>
+ </g>
+ </g>
+ <g id="xtick_16">
+ <g id="line2d_28">
+ <g>
+ <use xlink:href="#m1f6c4797ec" x="420.749891" y="171.120516" style="stroke: #000000; stroke-width: 0.6"/>
+ </g>
+ </g>
+ </g>
+ <g id="xtick_17">
+ <g id="line2d_29">
+ <g>
+ <use xlink:href="#m1f6c4797ec" x="428.39595" y="171.120516" style="stroke: #000000; stroke-width: 0.6"/>
+ </g>
+ </g>
+ </g>
+ <g id="xtick_18">
+ <g id="line2d_30">
+ <g>
+ <use xlink:href="#m1f6c4797ec" x="434.326693" y="171.120516" style="stroke: #000000; stroke-width: 0.6"/>
+ </g>
+ </g>
+ </g>
+ <g id="xtick_19">
+ <g id="line2d_31">
+ <g>
+ <use xlink:href="#m1f6c4797ec" x="439.172464" y="171.120516" style="stroke: #000000; stroke-width: 0.6"/>
+ </g>
+ </g>
+ </g>
+ <g id="xtick_20">
+ <g id="line2d_32">
+ <g>
+ <use xlink:href="#m1f6c4797ec" x="443.269504" y="171.120516" style="stroke: #000000; stroke-width: 0.6"/>
+ </g>
+ </g>
+ </g>
+ <g id="xtick_21">
+ <g id="line2d_33">
+ <g>
+ <use xlink:href="#m1f6c4797ec" x="446.818522" y="171.120516" style="stroke: #000000; stroke-width: 0.6"/>
+ </g>
+ </g>
+ </g>
+ <g id="xtick_22">
+ <g id="line2d_34">
+ <g>
+ <use xlink:href="#m1f6c4797ec" x="449.948978" y="171.120516" style="stroke: #000000; stroke-width: 0.6"/>
+ </g>
+ </g>
+ </g>
+ <g id="xtick_23">
+ <g id="line2d_35">
+ <g>
+ <use xlink:href="#m1f6c4797ec" x="471.171838" y="171.120516" style="stroke: #000000; stroke-width: 0.6"/>
+ </g>
+ </g>
+ </g>
+ <g id="xtick_24">
+ <g id="line2d_36">
+ <g>
+ <use xlink:href="#m1f6c4797ec" x="481.948352" y="171.120516" style="stroke: #000000; stroke-width: 0.6"/>
+ </g>
+ </g>
+ </g>
+ <g id="xtick_25">
+ <g id="line2d_37">
+ <g>
+ <use xlink:href="#m1f6c4797ec" x="489.594411" y="171.120516" style="stroke: #000000; stroke-width: 0.6"/>
+ </g>
+ </g>
+ </g>
+ <g id="xtick_26">
+ <g id="line2d_38">
+ <g>
+ <use xlink:href="#m1f6c4797ec" x="495.525155" y="171.120516" style="stroke: #000000; stroke-width: 0.6"/>
+ </g>
+ </g>
+ </g>
+ <g id="xtick_27">
+ <g id="line2d_39">
+ <g>
+ <use xlink:href="#m1f6c4797ec" x="500.370925" y="171.120516" style="stroke: #000000; stroke-width: 0.6"/>
+ </g>
+ </g>
+ </g>
+ <g id="text_15">
+ <text style="font-size: 9px; font-family: 'DejaVu Sans'; text-anchor: middle" x="423.00875" y="195.911266" transform="rotate(-0 423.00875 195.911266)">Residual sampling error (V/s, RMSE)</text>
+ </g>
+ </g>
+ <g id="matplotlib.axis_4">
+ <g id="ytick_7">
+ <g id="line2d_40">
+ <path d="M 343.230417 167.444857
+L 502.787083 167.444857
+" clip-path="url(#p800f26f182)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_41">
+ <g>
+ <use xlink:href="#m2ade93fa71" x="343.230417" y="167.444857" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_16">
+ <text style="font-size: 8px; font-family: 'DejaVu Sans'; text-anchor: end" x="336.730417" y="170.484232" transform="rotate(-0 336.730417 170.484232)">0</text>
+ </g>
+ </g>
+ <g id="ytick_8">
+ <g id="line2d_42">
+ <path d="M 343.230417 144.471991
+L 502.787083 144.471991
+" clip-path="url(#p800f26f182)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_43">
+ <g>
+ <use xlink:href="#m2ade93fa71" x="343.230417" y="144.471991" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_17">
+ <text style="font-size: 8px; font-family: 'DejaVu Sans'; text-anchor: end" x="336.730417" y="147.511366" transform="rotate(-0 336.730417 147.511366)">5</text>
+ </g>
+ </g>
+ <g id="ytick_9">
+ <g id="line2d_44">
+ <path d="M 343.230417 121.499125
+L 502.787083 121.499125
+" clip-path="url(#p800f26f182)" 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="#m2ade93fa71" x="343.230417" y="121.499125" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_18">
+ <text style="font-size: 8px; font-family: 'DejaVu Sans'; text-anchor: end" x="336.730417" y="124.5385" transform="rotate(-0 336.730417 124.5385)">10</text>
+ </g>
+ </g>
+ <g id="ytick_10">
+ <g id="line2d_46">
+ <path d="M 343.230417 98.52626
+L 502.787083 98.52626
+" clip-path="url(#p800f26f182)" 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="#m2ade93fa71" x="343.230417" y="98.52626" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_19">
+ <text style="font-size: 8px; font-family: 'DejaVu Sans'; text-anchor: end" x="336.730417" y="101.565635" transform="rotate(-0 336.730417 101.565635)">15</text>
+ </g>
+ </g>
+ <g id="ytick_11">
+ <g id="line2d_48">
+ <path d="M 343.230417 75.553394
+L 502.787083 75.553394
+" clip-path="url(#p800f26f182)" 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="#m2ade93fa71" x="343.230417" y="75.553394" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_20">
+ <text style="font-size: 8px; font-family: 'DejaVu Sans'; text-anchor: end" x="336.730417" y="78.592769" transform="rotate(-0 336.730417 78.592769)">20</text>
+ </g>
+ </g>
+ <g id="ytick_12">
+ <g id="line2d_50">
+ <path d="M 343.230417 52.580528
+L 502.787083 52.580528
+" clip-path="url(#p800f26f182)" 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="#m2ade93fa71" x="343.230417" y="52.580528" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_21">
+ <text style="font-size: 8px; font-family: 'DejaVu Sans'; text-anchor: end" x="336.730417" y="55.619903" transform="rotate(-0 336.730417 55.619903)">25</text>
+ </g>
+ </g>
+ <g id="ytick_13">
+ <g id="line2d_52">
+ <path d="M 343.230417 29.607662
+L 502.787083 29.607662
+" clip-path="url(#p800f26f182)" 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="#m2ade93fa71" x="343.230417" y="29.607662" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_22">
+ <text style="font-size: 8px; font-family: 'DejaVu Sans'; text-anchor: end" x="336.730417" y="32.647037" transform="rotate(-0 336.730417 32.647037)">30</text>
+ </g>
+ </g>
+ <g id="text_23">
+ <text style="font-size: 9px; font-family: 'DejaVu Sans'; text-anchor: middle" x="320.678698" y="95.769516" transform="rotate(-90 320.678698 95.769516)">Final classification error (%)</text>
+ </g>
+ </g>
+ <g id="LineCollection_2">
+ <path d="M 391.560157 167.444857
+L 391.560157 167.444857
+" clip-path="url(#p800f26f182)" style="fill: none; stroke: #d55e00; stroke-width: 1.35"/>
+ <path d="M 415.913474 167.444857
+L 415.913474 167.444857
+" clip-path="url(#p800f26f182)" style="fill: none; stroke: #d55e00; stroke-width: 1.35"/>
+ <path d="M 434.336046 167.444857
+L 434.336046 167.444857
+" clip-path="url(#p800f26f182)" style="fill: none; stroke: #d55e00; stroke-width: 1.35"/>
+ <path d="M 452.758619 166.726955
+L 452.758619 161.701641
+" clip-path="url(#p800f26f182)" style="fill: none; stroke: #d55e00; stroke-width: 1.35"/>
+ <path d="M 477.111935 158.11213
+L 477.111935 133.344509
+" clip-path="url(#p800f26f182)" style="fill: none; stroke: #d55e00; stroke-width: 1.35"/>
+ <path d="M 495.534508 140.164579
+L 495.534508 103.910525
+" clip-path="url(#p800f26f182)" style="fill: none; stroke: #d55e00; stroke-width: 1.35"/>
+ </g>
+ <g id="line2d_54">
+ <defs>
+ <path id="m12af69966a" d="M 2.2 0
+L -2.2 -0
+" style="stroke: #d55e00"/>
+ </defs>
+ <g clip-path="url(#p800f26f182)">
+ <use xlink:href="#m12af69966a" x="391.560157" y="167.444857" style="fill: #d55e00; stroke: #d55e00"/>
+ <use xlink:href="#m12af69966a" x="415.913474" y="167.444857" style="fill: #d55e00; stroke: #d55e00"/>
+ <use xlink:href="#m12af69966a" x="434.336046" y="167.444857" style="fill: #d55e00; stroke: #d55e00"/>
+ <use xlink:href="#m12af69966a" x="452.758619" y="166.726955" style="fill: #d55e00; stroke: #d55e00"/>
+ <use xlink:href="#m12af69966a" x="477.111935" y="158.11213" style="fill: #d55e00; stroke: #d55e00"/>
+ <use xlink:href="#m12af69966a" x="495.534508" y="140.164579" style="fill: #d55e00; stroke: #d55e00"/>
+ </g>
+ </g>
+ <g id="line2d_55">
+ <g clip-path="url(#p800f26f182)">
+ <use xlink:href="#m12af69966a" x="391.560157" y="167.444857" style="fill: #d55e00; stroke: #d55e00"/>
+ <use xlink:href="#m12af69966a" x="415.913474" y="167.444857" style="fill: #d55e00; stroke: #d55e00"/>
+ <use xlink:href="#m12af69966a" x="434.336046" y="167.444857" style="fill: #d55e00; stroke: #d55e00"/>
+ <use xlink:href="#m12af69966a" x="452.758619" y="161.701641" style="fill: #d55e00; stroke: #d55e00"/>
+ <use xlink:href="#m12af69966a" x="477.111935" y="133.344509" style="fill: #d55e00; stroke: #d55e00"/>
+ <use xlink:href="#m12af69966a" x="495.534508" y="103.910525" style="fill: #d55e00; stroke: #d55e00"/>
+ </g>
+ </g>
+ <g id="LineCollection_3">
+ <path d="M 350.482992 167.444857
+L 350.482992 167.444857
+" clip-path="url(#p800f26f182)" style="fill: none; stroke: #0072b2; stroke-width: 1.35"/>
+ <path d="M 393.255515 167.444857
+L 393.255515 167.444857
+" clip-path="url(#p800f26f182)" style="fill: none; stroke: #0072b2; stroke-width: 1.35"/>
+ <path d="M 411.690885 167.444857
+L 411.690885 167.444857
+" clip-path="url(#p800f26f182)" style="fill: none; stroke: #0072b2; stroke-width: 1.35"/>
+ <path d="M 436.061444 167.444857
+L 436.061444 165.650102
+" clip-path="url(#p800f26f182)" style="fill: none; stroke: #0072b2; stroke-width: 1.35"/>
+ <path d="M 454.660843 167.085906
+L 454.660843 161.34269
+" clip-path="url(#p800f26f182)" style="fill: none; stroke: #0072b2; stroke-width: 1.35"/>
+ </g>
+ <g id="line2d_56">
+ <defs>
+ <path id="m529fa2b800" d="M 2.2 0
+L -2.2 -0
+" style="stroke: #0072b2"/>
+ </defs>
+ <g clip-path="url(#p800f26f182)">
+ <use xlink:href="#m529fa2b800" x="350.482992" y="167.444857" style="fill: #0072b2; stroke: #0072b2"/>
+ <use xlink:href="#m529fa2b800" x="393.255515" y="167.444857" style="fill: #0072b2; stroke: #0072b2"/>
+ <use xlink:href="#m529fa2b800" x="411.690885" y="167.444857" style="fill: #0072b2; stroke: #0072b2"/>
+ <use xlink:href="#m529fa2b800" x="436.061444" y="167.444857" style="fill: #0072b2; stroke: #0072b2"/>
+ <use xlink:href="#m529fa2b800" x="454.660843" y="167.085906" style="fill: #0072b2; stroke: #0072b2"/>
+ </g>
+ </g>
+ <g id="line2d_57">
+ <g clip-path="url(#p800f26f182)">
+ <use xlink:href="#m529fa2b800" x="350.482992" y="167.444857" style="fill: #0072b2; stroke: #0072b2"/>
+ <use xlink:href="#m529fa2b800" x="393.255515" y="167.444857" style="fill: #0072b2; stroke: #0072b2"/>
+ <use xlink:href="#m529fa2b800" x="411.690885" y="167.444857" style="fill: #0072b2; stroke: #0072b2"/>
+ <use xlink:href="#m529fa2b800" x="436.061444" y="165.650102" style="fill: #0072b2; stroke: #0072b2"/>
+ <use xlink:href="#m529fa2b800" x="454.660843" y="161.34269" style="fill: #0072b2; stroke: #0072b2"/>
+ </g>
+ </g>
+ <g id="line2d_58">
+ <path d="M 391.560157 167.444857
+L 415.913474 167.444857
+L 434.336046 167.444857
+L 452.758619 164.573249
+L 477.111935 146.984648
+L 495.534508 122.934929
+" clip-path="url(#p800f26f182)" style="fill: none; stroke: #d55e00; stroke-width: 1.35; stroke-linecap: square"/>
+ <defs>
+ <path id="m9c08e3ae10" d="M 0 2.25
+C 0.596707 2.25 1.169055 2.012926 1.59099 1.59099
+C 2.012926 1.169055 2.25 0.596707 2.25 0
+C 2.25 -0.596707 2.012926 -1.169055 1.59099 -1.59099
+C 1.169055 -2.012926 0.596707 -2.25 0 -2.25
+C -0.596707 -2.25 -1.169055 -2.012926 -1.59099 -1.59099
+C -2.012926 -1.169055 -2.25 -0.596707 -2.25 0
+C -2.25 0.596707 -2.012926 1.169055 -1.59099 1.59099
+C -1.169055 2.012926 -0.596707 2.25 0 2.25
+z
+" style="stroke: #d55e00"/>
+ </defs>
+ <g clip-path="url(#p800f26f182)">
+ <use xlink:href="#m9c08e3ae10" x="391.560157" y="167.444857" style="fill: #d55e00; stroke: #d55e00"/>
+ <use xlink:href="#m9c08e3ae10" x="415.913474" y="167.444857" style="fill: #d55e00; stroke: #d55e00"/>
+ <use xlink:href="#m9c08e3ae10" x="434.336046" y="167.444857" style="fill: #d55e00; stroke: #d55e00"/>
+ <use xlink:href="#m9c08e3ae10" x="452.758619" y="164.573249" style="fill: #d55e00; stroke: #d55e00"/>
+ <use xlink:href="#m9c08e3ae10" x="477.111935" y="146.984648" style="fill: #d55e00; stroke: #d55e00"/>
+ <use xlink:href="#m9c08e3ae10" x="495.534508" y="122.934929" style="fill: #d55e00; stroke: #d55e00"/>
+ </g>
+ </g>
+ <g id="line2d_59">
+ <path d="M 350.482992 167.444857
+L 393.255515 167.444857
+L 411.690885 167.444857
+L 436.061444 166.726955
+L 454.660843 164.573249
+" clip-path="url(#p800f26f182)" style="fill: none; stroke: #0072b2; stroke-width: 1.35; stroke-linecap: square"/>
+ <defs>
+ <path id="m4be6bd8158" d="M -0 3.181981
+L 3.181981 0
+L 0 -3.181981
+L -3.181981 -0
+z
+" style="stroke: #0072b2; stroke-linejoin: miter"/>
+ </defs>
+ <g clip-path="url(#p800f26f182)">
+ <use xlink:href="#m4be6bd8158" x="350.482992" y="167.444857" style="fill: #0072b2; stroke: #0072b2; stroke-linejoin: miter"/>
+ <use xlink:href="#m4be6bd8158" x="393.255515" y="167.444857" style="fill: #0072b2; stroke: #0072b2; stroke-linejoin: miter"/>
+ <use xlink:href="#m4be6bd8158" x="411.690885" y="167.444857" style="fill: #0072b2; stroke: #0072b2; stroke-linejoin: miter"/>
+ <use xlink:href="#m4be6bd8158" x="436.061444" y="166.726955" style="fill: #0072b2; stroke: #0072b2; stroke-linejoin: miter"/>
+ <use xlink:href="#m4be6bd8158" x="454.660843" y="164.573249" style="fill: #0072b2; stroke: #0072b2; stroke-linejoin: miter"/>
+ </g>
+ </g>
+ <g id="patch_12">
+ <path d="M 343.230417 171.120516
+L 343.230417 20.418516
+" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/>
+ </g>
+ <g id="patch_13">
+ <path d="M 343.230417 171.120516
+L 502.787083 171.120516
+" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/>
+ </g>
+ <g id="text_24">
+ <text style="font-size: 9.5px; font-family: 'DejaVu Sans'; text-anchor: middle" x="423.00875" y="14.418516" transform="rotate(-0 423.00875 14.418516)">(b) Local sample-path mismatch</text>
+ </g>
+ <g id="legend_1">
+ <g id="LineCollection_4">
+ <path d="M 357.480417 33.992344
+L 357.480417 26.492344
+" style="fill: none; stroke: #d55e00; stroke-width: 1.35"/>
+ </g>
+ <g id="line2d_60">
+ <g>
+ <use xlink:href="#m12af69966a" x="357.480417" y="33.992344" style="fill: #d55e00; stroke: #d55e00"/>
+ </g>
+ </g>
+ <g id="line2d_61">
+ <g>
+ <use xlink:href="#m12af69966a" x="357.480417" y="26.492344" style="fill: #d55e00; stroke: #d55e00"/>
+ </g>
+ </g>
+ <g id="line2d_62">
+ <path d="M 349.980417 30.242344
+L 364.980417 30.242344
+" style="fill: none; stroke: #d55e00; stroke-width: 1.35; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_63">
+ <g>
+ <use xlink:href="#m9c08e3ae10" x="357.480417" y="30.242344" style="fill: #d55e00; stroke: #d55e00"/>
+ </g>
+ </g>
+ <g id="text_25">
+ <text style="font-size: 7.5px; font-family: 'DejaVu Sans'; text-anchor: start" x="370.980417" y="32.867344" transform="rotate(-0 370.980417 32.867344)">Pedestal mismatch</text>
+ </g>
+ <g id="LineCollection_5">
+ <path d="M 357.480417 45.000938
+L 357.480417 37.500938
+" style="fill: none; stroke: #0072b2; stroke-width: 1.35"/>
+ </g>
+ <g id="line2d_64">
+ <g>
+ <use xlink:href="#m529fa2b800" x="357.480417" y="45.000938" style="fill: #0072b2; stroke: #0072b2"/>
+ </g>
+ </g>
+ <g id="line2d_65">
+ <g>
+ <use xlink:href="#m529fa2b800" x="357.480417" y="37.500938" style="fill: #0072b2; stroke: #0072b2"/>
+ </g>
+ </g>
+ <g id="line2d_66">
+ <path d="M 349.980417 41.250938
+L 364.980417 41.250938
+" style="fill: none; stroke: #0072b2; stroke-width: 1.35; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_67">
+ <g>
+ <use xlink:href="#m4be6bd8158" x="357.480417" y="41.250938" style="fill: #0072b2; stroke: #0072b2; stroke-linejoin: miter"/>
+ </g>
+ </g>
+ <g id="text_26">
+ <text style="font-size: 7.5px; font-family: 'DejaVu Sans'; text-anchor: start" x="370.980417" y="43.875938" transform="rotate(-0 370.980417 43.875938)">Gain mismatch</text>
+ </g>
+ </g>
+ </g>
+ <g id="axes_3">
+ <g id="patch_14">
+ <path d="M 599.457083 171.120516
+L 759.01375 171.120516
+L 759.01375 20.418516
+L 599.457083 20.418516
+z
+" style="fill: #ffffff"/>
+ </g>
+ <g id="patch_15">
+ <path d="M 603.348709 41.582229
+L 603.348709 41.582229
+L 603.348709 27.268607
+L 603.348709 27.268607
+z
+" clip-path="url(#p6d538e60d8)" style="fill: #0072b2; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="patch_16">
+ <path d="M 603.348709 62.030262
+L 603.348709 62.030262
+L 603.348709 47.716639
+L 603.348709 47.716639
+z
+" clip-path="url(#p6d538e60d8)" style="fill: #0072b2; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="patch_17">
+ <path d="M 603.348709 82.478294
+L 603.348709 82.478294
+L 603.348709 68.164672
+L 603.348709 68.164672
+z
+" clip-path="url(#p6d538e60d8)" style="fill: #0072b2; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="patch_18">
+ <path d="M 603.348709 102.926327
+L 603.348709 102.926327
+L 603.348709 88.612704
+L 603.348709 88.612704
+z
+" clip-path="url(#p6d538e60d8)" style="fill: #0072b2; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="patch_19">
+ <path d="M 603.348709 123.37436
+L 603.348709 123.37436
+L 603.348709 109.060737
+L 603.348709 109.060737
+z
+" clip-path="url(#p6d538e60d8)" style="fill: #0072b2; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="patch_20">
+ <path d="M 603.348709 143.822392
+L 606.009001 143.822392
+L 606.009001 129.508769
+L 603.348709 129.508769
+z
+" clip-path="url(#p6d538e60d8)" style="fill: #0072b2; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="patch_21">
+ <path d="M 603.348709 164.270425
+L 603.348709 164.270425
+L 603.348709 149.956802
+L 603.348709 149.956802
+z
+" clip-path="url(#p6d538e60d8)" style="fill: #0072b2; stroke: #ffffff; stroke-width: 0.4; stroke-linejoin: miter"/>
+ </g>
+ <g id="matplotlib.axis_5">
+ <g id="xtick_28">
+ <g id="line2d_68">
+ <path d="M 603.348709 171.120516
+L 603.348709 20.418516
+" clip-path="url(#p6d538e60d8)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_69">
+ <g>
+ <use xlink:href="#m5974431c5e" x="603.348709" y="171.120516" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_27">
+ <text style="font-size: 7.7px; font-family: 'DejaVu Sans'; text-anchor: middle" x="603.348709" y="183.471313" transform="rotate(-0 603.348709 183.471313)">0</text>
+ </g>
+ </g>
+ <g id="xtick_29">
+ <g id="line2d_70">
+ <path d="M 651.994035 171.120516
+L 651.994035 20.418516
+" clip-path="url(#p6d538e60d8)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_71">
+ <g>
+ <use xlink:href="#m5974431c5e" x="651.994035" y="171.120516" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_28">
+ <text style="font-size: 7.7px; font-family: 'DejaVu Sans'; text-anchor: middle" x="651.994035" y="183.471313" transform="rotate(-0 651.994035 183.471313)">10</text>
+ </g>
+ </g>
+ <g id="xtick_30">
+ <g id="line2d_72">
+ <path d="M 700.63936 171.120516
+L 700.63936 20.418516
+" clip-path="url(#p6d538e60d8)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_73">
+ <g>
+ <use xlink:href="#m5974431c5e" x="700.63936" y="171.120516" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_29">
+ <text style="font-size: 7.7px; font-family: 'DejaVu Sans'; text-anchor: middle" x="700.63936" y="183.471313" transform="rotate(-0 700.63936 183.471313)">20</text>
+ </g>
+ </g>
+ <g id="xtick_31">
+ <g id="line2d_74">
+ <path d="M 749.284685 171.120516
+L 749.284685 20.418516
+" clip-path="url(#p6d538e60d8)" style="fill: none; stroke: #d9d9d9; stroke-opacity: 0.8; stroke-width: 0.55; stroke-linecap: square"/>
+ </g>
+ <g id="line2d_75">
+ <g>
+ <use xlink:href="#m5974431c5e" x="749.284685" y="171.120516" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_30">
+ <text style="font-size: 7.7px; font-family: 'DejaVu Sans'; text-anchor: middle" x="749.284685" y="183.471313" transform="rotate(-0 749.284685 183.471313)">30</text>
+ </g>
+ </g>
+ <g id="text_31">
+ <text style="font-size: 9px; font-family: 'DejaVu Sans'; text-anchor: middle" x="679.235417" y="195.911266" transform="rotate(-0 679.235417 195.911266)">Final classification error (%)</text>
+ </g>
+ </g>
+ <g id="matplotlib.axis_6">
+ <g id="ytick_14">
+ <g id="line2d_76">
+ <g>
+ <use xlink:href="#m2ade93fa71" x="599.457083" y="34.425418" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_32">
+ <text style="font-size: 8px; font-family: 'DejaVu Sans'; text-anchor: end" x="592.957083" y="37.464793" transform="rotate(-0 592.957083 37.464793)">Ideal CDS</text>
+ </g>
+ </g>
+ <g id="ytick_15">
+ <g id="line2d_77">
+ <g>
+ <use xlink:href="#m2ade93fa71" x="599.457083" y="54.87345" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_33">
+ <text style="font-size: 8px; font-family: 'DejaVu Sans'; text-anchor: end" x="592.957083" y="57.912825" transform="rotate(-0 592.957083 57.912825)">Common pedestal</text>
+ </g>
+ </g>
+ <g id="ytick_16">
+ <g id="line2d_78">
+ <g>
+ <use xlink:href="#m2ade93fa71" x="599.457083" y="75.321483" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_34">
+ <text style="font-size: 8px; font-family: 'DejaVu Sans'; text-anchor: end" x="592.957083" y="78.360858" transform="rotate(-0 592.957083 78.360858)">Sample noise</text>
+ </g>
+ </g>
+ <g id="ytick_17">
+ <g id="line2d_79">
+ <g>
+ <use xlink:href="#m2ade93fa71" x="599.457083" y="95.769516" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_35">
+ <text style="font-size: 8px; font-family: 'DejaVu Sans'; text-anchor: end" x="592.957083" y="98.808891" transform="rotate(-0 592.957083 98.808891)">Refresh every 4</text>
+ </g>
+ </g>
+ <g id="ytick_18">
+ <g id="line2d_80">
+ <g>
+ <use xlink:href="#m2ade93fa71" x="599.457083" y="116.217548" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_36">
+ <text style="font-size: 8px; font-family: 'DejaVu Sans'; text-anchor: end" x="592.957083" y="119.256923" transform="rotate(-0 592.957083 119.256923)">Mild combined</text>
+ </g>
+ </g>
+ <g id="ytick_19">
+ <g id="line2d_81">
+ <g>
+ <use xlink:href="#m2ade93fa71" x="599.457083" y="136.665581" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_37">
+ <text style="font-size: 8px; font-family: 'DejaVu Sans'; text-anchor: end" x="592.957083" y="139.704956" transform="rotate(-0 592.957083 139.704956)">Strong combined</text>
+ </g>
+ </g>
+ <g id="ytick_20">
+ <g id="line2d_82">
+ <g>
+ <use xlink:href="#m2ade93fa71" x="599.457083" y="157.113613" style="stroke: #000000; stroke-width: 0.8"/>
+ </g>
+ </g>
+ <g id="text_38">
+ <text style="font-size: 8px; font-family: 'DejaVu Sans'; text-anchor: end" x="592.957083" y="160.152988" transform="rotate(-0 592.957083 160.152988)">Overclamp + mild</text>
+ </g>
+ </g>
+ </g>
+ <g id="LineCollection_6">
+ <path d="M 603.348709 34.425418
+L 603.348709 34.425418
+" clip-path="url(#p6d538e60d8)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ <path d="M 603.348709 54.87345
+L 603.348709 54.87345
+" clip-path="url(#p6d538e60d8)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ <path d="M 603.348709 75.321483
+L 603.348709 75.321483
+" clip-path="url(#p6d538e60d8)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ <path d="M 603.348709 95.769516
+L 603.348709 95.769516
+" clip-path="url(#p6d538e60d8)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ <path d="M 603.348709 116.217548
+L 603.348709 116.217548
+" clip-path="url(#p6d538e60d8)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ <path d="M 603.728751 136.665581
+L 609.049333 136.665581
+" clip-path="url(#p6d538e60d8)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ <path d="M 603.348709 157.113613
+L 603.348709 157.113613
+" clip-path="url(#p6d538e60d8)" style="fill: none; stroke: #222222; stroke-width: 0.8"/>
+ </g>
+ <g id="line2d_83">
+ <g clip-path="url(#p6d538e60d8)">
+ <use xlink:href="#mc120802246" x="603.348709" y="34.425418" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#mc120802246" x="603.348709" y="54.87345" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#mc120802246" x="603.348709" y="75.321483" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#mc120802246" x="603.348709" y="95.769516" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#mc120802246" x="603.348709" y="116.217548" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#mc120802246" x="603.728751" y="136.665581" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#mc120802246" x="603.348709" y="157.113613" style="fill: #1f77b4; stroke: #222222"/>
+ </g>
+ </g>
+ <g id="line2d_84">
+ <g clip-path="url(#p6d538e60d8)">
+ <use xlink:href="#mc120802246" x="603.348709" y="34.425418" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#mc120802246" x="603.348709" y="54.87345" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#mc120802246" x="603.348709" y="75.321483" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#mc120802246" x="603.348709" y="95.769516" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#mc120802246" x="603.348709" y="116.217548" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#mc120802246" x="609.049333" y="136.665581" style="fill: #1f77b4; stroke: #222222"/>
+ <use xlink:href="#mc120802246" x="603.348709" y="157.113613" style="fill: #1f77b4; stroke: #222222"/>
+ </g>
+ </g>
+ <g id="patch_22">
+ <path d="M 599.457083 171.120516
+L 599.457083 20.418516
+" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/>
+ </g>
+ <g id="patch_23">
+ <path d="M 599.457083 171.120516
+L 759.01375 171.120516
+" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/>
+ </g>
+ <g id="text_39">
+ <text style="font-size: 9.5px; font-family: 'DejaVu Sans'; text-anchor: middle" x="679.235417" y="14.418516" transform="rotate(-0 679.235417 14.418516)">(c) Nonideal local sampling</text>
+ </g>
+ </g>
+ <g id="text_40">
+ <text style="font-size: 7px; font-family: 'DejaVu Sans'; text-anchor: end; fill: #666666" x="764.34175" y="222.248734" transform="rotate(-0 764.34175 222.248734)">40 tasks × 4 device draws; error bars are task-bootstrap 95% intervals</text>
+ </g>
+ </g>
+ <defs>
+ <clipPath id="pf3ff1facc5">
+ <rect x="87.00375" y="20.418516" width="159.556667" height="150.702"/>
+ </clipPath>
+ <clipPath id="p800f26f182">
+ <rect x="343.230417" y="20.418516" width="159.556667" height="150.702"/>
+ </clipPath>
+ <clipPath id="p6d538e60d8">
+ <rect x="599.457083" y="20.418516" width="159.556667" height="150.702"/>
+ </clipPath>
+ </defs>
+</svg>
diff --git a/results/physical_bias/p10_hardware_evidence_analysis.json b/results/physical_bias/p10_hardware_evidence_analysis.json
new file mode 100644
index 0000000..9811f5c
--- /dev/null
+++ b/results/physical_bias/p10_hardware_evidence_analysis.json
@@ -0,0 +1,308 @@
+{
+ "analysis": "physical_clln_hardware_evidence_figure",
+ "confirmatory": false,
+ "bootstrap": {
+ "unit": "task; four device draws averaged within task",
+ "task_clusters": 40,
+ "replicates": 20000,
+ "seed": 20260829,
+ "interval": "percentile 95%"
+ },
+ "physical_methods": {
+ "clean": {
+ "mean": 0.0,
+ "task_bootstrap_95ci": [
+ 0.0,
+ 0.0
+ ],
+ "task_clusters": 40,
+ "trials": 160
+ },
+ "raw": {
+ "mean": 0.25859375,
+ "task_bootstrap_95ci": [
+ 0.22265625,
+ 0.29609375
+ ],
+ "task_clusters": 40,
+ "trials": 160
+ },
+ "constant": {
+ "mean": 0.0375,
+ "task_bootstrap_95ci": [
+ 0.01875,
+ 0.05859375
+ ],
+ "task_clusters": 40,
+ "trials": 160
+ },
+ "overclamp": {
+ "mean": 0.04765625,
+ "task_bootstrap_95ci": [
+ 0.01875,
+ 0.0828125
+ ],
+ "task_clusters": 40,
+ "trials": 160
+ },
+ "sdil": {
+ "mean": 0.0,
+ "task_bootstrap_95ci": [
+ 0.0,
+ 0.0
+ ],
+ "task_clusters": 40,
+ "trials": 160
+ },
+ "overclamp_sdil": {
+ "mean": 0.0,
+ "task_bootstrap_95ci": [
+ 0.0,
+ 0.0
+ ],
+ "task_clusters": 40,
+ "trials": 160
+ }
+ },
+ "sampler_conditions": {
+ "combined_mild_refresh4": {
+ "mean": 0.0,
+ "task_bootstrap_95ci": [
+ 0.0,
+ 0.0
+ ],
+ "task_clusters": 40,
+ "trials": 160,
+ "zero_error_fraction": 1.0,
+ "solver_failure_fraction": 0.0,
+ "median_applied_rate_rmse_v_per_s": 0.46660538253621064
+ },
+ "combined_strong": {
+ "mean": 0.00546875,
+ "task_bootstrap_95ci": [
+ 0.00078125,
+ 0.01171875
+ ],
+ "task_clusters": 40,
+ "trials": 160,
+ "zero_error_fraction": 0.96875,
+ "solver_failure_fraction": 0.0,
+ "median_applied_rate_rmse_v_per_s": 0.36811707519239995
+ },
+ "common_pedestal_10": {
+ "mean": 0.0,
+ "task_bootstrap_95ci": [
+ 0.0,
+ 0.0
+ ],
+ "task_clusters": 40,
+ "trials": 160,
+ "zero_error_fraction": 1.0,
+ "solver_failure_fraction": 0.0,
+ "median_applied_rate_rmse_v_per_s": 7.092845963015098e-16
+ },
+ "gain_mismatch_0.001": {
+ "mean": 0.0,
+ "task_bootstrap_95ci": [
+ 0.0,
+ 0.0
+ ],
+ "task_clusters": 40,
+ "trials": 160,
+ "zero_error_fraction": 1.0,
+ "solver_failure_fraction": 0.0,
+ "median_applied_rate_rmse_v_per_s": 0.0021327524546310593
+ },
+ "gain_mismatch_0.005": {
+ "mean": 0.0,
+ "task_bootstrap_95ci": [
+ 0.0,
+ 0.0
+ ],
+ "task_clusters": 40,
+ "trials": 160,
+ "zero_error_fraction": 1.0,
+ "solver_failure_fraction": 0.0,
+ "median_applied_rate_rmse_v_per_s": 0.010662411888727154
+ },
+ "gain_mismatch_0.01": {
+ "mean": 0.0,
+ "task_bootstrap_95ci": [
+ 0.0,
+ 0.0
+ ],
+ "task_clusters": 40,
+ "trials": 160,
+ "zero_error_fraction": 1.0,
+ "solver_failure_fraction": 0.0,
+ "median_applied_rate_rmse_v_per_s": 0.021335094097404632
+ },
+ "gain_mismatch_0.025": {
+ "mean": 0.0015625,
+ "task_bootstrap_95ci": [
+ 0.0,
+ 0.00390625
+ ],
+ "task_clusters": 40,
+ "trials": 160,
+ "zero_error_fraction": 0.9875,
+ "solver_failure_fraction": 0.0,
+ "median_applied_rate_rmse_v_per_s": 0.05337234986008901
+ },
+ "gain_mismatch_0.05": {
+ "mean": 0.00625,
+ "task_bootstrap_95ci": [
+ 0.00078125,
+ 0.01328125
+ ],
+ "task_clusters": 40,
+ "trials": 160,
+ "zero_error_fraction": 0.9625,
+ "solver_failure_fraction": 0.0,
+ "median_applied_rate_rmse_v_per_s": 0.10745724630761008
+ },
+ "ideal_cds": {
+ "mean": 0.0,
+ "task_bootstrap_95ci": [
+ 0.0,
+ 0.0
+ ],
+ "task_clusters": 40,
+ "trials": 160,
+ "zero_error_fraction": 1.0,
+ "solver_failure_fraction": 0.0,
+ "median_applied_rate_rmse_v_per_s": 0.0
+ },
+ "overclamp_plus_combined_mild": {
+ "mean": 0.0,
+ "task_bootstrap_95ci": [
+ 0.0,
+ 0.0
+ ],
+ "task_clusters": 40,
+ "trials": 160,
+ "zero_error_fraction": 1.0,
+ "solver_failure_fraction": 0.0,
+ "median_applied_rate_rmse_v_per_s": 0.14562285807356745
+ },
+ "pedestal_mismatch_0.01": {
+ "mean": 0.0,
+ "task_bootstrap_95ci": [
+ 0.0,
+ 0.0
+ ],
+ "task_clusters": 40,
+ "trials": 160,
+ "zero_error_fraction": 1.0,
+ "solver_failure_fraction": 0.0,
+ "median_applied_rate_rmse_v_per_s": 0.010003519663837685
+ },
+ "pedestal_mismatch_0.025": {
+ "mean": 0.0,
+ "task_bootstrap_95ci": [
+ 0.0,
+ 0.0
+ ],
+ "task_clusters": 40,
+ "trials": 160,
+ "zero_error_fraction": 1.0,
+ "solver_failure_fraction": 0.0,
+ "median_applied_rate_rmse_v_per_s": 0.02500879915959358
+ },
+ "pedestal_mismatch_0.05": {
+ "mean": 0.0,
+ "task_bootstrap_95ci": [
+ 0.0,
+ 0.0
+ ],
+ "task_clusters": 40,
+ "trials": 160,
+ "zero_error_fraction": 1.0,
+ "solver_failure_fraction": 0.0,
+ "median_applied_rate_rmse_v_per_s": 0.05001759831918712
+ },
+ "pedestal_mismatch_0.1": {
+ "mean": 0.00625,
+ "task_bootstrap_95ci": [
+ 0.0015625,
+ 0.0125
+ ],
+ "task_clusters": 40,
+ "trials": 160,
+ "zero_error_fraction": 0.95625,
+ "solver_failure_fraction": 0.0,
+ "median_applied_rate_rmse_v_per_s": 0.10003519663837424
+ },
+ "pedestal_mismatch_0.25": {
+ "mean": 0.04453125,
+ "task_bootstrap_95ci": [
+ 0.0203125,
+ 0.07421875
+ ],
+ "task_clusters": 40,
+ "trials": 160,
+ "zero_error_fraction": 0.825,
+ "solver_failure_fraction": 0.00625,
+ "median_applied_rate_rmse_v_per_s": 0.25008799159594736
+ },
+ "pedestal_mismatch_0.5": {
+ "mean": 0.096875,
+ "task_bootstrap_95ci": [
+ 0.059375,
+ 0.13828125
+ ],
+ "task_clusters": 40,
+ "trials": 160,
+ "zero_error_fraction": 0.70625,
+ "solver_failure_fraction": 0.0125,
+ "median_applied_rate_rmse_v_per_s": 0.5001759831918947
+ },
+ "refresh_every_4": {
+ "mean": 0.0,
+ "task_bootstrap_95ci": [
+ 0.0,
+ 0.0
+ ],
+ "task_clusters": 40,
+ "trials": 160,
+ "zero_error_fraction": 1.0,
+ "solver_failure_fraction": 0.0,
+ "median_applied_rate_rmse_v_per_s": 0.4399296286060528
+ },
+ "sample_noise_1": {
+ "mean": 0.0,
+ "task_bootstrap_95ci": [
+ 0.0,
+ 0.0
+ ],
+ "task_clusters": 40,
+ "trials": 160,
+ "zero_error_fraction": 1.0,
+ "solver_failure_fraction": 0.0,
+ "median_applied_rate_rmse_v_per_s": 1.4156351463196133
+ }
+ },
+ "spice_primitive": {
+ "publication_evidence": false,
+ "scope": "Generic switch-capacitor acquisition and droop sanity check; this is not the complete CLLN edge circuit.",
+ "configuration_count": 48,
+ "fraction_below_1_percent_error_at_start": 0.4166666666666667,
+ "fraction_below_1_percent_error_at_end": 0.4166666666666667,
+ "reference_configuration": {
+ "acquisition_seconds": 1e-06,
+ "switch_resistance_ohm": 200.0,
+ "hold_capacitance_f": 1e-09,
+ "leakage_resistance_ohm": 1000000000.0,
+ "held_at_switch_open_v": 0.004966396,
+ "residual_at_learning_start_v": 0.001033604,
+ "residual_at_learning_end_v": 0.001034101,
+ "start_relative_error": 0.03360400000000001,
+ "end_relative_error": 0.03410099999999992
+ }
+ },
+ "sources": [
+ "results/physical_bias/p5_full_grid_bias_crossover.json",
+ "results/physical_bias/p9_grid_correlated_autozero.json",
+ "results/physical_bias/p8_spice_autozero_primitive.json"
+ ]
+}
diff --git a/results/physical_bias/p10_hardware_evidence_source.csv b/results/physical_bias/p10_hardware_evidence_source.csv
new file mode 100644
index 0000000..a2aec46
--- /dev/null
+++ b/results/physical_bias/p10_hardware_evidence_source.csv
@@ -0,0 +1,25 @@
+panel,series,condition,x,x_unit,mean_error,ci_low,ci_high,zero_error_fraction
+a,physical_method,Clean,,,0.0,0.0,0.0,
+a,physical_method,Raw,,,0.25859375,0.22265625,0.29609375,
+a,physical_method,Static calibration,,,0.0375,0.01875,0.05859375,
+a,physical_method,Overclamp,,,0.04765625,0.01875,0.0828125,
+a,physical_method,SDIL,,,0.0,0.0,0.0,
+a,physical_method,Overclamp + SDIL,,,0.0,0.0,0.0,
+b,pedestal mismatch,pedestal_mismatch_0.01,0.010003519663837685,V/s,0.0,0.0,0.0,1.0
+b,pedestal mismatch,pedestal_mismatch_0.025,0.02500879915959358,V/s,0.0,0.0,0.0,1.0
+b,pedestal mismatch,pedestal_mismatch_0.05,0.05001759831918712,V/s,0.0,0.0,0.0,1.0
+b,pedestal mismatch,pedestal_mismatch_0.1,0.10003519663837424,V/s,0.00625,0.0015625,0.0125,0.95625
+b,pedestal mismatch,pedestal_mismatch_0.25,0.25008799159594736,V/s,0.04453125,0.0203125,0.07421875,0.825
+b,pedestal mismatch,pedestal_mismatch_0.5,0.5001759831918947,V/s,0.096875,0.059375,0.13828125,0.70625
+b,gain mismatch,gain_mismatch_0.001,0.0021327524546310593,V/s,0.0,0.0,0.0,1.0
+b,gain mismatch,gain_mismatch_0.005,0.010662411888727154,V/s,0.0,0.0,0.0,1.0
+b,gain mismatch,gain_mismatch_0.01,0.021335094097404632,V/s,0.0,0.0,0.0,1.0
+b,gain mismatch,gain_mismatch_0.025,0.05337234986008901,V/s,0.0015625,0.0,0.00390625,0.9875
+b,gain mismatch,gain_mismatch_0.05,0.10745724630761008,V/s,0.00625,0.00078125,0.01328125,0.9625
+c,sampling condition,Ideal CDS,,,0.0,0.0,0.0,1.0
+c,sampling condition,Common pedestal,,,0.0,0.0,0.0,1.0
+c,sampling condition,Sample noise,,,0.0,0.0,0.0,1.0
+c,sampling condition,Refresh every 4,,,0.0,0.0,0.0,1.0
+c,sampling condition,Mild combined,,,0.0,0.0,0.0,1.0
+c,sampling condition,Strong combined,,,0.00546875,0.00078125,0.01171875,0.96875
+c,sampling condition,Overclamp + mild,,,0.0,0.0,0.0,1.0
diff --git a/visual-composer/physical-hardware-evidence.md b/visual-composer/physical-hardware-evidence.md
new file mode 100644
index 0000000..b271127
--- /dev/null
+++ b/visual-composer/physical-hardware-evidence.md
@@ -0,0 +1,40 @@
+# Visual contract: hardware-realistic CLLN evidence
+
+- **Artifact:** three-panel quantitative result figure.
+- **Target venue / format:** ICLR two-column paper, full-width figure.
+- **Core claim:** local innovation subtraction restores nonlinear CLLN
+ learning under published component imperfections and remains effective with
+ nonideal local sampling.
+- **Reviewer question:** does the method survive component mismatch, sampler
+ mismatch, noise, stale refresh, and their combination at the task endpoint?
+- **Evidence layer:** hardware-realistic simulation; no fabricated-chip claim.
+- **Source data:**
+ `results/physical_bias/p5_full_grid_bias_crossover.json`,
+ `results/physical_bias/p9_grid_correlated_autozero.json`, and
+ `results/physical_bias/p8_spice_autozero_primitive.json`.
+- **Statistics / uncertainty:** four device draws are averaged within each of
+ 40 tasks; tasks are resampled for percentile 95% bootstrap intervals.
+- **Figure prototype:** horizontal method comparison, mismatch response curves,
+ and horizontal nonideality comparison.
+- **Panel map:**
+ - (a) nonlinear CLLN final error across clean, raw, calibration,
+ overclamping, SDIL, and their combination;
+ - (b) final error against measured local sampling-error RMSE for pedestal
+ and gain mismatch sweeps;
+ - (c) final error under common pedestal, sampling noise, stale neutral
+ refresh, and combined nonidealities.
+- **Exact label inventory:** nonlinear CLLN, component errors, sample-path
+ mismatch, residual sampling error, nonideal local sampling, final
+ classification error.
+- **Caption role:** identify the published component-error scale, the local
+ correlated-double-sampling operation, task/device counts, and the boundary
+ between simulation and fabricated hardware.
+- **Manuscript placement:** Part 3, after the digital scaling result.
+- **Output formats:** editable SVG, vector PDF, PNG preview, analysis JSON, and
+ source CSV.
+- **Traceability:** every plotted aggregate is regenerated by
+ `experiments/plot_physical_hardware_evidence.py`; the SPICE primitive is
+ carried in the analysis manifest and reserved for supplementary evidence.
+- **Constraint:** circuit-solver failures remain classification failures.
+ Sampler RMSE excludes the missing diagnostic value on failed solves, while
+ the task endpoint includes every trial.
diff --git a/visual-composer/qa-ledger.md b/visual-composer/qa-ledger.md
index 319c1f2..c9e66e5 100644
--- a/visual-composer/qa-ledger.md
+++ b/visual-composer/qa-ledger.md
@@ -13,3 +13,16 @@ detached source note at 2,556-by-778 PNG resolution. The next QA pass replaces
the pilot source with the frozen multi-task, multi-device confirmation without
changing the plot grammar.
+## Hardware-realistic CLLN figure
+
+| Issue | Artifact | Severity | Fix | Status |
+|:--|:--|:--|:--|:--|
+| Method and condition labels collided | Panels (a) and (c) | High | Replaced vertical bars with horizontal bars and single-line labels | Resolved |
+| Different mismatch units were not directly comparable | Panel (b) | High | Used measured residual local-rate RMSE as the common x-axis and retained mismatch family as series | Resolved |
+| Zero-error conditions were visually ambiguous | Panels (a) and (c) | Low | Kept a small x-axis margin below zero so zero-valued error bars remain visible | Resolved |
+| Generic SPICE primitive could be read as a full-circuit simulation | Figure scope | High | Excluded it from the main plotted panels and recorded its limited scope in the analysis and visual contract | Resolved |
+| Color-only distinctions could fail in print | Panel (b) | Medium | Added distinct circle and diamond markers | Resolved |
+
+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.