|
12 | 12 | # See the License for the specific language governing permissions and
|
13 | 13 | # limitations under the License.
|
14 | 14 |
|
15 |
| -from typing import ( |
16 |
| - Any, |
17 |
| - Dict, |
18 |
| - Iterable, |
19 |
| - List, |
20 |
| - Mapping, |
21 |
| - NamedTuple, |
22 |
| - Optional, |
23 |
| - Sequence, |
24 |
| - Set, |
25 |
| - TYPE_CHECKING, |
26 |
| - Tuple, |
27 |
| - Union, |
28 |
| -) |
| 15 | +from typing import Any, Dict, List, Mapping, NamedTuple, Optional, Sequence, TYPE_CHECKING, Tuple |
29 | 16 | import dataclasses
|
30 | 17 | import numpy as np
|
31 | 18 | from matplotlib import pyplot as plt
|
32 |
| -from cirq import _compat, _import, circuits, devices, ops, protocols, sim, value, work |
| 19 | +from cirq import _compat, _import, protocols |
33 | 20 |
|
34 | 21 | if TYPE_CHECKING:
|
35 | 22 | import cirq
|
@@ -280,344 +267,3 @@ def __iter__(self):
|
280 | 267 |
|
281 | 268 | def __len__(self):
|
282 | 269 | return len(self.results)
|
283 |
| - |
284 |
| - |
285 |
| -@_compat.deprecated( |
286 |
| - deadline='v0.16', fix=('Use cirq.experiments.xeb_fitting.benchmark_2q_xeb_fidelities instead') |
287 |
| -) |
288 |
| -def cross_entropy_benchmarking( |
289 |
| - sampler: work.Sampler, |
290 |
| - qubits: Sequence[ops.Qid], |
291 |
| - *, |
292 |
| - benchmark_ops: Sequence[circuits.Moment] = None, |
293 |
| - num_circuits: int = 20, |
294 |
| - repetitions: int = 1000, |
295 |
| - cycles: Union[int, Iterable[int]] = range(2, 103, 10), |
296 |
| - scrambling_gates_per_cycle: List[List[ops.Gate]] = None, |
297 |
| - simulator: sim.Simulator = None, |
298 |
| -) -> CrossEntropyResult: |
299 |
| - r"""Cross-entropy benchmarking (XEB) of multiple qubits. |
300 |
| -
|
301 |
| - A total of M random circuits are generated, each of which comprises N |
302 |
| - layers where N = max('cycles') or 'cycles' if a single value is specified |
303 |
| - for the 'cycles' parameter. Every layer contains randomly generated |
304 |
| - single-qubit gates applied to each qubit, followed by a set of |
305 |
| - user-defined benchmarking operations (e.g. a set of two-qubit gates). |
306 |
| -
|
307 |
| - Each circuit (circuit_m) from the M random circuits is further used to |
308 |
| - generate a set of circuits {circuit_mn}, where circuit_mn is built from the |
309 |
| - first n cycles of circuit_m. n spans all the values in 'cycles'. |
310 |
| -
|
311 |
| - For each fixed value n, the experiment performs the following: |
312 |
| -
|
313 |
| - 1) Experimentally collect a number of bit-strings for each circuit_mn via |
314 |
| - projective measurements in the z-basis. |
315 |
| -
|
316 |
| - 2) Theoretically compute the expected bit-string probabilities |
317 |
| - $P^{th, mn}_|...00>$, $P^{th, mn}_|...01>$, $P^{th, mn}_|...10>$, |
318 |
| - $P^{th, mn}_|...11>$ ... at the end of circuit_mn for all m and for all |
319 |
| - possible bit-strings in the Hilbert space. |
320 |
| -
|
321 |
| - 3) Compute an experimental XEB function for each circuit_mn: |
322 |
| -
|
323 |
| - $f_{mn}^{meas} = \langle D * P^{th, mn}_q - 1 \rangle$ |
324 |
| -
|
325 |
| - where D is the number of states in the Hilbert space, $P^{th, mn}_q$ is the |
326 |
| - theoretical probability of a bit-string q at the end of circuit_mn, and |
327 |
| - $\langle \rangle$ corresponds to the ensemble average over all measured |
328 |
| - bit-strings. |
329 |
| -
|
330 |
| - Then, take the average of $f_{mn}^{meas}$ over all circuit_mn with fixed |
331 |
| - n to obtain: |
332 |
| -
|
333 |
| - $f_{n}^{meas} = (\sum_m f_{mn}^{meas}) / M$ |
334 |
| -
|
335 |
| - 4) Compute a theoretical XEB function for each circuit_mn: |
336 |
| -
|
337 |
| - $f_{mn}^{th} = D \sum_q (P^{th, mn}_q) ** 2 - 1$ |
338 |
| -
|
339 |
| - where the summation goes over all possible bit-strings q in the Hilbert |
340 |
| - space. |
341 |
| -
|
342 |
| - Similarly, we then average $f_m^{th}$ over all circuit_mn with fixed n to |
343 |
| - obtain: |
344 |
| -
|
345 |
| - $f_{n}^{th} = (\sum_m f_{mn}^{th}) / M$ |
346 |
| -
|
347 |
| - 5) Calculate the XEB fidelity $\alpha_n$ at fixed n: |
348 |
| -
|
349 |
| - $\alpha_n = f_{n} ^ {meas} / f_{n} ^ {th}$ |
350 |
| -
|
351 |
| - Args: |
352 |
| - sampler: The quantum engine or simulator to run the circuits. |
353 |
| - qubits: The qubits included in the XEB experiment. |
354 |
| - benchmark_ops: A sequence of circuits.Moment containing gate operations |
355 |
| - between specific qubits which are to be benchmarked for fidelity. |
356 |
| - If more than one circuits.Moment is specified, the random circuits |
357 |
| - will rotate between the circuits.Moment's. As an example, |
358 |
| - if benchmark_ops = [Moment([ops.CZ(q0, q1), ops.CZ(q2, q3)]), |
359 |
| - Moment([ops.CZ(q1, q2)]) where q0, q1, q2 and q3 are instances of |
360 |
| - Qid (such as GridQubits), each random circuit will apply CZ gate |
361 |
| - between q0 and q1 plus CZ between q2 and q3 for the first cycle, |
362 |
| - CZ gate between q1 and q2 for the second cycle, CZ between q0 and |
363 |
| - q1 and CZ between q2 and q3 for the third cycle and so on. If |
364 |
| - None, the circuits will consist only of single-qubit gates. |
365 |
| - num_circuits: The total number of random circuits to be used. |
366 |
| - repetitions: The number of measurements for each circuit to estimate |
367 |
| - the bit-string probabilities. |
368 |
| - cycles: The different numbers of circuit layers in the XEB study. |
369 |
| - Could be a single or a collection of values. |
370 |
| - scrambling_gates_per_cycle: If None (by default), the single-qubit |
371 |
| - gates are chosen from X/2 ($\pi/2$ rotation around the X axis), |
372 |
| - Y/2 ($\pi/2$ rotation around the Y axis) and (X + Y)/2 ($\pi/2$ |
373 |
| - rotation around an axis $\pi/4$ away from the X on the equator of |
374 |
| - the Bloch sphere). Otherwise the single-qubit gates for each layer |
375 |
| - are chosen from a list of possible choices (each choice is a list |
376 |
| - of one or more single-qubit gates). |
377 |
| - simulator: A simulator that calculates the bit-string probabilities |
378 |
| - of the ideal circuit. By default, this is set to sim.Simulator(). |
379 |
| -
|
380 |
| - Returns: |
381 |
| - A CrossEntropyResult object that stores and plots the result. |
382 |
| - """ |
383 |
| - simulator = sim.Simulator() if simulator is None else simulator |
384 |
| - num_qubits = len(qubits) |
385 |
| - |
386 |
| - if isinstance(cycles, int): |
387 |
| - cycle_range = [cycles] |
388 |
| - else: |
389 |
| - cycle_range = list(cycles) |
390 |
| - |
391 |
| - # These store the measured and simulated bit-string probabilities from |
392 |
| - # all trials in two dictionaries. The keys of the dictionaries are the |
393 |
| - # numbers of cycles. The values are 2D arrays with each row being the |
394 |
| - # probabilities obtained from a single trial. |
395 |
| - probs_meas = {n: np.zeros((num_circuits, 2**num_qubits)) for n in cycle_range} |
396 |
| - probs_th = {n: np.zeros((num_circuits, 2**num_qubits)) for n in cycle_range} |
397 |
| - |
398 |
| - for k in range(num_circuits): |
399 |
| - |
400 |
| - # Generates one random XEB circuit with max(num_cycle_range) cycles. |
401 |
| - # Then the first n cycles of the circuit are taken to generate |
402 |
| - # shorter circuits with n cycles (n taken from cycles). All of these |
403 |
| - # circuits are stored in circuits_k. |
404 |
| - circuits_k = _build_xeb_circuits( |
405 |
| - qubits, cycle_range, scrambling_gates_per_cycle, benchmark_ops |
406 |
| - ) |
407 |
| - |
408 |
| - # Run each circuit with the sampler to obtain a collection of |
409 |
| - # bit-strings, from which the bit-string probabilities are estimated. |
410 |
| - probs_meas_k = _measure_prob_distribution(sampler, repetitions, qubits, circuits_k) |
411 |
| - |
412 |
| - # Simulate each circuit with the Cirq simulator to obtain the |
413 |
| - # state vector at the end of each circuit, from which the |
414 |
| - # theoretically expected bit-string probabilities are obtained. |
415 |
| - probs_th_k: List[np.ndarray] = [] |
416 |
| - for circ_k in circuits_k: |
417 |
| - res = simulator.simulate(circ_k, qubit_order=qubits) |
418 |
| - state_probs = value.state_vector_to_probabilities(np.asarray(res.final_state_vector)) |
419 |
| - probs_th_k.append(state_probs) |
420 |
| - |
421 |
| - for i, num_cycle in enumerate(cycle_range): |
422 |
| - probs_th[num_cycle][k, :] = probs_th_k[i] |
423 |
| - probs_meas[num_cycle][k, :] = probs_meas_k[i] |
424 |
| - |
425 |
| - fidelity_vals = _xeb_fidelities(probs_th, probs_meas) |
426 |
| - xeb_data = [CrossEntropyPair(c, k) for (c, k) in zip(cycle_range, fidelity_vals)] |
427 |
| - return CrossEntropyResult(data=xeb_data, repetitions=repetitions) # type: ignore |
428 |
| - |
429 |
| - |
430 |
| -@_compat.deprecated( |
431 |
| - deadline='v0.16', fix=('Use cirq.experiments.random_quantum_circuit_generation instead') |
432 |
| -) |
433 |
| -def build_entangling_layers( |
434 |
| - qubits: Sequence[devices.GridQubit], two_qubit_gate: ops.Gate |
435 |
| -) -> List[circuits.Moment]: |
436 |
| - """Builds a sequence of gates that entangle all pairs of qubits on a grid. |
437 |
| -
|
438 |
| - The qubits are restricted to be physically on a square grid with distinct |
439 |
| - row and column indices (not every node of the grid needs to have a |
440 |
| - qubit). To entangle all pairs of qubits, a user-specified two-qubit gate |
441 |
| - is applied between each and every pair of qubit that are next to each |
442 |
| - other. In general, a total of four sets of parallel operations are needed to |
443 |
| - perform all possible two-qubit gates. We proceed as follows: |
444 |
| -
|
445 |
| - The first layer applies two-qubit gates to qubits (i, j) and (i, j + 1) |
446 |
| - where i is any integer and j is an even integer. The second layer |
447 |
| - applies two-qubit gates to qubits (i, j) and (i + 1, j) where i is an even |
448 |
| - integer and j is any integer. The third layer applies two-qubit gates |
449 |
| - to qubits (i, j) and (i, j + 1) where i is any integer and j is an odd |
450 |
| - integer. The fourth layer applies two-qubit gates to qubits (i, j) and |
451 |
| - (i + 1, j) where i is an odd integer and j is any integer. |
452 |
| -
|
453 |
| - After the layers are built as above, any empty layer is ejected.: |
454 |
| -
|
455 |
| - Cycle 1: Cycle 2: |
456 |
| - q00 ── q01 q02 ── q03 q00 q01 q02 q03 |
457 |
| - | | | | |
458 |
| - q10 ── q11 q12 ── q13 q10 q11 q12 q13 |
459 |
| -
|
460 |
| - q20 ── q21 q22 ── q23 q20 q21 q22 q23 |
461 |
| - | | | | |
462 |
| - q30 ── q31 q32 ── q33 q30 q31 q32 q33 |
463 |
| -
|
464 |
| - Cycle 3: Cycle 4: |
465 |
| - q00 q01 ── q02 q03 q00 q01 q02 q03 |
466 |
| -
|
467 |
| - q10 q11 ── q12 q13 q10 q11 q12 q13 |
468 |
| - | | | | |
469 |
| - q20 q21 ── q22 q23 q20 q21 q22 q23 |
470 |
| -
|
471 |
| - q30 q31 ── q32 q33 q30 q31 q32 q33 |
472 |
| -
|
473 |
| - Args: |
474 |
| - qubits: The grid qubits included in the entangling operations. |
475 |
| - two_qubit_gate: The two-qubit gate to be applied between all |
476 |
| - neighboring pairs of qubits. |
477 |
| -
|
478 |
| - Returns: |
479 |
| - A list of circuits.Moment, with a maximum length of 4. Each circuits.Moment |
480 |
| - includes two-qubit gates which can be performed at the same time. |
481 |
| -
|
482 |
| - Raises: |
483 |
| - ValueError: two-qubit gate is not used. |
484 |
| - """ |
485 |
| - if protocols.num_qubits(two_qubit_gate) != 2: |
486 |
| - raise ValueError('Input must be a two-qubit gate') |
487 |
| - interaction_sequence = _default_interaction_sequence(qubits) |
488 |
| - return [ |
489 |
| - circuits.Moment([two_qubit_gate(q_a, q_b) for (q_a, q_b) in pairs]) |
490 |
| - for pairs in interaction_sequence |
491 |
| - ] |
492 |
| - |
493 |
| - |
494 |
| -def _build_xeb_circuits( |
495 |
| - qubits: Sequence[ops.Qid], |
496 |
| - cycles: Sequence[int], |
497 |
| - single_qubit_gates: List[List[ops.Gate]] = None, |
498 |
| - benchmark_ops: Sequence[circuits.Moment] = None, |
499 |
| -) -> List[circuits.Circuit]: |
500 |
| - if benchmark_ops is not None: |
501 |
| - num_d = len(benchmark_ops) |
502 |
| - else: |
503 |
| - num_d = 0 |
504 |
| - max_cycles = max(cycles) |
505 |
| - |
506 |
| - if single_qubit_gates is None: |
507 |
| - single_rots = _random_half_rotations(qubits, max_cycles) |
508 |
| - else: |
509 |
| - single_rots = _random_any_gates(qubits, single_qubit_gates, max_cycles) |
510 |
| - all_circuits: List[circuits.Circuit] = [] |
511 |
| - for num_cycles in cycles: |
512 |
| - circuit_exp = circuits.Circuit() |
513 |
| - for i in range(num_cycles): |
514 |
| - circuit_exp.append(single_rots[i]) |
515 |
| - if benchmark_ops is not None: |
516 |
| - for op_set in benchmark_ops[i % num_d]: |
517 |
| - circuit_exp.append(op_set) |
518 |
| - all_circuits.append(circuit_exp) |
519 |
| - return all_circuits |
520 |
| - |
521 |
| - |
522 |
| -def _measure_prob_distribution( |
523 |
| - sampler: work.Sampler, |
524 |
| - repetitions: int, |
525 |
| - qubits: Sequence[ops.Qid], |
526 |
| - circuit_list: List[circuits.Circuit], |
527 |
| -) -> List[np.ndarray]: |
528 |
| - all_probs: List[np.ndarray] = [] |
529 |
| - num_states = 2 ** len(qubits) |
530 |
| - for circuit in circuit_list: |
531 |
| - trial_circuit = circuit.copy() |
532 |
| - trial_circuit.append(ops.measure(*qubits, key='z')) |
533 |
| - res = sampler.run(trial_circuit, repetitions=repetitions) |
534 |
| - res_hist = dict(res.histogram(key='z')) |
535 |
| - probs = np.zeros(num_states, dtype=float) |
536 |
| - for k, v in res_hist.items(): |
537 |
| - probs[k] = float(v) / float(repetitions) |
538 |
| - all_probs.append(probs) |
539 |
| - return all_probs |
540 |
| - |
541 |
| - |
542 |
| -def _xeb_fidelities( |
543 |
| - probs_th: Dict[int, np.ndarray], probs_meas: Dict[int, np.ndarray] |
544 |
| -) -> List[float]: |
545 |
| - """Compute XEB fidelity estimates for all circuit depths. |
546 |
| -
|
547 |
| - Args: |
548 |
| - probs_th: Theoretical output probabilities by cycle number. |
549 |
| - probs_meas: Experimental output probabilities by cycle number. |
550 |
| - Returns: |
551 |
| - List of fidelity estimates for each circuit depth. |
552 |
| - """ |
553 |
| - num_cycles = sorted(list(probs_th.keys())) |
554 |
| - return [_compute_fidelity(probs_th[n], probs_meas[n]) for n in num_cycles] |
555 |
| - |
556 |
| - |
557 |
| -def _compute_fidelity(probs_th: np.ndarray, probs_meas: np.ndarray) -> float: |
558 |
| - """Compute XEB fidelity estimate. |
559 |
| -
|
560 |
| - Args: |
561 |
| - probs_th: Theoretical output probabilities. |
562 |
| - probs_meas: Experimental output probabilities. |
563 |
| - Returns: |
564 |
| - XEB fidelity estimate. |
565 |
| - """ |
566 |
| - _, num_states = probs_th.shape |
567 |
| - pp_cross = probs_th * probs_meas |
568 |
| - pp_th = probs_th**2 |
569 |
| - f_meas = np.mean(num_states * np.sum(pp_cross, axis=1) - 1.0) |
570 |
| - f_th = np.mean(num_states * np.sum(pp_th, axis=1) - 1.0) |
571 |
| - return float(f_meas / f_th) |
572 |
| - |
573 |
| - |
574 |
| -def _random_half_rotations(qubits: Sequence[ops.Qid], num_layers: int) -> List[List[ops.OP_TREE]]: |
575 |
| - rot_ops = [ops.X**0.5, ops.Y**0.5, ops.PhasedXPowGate(phase_exponent=0.25, exponent=0.5)] |
576 |
| - num_qubits = len(qubits) |
577 |
| - rand_nums = np.random.choice(3, (num_qubits, num_layers)) |
578 |
| - single_q_layers: List[List[ops.OP_TREE]] = [] |
579 |
| - for i in range(num_layers): |
580 |
| - single_q_layers.append([rot_ops[rand_nums[j, i]](qubits[j]) for j in range(num_qubits)]) |
581 |
| - return single_q_layers |
582 |
| - |
583 |
| - |
584 |
| -def _random_any_gates( |
585 |
| - qubits: Sequence[ops.Qid], op_list: List[List[ops.Gate]], num_layers: int |
586 |
| -) -> List[List[ops.OP_TREE]]: |
587 |
| - num_ops = len(op_list) |
588 |
| - num_qubits = len(qubits) |
589 |
| - rand_nums = np.random.choice(num_ops, (num_qubits, num_layers)) |
590 |
| - single_q_layers: List[List[ops.OP_TREE]] = [] |
591 |
| - for i in range(num_layers): |
592 |
| - rots_i: List[ops.OP_TREE] = [] |
593 |
| - for j in range(num_qubits): |
594 |
| - rots_i.extend([rot(qubits[j]) for rot in op_list[rand_nums[j, i]]]) |
595 |
| - single_q_layers.append(rots_i) |
596 |
| - return single_q_layers |
597 |
| - |
598 |
| - |
599 |
| -def _default_interaction_sequence( |
600 |
| - qubits: Sequence[devices.GridQubit], |
601 |
| -) -> List[Set[Tuple[devices.GridQubit, devices.GridQubit]]]: |
602 |
| - qubit_dict = {(qubit.row, qubit.col): qubit for qubit in qubits} |
603 |
| - qubit_locs = set(qubit_dict) |
604 |
| - num_rows = max([q.row for q in qubits]) + 1 |
605 |
| - num_cols = max([q.col for q in qubits]) + 1 |
606 |
| - |
607 |
| - l_s: List[Set[Tuple[devices.GridQubit, devices.GridQubit]]] = [set() for _ in range(4)] |
608 |
| - for i in range(num_rows): |
609 |
| - for j in range(num_cols - 1): |
610 |
| - if (i, j) in qubit_locs and (i, j + 1) in qubit_locs: |
611 |
| - l_s[j % 2 * 2].add((qubit_dict[(i, j)], qubit_dict[(i, j + 1)])) |
612 |
| - |
613 |
| - for i in range(num_rows - 1): |
614 |
| - for j in range(num_cols): |
615 |
| - if (i, j) in qubit_locs and (i + 1, j) in qubit_locs: |
616 |
| - l_s[i % 2 * 2 + 1].add((qubit_dict[(i, j)], qubit_dict[(i + 1, j)])) |
617 |
| - |
618 |
| - l_final: List[Set[Tuple[devices.GridQubit, devices.GridQubit]]] = [] |
619 |
| - for gate_set in l_s: |
620 |
| - if len(gate_set) != 0: |
621 |
| - l_final.append(gate_set) |
622 |
| - |
623 |
| - return l_final |
0 commit comments