quantumlib / Cirq

A Python framework for creating, editing, and invoking Noisy Intermediate Scale Quantum (NISQ) circuits.
Apache License 2.0
4.24k stars 1.01k forks source link

KeyError raised when simulation with realistic noise #6607

Closed cosenal closed 4 months ago

cosenal commented 4 months ago

Description of the issue

I wanted to use the noise model described in https://quantumai.google/cirq/simulate/noisy_simulation#simulation_with_realistic_noise in the simulation of a very simple circuit (one C-Z gate, nothing else), but it throws a KeyError (see traceback below).

How to reproduce the issue

import cirq, cirq_google

q0, q1 = cirq.LineQubit.range(2)
circuit = cirq.Circuit(cirq.CZ.on(q0, q1))

processor_id = "rainbow"
cal = cirq_google.engine.load_median_device_calibration(processor_id)
noise_props = cirq_google.noise_properties_from_calibration(cal)
noise_model = cirq_google.NoiseModelFromGoogleNoiseProperties(noise_props)

cirq.DensityMatrixSimulator(noise=noise_model).simulate(circuit).final_density_matrix[0, 0].real

The same snippet works if I set noise=None, or if the circuit has a CNOT instead of a CZ gate.

Traceback:

--------------------------------------------------------------------------- KeyError Traceback (most recent call last) Cell In[10], [line 1](vscode-notebook-cell:?execution_count=10&line=1) ----> [1](vscode-notebook-cell:?execution_count=10&line=1) cirq.DensityMatrixSimulator(noise=noise_model).simulate(circuit).final_density_matrix[0, 0].real File /opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator.py:496, in SimulatesFinalState.simulate(self, program, param_resolver, qubit_order, initial_state) [471](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator.py:471) def simulate( [472](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator.py:472) self, [473](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator.py:473) program: 'cirq.AbstractCircuit', (...) [476](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator.py:476) initial_state: Any = None, [477](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator.py:477) ) -> TSimulationTrialResult: [478](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator.py:478) """Simulates the supplied Circuit. [479](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator.py:479) [480](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator.py:480) This method returns a result which allows access to the entire (...) [494](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator.py:494) SimulationTrialResults for the simulation. Includes the final state. [495](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator.py:495) """ --> [496](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator.py:496) return self.simulate_sweep( [497](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator.py:497) program, study.ParamResolver(param_resolver), qubit_order, initial_state [498](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator.py:498) )[0] File /opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator.py:511, in SimulatesFinalState.simulate_sweep(self, program, params, qubit_order, initial_state) [500](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator.py:500) def simulate_sweep( [501](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator.py:501) self, [502](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator.py:502) program: 'cirq.AbstractCircuit', (...) [505](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator.py:505) initial_state: Any = None, [506](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator.py:506) ) -> List[TSimulationTrialResult]: [507](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator.py:507) """Wraps computed states in a list. [508](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator.py:508) [509](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator.py:509) Prefer overriding `simulate_sweep_iter`. [510](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator.py:510) """ --> [511](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator.py:511) return list(self.simulate_sweep_iter(program, params, qubit_order, initial_state)) File /opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator_base.py:328, in SimulatorBase.simulate_sweep_iter(self, program, params, qubit_order, initial_state) [322](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator_base.py:322) prefix, suffix = ( [323](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator_base.py:323) split_into_matching_protocol_then_general(program, sweep_prefixable) [324](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator_base.py:324) if self._can_be_in_run_prefix(self.noise) [325](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator_base.py:325) else (program[0:0], program) [326](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator_base.py:326) ) [327](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator_base.py:327) step_result = None --> [328](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator_base.py:328) for step_result in self._core_iterator(circuit=prefix, sim_state=sim_state): [329](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator_base.py:329) pass [330](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator_base.py:330) sim_state = step_result._sim_state File /opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator_base.py:206, in SimulatorBase._core_iterator(self, circuit, sim_state, all_measurements_are_terminal) [203](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator_base.py:203) yield self._create_step_result(sim_state) [204](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator_base.py:204) return --> [206](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator_base.py:206) noisy_moments = self.noise.noisy_moments(circuit, sorted(circuit.all_qubits())) [207](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator_base.py:207) measured: Dict[Tuple['cirq.Qid', ...], bool] = collections.defaultdict(bool) [208](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/sim/simulator_base.py:208) for moment in noisy_moments: File /opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/devices/noise_properties.py:111, in NoiseModelFromNoiseProperties.noisy_moments(self, moments, system_qubits) [109](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/devices/noise_properties.py:109) noisy_circuit = split_measure_circuit.copy() [110](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/devices/noise_properties.py:110) for model in self.noise_models: --> [111](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/devices/noise_properties.py:111) noisy_circuit = noisy_circuit.with_noise(model) [113](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/devices/noise_properties.py:113) # Recombine measurements. [114](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/devices/noise_properties.py:114) final_moments = [] File /opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/circuits/circuit.py:2463, in Circuit.with_noise(self, noise) [2461](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/circuits/circuit.py:2461) qubits = sorted(self.all_qubits()) [2462](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/circuits/circuit.py:2462) c_noisy = Circuit() -> [2463](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/circuits/circuit.py:2463) for op_tree in noise_model.noisy_moments(self, qubits): [2464](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/circuits/circuit.py:2464) # Keep moments aligned [2465](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/circuits/circuit.py:2465) c_noisy += Circuit(op_tree) [2466](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/circuits/circuit.py:2466) return c_noisy File /opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/value/abc_alt.py:137, in ABCMetaImplementAnyOneOf.__new__..wrap_scope..impl_of_abstract(*args, **kwargs) [136](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/value/abc_alt.py:136) def impl_of_abstract(*args, **kwargs): --> [137](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/value/abc_alt.py:137) return impl(*args, **kwargs) File /opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/devices/noise_model.py:102, in NoiseModel._noisy_moments_impl_moment(self, moments, system_qubits) [100](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/devices/noise_model.py:100) result = [] [101](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/devices/noise_model.py:101) for moment in moments: --> [102](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/devices/noise_model.py:102) result.append(self.noisy_moment(moment, system_qubits)) [103](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/devices/noise_model.py:103) return result File /opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/devices/thermal_noise_model.py:278, in ThermalNoiseModel.noisy_moment(self, moment, system_qubits) [276](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/devices/thermal_noise_model.py:276) if self.skip_measurements and protocols.is_measurement(qubit_op): [277](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/devices/thermal_noise_model.py:277) continue --> [278](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/devices/thermal_noise_model.py:278) rates = self.rate_matrix_GHz[qubit] * moment_ns [279](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/devices/thermal_noise_model.py:279) kraus_ops = _kraus_ops_from_rates(tuple(rates.reshape(-1)), rates.shape) [280](https://file+.vscode-resource.vscode-cdn.net/opt/miniconda3/envs/mitiqenv/lib/python3.11/site-packages/cirq/devices/thermal_noise_model.py:280) noise_ops.append(ops.KrausChannel(kraus_ops).on(qubit)) KeyError: cirq.LineQubit(0)

Cirq version 1.3.0

NoureldinYosri commented 4 months ago

hi, this is exactly as the error says. the qubits don't exist on the device. the realsitic noise model represent the noise for the device, that's noise is keyed by qubits and qubit pairs. you can list the qubits on the device by calling

>> noise_props.qubits
[cirq.GridQubit(3, 2),
 cirq.GridQubit(4, 1),
 cirq.GridQubit(4, 2),
 cirq.GridQubit(4, 3),
 cirq.GridQubit(5, 0),
 cirq.GridQubit(5, 1),
 cirq.GridQubit(5, 2),
 cirq.GridQubit(5, 3),
 cirq.GridQubit(5, 4),
 cirq.GridQubit(6, 1),
 cirq.GridQubit(6, 2),
 cirq.GridQubit(6, 3),
 cirq.GridQubit(6, 4),
 cirq.GridQubit(6, 5),
 cirq.GridQubit(7, 2),
 cirq.GridQubit(7, 3),
 cirq.GridQubit(7, 4),
 cirq.GridQubit(7, 5),
 cirq.GridQubit(7, 6),
 cirq.GridQubit(8, 3),
 cirq.GridQubit(8, 4),
 cirq.GridQubit(8, 5),
 cirq.GridQubit(9, 4)]

for example I reran you circuit using cirq.GridQubit(4, 1), cirq.GridQubit(4, 2) which exist on the device

>> q0, q1 = cirq.q(4, 1), cirq.q(4, 2)
>> circuit = cirq.Circuit(cirq.CZ.on(q0, q1))
>> cirq.DensityMatrixSimulator(noise=noise_model).simulate(circuit)
measurements: (no measurements)

qubits: (cirq.GridQubit(4, 1), cirq.GridQubit(4, 2))
final density matrix:
[[1.+0.j 0.+0.j 0.+0.j 0.+0.j]
 [0.+0.j 0.+0.j 0.+0.j 0.+0.j]
 [0.+0.j 0.+0.j 0.+0.j 0.+0.j]
 [0.+0.j 0.+0.j 0.+0.j 0.+0.j]]

phase:
final density matrix:
[[1.+0.j]]

you may also want to call noise_props.expected_gates() to see which gates are supported by the device and if your circuit contains an unsupported gate then you can compile your circuit to the target gateset using cirq.optimize_for_target_gateset

cosenal commented 4 months ago

@NoureldinYosri Thanks for your reply.

this is exactly as the error says. the qubits don't exist on the device.

This doesn't explain why on the same device I can connect the same qubits with a CNOT gate (as opposed to a CZ), i.e., the following snippet works just fine 🤔

...
q0, q1 = cirq.LineQubit.range(2)
circuit = cirq.Circuit(cirq.CNOT.on(q0, q1))
...

And in fact, CNOT (CXPowGate) doesn't even show up in the list from noise_props.expected_gates() 🤔

NoureldinYosri commented 4 months ago

@cosenal CNOT is not a native gate on the device (but CZ is) so the simulator doesn't apply any noise to it (so it doesn't lookup the noise). notice how the result of the simulation is perfect without any noise.

from noise_props.expected_gates() we see that one of the native gatesets on the device is CZGateset so the correct way to run the noisy simulation would be to compile the circuit to that gateset (or another supported gateset)

>> q0, q1 = cirq.LineQubit.range(2)
>> circuit = cirq.Circuit(cirq.CX.on(q0, q1))
>> compiled_circuit = cirq.optimize_for_target_gateset(circuit, gateset=cirq.CZTargetGateset())
>> cirq.DensityMatrixSimulator(noise=noise_model).simulate(compiled_circuit)
KeyError: cirq.LineQubit(0)

Which gives the error as expected since now the simulator actually tries to add the noise and fails because the qubit is not present on the device, if instead we use qubits on the device we get the correct execution

>> q0, q1 = cirq.q(4, 1), cirq.q(4, 2)
>> circuit = cirq.Circuit(cirq.CX.on(q0, q1))
>> compiled_circuit = cirq.optimize_for_target_gateset(circuit, gateset=cirq.CZTargetGateset())
>> cirq.DensityMatrixSimulator(noise=noise_model).simulate(compiled_circuit)
measurements: (no measurements)

qubits: (cirq.GridQubit(4, 1), cirq.GridQubit(4, 2))
final density matrix:
[[ 9.9393892e-01+0.j -1.7796451e-03+0.j  0.0000000e+00+0.j
   0.0000000e+00+0.j]
 [-1.7796451e-03+0.j  4.6467967e-03+0.j  0.0000000e+00+0.j
   0.0000000e+00+0.j]
 [ 0.0000000e+00+0.j  0.0000000e+00+0.j  1.4076923e-03+0.j
  -2.5204695e-06+0.j]
 [ 0.0000000e+00+0.j  0.0000000e+00+0.j -2.5204695e-06+0.j
   6.5811464e-06+0.j]]

phase:
final density matrix:
[[1.+0.j]]

notice that in the ideal simulation (i.e. without noise) we expect to get density matrix with all zeros except at [0, 0], but in this noisy simulation we get a 0.99 at [0, 0] and small values at other entries.


I created https://github.com/quantumlib/Cirq/issues/6608 to raise an error when trying to run simulations with qubits not on the device or a circuit not compiled to a supported gateset.

cosenal commented 4 months ago

All clear now! Thanks, @NoureldinYosri