From 48e0b371e4ce9286fcf71eee8b8f6a26066834e1 Mon Sep 17 00:00:00 2001 From: Christopher Wood Date: Mon, 25 Oct 2021 16:43:31 -0400 Subject: [PATCH 1/7] Add backend property to BaseExperiment --- .../framework/base_experiment.py | 177 +++++++++++------- .../framework/experiment_data.py | 25 ++- 2 files changed, 123 insertions(+), 79 deletions(-) diff --git a/qiskit_experiments/framework/base_experiment.py b/qiskit_experiments/framework/base_experiment.py index 47815f98a3..bbf5ec07ab 100644 --- a/qiskit_experiments/framework/base_experiment.py +++ b/qiskit_experiments/framework/base_experiment.py @@ -14,9 +14,9 @@ """ from abc import ABC, abstractmethod -from typing import Iterable, Optional, Tuple, List, Dict import copy from numbers import Integral +from typing import Sequence, Optional, Tuple, List, Dict, Union from qiskit import transpile, assemble, QuantumCircuit from qiskit.providers import BaseJob @@ -47,12 +47,18 @@ class BaseExperiment(ABC): # ExperimentData class for experiment __experiment_data__ = ExperimentData - def __init__(self, qubits: Iterable[int], experiment_type: Optional[str] = None): + def __init__( + self, + qubits: Sequence[int], + backend: Optional[Backend] = None, + experiment_type: Optional[str] = None, + ): """Initialize the experiment object. Args: qubits: the number of qubits or list of physical qubits for the experiment. + backend: Optional, the backend to run the experiment on. experiment_type: Optional, the experiment type string. Raises: @@ -61,6 +67,11 @@ def __init__(self, qubits: Iterable[int], experiment_type: Optional[str] = None) # Experiment identification metadata self._type = experiment_type if experiment_type else type(self).__name__ + # Backend + self._backend = None + if backend is not None: + self._set_backend(backend) + # Circuit parameters if isinstance(qubits, Integral): self._num_qubits = qubits @@ -77,16 +88,65 @@ def __init__(self, qubits: Iterable[int], experiment_type: Optional[str] = None) self._run_options = self._default_run_options() self._analysis_options = self._default_analysis_options() + @property + def experiment_type(self) -> str: + """Return experiment type.""" + return self._type + + @property + def physical_qubits(self) -> Tuple[int]: + """Return the device qubits for the experiment.""" + return self._physical_qubits + + @property + def num_qubits(self) -> int: + """Return the number of qubits for the experiment.""" + return self._num_qubits + + @property + def backend(self) -> Union[Backend, None]: + """Return the backend for the experiment""" + return self._backend + + @backend.setter + def backend(self, backend: Union[Backend, None]) -> None: + """Set the backend for the experiment""" + self._set_backend(backend) + + def _set_backend(self, backend: Backend): + """Set the backend for the experiment. + + Subclasses can override this method to extract additional + properties from the supplied backend if required. + """ + self._backend = backend + + # Scheduling parameters + if not self._backend.configuration().simulator and not isinstance(backend, FakeBackend): + timing_constraints = getattr(self.transpile_options, "timing_constraints", {}) + if "acquire_alignment" not in timing_constraints: + timing_constraints["aquire_aligment"] = 16 + scheduling_method = getattr(self.transpile_options, "scheduling_method", "alap") + self.set_transpile_options( + timing_constraints=timing_constraints, scheduling_method=scheduling_method + ) + + def copy(self) -> "BaseExperiment": + """Return a copy of the experiment""" + return copy.copy(self) + def run( self, - backend: Backend, + backend: Optional[Backend] = None, analysis: bool = True, **run_options, ) -> ExperimentData: """Run an experiment and perform analysis. Args: - backend: The backend to run the experiment on. + backend: Optional, the backend to run the experiment on. This + will override any currently set backends for the single + execution. analysis: If True run analysis on the experiment data. run_options: backend runtime options used for circuit execution. @@ -97,70 +157,43 @@ def run( QiskitError: if experiment is run with an incompatible existing ExperimentData container. """ - # Create experiment data container - experiment_data = self._initialize_experiment_data(backend) + if backend is None: + experiment = self + else: + experiment = self.copy() + experiment._set_backend(backend) + if experiment.backend is None: + raise QiskitError("Cannot run experiment, no backend has been set.") + + # Initialize result container + experiment_data = experiment._initialize_experiment_data() # Run options - run_opts = copy.copy(self.run_options) + run_opts = copy.copy(experiment.run_options) run_opts.update_options(**run_options) run_opts = run_opts.__dict__ - # Scheduling parameters - if backend.configuration().simulator is False and isinstance(backend, FakeBackend) is False: - timing_constraints = getattr(self.transpile_options.__dict__, "timing_constraints", {}) - timing_constraints["acquire_alignment"] = getattr( - timing_constraints, "acquire_alignment", 16 - ) - scheduling_method = getattr( - self.transpile_options.__dict__, "scheduling_method", "alap" - ) - self.set_transpile_options( - timing_constraints=timing_constraints, scheduling_method=scheduling_method - ) - # Generate and transpile circuits - transpile_opts = copy.copy(self.transpile_options.__dict__) - transpile_opts["initial_layout"] = list(self._physical_qubits) - circuits = transpile(self.circuits(backend), backend, **transpile_opts) - self._postprocess_transpiled_circuits(circuits, backend, **run_options) - - # Run experiment jobs - max_experiments = getattr(backend.configuration(), "max_experiments", None) - if max_experiments and len(circuits) > max_experiments: - # Split jobs for backends that have a maximum job size - job_circuits = [ - circuits[i : i + max_experiments] for i in range(0, len(circuits), max_experiments) - ] - else: - # Run as single job - job_circuits = [circuits] + transpile_opts = copy.copy(experiment.transpile_options.__dict__) + transpile_opts["initial_layout"] = list(experiment.physical_qubits) + circuits = transpile(experiment.circuits(), experiment.backend, **transpile_opts) + experiment._postprocess_transpiled_circuits(circuits, **run_options) # Run jobs - jobs = [] - for circs in job_circuits: - if isinstance(backend, LegacyBackend): - qobj = assemble(circs, backend=backend, **run_opts) - job = backend.run(qobj) - else: - job = backend.run(circs, **run_opts) - jobs.append(job) - - # Add experiment option metadata - self._add_job_metadata(experiment_data, jobs, **run_opts) - - # Add jobs + jobs = experiment._run_jobs(circuits, **run_opts) experiment_data.add_data(jobs) + experiment._add_job_metadata(experiment_data, jobs, **run_opts) # Optionally run analysis - if analysis and self.__analysis_class__: + if analysis and self.__analysis_class__ is not None: experiment_data.add_analysis_callback(self.run_analysis) # Return the ExperimentData future return experiment_data - def _initialize_experiment_data(self, backend: Backend) -> ExperimentData: + def _initialize_experiment_data(self) -> ExperimentData: """Initialize the return data container for the experiment run""" - return self.__experiment_data__(experiment=self, backend=backend) + return self.__experiment_data__(experiment=self) def run_analysis(self, experiment_data: ExperimentData, **options) -> ExperimentData: """Run analysis and update ExperimentData with analysis result. @@ -187,20 +220,29 @@ def run_analysis(self, experiment_data: ExperimentData, **options) -> Experiment analysis.run(experiment_data, **analysis_options) return experiment_data - @property - def num_qubits(self) -> int: - """Return the number of qubits for this experiment.""" - return self._num_qubits - - @property - def physical_qubits(self) -> Tuple[int]: - """Return the physical qubits for this experiment.""" - return self._physical_qubits + def _run_jobs(self, circuits: List[QuantumCircuit], **run_options) -> List[BaseJob]: + """Run circuits on backend as 1 or more jobs.""" + # Run experiment jobs + max_experiments = getattr(self.backend.configuration(), "max_experiments", None) + if max_experiments and len(circuits) > max_experiments: + # Split jobs for backends that have a maximum job size + job_circuits = [ + circuits[i : i + max_experiments] for i in range(0, len(circuits), max_experiments) + ] + else: + # Run as single job + job_circuits = [circuits] - @property - def experiment_type(self) -> str: - """Return experiment type.""" - return self._type + # Run jobs + jobs = [] + for circs in job_circuits: + if isinstance(self.backend, LegacyBackend): + qobj = assemble(circs, backend=self.backend, **run_options) + job = self.backend.run(qobj) + else: + job = self.backend.run(circs, **run_options) + jobs.append(job) + return jobs @classmethod def analysis(cls): @@ -211,12 +253,9 @@ def analysis(cls): return cls.__analysis_class__() @abstractmethod - def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: + def circuits(self) -> List[QuantumCircuit]: """Return a list of experiment circuits. - Args: - backend: Optional, a backend object. - Returns: A list of :class:`QuantumCircuit`. @@ -330,7 +369,7 @@ def set_analysis_options(self, **fields): """ self._analysis_options.update_options(**fields) - def _postprocess_transpiled_circuits(self, circuits, backend, **run_options): + def _postprocess_transpiled_circuits(self, circuits: List[QuantumCircuit], **run_options): """Additional post-processing of transpiled circuits before running on backend""" pass diff --git a/qiskit_experiments/framework/experiment_data.py b/qiskit_experiments/framework/experiment_data.py index eae48238bb..f5a12769fb 100644 --- a/qiskit_experiments/framework/experiment_data.py +++ b/qiskit_experiments/framework/experiment_data.py @@ -35,9 +35,15 @@ def __init__(self, experiment=None, backend=None, parent_id=None, job_ids=None): in the setting of a composite experiment job_ids (list[str]): Optional, IDs of jobs submitted for the experiment. """ + if experiment is not None: + backend = backend or experiment.backend + experiment_type = experiment.experiment_type + else: + experiment_type = None + self._experiment = experiment super().__init__( - experiment_type=experiment.experiment_type if experiment else None, + experiment_type=experiment_type, backend=backend, parent_id=parent_id, job_ids=job_ids, @@ -46,8 +52,8 @@ def __init__(self, experiment=None, backend=None, parent_id=None, job_ids=None): @property def experiment(self): - """Return Experiment object. - + """Return the experiment for this data. + Returns: BaseExperiment: the experiment object. """ @@ -98,11 +104,10 @@ def _copy_metadata(self, new_instance: Optional["ExperimentData"] = None) -> "Ex return super()._copy_metadata(new_instance) def __repr__(self): - out = f"{type(self).__name__}({self.experiment_type}" - out += f", {self.experiment_id}" - if self.backend: - out += f", backend={self.backend}" - if self.job_ids: - out += f", job_ids={self.job_ids}" - out += ")" + out = ( + f"" + ) return out From de38c2ebcf9e164b7c57447d2db9c5693946d44d Mon Sep 17 00:00:00 2001 From: Christopher Wood Date: Mon, 25 Oct 2021 17:13:07 -0400 Subject: [PATCH 2/7] Update experiments for API changes --- .../example/example_experiment.py | 2 +- .../base_calibration_experiment.py | 10 +++--- .../framework/composite/batch_experiment.py | 4 +-- .../composite/composite_experiment.py | 18 ++++++++-- .../composite/parallel_experiment.py | 4 +-- .../library/calibration/drag.py | 16 ++++----- .../library/calibration/fine_amplitude.py | 6 +++- .../library/calibration/fine_drag.py | 16 ++++----- .../library/calibration/rabi.py | 22 ++++++------ .../library/calibration/ramsey_xy.py | 13 ++++--- .../library/calibration/rough_frequency.py | 6 +++- .../characterization/cr_hamiltonian.py | 15 ++++---- .../characterization/fine_amplitude.py | 13 +++---- .../characterization/qubit_spectroscopy.py | 35 +++++++++++-------- .../library/characterization/t1.py | 13 ++++--- .../library/characterization/t2ramsey.py | 19 +++++----- .../library/quantum_volume/qv_experiment.py | 11 +++--- .../interleaved_rb_experiment.py | 5 ++- .../randomized_benchmarking/rb_experiment.py | 14 ++++---- .../tomography/tomography_experiment.py | 7 ++-- test/fake_experiment.py | 2 +- test/test_framework.py | 2 +- 22 files changed, 138 insertions(+), 115 deletions(-) diff --git a/docs/_ext/custom_styles/example/example_experiment.py b/docs/_ext/custom_styles/example/example_experiment.py index 6c42e7e593..75648e86ca 100644 --- a/docs/_ext/custom_styles/example/example_experiment.py +++ b/docs/_ext/custom_styles/example/example_experiment.py @@ -212,5 +212,5 @@ def __init__(self, qubit: int): """ super().__init__(qubits=[qubit]) - def circuits(self, backend=None): + def circuits(self): pass diff --git a/qiskit_experiments/calibration_management/base_calibration_experiment.py b/qiskit_experiments/calibration_management/base_calibration_experiment.py index d46506aff6..899f83da58 100644 --- a/qiskit_experiments/calibration_management/base_calibration_experiment.py +++ b/qiskit_experiments/calibration_management/base_calibration_experiment.py @@ -339,9 +339,9 @@ def get_schedule( return schedules - def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: + def circuits(self) -> List[QuantumCircuit]: """A wrapper to introduce an optional hook to add circuit metadata.""" - circuits = super().circuits(backend) + circuits = super().circuits() self._add_cal_metadata(circuits) @@ -358,14 +358,16 @@ def _add_cal_metadata(self, circuits: List[QuantumCircuit]): def run( self, - backend: Backend, + backend: Optional[Backend] = None, analysis: bool = True, **run_options, ) -> ExperimentData: """Run an experiment, perform analysis, and update any calibrations. Args: - backend: The backend to run the experiment on. + backend: Optional, the backend to run the experiment on. This + will override any currently set backends for the single + execution. analysis: If True run analysis on the experiment data. run_options: backend runtime options used for circuit execution. diff --git a/qiskit_experiments/framework/composite/batch_experiment.py b/qiskit_experiments/framework/composite/batch_experiment.py index c6d0a71ada..43ab84d0d7 100644 --- a/qiskit_experiments/framework/composite/batch_experiment.py +++ b/qiskit_experiments/framework/composite/batch_experiment.py @@ -41,7 +41,7 @@ def __init__(self, experiments): qubits = tuple(self._qubit_map.keys()) super().__init__(experiments, qubits) - def circuits(self, backend=None): + def circuits(self): batch_circuits = [] @@ -51,7 +51,7 @@ def circuits(self, backend=None): qubit_mapping = None else: qubit_mapping = [self._qubit_map[qubit] for qubit in expr.physical_qubits] - for circuit in expr.circuits(backend): + for circuit in expr.circuits(): # Update metadata circuit.metadata = { "experiment_type": self._type, diff --git a/qiskit_experiments/framework/composite/composite_experiment.py b/qiskit_experiments/framework/composite/composite_experiment.py index 5c41d0e0f2..983ee9ec45 100644 --- a/qiskit_experiments/framework/composite/composite_experiment.py +++ b/qiskit_experiments/framework/composite/composite_experiment.py @@ -41,7 +41,7 @@ def __init__(self, experiments, qubits, experiment_type=None): super().__init__(qubits, experiment_type=experiment_type) @abstractmethod - def circuits(self, backend=None): + def circuits(self): pass @property @@ -64,6 +64,18 @@ def component_analysis(self, index): """Return the component experiment Analysis object""" return self.component_experiment(index).analysis() + def copy(self) -> "BaseExperiment": + """Return a copy of the experiment""" + ret = super().copy() + # Recursively call copy of component experiments + ret._experiments = [exp.copy() for exp in self._experiments] + return ret + + def _set_backend(self, backend): + super()._set_backend(backend) + for subexp in self._experiments: + subexp._set_backend(backend) + def _add_job_metadata(self, experiment_data, jobs, **run_options): # Add composite metadata super()._add_job_metadata(experiment_data, jobs, **run_options) @@ -85,7 +97,7 @@ def _add_job_metadata(self, experiment_data, jobs, **run_options): sub_data = experiment_data.component_experiment_data(i) sub_exp._add_job_metadata(sub_data, jobs, **run_options) - def _postprocess_transpiled_circuits(self, circuits, backend, **run_options): + def _postprocess_transpiled_circuits(self, circuits, **run_options): for expr in self._experiments: if not isinstance(expr, CompositeExperiment): - expr._postprocess_transpiled_circuits(circuits, backend, **run_options) + expr._postprocess_transpiled_circuits(circuits, **run_options) diff --git a/qiskit_experiments/framework/composite/parallel_experiment.py b/qiskit_experiments/framework/composite/parallel_experiment.py index 286b6c8efc..b22a1e5382 100644 --- a/qiskit_experiments/framework/composite/parallel_experiment.py +++ b/qiskit_experiments/framework/composite/parallel_experiment.py @@ -32,7 +32,7 @@ def __init__(self, experiments): qubits += exp.physical_qubits super().__init__(experiments, qubits) - def circuits(self, backend=None): + def circuits(self): sub_circuits = [] sub_qubits = [] @@ -42,7 +42,7 @@ def circuits(self, backend=None): # Generate data for combination for expr in self._experiments: # Add subcircuits - circs = expr.circuits(backend) + circs = expr.circuits() sub_circuits.append(circs) sub_size.append(len(circs)) diff --git a/qiskit_experiments/library/calibration/drag.py b/qiskit_experiments/library/calibration/drag.py index f939abef74..055c4b9e10 100644 --- a/qiskit_experiments/library/calibration/drag.py +++ b/qiskit_experiments/library/calibration/drag.py @@ -17,7 +17,7 @@ from qiskit import QuantumCircuit from qiskit.circuit import Gate, Parameter -from qiskit.providers import Backend +from qiskit.providers.backend import Backend import qiskit.pulse as pulse from qiskit_experiments.framework import BaseExperiment, Options @@ -132,20 +132,18 @@ def set_experiment_options(self, reps: Optional[List] = None, **fields): super().set_experiment_options(reps=reps, **fields) - def __init__(self, qubit: int): + def __init__(self, qubit: int, backend: Optional[Backend] = None): """ Args: qubit: The qubit for which to run the Drag calibration. + backend: Optional, the backend to run the experiment on. """ - super().__init__([qubit]) + super().__init__([qubit], backend=backend) - def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: + def circuits(self) -> List[QuantumCircuit]: """Create the circuits for the Drag calibration. - Args: - backend: A backend object. - Returns: circuits: The circuits that will run the Drag calibration. @@ -159,7 +157,7 @@ def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: if schedule is None: beta = Parameter("β") - with pulse.build(backend=backend, name="drag") as schedule: + with pulse.build(backend=self.backend, name="drag") as schedule: pulse.play( pulse.Drag( duration=self.experiment_options.duration, @@ -167,7 +165,7 @@ def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: sigma=self.experiment_options.sigma, beta=beta, ), - pulse.DriveChannel(self._physical_qubits[0]), + pulse.DriveChannel(self.physical_qubits[0]), ) if len(schedule.parameters) != 1: diff --git a/qiskit_experiments/library/calibration/fine_amplitude.py b/qiskit_experiments/library/calibration/fine_amplitude.py index 57dc010a44..d2f641a0ec 100644 --- a/qiskit_experiments/library/calibration/fine_amplitude.py +++ b/qiskit_experiments/library/calibration/fine_amplitude.py @@ -16,6 +16,7 @@ import numpy as np from qiskit.circuit import Gate, QuantumCircuit +from qiskit.providers.backend import Backend from qiskit_experiments.calibration_management import ( BaseCalibrationExperiment, @@ -43,6 +44,7 @@ def __init__( schedule_name: str, cal_parameter_name: Optional[str] = "amp", auto_update: bool = True, + backend: Optional[Backend] = None, ): """see class :class:`FineAmplitude` for details. @@ -53,6 +55,7 @@ def __init__( cal_parameter_name: The name of the parameter in the schedule to update. auto_update: Whether or not to automatically update the calibrations. By default this variable is set to True. + backend: Optional, the backend to run the experiment on. """ super().__init__( @@ -62,6 +65,7 @@ def __init__( schedule_name=schedule_name, cal_parameter_name=cal_parameter_name, auto_update=auto_update, + backend=backend, ) self.transpile_options.inst_map = calibrations.default_inst_map @@ -99,7 +103,7 @@ def _add_cal_metadata(self, circuits: List[QuantumCircuit]): param_val = self._cals.get_parameter_value( self._param_name, - self._physical_qubits, + self.physical_qubits, self._sched_name, group=self.experiment_options.group, ) diff --git a/qiskit_experiments/library/calibration/fine_drag.py b/qiskit_experiments/library/calibration/fine_drag.py index d8de4f72df..f8a6b6c82c 100644 --- a/qiskit_experiments/library/calibration/fine_drag.py +++ b/qiskit_experiments/library/calibration/fine_drag.py @@ -12,13 +12,13 @@ """Fine DRAG calibration experiment.""" -from typing import Optional, List +from typing import List, Optional import numpy as np from qiskit import QuantumCircuit from qiskit.circuit import Gate from qiskit.circuit.library import XGate, SXGate -from qiskit.providers import Backend +from qiskit.providers.backend import Backend from qiskit_experiments.framework import BaseExperiment, Options from qiskit_experiments.library.calibration.analysis.fine_drag_analysis import ( @@ -165,13 +165,14 @@ def _default_analysis_options(cls) -> Options: return options - def __init__(self, qubit: int): + def __init__(self, qubit: int, backend: Optional[Backend] = None): """Setup a fine amplitude experiment on the given qubit. Args: qubit: The qubit on which to run the fine amplitude calibration experiment. + backend: Optional, the backend to run the experiment on. """ - super().__init__([qubit]) + super().__init__([qubit], backend=backend) @staticmethod def _pre_circuit() -> QuantumCircuit: @@ -185,12 +186,9 @@ def _post_circuit() -> QuantumCircuit: circ.ry(np.pi / 2, 0) # Maps unwanted Z rotations to qubit population. return circ - def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: + def circuits(self) -> List[QuantumCircuit]: """Create the circuits for the fine DRAG calibration experiment. - Args: - backend: A backend object. - Returns: A list of circuits with a variable number of gates. Each gate has the same pulse schedule. @@ -216,7 +214,7 @@ def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: circuit.measure_all() if schedule is not None: - circuit.add_calibration(schedule.name, self._physical_qubits, schedule, params=[]) + circuit.add_calibration(schedule.name, self.physical_qubits, schedule, params=[]) circuit.metadata = { "experiment_type": self._type, diff --git a/qiskit_experiments/library/calibration/rabi.py b/qiskit_experiments/library/calibration/rabi.py index a85998ae1c..fcb97b7aea 100644 --- a/qiskit_experiments/library/calibration/rabi.py +++ b/qiskit_experiments/library/calibration/rabi.py @@ -104,7 +104,7 @@ def _default_analysis_options(cls) -> Options: return options - def __init__(self, qubit: int): + def __init__(self, qubit: int, backend: Optional[Backend] = None): """Initialize a Rabi experiment on the given qubit. The parameters of the Gaussian Rabi pulse can be specified at run-time. @@ -116,8 +116,9 @@ def __init__(self, qubit: int): Args: qubit: The qubit on which to run the Rabi experiment. + backend: Optional, the backend to run the experiment on. """ - super().__init__([qubit]) + super().__init__([qubit], backend=backend) def _template_circuit(self, amp_param) -> QuantumCircuit: """Return the template quantum circuit.""" @@ -144,12 +145,9 @@ def _default_gate_schedule(self, backend: Optional[Backend] = None): return default_schedule - def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: + def circuits(self) -> List[QuantumCircuit]: """Create the circuits for the Rabi experiment. - Args: - backend: A backend object. - Returns: A list of circuits with a rabi gate with an attached schedule. Each schedule will have a different value of the scanned amplitude. @@ -163,7 +161,7 @@ def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: schedule = self.experiment_options.get("schedule", None) if schedule is None: - schedule = self._default_gate_schedule(backend=backend) + schedule = self._default_gate_schedule(backend=self.backend) else: if self.physical_qubits[0] not in set(ch.index for ch in schedule.channels): raise CalibrationError( @@ -182,6 +180,12 @@ def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: self.__rabi_gate_name__, (self.physical_qubits[0],), schedule, params=[param] ) + # Get backend dt + if self.backend is not None: + backend_dt = getattr(self.backend.configuration(), "dt", "n.a.") + else: + backend_dt = "n.a" + # Create the circuits to run circs = [] for amp in self.experiment_options.amplitudes: @@ -194,11 +198,9 @@ def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: "unit": "arb. unit", "amplitude": amp, "schedule": str(schedule), + "dt": backend_dt, } - if backend: - assigned_circ.metadata["dt"] = getattr(backend.configuration(), "dt", "n.a.") - circs.append(assigned_circ) return circs diff --git a/qiskit_experiments/library/calibration/ramsey_xy.py b/qiskit_experiments/library/calibration/ramsey_xy.py index 7fa7d5d5ec..7f9edb3376 100644 --- a/qiskit_experiments/library/calibration/ramsey_xy.py +++ b/qiskit_experiments/library/calibration/ramsey_xy.py @@ -17,8 +17,8 @@ from qiskit import QuantumCircuit from qiskit.circuit import Parameter -from qiskit.providers import Backend from qiskit.utils import apply_prefix +from qiskit.providers.backend import Backend from qiskit_experiments.framework import BaseExperiment from qiskit_experiments.library.calibration.analysis.remsey_xy_analysis import RamseyXYAnalysis @@ -109,6 +109,7 @@ def __init__( delays: Optional[List] = None, unit: str = "s", osc_freq: float = 2e6, + backend: Optional[Backend] = None, ): """Create new experiment. @@ -118,8 +119,9 @@ def __init__( unit: The unit of the delays. osc_freq: the oscillation frequency induced by the user through a virtual Rz rotation. This quantity is given in Hz. + backend: Optional, the backend to run the experiment on. """ - super().__init__([qubit]) + super().__init__([qubit], backend=backend) delays = delays or self.experiment_options.delays self.set_experiment_options(delays=delays, unit=unit, osc_freq=osc_freq) @@ -132,12 +134,9 @@ def _pre_circuit(self) -> QuantumCircuit: """ return QuantumCircuit(1) - def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: + def circuits(self) -> List[QuantumCircuit]: """Create the circuits for the Ramsey XY calibration experiment. - Args: - backend: A backend object. - Returns: A list of circuits with a variable delay. @@ -149,7 +148,7 @@ def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: conversion_factor = 1 if self.experiment_options.unit == "dt": try: - conversion_factor = getattr(backend.configuration(), "dt") + conversion_factor = getattr(self.backend.configuration(), "dt") except AttributeError as no_dt: raise AttributeError( "Dt parameter is missing from the backend's configuration." diff --git a/qiskit_experiments/library/calibration/rough_frequency.py b/qiskit_experiments/library/calibration/rough_frequency.py index 5088ba0c7f..4cb720ac10 100644 --- a/qiskit_experiments/library/calibration/rough_frequency.py +++ b/qiskit_experiments/library/calibration/rough_frequency.py @@ -12,7 +12,8 @@ """Calibration version of spectroscopy experiments.""" -from typing import Iterable +from typing import Iterable, Optional +from qiskit.providers.backend import Backend from qiskit_experiments.library.characterization.qubit_spectroscopy import QubitSpectroscopy from qiskit_experiments.library.characterization.ef_spectroscopy import EFSpectroscopy @@ -34,6 +35,7 @@ def __init__( unit: str = "Hz", auto_update: bool = True, absolute: bool = True, + backend: Optional[Backend] = None, ): """See :class:`QubitSpectroscopy` for detailed documentation. @@ -48,6 +50,7 @@ def __init__( automatically update the frequency in the calibrations. absolute: Boolean to specify if the frequencies are absolute or relative to the qubit frequency in the backend. + backend: Optional, the backend to run the experiment on. Raises: QiskitError: if there are less than three frequency shifts or if the unit is not known. @@ -61,6 +64,7 @@ def __init__( absolute, updater=Frequency, auto_update=auto_update, + backend=backend, ) diff --git a/qiskit_experiments/library/characterization/cr_hamiltonian.py b/qiskit_experiments/library/characterization/cr_hamiltonian.py index 9d44ac925d..ad62bb1e45 100644 --- a/qiskit_experiments/library/characterization/cr_hamiltonian.py +++ b/qiskit_experiments/library/characterization/cr_hamiltonian.py @@ -13,7 +13,7 @@ Cross resonance Hamiltonian tomography. """ -from typing import List, Tuple, Optional, Iterable, Dict +from typing import List, Tuple, Iterable, Dict, Optional import numpy as np from qiskit import pulse, circuit, QuantumCircuit @@ -131,6 +131,7 @@ def __init__( qubits: Tuple[int, int], flat_top_widths: Iterable[float], unit: str = "dt", + backend: Optional[Backend] = None, **kwargs, ): """Create a new experiment. @@ -142,12 +143,13 @@ def __init__( to scan. The total pulse duration including Gaussian rising and falling edges is implicitly computed with experiment parameters ``sigma`` and ``risefall``. unit: The time unit of durations. + backend: Optional, the backend to run the experiment on. kwargs: Pulse parameters. See :meth:`experiment_options` for details. Raises: QiskitError: When ``qubits`` length is not 2. """ - super().__init__(qubits=qubits) + super().__init__(qubits, backend=backend) if len(qubits) != 2: raise QiskitError( @@ -252,12 +254,9 @@ def _build_cr_schedule( return cross_resonance - def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: + def circuits(self) -> List[QuantumCircuit]: """Return a list of experiment circuits. - Args: - backend: The target backend. - Returns: A list of :class:`QuantumCircuit`. @@ -268,7 +267,7 @@ def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: prefactor = 1.0 try: - dt_factor = backend.configuration().dt + dt_factor = self.backend.configuration().dt except AttributeError as ex: raise AttributeError("Backend configuration does not provide time resolution.") from ex @@ -331,7 +330,7 @@ def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: gate=cr_gate, qubits=self.physical_qubits, schedule=self._build_cr_schedule( - backend=backend, + backend=self.backend, flat_top_width=prefactor * flat_top_width / self.__n_cr_pulses__, sigma=prefactor * opt.sigma, ), diff --git a/qiskit_experiments/library/characterization/fine_amplitude.py b/qiskit_experiments/library/characterization/fine_amplitude.py index 6f42b02762..fadc9862df 100644 --- a/qiskit_experiments/library/characterization/fine_amplitude.py +++ b/qiskit_experiments/library/characterization/fine_amplitude.py @@ -18,8 +18,7 @@ from qiskit import QuantumCircuit from qiskit.circuit import Gate from qiskit.circuit.library import XGate, SXGate -from qiskit.providers import Backend - +from qiskit.providers.backend import Backend from qiskit_experiments.framework import BaseExperiment, Options from qiskit_experiments.library.calibration.analysis.fine_amplitude_analysis import ( FineAmplitudeAnalysis, @@ -126,14 +125,15 @@ def _default_analysis_options(cls) -> Options: return options - def __init__(self, qubit: int, gate: Gate): + def __init__(self, qubit: int, gate: Gate, backend: Optional[Backend] = None): """Setup a fine amplitude experiment on the given qubit. Args: qubit: The qubit on which to run the fine amplitude calibration experiment. gate: The gate that will be repeated. + backend: Optional, the backend to run the experiment on. """ - super().__init__([qubit]) + super().__init__([qubit], backend=backend) self.experiment_options.gate = gate def _pre_circuit(self) -> QuantumCircuit: @@ -149,12 +149,9 @@ def _pre_circuit(self) -> QuantumCircuit: return circuit - def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: + def circuits(self) -> List[QuantumCircuit]: """Create the circuits for the fine amplitude calibration experiment. - Args: - backend: A backend object. - Returns: A list of circuits with a variable number of gates. diff --git a/qiskit_experiments/library/characterization/qubit_spectroscopy.py b/qiskit_experiments/library/characterization/qubit_spectroscopy.py index d44b3038d3..f0dbeffbbc 100644 --- a/qiskit_experiments/library/characterization/qubit_spectroscopy.py +++ b/qiskit_experiments/library/characterization/qubit_spectroscopy.py @@ -97,6 +97,7 @@ def __init__( frequencies: Iterable[float], unit: str = "Hz", absolute: bool = True, + backend: Optional[Backend] = None, ): """ A spectroscopy experiment run by setting the frequency of the qubit drive. @@ -114,12 +115,13 @@ def __init__( 'MHz', 'GHz'. Internally, all frequencies will be converted to 'Hz'. absolute: Boolean to specify if the frequencies are absolute or relative to the qubit frequency in the backend. + backend: Optional, the backend to run the experiment on. Raises: QiskitError: if there are less than three frequency shifts or if the unit is not known. """ - super().__init__([qubit]) + super().__init__([qubit], backend=backend) if len(frequencies) < 3: raise QiskitError("Spectroscopy requires at least three frequencies.") @@ -166,15 +168,12 @@ def _template_circuit(self, freq_param) -> QuantumCircuit: return circuit - def circuits(self, backend: Optional[Backend] = None): + def circuits(self): """Create the circuit for the spectroscopy experiment. The circuits are based on a GaussianSquare pulse and a frequency_shift instruction encapsulated in a gate. - Args: - backend: A backend object. - Returns: circuits: The circuits that will run the spectroscopy experiment. @@ -182,24 +181,34 @@ def circuits(self, backend: Optional[Backend] = None): QiskitError: - If absolute frequencies are used but no backend is given. - If the backend configuration does not define dt. + AttributeError: If backend to run on does not contain 'dt' configuration. """ - if backend is None and self._absolute: + if self.backend is None and self._absolute: raise QiskitError("Cannot run spectroscopy absolute to qubit without a backend.") # Create a template circuit - sched, freq_param = self._spec_gate_schedule(backend) + sched, freq_param = self._spec_gate_schedule(self.backend) circuit = self._template_circuit(freq_param) circuit.add_calibration("Spec", (self.physical_qubits[0],), sched, params=[freq_param]) + # Get dt + try: + dt_factor = getattr(self.backend.configuration(), "dt") + except AttributeError as no_dt: + raise AttributeError("dt parameter is missing in backend configuration") from no_dt + + # Get center frequency from backend + if self._absolute: + center_freq = self.backend.defaults().qubit_freq_est[self.physical_qubits[0]] + else: + center_freq = None + # Create the circuits to run circs = [] for freq in self._frequencies: - freq_shift = freq if self._absolute: - center_freq = backend.defaults().qubit_freq_est[self.physical_qubits[0]] freq_shift -= center_freq - freq_shift = np.round(freq_shift, decimals=3) assigned_circ = circuit.assign_parameters({freq_param: freq_shift}, inplace=False) @@ -213,13 +222,9 @@ def circuits(self, backend: Optional[Backend] = None): "sigma": self.experiment_options.sigma, "width": self.experiment_options.width, "schedule": str(sched), + "dt": dt_factor, } - try: - assigned_circ.metadata["dt"] = getattr(backend.configuration(), "dt") - except AttributeError as no_dt: - raise QiskitError("Dt parameter is missing in backend configuration") from no_dt - circs.append(assigned_circ) return circs diff --git a/qiskit_experiments/library/characterization/t1.py b/qiskit_experiments/library/characterization/t1.py index d03b9761c1..55361a9e4c 100644 --- a/qiskit_experiments/library/characterization/t1.py +++ b/qiskit_experiments/library/characterization/t1.py @@ -16,8 +16,8 @@ from typing import List, Optional, Union import numpy as np -from qiskit.providers import Backend from qiskit.circuit import QuantumCircuit +from qiskit.providers.backend import Backend from qiskit_experiments.framework import BaseExperiment, Options from qiskit_experiments.library.characterization.t1_analysis import T1Analysis @@ -67,6 +67,7 @@ def __init__( qubit: int, delays: Union[List[float], np.array], unit: Optional[str] = "s", + backend: Optional[Backend] = None, ): """ Initialize the T1 experiment class @@ -76,6 +77,7 @@ def __init__( delays: delay times of the experiments unit: Optional, unit of the delay times. Supported units: 's', 'ms', 'us', 'ns', 'ps', 'dt'. + backend: Optional, the backend to run the experiment on. Raises: ValueError: if the number of delays is smaller than 3 @@ -84,18 +86,15 @@ def __init__( raise ValueError("T1 experiment: number of delays must be at least 3") # Initialize base experiment - super().__init__([qubit]) + super().__init__([qubit], backend=backend) # Set experiment options self.set_experiment_options(delays=delays, unit=unit) - def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: + def circuits(self) -> List[QuantumCircuit]: """ Return a list of experiment circuits - Args: - backend: Optional, a backend object - Returns: The experiment circuits @@ -104,7 +103,7 @@ def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: """ if self.experiment_options.unit == "dt": try: - dt_factor = getattr(backend.configuration(), "dt") + dt_factor = getattr(self.backend.configuration(), "dt") except AttributeError as no_dt: raise AttributeError("Dt parameter is missing in backend configuration") from no_dt diff --git a/qiskit_experiments/library/characterization/t2ramsey.py b/qiskit_experiments/library/characterization/t2ramsey.py index abb93d486f..8dc2a3c31c 100644 --- a/qiskit_experiments/library/characterization/t2ramsey.py +++ b/qiskit_experiments/library/characterization/t2ramsey.py @@ -14,13 +14,14 @@ """ -from typing import List, Optional, Union +from typing import List, Union, Optional import numpy as np import qiskit from qiskit.utils import apply_prefix -from qiskit.providers import Backend from qiskit.circuit import QuantumCircuit +from qiskit.providers.backend import Backend + from qiskit_experiments.framework import BaseExperiment, Options from .t2ramsey_analysis import T2RamseyAnalysis @@ -82,6 +83,7 @@ def __init__( delays: Union[List[float], np.array], unit: str = "s", osc_freq: float = 0.0, + backend: Optional[Backend] = None, ): """ **T2Ramsey class** @@ -96,31 +98,30 @@ def __init__( used for both T2Ramsey and for the frequency. osc_freq: the oscillation frequency induced by the user. \ The frequency is given in Hz. + backend: Optional, the backend to run the experiment on. """ - super().__init__([qubit]) + super().__init__([qubit], backend=backend) self.set_experiment_options(delays=delays, unit=unit, osc_freq=osc_freq) - def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: + def circuits(self) -> List[QuantumCircuit]: """Return a list of experiment circuits. Each circuit consists of a Hadamard gate, followed by a fixed delay, a phase gate (with a linear phase), and an additional Hadamard gate. - Args: - backend: Optional, a backend object - Returns: The experiment circuits Raises: - AttributeError: if unit is 'dt', but 'dt' parameter is missing in the backend configuration. + AttributeError: if unit is 'dt', but 'dt' parameter is missing in + the backend configuration. """ conversion_factor = 1 if self.experiment_options.unit == "dt": try: - dt_factor = getattr(backend._configuration, "dt") + dt_factor = getattr(self.backend._configuration, "dt") conversion_factor = dt_factor except AttributeError as no_dt: raise AttributeError("Dt parameter is missing in backend configuration") from no_dt diff --git a/qiskit_experiments/library/quantum_volume/qv_experiment.py b/qiskit_experiments/library/quantum_volume/qv_experiment.py index acfea48c74..f8920afc8c 100644 --- a/qiskit_experiments/library/quantum_volume/qv_experiment.py +++ b/qiskit_experiments/library/quantum_volume/qv_experiment.py @@ -15,7 +15,6 @@ from typing import Union, Iterable, Optional, List from numpy.random import Generator, default_rng -from qiskit.providers.backend import Backend try: from qiskit import Aer @@ -27,6 +26,7 @@ from qiskit import QuantumCircuit from qiskit.circuit.library import QuantumVolume as QuantumVolumeCircuit from qiskit import transpile +from qiskit.providers.backend import Backend from qiskit_experiments.framework import BaseExperiment, Options from .qv_analysis import QuantumVolumeAnalysis @@ -75,6 +75,7 @@ def __init__( qubits: Union[int, Iterable[int]], trials: Optional[int] = 100, seed: Optional[Union[int, Generator]] = None, + backend: Optional[Backend] = None, simulation_backend: Optional[Backend] = None, ): """Initialize a quantum volume experiment. @@ -85,13 +86,14 @@ def __init__( trials: The number of trials to run the quantum volume circuit. seed: Seed or generator object for random number generation. If None default_rng will be used. + backend: Optional, the backend to run the experiment on. simulation_backend: The simulator backend to use to generate the expected results. the simulator must have a 'save_probabilities' method. If None :class:`AerSimulator` simulator will be used (in case :class:`AerSimulator` is not installed :class:`qiskit.quantum_info.Statevector` will be used). """ - super().__init__(qubits) + super().__init__(qubits, backend=backend) # Set configurable options self.set_experiment_options(trials=trials) @@ -150,12 +152,9 @@ def _get_ideal_data(self, circuit: QuantumCircuit, **run_options) -> List[float] probabilities = state_vector.probabilities() return probabilities - def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: + def circuits(self) -> List[QuantumCircuit]: """Return a list of Quantum Volume circuits. - Args: - backend (Backend): Optional, a backend object. - Returns: A list of :class:`QuantumCircuit`. """ diff --git a/qiskit_experiments/library/randomized_benchmarking/interleaved_rb_experiment.py b/qiskit_experiments/library/randomized_benchmarking/interleaved_rb_experiment.py index 5b593254da..e24cca0fd7 100644 --- a/qiskit_experiments/library/randomized_benchmarking/interleaved_rb_experiment.py +++ b/qiskit_experiments/library/randomized_benchmarking/interleaved_rb_experiment.py @@ -20,6 +20,7 @@ from qiskit.circuit import Instruction from qiskit.quantum_info import Clifford from qiskit.exceptions import QiskitError +from qiskit.providers.backend import Backend from .rb_experiment import StandardRB from .interleaved_rb_analysis import InterleavedRBAnalysis @@ -54,6 +55,7 @@ def __init__( num_samples: int = 3, seed: Optional[Union[int, Generator]] = None, full_sampling: bool = False, + backend: Optional[Backend] = None, ): """Initialize an interleaved randomized benchmarking experiment. @@ -71,9 +73,10 @@ def __init__( all lengths. If False for sample of lengths longer sequences are constructed by appending additional Clifford samples to shorter sequences. + backend: The backend to run the experiment on. """ self._set_interleaved_element(interleaved_element) - super().__init__(qubits, lengths, num_samples, seed, full_sampling) + super().__init__(qubits, lengths, num_samples, seed, full_sampling, backend=backend) def _sample_circuits(self, lengths, seed=None): circuits = [] diff --git a/qiskit_experiments/library/randomized_benchmarking/rb_experiment.py b/qiskit_experiments/library/randomized_benchmarking/rb_experiment.py index f0c33c2db8..ad004a09b2 100644 --- a/qiskit_experiments/library/randomized_benchmarking/rb_experiment.py +++ b/qiskit_experiments/library/randomized_benchmarking/rb_experiment.py @@ -18,9 +18,9 @@ from numpy.random import Generator, default_rng from qiskit import QuantumCircuit, QiskitError -from qiskit.providers import Backend from qiskit.quantum_info import Clifford from qiskit.circuit import Gate +from qiskit.providers.backend import Backend import qiskit_experiments.data_processing as dp from qiskit_experiments.framework import BaseExperiment, ParallelExperiment, Options @@ -66,6 +66,7 @@ def __init__( num_samples: int = 3, seed: Optional[Union[int, Generator]] = None, full_sampling: Optional[bool] = False, + backend: Optional[Backend] = None, ): """Initialize a standard randomized benchmarking experiment. @@ -81,9 +82,10 @@ def __init__( sequences are constructed by appending additional Clifford samples to shorter sequences. The default is False. + backend: The backend to run the experiment on. """ # Initialize base experiment - super().__init__(qubits) + super().__init__(qubits, backend=backend) self._verify_parameters(lengths, num_samples) # Set configurable options @@ -133,13 +135,9 @@ def _default_experiment_options(cls) -> Options: return options - # pylint: disable = arguments-differ - def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: + def circuits(self) -> List[QuantumCircuit]: """Return a list of RB circuits. - Args: - backend (Backend): Optional, a backend object. - Returns: A list of :class:`QuantumCircuit`. """ @@ -231,7 +229,7 @@ def _get_circuit_metadata(self, circuit): return meta return None - def _postprocess_transpiled_circuits(self, circuits, backend, **run_options): + def _postprocess_transpiled_circuits(self, circuits, **run_options): """Additional post-processing of transpiled circuits before running on backend""" for c in circuits: meta = self._get_circuit_metadata(c) diff --git a/qiskit_experiments/library/tomography/tomography_experiment.py b/qiskit_experiments/library/tomography/tomography_experiment.py index ee79475f85..68e6f15afd 100644 --- a/qiskit_experiments/library/tomography/tomography_experiment.py +++ b/qiskit_experiments/library/tomography/tomography_experiment.py @@ -19,6 +19,7 @@ import numpy as np from qiskit.circuit import QuantumCircuit, Instruction from qiskit.circuit.library import Permutation +from qiskit.providers.backend import Backend from qiskit.quantum_info.operators.base_operator import BaseOperator import qiskit.quantum_info as qi from qiskit_experiments.exceptions import QiskitError @@ -59,6 +60,7 @@ def __init__( preparation_qubits: Optional[Iterable[int]] = None, basis_indices: Optional[Iterable[Tuple[List[int], List[int]]]] = None, qubits: Optional[Iterable[int]] = None, + backend: Optional[Backend] = None, ): """Initialize a tomography experiment. @@ -74,6 +76,7 @@ def __init__( basis_indices: Optional, the basis elements to be measured. If None All basis elements will be measured. qubits: Optional, the physical qubits for the initial state circuit. + backend: The backend to run the experiment on. Raises: QiskitError: if input params are invalid. @@ -81,7 +84,7 @@ def __init__( # Initialize BaseExperiment if qubits is None: qubits = circuit.num_qubits - super().__init__(qubits) + super().__init__(qubits, backend=backend) # Get the target tomography circuit if isinstance(circuit, QuantumCircuit): @@ -150,7 +153,7 @@ def _metadata(self): metadata["target"] = copy.copy(self._target) return metadata - def circuits(self, backend=None): + def circuits(self): # Get qubits and clbits meas_qubits = self._meas_qubits or range(self.num_qubits) diff --git a/test/fake_experiment.py b/test/fake_experiment.py index be11cbb244..f5abddba5a 100644 --- a/test/fake_experiment.py +++ b/test/fake_experiment.py @@ -37,6 +37,6 @@ def __init__(self, qubits=1): """Initialise the fake experiment.""" super().__init__(qubits) - def circuits(self, backend=None): + def circuits(self): """Fake circuits.""" return [] diff --git a/test/test_framework.py b/test/test_framework.py index 99c220b7f1..389b3a4eb6 100644 --- a/test/test_framework.py +++ b/test/test_framework.py @@ -35,7 +35,7 @@ def test_job_splitting(self, max_experiments): class Experiment(FakeExperiment): """Fake Experiment to test job splitting""" - def circuits(self, backend=None): + def circuits(self): """Generate fake circuits""" qc = QuantumCircuit(1) qc.measure_all() From 046117e2a033fe4cdbaa7cd0b0a88c29c0d5f8fc Mon Sep 17 00:00:00 2001 From: Christopher Wood Date: Mon, 25 Oct 2021 17:35:11 -0400 Subject: [PATCH 3/7] Update tests --- test/calibration/experiments/test_drag.py | 3 ++- test/calibration/experiments/test_fine_drag.py | 4 ++-- test/calibration/experiments/test_rabi.py | 9 ++++++--- test/test_cross_resonance_hamiltonian.py | 10 ++++------ test/test_qubit_spectroscopy.py | 3 ++- 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/test/calibration/experiments/test_drag.py b/test/calibration/experiments/test_drag.py index 4e182f9f8a..48d79d9126 100644 --- a/test/calibration/experiments/test_drag.py +++ b/test/calibration/experiments/test_drag.py @@ -109,7 +109,8 @@ def test_default_circuits(self): drag = DragCal(0) drag.set_experiment_options(reps=[2, 4, 8], schedule=self.x_plus) - circuits = drag.circuits(DragBackend(gate_name="xp")) + drag.backend = DragBackend(gate_name="xp") + circuits = drag.circuits() for idx, expected in enumerate([4, 8, 16]): ops = transpile(circuits[idx * 51], backend).count_ops() diff --git a/test/calibration/experiments/test_fine_drag.py b/test/calibration/experiments/test_fine_drag.py index 5eb7aa0ae6..f53f5f7f59 100644 --- a/test/calibration/experiments/test_fine_drag.py +++ b/test/calibration/experiments/test_fine_drag.py @@ -50,8 +50,8 @@ def test_circuits(self): drag = FineDrag(0) drag.set_experiment_options(schedule=self.schedule) - - for circuit in drag.circuits(FakeArmonk())[1:]: + drag.backend = FakeArmonk() + for circuit in drag.circuits()[1:]: for idx, name in enumerate(["Drag", "rz", "Drag", "rz"]): self.assertEqual(circuit.data[idx][0].name, name) diff --git a/test/calibration/experiments/test_rabi.py b/test/calibration/experiments/test_rabi.py index 0e1d6ad51e..da7bca0be0 100644 --- a/test/calibration/experiments/test_rabi.py +++ b/test/calibration/experiments/test_rabi.py @@ -141,7 +141,8 @@ def test_ef_rabi_circuit(self): anharm = -330e6 rabi12 = EFRabi(2) rabi12.set_experiment_options(amplitudes=[0.5], frequency_shift=anharm) - circ = rabi12.circuits(RabiBackend())[0] + rabi12.backend = RabiBackend() + circ = rabi12.circuits()[0] with pulse.build() as expected: pulse.shift_frequency(anharm, pulse.DriveChannel(2)) @@ -161,7 +162,8 @@ def test_default_schedule(self): rabi = Rabi(2) rabi.set_experiment_options(amplitudes=[0.5]) - circs = rabi.circuits(RabiBackend()) + rabi.backend = RabiBackend() + circs = rabi.circuits() with pulse.build() as expected: pulse.play(pulse.Gaussian(160, 0.5, 40), pulse.DriveChannel(2)) @@ -179,7 +181,8 @@ def test_user_schedule(self): rabi = Rabi(2) rabi.set_experiment_options(schedule=my_schedule, amplitudes=[0.5]) - circs = rabi.circuits(RabiBackend()) + rabi.backend = RabiBackend() + circs = rabi.circuits() assigned_sched = my_schedule.assign_parameters({amp: 0.5}, inplace=False) self.assertEqual(circs[0].calibrations["Rabi"][((2,), (0.5,))], assigned_sched) diff --git a/test/test_cross_resonance_hamiltonian.py b/test/test_cross_resonance_hamiltonian.py index a384d06543..c93d52bf25 100644 --- a/test/test_cross_resonance_hamiltonian.py +++ b/test/test_cross_resonance_hamiltonian.py @@ -160,8 +160,6 @@ class TestCrossResonanceHamiltonian(QiskitTestCase): def test_circuit_generation(self): """Test generated circuits.""" - backend = CrossResonanceHamiltonianBackend() - expr = cr_hamiltonian.CrossResonanceHamiltonian( qubits=(0, 1), flat_top_widths=[1000], @@ -170,6 +168,7 @@ def test_circuit_generation(self): sigma=64, risefall=2, ) + expr.backend = CrossResonanceHamiltonianBackend() nearlest_16 = 1248 @@ -187,7 +186,7 @@ def test_circuit_generation(self): pulse.delay(nearlest_16, pulse.DriveChannel(1)) cr_gate = circuit.Gate("cr_gate", num_qubits=2, params=[1000]) - expr_circs = expr.circuits(backend) + expr_circs = expr.circuits() x0_circ = QuantumCircuit(2, 1) x0_circ.append(cr_gate, [0, 1]) @@ -231,8 +230,6 @@ def test_circuit_generation(self): def test_circuit_generation_from_sec(self): """Test generated circuits when time unit is sec.""" - backend = CrossResonanceHamiltonianBackend() - expr = cr_hamiltonian.CrossResonanceHamiltonian( qubits=(0, 1), flat_top_widths=[500], @@ -241,6 +238,7 @@ def test_circuit_generation_from_sec(self): sigma=20, risefall=2, ) + expr.backend = CrossResonanceHamiltonianBackend() nearlest_16 = 576 @@ -258,7 +256,7 @@ def test_circuit_generation_from_sec(self): pulse.delay(nearlest_16, pulse.DriveChannel(1)) cr_gate = circuit.Gate("cr_gate", num_qubits=2, params=[500]) - expr_circs = expr.circuits(backend) + expr_circs = expr.circuits() x0_circ = QuantumCircuit(2, 1) x0_circ.append(cr_gate, [0, 1]) diff --git a/test/test_qubit_spectroscopy.py b/test/test_qubit_spectroscopy.py index 81ef75cb8b..ecf63b015c 100644 --- a/test/test_qubit_spectroscopy.py +++ b/test/test_qubit_spectroscopy.py @@ -134,6 +134,7 @@ def test_spectroscopy12_end2end_classified(self): # Note that the backend is not sophisticated enough to simulate an e-f # transition so we run the test with g-e. spec = EFSpectroscopy(qubit, frequencies, unit="Hz") + spec.backend = backend spec.set_run_options(meas_level=MeasLevel.CLASSIFIED) expdata = spec.run(backend) expdata.block_for_results() @@ -144,6 +145,6 @@ def test_spectroscopy12_end2end_classified(self): self.assertEqual(result.quality, "good") # Test the circuits - circ = spec.circuits(backend)[0] + circ = spec.circuits()[0] self.assertEqual(circ.data[0][0].name, "x") self.assertEqual(circ.data[1][0].name, "Spec") From 3e92a424784caff2ef9b3d5e45b0f204d27eb17f Mon Sep 17 00:00:00 2001 From: Christopher Wood Date: Tue, 26 Oct 2021 12:47:19 -0400 Subject: [PATCH 4/7] Add release note --- .../notes/exp-backend-691201fd10046566.yaml | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 releasenotes/notes/exp-backend-691201fd10046566.yaml diff --git a/releasenotes/notes/exp-backend-691201fd10046566.yaml b/releasenotes/notes/exp-backend-691201fd10046566.yaml new file mode 100644 index 0000000000..f941d03737 --- /dev/null +++ b/releasenotes/notes/exp-backend-691201fd10046566.yaml @@ -0,0 +1,22 @@ +--- +features: + - | + Add ``backend`` as an optional ``__init__`` kwarg for all experiments to + alow setting the backend at initialization. The backand can also be set + and retrieved from the experiment object after construction using the + :meth:`~qiskit_experiments.framework.BaseExperiment.backend` + property and setter. + + When using the ``backend`` kwarg of + :meth:`~qiskit_experiments.framework.BaseExperiment.run` to specify + a backend this will temporarily override any currently set backends + for that single execution. +developer: + - | + Added a ``_set_backend`` method to + :class:`~qiskit_experiments.framework.BaseExperiment` that is called + when a backend is set via initalization or the ``backend`` setter. This + can be overridden in experiment subclasses if required. For example this + could be used to extract any needed configuration or properties from the + specified backend, or to update experiment options of configuration based + on the backend. From 2c43f77d7a82bd476704feb83927d120a498cc8a Mon Sep 17 00:00:00 2001 From: Christopher Wood Date: Tue, 26 Oct 2021 14:11:31 -0400 Subject: [PATCH 5/7] Address review comments --- .../framework/base_experiment.py | 23 ++++++++----------- .../framework/experiment_data.py | 2 +- .../library/characterization/t1.py | 14 +++++++++++ .../library/characterization/t2ramsey.py | 14 +++++++++++ 4 files changed, 39 insertions(+), 14 deletions(-) diff --git a/qiskit_experiments/framework/base_experiment.py b/qiskit_experiments/framework/base_experiment.py index bbf5ec07ab..f60a170081 100644 --- a/qiskit_experiments/framework/base_experiment.py +++ b/qiskit_experiments/framework/base_experiment.py @@ -22,7 +22,6 @@ from qiskit.providers import BaseJob from qiskit.providers.backend import Backend from qiskit.providers.basebackend import BaseBackend as LegacyBackend -from qiskit.test.mock import FakeBackend from qiskit.exceptions import QiskitError from qiskit.qobj.utils import MeasLevel from qiskit_experiments.framework import Options @@ -94,7 +93,7 @@ def experiment_type(self) -> str: return self._type @property - def physical_qubits(self) -> Tuple[int]: + def physical_qubits(self) -> Tuple[int, ...]: """Return the device qubits for the experiment.""" return self._physical_qubits @@ -121,19 +120,17 @@ def _set_backend(self, backend: Backend): """ self._backend = backend - # Scheduling parameters - if not self._backend.configuration().simulator and not isinstance(backend, FakeBackend): - timing_constraints = getattr(self.transpile_options, "timing_constraints", {}) - if "acquire_alignment" not in timing_constraints: - timing_constraints["aquire_aligment"] = 16 - scheduling_method = getattr(self.transpile_options, "scheduling_method", "alap") - self.set_transpile_options( - timing_constraints=timing_constraints, scheduling_method=scheduling_method - ) - def copy(self) -> "BaseExperiment": """Return a copy of the experiment""" - return copy.copy(self) + # We want to avoid a deep copy be default for performance so we + # need to also copy the Options structures so that if they are + # updated on the copy they don't effect the original. + ret = copy.copy(self) + ret._experiment_options = copy.copy(self._experiment_options) + ret._run_options = copy.copy(self._run_options) + ret._transpile_options = copy.copy(self._transpile_options) + ret._analysis_options = copy.copy(self._analysis_options) + return ret def run( self, diff --git a/qiskit_experiments/framework/experiment_data.py b/qiskit_experiments/framework/experiment_data.py index f5a12769fb..225a932644 100644 --- a/qiskit_experiments/framework/experiment_data.py +++ b/qiskit_experiments/framework/experiment_data.py @@ -53,7 +53,7 @@ def __init__(self, experiment=None, backend=None, parent_id=None, job_ids=None): @property def experiment(self): """Return the experiment for this data. - + Returns: BaseExperiment: the experiment object. """ diff --git a/qiskit_experiments/library/characterization/t1.py b/qiskit_experiments/library/characterization/t1.py index 55361a9e4c..a0546acda9 100644 --- a/qiskit_experiments/library/characterization/t1.py +++ b/qiskit_experiments/library/characterization/t1.py @@ -18,6 +18,7 @@ from qiskit.circuit import QuantumCircuit from qiskit.providers.backend import Backend +from qiskit.test.mock import FakeBackend from qiskit_experiments.framework import BaseExperiment, Options from qiskit_experiments.library.characterization.t1_analysis import T1Analysis @@ -91,6 +92,19 @@ def __init__( # Set experiment options self.set_experiment_options(delays=delays, unit=unit) + def _set_backend(self, backend: Backend): + super()._set_backend(backend) + + # Scheduling parameters + if not self._backend.configuration().simulator and not isinstance(backend, FakeBackend): + timing_constraints = getattr(self.transpile_options, "timing_constraints", {}) + if "acquire_alignment" not in timing_constraints: + timing_constraints["aquire_aligment"] = 16 + scheduling_method = getattr(self.transpile_options, "scheduling_method", "alap") + self.set_transpile_options( + timing_constraints=timing_constraints, scheduling_method=scheduling_method + ) + def circuits(self) -> List[QuantumCircuit]: """ Return a list of experiment circuits diff --git a/qiskit_experiments/library/characterization/t2ramsey.py b/qiskit_experiments/library/characterization/t2ramsey.py index 8dc2a3c31c..b46d61a931 100644 --- a/qiskit_experiments/library/characterization/t2ramsey.py +++ b/qiskit_experiments/library/characterization/t2ramsey.py @@ -21,6 +21,7 @@ from qiskit.utils import apply_prefix from qiskit.circuit import QuantumCircuit from qiskit.providers.backend import Backend +from qiskit.test.mock import FakeBackend from qiskit_experiments.framework import BaseExperiment, Options from .t2ramsey_analysis import T2RamseyAnalysis @@ -105,6 +106,19 @@ def __init__( super().__init__([qubit], backend=backend) self.set_experiment_options(delays=delays, unit=unit, osc_freq=osc_freq) + def _set_backend(self, backend: Backend): + super()._set_backend(backend) + + # Scheduling parameters + if not self._backend.configuration().simulator and not isinstance(backend, FakeBackend): + timing_constraints = getattr(self.transpile_options, "timing_constraints", {}) + if "acquire_alignment" not in timing_constraints: + timing_constraints["aquire_aligment"] = 16 + scheduling_method = getattr(self.transpile_options, "scheduling_method", "alap") + self.set_transpile_options( + timing_constraints=timing_constraints, scheduling_method=scheduling_method + ) + def circuits(self) -> List[QuantumCircuit]: """Return a list of experiment circuits. From e65c4ef099f19d48e02c72cc85f718855ea74cdc Mon Sep 17 00:00:00 2001 From: Christopher Wood Date: Tue, 26 Oct 2021 17:20:40 -0400 Subject: [PATCH 6/7] Make backend first kwarg --- .../framework/composite/batch_experiment.py | 12 ++++++----- .../composite/composite_experiment.py | 20 +++++++++++++------ .../composite/parallel_experiment.py | 12 ++++++----- .../library/calibration/fine_amplitude.py | 8 ++++---- .../library/calibration/ramsey_xy.py | 4 ++-- .../library/calibration/rough_frequency.py | 10 +++++----- .../characterization/cr_hamiltonian.py | 4 ++-- .../characterization/qubit_spectroscopy.py | 4 ++-- .../library/characterization/t1.py | 4 ++-- .../library/characterization/t2ramsey.py | 11 ++++------ .../library/quantum_volume/qv_experiment.py | 4 ++-- .../interleaved_rb_experiment.py | 13 +++++++++--- .../randomized_benchmarking/rb_experiment.py | 4 ++-- .../tomography/tomography_experiment.py | 4 ++-- .../experiments/test_rough_frequency.py | 4 +++- 15 files changed, 68 insertions(+), 50 deletions(-) diff --git a/qiskit_experiments/framework/composite/batch_experiment.py b/qiskit_experiments/framework/composite/batch_experiment.py index 43ab84d0d7..67ee527a09 100644 --- a/qiskit_experiments/framework/composite/batch_experiment.py +++ b/qiskit_experiments/framework/composite/batch_experiment.py @@ -13,21 +13,23 @@ Batch Experiment class. """ +from typing import List, Optional from collections import OrderedDict from qiskit import QuantumCircuit - -from .composite_experiment import CompositeExperiment +from qiskit.providers.backend import Backend +from .composite_experiment import CompositeExperiment, BaseExperiment class BatchExperiment(CompositeExperiment): """Batch experiment class""" - def __init__(self, experiments): + def __init__(self, experiments: List[BaseExperiment], backend: Optional[Backend] = None): """Initialize a batch experiment. Args: - experiments (List[BaseExperiment]): a list of experiments. + experiments: a list of experiments. + backend: Optional, the backend to run the experiment on. """ # Generate qubit map @@ -39,7 +41,7 @@ def __init__(self, experiments): self._qubit_map[physical_qubit] = logical_qubit logical_qubit += 1 qubits = tuple(self._qubit_map.keys()) - super().__init__(experiments, qubits) + super().__init__(experiments, qubits, backend=backend) def circuits(self): diff --git a/qiskit_experiments/framework/composite/composite_experiment.py b/qiskit_experiments/framework/composite/composite_experiment.py index 983ee9ec45..484286aade 100644 --- a/qiskit_experiments/framework/composite/composite_experiment.py +++ b/qiskit_experiments/framework/composite/composite_experiment.py @@ -13,9 +13,11 @@ Composite Experiment abstract base class. """ +from typing import List, Sequence, Optional from abc import abstractmethod import warnings +from qiskit.providers.backend import Backend from qiskit_experiments.framework import BaseExperiment from .composite_experiment_data import CompositeExperimentData from .composite_analysis import CompositeAnalysis @@ -27,18 +29,24 @@ class CompositeExperiment(BaseExperiment): __analysis_class__ = CompositeAnalysis __experiment_data__ = CompositeExperimentData - def __init__(self, experiments, qubits, experiment_type=None): + def __init__( + self, + experiments: List[BaseExperiment], + qubits: Sequence[int], + backend: Optional[Backend] = None, + experiment_type: Optional[str] = None, + ): """Initialize the composite experiment object. Args: - experiments (List[BaseExperiment]): a list of experiment objects. - qubits (int or Iterable[int]): the number of qubits or list of - physical qubits for the experiment. - experiment_type (str): Optional, composite experiment subclass name. + experiments: a list of experiment objects. + qubits: the number of qubits or list of physical qubits for the experiment. + backend: Optional, the backend to run the experiment on. + experiment_type: Optional, composite experiment subclass name. """ self._experiments = experiments self._num_experiments = len(experiments) - super().__init__(qubits, experiment_type=experiment_type) + super().__init__(qubits, backend=backend, experiment_type=experiment_type) @abstractmethod def circuits(self): diff --git a/qiskit_experiments/framework/composite/parallel_experiment.py b/qiskit_experiments/framework/composite/parallel_experiment.py index b22a1e5382..4643b3f0e6 100644 --- a/qiskit_experiments/framework/composite/parallel_experiment.py +++ b/qiskit_experiments/framework/composite/parallel_experiment.py @@ -12,25 +12,27 @@ """ Parallel Experiment class. """ +from typing import List, Optional from qiskit import QuantumCircuit, ClassicalRegister - -from .composite_experiment import CompositeExperiment +from qiskit.providers.backend import Backend +from .composite_experiment import CompositeExperiment, BaseExperiment class ParallelExperiment(CompositeExperiment): """Parallel Experiment class""" - def __init__(self, experiments): + def __init__(self, experiments: List[BaseExperiment], backend: Optional[Backend] = None): """Initialize the analysis object. Args: - experiments (List[BaseExperiment]): a list of experiments. + experiments: a list of experiments. + backend: Optional, the backend to run the experiment on. """ qubits = [] for exp in experiments: qubits += exp.physical_qubits - super().__init__(experiments, qubits) + super().__init__(experiments, qubits, backend=backend) def circuits(self): diff --git a/qiskit_experiments/library/calibration/fine_amplitude.py b/qiskit_experiments/library/calibration/fine_amplitude.py index d2f641a0ec..63538736df 100644 --- a/qiskit_experiments/library/calibration/fine_amplitude.py +++ b/qiskit_experiments/library/calibration/fine_amplitude.py @@ -42,9 +42,9 @@ def __init__( qubit: int, calibrations: BackendCalibrations, schedule_name: str, + backend: Optional[Backend] = None, cal_parameter_name: Optional[str] = "amp", auto_update: bool = True, - backend: Optional[Backend] = None, ): """see class :class:`FineAmplitude` for details. @@ -52,20 +52,20 @@ def __init__( qubit: The qubit for which to run the fine amplitude calibration. calibrations: The calibrations instance with the schedules. schedule_name: The name of the schedule to calibrate. + backend: Optional, the backend to run the experiment on. cal_parameter_name: The name of the parameter in the schedule to update. auto_update: Whether or not to automatically update the calibrations. By default this variable is set to True. - backend: Optional, the backend to run the experiment on. - + on. """ super().__init__( calibrations, qubit, Gate(name=schedule_name, num_qubits=1, params=[]), schedule_name=schedule_name, + backend=backend, cal_parameter_name=cal_parameter_name, auto_update=auto_update, - backend=backend, ) self.transpile_options.inst_map = calibrations.default_inst_map diff --git a/qiskit_experiments/library/calibration/ramsey_xy.py b/qiskit_experiments/library/calibration/ramsey_xy.py index 7f9edb3376..3411a0f5a8 100644 --- a/qiskit_experiments/library/calibration/ramsey_xy.py +++ b/qiskit_experiments/library/calibration/ramsey_xy.py @@ -106,20 +106,20 @@ def _default_experiment_options(cls): def __init__( self, qubit: int, + backend: Optional[Backend] = None, delays: Optional[List] = None, unit: str = "s", osc_freq: float = 2e6, - backend: Optional[Backend] = None, ): """Create new experiment. Args: qubit: The qubit on which to run the Ramsey XY experiment. + backend: Optional, the backend to run the experiment on. delays: The delays to scan. unit: The unit of the delays. osc_freq: the oscillation frequency induced by the user through a virtual Rz rotation. This quantity is given in Hz. - backend: Optional, the backend to run the experiment on. """ super().__init__([qubit], backend=backend) diff --git a/qiskit_experiments/library/calibration/rough_frequency.py b/qiskit_experiments/library/calibration/rough_frequency.py index 4cb720ac10..bc5dc1a77c 100644 --- a/qiskit_experiments/library/calibration/rough_frequency.py +++ b/qiskit_experiments/library/calibration/rough_frequency.py @@ -32,10 +32,10 @@ def __init__( qubit: int, calibrations: BackendCalibrations, frequencies: Iterable[float], + backend: Optional[Backend] = None, unit: str = "Hz", auto_update: bool = True, absolute: bool = True, - backend: Optional[Backend] = None, ): """See :class:`QubitSpectroscopy` for detailed documentation. @@ -44,13 +44,13 @@ def __init__( calibrations: If calibrations is given then running the experiment may update the values of the frequencies stored in calibrations. frequencies: The frequencies to scan in the experiment. + backend: Optional, the backend to run the experiment on. unit: The unit in which the user specifies the frequencies. Can be one of 'Hz', 'kHz', 'MHz', 'GHz'. Internally, all frequencies will be converted to 'Hz'. auto_update: If set to True, which is the default, then the experiment will automatically update the frequency in the calibrations. absolute: Boolean to specify if the frequencies are absolute or relative to the qubit frequency in the backend. - backend: Optional, the backend to run the experiment on. Raises: QiskitError: if there are less than three frequency shifts or if the unit is not known. @@ -60,11 +60,11 @@ def __init__( calibrations, qubit, frequencies, - unit, - absolute, + backend=backend, + unit=unit, + absolute=absolute, updater=Frequency, auto_update=auto_update, - backend=backend, ) diff --git a/qiskit_experiments/library/characterization/cr_hamiltonian.py b/qiskit_experiments/library/characterization/cr_hamiltonian.py index ad62bb1e45..ccb640e67d 100644 --- a/qiskit_experiments/library/characterization/cr_hamiltonian.py +++ b/qiskit_experiments/library/characterization/cr_hamiltonian.py @@ -130,8 +130,8 @@ def __init__( self, qubits: Tuple[int, int], flat_top_widths: Iterable[float], - unit: str = "dt", backend: Optional[Backend] = None, + unit: str = "dt", **kwargs, ): """Create a new experiment. @@ -142,8 +142,8 @@ def __init__( flat_top_widths: The total duration of the square part of cross resonance pulse(s) to scan. The total pulse duration including Gaussian rising and falling edges is implicitly computed with experiment parameters ``sigma`` and ``risefall``. - unit: The time unit of durations. backend: Optional, the backend to run the experiment on. + unit: The time unit of durations. kwargs: Pulse parameters. See :meth:`experiment_options` for details. Raises: diff --git a/qiskit_experiments/library/characterization/qubit_spectroscopy.py b/qiskit_experiments/library/characterization/qubit_spectroscopy.py index f0dbeffbbc..09314d0131 100644 --- a/qiskit_experiments/library/characterization/qubit_spectroscopy.py +++ b/qiskit_experiments/library/characterization/qubit_spectroscopy.py @@ -95,9 +95,9 @@ def __init__( self, qubit: int, frequencies: Iterable[float], + backend: Optional[Backend] = None, unit: str = "Hz", absolute: bool = True, - backend: Optional[Backend] = None, ): """ A spectroscopy experiment run by setting the frequency of the qubit drive. @@ -111,11 +111,11 @@ def __init__( Args: qubit: The qubit on which to run spectroscopy. frequencies: The frequencies to scan in the experiment. + backend: Optional, the backend to run the experiment on. unit: The unit in which the user specifies the frequencies. Can be one of 'Hz', 'kHz', 'MHz', 'GHz'. Internally, all frequencies will be converted to 'Hz'. absolute: Boolean to specify if the frequencies are absolute or relative to the qubit frequency in the backend. - backend: Optional, the backend to run the experiment on. Raises: QiskitError: if there are less than three frequency shifts or if the unit is not known. diff --git a/qiskit_experiments/library/characterization/t1.py b/qiskit_experiments/library/characterization/t1.py index a0546acda9..1a01cc722e 100644 --- a/qiskit_experiments/library/characterization/t1.py +++ b/qiskit_experiments/library/characterization/t1.py @@ -67,8 +67,8 @@ def __init__( self, qubit: int, delays: Union[List[float], np.array], - unit: Optional[str] = "s", backend: Optional[Backend] = None, + unit: Optional[str] = "s", ): """ Initialize the T1 experiment class @@ -76,9 +76,9 @@ def __init__( Args: qubit: the qubit whose T1 is to be estimated delays: delay times of the experiments + backend: Optional, the backend to run the experiment on. unit: Optional, unit of the delay times. Supported units: 's', 'ms', 'us', 'ns', 'ps', 'dt'. - backend: Optional, the backend to run the experiment on. Raises: ValueError: if the number of delays is smaller than 3 diff --git a/qiskit_experiments/library/characterization/t2ramsey.py b/qiskit_experiments/library/characterization/t2ramsey.py index b46d61a931..dbef1e8186 100644 --- a/qiskit_experiments/library/characterization/t2ramsey.py +++ b/qiskit_experiments/library/characterization/t2ramsey.py @@ -82,25 +82,22 @@ def __init__( self, qubit: int, delays: Union[List[float], np.array], + backend: Optional[Backend] = None, unit: str = "s", osc_freq: float = 0.0, - backend: Optional[Backend] = None, ): """ - **T2Ramsey class** - Initialize the T2Ramsey class. Args: qubit: the qubit under test. delays: delay times of the experiments. + backend: Optional, the backend to run the experiment on. unit: Optional, time unit of `delays`. Supported units: 's', 'ms', 'us', 'ns', 'ps', 'dt'. The unit is used for both T2Ramsey and for the frequency. - osc_freq: the oscillation frequency induced by the user. \ - The frequency is given in Hz. - backend: Optional, the backend to run the experiment on. - + osc_freq: the oscillation frequency induced by the user. + The frequency is given in Hz. """ super().__init__([qubit], backend=backend) diff --git a/qiskit_experiments/library/quantum_volume/qv_experiment.py b/qiskit_experiments/library/quantum_volume/qv_experiment.py index f8920afc8c..e2c0ce076e 100644 --- a/qiskit_experiments/library/quantum_volume/qv_experiment.py +++ b/qiskit_experiments/library/quantum_volume/qv_experiment.py @@ -73,9 +73,9 @@ class QuantumVolume(BaseExperiment): def __init__( self, qubits: Union[int, Iterable[int]], + backend: Optional[Backend] = None, trials: Optional[int] = 100, seed: Optional[Union[int, Generator]] = None, - backend: Optional[Backend] = None, simulation_backend: Optional[Backend] = None, ): """Initialize a quantum volume experiment. @@ -83,10 +83,10 @@ def __init__( Args: qubits: The number of qubits or list of physical qubits for the experiment. + backend: Optional, the backend to run the experiment on. trials: The number of trials to run the quantum volume circuit. seed: Seed or generator object for random number generation. If None default_rng will be used. - backend: Optional, the backend to run the experiment on. simulation_backend: The simulator backend to use to generate the expected results. the simulator must have a 'save_probabilities' method. If None :class:`AerSimulator` simulator will be used diff --git a/qiskit_experiments/library/randomized_benchmarking/interleaved_rb_experiment.py b/qiskit_experiments/library/randomized_benchmarking/interleaved_rb_experiment.py index e24cca0fd7..b6c9ce92a5 100644 --- a/qiskit_experiments/library/randomized_benchmarking/interleaved_rb_experiment.py +++ b/qiskit_experiments/library/randomized_benchmarking/interleaved_rb_experiment.py @@ -52,10 +52,10 @@ def __init__( interleaved_element: Union[QuantumCircuit, Instruction, Clifford], qubits: Union[int, Iterable[int]], lengths: Iterable[int], + backend: Optional[Backend] = None, num_samples: int = 3, seed: Optional[Union[int, Generator]] = None, full_sampling: bool = False, - backend: Optional[Backend] = None, ): """Initialize an interleaved randomized benchmarking experiment. @@ -65,6 +65,7 @@ def __init__( qubits: The number of qubits or list of physical qubits for the experiment. lengths: A list of RB sequences lengths. + backend: The backend to run the experiment on. num_samples: Number of samples to generate for each sequence length seed: Seed or generator object for random number @@ -73,10 +74,16 @@ def __init__( all lengths. If False for sample of lengths longer sequences are constructed by appending additional Clifford samples to shorter sequences. - backend: The backend to run the experiment on. """ self._set_interleaved_element(interleaved_element) - super().__init__(qubits, lengths, num_samples, seed, full_sampling, backend=backend) + super().__init__( + qubits, + lengths, + backend=backend, + num_samples=num_samples, + seed=seed, + full_sampling=full_sampling, + ) def _sample_circuits(self, lengths, seed=None): circuits = [] diff --git a/qiskit_experiments/library/randomized_benchmarking/rb_experiment.py b/qiskit_experiments/library/randomized_benchmarking/rb_experiment.py index ad004a09b2..231b7eee06 100644 --- a/qiskit_experiments/library/randomized_benchmarking/rb_experiment.py +++ b/qiskit_experiments/library/randomized_benchmarking/rb_experiment.py @@ -63,10 +63,10 @@ def __init__( self, qubits: Union[int, Iterable[int]], lengths: Iterable[int], + backend: Optional[Backend] = None, num_samples: int = 3, seed: Optional[Union[int, Generator]] = None, full_sampling: Optional[bool] = False, - backend: Optional[Backend] = None, ): """Initialize a standard randomized benchmarking experiment. @@ -74,6 +74,7 @@ def __init__( qubits: The number of qubits or list of physical qubits for the experiment. lengths: A list of RB sequences lengths. + backend: The backend to run the experiment on. num_samples: Number of samples to generate for each sequence length. seed: Seed or generator object for random number generation. If None default_rng will be used. @@ -82,7 +83,6 @@ def __init__( sequences are constructed by appending additional Clifford samples to shorter sequences. The default is False. - backend: The backend to run the experiment on. """ # Initialize base experiment super().__init__(qubits, backend=backend) diff --git a/qiskit_experiments/library/tomography/tomography_experiment.py b/qiskit_experiments/library/tomography/tomography_experiment.py index 68e6f15afd..3685c3e2b1 100644 --- a/qiskit_experiments/library/tomography/tomography_experiment.py +++ b/qiskit_experiments/library/tomography/tomography_experiment.py @@ -54,19 +54,20 @@ def _default_experiment_options(cls) -> Options: def __init__( self, circuit: Union[QuantumCircuit, Instruction, BaseOperator], + backend: Optional[Backend] = None, measurement_basis: Optional[BaseTomographyMeasurementBasis] = None, measurement_qubits: Optional[Iterable[int]] = None, preparation_basis: Optional[BaseTomographyPreparationBasis] = None, preparation_qubits: Optional[Iterable[int]] = None, basis_indices: Optional[Iterable[Tuple[List[int], List[int]]]] = None, qubits: Optional[Iterable[int]] = None, - backend: Optional[Backend] = None, ): """Initialize a tomography experiment. Args: circuit: the quantum process circuit. If not a quantum circuit it must be a class that can be appended to a quantum circuit. + backend: The backend to run the experiment on. measurement_basis: Tomography basis for measurements. measurement_qubits: Optional, the qubits to be measured. These should refer to the logical qubits in the state circuit. @@ -76,7 +77,6 @@ def __init__( basis_indices: Optional, the basis elements to be measured. If None All basis elements will be measured. qubits: Optional, the physical qubits for the initial state circuit. - backend: The backend to run the experiment on. Raises: QiskitError: if input params are invalid. diff --git a/test/calibration/experiments/test_rough_frequency.py b/test/calibration/experiments/test_rough_frequency.py index 69a013bb81..1dc2d220ed 100644 --- a/test/calibration/experiments/test_rough_frequency.py +++ b/test/calibration/experiments/test_rough_frequency.py @@ -37,7 +37,9 @@ def test_init(self): auto_update = False absolute = False - freq = RoughFrequencyCal(qubit, cals, frequencies, unit, auto_update, absolute) + freq = RoughFrequencyCal( + qubit, cals, frequencies, unit=unit, auto_update=auto_update, absolute=absolute + ) self.assertEqual(freq.physical_qubits, (qubit,)) self.assertEqual(freq._frequencies, [1000, 2000, 3000]) From fe770bf13ff27f0823f448d1e92f9ff9aa94e156 Mon Sep 17 00:00:00 2001 From: Naoki Kanazawa Date: Wed, 27 Oct 2021 12:19:53 +0900 Subject: [PATCH 7/7] Update qiskit_experiments/library/characterization/fine_amplitude.py --- qiskit_experiments/library/characterization/fine_amplitude.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_experiments/library/characterization/fine_amplitude.py b/qiskit_experiments/library/characterization/fine_amplitude.py index fadc9862df..3c4c4c5fa2 100644 --- a/qiskit_experiments/library/characterization/fine_amplitude.py +++ b/qiskit_experiments/library/characterization/fine_amplitude.py @@ -134,7 +134,7 @@ def __init__(self, qubit: int, gate: Gate, backend: Optional[Backend] = None): backend: Optional, the backend to run the experiment on. """ super().__init__([qubit], backend=backend) - self.experiment_options.gate = gate + self.set_experiment_options(gate=gate) def _pre_circuit(self) -> QuantumCircuit: """Return a preparation circuit.