quantumlib/qsim

A strange problem about the QSimSimulator

AlaricCheng opened this issue · 1 comments

Hi there,

I encountered a strange problem when using qsim. Below is the script that can reproduce the error. Basically, I first generate the set of all single-qubit Clifford gates, which is _single_qubit_clifford. Then, I define a circuit by adding gates layer by layer; one layer of single-qubit Clifford gate followed by a layer of CNOTs and so on. Besides, I also added errors after each layer. This will be perfectly fine to the qsim simulator.

But for some reason, I need to insert some addition gates in the middle of the circuit, which is cirq.X(qubits[0]) in the example. Then, it return an error message, saying ValueError: could not broadcast input array from shape (0,) into shape (4,). Below is the case that I found will be no problem:

  • If the extra gate is removed, then that's OK;
  • If all errors are removed, then that's OK;
  • If the Pauli-X gate is merged with the gate before it, using the cirq.MatrixGate for construction, then that's also fine.

I tried to look into this problem for several hours, but I couldn't figure out what is going on exactly, only to discover how to work around it by the 'merge' method. Any idea what's happening? Thanks!

import cirq, qsimcirq
import itertools

# generate all single-qubit clifford gates
_pauli_list = (cirq.I, cirq.X, cirq.Y, cirq.Z)
_bools = (False, True)

def _all_rotation_pairs():
    for px, flip_x, pz, flip_z in itertools.product(_pauli_list[1:], _bools, _pauli_list[1:], _bools):
        if px == pz:
            continue
        yield (px, flip_x), (pz, flip_z)

def _all_clifford_gates():
    for trans_x, trans_z in _all_rotation_pairs():
        yield cirq.SingleQubitCliffordGate.from_xz_map(trans_x, trans_z)

_single_qubit_clifford = tuple(_all_clifford_gates())

# simulators
dm_sim = cirq.DensityMatrixSimulator()
qsim_sim = qsimcirq.QSimSimulator()

# define the circuit
qubits = cirq.LineQubit.range(4)
error = cirq.phase_flip(0.02)
circ = cirq.Circuit(
    _single_qubit_clifford[0].on_each(*qubits),
    cirq.X(qubits[0]),
    error.on_each(*qubits),
    cirq.CNOT(qubits[0], qubits[1]),
    cirq.CNOT(qubits[2], qubits[3]),
    error.on_each(*qubits),
    _single_qubit_clifford[4].on_each(*qubits),
    error.on_each(*qubits), 
    cirq.measure_each(*qubits)
)

# run
print(dm_sim.run(circ))
print(qsim_sim.run(circ))

The root error is the fuser: gate at time 4 is out of time order message this produces, which is due to an error in the circuit translation code. In short, noiseless ops can decompose into multiple time steps, but noisy ops cannot. qsim was adding a copy of the noisy gates for each time step produced by the noiseless gates.

A fix is up in #554. If you're using qsim by cloning the repo, you can simply patch the change in (it's only 3 lines); if you got qsim via pip install qsimcirq you can either try cloning and building locally or wait for a patch release - there should be at least one coming out in the next week.