From 9c755e9c27b19ba361e49e376b451c3aa60437cd Mon Sep 17 00:00:00 2001 From: "Christopher J. Wood" Date: Wed, 25 Jan 2023 10:02:57 -0500 Subject: [PATCH 1/2] Adds target kwarg to tomography init This allows specifying a custom target for analysis when initializing the experiments. If set to None automatic target calculation is disabled. --- .../library/tomography/qpt_experiment.py | 9 +++++++- .../library/tomography/qst_experiment.py | 9 +++++++- ....yaml => tomography-b091ce13d6983bc1.yaml} | 9 ++++---- .../tomography/test_process_tomography.py | 19 +++++++++++++++++ .../tomography/test_state_tomography.py | 21 ++++++++++++++++++- 5 files changed, 59 insertions(+), 8 deletions(-) rename releasenotes/notes/{tomo-kwargs-b091ce13d6983bc1.yaml => tomography-b091ce13d6983bc1.yaml} (76%) diff --git a/qiskit_experiments/library/tomography/qpt_experiment.py b/qiskit_experiments/library/tomography/qpt_experiment.py index bf672dabc7..5a3881c90b 100644 --- a/qiskit_experiments/library/tomography/qpt_experiment.py +++ b/qiskit_experiments/library/tomography/qpt_experiment.py @@ -64,6 +64,7 @@ def __init__( basis_indices: Optional[Iterable[Tuple[List[int], List[int]]]] = None, qubits: Optional[Sequence[int]] = None, analysis: Union[BaseAnalysis, None, str] = "default", + target: Union[Statevector, DensityMatrix, None, str] = "default", ): """Initialize a quantum process tomography experiment. @@ -94,6 +95,10 @@ def __init__( analysis: Optional, a custom analysis instance to use. If ``"default"`` :class:`~.ProcessTomographyAnalysis` will be used. If None no analysis instance will be set. + target: Optional, a custom quantum state target for computing the + state fidelity of the fitted density matrix during analysis. + If "default" the state will be inferred from the input circuit + if it contains no classical instructions. """ if analysis == "default": analysis = ProcessTomographyAnalysis() @@ -115,7 +120,9 @@ def __init__( # Set target quantum channel if isinstance(self.analysis, TomographyAnalysis): - self.analysis.set_options(target=self._target_quantum_channel()) + if target == "default": + target = self._target_quantum_channel() + self.analysis.set_options(target=target) def _target_quantum_channel(self) -> Union[Choi, Operator]: """Return the process tomography target""" diff --git a/qiskit_experiments/library/tomography/qst_experiment.py b/qiskit_experiments/library/tomography/qst_experiment.py index 35b1e51d10..63a0f1d612 100644 --- a/qiskit_experiments/library/tomography/qst_experiment.py +++ b/qiskit_experiments/library/tomography/qst_experiment.py @@ -59,6 +59,7 @@ def __init__( basis_indices: Optional[Iterable[List[int]]] = None, qubits: Optional[Sequence[int]] = None, analysis: Union[BaseAnalysis, None, str] = "default", + target: Union[Statevector, DensityMatrix, None, str] = "default", ): """Initialize a quantum process tomography experiment. @@ -82,6 +83,10 @@ def __init__( analysis: Optional, a custom analysis instance to use. If ``"default"`` :class:`~.StateTomographyAnalysis` will be used. If None no analysis instance will be set. + target: Optional, a custom quantum state target for computing the + state fidelity of the fitted density matrix during analysis. + If "default" the state will be inferred from the input circuit + if it contains no classical instructions. """ if isinstance(circuit, Statevector): # Convert to circuit using initialize instruction @@ -110,7 +115,9 @@ def __init__( # Set target quantum state if isinstance(self.analysis, TomographyAnalysis): - self.analysis.set_options(target=self._target_quantum_state()) + if target == "default": + target = self._target_quantum_state() + self.analysis.set_options(target=target) def _target_quantum_state(self) -> Union[Statevector, DensityMatrix]: """Return the state tomography target""" diff --git a/releasenotes/notes/tomo-kwargs-b091ce13d6983bc1.yaml b/releasenotes/notes/tomography-b091ce13d6983bc1.yaml similarity index 76% rename from releasenotes/notes/tomo-kwargs-b091ce13d6983bc1.yaml rename to releasenotes/notes/tomography-b091ce13d6983bc1.yaml index 7896c62a72..d3d365e1f6 100644 --- a/releasenotes/notes/tomo-kwargs-b091ce13d6983bc1.yaml +++ b/releasenotes/notes/tomography-b091ce13d6983bc1.yaml @@ -1,11 +1,10 @@ --- features: - | - Adds ``backend`` and ``analysis`` init kwargs to :class:`~.StateTomography` - and :class:`~.ProcessTomography` experiments to match the base - :class:`~.TomographyExperiment` init kwargs. This allows specifying the - intented backend, or a custom analysis class when initializing the - experiments. + Adds ``backend``, ``analysis``, and ``target`` init kwargs to the + :class:`~.StateTomography` and :class:`~.ProcessTomography` experiments. + These allow specifying intented backend, a custom analysis class, or a + custom target for fidelity calculations, when initializing the experiments. upgrade: - | Renames the ``qubits``, ``measurement_qubits``, and ``preparation_qubits`` diff --git a/test/library/tomography/test_process_tomography.py b/test/library/tomography/test_process_tomography.py index 75bd87b729..4f4587daaa 100644 --- a/test/library/tomography/test_process_tomography.py +++ b/test/library/tomography/test_process_tomography.py @@ -21,6 +21,7 @@ from qiskit_aer import AerSimulator from qiskit_experiments.library import ProcessTomography from qiskit_experiments.library.tomography import ProcessTomographyAnalysis +from qiskit_experiments.database_service import ExperimentEntryNotFound from .tomo_utils import FITTERS, filter_results, teleport_circuit, teleport_bell_circuit @@ -335,3 +336,21 @@ def test_expdata_serialization(self): self.assertExperimentDone(expdata) self.assertRoundTripPickle(expdata, check_func=self.experiment_data_equiv) self.assertRoundTripSerializable(expdata, check_func=self.experiment_data_equiv) + + def test_target_none(self): + """Test setting target=None disables fidelity calculation.""" + seed = 4343 + backend = AerSimulator(seed_simulator=seed) + target = qi.random_unitary(2, seed=seed) + exp = ProcessTomography(target, backend=backend, target=None) + expdata = exp.run() + self.assertExperimentDone(expdata) + state = expdata.analysis_results("state").value + self.assertTrue( + isinstance(state, qi.Choi), + msg=f"Fitted state is not Choi matrix", + ) + with self.assertRaises( + ExperimentEntryNotFound, msg="process_fidelity should not exist when target=None" + ): + expdata.analysis_results("process_fidelity") diff --git a/test/library/tomography/test_state_tomography.py b/test/library/tomography/test_state_tomography.py index b2c0668972..31dc174f5d 100644 --- a/test/library/tomography/test_state_tomography.py +++ b/test/library/tomography/test_state_tomography.py @@ -23,6 +23,7 @@ from qiskit_experiments.library import StateTomography from qiskit_experiments.library.tomography import StateTomographyAnalysis +from qiskit_experiments.database_service import ExperimentEntryNotFound from .tomo_utils import FITTERS, filter_results, teleport_circuit, teleport_bell_circuit @@ -243,8 +244,26 @@ def test_experiment_config(self): self.assertTrue(self.json_equiv(exp, loaded_exp)) def test_analysis_config(self): - """ "Test converting analysis to and from config works""" + """Test converting analysis to and from config works""" analysis = StateTomographyAnalysis() loaded = StateTomographyAnalysis.from_config(analysis.config()) self.assertNotEqual(analysis, loaded) self.assertEqual(analysis.config(), loaded.config()) + + def test_target_none(self): + """Test setting target=None disables fidelity calculation.""" + seed = 4343 + backend = AerSimulator(seed_simulator=seed) + target = qi.random_statevector(2, seed=seed) + exp = StateTomography(target, backend=backend, target=None) + expdata = exp.run() + self.assertExperimentDone(expdata) + state = expdata.analysis_results("state").value + self.assertTrue( + isinstance(state, qi.DensityMatrix), + msg=f"Fitted state is not density matrix", + ) + with self.assertRaises( + ExperimentEntryNotFound, msg="state_fidelity should not exist when target=None" + ): + expdata.analysis_results("state_fidelity") From e3b8ab192589bb4039a9a08824213c5abd9a1bdf Mon Sep 17 00:00:00 2001 From: "Christopher J. Wood" Date: Wed, 25 Jan 2023 11:05:33 -0500 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: Will Shanks --- releasenotes/notes/tomography-b091ce13d6983bc1.yaml | 2 +- test/library/tomography/test_process_tomography.py | 2 +- test/library/tomography/test_state_tomography.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/releasenotes/notes/tomography-b091ce13d6983bc1.yaml b/releasenotes/notes/tomography-b091ce13d6983bc1.yaml index d3d365e1f6..ecda89cfaf 100644 --- a/releasenotes/notes/tomography-b091ce13d6983bc1.yaml +++ b/releasenotes/notes/tomography-b091ce13d6983bc1.yaml @@ -3,7 +3,7 @@ features: - | Adds ``backend``, ``analysis``, and ``target`` init kwargs to the :class:`~.StateTomography` and :class:`~.ProcessTomography` experiments. - These allow specifying intented backend, a custom analysis class, or a + These allow specifying intended backend, a custom analysis class, or a custom target for fidelity calculations, when initializing the experiments. upgrade: - | diff --git a/test/library/tomography/test_process_tomography.py b/test/library/tomography/test_process_tomography.py index 4f4587daaa..fde44c8d8c 100644 --- a/test/library/tomography/test_process_tomography.py +++ b/test/library/tomography/test_process_tomography.py @@ -348,7 +348,7 @@ def test_target_none(self): state = expdata.analysis_results("state").value self.assertTrue( isinstance(state, qi.Choi), - msg=f"Fitted state is not Choi matrix", + msg="Fitted state is not Choi matrix", ) with self.assertRaises( ExperimentEntryNotFound, msg="process_fidelity should not exist when target=None" diff --git a/test/library/tomography/test_state_tomography.py b/test/library/tomography/test_state_tomography.py index 31dc174f5d..81f1fd0b6b 100644 --- a/test/library/tomography/test_state_tomography.py +++ b/test/library/tomography/test_state_tomography.py @@ -261,7 +261,7 @@ def test_target_none(self): state = expdata.analysis_results("state").value self.assertTrue( isinstance(state, qi.DensityMatrix), - msg=f"Fitted state is not density matrix", + msg="Fitted state is not density matrix", ) with self.assertRaises( ExperimentEntryNotFound, msg="state_fidelity should not exist when target=None"