Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cirq-core/cirq/devices/noise_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ def __init__(self, noise_properties: NoiseProperties) -> None:
self._noise_properties = noise_properties
self.noise_models = self._noise_properties.build_noise_models()

@property
def noise_properties(self):
return self._noise_properties

def _value_equality_values_(self):
return self._noise_properties

Expand Down
33 changes: 31 additions & 2 deletions cirq-core/cirq/sim/simulator_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
if TYPE_CHECKING:
import cirq

TStepResultBase = TypeVar('TStepResultBase', bound='StepResultBase')
TStepResultBase = TypeVar("TStepResultBase", bound="StepResultBase")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lots of modifications to this file due to running ruff format.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert. Use check/format-incremental for formatting instead.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, will do - fixed!



class SimulatorBase(
Expand Down Expand Up @@ -186,13 +186,42 @@ def _core_iterator(
StepResults from simulating a Moment of the Circuit.

Raises:
TypeError: The simulator encounters an op it does not support.
TypeError: The simulator encounters an op it or its noise model
does not support.
"""

if len(circuit) == 0:
yield self._create_step_result(sim_state)
return

# For any noise model derived from noise properties, check the circuit to ensure it
# is compatible with the noise properties.
if isinstance(self._noise, devices.NoiseModelFromNoiseProperties):
noise_props = self._noise.noise_properties
# So far, only SuperconductingQubitsNoiseProperties implements such constraints on
# the circuit.
if isinstance(noise_props, devices.SuperconductingQubitsNoiseProperties):
circuit_gates = {op.gate for op in circuit.all_operations()}
if not all(
any(
isinstance(gate, expected_gate)
for expected_gate in noise_props.expected_gates()
)
for gate in circuit_gates
):
raise TypeError(
f"Circuit uses "
f"{circuit_gates.difference(noise_props.expected_gates())} "
f"which is not supported by noise properties "
f'"{noise_props.__class__.__name__}"'
)
if not circuit.all_qubits().issubset(noise_props.qubits):
raise TypeError(
f"Circuit uses "
f"{circuit.all_qubits().difference(noise_props.qubits)} "
f"which is not supported by noise properties "
f'"{noise_props.__class__.__name__}"'
)
noisy_moments = self.noise.noisy_moments(circuit, sorted(circuit.all_qubits()))
measured: dict[tuple[cirq.Qid, ...], bool] = collections.defaultdict(bool)
for moment in noisy_moments:
Expand Down
81 changes: 65 additions & 16 deletions cirq-core/cirq/sim/simulator_base_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import sympy

import cirq
from cirq import devices, ops
from cirq.devices import noise_utils


class CountingState(cirq.qis.QuantumStateRepresentation):
Expand Down Expand Up @@ -229,7 +231,7 @@ def test_parameterized_copies_all_but_last() -> None:
sim = CountingSimulator()
n = 4
rs = sim.simulate_sweep(
cirq.Circuit(cirq.X(q0) ** sympy.Symbol('a')), [{'a': i} for i in range(n)]
cirq.Circuit(cirq.X(q0) ** sympy.Symbol("a")), [{"a": i} for i in range(n)]
)
for i in range(n):
r = rs[i]
Expand All @@ -252,19 +254,19 @@ def _act_on_(self, sim_state):
def test_run_one_gate_circuit() -> None:
sim = CountingSimulator()
r = sim.run(cirq.Circuit(cirq.X(q0), cirq.measure(q0)), repetitions=2)
assert np.allclose(r.measurements['q(0)'], [[1], [1]])
assert np.allclose(r.measurements["q(0)"], [[1], [1]])


def test_run_one_gate_circuit_noise() -> None:
sim = CountingSimulator(noise=cirq.X)
r = sim.run(cirq.Circuit(cirq.X(q0), cirq.measure(q0)), repetitions=2)
assert np.allclose(r.measurements['q(0)'], [[2], [2]])
assert np.allclose(r.measurements["q(0)"], [[2], [2]])


def test_run_non_unitary_circuit() -> None:
sim = CountingSimulator()
r = sim.run(cirq.Circuit(cirq.phase_damp(1).on(q0), cirq.measure(q0)), repetitions=2)
assert np.allclose(r.measurements['q(0)'], [[1], [1]])
assert np.allclose(r.measurements["q(0)"], [[1], [1]])


def test_run_non_unitary_circuit_non_unitary_state() -> None:
Expand All @@ -274,13 +276,13 @@ def _can_be_in_run_prefix(self, val):

sim = DensityCountingSimulator()
r = sim.run(cirq.Circuit(cirq.phase_damp(1).on(q0), cirq.measure(q0)), repetitions=2)
assert np.allclose(r.measurements['q(0)'], [[1], [1]])
assert np.allclose(r.measurements["q(0)"], [[1], [1]])


def test_run_non_terminal_measurement() -> None:
sim = CountingSimulator()
r = sim.run(cirq.Circuit(cirq.X(q0), cirq.measure(q0), cirq.X(q0)), repetitions=2)
assert np.allclose(r.measurements['q(0)'], [[1], [1]])
assert np.allclose(r.measurements["q(0)"], [[1], [1]])


def test_integer_initial_state_is_split() -> None:
Expand Down Expand Up @@ -370,7 +372,7 @@ def test_reorder_succeeds() -> None:
assert reordered.qubits == (q1, q0)


@pytest.mark.parametrize('split', [True, False])
@pytest.mark.parametrize("split", [True, False])
def test_sim_state_instance_unchanged_during_normal_sim(split: bool) -> None:
sim = SplittableCountingSimulator(split_untangled_states=split)
state = sim._create_simulation_state(0, (q0, q1))
Expand All @@ -383,12 +385,12 @@ def test_sim_state_instance_unchanged_during_normal_sim(split: bool) -> None:
def test_measurements_retained_in_step_results() -> None:
sim = SplittableCountingSimulator()
circuit = cirq.Circuit(
cirq.measure(q0, key='a'), cirq.measure(q0, key='b'), cirq.measure(q0, key='c')
cirq.measure(q0, key="a"), cirq.measure(q0, key="b"), cirq.measure(q0, key="c")
)
iterator = sim.simulate_moment_steps(circuit)
assert next(iterator).measurements.keys() == {'a'}
assert next(iterator).measurements.keys() == {'a', 'b'}
assert next(iterator).measurements.keys() == {'a', 'b', 'c'}
assert next(iterator).measurements.keys() == {"a"}
assert next(iterator).measurements.keys() == {"a", "b"}
assert next(iterator).measurements.keys() == {"a", "b", "c"}
assert not any(iterator)


Expand All @@ -415,11 +417,11 @@ def _has_unitary_(self):
return self.has_unitary

simulator = CountingSimulator()
params = [cirq.ParamResolver({'a': 0}), cirq.ParamResolver({'a': 1})]
params = [cirq.ParamResolver({"a": 0}), cirq.ParamResolver({"a": 1})]

op1 = TestOp(has_unitary=True)
op2 = TestOp(has_unitary=True)
circuit = cirq.Circuit(op1, cirq.XPowGate(exponent=sympy.Symbol('a'))(q), op2)
circuit = cirq.Circuit(op1, cirq.XPowGate(exponent=sympy.Symbol("a"))(q), op2)
rs = simulator.simulate_sweep(program=circuit, params=params)
assert isinstance(rs[0]._final_simulator_state, CountingSimulationState)
assert isinstance(rs[1]._final_simulator_state, CountingSimulationState)
Expand All @@ -430,7 +432,7 @@ def _has_unitary_(self):

op1 = TestOp(has_unitary=False)
op2 = TestOp(has_unitary=False)
circuit = cirq.Circuit(op1, cirq.XPowGate(exponent=sympy.Symbol('a'))(q), op2)
circuit = cirq.Circuit(op1, cirq.XPowGate(exponent=sympy.Symbol("a"))(q), op2)
rs = simulator.simulate_sweep(program=circuit, params=params)
assert isinstance(rs[0]._final_simulator_state, CountingSimulationState)
assert isinstance(rs[1]._final_simulator_state, CountingSimulationState)
Expand All @@ -442,7 +444,7 @@ def _has_unitary_(self):

def test_inhomogeneous_measurement_count_padding() -> None:
q = cirq.LineQubit(0)
key = cirq.MeasurementKey('m')
key = cirq.MeasurementKey("m")
sim = cirq.Simulator()
c = cirq.Circuit(
cirq.CircuitOperation(
Expand All @@ -453,4 +455,51 @@ def test_inhomogeneous_measurement_count_padding() -> None:
)
results = sim.run(c, repetitions=10)
for i in range(10):
assert np.sum(results.records['m'][i, :, :]) == 1
assert np.sum(results.records["m"][i, :, :]) == 1


def test_simulates_noise_only_on_valid_gates_and_qubits() -> None:
expected_single_qubit_gates = [cirq.XPowGate, cirq.ZPowGate]
expected_qubits = [cirq.GridQubit(1, 2)]

unexpected_qubits = [cirq.GridQubit(2, 2)]

class TestNoiseProperties(devices.SuperconductingQubitsNoiseProperties):

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the TestNoiseProperties created for this test might not make sense from a QC point of view just because of my limited domain knowledge - feel free to re-implement to something more sensible if this is a concern!

@classmethod
def single_qubit_gates(cls) -> set[type[ops.Gate]]:
return set(expected_single_qubit_gates)

@classmethod
def symmetric_two_qubit_gates(cls) -> set[type[ops.Gate]]:
return set()

@classmethod
def asymmetric_two_qubit_gates(cls) -> set[type[ops.Gate]]:
return set()

noise_props = TestNoiseProperties(
gate_times_ns=dict.fromkeys(expected_single_qubit_gates, 1e9),
t1_ns=dict.fromkeys(expected_qubits, 1e9),
tphi_ns=dict.fromkeys(expected_qubits, 1e9),
readout_errors=dict.fromkeys(expected_qubits, [0.5, 0.5]),
gate_pauli_errors={
noise_utils.OpIdentifier(gate, expected_qubits[0]): 0
for gate in expected_single_qubit_gates
},
)
noise_model = devices.NoiseModelFromNoiseProperties(noise_props)

simulator = cirq.Simulator(noise=noise_model)

valid_circuit_invalid_qubits = cirq.Circuit(cirq.X(unexpected_qubits[0]))
valid_circuit_valid_qubits = cirq.Circuit(cirq.X(expected_qubits[0]))
invalid_circuit_invalid_qubits = cirq.Circuit(cirq.Y(unexpected_qubits[0]))
invalid_circuit_valid_qubits = cirq.Circuit(cirq.Y(expected_qubits[0]))

with pytest.raises(TypeError):
simulator.simulate(invalid_circuit_invalid_qubits)
with pytest.raises(TypeError):
simulator.simulate(invalid_circuit_valid_qubits)
with pytest.raises(TypeError):
simulator.simulate(valid_circuit_invalid_qubits)
simulator.simulate(valid_circuit_valid_qubits)
Loading