From 8b0b562d3d1a090df55d0c2ea88951dbb784183a Mon Sep 17 00:00:00 2001 From: ItamarGoldman <51112651+ItamarGoldman@users.noreply.github.com> Date: Thu, 30 Nov 2023 14:06:18 +0200 Subject: [PATCH] Bugfix when copying the `ExperiemntData` object and add warning to `_retrieve_data` (#1316) ### Summary As said in #1314 , `copy` method in `Experiment Data` class will now copy the `provider` attribute to the copied instance and warnning if data isn't retrieved due to `provider=None` . ### Details and comments Some details that should be in this section include: - Why this change was necessary This is a bug that the provider isn't copied. In addition, `_retrive_data()` method in the `ExperimentData` class doesn;t retirive data if provider is not provided. Added a warning so the user will know. --- qiskit_experiments/framework/experiment_data.py | 8 ++++++++ qiskit_experiments/test/fake_backend.py | 11 +++++++++-- .../notes/bugfix_expdata_copy-2c73a21ad720858d.yaml | 5 +++++ test/database_service/test_db_experiment_data.py | 8 ++++---- test/fake_experiment.py | 9 +++++++-- 5 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 releasenotes/notes/bugfix_expdata_copy-2c73a21ad720858d.yaml diff --git a/qiskit_experiments/framework/experiment_data.py b/qiskit_experiments/framework/experiment_data.py index 0697dc3fde..c42f610ad4 100644 --- a/qiskit_experiments/framework/experiment_data.py +++ b/qiskit_experiments/framework/experiment_data.py @@ -1071,6 +1071,13 @@ def _retrieve_data(self): """Retrieve job data if missing experiment data.""" # Get job results if missing in experiment data. if self.provider is None: + # 'self._result_data' could be locked, so I check a copy of it. + if not self._result_data.copy(): + # Adding warning so the user will have indication why the analysis may fail. + LOG.warning( + "Provider for ExperimentData object doesn't exist, resulting in a failed attempt to" + " retrieve data from the server; no stored result data exists" + ) return retrieved_jobs = {} jobs_to_retrieve = [] # the list of all jobs to retrieve from the server @@ -2293,6 +2300,7 @@ def copy(self, copy_results: bool = True) -> "ExperimentData": new_instance = ExperimentData( backend=self.backend, service=self.service, + provider=self.provider, parent_id=self.parent_id, job_ids=self.job_ids, child_data=list(self._child_data.values()), diff --git a/qiskit_experiments/test/fake_backend.py b/qiskit_experiments/test/fake_backend.py index 07bab78a78..d9f4772350 100644 --- a/qiskit_experiments/test/fake_backend.py +++ b/qiskit_experiments/test/fake_backend.py @@ -14,6 +14,7 @@ import uuid from qiskit.circuit.library import Measure from qiskit.providers.backend import BackendV2 +from qiskit.providers.fake_provider import FakeProvider from qiskit.providers.options import Options from qiskit.transpiler import Target @@ -27,8 +28,14 @@ class FakeBackend(BackendV2): Fake backend for test purposes only. """ - def __init__(self, backend_name="fake_backend", num_qubits=1, max_experiments=100): - super().__init__(name=backend_name) + def __init__( + self, + provider=FakeProvider(), + backend_name="fake_backend", + num_qubits=1, + max_experiments=100, + ): + super().__init__(provider=provider, name=backend_name) self._target = Target(num_qubits=num_qubits) # Add a measure for each qubit so a simple measure circuit works self.target.add_instruction(Measure()) diff --git a/releasenotes/notes/bugfix_expdata_copy-2c73a21ad720858d.yaml b/releasenotes/notes/bugfix_expdata_copy-2c73a21ad720858d.yaml new file mode 100644 index 0000000000..cec8f54e99 --- /dev/null +++ b/releasenotes/notes/bugfix_expdata_copy-2c73a21ad720858d.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + 'copy' method under ExperimentData class now copies the provider. + \ No newline at end of file diff --git a/test/database_service/test_db_experiment_data.py b/test/database_service/test_db_experiment_data.py index 3d83fa8512..003d21af0f 100644 --- a/test/database_service/test_db_experiment_data.py +++ b/test/database_service/test_db_experiment_data.py @@ -14,6 +14,7 @@ """Test ExperimentData.""" from test.base import QiskitExperimentsTestCase +from test.fake_experiment import FakeExperiment import os from unittest import mock import copy @@ -46,6 +47,7 @@ ExperimentStatus, ) from qiskit_experiments.framework.matplotlib import get_non_gui_ax +from qiskit_experiments.test.fake_backend import FakeBackend class TestDbExperimentData(QiskitExperimentsTestCase): @@ -1044,14 +1046,12 @@ def test_additional_attr(self): def test_copy_metadata(self): """Test copy metadata.""" - exp_data = ExperimentData(experiment_type="qiskit_test") + exp_data = FakeExperiment(experiment_type="qiskit_test").run(backend=FakeBackend()) exp_data.add_data(self._get_job_result(1)) - result = mock.MagicMock() - result.result_id = str(uuid.uuid4()) - exp_data.add_analysis_results(result) copied = exp_data.copy(copy_results=False) self.assertEqual(exp_data.data(), copied.data()) self.assertFalse(copied.analysis_results()) + self.assertEqual(exp_data.provider, copied.provider) def test_copy_metadata_pending_job(self): """Test copy metadata with a pending job.""" diff --git a/test/fake_experiment.py b/test/fake_experiment.py index 5e4a2fa060..f904a7f4c4 100644 --- a/test/fake_experiment.py +++ b/test/fake_experiment.py @@ -48,11 +48,16 @@ def _default_experiment_options(cls) -> Options: options.dummyoption = None return options - def __init__(self, physical_qubits=None): + def __init__(self, physical_qubits=None, backend=None, experiment_type=None): """Initialise the fake experiment.""" if physical_qubits is None: physical_qubits = [0] - super().__init__(physical_qubits, analysis=FakeAnalysis()) + super().__init__( + physical_qubits, + analysis=FakeAnalysis(), + backend=backend, + experiment_type=experiment_type, + ) def circuits(self): """Fake circuits."""