Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
f2945b3
First draft of RestlessEnabledExperiment
catornow Feb 22, 2022
9660509
Modified the draft
catornow Feb 25, 2022
b7edd2a
Moved restless_experiment to framework folder and modified it
catornow Feb 25, 2022
d460e70
Implemented is_restless check
catornow Feb 25, 2022
3e8c5f3
Removed restless node tests for this PR
catornow Feb 25, 2022
6ef6690
Small modification
catornow Feb 25, 2022
7c9e1e8
Black
catornow Feb 25, 2022
271ce98
Fixed Lint error
catornow Feb 25, 2022
bf4f981
Removed backend arg
catornow Feb 28, 2022
cfcd629
Aligned code with current main
catornow Feb 28, 2022
c8ab58e
Merge branch 'RestlessMixin' of github.com:catornow/qiskit-experiment…
catornow Feb 28, 2022
0aac8bd
Modified code and added override_processor arg
catornow Feb 28, 2022
8c688ce
Some docstring modifications
catornow Feb 28, 2022
155657d
Added documentation
catornow Feb 28, 2022
b977083
If esp readout is enabled it is set to False
catornow Feb 28, 2022
747e445
Merge branch 'main' of github.com:Qiskit/qiskit-experiments into Rest…
catornow Feb 28, 2022
41fb63f
Lint error
catornow Mar 1, 2022
cf15779
Small modifications
catornow Mar 1, 2022
4a665a8
Update qiskit_experiments/framework/restless_experiment.py
catornow Mar 2, 2022
2f06ff5
Update qiskit_experiments/framework/restless_experiment.py
catornow Mar 2, 2022
db4bcb8
Update qiskit_experiments/framework/restless_experiment.py
catornow Mar 2, 2022
4ab4f4e
Update qiskit_experiments/framework/restless_experiment.py
catornow Mar 2, 2022
3bfc2c4
Update qiskit_experiments/framework/restless_experiment.py
catornow Mar 2, 2022
acd51ad
Update qiskit_experiments/framework/restless_experiment.py
catornow Mar 2, 2022
a011453
Update qiskit_experiments/test/mock_iq_backend.py
catornow Mar 2, 2022
19a544c
Black
catornow Mar 2, 2022
545b644
Corrected mistake
catornow Mar 3, 2022
927c6c2
Update qiskit_experiments/framework/restless_experiment.py
catornow Mar 3, 2022
56c5494
Update qiskit_experiments/test/mock_iq_backend.py
catornow Mar 3, 2022
5915e33
Small modification
catornow Mar 3, 2022
ec85768
Update qiskit_experiments/framework/restless_experiment.py
catornow Mar 3, 2022
8b30550
Update qiskit_experiments/framework/restless_experiment.py
catornow Mar 3, 2022
f254817
Small modification
catornow Mar 3, 2022
f31cef7
Merge branch 'RestlessMixin' of github.com:catornow/qiskit-experiment…
catornow Mar 3, 2022
72efed1
Modification of the doc string for esp readout
catornow Mar 3, 2022
768ffb5
Added test to check restless run options
catornow Mar 3, 2022
4342863
Merge branch 'main' into RestlessMixin
catornow Mar 3, 2022
2e79e24
Lint error
catornow Mar 3, 2022
5f424ad
Update qiskit_experiments/framework/restless_experiment.py
catornow Mar 4, 2022
2b66f12
Update qiskit_experiments/framework/restless_experiment.py
catornow Mar 4, 2022
b096209
Update qiskit_experiments/framework/restless_experiment.py
catornow Mar 4, 2022
0bf8e72
Update qiskit_experiments/library/characterization/fine_amplitude.py
catornow Mar 4, 2022
cef56f8
Update qiskit_experiments/library/characterization/fine_amplitude.py
catornow Mar 4, 2022
5a03619
Implemented Daniel's suggestions
catornow Mar 4, 2022
a74f24b
Implemented Daniel's suggestions
catornow Mar 4, 2022
4aafda5
Added a check that the repetition delay has to be positive or zero.
catornow Mar 4, 2022
dba1fc6
Adapted doc string
catornow Mar 4, 2022
6a736da
Attempt solving lint error
catornow Mar 4, 2022
567ce0f
Modified RestlessMixin to avoid no-member lint errors
catornow Mar 4, 2022
e3d1ab8
Update qiskit_experiments/framework/restless_mixin.py
catornow Mar 4, 2022
c78a723
Update qiskit_experiments/framework/restless_mixin.py
catornow Mar 4, 2022
7ee9173
Update qiskit_experiments/framework/restless_mixin.py
catornow Mar 4, 2022
ba9909b
Update qiskit_experiments/test/mock_iq_backend.py
catornow Mar 4, 2022
54b7d5f
Update qiskit_experiments/test/mock_iq_backend.py
catornow Mar 4, 2022
55a8183
Modified restless mock backend
catornow Mar 4, 2022
a55d405
Merge branch 'main' of github.com:Qiskit/qiskit-experiments into Rest…
catornow Mar 5, 2022
8ff090a
Corrected `init_qubit` to `init_qubits`
catornow Mar 6, 2022
d232b18
Modified doc string of enable_restless
catornow Mar 6, 2022
7c7c2d5
Small modification
catornow Mar 6, 2022
5067925
Fixed docs error
catornow Mar 7, 2022
79991cc
Update qiskit_experiments/framework/restless_mixin.py
catornow Mar 7, 2022
077e21e
Update qiskit_experiments/library/characterization/fine_amplitude.py
catornow Mar 7, 2022
a15d451
Update test/data_processing/test_restless_experiment.py
catornow Mar 7, 2022
3427aa3
Update test/data_processing/test_restless_experiment.py
catornow Mar 7, 2022
9d3da96
Implemented Daniel's suggestions
catornow Mar 7, 2022
0a4d4a2
Black
catornow Mar 7, 2022
b6784ec
Optional rep_delay
catornow Mar 7, 2022
d9a27cf
Added mixin documentation
catornow Mar 8, 2022
9904d96
Changed `override_restless_processor` to `override_processor_by_restl…
catornow Mar 8, 2022
e229c26
Implemented Naoki's suggestions
catornow Mar 9, 2022
709bec8
DataProcessorError is raised if the data_processor analysis options d…
catornow Mar 9, 2022
85d97a6
Update qiskit_experiments/framework/restless_mixin.py
eggerdj Mar 9, 2022
3189b73
Merge branch 'main' into RestlessMixin
eggerdj Mar 9, 2022
7799cc4
Black
catornow Mar 9, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 175 additions & 0 deletions qiskit_experiments/framework/restless_mixin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
# 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 mixin class."""

from typing import Callable, Sequence, Optional

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:
"""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
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 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.
"""

analysis: BaseAnalysis
set_run_options: Callable
backend: Backend
_physical_qubits: Sequence[int]
_num_qubits: int

def enable_restless(
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.

Args:
rep_delay: The repetition delay. This is the delay between a measurement
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.
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 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.
"""
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.
if self._t1_check(rep_delay):
if not self.analysis.options.get("data_processor", None):
self.set_run_options(
rep_delay=rep_delay,
init_qubits=False,
memory=True,
meas_level=2,
use_measure_esp=False,
)
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(
rep_delay=rep_delay,
init_qubits=False,
memory=True,
meas_level=2,
use_measure_esp=False,
)
else:
raise DataProcessorError(
"Cannot enable restless. Data processor has already been set and "
"override_processor_by_restless is True."
)
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.

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",
[
nodes.RestlessToCounts(self._num_qubits),
nodes.Probability(outcome),
],
)

def _t1_check(self, rep_delay: float) -> bool:
"""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
and the subsequent quantum circuit.

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.
"""

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
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
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@
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


class FineAmplitude(BaseExperiment):
class FineAmplitude(BaseExperiment, RestlessMixin):
r"""Error amplifying fine amplitude calibration experiment.

# section: overview
Expand Down
141 changes: 141 additions & 0 deletions qiskit_experiments/test/mock_iq_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,153 @@

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):
"""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."""
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]):
"""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)

if run_input[0].num_qubits != 2:
raise DataProcessorError(f"{self.__class__.__name__} is a two qubit mock device.")

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]

for _ in range(shots):
for circ_idx, _ in enumerate(run_input):
probs = self._precomputed_probabilities[(circ_idx, prev_outcome)]
# Generate the next shot dependent on the pre-computed probabilities.
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)))

prev_outcome = outcome

for idx, circ in enumerate(run_input):
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": counts,
"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 single-qubit fine amplitude experiments."""

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_1 = np.sin(angle / 2) ** 2
prob_0 = 1 - 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):
Expand Down
Loading