Skip to content

Commit 170b20b

Browse files
Parallel XEB: Add option to specify pairs (#6787)
Co-authored-by: Noureldin <noureldinyosri@google.com>
1 parent 59be462 commit 170b20b

File tree

2 files changed

+70
-14
lines changed

2 files changed

+70
-14
lines changed

cirq-core/cirq/experiments/two_qubit_xeb.py

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,46 @@ def _manhattan_distance(qubit1: 'cirq.GridQubit', qubit2: 'cirq.GridQubit') -> i
5353
return abs(qubit1.row - qubit2.row) + abs(qubit1.col - qubit2.col)
5454

5555

56+
def qubits_and_pairs(
57+
sampler: 'cirq.Sampler',
58+
qubits: Optional[Sequence['cirq.GridQubit']] = None,
59+
pairs: Optional[Sequence[tuple['cirq.GridQubit', 'cirq.GridQubit']]] = None,
60+
) -> Tuple[Sequence['cirq.GridQubit'], Sequence[tuple['cirq.GridQubit', 'cirq.GridQubit']]]:
61+
"""Extract qubits and pairs from sampler.
62+
63+
64+
If qubits are not provided, then they are extracted from the pairs (if given) or the sampler.
65+
If pairs are not provided then all pairs of adjacent qubits are used.
66+
67+
Args:
68+
sampler: The quantum engine or simulator to run the circuits.
69+
qubits: Optional list of qubits.
70+
pairs: Optional list of pair to use.
71+
72+
Returns:
73+
- Qubits to use.
74+
- Pairs of qubits to use.
75+
76+
Raises:
77+
ValueError: If qubits are not specified and can't be deduced from other arguments.
78+
"""
79+
if qubits is None:
80+
if pairs is None:
81+
qubits = _grid_qubits_for_sampler(sampler)
82+
if qubits is None:
83+
raise ValueError("Couldn't determine qubits from sampler. Please specify them.")
84+
else:
85+
qubits_set = set(itertools.chain(*pairs))
86+
qubits = list(qubits_set)
87+
88+
if pairs is None:
89+
pairs = [
90+
pair for pair in itertools.combinations(qubits, 2) if _manhattan_distance(*pair) == 1
91+
]
92+
93+
return qubits, pairs
94+
95+
5696
@dataclass(frozen=True)
5797
class TwoQubitXEBResult:
5898
"""Results from an XEB experiment."""
@@ -359,6 +399,7 @@ def parallel_xeb_workflow(
359399
cycle_depths: Sequence[int] = (5, 25, 50, 100, 200, 300),
360400
random_state: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None,
361401
ax: Optional[plt.Axes] = None,
402+
pairs: Optional[Sequence[tuple['cirq.GridQubit', 'cirq.GridQubit']]] = None,
362403
pool: Optional['multiprocessing.pool.Pool'] = None,
363404
**plot_kwargs,
364405
) -> Tuple[pd.DataFrame, Sequence['cirq.Circuit'], pd.DataFrame]:
@@ -375,6 +416,7 @@ def parallel_xeb_workflow(
375416
random_state: The random state to use.
376417
ax: the plt.Axes to plot the device layout on. If not given,
377418
no plot is created.
419+
pairs: Pairs to use. If not specified, use all pairs between adjacent qubits.
378420
pool: An optional multiprocessing pool.
379421
**plot_kwargs: Arguments to be passed to 'plt.Axes.plot'.
380422
@@ -391,14 +433,8 @@ def parallel_xeb_workflow(
391433
"""
392434
rs = value.parse_random_state(random_state)
393435

394-
if qubits is None:
395-
qubits = _grid_qubits_for_sampler(sampler)
396-
if qubits is None:
397-
raise ValueError("Couldn't determine qubits from sampler. Please specify them.")
398-
399-
graph = nx.Graph(
400-
pair for pair in itertools.combinations(qubits, 2) if _manhattan_distance(*pair) == 1
401-
)
436+
qubits, pairs = qubits_and_pairs(sampler, qubits, pairs)
437+
graph = nx.Graph(pairs)
402438

403439
if ax is not None:
404440
nx.draw_networkx(graph, pos={q: (q.row, q.col) for q in qubits}, ax=ax)
@@ -445,6 +481,7 @@ def parallel_two_qubit_xeb(
445481
cycle_depths: Sequence[int] = (5, 25, 50, 100, 200, 300),
446482
random_state: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None,
447483
ax: Optional[plt.Axes] = None,
484+
pairs: Optional[Sequence[tuple['cirq.GridQubit', 'cirq.GridQubit']]] = None,
448485
**plot_kwargs,
449486
) -> TwoQubitXEBResult:
450487
"""A convenience method that runs the full XEB workflow.
@@ -460,6 +497,7 @@ def parallel_two_qubit_xeb(
460497
random_state: The random state to use.
461498
ax: the plt.Axes to plot the device layout on. If not given,
462499
no plot is created.
500+
pairs: Pairs to use. If not specified, use all pairs between adjacent qubits.
463501
**plot_kwargs: Arguments to be passed to 'plt.Axes.plot'.
464502
Returns:
465503
A TwoQubitXEBResult object representing the results of the experiment.
@@ -469,6 +507,7 @@ def parallel_two_qubit_xeb(
469507
fids, *_ = parallel_xeb_workflow(
470508
sampler=sampler,
471509
qubits=qubits,
510+
pairs=pairs,
472511
entangling_gate=entangling_gate,
473512
n_repetitions=n_repetitions,
474513
n_combinations=n_combinations,
@@ -493,6 +532,7 @@ def run_rb_and_xeb(
493532
depths_xeb: Sequence[int] = (5, 25, 50, 100, 200, 300),
494533
xeb_combinations: int = 10,
495534
random_state: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None,
535+
pairs: Optional[Sequence[tuple['cirq.GridQubit', 'cirq.GridQubit']]] = None,
496536
) -> InferredXEBResult:
497537
"""A convenience method that runs both RB and XEB workflows.
498538
@@ -506,6 +546,7 @@ def run_rb_and_xeb(
506546
depths_xeb: The cycle depths to use for XEB.
507547
xeb_combinations: The number of combinations to generate for XEB.
508548
random_state: The random state to use.
549+
pairs: Pairs to use. If not specified, use all pairs between adjacent qubits.
509550
510551
Returns:
511552
An InferredXEBResult object representing the results of the experiment.
@@ -514,10 +555,7 @@ def run_rb_and_xeb(
514555
ValueError: If qubits are not specified and the sampler has no device.
515556
"""
516557

517-
if qubits is None:
518-
qubits = _grid_qubits_for_sampler(sampler)
519-
if qubits is None:
520-
raise ValueError("Couldn't determine qubits from sampler. Please specify them.")
558+
qubits, pairs = qubits_and_pairs(sampler, qubits, pairs)
521559

522560
rb = parallel_single_qubit_randomized_benchmarking(
523561
sampler=sampler,
@@ -530,6 +568,7 @@ def run_rb_and_xeb(
530568
xeb = parallel_two_qubit_xeb(
531569
sampler=sampler,
532570
qubits=qubits,
571+
pairs=pairs,
533572
entangling_gate=entangling_gate,
534573
n_repetitions=repetitions,
535574
n_circuits=num_circuits,

cirq-core/cirq/experiments/two_qubit_xeb_test.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,26 +261,43 @@ def test_inferred_plots(ax, target_error, kind):
261261

262262

263263
@pytest.mark.parametrize(
264-
'sampler,qubits',
264+
'sampler,qubits,pairs',
265265
[
266266
(
267267
cirq.DensityMatrixSimulator(
268268
seed=0, noise=cirq.ConstantQubitNoiseModel(cirq.amplitude_damp(0.1))
269269
),
270270
cirq.GridQubit.rect(3, 2, 4, 3),
271+
None,
272+
),
273+
(
274+
cirq.DensityMatrixSimulator(
275+
seed=0, noise=cirq.ConstantQubitNoiseModel(cirq.amplitude_damp(0.1))
276+
),
277+
None,
278+
[
279+
(cirq.GridQubit(0, 0), cirq.GridQubit(0, 1)),
280+
(cirq.GridQubit(0, 0), cirq.GridQubit(1, 0)),
281+
],
271282
),
272283
(
273284
DensityMatrixSimulatorWithProcessor(
274285
seed=0, noise=cirq.ConstantQubitNoiseModel(cirq.amplitude_damp(0.1))
275286
),
276287
None,
288+
None,
277289
),
278290
],
279291
)
280-
def test_run_rb_and_xeb(sampler: cirq.Sampler, qubits: Optional[Sequence[cirq.GridQubit]]):
292+
def test_run_rb_and_xeb(
293+
sampler: cirq.Sampler,
294+
qubits: Optional[Sequence[cirq.GridQubit]],
295+
pairs: Optional[Sequence[tuple[cirq.GridQubit, cirq.GridQubit]]],
296+
):
281297
res = cirq.experiments.run_rb_and_xeb(
282298
sampler=sampler,
283299
qubits=qubits,
300+
pairs=pairs,
284301
repetitions=100,
285302
num_clifford_range=tuple(np.arange(3, 10, 1)),
286303
xeb_combinations=1,

0 commit comments

Comments
 (0)