From 64e6049af3cb6ada9d84079514aca151eb89f6be Mon Sep 17 00:00:00 2001 From: "Christopher J. Wood" Date: Wed, 25 Jan 2023 13:05:30 -0500 Subject: [PATCH] Adds target kwarg to tomography init (#1025) This allows specifying a custom target for analysis when initializing the experiments. If set to None automatic target calculation is disabled. Co-authored-by: Will Shanks --- .../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 861dfeb178..aa9f13a789 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[Sequence[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 a86655114d..b13fde3e4d 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[Sequence[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..ecda89cfaf 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 intended 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 1b75e44de4..d86325d183 100644 --- a/test/library/tomography/test_process_tomography.py +++ b/test/library/tomography/test_process_tomography.py @@ -24,6 +24,7 @@ from qiskit_aer.noise import NoiseModel from qiskit_experiments.library import ProcessTomography, MitigatedProcessTomography from qiskit_experiments.library.tomography import ProcessTomographyAnalysis, basis +from qiskit_experiments.database_service import ExperimentEntryNotFound from .tomo_utils import ( FITTERS, filter_results, @@ -477,3 +478,21 @@ def test_mitigated_full_qpt_random_unitary(self, qubits): f_threshold, msg=f"{fitter} fit fidelity is low for qubits {qubits}", ) + + 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="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 67b846011b..738c6e5b11 100644 --- a/test/library/tomography/test_state_tomography.py +++ b/test/library/tomography/test_state_tomography.py @@ -26,6 +26,7 @@ from qiskit_experiments.library import StateTomography, MitigatedStateTomography from qiskit_experiments.library.tomography import StateTomographyAnalysis, basis +from qiskit_experiments.database_service import ExperimentEntryNotFound from .tomo_utils import ( FITTERS, filter_results, @@ -252,7 +253,7 @@ 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) @@ -375,3 +376,21 @@ def test_mitigated_full_qst(self, qubits): f_threshold, msg=f"{fitter} fit fidelity is low for qubits {qubits}", ) + + 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="Fitted state is not density matrix", + ) + with self.assertRaises( + ExperimentEntryNotFound, msg="state_fidelity should not exist when target=None" + ): + expdata.analysis_results("state_fidelity")