|
11 | 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12 | 12 | # See the License for the specific language governing permissions and
|
13 | 13 | # 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 | + |
15 | 17 | import dataclasses
|
16 | 18 | import time
|
17 |
| -from typing import Any, Dict, Iterable, List, Optional, TYPE_CHECKING |
18 | 19 |
|
19 |
| -import sympy |
20 | 20 | import numpy as np
|
21 |
| -from cirq import circuits, ops, study |
| 21 | + |
| 22 | +from cirq import circuits, ops |
22 | 23 |
|
23 | 24 | if TYPE_CHECKING:
|
24 | 25 | import cirq
|
25 | 26 |
|
26 | 27 |
|
27 |
| -@dataclasses.dataclass |
| 28 | +@dataclasses.dataclass(frozen=True) |
28 | 29 | class SingleQubitReadoutCalibrationResult:
|
29 | 30 | """Result of estimating single qubit readout error.
|
30 | 31 |
|
@@ -95,136 +96,19 @@ def estimate_single_qubit_readout_errors(
|
95 | 96 | the probabilities. Also stores a timestamp indicating the time when
|
96 | 97 | data was finished being collected from the sampler.
|
97 | 98 | """
|
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 |
| - """ |
149 | 99 | qubits = list(qubits)
|
150 | 100 |
|
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) |
179 | 104 | )
|
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) |
200 | 108 | timestamp = time.time()
|
201 | 109 |
|
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} |
228 | 112 |
|
229 | 113 | return SingleQubitReadoutCalibrationResult(
|
230 | 114 | zero_state_errors=zero_state_errors,
|
|
0 commit comments