-
Notifications
You must be signed in to change notification settings - Fork 118
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
Conversation
Two tests failing in test_api: test_nested_function and test_main_from_main |
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.
Reviewed test_api.py
|
||
assert main(4).to_ir() == expected | ||
assert main.make_bound_program(param_values={"n": 4}).to_ir() == expected |
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.
Since these weren't free parameters before, would you mind updating most of the tests to have the meta-programming style like you showed in your doc, with the outer builder function?
That way we can see what the new syntax looks like. I don't think make_bound_program
is the recommended way for customers to pass these meta programming inputs
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 I see. In my head, I interpreted these tests as testing the user story "customer wants to define this program and then build the program with a specific value for its input". I agree make_bound_program
is a bit clunky today for this, but we can add easier methods for Program -> Program
maps that bind variables, either by simply replacing the input declaration with an assignment, or fully inlining as an optimization pass.
If instead these tests were meant to test the codepath "non-input python variables are used during the transpilation step", then I can see how your suggestion is more relevant. It's not obvious to me which is the case, maybe we can chat so I can better understand here
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.
From sync review: We will update with the second interpretation
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
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## feature/autoqasm amazon-braket/amazon-braket-sdk-python#804 +/- ##
==================================================
Coverage 100.00% 100.00%
==================================================
Files 166 166
Lines 9895 9921 +26
Branches 2102 2107 +5
==================================================
+ Hits 9895 9921 +26 ☔ View full report in Codecov by Sentry. |
expected = """OPENQASM 3.0; | ||
qubit[4] __qubits__; | ||
expected_no_arg_partial = """OPENQASM 3.0; | ||
qubit[2] __qubits__; |
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.
The qubit length needs to be max qubit index + 1
qubit[2] __qubits__; | |
qubit[4] __qubits__; |
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.
Do we have any plans to validate this? This scenario seems like an example of when we have sufficient information to assert this
h __qubits__[1]; | ||
cnot __qubits__[1], __qubits__[3];""" | ||
|
||
bell_partial = aq.main(functools.partial(bell, 1)) | ||
assert bell_partial(3).to_ir() == expected | ||
bell_partial = aq.main(num_qubits=2)(functools.partial(bell, 1)) |
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.
Why do we have to supply num_qubits
on this input now? Maybe it's a rare enough use case that we don't have to worry about it. @rmshaffer, thoughts?
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.
Here we need num_qubits
because the program is built with variable qubit indices in use. For the case below, where we pass in a partial fully binding the qubit args, num_qubits
can be omitted. This seems correct to me.
It's worth noting that this function previously always built the Program with all variables bound, even though the aq.main
call was on varying partials. Now that building happens during the decorator call, incomplete partials will have free parameters.
We could test partials on a meta-programming approach that would more closely match the end result of the previous test, but I think this approach is more fitting, as the essence of this test was asserting the behavior of decorating partial functions.
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.
Agreed, this new behavior seems reasonable and the test makes sense to me.
|
||
a = MyClass() | ||
assert aq.main(a.bell)(1, 3).to_ir() == expected | ||
assert a.bell_decorated(1, 3).to_ir() == expected | ||
assert aq.main(num_qubits=2)(a.rx).to_ir() == expected |
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.
Shouldn't rx
be called with q0
?
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.
This line passes the function a.rx
to the decorator; the function isn't actually called at all. You can see from the expected ir that q0
is captured from the signature as an input. Regardless, I will delete this test as mentioned above
bell_noarg_partial = functools.partial(bell_decorated, 1, 3) | ||
assert bell_noarg_partial().to_ir() == expected | ||
bell_noarg_partial = aq.main(num_qubits=2)(functools.partial(bell, 1, 3)) | ||
assert bell_noarg_partial.to_ir() == expected_no_arg_partial | ||
|
||
|
||
def test_classmethod() -> None: |
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.
@rmshaffer @yitchen-tim is this important functionality? It seems like we lose this feature, of AutoQASM instance and class methods, if we go this route
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.
Here is some context from Ryan's comment on this test:
I believe I added these class method tests primarily for getting to 100% code coverage in transpiler, and since it was pretty easy to make it work in the old model.
I think it's legitimate to say that this no longer makes sense in the world where the program conversion happens at function declaration time, and we don't need to support this.
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.
I don't think it's important. A user can still easily make a class method which wraps an @aq.main
function and returns the Program
(which is essentially what it previously meant to mark an instance/class method as @aq.main
).
test/unit_tests/braket/experimental/autoqasm/test_transpiler.py
Outdated
Show resolved
Hide resolved
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.
Reviewed through notebook 4
@ajberdy what's the current status of this PR... seems like it's getting close? If you could please just ping @laurencap and me when all of the TODOs/comments have been addressed (or have issues opened) and you are ready for a final PR review pass. |
@rmshaffer Yes, looks like it's getting close! There are 3 TODOs on this PR, all tagged with issues and to be fixed in later PRs (#809, amazon-braket/autoqasm#14, amazon-braket/autoqasm#7). I believe @laurencap has a couple example notebooks left to review and possibly some remaining test files. I've responded and acted on all the comments, so there aren't currently any action items for the PR on my end. Once all the files are reviewed, I'd say we're good for a final pass. |
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.
A few very minor comments. Overall looks good to me, approving from my end but also deferring to @laurencap after she has a chance to re-review.
@@ -69,8 +69,12 @@ | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"# TODO: determine if this example should make these variables inputs to the program\n", |
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.
Reminder to remove this TODO before merging. IMO, keeping these as Python variables is fine. (Actually I'm not sure how you'd make qubit
an input given how it's used in a format string.)
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.
Technically, should work with qubit as an int. But agree that's not as straightforward as using it directly
Program | partial: A callable | ||
which returns the converted quantum program when called. |
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.
Program | partial: A callable | |
which returns the converted quantum program when called. | |
Program | partial: The Program object containing the converted quantum program, or a partial function | |
which returns the converted quantum program when called. |
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.
Good catch! Still a bit reminiscent of the old interface, so I've updated this doc string to
Program | partial: The Program object containing the converted quantum program, or a
partial function of the `main` decorator.
(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)
def _test_on_local_sim(program: aq.Program, inputs=None) -> None: | ||
device = LocalSimulator(backend=StateVectorSimulator()) | ||
task = device.run(program, shots=10) | ||
task = device.run(program, shots=10, inputs=inputs or {}) |
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.
[nit] any reason to not just have inputs={}
as the default value?
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.
Yes, Python behaves weirdly when you put a mutable object as a default in a function signature (if you mutate the object, it mutates the default for the function). (source)
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.
I had no idea.
Issue #, if available:
Fixes #805, fixes #806, fixes #813
Description of changes:
aq.main
to build AutoQASM program directly.Wrapped testing programs in fixtures so they don't get built during other tests.
Testing done:
Unit tests updated and passing, notebook tests passing as well.
Merge Checklist
Put an
x
in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your pull request.General
Tests
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.