PennyLaneAI / pennylane

PennyLane is a cross-platform Python library for quantum computing, quantum machine learning, and quantum chemistry. Train a quantum computer the same way as a neural network.
https://pennylane.ai
Apache License 2.0
2.34k stars 602 forks source link

Return every outcome with counts (those with `0` counts too) #2864

Closed antalszava closed 2 years ago

antalszava commented 2 years ago

Feature details

The ability to compute the counts of outcomes from raw samples has recently been added to PennyLane (https://github.com/PennyLaneAI/pennylane/pull/2686 https://github.com/PennyLaneAI/pennylane/pull/2839).

The output of the new measurement is a dictionary where keys are the outcomes and the values are counts. Possible outcomes that were not observed do not appear in the dictionary.

We would to switch to listing all possible outcomes in the resulting dictionary.

Considering the following QNode evaluation:

import pennylane as qml

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

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

circuit(0.3)

We would like the output structure to be changed from {'000': 531, '100': 469} to become {'000': 531, 001': 0, '010': 0, '011': 0, '100': 469, '101': 0, '110': 0 '111': 0}.

Note that in the above example, no observables or wires were passed to qml.sample. When providing an observable, then all possible eigenvalues should appear as keys in the dictionary, e.g.,

import pennylane as qml

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

@qml.qnode(device=dev)
def circuit(x):
    return qml.sample(qml.PauliZ(0), counts=True)

circuit(0.3)

Instead of {1: 1000} the output should be changed to {-1: 0, 1: 1000}.

Implementation

Potentially done using a defaultdict object. The implementation has to update the logic in QubitDevice and in specific most likely the QubitDevice.sample method and its capability to obtain counts.

How important would you say this feature is?

1: Not important. Would be nice to have.

Additional information

No response

lillian542 commented 2 years ago

@antalszava Hi! I installed PennyLane a couple days ago just to play around with it a bit, and using ‘counts=True’ as a kwarg was working as in the example given here. 

This afternoon I forked the repository and got set up properly to start looking at the issue, but it seems like some recent changes have updated so that ‘sample’ and ‘counts’ are separate, so that running the example code throws a TypeError.

I can update the qml.counts method for this fix, it doesn’t seem to me like the recent modifications change the task much. But since it seems like maybe other modifications to the ‘counts’ feature are being made simultaneously, I wanted to check if there is anyone I should coordinate with.

Thanks, Lillian

lillian542 commented 2 years ago

One other question: the issue description suggests updating the logic in the QubitDevice.sample object, but it seems like qml.sample calls the 'sample' function from measurements.py, not QubitDevice.sample

Should the QubitDevice.sample method be updated to return 0 counts, or is it being phased out in favour of qml.counts and qml.sample? It seems like a confusing duplicity that qml.sample and dev.sample are different functions that live in different files and take different arguments.

antalszava commented 2 years ago

Hi @lillian542,

I can update the qml.counts method for this fix, it doesn’t seem to me like the recent modifications change the task much. But since it seems like maybe other modifications to the ‘counts’ feature are being made simultaneously, I wanted to check if there is anyone I should coordinate with.

Yes, great catch there! We just changed the UI of counts to go from qml.sample(..., counts=True) to qml.counts. No further changes are planned.

Should the QubitDevice.sample method be updated to return 0 counts, or is it being phased out in favour of qml.counts and qml.sample? It seems like a confusing duplicity that qml.sample and dev.sample are different functions that live in different files and take different arguments.

The logic of determining the counts themselves is contained in QubitDevice.sample. The functions in measurements.py (i.e., qml.sample and qml.counts) createMeasurementProcessobjects that are then later processed by the device. That processing is done in theQubitDevice.statistics` method.

Hope this helps, let us know if you have further questions!

lillian542 commented 2 years ago

Hi @antalszava! Thanks so much, that's very helpful - I was getting a bit mixed up with how everything fit together, it makes a lot more sense now.

lillian542 commented 2 years ago

Hi again! When updating the measurements.rst doc file to reflect changes, I noticed an example where the counts function is used along with other measurement functions. That is:



@qml.qnode(dev)
def circuit():
    qml.Hadamard(wires=0)
    qml.CNOT(wires=[0,1])
    qml.PauliX(wires=2)
    return qml.expval(qml.PauliZ(0)),qml.expval(qml.PauliZ(1)), qml.counts()

This is not currently working, either on my current working branch or the master branch. Instead of returning an outcome like the one shown in measurements.rst:

result = circuit() print(result) [tensor(0.026, requires_grad=True) tensor(0.026, requires_grad=True) tensor({'001': 513, '111': 487}, dtype=object, requires_grad=True)]

it returns a WireError.

Should I flag this as a separate issue, or include a fix in the PR for this? Or does it just work differently now that counts is run as qml.counts() instead of qml.sample(counts=True)?

@antalszava

lillian542 commented 2 years ago

This is not currently working, either on my current working branch or the master branch.

Never mind, I now realize it was just an issue with the argument passed to PauliX in the example.

lillian542 commented 2 years ago

EDIT: I'll move these questions to the pull request, they don't seem like they really belong here.

I'm almost ready to submit a pull request, but I haven't run software testing and docs testing before, and I have a couple of questions:

  1. I've updated a few of the tests to reflect the new dictionary keys, but I get an error when running the test for TestCounts.test_multi_wire_counts_regular_shape that I'm not sure how to fix - any feedback on this would be much appreciated:

assert all(all(v.dtype == np.dtype("int") for v in r.values()) for r in result) AttributeError: 'int' object has no attribute 'dtype'

  1. When I run 'make docs', I get the error xcrun: error: invalid active developer path. Suggested solutions for this online seem to include downloading a 15 GB program, and I only have 5GB of space left on my personal computer. Would it be possible for someone else to run the 'make docs' check?

@antalszava

lillian542 commented 2 years ago

@antalszava

Hi! I’ve created a pull request now, but its still currently a draft - I haven’t quite gotten it to pass the last core-and-interface-tests. Is it possible someone could take a look at help me understand what’s going on? More details on the PR. 



Once this is done, the PR should be ready for review.

antalszava commented 2 years ago

Hi @lillian542, yes, will go through the PR later today. :+1: Thanks for submitting!

timmysilv commented 2 years ago

🎉