Skip to content

Commit

Permalink
Add fast transpilation method to BaseExperiment
Browse files Browse the repository at this point in the history
  • Loading branch information
wshanks committed Jun 5, 2024
1 parent d7df5f0 commit 2806a04
Show file tree
Hide file tree
Showing 10 changed files with 277 additions and 83 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,13 @@
from qiskit import QuantumCircuit
from qiskit.providers.options import Options
from qiskit.pulse import ScheduleBlock
from qiskit.transpiler import StagedPassManager, PassManager, Layout, CouplingMap
from qiskit.transpiler.passes import (
EnlargeWithAncilla,
FullAncillaAllocation,
ApplyLayout,
SetLayout,
)

from qiskit_experiments.calibration_management.calibrations import Calibrations
from qiskit_experiments.calibration_management.update_library import BaseUpdater
from qiskit_experiments.framework.base_analysis import BaseAnalysis
from qiskit_experiments.framework.base_experiment import BaseExperiment
from qiskit_experiments.framework.experiment_data import ExperimentData
from qiskit_experiments.framework.transpilation import map_qubits, minimal_transpile
from qiskit_experiments.exceptions import CalibrationError

LOG = logging.getLogger(__name__)
Expand Down Expand Up @@ -198,20 +192,6 @@ def _default_experiment_options(cls) -> Options:
options.update_options(result_index=-1, group="default")
return options

@classmethod
def _default_transpile_options(cls) -> Options:
"""Return empty default transpile options as optimization_level is not used."""
return Options()

def set_transpile_options(self, **fields):
r"""Add a warning message.
.. note::
If your experiment has overridden `_transpiled_circuits` and needs
transpile options then please also override `set_transpile_options`.
"""
warnings.warn(f"Transpile options are not used in {self.__class__.__name__ }.")

def update_calibrations(self, experiment_data: ExperimentData):
"""Update parameter values in the :class:`.Calibrations` instance.
Expand Down Expand Up @@ -295,42 +275,13 @@ def _transpiled_circuits(self) -> List[QuantumCircuit]:
Returns:
A list of transpiled circuits.
"""
transpiled = []
for circ in self.circuits():
circ = self._map_to_physical_qubits(circ)
circuits = [map_qubits(c, self.physical_qubits) for c in self.circuits()]
for circ in circuits:
self._attach_calibrations(circ)

transpiled.append(circ)
transpiled = minimal_transpile(circuits, self.backend, self.transpile_options)

return transpiled

def _map_to_physical_qubits(self, circuit: QuantumCircuit) -> QuantumCircuit:
"""Map program qubits to physical qubits.
Args:
circuit: The quantum circuit to map to device qubits.
Returns:
A quantum circuit that has the same number of qubits as the backend and where
the physical qubits of the experiment have been properly mapped.
"""
initial_layout = Layout.from_intlist(list(self.physical_qubits), *circuit.qregs)

coupling_map = self._backend_data.coupling_map
if coupling_map is not None:
coupling_map = CouplingMap(self._backend_data.coupling_map)

layout = PassManager(
[
SetLayout(initial_layout),
FullAncillaAllocation(coupling_map),
EnlargeWithAncilla(),
ApplyLayout(),
]
)

return StagedPassManager(["layout"], layout=layout).run(circuit)

@abstractmethod
def _attach_calibrations(self, circuit: QuantumCircuit):
"""Attach the calibrations to the quantum circuit.
Expand Down
43 changes: 36 additions & 7 deletions qiskit_experiments/framework/base_experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,23 @@
Base Experiment class.
"""

from abc import ABC, abstractmethod
import copy
from abc import ABC, abstractmethod
from collections import OrderedDict
from typing import Sequence, Optional, Tuple, List, Dict, Union

from qiskit import transpile, QuantumCircuit
from qiskit import QuantumCircuit
from qiskit.providers import Job, Backend
from qiskit.exceptions import QiskitError
from qiskit.qobj.utils import MeasLevel
from qiskit.providers.options import Options
from qiskit_experiments.framework import BackendData
from qiskit_experiments.framework.store_init_args import StoreInitArgs
from qiskit_experiments.framework.transpilation import (
DEFAULT_TRANSPILE_OPTIONS,
map_qubits,
minimal_transpile,
)
from qiskit_experiments.framework.base_analysis import BaseAnalysis
from qiskit_experiments.framework.experiment_data import ExperimentData
from qiskit_experiments.framework.configs import ExperimentConfig
Expand Down Expand Up @@ -373,9 +378,8 @@ def _transpiled_circuits(self) -> List[QuantumCircuit]:
This function can be overridden to define custom transpilation.
"""
transpile_opts = copy.copy(self.transpile_options.__dict__)
transpile_opts["initial_layout"] = list(self.physical_qubits)
transpiled = transpile(self.circuits(), self.backend, **transpile_opts)
circuits = [map_qubits(c, self.physical_qubits) for c in self.circuits()]
transpiled = minimal_transpile(circuits, self.backend, self.transpile_options)

return transpiled

Expand Down Expand Up @@ -418,11 +422,36 @@ def set_experiment_options(self, **fields):

@classmethod
def _default_transpile_options(cls) -> Options:
"""Default transpiler options for transpilation of circuits"""
"""Default transpiler options for transpilation of circuits
Transpile Options:
optimization_level (int): Optimization level to pass to
:func:`qiskit.transpile`.
num_processes (int): Number of processes to use during
transpilation on Qiskit >= 1.0.
full_transpile (bool): If ``True``,
``BaseExperiment._transpiled_circuits`` (called by
:meth:`BaseExperiment.run` if not overridden by a subclass)
will call :func:`qiskit.transpile` on the output of
:meth:`BaseExperiment.circuits` before executing the circuits.
If ``False``, ``BaseExperiment._transpiled_circuits`` will
reindex the qubits in the output of
:meth:`BaseExperiment.circuits` using the experiments'
:meth:`BaseExperiment.physical_qubits`. Then it will check if
the circuit operations are all defined in the
:class:`qiskit.transpiler.Target` of the experiment's backend
or in the indiivdual circuit calibrations. If not, it will use
:class:`qiskit.transpiler.passes.BasisTranslator` to map the
circuit instructions to the backend. Additionally,
the :class:`qiskit.transpiler.passes.PulseGates` transpiler
pass will be run if the :class:`qiskit.transpiler.Target`
contains any custom pulse gate calibrations.
"""
# Experiment subclasses can override this method if they need
# to set specific default transpiler options to transpile the
# experiment circuits.
return Options(optimization_level=0)
return copy.copy(DEFAULT_TRANSPILE_OPTIONS)

@property
def transpile_options(self) -> Options:
Expand Down
9 changes: 4 additions & 5 deletions qiskit_experiments/framework/experiment_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -866,11 +866,10 @@ def _add_job_data(
LOG.warning("Job was cancelled before completion [Job ID: %s]", jid)
return jid, False
if status == JobStatus.ERROR:
LOG.error(
"Job data not added for errored job [Job ID: %s]\nError message: %s",
jid,
job.error_message(),
)
msg = f"Job data not added for errored job [Job ID: {jid}]"
if hasattr(job, "error_message"):
msg += f"\nError message: {job.error_message()}"
LOG.error(msg)
return jid, False
LOG.warning("Adding data from job failed [Job ID: %s]", job.job_id())
raise ex
Expand Down
Loading

0 comments on commit 2806a04

Please sign in to comment.