-
Notifications
You must be signed in to change notification settings - Fork 600
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
Update default qubit so that _apply_operation is more functional #1025
Conversation
if isinstance(operation, QubitStateVector): | ||
self._apply_state_vector(operation.parameters[0], operation.wires) | ||
elif isinstance(operation, BasisState): | ||
self._apply_basis_state(operation.parameters[0], operation.wires) |
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.
These didn't really fit into the idea of applying an operation to a state, since these provide the initial state.
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.
Makes sense. One thing to consider would be to change them such that the state is returned rather than internally updated. This would be useful because then there would be no difference between applying a state preparation and applying an operation in terms of syntax.
self._state = self._apply_state_vector(operation.parameters[0], operation.wires)
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.
We could name the functions self._create_basis_state(state, wires)
and self._create_state_vector
and have them both return a state variable.
They aren't really "applying" something since it overrides what existed before, if anything existed before.
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.
Thanks! I agree with both suggestions, but suggest we defer them until after release as part of another look at default.qubit
.
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #1025 +/- ##
==========================================
- Coverage 97.92% 97.92% -0.01%
==========================================
Files 151 151
Lines 11231 11228 -3
==========================================
- Hits 10998 10995 -3
Misses 233 233 ☔ View full report in Codecov by Sentry. |
if isinstance(operation, QubitStateVector): | ||
self._apply_state_vector(operation.parameters[0], wires) | ||
return | ||
|
||
if isinstance(operation, BasisState): | ||
self._apply_basis_state(operation.parameters[0], wires) | ||
return | ||
|
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.
These have been moved to the parent apply()
method.
@@ -527,19 +525,23 @@ def _apply_basis_state(self, state, wires): | |||
|
|||
self._state = self._create_basis_state(num) | |||
|
|||
def _apply_unitary(self, mat, wires): | |||
def _apply_unitary(self, state, mat, wires): |
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.
Ideally we could think about making these as static methods or simply functions, but we do still use self
for things like self._tensordot
, i.e., interface-dependent calculations.
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 second making them static functions 👍
Keeping them methods in the class keeps them organized and contained a bit better, but they should probably be made static methods.
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 is a nice idea, which would make things even cleaner. 👍
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.
Note: eventually self._tensordot
and all the other ML static methods can be replaced by qml.math.tensordot
etc.
But until we do that, we'll likely need to keep them as instance methods 🙁
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.
Oh awesome, am loving the idea to use qml.math
!
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.
Looks good to me! 💯
if isinstance(operation, QubitStateVector): | ||
self._apply_state_vector(operation.parameters[0], operation.wires) | ||
elif isinstance(operation, BasisState): | ||
self._apply_basis_state(operation.parameters[0], operation.wires) |
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.
Makes sense. One thing to consider would be to change them such that the state is returned rather than internally updated. This would be useful because then there would be no difference between applying a state preparation and applying an operation in terms of syntax.
self._state = self._apply_state_vector(operation.parameters[0], operation.wires)
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 like it!
I'm pleasantly surprised this required no changes to |
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.
With this change, users will also have an easier time playing around in the simulator itself.
"Let's just apply this operation and see how it changes things"
"What's the state look like now, halfway through this circuit?"
I think this will show benefits elsewhere, in addition to just the rewind tape situation.
|
||
def _apply_operation(self, operation): | ||
"""Applies operations to the internal device state. | ||
def _apply_operation(self, state, operation): |
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'm mostly going on feeling here, but what about:
_apply_operation(operation, state)
My brain reads that more as "Apply operation
onto state
" instead of "update state
with operation
". It just seems a bit easier to process and remember.
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.
Nice that could work! In the interests of time I'd rather stick with the current, but we could consider swapping around post-release.
@@ -527,19 +525,23 @@ def _apply_basis_state(self, state, wires): | |||
|
|||
self._state = self._create_basis_state(num) | |||
|
|||
def _apply_unitary(self, mat, wires): | |||
def _apply_unitary(self, state, mat, wires): |
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 second making them static functions 👍
Keeping them methods in the class keeps them organized and contained a bit better, but they should probably be made static methods.
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.
Looks great! I like the functional route we're on! 🌌
@@ -527,19 +525,23 @@ def _apply_basis_state(self, state, wires): | |||
|
|||
self._state = self._create_basis_state(num) | |||
|
|||
def _apply_unitary(self, mat, wires): | |||
def _apply_unitary(self, state, mat, wires): |
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 is a nice idea, which would make things even cleaner. 👍
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.
Thanks for the comments!
if isinstance(operation, QubitStateVector): | ||
self._apply_state_vector(operation.parameters[0], operation.wires) | ||
elif isinstance(operation, BasisState): | ||
self._apply_basis_state(operation.parameters[0], operation.wires) |
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.
Thanks! I agree with both suggestions, but suggest we defer them until after release as part of another look at default.qubit
.
@@ -527,19 +525,23 @@ def _apply_basis_state(self, state, wires): | |||
|
|||
self._state = self._create_basis_state(num) | |||
|
|||
def _apply_unitary(self, mat, wires): | |||
def _apply_unitary(self, state, mat, wires): |
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.
Oh awesome, am loving the idea to use qml.math
!
|
||
def _apply_operation(self, operation): | ||
"""Applies operations to the internal device state. | ||
def _apply_operation(self, state, operation): |
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.
Nice that could work! In the interests of time I'd rather stick with the current, but we could consider swapping around post-release.
This is an update to the internals of
default.qubit
so that the_apply_operation
method accepts an input state and returns an output state. This allows for external functions to interact with the device and request an evolved state - something we need in #1017.It appears that we do not need to update the tests, since we have not written fully comprehensive unit tests for each method in
default.qubit
. The current tests use theapply()
method, which is one step above_apply_operation
, so at least the new changes are still being tested implicitly.