pennylane icon indicating copy to clipboard operation
pennylane copied to clipboard

[BUG] `qml.sample` with no observable fails for shot vectors

Open antalszava opened this issue 3 years ago • 1 comments

Expected behavior

qml.sample with no observable works well with shot vectors.

Actual behavior

Error raised.

Additional information

A more bigger scope change is upcoming, that will likely contain a bug fix to this.

Source code

import pennylane as qml

dev = qml.device("default.qubit", wires=3, shots=[10, 1000])

@qml.qnode(device=dev)
def circuit(x):
    qml.Hadamard(wires=[0])
    qml.CRX(x, wires=[0, 1])
    return qml.sample(wires=[0])

circuit(0.5)

Tracebacks

~/pennylane/_qubit_device.py in sample(self, observable, shot_range, bin_size, counts)
   1062             ]
   1063         return (
-> 1064             samples.reshape((3, bin_size, -1))
   1065             if no_observable_provided
   1066             else samples.reshape((bin_size, -1))

ValueError: cannot reshape array of size 10 into shape (3,10,newaxis)

System information

PennyLane master branch

Existing GitHub issues

  • [X] I have searched existing GitHub issues to make sure the issue does not already exist.

antalszava avatar Jul 16 '22 01:07 antalszava

I think this can be addressed by doing the following:

In pennylane/_qubit_device.py:1058, make the following changes:

        num_wires = len(device_wires) if len(device_wires) > 0 else self.num_wires
        if counts:
            shape = (-1, bin_size, num_wires) if no_observable_provided else (-1, bin_size)
            return [
                _samples_to_counts(bin_sample, no_observable_provided)
                for bin_sample in samples.reshape(shape)
            ]
        return (
            samples.reshape((num_wires, bin_size, -1))
            if no_observable_provided
            else samples.reshape((bin_size, -1))
        )

instead of

        if counts:
            shape = (-1, bin_size, 3) if no_observable_provided else (-1, bin_size)
            return [
                _samples_to_counts(bin_sample, no_observable_provided)
                for bin_sample in samples.reshape(shape)
            ]
        return (
            samples.reshape((3, bin_size, -1))
            if no_observable_provided
            else samples.reshape((bin_size, -1))
        )

Hardcoding the shape of the samples when raw samples are returned to be (3, bin_size, -1) only works when the number of wires being sampled is 3. To this effect, the testing suite in tests/test_measurements.py:TestCounts should also be expanded to include tests where varying numbers of wires are being sampled when counts=True.

Once I made the changes I proposed locally, I found another related bug. I noticed that only the counts for the first item in the shot vector show. An example is shown below:

>>> dev = qml.device("default.qubit", wires=4, shots=[10, 5])
>>> @qml.qnode(dev)
... def test():
...     qml.PauliX(wires=0)
...     qml.PauliY(wires=1)
...     qml.CNOT(wires=[1, 2])
...     return qml.sample(counts=True)
>>> test()
{'1110': 10}

Upon following the trace, I found that the output was being changed at pennylane/qnode.py:653:

        if self._qfunc_output.return_type is qml.measurements.Counts:
            # return a dictionary with counts not as a single-element array
            return res[0]

Since only the first item in res is returned, the dictionaries for all shot vectors except for the first one are removed.

Hope this is helpful!

mudit2812 avatar Jul 19 '22 21:07 mudit2812

Note that this was resolved as per Mudit's points. Thanks for that!

The code runs without errors, it does warn VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences that should be cleared by the new return types system.

antalszava avatar Oct 18 '22 14:10 antalszava