Skip to content

Sample with Cirq

examples/cirq_sampler.py

Run existing Cirq code through cirq.work.Sampler. QubitraSampler is a cirq.work.Sampler, so code written against Cirq’s sampler interface runs on the platform by swapping the sampler object and nothing else. The example samples a Bell pair, then sweeps a rotation over five points as one job.

Needs the extra: pip install 'qubitra-sdk[cirq]'.

import cirq
import sympy
from qubitra import QubitraClient
from qubitra.cirq import QubitraSampler
with QubitraClient() as client:
sampler = QubitraSampler(client, backend_id=backend_id)
q0, q1 = cirq.LineQubit.range(2)

QubitraSampler wraps a client and a backend id. Everything after this line is ordinary Cirq.

# -- a Bell pair, through cirq's single-run entry point -------------------
bell = cirq.Circuit([cirq.H(q0), cirq.CNOT(q0, q1), cirq.measure(q0, q1, key="m")])
print(f"running a Bell pair on {backend_id} ({SHOTS} shots)")
result = sampler.run(bell, repetitions=SHOTS)
histogram = Counter("".join(str(bit) for bit in row) for row in result.measurements["m"])
print()
for bits, count in sorted(histogram.items()):
bar = "█" * round(40 * count / SHOTS)
print(f" {bits} {count:5d} {bar}")

sampler.run returns a cirq.Result whose measurements["m"] is one row per shot. The example folds the rows into a histogram; an even split across 00 and 11 says the entangling really happened.

# -- a sweep: every point in ONE job --------------------------------------
theta = sympy.Symbol("theta")
rotation = cirq.Circuit([cirq.ry(theta)(q0), cirq.measure(q0, key="m")])
sweep = cirq.Linspace("theta", start=0.0, stop=math.pi, length=SWEEP_POINTS)
print(f"\nsweeping theta over {SWEEP_POINTS} points, as one multi-PUB job")
results = sampler.run_sweep(rotation, params=sweep, repetitions=SHOTS)

The parameter is a sympy.Symbol and the sweep a cirq.Linspace, Cirq’s own sweep vocabulary. Cirq exports OpenQASM 2, which carries no parameters, so the sampler resolves each point locally and submits the whole sweep as one multi-PUB job — five points is one round trip.

print()
for point in results:
angle = float(point.params.value_of("theta"))
p_one = float(point.measurements["m"].mean())
expected = math.sin(angle / 2) ** 2
bar = "█" * round(20 * p_one)
print(f" theta={angle:5.3f} P(1)={p_one:.3f} (sin²={expected:.3f}) {bar}")
return 0

run_sweep returns one result per point, each carrying its parameter binding. Rotating by theta about Y walks P(1) along sin²(θ/2), a curve you can check by eye.

Terminal window
QUBITRA_API_KEY=qpk_... python examples/cirq_sampler.py

Output from a live run:

running a Bell pair on sim-statevector-26q (1024 shots)
00 510 ████████████████████
11 514 ████████████████████
sweeping theta over 5 points, as one multi-PUB job
theta=0.000 P(1)=0.000 (sin²=0.000)
theta=0.785 P(1)=0.171 (sin²=0.146) ███
theta=1.571 P(1)=0.490 (sin²=0.500) ██████████
theta=2.356 P(1)=0.850 (sin²=0.854) █████████████████
theta=3.142 P(1)=1.000 (sin²=1.000) ████████████████████

The Bell counts split evenly, and the five sampled probabilities track sin²(θ/2) to within shot noise.

  • Cirq — the full adapter: export, bit order, and sweep batching.
  • Primitives and PUBs — the multi-PUB job the sweep becomes.