Edukaizen

Menu
  • Home
  • Projects
    • Simulate two slit experiment
    • LTC simulation
  • Hubbard 1D
    • Part 1: 1D Hubbard model
    • Part 2: Snake layout and fSWAP
    • Part 3: Qiskit and Fire Opal
    • Part 4: 120-qubit run
    • Part 5: Time-to-answer
    • Part 6: Tensor networks
    • Part 7: Majorana propagation
    • Part 8: Heatmaps
    • Part 9: 2D Hubbard outlook
  • Hubbard 2D
    • Part 1: 1D to 2D
    • Part 2: Cuprates
    • Part 3: 3×3
    • Part 4: Time
    • Part 5: 4×4
    • Part 6: 6×6 Fez
  • Hadron
    • Deel 1: Hadron op quantumprocessor
    • Deel 2: Quarks en confinement
    • Deel 3: SU(2) en LSH
    • Deel 4: Hamiltoniaan en circuit
    • Deel 5: Fire Opal
    • Deel 6: Klassieke simulaties
    • Deel 7: Quantumvoordeel
  • Quantum Tracker OLE Q80
    • Part 1: What we ran
    • Part 2: How OLE works
    • Part 3: Fire Opal and Kingston
    • Part 4: The tensor-network challenge
    • Part 5: Hawking and scrambling
    • Part 6: What the result proves
  • Random Graph
    • Part 1: Theory
    • Part 2: Circuit
    • Part 3: Qiskit
    • Part 4: Complexity
    • Part 5: Verification
    • Part 6: Workflow
    • Part 7: Conclusion
  • Advantage List
Menu

Implementing random-graph sampling in Qiskit

← Part 2 · Series overview · Part 4 →

A useful implementation should work at three levels: a small circuit that teaches the idea, a parser that reproduces the exact benchmark, and a guarded hardware path. The repository keeps those levels separate so that a tutorial run cannot be mistaken for the full experiment.

1. Build a small self-contained example

The following program combines the brickwork graph-state prefix, the face-state basis rotation and computational-basis measurement:

import math
import random

from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator

def random_graph_sampling_circuit(n, depth, seed=1729):
    rng = random.Random(seed)
    qc = QuantumCircuit(n)

    # |0...0> -> |+...+>
    qc.h(range(n))

    # Clifford graph-state prefix on a nearest-neighbour chain
    for layer in range(depth):
        for q in range(layer % 2, n - 1, 2):
            qc.cz(q, q + 1)

        for q in range(n):
            if rng.getrandbits(1):
                qc.s(q)
            qc.sx(q)

    # Non-Clifford face-state measurement basis
    theta = math.acos(1 / math.sqrt(3))
    for q in range(n):
        qc.tdg(q)
        qc.h(q)
        qc.rz(theta, q)
        qc.h(q)

    qc.measure_all()
    return qc

circuit = random_graph_sampling_circuit(n=8, depth=8)
backend = AerSimulator(method="statevector")
result = backend.run(
    circuit,
    shots=2048,
    seed_simulator=1729,
).result()

counts = result.get_counts()
print(counts)

The dictionary printed at the end maps bit strings to observed frequencies. Repeating the experiment estimates the distribution; it never constructs a list of all ideal probabilities on hardware.

2. Inspect the exact benchmark instead of regenerating it

The large instance has fixed random choices. Re-running a constructor with the same high-level recipe is not sufficient unless the original random stream, gate conventions and simplifications are identical. The repository therefore loads the supplied QASM and checks its cryptographic hash and circuit invariants.

/home/bram/.venvs/qiskit/bin/python \
  scripts/random_graph_sampling_runner.py inspect

/home/bram/.venvs/qiskit/bin/python \
  scripts/random_graph_sampling_runner.py validate

inspect reports width, depth and gate structure. validate checks the frozen files, nearest-neighbour CZ topology, ancilla connections and common measurement-basis suffix. A changed QASM file should fail validation rather than silently define a new experiment.

3. Run a bounded local smoke test

/home/bram/.venvs/qiskit/bin/python \
  scripts/random_graph_sampling_runner.py smoke \
  --start 0 \
  --qubits 8 \
  --shots 1024 \
  --seed 1729

The smoke command keeps only operations wholly contained in a selected interval. This produces a new small circuit that is practical to simulate exactly. It tests parsing, gate order, state construction and sampling. It is not the reduced density matrix or marginal distribution of the full circuit.

4. Compile before touching hardware

A hardware workflow should first transpile the circuit against a chosen backend and inspect the resulting layout, native two-qubit gates and depth. A fixed transpiler seed makes compilation reproducible:

compiled = transpile(
    circuit,
    backend=hardware_backend,
    optimization_level=3,
    seed_transpiler=1729,
)

print(compiled.layout)
print(compiled.count_ops())
print(compiled.depth())

Only after these checks should a separate submit command create a paid or scarce hardware job. The repository’s hardware runners therefore distinguish planning, validation, submission and retrieval.

What to change when experimenting

  • Increase n to study width.
  • Increase depth to spread stabilizers and entanglement.
  • Remove the face-state rotation to recover an efficiently simulable Clifford circuit.
  • Change the graph ensemble if the scientific question concerns Erdős–Rényi or random regular graphs rather than the supplied LNN ansatz.
  • Keep the seed fixed when comparing simulators or noise models.

Part 4 explains which circuit features create classical difficulty and why “large state vector” is not, by itself, a hardness argument.

← Part 2 · Series overview · Part 4 →

Recent Posts

  • Quantum Tracker OLE Q80, part 6: what the result proves and what comes next
  • Quantum Tracker OLE Q80, part 5: Hawking, black holes, and scrambling
  • Quantum Tracker OLE Q80, part 4: the tensor-network challenge
  • Quantum Tracker OLE Q80, part 3: Fire Opal on IBM Kingston
  • Quantum Tracker OLE Q80, part 2: how an Operator Loschmidt Echo works

Recent Comments

No comments to show.

Archives

  • July 2026
  • May 2026
  • March 2026
  • February 2026
  • September 2024

Categories

  • 10
  • Quantum Computing
  • Uncategorized
©2026 Edukaizen | Theme by SuperbThemes