-
Notifications
You must be signed in to change notification settings - Fork 603
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 dimension checking to QubitUnitary. #1439
Conversation
Hello. You may have forgotten to update the changelog!
|
@@ -125,6 +125,9 @@ def test_heisenberg(self, test_class, tol): | |||
def test_operation_init(self, test_class, monkeypatch): | |||
"Operation subclass initialization." | |||
|
|||
if test_class == qml.QubitUnitary: |
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 to write a separate test for the new __init__
method (see below). I'm actually not sure why it worked before; the issue was that in line 135, the number of wires is computed and then used to construct a list of wires, but QubitUnitary
works on any number of wires so it was yielding a list of length 0 and causing the test to fail.
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.
🤔
tests/test_operation.py
Outdated
op = qml.QubitUnitary(U, wires=wires) | ||
|
||
assert op.name == qml.QubitUnitary.__name__ | ||
assert np.allclose([U], op.data) |
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 and the next line are maybe redundant?
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.
😆
Codecov Report
@@ Coverage Diff @@
## master #1439 +/- ##
=======================================
Coverage 98.23% 98.23%
=======================================
Files 163 163
Lines 12076 12082 +6
=======================================
+ Hits 11863 11869 +6
Misses 213 213
Continue to review full report at Codecov.
|
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 @glassnotes! I left some minor comments in the review itself, but have a bigger request here:
Rather than having a unit test for __init__
(which seems a bit redundant), perhaps it makes more sense to have an integration test, which does the following:
- Tests the validation catches incorrect input and allows correct input using NumPy arrays and tensors
- Tests that
ControlledQubitUnitary
continues to avoid the validation?
pennylane/ops/qubit.py
Outdated
# For pure QubitUnitary operations (not controlled), check that the number | ||
# of wires fits the dimensions of the matrix | ||
if not isinstance(self, ControlledQubitUnitary): | ||
U = np.asarray(params[0]) |
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 realize that the previous version of the code does this, but, I only just noticed it 😆
We can't assume that params[0]
will be a NumPy array - it could very easily be a TensorFlow or a PyTorch tensor. As a result, best to use qml.math
here. E.g.,
U = params[0]
shape = qml.math.shape(U)
(and then down below, qml.math.dot
and qml.math.conj
)
pennylane/ops/qubit.py
Outdated
|
||
@classmethod | ||
def _matrix(cls, *params): | ||
return np.asarray(params[0]) |
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.
Same comment here, this should probably just return params[0]
?
@@ -125,6 +125,9 @@ def test_heisenberg(self, test_class, tol): | |||
def test_operation_init(self, test_class, monkeypatch): | |||
"Operation subclass initialization." | |||
|
|||
if test_class == qml.QubitUnitary: |
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.
🤔
tests/test_operation.py
Outdated
op = qml.QubitUnitary(U, wires=wires) | ||
|
||
assert op.name == qml.QubitUnitary.__name__ | ||
assert np.allclose([U], op.data) |
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.
😆
@josh146 probably, then, it would be best to split out the |
integration tests.
@pytest.mark.parametrize( | ||
"U", [np.array([0]), np.array([1, 0, 0, 1]), np.array([[[1, 0], [0, 1]]])] | ||
) | ||
def test_qubit_unitary_not_matrix_exception(self, U): |
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 removed this test because the case is already covered in the unitary_exceptions
test, where U[1:]
is passed as input.
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 👍
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.
@josh146 probably, then, it would be best to split out the QubitUnitary tests into their own test class like the excitation operations, since otherwise that will be quite a big chunk of test living in the TestOperation class. Furthermore, the extensive decomposition tests from #1427 could be included in the new class as well.
I think this makes a lot of sense, it has annoyed me for a while that that test file is so large and hard to add to!
Also new tests look 💯, thanks so much @glassnotes for adding them. It will make the QubitUnitary
operation a lot more bug resistant going forward.
@pytest.mark.parametrize( | ||
"U", [np.array([0]), np.array([1, 0, 0, 1]), np.array([[[1, 0], [0, 1]]])] | ||
) | ||
def test_qubit_unitary_not_matrix_exception(self, U): |
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 👍
Context: Development of #1427 uncovered a bug where
QubitUnitary
was not checking whether the number of wires matched with the size of the unitary matrix.Description of the Change: Adds an
__init__
method toQubitUnitary
to verify dimensionality and unitarity. Changes up the_matrix
method accordingly. Also, convertsQubitUnitary
functions to useqml.math
, and separatesQubitUnitary
tests into separate integration tests for all interfaces.Benefits: Fixes the bug and clears up ambiguous behaviour such as
which would previously run without warnings.
Possible Drawbacks: None
Related GitHub Issues: None