Skip to content
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

[BUG] Confusing behavior with the wires argument of qml.probs #3741

Closed
1 task done
bdoolittle opened this issue Feb 8, 2023 · 6 comments · Fixed by #3753
Closed
1 task done

[BUG] Confusing behavior with the wires argument of qml.probs #3741

bdoolittle opened this issue Feb 8, 2023 · 6 comments · Fixed by #3753
Assignees
Labels
bug 🐛 Something isn't working

Comments

@bdoolittle
Copy link

Expected behavior

Consider a case where where a device's wires are ordered as [2,0,1] and we'd like to get the probabilities associated with these wires in the order [0,1,2]. That is, if a qml.PauliX(wires=[0]) operation is applied, I would expect qml.probs(wires=[0,1,2]) to return the probabilities [0,0,0,0,1,0,0,0] because 0 is the ordered as the most significant bit in the wire ordering.

In the two following code snippets, I expect the [0,0,0,0,1,0,0,0] probability vector to be returned when qml.PauliX is applied to wire [0].

  1. Explicitly specifying the operation's wire:
dev = qml.device("default.qubit", wires=[2,0,1])
@qml.qnode(dev)
def circ():
    qml.PauliX(wires=[0])
    return qml.probs(wires=[0,1,2])

circ()
  1. Specifying the operation's wire based on its relative position in dev.wires:
dev = qml.device("default.qubit", wires=[2,0,1])
@qml.qnode(dev)
def circ():
    qml.PauliX(wires=dev.wires[1])
    return qml.probs(wires=[0,1,2])

circ()

I believe that this expectation should hold because I specify that the qml.PauliX operates upon the [0] wire and that qml.probs(wires=[0,1,2]) should return the probabilities ordered with the [0] wire being the most significant bit.

Actual behavior

Case 1.

dev = qml.device("default.qubit", wires=[2,0,1])
@qml.qnode(dev)
def circ():
    qml.PauliX(wires=[0])
    return qml.probs(wires=[0,1,2])

circ()

Returns:

tensor([0., 1., 0., 0., 0., 0., 0., 0.], requires_grad=True)

Case 2:

dev = qml.device("default.qubit", wires=[2,0,1])
@qml.qnode(dev)
def circ():
    qml.PauliX(wires=dev.wires[1])
    return qml.probs(wires=[0,1,2])

circ()

Returns:

tensor([0., 1., 0., 0., 0., 0., 0., 0.], requires_grad=True)

Problem

In both cases, it appears that the qml.PauliX operation is applied to wire [2] instead of wire [0] like I intended.

Additional information

No response

Source code

No response

Tracebacks

No response

System information

Name: PennyLane
Version: 0.28.0
Summary: PennyLane is a Python quantum machine learning library by Xanadu Inc.
Home-page: https://github.com/XanaduAI/pennylane
Author: 
Author-email: 
License: Apache License 2.0
Location: /Users/brian/opt/anaconda3/lib/python3.9/site-packages
Requires: appdirs, autograd, autoray, cachetools, networkx, numpy, pennylane-lightning, requests, retworkx, scipy, semantic-version, toml
Required-by: PennyLane-Lightning, qNetVO

Platform info:           macOS-10.16-x86_64-i386-64bit
Python version:          3.9.13
Numpy version:           1.21.5
Scipy version:           1.9.1
Installed devices:
- lightning.qubit (PennyLane-Lightning-0.28.1)
- default.gaussian (PennyLane-0.28.0)
- default.mixed (PennyLane-0.28.0)
- default.qubit (PennyLane-0.28.0)
- default.qubit.autograd (PennyLane-0.28.0)
- default.qubit.jax (PennyLane-0.28.0)
- default.qubit.tf (PennyLane-0.28.0)
- default.qubit.torch (PennyLane-0.28.0)
- default.qutrit (PennyLane-0.28.0)
- null.qubit (PennyLane-0.28.0)

Existing GitHub issues

  • I have searched existing GitHub issues to make sure the issue does not already exist.
@bdoolittle bdoolittle added the bug 🐛 Something isn't working label Feb 8, 2023
@rmoyard
Copy link
Contributor

rmoyard commented Feb 9, 2023

Hi @bdoolittle, thanks for opening this issue! @timmysilv will take a look at it.

@timmysilv
Copy link
Contributor

timmysilv commented Feb 9, 2023

EDIT: I've changed the wire labels to be letters to make this clearer

Hi @bdoolittle, I actually had the same confusion very recently! qml.probs certainly feels a bit awkward to reason about, but I'll try to explain what's going on.

First, the device is created with wires=['c', 'a', 'b']. Then PauliX(wires=['a']) is applied. At this point, the device state is $\ket{010}$ (dev.state[0b010] is 1, rest are 0).

Then, when qml.probs(wires=['a', 'b', 'c']) is returned, this is internally interpreted as "the actual wires are ['a', 'b', 'c'], with the (previously stored) $\ket{010}$ state". The state is then mapped/permuted back to the original device wires (['c', 'a', 'b']). So the logical order of "thoughts" by the device here is:

  1. I am in the $\ket{010}$ state, with a wire order of ['a', 'b', 'c'] (in other words, the second, "b"-labelled wire is on)
  2. I need to re-arrange my state to my original wire order of ['c', 'a', 'b']
  3. The "b"-labelled wire is now in the third position, so my new state is $\ket{001}$

lmk if I can clarify any of those details!

@timmysilv
Copy link
Contributor

sat with this issue a bit, chatted with @josh146, and we agree that it is in fact a bug. Now that I've spelled it out, I can say that the problem is with the first "thought": qml.probs(['a', 'b', 'c']) just kind of says to the device "your state that you already computed is for these wires that I'm giving you, and NOT for wires ['c', 'a', 'b']". Unfortunately, that's not true! I'll look into fixing this.

@timmysilv
Copy link
Contributor

Hi @bdoolittle - my linked PR auto-closed this, and should have fixed qml.probs to behave as expected. Lmk if you still see any weirdness and I'll re-open it.

@josh146
Copy link
Member

josh146 commented Feb 15, 2023

Hey @bdoolittle! I was wondering if you could pass on some feedback as to how you encountered this edge case? E.g., was there a workflow where you needed non-consecutive ordered device wires?

This will help in understanding how to improve how PL works with non-ordered wires (@trbromley)

@bdoolittle
Copy link
Author

bdoolittle commented Feb 16, 2023

Hey @bdoolittle! I was wondering if you could pass on some feedback as to how you encountered this edge case? E.g., was there a workflow where you needed non-consecutive ordered device wires?

This will help in understanding how to improve how PL works with non-ordered wires (@trbromley)

Hey @josh146 , I've built a pennylane extension that takes a description of a quantum networking system and builds a quantum circuit (see https://github.com/ChitambarLab/qNetVO). On the output, it is often desirable to incorporate post processing maps. The wires in the network ansatz circuit are not always in a convenient ordering for applying the postprocessing maps. Ideally, you'd want to permute the wires such that when you measure the probabilities, the postprocessing maps can easily be combined as a tensor product and applied to the output probabilities.

There are plenty of work arounds for the problem, but the point is that when I ask pennylane to give me probabilities in a particular wiring order, that pennylane ought to deliver me that ordering.
For me this led to nonphyisical, albeit very spectacular results, which I happened to catch. Had I naively trusted pennylane, it could have led to a public release of incorrect results.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🐛 Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants