quantbom 0.6.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.
Files changed (45) hide show
  1. quantbom/__init__.py +497 -0
  2. quantbom/_bitorder.py +121 -0
  3. quantbom/_circuit.py +77 -0
  4. quantbom/_collect.py +661 -0
  5. quantbom/_drift.py +333 -0
  6. quantbom/_fidelity.py +280 -0
  7. quantbom/_merge.py +187 -0
  8. quantbom/_qir.py +224 -0
  9. quantbom/_scan.py +118 -0
  10. quantbom/_sign.py +284 -0
  11. quantbom/_stats.py +125 -0
  12. quantbom/_verify.py +530 -0
  13. quantbom/actions/__init__.py +27 -0
  14. quantbom/actions/base.py +68 -0
  15. quantbom/actions/benchmark.py +53 -0
  16. quantbom/actions/bind_backend.py +71 -0
  17. quantbom/actions/capture.py +106 -0
  18. quantbom/actions/check_qir.py +44 -0
  19. quantbom/actions/check_results.py +75 -0
  20. quantbom/actions/compare_native.py +88 -0
  21. quantbom/actions/disclose_error_handling.py +66 -0
  22. quantbom/actions/execute.py +85 -0
  23. quantbom/actions/inventory.py +24 -0
  24. quantbom/actions/layout.py +118 -0
  25. quantbom/actions/prove_equivalence.py +225 -0
  26. quantbom/actions/record_provenance.py +49 -0
  27. quantbom/actions/verify_conversion.py +53 -0
  28. quantbom/attest.py +363 -0
  29. quantbom/cli.py +378 -0
  30. quantbom/frameworks/__init__.py +67 -0
  31. quantbom/frameworks/base.py +155 -0
  32. quantbom/frameworks/braket.py +132 -0
  33. quantbom/frameworks/cirq.py +106 -0
  34. quantbom/frameworks/cudaq.py +223 -0
  35. quantbom/frameworks/pennylane.py +116 -0
  36. quantbom/frameworks/qiskit.py +67 -0
  37. quantbom/frameworks/qrisp.py +267 -0
  38. quantbom/harness.json +1730 -0
  39. quantbom/harness.py +88 -0
  40. quantbom-0.6.0.dist-info/METADATA +207 -0
  41. quantbom-0.6.0.dist-info/RECORD +45 -0
  42. quantbom-0.6.0.dist-info/WHEEL +5 -0
  43. quantbom-0.6.0.dist-info/entry_points.txt +2 -0
  44. quantbom-0.6.0.dist-info/licenses/LICENSE +202 -0
  45. quantbom-0.6.0.dist-info/top_level.txt +1 -0
quantbom/_circuit.py ADDED
@@ -0,0 +1,77 @@
1
+ """Preconditions on the circuit being certified (HARNESS_KITS_PLAN.md K0).
2
+
3
+ The certified object must be the circuit that ran. Two properties decide what
4
+ the harness can honestly say about it, before any evidence is written:
5
+
6
+ * **closed** — every parameter bound. An unbound `Parameter` cannot be
7
+ executed; certifying it once produced a "measured" result for a run that
8
+ never happened, and QBOM-BENCH-02 then blamed the circuit. Refused here.
9
+ * **dynamic** — mid-circuit measurement or reset, or classical control flow
10
+ (`if_test`, `while_loop`, `for_loop`, `switch`). Accepted (decision
11
+ 2 Oct 2026), and recorded, because it changes how the circuit can be
12
+ executed (qiskit-aer) and proved equivalent (QCEC's dynamic-circuit
13
+ transformation).
14
+ """
15
+ from __future__ import annotations
16
+
17
+ from typing import Any
18
+
19
+ CONTROL_FLOW = {"if_else", "while_loop", "for_loop", "switch_case", "box"}
20
+ _PASSIVE = {"barrier", "delay"}
21
+
22
+
23
+ def inspect_circuit(qc: Any) -> dict[str, Any]:
24
+ """{'unbound_parameters': [...], 'dynamic': bool, 'dynamic_features': [...]}."""
25
+ features: set[str] = set()
26
+ measured: set[int] = set() # qubits measured so far
27
+ touched: set[int] = set() # qubits any operation has acted on
28
+ index = {q: i for i, q in enumerate(qc.qubits)}
29
+ for inst in qc.data:
30
+ name = inst.operation.name
31
+ qubits = [index[q] for q in inst.qubits]
32
+ if name in CONTROL_FLOW:
33
+ features.add("classical-control-flow")
34
+ elif name == "measure":
35
+ measured.update(qubits)
36
+ elif name == "reset":
37
+ if any(q in touched for q in qubits):
38
+ features.add("mid-circuit-reset")
39
+ elif name not in _PASSIVE and any(q in measured for q in qubits):
40
+ features.add("mid-circuit-measurement")
41
+ if name in CONTROL_FLOW and measured:
42
+ features.add("mid-circuit-measurement")
43
+ touched.update(qubits)
44
+ return {
45
+ "unbound_parameters": sorted(str(p) for p in getattr(qc, "parameters", ())),
46
+ "dynamic": bool(features),
47
+ "dynamic_features": sorted(features),
48
+ }
49
+
50
+
51
+ def require_closed(info: dict[str, Any]) -> None:
52
+ refuse_open(info["unbound_parameters"])
53
+
54
+
55
+ def refuse_dynamic(features: list[str], framework: str) -> None:
56
+ """A dynamic program from an adapter that does not claim dynamic support.
57
+
58
+ The adapter's lowering is conformance-tested for static circuits only, so
59
+ certifying this program would rest on an unverified translation. Refused
60
+ with the same wording for every framework; Qiskit input is accepted and
61
+ other frameworks gain support through their own conformance fixture (C7)."""
62
+ if features:
63
+ raise ValueError(
64
+ f"{framework} programs with {', '.join(features)} are not supported yet: the {framework} adapter's "
65
+ "lowering is conformance-tested for static circuits only, so a dynamic circuit cannot be certified "
66
+ "on it. Dynamic circuits are accepted as Qiskit input (qiskit.QuantumCircuit)"
67
+ )
68
+
69
+
70
+ def refuse_open(unbound: list[str], how: str = "qc.assign_parameters({...})") -> None:
71
+ """One refusal, worded the same for every framework."""
72
+ if unbound:
73
+ shown = ", ".join(unbound[:5]) + (" …" if len(unbound) > 5 else "")
74
+ raise ValueError(
75
+ f"the circuit has {len(unbound)} unbound parameter(s) ({shown}). Bind them before certifying "
76
+ f"(e.g. {how}): the certified circuit must be the circuit that ran"
77
+ )