-
Notifications
You must be signed in to change notification settings - Fork 121
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
feat: build autoqasm program directly from main decorator #804
Changes from 38 commits
621ae7c
ce413b5
1b2ebcb
802c1f9
71041f8
39ed22d
c082e83
53ad41b
a2193e7
264aaa5
79e3c08
3b0807c
805b816
d4d5430
552c9d0
0d27fb0
d61dddb
b5a7e5d
87f07c0
9282b8d
42a8b18
ef30424
e189311
1f76b13
b8757da
073a11b
608a5f3
3c64370
3e9d8d8
4f7b7d3
5319f02
6af70b7
0ffb40f
4077c12
d69312c
d1ba303
3635dee
3f2529e
6fb0644
b555794
18daf11
448fda4
55e2e7b
cafe0ae
598af87
af6acec
b1590fe
b2bcd7e
679bade
4406d4c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -42,16 +42,16 @@ | |||||||||
from braket.experimental.autoqasm.instructions.qubits import QubitIdentifierType as Qubit | ||||||||||
from braket.experimental.autoqasm.instructions.qubits import is_qubit_identifier_type | ||||||||||
from braket.experimental.autoqasm.program.gate_calibrations import GateCalibration | ||||||||||
from braket.parametric import FreeParameter | ||||||||||
|
||||||||||
|
||||||||||
def main( | ||||||||||
func: Optional[Callable] = None, | ||||||||||
*, | ||||||||||
num_qubits: Optional[int] = None, | ||||||||||
device: Optional[Union[Device, str]] = None, | ||||||||||
) -> Callable[..., aq_program.Program]: | ||||||||||
"""Decorator that converts a function into a callable that returns | ||||||||||
a Program object containing the quantum program. | ||||||||||
) -> aq_program.Program | functools.partial: | ||||||||||
"""Decorator that converts a function into a Program object containing the quantum program. | ||||||||||
|
||||||||||
The decorator re-converts the target function whenever the decorated | ||||||||||
function is called, and a new Program object is returned each time. | ||||||||||
|
@@ -65,13 +65,22 @@ def main( | |||||||||
program. Can be either an Device object or a valid Amazon Braket device ARN. | ||||||||||
|
||||||||||
Returns: | ||||||||||
Callable[..., Program]: A callable which returns the converted | ||||||||||
quantum program when called. | ||||||||||
Program | partial: A callable | ||||||||||
which returns the converted quantum program when called. | ||||||||||
rmshaffer marked this conversation as resolved.
Show resolved
Hide resolved
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.
Suggested change
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. Good catch! Still a bit reminiscent of the old interface, so I've updated this doc string to
(Technically the partial function returns the Program when called on the decorated function, but the language seems to imply an interface like we previously had) |
||||||||||
""" | ||||||||||
if isinstance(device, str): | ||||||||||
device = AwsDevice(device) | ||||||||||
|
||||||||||
return _function_wrapper( | ||||||||||
# decorator is called on a Program | ||||||||||
if isinstance(func, aq_program.Program): | ||||||||||
return func | ||||||||||
|
||||||||||
# decorator is used with parentheses | ||||||||||
# (see _function_wrapper for more details) | ||||||||||
if not (func and callable(func)): | ||||||||||
return functools.partial(main, num_qubits=num_qubits, device=device) | ||||||||||
|
||||||||||
program_builder = _function_wrapper( | ||||||||||
func, | ||||||||||
converter_callback=_convert_main, | ||||||||||
converter_args={ | ||||||||||
|
@@ -82,6 +91,8 @@ def main( | |||||||||
}, | ||||||||||
) | ||||||||||
|
||||||||||
return program_builder() | ||||||||||
|
||||||||||
|
||||||||||
def subroutine(func: Optional[Callable] = None) -> Callable[..., aq_program.Program]: | ||||||||||
"""Decorator that converts a function into a callable that will insert a subroutine into | ||||||||||
|
@@ -182,7 +193,7 @@ def _wrapper(*args, **kwargs) -> Callable: | |||||||||
optional_features=_autograph_optional_features(), | ||||||||||
) | ||||||||||
# Call the appropriate function converter | ||||||||||
return converter_callback(func, options, args, kwargs, **converter_args) | ||||||||||
return converter_callback(func, options=options, args=args, kwargs=kwargs, **converter_args) | ||||||||||
|
||||||||||
if inspect.isfunction(func) or inspect.ismethod(func): | ||||||||||
_wrapper = functools.update_wrapper(_wrapper, func) | ||||||||||
|
@@ -204,7 +215,7 @@ def _convert_main( | |||||||||
args: tuple[Any], | ||||||||||
kwargs: dict[str, Any], | ||||||||||
user_config: aq_program.UserConfig, | ||||||||||
) -> None: | ||||||||||
) -> aq_program.Program: | ||||||||||
"""Convert the initial callable `f` into a full AutoQASM program `program`. | ||||||||||
Puts the contents of `f` at the global level of the program, rather than | ||||||||||
putting it into a subroutine as done in `_convert_subroutine`. | ||||||||||
|
@@ -218,16 +229,25 @@ def _convert_main( | |||||||||
args (tuple[Any]): Arguments passed to the program when called. | ||||||||||
kwargs (dict[str, Any]): Keyword arguments passed to the program when called. | ||||||||||
user_config (UserConfig): User-specified settings that influence program building. | ||||||||||
|
||||||||||
Returns: | ||||||||||
aq_program.Program: Generated AutoQASM Program. | ||||||||||
""" | ||||||||||
if aq_program.in_active_program_conversion_context(): | ||||||||||
raise errors.AutoQasmTypeError( | ||||||||||
f"Cannot call main function '{f.__name__}' from another main function. Did you mean " | ||||||||||
"to use '@aq.subroutine'?" | ||||||||||
) | ||||||||||
kwargs = {} | ||||||||||
parameters = inspect.signature(f).parameters | ||||||||||
|
||||||||||
with aq_program.build_program(user_config) as program_conversion_context: | ||||||||||
# Capture inputs to decorated function as `FreeParameter` inputs for the Program | ||||||||||
for param in parameters.values(): | ||||||||||
if param.kind == param.POSITIONAL_OR_KEYWORD: | ||||||||||
kwargs[param.name] = FreeParameter(param.name) | ||||||||||
param_type = param.annotation if param.annotation is not param.empty else float | ||||||||||
program_conversion_context.register_parameter(param.name, param_type) | ||||||||||
else: | ||||||||||
raise NotImplementedError | ||||||||||
|
||||||||||
# Process the program | ||||||||||
aq_transpiler.converted_call(f, args, kwargs, options=options) | ||||||||||
aq_transpiler.converted_call(f, (), kwargs, options=options) | ||||||||||
|
||||||||||
# Modify program to add global declarations if necessary | ||||||||||
_add_qubit_declaration(program_conversion_context) | ||||||||||
|
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.
Just a note, please also run
tox -e notebooks
to test the service-dependent example notebooks that currently don't run in CI.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.
Ah, thanks for pointing this out! Just confirmed that it passes with the latest updates; I'll note this in the PR description