quantumlib/qsim

segmentation fault on too many observables

we-taper opened this issue · 4 comments

Dear QSim developers, during a noisy VQE simulation on LiH, I notice that getting expectation value of a certain number of observables seems to result in a segmentation fault. Here's a simple demonstration:

import cirq
import qsimcirq

observables = [
    (0.023037232547556415)
    * cirq.X(cirq.GridQubit(0, 0))
    * cirq.X(cirq.GridQubit(1, 0))
    * cirq.X(cirq.GridQubit(2, 0))
    * cirq.Y(cirq.GridQubit(3, 0))
    * cirq.Z(cirq.GridQubit(4, 0))
    * cirq.Y(cirq.GridQubit(5, 0))
    * cirq.X(cirq.GridQubit(6, 0))
]
moments = [cirq.X.on(cirq.GridQubit(i, 0)) for i in range(12)]
circuit = cirq.Circuit(moments)

# ev_simulator = cirq.Simulator()
ev_simulator = qsimcirq.QSimSimulator(
    qsim_options={
        't': 2,
        # 'r': 100
    }
)
# Calculate expectation values for the given observables.
ev_results = ev_simulator.simulate_expectation_values(
    circuit,
    observables=observables,
)
ev_results = sum(ev_results)
print(ev_results)

A few notes that might be helpful:

  • I have briefly tested the script above and found that whenever the number of observables is more than 7, the script will give segmentation fault.
  • It has been tested on qsimcirq==0.10.2 and the latest qsimcirq== 0.11.1.
  • If switching to the TFQ (built from source with git tag v0.5.1) with its Expectation layer, the issue does not appear, which is a bit confusing since I thought TFQ uses QSim as its backend.

This is known behavior, but we ought to have documentation for it (or guards against it) at the qsimcirq level as well. I'll take a look. (EDIT: correction - the code should route around this, but for some reason it doesn't.)

Thanks for the quick reply. Is it because for over 6 qubits, the fused gate matrix becomes too large that it might be numerically efficient? Now the work around I think is to add pre-rotation gates and measure them in the standard basis (which I guess has been done in TFQ).

Is it because for over 6 qubits, the fused gate matrix becomes too large that it might be numerically efficient?

More or less, yes. The gate fusion code limits gates to six qubits due to larger gates being inefficient to simulate, and the expectation value code relies on the same behavior.

Looking into this, I found that there's a more expensive expectation value method that avoids this limit, and the pybind layer should automatically use it for more than six qubits:

if (opsum_qubits <= 6) {
results.push_back(ExpectationValue<IO, Fuser>(opsum, simulator, state));
} else {
Fuser::Parameter param;
results.push_back(ExpectationValue<Fuser>(
param, opsum, state_space, simulator, state, scratch));
}

Clearly this switch is misbehaving somehow - if it wasn't, your code should work via the more expensive EV method.

The reason is that the more expensive method requires allocated scratch state. #498 should fix this issue.