qiboteam / qibo

A framework for quantum computing
https://qibo.science
Apache License 2.0
287 stars 58 forks source link

Expectation for observable does not take into account constants #1383

Closed mho291 closed 2 months ago

mho291 commented 2 months ago

I was fiddling around with a QAOA MaxCut Hamiltonian by feeding it into the ZNE as observable and stumbled upon some weird stuff.

The QAOA MaxCut Hamiltonian has the form $H = \sum_{<i,j> \in E} 0.5 ( 1 - Z_i Z_j)$, where the $Z_i$ and $Z_j$ are $Z$ operators on the $i$-th and $j$-th qubits.

For a simple fully connected 3 nodes graph (thus 3 qubits), $H$ has the form $H = 0.5 (1-Z(0) Z(1)) + 0.5 (1-Z(1) Z(2)) + 0.5 (1-Z(2) Z(0))$. We can simplify this to be $H = 1.5 - 0.5 Z(0) Z(1) - 0.5 Z(1) Z(2) - 0.5 Z(2) Z(0)$.

To compute the expectation value of the observable, we input observable $H = 1.5 - 0.5 Z(0) Z(1) - 0.5 Z(1) Z(2) - 0.5 Z(2) Z(0)$. The answer turns out wrong as the computation doesn't take into account the constant, which in this case is $1.5$. It does take into account the $0.5$ for each $Z(i) Z(j)$.

Therefore to properly compute H, we need to do two separate computations: First to compute $0.5 Z(0) Z(1) - 0.5 Z(1) Z(2) - 0.5 Z(2) Z(0)$, then we subtract it from $1.5$.

I was just wondering if this should be an issue because it's not intuitive to do two separate computations for a Hamiltonian that has constants.

renatomello commented 2 months ago

@mho291 I took the liberty to edit your comment to improve readability.

mho291 commented 2 months ago

@mho291 I took the liberty to edit your comment to improve readability.

Thank you! I appreciate it!

alecandido commented 2 months ago

@mho291 thanks for the detailed explanation.

It would be great (in general) if you can also add the code to reproduce the issue.

mho291 commented 2 months ago

I must correct the issue I raised. I think that the computation of obs is not accurate, not that it doesn't take into account the constant. It does take into account the constant but wrongly.

Example

First we generate a QAOA circuit.

from qibo import gates
from qibo import Circuit as QiboCircuit

from qibo.symbols import Z
from qibo.hamiltonians import SymbolicHamiltonian
from qibo.backends import NumpyBackend

circ = QiboCircuit(4)
circ.add(gates.H(0))
circ.add(gates.H(1))
circ.add(gates.H(2))
circ.add(gates.H(3))
circ.add(gates.CNOT(0, 1))
circ.add(gates.RZ(1, 2.06))
circ.add(gates.CNOT(0, 1))
circ.add(gates.CNOT(0, 2))
circ.add(gates.RZ(2, 2.06))
circ.add(gates.CNOT(0, 2))
circ.add(gates.CNOT(0, 3))
circ.add(gates.RZ(3, 2.06))
circ.add(gates.CNOT(0, 3))
circ.add(gates.CNOT(1, 2))
circ.add(gates.RZ(2, 2.06))
circ.add(gates.CNOT(1, 2))
circ.add(gates.CNOT(2, 3))
circ.add(gates.RZ(3, 2.06))
circ.add(gates.CNOT(2, 3))
circ.add(gates.RX(0, 1.28))
circ.add(gates.RX(1, 1.28))
circ.add(gates.RX(2, 1.28))
circ.add(gates.RX(3, 1.28))
circ.add(gates.M(0))
circ.add(gates.M(1))
circ.add(gates.M(2))
circ.add(gates.M(3))
print(circ.draw())

Suppose we want to compute the expectation value of this Hamiltonian, H = 3000 - 0.5*Z(0)*Z(1) - 0.5*Z(0)*Z(2) - 0.5*Z(0)*Z(3) - 0.5*Z(1)*Z(2) - 0.5*Z(2)*Z(3). Let's label H as obs_with_constant with the constant being the value 3000 (arbitrary large number for demonstration). Let's also compute obs_without_constant as the equation for H without the 3000.

obs_with_constant = 3000 - 0.5*Z(0)*Z(1) - 0.5*Z(0)*Z(2) - 0.5*Z(0)*Z(3) - 0.5*Z(1)*Z(2) - 0.5*Z(2)*Z(3)
obs_with_constant = SymbolicHamiltonian(obs_with_constant, nqubits=circ.nqubits, backend=NumpyBackend())

obs_without_constant = - 0.5*Z(0)*Z(1) - 0.5*Z(0)*Z(2) - 0.5*Z(0)*Z(3) - 0.5*Z(1)*Z(2) - 0.5*Z(2)*Z(3)
obs_without_constant = SymbolicHamiltonian(obs_without_constant, nqubits=circ.nqubits, backend=NumpyBackend())

result = circ.execute(nshots=10000)
counts = result.frequencies()

obs_with_constant_expectation = result.expectation_from_samples(obs_with_constant)
print(obs_with_constant_expectation)

obs_without_constant_expectation = result.expectation_from_samples(obs_without_constant)
print(obs_without_constant_expectation)

Results:

-1191.8083999999997
0.5903

The correct answer for obs_with_constant should be

3000.5903

In QAOA MaxCut, the constants are typically not of large value unless there are many edges. So that's why I chose a larger value of 3000 just to demonstrate the inaccuracy of expectation_from_samples.

alecandido commented 2 months ago

Thanks @mho291, the problem is here: https://github.com/qiboteam/qibo/blob/e3d8008b3ac69e95f614c1630712b3b305a0a6fc/src/qibo/hamiltonians/hamiltonians.py#L550-L570

Despite, during the computation of terms, a constant is identified and separated, https://github.com/qiboteam/qibo/blob/e3d8008b3ac69e95f614c1630712b3b305a0a6fc/src/qibo/hamiltonians/hamiltonians.py#L394-L399 this is unused and forgotten during the computation of the expectation. In particular, your constant is being used as the coefficient of the first term (the first pair of Zs) and all the others are shifted. Since all the others are equal, the only problem you notice is due to the missing constant, and the single coefficient set to the large constant.

It is definitely a bug, and pretty quick to fix: instead of extracting the coefficient again from self.form, it is sufficient to use the coefficients that are already stored within the terms, and add the constant at the end. I will make a PR later on.

mho291 commented 2 months ago

Thank you so much @alecandido !

alecandido commented 2 months ago

With #1389 and your commands above, I obtain the following:

❯ : python test-1383.py
q0: ─H─o────o─o────o─o────o───────────────RX─M─
q1: ─H─X─RZ─X─|────|─|────|─o────o────────RX─M─
q2: ─H────────X─RZ─X─|────|─X─RZ─X─o────o─RX─M─
q3: ─H───────────────X─RZ─X────────X─RZ─X─RX─M─
[Qibo 0.2.9|INFO|2024-07-12 23:23:25]: Using qibojit (numba) backend on /CPU:0
3000.5776
0.5776

test-1383.zip

So, it seems to work :)

(but I didn't expect a complex value...)