Skip to content

Commit

Permalink
Fix bug in tomography conditionals on multiple cregs (#967)
Browse files Browse the repository at this point in the history
Updates tomography circuit generation to work around issues in QuantumCircuit.compose for circuits with conditional instructions on multiple registers.
  • Loading branch information
chriseclectic authored Nov 8, 2022
1 parent 95664a1 commit fa08a9a
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 28 deletions.
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

0 comments on commit fa08a9a

Please sign in to comment.