diff --git a/qiskit_experiments/library/tomography/tomography_experiment.py b/qiskit_experiments/library/tomography/tomography_experiment.py index 5b57e888f3..b1039e79ce 100644 --- a/qiskit_experiments/library/tomography/tomography_experiment.py +++ b/qiskit_experiments/library/tomography/tomography_experiment.py @@ -15,7 +15,7 @@ from typing import Union, Optional, Iterable, List, Tuple, Sequence from itertools import product -from qiskit.circuit import QuantumCircuit, Instruction +from qiskit.circuit import QuantumCircuit, Instruction, ClassicalRegister from qiskit.circuit.library import Permutation from qiskit.providers.backend import Backend from qiskit.quantum_info.operators.base_operator import BaseOperator @@ -152,16 +152,18 @@ def __init__( def circuits(self): - # Get qubits and clbits - circ_qubits = list(range(self._circuit.num_qubits)) - total_clbits = self._circuit.num_clbits + len(self._meas_qubits) - circ_clbits = list(range(self._circuit.num_clbits)) - meas_clbits = list(range(self._circuit.num_clbits, total_clbits)) + circ_qubits = self._circuit.qubits + circ_clbits = self._circuit.clbits + meas_creg = ClassicalRegister((len(self._meas_qubits)), name="c_tomo") + template = QuantumCircuit( + *self._circuit.qregs, *self._circuit.cregs, meas_creg, name=f"{self._type}" + ) + meas_clbits = [template.find_bit(i).index for i in meas_creg] # Build circuits circuits = [] for prep_element, meas_element in self._basis_indices(): - name = f"{self._type}" + name = template.name metadata = {"clbits": meas_clbits} if meas_element: name += f"_{meas_element}" @@ -170,7 +172,7 @@ def circuits(self): name += f"_{prep_element}" metadata["p_idx"] = list(prep_element) - circ = QuantumCircuit(self.num_qubits, total_clbits, name=name) + circ = template.copy(name=name) if prep_element: # Add tomography preparation diff --git a/releasenotes/notes/fix-tomography-943-0f2b35964f2cf8ef.yaml b/releasenotes/notes/fix-tomography-943-0f2b35964f2cf8ef.yaml new file mode 100644 index 0000000000..305bb77ffa --- /dev/null +++ b/releasenotes/notes/fix-tomography-943-0f2b35964f2cf8ef.yaml @@ -0,0 +1,11 @@ +--- +fixes: + - | + Fixes bug in :class:`~.StateTomography` and :class:`~.ProcessTomography` + experiments where if the input circuit contained conditional instructions + with multiple classical registers the tomography measurement circuits + would contain incorrect conditionals due to a bug in the + :meth:`.QuantumCircuit.compose` method. + + See Issue #942 `_ + for additional details. diff --git a/test/library/tomography/test_process_tomography.py b/test/library/tomography/test_process_tomography.py index a16f11e67f..80091dfc82 100644 --- a/test/library/tomography/test_process_tomography.py +++ b/test/library/tomography/test_process_tomography.py @@ -21,7 +21,7 @@ from qiskit_aer import AerSimulator from qiskit_experiments.library import ProcessTomography from qiskit_experiments.library.tomography import ProcessTomographyAnalysis -from .tomo_utils import FITTERS, filter_results, teleport_circuit +from .tomo_utils import FITTERS, filter_results, teleport_circuit, teleport_bell_circuit @ddt.ddt @@ -244,22 +244,22 @@ def test_full_exp_meas_prep_qubits(self, qubits): target_fid = qi.process_fidelity(state, target, require_tp=False, require_cp=False) self.assertAlmostEqual(fid, target_fid, places=6, msg="result fidelity is incorrect") - def test_qpt_teleport(self): + @ddt.data(True, False) + def test_qpt_teleport(self, flatten_creg): """Test subset state tomography generation""" - # NOTE: This test breaks transpiler. I think it is a bug with - # conditionals in Terra. - # Teleport qubit 0 -> 2 backend = AerSimulator(seed_simulator=9000) - exp = ProcessTomography(teleport_circuit(), measurement_qubits=[2], preparation_qubits=[0]) - expdata = exp.run(backend, shots=10000) + exp = ProcessTomography( + teleport_circuit(flatten_creg), measurement_qubits=[2], preparation_qubits=[0] + ) + expdata = exp.run(backend, shots=1000) self.assertExperimentDone(expdata) results = expdata.analysis_results() # Check result f_threshold = 0.95 - # Check state is density matrix + # Check state is a Choi matrix state = filter_results(results, "state").value self.assertTrue(isinstance(state, qi.Choi), msg="fitted state is not a Choi matrix") @@ -267,6 +267,37 @@ def test_qpt_teleport(self): fid = qi.process_fidelity(state, require_tp=False, require_cp=False) self.assertGreater(fid, f_threshold, msg="fitted state fidelity is low") + @ddt.data(True, False) + def test_qpt_teleport_bell(self, flatten_creg): + """Test subset state tomography generation""" + # Teleport qubit 0 -> 2 + backend = AerSimulator(seed_simulator=9000) + exp = ProcessTomography( + teleport_bell_circuit(flatten_creg), + measurement_qubits=[2, 3], + preparation_qubits=[0, 3], + ) + expdata = exp.run(backend, shots=1000) + self.assertExperimentDone(expdata) + results = expdata.analysis_results() + + # Check result + f_threshold = 0.95 + + # Check state is a Choi matrix + state = filter_results(results, "state").value + self.assertTrue(isinstance(state, qi.Choi), msg="fitted state is not a Choi matrix") + + # Target circuit + target = QuantumCircuit(2) + target.h(0) + target.cx(0, 1) + target = qi.Operator(target) + + # Manually check fidelity + fid = qi.process_fidelity(state, target, require_tp=False, require_cp=False) + self.assertGreater(fid, f_threshold, msg="fitted state fidelity is low") + def test_experiment_config(self): """Test converting to and from config works""" exp = ProcessTomography(teleport_circuit(), measurement_qubits=[2], preparation_qubits=[0]) diff --git a/test/library/tomography/test_state_tomography.py b/test/library/tomography/test_state_tomography.py index 4223a1919d..b3b8c144a3 100644 --- a/test/library/tomography/test_state_tomography.py +++ b/test/library/tomography/test_state_tomography.py @@ -14,14 +14,16 @@ StateTomography experiment tests """ from test.base import QiskitExperimentsTestCase +from math import sqrt import ddt from qiskit import QuantumCircuit from qiskit.circuit.library import XGate import qiskit.quantum_info as qi from qiskit_aer import AerSimulator + from qiskit_experiments.library import StateTomography from qiskit_experiments.library.tomography import StateTomographyAnalysis -from .tomo_utils import FITTERS, filter_results, teleport_circuit +from .tomo_utils import FITTERS, filter_results, teleport_circuit, teleport_bell_circuit @ddt.ddt @@ -69,14 +71,12 @@ def test_full_qst(self, num_qubits): fid, target_fid, places=6, msg=f"{fitter} result fidelity is incorrect" ) - def test_qst_teleport(self): + @ddt.data(True, False) + def test_qst_teleport(self, flatten_creg): """Test subset state tomography generation""" - # NOTE: This test breaks transpiler. I think it is a bug with - # conditionals in Terra. - # Teleport qubit 0 -> 2 backend = AerSimulator(seed_simulator=9000) - exp = StateTomography(teleport_circuit(), measurement_qubits=[2]) + exp = StateTomography(teleport_circuit(flatten_creg), measurement_qubits=[2]) expdata = exp.run(backend) self.assertExperimentDone(expdata) results = expdata.analysis_results() @@ -94,6 +94,29 @@ def test_qst_teleport(self): fid = qi.state_fidelity(state, qi.Statevector([1, 0]), validate=False) self.assertGreater(fid, f_threshold, msg="fitted state fidelity is low") + @ddt.data(True, False) + def test_qst_teleport_bell(self, flatten_creg): + """Test subset state tomography generation""" + # Teleport qubit 0 -> 2 + backend = AerSimulator(seed_simulator=9000) + exp = StateTomography(teleport_bell_circuit(flatten_creg), measurement_qubits=[2, 3]) + expdata = exp.run(backend) + self.assertExperimentDone(expdata) + results = expdata.analysis_results() + + # Check result + f_threshold = 0.95 + + # Check state is density matrix + state = filter_results(results, "state").value + self.assertTrue( + isinstance(state, qi.DensityMatrix), msg="fitted state is not a density matrix" + ) + + # Manually check fidelity + fid = qi.state_fidelity(state, qi.Statevector([1, 0, 0, 1]) / sqrt(2), validate=False) + self.assertGreater(fid, f_threshold, msg="fitted state fidelity is low") + @ddt.data( [0], [1], diff --git a/test/library/tomography/tomo_utils.py b/test/library/tomography/tomo_utils.py index b419f3005c..7cdfc5f892 100644 --- a/test/library/tomography/tomo_utils.py +++ b/test/library/tomography/tomo_utils.py @@ -13,7 +13,7 @@ """ Common methods for tomography tests """ -from qiskit import QuantumCircuit +from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister FITTERS = [ @@ -32,17 +32,48 @@ def filter_results(analysis_results, name): return None -def teleport_circuit(): +def teleport_circuit(flatten_creg=True): """Teleport qubit 0 to qubit 2""" - teleport = QuantumCircuit(3, 2) + if flatten_creg: + teleport = QuantumCircuit(3, 2) + creg = teleport.cregs[0] + else: + qr = QuantumRegister(3) + c0 = ClassicalRegister(1, "c0") + c1 = ClassicalRegister(1, "c1") + teleport = QuantumCircuit(qr, c0, c1) + creg = [c0, c1] teleport.h(1) teleport.cx(1, 2) teleport.cx(0, 1) teleport.h(0) - teleport.measure(0, 0) - teleport.measure(1, 1) + teleport.measure(0, creg[0]) + teleport.measure(1, creg[1]) # Conditionals - creg = teleport.cregs[0] + teleport.z(2).c_if(creg[0], 1) + teleport.x(2).c_if(creg[1], 1) + return teleport + + +def teleport_bell_circuit(flatten_creg=True): + """Teleport entangled qubit 0 -> 2""" + if flatten_creg: + teleport = QuantumCircuit(4, 2) + creg = teleport.cregs[0] + else: + qr = QuantumRegister(4) + c0 = ClassicalRegister(1) + c1 = ClassicalRegister(1) + teleport = QuantumCircuit(qr, c0, c1) + creg = [c0, c1] + teleport.h(0) + teleport.cx(0, 3) + teleport.h(1) + teleport.cx(1, 2) + teleport.cx(0, 1) + teleport.h(0) + teleport.measure(0, creg[0]) + teleport.measure(1, creg[1]) teleport.z(2).c_if(creg[0], 1) teleport.x(2).c_if(creg[1], 1) return teleport