Skip to content

Commit 6cfdda7

Browse files
authored
[XEB] Cycle depths during analysis (#4278)
- Make the argument optional in benchmark_ - If provided, you can do a subset of those in sampled_df - More robust checking that it matches sampled_df
1 parent 5c3125e commit 6cfdda7

File tree

2 files changed

+60
-11
lines changed

2 files changed

+60
-11
lines changed

cirq-core/cirq/experiments/xeb_fitting.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
def benchmark_2q_xeb_fidelities(
5353
sampled_df: pd.DataFrame,
5454
circuits: Sequence['cirq.Circuit'],
55-
cycle_depths: Sequence[int],
55+
cycle_depths: Optional[Sequence[int]] = None,
5656
param_resolver: 'cirq.ParamResolverOrSimilarType' = None,
5757
pool: Optional['multiprocessing.pool.Pool'] = None,
5858
) -> pd.DataFrame:
@@ -66,18 +66,37 @@ def benchmark_2q_xeb_fidelities(
6666
sampled_df: The sampled results to benchmark. This is likely produced by a call to
6767
`sample_2q_xeb_circuits`.
6868
circuits: The library of circuits corresponding to the sampled results in `sampled_df`.
69-
cycle_depths: The sequence of cycle depths to simulate the circuits.
69+
cycle_depths: The sequence of cycle depths to benchmark the circuits. If not provided,
70+
we use the cycle depths found in `sampled_df`. All requested `cycle_depths` must be
71+
present in `sampled_df`.
7072
param_resolver: If circuits contain parameters, resolve according to this ParamResolver
7173
prior to simulation
7274
pool: If provided, execute the simulations in parallel.
7375
7476
Returns:
7577
A DataFrame with columns 'cycle_depth' and 'fidelity'.
7678
"""
79+
sampled_cycle_depths = (
80+
sampled_df.index.get_level_values('cycle_depth').drop_duplicates().sort_values()
81+
)
82+
if cycle_depths is not None:
83+
if len(cycle_depths) == 0:
84+
raise ValueError("`cycle_depths` should be a non-empty array_like")
85+
not_in_sampled = np.setdiff1d(cycle_depths, sampled_cycle_depths)
86+
if len(not_in_sampled) > 0:
87+
raise ValueError(
88+
f"The `cycle_depths` provided include some not "
89+
f"available in `sampled_df`: {not_in_sampled}"
90+
)
91+
sim_cycle_depths = cycle_depths
92+
else:
93+
sim_cycle_depths = sampled_cycle_depths
7794
simulated_df = simulate_2q_xeb_circuits(
78-
circuits=circuits, cycle_depths=cycle_depths, param_resolver=param_resolver, pool=pool
95+
circuits=circuits, cycle_depths=sim_cycle_depths, param_resolver=param_resolver, pool=pool
7996
)
80-
df = sampled_df.join(simulated_df)
97+
# Join the `pure_probs` onto `sampled_df`. By using 'inner', we let
98+
# the `cycle_depths` argument to this function control what cycle depths are benchmarked.
99+
df = sampled_df.join(simulated_df, how='inner').reset_index()
81100

82101
D = 4 # two qubits
83102
pure_probs = np.array(df['pure_probs'].to_list())
@@ -117,7 +136,7 @@ def _try_keep(k):
117136
else:
118137
groupby_names = ['cycle_depth']
119138

120-
return df.reset_index().groupby(groupby_names).apply(per_cycle_depth).reset_index()
139+
return df.groupby(groupby_names).apply(per_cycle_depth).reset_index()
121140

122141

123142
class XEBCharacterizationOptions(ABC):

cirq-core/cirq/experiments/xeb_fitting_test.py

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,31 +37,61 @@
3737
from cirq.experiments.xeb_sampling import sample_2q_xeb_circuits
3838

3939

40-
def test_benchmark_2q_xeb_fidelities():
40+
@pytest.fixture(scope='module')
41+
def circuits_cycle_depths_sampled_df():
4142
q0, q1 = cirq.LineQubit.range(2)
4243
circuits = [
4344
rqcg.random_rotations_between_two_qubit_circuit(
4445
q0, q1, depth=50, two_qubit_op_factory=lambda a, b, _: SQRT_ISWAP(a, b), seed=52
4546
)
4647
for _ in range(2)
4748
]
48-
cycle_depths = np.arange(3, 50, 9)
49+
cycle_depths = np.arange(10, 40 + 1, 10)
4950

5051
sampled_df = sample_2q_xeb_circuits(
5152
sampler=cirq.Simulator(seed=53), circuits=circuits, cycle_depths=cycle_depths
5253
)
53-
fid_df = benchmark_2q_xeb_fidelities(sampled_df, circuits, cycle_depths)
54+
return circuits, cycle_depths, sampled_df
55+
56+
57+
@pytest.mark.parametrize('pass_cycle_depths', (True, False))
58+
def test_benchmark_2q_xeb_fidelities(circuits_cycle_depths_sampled_df, pass_cycle_depths):
59+
circuits, cycle_depths, sampled_df = circuits_cycle_depths_sampled_df
60+
61+
if pass_cycle_depths:
62+
fid_df = benchmark_2q_xeb_fidelities(sampled_df, circuits, cycle_depths)
63+
else:
64+
fid_df = benchmark_2q_xeb_fidelities(sampled_df, circuits)
5465
assert len(fid_df) == len(cycle_depths)
55-
for _, row in fid_df.iterrows():
56-
assert row['cycle_depth'] in cycle_depths
57-
assert row['fidelity'] > 0.98
66+
assert sorted(fid_df['cycle_depth'].unique()) == cycle_depths.tolist()
67+
assert np.all(fid_df['fidelity'] > 0.98)
5868

5969
fit_df = fit_exponential_decays(fid_df)
6070
for _, row in fit_df.iterrows():
6171
assert list(row['cycle_depths']) == list(cycle_depths)
6272
assert len(row['fidelities']) == len(cycle_depths)
6373

6474

75+
def test_benchmark_2q_xeb_subsample_depths(circuits_cycle_depths_sampled_df):
76+
circuits, _, sampled_df = circuits_cycle_depths_sampled_df
77+
cycle_depths = [10, 20]
78+
fid_df = benchmark_2q_xeb_fidelities(sampled_df, circuits, cycle_depths)
79+
assert len(fid_df) == len(cycle_depths)
80+
assert sorted(fid_df['cycle_depth'].unique()) == cycle_depths
81+
82+
cycle_depths = [11, 21]
83+
with pytest.raises(ValueError):
84+
_ = benchmark_2q_xeb_fidelities(sampled_df, circuits, cycle_depths)
85+
86+
cycle_depths = [10, 100_000]
87+
with pytest.raises(ValueError):
88+
_ = benchmark_2q_xeb_fidelities(sampled_df, circuits, cycle_depths)
89+
90+
cycle_depths = []
91+
with pytest.raises(ValueError):
92+
_ = benchmark_2q_xeb_fidelities(sampled_df, circuits, cycle_depths)
93+
94+
6595
def _gridqubits_to_graph_device(qubits: Iterable[cirq.GridQubit]):
6696
# cirq contrib: routing.gridqubits_to_graph_device
6797
def _manhattan_distance(qubit1: cirq.GridQubit, qubit2: cirq.GridQubit) -> int:

0 commit comments

Comments
 (0)