diff --git a/qiskit_experiments/base_experiment.py b/qiskit_experiments/base_experiment.py index a78b64b240..929d78774b 100644 --- a/qiskit_experiments/base_experiment.py +++ b/qiskit_experiments/base_experiment.py @@ -77,9 +77,6 @@ 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() - # Set initial layout from qubits - self._transpile_options.initial_layout = list(self._physical_qubits) - def run( self, backend: Backend, @@ -125,7 +122,9 @@ def run( run_opts = run_opts.__dict__ # Generate and transpile circuits - circuits = transpile(self.circuits(backend), backend, **self.transpile_options.__dict__) + transpile_opts = 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) if isinstance(backend, LegacyBackend): diff --git a/qiskit_experiments/composite/composite_experiment.py b/qiskit_experiments/composite/composite_experiment.py index 91e21792c2..ba054ba25d 100644 --- a/qiskit_experiments/composite/composite_experiment.py +++ b/qiskit_experiments/composite/composite_experiment.py @@ -14,6 +14,7 @@ """ from abc import abstractmethod +import warnings from qiskit_experiments.base_experiment import BaseExperiment from .composite_experiment_data import CompositeExperimentData @@ -63,6 +64,17 @@ def _add_job_metadata(self, experiment_data, job, **run_options): # Add sub-experiment options for i in range(self.num_experiments): sub_exp = self.component_experiment(i) + + # Run and transpile options are always overridden + if ( + sub_exp.run_options != sub_exp._default_run_options() + or sub_exp.transpile_options != sub_exp._default_transpile_options() + ): + + warnings.warn( + "Sub-experiment run and transpile options" + " are overridden by composite experiment options." + ) sub_data = experiment_data.component_experiment_data(i) sub_exp._add_job_metadata(sub_data, job, **run_options) diff --git a/test/analysis/test_curve_fit.py b/test/analysis/test_curve_fit.py index 79345a5640..ef9da8ac78 100644 --- a/test/analysis/test_curve_fit.py +++ b/test/analysis/test_curve_fit.py @@ -15,6 +15,8 @@ from typing import List +from test.fake_experiment import FakeExperiment + import numpy as np from qiskit.test import QiskitTestCase from qiskit.qobj.utils import MeasLevel @@ -22,20 +24,9 @@ from qiskit_experiments import ExperimentData from qiskit_experiments.analysis import CurveAnalysis, SeriesDef, fit_function from qiskit_experiments.analysis.data_processing import probability -from qiskit_experiments.base_experiment import BaseExperiment from qiskit_experiments.exceptions import AnalysisError -class FakeExperiment(BaseExperiment): - """A fake experiment class.""" - - def __init__(self): - super().__init__(qubits=(0,), experiment_type="fake_experiment") - - def circuits(self, backend=None): - return [] - - def simulate_output_data(func, xvals, param_dict, **metadata): """Generate arbitrary fit data.""" __shots = 100000 diff --git a/test/data_processing/__init__.py b/test/data_processing/__init__.py index e69de29bb2..784328ee92 100644 --- a/test/data_processing/__init__.py +++ b/test/data_processing/__init__.py @@ -0,0 +1,14 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Data processing tests.""" +from .base_data_processor_test import BaseDataProcessorTest diff --git a/test/data_processing/fake_experiment.py b/test/data_processing/base_data_processor_test.py similarity index 72% rename from test/data_processing/fake_experiment.py rename to test/data_processing/base_data_processor_test.py index d925c8d68e..d41c72e3fe 100644 --- a/test/data_processing/fake_experiment.py +++ b/test/data_processing/base_data_processor_test.py @@ -10,24 +10,10 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""A FakeExperiment for data processor testing.""" +"""Base class for data processor tests.""" from qiskit.test import QiskitTestCase from qiskit.qobj.common import QobjExperimentHeader -from qiskit_experiments.base_experiment import BaseExperiment - - -class FakeExperiment(BaseExperiment): - """Fake experiment class for testing.""" - - def __init__(self): - """Initialise the fake experiment.""" - self._type = None - super().__init__((0,), "fake_test_experiment") - - def circuits(self, backend=None): - """Fake circuits.""" - return [] class BaseDataProcessorTest(QiskitTestCase): diff --git a/test/data_processing/test_data_processing.py b/test/data_processing/test_data_processing.py index 70b4126d77..f756977a6f 100644 --- a/test/data_processing/test_data_processing.py +++ b/test/data_processing/test_data_processing.py @@ -14,7 +14,8 @@ # pylint: disable=unbalanced-tuple-unpacking -from test.data_processing.fake_experiment import FakeExperiment, BaseDataProcessorTest +from test.fake_experiment import FakeExperiment + import numpy as np from qiskit.result.models import ExperimentResultData, ExperimentResult from qiskit.result import Result @@ -31,6 +32,8 @@ MinMaxNormalize, ) +from . import BaseDataProcessorTest + class DataProcessorTest(BaseDataProcessorTest): """Class to test DataProcessor.""" diff --git a/test/data_processing/test_nodes.py b/test/data_processing/test_nodes.py index faaa9e9d1b..e01dba7b4d 100644 --- a/test/data_processing/test_nodes.py +++ b/test/data_processing/test_nodes.py @@ -14,7 +14,7 @@ # pylint: disable=unbalanced-tuple-unpacking -from test.data_processing.fake_experiment import FakeExperiment, BaseDataProcessorTest +from test.fake_experiment import FakeExperiment from typing import Any, List import numpy as np @@ -26,6 +26,8 @@ from qiskit_experiments.data_processing.nodes import SVD, AverageData, MinMaxNormalize from qiskit_experiments.data_processing.data_processor import DataProcessor +from . import BaseDataProcessorTest + class TestAveraging(QiskitTestCase): """Test the averaging nodes.""" diff --git a/test/fake_backend.py b/test/fake_backend.py new file mode 100644 index 0000000000..44ecd400f2 --- /dev/null +++ b/test/fake_backend.py @@ -0,0 +1,58 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Fake backend class for tests.""" +from qiskit.providers.backend import BackendV1 +from qiskit.providers.models import QasmBackendConfiguration +from qiskit.providers.options import Options + +from qiskit.result import Result + +from qiskit_experiments.test.mock_job import MockJob + + +class FakeBackend(BackendV1): + """ + Fake backend for test purposes only. + """ + + def __init__(self): + configuration = QasmBackendConfiguration( + backend_name="dummy_backend", + backend_version="0", + n_qubits=int(1e6), + basis_gates=["barrier", "x", "delay", "measure"], + gates=[], + local=True, + simulator=True, + conditional=False, + open_pulse=False, + memory=False, + max_shots=int(1e6), + coupling_map=None, + ) + super().__init__(configuration) + + @classmethod + def _default_options(cls): + return Options() + + def run(self, run_input, **options): + result = { + "backend_name": "Dummmy backend", + "backend_version": "0", + "qobj_id": 0, + "job_id": 0, + "success": True, + "results": [], + } + return MockJob(backend=self, result=Result.from_dict(result)) diff --git a/test/fake_experiment.py b/test/fake_experiment.py new file mode 100644 index 0000000000..93a9395f83 --- /dev/null +++ b/test/fake_experiment.py @@ -0,0 +1,46 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""A FakeExperiment for testing.""" + +from qiskit.providers.options import Options + +from qiskit_experiments.base_experiment import BaseExperiment +from qiskit_experiments.base_analysis import BaseAnalysis + + +class FakeAnalysis(BaseAnalysis): + """ + Dummy analysis class for test purposes only. + """ + + def _run_analysis(self, experiment_data, **options): + return [], None + + +class FakeExperiment(BaseExperiment): + """Fake experiment class for testing.""" + + __analysis_class__ = FakeAnalysis + + @classmethod + def _default_experiment_options(cls) -> Options: + return Options(dummyoption=None) + + def __init__(self, qubit=0): + """Initialise the fake experiment.""" + self._type = None + super().__init__((qubit,), "fake_test_experiment") + + def circuits(self, backend=None): + """Fake circuits.""" + return [] diff --git a/test/test_composite.py b/test/test_composite.py new file mode 100644 index 0000000000..9b93fdce17 --- /dev/null +++ b/test/test_composite.py @@ -0,0 +1,55 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Class to test composite experiments.""" + +from test.fake_backend import FakeBackend +from test.fake_experiment import FakeExperiment + +from qiskit.test import QiskitTestCase +from qiskit.providers.options import Options + +from qiskit_experiments.composite.parallel_experiment import ParallelExperiment + + +class TestComposite(QiskitTestCase): + """ + Test composite experiment behavior. + """ + + def test_parallel_options(self): + """ + Test parallel experiments overriding sub-experiment run and transpile options. + """ + + # These options will all be overridden + exp0 = FakeExperiment(0) + exp0.set_transpile_options(optimization_level=1) + exp2 = FakeExperiment(2) + exp2.set_experiment_options(dummyoption="test") + exp2.set_run_options(shots=2000) + exp2.set_transpile_options(optimization_level=1) + exp2.set_analysis_options(dummyoption="test") + + par_exp = ParallelExperiment([exp0, exp2]) + + with self.assertWarnsRegex( + Warning, + "Sub-experiment run and transpile options" + " are overridden by composite experiment options.", + ): + self.assertEqual(par_exp.experiment_options, Options()) + self.assertEqual(par_exp.run_options, Options(meas_level=2)) + self.assertEqual(par_exp.transpile_options, Options(optimization_level=0)) + self.assertEqual(par_exp.analysis_options, Options()) + + par_exp.run(FakeBackend())