pennylane icon indicating copy to clipboard operation
pennylane copied to clipboard

[BUG] Should the inverse of QubitStateVector be supported?

Open trbromley opened this issue 2 years ago • 4 comments

Expected behavior

Consider the following circuit:

import pennylane as qml
import numpy as np

dev = qml.device("default.qubit", wires=1)

# The state found by applying qml.RX(0.4, wires=0) to |0>:
state = np.array([0.98006658, -0.19866933j])

@qml.qnode(dev)
def f():
    qml.QubitStateVector(state, wires=0)
    return qml.expval(qml.PauliY(0))

f()
# tensor(-0.38941834, requires_grad=True)

Should we support qml.adjoint(qml.QubitStateVector)(state, wires=0) or qml.QubitStateVector(state, wires=0).inv() and what do we expect the output of the circuit to be? For me, I'd interpret |psi> = U|0> and the inverse to be to apply U*. The above would then be:

@qml.qnode(dev)
def f():
    qml.adjoint(qml.QubitStateVector)(state, wires=0)
    return qml.expval(qml.PauliY(0))

f()
# tensor(0.38941834, requires_grad=True)

However, maybe it's just better to raise an error if inverting a state preparation?

Actual behavior

An error is not raised when using qml.adjoint() or .inv(), but the results aren't what I'd expect.

For the adjoint function:

@qml.qnode(dev)
def f():
    qml.adjoint(qml.QubitStateVector)(state, wires=0)
    return qml.expval(qml.PauliY(0))

f()
# tensor(-5.55111512e-17, requires_grad=True)

For the .inv() method:

@qml.qnode(dev)
def f():
    qml.QubitStateVector(state, wires=0).inv()
    return qml.expval(qml.PauliY(0))

f()
# tensor(-0.38941834, requires_grad=True)

Additional information

No response

Source code

No response

Tracebacks

No response

System information

Dev PL

Existing GitHub issues

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

trbromley avatar Jun 06 '22 14:06 trbromley

@trbromley I'm curious what happens here. My gut feeling is that since the adjoint of QubitStateVector is not defined, this fallsback to the adjoint of the mottonen method, which should be equivalent to $U^\dagger|0\rangle$.

josh146 avatar Jun 06 '22 14:06 josh146

My gut feeling is that since the adjoint of QubitStateVector is not defined, this fallsback to the adjoint of the mottonen method, which should be equivalent to ...

That would be good! I don't think it's happening though, qml.adjoint seems to result in the state preparation being ignored completely, whereas .inv() seems to have no effect (i.e., just applies QubitStateVector).

trbromley avatar Jun 06 '22 18:06 trbromley

@trbromley Running on current master (this behaviour was updated last Friday), we get different behaviour for QubitStateVector and Adjoint(QubitStateVector).

import pennylane as qml
import numpy as np

dev = qml.device("default.qubit", wires=1)

state = np.array([0.98006658, -0.19866933j])

@qml.qnode(dev)
def f():
    qml.QubitStateVector(state, wires=0)
    return qml.state()

@qml.qnode(dev)
def g():
    qml.adjoint(qml.QubitStateVector)(state, wires=0)
    return qml.state()

print(f())
print(g())

print(qml.draw(f, expansion_strategy="device")())
print(qml.draw(g, expansion_strategy="device")())

[ 0.98006658+0.j         -0.        -0.19866933j]
[ 0.69301172-0.69301172j -0.14048043+0.14048043j]
0: ──QubitStateVector(M0)─┤  State
0: ──RZ(1.57)──RY(-0.40)─┤  State

If we look at the expansion, we can see what happens:

>>> qml.QubitStateVector(state, wires=0).expand().expand().circuit
[RY(array(0.4), wires=[0]), RZ(array(-1.57079633), wires=[0])]
>>> qml.adjoint(qml.QubitStateVector(state, wires=0)).expand().expand(depth=2).circuit
[RZ(1.5707963267948966, wires=[0]), RY(-0.399999997583816, wires=[0])]

The adjoint of the QubitStateVector applies the adjoint of the MottonenStatePrep decomposition.

albi3ro avatar Jun 07 '22 23:06 albi3ro

@trbromley just double-checking: can this issue be closed?

antalszava avatar Jul 29 '22 14:07 antalszava