From f2945b3ebfc73aeb84020f7a523fea22ecdaa1e7 Mon Sep 17 00:00:00 2001 From: catornow Date: Tue, 22 Feb 2022 19:07:50 +0100 Subject: [PATCH 01/68] First draft of RestlessEnabledExperiment --- .../calibration_management/restless_mix_in.py | 74 ++++++++ qiskit_experiments/data_processing/nodes.py | 172 +++++++++++++++++- .../characterization/fine_amplitude.py | 4 +- qiskit_experiments/test/mock_iq_backend.py | 141 ++++++++++++++ test/data_processing/test_nodes.py | 57 ++++++ .../test_restless_experiment.py | 86 +++++++++ 6 files changed, 532 insertions(+), 2 deletions(-) create mode 100644 qiskit_experiments/calibration_management/restless_mix_in.py create mode 100644 test/data_processing/test_restless_experiment.py diff --git a/qiskit_experiments/calibration_management/restless_mix_in.py b/qiskit_experiments/calibration_management/restless_mix_in.py new file mode 100644 index 0000000000..a29682af6c --- /dev/null +++ b/qiskit_experiments/calibration_management/restless_mix_in.py @@ -0,0 +1,74 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Restless mix in class.""" + +from abc import ABC +from qiskit_experiments.framework.base_experiment import BaseExperiment + +from qiskit_experiments.data_processing.data_processor import DataProcessor +from qiskit_experiments.data_processing.exceptions import DataProcessorError +from qiskit_experiments.data_processing import nodes + + +class RestlessEnabledExperiment(BaseExperiment, ABC): + """Restless enabled class.""" + + def enable_restless(self, rep_delay: float, outcome: str): + """Enables a restless experiment by setting the restless run options and + the restless data processor. + + Args: + rep_delay: The repetition delay. + outcome + """ + # Todo: outcome can be extracted from the analysis options. + # if self._is_restless(rep_delay): + self.set_run_options(rep_delay=rep_delay, init_qubit=False, memory=True, meas_level=2) + self.analysis.set_options(data_processor=self._get_restless_processor(outcome * self.num_qubits)) + + def _get_restless_processor(self, outcome: str) -> DataProcessor: + return DataProcessor( + "memory", + [ + nodes.RestlessToCounts(self.num_qubits), + nodes.Probability(outcome), + ], + ) + + def _is_restless(self, rep_delay: float) -> bool: + """Checks if the specified repetition delay is much smaller than the T1 + times of the physical qubits in the experiment. + + Args: + rep_delay: The repetition delay. + Returns: + True if the repetition delay is much smaller than the qubit T1 times. + """ + + esp_enabled = self.analysis.options.get("use_measure_esp", False) + + # Todo: include properties in mock backend. + t1_values = [ + self.backend.properties().qubits[physical_qubit][0].value * 1e-6 + for physical_qubit in self.physical_qubits + ] + + if all(rep_delay / t1_value < 1. for t1_value in t1_values): + if esp_enabled: + raise DataProcessorError( + "Restless experiments are not compatible with the excited " + "state promotion readout analysis option." + ) + return True + + return False diff --git a/qiskit_experiments/data_processing/nodes.py b/qiskit_experiments/data_processing/nodes.py index 0f3c5039b5..42509419cf 100644 --- a/qiskit_experiments/data_processing/nodes.py +++ b/qiskit_experiments/data_processing/nodes.py @@ -13,13 +13,16 @@ """Different data analysis steps.""" from abc import abstractmethod +from abc import ABC from enum import Enum from numbers import Number -from typing import Union, Sequence +from typing import Union, Sequence, Any +from collections import defaultdict import numpy as np from uncertainties import unumpy as unp, ufloat +from qiskit.result.postprocess import format_counts_memory from qiskit_experiments.data_processing.data_action import DataAction, TrainableDataAction from qiskit_experiments.data_processing.exceptions import DataProcessorError from qiskit_experiments.framework import Options @@ -596,3 +599,170 @@ class ProjectorType(Enum): ABS = ToAbs REAL = ToReal IMAG = ToImag + + +class RestlessNode(DataAction, ABC): + """An abstract node for restless data processing nodes. + + In restless measurements, the qubit is not reset after each measurement. Instead, the + outcome of the previous quantum non-demolition measurement is the initial state for the + current circuit. Restless measurements therefore require special data processing nodes + that are implemented as sub-classes of `RestlessNode`. Restless experiments provide a + fast alternative for several calibration and characterization tasks, for details + see https://arxiv.org/pdf/2202.06981.pdf. + """ + + def __init__(self, validate: bool = True): # , circuits_first: bool = True): + """Initialize a restless node. + + Args: + validate: If set to True the node will validate its input. + """ + super().__init__(validate) + self._n_shots = None + self._n_circuits = None + + def _format_data(self, data: Any) -> Any: + """Convert the data to an array. + + This node will also set all the attributes needed to process the data such as + the number of shots and the number of circuits. + + Args: + data: An array representing the memory. + + Returns: + The data that has been processed. + + Raises: + DataProcessorError: If the datum has the wrong shape. + """ + + self._n_shots = len(data[0]) + self._n_circuits = len(data) + + datum = np.array(data) + + if self._validate: + if datum.shape != (self._n_circuits, self._n_shots): + raise DataProcessorError( + f"The datum given to {self.__class__.__name__} does not convert " + "of an array with dimension (number of circuit, number of shots)." + ) + + return data + + def _reorder(self, unordered_data: np.array) -> np.array: + """Reorder the measured data according to the measurement sequence. + + Here, is assumed that the inner loop of the measurement is done over the circuits + and the outer loop is done over the shots. + """ + if unordered_data is None: + return unordered_data + + order_data = [] + + for shot_idx in range(self._n_shots): + for circuit_idx in range(self._n_circuits): + order_data.append(unordered_data[circuit_idx][shot_idx]) + + return np.array(order_data) + + +class RestlessToCounts(RestlessNode): + """Convert restless memory to counts. + + This node takes as input a list of lists where the sublist is the memory of + each measured circuit. The sublists therefore have a length given by the + number of shots. This data is reordered into a one dimensional array where + the element at index j was the jth measured shot. This node assumes that + a list of circuits :code:`[circ_1, cric_2, ..., circ_m]` is measured :code:`n_shots` + times according to the following order: + + .. parsed-literal:: + + [ + circuit 1 - shot 1, + circuit 2 - shot 1, + ... + circuit m - shot 1, + circuit 1 - shot 2, + circuit 2 - shot 2, + ... + circuit m - shot 2, + circuit 1 - shot 3, + ... + circuit m - shot n, + ] + + Once the shots have been ordered in this fashion the node compares each shot with the + previous shot. If they are the same then the shot corresponds to a 0, i.e. no state + change, and if they are different then the shot corresponds to a 1, i.e. there was + a state change. + """ + + def __init__(self, num_qubits: int, validate: bool = True): + """ + Args: + num_qubits: The number of qubits which is needed to construct the header needed + by :code:`qiskit.result.postprocess.format_counts_memory` to convert the memory + into a bit-string of counts. + validate: If set to False the DataAction will not validate its input. + """ + super().__init__(validate) + self._num_qubits = num_qubits + + def _process(self, data: np.array) -> np.array: + """Reorder the shots and assign values to them based on the previous outcome. + + Args: + data: An array representing the memory. + + Returns: + A counts dictionary processed according to the restless methodology. + """ + + # Step 1. Reorder the data. + memory = self._reorder(data) + + # Step 2. Do the restless classification into counts. + counts = [defaultdict(int) for _ in range(self._n_circuits)] + prev_shot = "0" * self._num_qubits + header = {"memory_slots": self._num_qubits} + + for idx, shot in enumerate(memory): + shot = format_counts_memory(shot, header) + + restless_adjusted_shot = RestlessToCounts._restless_classify(shot, prev_shot) + + circuit_idx = idx % self._n_circuits + + counts[circuit_idx][restless_adjusted_shot] += 1 + + prev_shot = shot + + return np.array([dict(counts_dict) for counts_dict in counts]) + + @staticmethod + def _restless_classify(shot: str, prev_shot: str) -> str: + """Adjust the measured shot based on the previous shot. + + Each bitstring of shot is compared to the previous bitstring. If both are equal + the restless adjusted bitstring is 0 (no state change) otherwise it is 1 (the + qubit changed state). This corresponds to taking the exclusive OR operation + between each bit and its previous outcome. + + Args: + shot: A measured shot as a binary string, e.g. "0110100". + prev_shot: The shot that was measured in the previous circuit. + + Returns: + The restless adjusted string computed by comparing the shot with the previous shot. + """ + restless_adjusted_bits = [] + + for idx, bit in enumerate(shot): + restless_adjusted_bits.append("0" if bit == prev_shot[idx] else "1") + + return "".join(restless_adjusted_bits) diff --git a/qiskit_experiments/library/characterization/fine_amplitude.py b/qiskit_experiments/library/characterization/fine_amplitude.py index 7cf6509fa6..e3e36632b9 100644 --- a/qiskit_experiments/library/characterization/fine_amplitude.py +++ b/qiskit_experiments/library/characterization/fine_amplitude.py @@ -22,8 +22,10 @@ from qiskit_experiments.framework import BaseExperiment, Options from qiskit_experiments.library.characterization.analysis import FineAmplitudeAnalysis +from qiskit_experiments.calibration_management.restless_mix_in import RestlessEnabledExperiment -class FineAmplitude(BaseExperiment): + +class FineAmplitude(RestlessEnabledExperiment): r"""Error amplifying fine amplitude calibration experiment. # section: overview diff --git a/qiskit_experiments/test/mock_iq_backend.py b/qiskit_experiments/test/mock_iq_backend.py index 6d471ada5d..d583c4669b 100644 --- a/qiskit_experiments/test/mock_iq_backend.py +++ b/qiskit_experiments/test/mock_iq_backend.py @@ -26,6 +26,147 @@ from qiskit_experiments.test.utils import FakeJob +class MockRestlessBackend(FakeOpenPulse2Q): + """An abstract backend for testing that can mock restless data.""" + + def __init__(self, rng_seed: int = 0): + """ + Initialize the backend. + """ + self._rng = np.random.default_rng(rng_seed) + self._precomputed_probabilities = None + super().__init__() + + def _default_options(self): + """Default options of the test backend.""" + return Options( + shots=1024, + meas_level=MeasLevel.CLASSIFIED, + meas_return="single", + ) + + @staticmethod + def _get_state_strings(n_qubits: int) -> List[str]: + """Generate all state strings for the system.""" + states, format_str = [], "{0:0" + str(n_qubits) + "b}" + + for state_num in range(2 ** n_qubits): + states.append(format_str.format(state_num)) + + return states + + @abstractmethod + def _compute_outcome_probabilities(self, circuits: List[QuantumCircuit]): + """Compute the probabilities of measuring 0 or 1 for each of the given + circuits based on the previous measurement shot. + + This methods computes the dictionary self._precomputed_probabilities where + the keys are a tuple consisting of the circuit index and the previous outcome, + e.g. "0" or "1" for a single qubit. The values are the corresponding probabilities. + + Args: + circuits: The circuits from which to compute the probabilities. + """ + + def run(self, run_input, **options): + """Run the restless backend.""" + + self.options.update_options(**options) + shots = self.options.get("shots") + meas_level = self.options.get("meas_level") + + result = { + "backend_name": f"{self.__class__.__name__}", + "backend_version": "0", + "qobj_id": 0, + "job_id": 0, + "success": True, + "results": [], + } + + self._compute_outcome_probabilities(run_input) + + num_qubits = run_input[0].num_qubits + + prev_outcome = "0" * num_qubits + state_strings = self._get_state_strings(num_qubits) + + # Setup the list of dicts where each dict corresponds to a circuit. + sorted_memory = [{"memory": [], "metadata": circ.metadata} for circ in run_input] + + for _ in range(shots): + for circ_idx, _ in enumerate(run_input): + prob = self._precomputed_probabilities[(circ_idx, prev_outcome)] + # Generate the next shot dependent on the pre-computed probabilities. + outcome = self._rng.choice(state_strings[:2], p=[1 - prob, prob]) + # Append the single shot to the memory of the corresponding circuit. + sorted_memory[circ_idx]["memory"].append(hex(int(outcome, 2))) + + prev_outcome = outcome + + for idx, circ in enumerate(run_input): + ones = sorted_memory[idx]["memory"].count("0x1") + run_result = { + "shots": shots, + "success": True, + "header": {"metadata": circ.metadata}, + "meas_level": meas_level, + "data": { + "counts": {"1": ones, "0": shots - ones}, + "memory": sorted_memory[idx]["memory"], + }, + } + + result["results"].append(run_result) + + return FakeJob(self, Result.from_dict(result)) + + +class MockRestlessFineAmp(MockRestlessBackend): + """A mock backend for restless fine amplitude calibration.""" + + def __init__( + self, angle_error: float, angle_per_gate: float, gate_name: str, rng_seed: int = 0 + ): + """Setup a mock backend to test the restless fine amplitude calibration. + + Args: + angle_error: The rotation error per gate. + angle_per_gate: The angle per gate. + gate_name: The name of the gate to find in the circuit. + rng_seed: The random bit generator seed. + """ + self.angle_error = angle_error + self._gate_name = gate_name + self._angle_per_gate = angle_per_gate + super().__init__(rng_seed=rng_seed) + + self.configuration().basis_gates.extend(["sx", "x"]) + + def _compute_outcome_probabilities(self, circuits: List[QuantumCircuit]): + """Compute the probabilities of being in the excited state or + ground state for all circuits.""" + + self._precomputed_probabilities = {} + + for idx, circuit in enumerate(circuits): + + n_ops = circuit.count_ops().get(self._gate_name, 0) + angle = n_ops * (self._angle_per_gate + self.angle_error) + + if self._gate_name != "sx": + angle += np.pi / 2 * circuit.count_ops().get("sx", 0) + + if self._gate_name != "x": + angle += np.pi * circuit.count_ops().get("x", 0) + + prob_0 = np.sin(angle / 2) ** 2 + prob_1 = 1 - prob_0 + + self._precomputed_probabilities[(idx, "00")] = prob_0 + self._precomputed_probabilities[(idx, "01")] = prob_1 + + class MockIQBackend(FakeOpenPulse2Q): """An abstract backend for testing that can mock IQ data.""" diff --git a/test/data_processing/test_nodes.py b/test/data_processing/test_nodes.py index c5534e713c..49f9588712 100644 --- a/test/data_processing/test_nodes.py +++ b/test/data_processing/test_nodes.py @@ -24,6 +24,7 @@ AverageData, MinMaxNormalize, Probability, + RestlessToCounts, ) from qiskit_experiments.framework.json import ExperimentDecoder, ExperimentEncoder from . import BaseDataProcessorTest @@ -377,3 +378,59 @@ def test_json(self): """Check if the node is serializable.""" node = Probability(outcome="00", alpha_prior=0.2) self.assertRoundTripSerializable(node, check_func=self.json_equiv) + + +class TestRestless(QiskitExperimentsTestCase): + """Test the restless measurements node.""" + + def test_restless_classify_1(self): + """Test the classification of restless shots for two single-qubit shots. + This example corresponds to running two single-qubit circuits without qubit reset where + the first and second circuit would be, e.g. an X gate and an identity gate, respectively. + We measure the qubit in the 1 state for the first circuit and measure 1 again for the + second circuit. The second shot is reclassified as a 0 since there was no state change.""" + previous_shot = "1" + shot = "1" + + restless_classified_shot = RestlessToCounts._restless_classify(shot, previous_shot) + self.assertAlmostEqual(restless_classified_shot, "0") + + def test_restless_classify_2(self): + """Test the classification of restless shots for two eight-qubit shots. + In this example we run two eight qubit circuits. The first circuit applies an + X, X, Id, Id, Id, X, X and Id gate, the second an Id, Id, X, Id, Id, X, Id and Id gate + to qubits one to eight, respectively.""" + previous_shot = "11000110" + shot = "11100010" + + restless_classified_shot = RestlessToCounts._restless_classify(shot, previous_shot) + self.assertAlmostEqual(restless_classified_shot, "00100100") + + def test_restless_process_1(self): + """Test that a single-qubit restless memory is correctly post-processed. + This example corresponds to running an X gate and a SX gate with four shots + in an ideal restless setting.""" + n_qubits = 1 + node = RestlessToCounts(n_qubits) + + data = [["0x1", "0x1", "0x0", "0x0"], ["0x0", "0x1", "0x1", "0x0"]] + processed_data = node(data=np.array(data)) + # time-ordered data: ["1", "0", "1", "1", "0", "1", "0", "0"] + # classification: ["1", "1", "1", "0", "1", "1", "1", "0"] + expected_data = np.array([{"1": 4}, {"1": 2, "0": 2}]) + self.assertTrue(processed_data.all() == expected_data.all()) + + def test_restless_process_2(self): + """Test if a two-qubit restless memory is correctly post-processed. + This example corresponds to running two two-qubit circuits in an ideal restless setting. + The first circuit applies an X gate to the first and a SX gate to the second qubit. The + second circuit applies two identity gates.""" + n_qubits = 2 + node = RestlessToCounts(n_qubits) + + data = [["0x3", "0x1", "0x2", "0x0"], ["0x3", "0x1", "0x2", "0x0"]] + processed_data = node(data=np.array(data)) + # time-ordered data: ["11", "11", "01", "01", "10", "10", "00", "00"] + # classification: ["11", "00", "10", "00", "11", "00", "10", "00"] + expected_data = np.array([{"10": 2, "11": 2}, {"00": 4}]) + self.assertTrue(processed_data.all() == expected_data.all()) diff --git a/test/data_processing/test_restless_experiment.py b/test/data_processing/test_restless_experiment.py new file mode 100644 index 0000000000..a8e7e24ec3 --- /dev/null +++ b/test/data_processing/test_restless_experiment.py @@ -0,0 +1,86 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Test restless fine amplitude characterization and calibration experiments.""" +from test.base import QiskitExperimentsTestCase + +import numpy as np +from ddt import ddt, data + +from qiskit_experiments.library import ( + FineXAmplitude, +) + +from qiskit_experiments.test.mock_iq_backend import MockRestlessFineAmp + +from qiskit_experiments.data_processing.data_processor import DataProcessor +from qiskit_experiments.data_processing.nodes import Probability, RestlessToCounts + + +@ddt +class TestFineAmpEndToEndRestless(QiskitExperimentsTestCase): + """Test the fine amplitude experiment in a restless measurement setting.""" + + @data(-0.03, -0.02, -0.01, 0.02, 0.04) + def test_end_to_end_restless(self, pi_ratio): + """Test the restless experiment end to end.""" + + amp_exp = FineXAmplitude(0) + amp_exp.set_run_options(rep_delay=1e-6, meas_level=2, memory=True, init_qubits=False) + # restless_data_processor = DataProcessor( + # "memory", + # [ + # RestlessToCounts(1), + # Probability("1"), + # ], + # ) + # amp_exp.analysis.set_options(data_processor=restless_data_processor) + amp_exp.enable_restless(1e-6, "1") + + error = -np.pi * pi_ratio + backend = MockRestlessFineAmp(error, np.pi, "x") + + expdata = amp_exp.run(backend) + self.assertExperimentDone(expdata) + result = expdata.analysis_results(1) + d_theta = result.value.n + + self.assertAlmostEqual(d_theta, error, delta=0.01) + self.assertEqual(result.quality, "good") + + # check that the fit amplitude is almost 1 as expected. + amp_fit = expdata.analysis_results(0).value[0] + self.assertAlmostEqual(amp_fit, 1.0, delta=0.02) + + @data(-0.02, 0.03, 0.04) + def test_end_to_end_restless_standard_processor(self, pi_ratio): + """Test the restless experiment with a standard processor end to end.""" + + amp_exp = FineXAmplitude(0) + # standard data processor. + standard_processor = DataProcessor("counts", [Probability("1")]) + amp_exp.analysis.set_options(data_processor=standard_processor) + amp_exp.set_run_options(rep_delay=1e-6, meas_level=2, memory=True, init_qubits=False) + + error = -np.pi * pi_ratio + backend = MockRestlessFineAmp(error, np.pi, "x") + + expdata = amp_exp.run(backend) + self.assertExperimentDone(expdata) + result = expdata.analysis_results(1) + d_theta = result.value.n + + self.assertTrue(abs(d_theta - error) > 0.01) + + # check that the fit amplitude is much smaller than 1. + amp_fit = expdata.analysis_results(0).value[0] + self.assertTrue(amp_fit < 0.05) \ No newline at end of file From 966050935358c8b7c808b741ddd4eb1ac70082ce Mon Sep 17 00:00:00 2001 From: catornow Date: Fri, 25 Feb 2022 10:59:03 +0100 Subject: [PATCH 02/68] Modified the draft --- .../calibration_management/restless_mix_in.py | 18 +++++++++++++----- .../test_restless_experiment.py | 13 +++---------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/qiskit_experiments/calibration_management/restless_mix_in.py b/qiskit_experiments/calibration_management/restless_mix_in.py index a29682af6c..6b743e1555 100644 --- a/qiskit_experiments/calibration_management/restless_mix_in.py +++ b/qiskit_experiments/calibration_management/restless_mix_in.py @@ -23,18 +23,20 @@ class RestlessEnabledExperiment(BaseExperiment, ABC): """Restless enabled class.""" - def enable_restless(self, rep_delay: float, outcome: str): + def enable_restless(self, rep_delay: float): """Enables a restless experiment by setting the restless run options and the restless data processor. Args: rep_delay: The repetition delay. - outcome """ - # Todo: outcome can be extracted from the analysis options. - # if self._is_restless(rep_delay): + outcome = self.analysis.options.get("outcome", "1" * self.num_qubits) self.set_run_options(rep_delay=rep_delay, init_qubit=False, memory=True, meas_level=2) - self.analysis.set_options(data_processor=self._get_restless_processor(outcome * self.num_qubits)) + self.analysis.set_options(data_processor=self._get_restless_processor(outcome)) + # print([ + # self.backend.properties().qubit_property(physical_qubit)["T1"][0] + # for physical_qubit in self.physical_qubits + # ]) def _get_restless_processor(self, outcome: str) -> DataProcessor: return DataProcessor( @@ -51,6 +53,7 @@ def _is_restless(self, rep_delay: float) -> bool: Args: rep_delay: The repetition delay. + Returns: True if the repetition delay is much smaller than the qubit T1 times. """ @@ -63,6 +66,11 @@ def _is_restless(self, rep_delay: float) -> bool: for physical_qubit in self.physical_qubits ] + t1_values = [ + self.backend.properties().qubit_property(physical_qubit)["T1"][0] + for physical_qubit in self.physical_qubits + ] + if all(rep_delay / t1_value < 1. for t1_value in t1_values): if esp_enabled: raise DataProcessorError( diff --git a/test/data_processing/test_restless_experiment.py b/test/data_processing/test_restless_experiment.py index a8e7e24ec3..46a3407a14 100644 --- a/test/data_processing/test_restless_experiment.py +++ b/test/data_processing/test_restless_experiment.py @@ -35,16 +35,8 @@ def test_end_to_end_restless(self, pi_ratio): """Test the restless experiment end to end.""" amp_exp = FineXAmplitude(0) - amp_exp.set_run_options(rep_delay=1e-6, meas_level=2, memory=True, init_qubits=False) - # restless_data_processor = DataProcessor( - # "memory", - # [ - # RestlessToCounts(1), - # Probability("1"), - # ], - # ) - # amp_exp.analysis.set_options(data_processor=restless_data_processor) - amp_exp.enable_restless(1e-6, "1") + # enable a restless measurement setting. + amp_exp.enable_restless(rep_delay=1e-6) error = -np.pi * pi_ratio backend = MockRestlessFineAmp(error, np.pi, "x") @@ -69,6 +61,7 @@ def test_end_to_end_restless_standard_processor(self, pi_ratio): # standard data processor. standard_processor = DataProcessor("counts", [Probability("1")]) amp_exp.analysis.set_options(data_processor=standard_processor) + # set restless run options. amp_exp.set_run_options(rep_delay=1e-6, meas_level=2, memory=True, init_qubits=False) error = -np.pi * pi_ratio From b7edd2a5798b6466dff72641225e42ed93eb6da0 Mon Sep 17 00:00:00 2001 From: catornow Date: Fri, 25 Feb 2022 14:42:58 +0100 Subject: [PATCH 03/68] Moved restless_experiment to framework folder and modified it --- qiskit_experiments/data_processing/nodes.py | 100 +++++++++--------- .../restless_experiment.py} | 0 .../characterization/fine_amplitude.py | 4 +- .../test_restless_experiment.py | 4 +- 4 files changed, 55 insertions(+), 53 deletions(-) rename qiskit_experiments/{calibration_management/restless_mix_in.py => framework/restless_experiment.py} (100%) diff --git a/qiskit_experiments/data_processing/nodes.py b/qiskit_experiments/data_processing/nodes.py index 42509419cf..e3443d36a7 100644 --- a/qiskit_experiments/data_processing/nodes.py +++ b/qiskit_experiments/data_processing/nodes.py @@ -16,7 +16,7 @@ from abc import ABC from enum import Enum from numbers import Number -from typing import Union, Sequence, Any +from typing import Union, Sequence from collections import defaultdict import numpy as np @@ -610,19 +610,48 @@ class RestlessNode(DataAction, ABC): that are implemented as sub-classes of `RestlessNode`. Restless experiments provide a fast alternative for several calibration and characterization tasks, for details see https://arxiv.org/pdf/2202.06981.pdf. + + This node takes as input an array of arrays (2d array) where the sub-arrays are + the memories of each measured circuit. The sub-arrays therefore have a length + given by the number of shots. This data is reordered into a one dimensional array where + the element at index j was the jth measured shot. This node assumes by default that + a list of circuits :code:`[circ_1, cric_2, ..., circ_m]` is measured :code:`n_shots` + times according to the following order: + + .. parsed-literal:: + + [ + circuit 1 - shot 1, + circuit 2 - shot 1, + ... + circuit m - shot 1, + circuit 1 - shot 2, + circuit 2 - shot 2, + ... + circuit m - shot 2, + circuit 1 - shot 3, + ... + circuit m - shot n, + ] + + Once the shots have been ordered in this fashion the data can be post-processed. """ - def __init__(self, validate: bool = True): # , circuits_first: bool = True): + def __init__(self, validate: bool = True, circuits_first: bool = True): """Initialize a restless node. Args: validate: If set to True the node will validate its input. + circuits_first: If set to True the node assumes that the backend + subsequently first measures all circuits and then repeats this + n times, where n is the total number of shots. """ super().__init__(validate) self._n_shots = None self._n_circuits = None + self._circuits_first = circuits_first - def _format_data(self, data: Any) -> Any: + def _format_data(self, data: np.ndarray) -> np.ndarray: """Convert the data to an array. This node will also set all the attributes needed to process the data such as @@ -641,10 +670,8 @@ def _format_data(self, data: Any) -> Any: self._n_shots = len(data[0]) self._n_circuits = len(data) - datum = np.array(data) - if self._validate: - if datum.shape != (self._n_circuits, self._n_shots): + if data.shape != (self._n_circuits, self._n_shots): raise DataProcessorError( f"The datum given to {self.__class__.__name__} does not convert " "of an array with dimension (number of circuit, number of shots)." @@ -652,68 +679,43 @@ def _format_data(self, data: Any) -> Any: return data - def _reorder(self, unordered_data: np.array) -> np.array: + def _reorder(self, unordered_data: np.ndarray) -> np.ndarray: """Reorder the measured data according to the measurement sequence. - Here, is assumed that the inner loop of the measurement is done over the circuits - and the outer loop is done over the shots. + Here, by default, it is assumed that the inner loop of the measurement + is done over the circuits and the outer loop is done over the shots. + The returned data is a one-dimensional array of time-ordered shots. """ if unordered_data is None: return unordered_data - order_data = [] - - for shot_idx in range(self._n_shots): - for circuit_idx in range(self._n_circuits): - order_data.append(unordered_data[circuit_idx][shot_idx]) - - return np.array(order_data) + if self._circuits_first: + return unordered_data.T.flatten() + else: + return unordered_data.flatten() class RestlessToCounts(RestlessNode): - """Convert restless memory to counts. - - This node takes as input a list of lists where the sublist is the memory of - each measured circuit. The sublists therefore have a length given by the - number of shots. This data is reordered into a one dimensional array where - the element at index j was the jth measured shot. This node assumes that - a list of circuits :code:`[circ_1, cric_2, ..., circ_m]` is measured :code:`n_shots` - times according to the following order: - - .. parsed-literal:: + """Post-process restless data and convert restless memory to counts. - [ - circuit 1 - shot 1, - circuit 2 - shot 1, - ... - circuit m - shot 1, - circuit 1 - shot 2, - circuit 2 - shot 2, - ... - circuit m - shot 2, - circuit 1 - shot 3, - ... - circuit m - shot n, - ] - - Once the shots have been ordered in this fashion the node compares each shot with the - previous shot. If they are the same then the shot corresponds to a 0, i.e. no state - change, and if they are different then the shot corresponds to a 1, i.e. there was - a state change. + This node first orders the measured restless data according to the measurement + sequence and then compares each bit in a shot with its value in the previous shot. + If they are the same then the bit corresponds to a 0, i.e. no state change, and if + they are different then the bit corresponds to a 1, i.e. there was a state change. """ def __init__(self, num_qubits: int, validate: bool = True): """ Args: num_qubits: The number of qubits which is needed to construct the header needed - by :code:`qiskit.result.postprocess.format_counts_memory` to convert the memory - into a bit-string of counts. + by :code:`qiskit.result.postprocess.format_counts_memory` to convert the memory + into a bit-string of counts. validate: If set to False the DataAction will not validate its input. """ super().__init__(validate) self._num_qubits = num_qubits - def _process(self, data: np.array) -> np.array: + def _process(self, data: np.ndarray) -> np.ndarray: """Reorder the shots and assign values to them based on the previous outcome. Args: @@ -748,8 +750,8 @@ def _process(self, data: np.array) -> np.array: def _restless_classify(shot: str, prev_shot: str) -> str: """Adjust the measured shot based on the previous shot. - Each bitstring of shot is compared to the previous bitstring. If both are equal - the restless adjusted bitstring is 0 (no state change) otherwise it is 1 (the + Each bit in shot is compared to its value in the previous shot. If both are equal + the restless adjusted bit is 0 (no state change) otherwise it is 1 (the qubit changed state). This corresponds to taking the exclusive OR operation between each bit and its previous outcome. diff --git a/qiskit_experiments/calibration_management/restless_mix_in.py b/qiskit_experiments/framework/restless_experiment.py similarity index 100% rename from qiskit_experiments/calibration_management/restless_mix_in.py rename to qiskit_experiments/framework/restless_experiment.py diff --git a/qiskit_experiments/library/characterization/fine_amplitude.py b/qiskit_experiments/library/characterization/fine_amplitude.py index e3e36632b9..bae74acb7b 100644 --- a/qiskit_experiments/library/characterization/fine_amplitude.py +++ b/qiskit_experiments/library/characterization/fine_amplitude.py @@ -19,10 +19,10 @@ from qiskit.circuit import Gate from qiskit.circuit.library import XGate, SXGate from qiskit.providers.backend import Backend -from qiskit_experiments.framework import BaseExperiment, Options +from qiskit_experiments.framework import Options from qiskit_experiments.library.characterization.analysis import FineAmplitudeAnalysis -from qiskit_experiments.calibration_management.restless_mix_in import RestlessEnabledExperiment +from qiskit_experiments.framework.restless_experiment import RestlessEnabledExperiment class FineAmplitude(RestlessEnabledExperiment): diff --git a/test/data_processing/test_restless_experiment.py b/test/data_processing/test_restless_experiment.py index 46a3407a14..6679b051b8 100644 --- a/test/data_processing/test_restless_experiment.py +++ b/test/data_processing/test_restless_experiment.py @@ -30,7 +30,7 @@ class TestFineAmpEndToEndRestless(QiskitExperimentsTestCase): """Test the fine amplitude experiment in a restless measurement setting.""" - @data(-0.03, -0.02, -0.01, 0.02, 0.04) + @data(-0.03, -0.01, 0.02, 0.04) def test_end_to_end_restless(self, pi_ratio): """Test the restless experiment end to end.""" @@ -53,7 +53,7 @@ def test_end_to_end_restless(self, pi_ratio): amp_fit = expdata.analysis_results(0).value[0] self.assertAlmostEqual(amp_fit, 1.0, delta=0.02) - @data(-0.02, 0.03, 0.04) + @data(-0.02, 0.04) def test_end_to_end_restless_standard_processor(self, pi_ratio): """Test the restless experiment with a standard processor end to end.""" From d460e70eb96dcad0ed1caa28eda702d65ff0ee42 Mon Sep 17 00:00:00 2001 From: catornow Date: Fri, 25 Feb 2022 16:21:45 +0100 Subject: [PATCH 04/68] Implemented is_restless check --- .../framework/restless_experiment.py | 76 +++++++++++-------- .../test_restless_experiment.py | 19 +++-- 2 files changed, 54 insertions(+), 41 deletions(-) diff --git a/qiskit_experiments/framework/restless_experiment.py b/qiskit_experiments/framework/restless_experiment.py index 6b743e1555..c5bf9a2125 100644 --- a/qiskit_experiments/framework/restless_experiment.py +++ b/qiskit_experiments/framework/restless_experiment.py @@ -10,9 +10,13 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Restless mix in class.""" +"""Restless experiment class.""" from abc import ABC +from typing import Union + +from qiskit.providers import BaseBackend +from qiskit.test.mock.fake_backend import FakeBackend from qiskit_experiments.framework.base_experiment import BaseExperiment from qiskit_experiments.data_processing.data_processor import DataProcessor @@ -21,57 +25,67 @@ class RestlessEnabledExperiment(BaseExperiment, ABC): - """Restless enabled class.""" + """Restless experiment class.""" - def enable_restless(self, rep_delay: float): + def enable_restless(self, rep_delay: float, backend: Union[BaseBackend, FakeBackend]): """Enables a restless experiment by setting the restless run options and the restless data processor. Args: rep_delay: The repetition delay. + backend: The experiment backend. + + Raises: + DataProcessorError: if the rep_delay is equal to or greater than the + T1 time of one of the physical qubits in the experiment. """ + if self._is_restless(rep_delay, backend): + self.set_run_options(rep_delay=rep_delay, init_qubit=False, memory=True, meas_level=2) + if self.analysis.options.get("data_processor", None): + pass + else: + self.analysis.set_options(data_processor=self._get_restless_processor()) + else: + raise DataProcessorError( + f"The specified repetition delay {rep_delay} is equal to or greater" + f"than the T1 time of one of the physical qubits" + f"{self.physical_qubits} in the experiment. Consider choosing" + f"a smaller repetition delay for the restless experiment." + ) + + def _get_restless_processor(self) -> DataProcessor: + """Returns the restless experiments data processor.""" outcome = self.analysis.options.get("outcome", "1" * self.num_qubits) - self.set_run_options(rep_delay=rep_delay, init_qubit=False, memory=True, meas_level=2) - self.analysis.set_options(data_processor=self._get_restless_processor(outcome)) - # print([ - # self.backend.properties().qubit_property(physical_qubit)["T1"][0] - # for physical_qubit in self.physical_qubits - # ]) - - def _get_restless_processor(self, outcome: str) -> DataProcessor: return DataProcessor( - "memory", - [ - nodes.RestlessToCounts(self.num_qubits), - nodes.Probability(outcome), - ], - ) - - def _is_restless(self, rep_delay: float) -> bool: - """Checks if the specified repetition delay is much smaller than the T1 + "memory", + [ + nodes.RestlessToCounts(self.num_qubits), + nodes.Probability(outcome), + ], + ) + + def _is_restless(self, rep_delay: float, backend: Union[BaseBackend, FakeBackend]) -> bool: + """Checks if the specified repetition delay is smaller than the T1 times of the physical qubits in the experiment. Args: rep_delay: The repetition delay. + backend: The experiment backend. Returns: - True if the repetition delay is much smaller than the qubit T1 times. - """ - - esp_enabled = self.analysis.options.get("use_measure_esp", False) + True if the repetition delay is smaller than the qubit T1 times. - # Todo: include properties in mock backend. - t1_values = [ - self.backend.properties().qubits[physical_qubit][0].value * 1e-6 - for physical_qubit in self.physical_qubits - ] + Raises: + DataProcessorError: if excited state promotion readout is enabled in the restless setting. + """ t1_values = [ - self.backend.properties().qubit_property(physical_qubit)["T1"][0] + backend.properties().qubit_property(physical_qubit)["T1"][0] for physical_qubit in self.physical_qubits ] - if all(rep_delay / t1_value < 1. for t1_value in t1_values): + if all(rep_delay / t1_value < 1.0 for t1_value in t1_values): + esp_enabled = self.analysis.options.get("use_measure_esp", False) if esp_enabled: raise DataProcessorError( "Restless experiments are not compatible with the excited " diff --git a/test/data_processing/test_restless_experiment.py b/test/data_processing/test_restless_experiment.py index 6679b051b8..f476def5c7 100644 --- a/test/data_processing/test_restless_experiment.py +++ b/test/data_processing/test_restless_experiment.py @@ -34,13 +34,13 @@ class TestFineAmpEndToEndRestless(QiskitExperimentsTestCase): def test_end_to_end_restless(self, pi_ratio): """Test the restless experiment end to end.""" - amp_exp = FineXAmplitude(0) - # enable a restless measurement setting. - amp_exp.enable_restless(rep_delay=1e-6) - error = -np.pi * pi_ratio backend = MockRestlessFineAmp(error, np.pi, "x") + amp_exp = FineXAmplitude(0) + # enable a restless measurement setting. + amp_exp.enable_restless(rep_delay=1e-6, backend=backend) + expdata = amp_exp.run(backend) self.assertExperimentDone(expdata) result = expdata.analysis_results(1) @@ -57,15 +57,14 @@ def test_end_to_end_restless(self, pi_ratio): def test_end_to_end_restless_standard_processor(self, pi_ratio): """Test the restless experiment with a standard processor end to end.""" + error = -np.pi * pi_ratio + backend = MockRestlessFineAmp(error, np.pi, "x") + amp_exp = FineXAmplitude(0) # standard data processor. standard_processor = DataProcessor("counts", [Probability("1")]) amp_exp.analysis.set_options(data_processor=standard_processor) - # set restless run options. - amp_exp.set_run_options(rep_delay=1e-6, meas_level=2, memory=True, init_qubits=False) - - error = -np.pi * pi_ratio - backend = MockRestlessFineAmp(error, np.pi, "x") + amp_exp.enable_restless(rep_delay=1e-6, backend=backend) expdata = amp_exp.run(backend) self.assertExperimentDone(expdata) @@ -76,4 +75,4 @@ def test_end_to_end_restless_standard_processor(self, pi_ratio): # check that the fit amplitude is much smaller than 1. amp_fit = expdata.analysis_results(0).value[0] - self.assertTrue(amp_fit < 0.05) \ No newline at end of file + self.assertTrue(amp_fit < 0.05) From 3e8c5f34f59ffb532a17317c90963b88a756e62e Mon Sep 17 00:00:00 2001 From: catornow Date: Fri, 25 Feb 2022 16:31:00 +0100 Subject: [PATCH 05/68] Removed restless node tests for this PR --- test/data_processing/test_nodes.py | 57 ------------------------------ 1 file changed, 57 deletions(-) diff --git a/test/data_processing/test_nodes.py b/test/data_processing/test_nodes.py index 49f9588712..c5534e713c 100644 --- a/test/data_processing/test_nodes.py +++ b/test/data_processing/test_nodes.py @@ -24,7 +24,6 @@ AverageData, MinMaxNormalize, Probability, - RestlessToCounts, ) from qiskit_experiments.framework.json import ExperimentDecoder, ExperimentEncoder from . import BaseDataProcessorTest @@ -378,59 +377,3 @@ def test_json(self): """Check if the node is serializable.""" node = Probability(outcome="00", alpha_prior=0.2) self.assertRoundTripSerializable(node, check_func=self.json_equiv) - - -class TestRestless(QiskitExperimentsTestCase): - """Test the restless measurements node.""" - - def test_restless_classify_1(self): - """Test the classification of restless shots for two single-qubit shots. - This example corresponds to running two single-qubit circuits without qubit reset where - the first and second circuit would be, e.g. an X gate and an identity gate, respectively. - We measure the qubit in the 1 state for the first circuit and measure 1 again for the - second circuit. The second shot is reclassified as a 0 since there was no state change.""" - previous_shot = "1" - shot = "1" - - restless_classified_shot = RestlessToCounts._restless_classify(shot, previous_shot) - self.assertAlmostEqual(restless_classified_shot, "0") - - def test_restless_classify_2(self): - """Test the classification of restless shots for two eight-qubit shots. - In this example we run two eight qubit circuits. The first circuit applies an - X, X, Id, Id, Id, X, X and Id gate, the second an Id, Id, X, Id, Id, X, Id and Id gate - to qubits one to eight, respectively.""" - previous_shot = "11000110" - shot = "11100010" - - restless_classified_shot = RestlessToCounts._restless_classify(shot, previous_shot) - self.assertAlmostEqual(restless_classified_shot, "00100100") - - def test_restless_process_1(self): - """Test that a single-qubit restless memory is correctly post-processed. - This example corresponds to running an X gate and a SX gate with four shots - in an ideal restless setting.""" - n_qubits = 1 - node = RestlessToCounts(n_qubits) - - data = [["0x1", "0x1", "0x0", "0x0"], ["0x0", "0x1", "0x1", "0x0"]] - processed_data = node(data=np.array(data)) - # time-ordered data: ["1", "0", "1", "1", "0", "1", "0", "0"] - # classification: ["1", "1", "1", "0", "1", "1", "1", "0"] - expected_data = np.array([{"1": 4}, {"1": 2, "0": 2}]) - self.assertTrue(processed_data.all() == expected_data.all()) - - def test_restless_process_2(self): - """Test if a two-qubit restless memory is correctly post-processed. - This example corresponds to running two two-qubit circuits in an ideal restless setting. - The first circuit applies an X gate to the first and a SX gate to the second qubit. The - second circuit applies two identity gates.""" - n_qubits = 2 - node = RestlessToCounts(n_qubits) - - data = [["0x3", "0x1", "0x2", "0x0"], ["0x3", "0x1", "0x2", "0x0"]] - processed_data = node(data=np.array(data)) - # time-ordered data: ["11", "11", "01", "01", "10", "10", "00", "00"] - # classification: ["11", "00", "10", "00", "11", "00", "10", "00"] - expected_data = np.array([{"10": 2, "11": 2}, {"00": 4}]) - self.assertTrue(processed_data.all() == expected_data.all()) From 6ef6690228823a8ba2f16c2622cecdbacf2e0f61 Mon Sep 17 00:00:00 2001 From: catornow Date: Fri, 25 Feb 2022 16:32:31 +0100 Subject: [PATCH 06/68] Small modification --- test/data_processing/test_restless_experiment.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/data_processing/test_restless_experiment.py b/test/data_processing/test_restless_experiment.py index f476def5c7..9f1b4ec735 100644 --- a/test/data_processing/test_restless_experiment.py +++ b/test/data_processing/test_restless_experiment.py @@ -64,6 +64,7 @@ def test_end_to_end_restless_standard_processor(self, pi_ratio): # standard data processor. standard_processor = DataProcessor("counts", [Probability("1")]) amp_exp.analysis.set_options(data_processor=standard_processor) + # enable a restless measurement setting. amp_exp.enable_restless(rep_delay=1e-6, backend=backend) expdata = amp_exp.run(backend) From 7c9e1e8af7c6b2b9b81c97156b4b59b4ce12b0d9 Mon Sep 17 00:00:00 2001 From: catornow Date: Fri, 25 Feb 2022 16:39:47 +0100 Subject: [PATCH 07/68] Black --- qiskit_experiments/test/mock_iq_backend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit_experiments/test/mock_iq_backend.py b/qiskit_experiments/test/mock_iq_backend.py index d583c4669b..dddc2f7ef0 100644 --- a/qiskit_experiments/test/mock_iq_backend.py +++ b/qiskit_experiments/test/mock_iq_backend.py @@ -50,7 +50,7 @@ def _get_state_strings(n_qubits: int) -> List[str]: """Generate all state strings for the system.""" states, format_str = [], "{0:0" + str(n_qubits) + "b}" - for state_num in range(2 ** n_qubits): + for state_num in range(2**n_qubits): states.append(format_str.format(state_num)) return states @@ -126,7 +126,7 @@ class MockRestlessFineAmp(MockRestlessBackend): """A mock backend for restless fine amplitude calibration.""" def __init__( - self, angle_error: float, angle_per_gate: float, gate_name: str, rng_seed: int = 0 + self, angle_error: float, angle_per_gate: float, gate_name: str, rng_seed: int = 0 ): """Setup a mock backend to test the restless fine amplitude calibration. From 271ce989b7017de33dd15b7ea1b9388f2d6b003d Mon Sep 17 00:00:00 2001 From: catornow Date: Fri, 25 Feb 2022 18:57:10 +0100 Subject: [PATCH 08/68] Fixed Lint error --- test/data_processing/test_restless_experiment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data_processing/test_restless_experiment.py b/test/data_processing/test_restless_experiment.py index 9f1b4ec735..fc5ffe3d69 100644 --- a/test/data_processing/test_restless_experiment.py +++ b/test/data_processing/test_restless_experiment.py @@ -23,7 +23,7 @@ from qiskit_experiments.test.mock_iq_backend import MockRestlessFineAmp from qiskit_experiments.data_processing.data_processor import DataProcessor -from qiskit_experiments.data_processing.nodes import Probability, RestlessToCounts +from qiskit_experiments.data_processing.nodes import Probability @ddt From bf4f9817a45b9b4b2bea005d89a47f7be968de51 Mon Sep 17 00:00:00 2001 From: catornow Date: Mon, 28 Feb 2022 11:55:00 +0100 Subject: [PATCH 09/68] Removed backend arg --- qiskit_experiments/framework/restless_experiment.py | 12 +++++------- test/data_processing/test_restless_experiment.py | 8 ++++---- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/qiskit_experiments/framework/restless_experiment.py b/qiskit_experiments/framework/restless_experiment.py index c5bf9a2125..d859b6ef71 100644 --- a/qiskit_experiments/framework/restless_experiment.py +++ b/qiskit_experiments/framework/restless_experiment.py @@ -27,19 +27,18 @@ class RestlessEnabledExperiment(BaseExperiment, ABC): """Restless experiment class.""" - def enable_restless(self, rep_delay: float, backend: Union[BaseBackend, FakeBackend]): + def enable_restless(self, rep_delay: float): """Enables a restless experiment by setting the restless run options and the restless data processor. Args: rep_delay: The repetition delay. - backend: The experiment backend. Raises: DataProcessorError: if the rep_delay is equal to or greater than the T1 time of one of the physical qubits in the experiment. """ - if self._is_restless(rep_delay, backend): + if self._is_restless(rep_delay): self.set_run_options(rep_delay=rep_delay, init_qubit=False, memory=True, meas_level=2) if self.analysis.options.get("data_processor", None): pass @@ -64,13 +63,12 @@ def _get_restless_processor(self) -> DataProcessor: ], ) - def _is_restless(self, rep_delay: float, backend: Union[BaseBackend, FakeBackend]) -> bool: + def _is_restless(self, rep_delay: float) -> bool: """Checks if the specified repetition delay is smaller than the T1 times of the physical qubits in the experiment. Args: rep_delay: The repetition delay. - backend: The experiment backend. Returns: True if the repetition delay is smaller than the qubit T1 times. @@ -80,12 +78,12 @@ def _is_restless(self, rep_delay: float, backend: Union[BaseBackend, FakeBackend """ t1_values = [ - backend.properties().qubit_property(physical_qubit)["T1"][0] + self.backend.properties().qubit_property(physical_qubit)["T1"][0] for physical_qubit in self.physical_qubits ] if all(rep_delay / t1_value < 1.0 for t1_value in t1_values): - esp_enabled = self.analysis.options.get("use_measure_esp", False) + esp_enabled = self.run_options.get("use_measure_esp", False) if esp_enabled: raise DataProcessorError( "Restless experiments are not compatible with the excited " diff --git a/test/data_processing/test_restless_experiment.py b/test/data_processing/test_restless_experiment.py index fc5ffe3d69..2d5df924b8 100644 --- a/test/data_processing/test_restless_experiment.py +++ b/test/data_processing/test_restless_experiment.py @@ -37,9 +37,9 @@ def test_end_to_end_restless(self, pi_ratio): error = -np.pi * pi_ratio backend = MockRestlessFineAmp(error, np.pi, "x") - amp_exp = FineXAmplitude(0) + amp_exp = FineXAmplitude(0, backend) # enable a restless measurement setting. - amp_exp.enable_restless(rep_delay=1e-6, backend=backend) + amp_exp.enable_restless(rep_delay=1e-6) expdata = amp_exp.run(backend) self.assertExperimentDone(expdata) @@ -60,12 +60,12 @@ def test_end_to_end_restless_standard_processor(self, pi_ratio): error = -np.pi * pi_ratio backend = MockRestlessFineAmp(error, np.pi, "x") - amp_exp = FineXAmplitude(0) + amp_exp = FineXAmplitude(0, backend) # standard data processor. standard_processor = DataProcessor("counts", [Probability("1")]) amp_exp.analysis.set_options(data_processor=standard_processor) # enable a restless measurement setting. - amp_exp.enable_restless(rep_delay=1e-6, backend=backend) + amp_exp.enable_restless(rep_delay=1e-6) expdata = amp_exp.run(backend) self.assertExperimentDone(expdata) From cfcd6293412661db7914383aa111bc33ed28fe17 Mon Sep 17 00:00:00 2001 From: catornow Date: Mon, 28 Feb 2022 13:02:09 +0100 Subject: [PATCH 10/68] Aligned code with current main --- qiskit_experiments/data_processing/nodes.py | 33 ++++++++++-- test/data_processing/test_nodes.py | 57 +++++++++++++++++++++ 2 files changed, 85 insertions(+), 5 deletions(-) diff --git a/qiskit_experiments/data_processing/nodes.py b/qiskit_experiments/data_processing/nodes.py index e3443d36a7..b5ed8503a9 100644 --- a/qiskit_experiments/data_processing/nodes.py +++ b/qiskit_experiments/data_processing/nodes.py @@ -601,6 +601,25 @@ class ProjectorType(Enum): IMAG = ToImag +class ShotOrder(Enum): + """Shot order allowed values. + + Generally, there are two possible modes in which a backend measures m + circuits with n shots: + - In the "circuit_first" mode, the backend subsequently first measures + all m circuits and then repeats this n times. + - In the "shot_first" mode, the backend first measures the 1st circuit + n times, then the 2nd circuit n times, and it proceeds with the remaining + circuits in the same way until it measures the m-th circuit n times. + + The current default mode of IBM Quantum devices is "circuit_first". + """ + + # pylint: disable=invalid-name + circuit_first = "c" + shot_first = "s" + + class RestlessNode(DataAction, ABC): """An abstract node for restless data processing nodes. @@ -637,19 +656,23 @@ class RestlessNode(DataAction, ABC): Once the shots have been ordered in this fashion the data can be post-processed. """ - def __init__(self, validate: bool = True, circuits_first: bool = True): + def __init__( + self, validate: bool = True, memory_allocation: ShotOrder = ShotOrder.circuit_first + ): """Initialize a restless node. Args: validate: If set to True the node will validate its input. - circuits_first: If set to True the node assumes that the backend + memory_allocation: If set to "c" the node assumes that the backend subsequently first measures all circuits and then repeats this - n times, where n is the total number of shots. + n times, where n is the total number of shots. The default value + is "c". If set to "s" it is assumed that the backend subsequently + measures each circuit n times. """ super().__init__(validate) self._n_shots = None self._n_circuits = None - self._circuits_first = circuits_first + self._memory_allocation = memory_allocation def _format_data(self, data: np.ndarray) -> np.ndarray: """Convert the data to an array. @@ -689,7 +712,7 @@ def _reorder(self, unordered_data: np.ndarray) -> np.ndarray: if unordered_data is None: return unordered_data - if self._circuits_first: + if self._memory_allocation == ShotOrder.circuit_first: return unordered_data.T.flatten() else: return unordered_data.flatten() diff --git a/test/data_processing/test_nodes.py b/test/data_processing/test_nodes.py index c5534e713c..26ddf8334e 100644 --- a/test/data_processing/test_nodes.py +++ b/test/data_processing/test_nodes.py @@ -24,6 +24,7 @@ AverageData, MinMaxNormalize, Probability, + RestlessToCounts, ) from qiskit_experiments.framework.json import ExperimentDecoder, ExperimentEncoder from . import BaseDataProcessorTest @@ -377,3 +378,59 @@ def test_json(self): """Check if the node is serializable.""" node = Probability(outcome="00", alpha_prior=0.2) self.assertRoundTripSerializable(node, check_func=self.json_equiv) + + +class TestRestless(QiskitExperimentsTestCase): + """Test the restless measurements node.""" + + def test_restless_classify_1(self): + """Test the classification of restless shots for two single-qubit shots. + This example corresponds to running two single-qubit circuits without qubit reset where + the first and second circuit would be, e.g. an X gate and an identity gate, respectively. + We measure the qubit in the 1 state for the first circuit and measure 1 again for the + second circuit. The second shot is reclassified as a 0 since there was no state change.""" + previous_shot = "1" + shot = "1" + + restless_classified_shot = RestlessToCounts._restless_classify(shot, previous_shot) + self.assertEqual(restless_classified_shot, "0") + + def test_restless_classify_2(self): + """Test the classification of restless shots for two eight-qubit shots. + In this example we run two eight qubit circuits. The first circuit applies an + X, X, Id, Id, Id, X, X and Id gate, the second an Id, Id, X, Id, Id, X, Id and Id gate + to qubits one to eight, respectively.""" + previous_shot = "11000110" + shot = "11100010" + + restless_classified_shot = RestlessToCounts._restless_classify(shot, previous_shot) + self.assertEqual(restless_classified_shot, "00100100") + + def test_restless_process_1(self): + """Test that a single-qubit restless memory is correctly post-processed. + This example corresponds to running an X gate and a SX gate with four shots + in an ideal restless setting.""" + n_qubits = 1 + node = RestlessToCounts(n_qubits) + + data = [["0x1", "0x1", "0x0", "0x0"], ["0x0", "0x1", "0x1", "0x0"]] + processed_data = node(data=np.array(data)) + # time-ordered data: ["1", "0", "1", "1", "0", "1", "0", "0"] + # classification: ["1", "1", "1", "0", "1", "1", "1", "0"] + expected_data = np.array([{"1": 4}, {"1": 2, "0": 2}]) + self.assertTrue(processed_data.all() == expected_data.all()) + + def test_restless_process_2(self): + """Test if a two-qubit restless memory is correctly post-processed. + This example corresponds to running two two-qubit circuits in an ideal restless setting. + The first circuit applies an X gate to the first and a SX gate to the second qubit. The + second circuit applies two identity gates.""" + n_qubits = 2 + node = RestlessToCounts(n_qubits) + + data = [["0x3", "0x1", "0x2", "0x0"], ["0x3", "0x1", "0x2", "0x0"]] + processed_data = node(data=np.array(data)) + # time-ordered data: ["11", "11", "01", "01", "10", "10", "00", "00"] + # classification: ["11", "00", "10", "00", "11", "00", "10", "00"] + expected_data = np.array([{"10": 2, "11": 2}, {"00": 4}]) + self.assertTrue(processed_data.all() == expected_data.all()) From 0aac8bd682aeae6e918c8b04d00d89937462b4b3 Mon Sep 17 00:00:00 2001 From: catornow Date: Mon, 28 Feb 2022 13:59:56 +0100 Subject: [PATCH 11/68] Modified code and added override_processor arg --- .../framework/restless_experiment.py | 53 +++++++++++-------- .../test_restless_experiment.py | 2 +- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/qiskit_experiments/framework/restless_experiment.py b/qiskit_experiments/framework/restless_experiment.py index d859b6ef71..25e80f5f21 100644 --- a/qiskit_experiments/framework/restless_experiment.py +++ b/qiskit_experiments/framework/restless_experiment.py @@ -13,10 +13,6 @@ """Restless experiment class.""" from abc import ABC -from typing import Union - -from qiskit.providers import BaseBackend -from qiskit.test.mock.fake_backend import FakeBackend from qiskit_experiments.framework.base_experiment import BaseExperiment from qiskit_experiments.data_processing.data_processor import DataProcessor @@ -27,33 +23,53 @@ class RestlessEnabledExperiment(BaseExperiment, ABC): """Restless experiment class.""" - def enable_restless(self, rep_delay: float): + def enable_restless(self, rep_delay: float, override_processor: bool = False): """Enables a restless experiment by setting the restless run options and the restless data processor. Args: rep_delay: The repetition delay. + override_processor: Raises: DataProcessorError: if the rep_delay is equal to or greater than the T1 time of one of the physical qubits in the experiment. + DataProcessorError: if excited state promotion readout is enabled in the + restless setting. """ - if self._is_restless(rep_delay): - self.set_run_options(rep_delay=rep_delay, init_qubit=False, memory=True, meas_level=2) - if self.analysis.options.get("data_processor", None): - pass - else: + esp_enabled = self.run_options.get("use_measure_esp", False) + if esp_enabled: + raise DataProcessorError( + "Restless experiments are not compatible with the excited " + "state promotion readout analysis option." + ) + + if self._t1_check(rep_delay): + if not self.analysis.options.get("data_processor", None): + self.set_run_options(rep_delay=rep_delay, init_qubit=False, memory=True, meas_level=2) self.analysis.set_options(data_processor=self._get_restless_processor()) + else: + if override_processor: + self.set_run_options(rep_delay=rep_delay, init_qubit=False, memory=True, meas_level=2) + else: + raise DataProcessorError( + "Cannot enable restless. Data processor has already been set and " + "override_processor is False." + ) else: raise DataProcessorError( - f"The specified repetition delay {rep_delay} is equal to or greater" + f"The specified repetition delay {rep_delay} is equal to or greater " f"than the T1 time of one of the physical qubits" - f"{self.physical_qubits} in the experiment. Consider choosing" + f"{self.physical_qubits} in the experiment. Consider choosing " f"a smaller repetition delay for the restless experiment." ) def _get_restless_processor(self) -> DataProcessor: - """Returns the restless experiments data processor.""" + """Returns the restless experiments data processor. + + Notes: + Sub-classes can override this method if they need more complex data processing. + """ outcome = self.analysis.options.get("outcome", "1" * self.num_qubits) return DataProcessor( "memory", @@ -63,7 +79,7 @@ def _get_restless_processor(self) -> DataProcessor: ], ) - def _is_restless(self, rep_delay: float) -> bool: + def _t1_check(self, rep_delay: float) -> bool: """Checks if the specified repetition delay is smaller than the T1 times of the physical qubits in the experiment. @@ -72,9 +88,6 @@ def _is_restless(self, rep_delay: float) -> bool: Returns: True if the repetition delay is smaller than the qubit T1 times. - - Raises: - DataProcessorError: if excited state promotion readout is enabled in the restless setting. """ t1_values = [ @@ -83,12 +96,6 @@ def _is_restless(self, rep_delay: float) -> bool: ] if all(rep_delay / t1_value < 1.0 for t1_value in t1_values): - esp_enabled = self.run_options.get("use_measure_esp", False) - if esp_enabled: - raise DataProcessorError( - "Restless experiments are not compatible with the excited " - "state promotion readout analysis option." - ) return True return False diff --git a/test/data_processing/test_restless_experiment.py b/test/data_processing/test_restless_experiment.py index 2d5df924b8..6f7bc7465a 100644 --- a/test/data_processing/test_restless_experiment.py +++ b/test/data_processing/test_restless_experiment.py @@ -65,7 +65,7 @@ def test_end_to_end_restless_standard_processor(self, pi_ratio): standard_processor = DataProcessor("counts", [Probability("1")]) amp_exp.analysis.set_options(data_processor=standard_processor) # enable a restless measurement setting. - amp_exp.enable_restless(rep_delay=1e-6) + amp_exp.enable_restless(rep_delay=1e-6, override_processor=True) expdata = amp_exp.run(backend) self.assertExperimentDone(expdata) From 8c688ceb163a11aa7489fb98700220ab04ade8a9 Mon Sep 17 00:00:00 2001 From: catornow Date: Mon, 28 Feb 2022 14:49:00 +0100 Subject: [PATCH 12/68] Some docstring modifications --- qiskit_experiments/framework/restless_experiment.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/qiskit_experiments/framework/restless_experiment.py b/qiskit_experiments/framework/restless_experiment.py index 25e80f5f21..d6c8a027ab 100644 --- a/qiskit_experiments/framework/restless_experiment.py +++ b/qiskit_experiments/framework/restless_experiment.py @@ -29,7 +29,8 @@ def enable_restless(self, rep_delay: float, override_processor: bool = False): Args: rep_delay: The repetition delay. - override_processor: + override_processor: If True, a data processor that is specified in the + analysis options of the experiment can override the restless data processor. Raises: DataProcessorError: if the rep_delay is equal to or greater than the @@ -46,11 +47,15 @@ def enable_restless(self, rep_delay: float, override_processor: bool = False): if self._t1_check(rep_delay): if not self.analysis.options.get("data_processor", None): - self.set_run_options(rep_delay=rep_delay, init_qubit=False, memory=True, meas_level=2) + self.set_run_options( + rep_delay=rep_delay, init_qubit=False, memory=True, meas_level=2 + ) self.analysis.set_options(data_processor=self._get_restless_processor()) else: if override_processor: - self.set_run_options(rep_delay=rep_delay, init_qubit=False, memory=True, meas_level=2) + self.set_run_options( + rep_delay=rep_delay, init_qubit=False, memory=True, meas_level=2 + ) else: raise DataProcessorError( "Cannot enable restless. Data processor has already been set and " From 155657dd5c53cded2ceea8a4ab21e4503355f586 Mon Sep 17 00:00:00 2001 From: catornow Date: Mon, 28 Feb 2022 15:48:53 +0100 Subject: [PATCH 13/68] Added documentation --- .../framework/restless_experiment.py | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/qiskit_experiments/framework/restless_experiment.py b/qiskit_experiments/framework/restless_experiment.py index d6c8a027ab..c2c9428387 100644 --- a/qiskit_experiments/framework/restless_experiment.py +++ b/qiskit_experiments/framework/restless_experiment.py @@ -21,16 +21,34 @@ class RestlessEnabledExperiment(BaseExperiment, ABC): - """Restless experiment class.""" + """Restless enabled experiment class. + + A restless enabled experiment is an experiment that is run in a restless + measurement setting. In restless measurements, the qubit is not reset after + each measurement. Instead, the outcome of the previous quantum non-demolition + measurement is the initial state for the current circuit. Restless measurements + therefore require special data processing which is provided by sub-classes of + the :code:`RestlessNode`. Restless experiments provide a fast alternative for + several calibration and characterization tasks, for details see + https://arxiv.org/pdf/2202.06981.pdf. + This class provides convenience for users to consistently enable the restless + operation mode for an experiment without specifying the needed experiment run + options and restless data processing nodes. + """ def enable_restless(self, rep_delay: float, override_processor: bool = False): """Enables a restless experiment by setting the restless run options and the restless data processor. Args: - rep_delay: The repetition delay. + rep_delay: The repetition delay. This is the delay between a measurement + and the subsequent quantum circuit. Since IBM Quantum backends have + dynamic repetition rates, the repetition delay can be set to a small + value which is required for restless experiments. Typical values are + 1 us or less. override_processor: If True, a data processor that is specified in the - analysis options of the experiment can override the restless data processor. + analysis options of the experiment can override the restless data + processor. Raises: DataProcessorError: if the rep_delay is equal to or greater than the From b9770833f6a59271575b3b0eeeb909236b0d9991 Mon Sep 17 00:00:00 2001 From: catornow Date: Mon, 28 Feb 2022 20:30:33 +0100 Subject: [PATCH 14/68] If esp readout is enabled it is set to False --- .../framework/restless_experiment.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/qiskit_experiments/framework/restless_experiment.py b/qiskit_experiments/framework/restless_experiment.py index c2c9428387..aae7877b0f 100644 --- a/qiskit_experiments/framework/restless_experiment.py +++ b/qiskit_experiments/framework/restless_experiment.py @@ -56,12 +56,12 @@ def enable_restless(self, rep_delay: float, override_processor: bool = False): DataProcessorError: if excited state promotion readout is enabled in the restless setting. """ - esp_enabled = self.run_options.get("use_measure_esp", False) - if esp_enabled: - raise DataProcessorError( - "Restless experiments are not compatible with the excited " - "state promotion readout analysis option." - ) + + # If excited state promotion readout analysis option is enabled, + # it will be set to False because it is not compatible with a + # restless experiment. + if self.run_options.get("use_measure_esp", False): + self.set_run_options(use_measure_esp=False) if self._t1_check(rep_delay): if not self.analysis.options.get("data_processor", None): @@ -107,7 +107,8 @@ def _t1_check(self, rep_delay: float) -> bool: times of the physical qubits in the experiment. Args: - rep_delay: The repetition delay. + rep_delay: The repetition delay. This is the delay between a measurement + and the subsequent quantum circuit. Returns: True if the repetition delay is smaller than the qubit T1 times. From 41fb63f58a354cd3a38913beb3b786c663c3b543 Mon Sep 17 00:00:00 2001 From: catornow Date: Tue, 1 Mar 2022 14:45:56 +0100 Subject: [PATCH 15/68] Lint error --- qiskit_experiments/framework/restless_experiment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_experiments/framework/restless_experiment.py b/qiskit_experiments/framework/restless_experiment.py index aae7877b0f..7f150ca908 100644 --- a/qiskit_experiments/framework/restless_experiment.py +++ b/qiskit_experiments/framework/restless_experiment.py @@ -54,7 +54,7 @@ def enable_restless(self, rep_delay: float, override_processor: bool = False): DataProcessorError: if the rep_delay is equal to or greater than the T1 time of one of the physical qubits in the experiment. DataProcessorError: if excited state promotion readout is enabled in the - restless setting. + restless setting. """ # If excited state promotion readout analysis option is enabled, From cf157799c0f80d0804a211a74c42d0dc14fc4ae1 Mon Sep 17 00:00:00 2001 From: catornow Date: Tue, 1 Mar 2022 15:22:01 +0100 Subject: [PATCH 16/68] Small modifications --- qiskit_experiments/framework/restless_experiment.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit_experiments/framework/restless_experiment.py b/qiskit_experiments/framework/restless_experiment.py index 7f150ca908..3a7daaffb4 100644 --- a/qiskit_experiments/framework/restless_experiment.py +++ b/qiskit_experiments/framework/restless_experiment.py @@ -28,7 +28,7 @@ class RestlessEnabledExperiment(BaseExperiment, ABC): each measurement. Instead, the outcome of the previous quantum non-demolition measurement is the initial state for the current circuit. Restless measurements therefore require special data processing which is provided by sub-classes of - the :code:`RestlessNode`. Restless experiments provide a fast alternative for + the :code:`RestlessNode`. Restless experiments are a fast alternative for several calibration and characterization tasks, for details see https://arxiv.org/pdf/2202.06981.pdf. This class provides convenience for users to consistently enable the restless @@ -57,7 +57,7 @@ def enable_restless(self, rep_delay: float, override_processor: bool = False): restless setting. """ - # If excited state promotion readout analysis option is enabled, + # If the excited state promotion readout analysis option is enabled, # it will be set to False because it is not compatible with a # restless experiment. if self.run_options.get("use_measure_esp", False): From 4a665a8528b75420ab108196b2d05acf39d03f74 Mon Sep 17 00:00:00 2001 From: Caroline Tornow <79633854+catornow@users.noreply.github.com> Date: Wed, 2 Mar 2022 21:20:13 +0100 Subject: [PATCH 17/68] Update qiskit_experiments/framework/restless_experiment.py Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com> --- qiskit_experiments/framework/restless_experiment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_experiments/framework/restless_experiment.py b/qiskit_experiments/framework/restless_experiment.py index 3a7daaffb4..1132a67126 100644 --- a/qiskit_experiments/framework/restless_experiment.py +++ b/qiskit_experiments/framework/restless_experiment.py @@ -23,7 +23,7 @@ class RestlessEnabledExperiment(BaseExperiment, ABC): """Restless enabled experiment class. - A restless enabled experiment is an experiment that is run in a restless + A restless enabled experiment is an experiment that can be run in a restless measurement setting. In restless measurements, the qubit is not reset after each measurement. Instead, the outcome of the previous quantum non-demolition measurement is the initial state for the current circuit. Restless measurements From 2f06ff5945326f23d6efd1a7f532cb3d648a1991 Mon Sep 17 00:00:00 2001 From: Caroline Tornow <79633854+catornow@users.noreply.github.com> Date: Wed, 2 Mar 2022 21:39:32 +0100 Subject: [PATCH 18/68] Update qiskit_experiments/framework/restless_experiment.py Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com> --- qiskit_experiments/framework/restless_experiment.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/qiskit_experiments/framework/restless_experiment.py b/qiskit_experiments/framework/restless_experiment.py index 1132a67126..424da90e9e 100644 --- a/qiskit_experiments/framework/restless_experiment.py +++ b/qiskit_experiments/framework/restless_experiment.py @@ -31,9 +31,12 @@ class RestlessEnabledExperiment(BaseExperiment, ABC): the :code:`RestlessNode`. Restless experiments are a fast alternative for several calibration and characterization tasks, for details see https://arxiv.org/pdf/2202.06981.pdf. - This class provides convenience for users to consistently enable the restless - operation mode for an experiment without specifying the needed experiment run - options and restless data processing nodes. + This class makes it possible for users to enter a restless run-mode without having + to set all the required run options and the data processor. Furthermore, subclasses + can override the :meth:`_get_restless_processor` method if they require more + complex restless data processing such as two-qubit calibrations. In addition, this + class makes it easy to determine if restless measurements are supported for a given + experiments. """ def enable_restless(self, rep_delay: float, override_processor: bool = False): From db4bcb88664a9e016815f2bc7d6336a4850c16f9 Mon Sep 17 00:00:00 2001 From: Caroline Tornow <79633854+catornow@users.noreply.github.com> Date: Wed, 2 Mar 2022 21:39:45 +0100 Subject: [PATCH 19/68] Update qiskit_experiments/framework/restless_experiment.py Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com> --- qiskit_experiments/framework/restless_experiment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_experiments/framework/restless_experiment.py b/qiskit_experiments/framework/restless_experiment.py index 424da90e9e..9aca9bc129 100644 --- a/qiskit_experiments/framework/restless_experiment.py +++ b/qiskit_experiments/framework/restless_experiment.py @@ -69,7 +69,7 @@ def enable_restless(self, rep_delay: float, override_processor: bool = False): if self._t1_check(rep_delay): if not self.analysis.options.get("data_processor", None): self.set_run_options( - rep_delay=rep_delay, init_qubit=False, memory=True, meas_level=2 + rep_delay=rep_delay, init_qubit=False, memory=True, meas_level=2, use_measure_esp=False ) self.analysis.set_options(data_processor=self._get_restless_processor()) else: From 4ab4f4ebdc91625d4b01a71cd3b3c2f16f66b5ba Mon Sep 17 00:00:00 2001 From: Caroline Tornow <79633854+catornow@users.noreply.github.com> Date: Wed, 2 Mar 2022 21:40:28 +0100 Subject: [PATCH 20/68] Update qiskit_experiments/framework/restless_experiment.py Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com> --- qiskit_experiments/framework/restless_experiment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_experiments/framework/restless_experiment.py b/qiskit_experiments/framework/restless_experiment.py index 9aca9bc129..42487c965d 100644 --- a/qiskit_experiments/framework/restless_experiment.py +++ b/qiskit_experiments/framework/restless_experiment.py @@ -49,7 +49,7 @@ def enable_restless(self, rep_delay: float, override_processor: bool = False): dynamic repetition rates, the repetition delay can be set to a small value which is required for restless experiments. Typical values are 1 us or less. - override_processor: If True, a data processor that is specified in the + override_restless_processor: If True, a data processor that is specified in the analysis options of the experiment can override the restless data processor. From 3bfc2c465fde62b74e5cde3e0eb72aca026783d4 Mon Sep 17 00:00:00 2001 From: Caroline Tornow <79633854+catornow@users.noreply.github.com> Date: Wed, 2 Mar 2022 21:40:40 +0100 Subject: [PATCH 21/68] Update qiskit_experiments/framework/restless_experiment.py Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com> --- qiskit_experiments/framework/restless_experiment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_experiments/framework/restless_experiment.py b/qiskit_experiments/framework/restless_experiment.py index 42487c965d..d1ef6c813f 100644 --- a/qiskit_experiments/framework/restless_experiment.py +++ b/qiskit_experiments/framework/restless_experiment.py @@ -39,7 +39,7 @@ class makes it easy to determine if restless measurements are supported for a gi experiments. """ - def enable_restless(self, rep_delay: float, override_processor: bool = False): + def enable_restless(self, rep_delay: float, override_restless_processor: bool = False): """Enables a restless experiment by setting the restless run options and the restless data processor. From acd51ad2b591bef5df4d20a37cf1c6c2fede02f8 Mon Sep 17 00:00:00 2001 From: Caroline Tornow <79633854+catornow@users.noreply.github.com> Date: Wed, 2 Mar 2022 21:40:52 +0100 Subject: [PATCH 22/68] Update qiskit_experiments/framework/restless_experiment.py Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com> --- qiskit_experiments/framework/restless_experiment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_experiments/framework/restless_experiment.py b/qiskit_experiments/framework/restless_experiment.py index d1ef6c813f..b22c9a30b4 100644 --- a/qiskit_experiments/framework/restless_experiment.py +++ b/qiskit_experiments/framework/restless_experiment.py @@ -73,7 +73,7 @@ def enable_restless(self, rep_delay: float, override_restless_processor: bool = ) self.analysis.set_options(data_processor=self._get_restless_processor()) else: - if override_processor: + if override_restless_processor: self.set_run_options( rep_delay=rep_delay, init_qubit=False, memory=True, meas_level=2 ) From a01145322bab8e00220f1597f46d2fe20b8d58d4 Mon Sep 17 00:00:00 2001 From: Caroline Tornow <79633854+catornow@users.noreply.github.com> Date: Wed, 2 Mar 2022 21:41:18 +0100 Subject: [PATCH 23/68] Update qiskit_experiments/test/mock_iq_backend.py Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com> --- qiskit_experiments/test/mock_iq_backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_experiments/test/mock_iq_backend.py b/qiskit_experiments/test/mock_iq_backend.py index 121665ab70..49c8681a93 100644 --- a/qiskit_experiments/test/mock_iq_backend.py +++ b/qiskit_experiments/test/mock_iq_backend.py @@ -123,7 +123,7 @@ def run(self, run_input, **options): class MockRestlessFineAmp(MockRestlessBackend): - """A mock backend for restless fine amplitude calibration.""" + """A mock backend for restless single-qubit fine amplitude experiments.""" def __init__( self, angle_error: float, angle_per_gate: float, gate_name: str, rng_seed: int = 0 From 19a544c3118cfe0d5af30f2ee627bc236c08fc24 Mon Sep 17 00:00:00 2001 From: catornow Date: Wed, 2 Mar 2022 21:44:23 +0100 Subject: [PATCH 24/68] Black --- qiskit_experiments/framework/restless_experiment.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/qiskit_experiments/framework/restless_experiment.py b/qiskit_experiments/framework/restless_experiment.py index b22c9a30b4..0ea656f531 100644 --- a/qiskit_experiments/framework/restless_experiment.py +++ b/qiskit_experiments/framework/restless_experiment.py @@ -69,7 +69,11 @@ def enable_restless(self, rep_delay: float, override_restless_processor: bool = if self._t1_check(rep_delay): if not self.analysis.options.get("data_processor", None): self.set_run_options( - rep_delay=rep_delay, init_qubit=False, memory=True, meas_level=2, use_measure_esp=False + rep_delay=rep_delay, + init_qubit=False, + memory=True, + meas_level=2, + use_measure_esp=False, ) self.analysis.set_options(data_processor=self._get_restless_processor()) else: From 545b644d4148c241fbc58246743aa41e52cbaa73 Mon Sep 17 00:00:00 2001 From: catornow Date: Thu, 3 Mar 2022 17:06:30 +0100 Subject: [PATCH 25/68] Corrected mistake --- test/data_processing/test_restless_experiment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data_processing/test_restless_experiment.py b/test/data_processing/test_restless_experiment.py index 6f7bc7465a..4685de9871 100644 --- a/test/data_processing/test_restless_experiment.py +++ b/test/data_processing/test_restless_experiment.py @@ -65,7 +65,7 @@ def test_end_to_end_restless_standard_processor(self, pi_ratio): standard_processor = DataProcessor("counts", [Probability("1")]) amp_exp.analysis.set_options(data_processor=standard_processor) # enable a restless measurement setting. - amp_exp.enable_restless(rep_delay=1e-6, override_processor=True) + amp_exp.enable_restless(rep_delay=1e-6, override_restless_processor=True) expdata = amp_exp.run(backend) self.assertExperimentDone(expdata) From 927c6c2e76c65152aa3d1aeb3a3a4a5cc9a5e816 Mon Sep 17 00:00:00 2001 From: Caroline Tornow <79633854+catornow@users.noreply.github.com> Date: Thu, 3 Mar 2022 17:12:13 +0100 Subject: [PATCH 26/68] Update qiskit_experiments/framework/restless_experiment.py Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com> --- qiskit_experiments/framework/restless_experiment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_experiments/framework/restless_experiment.py b/qiskit_experiments/framework/restless_experiment.py index 0ea656f531..e548cc8641 100644 --- a/qiskit_experiments/framework/restless_experiment.py +++ b/qiskit_experiments/framework/restless_experiment.py @@ -84,7 +84,7 @@ def enable_restless(self, rep_delay: float, override_restless_processor: bool = else: raise DataProcessorError( "Cannot enable restless. Data processor has already been set and " - "override_processor is False." + "override_restless_processor is False." ) else: raise DataProcessorError( From 56c54946086896565d4c18c047be17b6d8719c78 Mon Sep 17 00:00:00 2001 From: Caroline Tornow <79633854+catornow@users.noreply.github.com> Date: Thu, 3 Mar 2022 17:12:24 +0100 Subject: [PATCH 27/68] Update qiskit_experiments/test/mock_iq_backend.py Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com> --- qiskit_experiments/test/mock_iq_backend.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/qiskit_experiments/test/mock_iq_backend.py b/qiskit_experiments/test/mock_iq_backend.py index 49c8681a93..c644c889a3 100644 --- a/qiskit_experiments/test/mock_iq_backend.py +++ b/qiskit_experiments/test/mock_iq_backend.py @@ -105,14 +105,12 @@ def run(self, run_input, **options): prev_outcome = outcome for idx, circ in enumerate(run_input): - ones = sorted_memory[idx]["memory"].count("0x1") run_result = { "shots": shots, "success": True, "header": {"metadata": circ.metadata}, "meas_level": meas_level, "data": { - "counts": {"1": ones, "0": shots - ones}, "memory": sorted_memory[idx]["memory"], }, } From 5915e331479bc308bd513bbff9190d6d49155489 Mon Sep 17 00:00:00 2001 From: catornow Date: Thu, 3 Mar 2022 17:20:41 +0100 Subject: [PATCH 28/68] Small modification --- qiskit_experiments/framework/restless_experiment.py | 6 +++++- qiskit_experiments/test/mock_iq_backend.py | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/qiskit_experiments/framework/restless_experiment.py b/qiskit_experiments/framework/restless_experiment.py index e548cc8641..cb02c704b6 100644 --- a/qiskit_experiments/framework/restless_experiment.py +++ b/qiskit_experiments/framework/restless_experiment.py @@ -79,7 +79,11 @@ def enable_restless(self, rep_delay: float, override_restless_processor: bool = else: if override_restless_processor: self.set_run_options( - rep_delay=rep_delay, init_qubit=False, memory=True, meas_level=2 + rep_delay=rep_delay, + init_qubit=False, + memory=True, + meas_level=2, + use_measure_esp=False, ) else: raise DataProcessorError( diff --git a/qiskit_experiments/test/mock_iq_backend.py b/qiskit_experiments/test/mock_iq_backend.py index c644c889a3..49c8681a93 100644 --- a/qiskit_experiments/test/mock_iq_backend.py +++ b/qiskit_experiments/test/mock_iq_backend.py @@ -105,12 +105,14 @@ def run(self, run_input, **options): prev_outcome = outcome for idx, circ in enumerate(run_input): + ones = sorted_memory[idx]["memory"].count("0x1") run_result = { "shots": shots, "success": True, "header": {"metadata": circ.metadata}, "meas_level": meas_level, "data": { + "counts": {"1": ones, "0": shots - ones}, "memory": sorted_memory[idx]["memory"], }, } From ec85768d06ed10bb4ad6553feed67ef32f76f18a Mon Sep 17 00:00:00 2001 From: Caroline Tornow <79633854+catornow@users.noreply.github.com> Date: Thu, 3 Mar 2022 17:29:39 +0100 Subject: [PATCH 29/68] Update qiskit_experiments/framework/restless_experiment.py Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com> --- qiskit_experiments/framework/restless_experiment.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/qiskit_experiments/framework/restless_experiment.py b/qiskit_experiments/framework/restless_experiment.py index cb02c704b6..1c2cd82ef9 100644 --- a/qiskit_experiments/framework/restless_experiment.py +++ b/qiskit_experiments/framework/restless_experiment.py @@ -114,8 +114,7 @@ def _get_restless_processor(self) -> DataProcessor: ) def _t1_check(self, rep_delay: float) -> bool: - """Checks if the specified repetition delay is smaller than the T1 - times of the physical qubits in the experiment. + """Check that repetition delay < T1of the physical qubits in the experiment. Args: rep_delay: The repetition delay. This is the delay between a measurement From 8b305501398bed8047aa23b8a2721aad722ff8d4 Mon Sep 17 00:00:00 2001 From: Caroline Tornow <79633854+catornow@users.noreply.github.com> Date: Thu, 3 Mar 2022 18:22:30 +0100 Subject: [PATCH 30/68] Update qiskit_experiments/framework/restless_experiment.py Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com> --- qiskit_experiments/framework/restless_experiment.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/qiskit_experiments/framework/restless_experiment.py b/qiskit_experiments/framework/restless_experiment.py index 1c2cd82ef9..83166e0b6d 100644 --- a/qiskit_experiments/framework/restless_experiment.py +++ b/qiskit_experiments/framework/restless_experiment.py @@ -63,8 +63,6 @@ def enable_restless(self, rep_delay: float, override_restless_processor: bool = # If the excited state promotion readout analysis option is enabled, # it will be set to False because it is not compatible with a # restless experiment. - if self.run_options.get("use_measure_esp", False): - self.set_run_options(use_measure_esp=False) if self._t1_check(rep_delay): if not self.analysis.options.get("data_processor", None): From f2548172ccfb81560688aa505185cea0e13d2c50 Mon Sep 17 00:00:00 2001 From: catornow Date: Thu, 3 Mar 2022 18:24:13 +0100 Subject: [PATCH 31/68] Small modification --- qiskit_experiments/framework/restless_experiment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_experiments/framework/restless_experiment.py b/qiskit_experiments/framework/restless_experiment.py index 1c2cd82ef9..0538ccad10 100644 --- a/qiskit_experiments/framework/restless_experiment.py +++ b/qiskit_experiments/framework/restless_experiment.py @@ -114,7 +114,7 @@ def _get_restless_processor(self) -> DataProcessor: ) def _t1_check(self, rep_delay: float) -> bool: - """Check that repetition delay < T1of the physical qubits in the experiment. + """Check that repetition delay < T1 of the physical qubits in the experiment. Args: rep_delay: The repetition delay. This is the delay between a measurement From 72efed1dd5f81fc0f656977ba7971413c547a5cd Mon Sep 17 00:00:00 2001 From: catornow Date: Thu, 3 Mar 2022 18:28:47 +0100 Subject: [PATCH 32/68] Modification of the doc string for esp readout --- qiskit_experiments/framework/restless_experiment.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/qiskit_experiments/framework/restless_experiment.py b/qiskit_experiments/framework/restless_experiment.py index ec52270c03..35c1d84e24 100644 --- a/qiskit_experiments/framework/restless_experiment.py +++ b/qiskit_experiments/framework/restless_experiment.py @@ -60,10 +60,8 @@ def enable_restless(self, rep_delay: float, override_restless_processor: bool = restless setting. """ - # If the excited state promotion readout analysis option is enabled, - # it will be set to False because it is not compatible with a - # restless experiment. - + # The excited state promotion readout analysis option is set to + # False because it is not compatible with a restless experiment. if self._t1_check(rep_delay): if not self.analysis.options.get("data_processor", None): self.set_run_options( From 768ffb5e8bf9a78722d67c77d4a89f00a51085c3 Mon Sep 17 00:00:00 2001 From: catornow Date: Thu, 3 Mar 2022 20:22:42 +0100 Subject: [PATCH 33/68] Added test to check restless run options --- .../test_restless_experiment.py | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/test/data_processing/test_restless_experiment.py b/test/data_processing/test_restless_experiment.py index 4685de9871..8b8d546ed7 100644 --- a/test/data_processing/test_restless_experiment.py +++ b/test/data_processing/test_restless_experiment.py @@ -23,13 +23,31 @@ from qiskit_experiments.test.mock_iq_backend import MockRestlessFineAmp from qiskit_experiments.data_processing.data_processor import DataProcessor -from qiskit_experiments.data_processing.nodes import Probability +from qiskit_experiments.data_processing.nodes import Probability, RestlessToCounts +from qiskit_experiments.framework import Options @ddt class TestFineAmpEndToEndRestless(QiskitExperimentsTestCase): """Test the fine amplitude experiment in a restless measurement setting.""" + def test_enable_restless(self): + """Test the enable_restless method.""" + + error = -np.pi * 0.01 + backend = MockRestlessFineAmp(error, np.pi, "x") + + amp_exp = FineXAmplitude(0, backend) + # enable a restless measurement setting. + amp_exp.enable_restless(rep_delay=2e-6) + + self.assertTrue( + amp_exp.run_options, + Options( + meas_level=2, rep_delay=2e-6, init_qubit=False, memory=True, use_measure_esp=False + ), + ) + @data(-0.03, -0.01, 0.02, 0.04) def test_end_to_end_restless(self, pi_ratio): """Test the restless experiment end to end.""" From 2e79e24fcf391b4acdc5453c4aced8b84efa7478 Mon Sep 17 00:00:00 2001 From: catornow Date: Thu, 3 Mar 2022 20:32:01 +0100 Subject: [PATCH 34/68] Lint error --- test/data_processing/test_restless_experiment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data_processing/test_restless_experiment.py b/test/data_processing/test_restless_experiment.py index 8b8d546ed7..7f630541fd 100644 --- a/test/data_processing/test_restless_experiment.py +++ b/test/data_processing/test_restless_experiment.py @@ -23,7 +23,7 @@ from qiskit_experiments.test.mock_iq_backend import MockRestlessFineAmp from qiskit_experiments.data_processing.data_processor import DataProcessor -from qiskit_experiments.data_processing.nodes import Probability, RestlessToCounts +from qiskit_experiments.data_processing.nodes import Probability from qiskit_experiments.framework import Options From 5f424adb0be80c2067f531cdca661e02abab730c Mon Sep 17 00:00:00 2001 From: Caroline Tornow <79633854+catornow@users.noreply.github.com> Date: Fri, 4 Mar 2022 14:48:11 +0100 Subject: [PATCH 35/68] Update qiskit_experiments/framework/restless_experiment.py Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com> --- qiskit_experiments/framework/restless_experiment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_experiments/framework/restless_experiment.py b/qiskit_experiments/framework/restless_experiment.py index 35c1d84e24..ee4bf05f52 100644 --- a/qiskit_experiments/framework/restless_experiment.py +++ b/qiskit_experiments/framework/restless_experiment.py @@ -20,7 +20,7 @@ from qiskit_experiments.data_processing import nodes -class RestlessEnabledExperiment(BaseExperiment, ABC): +class RestlessMixin: """Restless enabled experiment class. A restless enabled experiment is an experiment that can be run in a restless From 2b66f1269c5c2c5313ce74fb1f6e9c7a7a0539d2 Mon Sep 17 00:00:00 2001 From: Caroline Tornow <79633854+catornow@users.noreply.github.com> Date: Fri, 4 Mar 2022 14:48:19 +0100 Subject: [PATCH 36/68] Update qiskit_experiments/framework/restless_experiment.py Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com> --- qiskit_experiments/framework/restless_experiment.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/qiskit_experiments/framework/restless_experiment.py b/qiskit_experiments/framework/restless_experiment.py index ee4bf05f52..df0adf9007 100644 --- a/qiskit_experiments/framework/restless_experiment.py +++ b/qiskit_experiments/framework/restless_experiment.py @@ -39,6 +39,12 @@ class makes it easy to determine if restless measurements are supported for a gi experiments. """ + analysis: BaseAnalysis + set_run_options: Callable + physical_qubits: Sequence[int] + num_qubits: int + backend: Backend + def enable_restless(self, rep_delay: float, override_restless_processor: bool = False): """Enables a restless experiment by setting the restless run options and the restless data processor. From b096209870a8805037e14f9f643875c4ce573462 Mon Sep 17 00:00:00 2001 From: Caroline Tornow <79633854+catornow@users.noreply.github.com> Date: Fri, 4 Mar 2022 14:48:27 +0100 Subject: [PATCH 37/68] Update qiskit_experiments/framework/restless_experiment.py Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com> --- qiskit_experiments/framework/restless_experiment.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/qiskit_experiments/framework/restless_experiment.py b/qiskit_experiments/framework/restless_experiment.py index df0adf9007..a9dfdb6986 100644 --- a/qiskit_experiments/framework/restless_experiment.py +++ b/qiskit_experiments/framework/restless_experiment.py @@ -12,12 +12,13 @@ """Restless experiment class.""" -from abc import ABC -from qiskit_experiments.framework.base_experiment import BaseExperiment +from typing import Callable, Sequence +from qiskit.providers import Backend from qiskit_experiments.data_processing.data_processor import DataProcessor from qiskit_experiments.data_processing.exceptions import DataProcessorError from qiskit_experiments.data_processing import nodes +from qiskit_experiments.framework.base_analysis import BaseAnalysis class RestlessMixin: From 0bf8e72f2345c17df861fa6335fe2c82890f7c74 Mon Sep 17 00:00:00 2001 From: Caroline Tornow <79633854+catornow@users.noreply.github.com> Date: Fri, 4 Mar 2022 14:48:36 +0100 Subject: [PATCH 38/68] Update qiskit_experiments/library/characterization/fine_amplitude.py Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com> --- qiskit_experiments/library/characterization/fine_amplitude.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_experiments/library/characterization/fine_amplitude.py b/qiskit_experiments/library/characterization/fine_amplitude.py index bae74acb7b..866910dffe 100644 --- a/qiskit_experiments/library/characterization/fine_amplitude.py +++ b/qiskit_experiments/library/characterization/fine_amplitude.py @@ -22,7 +22,7 @@ from qiskit_experiments.framework import Options from qiskit_experiments.library.characterization.analysis import FineAmplitudeAnalysis -from qiskit_experiments.framework.restless_experiment import RestlessEnabledExperiment +from qiskit_experiments.framework.restless_mixin import RestlessMixin class FineAmplitude(RestlessEnabledExperiment): From cef56f8f2c0a8531c03f871eb0546ec38322b482 Mon Sep 17 00:00:00 2001 From: Caroline Tornow <79633854+catornow@users.noreply.github.com> Date: Fri, 4 Mar 2022 14:48:44 +0100 Subject: [PATCH 39/68] Update qiskit_experiments/library/characterization/fine_amplitude.py Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com> --- qiskit_experiments/library/characterization/fine_amplitude.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_experiments/library/characterization/fine_amplitude.py b/qiskit_experiments/library/characterization/fine_amplitude.py index 866910dffe..c9c19689fe 100644 --- a/qiskit_experiments/library/characterization/fine_amplitude.py +++ b/qiskit_experiments/library/characterization/fine_amplitude.py @@ -25,7 +25,7 @@ from qiskit_experiments.framework.restless_mixin import RestlessMixin -class FineAmplitude(RestlessEnabledExperiment): +class FineAmplitude(BaseExperiment, RestlessMixin): r"""Error amplifying fine amplitude calibration experiment. # section: overview From 5a03619ec8c0164309b2c1f3cfcd3e3ea1c8787d Mon Sep 17 00:00:00 2001 From: catornow Date: Fri, 4 Mar 2022 14:55:55 +0100 Subject: [PATCH 40/68] Implemented Daniel's suggestions --- .../framework/{restless_experiment.py => restless_mixin.py} | 0 qiskit_experiments/library/characterization/fine_amplitude.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename qiskit_experiments/framework/{restless_experiment.py => restless_mixin.py} (100%) diff --git a/qiskit_experiments/framework/restless_experiment.py b/qiskit_experiments/framework/restless_mixin.py similarity index 100% rename from qiskit_experiments/framework/restless_experiment.py rename to qiskit_experiments/framework/restless_mixin.py diff --git a/qiskit_experiments/library/characterization/fine_amplitude.py b/qiskit_experiments/library/characterization/fine_amplitude.py index c9c19689fe..75a4262e5a 100644 --- a/qiskit_experiments/library/characterization/fine_amplitude.py +++ b/qiskit_experiments/library/characterization/fine_amplitude.py @@ -19,7 +19,7 @@ from qiskit.circuit import Gate from qiskit.circuit.library import XGate, SXGate from qiskit.providers.backend import Backend -from qiskit_experiments.framework import Options +from qiskit_experiments.framework import BaseExperiment, Options from qiskit_experiments.library.characterization.analysis import FineAmplitudeAnalysis from qiskit_experiments.framework.restless_mixin import RestlessMixin From a74f24bd059a5d962acd7f12a50f67a62ecbbb31 Mon Sep 17 00:00:00 2001 From: catornow Date: Fri, 4 Mar 2022 15:03:18 +0100 Subject: [PATCH 41/68] Implemented Daniel's suggestions --- .../framework/restless_mixin.py | 69 ++++++++++--------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/qiskit_experiments/framework/restless_mixin.py b/qiskit_experiments/framework/restless_mixin.py index a9dfdb6986..44afb64ac4 100644 --- a/qiskit_experiments/framework/restless_mixin.py +++ b/qiskit_experiments/framework/restless_mixin.py @@ -24,21 +24,21 @@ class RestlessMixin: """Restless enabled experiment class. - A restless enabled experiment is an experiment that can be run in a restless - measurement setting. In restless measurements, the qubit is not reset after - each measurement. Instead, the outcome of the previous quantum non-demolition - measurement is the initial state for the current circuit. Restless measurements - therefore require special data processing which is provided by sub-classes of - the :code:`RestlessNode`. Restless experiments are a fast alternative for - several calibration and characterization tasks, for details see - https://arxiv.org/pdf/2202.06981.pdf. - This class makes it possible for users to enter a restless run-mode without having - to set all the required run options and the data processor. Furthermore, subclasses - can override the :meth:`_get_restless_processor` method if they require more - complex restless data processing such as two-qubit calibrations. In addition, this - class makes it easy to determine if restless measurements are supported for a given - experiments. - """ + A restless enabled experiment is an experiment that can be run in a restless + measurement setting. In restless measurements, the qubit is not reset after + each measurement. Instead, the outcome of the previous quantum non-demolition + measurement is the initial state for the current circuit. Restless measurements + therefore require special data processing which is provided by sub-classes of + the :code:`RestlessNode`. Restless experiments are a fast alternative for + several calibration and characterization tasks, for details see + https://arxiv.org/pdf/2202.06981.pdf. + This class makes it possible for users to enter a restless run-mode without having + to set all the required run options and the data processor. Furthermore, subclasses + can override the :meth:`_get_restless_processor` method if they require more + complex restless data processing such as two-qubit calibrations. In addition, this + class makes it easy to determine if restless measurements are supported for a given + experiments. + """ analysis: BaseAnalysis set_run_options: Callable @@ -47,25 +47,26 @@ class makes it easy to determine if restless measurements are supported for a gi backend: Backend def enable_restless(self, rep_delay: float, override_restless_processor: bool = False): + """Enables a restless experiment by setting the restless run options and - the restless data processor. - - Args: - rep_delay: The repetition delay. This is the delay between a measurement - and the subsequent quantum circuit. Since IBM Quantum backends have - dynamic repetition rates, the repetition delay can be set to a small - value which is required for restless experiments. Typical values are - 1 us or less. - override_restless_processor: If True, a data processor that is specified in the - analysis options of the experiment can override the restless data - processor. - - Raises: - DataProcessorError: if the rep_delay is equal to or greater than the - T1 time of one of the physical qubits in the experiment. - DataProcessorError: if excited state promotion readout is enabled in the - restless setting. - """ + the restless data processor. + + Args: + rep_delay: The repetition delay. This is the delay between a measurement + and the subsequent quantum circuit. Since IBM Quantum backends have + dynamic repetition rates, the repetition delay can be set to a small + value which is required for restless experiments. Typical values are + 1 us or less. + override_restless_processor: If True, a data processor that is specified in the + analysis options of the experiment can override the restless data + processor. + + Raises: + DataProcessorError: if the rep_delay is equal to or greater than the + T1 time of one of the physical qubits in the experiment. + DataProcessorError: if excited state promotion readout is enabled in the + restless setting. + """ # The excited state promotion readout analysis option is set to # False because it is not compatible with a restless experiment. @@ -135,4 +136,4 @@ def _t1_check(self, rep_delay: float) -> bool: if all(rep_delay / t1_value < 1.0 for t1_value in t1_values): return True - return False + return False \ No newline at end of file From 4aafda512cbf2fc2772d86b6833776c09667ed1a Mon Sep 17 00:00:00 2001 From: catornow Date: Fri, 4 Mar 2022 15:08:32 +0100 Subject: [PATCH 42/68] Added a check that the repetition delay has to be positive or zero. --- .../framework/restless_mixin.py | 72 ++++++++++--------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/qiskit_experiments/framework/restless_mixin.py b/qiskit_experiments/framework/restless_mixin.py index 44afb64ac4..1d4139738a 100644 --- a/qiskit_experiments/framework/restless_mixin.py +++ b/qiskit_experiments/framework/restless_mixin.py @@ -24,21 +24,21 @@ class RestlessMixin: """Restless enabled experiment class. - A restless enabled experiment is an experiment that can be run in a restless - measurement setting. In restless measurements, the qubit is not reset after - each measurement. Instead, the outcome of the previous quantum non-demolition - measurement is the initial state for the current circuit. Restless measurements - therefore require special data processing which is provided by sub-classes of - the :code:`RestlessNode`. Restless experiments are a fast alternative for - several calibration and characterization tasks, for details see - https://arxiv.org/pdf/2202.06981.pdf. - This class makes it possible for users to enter a restless run-mode without having - to set all the required run options and the data processor. Furthermore, subclasses - can override the :meth:`_get_restless_processor` method if they require more - complex restless data processing such as two-qubit calibrations. In addition, this - class makes it easy to determine if restless measurements are supported for a given - experiments. - """ + A restless enabled experiment is an experiment that can be run in a restless + measurement setting. In restless measurements, the qubit is not reset after + each measurement. Instead, the outcome of the previous quantum non-demolition + measurement is the initial state for the current circuit. Restless measurements + therefore require special data processing which is provided by sub-classes of + the :code:`RestlessNode`. Restless experiments are a fast alternative for + several calibration and characterization tasks, for details see + https://arxiv.org/pdf/2202.06981.pdf. + This class makes it possible for users to enter a restless run-mode without having + to set all the required run options and the data processor. Furthermore, subclasses + can override the :meth:`_get_restless_processor` method if they require more + complex restless data processing such as two-qubit calibrations. In addition, this + class makes it easy to determine if restless measurements are supported for a given + experiments. + """ analysis: BaseAnalysis set_run_options: Callable @@ -49,24 +49,28 @@ class makes it easy to determine if restless measurements are supported for a gi def enable_restless(self, rep_delay: float, override_restless_processor: bool = False): """Enables a restless experiment by setting the restless run options and - the restless data processor. - - Args: - rep_delay: The repetition delay. This is the delay between a measurement - and the subsequent quantum circuit. Since IBM Quantum backends have - dynamic repetition rates, the repetition delay can be set to a small - value which is required for restless experiments. Typical values are - 1 us or less. - override_restless_processor: If True, a data processor that is specified in the - analysis options of the experiment can override the restless data - processor. - - Raises: - DataProcessorError: if the rep_delay is equal to or greater than the - T1 time of one of the physical qubits in the experiment. - DataProcessorError: if excited state promotion readout is enabled in the - restless setting. - """ + the restless data processor. + + Args: + rep_delay: The repetition delay. This is the delay between a measurement + and the subsequent quantum circuit. Since IBM Quantum backends have + dynamic repetition rates, the repetition delay can be set to a small + value which is required for restless experiments. Typical values are + 1 us or less. + override_restless_processor: If True, a data processor that is specified in the + analysis options of the experiment can override the restless data + processor. + + Raises: + DataProcessorError: if the rep_delay is equal to or greater than the + T1 time of one of the physical qubits in the experiment. + DataProcessorError: if excited state promotion readout is enabled in the + restless setting. + """ + + # check that rep_delay is not negative. + if rep_delay < 0.0: + raise DataProcessorError("The repetition delay has to be positive or zero.") # The excited state promotion readout analysis option is set to # False because it is not compatible with a restless experiment. @@ -136,4 +140,4 @@ def _t1_check(self, rep_delay: float) -> bool: if all(rep_delay / t1_value < 1.0 for t1_value in t1_values): return True - return False \ No newline at end of file + return False From dba1fc6013e74b2b3198c19f503d0071bc7a969f Mon Sep 17 00:00:00 2001 From: catornow Date: Fri, 4 Mar 2022 15:10:05 +0100 Subject: [PATCH 43/68] Adapted doc string --- qiskit_experiments/framework/restless_mixin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qiskit_experiments/framework/restless_mixin.py b/qiskit_experiments/framework/restless_mixin.py index 1d4139738a..cdb8c8b84a 100644 --- a/qiskit_experiments/framework/restless_mixin.py +++ b/qiskit_experiments/framework/restless_mixin.py @@ -62,6 +62,7 @@ def enable_restless(self, rep_delay: float, override_restless_processor: bool = processor. Raises: + DataProcessorError: if the rep_delay is negative. DataProcessorError: if the rep_delay is equal to or greater than the T1 time of one of the physical qubits in the experiment. DataProcessorError: if excited state promotion readout is enabled in the From 6a736da266f7407466d5fcee2f7ab81b13b98dc1 Mon Sep 17 00:00:00 2001 From: catornow Date: Fri, 4 Mar 2022 15:44:36 +0100 Subject: [PATCH 44/68] Attempt solving lint error --- .../library/characterization/fine_amplitude.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/qiskit_experiments/library/characterization/fine_amplitude.py b/qiskit_experiments/library/characterization/fine_amplitude.py index 75a4262e5a..e6330acfc1 100644 --- a/qiskit_experiments/library/characterization/fine_amplitude.py +++ b/qiskit_experiments/library/characterization/fine_amplitude.py @@ -147,7 +147,7 @@ def _spam_cal_circuits(self, meas_circuit: QuantumCircuit) -> List[QuantumCircui cal_circuits = [] for add_x in [0, 1]: - circ = QuantumCircuit(self.num_qubits, meas_circuit.num_clbits) + circ = QuantumCircuit(self._num_qubits, meas_circuit.num_clbits) if add_x: qubits = meas_circuit.get_instructions("measure")[0][1] @@ -157,7 +157,7 @@ def _spam_cal_circuits(self, meas_circuit: QuantumCircuit) -> List[QuantumCircui circ.metadata = { "experiment_type": self._type, - "qubits": self.physical_qubits, + "qubits": self._physical_qubits, "xval": add_x, "unit": "gate number", "series": "spam-cal", @@ -173,7 +173,7 @@ def _pre_circuit(self, num_clbits: int) -> QuantumCircuit: This method can be overridden by subclasses e.g. to calibrate gates on transitions other than the 0 <-> 1 transition. """ - return QuantumCircuit(self.num_qubits, num_clbits) + return QuantumCircuit(self._num_qubits, num_clbits) def _measure_circuit(self) -> QuantumCircuit: """Create the measurement part of the quantum circuit. @@ -183,7 +183,7 @@ def _measure_circuit(self) -> QuantumCircuit: Returns: A quantum circuit which defines the qubits that will be measured. """ - circuit = QuantumCircuit(self.num_qubits, len(self._measurement_qubits)) + circuit = QuantumCircuit(self._num_qubits, len(self._measurement_qubits)) for idx, qubit in enumerate(self._measurement_qubits): circuit.measure(qubit, idx) @@ -201,7 +201,7 @@ def circuits(self) -> List[QuantumCircuit]: """ repetitions = self.experiment_options.get("repetitions") - qubits = range(self.num_qubits) + qubits = range(self._num_qubits) meas_circ = self._measure_circuit() pre_circ = self._pre_circuit(meas_circ.num_clbits) @@ -211,7 +211,7 @@ def circuits(self) -> List[QuantumCircuit]: circuits = [] for repetition in repetitions: - circuit = QuantumCircuit(self.num_qubits, meas_circ.num_clbits) + circuit = QuantumCircuit(self._num_qubits, meas_circ.num_clbits) # Add pre-circuit circuit.compose(pre_circ, qubits, range(meas_circ.num_clbits), inplace=True) @@ -224,7 +224,7 @@ def circuits(self) -> List[QuantumCircuit]: circuit.metadata = { "experiment_type": self._type, - "qubits": self.physical_qubits, + "qubits": self._physical_qubits, "xval": repetition, "unit": "gate number", "series": 1, @@ -267,7 +267,7 @@ def _default_experiment_options(cls) -> Options: def _pre_circuit(self, num_clbits: int) -> QuantumCircuit: """The preparation circuit is an sx gate to move to the equator of the Bloch sphere.""" - circuit = QuantumCircuit(self.num_qubits, num_clbits) + circuit = QuantumCircuit(self._num_qubits, num_clbits) circuit.sx(0) return circuit From 567ce0f43aeec99a608668c2cdd5753239746727 Mon Sep 17 00:00:00 2001 From: catornow Date: Fri, 4 Mar 2022 18:27:01 +0100 Subject: [PATCH 45/68] Modified RestlessMixin to avoid no-member lint errors --- qiskit_experiments/framework/restless_mixin.py | 12 ++++++------ .../library/characterization/fine_amplitude.py | 16 ++++++++-------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/qiskit_experiments/framework/restless_mixin.py b/qiskit_experiments/framework/restless_mixin.py index cdb8c8b84a..d76b5088a3 100644 --- a/qiskit_experiments/framework/restless_mixin.py +++ b/qiskit_experiments/framework/restless_mixin.py @@ -42,9 +42,9 @@ class makes it easy to determine if restless measurements are supported for a gi analysis: BaseAnalysis set_run_options: Callable - physical_qubits: Sequence[int] - num_qubits: int backend: Backend + _physical_qubits: Sequence[int] + _num_qubits: int def enable_restless(self, rep_delay: float, override_restless_processor: bool = False): @@ -103,7 +103,7 @@ def enable_restless(self, rep_delay: float, override_restless_processor: bool = raise DataProcessorError( f"The specified repetition delay {rep_delay} is equal to or greater " f"than the T1 time of one of the physical qubits" - f"{self.physical_qubits} in the experiment. Consider choosing " + f"{self._physical_qubits} in the experiment. Consider choosing " f"a smaller repetition delay for the restless experiment." ) @@ -113,11 +113,11 @@ def _get_restless_processor(self) -> DataProcessor: Notes: Sub-classes can override this method if they need more complex data processing. """ - outcome = self.analysis.options.get("outcome", "1" * self.num_qubits) + outcome = self.analysis.options.get("outcome", "1" * self._num_qubits) return DataProcessor( "memory", [ - nodes.RestlessToCounts(self.num_qubits), + nodes.RestlessToCounts(self._num_qubits), nodes.Probability(outcome), ], ) @@ -135,7 +135,7 @@ def _t1_check(self, rep_delay: float) -> bool: t1_values = [ self.backend.properties().qubit_property(physical_qubit)["T1"][0] - for physical_qubit in self.physical_qubits + for physical_qubit in self._physical_qubits ] if all(rep_delay / t1_value < 1.0 for t1_value in t1_values): diff --git a/qiskit_experiments/library/characterization/fine_amplitude.py b/qiskit_experiments/library/characterization/fine_amplitude.py index e6330acfc1..75a4262e5a 100644 --- a/qiskit_experiments/library/characterization/fine_amplitude.py +++ b/qiskit_experiments/library/characterization/fine_amplitude.py @@ -147,7 +147,7 @@ def _spam_cal_circuits(self, meas_circuit: QuantumCircuit) -> List[QuantumCircui cal_circuits = [] for add_x in [0, 1]: - circ = QuantumCircuit(self._num_qubits, meas_circuit.num_clbits) + circ = QuantumCircuit(self.num_qubits, meas_circuit.num_clbits) if add_x: qubits = meas_circuit.get_instructions("measure")[0][1] @@ -157,7 +157,7 @@ def _spam_cal_circuits(self, meas_circuit: QuantumCircuit) -> List[QuantumCircui circ.metadata = { "experiment_type": self._type, - "qubits": self._physical_qubits, + "qubits": self.physical_qubits, "xval": add_x, "unit": "gate number", "series": "spam-cal", @@ -173,7 +173,7 @@ def _pre_circuit(self, num_clbits: int) -> QuantumCircuit: This method can be overridden by subclasses e.g. to calibrate gates on transitions other than the 0 <-> 1 transition. """ - return QuantumCircuit(self._num_qubits, num_clbits) + return QuantumCircuit(self.num_qubits, num_clbits) def _measure_circuit(self) -> QuantumCircuit: """Create the measurement part of the quantum circuit. @@ -183,7 +183,7 @@ def _measure_circuit(self) -> QuantumCircuit: Returns: A quantum circuit which defines the qubits that will be measured. """ - circuit = QuantumCircuit(self._num_qubits, len(self._measurement_qubits)) + circuit = QuantumCircuit(self.num_qubits, len(self._measurement_qubits)) for idx, qubit in enumerate(self._measurement_qubits): circuit.measure(qubit, idx) @@ -201,7 +201,7 @@ def circuits(self) -> List[QuantumCircuit]: """ repetitions = self.experiment_options.get("repetitions") - qubits = range(self._num_qubits) + qubits = range(self.num_qubits) meas_circ = self._measure_circuit() pre_circ = self._pre_circuit(meas_circ.num_clbits) @@ -211,7 +211,7 @@ def circuits(self) -> List[QuantumCircuit]: circuits = [] for repetition in repetitions: - circuit = QuantumCircuit(self._num_qubits, meas_circ.num_clbits) + circuit = QuantumCircuit(self.num_qubits, meas_circ.num_clbits) # Add pre-circuit circuit.compose(pre_circ, qubits, range(meas_circ.num_clbits), inplace=True) @@ -224,7 +224,7 @@ def circuits(self) -> List[QuantumCircuit]: circuit.metadata = { "experiment_type": self._type, - "qubits": self._physical_qubits, + "qubits": self.physical_qubits, "xval": repetition, "unit": "gate number", "series": 1, @@ -267,7 +267,7 @@ def _default_experiment_options(cls) -> Options: def _pre_circuit(self, num_clbits: int) -> QuantumCircuit: """The preparation circuit is an sx gate to move to the equator of the Bloch sphere.""" - circuit = QuantumCircuit(self._num_qubits, num_clbits) + circuit = QuantumCircuit(self.num_qubits, num_clbits) circuit.sx(0) return circuit From e3d1ab88bf547fd5d5a192e656be8599e6a4d3b5 Mon Sep 17 00:00:00 2001 From: Caroline Tornow <79633854+catornow@users.noreply.github.com> Date: Fri, 4 Mar 2022 19:39:14 +0100 Subject: [PATCH 46/68] Update qiskit_experiments/framework/restless_mixin.py Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com> --- qiskit_experiments/framework/restless_mixin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_experiments/framework/restless_mixin.py b/qiskit_experiments/framework/restless_mixin.py index d76b5088a3..ec1a1f7e53 100644 --- a/qiskit_experiments/framework/restless_mixin.py +++ b/qiskit_experiments/framework/restless_mixin.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Restless experiment class.""" +"""Restless mixin class.""" from typing import Callable, Sequence From c78a723c3f95d8c6a796ef3fa8453e03ba9b22f2 Mon Sep 17 00:00:00 2001 From: Caroline Tornow <79633854+catornow@users.noreply.github.com> Date: Fri, 4 Mar 2022 19:39:25 +0100 Subject: [PATCH 47/68] Update qiskit_experiments/framework/restless_mixin.py Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com> --- qiskit_experiments/framework/restless_mixin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_experiments/framework/restless_mixin.py b/qiskit_experiments/framework/restless_mixin.py index ec1a1f7e53..946c6f089a 100644 --- a/qiskit_experiments/framework/restless_mixin.py +++ b/qiskit_experiments/framework/restless_mixin.py @@ -22,7 +22,7 @@ class RestlessMixin: - """Restless enabled experiment class. + """A mixin to facilitate restless experiments. A restless enabled experiment is an experiment that can be run in a restless measurement setting. In restless measurements, the qubit is not reset after From 7ee9173a65f1b1d034ff8a4673e6907bfc57de6d Mon Sep 17 00:00:00 2001 From: Caroline Tornow <79633854+catornow@users.noreply.github.com> Date: Fri, 4 Mar 2022 19:40:03 +0100 Subject: [PATCH 48/68] Update qiskit_experiments/framework/restless_mixin.py Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com> --- qiskit_experiments/framework/restless_mixin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/qiskit_experiments/framework/restless_mixin.py b/qiskit_experiments/framework/restless_mixin.py index 946c6f089a..ade4b2f2cf 100644 --- a/qiskit_experiments/framework/restless_mixin.py +++ b/qiskit_experiments/framework/restless_mixin.py @@ -47,7 +47,6 @@ class makes it easy to determine if restless measurements are supported for a gi _num_qubits: int def enable_restless(self, rep_delay: float, override_restless_processor: bool = False): - """Enables a restless experiment by setting the restless run options and the restless data processor. From ba9909b37b06cf562fb292af564ab90bb532d48d Mon Sep 17 00:00:00 2001 From: Caroline Tornow <79633854+catornow@users.noreply.github.com> Date: Fri, 4 Mar 2022 19:40:53 +0100 Subject: [PATCH 49/68] Update qiskit_experiments/test/mock_iq_backend.py Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com> --- qiskit_experiments/test/mock_iq_backend.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/qiskit_experiments/test/mock_iq_backend.py b/qiskit_experiments/test/mock_iq_backend.py index 49c8681a93..195a38bc0e 100644 --- a/qiskit_experiments/test/mock_iq_backend.py +++ b/qiskit_experiments/test/mock_iq_backend.py @@ -48,12 +48,8 @@ def _default_options(self): @staticmethod def _get_state_strings(n_qubits: int) -> List[str]: """Generate all state strings for the system.""" - states, format_str = [], "{0:0" + str(n_qubits) + "b}" - - for state_num in range(2**n_qubits): - states.append(format_str.format(state_num)) - - return states + format_str = "{0:0" + str(n_qubits) + "b}" + return list(format_str.format(state_num) for state_num in range(2 ** n_qubits)) @abstractmethod def _compute_outcome_probabilities(self, circuits: List[QuantumCircuit]): From 54b7d5f6228e6713bacbd24ca835be3de5169619 Mon Sep 17 00:00:00 2001 From: Caroline Tornow <79633854+catornow@users.noreply.github.com> Date: Fri, 4 Mar 2022 19:41:51 +0100 Subject: [PATCH 50/68] Update qiskit_experiments/test/mock_iq_backend.py Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com> --- qiskit_experiments/test/mock_iq_backend.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qiskit_experiments/test/mock_iq_backend.py b/qiskit_experiments/test/mock_iq_backend.py index 195a38bc0e..0a8edb1296 100644 --- a/qiskit_experiments/test/mock_iq_backend.py +++ b/qiskit_experiments/test/mock_iq_backend.py @@ -82,10 +82,10 @@ def run(self, run_input, **options): self._compute_outcome_probabilities(run_input) - num_qubits = run_input[0].num_qubits + if run_input[0].num_qubits != 2: + raise DataProcessingError(f"{self.__class__.__name__} is a two qubit mock device.") - prev_outcome = "0" * num_qubits - state_strings = self._get_state_strings(num_qubits) + prev_outcome, state_strings = "00", self._get_state_strings(2) # Setup the list of dicts where each dict corresponds to a circuit. sorted_memory = [{"memory": [], "metadata": circ.metadata} for circ in run_input] From 55a818331f55628f71e9ea2cfcc709025d6734f4 Mon Sep 17 00:00:00 2001 From: catornow Date: Fri, 4 Mar 2022 20:04:59 +0100 Subject: [PATCH 51/68] Modified restless mock backend --- qiskit_experiments/test/mock_iq_backend.py | 20 ++++++++++--------- .../test_restless_experiment.py | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/qiskit_experiments/test/mock_iq_backend.py b/qiskit_experiments/test/mock_iq_backend.py index 0a8edb1296..55dc84f68f 100644 --- a/qiskit_experiments/test/mock_iq_backend.py +++ b/qiskit_experiments/test/mock_iq_backend.py @@ -18,12 +18,14 @@ from qiskit import QuantumCircuit from qiskit.result import Result + from qiskit.providers.aer import AerSimulator from qiskit.test.mock import FakeOpenPulse2Q from qiskit.qobj.utils import MeasLevel from qiskit_experiments.framework import Options from qiskit_experiments.test.utils import FakeJob +from qiskit_experiments.data_processing.exceptions import DataProcessorError class MockRestlessBackend(FakeOpenPulse2Q): @@ -49,7 +51,7 @@ def _default_options(self): def _get_state_strings(n_qubits: int) -> List[str]: """Generate all state strings for the system.""" format_str = "{0:0" + str(n_qubits) + "b}" - return list(format_str.format(state_num) for state_num in range(2 ** n_qubits)) + return list(format_str.format(state_num) for state_num in range(2**n_qubits)) @abstractmethod def _compute_outcome_probabilities(self, circuits: List[QuantumCircuit]): @@ -83,7 +85,7 @@ def run(self, run_input, **options): self._compute_outcome_probabilities(run_input) if run_input[0].num_qubits != 2: - raise DataProcessingError(f"{self.__class__.__name__} is a two qubit mock device.") + raise DataProcessorError(f"{self.__class__.__name__} is a two qubit mock device.") prev_outcome, state_strings = "00", self._get_state_strings(2) @@ -92,9 +94,9 @@ def run(self, run_input, **options): for _ in range(shots): for circ_idx, _ in enumerate(run_input): - prob = self._precomputed_probabilities[(circ_idx, prev_outcome)] + probs = self._precomputed_probabilities[(circ_idx, prev_outcome)] # Generate the next shot dependent on the pre-computed probabilities. - outcome = self._rng.choice(state_strings[:2], p=[1 - prob, prob]) + outcome = self._rng.choice(state_strings, p=probs) # Append the single shot to the memory of the corresponding circuit. sorted_memory[circ_idx]["memory"].append(hex(int(outcome, 2))) @@ -108,7 +110,7 @@ def run(self, run_input, **options): "header": {"metadata": circ.metadata}, "meas_level": meas_level, "data": { - "counts": {"1": ones, "0": shots - ones}, + "counts": {"01": ones, "00": shots - ones}, "memory": sorted_memory[idx]["memory"], }, } @@ -156,11 +158,11 @@ def _compute_outcome_probabilities(self, circuits: List[QuantumCircuit]): if self._gate_name != "x": angle += np.pi * circuit.count_ops().get("x", 0) - prob_0 = np.sin(angle / 2) ** 2 - prob_1 = 1 - prob_0 + prob_1 = np.sin(angle / 2) ** 2 + prob_0 = 1 - prob_1 - self._precomputed_probabilities[(idx, "00")] = prob_0 - self._precomputed_probabilities[(idx, "01")] = prob_1 + self._precomputed_probabilities[(idx, "00")] = [prob_0, prob_1, 0, 0] + self._precomputed_probabilities[(idx, "01")] = [prob_1, prob_0, 0, 0] class MockIQBackend(FakeOpenPulse2Q): diff --git a/test/data_processing/test_restless_experiment.py b/test/data_processing/test_restless_experiment.py index 7f630541fd..4182e8fe7e 100644 --- a/test/data_processing/test_restless_experiment.py +++ b/test/data_processing/test_restless_experiment.py @@ -80,7 +80,7 @@ def test_end_to_end_restless_standard_processor(self, pi_ratio): amp_exp = FineXAmplitude(0, backend) # standard data processor. - standard_processor = DataProcessor("counts", [Probability("1")]) + standard_processor = DataProcessor("counts", [Probability("01")]) amp_exp.analysis.set_options(data_processor=standard_processor) # enable a restless measurement setting. amp_exp.enable_restless(rep_delay=1e-6, override_restless_processor=True) From 8ff090a02758504c9cb3556361c9ffcec57a7d12 Mon Sep 17 00:00:00 2001 From: catornow Date: Sun, 6 Mar 2022 10:53:01 +0100 Subject: [PATCH 52/68] Corrected `init_qubit` to `init_qubits` --- qiskit_experiments/framework/restless_mixin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit_experiments/framework/restless_mixin.py b/qiskit_experiments/framework/restless_mixin.py index ade4b2f2cf..30e767dda2 100644 --- a/qiskit_experiments/framework/restless_mixin.py +++ b/qiskit_experiments/framework/restless_mixin.py @@ -78,7 +78,7 @@ def enable_restless(self, rep_delay: float, override_restless_processor: bool = if not self.analysis.options.get("data_processor", None): self.set_run_options( rep_delay=rep_delay, - init_qubit=False, + init_qubits=False, memory=True, meas_level=2, use_measure_esp=False, @@ -88,7 +88,7 @@ def enable_restless(self, rep_delay: float, override_restless_processor: bool = if override_restless_processor: self.set_run_options( rep_delay=rep_delay, - init_qubit=False, + init_qubits=False, memory=True, meas_level=2, use_measure_esp=False, From d232b1820c77a5d0296ec0c009bdc5ed590b409a Mon Sep 17 00:00:00 2001 From: catornow Date: Sun, 6 Mar 2022 11:11:17 +0100 Subject: [PATCH 53/68] Modified doc string of enable_restless --- qiskit_experiments/framework/restless_mixin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit_experiments/framework/restless_mixin.py b/qiskit_experiments/framework/restless_mixin.py index 30e767dda2..a8085fed1b 100644 --- a/qiskit_experiments/framework/restless_mixin.py +++ b/qiskit_experiments/framework/restless_mixin.py @@ -62,10 +62,10 @@ def enable_restless(self, rep_delay: float, override_restless_processor: bool = Raises: DataProcessorError: if the rep_delay is negative. + DataProcessorError: if a data processor has already been set but + override_restless_processor is False. DataProcessorError: if the rep_delay is equal to or greater than the T1 time of one of the physical qubits in the experiment. - DataProcessorError: if excited state promotion readout is enabled in the - restless setting. """ # check that rep_delay is not negative. From 7c7c2d503a3f5b1603da4ff74f3037d3c3afe024 Mon Sep 17 00:00:00 2001 From: catornow Date: Sun, 6 Mar 2022 11:13:38 +0100 Subject: [PATCH 54/68] Small modification --- test/data_processing/test_restless_experiment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data_processing/test_restless_experiment.py b/test/data_processing/test_restless_experiment.py index 4182e8fe7e..da5e20d70a 100644 --- a/test/data_processing/test_restless_experiment.py +++ b/test/data_processing/test_restless_experiment.py @@ -44,7 +44,7 @@ def test_enable_restless(self): self.assertTrue( amp_exp.run_options, Options( - meas_level=2, rep_delay=2e-6, init_qubit=False, memory=True, use_measure_esp=False + meas_level=2, rep_delay=2e-6, init_qubits=False, memory=True, use_measure_esp=False ), ) From 50679254196dedee267d4163b1066d9c7526c714 Mon Sep 17 00:00:00 2001 From: catornow Date: Mon, 7 Mar 2022 09:17:01 +0100 Subject: [PATCH 55/68] Fixed docs error --- .../framework/restless_mixin.py | 38 +++++++++---------- qiskit_experiments/test/mock_iq_backend.py | 1 - 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/qiskit_experiments/framework/restless_mixin.py b/qiskit_experiments/framework/restless_mixin.py index a8085fed1b..c15a59bd7c 100644 --- a/qiskit_experiments/framework/restless_mixin.py +++ b/qiskit_experiments/framework/restless_mixin.py @@ -47,25 +47,25 @@ class makes it easy to determine if restless measurements are supported for a gi _num_qubits: int def enable_restless(self, rep_delay: float, override_restless_processor: bool = False): - """Enables a restless experiment by setting the restless run options and - the restless data processor. - - Args: - rep_delay: The repetition delay. This is the delay between a measurement - and the subsequent quantum circuit. Since IBM Quantum backends have - dynamic repetition rates, the repetition delay can be set to a small - value which is required for restless experiments. Typical values are - 1 us or less. - override_restless_processor: If True, a data processor that is specified in the - analysis options of the experiment can override the restless data - processor. - - Raises: - DataProcessorError: if the rep_delay is negative. - DataProcessorError: if a data processor has already been set but - override_restless_processor is False. - DataProcessorError: if the rep_delay is equal to or greater than the - T1 time of one of the physical qubits in the experiment. + """Enables a restless experiment by setting the restless run options and the + restless data processor. + + Args: + rep_delay: The repetition delay. This is the delay between a measurement + and the subsequent quantum circuit. Since IBM Quantum backends have + dynamic repetition rates, the repetition delay can be set to a small + value which is required for restless experiments. Typical values are + 1 us or less. + override_restless_processor: If True, a data processor that is specified in the + analysis options of the experiment can override the restless data + processor. + + Raises: + DataProcessorError: if the rep_delay is negative. + DataProcessorError: if a data processor has already been set but + override_restless_processor is False. + DataProcessorError: if the rep_delay is equal to or greater than the + T1 time of one of the physical qubits in the experiment. """ # check that rep_delay is not negative. diff --git a/qiskit_experiments/test/mock_iq_backend.py b/qiskit_experiments/test/mock_iq_backend.py index 55dc84f68f..cd4fa9ecea 100644 --- a/qiskit_experiments/test/mock_iq_backend.py +++ b/qiskit_experiments/test/mock_iq_backend.py @@ -18,7 +18,6 @@ from qiskit import QuantumCircuit from qiskit.result import Result - from qiskit.providers.aer import AerSimulator from qiskit.test.mock import FakeOpenPulse2Q From 79991cc6afc7acaae75779edfc3244747c7f66b6 Mon Sep 17 00:00:00 2001 From: Caroline Tornow <79633854+catornow@users.noreply.github.com> Date: Mon, 7 Mar 2022 11:01:51 +0100 Subject: [PATCH 56/68] Update qiskit_experiments/framework/restless_mixin.py Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com> --- qiskit_experiments/framework/restless_mixin.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/qiskit_experiments/framework/restless_mixin.py b/qiskit_experiments/framework/restless_mixin.py index c15a59bd7c..2499ebcd7a 100644 --- a/qiskit_experiments/framework/restless_mixin.py +++ b/qiskit_experiments/framework/restless_mixin.py @@ -67,8 +67,6 @@ def enable_restless(self, rep_delay: float, override_restless_processor: bool = DataProcessorError: if the rep_delay is equal to or greater than the T1 time of one of the physical qubits in the experiment. """ - - # check that rep_delay is not negative. if rep_delay < 0.0: raise DataProcessorError("The repetition delay has to be positive or zero.") From 077e21e57cc707cb4f0f736063e27698954b9739 Mon Sep 17 00:00:00 2001 From: Caroline Tornow <79633854+catornow@users.noreply.github.com> Date: Mon, 7 Mar 2022 11:02:14 +0100 Subject: [PATCH 57/68] Update qiskit_experiments/library/characterization/fine_amplitude.py Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com> --- qiskit_experiments/library/characterization/fine_amplitude.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_experiments/library/characterization/fine_amplitude.py b/qiskit_experiments/library/characterization/fine_amplitude.py index 75a4262e5a..2f1640b9c7 100644 --- a/qiskit_experiments/library/characterization/fine_amplitude.py +++ b/qiskit_experiments/library/characterization/fine_amplitude.py @@ -20,9 +20,9 @@ from qiskit.circuit.library import XGate, SXGate from qiskit.providers.backend import Backend from qiskit_experiments.framework import BaseExperiment, Options +from qiskit_experiments.framework.restless_mixin import RestlessMixin from qiskit_experiments.library.characterization.analysis import FineAmplitudeAnalysis -from qiskit_experiments.framework.restless_mixin import RestlessMixin class FineAmplitude(BaseExperiment, RestlessMixin): From a15d451d17a91ab435ebed5978a2b42efc08bbf6 Mon Sep 17 00:00:00 2001 From: Caroline Tornow <79633854+catornow@users.noreply.github.com> Date: Mon, 7 Mar 2022 11:02:23 +0100 Subject: [PATCH 58/68] Update test/data_processing/test_restless_experiment.py Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com> --- test/data_processing/test_restless_experiment.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/data_processing/test_restless_experiment.py b/test/data_processing/test_restless_experiment.py index da5e20d70a..0fb020217f 100644 --- a/test/data_processing/test_restless_experiment.py +++ b/test/data_processing/test_restless_experiment.py @@ -38,7 +38,6 @@ def test_enable_restless(self): backend = MockRestlessFineAmp(error, np.pi, "x") amp_exp = FineXAmplitude(0, backend) - # enable a restless measurement setting. amp_exp.enable_restless(rep_delay=2e-6) self.assertTrue( From 3427aa3a71883ca6d5004a64276833f76f974221 Mon Sep 17 00:00:00 2001 From: Caroline Tornow <79633854+catornow@users.noreply.github.com> Date: Mon, 7 Mar 2022 11:02:31 +0100 Subject: [PATCH 59/68] Update test/data_processing/test_restless_experiment.py Co-authored-by: Daniel J. Egger <38065505+eggerdj@users.noreply.github.com> --- test/data_processing/test_restless_experiment.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/data_processing/test_restless_experiment.py b/test/data_processing/test_restless_experiment.py index 0fb020217f..412cf97597 100644 --- a/test/data_processing/test_restless_experiment.py +++ b/test/data_processing/test_restless_experiment.py @@ -55,7 +55,6 @@ def test_end_to_end_restless(self, pi_ratio): backend = MockRestlessFineAmp(error, np.pi, "x") amp_exp = FineXAmplitude(0, backend) - # enable a restless measurement setting. amp_exp.enable_restless(rep_delay=1e-6) expdata = amp_exp.run(backend) From 9d3da96d0a08426b11eab7d1f8c1354843c6fdde Mon Sep 17 00:00:00 2001 From: catornow Date: Mon, 7 Mar 2022 11:09:09 +0100 Subject: [PATCH 60/68] Implemented Daniel's suggestions --- qiskit_experiments/test/mock_iq_backend.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/qiskit_experiments/test/mock_iq_backend.py b/qiskit_experiments/test/mock_iq_backend.py index cd4fa9ecea..b9fc60eaff 100644 --- a/qiskit_experiments/test/mock_iq_backend.py +++ b/qiskit_experiments/test/mock_iq_backend.py @@ -18,6 +18,7 @@ from qiskit import QuantumCircuit from qiskit.result import Result + from qiskit.providers.aer import AerSimulator from qiskit.test.mock import FakeOpenPulse2Q @@ -102,14 +103,16 @@ def run(self, run_input, **options): prev_outcome = outcome for idx, circ in enumerate(run_input): - ones = sorted_memory[idx]["memory"].count("0x1") + counts = {} + for key1, key2 in zip(["00", "01", "10", "11"], ["0x0", "0x1", "0x2", "0x3"]): + counts[key1] = sorted_memory[idx]["memory"].count(key2) run_result = { "shots": shots, "success": True, "header": {"metadata": circ.metadata}, "meas_level": meas_level, "data": { - "counts": {"01": ones, "00": shots - ones}, + "counts": counts, "memory": sorted_memory[idx]["memory"], }, } From 0a4d4a27c1bbbfc24e3deac19a354f74daa8f2b7 Mon Sep 17 00:00:00 2001 From: catornow Date: Mon, 7 Mar 2022 11:24:07 +0100 Subject: [PATCH 61/68] Black --- qiskit_experiments/library/characterization/fine_amplitude.py | 1 - 1 file changed, 1 deletion(-) diff --git a/qiskit_experiments/library/characterization/fine_amplitude.py b/qiskit_experiments/library/characterization/fine_amplitude.py index 2f1640b9c7..f146a4c32e 100644 --- a/qiskit_experiments/library/characterization/fine_amplitude.py +++ b/qiskit_experiments/library/characterization/fine_amplitude.py @@ -24,7 +24,6 @@ from qiskit_experiments.library.characterization.analysis import FineAmplitudeAnalysis - class FineAmplitude(BaseExperiment, RestlessMixin): r"""Error amplifying fine amplitude calibration experiment. From b6784ecdec9f7d82cb2b344ab057446e4db69ed7 Mon Sep 17 00:00:00 2001 From: catornow Date: Mon, 7 Mar 2022 21:38:10 +0100 Subject: [PATCH 62/68] Optional rep_delay --- qiskit_experiments/framework/restless_mixin.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/qiskit_experiments/framework/restless_mixin.py b/qiskit_experiments/framework/restless_mixin.py index 2499ebcd7a..91fa280d7d 100644 --- a/qiskit_experiments/framework/restless_mixin.py +++ b/qiskit_experiments/framework/restless_mixin.py @@ -12,7 +12,7 @@ """Restless mixin class.""" -from typing import Callable, Sequence +from typing import Callable, Sequence, Optional from qiskit.providers import Backend from qiskit_experiments.data_processing.data_processor import DataProcessor @@ -46,13 +46,15 @@ class makes it easy to determine if restless measurements are supported for a gi _physical_qubits: Sequence[int] _num_qubits: int - def enable_restless(self, rep_delay: float, override_restless_processor: bool = False): + def enable_restless( + self, rep_delay: Optional[float] = None, override_restless_processor: bool = False + ): """Enables a restless experiment by setting the restless run options and the restless data processor. Args: rep_delay: The repetition delay. This is the delay between a measurement - and the subsequent quantum circuit. Since IBM Quantum backends have + and the subsequent quantum circuit. Since the backends have dynamic repetition rates, the repetition delay can be set to a small value which is required for restless experiments. Typical values are 1 us or less. @@ -67,6 +69,9 @@ def enable_restless(self, rep_delay: float, override_restless_processor: bool = DataProcessorError: if the rep_delay is equal to or greater than the T1 time of one of the physical qubits in the experiment. """ + if not rep_delay: + rep_delay = self.backend.configuration().rep_delay_range[0] + if rep_delay < 0.0: raise DataProcessorError("The repetition delay has to be positive or zero.") From d9a27cf721e37b0b84e2abba84d3769b5391df71 Mon Sep 17 00:00:00 2001 From: catornow Date: Tue, 8 Mar 2022 12:51:54 +0100 Subject: [PATCH 63/68] Added mixin documentation --- qiskit_experiments/framework/restless_mixin.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/qiskit_experiments/framework/restless_mixin.py b/qiskit_experiments/framework/restless_mixin.py index 91fa280d7d..eac782b6f4 100644 --- a/qiskit_experiments/framework/restless_mixin.py +++ b/qiskit_experiments/framework/restless_mixin.py @@ -24,6 +24,12 @@ class RestlessMixin: """A mixin to facilitate restless experiments. + This class defines the following methods + + - :meth:`enable_restless` + - :meth:`_get_restless_processor` + - :meth:`_t1_check` + A restless enabled experiment is an experiment that can be run in a restless measurement setting. In restless measurements, the qubit is not reset after each measurement. Instead, the outcome of the previous quantum non-demolition From 9904d967bdfa83cafd3a6a0f959a349086235021 Mon Sep 17 00:00:00 2001 From: catornow Date: Tue, 8 Mar 2022 13:15:36 +0100 Subject: [PATCH 64/68] Changed `override_restless_processor` to `override_processor_by_restless` and modified default value --- qiskit_experiments/framework/restless_mixin.py | 10 +++++----- test/data_processing/test_restless_experiment.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/qiskit_experiments/framework/restless_mixin.py b/qiskit_experiments/framework/restless_mixin.py index eac782b6f4..b66dc06c88 100644 --- a/qiskit_experiments/framework/restless_mixin.py +++ b/qiskit_experiments/framework/restless_mixin.py @@ -53,7 +53,7 @@ class makes it easy to determine if restless measurements are supported for a gi _num_qubits: int def enable_restless( - self, rep_delay: Optional[float] = None, override_restless_processor: bool = False + self, rep_delay: Optional[float] = None, override_processor_by_restless: bool = True ): """Enables a restless experiment by setting the restless run options and the restless data processor. @@ -64,9 +64,9 @@ def enable_restless( dynamic repetition rates, the repetition delay can be set to a small value which is required for restless experiments. Typical values are 1 us or less. - override_restless_processor: If True, a data processor that is specified in the - analysis options of the experiment can override the restless data - processor. + override_processor_by_restless: If False, a data processor that is specified in the + analysis options of the experiment is not overridden by the restless data + processor. The default is True. Raises: DataProcessorError: if the rep_delay is negative. @@ -94,7 +94,7 @@ def enable_restless( ) self.analysis.set_options(data_processor=self._get_restless_processor()) else: - if override_restless_processor: + if not override_processor_by_restless: self.set_run_options( rep_delay=rep_delay, init_qubits=False, diff --git a/test/data_processing/test_restless_experiment.py b/test/data_processing/test_restless_experiment.py index 412cf97597..4165f87f01 100644 --- a/test/data_processing/test_restless_experiment.py +++ b/test/data_processing/test_restless_experiment.py @@ -81,7 +81,7 @@ def test_end_to_end_restless_standard_processor(self, pi_ratio): standard_processor = DataProcessor("counts", [Probability("01")]) amp_exp.analysis.set_options(data_processor=standard_processor) # enable a restless measurement setting. - amp_exp.enable_restless(rep_delay=1e-6, override_restless_processor=True) + amp_exp.enable_restless(rep_delay=1e-6, override_processor_by_restless=False) expdata = amp_exp.run(backend) self.assertExperimentDone(expdata) From e229c265d551b7529e1e29b2e39317979fb7802e Mon Sep 17 00:00:00 2001 From: catornow Date: Wed, 9 Mar 2022 15:10:40 +0100 Subject: [PATCH 65/68] Implemented Naoki's suggestions --- .../framework/restless_mixin.py | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/qiskit_experiments/framework/restless_mixin.py b/qiskit_experiments/framework/restless_mixin.py index b66dc06c88..9af8983ca7 100644 --- a/qiskit_experiments/framework/restless_mixin.py +++ b/qiskit_experiments/framework/restless_mixin.py @@ -69,17 +69,21 @@ def enable_restless( processor. The default is True. Raises: - DataProcessorError: if the rep_delay is negative. + DataProcessorError: if the attribute rep_delay_range is not defined for the backend. DataProcessorError: if a data processor has already been set but - override_restless_processor is False. + override_processor_by_restless is True. DataProcessorError: if the rep_delay is equal to or greater than the T1 time of one of the physical qubits in the experiment. """ - if not rep_delay: - rep_delay = self.backend.configuration().rep_delay_range[0] - - if rep_delay < 0.0: - raise DataProcessorError("The repetition delay has to be positive or zero.") + try: + if not rep_delay: + rep_delay = self.backend.configuration().rep_delay_range[0] + except AttributeError as error: + raise DataProcessorError( + "The restless experiment can not be enabled because " + "the attribute rep_delay_range is not defined for this backend " + "and a minimum rep_delay can not be set." + ) from error # The excited state promotion readout analysis option is set to # False because it is not compatible with a restless experiment. @@ -92,7 +96,8 @@ def enable_restless( meas_level=2, use_measure_esp=False, ) - self.analysis.set_options(data_processor=self._get_restless_processor()) + if hasattr(self.analysis.options, "data_processor"): + self.analysis.set_options(data_processor=self._get_restless_processor()) else: if not override_processor_by_restless: self.set_run_options( @@ -105,7 +110,7 @@ def enable_restless( else: raise DataProcessorError( "Cannot enable restless. Data processor has already been set and " - "override_restless_processor is False." + "override_processor_by_restless is True." ) else: raise DataProcessorError( @@ -139,14 +144,24 @@ def _t1_check(self, rep_delay: float) -> bool: Returns: True if the repetition delay is smaller than the qubit T1 times. + + Raises: + DataProcessorError: if the T1 values are not defined for the qubits of + the used backend. """ - t1_values = [ - self.backend.properties().qubit_property(physical_qubit)["T1"][0] - for physical_qubit in self._physical_qubits - ] + try: + t1_values = [ + self.backend.properties().qubit_property(physical_qubit)["T1"][0] + for physical_qubit in self._physical_qubits + ] - if all(rep_delay / t1_value < 1.0 for t1_value in t1_values): - return True + if all(rep_delay / t1_value < 1.0 for t1_value in t1_values): + return True + except AttributeError as error: + raise DataProcessorError( + "The restless experiment can not be enabled since " + "T1 values are not defined for the qubits of the used backend." + ) from error return False From 709bec895503ee0a6b3893b066814a9e57e10213 Mon Sep 17 00:00:00 2001 From: catornow Date: Wed, 9 Mar 2022 15:39:10 +0100 Subject: [PATCH 66/68] DataProcessorError is raised if the data_processor analysis options does not exist --- qiskit_experiments/framework/restless_mixin.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/qiskit_experiments/framework/restless_mixin.py b/qiskit_experiments/framework/restless_mixin.py index 9af8983ca7..8411a8071b 100644 --- a/qiskit_experiments/framework/restless_mixin.py +++ b/qiskit_experiments/framework/restless_mixin.py @@ -72,6 +72,8 @@ def enable_restless( DataProcessorError: if the attribute rep_delay_range is not defined for the backend. DataProcessorError: if a data processor has already been set but override_processor_by_restless is True. + DataProcessorError: if the experiment analysis does not have the data_processor + option. DataProcessorError: if the rep_delay is equal to or greater than the T1 time of one of the physical qubits in the experiment. """ @@ -98,6 +100,11 @@ def enable_restless( ) if hasattr(self.analysis.options, "data_processor"): self.analysis.set_options(data_processor=self._get_restless_processor()) + else: + raise DataProcessorError( + "The restless data processor can not be set since the experiment analysis" + "does not have the data_processor option." + ) else: if not override_processor_by_restless: self.set_run_options( From 85d97a66e75d88182ea78f929934a2140191106a Mon Sep 17 00:00:00 2001 From: "Daniel J. Egger" <38065505+eggerdj@users.noreply.github.com> Date: Wed, 9 Mar 2022 16:49:52 +0100 Subject: [PATCH 67/68] Update qiskit_experiments/framework/restless_mixin.py --- qiskit_experiments/framework/restless_mixin.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/qiskit_experiments/framework/restless_mixin.py b/qiskit_experiments/framework/restless_mixin.py index 8411a8071b..9205a3a39f 100644 --- a/qiskit_experiments/framework/restless_mixin.py +++ b/qiskit_experiments/framework/restless_mixin.py @@ -39,8 +39,9 @@ class RestlessMixin: several calibration and characterization tasks, for details see https://arxiv.org/pdf/2202.06981.pdf. This class makes it possible for users to enter a restless run-mode without having - to set all the required run options and the data processor. Furthermore, subclasses - can override the :meth:`_get_restless_processor` method if they require more + to manually set all the required run options and the data processor. The required options + are ``rep_delay``, ``init_qubits``, ``memory``, and ``meas_level``. Furthermore, + subclasses can override the :meth:`_get_restless_processor` method if they require more complex restless data processing such as two-qubit calibrations. In addition, this class makes it easy to determine if restless measurements are supported for a given experiments. From 7799cc4ea0e3c239813a91edb02c94f472339fe4 Mon Sep 17 00:00:00 2001 From: catornow Date: Wed, 9 Mar 2022 20:37:19 +0100 Subject: [PATCH 68/68] Black --- qiskit_experiments/framework/restless_mixin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_experiments/framework/restless_mixin.py b/qiskit_experiments/framework/restless_mixin.py index 9205a3a39f..82814cafa9 100644 --- a/qiskit_experiments/framework/restless_mixin.py +++ b/qiskit_experiments/framework/restless_mixin.py @@ -40,7 +40,7 @@ class RestlessMixin: https://arxiv.org/pdf/2202.06981.pdf. This class makes it possible for users to enter a restless run-mode without having to manually set all the required run options and the data processor. The required options - are ``rep_delay``, ``init_qubits``, ``memory``, and ``meas_level``. Furthermore, + are ``rep_delay``, ``init_qubits``, ``memory``, and ``meas_level``. Furthermore, subclasses can override the :meth:`_get_restless_processor` method if they require more complex restless data processing such as two-qubit calibrations. In addition, this class makes it easy to determine if restless measurements are supported for a given