BotShelf Vampire BOTSHELF VAMPIRE Register

Quantum: first real programs on a simulator

Short laptop projects that check themselves: H2 VQE, Bell noise, Grover, QAOA, Deutsch–Jozsa, GHZ, teleportation, RX⟨Z⟩, Bernstein–Vazirani, swap-test overlap, 3-qubit QFT and Ising ZZ — simulators only.

Research and education only. All recipes run on simulators. Results on real quantum hardware will be noisier and depend on the device — BSV does not claim hardware advantage.

Interactive tool, runs in your browser (English / Japanese)
Quantum state explorer →
Explore how phase changes ideal X and Z measurement probabilities.

Build recipes

Step-by-step workflows written by BSV. Each one combines real open tools into something you can make today.

Your first VQE: the H₂ ground-state energy on a simulator (PennyLane)

Tested by BSV

Build the 4-qubit hydrogen Hamiltonian, optimise a one-parameter double-excitation circuit and compare the result with exact diagonalisation of the same Hamiltonian.

Input
H–H bond length in ångström (default 0.74), optimiser steps (default 60).
Output
vqe_h2.csv (step, energy in hartree) and printed Hartree–Fock start, VQE result, exact value and error in mHa.
Prerequisites
Python 3.10 or later and pip install pennylane.
Steps
  1. Save the code below as vqe_h2_pennylane.py.
  2. Run python vqe_h2_pennylane.py.
  3. Plot vqe_h2.csv to watch the energy fall from the Hartree–Fock value.
  4. Scan the bond length (for example 0.5 to 2.0 Å) and plot energy against distance: that is a potential-energy curve.
  5. Try fewer steps or a different step size and see when the error rises above chemical accuracy (about 1.6 mHa).
Expected result
The VQE energy ends below the Hartree–Fock start and matches the exact value of the same 4-qubit Hamiltonian to well within chemical accuracy. Matching the exact value of a small-basis model is not the same as matching experiment.
Next step
Read PennyLane's VQE demo for other ansätze and optimisers, then try a slightly larger molecule. Open
Code (BSV original, MIT licence)

Download .py · vqe_h2_pennylane.py

#!/usr/bin/env python3
"""BSV recipe: your first VQE on a simulator — H2 ground-state energy with PennyLane.

Input : H-H bond length in angstrom (default 0.74), optimiser steps (default 60).
Output: vqe_h2.csv (step, energy in Hartree) and a printed comparison with exact diagonalisation
        of the same qubit Hamiltonian (STO-3G basis, 4 qubits). Simulator only (default.qubit).
Original BSV code, MIT. Library: PennyLane (Apache-2.0); Hamiltonian from PennyLane's built-in qchem (differentiable Hartree-Fock).
"""
import csv, sys
import pennylane as qml
from pennylane import numpy as np

def main(bond_angstrom="0.74", steps="60"):
    d = float(bond_angstrom) / 0.529177210903          # angstrom -> bohr (CODATA 2018 Bohr radius)
    symbols, coords = ["H", "H"], np.array([0.0, 0.0, -d / 2, 0.0, 0.0, d / 2])
    H, n_qubits = qml.qchem.molecular_hamiltonian(symbols, coords)
    hf = qml.qchem.hf_state(electrons=2, orbitals=n_qubits)
    dev = qml.device("default.qubit", wires=n_qubits)

    @qml.qnode(dev)
    def energy(theta):
        qml.BasisState(hf, wires=range(n_qubits))
        qml.DoubleExcitation(theta[0], wires=[0, 1, 2, 3])
        return qml.expval(H)

    opt = qml.GradientDescentOptimizer(stepsize=0.4)
    theta = np.array([0.0], requires_grad=True)
    trace = []
    for i in range(int(steps)):
        theta, e = opt.step_and_cost(energy, theta)
        trace.append((i, float(e)))
    final = float(energy(theta))
    exact = float(min(np.linalg.eigvalsh(qml.matrix(H, wire_order=range(n_qubits)))))
    with open("vqe_h2.csv", "w", newline="") as f:
        w = csv.writer(f); w.writerow(["step", "energy_hartree"]); w.writerows(trace)
    print(f"qubits {n_qubits}, terms {len(H.terms()[0])}")
    print(f"HF start   {trace[0][1]:.6f} Ha")
    print(f"VQE final  {final:.6f} Ha  (theta = {float(theta[0]):.4f})")
    print(f"exact      {exact:.6f} Ha  (same Hamiltonian, numpy eigvalsh)")
    print(f"error      {abs(final - exact) * 1000:.3f} mHa  (chemical accuracy is about 1.6 mHa)")

if __name__ == "__main__":
    main(*sys.argv[1:])
Output of BSV's own test run

Run on 2026-10-06 23:37 JST · Python 3.13.5, pennylane 0.45.1

qubits 4, terms 15
HF start   -1.116759 Ha
VQE final  -1.137284 Ha  (theta = 0.2256)
exact      -1.137284 Ha  (same Hamiltonian, numpy eigvalsh)
error      0.000 mHa  (chemical accuracy is about 1.6 mHa)

Bell pair: ideal versus noisy simulator (Qiskit Aer)

Tested by BSV

Entangle two qubits, measure them thousands of times, then add a simple depolarising and readout noise model and see how the correlations degrade.

Input
One- and two-qubit error rates (defaults 0.01 and 0.03 — example settings, not any real device's figures), shots.
Output
bell_counts.csv (outcome, ideal count, noisy count) and the share of correlated outcomes (00 + 11) for each run.
Prerequisites
Python 3.10 or later and pip install qiskit qiskit-aer.
Steps
  1. Save the code below as bell_noise_qiskit.py.
  2. Run python bell_noise_qiskit.py.
  3. Compare the two rows: the ideal run shows only 00 and 11; the noisy run also shows 01 and 10.
  4. Raise the two-qubit error, e.g. python bell_noise_qiskit.py 0.01 0.10, and watch the correlated share drop.
  5. Look up a real backend's published error rates on the IBM Quantum Platform and plug them in for a closer comparison.
Expected result
The ideal run splits roughly evenly between 00 and 11. The noisy run leaks into 01 and 10; how much depends on the rates you set.
Next step
Add a third qubit to make a GHZ state and see how noise scales with circuit size. Open
Code (BSV original, MIT licence)

Download .py · bell_noise_qiskit.py

#!/usr/bin/env python3
"""BSV recipe: a Bell pair on an ideal simulator vs. a simple noisy simulator (Qiskit + Qiskit Aer).

Input : one-qubit and two-qubit depolarising error rates (defaults 0.01 and 0.03 — example settings,
        not the figures of any real device), shots (default 4000).
Output: bell_counts.csv with counts per outcome for both runs, and the share of correlated outcomes (00 + 11).
Original BSV code, MIT. Libraries: Qiskit, Qiskit Aer (Apache-2.0).
"""
import csv, sys
from qiskit import QuantumCircuit, transpile
from qiskit_aer import AerSimulator
from qiskit_aer.noise import NoiseModel, depolarizing_error, ReadoutError

def main(p1="0.01", p2="0.03", shots="4000"):
    qc = QuantumCircuit(2, 2)
    qc.h(0); qc.cx(0, 1); qc.measure([0, 1], [0, 1])
    noise = NoiseModel()
    noise.add_all_qubit_quantum_error(depolarizing_error(float(p1), 1), ["h", "sx", "x", "rz"])
    noise.add_all_qubit_quantum_error(depolarizing_error(float(p2), 2), ["cx"])
    noise.add_all_qubit_readout_error(ReadoutError([[0.98, 0.02], [0.02, 0.98]]))
    results = {}
    for label, sim in (("ideal", AerSimulator(seed_simulator=7)), ("noisy", AerSimulator(noise_model=noise, seed_simulator=7))):
        tc = transpile(qc, sim)
        results[label] = sim.run(tc, shots=int(shots)).result().get_counts()
    with open("bell_counts.csv", "w", newline="") as f:
        w = csv.writer(f); w.writerow(["outcome", "ideal", "noisy"])
        for k in ("00", "01", "10", "11"):
            w.writerow([k, results["ideal"].get(k, 0), results["noisy"].get(k, 0)])
    for label, c in results.items():
        corr = (c.get("00", 0) + c.get("11", 0)) / int(shots)
        print(f"{label:5s} {dict(sorted(c.items()))}  correlated share {corr:.3f}")

if __name__ == "__main__":
    main(*sys.argv[1:])
Output of BSV's own test run

Run on 2026-10-06 23:37 JST · Python 3.13.5, qiskit 2.5.2, qiskit-aer 0.17.2

ideal {'00': 1998, '11': 2002}  correlated share 1.000
noisy {'00': 1891, '01': 100, '10': 116, '11': 1893}  correlated share 0.946

Grover search on 3 qubits: measured against theory (Qiskit)

Tested by BSV

Mark one bitstring with an oracle, amplify it with the diffuser and compare the measured success rate with the textbook formula.

Input
The marked bitstring (default 101), shots.
Output
grover_counts.csv and a printed line: measured success, theoretical success, random-guess probability.
Prerequisites
Python 3.10 or later and pip install qiskit.
Steps
  1. Save the code below as grover_qiskit.py.
  2. Run python grover_qiskit.py 101.
  3. Check that the marked string dominates grover_counts.csv.
  4. Try a 4-bit string (python grover_qiskit.py 1011): the script picks the iteration count for you.
  5. Change the code to run one iteration too many and watch the success rate fall — the 'overcooking' effect.
Expected result
Measured success close to the theoretical value and far above random guessing. Small differences come from finite shots.
Next step
Run the same circuit through the noisy simulator from the Bell-pair recipe and see how fast the advantage shrinks. Open
Code (BSV original, MIT licence)

Download .py · grover_qiskit.py

#!/usr/bin/env python3
"""BSV recipe: Grover search for one marked bitstring, measured vs. theory (Qiskit reference primitives).

Input : marked bitstring (default 101), shots (default 2000). Iterations = round(pi/4 * sqrt(N) - 1/2).
Output: grover_counts.csv and a printed success rate next to the textbook value sin^2((2k+1)*theta), sin(theta)=1/sqrt(N).
Original BSV code, MIT. Library: Qiskit (Apache-2.0). Simulator only (StatevectorSampler).
"""
import csv, math, sys
from qiskit import QuantumCircuit
from qiskit.circuit.library import MCXGate
from qiskit.primitives import StatevectorSampler

def oracle(qc, marked):
    n = len(marked)
    for i, bit in enumerate(reversed(marked)):     # qubit 0 = rightmost bit
        if bit == "0":
            qc.x(i)
    qc.h(n - 1); qc.append(MCXGate(n - 1), list(range(n))); qc.h(n - 1)
    for i, bit in enumerate(reversed(marked)):
        if bit == "0":
            qc.x(i)

def diffuser(qc, n):
    qc.h(range(n)); qc.x(range(n))
    qc.h(n - 1); qc.append(MCXGate(n - 1), list(range(n))); qc.h(n - 1)
    qc.x(range(n)); qc.h(range(n))

def main(marked="101", shots="2000"):
    n = len(marked); N = 2 ** n
    k = max(1, round(math.pi / 4 * math.sqrt(N) - 0.5))
    qc = QuantumCircuit(n)
    qc.h(range(n))
    for _ in range(k):
        oracle(qc, marked); diffuser(qc, n)
    qc.measure_all()
    counts = StatevectorSampler(seed=11).run([qc], shots=int(shots)).result()[0].data.meas.get_counts()
    with open("grover_counts.csv", "w", newline="") as f:
        w = csv.writer(f); w.writerow(["bitstring", "count"])
        for b, c in sorted(counts.items(), key=lambda kv: -kv[1]):
            w.writerow([b, c])
    theta = math.asin(1 / math.sqrt(N))
    print(f"N={N}, iterations={k}, marked={marked}")
    print(f"measured success {counts.get(marked, 0) / int(shots):.3f}  theory {math.sin((2 * k + 1) * theta) ** 2:.3f}  random guess {1 / N:.3f}")

if __name__ == "__main__":
    main(*sys.argv[1:])
Output of BSV's own test run

Run on 2026-10-06 23:37 JST · Python 3.13.5, qiskit 2.5.2

N=8, iterations=2, marked=101
measured success 0.947  theory 0.945  random guess 0.125

QAOA for Max-Cut on a small graph, checked by brute force (PennyLane)

Tested by BSV

Optimise a depth-2 QAOA circuit for a 5-node graph, read off the most likely cut and compare it with the true optimum found by trying every partition.

Input
Edge list (default 0-1,1-2,2-3,3-4,4-0,0-2), depth p, optimiser steps.
Output
qaoa_result.json: most likely bitstring, its cut size, brute-force maximum cut, probability on optimal cuts and the random-guess baseline.
Prerequisites
Python 3.10 or later and pip install pennylane networkx.
Steps
  1. Save the code below as qaoa_maxcut_pennylane.py.
  2. Run python qaoa_maxcut_pennylane.py.
  3. Compare its_cut with brute_force_max_cut and prob_on_optimal_cuts with random_guess_prob.
  4. Try p = 1 and p = 3 and see how the probability on optimal cuts changes.
  5. Use your own small graph (up to about 12 nodes stays fast on a laptop).
Expected result
The most likely bitstring is an optimal cut, and the probability on optimal cuts is clearly above the random baseline. For graphs this small a classical solver is faster — the point is to learn the workflow and verify it.
Next step
Compare against a classical heuristic on a 20-node graph and write down where each approach stands. Open
Code (BSV original, MIT licence)

Download .py · qaoa_maxcut_pennylane.py

#!/usr/bin/env python3
"""BSV recipe: QAOA for Max-Cut on a small graph, checked against brute force (PennyLane simulator).

Input : edge list (default: 5-node graph "0-1,1-2,2-3,3-4,4-0,0-2"), QAOA depth p (default 2), steps (default 80).
Output: qaoa_result.json with the most likely bitstring, its cut size, the brute-force optimum and the
        probability QAOA puts on optimal cuts.
Original BSV code, MIT. Library: PennyLane (Apache-2.0).
"""
import itertools, json, sys
import networkx as nx
import pennylane as qml
from pennylane import numpy as np

def cut(bits, edges):
    return sum(1 for a, b in edges if bits[a] != bits[b])

def main(edge_str="0-1,1-2,2-3,3-4,4-0,0-2", p="2", steps="80"):
    edges = [tuple(int(x) for x in e.split("-")) for e in edge_str.split(",")]
    g = nx.Graph(edges); n = g.number_of_nodes(); p = int(p)
    cost_h, mixer_h = qml.qaoa.maxcut(g)
    dev = qml.device("default.qubit", wires=n)

    def layers(params):
        for w in range(n):
            qml.Hadamard(wires=w)
        for gamma, alpha in params:
            qml.qaoa.cost_layer(gamma, cost_h)
            qml.qaoa.mixer_layer(alpha, mixer_h)

    @qml.qnode(dev)
    def cost(params):
        layers(params); return qml.expval(cost_h)

    @qml.qnode(dev)
    def probs(params):
        layers(params); return qml.probs(wires=range(n))

    np.random.seed(3)
    params = np.array(np.random.uniform(0, np.pi / 2, (p, 2)), requires_grad=True)
    opt = qml.AdamOptimizer(0.05)
    for _ in range(int(steps)):
        params = opt.step(cost, params)
    pr = probs(params)
    best_cut = max(cut(b, edges) for b in itertools.product([0, 1], repeat=n))
    top = int(np.argmax(pr)); top_bits = [int(x) for x in format(top, f"0{n}b")]
    p_opt = float(sum(pr[i] for i in range(2 ** n) if cut([int(x) for x in format(i, f"0{n}b")], edges) == best_cut))
    res = {"edges": edges, "p": p, "most_likely": "".join(map(str, top_bits)), "its_cut": cut(top_bits, edges),
           "brute_force_max_cut": best_cut, "prob_on_optimal_cuts": round(p_opt, 4),
           "random_guess_prob": round(sum(1 for b in itertools.product([0, 1], repeat=n) if cut(b, edges) == best_cut) / 2 ** n, 4)}
    json.dump(res, open("qaoa_result.json", "w"), indent=1)
    print(json.dumps(res))

if __name__ == "__main__":
    main(*sys.argv[1:])
Output of BSV's own test run

Run on 2026-10-06 23:37 JST · Python 3.13.5, pennylane 0.45.1

{"edges": [[0, 1], [1, 2], [2, 3], [3, 4], [4, 0], [0, 2]], "p": 2, "most_likely": "00101", "its_cut": 5, "brute_force_max_cut": 5, "prob_on_optimal_cuts": 0.7055, "random_guess_prob": 0.125}

Deutsch–Jozsa: constant vs balanced in one query

Tested by BSV

Run a 3-bit DJ circuit on Aer. Constant oracles peak on 000; balanced ones do not. Simulator education only.

Input
Oracle kind constant0|constant1|balanced (default balanced), shots (default 2000).
Output
dj_result.csv with counts, classified label and zero_share.
Prerequisites
Python 3 + qiskit + qiskit-aer (BSV fieldsvenv).
Steps
  1. Save the script.
  2. Run: python deutsch_jozsa_qiskit.py balanced
  3. Compare with: python deutsch_jozsa_qiskit.py constant0
Expected result
balanced → classified=balanced with near-zero 000 share; constant0 → classified=constant.
Next step
Prepare a 3-qubit GHZ state next. Open
Code (BSV original, MIT licence)

Download .py · deutsch_jozsa_qiskit.py

#!/usr/bin/env python3
"""BSV recipe: Deutsch–Jozsa — constant vs balanced with one oracle query (Qiskit Aer).

Research / education only. Simulator only — not a hardware-advantage claim.
Input : oracle kind constant0|constant1|balanced (default balanced), shots (default 2000).
Output: dj_result.csv with per-outcome counts, oracle_kind, classified, zero_share.
Original BSV code, MIT. Libraries: Qiskit, Qiskit Aer (Apache-2.0).
"""
import csv, sys
from qiskit import QuantumCircuit, transpile
from qiskit_aer import AerSimulator

def apply_oracle(qc, kind, n=3):
    if kind == "constant0":
        return
    if kind == "constant1":
        qc.x(n); return
    if kind == "balanced":
        for i in range(n):
            qc.cx(i, n)
        return
    raise SystemExit(f"unknown oracle kind {kind!r}")

def main(kind="balanced", shots="2000"):
    kind = (kind or "balanced").strip()
    shots = int(shots)
    n = 3
    qc = QuantumCircuit(n + 1, n)
    qc.x(n); qc.h(n)
    qc.h(range(n))
    apply_oracle(qc, kind, n)
    qc.h(range(n))
    qc.measure(range(n), range(n))
    sim = AerSimulator(seed_simulator=7)
    counts = sim.run(transpile(qc, sim), shots=shots).result().get_counts()
    zero_key = "0" * n
    zero = counts.get(zero_key, 0)
    classified = "constant" if zero / shots >= 0.5 else "balanced"
    with open("dj_result.csv", "w", newline="", encoding="utf-8") as f:
        w = csv.writer(f); w.writerow(["outcome", "count"])
        for k in sorted(counts):
            w.writerow([k, counts[k]])
        w.writerow(["oracle_kind", kind])
        w.writerow(["classified", classified])
        w.writerow(["zero_share", f"{zero / shots:.4f}"])
    print(f"oracle={kind} classified={classified} zero_share={zero/shots:.3f} -> dj_result.csv")

if __name__ == "__main__":
    main(*sys.argv[1:3])
Output of BSV's own test run

Run on 2026-10-07 02:24 JST · Python 3.13.5, qiskit 2.5.2, qiskit-aer 0.17.2

oracle=balanced classified=balanced zero_share=0.000 -> dj_result.csv

Measure a 3-qubit GHZ state

Tested by BSV

H+CX ladder to |GHZ⟩, then Z-basis shots. Ideal share of 000+111 approaches 1.0 on the simulator.

Input
Shots (default 4000).
Output
ghz_counts.csv.
Prerequisites
Python 3 + qiskit + qiskit-aer.
Steps
  1. Save the script.
  2. Run: python ghz_three_qiskit.py 4000
  3. Confirm almost all shots are 000 or 111.
Expected result
Correlated share printed near 1.0 on Aer with the default seed.
Next step
Run the teleportation protocol demo next. Open
Code (BSV original, MIT licence)

Download .py · ghz_three_qiskit.py

#!/usr/bin/env python3
"""BSV recipe: prepare a 3-qubit GHZ state and measure correlations (Qiskit Aer).

Research / education only. Simulator only.
Input : shots (default 4000).
Output: ghz_counts.csv with outcome counts and a printed GHZ-correlated share (000+111).
Original BSV code, MIT. Libraries: Qiskit, Qiskit Aer (Apache-2.0).
"""
import csv, sys
from qiskit import QuantumCircuit, transpile
from qiskit_aer import AerSimulator

def main(shots="4000"):
    shots = int(shots)
    qc = QuantumCircuit(3, 3)
    qc.h(0); qc.cx(0, 1); qc.cx(0, 2)
    qc.measure([0, 1, 2], [0, 1, 2])
    sim = AerSimulator(seed_simulator=7)
    counts = sim.run(transpile(qc, sim), shots=shots).result().get_counts()
    with open("ghz_counts.csv", "w", newline="", encoding="utf-8") as f:
        w = csv.writer(f); w.writerow(["outcome", "count"])
        for k in ("000", "001", "010", "011", "100", "101", "110", "111"):
            w.writerow([k, counts.get(k, 0)])
    corr = (counts.get("000", 0) + counts.get("111", 0)) / shots
    print(f"GHZ correlated share (000+111)={corr:.3f} -> ghz_counts.csv")

if __name__ == "__main__":
    main(*sys.argv[1:2])
Output of BSV's own test run

Run on 2026-10-07 02:24 JST · Python 3.13.5, qiskit 2.5.2, qiskit-aer 0.17.2

GHZ correlated share (000+111)=1.000 -> ghz_counts.csv

Teleportation protocol demo (simulator)

Tested by BSV

Alice prepares RX(θ)|0⟩, shares a Bell pair with Bob, and Bob's Z statistics match the ideal state. Education only — not a communications product.

Input
θ radians (default 0.7), shots (default 4000).
Output
teleport_counts.csv with empirical vs ideal bit shares.
Prerequisites
Python 3 + qiskit + qiskit-aer.
Steps
  1. Save the script.
  2. Run: python teleport_demo_qiskit.py 0.7 4000
  3. Compare Bob's P(0) to cos²(θ/2).
Expected result
Empirical shares within a few percent of the analytic column at 4000 shots.
Next step
Check PennyLane RX(θ) ⟨Z⟩ against cos(θ). Open
Code (BSV original, MIT licence)

Download .py · teleport_demo_qiskit.py

#!/usr/bin/env python3
"""BSV recipe: quantum teleportation protocol demo on a simulator (Qiskit Aer).

Research / education only. Shows Alice→Bob state transfer via entanglement + classical bits.
Not a communications product and not a hardware claim.

Input : Alice's initial RX angle radians (default 0.7), shots (default 4000).
Output: teleport_counts.csv of Bob's measured bit vs ideal Z-basis probabilities for the prepared state.
Original BSV code, MIT. Libraries: Qiskit, Qiskit Aer (Apache-2.0).
"""
import csv, math, sys
from qiskit import QuantumCircuit, transpile
from qiskit_aer import AerSimulator

def main(theta="0.7", shots="4000"):
    theta = float(theta); shots = int(shots)
    # q0 = message (Alice), q1 = Alice's Bell half, q2 = Bob
    qc = QuantumCircuit(3, 1)
    qc.rx(theta, 0)                 # message state
    qc.h(1); qc.cx(1, 2)            # Bell pair
    qc.cx(0, 1); qc.h(0)            # Bell measurement on Alice
    qc.cx(1, 2); qc.cz(0, 2)        # Bob corrections (deferred to gates for demo)
    qc.measure(2, 0)
    sim = AerSimulator(seed_simulator=7)
    counts = sim.run(transpile(qc, sim), shots=shots).result().get_counts()
    # Ideal |ψ> = RX(θ)|0> => P(0)=cos²(θ/2), P(1)=sin²(θ/2)
    p0 = math.cos(theta / 2) ** 2
    p1 = math.sin(theta / 2) ** 2
    c0 = counts.get("0", 0); c1 = counts.get("1", 0)
    with open("teleport_counts.csv", "w", newline="", encoding="utf-8") as f:
        w = csv.writer(f)
        w.writerow(["bit", "count", "empirical_share", "ideal_share"])
        w.writerow(["0", c0, f"{c0/shots:.4f}", f"{p0:.4f}"])
        w.writerow(["1", c1, f"{c1/shots:.4f}", f"{p1:.4f}"])
        w.writerow(["theta_rad", theta, "", ""])
    print(f"teleport θ={theta:.3f} Bob P0={c0/shots:.3f} (ideal {p0:.3f}) -> teleport_counts.csv")

if __name__ == "__main__":
    main(*sys.argv[1:3])
Output of BSV's own test run

Run on 2026-10-07 02:24 JST · Python 3.13.5, qiskit 2.5.2, qiskit-aer 0.17.2

teleport θ=0.700 Bob P0=0.881 (ideal 0.882) -> teleport_counts.csv

PennyLane RX(θ): ⟨Z⟩ vs cos(θ)

Tested by BSV

Sweep angles on default.qubit and write expval vs analytic cosine. Good unit check before larger VQE/QAOA work.

Input
Comma-separated radians (default 0,0.5,1,1.5,2,2.5,3).
Output
rx_expectation.csv with theta, expval_z, analytic_cos, abs_err.
Prerequisites
Python 3 + pennylane (BSV fieldsvenv).
Steps
  1. Save the script.
  2. Run: python pennylane_rx_expectation.py
  3. Confirm max abs_err is ~0 on the analytic simulator.
Expected result
Seven rows by default with abs_err near machine precision.
Next step
Recover a secret bitstring with Bernstein–Vazirani next. Open
Code (BSV original, MIT licence)

Download .py · pennylane_rx_expectation.py

#!/usr/bin/env python3
"""BSV recipe: PennyLane RX(θ) circuit — ⟨Z⟩ vs analytic cos(θ).

Research / education only. Default.qubit simulator — not hardware.
Input : comma-separated angles in radians (default 0,0.5,1,1.5,2,2.5,3).
Output: rx_expectation.csv with theta, expval_z, analytic_cos, abs_err.
Original BSV code, MIT. Library: PennyLane (Apache-2.0).
"""
import csv, math, sys
import pennylane as qml
from pennylane import numpy as np

def main(angles="0,0.5,1,1.5,2,2.5,3"):
    thetas = [float(x) for x in (angles or "0").split(",") if x.strip() != ""]
    dev = qml.device("default.qubit", wires=1)

    @qml.qnode(dev)
    def circ(theta):
        qml.RX(theta, wires=0)
        return qml.expval(qml.PauliZ(0))

    rows = []
    for th in thetas:
        val = float(circ(th))
        ana = math.cos(th)
        rows.append({"theta": th, "expval_z": val, "analytic_cos": ana, "abs_err": abs(val - ana)})
    with open("rx_expectation.csv", "w", newline="", encoding="utf-8") as f:
        w = csv.DictWriter(f, fieldnames=["theta", "expval_z", "analytic_cos", "abs_err"])
        w.writeheader(); w.writerows(rows)
    max_err = max(r["abs_err"] for r in rows)
    print(f"wrote {len(rows)} RX⟨Z⟩ points, max |err|={max_err:.2e} -> rx_expectation.csv")

if __name__ == "__main__":
    main(*sys.argv[1:2])
Output of BSV's own test run

Run on 2026-10-07 02:25 JST · Python 3.13.5, pennylane 0.45.1

wrote 7 RX⟨Z⟩ points, max |err|=1.25e-16 -> rx_expectation.csv

Bernstein–Vazirani: recover a secret string

Tested by BSV

One oracle query on Aer recovers an n-bit secret. Simulator education only — not a crypto break.

Input
Secret bitstring length 3–6 (default 101), shots (default 2000).
Output
bv_result.csv with counts, secret, recovered, match.
Prerequisites
Python 3 + qiskit + qiskit-aer (BSV fieldsvenv).
Steps
  1. Save the script.
  2. Run: python bernstein_vazirani_qiskit.py 101
  3. Confirm recovered equals secret.
Expected result
match=1 and top_share near 1.0 on Aer with the default seed.
Next step
Estimate state overlap with a swap test next. Open
Code (BSV original, MIT licence)

Download .py · bernstein_vazirani_qiskit.py

#!/usr/bin/env python3
"""BSV recipe: Bernstein–Vazirani — recover a secret bitstring with one oracle query (Qiskit Aer).

Research / education only. Simulator only — not a hardware-advantage claim.
Input : secret bitstring of length 3–6 using 0/1 (default 101), shots (default 2000).
Output: bv_result.csv with counts, secret, recovered, match.
Original BSV code, MIT. Libraries: Qiskit, Qiskit Aer (Apache-2.0).
"""
import csv, sys
from qiskit import QuantumCircuit, transpile
from qiskit_aer import AerSimulator

def main(secret="101", shots="2000"):
    secret = "".join(ch for ch in (secret or "101") if ch in "01")
    if not (3 <= len(secret) <= 6):
        raise SystemExit("secret must be 3–6 bits of 0/1")
    shots = int(shots)
    n = len(secret)
    qc = QuantumCircuit(n + 1, n)
    qc.x(n); qc.h(n)
    qc.h(range(n))
    for i, bit in enumerate(reversed(secret)):  # qiskit measure bit order: c0 = qubit 0
        if bit == "1":
            qc.cx(i, n)
    qc.h(range(n))
    qc.measure(range(n), range(n))
    sim = AerSimulator(seed_simulator=11)
    counts = sim.run(transpile(qc, sim), shots=shots).result().get_counts()
    # Aer returns bitstrings with qubit n-1 on the left
    recovered = max(counts, key=counts.get)
    # Map measured string (q_n-1 … q_0) to secret written left-to-right as s_{n-1}…s_0
    match = recovered == secret
    with open("bv_result.csv", "w", newline="", encoding="utf-8") as f:
        w = csv.writer(f); w.writerow(["outcome", "count"])
        for k in sorted(counts):
            w.writerow([k, counts[k]])
        w.writerow(["secret", secret])
        w.writerow(["recovered", recovered])
        w.writerow(["match", int(match)])
        w.writerow(["top_share", f"{counts[recovered] / shots:.4f}"])
    print(f"secret={secret} recovered={recovered} match={int(match)} top_share={counts[recovered]/shots:.3f} -> bv_result.csv")

if __name__ == "__main__":
    main(*sys.argv[1:3])
Output of BSV's own test run

Run on 2026-10-07 03:03 JST · Python 3.13.5, qiskit 2.5.2, qiskit-aer 0.17.2

secret=101 recovered=101 match=1 top_share=1.000 -> bv_result.csv

Swap test: estimate |⟨ψ|φ⟩|²

Tested by BSV

Ancilla swap test on two RX|0⟩ states. Compare 2P(0)−1 to cos²(Δθ/2). Education only.

Input
theta_psi, theta_phi radians (default 0.0 0.8), shots (default 4000).
Output
swap_test.csv with p0, overlap_est, analytic_overlap_sq, abs_err.
Prerequisites
Python 3 + qiskit + qiskit-aer.
Steps
  1. Save the script.
  2. Run: python swap_test_overlap_qiskit.py 0.0 0.8 4000
  3. Compare overlap_est to analytic_overlap_sq.
Expected result
At 4000 shots, abs_err typically a few percent of the analytic value.
Next step
Apply a 3-qubit QFT to a basis state next. Open
Code (BSV original, MIT licence)

Download .py · swap_test_overlap_qiskit.py

#!/usr/bin/env python3
"""BSV recipe: Swap test — estimate |⟨ψ|φ⟩|² from an ancilla (Qiskit Aer).

Research / education only. Simulator only.
Input : theta_psi, theta_phi in radians for RX|0⟩ states (defaults 0.0 0.8), shots (default 4000).
Output: swap_test.csv with p0, overlap_est, analytic_overlap_sq, abs_err.
Original BSV code, MIT. Libraries: Qiskit, Qiskit Aer (Apache-2.0).
"""
import csv, math, sys
from qiskit import QuantumCircuit, transpile
from qiskit_aer import AerSimulator

def main(theta_psi="0.0", theta_phi="0.8", shots="4000"):
    th_a, th_b = float(theta_psi), float(theta_phi)
    shots = int(shots)
    # wires: 0=ancilla, 1=psi, 2=phi
    qc = QuantumCircuit(3, 1)
    qc.rx(th_a, 1)
    qc.rx(th_b, 2)
    qc.h(0)
    qc.cswap(0, 1, 2)
    qc.h(0)
    qc.measure(0, 0)
    sim = AerSimulator(seed_simulator=13)
    counts = sim.run(transpile(qc, sim), shots=shots).result().get_counts()
    p0 = counts.get("0", 0) / shots
    # For pure states, P(0) = (1 + |⟨ψ|φ⟩|²) / 2  =>  |⟨|² = 2P0 - 1
    overlap_est = max(0.0, min(1.0, 2 * p0 - 1))
    # |0⟩ and RX(θ)|0⟩: ⟨0|RX(θ)|0⟩ = cos(θ/2); two RX states: cos((θa-θb)/2) up to global phase
    # ⟨ψ|φ⟩ for RX(a)|0⟩ and RX(b)|0⟩ = cos((a-b)/2) * (phase); magnitude cos((a-b)/2)
    ana = math.cos((th_a - th_b) / 2.0) ** 2
    err = abs(overlap_est - ana)
    with open("swap_test.csv", "w", newline="", encoding="utf-8") as f:
        w = csv.DictWriter(f, fieldnames=["theta_psi", "theta_phi", "p0", "overlap_est", "analytic_overlap_sq", "abs_err", "shots"])
        w.writeheader()
        w.writerow({"theta_psi": th_a, "theta_phi": th_b, "p0": p0, "overlap_est": overlap_est,
                    "analytic_overlap_sq": ana, "abs_err": err, "shots": shots})
    print(f"P(0)={p0:.3f} |overlap|²≈{overlap_est:.3f} analytic={ana:.3f} |err|={err:.3f} -> swap_test.csv")

if __name__ == "__main__":
    main(*sys.argv[1:4])
Output of BSV's own test run

Run on 2026-10-07 03:03 JST · Python 3.13.5, qiskit 2.5.2, qiskit-aer 0.17.2

P(0)=0.917 |overlap|²≈0.833 analytic=0.848 |err|=0.015 -> swap_test.csv

3-qubit QFT on |k⟩

Tested by BSV

Prepare |k⟩ (k=0..7), apply QFTGate, measure. Illustrates frequency spread — not Shor's algorithm.

Input
Basis integer k in 0..7 (default 3), shots (default 4000).
Output
qft_basis.csv with outcome counts and prepared_k.
Prerequisites
Python 3 + qiskit + qiskit-aer.
Steps
  1. Save the script.
  2. Run: python qft_basis_qiskit.py 3
  3. Compare histograms for k=0 vs k=3.
Expected result
Eight bins populated for generic k; k=0 concentrates on 000.
Next step
Check PennyLane ⟨Z⊗Z⟩ on two RX rotations next. Open
Code (BSV original, MIT licence)

Download .py · qft_basis_qiskit.py

#!/usr/bin/env python3
"""BSV recipe: 3-qubit QFT on a computational basis state (Qiskit Aer).

Prepare |k⟩, apply QFT, measure. Education only — illustrates frequency peaks, not Shor.
Input : basis integer k in 0..7 (default 3), shots (default 4000).
Output: qft_basis.csv with outcome counts and prepared_k.
Original BSV code, MIT. Libraries: Qiskit, Qiskit Aer (Apache-2.0).
"""
import csv, sys
from qiskit import QuantumCircuit, transpile
from qiskit_aer import AerSimulator
from qiskit.circuit.library import QFTGate

def main(k="3", shots="4000"):
    k = int(k); shots = int(shots)
    if not (0 <= k <= 7):
        raise SystemExit("k must be 0..7")
    n = 3
    qc = QuantumCircuit(n, n)
    for i in range(n):
        if (k >> i) & 1:
            qc.x(i)
    qc.append(QFTGate(n), range(n))
    qc.measure(range(n), range(n))
    sim = AerSimulator(seed_simulator=17)
    counts = sim.run(transpile(qc, sim), shots=shots).result().get_counts()
    with open("qft_basis.csv", "w", newline="", encoding="utf-8") as f:
        w = csv.writer(f); w.writerow(["outcome", "count", "share"])
        for outcome in sorted(counts):
            w.writerow([outcome, counts[outcome], f"{counts[outcome]/shots:.4f}"])
        w.writerow(["prepared_k", k, ""])
        w.writerow(["shots", shots, ""])
    top = max(counts, key=counts.get)
    print(f"|{k}> --QFT--> top={top} share={counts[top]/shots:.3f} ({len(counts)} bins) -> qft_basis.csv")

if __name__ == "__main__":
    main(*sys.argv[1:3])
Output of BSV's own test run

Run on 2026-10-07 03:03 JST · Python 3.13.5, qiskit 2.5.2, qiskit-aer 0.17.2

|3> --QFT--> top=111 share=0.136 (8 bins) -> qft_basis.csv

PennyLane Ising: ⟨Z⊗Z⟩ vs cos·cos

Tested by BSV

Two-wire RX then ⟨Z⊗Z⟩ on default.qubit; analytic cos(a)cos(b). Unit check before larger Ising/VQE work.

Input
Comma-separated a|b radian pairs (default 0.3|0.5,0.8|0.2,1.2|1.0).
Output
ising_zz.csv with theta_a, theta_b, expval_zz, analytic, abs_err.
Prerequisites
Python 3 + pennylane (BSV fieldsvenv).
Steps
  1. Save the script.
  2. Run: python pennylane_ising_zz.py
  3. Confirm max abs_err is ~0 on the analytic simulator.
Expected result
Three default rows with abs_err near machine precision.
Next step
Return to the H2 VQE, or revisit Grover / QAOA. Open
Code (BSV original, MIT licence)

Download .py · pennylane_ising_zz.py

#!/usr/bin/env python3
"""BSV recipe: PennyLane 2-qubit Ising — ⟨Z⊗Z⟩ after RX on both wires vs analytic.

Research / education only. default.qubit — not hardware.
Input : comma-separated theta pairs "a|b" (default 0.3|0.5,0.8|0.2,1.2|1.0).
Output: ising_zz.csv with theta_a, theta_b, expval_zz, analytic, abs_err.
Original BSV code, MIT. Library: PennyLane (Apache-2.0).
"""
import csv, math, sys
import pennylane as qml

def main(pairs="0.3|0.5,0.8|0.2,1.2|1.0"):
    items = []
    for part in (pairs or "").split(","):
        part = part.strip()
        if not part:
            continue
        a, b = part.split("|")
        items.append((float(a), float(b)))
    if not items:
        raise SystemExit("need at least one a|b pair")
    dev = qml.device("default.qubit", wires=2)

    @qml.qnode(dev)
    def circ(ta, tb):
        qml.RX(ta, wires=0)
        qml.RX(tb, wires=1)
        return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1))

    rows = []
    for ta, tb in items:
        val = float(circ(ta, tb))
        # ⟨Z⊗Z⟩ on RX(a)|0⟩⊗RX(b)|0⟩ = cos(a)cos(b)
        ana = math.cos(ta) * math.cos(tb)
        rows.append({"theta_a": ta, "theta_b": tb, "expval_zz": val, "analytic": ana, "abs_err": abs(val - ana)})
    with open("ising_zz.csv", "w", newline="", encoding="utf-8") as f:
        w = csv.DictWriter(f, fieldnames=["theta_a", "theta_b", "expval_zz", "analytic", "abs_err"])
        w.writeheader(); w.writerows(rows)
    max_err = max(r["abs_err"] for r in rows)
    print(f"wrote {len(rows)} Ising ZZ points, max |err|={max_err:.2e} -> ising_zz.csv")

if __name__ == "__main__":
    main(*sys.argv[1:2])
Output of BSV's own test run

Run on 2026-10-07 03:03 JST · Python 3.13.5, pennylane 0.45.1

wrote 3 Ising ZZ points, max |err|=3.33e-16 -> ising_zz.csv

Compare the tools

Difficulty is BSV's own rating for a first project. Check each licence on the official page before you ship anything.

ToolJobLicenceDifficultyLocal / cloud
QiskitBuild, transpile and run circuits; primitivesApache-2.0BeginnerLocal + cloud
Qiskit AerHigh-performance simulators with noise modelsApache-2.0IntermediateLocal
PennyLaneDifferentiable quantum programming, VQE and QMLApache-2.0BeginnerLocal + cloud
Qiskit textbook — Deutsch–JozsaDJ and other algorithm explainers (IBM Quantum Learning)Apache-2.0 / educationalBeginnerCloud / web API
PennyLane demosDifferentiable circuit demos on default.qubitApache-2.0BeginnerCloud / web API
CirqCircuits for near-term devicesApache-2.0IntermediateLocal
QuTiPDynamics of open quantum systemsBSD-3-ClauseAdvancedLocal
Amazon Braket SDKOne SDK for several hardware providers (paid service)Apache-2.0 (SDK)IntermediateCloud / web API

Starter stack: verified sources

Official pages only. BSV opened each link and recorded the HTTP status and date shown. We link out and summarise; we do not copy or rehost their code.

Related on BSV