Skip to content

Commit

Permalink
Modify rep_delays from backend to be a sliding rep_delay_range (#…
Browse files Browse the repository at this point in the history
…4830)

* Add default rep delay w/ rep delay range

* Update backend config schema

* Try to change back toronto

* Fix montreal conf

* Modify comment

* Remove old rep delays line

* Fix schema issue

* Minor comment updates and add test for dict output

* Update tests

* Add back check for rep delay range

* Update assemble error

* Update releasenotes/notes/add-rep-delay-c97d5aa8fc9696da.yaml

Co-authored-by: Lauren Capelluto <laurencapelluto@gmail.com>

* Fix error check

* Update error loop for rep delay range

* Disable lint error

* Seperate errors again

* Fix lint issue

* Remove minor comment

Co-authored-by: Thomas Alexander <thomasalexander2718@gmail.com>
Co-authored-by: Lauren Capelluto <laurencapelluto@gmail.com>
  • Loading branch information
3 people committed Aug 5, 2020
1 parent adf6550 commit e9d29ee
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 71 deletions.
55 changes: 37 additions & 18 deletions qiskit/compiler/assemble.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,13 @@ 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: 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``).
rep_time (int): Time per program execution in seconds. Must be from the list provided
by the backend (``backend.configuration().rep_times``). Defaults to the first entry.
rep_delay (float): Delay between programs in seconds. Only supported on certain
backends (if ``backend.configuration().dynamic_reprate_enabled=True``). If supported,
``rep_delay`` will be used instead of ``rep_time`` and must be from the range supplied
by the backend (``backend.configuration().rep_delay_range``). Default is given by
``backend.configuration().default_rep_delay``.
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 @@ -189,7 +190,7 @@ def _parse_common_args(backend, qobj_id, qobj_header, shots,
Raises:
QiskitError: if the memory arg is True and the backend does not support
memory. Also if shots exceeds max_shots for the configured backend.
memory. Also if shots exceeds max_shots for the configured backend.
"""
# grab relevant info from backend if it exists
backend_config = None
Expand Down Expand Up @@ -250,7 +251,8 @@ def _parse_pulse_args(backend, qubit_lo_freq, meas_lo_freq, qubit_lo_range,
RunConfig: a run config, which is a standardized object that configures the qobj
and determines the runtime environment.
Raises:
SchemaValidationError: if the given meas_level is not allowed for the given `backend`.
SchemaValidationError: If the given meas_level is not allowed for the given `backend`. If
rep_delay is not in the backend rep_delay_range.
"""
# grab relevant info from backend if it exists
backend_config = None
Expand Down Expand Up @@ -289,20 +291,37 @@ def _parse_pulse_args(backend, qubit_lo_freq, meas_lo_freq, qubit_lo_range,
if rep_time:
if dynamic_reprate_enabled:
warnings.warn("Dynamic rep rates are supported on this backend. 'rep_delay' will be "
"used instead, if specified.", RuntimeWarning)
"used instead of 'rep_time'.", RuntimeWarning)
if isinstance(rep_time, list):
rep_time = rep_time[0]
rep_time = int(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
if dynamic_reprate_enabled:
rep_delay = rep_delay or getattr(backend_config, "default_rep_delay", None)
if rep_delay is not None:
rep_delay_range = getattr(backend_config, "rep_delay_range", None)
# check that rep_delay is in rep_delay_range
if rep_delay_range is not None and isinstance(rep_delay_range, list):
# pylint: disable=E1136
if len(rep_delay_range) != 2:
raise SchemaValidationError(
"Backend rep_delay_range {} must be a list with two entries.".format(
rep_delay_range
)
)
if not rep_delay_range[0] <= rep_delay <= rep_delay_range[1]:
raise SchemaValidationError(
"Supplied rep delay {} not in the supported "
"backend range {}".format(rep_delay, rep_delay_range)
)
rep_delay = rep_delay * 1e6 # convert sec to μs
else:
rep_delay = None
warnings.warn(
"Dynamic rep rates not supported on this backend. rep_time will be "
"used instead of rep_delay.",
RuntimeWarning,
)

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

Expand Down
15 changes: 8 additions & 7 deletions qiskit/execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,13 +172,14 @@ def execute(experiments, backend,
memory_slot_size (int): Size of each memory slot if the output is Level 0.
rep_time (int): 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``).
rep_time (int): Time per program execution in seconds. Must be from the list provided
by the backend (``backend.configuration().rep_times``). Defaults to the first entry.
rep_delay (float): Delay between programs in seconds. Only supported on certain
backends (``backend.configuration().dynamic_reprate_enabled`` ). If supported,
``rep_delay`` will be used instead of ``rep_time`` and must be from the range supplied
by the backend (``backend.configuration().rep_delay_range``). Default is given by
``backend.configuration().default_rep_delay``.
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
32 changes: 18 additions & 14 deletions qiskit/providers/models/backendconfiguration.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,8 +430,9 @@ def __init__(self,
rep_times: List[float],
meas_kernels: List[str],
discriminators: List[str],
rep_delays: List[float] = None,
dynamic_reprate_enabled: bool = False,
rep_delay_range: List[float] = None,
default_rep_delay: float = None,
hamiltonian: Dict[str, Any] = None,
channel_bandwidth=None,
acquisition_latency=None,
Expand Down Expand Up @@ -476,10 +477,14 @@ def __init__(self,
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.
rep_delay_range: 2d list defining supported range of repetition delays (delay
programs) for backend in μs. First entry is lower end of the range, second entry is
higher end of the range. Optional, but will be specified when
``dynamic_reprate_enabled=True``.
default_rep_delay: Value of ``rep_delay`` if not specified by user and
``dynamic_reprate_enabled=True``.
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 @@ -523,10 +528,11 @@ def __init__(self,
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
if rep_delay_range:
self.rep_delay_range = [_rd * 1e-6 for _rd in rep_delay_range] # convert to sec
if default_rep_delay:
self.default_rep_delay = default_rep_delay * 1e-6 # convert to sec

self.dt = dt * 1e-9 # pylint: disable=invalid-name
self.dtm = dtm * 1e-9

Expand Down Expand Up @@ -603,11 +609,12 @@ def to_dict(self):
'rep_times': self.rep_times,
'dt': self.dt,
'dtm': self.dtm,
'dynamic_reprate_enabled': self.dynamic_reprate_enabled
})
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, 'rep_delay_range'):
out_dict['rep_delay_range'] = [_rd * 1e6 for _rd in self.rep_delay_range]
if hasattr(self, 'default_rep_delay'):
out_dict['default_rep_delay'] = self.default_rep_delay*1e6
if hasattr(self, 'channel_bandwidth'):
out_dict['channel_bandwidth'] = self.channel_bandwidth
if hasattr(self, 'meas_map'):
Expand All @@ -634,9 +641,6 @@ def to_dict(self):
if self.rep_times:
out_dict['rep_times'] = [_rt * 1e6 for _rt in self.rep_times]

if self.rep_delays:
out_dict['rep_delays'] = [_rd * 1e6 for _rd in self.rep_delays]

out_dict['dt'] = out_dict['dt'] * 1e9 # pylint: disable=invalid-name
out_dict['dtm'] = out_dict['dtm'] * 1e9

Expand Down
10 changes: 6 additions & 4 deletions qiskit/qobj/pulse_qobj.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,11 +271,13 @@ def __init__(self, meas_level, meas_return, pulse_library,
memory_slot_size (int): Size of each memory slot if the output is
Level 0.
rep_time (int): Time per program execution in sec. Must be from the list provided
by the backend (``backend.configuration().rep_times``).
by the backend (``backend.configuration().rep_times``). Defaults to the first entry
in ``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``).
backends (``backend.configuration().dynamic_reprate_enabled`` ). If supported,
``rep_delay`` will be used instead of ``rep_time`` and must be from the range
supplied by the backend (``backend.configuration().rep_delay_range``). Default is
``backend.configuration().default_rep_delay``.
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 Down
10 changes: 7 additions & 3 deletions qiskit/schemas/backend_configuration_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -221,11 +221,15 @@
"minItems": 1,
"description": "Program execution times (microseconds) supported by backend.",
"items": {"type": "number", "minimum": 0}},
"rep_delays": {
"rep_delay_range": {
"type": "array",
"minItems": 0,
"description": "Delay times between programs (microseconds) supported by backend.",
"items": {"type": "number", "minimum": 0}},
"description": "Range of delay times between programs (microseconds) allowed by backend.",
"items": {"type": "array", "minItems": 2, "maxItems": 2, "items": {"type": "number", "minimum": 0}}},
"default_rep_delay": {
"type": "number",
"description": "Default rep delay.",
"minimum": 0},
"dynamic_reprate_enabled": {
"type": "boolean",
"description": "Whether delay between programs can be set dynamically using 'rep_delay').",
Expand Down
16 changes: 9 additions & 7 deletions releasenotes/notes/add-rep-delay-c97d5aa8fc9696da.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
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.
denotes the time between program executions. It will only be used if
``backend.configuration().dynamic_reprate_enabled``. In this case, its value must be within
range defined by the backend as ``backend.configuration().rep_delay_range``. If the user does
not supply a ``rep_delay`, the backend default
``backend.configuration().default_rep_delay`` will be used (assuming it exists). If dynamic rep
rates are not enabled on the device, the qobj value of ``rep_delay`` will be ignored and
``rep_time`` (the total duration of the program) will be used instead.
- |
``qobj`` schema has been updated to include ``rep_delay``.
Backend configuration schema has been updated to include ``rep_delay_range`` and
``default_rep_delay``.
53 changes: 35 additions & 18 deletions test/python/compiler/test_assembler.py
Original file line number Diff line number Diff line change
Expand Up @@ -735,53 +735,70 @@ def test_assemble_backend_rep_times_delays(self):
"""Check that rep_time and rep_delay are properly set from backend values."""
# use first entry from allowed backend values
rep_times = [2.0, 3.0, 4.0] # sec
rep_delays = [2.5e-3, 3.5e-3, 4.5e-3]
rep_delay_range = [2.5e-3, 4.5e-3]
default_rep_delay = 3.0e-3
self.backend_config.rep_times = rep_times
self.backend_config.rep_delays = rep_delays
# RuntimeWarning bc using ``rep_delay`` when dynamic rep rates not enabled
with self.assertWarns(RuntimeWarning):
qobj = assemble(self.schedule, self.backend)
self.assertEqual(qobj.config.rep_time, int(rep_times[0]*1e6))
self.assertEqual(qobj.config.rep_delay, rep_delays[0]*1e6)
setattr(self.backend_config, 'rep_delay_range', rep_delay_range)
setattr(self.backend_config, 'default_rep_delay', default_rep_delay)

# remove rep_delays from backend config and make sure things work
# now no warning
del self.backend_config.rep_delays
# dynamic rep rates off
qobj = assemble(self.schedule, self.backend)
self.assertEqual(qobj.config.rep_time, int(rep_times[0]*1e6))
self.assertEqual(hasattr(qobj.config, 'rep_delay'), False)

# dynamic rep rates on
setattr(self.backend_config, 'dynamic_reprate_enabled', True)
# RuntimeWarning bc ``rep_time`` is specified`` when dynamic rep rates not enabled
with self.assertWarns(RuntimeWarning):
qobj = assemble(self.schedule, self.backend)
self.assertEqual(qobj.config.rep_time, int(rep_times[0]*1e6))
self.assertEqual(qobj.config.rep_delay, default_rep_delay*1e6)

def test_assemble_user_rep_time_delay(self):
"""Check that user runtime config rep_time and rep_delay work."""
# set custom rep_time and rep_delay in runtime config
rep_time = 200.0e-6
rep_delay = 2.5e-6
self.config['rep_time'] = rep_time
self.config['rep_delay'] = rep_delay
# RuntimeWarning bc using ``rep_delay`` when dynamic rep rates not enabled

# dynamic rep rates off
# RuntimeWarning bc using ``rep_delay`` when dynamic rep rates off
with self.assertWarns(RuntimeWarning):
qobj = assemble(self.schedule, self.backend, **self.config)
self.assertEqual(qobj.config.rep_time, int(rep_time*1e6))
self.assertEqual(qobj.config.rep_delay, rep_delay*1e6)
self.assertEqual(hasattr(qobj.config, 'rep_delay'), False)

# now remove rep_delay and set enable dynamic rep rates
# now remove rep_delay and enable dynamic rep rates
# RuntimeWarning bc using ``rep_time`` when dynamic rep rates are enabled
del self.config['rep_delay']
self.backend_config.dynamic_reprate_enabled = True
setattr(self.backend_config, 'dynamic_reprate_enabled', True)
with self.assertWarns(RuntimeWarning):
qobj = assemble(self.schedule, self.backend, **self.config)
self.assertEqual(qobj.config.rep_time, int(rep_time*1e6))
self.assertEqual(hasattr(qobj.config, 'rep_delay'), False)

# finally, only use rep_delay and verify that everything runs w/ no warning
# rep_time comes from allowed backed rep_times
# use ``default_rep_delay``
# ``rep_time`` comes from allowed backend rep_times
rep_times = [0.5, 1.0, 1.5] # sec
self.backend_config.rep_times = rep_times
setattr(self.backend_config, 'rep_delay_range', [0, 3.0e-6])
setattr(self.backend_config, 'default_rep_delay', 2.2e-6)
del self.config['rep_time']
self.config['rep_delay'] = rep_delay
qobj = assemble(self.schedule, self.backend, **self.config)
self.assertEqual(qobj.config.rep_time, int(rep_times[0]*1e6))
self.assertEqual(qobj.config.rep_delay, rep_delay*1e6)
self.assertEqual(qobj.config.rep_delay, 2.2)

# use qobj ``default_rep_delay``
self.config['rep_delay'] = 1.5e-6
qobj = assemble(self.schedule, self.backend, **self.config)
self.assertEqual(qobj.config.rep_time, int(rep_times[0]*1e6))
self.assertEqual(qobj.config.rep_delay, 1.5)

# use ``rep_delay`` outside of ``rep_delay_range
self.config['rep_delay'] = 5.0e-6
with self.assertRaises(SchemaValidationError):
assemble(self.schedule, self.backend, **self.config)

def test_assemble_with_individual_discriminators(self):
"""Test that assembly works with individual discriminators."""
Expand Down
15 changes: 15 additions & 0 deletions test/python/providers/test_backendconfiguration.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,21 @@ def test_get_rep_times(self):
for rep_time in self.config.to_dict()['rep_times']:
self.assertGreater(rep_time, 0)

def test_get_default_rep_delay_and_range(self):
"""Test whether rep time property is the right size"""
_rep_delay_range_us = [100, 1000]
_rep_delay_range_s = [_rd * 1.e-6 for _rd in _rep_delay_range_us]
_default_rep_delay_us = 500
_default_rep_delay_s = 500 * 1.e-6

setattr(self.config, 'rep_delay_range', _rep_delay_range_s)
setattr(self.config, 'default_rep_delay', _default_rep_delay_s)

config_dict = self.config.to_dict()
for i, rd in enumerate(config_dict['rep_delay_range']):
self.assertAlmostEqual(rd, _rep_delay_range_us[i], delta=1e-8)
self.assertEqual(config_dict['default_rep_delay'], _default_rep_delay_us)

def test_get_channel_prefix_index(self):
"""Test private method to get channel and index."""
self.assertEqual(self.config._get_channel_prefix_index('acquire0'), ('acquire', 0))
Expand Down

0 comments on commit e9d29ee

Please sign in to comment.