tket icon indicating copy to clipboard operation
tket copied to clipboard

`QubitPauliString.state_expectation` does not work for named qubits

Open CalMacCQ opened this issue 2 years ago • 2 comments

I'd expect to be able to calculate $\langle X\rangle$ on a qubit named "p" as follows with QubitPauliString.state_expectation.

from pytket import Circuit, Qubit
from pytket.pauli import Pauli, QubitPauliString

circ = Circuit()
qubit_p = Qubit("p", 0)
circ.add_qubit(qubit_p)

pauli_x = QubitPauliString(circ.qubits, [Pauli.X])
print(pauli_x.state_expectation(circ.get_statevector()))

However this gives

---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
Cell In[16], line 9
      6 circ.add_qubit(qubit_p)
      8 pauli_x = QubitPauliString(circ.qubits, [Pauli.X])
----> 9 print(pauli_x.state_expectation(circ.get_statevector()))

RuntimeError: Qubit list given to to_sparse_matrix doesn't contain p[0]

If I simply substitute "p" for the default qubit name "q" everything works fine.

CalMacCQ avatar Nov 17 '23 17:11 CalMacCQ

@willsimmons1465 I'm interested in your opinion on this. It feels like it should work with non-default qubits, but should it? If one digs into the code path there are explicit requirements that the qubit register be the default: e.g. here.

cqc-alec avatar Dec 13 '23 16:12 cqc-alec

This issue has been automatically marked as stale.

github-actions[bot] avatar Jun 08 '24 05:06 github-actions[bot]

Sorry, this completely slipped by me!

Note that QubitPauliString.state_expectation is an overloaded method. In the docs @CalMacCQ linked, there are two overloads:

  • The case that he called, which the documentation lists as only working for default register qubits, and so by the documentation is not expected to work here;
  • The case that requires passing in an additional list of qubits demonstrating their ordering in the statevector. This is the case that should be used to solve the problem at hand.

The design choice to throw the error here was because it might not always be safe to assume the qubit ordering when they are not in the default register because of the sparsity of the representation. If we have a QubitPauliString with elements (d[0], X) and (r[0], Z), but I give a three qubit statevector, then it is ambiguous which qubits these refer to as the full Pauli string could be any of I@a[0] X@d[0] Z@r[0], X@d[0] I@k[0] Z@r[0], or X@d[0] Z@r[0] I@w[0].

I don't see this as a mistake in functionality, but if you guys think either the docs or error messages are misleading then we should go ahead and change those.

willsimmons1465 avatar Apr 09 '25 08:04 willsimmons1465

Thanks @willsimmons1465! Indeed if I supply the qubits arg (the second case) I can calculate the expectation value fine. It seems I was using this method improperly in the orignal code snippet.

from pytket import Circuit, Qubit
from pytket.pauli import Pauli, QubitPauliString

circ = Circuit()
qubit_p = Qubit("p", 0)
circ.add_qubit(qubit_p)

pauli_x = QubitPauliString(circ.qubits, [Pauli.X])
print(pauli_x.state_expectation(circ.get_statevector(), qubits=[qubit_p]))

Output 0j

Note the qubits=[qubit_p] arg. The docs do make clear that the first overload is only to be used with the default register name so I think we can close this issue.

CalMacCQ avatar Apr 22 '25 16:04 CalMacCQ