diff --git a/qiskit_experiments/framework/analysis_result_table.py b/qiskit_experiments/framework/analysis_result_table.py index 2c84a08e2a..059923d7a1 100644 --- a/qiskit_experiments/framework/analysis_result_table.py +++ b/qiskit_experiments/framework/analysis_result_table.py @@ -204,16 +204,26 @@ def clear(self): with self._lock: self._data = pd.DataFrame(columns=self.DEFAULT_COLUMNS) - def copy(self): + def copy(self, new_ids: bool = True): """Create new thread-safe instance with the same data. - .. note:: - This returns a new object with shallow copied data frame. + Args: + new_ids: Whether to generate new IDs for copied entries. Defaults to True. + + Returns: + A new shallow copied DataFrame object. """ with self._lock: # Hold the lock so that no data can be added new_instance = self.__class__() new_instance._data = self._data.copy(deep=False) + if new_ids: + new_instance._data["result_id"] = None + for idx, _ in new_instance._data.iterrows(): + new_instance._data.at[idx, "result_id"] = new_instance._create_unique_hash() + new_instance._data.index = [ + result_id[:8] for result_id in new_instance._data["result_id"] + ] return new_instance def _create_unique_hash(self) -> str: diff --git a/qiskit_experiments/framework/experiment_data.py b/qiskit_experiments/framework/experiment_data.py index 56857164b9..7088954641 100644 --- a/qiskit_experiments/framework/experiment_data.py +++ b/qiskit_experiments/framework/experiment_data.py @@ -2396,7 +2396,10 @@ def copy(self, copy_results: bool = True) -> "ExperimentData": # Copy results and figures. # This requires analysis callbacks to finish self._wait_for_futures(self._analysis_futures.values(), name="analysis") - new_instance._analysis_results = self._analysis_results.copy() + copied_results = self._analysis_results.copy() + # Analysis results should have experiment ID of the copied experiment + copied_results._data["experiment_id"] = new_instance.experiment_id + new_instance._analysis_results = copied_results with self._figures.lock: new_instance._figures = ThreadSafeOrderedDict() new_instance.add_figures(self._figures.values()) diff --git a/releasenotes/notes/fix-copy-analysis-results-e7a8381a7b720f47.yaml b/releasenotes/notes/fix-copy-analysis-results-e7a8381a7b720f47.yaml new file mode 100644 index 0000000000..8f9a42ec45 --- /dev/null +++ b/releasenotes/notes/fix-copy-analysis-results-e7a8381a7b720f47.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Fixed a bug where a copied :class:`.ExperimentData` instance's analysis results didn't get + new IDs and experiement ID. diff --git a/test/database_service/test_db_experiment_data.py b/test/database_service/test_db_experiment_data.py index 020e871cc7..b8a66ca013 100644 --- a/test/database_service/test_db_experiment_data.py +++ b/test/database_service/test_db_experiment_data.py @@ -15,6 +15,8 @@ """Test ExperimentData.""" from test.base import QiskitExperimentsTestCase from test.fake_experiment import FakeExperiment +from test.extended_equality import is_equivalent + import os from unittest import mock import copy @@ -1058,12 +1060,25 @@ def test_additional_attr(self): def test_copy_metadata(self): """Test copy metadata.""" exp_data = FakeExperiment(experiment_type="qiskit_test").run(backend=FakeBackend()) + self.assertExperimentDone(exp_data) exp_data.add_data(self._get_job_result(1)) 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_analysis_results(self): + """Test copy analysis results.""" + exp_data = FakeExperiment(experiment_type="qiskit_test").run(backend=FakeBackend()) + self.assertExperimentDone(exp_data) + exp_data.add_data(self._get_job_result(1)) + copied = exp_data.copy(copy_results=True) + for res in copied.analysis_results(): + self.assertEqual(res.experiment_id, copied.experiment_id) + # copied analysis results should be identical to the original except for experiment ID + copied._analysis_results._data["experiment_id"] = exp_data.experiment_id + self.assertTrue(is_equivalent(exp_data.analysis_results(), copied.analysis_results())) + def test_copy_figure_artifacts(self): """Test copy expdata figures and artifacts.""" exp_data = FakeExperiment(experiment_type="qiskit_test").run(backend=FakeBackend())