Closed
Description
I wrote a custom simulator recently and I had to copy quite a bit of boilerplate code out of cirq/sim/sparse_simulator.py. Below is my code with the boilerplate pointed out. It would be nice if the code in SimulatesSamples and SimulatesIntermediateState parent classes were rearranged to reduce this. @Strilanc
class MySimulator(cirq.SimulatesSamples,
cirq.SimulatesIntermediateState):
# Boilerplate
def _run(self, circuit, param_resolver, repetitions):
param_resolver = param_resolver or cirq.ParamResolver({})
resolved_circuit = cirq.resolve_parameters(circuit, param_resolver)
assert not cirq.is_parameterized(resolved_circuit)
return self._run_sweep_repeat(resolved_circuit, repetitions)
# Boilerplate
def _run_sweep_repeat(self, circuit, repetitions):
measurements = defaultdict(list)
for _ in range(repetitions):
all_step_results = self._base_iterator(
circuit,
qubit_order=cirq.QubitOrder.DEFAULT,
initial_state=0)
for step_result in all_step_results:
for k, v in step_result.measurements.items():
measurements[k].append(np.array(v, dtype=np.uint8))
return {k: np.array(v, dtype=np.uint8) for k, v in measurements.items()}
# Boilerplate
def _simulator_iterator(self, circuit, param_resolver, qubit_order,
initial_state):
param_resolver = param_resolver or cirq.ParamResolver({})
resolved_circuit = cirq.resolve_parameters(circuit, param_resolver)
assert not cirq.is_parameterized(resolved_circuit)
actual_initial_state = 0 if initial_state is None else initial_state
return self._base_iterator(resolved_circuit, qubit_order,
actual_initial_state,
perform_measurements=True)
def _base_iterator(self, circuit, qubit_order, initial_state,
perform_measurements=True):
# Boilerplate
qubits = cirq.QubitOrder.as_qubit_order(qubit_order).order_for(
circuit.all_qubits())
num_qubits = len(qubits)
qid_shape = cirq.qid_shape(qubits)
qubit_map = {q: i for i, q in enumerate(qubits)}
if len(circuit) == 0:
yield MySimulatorStep(<default state>, {}, qubit_map)
def on_stuck(bad_op):
return TypeError(
"Can't simulate unknown operations that don't specify a"
"unitary or a decomposition. {!r}".format(bad_op))
def keep(potential_op):
return (cirq.has_unitary(potential_op) or
cirq.has_mixture(potential_op) or
cirq.is_measurement(potential_op) or
cirq.op_gate_isinstance(potential_op, cirq.ResetChannel))
simulator_state = ... # New code
for moment in circuit:
measurements = defaultdict(list)
known_ops = cirq.decompose(moment, keep=keep,
on_stuck_raise=on_stuck)
for op in known_ops:
self.simulate_op(op, simulator_state) # New code
yield MySimulatorStep(simulator_state, measurements, qubit_map)
def simulate_op(op, state):
# Actual simulation code here
...
class MySimulatorStep(cirq.StepResult):
# Boilerplate
def __init__(self, state, measurements, qubit_map):
super().__init__(measurements)
self.state = state
self.qubit_map = qubit_map
# Boilerplate
def _simulator_state(self):
return self.state
def sample(self, qubits, repetitions=1):
# Actual sampling code here
return ...