Skip to content

Commit

Permalink
Remove metadata dependency from experiment helper
Browse files Browse the repository at this point in the history
  • Loading branch information
nkanazawa1989 committed Nov 9, 2023
1 parent 64e8186 commit 8d8c4ba
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 140 deletions.
85 changes: 19 additions & 66 deletions qiskit_experiments/test/mock_iq_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -389,57 +389,34 @@ def _parallel_exp_circ_splitter(self, qc_list: List[QuantumCircuit]):
experiment.
TypeError: The data type provided doesn't match the expected type (`tuple` or `int`).
"""
# exp_idx_map connects an experiment to its circuit in the output.
exp_idx_map = {exp: exp_idx for exp_idx, exp in enumerate(self.exp_list)}
qubit_exp_map = self._create_qubit_exp_map()

exp_circuits_list = [[] for _ in self.exp_list]
qubits_expid_map = {exp.physical_qubits: i for i, exp in enumerate(self.exp_list)}

for qc in qc_list:
# Quantum Register to qubit mapping
qubit_indices = {bit: idx for idx, bit in enumerate(qc.qubits)}

# initialize quantum circuit for each experiment for this instance of circuit to fill
# with instructions.
for exp_circuit in exp_circuits_list:
for i in range(len(self.exp_list)):
# we copy the circuit to ensure that the circuit properties (e.g. calibrations and qubit
# frequencies) are the same in the new circuit.
qcirc = qc.copy()
qcirc.data.clear()
qcirc.metadata.clear()
exp_circuit.append(qcirc)
empty_qc = qc.copy_empty_like()
empty_qc.metadata.clear()
exp_circuits_list[i].append(empty_qc)

# fixing metadata
for exp_metadata in qc.metadata["composite_metadata"]:
# getting a qubit of one of the experiments that we ran in parallel. The key in the
# metadata is different for different experiments.
qubit_metadata = (
exp_metadata.get("qubit")
if exp_metadata.get("qubit") is not None
else exp_metadata.get("qubits")
)
if isinstance(qubit_metadata, tuple):
exp = qubit_exp_map[qubit_metadata[0]]
elif isinstance(qubit_metadata, int):
exp = qubit_exp_map[qubit_metadata]
else:
raise TypeError(
f"The qubit information in the metadata is of type {type(qubit_metadata)}."
f" Supported formats are `tuple` and `int`"
)
# using the qubit to access the experiment. Then, we go to the last circuit in
# `exp_circuit` of the corresponding experiment, and we overwrite the metadata.
exp_circuits_list[exp_idx_map[exp]][-1].metadata = exp_metadata.copy()
for exp_idx, sub_metadata in zip(
qc.metadata["composite_index"],
qc.metadata["composite_metadata"],
):
exp_circuits_list[exp_idx][-1].metadata = sub_metadata.copy()

# sorting instructions by qubits indexes and inserting them into a circuit of the relevant
# experiment
for inst, qarg, carg in qc.data:
exp = qubit_exp_map[qubit_indices[qarg[0]]]
# making a list from the qubits the instruction affects
qubit_indexes = [qubit_indices[qr] for qr in qarg]
# check that the instruction is part of the experiment
if set(qubit_indexes).issubset(set(exp.physical_qubits)):
# appending exp_circuits_list[experiment_index][last_circuit]
exp_circuits_list[exp_idx_map[exp]][-1].append(inst, qarg, carg)
qubit_indices = set(qc.find_bit(qr).index for qr in qarg)
for qubits, exp_idx in qubits_expid_map.items():
if qubit_indices.issubset(qubits):
exp_circuits_list[exp_idx][-1].append(inst, qarg, carg)
break
else:
raise QiskitError(
"A gate operates on two qubits that don't belong to the same experiment."
Expand All @@ -453,27 +430,6 @@ def _parallel_exp_circ_splitter(self, qc_list: List[QuantumCircuit]):

return exp_circuits_list

def _create_qubit_exp_map(self) -> Dict[int, BaseExperiment]:
"""
Creating a dictionary that connect qubits to their respective experiments.
Returns:
Dict: A dictionary in the form {num: experiment} where num in experiment.physical_qubits
Raises:
QiskitError: If a qubit belong to two experiments.
"""
qubit_experiment_mapping = {}
for exp in self.exp_list:
for qubit in exp.physical_qubits:
if qubit not in qubit_experiment_mapping:
qubit_experiment_mapping[qubit] = exp
else:
raise QiskitError(
"There are duplications of qubits between parallel experiments"
)

return qubit_experiment_mapping


class MockIQDragHelper(MockIQExperimentHelper):
"""Functions needed for test_drag"""
Expand Down Expand Up @@ -861,12 +817,12 @@ class MockIQT1Helper(MockIQExperimentHelper):

def __init__(
self,
t1: List[float] = None,
t1: float = None,
iq_cluster_centers: Optional[List[Tuple[IQPoint, IQPoint]]] = None,
iq_cluster_width: Optional[List[float]] = None,
):
super().__init__(iq_cluster_centers, iq_cluster_width)
self._t1 = t1 or [90e-6]
self._t1 = t1 or 90e-6

def compute_probabilities(self, circuits: List[QuantumCircuit]) -> List[Dict[str, float]]:
"""Return the probability of being in the excited state."""
Expand All @@ -875,13 +831,10 @@ def compute_probabilities(self, circuits: List[QuantumCircuit]) -> List[Dict[str
probability_output_dict = {}

# extracting information from the circuit.
qubit_idx = circuit.metadata["qubit"]
delay = circuit.metadata["xval"]

# creating a probability dict.
if qubit_idx >= len(self._t1):
raise QiskitError(f"There is no 'T1' value for qubit index {qubit_idx}.")
probability_output_dict["1"] = np.exp(-delay / self._t1[qubit_idx])
probability_output_dict["1"] = np.exp(-delay / self._t1)
probability_output_dict["0"] = 1 - probability_output_dict["1"]
output_dict_list.append(probability_output_dict)

Expand Down
97 changes: 34 additions & 63 deletions test/library/characterization/test_t1.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ def test_t1_measurement_level_1(self):

ns = 1e-9
mu = 1e-6
t1 = [45 * mu, 45 * mu]
t1 = 45 * mu

# delays
delays = np.logspace(1, 11, num=23, base=np.exp(1))
delays *= ns
delays = np.insert(delays, 0, 0)
delays = np.append(delays, [t1[0] * 3])
delays = np.append(delays, [t1 * 3])

num_shots = 4096
backend = MockIQBackend(
Expand All @@ -78,7 +78,7 @@ def test_t1_measurement_level_1(self):
exp0 = T1([0], delays)
exp0.analysis = T1KerneledAnalysis()

exp0.analysis.set_options(p0={"amp": 1, "tau": t1[0], "base": 0})
exp0.analysis.set_options(p0={"amp": 1, "tau": t1, "base": 0})
expdata0 = exp0.run(
backend=backend,
meas_return="avg",
Expand All @@ -92,7 +92,7 @@ def test_t1_measurement_level_1(self):

res = expdata0.analysis_results("T1")
self.assertEqual(res.quality, "good")
self.assertAlmostEqual(res.value.n, t1[0], delta=3)
self.assertAlmostEqual(res.value.n, t1, delta=3)
self.assertEqual(res.extra["unit"], "s")

def test_t1_parallel(self):
Expand Down Expand Up @@ -129,34 +129,26 @@ def test_t1_parallel_measurement_level_1(self):

ns = 1e-9
mu = 1e-6
t1 = [25 * mu, 20 * mu, 15 * mu]
t1s = [25 * mu, 20 * mu]
qubits = [0, 1]
num_shots = 4096

# qubits
qubit0 = 0
qubit1 = 1

quantum_bit = [qubit0, qubit1]

# Delays
delays = np.logspace(1, 11, num=23, base=np.exp(1))
delays *= ns
delays = np.insert(delays, 0, 0)
delays = np.append(delays, [t1[0] * 3])

# Experiments
exp0 = T1(physical_qubits=[qubit0], delays=delays)
exp0.analysis = T1KerneledAnalysis()

exp2 = T1(physical_qubits=[qubit1], delays=delays)
exp2.analysis = T1KerneledAnalysis()

par_exp_list = [exp0, exp2]
par_exp = ParallelExperiment([exp0, exp2], flatten_results=False)

# Helpers
exp_helper = [
MockIQT1Helper(
delays = np.append(delays, [t1s[0] * 3])

par_exp_list = []
exp_helpers = []
for qidx, t1 in zip(qubits, t1s):
# Experiment
exp = T1(physical_qubits=[qidx], delays=delays)
exp.analysis = T1KerneledAnalysis()
par_exp_list.append(exp)

# Helper
helper = MockIQT1Helper(
t1=t1,
iq_cluster_centers=[
((-5.0, -4.0), (-5.0, 4.0)),
Expand All @@ -165,10 +157,15 @@ def test_t1_parallel_measurement_level_1(self):
],
iq_cluster_width=[1.0, 2.0, 1.0],
)
for _ in par_exp_list
]
exp_helpers.append(helper)

par_exp = ParallelExperiment(
par_exp_list,
flatten_results=False,
)
par_helper = MockIQParallelExperimentHelper(
exp_list=par_exp_list, exp_helper_list=exp_helper
exp_list=par_exp_list,
exp_helper_list=exp_helpers,
)

# Backend
Expand All @@ -185,10 +182,10 @@ def test_t1_parallel_measurement_level_1(self):
self.assertExperimentDone(res)

# Checking analysis
for i, qb in enumerate(quantum_bit):
for i, t1 in enumerate(t1s):
sub_res = res.child_data(i).analysis_results("T1")
self.assertEqual(sub_res.quality, "good")
self.assertAlmostEqual(sub_res.value.n, t1[qb], delta=3)
self.assertAlmostEqual(sub_res.value.n, t1, delta=3)

def test_t1_analysis(self):
"""
Expand All @@ -204,12 +201,7 @@ def test_t1_analysis(self):
data.add_data(
{
"counts": {"0": count0, "1": 10000 - count0},
"metadata": {
"xval": (3 * i + 1) * 1e-9,
"experiment_type": "T1",
"qubit": 0,
"unit": "s",
},
"metadata": {"xval": (3 * i + 1) * 1e-9},
}
)

Expand All @@ -230,16 +222,8 @@ def test_t1_metadata(self):
self.assertEqual(len(circs), len(delays))

for delay, circ in zip(delays, circs):
xval = circ.metadata.pop("xval")
self.assertAlmostEqual(xval, delay)
self.assertEqual(
circ.metadata,
{
"experiment_type": "T1",
"qubit": 0,
"unit": "s",
},
)
# xval is rounded to nealest granularity value.
self.assertAlmostEqual(circ.metadata["xval"], delay)

def test_t1_low_quality(self):
"""
Expand All @@ -253,12 +237,7 @@ def test_t1_low_quality(self):
data.add_data(
{
"counts": {"0": 10, "1": 10},
"metadata": {
"xval": i * 1e-9,
"experiment_type": "T1",
"qubit": 0,
"unit": "s",
},
"metadata": {"xval": i * 1e-9},
}
)

Expand Down Expand Up @@ -338,13 +317,5 @@ def test_circuits_with_backend(self):
self.assertEqual(len(circs), len(delays))

for delay, circ in zip(delays, circs):
xval = circ.metadata.pop("xval")
self.assertAlmostEqual(xval, delay)
self.assertEqual(
circ.metadata,
{
"experiment_type": "T1",
"qubit": 0,
"unit": "s",
},
)
# xval is rounded to nealest granularity value.
self.assertAlmostEqual(circ.metadata["xval"], delay)
22 changes: 11 additions & 11 deletions test/library/characterization/test_tphi.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,12 @@ def test_tphi_with_changing_params(self):
x_values_t1 = []
x_values_t2 = []
for datum in expdata.data():
comp_meta = datum["metadata"]["composite_metadata"][0]
if comp_meta["experiment_type"] == "T1":
x_values_t1.append(comp_meta["xval"])
metadata = datum["metadata"]
xval = metadata["composite_metadata"][0]["xval"]
if metadata["composite_index"][0] == 0:
x_values_t1.append(xval)
else:
x_values_t2.append(comp_meta["xval"])
x_values_t2.append(xval)
self.assertListEqual(x_values_t1, delays_t1, "Incorrect delays_t1")
self.assertListEqual(x_values_t2, delays_t2, "Incorrect delays_t2")

Expand All @@ -104,15 +105,14 @@ def test_tphi_with_changing_params(self):
# Extract x values from metadata
x_values_t1 = []
x_values_t2 = []
new_freq_t2 = None
new_freq_t2 = expdata.metadata["component_metadata"][1]["osc_freq"]
for datum in expdata.data():
comp_meta = datum["metadata"]["composite_metadata"][0]
if comp_meta["experiment_type"] == "T1":
x_values_t1.append(comp_meta["xval"])
metadata = datum["metadata"]
xval = metadata["composite_metadata"][0]["xval"]
if metadata["composite_index"][0] == 0:
x_values_t1.append(xval)
else:
x_values_t2.append(comp_meta["xval"])
if new_freq_t2 is None:
new_freq_t2 = comp_meta["osc_freq"]
x_values_t2.append(xval)
self.assertListEqual(x_values_t1, new_delays_t1, "Incorrect delays_t1")
self.assertListEqual(x_values_t2, new_delays_t2, "Incorrect delays_t2")
self.assertEqual(new_freq_t2, new_osc_freq, "Option osc_freq not set correctly")
Expand Down

0 comments on commit 8d8c4ba

Please sign in to comment.