Skip to content

Commit

Permalink
Add support for saving/loading calibration parameters without schedul…
Browse files Browse the repository at this point in the history
…es (#1357)

5b6fa06 added support for saving and
loading `Calibrations` objects with a JSON format that preserved
calibrated gate schedules. However, it did not capture and restore the
`Parameter` objects for calibration parameters (like `drive_freq`) which
were not associated with a schedule. This commit adds support for these
parameters by adding an entry to the serialization model that holds a
placeholder `QuantumCircuit` with the parameters attached to placeholder
instructions. `ExperimentEncoder` can serialize `QuantumCircuit` to qpy
and so supports this format. Using a placeholder circuit like this is
the only supported way to serialize `Parameter` objects. An alternative
approach would be to store and retrieve the hidden attributes of the
`Parameter` objects directly.

Closes #1355
  • Loading branch information
wshanks authored Jan 31, 2024
1 parent 32f02b1 commit a7a3112
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 4 deletions.
32 changes: 32 additions & 0 deletions qiskit_experiments/calibration_management/save_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
from datetime import datetime
from typing import List, Dict, Any

from qiskit import QuantumCircuit
from qiskit.circuit import Instruction
from qiskit.pulse import ScheduleBlock

from .calibrations import Calibrations
Expand Down Expand Up @@ -118,6 +120,14 @@ class CalibrationModelV1:
parameters: List[ParameterModelV1] = field(default_factory=list)
"""List of calibrated pulse parameters."""

schedule_free_parameters: QuantumCircuit = field(default_factory=lambda: QuantumCircuit(1))
"""Placeholder circuit for parameters not associated with a schedule
The circuit contains placeholder instructions which have the Parameter
objects attached and operate on the qubits that the parameter is associated
with in the calibrations.
"""

schema_version: str = "1.0"
"""Version of this data model. This must be static."""

Expand Down Expand Up @@ -177,13 +187,26 @@ def calibrations_to_dict(
sched_obj.metadata.update(qubit_metadata)
sched_entries.append(sched_obj)

max_qubit = max(
(max(k.qubits or (0,)) for k in cals._parameter_map if k.schedule is None),
default=0,
)
schedule_free_parameters = QuantumCircuit(max_qubit + 1)
for sched_key, param in cals._parameter_map.items():
if sched_key.schedule is None:
schedule_free_parameters.append(
Instruction("parameter_container", len(sched_key.qubits), 0, [param]),
sched_key.qubits,
)

model = CalibrationModelV1(
backend_name=cals.backend_name,
backend_version=cals.backend_version,
device_coupling_graph=getattr(cals, "_coupling_map"),
control_channel_map=ControlChannelMap(getattr(cals, "_control_channel_map")),
schedules=sched_entries,
parameters=data_entries,
schedule_free_parameters=schedule_free_parameters,
)

return asdict(model)
Expand Down Expand Up @@ -257,6 +280,15 @@ def calibrations_from_dict(
schedule=param.schedule,
update_inst_map=False,
)

for instruction in model.schedule_free_parameters.data:
# For some reason, pylint thinks the items in data are tuples instead
# of CircuitInstruction. Remove the following line if it ever stops
# thinking that:
# pylint: disable=no-member
for param in instruction.operation.params:
cals._register_parameter(param, instruction.qubits)

cals.update_inst_map()

return cals
4 changes: 0 additions & 4 deletions test/calibration/test_calibrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

from test.base import QiskitExperimentsTestCase
import os
import unittest
import uuid
from collections import defaultdict
from datetime import datetime, timezone, timedelta
Expand Down Expand Up @@ -1692,9 +1691,6 @@ def test_save_load_library_csv(self):
BackendData(backend).drive_freqs[0],
)

# Expected to fail because json calibration loading does not support
# restoring Parameter objects
@unittest.expectedFailure
def test_save_load_library(self):
"""Test that we can load and save a library.
Expand Down

0 comments on commit a7a3112

Please sign in to comment.