Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix bug in tomography conditionals on multiple cregs #967

Merged
merged 5 commits into from
Nov 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions qiskit_experiments/library/tomography/tomography_experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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}"
Expand All @@ -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
Expand Down
11 changes: 11 additions & 0 deletions releasenotes/notes/fix-tomography-943-0f2b35964f2cf8ef.yaml
Original file line number Diff line number Diff line change
@@ -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 <https://github.com/Qiskit/qiskit-experiments/issues/943>`_
for additional details.
47 changes: 39 additions & 8 deletions test/library/tomography/test_process_tomography.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -244,29 +244,60 @@ 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")

# Manually check fidelity
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])
Expand Down
35 changes: 29 additions & 6 deletions test/library/tomography/test_state_tomography.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand All @@ -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],
Expand Down
43 changes: 37 additions & 6 deletions test/library/tomography/tomo_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"""
Common methods for tomography tests
"""
from qiskit import QuantumCircuit
from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister


FITTERS = [
Expand All @@ -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