Closed mho291 closed 2 months ago
@mho291 I took the liberty to edit your comment to improve readability.
@mho291 I took the liberty to edit your comment to improve readability.
Thank you! I appreciate it!
@mho291 thanks for the detailed explanation.
It would be great (in general) if you can also add the code to reproduce the issue.
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
.
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 Z
s) 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.
Thank you so much @alecandido !
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
So, it seems to work :)
(but I didn't expect a complex value...)
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.