Steven GellerQuantum Computing, End to End

Book contents

Current section

Part V. Practical Software

  1. Python and Quantum Programming Workflow
  2. Building a Small Simulator From Scratch
  3. Qiskit, Primitives, and Circuit Execution
  4. Cirq and Alternative Toolchains
  5. Transpilation and Hardware-Aware Compilation
  6. Noise Models and Noisy Simulation
  7. Benchmarking Quantum Programs
  8. Resource Estimation for Fault-Tolerant Algorithms
  9. Reproducible Quantum Labs

Part V. Practical Software · Chapter 36

Python and Quantum Programming Workflow

A quantum result you cannot rerun is a story, not evidence. This chapter sets up the Python workflow — pinned environments, runnable tests, explicit run records — that makes a quantum experiment reproducible enough for someone else to audit.

Lab
In this chapter 10 sections

Use a locked Python environment, separate invariant circuit fixtures from SDK adapters, execute every result through tests or scripts, and commit a run record containing the command, versions, seed, shots, backend, expected output, and known limits.

A screenshot records appearance. A notebook records one path through one interpreter. Neither tells a second engineer which commit ran, what was installed, or whether the observed counts were permitted by the circuit. The unit of work here is therefore a repository state plus an executable claim.

Reproducible experiment layerscircuitoracleSDKadapterbackendoptionsrawresultrunrecord
Figure 36.1. A mathematical oracle should survive an SDK migration. The adapter and backend record explain how that oracle became this particular output.

The experiment begins at a clean checkout

“Works on my machine” usually means the machine is an undocumented input. Remove it. The starting condition is a commit identifier, an unmodified working tree, a supported Python version, and a dependency lock whose hash is recorded before installation. The command must run from a named directory; relative paths and environment variables are part of its interface. Wall-clock time is reported in seconds, not as “fast,” and shots are a count, not a precision label.

The core companion package deliberately uses the Python standard library. Optional SDK adapters live outside that core because Qiskit’s supported execution interfaces are versioned software, not laws of quantum mechanics [Qiskit documentation]. Cirq has its own circuit, simulator, and result conventions and must be pinned independently [Cirq documentation]. A lock should be regenerated intentionally, reviewed as code, and tied to an API review date.

repository: quantum-end-to-end
commit: <40-hex-character revision>
python: 3.13.x
lock_sha256: <64 hex characters>
command: PYTHONPATH=src python -m quantum_end_to_end bell --shots 10000 --seed 7
cwd: labs

A portable circuit representation can help, but it has a boundary. OpenQASM 3 specifies circuit and control semantics subject to implementation support; it does not standardize an SDK’s Python objects, package resolver, displayed bit order, or backend calibration [OpenQASM 3 specification].

Invariant fixtures, adapters, and run records

Put three concerns in three places. An invariant fixture states what the circuit means: the Bell preparation has exact support on 00 and 11 and zero support on 01 and 10 when q0 is displayed as the most-significant bit. An adapter translates that contract into one SDK. A run record stores what one execution did. If an adapter changes, the fixture does not. If finite-shot counts move, the exact fixture does not.

The record needs enough evidence to falsify the claim: code and input hashes, interpreter and dependency versions, adapter and backend identity, transpiled representation where applicable, seed, shots, exact oracle, statistical acceptance rule, stdout or raw result, duration, and known omissions. ACM’s artifact framework distinguishes availability, functionality, reusability, and reproducibility; placing a zip online establishes only the first dimension [ACM artifact review guidance].

One Bell run, recorded end to end

The ideal state after Hadamard on q0 and CNOT(q0,q1) is

ψ=(00+11)/2|\psi\rangle=(|00\rangle+|11\rangle)/\sqrt{2}(36.1)

That equation yields exact probabilities 0.5, 0, 0, 0.5 in displayed order. It does not yield an exact 5,000/5,000 histogram. The seeded sampler makes the book’s transcript deterministic for regression purposes, while its scientific interpretation remains stochastic. The record must preserve both layers rather than replacing the probability oracle with one lucky histogram.

Clean-checkout Bell experiment and signed run record

Exemplar run record
FieldRecorded valueAcceptance
commandPYTHONPATH=src python -m quantum_end_to_end bell --shots 10000 --seed 7exit 0
exact support00, 1101=10=0
shots10,000counts sum exactly
provenancecommit, lock hash, Python, file hashesall present and re-hash
boundaryideal local simulatorno hardware inference
import hashlib, json
REQUIRED = {"python": str, "seed": int, "shots": int, "backend": str,
            "support": dict, "command": str}
def validate_record(record):
    errors = [name for name, kind in REQUIRED.items()
              if name not in record or type(record[name]) is not kind]
    support = record.get("support", {})
    if (record.get("shots", 0) <= 0 or not support or
            any(type(value) not in (int, float) or value < 0 for value in support.values()) or
            abs(sum(support.values()) - 1.0) > 1e-12):
        errors.append("shots_or_support")
    if errors:
        raise ValueError(",".join(sorted(set(errors))))
    payload = json.dumps(record, sort_keys=True, separators=(",", ":")).encode()
    return {"digest": hashlib.sha256(payload).hexdigest(),
            "outcomes": sorted(support), "probability_mass": sum(support.values())}

record = {"python": "3.13", "seed": 7, "shots": 10_000,
          "backend": "quantum_end_to_end.Statevector",
          "support": {"00": 0.5, "11": 0.5},
          "command": "PYTHONPATH=src python -m quantum_end_to_end bell --shots 10000 --seed 7"}
baseline = validate_record(record)
changed_seed = validate_record({**record, "seed": 8})
missing_rejected = False
try:
    validate_record({key: value for key, value in record.items() if key != "command"})
except ValueError:
    missing_rejected = True
assert baseline["outcomes"] == ["00", "11"] and baseline["probability_mass"] == 1.0
assert baseline["digest"] != changed_seed["digest"] and missing_rejected
print(f"PASS: 36 run record digest7={baseline['digest'][:12]} digest8={changed_seed['digest'][:12]} missing_command={missing_rejected}")

Reproduce: cd labs && python -m unittest discover -s tests -v, then run the recorded Bell command. The first command must pass; the second must match the committed seed-7 transcript and total 10,000 shots.

Failure drill: version drift versus semantic error

Version drift fails before or at the adapter boundary: an import moved, a result field changed, a backend option disappeared, or the lock no longer resolves on a supported interpreter. Preserve the traceback, installed versions, and failing adapter test. Do not “repair” the circuit oracle to make an SDK migration green.

A semantic error survives installation but violates the invariant fixture. A classic example is reversing the display convention: the SDK may return classical bits in a native order different from the book’s q0-most-significant strings. The fix belongs in normalization, with a truth-table test. Evidence differs: an environment fault is diagnosed by dependency and API records; a semantic fault is diagnosed by exact support, ordering, and circuit hashes.

The minimum publishable Python artifact

CI should start from the lock, run the stable suite, validate record schemas, regenerate hashes, and compare deterministic transcripts. A probabilistic claim needs a predeclared acceptance interval rather than byte equality. Optional adapters that cannot install must be reported as blocked or expired, not silently skipped. A publishable artifact also includes a license and tells the reviewer what the suite does not prove.

Treat the run record as an append-only observation, not a cache to be overwritten. A rerun under a new interpreter receives a new record and points back to the circuit fixture it tested. That preserves a useful distinction: identical source hashes with different dependency hashes diagnose environment sensitivity; different source hashes are a new experiment. CI can then compare records field by field and refuse the seductive but meaningless verdict that two JSON files are “close enough.”

The controlled failure belongs in the published evidence. In the drift branch, change only the declared dependency constraint and keep the commit, command, seed, shots, and Bell oracle fixed. A resolver or import failure is then an environment result. In the semantic branch, keep the environment fixed and reverse the bit normalization for an asymmetric fixture; that run should install cleanly and fail the oracle. These failures occupy different layers, so a reader can reproduce both without guessing what the author changed.

The boundary here is deliberate. This chapter establishes repository and evidence structure; it does not derive the state-vector implementation, and it does not nominate Qiskit, Cirq, or any one SDK as the book-wide programming model.

Repository drift drill

Prompt: Clone or copy the lab into an empty environment, run the Bell fixture, then introduce one dependency-version mismatch and document how the failure differs from a wrong expected distribution.

Deliverable: A JSON run record, command transcript, lock hash, and two-paragraph diagnosis of the controlled failure.

Pass condition: A fresh environment reproduces the stated exact state/probabilities and seeded counts, and the diagnosis identifies environment drift without changing the circuit oracle.

Solution record

Format: Checked exemplar run-record JSON plus a short failure transcript.

Verification: Validate required JSON keys and hashes, rerun the recorded command, and compare stdout byte-for-byte with the committed seeded fixture.

A passing submission keeps the Bell oracle unchanged while the intentionally mismatched environment fails at dependency resolution or adapter import. The diagnosis names the differing version and preserves both transcripts; it does not edit expected support or probabilities to accommodate the broken installation.

Companion work

Artifacts for this chapter

These entries resolve to checked-in local source. Commands are reproduced exactly from the chapter manifest, and source-embedded fixtures are exported as direct downloads.

  1. Reproduce or test

    python3 tools/validate_briefs.py --briefs data/editorial_briefs_36_63.json --from 36 --through 63 --check-rewritten-sources --execute-artifacts

Provenance

Sources and review

  1. IBM Quantum. Qiskit documentation. IBM. 2026official documentation
  2. Google Quantum AI. Cirq documentation. Google. 2026official documentation
  3. OpenQASM Technical Steering Committee. OpenQASM 3 specification. Linux Foundation Joint Development Foundation. 2026official technical specification
  4. Association for Computing Machinery. Artifact Review and Badging. ACM Publications. 2026official reproducibility policy

The load-bearing claims in the chapter are mapped inline to this registered source set. A citation supports only the bounded claim beside it.

Cite this chapter