diff --git a/cirq-core/cirq/__init__.py b/cirq-core/cirq/__init__.py index be2e0155725..48c72551b63 100644 --- a/cirq-core/cirq/__init__.py +++ b/cirq-core/cirq/__init__.py @@ -403,6 +403,7 @@ StateVectorStepResult, StateVectorTrialResult, StepResult, + StepResultBase, ) from cirq.study import ( diff --git a/cirq-core/cirq/contrib/quimb/mps_simulator.py b/cirq-core/cirq/contrib/quimb/mps_simulator.py index f868ec9c907..aa921fc766e 100644 --- a/cirq-core/cirq/contrib/quimb/mps_simulator.py +++ b/cirq-core/cirq/contrib/quimb/mps_simulator.py @@ -117,12 +117,9 @@ def _create_partial_act_on_args( def _create_step_result( self, - sim_state: 'MPSState', - qubit_map: Dict['cirq.Qid', int], + sim_state: 'cirq.OperationTarget[MPSState]', ): - return MPSSimulatorStepResult( - measurements=sim_state.log_of_measurement_results, state=sim_state - ) + return MPSSimulatorStepResult(sim_state) def _create_simulator_trial_result( self, @@ -169,22 +166,22 @@ def __str__(self) -> str: return f'measurements: {samples}\noutput state: {final}' -class MPSSimulatorStepResult(simulator.StepResult['MPSState']): +class MPSSimulatorStepResult(simulator_base.StepResultBase['MPSState', 'MPSState']): """A `StepResult` that can perform measurements.""" - def __init__(self, state, measurements): + def __init__( + self, + sim_state: 'cirq.OperationTarget[MPSState]', + ): """Results of a step of the simulator. Attributes: - state: A MPSState - measurements: A dictionary from measurement gate key to measurement - results, ordered by the qubits that the measurement operates on. - qubit_map: A map from the Qubits in the Circuit to the the index - of this qubit for a canonical ordering. This canonical ordering - is used to define the state vector (see the state_vector() - method). + sim_state: The qubit:ActOnArgs lookup for this step. """ - self.measurements = measurements - self.state = state.copy() + super().__init__(sim_state) + + @property + def state(self): + return self._merged_sim_state def __str__(self) -> str: def bitstring(vals): @@ -204,24 +201,6 @@ def bitstring(vals): def _simulator_state(self): return self.state - def sample( - self, - qubits: List[ops.Qid], - repetitions: int = 1, - seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, - ) -> np.ndarray: - - measurements: List[int] = [] - - for _ in range(repetitions): - measurements.append( - self.state.perform_measurement( - qubits, value.parse_random_state(seed), collapse_state_vector=False - ) - ) - - return np.array(measurements, dtype=int) - @value.value_equality class MPSState(ActOnArgs): @@ -537,3 +516,21 @@ def perform_measurement( def _perform_measurement(self, qubits: Sequence['cirq.Qid']) -> List[int]: """Measures the axes specified by the simulator.""" return self.perform_measurement(qubits, self.prng) + + def sample( + self, + qubits: Sequence[ops.Qid], + repetitions: int = 1, + seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, + ) -> np.ndarray: + + measurements: List[List[int]] = [] + + for _ in range(repetitions): + measurements.append( + self.perform_measurement( + qubits, value.parse_random_state(seed), collapse_state_vector=False + ) + ) + + return np.array(measurements, dtype=int) diff --git a/cirq-core/cirq/contrib/quimb/mps_simulator_test.py b/cirq-core/cirq/contrib/quimb/mps_simulator_test.py index bff931da378..a5660621c75 100644 --- a/cirq-core/cirq/contrib/quimb/mps_simulator_test.py +++ b/cirq-core/cirq/contrib/quimb/mps_simulator_test.py @@ -274,11 +274,11 @@ def test_trial_result_str(): def test_empty_step_result(): q0 = cirq.LineQubit(0) - state = ccq.mps_simulator.MPSState(qubits=(q0,), prng=value.parse_random_state(0)) - step_result = ccq.mps_simulator.MPSSimulatorStepResult(state, measurements={'0': [1]}) + sim = ccq.mps_simulator.MPSSimulator() + step_result = next(sim.simulate_moment_steps(cirq.Circuit(cirq.measure(q0)))) assert ( str(step_result) - == """0=1 + == """0=0 TensorNetwork([ Tensor(shape=(2,), inds=('i_0',), tags=set()), ])""" diff --git a/cirq-core/cirq/experiments/grid_parallel_two_qubit_xeb_test.py b/cirq-core/cirq/experiments/grid_parallel_two_qubit_xeb_test.py index a8ae6b02e6a..23d13382ce2 100644 --- a/cirq-core/cirq/experiments/grid_parallel_two_qubit_xeb_test.py +++ b/cirq-core/cirq/experiments/grid_parallel_two_qubit_xeb_test.py @@ -20,7 +20,7 @@ def test_estimate_parallel_two_qubit_xeb_fidelity_on_grid_no_noise(tmpdir): two_qubit_gate = cirq.ISWAP ** 0.5 cycles = [5, 10, 15] data_collection_id = collect_grid_parallel_two_qubit_xeb_data( - sampler=cirq.Simulator(seed=34310), + sampler=cirq.Simulator(seed=34310, split_untangled_states=False), qubits=qubits, two_qubit_gate=two_qubit_gate, num_circuits=2, @@ -53,7 +53,9 @@ def test_estimate_parallel_two_qubit_xeb_fidelity_on_grid_depolarizing(tmpdir): cycles = [5, 10, 15] e = 0.01 data_collection_id = collect_grid_parallel_two_qubit_xeb_data( - sampler=cirq.DensityMatrixSimulator(noise=cirq.depolarize(e), seed=65008), + sampler=cirq.DensityMatrixSimulator( + noise=cirq.depolarize(e), seed=65008, split_untangled_states=False + ), qubits=qubits, two_qubit_gate=two_qubit_gate, num_circuits=2, diff --git a/cirq-core/cirq/experiments/single_qubit_readout_calibration_test.py b/cirq-core/cirq/experiments/single_qubit_readout_calibration_test.py index cea97210a63..8c5675fcb84 100644 --- a/cirq-core/cirq/experiments/single_qubit_readout_calibration_test.py +++ b/cirq-core/cirq/experiments/single_qubit_readout_calibration_test.py @@ -31,7 +31,7 @@ def __init__(self, p0: float, p1: float, seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' self.p0 = p0 self.p1 = p1 self.prng = cirq.value.parse_random_state(seed) - self.simulator = cirq.Simulator(seed=self.prng) + self.simulator = cirq.Simulator(seed=self.prng, split_untangled_states=False) def run_sweep( self, diff --git a/cirq-core/cirq/protocols/act_on_protocol_test.py b/cirq-core/cirq/protocols/act_on_protocol_test.py index dc83f3021bd..11b056cb9d5 100644 --- a/cirq-core/cirq/protocols/act_on_protocol_test.py +++ b/cirq-core/cirq/protocols/act_on_protocol_test.py @@ -37,6 +37,9 @@ def copy(self): def _act_on_fallback_(self, action, qubits, allow_decompose): return self.fallback_result + def sample(self, qubits, repetitions=1, seed=None): + pass + op = cirq.X(cirq.LineQubit(0)) diff --git a/cirq-core/cirq/sim/__init__.py b/cirq-core/cirq/sim/__init__.py index fe1ef692654..e04a560733d 100644 --- a/cirq-core/cirq/sim/__init__.py +++ b/cirq-core/cirq/sim/__init__.py @@ -64,6 +64,7 @@ ) from cirq.sim.simulator_base import ( + StepResultBase, SimulatorBase, ) diff --git a/cirq-core/cirq/sim/act_on_args.py b/cirq-core/cirq/sim/act_on_args.py index 2cecaa444e8..69b33ee93d9 100644 --- a/cirq-core/cirq/sim/act_on_args.py +++ b/cirq-core/cirq/sim/act_on_args.py @@ -60,8 +60,7 @@ def __init__( axes: The indices of axes corresponding to the qubits that the operation is supposed to act upon. log_of_measurement_results: A mutable object that measurements are - being recorded into. Edit it easily by calling - `ActOnStateVectorArgs.record_measurement_result`. + being recorded into. """ if prng is None: prng = cast(np.random.RandomState, np.random) @@ -72,7 +71,7 @@ def __init__( if log_of_measurement_results is None: log_of_measurement_results = {} self._qubits = tuple(qubits) - self.qubit_map = {q: i for i, q in enumerate(self.qubits)} + self.qubit_map = {q: i for i, q in enumerate(qubits)} self._axes = tuple(axes) self.prng = prng self._log_of_measurement_results = log_of_measurement_results @@ -89,9 +88,9 @@ def measure(self, qubits: Sequence['cirq.Qid'], key: str, invert_mask: Sequence[ """ bits = self._perform_measurement(qubits) corrected = [bit ^ (bit < 2 and mask) for bit, mask in zip(bits, invert_mask)] - if key in self.log_of_measurement_results: + if key in self._log_of_measurement_results: raise ValueError(f"Measurement already logged to key {key!r}") - self.log_of_measurement_results[key] = corrected + self._log_of_measurement_results[key] = corrected def get_axes(self, qubits: Sequence['cirq.Qid']) -> List[int]: return [self.qubit_map[q] for q in qubits] diff --git a/cirq-core/cirq/sim/act_on_args_container.py b/cirq-core/cirq/sim/act_on_args_container.py index db9e9b97307..fd5f9b11888 100644 --- a/cirq-core/cirq/sim/act_on_args_container.py +++ b/cirq-core/cirq/sim/act_on_args_container.py @@ -20,10 +20,14 @@ Sequence, Optional, Iterator, - Tuple, Any, + Tuple, + Set, + List, ) +import numpy as np + from cirq import ops from cirq.sim.operation_target import OperationTarget from cirq.sim.simulator import ( @@ -122,6 +126,26 @@ def qubits(self) -> Tuple['cirq.Qid', ...]: def log_of_measurement_results(self) -> Dict[str, Any]: return self._log_of_measurement_results + def sample( + self, + qubits: List[ops.Qid], + repetitions: int = 1, + seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, + ) -> np.ndarray: + columns = [] + selected_order: List[ops.Qid] = [] + q_set = set(qubits) + for v in dict.fromkeys(self.args.values()): + qs = [q for q in v.qubits if q in q_set] + if any(qs): + column = v.sample(qs, repetitions, seed) + columns.append(column) + selected_order += qs + stacked = np.column_stack(columns) + qubit_map = {q: i for i, q in enumerate(selected_order)} + index_order = [qubit_map[q] for q in qubits] + return stacked[:, index_order] + def __getitem__(self, item: Optional['cirq.Qid']) -> TActOnArgs: return self.args[item] diff --git a/cirq-core/cirq/sim/act_on_args_container_test.py b/cirq-core/cirq/sim/act_on_args_container_test.py index 02cbeded721..23025289bf0 100644 --- a/cirq-core/cirq/sim/act_on_args_container_test.py +++ b/cirq-core/cirq/sim/act_on_args_container_test.py @@ -64,6 +64,9 @@ def transpose_to_qubit_order(self, qubits: Sequence['cirq.Qid']) -> 'EmptyActOnA logs=self.log_of_measurement_results, ) + def sample(self, qubits, repetitions=1, seed=None): + pass + q0, q1 = qs2 = cirq.LineQubit.range(2) diff --git a/cirq-core/cirq/sim/act_on_args_test.py b/cirq-core/cirq/sim/act_on_args_test.py index 5774a3a610e..dc323a6e140 100644 --- a/cirq-core/cirq/sim/act_on_args_test.py +++ b/cirq-core/cirq/sim/act_on_args_test.py @@ -25,6 +25,9 @@ def __init__(self): def copy(self): pass + def sample(self, qubits, repetitions=1, seed=None): + pass + def _perform_measurement(self, qubits): return [5, 3] diff --git a/cirq-core/cirq/sim/act_on_density_matrix_args.py b/cirq-core/cirq/sim/act_on_density_matrix_args.py index fb98e635894..ae47fae0bbb 100644 --- a/cirq-core/cirq/sim/act_on_density_matrix_args.py +++ b/cirq-core/cirq/sim/act_on_density_matrix_args.py @@ -82,8 +82,7 @@ def __init__( prng: The pseudo random number generator to use for probabilistic effects. log_of_measurement_results: A mutable object that measurements are - being recorded into. Edit it easily by calling - `ActOnStateVectorArgs.record_measurement_result`. + being recorded into. axes: The indices of axes corresponding to the qubits that the operation is supposed to act upon. """ @@ -197,6 +196,21 @@ def transpose_to_qubit_order( log_of_measurement_results=self.log_of_measurement_results, ) + def sample( + self, + qubits: Sequence['cirq.Qid'], + repetitions: int = 1, + seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, + ) -> np.ndarray: + indices = [self.qubit_map[q] for q in qubits] + return sim.sample_density_matrix( + self.target_tensor, + indices, + qid_shape=tuple(q.dimension for q in self.qubits), + repetitions=repetitions, + seed=seed, + ) + def _strat_apply_channel_to_state( action: Any, args: ActOnDensityMatrixArgs, qubits: Sequence['cirq.Qid'] diff --git a/cirq-core/cirq/sim/act_on_state_vector_args.py b/cirq-core/cirq/sim/act_on_state_vector_args.py index 71cd8b9ed3e..18630ddcf96 100644 --- a/cirq-core/cirq/sim/act_on_state_vector_args.py +++ b/cirq-core/cirq/sim/act_on_state_vector_args.py @@ -41,13 +41,12 @@ def _rewrite_deprecated_args(args, kwargs): class ActOnStateVectorArgs(ActOnArgs): """State and context for an operation acting on a state vector. - There are three common ways to act on this object: + There are two common ways to act on this object: 1. Directly edit the `target_tensor` property, which is storing the state vector of the quantum system as a numpy array with one axis per qudit. 2. Overwrite the `available_buffer` property with the new state vector, and then pass `available_buffer` into `swap_target_tensor_for`. - 3. Call `record_measurement_result(key, val)` to log a measurement result. """ @deprecated_parameter( @@ -84,8 +83,7 @@ def __init__( prng: The pseudo random number generator to use for probabilistic effects. log_of_measurement_results: A mutable object that measurements are - being recorded into. Edit it easily by calling - `ActOnStateVectorArgs.record_measurement_result`. + being recorded into. axes: The indices of axes corresponding to the qubits that the operation is supposed to act upon. """ @@ -255,6 +253,21 @@ def transpose_to_qubit_order(self, qubits: Sequence['cirq.Qid']) -> 'cirq.ActOnS ) return new_args + def sample( + self, + qubits: Sequence['cirq.Qid'], + repetitions: int = 1, + seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, + ) -> np.ndarray: + indices = [self.qubit_map[q] for q in qubits] + return sim.sample_state_vector( + self.target_tensor, + indices, + qid_shape=tuple(q.dimension for q in self.qubits), + repetitions=repetitions, + seed=seed, + ) + def _strat_act_on_state_vector_from_apply_unitary( unitary_value: Any, diff --git a/cirq-core/cirq/sim/clifford/act_on_clifford_tableau_args.py b/cirq-core/cirq/sim/clifford/act_on_clifford_tableau_args.py index 23a8e830ab7..8f423a0e667 100644 --- a/cirq-core/cirq/sim/clifford/act_on_clifford_tableau_args.py +++ b/cirq-core/cirq/sim/clifford/act_on_clifford_tableau_args.py @@ -44,10 +44,9 @@ def _rewrite_deprecated_args(args, kwargs): class ActOnCliffordTableauArgs(ActOnArgs): """State and context for an operation acting on a clifford tableau. - There are two common ways to act on this object: - 1. Directly edit the `tableau` property, which is storing the clifford - tableau of the quantum system with one axis per qubit. - 2. Call `record_measurement_result(key, val)` to log a measurement result. + + To act on this object, directly edit the `tableau` property, which is + storing the density matrix of the quantum system with one axis per qubit. """ @deprecated_parameter( @@ -77,8 +76,7 @@ def __init__( prng: The pseudo random number generator to use for probabilistic effects. log_of_measurement_results: A mutable object that measurements are - being recorded into. Edit it easily by calling - `ActOnCliffordTableauArgs.record_measurement_result`. + being recorded into. axes: The indices of axes corresponding to the qubits that the operation is supposed to act upon. """ @@ -111,6 +109,15 @@ def copy(self) -> 'cirq.ActOnCliffordTableauArgs': log_of_measurement_results=self.log_of_measurement_results.copy(), ) + def sample( + self, + qubits: Sequence['cirq.Qid'], + repetitions: int = 1, + seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, + ) -> np.ndarray: + # Unnecessary for now but can be added later if there is a use case. + raise NotImplementedError() + def _strat_act_on_clifford_tableau_from_single_qubit_decompose( val: Any, args: 'cirq.ActOnCliffordTableauArgs', qubits: Sequence['cirq.Qid'] diff --git a/cirq-core/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py b/cirq-core/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py index 86911788c93..417e5fbcc56 100644 --- a/cirq-core/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py +++ b/cirq-core/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py @@ -16,6 +16,7 @@ import numpy as np +from cirq import value, ops, protocols from cirq._compat import deprecated_parameter from cirq.ops import common_gates, pauli_gates from cirq.ops.clifford_gate import SingleQubitCliffordGate @@ -74,8 +75,7 @@ def __init__( prng: The pseudo random number generator to use for probabilistic effects. log_of_measurement_results: A mutable object that measurements are - being recorded into. Edit it easily by calling - `ActOnStabilizerCHFormArgs.record_measurement_result`. + being recorded into. axes: The indices of axes corresponding to the qubits that the operation is supposed to act upon. """ @@ -106,6 +106,21 @@ def copy(self) -> 'cirq.ActOnStabilizerCHFormArgs': log_of_measurement_results=self.log_of_measurement_results.copy(), ) + def sample( + self, + qubits: Sequence['cirq.Qid'], + repetitions: int = 1, + seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, + ) -> np.ndarray: + measurements: Dict[str, List[np.ndarray]] = {} + prng = value.parse_random_state(seed) + for i in range(repetitions): + op = ops.measure(*qubits, key=str(i)) + state = self.state.copy() + ch_form_args = ActOnStabilizerCHFormArgs(state, prng, measurements, self.qubits) + protocols.act_on(op, ch_form_args) + return np.array(list(measurements.values()), dtype=bool) + def _strat_act_on_stabilizer_ch_form_from_single_qubit_decompose( val: Any, args: 'cirq.ActOnStabilizerCHFormArgs', qubits: Sequence['cirq.Qid'] diff --git a/cirq-core/cirq/sim/clifford/clifford_simulator.py b/cirq-core/cirq/sim/clifford/clifford_simulator.py index 44a28a40e06..cba51cd93ce 100644 --- a/cirq-core/cirq/sim/clifford/clifford_simulator.py +++ b/cirq-core/cirq/sim/clifford/clifford_simulator.py @@ -34,7 +34,7 @@ import numpy as np import cirq -from cirq import study, ops, protocols, value +from cirq import study, protocols, value from cirq.protocols import act_on from cirq.sim import clifford, simulator, simulator_base @@ -97,14 +97,9 @@ def _create_partial_act_on_args( def _create_step_result( self, - sim_state: clifford.ActOnStabilizerCHFormArgs, - qubit_map: Dict['cirq.Qid', int], + sim_state: 'cirq.OperationTarget[clifford.ActOnStabilizerCHFormArgs]', ): - state = CliffordState(qubit_map) - state.ch_form = sim_state.state.copy() - return CliffordSimulatorStepResult( - measurements=sim_state.log_of_measurement_results, state=state - ) + return CliffordSimulatorStepResult(sim_state=sim_state) def _create_simulator_trial_result( self, @@ -137,22 +132,21 @@ def __str__(self) -> str: return f'measurements: {samples}\noutput state: {final}' -class CliffordSimulatorStepResult(simulator.StepResult['CliffordState']): +class CliffordSimulatorStepResult( + simulator_base.StepResultBase['clifford.CliffordState', 'clifford.ActOnStabilizerCHFormArgs'] +): """A `StepResult` that includes `StateVectorMixin` methods.""" - def __init__(self, state: 'CliffordState', measurements): + def __init__( + self, + sim_state: 'cirq.OperationTarget[clifford.ActOnStabilizerCHFormArgs]', + ): """Results of a step of the simulator. Attributes: - state: A CliffordState - measurements: A dictionary from measurement gate key to measurement - results, ordered by the qubits that the measurement operates on. - qubit_map: A map from the Qubits in the Circuit to the the index - of this qubit for a canonical ordering. This canonical ordering - is used to define the state vector (see the state_vector() - method). + sim_state: The qubit:ActOnArgs lookup for this step. """ - self.measurements = measurements - self.state = state.copy() + super().__init__(sim_state) + self._clifford_state = None def __str__(self) -> str: def bitstring(vals): @@ -169,28 +163,17 @@ def bitstring(vals): return f'{measurements}{final}' + @property + def state(self): + if self._clifford_state is None: + clifford_state = CliffordState(self._qubit_mapping) + clifford_state.ch_form = self._merged_sim_state.state.copy() + self._clifford_state = clifford_state + return self._clifford_state + def _simulator_state(self): return self.state - def sample( - self, - qubits: List[ops.Qid], - repetitions: int = 1, - seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, - ) -> np.ndarray: - - measurements = {} # type: Dict[str, List[np.ndarray]] - - for i in range(repetitions): - self.state.apply_measurement( - cirq.measure(*qubits, key=str(i)), - measurements, - value.parse_random_state(seed), - collapse_state_vector=False, - ) - - return np.array(list(measurements.values()), dtype=bool) - @value.value_equality class CliffordState: diff --git a/cirq-core/cirq/sim/clifford/clifford_simulator_test.py b/cirq-core/cirq/sim/clifford/clifford_simulator_test.py index b68e14c24c1..5527c7f8725 100644 --- a/cirq-core/cirq/sim/clifford/clifford_simulator_test.py +++ b/cirq-core/cirq/sim/clifford/clifford_simulator_test.py @@ -241,26 +241,16 @@ def test_clifford_trial_result_str(): def test_clifford_step_result_str(): q0 = cirq.LineQubit(0) - final_simulator_state = cirq.CliffordState(qubit_map={q0: 0}) - - assert ( - str( - cirq.CliffordSimulatorStepResult( - measurements={'m': np.array([[1]])}, state=final_simulator_state - ) - ) - == "m=1\n" - "|0⟩" + result = next( + cirq.CliffordSimulator().simulate_moment_steps(cirq.Circuit(cirq.measure(q0, key='m'))) ) + assert str(result) == "m=0\n" "|0⟩" def test_clifford_step_result_no_measurements_str(): q0 = cirq.LineQubit(0) - final_simulator_state = cirq.CliffordState(qubit_map={q0: 0}) - - assert ( - str(cirq.CliffordSimulatorStepResult(measurements={}, state=final_simulator_state)) == "|0⟩" - ) + result = next(cirq.CliffordSimulator().simulate_moment_steps(cirq.Circuit(cirq.I(q0)))) + assert str(result) == "|0⟩" def test_clifford_state_str(): diff --git a/cirq-core/cirq/sim/density_matrix_simulator.py b/cirq-core/cirq/sim/density_matrix_simulator.py index b15fd6b5646..cef77ca1275 100644 --- a/cirq-core/cirq/sim/density_matrix_simulator.py +++ b/cirq-core/cirq/sim/density_matrix_simulator.py @@ -12,12 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. """Simulator for density matrices that simulates noisy quantum circuits.""" -from typing import Any, Dict, List, TYPE_CHECKING, Tuple, Union, Sequence +from typing import Any, Dict, TYPE_CHECKING, Tuple, Union, Sequence, Optional, List import numpy as np from cirq import ops, protocols, qis, study, value -from cirq.sim import density_matrix_utils, simulator, act_on_density_matrix_args, simulator_base +from cirq.sim import ( + simulator, + act_on_density_matrix_args, + simulator_base, +) if TYPE_CHECKING: import cirq @@ -119,7 +123,7 @@ def __init__( noise: 'cirq.NOISE_MODEL_LIKE' = None, seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, ignore_measurement_results: bool = False, - split_untangled_states: bool = False, + split_untangled_states: bool = True, ): """Density matrix simulator. @@ -206,15 +210,12 @@ def _can_be_in_run_prefix(self, val: Any): def _create_step_result( self, - sim_state: act_on_density_matrix_args.ActOnDensityMatrixArgs, - qubit_map: Dict['cirq.Qid', int], + sim_state: 'cirq.OperationTarget[cirq.ActOnDensityMatrixArgs]', ): return DensityMatrixStepResult( - density_matrix=sim_state.target_tensor, - measurements=dict(sim_state.log_of_measurement_results), - qubit_map=qubit_map, + sim_state=sim_state, + simulator=self, dtype=self._dtype, - split_untangled_states=self._split_untangled_states, ) def _create_simulator_trial_result( @@ -262,54 +263,43 @@ def simulate_expectation_values_sweep( return swept_evs -class DensityMatrixStepResult(simulator.StepResult['DensityMatrixSimulatorState']): +class DensityMatrixStepResult( + simulator_base.StepResultBase[ + 'DensityMatrixSimulatorState', act_on_density_matrix_args.ActOnDensityMatrixArgs + ] +): """A single step in the simulation of the DensityMatrixSimulator. Attributes: - qubit_map: A map from the Qubits in the Circuit to the the index - of this qubit for a canonical ordering. This canonical ordering - is used to define the state vector (see the state_vector() - method). measurements: A dictionary from measurement gate key to measurement results, ordered by the qubits that the measurement operates on. """ def __init__( self, - density_matrix: np.ndarray, - measurements: Dict[str, np.ndarray], - qubit_map: Dict[ops.Qid, int], + sim_state: 'cirq.OperationTarget[cirq.ActOnDensityMatrixArgs]', + simulator: DensityMatrixSimulator, dtype: 'DTypeLike' = np.complex64, - split_untangled_states: bool = False, ): """DensityMatrixStepResult. Args: - density_matrix: The density matrix at this step. Can be mutated. - measurements: The measurements for this step of the simulation. - qubit_map: A map from qid to index used to define the - ordering of the basis in density_matrix. - dtype: The numpy dtype for the density matrix. + sim_state: The qubit:ActOnArgs lookup for this step. + simulator: The simulator used to create this. + dtype: The `numpy.dtype` used by the simulation. One of + `numpy.complex64` or `numpy.complex128`. """ - super().__init__(measurements) - self._density_matrix = density_matrix - self._qubit_map = qubit_map + super().__init__(sim_state) self._dtype = dtype - self._qid_shape = simulator._qubit_map_to_shape(qubit_map) - self._split_untangled_states = split_untangled_states - - def _qid_shape_(self): - return self._qid_shape + self._density_matrix: Optional[np.ndarray] = None + self._simulator = simulator def _simulator_state(self) -> 'DensityMatrixSimulatorState': - return DensityMatrixSimulatorState(self._density_matrix, self._qubit_map) + return DensityMatrixSimulatorState(self.density_matrix(copy=False), self._qubit_mapping) def set_density_matrix(self, density_matrix_repr: Union[int, np.ndarray]): """Set the density matrix to a new density matrix. - Note that this feature is incompatible with the simulation setting - `split_untangled_states=True`, and will throw an error if attempted. - Args: density_matrix_repr: If this is an int, the density matrix is set to the computational basis state corresponding to this state. Otherwise @@ -320,17 +310,7 @@ def set_density_matrix(self, density_matrix_repr: Union[int, np.ndarray]): mixed state it must be correctly sized and positive semidefinite with trace one. """ - if self._split_untangled_states: - # TODO: Fix in #4110 - raise ValueError( # coverage: ignore - 'Cannot set states when using `split_untangled_states` option.' # coverage: ignore - ) # coverage: ignore - density_matrix = qis.to_valid_density_matrix( - density_matrix_repr, len(self._qubit_map), qid_shape=self._qid_shape, dtype=self._dtype - ) - sim_state_matrix = self._simulator_state().density_matrix - density_matrix = np.reshape(density_matrix, sim_state_matrix.shape) - np.copyto(dst=sim_state_matrix, src=density_matrix) + self._sim_state = self._simulator._create_act_on_args(density_matrix_repr, self._qubits) def density_matrix(self, copy=True): """Returns the density matrix at this step in the simulation. @@ -367,24 +347,14 @@ def density_matrix(self, copy=True): parameters from the density matrix and store then using False can speed up simulation by eliminating a memory copy. """ - size = np.prod(self._qid_shape, dtype=int) - matrix = self._density_matrix.copy() if copy else self._density_matrix - return np.reshape(matrix, (size, size)) - - def sample( - self, - qubits: List[ops.Qid], - repetitions: int = 1, - seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, - ) -> np.ndarray: - indices = [self._qubit_map[q] for q in qubits] - return density_matrix_utils.sample_density_matrix( - self._simulator_state().density_matrix, - indices, - qid_shape=self._qid_shape, - repetitions=repetitions, - seed=seed, - ) + if self._density_matrix is None: + self._density_matrix = np.array(1) + state = self._merged_sim_state + if state is not None: + matrix = state.target_tensor + size = int(np.sqrt(np.prod(matrix.shape, dtype=int))) + self._density_matrix = np.reshape(matrix, (size, size)) + return self._density_matrix.copy() if copy else self._density_matrix @value.value_equality(unhashable=True) diff --git a/cirq-core/cirq/sim/density_matrix_simulator_test.py b/cirq-core/cirq/sim/density_matrix_simulator_test.py index 3ff1c34d681..44f61816d9f 100644 --- a/cirq-core/cirq/sim/density_matrix_simulator_test.py +++ b/cirq-core/cirq/sim/density_matrix_simulator_test.py @@ -532,7 +532,7 @@ def test_simulate_qudits(dtype: Type[np.number], split: bool): [cirq.testing.random_circuit(cirq.LineQubit.range(4), 5, 0.9) for _ in range(20)], ), ) -def test_simulate_compare_to_state_vector_simulator(dtype, circuit): +def test_simulate_compare_to_state_vector_simulator(dtype: Type[np.number], circuit): qubits = cirq.LineQubit.range(4) pure_result = ( cirq.Simulator(dtype=dtype).simulate(circuit, qubit_order=qubits).density_matrix_of() @@ -543,7 +543,7 @@ def test_simulate_compare_to_state_vector_simulator(dtype, circuit): .final_density_matrix ) assert mixed_result.shape == (16, 16) - np.testing.assert_almost_equal(mixed_result, pure_result) + np.testing.assert_almost_equal(mixed_result, pure_result, decimal=6) @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) @@ -768,10 +768,7 @@ def test_simulate_moment_steps_empty_circuit(dtype: Type[np.number], split: bool for step in simulator.simulate_moment_steps(circuit): pass assert step._simulator_state() == cirq.DensityMatrixSimulatorState( - density_matrix=np.array( - 1, - ), - qubit_map={}, + density_matrix=np.array([[1]]), qubit_map={} ) @@ -1486,7 +1483,7 @@ def test_measuring_subcircuits_cause_sweep_repeat(): def test_density_matrix_copy(): - sim = cirq.DensityMatrixSimulator() + sim = cirq.DensityMatrixSimulator(split_untangled_states=False) q = cirq.LineQubit(0) circuit = cirq.Circuit(cirq.H(q), cirq.H(q)) diff --git a/cirq-core/cirq/sim/operation_target.py b/cirq-core/cirq/sim/operation_target.py index 1f7e4f29f1d..dcba5c7899f 100644 --- a/cirq-core/cirq/sim/operation_target.py +++ b/cirq-core/cirq/sim/operation_target.py @@ -13,7 +13,9 @@ # limitations under the License. """An interface for quantum states as targets for operations.""" import abc -from typing import TypeVar, TYPE_CHECKING, Generic, Dict, Any, Tuple, Optional, Iterator +from typing import TypeVar, TYPE_CHECKING, Generic, Dict, Any, Tuple, Optional, Iterator, List + +import numpy as np if TYPE_CHECKING: import cirq @@ -48,6 +50,15 @@ def qubits(self) -> Tuple['cirq.Qid', ...]: def log_of_measurement_results(self) -> Dict[str, Any]: """Gets the log of measurement results.""" + @abc.abstractmethod + def sample( + self, + qubits: List['cirq.Qid'], + repetitions: int = 1, + seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, + ) -> np.ndarray: + """Samples the state value.""" + def __getitem__(self, item: Optional['cirq.Qid']) -> TActOnArgs: """Gets the item associated with the qubit.""" diff --git a/cirq-core/cirq/sim/simulator_base.py b/cirq-core/cirq/sim/simulator_base.py index 28170ab3782..e1d64a46d7a 100644 --- a/cirq-core/cirq/sim/simulator_base.py +++ b/cirq-core/cirq/sim/simulator_base.py @@ -28,6 +28,7 @@ Type, Sequence, Optional, + TypeVar, ) import numpy as np @@ -36,12 +37,12 @@ from cirq.sim import ActOnArgsContainer from cirq.sim.operation_target import OperationTarget from cirq.sim.simulator import ( - TStepResult, TSimulationTrialResult, TSimulatorState, TActOnArgs, SimulatesIntermediateState, SimulatesSamples, + StepResult, check_all_resolved, split_into_matching_protocol_then_general, ) @@ -50,9 +51,14 @@ import cirq +TStepResultBase = TypeVar('TStepResultBase', bound='StepResultBase') + + class SimulatorBase( - Generic[TStepResult, TSimulationTrialResult, TSimulatorState, TActOnArgs], - SimulatesIntermediateState[TStepResult, TSimulationTrialResult, TSimulatorState, TActOnArgs], + Generic[TStepResultBase, TSimulationTrialResult, TSimulatorState, TActOnArgs], + SimulatesIntermediateState[ + TStepResultBase, TSimulationTrialResult, TSimulatorState, TActOnArgs + ], SimulatesSamples, metaclass=abc.ABCMeta, ): @@ -135,16 +141,12 @@ def _create_partial_act_on_args( @abc.abstractmethod def _create_step_result( self, - sim_state: TActOnArgs, - qubit_map: Dict['cirq.Qid', int], - ) -> TStepResult: + sim_state: OperationTarget[TActOnArgs], + ) -> TStepResultBase: """This method should be implemented to create a step result. Args: - sim_state: The TActOnArgs for this trial. - qubit_map: Determines the canonical ordering of the qubits. This - is often used in specifying the initial state, i.e. the - ordering of the computational basis states. + sim_state: The OperationTarget for this trial. Returns: The StepResult. @@ -177,7 +179,7 @@ def _core_iterator( circuit: circuits.Circuit, sim_state: OperationTarget[TActOnArgs], all_measurements_are_terminal: bool = False, - ) -> Iterator[TStepResult]: + ) -> Iterator[TStepResultBase]: """Standard iterator over StepResult from Moments of a Circuit. Args: @@ -191,8 +193,7 @@ def _core_iterator( """ if len(circuit) == 0: - step_state = sim_state.create_merged_state() - yield self._create_step_result(step_state, step_state.qubit_map) + yield self._create_step_result(sim_state) return noisy_moments = self.noise.noisy_moments(circuit, sorted(circuit.all_qubits())) @@ -218,9 +219,10 @@ def _core_iterator( except TypeError: raise TypeError(f"{self.__class__.__name__} doesn't support {op!r}") - step_state = sim_state.create_merged_state() - yield self._create_step_result(step_state, step_state.qubit_map) - step_state.log_of_measurement_results.clear() + step_result = self._create_step_result(sim_state) + yield step_result + sim_state = step_result._sim_state + sim_state.log_of_measurement_results.clear() def _run( self, circuit: circuits.Circuit, param_resolver: study.ParamResolver, repetitions: int @@ -307,3 +309,41 @@ def _create_act_on_args( qubits=qubits, logs=log, ) + + +class StepResultBase(Generic[TSimulatorState, TActOnArgs], StepResult[TSimulatorState], abc.ABC): + """A base class for step results.""" + + def __init__( + self, + sim_state: OperationTarget[TActOnArgs], + ): + """Initializes the step result. + + Args: + sim_state: The `OperationTarget` for this step. + """ + self._sim_state = sim_state + self._merged_sim_state_cache: Optional[TActOnArgs] = None + super().__init__(sim_state.log_of_measurement_results) + qubits = sim_state.qubits + self._qubits = qubits + self._qubit_mapping = {q: i for i, q in enumerate(qubits)} + self._qubit_shape = tuple(q.dimension for q in qubits) + + def _qid_shape_(self): + return self._qubit_shape + + @property + def _merged_sim_state(self): + if self._merged_sim_state_cache is None: + self._merged_sim_state_cache = self._sim_state.create_merged_state() + return self._merged_sim_state_cache + + def sample( + self, + qubits: List[ops.Qid], + repetitions: int = 1, + seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, + ) -> np.ndarray: + return self._sim_state.sample(qubits, repetitions, seed) diff --git a/cirq-core/cirq/sim/simulator_base_test.py b/cirq-core/cirq/sim/simulator_base_test.py index 6fc2487cd42..7e6bb7e54ae 100644 --- a/cirq-core/cirq/sim/simulator_base_test.py +++ b/cirq-core/cirq/sim/simulator_base_test.py @@ -49,6 +49,9 @@ def _act_on_fallback_(self, action: Any, qubits: Sequence['cirq.Qid'], allow_dec self.gate_count += 1 return True + def sample(self, qubits, repetitions=1, seed=None): + pass + class SplittableCountingActOnArgs(CountingActOnArgs): def kronecker_product( @@ -97,16 +100,7 @@ def transpose_to_qubit_order( return args -class CountingStepResult(cirq.StepResult[CountingActOnArgs]): - def __init__( - self, - sim_state: CountingActOnArgs, - qubit_map: Dict[cirq.Qid, int], - ): - super().__init__(measurements=sim_state.log_of_measurement_results.copy()) - self.sim_state = sim_state - self.qubit_map = qubit_map - +class CountingStepResult(cirq.StepResultBase[CountingActOnArgs, CountingActOnArgs]): def sample( self, qubits: List[cirq.Qid], @@ -115,11 +109,11 @@ def sample( ) -> np.ndarray: measurements: List[List[int]] = [] for _ in range(repetitions): - measurements.append(self.sim_state._perform_measurement(qubits)) + measurements.append(self._merged_sim_state._perform_measurement(qubits)) return np.array(measurements, dtype=int) def _simulator_state(self) -> CountingActOnArgs: - return self.sim_state + return self._merged_sim_state class CountingTrialResult(cirq.SimulationTrialResult): @@ -155,10 +149,9 @@ def _create_simulator_trial_result( def _create_step_result( self, - sim_state: CountingActOnArgs, - qubit_map: Dict[cirq.Qid, int], + sim_state: cirq.OperationTarget[CountingActOnArgs], ) -> CountingStepResult: - return CountingStepResult(sim_state, qubit_map) + return CountingStepResult(sim_state) class SplittableCountingSimulator(CountingSimulator): @@ -194,6 +187,7 @@ def test_simulate_empty_circuit(): sim = CountingSimulator() r = sim.simulate(cirq.Circuit()) assert r._final_simulator_state.gate_count == 0 + assert r._final_simulator_state.measurement_count == 0 def test_simulate_one_gate_circuit(): @@ -353,3 +347,25 @@ def test_reorder_succeeds(): args = sim._create_act_on_args(entangled_state_repr, (q0, q1)) reordered = args[q0].transpose_to_qubit_order([q1, q0]) assert reordered.qubits == (q1, q0) + + +@pytest.mark.parametrize('split', [True, False]) +def test_sim_state_instance_unchanged_during_normal_sim(split: bool): + sim = SplittableCountingSimulator(split_untangled_states=split) + args = sim._create_act_on_args(0, (q0, q1)) + circuit = cirq.Circuit(cirq.H(q0), cirq.CNOT(q0, q1), cirq.reset(q1)) + for step in sim.simulate_moment_steps(circuit, initial_state=args): + assert step._sim_state is args + assert (step._merged_sim_state is not args) == split + + +@pytest.mark.parametrize('split', [True, False]) +def test_sim_state_instance_gets_changes_from_step_result(split: bool): + sim = SplittableCountingSimulator(split_untangled_states=split) + args = sim._create_act_on_args(0, (q0, q1)) + circuit = cirq.Circuit(cirq.H(q0), cirq.CNOT(q0, q1), cirq.reset(q1)) + for step in sim.simulate_moment_steps(circuit, initial_state=args): + assert step._sim_state is args + args = sim._create_act_on_args(0, (q0, q1)) + step._sim_state = args + assert (step._merged_sim_state is not args) == split diff --git a/cirq-core/cirq/sim/simulator_test.py b/cirq-core/cirq/sim/simulator_test.py index 9fee3d5650b..2acae3fd0b0 100644 --- a/cirq-core/cirq/sim/simulator_test.py +++ b/cirq-core/cirq/sim/simulator_test.py @@ -189,7 +189,7 @@ def state_vector(self): def __setstate__(self, state): pass - def sample(self, qubits, repetitions, seed): + def sample(self, qubits, repetitions=1, seed=None): return np.array([[qubit in self._ones_qubits for qubit in qubits]] * repetitions) diff --git a/cirq-core/cirq/sim/sparse_simulator.py b/cirq-core/cirq/sim/sparse_simulator.py index b04daf511d7..7699708fef1 100644 --- a/cirq-core/cirq/sim/sparse_simulator.py +++ b/cirq-core/cirq/sim/sparse_simulator.py @@ -23,6 +23,7 @@ TYPE_CHECKING, Union, Sequence, + Optional, ) import numpy as np @@ -144,7 +145,7 @@ def __init__( dtype: Type[np.number] = np.complex64, noise: 'cirq.NOISE_MODEL_LIKE' = None, seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, - split_untangled_states: bool = False, + split_untangled_states: bool = True, ): """A sparse matrix simulator. @@ -202,15 +203,12 @@ def _create_partial_act_on_args( def _create_step_result( self, - sim_state: act_on_state_vector_args.ActOnStateVectorArgs, - qubit_map: Dict['cirq.Qid', int], + sim_state: 'cirq.OperationTarget[cirq.ActOnStateVectorArgs]', ): return SparseSimulatorStep( - state_vector=sim_state.target_tensor, - measurements=dict(sim_state.log_of_measurement_results), - qubit_map=qubit_map, + sim_state=sim_state, + simulator=self, dtype=self._dtype, - split_untangled_states=self._split_untangled_states, ) def simulate_expectation_values_sweep_iter( @@ -242,37 +240,34 @@ def simulate_expectation_values_sweep_iter( class SparseSimulatorStep( - state_vector.StateVectorMixin, state_vector_simulator.StateVectorStepResult + state_vector.StateVectorMixin, + state_vector_simulator.StateVectorStepResult, ): """A `StepResult` that includes `StateVectorMixin` methods.""" def __init__( self, - state_vector: np.ndarray, - measurements: Dict[str, np.ndarray], - qubit_map: Dict[ops.Qid, int], + sim_state: 'cirq.OperationTarget[cirq.ActOnStateVectorArgs]', + simulator: Simulator, dtype: 'DTypeLike' = np.complex64, - split_untangled_states: bool = False, ): """Results of a step of the simulator. Args: - qubit_map: A map from the Qubits in the Circuit to the the index - of this qubit for a canonical ordering. This canonical ordering - is used to define the state vector (see the state_vector() - method). - measurements: A dictionary from measurement gate key to measurement - results, ordered by the qubits that the measurement operates on. + sim_state: The qubit:ActOnArgs lookup for this step. + simulator: The simulator used to create this. + dtype: The `numpy.dtype` used by the simulation. One of + `numpy.complex64` or `numpy.complex128`. """ - super().__init__(measurements=measurements, qubit_map=qubit_map) + qubit_map = {q: i for i, q in enumerate(sim_state.qubits)} + super().__init__(sim_state=sim_state, qubit_map=qubit_map) self._dtype = dtype - size = np.prod(protocols.qid_shape(self), dtype=int) - self._state_vector = np.reshape(state_vector, size) - self._split_untangled_states = split_untangled_states + self._state_vector: Optional[np.ndarray] = None + self._simulator = simulator def _simulator_state(self) -> state_vector_simulator.StateVectorSimulatorState: return state_vector_simulator.StateVectorSimulatorState( - qubit_map=self.qubit_map, state_vector=self._state_vector + qubit_map=self.qubit_map, state_vector=self.state_vector(copy=False) ) def state_vector(self, copy: bool = True): @@ -308,8 +303,14 @@ def state_vector(self, copy: bool = True): parameters from the state vector and store then using False can speed up simulation by eliminating a memory copy. """ - vector = self._simulator_state().state_vector - return vector.copy() if copy else vector + if self._state_vector is None: + self._state_vector = np.array([1]) + state = self._merged_sim_state + if state is not None: + vector = state.target_tensor + size = np.prod(vector.shape, dtype=int) + self._state_vector = np.reshape(vector, size) + return self._state_vector.copy() if copy else self._state_vector def set_state_vector(self, state: 'cirq.STATE_VECTOR_LIKE'): """Set the state vector. @@ -319,35 +320,9 @@ def set_state_vector(self, state: 'cirq.STATE_VECTOR_LIKE'): will be set to lie entirely in the computation basis state for the binary expansion of the passed integer. - Note that this feature is incompatible with the simulation setting - `split_untangled_states=True`, and will throw an error if attempted. - Args: state: If an int, the state vector set is the state vector corresponding to a computational basis state. If a numpy array this is the full state vector. """ - if self._split_untangled_states: - # TODO: Fix in #4110 - raise ValueError( # coverage: ignore - 'Cannot set states when using `split_untangled_states` option.' # coverage: ignore - ) # coverage: ignore - update_state = qis.to_valid_state_vector( - state, len(self.qubit_map), qid_shape=protocols.qid_shape(self, None), dtype=self._dtype - ) - np.copyto(self._state_vector, update_state) - - def sample( - self, - qubits: List[ops.Qid], - repetitions: int = 1, - seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, - ) -> np.ndarray: - indices = [self.qubit_map[qubit] for qubit in qubits] - return state_vector.sample_state_vector( - self._state_vector, - indices, - qid_shape=protocols.qid_shape(self, None), - repetitions=repetitions, - seed=seed, - ) + self._sim_state = self._simulator._create_act_on_args(state, self._qubits) diff --git a/cirq-core/cirq/sim/sparse_simulator_test.py b/cirq-core/cirq/sim/sparse_simulator_test.py index 22bf2d02910..2b2143e0db4 100644 --- a/cirq-core/cirq/sim/sparse_simulator_test.py +++ b/cirq-core/cirq/sim/sparse_simulator_test.py @@ -764,12 +764,17 @@ def _apply_unitary_(self, args: cirq.ApplyUnitaryArgs): def test_simulator_step_state_mixin(): qubits = cirq.LineQubit.range(2) - qubit_map = {qubits[i]: i for i in range(2)} + args = cirq.ActOnStateVectorArgs( + log_of_measurement_results={'m': np.array([1, 2])}, + target_tensor=np.array([0, 1, 0, 0]).reshape((2, 2)), + available_buffer=np.array([0, 1, 0, 0]).reshape((2, 2)), + prng=cirq.value.parse_random_state(0), + qubits=qubits, + ) result = cirq.SparseSimulatorStep( - measurements={'m': np.array([1, 2])}, - state_vector=np.array([0, 1, 0, 0]), - qubit_map=qubit_map, + sim_state=args, dtype=np.complex64, + simulator=None, # type: ignore ) rho = np.array([[0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]) np.testing.assert_array_almost_equal(rho, result.density_matrix_of(qubits)) @@ -1229,7 +1234,7 @@ def test_separated_measurements(): def test_state_vector_copy(): - sim = cirq.Simulator() + sim = cirq.Simulator(split_untangled_states=False) class InplaceGate(cirq.SingleQubitGate): """A gate that modifies the target tensor in place, multiply by -1.""" @@ -1300,7 +1305,7 @@ def test_nondeterministic_mixture_noise(): def test_act_on_args_pure_state_creation(): - sim = cirq.Simulator(split_untangled_states=True) + sim = cirq.Simulator() qids = cirq.LineQubit.range(3) shape = cirq.qid_shape(qids) args = sim._create_act_on_args(1, qids) diff --git a/cirq-core/cirq/sim/state_vector_simulator.py b/cirq-core/cirq/sim/state_vector_simulator.py index 2e31dd07129..59d6f67d7e1 100644 --- a/cirq-core/cirq/sim/state_vector_simulator.py +++ b/cirq-core/cirq/sim/state_vector_simulator.py @@ -98,7 +98,8 @@ def compute_amplitudes_sweep_iter( class StateVectorStepResult( - simulator.StepResult['StateVectorSimulatorState'], metaclass=abc.ABCMeta + simulator_base.StepResultBase['StateVectorSimulatorState', 'cirq.ActOnStateVectorArgs'], + metaclass=abc.ABCMeta, ): @abc.abstractmethod def _simulator_state(self) -> 'StateVectorSimulatorState': diff --git a/cirq-google/cirq_google/calibration/engine_simulator.py b/cirq-google/cirq_google/calibration/engine_simulator.py index b6a10d58d36..d8ce697ad6c 100644 --- a/cirq-google/cirq_google/calibration/engine_simulator.py +++ b/cirq-google/cirq_google/calibration/engine_simulator.py @@ -463,8 +463,7 @@ def _create_partial_act_on_args( def _create_step_result( self, - sim_state: cirq.ActOnStateVectorArgs, - qubit_map: Dict[cirq.Qid, int], + sim_state: cirq.OperationTarget, ) -> cirq.SparseSimulatorStep: # Needs an implementation since it's abstract but will never actually be called. raise NotImplementedError()