Skill Tracer
Skills › Scientific Agent Skills › cirq › simulation.md

simulation.md

176 lines · skills/cirq/references/simulation.md at 5f2bc03

Open on GitHub
1# Simulation in Cirq
2
3These examples target Cirq 1.7.0. Run the blocks in order. Local simulations need
4no provider credentials. Keep preparation circuits separate from measurement
5circuits so a sampled collapse is not mistaken for an unmeasured final state.
6

Simulation in Cirq

These examples target Cirq 1.7.0. Run the blocks in order. Local simulations need no provider credentials. Keep preparation circuits separate from measurement circuits so a sampled collapse is not mistaken for an unmeasured final state.

Raw lines 1 to 6
7## State vectors, density matrices, and samples
8
9```python
10import cirq
11import numpy as np
12import sympy
14qubits = list(cirq.LineQubit.range(2))
15q0, q1 = qubits
16preparation = cirq.Circuit(cirq.H(q0), cirq.CNOT(q0, q1))
17measured = preparation + cirq.Circuit(cirq.measure(*qubits, key="result"))
18simulator = cirq.Simulator(seed=42, dtype=np.complex128)
20state = simulator.simulate(preparation, qubit_order=qubits).final_state_vector
21np.testing.assert_allclose(np.abs(state)**2, [0.5, 0, 0, 0.5])
22result = simulator.run(measured, repetitions=1000)
23print(result.histogram(key="result")) # only 0 (00) and 3 (11)
24assert result.measurements["result"].shape == (1000, 2)
26density_sim = cirq.DensityMatrixSimulator(dtype=np.complex128, seed=42)
27rho = density_sim.simulate(preparation, qubit_order=qubits).final_density_matrix
28np.testing.assert_allclose(rho, np.outer(state, state.conj()))
29```
31`run` returns sampled measurement data. `simulate` returns the final quantum state;
32if the circuit contains measurements, it simulates their collapse. To obtain an
33ensemble state after unobserved measurements, use `cirq.dephase_measurements`
34and a density matrix simulator (classical feedback requires separate handling).
35Histogram integers are big-endian in the measurement's qubit argument order.

State vectors, density matrices, and samples

import cirq
import numpy as np
import sympy

qubits = list(cirq.LineQubit.range(2))
q0, q1 = qubits
preparation = cirq.Circuit(cirq.H(q0), cirq.CNOT(q0, q1))
measured = preparation + cirq.Circuit(cirq.measure(*qubits, key="result"))
simulator = cirq.Simulator(seed=42, dtype=np.complex128)

state = simulator.simulate(preparation, qubit_order=qubits).final_state_vector
np.testing.assert_allclose(np.abs(state)**2, [0.5, 0, 0, 0.5])
result = simulator.run(measured, repetitions=1000)
print(result.histogram(key="result"))  # only 0 (00) and 3 (11)
assert result.measurements["result"].shape == (1000, 2)

density_sim = cirq.DensityMatrixSimulator(dtype=np.complex128, seed=42)
rho = density_sim.simulate(preparation, qubit_order=qubits).final_density_matrix
np.testing.assert_allclose(rho, np.outer(state, state.conj()))

run returns sampled measurement data. simulate returns the final quantum state; if the circuit contains measurements, it simulates their collapse. To obtain an ensemble state after unobserved measurements, use cirq.dephase_measurements and a density matrix simulator (classical feedback requires separate handling). Histogram integers are big-endian in the measurement's qubit argument order.

Raw lines 7 to 36
37## Expectation values and reduced states
39```python
40observable = cirq.Z(q0) * cirq.Z(q1)
41expectation = simulator.simulate_expectation_values(
42 preparation, observables=[observable], qubit_order=qubits
43)[0]
44assert np.isclose(expectation, 1)
46# Reduced state of q0 from a pure state, in the explicit order above.
47reduced = cirq.density_matrix_from_state_vector(state, indices=[0], qid_shape=(2, 2))
48np.testing.assert_allclose(reduced, np.eye(2) / 2)
50# partial_trace expects a tensor with one ket and one bra axis per subsystem.
51reduced_from_rho = cirq.partial_trace(rho.reshape(2, 2, 2, 2), keep_indices=[0])
52np.testing.assert_allclose(reduced_from_rho, reduced)
53```
55Only drop **terminal** measurements when computing pre-measurement observables.
56Removing mid-circuit measurements changes the computation. A `Simulator` result
57has `final_state_vector`, whereas a `DensityMatrixSimulator` result has
58`final_density_matrix`; do not mix these result interfaces.

Expectation values and reduced states

observable = cirq.Z(q0) * cirq.Z(q1)
expectation = simulator.simulate_expectation_values(
    preparation, observables=[observable], qubit_order=qubits
)[0]
assert np.isclose(expectation, 1)

# Reduced state of q0 from a pure state, in the explicit order above.
reduced = cirq.density_matrix_from_state_vector(state, indices=[0], qid_shape=(2, 2))
np.testing.assert_allclose(reduced, np.eye(2) / 2)

# partial_trace expects a tensor with one ket and one bra axis per subsystem.
reduced_from_rho = cirq.partial_trace(rho.reshape(2, 2, 2, 2), keep_indices=[0])
np.testing.assert_allclose(reduced_from_rho, reduced)

Only drop terminal measurements when computing pre-measurement observables. Removing mid-circuit measurements changes the computation. A Simulator result has final_state_vector, whereas a DensityMatrixSimulator result has final_density_matrix; do not mix these result interfaces.

Raw lines 37 to 59
60## Parameter sweeps
62```python
63theta, phi = sympy.symbols("theta phi")
64parameterized = cirq.Circuit(
65 cirq.ry(theta)(q0), cirq.rx(phi)(q1),
66 cirq.measure(q0, q1, key="result"),
68product_sweep = cirq.Product(
69 cirq.Linspace("theta", 0, np.pi, 3),
70 cirq.Linspace("phi", 0, 2*np.pi, 4),
72results = simulator.run_sweep(parameterized, params=product_sweep, repetitions=100)
73assert len(results) == 12
74paired_sweep = cirq.Zip(
75 cirq.Linspace("theta", 0, np.pi, 3),
76 cirq.Linspace("phi", 0, 2*np.pi, 3),
78paired_results = simulator.run_sweep(parameterized, params=paired_sweep, repetitions=100)
79assert len(paired_results) == 3
80```
82Sampling requires measurements. For measurement-free circuits use `simulate_sweep`
83or `simulate_expectation_values_sweep`. Product sweeps evaluate every combination;
84Zip sweeps pair values and stop at the shortest constituent sweep.

Parameter sweeps

theta, phi = sympy.symbols("theta phi")
parameterized = cirq.Circuit(
    cirq.ry(theta)(q0), cirq.rx(phi)(q1),
    cirq.measure(q0, q1, key="result"),
)
product_sweep = cirq.Product(
    cirq.Linspace("theta", 0, np.pi, 3),
    cirq.Linspace("phi", 0, 2*np.pi, 4),
)
results = simulator.run_sweep(parameterized, params=product_sweep, repetitions=100)
assert len(results) == 12
paired_sweep = cirq.Zip(
    cirq.Linspace("theta", 0, np.pi, 3),
    cirq.Linspace("phi", 0, 2*np.pi, 3),
)
paired_results = simulator.run_sweep(parameterized, params=paired_sweep, repetitions=100)
assert len(paired_results) == 3

Sampling requires measurements. For measurement-free circuits use simulate_sweep or simulate_expectation_values_sweep. Product sweeps evaluate every combination; Zip sweeps pair values and stop at the shortest constituent sweep.

Raw lines 60 to 85
86## Noisy simulation
88```python
89noisy = preparation.with_noise(cirq.depolarize(p=0.01))
90noisy_rho = density_sim.simulate(noisy, qubit_order=qubits).final_density_matrix
91assert np.isclose(np.trace(noisy_rho), 1)
92assert np.linalg.eigvalsh(noisy_rho).min() >= -1e-10
93noisy_samples = density_sim.run(
94 noisy + cirq.Circuit(cirq.measure(*qubits, key="result")), repetitions=1000
96```
98`with_noise(channel)` inserts a constant noise layer on **all system qubits after
99each moment**, including idle qubits and measurement moments. It is a toy model,
100not automatically a calibrated gate-duration model. See [noise.md](noise.md) for
101gate-specific, relaxation, and asymmetric readout examples.
103`cirq.Simulator` can sample noisy trajectories for supported channels. Each
104trajectory has a pure state; averaging many trajectories approximates the mixed
105state. `DensityMatrixSimulator` propagates the ensemble density matrix directly.
106The measurement sampling still has finite-shot uncertainty in both cases.

Noisy simulation

noisy = preparation.with_noise(cirq.depolarize(p=0.01))
noisy_rho = density_sim.simulate(noisy, qubit_order=qubits).final_density_matrix
assert np.isclose(np.trace(noisy_rho), 1)
assert np.linalg.eigvalsh(noisy_rho).min() >= -1e-10
noisy_samples = density_sim.run(
    noisy + cirq.Circuit(cirq.measure(*qubits, key="result")), repetitions=1000
)

with_noise(channel) inserts a constant noise layer on all system qubits after each moment, including idle qubits and measurement moments. It is a toy model, not automatically a calibrated gate-duration model. See noise.md for gate-specific, relaxation, and asymmetric readout examples.

cirq.Simulator can sample noisy trajectories for supported channels. Each trajectory has a pure state; averaging many trajectories approximates the mixed state. DensityMatrixSimulator propagates the ensemble density matrix directly. The measurement sampling still has finite-shot uncertainty in both cases.

Raw lines 86 to 107
108## Moment steps and initial states
110```python
111for index, step in enumerate(simulator.simulate_moment_steps(preparation, qubit_order=qubits)):
112 print(index, step.state_vector())
114bell_initial_state = np.array([1, 0, 0, 1], dtype=complex) / np.sqrt(2)
115initial_result = simulator.simulate(
116 cirq.Circuit(cirq.I.on_each(*qubits)),
117 initial_state=bell_initial_state, qubit_order=qubits,
119np.testing.assert_allclose(initial_result.final_state_vector, bell_initial_state)
120```

Moment steps and initial states

for index, step in enumerate(simulator.simulate_moment_steps(preparation, qubit_order=qubits)):
    print(index, step.state_vector())

bell_initial_state = np.array([1, 0, 0, 1], dtype=complex) / np.sqrt(2)
initial_result = simulator.simulate(
    cirq.Circuit(cirq.I.on_each(*qubits)),
    initial_state=bell_initial_state, qubit_order=qubits,
)
np.testing.assert_allclose(initial_result.final_state_vector, bell_initial_state)
Raw lines 108 to 121
122## Local Google Quantum Virtual Machine
124The released package bundles historical/median calibration data. A QVM emulates
125those data, not the currently assigned live processor. No Google Cloud project,
126credentials, or hardware submission is needed here. Only simulate the two qubits
127used by the circuit; do not initialize the entire device density matrix.
129```python
130import cirq_google as cg
132available = cg.engine.list_virtual_processors()
133processor_id = "willow_pink" # present in Cirq 1.7.0; inspect available before changing
134assert processor_id in available
135virtual_engine = cg.engine.create_default_noisy_quantum_virtual_machine(
136 processor_id, simulator_class=cirq.DensityMatrixSimulator, seed=42,
138device = virtual_engine.get_processor(processor_id).get_device()
139a, b = next(iter(device.metadata.nx_graph.edges))
140virtual_circuit = cirq.Circuit(
141 cirq.H(a), cirq.CNOT(a, b), cirq.measure(a, b, key="result")
143compiled = cirq.optimize_for_target_gateset(
144 virtual_circuit, gateset=device.metadata.compilation_target_gatesets[0]
146device.validate_circuit(compiled)
147virtual_result = virtual_engine.get_sampler(processor_id).run(compiled, repetitions=100)
148```
150For direct use of the bundled noise properties:
151`cg.engine.load_device_noise_properties(processor_id)` returns the object needed
152by `cg.NoiseModelFromGoogleNoiseProperties`. An Engine `DeviceSpecification`
153protobuf is **not** a `GoogleNoiseProperties` object. For live calibration, the
154conversion also needs valid gate durations; consult the processor-specific noise
155documentation before substituting historical values.

Local Google Quantum Virtual Machine

The released package bundles historical/median calibration data. A QVM emulates those data, not the currently assigned live processor. No Google Cloud project, credentials, or hardware submission is needed here. Only simulate the two qubits used by the circuit; do not initialize the entire device density matrix.

import cirq_google as cg

available = cg.engine.list_virtual_processors()
processor_id = "willow_pink"  # present in Cirq 1.7.0; inspect available before changing
assert processor_id in available
virtual_engine = cg.engine.create_default_noisy_quantum_virtual_machine(
    processor_id, simulator_class=cirq.DensityMatrixSimulator, seed=42,
)
device = virtual_engine.get_processor(processor_id).get_device()
a, b = next(iter(device.metadata.nx_graph.edges))
virtual_circuit = cirq.Circuit(
    cirq.H(a), cirq.CNOT(a, b), cirq.measure(a, b, key="result")
)
compiled = cirq.optimize_for_target_gateset(
    virtual_circuit, gateset=device.metadata.compilation_target_gatesets[0]
)
device.validate_circuit(compiled)
virtual_result = virtual_engine.get_sampler(processor_id).run(compiled, repetitions=100)

For direct use of the bundled noise properties: cg.engine.load_device_noise_properties(processor_id) returns the object needed by cg.NoiseModelFromGoogleNoiseProperties. An Engine DeviceSpecification protobuf is not a GoogleNoiseProperties object. For live calibration, the conversion also needs valid gate durations; consult the processor-specific noise documentation before substituting historical values.

Raw lines 122 to 156
157## Resource choice and visualization
159A dense n-qubit state vector stores `2**n` complex numbers; a density matrix stores
160`4**n`. Multiply by `np.dtype(dtype).itemsize` for the array alone, then allow for
161scratch memory. Do not promise a qubit limit without checking the machine and
162simulator. For Clifford-only circuits use `cirq.CliffordSimulator`:
164```python
165clifford_result = cirq.CliffordSimulator(seed=42).run(measured, repetitions=100)
166assert set(clifford_result.histogram(key="result")) <= {0, 3}
167```
169Plot state probabilities as `np.abs(state)**2`, or sampled histogram counts,
170labeling which quantity is shown. Preserve the qubit order in axis labels and
171record seeds, dtype, shot count, package version, and noise parameters.
173Sources: [simulation](https://quantumai.google/cirq/simulate/simulation),
174[QVM](https://quantumai.google/cirq/simulate/quantum_virtual_machine),
175[partial trace](https://quantumai.google/reference/python/cirq/partial_trace),
176[noise representation](https://quantumai.google/cirq/noise/representing_noise).

Resource choice and visualization

A dense n-qubit state vector stores 2**n complex numbers; a density matrix stores 4**n. Multiply by np.dtype(dtype).itemsize for the array alone, then allow for scratch memory. Do not promise a qubit limit without checking the machine and simulator. For Clifford-only circuits use cirq.CliffordSimulator:

clifford_result = cirq.CliffordSimulator(seed=42).run(measured, repetitions=100)
assert set(clifford_result.histogram(key="result")) <= {0, 3}

Plot state probabilities as np.abs(state)**2, or sampled histogram counts, labeling which quantity is shown. Preserve the qubit order in axis labels and record seeds, dtype, shot count, package version, and noise parameters.

Sources: simulation, QVM, partial trace, noise representation.

Raw lines 157 to 176