Sampling and shots¶
Run a circuit for a number of shots and read the measurement counts.
import maestro
from maestro.circuits import QuantumCircuit
qc = QuantumCircuit()
qc.h(0)
qc.cx(0, 1)
qc.measure([(0, 0), (1, 1)])
result = qc.execute(shots=1000)
print(result["counts"])
{'11': 496, '00': 504}
counts maps each measured bitstring to the number of shots that produced it.
Warning
Maestro writes classical bit 0 first, on the left. Qiskit writes it last, on
the right. The same outcome reads "100" in Maestro and "001" in Qiskit.
flip = QuantumCircuit()
flip.x(0) # flip only qubit 0 of three
flip.measure([(0, 0), (1, 1), (2, 2)])
counts = flip.execute(shots=10)["counts"]
print("Maestro order:", counts)
print("Qiskit order: ", {bits[::-1]: n for bits, n in counts.items()})
Maestro order: {'100': 10}
Qiskit order: {'001': 10}
The result dictionary¶
execute returns a dictionary:
print(result)
{'counts': {'11': 496, '00': 504}, 'time_taken': 0.000438081, 'simulator': SimulatorType.QCSim, 'method': SimulationType.Statevector, 'sampling_components': 1, 'largest_component_qubits': 2}
Key |
Type |
Meaning |
|---|---|---|
|
|
Bitstring to number of shots |
|
|
Wall-clock execution time in seconds |
|
|
Backend that ran the circuit |
|
|
Simulation method that ran it |
|
|
Number of independent parts sampled separately; 1 unless |
|
|
Qubits in the largest of those parts |
simulator and method are enum members, so you can compare them directly:
print(result["simulator"], result["method"])
print(result["method"] == maestro.SimulationType.Statevector)
SimulatorType.QCSim SimulationType.Statevector
True
They also compare equal to the integers that releases up to 0.3.10 returned, so
existing checks such as result["method"] == 0 keep working.
Measurement¶
measure takes (qubit, classical bit) pairs. measure_all measures every qubit
into the classical bit with the same index. readout_error flips each recorded
result with that probability, like Stim’s M(p):
noisy_readout = QuantumCircuit()
noisy_readout.x(0)
noisy_readout.measure([(0, 0)], readout_error=0.1)
print(noisy_readout.execute(shots=1000)["counts"]) # about 10% read as 0
{'1': 900, '0': 100}
Mid-circuit measurement and reset¶
Measurements can sit anywhere in the circuit. Here qubit 0 is measured after the H gate, flipped, and measured again into a second bit, so the two bits always disagree:
mid = QuantumCircuit()
mid.h(0)
mid.measure([(0, 0)])
mid.x(0)
mid.measure([(0, 1)])
print(mid.execute(shots=1000)["counts"])
{'01': 514, '10': 486}
reset(qubit) and reset_qubits([...]) return qubits to |0⟩ mid-circuit:
reuse = QuantumCircuit()
reuse.x(0)
reuse.measure([(0, 0)])
reuse.reset(0)
reuse.measure([(0, 1)])
print(reuse.execute(shots=100)["counts"])
{'10': 100}
QuantumCircuit has no method for gates conditioned on a measurement. Write
classical feed-forward in OpenQASM 2.0 and run it with simple_execute. Here
qubit 1 is flipped only when qubit 0 measured 1:
feed_forward = """
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg c[2];
h q[0];
measure q[0] -> c[0];
if (c==1) x q[1];
measure q[1] -> c[1];
"""
print(maestro.simple_execute(feed_forward, shots=1000)["counts"])
{'11': 505, '00': 495}
Reproducible results¶
Set seed on the config to make sampling repeatable. Parallel jobs derive
distinct, deterministic random streams from it.
config = maestro.SimulatorConfig(seed=1234)
first = qc.execute(shots=1000, config=config)["counts"]
second = qc.execute(shots=1000, config=config)["counts"]
print(first, first == second)
{'11': 485, '00': 515} True