qtdf 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
qtdf/__init__.py ADDED
@@ -0,0 +1,34 @@
1
+ """QTDF — Quantum Test Data Format (v0.1 reference implementation)."""
2
+ from .core import (
3
+ QTDF_VERSION,
4
+ content_hash,
5
+ finalize,
6
+ migrate,
7
+ new_record_id,
8
+ read_record,
9
+ utc_now,
10
+ verify_hash,
11
+ write_record,
12
+ )
13
+ from .store import Store
14
+ from .validate import errors_only, is_valid, validate
15
+
16
+ # package (distribution) version — distinct from QTDF_VERSION, the schema
17
+ # version stamped into records. 0.3.0 = the qtdf.* namespace consolidation.
18
+ __version__ = "0.3.0"
19
+
20
+ __all__ = [
21
+ "QTDF_VERSION",
22
+ "content_hash",
23
+ "finalize",
24
+ "migrate",
25
+ "new_record_id",
26
+ "read_record",
27
+ "utc_now",
28
+ "verify_hash",
29
+ "write_record",
30
+ "validate",
31
+ "errors_only",
32
+ "is_valid",
33
+ "Store",
34
+ ]
qtdf/cli.py ADDED
@@ -0,0 +1,217 @@
1
+ """qtdf — the command line for QTDF stores and records.
2
+
3
+ qtdf validate FILE... validate record files (exit 1 on hard errors)
4
+ qtdf show FILE human-readable record summary + hash check
5
+ qtdf query STORE [filters] list/count records in a store
6
+ qtdf verify STORE re-hash every record in a store
7
+ qtdf diff A B semantic diff of two records
8
+ qtdf demo the end-to-end pipeline on a virtual lot
9
+ qtdf version library + schema version
10
+
11
+ Stdlib only, like everything in qtdf-core.
12
+ """
13
+ from __future__ import annotations
14
+
15
+ import argparse
16
+ import json
17
+ import sys
18
+
19
+
20
+ def _load(path: str) -> dict:
21
+ with open(path, encoding="utf-8") as fh:
22
+ return json.load(fh)
23
+
24
+
25
+ # ------------------------------------------------------------------ #
26
+ def cmd_validate(args) -> int:
27
+ import qtdf
28
+ worst = 0
29
+ for path in args.files:
30
+ problems = qtdf.validate(_load(path))
31
+ errors = [p for p in problems if not p.startswith("warning: ")]
32
+ warnings = [p for p in problems if p.startswith("warning: ")]
33
+ status = "OK" if not errors else "INVALID"
34
+ print(f"{status:8s} {path}"
35
+ + (f" ({len(warnings)} warning{'s' * (len(warnings) != 1)})"
36
+ if warnings else ""))
37
+ for p in errors + (warnings if args.verbose else []):
38
+ print(f" - {p}")
39
+ worst = max(worst, 1 if errors else 0)
40
+ return worst
41
+
42
+
43
+ def cmd_show(args) -> int:
44
+ import qtdf
45
+ rec = _load(args.file)
46
+ dev, disp, run = rec.get("device", {}), rec.get("disposition", {}), rec.get("run") or {}
47
+ w = dev.get("wafer") or {}
48
+ print(f"record : {rec.get('record_id')} (qtdf {rec.get('qtdf_version')})")
49
+ print(f"type : {rec.get('record_type')} [{rec.get('data_source')}]")
50
+ print(f"device : {dev.get('device_id')}"
51
+ + (f" (lot {w.get('lot_id')} wafer {w.get('wafer_id')}"
52
+ f" die {w.get('die_x')},{w.get('die_y')})" if w else ""))
53
+ c = rec.get("carrier", {})
54
+ print(f"carrier : {c.get('provider')}"
55
+ + (f" {c.get('carrier_part')}" if c.get("carrier_part") else "")
56
+ + (f" socket {c.get('socket')}" if c.get("socket") else ""))
57
+ f = rec.get("fixture", {})
58
+ print(f"fixture : {f.get('fridge_id')}"
59
+ + (f" @ {f.get('temperature_K')} K" if f.get("temperature_K") is not None else ""))
60
+ if run:
61
+ ph = run.get("plan_hash") or ""
62
+ print(f"run : {run.get('run_id')} {run.get('cooldown_id') or ''}"
63
+ + (f" plan {ph[:18]}…" if ph else ""))
64
+ print(f"verdict : {disp.get('verdict')} (bin {disp.get('bin')})"
65
+ + (f" — {disp['reason']}" if disp.get("reason") else ""))
66
+ print("measurements:")
67
+ for m in rec.get("measurements", []):
68
+ lim = m.get("limit")
69
+ lim_s = f"{lim['op']} {lim['value']}" if lim else ""
70
+ ok = {True: "PASS", False: "FAIL", None: " "}[m.get("pass")]
71
+ val = m.get("value")
72
+ if isinstance(val, float):
73
+ val_s = f"{val:12.5g}"
74
+ elif isinstance(val, (list, dict)): # array values (e.g. band lobes)
75
+ s = json.dumps(val)
76
+ val_s = s if len(s) <= 28 else s[:25] + "..."
77
+ else:
78
+ val_s = f"{val!s:>12}"
79
+ print(f" {m.get('quantity', ''):16s} {val_s} {m.get('unit', ''):4s}"
80
+ f" {lim_s:12s} {ok}")
81
+ print(f"hash : {'verified OK' if qtdf.verify_hash(rec) else 'MISMATCH'}")
82
+ return 0
83
+
84
+
85
+ def cmd_query(args) -> int:
86
+ from qtdf.store import Store
87
+ filters = {}
88
+ for key in ("record_type", "data_source", "verdict", "run_id"):
89
+ v = getattr(args, key)
90
+ if v is not None:
91
+ filters[key] = v
92
+ rows = Store(args.store).query(device_prefix=args.device_prefix, **filters)
93
+ if args.count:
94
+ print(sum(1 for _ in rows))
95
+ return 0
96
+ n = 0
97
+ for row in rows:
98
+ print(f"{row['verdict'] or '-':5s} {row['record_type'] or '-':26s} "
99
+ f"{row['device_id'] or '-'}")
100
+ n += 1
101
+ if args.limit and n >= args.limit:
102
+ print(f"... (--limit {args.limit} reached)")
103
+ break
104
+ return 0
105
+
106
+
107
+ def cmd_verify(args) -> int:
108
+ from qtdf.store import Store
109
+ st = Store(args.store)
110
+ bad = st.verify_all()
111
+ n = sum(1 for _ in st.index())
112
+ if bad:
113
+ print(f"{len(bad)}/{n} records FAILED hash verification:")
114
+ for rid in bad[:20]:
115
+ print(f" {rid}")
116
+ return 1
117
+ print(f"{n} records, all hashes verified")
118
+ return 0
119
+
120
+
121
+ def cmd_diff(args) -> int:
122
+ a, b = _load(args.a), _load(args.b)
123
+ if a.get("device", {}).get("device_id") != b.get("device", {}).get("device_id"):
124
+ print(f"device : {a['device']['device_id']} vs {b['device']['device_id']}")
125
+ ma = {m["quantity"]: m for m in a.get("measurements", [])}
126
+ mb = {m["quantity"]: m for m in b.get("measurements", [])}
127
+ changed = False
128
+ for q in sorted(set(ma) | set(mb)):
129
+ va, vb = (ma.get(q) or {}).get("value"), (mb.get(q) or {}).get("value")
130
+ if va == vb:
131
+ continue
132
+ changed = True
133
+ delta = ""
134
+ if isinstance(va, (int, float)) and isinstance(vb, (int, float)) and va:
135
+ delta = f" ({100.0 * (vb - va) / abs(va):+.1f}%)"
136
+ print(f"{q:16s}: {va} -> {vb}{delta}")
137
+ da, db = a.get("disposition", {}), b.get("disposition", {})
138
+ if (da.get("verdict"), da.get("bin")) != (db.get("verdict"), db.get("bin")):
139
+ changed = True
140
+ print(f"verdict : {da.get('verdict')}(bin {da.get('bin')})"
141
+ f" -> {db.get('verdict')}(bin {db.get('bin')})")
142
+ pa = (a.get("run") or {}).get("plan_hash")
143
+ pb = (b.get("run") or {}).get("plan_hash")
144
+ if pa != pb:
145
+ changed = True
146
+ print(f"plan_hash : {'differs' if pa and pb else 'added/removed'}"
147
+ f" ({(pa or '-')[:18]}… -> {(pb or '-')[:18]}…)")
148
+ if not changed:
149
+ print("no semantic differences (values, verdict, plan)")
150
+ return 0
151
+
152
+
153
+ def cmd_demo(args) -> int:
154
+ from qtdf.demo import run
155
+ return run(seed=args.seed, rows=args.rows, cols=args.cols,
156
+ store_dir=args.store)
157
+
158
+
159
+ def cmd_version(_args) -> int:
160
+ import qtdf
161
+ print(f"qtdf {qtdf.__version__} (schema {qtdf.QTDF_VERSION})")
162
+ return 0
163
+
164
+
165
+ # ------------------------------------------------------------------ #
166
+ def main(argv=None) -> int:
167
+ ap = argparse.ArgumentParser(prog="qtdf", description=__doc__,
168
+ formatter_class=argparse.RawDescriptionHelpFormatter)
169
+ sub = ap.add_subparsers(dest="cmd", required=True)
170
+
171
+ p = sub.add_parser("validate", help="validate record files")
172
+ p.add_argument("files", nargs="+")
173
+ p.add_argument("-v", "--verbose", action="store_true",
174
+ help="also print warnings")
175
+ p.set_defaults(fn=cmd_validate)
176
+
177
+ p = sub.add_parser("show", help="summarize one record")
178
+ p.add_argument("file")
179
+ p.set_defaults(fn=cmd_show)
180
+
181
+ p = sub.add_parser("query", help="filter a store's index")
182
+ p.add_argument("store")
183
+ p.add_argument("--record-type", dest="record_type")
184
+ p.add_argument("--data-source", dest="data_source")
185
+ p.add_argument("--verdict")
186
+ p.add_argument("--run-id", dest="run_id")
187
+ p.add_argument("--device-prefix", dest="device_prefix")
188
+ p.add_argument("--count", action="store_true")
189
+ p.add_argument("--limit", type=int, default=40)
190
+ p.set_defaults(fn=cmd_query)
191
+
192
+ p = sub.add_parser("verify", help="re-hash every record in a store")
193
+ p.add_argument("store")
194
+ p.set_defaults(fn=cmd_verify)
195
+
196
+ p = sub.add_parser("diff", help="semantic diff of two records")
197
+ p.add_argument("a")
198
+ p.add_argument("b")
199
+ p.set_defaults(fn=cmd_diff)
200
+
201
+ p = sub.add_parser("demo", help="end-to-end pipeline on a virtual lot")
202
+ p.add_argument("--seed", type=int, default=42)
203
+ p.add_argument("--rows", type=int, default=10)
204
+ p.add_argument("--cols", type=int, default=10)
205
+ p.add_argument("--store", default=None,
206
+ help="keep the demo store here (default: temp dir)")
207
+ p.set_defaults(fn=cmd_demo)
208
+
209
+ p = sub.add_parser("version", help="print schema/library version")
210
+ p.set_defaults(fn=cmd_version)
211
+
212
+ args = ap.parse_args(argv)
213
+ return args.fn(args)
214
+
215
+
216
+ if __name__ == "__main__":
217
+ sys.exit(main())
qtdf/core.py ADDED
@@ -0,0 +1,151 @@
1
+ """QTDF core — Quantum Test Data Format, reference implementation (v0.1).
2
+
3
+ Zero external dependencies (Python stdlib only). A QTDF record is a plain dict
4
+ that round-trips to JSON. This module supplies:
5
+ - the version + controlled vocabularies,
6
+ - canonical serialization + content hashing (tamper-evident provenance),
7
+ - read/write helpers,
8
+ - a forward-migration hook.
9
+
10
+ Validation lives in ``qtdf.validate``; touchstone ingest in ``qtdf.touchstone``.
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import copy
15
+ import datetime as _dt
16
+ import hashlib
17
+ import json
18
+ import uuid
19
+ from typing import Any
20
+
21
+ # Schema semver. Bump MINOR for additive/compatible changes, MAJOR for breaking.
22
+ # v0.2.0 (additive over 0.1): optional `device.wafer` genealogy (lot/wafer/die x,y),
23
+ # optional top-level `run` grouping (cooldown/session), per-record_type required-
24
+ # quantity profiles in the validator, and the qubit_coherence_screen vocabulary.
25
+ # v0.1 records remain valid v0.2 records — hashes and validity are unchanged.
26
+ QTDF_VERSION = "0.2.0"
27
+
28
+ # --- Controlled vocabularies (recommended, not closed; validator warns on novel) ---
29
+
30
+ # What kind of test the record captures. Open vocab, but these are the seeds.
31
+ RECORD_TYPES = frozenset({
32
+ "rf_launch_qualification", # S-parameter qualification of an RF transition/fixture
33
+ "qubit_coherence_screen", # T1 / T2* / T2echo
34
+ "readout_fidelity",
35
+ "gate_benchmarking", # RB / XEB
36
+ "spectroscopy", # f_01, anharmonicity, TLS
37
+ "continuity", # DC / wiring integrity
38
+ })
39
+
40
+ # Whether the numbers are physically measured or predicted. CRITICAL to keep
41
+ # straight — a test-data standard must never let a sim be mistaken for a DUT.
42
+ DATA_SOURCES = frozenset({"measured", "simulation", "emulated"})
43
+
44
+ # How the device under test reached the fridge. This is the cassette tier.
45
+ CARRIER_PROVIDERS = frozenset({
46
+ "cassette", # CPN cassette: socket->device_id is automatic, RF env qualified
47
+ "manual", # bare wirebond / hand-wired: metadata entered by operator
48
+ "custom", # third-party carrier via an adapter
49
+ })
50
+
51
+ # Dispositioning verdicts (the disposition standard). Overall verdict may be
52
+ # derived from measurement passes OR set by engineering override (with reason).
53
+ VERDICTS = frozenset({"pass", "fail", "hold", "scrap", "rework"})
54
+
55
+ # Comparison operators for a measurement limit.
56
+ LIMIT_OPS = frozenset({"<=", ">=", "<", ">", "==", "in_range"})
57
+
58
+
59
+ # --------------------------------------------------------------------------- #
60
+ # Identity + timestamps
61
+ # --------------------------------------------------------------------------- #
62
+ def new_record_id() -> str:
63
+ """Return a fresh urn:uuid record identifier."""
64
+ return f"urn:uuid:{uuid.uuid4()}"
65
+
66
+
67
+ def utc_now() -> str:
68
+ """ISO-8601 UTC timestamp, second precision, 'Z' suffix."""
69
+ return _dt.datetime.now(_dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
70
+
71
+
72
+ # --------------------------------------------------------------------------- #
73
+ # Canonicalization + content hashing
74
+ # --------------------------------------------------------------------------- #
75
+ def canonical_json(obj: Any) -> str:
76
+ """Deterministic JSON: sorted keys, no insignificant whitespace.
77
+
78
+ Two records with the same content hash to the same string regardless of key
79
+ insertion order, so ``content_hash`` is a stable identity for the payload.
80
+ """
81
+ return json.dumps(obj, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
82
+
83
+
84
+ def content_hash(record: dict) -> str:
85
+ """sha256 over the record with ``provenance.content_hash`` excluded.
86
+
87
+ The hash cannot cover itself, so we strip it before hashing. Returns a
88
+ 'sha256:...' prefixed hex digest.
89
+ """
90
+ stripped = copy.deepcopy(record)
91
+ prov = stripped.get("provenance")
92
+ if isinstance(prov, dict):
93
+ prov.pop("content_hash", None)
94
+ digest = hashlib.sha256(canonical_json(stripped).encode("utf-8")).hexdigest()
95
+ return f"sha256:{digest}"
96
+
97
+
98
+ def finalize(record: dict) -> dict:
99
+ """Stamp ``provenance.content_hash`` in place and return the record.
100
+
101
+ Call once, immediately before persisting. A record is immutable after
102
+ finalize; corrections are new records that set ``supersedes``.
103
+ """
104
+ record.setdefault("provenance", {})
105
+ record["provenance"]["content_hash"] = content_hash(record)
106
+ return record
107
+
108
+
109
+ def verify_hash(record: dict) -> bool:
110
+ """True iff the stored content hash matches a fresh recomputation."""
111
+ stored = record.get("provenance", {}).get("content_hash")
112
+ return bool(stored) and stored == content_hash(record)
113
+
114
+
115
+ # --------------------------------------------------------------------------- #
116
+ # I/O
117
+ # --------------------------------------------------------------------------- #
118
+ def write_record(record: dict, path: str) -> None:
119
+ """Write a record as pretty JSON (finalize first if not already hashed)."""
120
+ if not record.get("provenance", {}).get("content_hash"):
121
+ finalize(record)
122
+ with open(path, "w", encoding="utf-8") as fh:
123
+ json.dump(record, fh, indent=1, ensure_ascii=False)
124
+ fh.write("\n")
125
+
126
+
127
+ def read_record(path: str) -> dict:
128
+ """Read a record from JSON and migrate it to the current QTDF_VERSION."""
129
+ with open(path, encoding="utf-8") as fh:
130
+ return migrate(json.load(fh))
131
+
132
+
133
+ # --------------------------------------------------------------------------- #
134
+ # Migration
135
+ # --------------------------------------------------------------------------- #
136
+ def migrate(record: dict) -> dict:
137
+ """Forward-migrate an older-minor record to QTDF_VERSION.
138
+
139
+ v0.1 is the floor, so this is currently a version-gate: it refuses a record
140
+ whose MAJOR differs from ours. As the schema evolves, add step functions
141
+ here (0.1 -> 0.2 -> ...) so historical records keep loading — backward
142
+ compatibility is a product feature, labs keep data for years.
143
+ """
144
+ ver = str(record.get("qtdf_version", "0.0.0"))
145
+ major = ver.split(".")[0]
146
+ if major != QTDF_VERSION.split(".")[0]:
147
+ raise ValueError(
148
+ f"record qtdf_version {ver} is a different MAJOR than {QTDF_VERSION}; "
149
+ "no migration path is defined"
150
+ )
151
+ return record
qtdf/demo.py ADDED
@@ -0,0 +1,171 @@
1
+ """The packaged end-to-end demo: `qtdf demo`.
2
+
3
+ Self-contained — the plan is embedded and the emulator uses its built-in
4
+ (fleet-calibrated) defaults, so this runs identically on a clean install with
5
+ zero repo files: virtual lot -> executive screen -> wafer map -> policy
6
+ pricing vs truth -> MCM assembly -> store integrity.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ import shutil
12
+ import tempfile
13
+
14
+ DEMO_PLAN = {
15
+ "plan_id": "demo-coherence-screen",
16
+ "plan_version": "1.0.0",
17
+ "record_type": "qubit_coherence_screen",
18
+ "quantities": [
19
+ {"quantity": "T1", "symbol": "T1", "unit": "us",
20
+ "limit": {"op": ">=", "value": 150.0}},
21
+ {"quantity": "T2", "symbol": "T2", "unit": "us"},
22
+ {"quantity": "f_01", "symbol": "f01", "unit": "GHz"},
23
+ {"quantity": "readout_error", "symbol": "eps_ro", "unit": "1",
24
+ "limit": {"op": "<=", "value": 0.02}},
25
+ ],
26
+ "disposition": {
27
+ "spec_id": "MCM-GRADE-v0",
28
+ "response_quantity": "T1",
29
+ "pass_bin": 1,
30
+ "default_fail_bin": 9,
31
+ "bin_map": [
32
+ {"bin": 2, "when_failed": ["T1"]},
33
+ {"bin": 3, "when_failed": ["readout_error"]},
34
+ {"bin": 4, "when_failed": ["T1", "readout_error"]},
35
+ {"bin": 5, "nonfunctional": True},
36
+ ],
37
+ },
38
+ }
39
+
40
+
41
+ def run(seed: int = 42, rows: int = 10, cols: int = 10,
42
+ store_dir: str | None = None, echo=print) -> int:
43
+ from qtdf.executive import (
44
+ CassetteCarrier, FridgeProfile, VFridgeBackend, execute, plan_hash,
45
+ read_capture, replay, slots_from_wafer,
46
+ )
47
+ from qtdf.store import Store
48
+ from qtdf.vfridge import EmuConfig, generate_wafer
49
+
50
+ # the dispositioning/analytics layer ships separately (commercial); the
51
+ # demo runs end-to-end either way, swapping the finale
52
+ try:
53
+ from qtdf.analytics import ModuleSpec, ascii_map, die_map_from_store, \
54
+ radial_yield, score_module, select_modules
55
+ from qtdf.disposition import (
56
+ ConfirmPass, GrayZoneRetest, SinglePass, Spec, compare,
57
+ measured_die_values,
58
+ )
59
+ commercial = True
60
+ except ImportError:
61
+ commercial = False
62
+
63
+ tmp = None
64
+ if store_dir is None:
65
+ tmp = tempfile.mkdtemp(prefix="qtdf-demo-")
66
+ store_dir = tmp
67
+ try:
68
+ store = Store(store_dir)
69
+ cfg = EmuConfig.from_calibration(seed=seed, lot_id="LOT-DEMO",
70
+ rows=rows, cols=cols, qubits_per_die=4)
71
+ wafer = generate_wafer(cfg, 1)
72
+ limits = {q["quantity"]: q["limit"]
73
+ for q in DEMO_PLAN["quantities"] if q.get("limit")}
74
+ spec_id = DEMO_PLAN["disposition"]["spec_id"]
75
+ echo(f"qtdf end-to-end demo — lot {cfg.lot_id}: {rows}x{cols} dies x 4 qubits"
76
+ f" (seed {seed})")
77
+ echo(f"plan {DEMO_PLAN['plan_id']} {plan_hash(DEMO_PLAN)[:18]}… "
78
+ f"spec {spec_id}: T1>={limits['T1']['value']}us, "
79
+ f"ro<={limits['readout_error']['value']}\n")
80
+
81
+ # 1 — screen the wafer through the executive (cassette carrier)
82
+ carrier = CassetteCarrier(capacity=10 ** 6)
83
+ slots = carrier.load(slots_from_wafer(wafer))
84
+ run_id = "demo:screen:r1"
85
+ cap_path = os.path.join(store_dir, "demo_capture.json")
86
+ summary = execute(DEMO_PLAN, slots, carrier.meta(),
87
+ VFridgeBackend(wafer, cfg),
88
+ FridgeProfile("VF-1", 0.012), store, run_id=run_id,
89
+ capture_path=None if commercial else cap_path)
90
+ echo(f"== screen: {summary['records']} qubit records, "
91
+ f"verdicts {summary['verdicts']} ==\n")
92
+
93
+ if not commercial:
94
+ # open-build finale: prove the record/replay property live
95
+ rep = Store(os.path.join(store_dir, "replay"))
96
+ s2 = replay(read_capture(cap_path), rep)
97
+ live = {r["record_id"]: r["content_hash"] for r in store.index()}
98
+ back = {r["record_id"]: r["content_hash"] for r in rep.index()}
99
+ echo("== record/replay: captured run replayed into a fresh store ==")
100
+ echo(f" byte-identical reproduction: {live == back}"
101
+ f" ({s2['records']} records)")
102
+ echo(" (any captured run — virtual or a real fridge session — is a"
103
+ " CI fixture)\n")
104
+ echo("(wafer maps, policy pricing, and MCM assembly-risk scoring live"
105
+ " in the\n commercial layer; this open build carries the standard"
106
+ " + executive)\n")
107
+ n = sum(1 for _ in store.index())
108
+ bad = store.verify_all()
109
+ echo(f"store: {n} records, "
110
+ f"{'all hashes verified' if not bad else f'{len(bad)} HASH FAILURES'}")
111
+ return 0 if not bad and live == back else 1
112
+
113
+ spec = Spec.from_plan(DEMO_PLAN)
114
+
115
+ # 2 — wafer map (a VIEW: values re-dispositioned under the spec)
116
+ dm = die_map_from_store(store, run_id, spec)
117
+ echo(f"== wafer map ('.' = known-good-die under {spec.spec_id}) ==")
118
+ echo(ascii_map(dm, rows, cols))
119
+ rz = radial_yield(dm, rows, cols)
120
+ (cy, cn), (ey, en) = rz["center"], rz["edge"]
121
+ echo(f"radial: center {100 * cy:.0f}% ({cn} dies) vs edge {100 * ey:.0f}%"
122
+ f" ({en} dies)\n")
123
+
124
+ # 3 — screening policies priced exactly against truth
125
+ echo("== screening policies priced vs truth "
126
+ "(escape $50k, overkill $2k, test $200) ==")
127
+ rows_ = compare([SinglePass(), ConfirmPass(2), GrayZoneRetest(0.15)],
128
+ [wafer], cfg, DEMO_PLAN, store, spec)
129
+ for m in rows_:
130
+ echo(f" {m['policy']:14s} ship {m['shipped']:3d} esc {m['escapes']:2d}"
131
+ f" ovk {m['overkill']:2d} cost {m['cost_die_cooldowns']:3d}"
132
+ f" -> ${m['usd_total']:>9,.0f}")
133
+ echo(f" winner: {rows_[0]['policy']} "
134
+ f"(true good dies: {rows_[0]['true_good']}/{rows_[0]['dies']})\n")
135
+
136
+ # 4 — MCM assembly. The strict-KGD bin is tiny AND same-index qubits
137
+ # share one frequency plan across dies, so chains built only from that
138
+ # bin usually collide at the >=25 MHz interface rule (this is why real
139
+ # module builders stagger die frequency plans). So: assemble from all
140
+ # functional dies and let the scorer price each chain's risk.
141
+ vals = measured_die_values(store, run_id)
142
+ kgd = [d for d, qv in vals.items() if spec.die_pass(qv)]
143
+ functional = [
144
+ {"die_id": d, "qubits": qv} for d, qv in sorted(vals.items())
145
+ if all(v is not None for qv2 in qv for v in qv2.values())
146
+ ]
147
+ mspec = ModuleSpec(dies_per_module=3)
148
+ modules = select_modules(functional, mspec, mc=400, seed=seed)
149
+ echo(f"== MCM assembly ({mspec.dies_per_module}-die chains, "
150
+ f">= {mspec.min_sep_MHz:.0f} MHz interface separation) ==")
151
+ echo(f" strict-KGD bin: {len(kgd)} dies (identical frequency plans -> "
152
+ f"chains collide); assembling from {len(functional)} functional dies:")
153
+ scored = sorted(
154
+ ((score_module(m, mspec, mc=800, seed=seed), m) for m in modules),
155
+ key=lambda x: -x[0])
156
+ for i, (p, mod) in enumerate(scored[:3]):
157
+ echo(f" best module {i + 1}: P(meets spec) = {p:.2f} "
158
+ f"[{', '.join(d['die_id'].rsplit(':', 1)[-1] for d in mod)}]")
159
+ if len(scored) > 3:
160
+ echo(f" ... {len(scored) - 3} more assembled; "
161
+ f"worst P = {scored[-1][0]:.2f} — pick with eyes open")
162
+
163
+ # 5 — integrity
164
+ n = sum(1 for _ in store.index())
165
+ bad = store.verify_all()
166
+ echo(f"\nstore: {n} records, "
167
+ f"{'all hashes verified' if not bad else f'{len(bad)} HASH FAILURES'}")
168
+ return 0 if not bad else 1
169
+ finally:
170
+ if tmp:
171
+ shutil.rmtree(tmp, ignore_errors=True)
@@ -0,0 +1,24 @@
1
+ """qtdf-exec — the cross-fridge test executive (stdlib only).
2
+
3
+ Plans (declarative, hashed) x adapters (carrier/backend/fridge) -> QTDF records
4
+ with deterministic identity; captures make any run a replayable CI fixture.
5
+ """
6
+ from .adapters import CassetteCarrier, DeviceSlot, FridgeProfile, ManualCarrier
7
+ from .backends import (
8
+ ReplayBackend,
9
+ VFridgeBackend,
10
+ read_capture,
11
+ slots_from_capture,
12
+ slots_from_wafer,
13
+ write_capture,
14
+ )
15
+ from .plan import disposition, load_plan, plan_hash, validate_plan
16
+ from .run import execute, record_id_for, replay
17
+
18
+ __all__ = [
19
+ "DeviceSlot", "FridgeProfile", "CassetteCarrier", "ManualCarrier",
20
+ "VFridgeBackend", "ReplayBackend",
21
+ "slots_from_wafer", "slots_from_capture", "read_capture", "write_capture",
22
+ "load_plan", "plan_hash", "validate_plan", "disposition",
23
+ "execute", "replay", "record_id_for",
24
+ ]