Skip to content

Commit 32a806e

Browse files
mrwojtekdstrain115CirqBot
authored
Add a convenience interface that allows to issue Floquet calibration requests on cirq.google.Engine (#3690)
* Add PhasedFSimParameters data class. * Add basic containers for PhasedFSim calibration * Fix class name * Implement request encoding and decoding. * Add helper method to extract parameters * Fix import * Clean up data classes * Collect all the functionality from the children PRs. * Rename estimate to characterize. * Add some todos * Re-interoduce changes from child branch * Fix import * Fix imports * Rename PhasedFSimParameters to PhasedFSimCharacterization * Add docstring to the PhasedFSimCharacterization class * Add tests for PhasedFsimCharacterization * Fix json serialization * Apply Doug's comments * Reformat file * Remove support for json serialization * Add note to add JSON serialization * Fix documentation * Remove support for JSON serialization * Fix linter errors * Add documentation for request and result objects * Add documentation for run_characaterizations * Remove handler_name argument * Fix type for pairs field * Add JSON tests for PhasedFSimCharacterization. * Revert "Remove support for json serialization" This reverts commit 7bb1e34. * fix formatting * Add tests for encoding and decoding of calibration requests/results. * Fix lint error * Fix lint errors * Remove gate set from dataclasses * Add reference to mypy import issue * Add JSON serialization for all Floquet calibration objects * Formatting * Add tests fro get_parameters * Increase test coverage a bit more. * Add copyrights and one more trivial test * Add copyright notices * Fix remaining test coverage by providing a test for run_characterization * Addressed review comments * Remove debug line. * Fix type checks * Restore abstract class for request container * Ignore type check for abstract dataclass * Fix formatting * Remove TODOs * Fixes to json handling * Clarify documentation for gate attribute. * Remove Floquet-specific characterization result. Co-authored-by: Doug Strain <dstrain@google.com> Co-authored-by: Cirq Bot <craiggidney+github+cirqbot@google.com>
1 parent c5c2d95 commit 32a806e

13 files changed

+699
-6
lines changed

cirq/google/__init__.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,14 @@
2020
arg_from_proto,
2121
)
2222

23-
from cirq.google.calibration import PhasedFSimCharacterization
23+
from cirq.google.calibration import (
24+
FloquetPhasedFSimCalibrationOptions,
25+
FloquetPhasedFSimCalibrationRequest,
26+
PhasedFSimCalibrationRequest,
27+
PhasedFSimCalibrationResult,
28+
PhasedFSimCharacterization,
29+
run_characterizations,
30+
)
2431

2532
from cirq.google.devices import (
2633
Bristlecone,

cirq/google/calibration/__init__.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,11 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
from cirq.google.calibration.phased_fsim import PhasedFSimCharacterization
15+
from cirq.google.calibration.phased_fsim import (
16+
FloquetPhasedFSimCalibrationOptions,
17+
FloquetPhasedFSimCalibrationRequest,
18+
PhasedFSimCalibrationRequest,
19+
PhasedFSimCalibrationResult,
20+
PhasedFSimCharacterization,
21+
)
22+
from cirq.google.calibration.workflow import run_characterizations

cirq/google/calibration/phased_fsim.py

Lines changed: 197 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,20 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14+
from typing import Any, Dict, List, Optional, Tuple, TYPE_CHECKING
1415

15-
from typing import Dict, Optional, TYPE_CHECKING
16-
16+
import abc
17+
import collections
1718
import dataclasses
19+
import re
20+
21+
from cirq.circuits import Circuit
22+
from cirq.ops import Gate, Qid
23+
from cirq.google.api import v2
24+
from cirq.google.engine import CalibrationLayer, CalibrationResult
25+
26+
27+
_FLOQUET_PHASED_FSIM_HANDLER_NAME = 'floquet_phased_fsim_characterization'
1828

1929
if TYPE_CHECKING:
2030
# Workaround for mypy custom dataclasses (python/mypy#5406)
@@ -130,3 +140,188 @@ def override_by(self, other: 'PhasedFSimCharacterization') -> 'PhasedFSimCharact
130140
other that are not None). Otherwise the current values are used.
131141
"""
132142
return other.merge_with(self)
143+
144+
145+
class PhasedFSimCalibrationOptions(abc.ABC):
146+
"""Base class for calibration-specific options passed together with the requests."""
147+
148+
149+
@dataclasses.dataclass(frozen=True)
150+
class PhasedFSimCalibrationResult:
151+
"""The PhasedFSimGate characterization result.
152+
153+
Attributes:
154+
parameters: Map from qubit pair to characterization result. For each pair of characterized
155+
quibts a and b either only (a, b) or only (b, a) is present.
156+
gate: Characterized gate for each qubit pair. This is copied from the matching
157+
PhasedFSimCalibrationRequest and is included to preserve execution context.
158+
"""
159+
160+
parameters: Dict[Tuple[Qid, Qid], PhasedFSimCharacterization]
161+
gate: Gate
162+
options: PhasedFSimCalibrationOptions
163+
164+
def get_parameters(self, a: Qid, b: Qid) -> Optional['PhasedFSimCharacterization']:
165+
"""Returns parameters for a qubit pair (a, b) or None when unknown."""
166+
if (a, b) in self.parameters:
167+
return self.parameters[(a, b)]
168+
elif (b, a) in self.parameters:
169+
return self.parameters[(b, a)].parameters_for_qubits_swapped()
170+
else:
171+
return None
172+
173+
@classmethod
174+
def _create_parameters_dict(
175+
cls,
176+
parameters: List[Tuple[Qid, Qid, PhasedFSimCharacterization]],
177+
) -> Dict[Tuple[Qid, Qid], PhasedFSimCharacterization]:
178+
"""Utility function to create parameters from JSON.
179+
180+
Can be used from child classes to instantiate classes in a _from_json_dict_
181+
method."""
182+
return {(q_a, q_b): params for q_a, q_b, params in parameters}
183+
184+
@classmethod
185+
def _from_json_dict_(
186+
cls,
187+
**kwargs,
188+
) -> 'PhasedFSimCalibrationResult':
189+
"""Magic method for the JSON serialization protocol.
190+
191+
Converts serialized dictionary into a dict suitable for
192+
class instantiation."""
193+
del kwargs['cirq_type']
194+
kwargs['parameters'] = cls._create_parameters_dict(kwargs['parameters'])
195+
return cls(**kwargs)
196+
197+
def _json_dict_(self) -> Dict[str, Any]:
198+
"""Magic method for the JSON serialization protocol."""
199+
return {
200+
'cirq_type': 'PhasedFSimCalibrationResult',
201+
'gate': self.gate,
202+
'parameters': [(q_a, q_b, params) for (q_a, q_b), params in self.parameters.items()],
203+
'options': self.options,
204+
}
205+
206+
207+
# We have to relax a mypy constraint, see https://github.com/python/mypy/issues/5374
208+
@dataclasses.dataclass(frozen=True) # type: ignore
209+
class PhasedFSimCalibrationRequest(abc.ABC):
210+
"""Description of the request to characterize PhasedFSimGate.
211+
212+
Attributes:
213+
pairs: Set of qubit pairs to characterize. A single qubit can appear on at most one pair in
214+
the set.
215+
gate: Gate to characterize for each qubit pair from pairs. This must be a supported gate
216+
which can be described cirq.PhasedFSim gate. This gate must be serialized by the
217+
cirq.google.SerializableGateSet used
218+
"""
219+
220+
pairs: Tuple[Tuple[Qid, Qid], ...]
221+
gate: Gate # Any gate which can be described by cirq.PhasedFSim
222+
223+
@abc.abstractmethod
224+
def to_calibration_layer(self) -> CalibrationLayer:
225+
"""Encodes this characterization request in a CalibrationLayer object."""
226+
227+
@abc.abstractmethod
228+
def parse_result(self, result: CalibrationResult) -> PhasedFSimCalibrationResult:
229+
"""Decodes the characterization result issued for this request."""
230+
231+
232+
@json_serializable_dataclass(frozen=True)
233+
class FloquetPhasedFSimCalibrationOptions(PhasedFSimCalibrationOptions):
234+
"""Options specific to Floquet PhasedFSimCalibration.
235+
236+
Some angles require another angle to be characterized first so result might have more angles
237+
characterized than requested here.
238+
239+
Attributes:
240+
characterize_theta: Whether to characterize θ angle.
241+
characterize_zeta: Whether to characterize ζ angle.
242+
characterize_chi: Whether to characterize χ angle.
243+
characterize_gamma: Whether to characterize γ angle.
244+
characterize_phi: Whether to characterize φ angle.
245+
"""
246+
247+
characterize_theta: bool
248+
characterize_zeta: bool
249+
characterize_chi: bool
250+
characterize_gamma: bool
251+
characterize_phi: bool
252+
253+
254+
@dataclasses.dataclass(frozen=True)
255+
class FloquetPhasedFSimCalibrationRequest(PhasedFSimCalibrationRequest):
256+
"""PhasedFSim characterization request specific to Floquet calibration.
257+
258+
Attributes:
259+
options: Floquet-specific characterization options.
260+
"""
261+
262+
options: FloquetPhasedFSimCalibrationOptions
263+
264+
def to_calibration_layer(self) -> CalibrationLayer:
265+
circuit = Circuit([self.gate.on(*pair) for pair in self.pairs])
266+
return CalibrationLayer(
267+
calibration_type=_FLOQUET_PHASED_FSIM_HANDLER_NAME,
268+
program=circuit,
269+
args={
270+
'est_theta': self.options.characterize_theta,
271+
'est_zeta': self.options.characterize_zeta,
272+
'est_chi': self.options.characterize_chi,
273+
'est_gamma': self.options.characterize_gamma,
274+
'est_phi': self.options.characterize_phi,
275+
# Experimental option that should always be set to True.
276+
'readout_corrections': True,
277+
},
278+
)
279+
280+
def parse_result(self, result: CalibrationResult) -> PhasedFSimCalibrationResult:
281+
decoded: Dict[int, Dict[str, Any]] = collections.defaultdict(lambda: {})
282+
for keys, values in result.metrics['angles'].items():
283+
for key, value in zip(keys, values):
284+
match = re.match(r'(\d+)_(.+)', str(key))
285+
if not match:
286+
raise ValueError(f'Unknown metric name {key}')
287+
index = int(match[1])
288+
name = match[2]
289+
decoded[index][name] = value
290+
291+
parsed = {}
292+
for data in decoded.values():
293+
a = v2.qubit_from_proto_id(data['qubit_a'])
294+
b = v2.qubit_from_proto_id(data['qubit_b'])
295+
parsed[(a, b)] = PhasedFSimCharacterization(
296+
theta=data.get('theta_est', None),
297+
zeta=data.get('zeta_est', None),
298+
chi=data.get('chi_est', None),
299+
gamma=data.get('gamma_est', None),
300+
phi=data.get('phi_est', None),
301+
)
302+
303+
return PhasedFSimCalibrationResult(parameters=parsed, gate=self.gate, options=self.options)
304+
305+
@classmethod
306+
def _from_json_dict_(
307+
cls,
308+
gate: Gate,
309+
pairs: List[Tuple[Qid, Qid]],
310+
options: FloquetPhasedFSimCalibrationOptions,
311+
**kwargs,
312+
) -> 'PhasedFSimCalibrationRequest':
313+
"""Magic method for the JSON serialization protocol.
314+
315+
Converts serialized dictionary into a dict suitable for
316+
class instantiation."""
317+
instantiation_pairs = tuple((q_a, q_b) for q_a, q_b in pairs)
318+
return cls(instantiation_pairs, gate, options)
319+
320+
def _json_dict_(self) -> Dict[str, Any]:
321+
"""Magic method for the JSON serialization protocol."""
322+
return {
323+
'cirq_type': 'FloquetPhasedFSimCalibrationRequest',
324+
'pairs': [(pair[0], pair[1]) for pair in self.pairs],
325+
'gate': self.gate,
326+
'options': self.options,
327+
}

0 commit comments

Comments
 (0)