Skip to content

Commit

Permalink
Add rep delay (#4539)
Browse files Browse the repository at this point in the history
* Initial implementation of rep_delay flag

* Add dynamic reprate flag

* Update config files and tests

* Update tests

* Update tests and comments

* Use . rather than dict

* Fix assemble.py line lengths

* Fix linting issues

* Update w/ Lauren comment

* Fix lint error and add release note
  • Loading branch information
zachschoenfeld33 authored Jun 12, 2020
1 parent f72f5cd commit 86f5d46
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 52 deletions.
40 changes: 31 additions & 9 deletions qiskit/compiler/assemble.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import uuid
import copy
import logging
import warnings
from time import time

from typing import Union, List, Dict, Optional
Expand Down Expand Up @@ -58,6 +59,7 @@ def assemble(experiments: Union[QuantumCircuit, List[QuantumCircuit], Schedule,
meas_map: Optional[List[List[Qubit]]] = None,
memory_slot_size: int = 100,
rep_time: Optional[float] = None,
rep_delay: Optional[float] = None,
parameter_binds: Optional[List[Dict[Parameter, float]]] = None,
parametric_pulses: Optional[List[str]] = None,
init_qubits: bool = True,
Expand Down Expand Up @@ -103,9 +105,12 @@ def assemble(experiments: Union[QuantumCircuit, List[QuantumCircuit], Schedule,
* ``avg`` returns average measurement output (averaged over number of shots).
meas_map: List of lists, containing qubits that must be measured together.
memory_slot_size: Size of each memory slot if the output is Level 0.
rep_time: Repetition time of the experiment in s.
The delay between experiments will be ``rep_time``.
Must be from the list provided by the device.
rep_time: Time per program execution in sec. Must be from the list provided
by the backend (``backend.configuration().rep_times``).
rep_delay: Delay between programs in sec. Only supported on certain
backends (``backend.configuration().dynamic_reprate_enabled`` ).
If supported, ``rep_delay`` will be used instead of ``rep_time``. Must be from the list
provided by the backend (``backend.configuration().rep_delays``).
parameter_binds: List of Parameter bindings over which the set of experiments will be
executed. Each list element (bind) should be of the form
{Parameter1: value1, Parameter2: value2, ...}. All binds will be
Expand Down Expand Up @@ -152,7 +157,8 @@ def assemble(experiments: Union[QuantumCircuit, List[QuantumCircuit], Schedule,
run_config = _parse_pulse_args(backend, qubit_lo_freq, meas_lo_freq,
qubit_lo_range, meas_lo_range,
schedule_los, meas_level, meas_return,
meas_map, memory_slot_size, rep_time,
meas_map, memory_slot_size,
rep_time, rep_delay,
parametric_pulses,
**run_config_common_dict)

Expand Down Expand Up @@ -234,7 +240,8 @@ def _parse_common_args(backend, qobj_id, qobj_header, shots,
def _parse_pulse_args(backend, qubit_lo_freq, meas_lo_freq, qubit_lo_range,
meas_lo_range, schedule_los, meas_level,
meas_return, meas_map,
memory_slot_size, rep_time,
memory_slot_size,
rep_time, rep_delay,
parametric_pulses,
**run_config):
"""Build a pulse RunConfig replacing unset arguments with defaults derived from the `backend`.
Expand Down Expand Up @@ -276,13 +283,27 @@ def _parse_pulse_args(backend, qubit_lo_freq, meas_lo_freq, qubit_lo_range,

qubit_lo_range = qubit_lo_range or getattr(backend_config, 'qubit_lo_range', None)
meas_lo_range = meas_lo_range or getattr(backend_config, 'meas_lo_range', None)
rep_time = rep_time or getattr(backend_config, 'rep_times', None)

if isinstance(rep_time, list):
rep_time = rep_time[0]
dynamic_reprate_enabled = getattr(backend_config, 'dynamic_reprate_enabled', False)

rep_time = rep_time or getattr(backend_config, 'rep_times', None)
if rep_time:
rep_time = int(rep_time * 1e6)
if dynamic_reprate_enabled:
warnings.warn("Dynamic rep rates are supported on this backend. 'rep_delay' will be "
"used instead, if specified.", RuntimeWarning)
if isinstance(rep_time, list):
rep_time = rep_time[0]
rep_time = rep_time * 1e6 # convert sec to μs

rep_delay = rep_delay or getattr(backend_config, 'rep_delays', None)
if rep_delay:
if not dynamic_reprate_enabled:
warnings.warn("Dynamic rep rates not supported on this backend. 'rep_time' will be "
"used instead.", RuntimeWarning)

if isinstance(rep_delay, list):
rep_delay = rep_delay[0]
rep_delay = rep_delay * 1e6 # convert sec to μs

parametric_pulses = parametric_pulses or getattr(backend_config, 'parametric_pulses', [])

Expand All @@ -297,6 +318,7 @@ def _parse_pulse_args(backend, qubit_lo_freq, meas_lo_freq, qubit_lo_range,
meas_map=meas_map,
memory_slot_size=memory_slot_size,
rep_time=rep_time,
rep_delay=rep_delay,
parametric_pulses=parametric_pulses,
**run_config)
run_config = RunConfig(**{k: v for k, v in run_config_dict.items() if v is not None})
Expand Down
17 changes: 11 additions & 6 deletions qiskit/execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ def execute(experiments, backend,
default_qubit_los=None, default_meas_los=None, # schedule run options
schedule_los=None, meas_level=MeasLevel.CLASSIFIED,
meas_return=MeasReturnType.AVERAGE,
memory_slots=None, memory_slot_size=100, rep_time=None, parameter_binds=None,
schedule_circuit=False, inst_map=None, meas_map=None, scheduling_method=None,
init_qubits=None,
memory_slots=None, memory_slot_size=100, rep_time=None, rep_delay=None,
parameter_binds=None, schedule_circuit=False, inst_map=None, meas_map=None,
scheduling_method=None, init_qubits=None,
**run_config):
"""Execute a list of :class:`qiskit.circuit.QuantumCircuit` or
:class:`qiskit.pulse.Schedule` on a backend.
Expand Down Expand Up @@ -172,9 +172,13 @@ def execute(experiments, backend,
memory_slot_size (int): Size of each memory slot if the output is Level 0.
rep_time (int): repetition time of the experiment in μs.
The delay between experiments will be rep_time.
Must be from the list provided by the device.
rep_time (list[float]): Time per program execution in sec. Must be from the list provided
by the backend (``backend.configuration().rep_times``).
rep_delay (list[float]): Delay between programs in sec. Only supported on certain
backends (``backend.configuration().dynamic_reprate_enabled`` ).
If supported, ``rep_delay`` will be used instead of ``rep_time``. Must be from the list
provided by the backend (``backend.configuration().rep_delays``).
parameter_binds (list[dict]): List of Parameter bindings over which the set of
experiments will be executed. Each list element (bind) should be of the form
Expand Down Expand Up @@ -277,6 +281,7 @@ def execute(experiments, backend,
memory_slots=memory_slots,
memory_slot_size=memory_slot_size,
rep_time=rep_time,
rep_delay=rep_delay,
parameter_binds=parameter_binds,
backend=backend,
init_qubits=init_qubits,
Expand Down
21 changes: 19 additions & 2 deletions qiskit/providers/models/backendconfiguration.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ def __init__(self, backend_name, backend_version, n_qubits,
kwargs['meas_lo_range'] = [[min_range * 1e9, max_range * 1e9] for
(min_range, max_range) in kwargs['meas_lo_range']]

# convert rep_times from μs to sec
if 'rep_times' in kwargs.keys():
kwargs['rep_times'] = [_rt * 1e-6 for _rt in kwargs['rep_times']]

Expand Down Expand Up @@ -393,6 +394,8 @@ def __init__(self,
rep_times: List[float],
meas_kernels: List[str],
discriminators: List[str],
rep_delays: List[float] = None,
dynamic_reprate_enabled: bool = False,
hamiltonian: Dict[str, str] = None,
channel_bandwidth=None,
acquisition_latency=None,
Expand Down Expand Up @@ -434,9 +437,13 @@ def __init__(self,
meas_lo_range: Measurement lo ranges for each qubit with form (min, max) in GHz.
dt: Qubit drive channel timestep in nanoseconds.
dtm: Measurement drive channel timestep in nanoseconds.
rep_times: Supported repetition times for device in microseconds.
rep_times: Supported repetition times (program execution time) for backend in μs.
meas_kernels: Supported measurement kernels.
discriminators: Supported discriminators.
rep_delays: Supported repetition delays (delay between programs) for backend in μs.
Optional, but will be specified when ``dynamic_reprate_enabled=True``.
dynamic_reprate_enabled: whether delay between programs can be set dynamically
(ie via ``rep_delay``). Defaults to False.
hamiltonian: An optional dictionary with fields characterizing the system hamiltonian.
channel_bandwidth (list): Bandwidth of all channels
(qubit, measurement, and U)
Expand Down Expand Up @@ -477,7 +484,13 @@ def __init__(self,
self.discriminators = discriminators
self.hamiltonian = hamiltonian

self.rep_times = [_rt * 1e-6 for _rt in rep_times]
self.dynamic_reprate_enabled = dynamic_reprate_enabled

self.rep_times = [_rt * 1e-6 for _rt in rep_times] # convert to sec
# if ``rep_delays`` not specified, leave as None
self.rep_delays = None
if rep_delays:
self.rep_delays = [_rd * 1e-6 for _rd in rep_delays] # convert to sec
self.dt = dt * 1e-9 # pylint: disable=invalid-name
self.dtm = dtm * 1e-9

Expand Down Expand Up @@ -556,6 +569,10 @@ def to_dict(self):
'dt': self.dt,
'dtm': self.dtm,
})
if hasattr(self, 'rep_delays'):
out_dict['rep_delays'] = self.rep_delays
if hasattr(self, 'dynamic_reprate_enabled'):
out_dict['dynamic_reprate_enabled'] = self.dynamic_reprate_enabled
if hasattr(self, 'channel_bandwidth'):
out_dict['channel_bandwidth'] = self.channel_bandwidth
if hasattr(self, 'meas_map'):
Expand Down
13 changes: 10 additions & 3 deletions qiskit/qobj/pulse_qobj.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ class PulseQobjConfig(QobjDictField):

def __init__(self, meas_level, meas_return, pulse_library,
qubit_lo_freq, meas_lo_freq, memory_slot_size=None,
rep_time=None, shots=None, max_credits=None,
rep_time=None, rep_delay=None, shots=None, max_credits=None,
seed_simulator=None, memory_slots=None, **kwargs):
"""Instantiate a PulseQobjConfig object.
Expand All @@ -249,7 +249,12 @@ def __init__(self, meas_level, meas_return, pulse_library,
measurement driver LO's in GHz.
memory_slot_size (int): Size of each memory slot if the output is
Level 0.
rep_time (int): Repetition time of the experiment in μs
rep_time (float): Time per program execution in sec. Must be from the list provided
by the backend (``backend.configuration().rep_times``).
rep_delay (float): Delay between programs in sec. Only supported on certain
backends (``backend.configuration().dynamic_reprate_enabled``).
If supported, ``rep_delay`` will be used instead of ``rep_time``. Must be from the
list provided by the backend (``backend.configuration().rep_delays``).
shots (int): The number of shots
max_credits (int): the max_credits to use on the IBMQ public devices.
seed_simulator (int): the seed to use in the simulator
Expand All @@ -265,7 +270,9 @@ def __init__(self, meas_level, meas_return, pulse_library,
if memory_slot_size is not None:
self.memory_slot_size = memory_slot_size
if rep_time is not None:
self.rep_time = rep_time or []
self.rep_time = rep_time
if rep_delay is not None:
self.rep_delay = rep_delay
if shots is not None:
self.shots = int(shots)

Expand Down
15 changes: 12 additions & 3 deletions qiskit/schemas/backend_configuration_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,8 @@
},
"openpulse_config":{
"required": ["open_pulse","n_uchannels","hamiltonian","u_channel_lo",
"meas_levels","qubit_lo_range","meas_lo_range","dt","dtm","rep_times",
"meas_kernels","discriminators"],
"meas_levels","qubit_lo_range","meas_lo_range","dt","dtm",
"rep_times", "meas_kernels","discriminators"],
"properties": {
"open_pulse": {
"enum": [ true ],
Expand Down Expand Up @@ -219,8 +219,17 @@
"rep_times": {
"type": "array",
"minItems": 1,
"description": "Available experiment repetition rates",
"description": "Program execution times (microseconds) supported by backend.",
"items": {"type": "number", "minimum": 0}},
"rep_delays": {
"type": "array",
"minItems": 0,
"description": "Delay times between programs (microseconds) supported by backend.",
"items": {"type": "number", "minimum": 0}},
"dynamic_reprate_enabled": {
"type": "boolean",
"description": "Whether delay between programs can be set dynamically using 'rep_delay').",
"default": false},
"meas_map": {
"type": "array",
"minItems": 1,
Expand Down
10 changes: 8 additions & 2 deletions qiskit/schemas/qobj_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1085,8 +1085,14 @@
"type": "array"
},
"rep_time": {
"minimum": 1,
"type": "integer"
"minimum": 0,
"description": "Execution time of program (microseconds).",
"type": "number"
},
"rep_delay": {
"minimum": 0,
"description": "Delay between programs (microseconds).",
"type": "number"
}
},
"required": [
Expand Down
13 changes: 13 additions & 0 deletions releasenotes/notes/add-rep-delay-c97d5aa8fc9696da.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
features:
- |
Support for ``rep_delay`` flag added when assembling/executing a ``qobj``. ``rep_delay``
denotes the time between program executions. It must be chosen from a list of ``rep_delays``
from the backend, accessed as ``backend.configuration().rep_delays``.
``rep_delay`` only works on backends which allow for dynamic repetition time. This setting is
found from ``backend.configuration().dynamic_reprate_enabled``. If false, ``rep_time`` will be
used rather than ``rep_delay``. ``rep_time`` only allows users to specify the duration of a
program, rather than the delay between programs.
- |
``qobj`` schema has been updated to include ``rep_delay``.
Loading

0 comments on commit 86f5d46

Please sign in to comment.