-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add analytic pulse function samplers #2042
Merged
Merged
Changes from 32 commits
Commits
Show all changes
50 commits
Select commit
Hold shift + click to select a range
e0e75e2
Update remote simulator test for backend IBMQ/Aer changes
taalexander 9d6144d
Added test module and case for samplers.
taalexander 070fc7d
Added base sampling decorator along with left, right, and midpoint sa…
taalexander 7d17266
Added tests to samplers.
taalexander 5a219a3
change FunctionalPulse to decorator
nkanazawa1989 4e7be10
update error function and imports
nkanazawa1989 df05d77
update unittest
nkanazawa1989 d0bdb32
add functools.wraps
nkanazawa1989 dbd7c28
remove getter, setter from sample pulse
nkanazawa1989 a57c08a
fix lint
nkanazawa1989 936ff28
Added dynamic updating of docstrings for decorated sampled functions.
taalexander 14a39cd
Added explicit documentation for standard library samplers.
taalexander 3d83a51
Added extensive sampler module docstring for developers.
taalexander d45746e
Update changelog with sampler.
taalexander 05e3686
Separate standard library and external libraries
taalexander d612719
Updated changelog for functional_pulse.
taalexander 40a199e
Merge branch 'master' into pulse_decorator
taalexander fe2067d
Merge remote-tracking branch 'naoki/pulse_decorator' into issue-1935-…
taalexander 8bd7708
update sampler for functional_pulse decorator. Add additional tests.
taalexander 89fc807
Merge branch 'master' into issue-1935-samplers
taalexander 6aa07e5
Changed all instances of 'analytic' to 'continuous'
taalexander 1347ad1
Merge branch 'master' of gh:taalexander/qiskit-terra into issue-1935-…
taalexander fa69384
Merge branch 'issue-1935-samplers' of gh:taalexander/qiskit-terra int…
taalexander 65baaa5
Merge remote-tracking branch 'upstream/master' into issue-1935-samplers
taalexander 568f635
Update sampler module structure to separate sampling functions and de…
taalexander 73c62ac
Merge branch 'master' into issue-1935-samplers
taalexander 12f635c
Remove sampler prefix to sampler module's modules.
taalexander a14489c
Update small bug from rename
taalexander 10047a6
Change dtype casting to np.complex_
taalexander c148c74
remove accidental remote simulator file.
taalexander 0647592
Merge branch 'master' of gh:Qiskit/qiskit-terra into issue-1935-samplers
taalexander 7cdff43
Removed required default parameters.
taalexander b470ad3
Update changelog. Update defaults model validation to add nonetypes.
taalexander f619d29
Update changelog.
taalexander 80a2aa4
Merge branch 'master' into issue-2098-pulse-defaults-required
taalexander f5a7957
Merge branch 'master' into issue-2098-pulse-defaults-required
taalexander 4ffc2d0
Merge branch 'master' into issue-1935-samplers
ajavadia db5b78b
Merge branch 'master' of gh:Qiskit/qiskit-terra into issue-2098-pulse…
taalexander 2a954ec
Readd parameters to be required.
taalexander a072785
Merge branch 'issue-2098-pulse-defaults-required' of gh:taalexander/q…
taalexander b5968d7
Merge branch 'master' into issue-1935-samplers
taalexander a3f5eaa
Merge branch 'issue-2098-pulse-defaults-required' of gh:taalexander/q…
taalexander 7410f93
Update docstring and delete extra function following review.
taalexander 307d048
Merge branch 'issue-1935-samplers' of gh:taalexander/qiskit-terra int…
taalexander 72d1604
Merge branch 'master' into issue-1935-samplers
taalexander db47850
Merge branch 'master' into issue-1935-samplers
taalexander 59a7a29
Merge branch 'master' into issue-1935-samplers
taalexander 0c57e17
Merge branch 'master' into issue-1935-samplers
taalexander 0c4e5df
Merge branch 'master' into issue-1935-samplers
taalexander 6841c65
Merge branch 'master' into issue-1935-samplers
kdk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
# Copyright 2019, IBM. | ||
# | ||
# This source code is licensed under the Apache License, Version 2.0 found in | ||
# the LICENSE.txt file in the root directory of this source tree. | ||
|
||
"""Module for Samplers.""" | ||
|
||
from .decorators import * |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,281 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
# Copyright 2019, IBM. | ||
# | ||
# This source code is licensed under the Apache License, Version 2.0 found in | ||
# the LICENSE.txt file in the root directory of this source tree. | ||
|
||
# pylint: disable=missing-return-doc | ||
|
||
"""Sampler decorator module for sampling of continuous pulses to discrete pulses to be | ||
exposed to user. | ||
|
||
Some atypical boilerplate has been added to solve the problem of decorators not preserving | ||
their wrapped function signatures. Below we explain the problem that samplers solve and how | ||
we implement this. | ||
|
||
A sampler is a function that takes an continuous pulse function with signature: | ||
```python | ||
def f(times: np.ndarray, *args, **kwargs) -> np.ndarray: | ||
... | ||
``` | ||
and returns a new function: | ||
def f(duration: int, *args, **kwargs) -> SamplePulse: | ||
... | ||
|
||
Samplers are used to build up pulse commands from continuous pulse functions. | ||
|
||
In Python the creation of a dynamic function that wraps another function will cause | ||
the underlying signature and documentation of the underlying function to be overwritten. | ||
In order to circumvent this issue the Python standard library provides the decorator | ||
`functools.wraps` which allows the programmer to expose the names and signature of the | ||
wrapped function as those of the dynamic function. | ||
|
||
Samplers are implemented by creating a function with signature | ||
@sampler | ||
def left(continuous_pulse: Callable, duration: int, *args, **kwargs) | ||
... | ||
|
||
This will create a sampler function for `left`. Since it is a dynamic function it would not | ||
have the docstring of `left` available too `help`. This could be fixed by wrapping with | ||
`functools.wraps` in the `sampler`, but this would then cause the signature to be that of the | ||
sampler function which is called on the continuous pulse, below: | ||
`(continuous_pulse: Callable, duration: int, *args, **kwargs)`` | ||
This is not correct for the sampler as the output sampled functions accept only a function. | ||
For the standard sampler we get around this by not using `functools.wraps` and | ||
explicitly defining our samplers such as `left`, `right` and `midpoint` and | ||
calling `sampler` internally on the function that implements the sampling schemes such as | ||
`_left`, `_right` and `_midpoint` respectively. See `left` for an example of this. | ||
|
||
In this way our standard samplers will expose the proper help signature, but a user can | ||
still create their own sampler with | ||
@sampler | ||
def custom_sampler(time, *args, **kwargs): | ||
... | ||
However, in this case it will be missing documentation of the underlying sampling methods. | ||
We believe that the definition of custom samplers will be rather infrequent. | ||
|
||
However, users will frequently apply sampler instances too continuous pulses. Therefore, a different | ||
approach was required for sampled continuous functions (the output of an continuous pulse function | ||
decorated by a sampler instance). | ||
|
||
A sampler instance is a decorator that may be used to wrap continuous pulse functions such as | ||
linear below: | ||
```python | ||
@left | ||
def linear(times: np.ndarray, m: float, b: float) -> np.ndarray: | ||
```Linear test function | ||
Args: | ||
times: Input times. | ||
m: Slope. | ||
b: Intercept | ||
Returns: | ||
np.ndarray | ||
``` | ||
return m*times+b | ||
``` | ||
Which after decoration may be called with a duration rather than an array of times | ||
```python | ||
duration = 10 | ||
pulse_command = linear(10, 0.1, 0.1) | ||
``` | ||
If one calls help on `linear` they will find | ||
``` | ||
linear(duration:int, *args, **kwargs) -> numpy.ndarray | ||
Discretized continuous pulse function: `linear` using | ||
sampler: `_left`. | ||
|
||
The first argument (time) of the continuous pulse function has been replaced with | ||
a discretized `duration` of type (int). | ||
|
||
Args: | ||
duration (int) | ||
*args: Remaining arguments of continuous pulse function. | ||
See continuous pulse function documentation below. | ||
**kwargs: Remaining kwargs of continuous pulse function. | ||
See continuous pulse function documentation below. | ||
|
||
Sampled continuous function: | ||
|
||
function linear in module test.python.pulse.test_samplers | ||
linear(x:numpy.ndarray, m:float, b:float) -> numpy.ndarray | ||
Linear test function | ||
Args: | ||
x: Input times. | ||
m: Slope. | ||
b: Intercept | ||
Returns: | ||
np.ndarray | ||
``` | ||
This is partly because `functools.wraps` has been used on the underlying function. | ||
This in itself is not sufficient as the signature of the sampled function has | ||
`duration`, whereas the signature of the continuous function is `time`. | ||
|
||
This is acheived by removing `__wrapped__` set by `functools.wraps` in order to preserve | ||
the correct signature and also applying `_update_annotations` and `_update_docstring` | ||
to the generated function which corrects the function annotations and adds an informative | ||
docstring respectively. | ||
|
||
The user therefore has access to the correct sampled function docstring in its entirety, while | ||
still seeing the signature for the continuous pulse function and all of its arguments. | ||
""" | ||
|
||
import functools | ||
from typing import Callable | ||
import textwrap | ||
import pydoc | ||
|
||
import numpy as np | ||
|
||
from qiskit.pulse.samplers import strategies | ||
import qiskit.pulse.commands as commands | ||
|
||
|
||
def _update_annotations(discretized_pulse: Callable) -> Callable: | ||
"""Update annotations of discretized continuous pulse function with duration. | ||
|
||
Args: | ||
discretized_pulse: Discretized decorated continuous pulse. | ||
""" | ||
undecorated_annotations = list(discretized_pulse.__annotations__.items()) | ||
decorated_annotations = undecorated_annotations[1:] | ||
decorated_annotations.insert(0, ('duration', int)) | ||
discretized_pulse.__annotations__ = dict(decorated_annotations) | ||
return discretized_pulse | ||
|
||
|
||
def _update_docstring(discretized_pulse: Callable, sampler_inst: Callable) -> Callable: | ||
"""Update annotations of discretized continuous pulse function. | ||
|
||
Args: | ||
discretized_pulse: Discretized decorated continuous pulse. | ||
sampler_inst: Applied sampler. | ||
""" | ||
wrapped_docstring = pydoc.render_doc(discretized_pulse, '%s') | ||
header, body = wrapped_docstring.split('\n', 1) | ||
body = textwrap.indent(body, ' ') | ||
wrapped_docstring = header+body | ||
updated_ds = """ | ||
Discretized continuous pulse function: `{continuous_name}` using | ||
sampler: `{sampler_name}`. | ||
|
||
The first argument (time) of the continuous pulse function has been replaced with | ||
a discretized `duration` of type (int). | ||
|
||
Args: | ||
duration (int) | ||
*args: Remaining arguments of continuous pulse function. | ||
See continuous pulse function documentation below. | ||
**kwargs: Remaining kwargs of continuous pulse function. | ||
See continuous pulse function documentation below. | ||
|
||
Sampled continuous function: | ||
|
||
{continuous_doc} | ||
""".format(continuous_name=discretized_pulse.__name__, | ||
sampler_name=sampler_inst.__name__, | ||
continuous_doc=wrapped_docstring) | ||
|
||
discretized_pulse.__doc__ = updated_ds | ||
return discretized_pulse | ||
|
||
|
||
def sampler(sample_function: Callable) -> Callable: | ||
"""Sampler decorator base method. | ||
|
||
Samplers are used for converting an continuous function to a discretized pulse. | ||
|
||
They operate on a function with the signature: | ||
`def f(times: np.ndarray, *args, **kwargs) -> np.ndarray` | ||
Where `times` is a numpy array of floats with length `n_times` and the output array | ||
is a complex numpy array with length `n_times`. The output of the decorator is an | ||
instance of `FunctionalPulse` with signature: | ||
`def g(duration: int, *args, **kwargs) -> SamplePulse` | ||
|
||
Note if your continuous pulse function outputs a `complex` scalar rather than a | ||
`np.array`, you should first vectorize it before applying a sampler. | ||
|
||
This class implements the sampler boilerplate for the sampler. | ||
|
||
Args: | ||
sample_function: A sampler function to be decorated. | ||
""" | ||
|
||
def generate_sampler(continuous_pulse: Callable) -> Callable: | ||
"""Return a decorated sampler function.""" | ||
|
||
@functools.wraps(continuous_pulse) | ||
def call_sampler(duration: int, *args, **kwargs) -> commands.SamplePulse: | ||
"""Replace the call to the continuous function with a call to the sampler applied | ||
to the anlytic pulse function.""" | ||
sampled_pulse = sample_function(continuous_pulse, duration, *args, **kwargs) | ||
return np.asarray(sampled_pulse, dtype=np.complex_) | ||
|
||
# Update type annotations for wrapped continuous function to be discrete | ||
call_sampler = _update_annotations(call_sampler) | ||
# Update docstring with that of the sampler and include sampled function documentation. | ||
call_sampler = _update_docstring(call_sampler, sample_function) | ||
# Unset wrapped to return base sampler signature | ||
# but still get rest of benefits of wraps | ||
# such as __name__, __qualname__ | ||
call_sampler.__dict__.pop('__wrapped__') | ||
# wrap with functional pulse | ||
return commands.functional_pulse(call_sampler) | ||
|
||
return generate_sampler | ||
|
||
|
||
def left_sample_fun(continuous_pulse: Callable, duration: int, *args, **kwargs) -> np.ndarray: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. seems like this function should be deleted. |
||
"""Left sample a continuous function. | ||
Args: | ||
continuous_pulse: Continuous pulse function to sample. | ||
duration: Duration to sample for. | ||
*args: Continuous pulse function args. | ||
*kwargs: Continuous pulse function kwargs. | ||
""" | ||
times = np.arange(duration) | ||
return continuous_pulse(times, *args, **kwargs) | ||
|
||
|
||
def left(continuous_pulse: Callable) -> Callable: | ||
r"""Left sampling strategy decorator. | ||
|
||
See `pulse.samplers.sampler` for more information. | ||
|
||
For `duration`, return: | ||
$$\{f(t) \in \mathbb{C} | t \in \mathbb{Z} \wedge 0<=t<\texttt{duration}\}$$ | ||
|
||
Args: | ||
continuous_pulse: To sample. | ||
""" | ||
|
||
return sampler(strategies.left_sample)(continuous_pulse) | ||
|
||
|
||
def right(continuous_pulse: Callable) -> Callable: | ||
r"""Right sampling strategy decorator. | ||
|
||
See `pulse.samplers.sampler` for more information. | ||
|
||
For `duration`, return: | ||
$$\{f(t) \in \mathbb{C} | t \in \mathbb{Z} \wedge 0<t<=\texttt{duration}\}$$ | ||
|
||
Args: | ||
continuous_pulse: To sample. | ||
""" | ||
|
||
return sampler(strategies.right_sample)(continuous_pulse) | ||
|
||
|
||
def midpoint(continuous_pulse: Callable) -> Callable: | ||
r"""Midpoint sampling strategy decorator. | ||
|
||
See `pulse.samplers.sampler` for more information. | ||
|
||
For `duration`, return: | ||
$$\{f(t+0.5) \in \mathbb{C} | t \in \mathbb{Z} \wedge 0<=t<\texttt{duration}\}$$ | ||
|
||
Args: | ||
continuous_pulse: To sample. | ||
""" | ||
return sampler(strategies.midpoint_sample)(continuous_pulse) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
# Copyright 2019, IBM. | ||
# | ||
# This source code is licensed under the Apache License, Version 2.0 found in | ||
# the LICENSE.txt file in the root directory of this source tree. | ||
|
||
# pylint: disable=missing-return-doc | ||
|
||
"""Sampler strategy module for sampler functions. | ||
|
||
Sampler functions have signature. | ||
```python | ||
def sampler_function(continuous_pulse: Callable, duration: int, *args, **kwargs) -> np.ndarray: | ||
... | ||
``` | ||
where the supplied `continuous_pulse` is a function with signature: | ||
```python | ||
def f(times: np.ndarray, *args, **kwargs) -> np.ndarray: | ||
... | ||
``` | ||
The sampler will call the `continuous_pulse` function with a set of times it will decide | ||
according to the sampling strategy it implments along with the passed `args` and `kwargs`. | ||
""" | ||
|
||
from typing import Callable | ||
|
||
import numpy as np | ||
|
||
|
||
def left_sample(continuous_pulse: Callable, duration: int, *args, **kwargs) -> np.ndarray: | ||
"""Left sample a continuous function. | ||
Args: | ||
continuous_pulse: Continuous pulse function to sample. | ||
duration: Duration to sample for. | ||
*args: Continuous pulse function args. | ||
**kwargs: Continuous pulse function kwargs. | ||
""" | ||
times = np.arange(duration) | ||
return continuous_pulse(times, *args, **kwargs) | ||
|
||
|
||
def right_sample(continuous_pulse: Callable, duration: int, *args, **kwargs) -> np.ndarray: | ||
"""Sampling strategy for decorator. | ||
Args: | ||
continuous_pulse: Continuous pulse function to sample. | ||
duration: Duration to sample for. | ||
*args: Continuous pulse function args. | ||
**kwargs: Continuous pulse function kwargs. | ||
""" | ||
times = np.arange(1, duration+1) | ||
return continuous_pulse(times, *args, **kwargs) | ||
|
||
|
||
def midpoint_sample(continuous_pulse: Callable, duration: int, *args, **kwargs) -> np.ndarray: | ||
"""Sampling strategy for decorator. | ||
Args: | ||
continuous_pulse: Continuous pulse function to sample. | ||
duration: Duration to sample for. | ||
*args: Continuous pulse function args. | ||
**kwargs: Continuous pulse function kwargs. | ||
""" | ||
times = np.arange(1/2, duration + 1/2) | ||
return continuous_pulse(times, *args, **kwargs) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
update documentation (
n_times
not defined anywhere)