cirq-core 1.5.0.dev20241105235147__py3-none-any.whl → 1.5.0.dev20241108000946__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cirq-core might be problematic. Click here for more details.
- cirq/_version.py +1 -1
- cirq/_version_test.py +1 -1
- cirq/experiments/__init__.py +6 -0
- cirq/experiments/two_qubit_xeb.py +4 -1
- cirq/experiments/xeb_fitting.py +22 -5
- cirq/experiments/z_phase_calibration.py +273 -0
- cirq/experiments/z_phase_calibration_test.py +207 -0
- {cirq_core-1.5.0.dev20241105235147.dist-info → cirq_core-1.5.0.dev20241108000946.dist-info}/METADATA +1 -1
- {cirq_core-1.5.0.dev20241105235147.dist-info → cirq_core-1.5.0.dev20241108000946.dist-info}/RECORD +12 -10
- {cirq_core-1.5.0.dev20241105235147.dist-info → cirq_core-1.5.0.dev20241108000946.dist-info}/LICENSE +0 -0
- {cirq_core-1.5.0.dev20241105235147.dist-info → cirq_core-1.5.0.dev20241108000946.dist-info}/WHEEL +0 -0
- {cirq_core-1.5.0.dev20241105235147.dist-info → cirq_core-1.5.0.dev20241108000946.dist-info}/top_level.txt +0 -0
cirq/_version.py
CHANGED
cirq/_version_test.py
CHANGED
cirq/experiments/__init__.py
CHANGED
|
@@ -82,3 +82,9 @@ from cirq.experiments.two_qubit_xeb import (
|
|
|
82
82
|
parallel_two_qubit_xeb as parallel_two_qubit_xeb,
|
|
83
83
|
run_rb_and_xeb as run_rb_and_xeb,
|
|
84
84
|
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
from cirq.experiments.z_phase_calibration import (
|
|
88
|
+
z_phase_calibration_workflow as z_phase_calibration_workflow,
|
|
89
|
+
calibrate_z_phases as calibrate_z_phases,
|
|
90
|
+
)
|
|
@@ -38,6 +38,7 @@ from cirq.qis import noise_utils
|
|
|
38
38
|
from cirq._compat import cached_method
|
|
39
39
|
|
|
40
40
|
if TYPE_CHECKING:
|
|
41
|
+
import multiprocessing
|
|
41
42
|
import cirq
|
|
42
43
|
|
|
43
44
|
|
|
@@ -358,6 +359,7 @@ def parallel_xeb_workflow(
|
|
|
358
359
|
cycle_depths: Sequence[int] = (5, 25, 50, 100, 200, 300),
|
|
359
360
|
random_state: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None,
|
|
360
361
|
ax: Optional[plt.Axes] = None,
|
|
362
|
+
pool: Optional['multiprocessing.pool.Pool'] = None,
|
|
361
363
|
**plot_kwargs,
|
|
362
364
|
) -> Tuple[pd.DataFrame, Sequence['cirq.Circuit'], pd.DataFrame]:
|
|
363
365
|
"""A utility method that runs the full XEB workflow.
|
|
@@ -373,6 +375,7 @@ def parallel_xeb_workflow(
|
|
|
373
375
|
random_state: The random state to use.
|
|
374
376
|
ax: the plt.Axes to plot the device layout on. If not given,
|
|
375
377
|
no plot is created.
|
|
378
|
+
pool: An optional multiprocessing pool.
|
|
376
379
|
**plot_kwargs: Arguments to be passed to 'plt.Axes.plot'.
|
|
377
380
|
|
|
378
381
|
Returns:
|
|
@@ -426,7 +429,7 @@ def parallel_xeb_workflow(
|
|
|
426
429
|
)
|
|
427
430
|
|
|
428
431
|
fids = benchmark_2q_xeb_fidelities(
|
|
429
|
-
sampled_df=sampled_df, circuits=circuit_library, cycle_depths=cycle_depths
|
|
432
|
+
sampled_df=sampled_df, circuits=circuit_library, cycle_depths=cycle_depths, pool=pool
|
|
430
433
|
)
|
|
431
434
|
|
|
432
435
|
return fids, circuit_library, sampled_df
|
cirq/experiments/xeb_fitting.py
CHANGED
|
@@ -95,6 +95,11 @@ def benchmark_2q_xeb_fidelities(
|
|
|
95
95
|
df['e_u'] = np.sum(pure_probs**2, axis=1)
|
|
96
96
|
df['u_u'] = np.sum(pure_probs, axis=1) / D
|
|
97
97
|
df['m_u'] = np.sum(pure_probs * sampled_probs, axis=1)
|
|
98
|
+
# Var[m_u] = Var[sum p(x) * p_sampled(x)]
|
|
99
|
+
# = sum p(x)^2 Var[p_sampled(x)]
|
|
100
|
+
# = sum p(x)^2 p(x) (1 - p(x))
|
|
101
|
+
# = sum p(x)^3 (1 - p(x))
|
|
102
|
+
df['var_m_u'] = np.sum(pure_probs**3 * (1 - pure_probs), axis=1)
|
|
98
103
|
df['y'] = df['m_u'] - df['u_u']
|
|
99
104
|
df['x'] = df['e_u'] - df['u_u']
|
|
100
105
|
df['numerator'] = df['x'] * df['y']
|
|
@@ -103,7 +108,11 @@ def benchmark_2q_xeb_fidelities(
|
|
|
103
108
|
def per_cycle_depth(df):
|
|
104
109
|
"""This function is applied per cycle_depth in the following groupby aggregation."""
|
|
105
110
|
fid_lsq = df['numerator'].sum() / df['denominator'].sum()
|
|
106
|
-
|
|
111
|
+
# Note: both df['denominator'] and df['x'] are constants.
|
|
112
|
+
# Var[f] = Var[df['numerator']] / (sum df['denominator'])^2
|
|
113
|
+
# = sum (df['x']^2 * df['var_m_u']) / (sum df['denominator'])^2
|
|
114
|
+
var_fid = (df['var_m_u'] * df['x'] ** 2).sum() / df['denominator'].sum() ** 2
|
|
115
|
+
ret = {'fidelity': fid_lsq, 'fidelity_variance': var_fid}
|
|
107
116
|
|
|
108
117
|
def _try_keep(k):
|
|
109
118
|
"""If all the values for a key `k` are the same in this group, we can keep it."""
|
|
@@ -385,16 +394,21 @@ def SqrtISwapXEBOptions(*args, **kwargs):
|
|
|
385
394
|
|
|
386
395
|
|
|
387
396
|
def parameterize_circuit(
|
|
388
|
-
circuit: 'cirq.Circuit',
|
|
397
|
+
circuit: 'cirq.Circuit',
|
|
398
|
+
options: XEBCharacterizationOptions,
|
|
399
|
+
target_gatefamily: Optional[ops.GateFamily] = None,
|
|
389
400
|
) -> 'cirq.Circuit':
|
|
390
401
|
"""Parameterize PhasedFSim-like gates in a given circuit according to
|
|
391
402
|
`phased_fsim_options`.
|
|
392
403
|
"""
|
|
404
|
+
if isinstance(target_gatefamily, ops.GateFamily):
|
|
405
|
+
should_parameterize = lambda op: op in target_gatefamily or options.should_parameterize(op)
|
|
406
|
+
else:
|
|
407
|
+
should_parameterize = options.should_parameterize
|
|
393
408
|
gate = options.get_parameterized_gate()
|
|
394
409
|
return circuits.Circuit(
|
|
395
410
|
circuits.Moment(
|
|
396
|
-
gate.on(*op.qubits) if
|
|
397
|
-
for op in moment.operations
|
|
411
|
+
gate.on(*op.qubits) if should_parameterize(op) else op for op in moment.operations
|
|
398
412
|
)
|
|
399
413
|
for moment in circuit.moments
|
|
400
414
|
)
|
|
@@ -667,13 +681,16 @@ def fit_exponential_decays(fidelities_df: pd.DataFrame) -> pd.DataFrame:
|
|
|
667
681
|
a, layer_fid, a_std, layer_fid_std = _fit_exponential_decay(
|
|
668
682
|
f1['cycle_depth'], f1['fidelity']
|
|
669
683
|
)
|
|
684
|
+
fidelity_variance = 0
|
|
685
|
+
if 'fidelity_variance' in f1:
|
|
686
|
+
fidelity_variance = f1['fidelity_variance'].values
|
|
670
687
|
record = {
|
|
671
688
|
'a': a,
|
|
672
689
|
'layer_fid': layer_fid,
|
|
673
690
|
'cycle_depths': f1['cycle_depth'].values,
|
|
674
691
|
'fidelities': f1['fidelity'].values,
|
|
675
692
|
'a_std': a_std,
|
|
676
|
-
'layer_fid_std': layer_fid_std,
|
|
693
|
+
'layer_fid_std': np.sqrt(layer_fid_std**2 + fidelity_variance),
|
|
677
694
|
}
|
|
678
695
|
return pd.Series(record)
|
|
679
696
|
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
# Copyright 2024 The Cirq Developers
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""Provides a method to do z-phase calibration for excitation-preserving gates."""
|
|
16
|
+
from typing import Union, Optional, Sequence, Tuple, Dict, TYPE_CHECKING
|
|
17
|
+
import multiprocessing
|
|
18
|
+
import multiprocessing.pool
|
|
19
|
+
|
|
20
|
+
import matplotlib.pyplot as plt
|
|
21
|
+
import numpy as np
|
|
22
|
+
|
|
23
|
+
from cirq.experiments import xeb_fitting
|
|
24
|
+
from cirq.experiments.two_qubit_xeb import parallel_xeb_workflow
|
|
25
|
+
from cirq import ops
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
import cirq
|
|
29
|
+
import pandas as pd
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def z_phase_calibration_workflow(
|
|
33
|
+
sampler: 'cirq.Sampler',
|
|
34
|
+
qubits: Optional[Sequence['cirq.GridQubit']] = None,
|
|
35
|
+
two_qubit_gate: 'cirq.Gate' = ops.CZ,
|
|
36
|
+
options: Optional[xeb_fitting.XEBPhasedFSimCharacterizationOptions] = None,
|
|
37
|
+
n_repetitions: int = 10**4,
|
|
38
|
+
n_combinations: int = 10,
|
|
39
|
+
n_circuits: int = 20,
|
|
40
|
+
cycle_depths: Sequence[int] = tuple(np.arange(3, 100, 20)),
|
|
41
|
+
random_state: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None,
|
|
42
|
+
atol: float = 1e-3,
|
|
43
|
+
num_workers_or_pool: Union[int, 'multiprocessing.pool.Pool'] = -1,
|
|
44
|
+
) -> Tuple[xeb_fitting.XEBCharacterizationResult, 'pd.DataFrame']:
|
|
45
|
+
"""Perform z-phase calibration for excitation-preserving gates.
|
|
46
|
+
|
|
47
|
+
For a given excitation-preserving two-qubit gate we assume an error model that can be described
|
|
48
|
+
using Z-rotations:
|
|
49
|
+
0: ───Rz(a)───two_qubit_gate───Rz(c)───
|
|
50
|
+
│
|
|
51
|
+
1: ───Rz(b)───two_qubit_gate───Rz(d)───
|
|
52
|
+
for some angles a, b, c, and d.
|
|
53
|
+
|
|
54
|
+
Since the two-qubit gate is a excitation-preserving-gate, it can be represented by an FSimGate
|
|
55
|
+
and the effect of rotations turns it into a PhasedFSimGate. Using XEB-data we find the
|
|
56
|
+
PhasedFSimGate parameters that minimize the infidelity of the gate.
|
|
57
|
+
|
|
58
|
+
References:
|
|
59
|
+
- https://arxiv.org/abs/2001.08343
|
|
60
|
+
- https://arxiv.org/abs/2010.07965
|
|
61
|
+
- https://arxiv.org/abs/1910.11333
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
sampler: The quantum engine or simulator to run the circuits.
|
|
65
|
+
qubits: Qubits to use. If none, use all qubits on the sampler's device.
|
|
66
|
+
two_qubit_gate: The entangling gate to use.
|
|
67
|
+
options: The XEB-fitting options. If None, calibrate only the three phase angles
|
|
68
|
+
(chi, gamma, zeta) using the representation of a two-qubit gate as an FSimGate
|
|
69
|
+
for the initial guess.
|
|
70
|
+
n_repetitions: The number of repetitions to use.
|
|
71
|
+
n_combinations: The number of combinations to generate.
|
|
72
|
+
n_circuits: The number of circuits to generate.
|
|
73
|
+
cycle_depths: The cycle depths to use.
|
|
74
|
+
random_state: The random state to use.
|
|
75
|
+
atol: Absolute tolerance to be used by the minimizer.
|
|
76
|
+
num_workers_or_pool: An optional multi-processing pool or number of workers.
|
|
77
|
+
A zero value means no multiprocessing.
|
|
78
|
+
A positive integer value will create a pool with the given number of workers.
|
|
79
|
+
A negative value will create pool with maximum number of workers.
|
|
80
|
+
Returns:
|
|
81
|
+
- An `XEBCharacterizationResult` object that contains the calibration result.
|
|
82
|
+
- A `pd.DataFrame` comparing the before and after fidelities.
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
pool: Optional['multiprocessing.pool.Pool'] = None
|
|
86
|
+
local_pool = False
|
|
87
|
+
if isinstance(num_workers_or_pool, multiprocessing.pool.Pool):
|
|
88
|
+
pool = num_workers_or_pool # pragma: no cover
|
|
89
|
+
elif num_workers_or_pool != 0:
|
|
90
|
+
pool = multiprocessing.Pool(num_workers_or_pool if num_workers_or_pool > 0 else None)
|
|
91
|
+
local_pool = True
|
|
92
|
+
|
|
93
|
+
fids_df_0, circuits, sampled_df = parallel_xeb_workflow(
|
|
94
|
+
sampler=sampler,
|
|
95
|
+
qubits=qubits,
|
|
96
|
+
entangling_gate=two_qubit_gate,
|
|
97
|
+
n_repetitions=n_repetitions,
|
|
98
|
+
cycle_depths=cycle_depths,
|
|
99
|
+
n_circuits=n_circuits,
|
|
100
|
+
n_combinations=n_combinations,
|
|
101
|
+
random_state=random_state,
|
|
102
|
+
pool=pool,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
if options is None:
|
|
106
|
+
options = xeb_fitting.XEBPhasedFSimCharacterizationOptions(
|
|
107
|
+
characterize_chi=True,
|
|
108
|
+
characterize_gamma=True,
|
|
109
|
+
characterize_zeta=True,
|
|
110
|
+
characterize_theta=False,
|
|
111
|
+
characterize_phi=False,
|
|
112
|
+
).with_defaults_from_gate(two_qubit_gate)
|
|
113
|
+
|
|
114
|
+
p_circuits = [
|
|
115
|
+
xeb_fitting.parameterize_circuit(circuit, options, ops.GateFamily(two_qubit_gate))
|
|
116
|
+
for circuit in circuits
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
result = xeb_fitting.characterize_phased_fsim_parameters_with_xeb_by_pair(
|
|
120
|
+
sampled_df=sampled_df,
|
|
121
|
+
parameterized_circuits=p_circuits,
|
|
122
|
+
cycle_depths=cycle_depths,
|
|
123
|
+
options=options,
|
|
124
|
+
fatol=atol,
|
|
125
|
+
xatol=atol,
|
|
126
|
+
pool=pool,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
before_after = xeb_fitting.before_and_after_characterization(
|
|
130
|
+
fids_df_0, characterization_result=result
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
if local_pool:
|
|
134
|
+
assert isinstance(pool, multiprocessing.pool.Pool)
|
|
135
|
+
pool.close()
|
|
136
|
+
return result, before_after
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def calibrate_z_phases(
|
|
140
|
+
sampler: 'cirq.Sampler',
|
|
141
|
+
qubits: Optional[Sequence['cirq.GridQubit']] = None,
|
|
142
|
+
two_qubit_gate: 'cirq.Gate' = ops.CZ,
|
|
143
|
+
options: Optional[xeb_fitting.XEBPhasedFSimCharacterizationOptions] = None,
|
|
144
|
+
n_repetitions: int = 10**4,
|
|
145
|
+
n_combinations: int = 10,
|
|
146
|
+
n_circuits: int = 20,
|
|
147
|
+
cycle_depths: Sequence[int] = tuple(np.arange(3, 100, 20)),
|
|
148
|
+
random_state: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None,
|
|
149
|
+
atol: float = 1e-3,
|
|
150
|
+
num_workers_or_pool: Union[int, 'multiprocessing.pool.Pool'] = -1,
|
|
151
|
+
) -> Dict[Tuple['cirq.Qid', 'cirq.Qid'], 'cirq.PhasedFSimGate']:
|
|
152
|
+
"""Perform z-phase calibration for excitation-preserving gates.
|
|
153
|
+
|
|
154
|
+
For a given excitation-preserving two-qubit gate we assume an error model that can be described
|
|
155
|
+
using Z-rotations:
|
|
156
|
+
0: ───Rz(a)───two_qubit_gate───Rz(c)───
|
|
157
|
+
│
|
|
158
|
+
1: ───Rz(b)───two_qubit_gate───Rz(d)───
|
|
159
|
+
for some angles a, b, c, and d.
|
|
160
|
+
|
|
161
|
+
Since the two-qubit gate is a excitation-preserving gate, it can be represented by an FSimGate
|
|
162
|
+
and the effect of rotations turns it into a PhasedFSimGate. Using XEB-data we find the
|
|
163
|
+
PhasedFSimGate parameters that minimize the infidelity of the gate.
|
|
164
|
+
|
|
165
|
+
References:
|
|
166
|
+
- https://arxiv.org/abs/2001.08343
|
|
167
|
+
- https://arxiv.org/abs/2010.07965
|
|
168
|
+
- https://arxiv.org/abs/1910.11333
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
sampler: The quantum engine or simulator to run the circuits.
|
|
172
|
+
qubits: Qubits to use. If none, use all qubits on the sampler's device.
|
|
173
|
+
two_qubit_gate: The entangling gate to use.
|
|
174
|
+
options: The XEB-fitting options. If None, calibrate only the three phase angles
|
|
175
|
+
(chi, gamma, zeta) using the representation of a two-qubit gate as an FSimGate
|
|
176
|
+
for the initial guess.
|
|
177
|
+
n_repetitions: The number of repetitions to use.
|
|
178
|
+
n_combinations: The number of combinations to generate.
|
|
179
|
+
n_circuits: The number of circuits to generate.
|
|
180
|
+
cycle_depths: The cycle depths to use.
|
|
181
|
+
random_state: The random state to use.
|
|
182
|
+
atol: Absolute tolerance to be used by the minimizer.
|
|
183
|
+
num_workers_or_pool: An optional multi-processing pool or number of workers.
|
|
184
|
+
A zero value means no multiprocessing.
|
|
185
|
+
A positive integer value will create a pool with the given number of workers.
|
|
186
|
+
A negative value will create pool with maximum number of workers.
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
- A dictionary mapping qubit pairs to the calibrated PhasedFSimGates.
|
|
190
|
+
"""
|
|
191
|
+
|
|
192
|
+
if options is None:
|
|
193
|
+
options = xeb_fitting.XEBPhasedFSimCharacterizationOptions(
|
|
194
|
+
characterize_chi=True,
|
|
195
|
+
characterize_gamma=True,
|
|
196
|
+
characterize_zeta=True,
|
|
197
|
+
characterize_theta=False,
|
|
198
|
+
characterize_phi=False,
|
|
199
|
+
).with_defaults_from_gate(two_qubit_gate)
|
|
200
|
+
|
|
201
|
+
result, _ = z_phase_calibration_workflow(
|
|
202
|
+
sampler=sampler,
|
|
203
|
+
qubits=qubits,
|
|
204
|
+
two_qubit_gate=two_qubit_gate,
|
|
205
|
+
options=options,
|
|
206
|
+
n_repetitions=n_repetitions,
|
|
207
|
+
n_combinations=n_combinations,
|
|
208
|
+
n_circuits=n_circuits,
|
|
209
|
+
cycle_depths=cycle_depths,
|
|
210
|
+
random_state=random_state,
|
|
211
|
+
atol=atol,
|
|
212
|
+
num_workers_or_pool=num_workers_or_pool,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
gates = {}
|
|
216
|
+
for pair, params in result.final_params.items():
|
|
217
|
+
params['theta'] = params.get('theta', options.theta_default or 0)
|
|
218
|
+
params['phi'] = params.get('phi', options.phi_default or 0)
|
|
219
|
+
params['zeta'] = params.get('zeta', options.zeta_default or 0)
|
|
220
|
+
params['chi'] = params.get('chi', options.chi_default or 0)
|
|
221
|
+
params['gamma'] = params.get('gamma', options.gamma_default or 0)
|
|
222
|
+
gates[pair] = ops.PhasedFSimGate(**params)
|
|
223
|
+
return gates
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def plot_z_phase_calibration_result(
|
|
227
|
+
before_after_df: 'pd.DataFrame',
|
|
228
|
+
axes: Optional[np.ndarray[Sequence[Sequence['plt.Axes']], np.dtype[np.object_]]] = None,
|
|
229
|
+
pairs: Optional[Sequence[Tuple['cirq.Qid', 'cirq.Qid']]] = None,
|
|
230
|
+
*,
|
|
231
|
+
with_error_bars: bool = False,
|
|
232
|
+
) -> np.ndarray[Sequence[Sequence['plt.Axes']], np.dtype[np.object_]]:
|
|
233
|
+
"""A helper method to plot the result of running z-phase calibration.
|
|
234
|
+
|
|
235
|
+
Note that the plotted fidelity is a statistical estimate of the true fidelity and as a result
|
|
236
|
+
may be outside the [0, 1] range.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
before_after_df: The second return object of running `z_phase_calibration_workflow`.
|
|
240
|
+
axes: And ndarray of the axes to plot on.
|
|
241
|
+
The number of axes is expected to be >= number of qubit pairs.
|
|
242
|
+
pairs: If provided, only the given pairs are plotted.
|
|
243
|
+
with_error_bars: Whether to add error bars or not.
|
|
244
|
+
The width of the bar is an upper bound on standard variation of the estimated fidelity.
|
|
245
|
+
"""
|
|
246
|
+
if pairs is None:
|
|
247
|
+
pairs = before_after_df.index
|
|
248
|
+
if axes is None:
|
|
249
|
+
# Create a 16x9 rectangle.
|
|
250
|
+
ncols = int(np.ceil(np.sqrt(9 / 16 * len(pairs))))
|
|
251
|
+
nrows = (len(pairs) + ncols - 1) // ncols
|
|
252
|
+
_, axes = plt.subplots(nrows=nrows, ncols=ncols)
|
|
253
|
+
axes = axes if isinstance(axes, np.ndarray) else np.array(axes)
|
|
254
|
+
for pair, ax in zip(pairs, axes.flatten()):
|
|
255
|
+
row = before_after_df.loc[[pair]].iloc[0]
|
|
256
|
+
ax.errorbar(
|
|
257
|
+
row.cycle_depths_0,
|
|
258
|
+
row.fidelities_0,
|
|
259
|
+
yerr=row.layer_fid_std_0 * with_error_bars,
|
|
260
|
+
label='original',
|
|
261
|
+
)
|
|
262
|
+
ax.errorbar(
|
|
263
|
+
row.cycle_depths_0,
|
|
264
|
+
row.fidelities_c,
|
|
265
|
+
yerr=row.layer_fid_std_c * with_error_bars,
|
|
266
|
+
label='calibrated',
|
|
267
|
+
)
|
|
268
|
+
ax.axhline(1, linestyle='--')
|
|
269
|
+
ax.set_xlabel('cycle depth')
|
|
270
|
+
ax.set_ylabel('fidelity estimate')
|
|
271
|
+
ax.set_title('-'.join(str(q) for q in pair))
|
|
272
|
+
ax.legend()
|
|
273
|
+
return axes
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Copyright 2024 The Cirq Developers
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
import pytest
|
|
16
|
+
import numpy as np
|
|
17
|
+
import pandas as pd
|
|
18
|
+
|
|
19
|
+
import cirq
|
|
20
|
+
|
|
21
|
+
from cirq.experiments.z_phase_calibration import (
|
|
22
|
+
calibrate_z_phases,
|
|
23
|
+
z_phase_calibration_workflow,
|
|
24
|
+
plot_z_phase_calibration_result,
|
|
25
|
+
)
|
|
26
|
+
from cirq.experiments.xeb_fitting import XEBPhasedFSimCharacterizationOptions
|
|
27
|
+
|
|
28
|
+
_ANGLES = ['theta', 'phi', 'chi', 'zeta', 'gamma']
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _create_tests(n, seed, with_options: bool = False):
|
|
32
|
+
rng = np.random.default_rng(seed)
|
|
33
|
+
angles = (rng.random((n, 5)) * 2 - 1) * np.pi
|
|
34
|
+
# Add errors to the last 3 angles (chi, zeta, gamma).
|
|
35
|
+
# The errors are in the union (-2, -1) U (1, 2).
|
|
36
|
+
# This is because we run the tests with few repetitions so a small error might not get fixed.
|
|
37
|
+
error = np.concatenate(
|
|
38
|
+
[np.zeros((n, 2)), (rng.random((n, 3)) + 1) * rng.choice([-1, 1], (n, 3))], axis=-1
|
|
39
|
+
)
|
|
40
|
+
if with_options:
|
|
41
|
+
options = []
|
|
42
|
+
for _ in range(n):
|
|
43
|
+
v = [False, False, False]
|
|
44
|
+
# Calibrate only one to keep the run time down.
|
|
45
|
+
v[rng.integers(0, 3)] = True
|
|
46
|
+
options.append(
|
|
47
|
+
{
|
|
48
|
+
'characterize_chi': v[0],
|
|
49
|
+
'characterize_gamma': v[1],
|
|
50
|
+
'characterize_zeta': v[2],
|
|
51
|
+
'characterize_phi': False,
|
|
52
|
+
'characterize_theta': False,
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
return zip(angles, error, options)
|
|
57
|
+
return zip(angles, error)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _trace_distance(A, B):
|
|
61
|
+
return 0.5 * np.abs(np.linalg.eigvals(A - B)).sum()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class _TestSimulator(cirq.Simulator):
|
|
65
|
+
"""A simulator that replaces a specific gate by another."""
|
|
66
|
+
|
|
67
|
+
def __init__(self, gate: cirq.Gate, replacement: cirq.Gate, **kwargs):
|
|
68
|
+
super().__init__(**kwargs)
|
|
69
|
+
self.gate = gate
|
|
70
|
+
self.replacement = replacement
|
|
71
|
+
|
|
72
|
+
def _core_iterator(
|
|
73
|
+
self,
|
|
74
|
+
circuit: 'cirq.AbstractCircuit',
|
|
75
|
+
sim_state,
|
|
76
|
+
all_measurements_are_terminal: bool = False,
|
|
77
|
+
):
|
|
78
|
+
new_circuit = cirq.Circuit(
|
|
79
|
+
[
|
|
80
|
+
[op if op.gate != self.gate else self.replacement(*op.qubits) for op in m]
|
|
81
|
+
for m in circuit
|
|
82
|
+
]
|
|
83
|
+
)
|
|
84
|
+
yield from super()._core_iterator(new_circuit, sim_state, all_measurements_are_terminal)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@pytest.mark.parametrize(
|
|
88
|
+
['angles', 'error', 'characterization_flags'],
|
|
89
|
+
_create_tests(n=10, seed=32432432, with_options=True),
|
|
90
|
+
)
|
|
91
|
+
def test_calibrate_z_phases(angles, error, characterization_flags):
|
|
92
|
+
|
|
93
|
+
original_gate = cirq.PhasedFSimGate(**{k: v for k, v in zip(_ANGLES, angles)})
|
|
94
|
+
actual_gate = cirq.PhasedFSimGate(**{k: v + e for k, v, e in zip(_ANGLES, angles, error)})
|
|
95
|
+
|
|
96
|
+
options = XEBPhasedFSimCharacterizationOptions(
|
|
97
|
+
**{f'{n}_default': t for n, t in zip(_ANGLES, angles)}, **characterization_flags
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
sampler = _TestSimulator(original_gate, actual_gate, seed=0)
|
|
101
|
+
qubits = cirq.q(0, 0), cirq.q(0, 1)
|
|
102
|
+
calibrated_gate = calibrate_z_phases(
|
|
103
|
+
sampler,
|
|
104
|
+
qubits,
|
|
105
|
+
original_gate,
|
|
106
|
+
options,
|
|
107
|
+
n_repetitions=10,
|
|
108
|
+
n_combinations=10,
|
|
109
|
+
n_circuits=10,
|
|
110
|
+
cycle_depths=range(3, 10),
|
|
111
|
+
)[qubits]
|
|
112
|
+
|
|
113
|
+
initial_unitary = cirq.unitary(original_gate)
|
|
114
|
+
final_unitary = cirq.unitary(calibrated_gate)
|
|
115
|
+
target_unitary = cirq.unitary(actual_gate)
|
|
116
|
+
maximally_mixed_state = np.eye(4) / 2
|
|
117
|
+
dm_initial = initial_unitary @ maximally_mixed_state @ initial_unitary.T.conj()
|
|
118
|
+
dm_final = final_unitary @ maximally_mixed_state @ final_unitary.T.conj()
|
|
119
|
+
dm_target = target_unitary @ maximally_mixed_state @ target_unitary.T.conj()
|
|
120
|
+
|
|
121
|
+
original_dist = _trace_distance(dm_initial, dm_target)
|
|
122
|
+
new_dist = _trace_distance(dm_final, dm_target)
|
|
123
|
+
|
|
124
|
+
# Either we reduced the error or the error is small enough.
|
|
125
|
+
assert new_dist < original_dist or new_dist < 1e-6
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@pytest.mark.parametrize(['angles', 'error'], _create_tests(n=3, seed=32432432))
|
|
129
|
+
def test_calibrate_z_phases_no_options(angles, error):
|
|
130
|
+
|
|
131
|
+
original_gate = cirq.PhasedFSimGate(**{k: v for k, v in zip(_ANGLES, angles)})
|
|
132
|
+
actual_gate = cirq.PhasedFSimGate(**{k: v + e for k, v, e in zip(_ANGLES, angles, error)})
|
|
133
|
+
|
|
134
|
+
sampler = _TestSimulator(original_gate, actual_gate, seed=0)
|
|
135
|
+
qubits = cirq.q(0, 0), cirq.q(0, 1)
|
|
136
|
+
calibrated_gate = calibrate_z_phases(
|
|
137
|
+
sampler,
|
|
138
|
+
qubits,
|
|
139
|
+
original_gate,
|
|
140
|
+
options=None,
|
|
141
|
+
n_repetitions=10,
|
|
142
|
+
n_combinations=10,
|
|
143
|
+
n_circuits=10,
|
|
144
|
+
cycle_depths=range(3, 10),
|
|
145
|
+
)[qubits]
|
|
146
|
+
|
|
147
|
+
initial_unitary = cirq.unitary(original_gate)
|
|
148
|
+
final_unitary = cirq.unitary(calibrated_gate)
|
|
149
|
+
target_unitary = cirq.unitary(actual_gate)
|
|
150
|
+
maximally_mixed_state = np.eye(4) / 2
|
|
151
|
+
dm_initial = initial_unitary @ maximally_mixed_state @ initial_unitary.T.conj()
|
|
152
|
+
dm_final = final_unitary @ maximally_mixed_state @ final_unitary.T.conj()
|
|
153
|
+
dm_target = target_unitary @ maximally_mixed_state @ target_unitary.T.conj()
|
|
154
|
+
|
|
155
|
+
original_dist = _trace_distance(dm_initial, dm_target)
|
|
156
|
+
new_dist = _trace_distance(dm_final, dm_target)
|
|
157
|
+
|
|
158
|
+
# Either we reduced the error or the error is small enough.
|
|
159
|
+
assert new_dist < original_dist or new_dist < 1e-6
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
@pytest.mark.parametrize(['angles', 'error'], _create_tests(n=3, seed=32432432))
|
|
163
|
+
def test_calibrate_z_phases_workflow_no_options(angles, error):
|
|
164
|
+
|
|
165
|
+
original_gate = cirq.PhasedFSimGate(**{k: v for k, v in zip(_ANGLES, angles)})
|
|
166
|
+
actual_gate = cirq.PhasedFSimGate(**{k: v + e for k, v, e in zip(_ANGLES, angles, error)})
|
|
167
|
+
|
|
168
|
+
sampler = _TestSimulator(original_gate, actual_gate, seed=0)
|
|
169
|
+
qubits = cirq.q(0, 0), cirq.q(0, 1)
|
|
170
|
+
result, _ = z_phase_calibration_workflow(
|
|
171
|
+
sampler,
|
|
172
|
+
qubits,
|
|
173
|
+
original_gate,
|
|
174
|
+
options=None,
|
|
175
|
+
n_repetitions=1,
|
|
176
|
+
n_combinations=1,
|
|
177
|
+
n_circuits=1,
|
|
178
|
+
cycle_depths=(1, 2),
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
for params in result.final_params.values():
|
|
182
|
+
assert 'zeta' in params
|
|
183
|
+
assert 'chi' in params
|
|
184
|
+
assert 'gamma' in params
|
|
185
|
+
assert 'phi' not in params
|
|
186
|
+
assert 'theta' not in params
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def test_plot_z_phase_calibration_result():
|
|
190
|
+
df = pd.DataFrame()
|
|
191
|
+
qs = cirq.q(0, 0), cirq.q(0, 1), cirq.q(0, 2)
|
|
192
|
+
df.index = [qs[:2], qs[-2:]]
|
|
193
|
+
df['cycle_depths_0'] = [[1, 2, 3]] * 2
|
|
194
|
+
df['fidelities_0'] = [[0.9, 0.8, 0.7], [0.6, 0.4, 0.1]]
|
|
195
|
+
df['layer_fid_std_0'] = [0.1, 0.2]
|
|
196
|
+
df['fidelities_c'] = [[0.9, 0.92, 0.93], [0.7, 0.77, 0.8]]
|
|
197
|
+
df['layer_fid_std_c'] = [0.2, 0.3]
|
|
198
|
+
|
|
199
|
+
axes = plot_z_phase_calibration_result(before_after_df=df)
|
|
200
|
+
|
|
201
|
+
np.testing.assert_allclose(axes[0].lines[0].get_xdata().astype(float), [1, 2, 3])
|
|
202
|
+
np.testing.assert_allclose(axes[0].lines[0].get_ydata().astype(float), [0.9, 0.8, 0.7])
|
|
203
|
+
np.testing.assert_allclose(axes[0].lines[1].get_ydata().astype(float), [0.9, 0.92, 0.93])
|
|
204
|
+
|
|
205
|
+
np.testing.assert_allclose(axes[1].lines[0].get_xdata().astype(float), [1, 2, 3])
|
|
206
|
+
np.testing.assert_allclose(axes[1].lines[0].get_ydata().astype(float), [0.6, 0.4, 0.1])
|
|
207
|
+
np.testing.assert_allclose(axes[1].lines[1].get_ydata().astype(float), [0.7, 0.77, 0.8])
|
{cirq_core-1.5.0.dev20241105235147.dist-info → cirq_core-1.5.0.dev20241108000946.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cirq-core
|
|
3
|
-
Version: 1.5.0.
|
|
3
|
+
Version: 1.5.0.dev20241108000946
|
|
4
4
|
Summary: A framework for creating, editing, and invoking Noisy Intermediate Scale Quantum (NISQ) circuits.
|
|
5
5
|
Home-page: http://github.com/quantumlib/cirq
|
|
6
6
|
Author: The Cirq Developers
|
{cirq_core-1.5.0.dev20241105235147.dist-info → cirq_core-1.5.0.dev20241108000946.dist-info}/RECORD
RENAMED
|
@@ -4,8 +4,8 @@ cirq/_compat_test.py,sha256=Qq3ZcfgD-Nb81cEppQdJqhAyrVqXKtfXZYGXT0p-Wh0,34718
|
|
|
4
4
|
cirq/_doc.py,sha256=yDyWUD_2JDS0gShfGRb-rdqRt9-WeL7DhkqX7np0Nko,2879
|
|
5
5
|
cirq/_import.py,sha256=p9gMHJscbtDDkfHOaulvd3Aer0pwUF5AXpL89XR8dNw,8402
|
|
6
6
|
cirq/_import_test.py,sha256=6K_v0riZJXOXUphHNkGA8MY-JcmGlezFaGmvrNhm3OQ,1015
|
|
7
|
-
cirq/_version.py,sha256=
|
|
8
|
-
cirq/_version_test.py,sha256=
|
|
7
|
+
cirq/_version.py,sha256=nLBLggkBN0DieQBaZbvaR3lLwkBovuzp-fGSnDMTHWg,1206
|
|
8
|
+
cirq/_version_test.py,sha256=pb1073s50b2-w67rs2zQmSYC-Gu6JxTNT-JALyhxxLM,147
|
|
9
9
|
cirq/conftest.py,sha256=X7yLFL8GLhg2CjPw0hp5e_dGASfvHx1-QT03aUbhKJw,1168
|
|
10
10
|
cirq/json_resolver_cache.py,sha256=ytePZtNZgKjOF2NiVpUTuotB-JKZmQNOFIFdvXqsxHw,13271
|
|
11
11
|
cirq/py.typed,sha256=VFSlmh_lNwnaXzwY-ZuW-C2Ws5PkuDoVgBdNCs0jXJE,63
|
|
@@ -175,7 +175,7 @@ cirq/devices/thermal_noise_model.py,sha256=qdWrN7Kqw5dq1rwVUBLY1DDw1nCPADSaxoLtG
|
|
|
175
175
|
cirq/devices/thermal_noise_model_test.py,sha256=ox9b0BoHH6d73CjWWI1fIAGd_o3r-4qy8v2ggUwc-pw,12246
|
|
176
176
|
cirq/devices/unconstrained_device.py,sha256=cnwn1caPDj7GnBf9_GYX6jMCTCWi4q5g3RkLaVKnDLY,1525
|
|
177
177
|
cirq/devices/unconstrained_device_test.py,sha256=OF9E2m1HPpq6hyrNAwFGWaNOpQ3H2efAEj6V8Y-hRW4,832
|
|
178
|
-
cirq/experiments/__init__.py,sha256=
|
|
178
|
+
cirq/experiments/__init__.py,sha256=Sx2sW3Uj0p7W-E_HkZ21YpHVUvKlp_zc5WWtago4rlo,3667
|
|
179
179
|
cirq/experiments/fidelity_estimation.py,sha256=JK9yUoD4TL3nkf2yiEJ5f_RR-rhkAHSKpeLlYCRvZU4,9214
|
|
180
180
|
cirq/experiments/fidelity_estimation_test.py,sha256=SX5hwQjyzWm1yr1q0C_LCgbFfUF_Ye36g6HuQbtinGI,4918
|
|
181
181
|
cirq/experiments/grid_parallel_two_qubit_xeb.py,sha256=ROJZiXLaeF5IYo06DxeIvDHpwNJTIiqEAfKxs6v0vHM,2216
|
|
@@ -196,14 +196,16 @@ cirq/experiments/t1_decay_experiment.py,sha256=ealdmc_RTE__z1YUcaDEncDzQOaiT0K6I
|
|
|
196
196
|
cirq/experiments/t1_decay_experiment_test.py,sha256=Pgbm-37JiCdw9iQg2OaXVvs72xGWV2629CgsTQlLQnw,9139
|
|
197
197
|
cirq/experiments/t2_decay_experiment.py,sha256=lTgZ9yJ7Fk9_ozUCHysQn1qKrMQwTpsgEv-QnvsEif0,19158
|
|
198
198
|
cirq/experiments/t2_decay_experiment_test.py,sha256=DFR0BGn0Id4qNPfqIExj70TEAqf7Vrc8eK91Wj0YKTc,15031
|
|
199
|
-
cirq/experiments/two_qubit_xeb.py,sha256=
|
|
199
|
+
cirq/experiments/two_qubit_xeb.py,sha256=GFTB6yBvvda5hQvbjhjduXeNSQNJwbcOMPeDb0n_Wa0,20085
|
|
200
200
|
cirq/experiments/two_qubit_xeb_test.py,sha256=ZeZvClUAB8ir42Bd3PWr-s0_-QKWbFdYqfvvOMawsm0,10204
|
|
201
|
-
cirq/experiments/xeb_fitting.py,sha256=
|
|
201
|
+
cirq/experiments/xeb_fitting.py,sha256=Tzo2kg62udpRp654XArSDVcyVNhlhkNmpx9UVyxZiiw,30337
|
|
202
202
|
cirq/experiments/xeb_fitting_test.py,sha256=0GQ6ifSWdvEJ6-ICIcSR-R9lFLRwBykgf6toLElmg0o,15483
|
|
203
203
|
cirq/experiments/xeb_sampling.py,sha256=6ZOidGi7Kt6p4cMQCjK7qQuIUXVHCYl47B2GnL8M-Bw,14987
|
|
204
204
|
cirq/experiments/xeb_sampling_test.py,sha256=0XkQGvcURsug3IblE_wZrHVDoOQV3WuQilrqCJbDHjI,6784
|
|
205
205
|
cirq/experiments/xeb_simulation.py,sha256=yML2NAnYTRFG1wsQHvxtNEGEMXuExbWjrE2JYuCqnrk,5076
|
|
206
206
|
cirq/experiments/xeb_simulation_test.py,sha256=YWFKXPdtBFuZNhQoG06W1EetVhXighc3zyXwhKfGAeo,5652
|
|
207
|
+
cirq/experiments/z_phase_calibration.py,sha256=zKfxMg9X8u_wuBfABttOu83ZDa0eF6pJscFHtA4plPM,11303
|
|
208
|
+
cirq/experiments/z_phase_calibration_test.py,sha256=Hk6FR-mvlkwol1WQvc75gw6oC_adRW-0_JF5y0X598E,7592
|
|
207
209
|
cirq/interop/__init__.py,sha256=Xt1xU9UegP_jBNa9xaeOFSgtC0lYb_HNHq4hQQ0J20k,784
|
|
208
210
|
cirq/interop/quirk/__init__.py,sha256=W11jqaExSgvoUkjM_d0Kik4R8bqETF9Ezo27CDEB3iw,1237
|
|
209
211
|
cirq/interop/quirk/url_to_circuit.py,sha256=1ToWnFJdJIhCko9q62BEvOoCGxCpOUl8891IdCa52MM,14211
|
|
@@ -1186,8 +1188,8 @@ cirq/work/sampler.py,sha256=BDd1HrUOOcHBOvXNJqVX0vRcvmm8btZmZa5yYC5y8n0,19856
|
|
|
1186
1188
|
cirq/work/sampler_test.py,sha256=hL2UWx3dz2ukZVNxWftiKVvJcQoLplLZdQm-k1QcA40,13282
|
|
1187
1189
|
cirq/work/zeros_sampler.py,sha256=x1C7cup66a43n-3tm8QjhiqJa07qcJW10FxNp9jJ59Q,2356
|
|
1188
1190
|
cirq/work/zeros_sampler_test.py,sha256=JIkpBBFPJe5Ba4142vzogyWyboG1Q1ZAm0UVGgOoZn8,3279
|
|
1189
|
-
cirq_core-1.5.0.
|
|
1190
|
-
cirq_core-1.5.0.
|
|
1191
|
-
cirq_core-1.5.0.
|
|
1192
|
-
cirq_core-1.5.0.
|
|
1193
|
-
cirq_core-1.5.0.
|
|
1191
|
+
cirq_core-1.5.0.dev20241108000946.dist-info/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
|
|
1192
|
+
cirq_core-1.5.0.dev20241108000946.dist-info/METADATA,sha256=6X_nrqT_Q3cxXDC8_UOdP1Eas1a4Hn5zTvaFG7pGB9g,1992
|
|
1193
|
+
cirq_core-1.5.0.dev20241108000946.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
|
|
1194
|
+
cirq_core-1.5.0.dev20241108000946.dist-info/top_level.txt,sha256=Sz9iOxHU0IEMLSFGwiwOCaN2e9K-jFbBbtpPN1hB73g,5
|
|
1195
|
+
cirq_core-1.5.0.dev20241108000946.dist-info/RECORD,,
|
{cirq_core-1.5.0.dev20241105235147.dist-info → cirq_core-1.5.0.dev20241108000946.dist-info}/LICENSE
RENAMED
|
File without changes
|
{cirq_core-1.5.0.dev20241105235147.dist-info → cirq_core-1.5.0.dev20241108000946.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|