-
Notifications
You must be signed in to change notification settings - Fork 18
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
Fix issue when using a wire subset with BasisState
#61
Conversation
Codecov Report
@@ Coverage Diff @@
## master #61 +/- ##
==========================================
+ Coverage 99.10% 99.14% +0.03%
==========================================
Files 8 8
Lines 334 349 +15
==========================================
+ Hits 331 346 +15
Misses 3 3
Continue to review full report at Codecov.
|
pennylane_cirq/simulator_device.py
Outdated
try: | ||
wires = self.map_wires(qubit_state_vector_operation.wires) | ||
except WireError: | ||
wires = qubit_state_vector_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'm not sure why this is, but there are cases when QubitStateVector
is called on more wires than the device has. E.g., where it's called on wires [0, 1]
but the device only has 1 wire, as in the following test.
def test_var_single_wire_no_parameters(
self, simulator_device_1_wire, tol, operation, input, expected_output
):
"""Tests that variances are properly calculated for single-wire observables without parameters."""
op = operation(0, do_queue=False)
simulator_device_1_wire.reset()
simulator_device_1_wire.apply(
[qml.QubitStateVector(np.array(input), wires=[0, 1])],
rotations=op.diagonalizing_gates(),
)
This causes issues with map_wires
(thus the try-except) and also the _expand_state
below (thus the if len(wires) < self.num_wires ...
instead of if len(wires) != self.num_wires ...
, as I first wrote).
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.
@thisac I would consider this a bug in the tests :) I suggest:
- removing the try-except here
- updating the test to use a 2 wire device
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 did a quick skim through, looks good so far @thisac! I haven't done a proper review yet, as I noticed there are no tests (yet) to ensure that this bug is fixed
pennylane_cirq/simulator_device.py
Outdated
try: | ||
wires = self.map_wires(qubit_state_vector_operation.wires) | ||
except WireError: | ||
wires = qubit_state_vector_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.
@thisac I would consider this a bug in the tests :) I suggest:
- removing the try-except here
- updating the test to use a 2 wire device
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 @thisac! Tests look very nice.
Only suggestion is to use standard NumPy rather than the device static methods.
self._initial_state = np.zeros(2 ** len(self.qubits), dtype=np.complex64) | ||
basis_state_idx = np.sum(2 ** np.argwhere(np.flip(basis_state_array) == 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.
Oh, this is a permutation of converting from base2 to base10 that I hadn't seen before!
Interestingly, however, this seems to be a rare case where the native Python approach is still faster:
>>> bs = np.array([1, 1, 0, 1, 1, 0, 0, 1, 1])
>>> int("".join(str(i) for i in bs), 2)
435
>>> np.sum(2 ** np.argwhere(np.flip(bs) == 1))
435
>>> %timeit np.sum(2 ** np.argwhere(np.flip(bs) == 1))
41.4 µs ± 4.82 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> %timeit int("".join(str(i) for i in bs), 2)
14.4 µs ± 489 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
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.
Interesting! I feel like the Python solution is somewhat cleaner. The time spent there is almost all in the "".join
while the NumPy solution spends a significant amounts of time in np.argwhere
, 2 **
and np.sum
. I guess there's just more that's going on there. 🤔
pennylane_cirq/simulator_device.py
Outdated
state_vector = self._scatter(ravelled_indices, state_vector, [2 ** self.num_wires]) | ||
state_vector = self._reshape(state_vector, [2] * self.num_wires) | ||
state_vector = self._asarray(state_vector, dtype=self.C_DTYPE) | ||
|
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 as over on the Qulacs PR, recommend changing this to simply use native NumPy functions :)
dev = SimulatorDevice(device_wires) | ||
res = dev._expand_state(state, op_wires) | ||
|
||
assert np.allclose(res, expected, **tol) |
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.
great tests!
Fixes an issue when calling
BasisState
when using a subset of the device wires.The following now works:
Previously it raised
DeviceError: For BasisState, the state has to be specified for the correct number of qubits. Got a state for 1 qubits, expected 2.
and now it should output the correct
[1, -1]
.