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..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 75bd87b729..fde44c8d8c 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="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..81f1fd0b6b 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="Fitted state is not density matrix", + ) + with self.assertRaises( + ExperimentEntryNotFound, msg="state_fidelity should not exist when target=None" + ): + expdata.analysis_results("state_fidelity")