pennylane icon indicating copy to clipboard operation
pennylane copied to clipboard

[BUG] Incorrect single-shot Hamiltonian expectation

Open josh146 opened this issue 2 years ago • 2 comments

Expected behavior

Consider the following circuit:

dev = qml.device("lightning.qubit", wires=2, shots=1)
H = qml.PauliZ(0) + qml.PauliZ(1)

@qml.qnode(dev)
def cost_function3():
    qml.Hadamard(0)
    qml.CNOT(wires=[0, 1])
    return qml.expval(H)

This circuit will only generate states $|00\rangle$ and $|11\rangle$.

If we print the matrix of the Hamiltonian,

>>> qml.matrix(H)
array([[ 2.+0.j,  0.+0.j,  0.+0.j,  0.+0.j],
       [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j],
       [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j],
       [ 0.+0.j,  0.+0.j,  0.+0.j, -2.+0.j]])

we see that we should only get expectation values of 2 (corresponding to state $|00\rangle$) and -2 ($|11\rangle$).

Actual behavior

This is not the case:

>>> for i in range(10):
...     print(cost_function3(), dev._samples)
0.0 [[1 1]]
2.0 [[0 0]]
-2.0 [[1 1]]
2.0 [[0 0]]
0.0 [[0 0]]
0.0 [[1 1]]
0.0 [[0 0]]
0.0 [[0 0]]
2.0 [[0 0]]
0.0 [[1 1]]

Additional information

Note that if we turn on grouping of the Hamiltonian (H.compute_grouping()) before creating the QNode, the bug is gone:

dev = qml.device("lightning.qubit", wires=2, shots=1)
H = qml.PauliZ(0) + qml.PauliZ(1)
H.compute_grouping()

@qml.qnode(dev)
def cost_function3():
    qml.Hadamard(0)
    qml.CNOT(wires=[0, 1])
    return qml.expval(H)
>>> for i in range(10):
...     print(cost_function3(), dev._samples)
-2.0 [[1 1]]
-2.0 [[1 1]]
2.0 [[0 0]]
-2.0 [[1 1]]
-2.0 [[1 1]]
-2.0 [[1 1]]
-2.0 [[1 1]]
-2.0 [[1 1]]
-2.0 [[1 1]]
2.0 [[0 0]]

Source code

No response

Tracebacks

No response

System information

.

Existing GitHub issues

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

josh146 avatar Jun 13 '22 22:06 josh146

Update: what is happening is that, in the non-grouped version, two circuits are being executed under the hood, one for each term in the Hamiltonian sum. Since the two circuit executions are not necessarily correlated, it is possible for one circuit to output 2 and the other to output -2, leading to 0.

So this is probably not a bug, and moreso, unexpected behaviour resulting from how PL treats shots. shots=1 does not mean a single shot for the constructed circuit, it means "The device may execute multiple circuits, with each execution taking 1 shot". So it is better to think of the non-grouped example as taking 2 shots.

So the solution to this 'bug'/un-intuitive behaviour is probably more long-term, and involves changing how shots are budgeted in PennyLane.

josh146 avatar Jun 13 '22 22:06 josh146

>>> dev = qml.device("lightning.qubit", wires=2, shots=1)
>>> H = qml.PauliZ(0) + qml.PauliZ(1)
>>> @qml.qnode(dev)
... def cost_function3():
...     qml.Hadamard(0)
...     qml.CNOT(wires=[0, 1])
...     return qml.expval(H)
>>> with qml.Tracker(dev) as tracker:
...     cost_function3()
>>> tracker.totals
{'executions': 2, 'shots': 2, 'batches': 1, 'batch_len': 2}

josh146 avatar Jun 13 '22 22:06 josh146