qsim
qsim copied to clipboard
Update docs and error messages for qsim flags
Qsimh sometimes gives empty output, seemingly on small circuits:
import numpy as np
import cirq
from cirq.experiments import (
random_rotations_between_grid_interaction_layers_circuit)
import qsimcirq
qsim_simulator = qsimcirq.QSimSimulator()
qsimh_options = {'k': [0], 'w': 0, 'p': 1, 'r': 1}
qsimh_simulator = qsimcirq.QSimhSimulator(qsimh_options)
a = cirq.LineQubit(0)
circuit = cirq.Circuit(
cirq.X(a)**0.5
)
qsim_circuit = qsimcirq.QSimCircuit(circuit)
bitstrings = ['0', '1']
qsim_amplitudes = qsim_simulator.compute_amplitudes(qsim_circuit, bitstrings)
qsimh_amplitudes = qsimh_simulator.compute_amplitudes(qsim_circuit, bitstrings)
print('Circuit:')
print(circuit)
print()
print(f'Qsim amplitudes: {qsim_amplitudes}')
print(f'Qsimh amplitudes: {qsimh_amplitudes}')
Output:
Circuit:
0: ───X^0.5───
Qsim amplitudes: [(0.5+0.5j), (0.5-0.5j)]
Qsimh amplitudes: []
When Qsimh does produce output (I seem to have to make the circuit big enough for this to happen), it is incorrect:
def optimized_for_qsim(circuit: cirq.Circuit):
optimized_circuit = cirq.Circuit()
for op in circuit.all_operations():
if isinstance(op.gate, cirq.PhasedXPowGate):
optimized_circuit.append(cirq.decompose(op))
else:
optimized_circuit.append(op)
return optimized_circuit
qubits = cirq.GridQubit.square(2)
circuit = random_rotations_between_grid_interaction_layers_circuit(
qubits=qubits, depth=3, seed=1234)
circuit = optimized_for_qsim(circuit)
qsim_circuit = qsimcirq.QSimCircuit(circuit)
bitstrings = ['0000', '0001']
qsim_amplitudes = qsim_simulator.compute_amplitudes(qsim_circuit, bitstrings)
qsimh_amplitudes = qsimh_simulator.compute_amplitudes(qsim_circuit, bitstrings)
print('Circuit:')
print(circuit)
print()
print(f'Qsim amplitudes: {qsim_amplitudes}')
print(f'Qsimh amplitudes: {qsimh_amplitudes}')
cirq.testing.assert_allclose_up_to_global_phase(np.array(qsim_amplitudes),
np.array(qsimh_amplitudes),
atol=1e-5)
Output:
Circuit:
┌────┐ ┌────────┐
(0, 0): ───T^-1────X^0.5───T────────SYC──────X^0.5───────T^-1────X^0.5───T───────Y^0.5──────────────────────
│
(0, 1): ───Y^0.5───T^-1────X^0.5────┼──T──────────SYC────X^0.5───Y^0.5──────────────────────────────────────
│ │
(1, 0): ───X^0.5────────────────────SYC──────T^-1─┼──────X^0.5───T───────X^0.5───SYC─────T^-1───X^0.5───T───
│ │
(1, 1): ───X^0.5───T^-1────X^0.5────T─────────────SYC────X^0.5───────────────────SYC─────T^-1───X^0.5───T───
└────┘ └────────┘
Qsim amplitudes: [(0.22928695380687714-0.1546979546546936j), (0.0881662517786026+0.3213808536529541j)]
Qsimh amplitudes: [(0.12853418290615082+0.01285102590918541j), (-0.031721945852041245+0.05422064661979675j)]
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
<ipython-input-2-fc37687fd8cd> in <module>
25 cirq.testing.assert_allclose_up_to_global_phase(np.array(qsim_amplitudes),
26 np.array(qsimh_amplitudes),
---> 27 atol=1e-5)
~/Projects/Cirq/cirq/testing/lin_alg_utils.py in assert_allclose_up_to_global_phase(actual, desired, rtol, atol, equal_nan, err_msg, verbose)
193 equal_nan=equal_nan,
194 err_msg=err_msg,
--> 195 verbose=verbose)
/usr/lib/python3/dist-packages/numpy/testing/_private/utils.py in assert_allclose(actual, desired, rtol, atol, equal_nan, err_msg, verbose)
1501 header = 'Not equal to tolerance rtol=%g, atol=%g' % (rtol, atol)
1502 assert_array_compare(compare, actual, desired, err_msg=str(err_msg),
-> 1503 verbose=verbose, header=header, equal_nan=equal_nan)
1504
1505
/usr/lib/python3/dist-packages/numpy/testing/_private/utils.py in assert_array_compare(comparison, x, y, err_msg, verbose, header, precision, equal_nan, equal_inf)
827 verbose=verbose, header=header,
828 names=('x', 'y'), precision=precision)
--> 829 raise AssertionError(msg)
830 except ValueError:
831 import traceback
AssertionError:
Not equal to tolerance rtol=1e-07, atol=1e-05
Mismatch: 100%
Max absolute difference: 0.27085387
Max relative difference: 4.31169146
x: array([ 0.276594-2.775558e-17j, -0.10666 +3.157253e-01j])
y: array([ 0.129175+1.734723e-18j, -0.02617 +5.710753e-02j])
Could this be due to the options passed to the initialization of the Qsimh simulator? I just copied the options from https://github.com/quantumlib/qsim/blob/master/docs/cirq_interface.md; I don't know what they mean and I can't find their documentation.
Could this be due to the options passed to the initialization of the Qsimh simulator? I just copied the options from https://github.com/quantumlib/qsim/blob/master/docs/cirq_interface.md; I don't know what they mean and I can't find their documentation.
These flags are shared with the C++ apps; definitions can be found in the "qsimh_base" docs. I suspect that the empty output is a result of this; if the prefix/root/suffix gate count doesn't make sense with the provided circuit, qsimh will likely misbehave. We should have an error for this, though...
It's possible that the flag issue is also causing the non-empty output to be incorrect, although I'm less certain about this. @kevinsung, could you retry with new flag values based on the docs and update this issue with the results?
=====
TODO items identified from this:
- Copy docs from
apps
to qsimcirq to clarify that flag usage is the same - Return a useful error if qsimh is configured with too few/many prefix/root/suffix gates
qsimh is a hybrid Schrödinger-Feynman simulator. The circuit is split into two parts. So there should be at least two qubits for non-empty output. qsimh option 'p' specifies the number of "prefix" gates. To sum over all the paths, p should be equal to 0. If p > 0 then only partial summation is performed. The results still should be correct but differ from qsim results in general.
The results still should be correct but differ from qsim results in general.
@sergeisakov In the second example that I gave above, the output is actually incorrect.
Changing p to 0 does seem to give correct results, but still returns empty output on some circuits with at least 2 qubits. For example:
import numpy as np
import cirq
from cirq.experiments import (
random_rotations_between_grid_interaction_layers_circuit)
import qsimcirq
qsimh_options = {'k': [0], 'w': 0, 'p': 0, 'r': 1}
qsimh_simulator = qsimcirq.QSimhSimulator(qsimh_options)
def optimized_for_qsim(circuit: cirq.Circuit):
optimized_circuit = cirq.Circuit()
for op in circuit.all_operations():
if isinstance(op.gate, cirq.PhasedXPowGate):
optimized_circuit.append(cirq.decompose(op))
else:
optimized_circuit.append(op)
return optimized_circuit
qubits = cirq.GridQubit.square(2)
circuit = random_rotations_between_grid_interaction_layers_circuit(
qubits=qubits, depth=1, seed=1234)
circuit = optimized_for_qsim(circuit)
qsim_circuit = qsimcirq.QSimCircuit(circuit)
bitstrings = [0, 1]
qsimh_amplitudes = qsimh_simulator.compute_amplitudes(qsim_circuit, bitstrings)
print('Circuit:')
print(circuit)
print()
print(f'Qsimh amplitudes: {qsimh_amplitudes}')
Output:
Circuit:
┌────┐
(0, 0): ───T^-1────X^0.5───T────────SYC─────X^0.5───────────────
│
(0, 1): ───Y^0.5───T^-1────X^0.5────┼──T────────────────────────
│
(1, 0): ───X^0.5────────────────────SYC─────T^-1────X^0.5───T───
(1, 1): ───X^0.5───T^-1────X^0.5────T───────────────────────────
└────┘
Qsimh amplitudes: []
The results still should be correct but differ from qsim results in general.
@sergeisakov In the second example that I gave above, the output is actually incorrect.
To clarify @sergeisakov's comment, the output from a p > 0
run is effectively a reduced-fidelity simulation. It is "correct" in the sense that the sum-over-subset-of-paths it performs is accurate, but its results will necessarily differ from a p = 0
"sum-over-all-paths" simulation.
The circuit is split into two parts. The number of prefix gates p
plus the number of root gates r
should not exceed the number of gates on the cut between the two parts. It seems that there are no gates on the cut in the last example. This leads to empty output because r = 1
. You can set r
to zero or you can specify a different cut, say, 'k': [0, 1]
.
As @95-martin-orion pointed out, we need to update qsimcirq docs and to show a useful error message.