From c38034896436e68c8a2b3dbb2b94de26a4d11d8d Mon Sep 17 00:00:00 2001 From: Christopher Wood Date: Wed, 2 Mar 2022 17:40:09 -0500 Subject: [PATCH 1/7] Set runtime run options on Experiment instance --- qiskit_experiments/framework/base_experiment.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/qiskit_experiments/framework/base_experiment.py b/qiskit_experiments/framework/base_experiment.py index af722a06ca..05f5d1e81b 100644 --- a/qiskit_experiments/framework/base_experiment.py +++ b/qiskit_experiments/framework/base_experiment.py @@ -245,13 +245,15 @@ def run( stacklevel=2, ) - if backend is not None or analysis != "default": + if backend is not None or analysis != "default" or run_options: # Make a copy to update analysis or backend if one is provided at runtime experiment = self.copy() if backend: experiment._set_backend(backend) if isinstance(analysis, BaseAnalysis): experiment.analysis = analysis + if run_options: + experiment.set_run_options(**run_options) else: experiment = self @@ -261,14 +263,12 @@ def run( # Initialize result container experiment_data = experiment._initialize_experiment_data() - # Run options - run_opts = copy.copy(experiment.run_options) - run_opts.update_options(**run_options) - run_opts = run_opts.__dict__ - # Generate and transpile circuits transpiled_circuits = experiment._transpiled_circuits() + # Run options + run_opts = experiment.run_options.__dict__ + # Run jobs jobs = experiment._run_jobs(transpiled_circuits, **run_opts) experiment_data.add_jobs(jobs, timeout=timeout) From f45a397ea105842d964d3bc14b62aeac3c910571 Mon Sep 17 00:00:00 2001 From: Christopher Wood Date: Tue, 1 Mar 2022 13:46:03 -0500 Subject: [PATCH 2/7] Adds BaseExperient._finalize method for subclassing This adds a `_finalize` method to BaseExperiment called in run before the actual experiment is run which gives users a hook for setting any custom experiment or option values based on the final configuration of the experiment before execution. --- qiskit_experiments/framework/base_experiment.py | 12 ++++++++++++ .../notes/exp-finalize-b7ca0a139ad5f872.yaml | 12 ++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 releasenotes/notes/exp-finalize-b7ca0a139ad5f872.yaml diff --git a/qiskit_experiments/framework/base_experiment.py b/qiskit_experiments/framework/base_experiment.py index 05f5d1e81b..c852f6849f 100644 --- a/qiskit_experiments/framework/base_experiment.py +++ b/qiskit_experiments/framework/base_experiment.py @@ -260,6 +260,9 @@ def run( if experiment.backend is None: raise QiskitError("Cannot run experiment, no backend has been set.") + # Finalize experiment before executions + experiment._finalize() + # Initialize result container experiment_data = experiment._initialize_experiment_data() @@ -319,6 +322,15 @@ def run_analysis( ) return self.analysis.run(experiment_data, replace_results=replace_results, **options) + def _finalize(self): + """Finalize experiment object before running jobs. + + Subclasses can override this method to set any final option + values derived from other options or attributes of the + experiment before `_run` is called. + """ + pass + def _run_jobs(self, circuits: List[QuantumCircuit], **run_options) -> List[BaseJob]: """Run circuits on backend as 1 or more jobs.""" # Run experiment jobs diff --git a/releasenotes/notes/exp-finalize-b7ca0a139ad5f872.yaml b/releasenotes/notes/exp-finalize-b7ca0a139ad5f872.yaml new file mode 100644 index 0000000000..a9a01e880c --- /dev/null +++ b/releasenotes/notes/exp-finalize-b7ca0a139ad5f872.yaml @@ -0,0 +1,12 @@ +--- +features: + - | + Adds a :meth:`.BaseExperiment._finalize` method to :class:`.BaseExperiment` + which is after configuring any runtime options, backend, or analysis + classes but before generation and execution of experiment + circuits during :class:`.BaseExperiment.run`. + + This method is intended to be overridden in experiment subclasses if they + need to configure any analysis or runtime options based on a combination + of properties of the experiment, for example some combination of backend, + experiment and run options. From b76f856a05cf10cdf4723d056fc4fcce9d1f4978 Mon Sep 17 00:00:00 2001 From: Christopher Wood Date: Wed, 2 Mar 2022 11:07:28 -0500 Subject: [PATCH 3/7] Add _finalize to CompositeExperiment --- .../framework/composite/composite_experiment.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/qiskit_experiments/framework/composite/composite_experiment.py b/qiskit_experiments/framework/composite/composite_experiment.py index 63aa510c8e..d000ca3674 100644 --- a/qiskit_experiments/framework/composite/composite_experiment.py +++ b/qiskit_experiments/framework/composite/composite_experiment.py @@ -98,11 +98,20 @@ def copy(self) -> "BaseExperiment": ret.analysis._analyses[i] = ret._experiments[i].analysis return ret + def set_run_options(self, **fields): + super().set_run_options(**fields) + for subexp in self._experiments: + subexp.set_run_options(**fields) + def _set_backend(self, backend): super()._set_backend(backend) for subexp in self._experiments: subexp._set_backend(backend) + def _finalize(self): + for subexp in self._experiments: + subexp._finalize() + def _initialize_experiment_data(self): """Initialize the return data container for the experiment run""" experiment_data = ExperimentData(experiment=self) From da3541dc4eb071273cc95622980743cec6ba4d20 Mon Sep 17 00:00:00 2001 From: Christopher Wood Date: Thu, 3 Mar 2022 13:27:44 -0500 Subject: [PATCH 4/7] Remove warning for sub-experiment run options These options are now recursively set during when setting an experiments run options. Note that all this job metadata is planned to be removed asap. --- .../framework/composite/composite_experiment.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/qiskit_experiments/framework/composite/composite_experiment.py b/qiskit_experiments/framework/composite/composite_experiment.py index d000ca3674..44b1d998f5 100644 --- a/qiskit_experiments/framework/composite/composite_experiment.py +++ b/qiskit_experiments/framework/composite/composite_experiment.py @@ -134,9 +134,4 @@ def _add_job_metadata(self, metadata, jobs, **run_options): for sub_metadata, sub_exp in zip( metadata["component_metadata"], self.component_experiment() ): - # Run and transpile options are always overridden - if sub_exp.run_options != sub_exp._default_run_options(): - warnings.warn( - "Sub-experiment run options" " are overridden by composite experiment options." - ) sub_exp._add_job_metadata(sub_metadata, jobs, **run_options) From 8e03aad08f285a9f53f79e1926829ba0c4597779 Mon Sep 17 00:00:00 2001 From: Christopher Wood Date: Thu, 3 Mar 2022 15:47:22 -0500 Subject: [PATCH 5/7] Fix warning for composite exp run options Move the validation that sub experiments did not explicitly set custom run options different from the parent experiment into composite experiment _finalize method. --- .../composite/composite_experiment.py | 19 ++++++++++++++++++- test/test_composite.py | 15 ++++++--------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/qiskit_experiments/framework/composite/composite_experiment.py b/qiskit_experiments/framework/composite/composite_experiment.py index 44b1d998f5..1a7ea2abcb 100644 --- a/qiskit_experiments/framework/composite/composite_experiment.py +++ b/qiskit_experiments/framework/composite/composite_experiment.py @@ -109,7 +109,24 @@ def _set_backend(self, backend): subexp._set_backend(backend) def _finalize(self): - for subexp in self._experiments: + # NOTE: When CompositeAnalysis is updated to support level-1 + # measurements this method should be updated to validate that all + # sub-experiments have the same meas level and meas return types, + # and update the composite experiment run option to that value. + + for i, subexp in enumerate(self._experiments): + # Raise warning if different run options were set for individual + # component experiments and not through the composite experiments + # set_run_options + for key in subexp._set_run_options: + subval = getattr(subexp.run_options, key) + compval = getattr(self.run_options, key, None) + if subval != compval: + warnings.warn( + f"Component experiment {i} run option {key}={subval} is " + f" differs from the the composite experiment value {compval}" + " and will be overridden." + ) subexp._finalize() def _initialize_experiment_data(self): diff --git a/test/test_composite.py b/test/test_composite.py index b17061515b..eacf0e2580 100644 --- a/test/test_composite.py +++ b/test/test_composite.py @@ -58,17 +58,14 @@ def test_parallel_options(self): par_exp = ParallelExperiment([exp0, exp2]) - with self.assertWarnsRegex( - Warning, - "Sub-experiment run 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()) + 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()) + with self.assertWarns(Warning): expdata = par_exp.run(FakeBackend()) - self.assertExperimentDone(expdata) + self.assertExperimentDone(expdata) def test_experiment_config(self): """Test converting to and from config works""" From a7cb763d7c6a6e6d23694f169c1d4c6128992bd3 Mon Sep 17 00:00:00 2001 From: Christopher Wood Date: Thu, 3 Mar 2022 16:15:04 -0500 Subject: [PATCH 6/7] Improve warning message --- .../composite/composite_experiment.py | 37 +++++++++++++------ test/test_composite.py | 2 +- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/qiskit_experiments/framework/composite/composite_experiment.py b/qiskit_experiments/framework/composite/composite_experiment.py index 1a7ea2abcb..147a295087 100644 --- a/qiskit_experiments/framework/composite/composite_experiment.py +++ b/qiskit_experiments/framework/composite/composite_experiment.py @@ -115,18 +115,31 @@ def _finalize(self): # and update the composite experiment run option to that value. for i, subexp in enumerate(self._experiments): - # Raise warning if different run options were set for individual - # component experiments and not through the composite experiments - # set_run_options - for key in subexp._set_run_options: - subval = getattr(subexp.run_options, key) - compval = getattr(self.run_options, key, None) - if subval != compval: - warnings.warn( - f"Component experiment {i} run option {key}={subval} is " - f" differs from the the composite experiment value {compval}" - " and will be overridden." - ) + # Validate set and default run options in component experiment + # against and component experiment run options and raise a + # warning if any are different and will be overridden + overridden_keys = [] + sub_vals = [] + comp_vals = [] + for key, sub_val in subexp.run_options.__dict__.items(): + comp_val = getattr(self.run_options, key, None) + if sub_val != comp_val: + overridden_keys.append(key) + sub_vals.append(sub_val) + comp_vals.append(comp_val) + + if overridden_keys: + warnings.warn( + f"Component experiment {i} run options {overridden_keys} values" + f" {sub_vals} will be overridden with component experiment" + f" values {comp_vals}.", + UserWarning, + ) + # Update sub-experiment options with actual run option values so + # they can be used by that sub experiments _finalize method. + subexp.set_run_options(**dict(zip(overridden_keys, comp_vals))) + + # Call sub-experiments finalize method subexp._finalize() def _initialize_experiment_data(self): diff --git a/test/test_composite.py b/test/test_composite.py index eacf0e2580..fbdd640445 100644 --- a/test/test_composite.py +++ b/test/test_composite.py @@ -63,7 +63,7 @@ def test_parallel_options(self): self.assertEqual(par_exp.transpile_options, Options(optimization_level=0)) self.assertEqual(par_exp.analysis.options, Options()) - with self.assertWarns(Warning): + with self.assertWarns(UserWarning): expdata = par_exp.run(FakeBackend()) self.assertExperimentDone(expdata) From ad67071cf749b2c2a7359f87ba986eb52560b57c Mon Sep 17 00:00:00 2001 From: Christopher Wood Date: Thu, 3 Mar 2022 16:20:56 -0500 Subject: [PATCH 7/7] Add experiment types to warning message --- .../framework/composite/composite_experiment.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qiskit_experiments/framework/composite/composite_experiment.py b/qiskit_experiments/framework/composite/composite_experiment.py index 147a295087..efd4b14f8e 100644 --- a/qiskit_experiments/framework/composite/composite_experiment.py +++ b/qiskit_experiments/framework/composite/composite_experiment.py @@ -130,9 +130,9 @@ def _finalize(self): if overridden_keys: warnings.warn( - f"Component experiment {i} run options {overridden_keys} values" - f" {sub_vals} will be overridden with component experiment" - f" values {comp_vals}.", + f"Component {i} {subexp.experiment_type} experiment run options" + f" {overridden_keys} values {sub_vals} will be overridden with" + f" {self.experiment_type} values {comp_vals}.", UserWarning, ) # Update sub-experiment options with actual run option values so