Skip to content

Commit 71b86fe

Browse files
Revert "Simultaneous readout (#4307)"
This reverts commit eb72fb5.
1 parent 10c78c9 commit 71b86fe

File tree

5 files changed

+23
-269
lines changed

5 files changed

+23
-269
lines changed

cirq-core/cirq/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@
9494
)
9595

9696
from cirq.experiments import (
97-
estimate_parallel_single_qubit_readout_errors,
9897
estimate_single_qubit_readout_errors,
9998
hog_score_xeb_fidelity_from_probabilities,
10099
least_squares_xeb_fidelity_from_expectations,

cirq-core/cirq/experiments/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@
7272
)
7373

7474
from cirq.experiments.single_qubit_readout_calibration import (
75-
estimate_parallel_single_qubit_readout_errors,
7675
estimate_single_qubit_readout_errors,
7776
SingleQubitReadoutCalibrationResult,
7877
)

cirq-core/cirq/experiments/single_qubit_readout_calibration.py

Lines changed: 14 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,21 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14-
"""Single qubit readout experiments using parallel or isolated statistics."""
14+
15+
from typing import Any, Dict, Iterable, TYPE_CHECKING
16+
1517
import dataclasses
1618
import time
17-
from typing import Any, Dict, Iterable, List, Optional, TYPE_CHECKING
1819

19-
import sympy
2020
import numpy as np
21-
from cirq import circuits, ops, study
21+
22+
from cirq import circuits, ops
2223

2324
if TYPE_CHECKING:
2425
import cirq
2526

2627

27-
@dataclasses.dataclass
28+
@dataclasses.dataclass(frozen=True)
2829
class SingleQubitReadoutCalibrationResult:
2930
"""Result of estimating single qubit readout error.
3031
@@ -95,136 +96,19 @@ def estimate_single_qubit_readout_errors(
9596
the probabilities. Also stores a timestamp indicating the time when
9697
data was finished being collected from the sampler.
9798
"""
98-
num_qubits = len(list(qubits))
99-
return estimate_parallel_single_qubit_readout_errors(
100-
sampler=sampler,
101-
qubits=qubits,
102-
repetitions=repetitions,
103-
trials=2,
104-
bit_strings=np.array([[0] * num_qubits, [1] * num_qubits]),
105-
)
106-
107-
108-
def estimate_parallel_single_qubit_readout_errors(
109-
sampler: 'cirq.Sampler',
110-
*,
111-
qubits: Iterable['cirq.Qid'],
112-
trials: int = 20,
113-
repetitions: int = 1000,
114-
trials_per_batch: Optional[int] = None,
115-
bit_strings: np.ndarray = None,
116-
) -> SingleQubitReadoutCalibrationResult:
117-
"""Estimate single qubit readout error using parallel operations.
118-
119-
For each trial, prepare and then measure a random computational basis
120-
bitstring on qubits using gates in parallel.
121-
Returns a SingleQubitReadoutCalibrationResult which can be used to
122-
compute readout errors for each qubit.
123-
124-
Args:
125-
sampler: The `cirq.Sampler` used to run the circuits.
126-
qubits: The qubits being tested.
127-
repetitions: The number of measurement repetitions to perform for
128-
each trial.
129-
trials: The number of bitstrings to prepare.
130-
trials_per_batch: If provided, split the experiment into batches
131-
with this number of trials in each batch.
132-
bit_strings: Optional numpy array of shape (trials, qubits) where the
133-
first dimension is the number of the trial and the second
134-
dimension is the qubit (ordered by the qubit order from
135-
the qubits parameter). Each value should be a 0 or 1 which
136-
specifies which state the qubit should be prepared into during
137-
that trial. If not provided, the function will generate random
138-
bit strings for you.
139-
140-
Returns:
141-
A SingleQubitReadoutCalibrationResult storing the readout error
142-
probabilities as well as the number of repetitions used to estimate
143-
the probabilities. Also stores a timestamp indicating the time when
144-
data was finished being collected from the sampler. Note that,
145-
if there did not exist a trial where a given qubit was set to |0〉,
146-
the zero-state error will be set to `nan` (not a number). Likewise
147-
for qubits with no |1〉trial and one-state error.
148-
"""
14999
qubits = list(qubits)
150100

151-
if trials <= 0:
152-
raise ValueError("Must provide non-zero trials for readout calibration.")
153-
if repetitions <= 0:
154-
raise ValueError("Must provide non-zero repetition for readout calibration.")
155-
if bit_strings is None:
156-
bit_strings = np.random.randint(0, 2, size=(trials, len(qubits)))
157-
else:
158-
if not hasattr(bit_strings, 'shape') or bit_strings.shape != (trials, len(qubits)):
159-
raise ValueError(
160-
'bit_strings must be numpy array '
161-
f'of shape (trials, qubits) ({trials}, {len(qubits)}) '
162-
f"but was {bit_strings.shape if hasattr(bit_strings, 'shape') else None}"
163-
)
164-
if not np.all((bit_strings == 0) | (bit_strings == 1)):
165-
raise ValueError('bit_strings values must be all 0 or 1')
166-
if trials_per_batch is None:
167-
trials_per_batch = trials
168-
if trials_per_batch <= 0:
169-
raise ValueError("Must provide non-zero trials_per_batch for readout calibration.")
170-
171-
all_sweeps: List[study.Sweepable] = []
172-
num_batches = (trials + trials_per_batch - 1) // trials_per_batch
173-
174-
# Initialize circuits
175-
flip_symbols = sympy.symbols(f'flip_0:{len(qubits)}')
176-
flip_circuit = circuits.Circuit(
177-
[ops.X(q) ** s for q, s in zip(qubits, flip_symbols)],
178-
[ops.measure_each(*qubits, key_func=repr)],
101+
zeros_circuit = circuits.Circuit(ops.measure_each(*qubits, key_func=repr))
102+
ones_circuit = circuits.Circuit(
103+
ops.X.on_each(*qubits), ops.measure_each(*qubits, key_func=repr)
179104
)
180-
all_circuits = [flip_circuit] * num_batches
181-
182-
# Initialize sweeps
183-
for batch in range(num_batches):
184-
single_sweeps = []
185-
for qubit_idx in range(len(qubits)):
186-
trial_range = range(
187-
batch * trials_per_batch, min((batch + 1) * trials_per_batch, trials)
188-
)
189-
single_sweeps.append(
190-
study.Points(
191-
key=f'flip_{qubit_idx}',
192-
points=[bit_strings[bit][qubit_idx] for bit in trial_range],
193-
)
194-
)
195-
total_sweeps = study.Zip(*single_sweeps)
196-
all_sweeps.append(total_sweeps)
197-
198-
# Execute circuits
199-
results = sampler.run_batch(all_circuits, all_sweeps, repetitions=repetitions)
105+
106+
zeros_result = sampler.run(zeros_circuit, repetitions=repetitions)
107+
ones_result = sampler.run(ones_circuit, repetitions=repetitions)
200108
timestamp = time.time()
201109

202-
# Analyze results
203-
zero_state_trials = np.zeros((1, len(qubits)))
204-
one_state_trials = np.zeros((1, len(qubits)))
205-
zero_state_totals = np.zeros((1, len(qubits)))
206-
one_state_totals = np.zeros((1, len(qubits)))
207-
for batch_result in results:
208-
for trial_idx, trial_result in enumerate(batch_result):
209-
all_measurements = trial_result.data[[repr(x) for x in qubits]].to_numpy()
210-
sample_counts = np.einsum('ij->j', all_measurements)
211-
zero_state_trials += sample_counts * (1 - bit_strings[trial_idx])
212-
zero_state_totals += repetitions * (1 - bit_strings[trial_idx])
213-
one_state_trials += (repetitions - sample_counts) * bit_strings[trial_idx]
214-
one_state_totals += repetitions * bit_strings[trial_idx]
215-
216-
zero_state_errors = {
217-
q: zero_state_trials[0][qubit_idx] / zero_state_totals[0][qubit_idx]
218-
if zero_state_totals[0][qubit_idx] > 0
219-
else np.nan
220-
for qubit_idx, q in enumerate(qubits)
221-
}
222-
one_state_errors = {
223-
q: one_state_trials[0][qubit_idx] / one_state_totals[0][qubit_idx]
224-
if one_state_totals[0][qubit_idx] > 0
225-
else np.nan
226-
for qubit_idx, q in enumerate(qubits)
227-
}
110+
zero_state_errors = {q: np.mean(zeros_result.measurements[repr(q)]) for q in qubits}
111+
one_state_errors = {q: 1 - np.mean(ones_result.measurements[repr(q)]) for q in qubits}
228112

229113
return SingleQubitReadoutCalibrationResult(
230114
zero_state_errors=zero_state_errors,

cirq-core/cirq/experiments/single_qubit_readout_calibration_test.py

Lines changed: 8 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,14 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14+
1415
from typing import List
15-
import pytest
1616

1717
import numpy as np
1818

1919
import cirq
2020

2121

22-
def test_single_qubit_readout_result_repr():
23-
result = cirq.experiments.SingleQubitReadoutCalibrationResult(
24-
zero_state_errors={cirq.LineQubit(0): 0.1},
25-
one_state_errors={cirq.LineQubit(0): 0.2},
26-
repetitions=1000,
27-
timestamp=0.3,
28-
)
29-
cirq.testing.assert_equivalent_repr(result)
30-
31-
3222
class NoisySingleQubitReadoutSampler(cirq.Sampler):
3323
def __init__(self, p0: float, p1: float, seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None):
3424
"""Sampler that flips some bits upon readout.
@@ -89,129 +79,11 @@ def test_estimate_single_qubit_readout_errors_with_noise():
8979
assert isinstance(result.timestamp, float)
9080

9181

92-
def test_estimate_parallel_readout_errors_no_noise():
93-
qubits = cirq.LineQubit.range(10)
94-
sampler = cirq.Simulator()
95-
repetitions = 1000
96-
result = cirq.estimate_parallel_single_qubit_readout_errors(
97-
sampler, qubits=qubits, repetitions=repetitions
98-
)
99-
assert result.zero_state_errors == {q: 0 for q in qubits}
100-
assert result.one_state_errors == {q: 0 for q in qubits}
101-
assert result.repetitions == repetitions
102-
assert isinstance(result.timestamp, float)
103-
104-
105-
def test_estimate_parallel_readout_errors_all_zeros():
106-
qubits = cirq.LineQubit.range(10)
107-
sampler = cirq.ZerosSampler()
108-
repetitions = 1000
109-
result = cirq.estimate_parallel_single_qubit_readout_errors(
110-
sampler, qubits=qubits, repetitions=repetitions
111-
)
112-
assert result.zero_state_errors == {q: 0 for q in qubits}
113-
assert result.one_state_errors == {q: 1 for q in qubits}
114-
assert result.repetitions == repetitions
115-
assert isinstance(result.timestamp, float)
116-
117-
118-
def test_estimate_parallel_readout_errors_bad_bit_string():
119-
qubits = cirq.LineQubit.range(4)
120-
with pytest.raises(ValueError, match='but was None'):
121-
_ = cirq.estimate_parallel_single_qubit_readout_errors(
122-
cirq.ZerosSampler(),
123-
qubits=qubits,
124-
repetitions=1000,
125-
trials=35,
126-
trials_per_batch=10,
127-
bit_strings=[[1] * 4],
128-
)
129-
with pytest.raises(ValueError, match='0 or 1'):
130-
_ = cirq.estimate_parallel_single_qubit_readout_errors(
131-
cirq.ZerosSampler(),
132-
qubits=qubits,
133-
repetitions=1000,
134-
trials=2,
135-
bit_strings=np.array([[12, 47, 2, -4], [0.1, 7, 0, 0]]),
136-
)
137-
138-
139-
def test_estimate_parallel_readout_errors_zero_reps():
140-
qubits = cirq.LineQubit.range(10)
141-
with pytest.raises(ValueError, match='non-zero repetition'):
142-
_ = cirq.estimate_parallel_single_qubit_readout_errors(
143-
cirq.ZerosSampler(),
144-
qubits=qubits,
145-
repetitions=0,
146-
trials=35,
147-
trials_per_batch=10,
148-
)
149-
150-
151-
def test_estimate_parallel_readout_errors_zero_trials():
152-
qubits = cirq.LineQubit.range(10)
153-
with pytest.raises(ValueError, match='non-zero trials'):
154-
_ = cirq.estimate_parallel_single_qubit_readout_errors(
155-
cirq.ZerosSampler(),
156-
qubits=qubits,
157-
repetitions=1000,
158-
trials=0,
159-
trials_per_batch=10,
160-
)
161-
162-
163-
def test_estimate_parallel_readout_errors_zero_batch():
164-
qubits = cirq.LineQubit.range(10)
165-
with pytest.raises(ValueError, match='non-zero trials_per_batch'):
166-
_ = cirq.estimate_parallel_single_qubit_readout_errors(
167-
cirq.ZerosSampler(),
168-
qubits=qubits,
169-
repetitions=1000,
170-
trials=10,
171-
trials_per_batch=0,
172-
)
173-
174-
175-
def test_estimate_parallel_readout_errors_batching():
176-
qubits = cirq.LineQubit.range(10)
177-
sampler = cirq.ZerosSampler()
178-
repetitions = 1000
179-
result = cirq.estimate_parallel_single_qubit_readout_errors(
180-
sampler, qubits=qubits, repetitions=repetitions, trials=45, trials_per_batch=10
181-
)
182-
assert result.zero_state_errors == {q: 0.0 for q in qubits}
183-
assert result.one_state_errors == {q: 1.0 for q in qubits}
184-
assert result.repetitions == repetitions
185-
assert isinstance(result.timestamp, float)
186-
187-
188-
def test_estimate_parallel_readout_errors_with_noise():
189-
qubits = cirq.LineQubit.range(5)
190-
sampler = NoisySingleQubitReadoutSampler(p0=0.1, p1=0.2, seed=1234)
191-
repetitions = 1000
192-
result = cirq.estimate_parallel_single_qubit_readout_errors(
193-
sampler, qubits=qubits, repetitions=repetitions, trials=40
194-
)
195-
for error in result.one_state_errors.values():
196-
assert 0.17 < error < 0.23
197-
for error in result.zero_state_errors.values():
198-
assert 0.07 < error < 0.13
199-
assert result.repetitions == repetitions
200-
assert isinstance(result.timestamp, float)
201-
202-
203-
def test_estimate_parallel_readout_errors_missing_qubits():
204-
qubits = cirq.LineQubit.range(4)
205-
206-
result = cirq.estimate_parallel_single_qubit_readout_errors(
207-
cirq.ZerosSampler(),
208-
qubits=qubits,
209-
repetitions=2000,
210-
trials=1,
211-
bit_strings=np.array([[0] * 4]),
82+
def test_single_qubit_readout_calibration_result_repr():
83+
result = cirq.experiments.SingleQubitReadoutCalibrationResult(
84+
zero_state_errors={cirq.LineQubit(0): 0.1},
85+
one_state_errors={cirq.LineQubit(0): 0.2},
86+
repetitions=1000,
87+
timestamp=0.3,
21288
)
213-
assert result.zero_state_errors == {q: 0 for q in qubits}
214-
# Trial did not include a one-state
215-
assert all(np.isnan(result.one_state_errors[q]) for q in qubits)
216-
assert result.repetitions == 2000
217-
assert isinstance(result.timestamp, float)
89+
cirq.testing.assert_equivalent_repr(result)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
cirq.experiments.SingleQubitReadoutCalibrationResult(zero_state_errors={cirq.LineQubit(0): 0.1}, one_state_errors={cirq.LineQubit(0): 0.2}, repetitions=1000, timestamp=0.3)
1+
cirq.experiments.SingleQubitReadoutCalibrationResult(zero_state_errors={cirq.LineQubit(0): 0.1}, one_state_errors={cirq.LineQubit(0): 0.2}, repetitions=1000, timestamp=0.3)

0 commit comments

Comments
 (0)