From 8ad4224ab0657423a311681a031684b36da34d27 Mon Sep 17 00:00:00 2001 From: Tony Bruguier Date: Sat, 20 Mar 2021 00:09:01 +0000 Subject: [PATCH 01/44] Move MPSSimulator to use act_on --- cirq/contrib/quimb/mps_simulator.py | 18 +++++++++++++----- cirq/contrib/quimb/mps_simulator_test.py | 12 +++++++++--- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/cirq/contrib/quimb/mps_simulator.py b/cirq/contrib/quimb/mps_simulator.py index 7d87c981d8a..9b963e5670d 100644 --- a/cirq/contrib/quimb/mps_simulator.py +++ b/cirq/contrib/quimb/mps_simulator.py @@ -107,6 +107,7 @@ def _base_iterator( measurements={}, state=MPSState( qubit_map, + self.prng, self.simulation_options, self.grouping, initial_state=initial_state, @@ -116,6 +117,7 @@ def _base_iterator( state = MPSState( qubit_map, + self.prng, self.simulation_options, self.grouping, initial_state=initial_state, @@ -126,11 +128,11 @@ def _base_iterator( measurements: Dict[str, List[int]] = collections.defaultdict(list) for op in flatten_to_ops(op_tree): - if isinstance(op.gate, ops.MeasurementGate): + if protocols.is_measurement(op): key = str(protocols.measurement_key(op)) measurements[key].extend(state.perform_measurement(op.qubits, self.prng)) elif protocols.has_mixture(op): - state.apply_op(op, self.prng) + protocols.act_on(op, state) else: raise NotImplementedError(f"Unrecognized operation: {op!r}") @@ -288,6 +290,7 @@ class MPSState: def __init__( self, qubit_map: Dict['cirq.Qid', int], + prng: np.random.RandomState, simulation_options: MPSOptions = MPSOptions(), grouping: Optional[Dict['cirq.Qid', int]] = None, initial_state: int = 0, @@ -296,6 +299,7 @@ def __init__( Args: qubit_map: A map from Qid to an integer that uniquely identifies it. + prng: A random number generator, used to simulate measurements. simulation_options: Numerical options for the simulation. grouping: How to group qubits together, if None all are individual. initial_state: An integer representing the initial state. @@ -335,6 +339,7 @@ def __init__( initial_state = initial_state // d self.simulation_options = simulation_options self.estimated_gate_error_list: List[float] = [] + self.prng = prng def i_str(self, i: int) -> str: # Returns the index name for the i'th qid. @@ -355,7 +360,7 @@ def _value_equality_values_(self) -> Any: return self.qubit_map, self.M, self.simulation_options, self.grouping def copy(self) -> 'MPSState': - state = MPSState(self.qubit_map, self.simulation_options, self.grouping) + state = MPSState(self.qubit_map, self.prng, self.simulation_options, self.grouping) state.M = [x.copy() for x in self.M] state.estimated_gate_error_list = self.estimated_gate_error_list return state @@ -415,7 +420,7 @@ def to_numpy(self) -> np.ndarray: """An alias for the state vector.""" return self.state_vector() - def apply_op(self, op: 'cirq.Operation', prng: np.random.RandomState): + def _act_on_fallback_(self, op: Any, allow_decompose: bool): """Applies a unitary operation, mutating the object to represent the new state. op: @@ -430,7 +435,9 @@ def apply_op(self, op: 'cirq.Operation', prng: np.random.RandomState): U = protocols.unitary(op) else: mixtures = protocols.mixture(op) - mixture_idx = int(prng.choice(len(mixtures), p=[mixture[0] for mixture in mixtures])) + mixture_idx = int( + self.prng.choice(len(mixtures), p=[mixture[0] for mixture in mixtures]) + ) U = mixtures[mixture_idx][1] U = qtn.Tensor( U.reshape([qubit.dimension for qubit in op.qubits] * 2), inds=(new_inds + old_inds) @@ -494,6 +501,7 @@ def apply_op(self, op: 'cirq.Operation', prng: np.random.RandomState): # TODO(tonybruguier): Evaluate whether it's even useful to implement and learn more # about HOSVDs. raise ValueError('Can only handle 1 and 2 qubit operations') + return True def estimation_stats(self): "Returns some statistics about the memory usage and quality of the approximation." diff --git a/cirq/contrib/quimb/mps_simulator_test.py b/cirq/contrib/quimb/mps_simulator_test.py index cd1b43408ec..2f6a14ba608 100644 --- a/cirq/contrib/quimb/mps_simulator_test.py +++ b/cirq/contrib/quimb/mps_simulator_test.py @@ -6,6 +6,7 @@ import sympy import cirq +from cirq import value import cirq.contrib.quimb as ccq import cirq.experiments.google_v2_supremacy_circuit as supremacy_v2 @@ -256,7 +257,9 @@ def test_measurement_str(): def test_trial_result_str(): q0 = cirq.LineQubit(0) final_simulator_state = ccq.mps_simulator.MPSState( - qubit_map={q0: 0}, simulation_options=ccq.mps_simulator.MPSOptions() + qubit_map={q0: 0}, + prng=value.parse_random_state(0), + simulation_options=ccq.mps_simulator.MPSOptions(), ) assert ( str( @@ -275,7 +278,7 @@ def test_trial_result_str(): def test_empty_step_result(): q0 = cirq.LineQubit(0) - state = ccq.mps_simulator.MPSState(qubit_map={q0: 0}) + state = ccq.mps_simulator.MPSState(qubit_map={q0: 0}, prng=value.parse_random_state(0)) step_result = ccq.mps_simulator.MPSSimulatorStepResult(state, measurements={'0': [1]}) assert ( str(step_result) @@ -290,14 +293,17 @@ def test_state_equal(): q0, q1 = cirq.LineQubit.range(2) state0 = ccq.mps_simulator.MPSState( qubit_map={q0: 0}, + prng=value.parse_random_state(0), simulation_options=ccq.mps_simulator.MPSOptions(cutoff=1e-3, sum_prob_atol=1e-3), ) state1a = ccq.mps_simulator.MPSState( qubit_map={q1: 0}, + prng=value.parse_random_state(0), simulation_options=ccq.mps_simulator.MPSOptions(cutoff=1e-3, sum_prob_atol=1e-3), ) state1b = ccq.mps_simulator.MPSState( qubit_map={q1: 0}, + prng=value.parse_random_state(0), simulation_options=ccq.mps_simulator.MPSOptions(cutoff=1729.0, sum_prob_atol=1e-3), ) assert state0 == state0 @@ -324,7 +330,7 @@ def test_supremacy_equal_more_cols(): def test_tensor_index_names(): qubits = cirq.LineQubit.range(12) qubit_map = {qubit: i for i, qubit in enumerate(qubits)} - state = ccq.mps_simulator.MPSState(qubit_map) + state = ccq.mps_simulator.MPSState(qubit_map, prng=value.parse_random_state(0)) assert state.i_str(0) == "i_00" assert state.i_str(11) == "i_11" From ffd7b86b16a80c5b73c5ac44360713cab338eccc Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Tue, 23 Mar 2021 10:48:37 -0700 Subject: [PATCH 02/44] Add axes to mps --- cirq/contrib/quimb/mps_simulator.py | 48 +++++++++++------------- cirq/contrib/quimb/mps_simulator_test.py | 17 ++++++++- 2 files changed, 37 insertions(+), 28 deletions(-) diff --git a/cirq/contrib/quimb/mps_simulator.py b/cirq/contrib/quimb/mps_simulator.py index 9b963e5670d..44cfa5ba076 100644 --- a/cirq/contrib/quimb/mps_simulator.py +++ b/cirq/contrib/quimb/mps_simulator.py @@ -19,7 +19,7 @@ import collections import math -from typing import Any, Dict, List, Iterator, Optional, Sequence, Set, TYPE_CHECKING +from typing import Any, Dict, List, Iterator, Optional, Set, TYPE_CHECKING, Iterable import dataclasses import numpy as np @@ -28,6 +28,7 @@ from cirq import circuits, devices, study, ops, protocols, value from cirq.ops import flatten_to_ops from cirq.sim import simulator +from cirq.sim.act_on_args import ActOnArgs, strat_act_on_from_apply_decompose if TYPE_CHECKING: import cirq @@ -107,7 +108,9 @@ def _base_iterator( measurements={}, state=MPSState( qubit_map, + [], self.prng, + {}, self.simulation_options, self.grouping, initial_state=initial_state, @@ -117,7 +120,9 @@ def _base_iterator( state = MPSState( qubit_map, + [], self.prng, + {}, self.simulation_options, self.grouping, initial_state=initial_state, @@ -125,18 +130,15 @@ def _base_iterator( noisy_moments = self.noise.noisy_moments(circuit, sorted(circuit.all_qubits())) for op_tree in noisy_moments: - measurements: Dict[str, List[int]] = collections.defaultdict(list) - for op in flatten_to_ops(op_tree): - if protocols.is_measurement(op): - key = str(protocols.measurement_key(op)) - measurements[key].extend(state.perform_measurement(op.qubits, self.prng)) - elif protocols.has_mixture(op): + if protocols.is_measurement(op) or protocols.has_mixture(op): + state.axes = tuple(qubit_map[qubit] for qubit in op.qubits) protocols.act_on(op, state) else: raise NotImplementedError(f"Unrecognized operation: {op!r}") - yield MPSSimulatorStepResult(measurements=measurements, state=state) + yield MPSSimulatorStepResult(measurements=state.log_of_measurement_results, state=state) + state.log_of_measurement_results.clear() def _create_simulator_trial_result( self, @@ -275,22 +277,22 @@ def sample( for _ in range(repetitions): measurements.append( - self.state.perform_measurement( - qubits, value.parse_random_state(seed), collapse_state_vector=False - ) + self.state._perform_measurement() ) return np.array(measurements, dtype=int) @value.value_equality -class MPSState: +class MPSState(ActOnArgs): """A state of the MPS simulation.""" def __init__( self, qubit_map: Dict['cirq.Qid', int], + axes: Iterable[int], prng: np.random.RandomState, + log_of_measurement_results: Dict[str, Any], simulation_options: MPSOptions = MPSOptions(), grouping: Optional[Dict['cirq.Qid', int]] = None, initial_state: int = 0, @@ -304,6 +306,7 @@ def __init__( grouping: How to group qubits together, if None all are individual. initial_state: An integer representing the initial state. """ + super().__init__(axes, prng, log_of_measurement_results) self.qubit_map = qubit_map self.grouping = qubit_map if grouping is None else grouping if self.grouping.keys() != self.qubit_map.keys(): @@ -339,7 +342,6 @@ def __init__( initial_state = initial_state // d self.simulation_options = simulation_options self.estimated_gate_error_list: List[float] = [] - self.prng = prng def i_str(self, i: int) -> str: # Returns the index name for the i'th qid. @@ -360,7 +362,7 @@ def _value_equality_values_(self) -> Any: return self.qubit_map, self.M, self.simulation_options, self.grouping def copy(self) -> 'MPSState': - state = MPSState(self.qubit_map, self.prng, self.simulation_options, self.grouping) + state = MPSState(self.qubit_map, self.axes, self.prng, self.log_of_measurement_results, self.simulation_options, self.grouping) state.M = [x.copy() for x in self.M] state.estimated_gate_error_list = self.estimated_gate_error_list return state @@ -523,9 +525,7 @@ def estimation_stats(self): "estimated_fidelity": estimated_fidelity, } - def perform_measurement( - self, qubits: Sequence[ops.Qid], prng: np.random.RandomState, collapse_state_vector=True - ) -> List[int]: + def _perform_measurement(self) -> List[int]: """Performs a measurement over one or more qubits. Args: @@ -536,15 +536,11 @@ def perform_measurement( """ results: List[int] = [] - if collapse_state_vector: - state = self - else: - state = self.copy() - - for qubit in qubits: - n = state.qubit_map[qubit] - + state = self.copy() + qubit_map_inv = {v: k for k, v in self.qubit_map.items()} + for n in self.axes: # Trace out other qubits + qubit = qubit_map_inv[n] M = state.partial_trace(keep_qubits={qubit}) probs = np.diag(M).real sum_probs = sum(probs) @@ -556,7 +552,7 @@ def perform_measurement( norm_probs = [x / sum_probs for x in probs] d = qubit.dimension - result: int = int(prng.choice(d, p=norm_probs)) + result: int = int(self.prng.choice(d, p=norm_probs)) collapser = np.zeros((d, d)) collapser[result][result] = 1.0 / math.sqrt(probs[result]) diff --git a/cirq/contrib/quimb/mps_simulator_test.py b/cirq/contrib/quimb/mps_simulator_test.py index 2f6a14ba608..8ae69697020 100644 --- a/cirq/contrib/quimb/mps_simulator_test.py +++ b/cirq/contrib/quimb/mps_simulator_test.py @@ -260,6 +260,8 @@ def test_trial_result_str(): qubit_map={q0: 0}, prng=value.parse_random_state(0), simulation_options=ccq.mps_simulator.MPSOptions(), + axes=[], + log_of_measurement_results={}, ) assert ( str( @@ -278,7 +280,7 @@ def test_trial_result_str(): def test_empty_step_result(): q0 = cirq.LineQubit(0) - state = ccq.mps_simulator.MPSState(qubit_map={q0: 0}, prng=value.parse_random_state(0)) + state = ccq.mps_simulator.MPSState(qubit_map={q0: 0}, prng=value.parse_random_state(0), axes=[], log_of_measurement_results={}) step_result = ccq.mps_simulator.MPSSimulatorStepResult(state, measurements={'0': [1]}) assert ( str(step_result) @@ -295,16 +297,22 @@ def test_state_equal(): qubit_map={q0: 0}, prng=value.parse_random_state(0), simulation_options=ccq.mps_simulator.MPSOptions(cutoff=1e-3, sum_prob_atol=1e-3), + axes=[], + log_of_measurement_results={}, ) state1a = ccq.mps_simulator.MPSState( qubit_map={q1: 0}, prng=value.parse_random_state(0), simulation_options=ccq.mps_simulator.MPSOptions(cutoff=1e-3, sum_prob_atol=1e-3), + axes=[], + log_of_measurement_results={}, ) state1b = ccq.mps_simulator.MPSState( qubit_map={q1: 0}, prng=value.parse_random_state(0), simulation_options=ccq.mps_simulator.MPSOptions(cutoff=1729.0, sum_prob_atol=1e-3), + axes=[], + log_of_measurement_results={}, ) assert state0 == state0 assert state0 != state1a @@ -330,7 +338,12 @@ def test_supremacy_equal_more_cols(): def test_tensor_index_names(): qubits = cirq.LineQubit.range(12) qubit_map = {qubit: i for i, qubit in enumerate(qubits)} - state = ccq.mps_simulator.MPSState(qubit_map, prng=value.parse_random_state(0)) + state = ccq.mps_simulator.MPSState( + qubit_map, + prng=value.parse_random_state(0), + axes=[], + log_of_measurement_results={}, + ) assert state.i_str(0) == "i_00" assert state.i_str(11) == "i_11" From 6a28c01346f74f58b606d574030460730c3d0e8a Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Tue, 23 Mar 2021 11:15:12 -0700 Subject: [PATCH 03/44] Finish act-on for mps --- cirq/contrib/quimb/mps_simulator.py | 41 ++++++++++++++++++------ cirq/contrib/quimb/mps_simulator_test.py | 4 ++- cirq/sim/act_on_state_vector_args.py | 2 +- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/cirq/contrib/quimb/mps_simulator.py b/cirq/contrib/quimb/mps_simulator.py index 44cfa5ba076..14e87aab1a1 100644 --- a/cirq/contrib/quimb/mps_simulator.py +++ b/cirq/contrib/quimb/mps_simulator.py @@ -19,7 +19,7 @@ import collections import math -from typing import Any, Dict, List, Iterator, Optional, Set, TYPE_CHECKING, Iterable +from typing import Any, Dict, List, Iterator, Optional, Sequence, Set, TYPE_CHECKING, Iterable import dataclasses import numpy as np @@ -28,7 +28,7 @@ from cirq import circuits, devices, study, ops, protocols, value from cirq.ops import flatten_to_ops from cirq.sim import simulator -from cirq.sim.act_on_args import ActOnArgs, strat_act_on_from_apply_decompose +from cirq.sim.act_on_args import ActOnArgs if TYPE_CHECKING: import cirq @@ -277,7 +277,9 @@ def sample( for _ in range(repetitions): measurements.append( - self.state._perform_measurement() + self.state.perform_measurement( + qubits, value.parse_random_state(seed), collapse_state_vector=False + ) ) return np.array(measurements, dtype=int) @@ -362,7 +364,14 @@ def _value_equality_values_(self) -> Any: return self.qubit_map, self.M, self.simulation_options, self.grouping def copy(self) -> 'MPSState': - state = MPSState(self.qubit_map, self.axes, self.prng, self.log_of_measurement_results, self.simulation_options, self.grouping) + state = MPSState( + self.qubit_map, + self.axes, + self.prng, + self.log_of_measurement_results, + self.simulation_options, + self.grouping, + ) state.M = [x.copy() for x in self.M] state.estimated_gate_error_list = self.estimated_gate_error_list return state @@ -525,7 +534,9 @@ def estimation_stats(self): "estimated_fidelity": estimated_fidelity, } - def _perform_measurement(self) -> List[int]: + def perform_measurement( + self, qubits: Sequence[ops.Qid], prng: np.random.RandomState, collapse_state_vector=True + ) -> List[int]: """Performs a measurement over one or more qubits. Args: @@ -536,11 +547,15 @@ def _perform_measurement(self) -> List[int]: """ results: List[int] = [] - state = self.copy() - qubit_map_inv = {v: k for k, v in self.qubit_map.items()} - for n in self.axes: + if collapse_state_vector: + state = self + else: + state = self.copy() + + for qubit in qubits: + n = state.qubit_map[qubit] + # Trace out other qubits - qubit = qubit_map_inv[n] M = state.partial_trace(keep_qubits={qubit}) probs = np.diag(M).real sum_probs = sum(probs) @@ -552,7 +567,7 @@ def _perform_measurement(self) -> List[int]: norm_probs = [x / sum_probs for x in probs] d = qubit.dimension - result: int = int(self.prng.choice(d, p=norm_probs)) + result: int = int(prng.choice(d, p=norm_probs)) collapser = np.zeros((d, d)) collapser[result][result] = 1.0 / math.sqrt(probs[result]) @@ -567,3 +582,9 @@ def _perform_measurement(self) -> List[int]: results.append(result) return results + + def _perform_measurement(self) -> List[int]: + """Measures the axes specified by the simulator.""" + qubit_map_inv = {v: k for k, v in self.qubit_map.items()} + qubits = [qubit_map_inv[key] for key in self.axes] + return self.perform_measurement(qubits, self.prng) diff --git a/cirq/contrib/quimb/mps_simulator_test.py b/cirq/contrib/quimb/mps_simulator_test.py index 8ae69697020..cc7ed43ee2f 100644 --- a/cirq/contrib/quimb/mps_simulator_test.py +++ b/cirq/contrib/quimb/mps_simulator_test.py @@ -280,7 +280,9 @@ def test_trial_result_str(): def test_empty_step_result(): q0 = cirq.LineQubit(0) - state = ccq.mps_simulator.MPSState(qubit_map={q0: 0}, prng=value.parse_random_state(0), axes=[], log_of_measurement_results={}) + state = ccq.mps_simulator.MPSState( + qubit_map={q0: 0}, prng=value.parse_random_state(0), axes=[], log_of_measurement_results={} + ) step_result = ccq.mps_simulator.MPSSimulatorStepResult(state, measurements={'0': [1]}) assert ( str(step_result) diff --git a/cirq/sim/act_on_state_vector_args.py b/cirq/sim/act_on_state_vector_args.py index 7da79f41a4d..001dff7a7e8 100644 --- a/cirq/sim/act_on_state_vector_args.py +++ b/cirq/sim/act_on_state_vector_args.py @@ -157,7 +157,7 @@ def _act_on_fallback_(self, action: Any, allow_decompose: bool): ) def _perform_measurement(self) -> List[int]: - """Delegates the call to measure the density matrix.""" + """Delegates the call to measure the state vector.""" bits, _ = sim.measure_state_vector( self.target_tensor, self.axes, From ca0a765e544bb5263e1e82be7675e40436bde9a9 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Tue, 23 Mar 2021 11:55:23 -0700 Subject: [PATCH 04/44] lint --- cirq/contrib/quimb/mps_simulator.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cirq/contrib/quimb/mps_simulator.py b/cirq/contrib/quimb/mps_simulator.py index 14e87aab1a1..8fff3f7697e 100644 --- a/cirq/contrib/quimb/mps_simulator.py +++ b/cirq/contrib/quimb/mps_simulator.py @@ -17,7 +17,6 @@ https://arxiv.org/abs/2002.07730 """ -import collections import math from typing import Any, Dict, List, Iterator, Optional, Sequence, Set, TYPE_CHECKING, Iterable From 988d7aeee40b376ed6ac6d682cfca7625dc4473a Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Tue, 23 Mar 2021 16:35:59 -0700 Subject: [PATCH 05/44] Add a copy method for all act_on_args --- cirq/contrib/quimb/mps_simulator.py | 2 +- cirq/sim/act_on_args.py | 8 +++++++- cirq/sim/act_on_density_matrix_args.py | 10 ++++++++++ cirq/sim/act_on_state_vector_args.py | 9 +++++++++ cirq/sim/clifford/act_on_clifford_tableau_args.py | 8 ++++++++ cirq/sim/clifford/act_on_stabilizer_ch_form_args.py | 8 ++++++++ 6 files changed, 43 insertions(+), 2 deletions(-) diff --git a/cirq/contrib/quimb/mps_simulator.py b/cirq/contrib/quimb/mps_simulator.py index 8fff3f7697e..60facb466fd 100644 --- a/cirq/contrib/quimb/mps_simulator.py +++ b/cirq/contrib/quimb/mps_simulator.py @@ -362,7 +362,7 @@ def __str__(self) -> str: def _value_equality_values_(self) -> Any: return self.qubit_map, self.M, self.simulation_options, self.grouping - def copy(self) -> 'MPSState': + def copy(self): state = MPSState( self.qubit_map, self.axes, diff --git a/cirq/sim/act_on_args.py b/cirq/sim/act_on_args.py index a9b2f381292..b7cfd388155 100644 --- a/cirq/sim/act_on_args.py +++ b/cirq/sim/act_on_args.py @@ -13,13 +13,15 @@ # limitations under the License. """Objects and methods for acting efficiently on a state tensor.""" import abc -from typing import Any, Iterable, Dict, List +from typing import Any, Iterable, Dict, List, TypeVar import numpy as np from cirq import protocols from cirq.protocols.decompose_protocol import _try_decompose_into_operations_and_qubits +TSelf = TypeVar('TSelf', bound='ActOnArgs') + class ActOnArgs: """State and context for an operation acting on a state tensor.""" @@ -64,6 +66,10 @@ def _perform_measurement(self) -> List[int]: """Child classes that perform measurements should implement this with the implementation.""" + @abc.abstractmethod + def copy(self: TSelf) -> TSelf: + """Creates a copy of the object.""" + def strat_act_on_from_apply_decompose( val: Any, diff --git a/cirq/sim/act_on_density_matrix_args.py b/cirq/sim/act_on_density_matrix_args.py index a1e6bb4e023..e43e55125ec 100644 --- a/cirq/sim/act_on_density_matrix_args.py +++ b/cirq/sim/act_on_density_matrix_args.py @@ -92,6 +92,16 @@ def _perform_measurement(self) -> List[int]: ) return bits + def copy(self): + return ActOnDensityMatrixArgs( + self.target_tensor.copy(), + [b.copy() for b in self.available_buffer], + [a for a in self.axes], + self.qid_shape, + self.prng, + self.log_of_measurement_results.copy(), + ) + def _strat_apply_channel_to_state( action: Any, diff --git a/cirq/sim/act_on_state_vector_args.py b/cirq/sim/act_on_state_vector_args.py index 001dff7a7e8..44cd7fd89a8 100644 --- a/cirq/sim/act_on_state_vector_args.py +++ b/cirq/sim/act_on_state_vector_args.py @@ -167,6 +167,15 @@ def _perform_measurement(self) -> List[int]: ) return bits + def copy(self): + return ActOnStateVectorArgs( + self.target_tensor.copy(), + self.available_buffer.copy(), + [a for a in self.axes], + self.prng, + self.log_of_measurement_results.copy(), + ) + def _strat_act_on_state_vector_from_apply_unitary( unitary_value: Any, diff --git a/cirq/sim/clifford/act_on_clifford_tableau_args.py b/cirq/sim/clifford/act_on_clifford_tableau_args.py index 9b30c7243d1..079d65eb124 100644 --- a/cirq/sim/clifford/act_on_clifford_tableau_args.py +++ b/cirq/sim/clifford/act_on_clifford_tableau_args.py @@ -77,6 +77,14 @@ def _perform_measurement(self) -> List[int]: """Returns the measurement from the tableau.""" return [self.tableau._measure(q, self.prng) for q in self.axes] + def copy(self): + return ActOnCliffordTableauArgs( + self.tableau.copy(), + [a for a in self.axes], + self.prng, + self.log_of_measurement_results.copy(), + ) + def _strat_act_on_clifford_tableau_from_single_qubit_decompose( val: Any, args: 'cirq.ActOnCliffordTableauArgs' diff --git a/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py b/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py index b7362a5be10..7f4e4077fe5 100644 --- a/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py +++ b/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py @@ -72,6 +72,14 @@ def _perform_measurement(self) -> List[int]: """Returns the measurement from the stabilizer state form.""" return [self.state._measure(q, self.prng) for q in self.axes] + def copy(self): + return ActOnStabilizerCHFormArgs( + self.state.copy(), + [a for a in self.axes], + self.prng, + self.log_of_measurement_results.copy(), + ) + def _strat_act_on_stabilizer_ch_form_from_single_qubit_decompose( val: Any, args: 'cirq.ActOnStabilizerCHFormArgs' From b1774b7d5c126044bf7e5c26ca2caac4aa097558 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Tue, 23 Mar 2021 17:49:07 -0700 Subject: [PATCH 06/44] Split creation of act_on_args and iteration --- cirq/contrib/quimb/mps_simulator.py | 21 +++++++--- cirq/sim/clifford/clifford_simulator.py | 25 ++++++++---- cirq/sim/density_matrix_simulator.py | 26 ++++++------- cirq/sim/simulator.py | 52 +++++++++++++++++++++++-- cirq/sim/simulator_test.py | 5 ++- cirq/sim/sparse_simulator.py | 14 +++++-- cirq/sim/state_vector_simulator.py | 3 +- 7 files changed, 111 insertions(+), 35 deletions(-) diff --git a/cirq/contrib/quimb/mps_simulator.py b/cirq/contrib/quimb/mps_simulator.py index 60facb466fd..704064cecbe 100644 --- a/cirq/contrib/quimb/mps_simulator.py +++ b/cirq/contrib/quimb/mps_simulator.py @@ -54,7 +54,7 @@ class MPSOptions: class MPSSimulator( simulator.SimulatesSamples, - simulator.SimulatesIntermediateState['MPSSimulatorStepResult', 'MPSTrialResult', 'MPSState'], + simulator.SimulatesIntermediateState['MPSSimulatorStepResult', 'MPSTrialResult', 'MPSState', 'MPSState'], ): """An efficient simulator for MPS circuits.""" @@ -82,9 +82,12 @@ def __init__( self.simulation_options = simulation_options self.grouping = grouping - def _base_iterator( - self, circuit: circuits.Circuit, qubit_order: ops.QubitOrderOrList, initial_state: int - ) -> Iterator['MPSSimulatorStepResult']: + def create_act_on_args( + self, + circuit: circuits.Circuit, + qubit_order: ops.QubitOrderOrList, + initial_state: int, + ): """Iterator over MPSSimulatorStepResult from Moments of a Circuit Args: @@ -117,7 +120,7 @@ def _base_iterator( ) return - state = MPSState( + return MPSState( qubit_map, [], self.prng, @@ -127,11 +130,17 @@ def _base_iterator( initial_state=initial_state, ) + def iterate_circuit( + self, + circuit: circuits.Circuit, + qubit_order: ops.QubitOrderOrList, + state: 'MPSState', + ): noisy_moments = self.noise.noisy_moments(circuit, sorted(circuit.all_qubits())) for op_tree in noisy_moments: for op in flatten_to_ops(op_tree): if protocols.is_measurement(op) or protocols.has_mixture(op): - state.axes = tuple(qubit_map[qubit] for qubit in op.qubits) + state.axes = tuple(state.qubit_map[qubit] for qubit in op.qubits) protocols.act_on(op, state) else: raise NotImplementedError(f"Unrecognized operation: {op!r}") diff --git a/cirq/sim/clifford/clifford_simulator.py b/cirq/sim/clifford/clifford_simulator.py index 03d31e33e79..0944f10ed53 100644 --- a/cirq/sim/clifford/clifford_simulator.py +++ b/cirq/sim/clifford/clifford_simulator.py @@ -45,7 +45,7 @@ class CliffordSimulator( simulator.SimulatesSamples, simulator.SimulatesIntermediateState[ - 'CliffordSimulatorStepResult', 'CliffordTrialResult', 'CliffordState' + 'CliffordSimulatorStepResult', 'CliffordTrialResult', 'CliffordState', clifford.ActOnStabilizerCHFormArgs, ], ): """An efficient simulator for Clifford circuits.""" @@ -65,9 +65,12 @@ def is_supported_operation(op: 'cirq.Operation') -> bool: # TODO: support more general Pauli measurements return protocols.has_stabilizer_effect(op) - def _base_iterator( - self, circuit: circuits.Circuit, qubit_order: ops.QubitOrderOrList, initial_state: int - ) -> Iterator['cirq.CliffordSimulatorStepResult']: + def create_act_on_args( + self, + circuit: circuits.Circuit, + qubit_order: ops.QubitOrderOrList, + initial_state: int, + ): """Iterator over CliffordSimulatorStepResult from Moments of a Circuit Args: @@ -93,19 +96,27 @@ def _base_iterator( return state = CliffordState(qubit_map, initial_state=initial_state) - ch_form_args = clifford.ActOnStabilizerCHFormArgs( + return clifford.ActOnStabilizerCHFormArgs( state.ch_form, [], self._prng, {}, ) + def iterate_circuit( + self, + circuit: circuits.Circuit, + qubit_order: ops.QubitOrderOrList, + ch_form_args: clifford.ActOnStabilizerCHFormArgs, + ): + qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) + qubit_map = {q: i for i, q in enumerate(qubits)} for moment in circuit: ch_form_args.log_of_measurement_results = {} for op in moment: try: - ch_form_args.axes = tuple(state.qubit_map[i] for i in op.qubits) + ch_form_args.axes = tuple(qubit_map[i] for i in op.qubits) act_on(op, ch_form_args) except TypeError: raise NotImplementedError( @@ -113,7 +124,7 @@ def _base_iterator( ) # type: ignore yield CliffordSimulatorStepResult( - measurements=ch_form_args.log_of_measurement_results, state=state + measurements=ch_form_args.log_of_measurement_results, state=ch_form_args.state ) def _create_simulator_trial_result( diff --git a/cirq/sim/density_matrix_simulator.py b/cirq/sim/density_matrix_simulator.py index 6c1b23bc580..c19d7c345b4 100644 --- a/cirq/sim/density_matrix_simulator.py +++ b/cirq/sim/density_matrix_simulator.py @@ -29,7 +29,7 @@ class DensityMatrixSimulator( simulator.SimulatesSamples, simulator.SimulatesIntermediateState[ - 'DensityMatrixStepResult', 'DensityMatrixTrialResult', 'DensityMatrixSimulatorState' + 'DensityMatrixStepResult', 'DensityMatrixTrialResult', 'DensityMatrixSimulatorState', act_on_density_matrix_args.ActOnDensityMatrixArgs, ], ): """A simulator for density matrices and noisy quantum circuits. @@ -232,33 +232,24 @@ def _run_sweep_repeat( measurements[k].append(np.array(v, dtype=np.uint8)) return {k: np.array(v) for k, v in measurements.items()} - def _base_iterator( + def create_act_on_args( self, circuit: circuits.Circuit, qubit_order: ops.QubitOrderOrList, initial_state: Union[np.ndarray, 'cirq.STATE_VECTOR_LIKE'], - all_measurements_are_terminal=False, - is_raw_state=False, - ) -> Iterator['DensityMatrixStepResult']: + ): qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) qid_shape = protocols.qid_shape(qubits) - qubit_map = {q: i for i, q in enumerate(qubits)} initial_matrix = ( qis.to_valid_density_matrix( initial_state, len(qid_shape), qid_shape=qid_shape, dtype=self._dtype ) - if not is_raw_state - else initial_state ) if np.may_share_memory(initial_matrix, initial_state): initial_matrix = initial_matrix.copy() - if len(circuit) == 0: - yield DensityMatrixStepResult(initial_matrix, {}, qubit_map, self._dtype) - return - tensor = initial_matrix.reshape(qid_shape * 2) - sim_state = act_on_density_matrix_args.ActOnDensityMatrixArgs( + return act_on_density_matrix_args.ActOnDensityMatrixArgs( target_tensor=tensor, available_buffer=[np.empty_like(tensor) for _ in range(3)], axes=[], @@ -267,6 +258,15 @@ def _base_iterator( log_of_measurement_results={}, ) + def iterate_circuit( + self, + circuit: circuits.Circuit, + qubit_order: ops.QubitOrderOrList, + sim_state: act_on_density_matrix_args.ActOnDensityMatrixArgs, + all_measurements_are_terminal: bool + ): + qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) + qubit_map = {q: i for i, q in enumerate(qubits)} noisy_moments = self.noise.noisy_moments(circuit, sorted(circuit.all_qubits())) measured = collections.defaultdict(bool) # type: Dict[Tuple[cirq.Qid, ...], bool] for moment in noisy_moments: diff --git a/cirq/sim/simulator.py b/cirq/sim/simulator.py index 1c47c90a7ae..7e2909c873b 100644 --- a/cirq/sim/simulator.py +++ b/cirq/sim/simulator.py @@ -51,6 +51,7 @@ from cirq import circuits, ops, protocols, study, value, work from cirq._compat import deprecated +from cirq.sim.act_on_args import ActOnArgs if TYPE_CHECKING: import cirq @@ -59,6 +60,7 @@ TStepResult = TypeVar('TStepResult', bound='StepResult') TSimulationTrialResult = TypeVar('TSimulationTrialResult', bound='SimulationTrialResult') TSimulatorState = TypeVar('TSimulatorState') +TActOnArgs = TypeVar('TActOnArgs', bound=ActOnArgs) class SimulatesSamples(work.Sampler, metaclass=abc.ABCMeta): @@ -374,7 +376,7 @@ def simulate_sweep( class SimulatesIntermediateState( - Generic[TStepResult, TSimulationTrialResult, TSimulatorState], + Generic[TStepResult, TSimulationTrialResult, TSimulatorState, TActOnArgs], SimulatesFinalState[TSimulationTrialResult], metaclass=abc.ABCMeta, ): @@ -503,7 +505,6 @@ def _simulator_iterator( """ return self.simulate_moment_steps(circuit, param_resolver, qubit_order, initial_state) - @abc.abstractmethod def _base_iterator( self, circuit: circuits.Circuit, @@ -526,7 +527,52 @@ def _base_iterator( Yields: StepResults from simulating a Moment of the Circuit. """ - raise NotImplementedError() + acton_args = self.create_act_on_args(circuit, qubit_order, initial_state) + return self.iterate_circuit(circuit, qubit_order, acton_args) + + @abc.abstractmethod + def create_act_on_args( + self, + circuit: circuits.Circuit, + qubit_order: ops.QubitOrderOrList, + initial_state: Any, + ) -> TActOnArgs: + """Creates the `act_on` state for a simulator. + + Args: + circuit: The circuit to simulate. + qubit_order: 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. + initial_state: The initial state for the simulation. The form of + this state depends on the simulation implementation. See + documentation of the implementing class for details. + + Returns: + `act_on_args` for the simulator. + """ + + @abc.abstractmethod + def iterate_circuit( + self, + circuit: circuits.Circuit, + qubit_order: ops.QubitOrderOrList, + initial_state: TActOnArgs, + ) -> Iterator[TStepResult]: + """Iterator over StepResult from Moments of a Circuit. + + Args: + circuit: The circuit to simulate. + qubit_order: 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. + initial_state: The initial state for the simulation. The form of + this state depends on the simulation implementation. See + documentation of the implementing class for details. + + Yields: + StepResults from simulating a Moment of the Circuit. + """ @abc.abstractmethod def _create_simulator_trial_result( diff --git a/cirq/sim/simulator_test.py b/cirq/sim/simulator_test.py index c2d6865289d..5364b6d7c80 100644 --- a/cirq/sim/simulator_test.py +++ b/cirq/sim/simulator_test.py @@ -25,12 +25,13 @@ TSimulatorState, SimulatesIntermediateState, SimulationTrialResult, + TActOnArgs, ) class SimulatesIntermediateStateImpl( - Generic[TStepResult, TSimulatorState], - SimulatesIntermediateState[TStepResult, 'SimulationTrialResult', TSimulatorState], + Generic[TStepResult, TSimulatorState, TActOnArgs], + SimulatesIntermediateState[TStepResult, 'SimulationTrialResult', TSimulatorState, TActOnArgs], metaclass=abc.ABCMeta, ): """A SimulatesIntermediateState that uses the default SimulationTrialResult type.""" diff --git a/cirq/sim/sparse_simulator.py b/cirq/sim/sparse_simulator.py index 0722105ed32..564ae2c4a88 100644 --- a/cirq/sim/sparse_simulator.py +++ b/cirq/sim/sparse_simulator.py @@ -230,12 +230,12 @@ def _brute_force_samples( measurements[k].append(np.array(v, dtype=np.uint8)) return {k: np.array(v) for k, v in measurements.items()} - def _base_iterator( + def create_act_on_args( self, circuit: circuits.Circuit, qubit_order: ops.QubitOrderOrList, initial_state: 'cirq.STATE_VECTOR_LIKE', - ) -> Iterator['SparseSimulatorStep']: + ): qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) num_qubits = len(qubits) qid_shape = protocols.qid_shape(qubits) @@ -246,7 +246,7 @@ def _base_iterator( if len(circuit) == 0: yield SparseSimulatorStep(state, {}, qubit_map, self._dtype) - sim_state = act_on_state_vector_args.ActOnStateVectorArgs( + return act_on_state_vector_args.ActOnStateVectorArgs( target_tensor=np.reshape(state, qid_shape), available_buffer=np.empty(qid_shape, dtype=self._dtype), axes=[], @@ -254,6 +254,14 @@ def _base_iterator( log_of_measurement_results={}, ) + def iterate_circuit( + self, + circuit: circuits.Circuit, + qubit_order: ops.QubitOrderOrList, + sim_state: act_on_state_vector_args.ActOnStateVectorArgs, + ): + qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) + qubit_map = {q: i for i, q in enumerate(qubits)} noisy_moments = self.noise.noisy_moments(circuit, sorted(circuit.all_qubits())) for op_tree in noisy_moments: for op in flatten_to_ops(op_tree): diff --git a/cirq/sim/state_vector_simulator.py b/cirq/sim/state_vector_simulator.py index 4e6e013833e..c8a3e102f10 100644 --- a/cirq/sim/state_vector_simulator.py +++ b/cirq/sim/state_vector_simulator.py @@ -21,6 +21,7 @@ from cirq import ops, study, value from cirq.sim import simulator, state_vector +from cirq.sim.act_on_state_vector_args import ActOnStateVectorArgs if TYPE_CHECKING: import cirq @@ -33,7 +34,7 @@ class SimulatesIntermediateStateVector( Generic[TStateVectorStepResult], simulator.SimulatesAmplitudes, simulator.SimulatesIntermediateState[ - TStateVectorStepResult, 'StateVectorTrialResult', 'StateVectorSimulatorState' + TStateVectorStepResult, 'StateVectorTrialResult', 'StateVectorSimulatorState', ActOnStateVectorArgs ], metaclass=abc.ABCMeta, ): From bfa7ed0e536a1114a7bfdc15112ac3d6371e1350 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Tue, 23 Mar 2021 19:47:34 -0700 Subject: [PATCH 07/44] remove the empty yields --- cirq/contrib/quimb/mps_simulator.py | 15 --------------- cirq/sim/clifford/clifford_simulator.py | 6 ------ cirq/sim/density_matrix_simulator.py | 23 +++++++++++------------ cirq/sim/simulator.py | 2 -- cirq/sim/sparse_simulator.py | 3 --- 5 files changed, 11 insertions(+), 38 deletions(-) diff --git a/cirq/contrib/quimb/mps_simulator.py b/cirq/contrib/quimb/mps_simulator.py index 704064cecbe..9ad5d9985e8 100644 --- a/cirq/contrib/quimb/mps_simulator.py +++ b/cirq/contrib/quimb/mps_simulator.py @@ -105,21 +105,6 @@ def create_act_on_args( qubit_map = {q: i for i, q in enumerate(qubits)} - if len(circuit) == 0: - yield MPSSimulatorStepResult( - measurements={}, - state=MPSState( - qubit_map, - [], - self.prng, - {}, - self.simulation_options, - self.grouping, - initial_state=initial_state, - ), - ) - return - return MPSState( qubit_map, [], diff --git a/cirq/sim/clifford/clifford_simulator.py b/cirq/sim/clifford/clifford_simulator.py index 0944f10ed53..3c67245eea4 100644 --- a/cirq/sim/clifford/clifford_simulator.py +++ b/cirq/sim/clifford/clifford_simulator.py @@ -89,12 +89,6 @@ def create_act_on_args( qubit_map = {q: i for i, q in enumerate(qubits)} - if len(circuit) == 0: - yield CliffordSimulatorStepResult( - measurements={}, state=CliffordState(qubit_map, initial_state=initial_state) - ) - return - state = CliffordState(qubit_map, initial_state=initial_state) return clifford.ActOnStabilizerCHFormArgs( state.ch_form, diff --git a/cirq/sim/density_matrix_simulator.py b/cirq/sim/density_matrix_simulator.py index c19d7c345b4..68c25a2ce4e 100644 --- a/cirq/sim/density_matrix_simulator.py +++ b/cirq/sim/density_matrix_simulator.py @@ -171,16 +171,17 @@ def _run( prefix, general_suffix = split_into_matching_protocol_then_general( resolved_circuit, lambda op: not protocols.is_measurement(op) ) + acton_args = self.create_act_on_args(prefix, qubit_order, 0) step_result = None - for step_result in self._base_iterator( + for step_result in self.iterate_circuit( circuit=prefix, qubit_order=qubit_order, - initial_state=0, + sim_state=acton_args, ): pass assert step_result is not None - intermediate_state = step_result._density_matrix + intermediate_state = acton_args.copy() if general_suffix.are_all_measurements_terminal() and not any( general_suffix.findall_operations(lambda op: isinstance(op, circuits.CircuitOperation)) ): @@ -194,14 +195,13 @@ def _run_sweep_sample( circuit: circuits.Circuit, repetitions: int, qubit_order: ops.QubitOrderOrList, - intermediate_state: np.ndarray, + acton_args: act_on_density_matrix_args.ActOnDensityMatrixArgs, ) -> Dict[str, np.ndarray]: - for step_result in self._base_iterator( + for step_result in self.iterate_circuit( circuit=circuit, qubit_order=qubit_order, - initial_state=intermediate_state, + sim_state=acton_args, all_measurements_are_terminal=True, - is_raw_state=True, ): pass measurement_ops = [ @@ -214,16 +214,15 @@ def _run_sweep_repeat( circuit: circuits.Circuit, repetitions: int, qubit_order: ops.QubitOrderOrList, - intermediate_state: np.ndarray, + acton_args: act_on_density_matrix_args.ActOnDensityMatrixArgs, ) -> Dict[str, np.ndarray]: measurements = {} # type: Dict[str, List[np.ndarray]] for _ in range(repetitions): - all_step_results = self._base_iterator( + all_step_results = self.iterate_circuit( circuit, qubit_order=qubit_order, - initial_state=intermediate_state, - is_raw_state=True, + sim_state=acton_args.copy(), ) for step_result in all_step_results: for k, v in step_result.measurements.items(): @@ -263,7 +262,7 @@ def iterate_circuit( circuit: circuits.Circuit, qubit_order: ops.QubitOrderOrList, sim_state: act_on_density_matrix_args.ActOnDensityMatrixArgs, - all_measurements_are_terminal: bool + all_measurements_are_terminal: bool = False, ): qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) qubit_map = {q: i for i, q in enumerate(qubits)} diff --git a/cirq/sim/simulator.py b/cirq/sim/simulator.py index 7e2909c873b..dbb1631db5c 100644 --- a/cirq/sim/simulator.py +++ b/cirq/sim/simulator.py @@ -515,8 +515,6 @@ def _base_iterator( Args: circuit: The circuit to simulate. - param_resolver: A ParamResolver for determining values of - Symbols. qubit_order: 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. diff --git a/cirq/sim/sparse_simulator.py b/cirq/sim/sparse_simulator.py index 564ae2c4a88..91e5cd89e58 100644 --- a/cirq/sim/sparse_simulator.py +++ b/cirq/sim/sparse_simulator.py @@ -239,12 +239,9 @@ def create_act_on_args( qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) num_qubits = len(qubits) qid_shape = protocols.qid_shape(qubits) - qubit_map = {q: i for i, q in enumerate(qubits)} state = qis.to_valid_state_vector( initial_state, num_qubits, qid_shape=qid_shape, dtype=self._dtype ) - if len(circuit) == 0: - yield SparseSimulatorStep(state, {}, qubit_map, self._dtype) return act_on_state_vector_args.ActOnStateVectorArgs( target_tensor=np.reshape(state, qid_shape), From 180bb67f095cdae6af050fd834a0f5c01aacab98 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Wed, 24 Mar 2021 10:01:27 -0700 Subject: [PATCH 08/44] Fix most unit tests --- cirq/contrib/quimb/mps_simulator.py | 4 ++++ cirq/sim/clifford/clifford_simulator.py | 6 ++++++ cirq/sim/density_matrix_simulator.py | 9 +++++++++ cirq/sim/density_matrix_simulator_test.py | 16 ++++++++-------- cirq/sim/sparse_simulator.py | 9 +++++++++ 5 files changed, 36 insertions(+), 8 deletions(-) diff --git a/cirq/contrib/quimb/mps_simulator.py b/cirq/contrib/quimb/mps_simulator.py index 9ad5d9985e8..0518291c2ee 100644 --- a/cirq/contrib/quimb/mps_simulator.py +++ b/cirq/contrib/quimb/mps_simulator.py @@ -121,6 +121,10 @@ def iterate_circuit( qubit_order: ops.QubitOrderOrList, state: 'MPSState', ): + if len(circuit) == 0: + yield MPSSimulatorStepResult(measurements=state.log_of_measurement_results, state=state) + return + noisy_moments = self.noise.noisy_moments(circuit, sorted(circuit.all_qubits())) for op_tree in noisy_moments: for op in flatten_to_ops(op_tree): diff --git a/cirq/sim/clifford/clifford_simulator.py b/cirq/sim/clifford/clifford_simulator.py index 3c67245eea4..7e05b1d1116 100644 --- a/cirq/sim/clifford/clifford_simulator.py +++ b/cirq/sim/clifford/clifford_simulator.py @@ -103,6 +103,12 @@ def iterate_circuit( qubit_order: ops.QubitOrderOrList, ch_form_args: clifford.ActOnStabilizerCHFormArgs, ): + if len(circuit) == 0: + yield CliffordSimulatorStepResult( + measurements=ch_form_args.log_of_measurement_results, state=ch_form_args.state + ) + return + qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) qubit_map = {q: i for i, q in enumerate(qubits)} for moment in circuit: diff --git a/cirq/sim/density_matrix_simulator.py b/cirq/sim/density_matrix_simulator.py index 68c25a2ce4e..1de73d9a627 100644 --- a/cirq/sim/density_matrix_simulator.py +++ b/cirq/sim/density_matrix_simulator.py @@ -266,6 +266,15 @@ def iterate_circuit( ): qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) qubit_map = {q: i for i, q in enumerate(qubits)} + if len(circuit) == 0: + yield DensityMatrixStepResult( + density_matrix=sim_state.target_tensor, + measurements=dict(sim_state.log_of_measurement_results), + qubit_map=qubit_map, + dtype=self._dtype, + ) + return + noisy_moments = self.noise.noisy_moments(circuit, sorted(circuit.all_qubits())) measured = collections.defaultdict(bool) # type: Dict[Tuple[cirq.Qid, ...], bool] for moment in noisy_moments: diff --git a/cirq/sim/density_matrix_simulator_test.py b/cirq/sim/density_matrix_simulator_test.py index 6e652f8e325..8fe02f0d6d0 100644 --- a/cirq/sim/density_matrix_simulator_test.py +++ b/cirq/sim/density_matrix_simulator_test.py @@ -269,7 +269,7 @@ def _channel_(self): def test_run_measure_at_end_no_repetitions(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.DensityMatrixSimulator(dtype=dtype) - with mock.patch.object(simulator, '_base_iterator', wraps=simulator._base_iterator) as mock_sim: + with mock.patch.object(simulator, 'iterate_circuit', wraps=simulator.iterate_circuit) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -287,7 +287,7 @@ def test_run_measure_at_end_no_repetitions(dtype): def test_run_repetitions_measure_at_end(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.DensityMatrixSimulator(dtype=dtype) - with mock.patch.object(simulator, '_base_iterator', wraps=simulator._base_iterator) as mock_sim: + with mock.patch.object(simulator, 'iterate_circuit', wraps=simulator.iterate_circuit) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -303,7 +303,7 @@ def test_run_repetitions_measure_at_end(dtype): def test_run_qudits_repetitions_measure_at_end(dtype): q0, q1 = cirq.LineQid.for_qid_shape((2, 3)) simulator = cirq.DensityMatrixSimulator(dtype=dtype) - with mock.patch.object(simulator, '_base_iterator', wraps=simulator._base_iterator) as mock_sim: + with mock.patch.object(simulator, 'iterate_circuit', wraps=simulator.iterate_circuit) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1, 2]: circuit = cirq.Circuit( @@ -321,7 +321,7 @@ def test_run_qudits_repetitions_measure_at_end(dtype): def test_run_measurement_not_terminal_no_repetitions(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.DensityMatrixSimulator(dtype=dtype) - with mock.patch.object(simulator, '_base_iterator', wraps=simulator._base_iterator) as mock_sim: + with mock.patch.object(simulator, 'iterate_circuit', wraps=simulator.iterate_circuit) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -344,7 +344,7 @@ def test_run_measurement_not_terminal_no_repetitions(dtype): def test_run_repetitions_measurement_not_terminal(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.DensityMatrixSimulator(dtype=dtype) - with mock.patch.object(simulator, '_base_iterator', wraps=simulator._base_iterator) as mock_sim: + with mock.patch.object(simulator, 'iterate_circuit', wraps=simulator.iterate_circuit) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -365,7 +365,7 @@ def test_run_repetitions_measurement_not_terminal(dtype): def test_run_qudits_repetitions_measurement_not_terminal(dtype): q0, q1 = cirq.LineQid.for_qid_shape((2, 3)) simulator = cirq.DensityMatrixSimulator(dtype=dtype) - with mock.patch.object(simulator, '_base_iterator', wraps=simulator._base_iterator) as mock_sim: + with mock.patch.object(simulator, 'iterate_circuit', wraps=simulator.iterate_circuit) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1, 2]: circuit = cirq.Circuit( @@ -1304,7 +1304,7 @@ def test_nonmeasuring_subcircuits_do_not_cause_sweep_repeat(): cirq.measure(q, key='x'), ) simulator = cirq.DensityMatrixSimulator() - with mock.patch.object(simulator, '_base_iterator', wraps=simulator._base_iterator) as mock_sim: + with mock.patch.object(simulator, 'iterate_circuit', wraps=simulator.iterate_circuit) as mock_sim: simulator.run(circuit, repetitions=10) assert mock_sim.call_count == 2 @@ -1316,7 +1316,7 @@ def test_measuring_subcircuits_cause_sweep_repeat(): cirq.measure(q, key='x'), ) simulator = cirq.DensityMatrixSimulator() - with mock.patch.object(simulator, '_base_iterator', wraps=simulator._base_iterator) as mock_sim: + with mock.patch.object(simulator, 'iterate_circuit', wraps=simulator.iterate_circuit) as mock_sim: simulator.run(circuit, repetitions=10) assert mock_sim.call_count == 11 diff --git a/cirq/sim/sparse_simulator.py b/cirq/sim/sparse_simulator.py index 91e5cd89e58..311edf27b90 100644 --- a/cirq/sim/sparse_simulator.py +++ b/cirq/sim/sparse_simulator.py @@ -259,6 +259,15 @@ def iterate_circuit( ): qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) qubit_map = {q: i for i, q in enumerate(qubits)} + if len(circuit) == 0: + yield SparseSimulatorStep( + state_vector=sim_state.target_tensor, + measurements=dict(sim_state.log_of_measurement_results), + qubit_map=qubit_map, + dtype=self._dtype, + ) + return + noisy_moments = self.noise.noisy_moments(circuit, sorted(circuit.all_qubits())) for op_tree in noisy_moments: for op in flatten_to_ops(op_tree): From 25efdad0e267c66cdde599884b82fc19ae811665 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Wed, 24 Mar 2021 10:55:40 -0700 Subject: [PATCH 09/44] Change sparse simulator to use act_on_args in _run. --- cirq/sim/clifford/clifford_simulator.py | 2 +- cirq/sim/density_matrix_simulator.py | 5 ++--- cirq/sim/sparse_simulator.py | 15 +++++++-------- cirq/sim/sparse_simulator_test.py | 12 ++++++------ 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/cirq/sim/clifford/clifford_simulator.py b/cirq/sim/clifford/clifford_simulator.py index 7e05b1d1116..98a941b0df7 100644 --- a/cirq/sim/clifford/clifford_simulator.py +++ b/cirq/sim/clifford/clifford_simulator.py @@ -184,7 +184,7 @@ def __str__(self) -> str: class CliffordSimulatorStepResult(simulator.StepResult['CliffordState']): """A `StepResult` that includes `StateVectorMixin` methods.""" - def __init__(self, state, measurements): + def __init__(self, state: 'CliffordState', measurements): """Results of a step of the simulator. Attributes: state: A CliffordState diff --git a/cirq/sim/density_matrix_simulator.py b/cirq/sim/density_matrix_simulator.py index 1de73d9a627..383ef15fe9c 100644 --- a/cirq/sim/density_matrix_simulator.py +++ b/cirq/sim/density_matrix_simulator.py @@ -181,14 +181,13 @@ def _run( pass assert step_result is not None - intermediate_state = acton_args.copy() if general_suffix.are_all_measurements_terminal() and not any( general_suffix.findall_operations(lambda op: isinstance(op, circuits.CircuitOperation)) ): return self._run_sweep_sample( - general_suffix, repetitions, qubit_order, intermediate_state + general_suffix, repetitions, qubit_order, acton_args ) - return self._run_sweep_repeat(general_suffix, repetitions, qubit_order, intermediate_state) + return self._run_sweep_repeat(general_suffix, repetitions, qubit_order, acton_args) def _run_sweep_sample( self, diff --git a/cirq/sim/sparse_simulator.py b/cirq/sim/sparse_simulator.py index 311edf27b90..2f476c3f6fb 100644 --- a/cirq/sim/sparse_simulator.py +++ b/cirq/sim/sparse_simulator.py @@ -182,11 +182,12 @@ def _run( if protocols.has_unitary(self.noise) else (resolved_circuit[0:0], resolved_circuit) ) + acton_args = self.create_act_on_args(unitary_prefix, qubit_order, 0) step_result = None - for step_result in self._base_iterator( + for step_result in self.iterate_circuit( circuit=unitary_prefix, qubit_order=qubit_order, - initial_state=0, + sim_state=acton_args, ): pass assert step_result is not None @@ -201,10 +202,8 @@ def _run( seed=self._prng, ) - qid_shape = protocols.qid_shape(qubit_order) - intermediate_state = step_result.state_vector().reshape(qid_shape) return self._brute_force_samples( - initial_state=intermediate_state, + acton_args=acton_args, circuit=general_suffix, repetitions=repetitions, qubit_order=qubit_order, @@ -212,7 +211,7 @@ def _run( def _brute_force_samples( self, - initial_state: np.ndarray, + acton_args: act_on_state_vector_args.ActOnStateVectorArgs, circuit: circuits.Circuit, qubit_order: 'cirq.QubitOrderOrList', repetitions: int, @@ -221,8 +220,8 @@ def _brute_force_samples( measurements: DefaultDict[str, List[np.ndarray]] = collections.defaultdict(list) for _ in range(repetitions): - all_step_results = self._base_iterator( - circuit, initial_state=initial_state, qubit_order=qubit_order + all_step_results = self.iterate_circuit( + circuit, sim_state=acton_args.copy(), qubit_order=qubit_order ) for step_result in all_step_results: diff --git a/cirq/sim/sparse_simulator_test.py b/cirq/sim/sparse_simulator_test.py index 93775f2b2b2..1823ee699c1 100644 --- a/cirq/sim/sparse_simulator_test.py +++ b/cirq/sim/sparse_simulator_test.py @@ -89,7 +89,7 @@ def test_run_bit_flips(dtype): def test_run_measure_at_end_no_repetitions(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.Simulator(dtype=dtype) - with mock.patch.object(simulator, '_base_iterator', wraps=simulator._base_iterator) as mock_sim: + with mock.patch.object(simulator, 'iterate_circuit', wraps=simulator.iterate_circuit) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -114,7 +114,7 @@ def test_run_repetitions_terminal_measurement_stochastic(): def test_run_repetitions_measure_at_end(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.Simulator(dtype=dtype) - with mock.patch.object(simulator, '_base_iterator', wraps=simulator._base_iterator) as mock_sim: + with mock.patch.object(simulator, 'iterate_circuit', wraps=simulator.iterate_circuit) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -131,7 +131,7 @@ def test_run_repetitions_measure_at_end(dtype): def test_run_invert_mask_measure_not_terminal(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.Simulator(dtype=dtype) - with mock.patch.object(simulator, '_base_iterator', wraps=simulator._base_iterator) as mock_sim: + with mock.patch.object(simulator, 'iterate_circuit', wraps=simulator.iterate_circuit) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -151,7 +151,7 @@ def test_run_invert_mask_measure_not_terminal(dtype): def test_run_partial_invert_mask_measure_not_terminal(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.Simulator(dtype=dtype) - with mock.patch.object(simulator, '_base_iterator', wraps=simulator._base_iterator) as mock_sim: + with mock.patch.object(simulator, 'iterate_circuit', wraps=simulator.iterate_circuit) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -171,7 +171,7 @@ def test_run_partial_invert_mask_measure_not_terminal(dtype): def test_run_measurement_not_terminal_no_repetitions(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.Simulator(dtype=dtype) - with mock.patch.object(simulator, '_base_iterator', wraps=simulator._base_iterator) as mock_sim: + with mock.patch.object(simulator, 'iterate_circuit', wraps=simulator.iterate_circuit) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -194,7 +194,7 @@ def test_run_measurement_not_terminal_no_repetitions(dtype): def test_run_repetitions_measurement_not_terminal(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.Simulator(dtype=dtype) - with mock.patch.object(simulator, '_base_iterator', wraps=simulator._base_iterator) as mock_sim: + with mock.patch.object(simulator, 'iterate_circuit', wraps=simulator.iterate_circuit) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( From 49f4ae40d9d9687dc6f580fdd07d83d627b5ba43 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Wed, 24 Mar 2021 12:18:14 -0700 Subject: [PATCH 10/44] Fix clifford simulator --- cirq/sim/clifford/clifford_simulator.py | 14 ++++++++++---- cirq/sim/density_matrix_simulator_test.py | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/cirq/sim/clifford/clifford_simulator.py b/cirq/sim/clifford/clifford_simulator.py index 98a941b0df7..241b51b4d5e 100644 --- a/cirq/sim/clifford/clifford_simulator.py +++ b/cirq/sim/clifford/clifford_simulator.py @@ -103,14 +103,20 @@ def iterate_circuit( qubit_order: ops.QubitOrderOrList, ch_form_args: clifford.ActOnStabilizerCHFormArgs, ): + qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) + qubit_map = {q: i for i, q in enumerate(qubits)} + + def create_state(): + state = CliffordState(qubit_map) + state.ch_form = ch_form_args.state.copy() + return state + if len(circuit) == 0: yield CliffordSimulatorStepResult( - measurements=ch_form_args.log_of_measurement_results, state=ch_form_args.state + measurements=ch_form_args.log_of_measurement_results, state=create_state() ) return - qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) - qubit_map = {q: i for i, q in enumerate(qubits)} for moment in circuit: ch_form_args.log_of_measurement_results = {} @@ -124,7 +130,7 @@ def iterate_circuit( ) # type: ignore yield CliffordSimulatorStepResult( - measurements=ch_form_args.log_of_measurement_results, state=ch_form_args.state + measurements=ch_form_args.log_of_measurement_results, state=create_state() ) def _create_simulator_trial_result( diff --git a/cirq/sim/density_matrix_simulator_test.py b/cirq/sim/density_matrix_simulator_test.py index 8fe02f0d6d0..e8bb1240532 100644 --- a/cirq/sim/density_matrix_simulator_test.py +++ b/cirq/sim/density_matrix_simulator_test.py @@ -710,7 +710,7 @@ def test_simulate_moment_steps_empty_circuit(dtype): 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={} ) From d11d34fc44b365d24fb4c52ba39e5f2b0d7cc548 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Wed, 24 Mar 2021 12:35:23 -0700 Subject: [PATCH 11/44] format --- cirq/contrib/quimb/mps_simulator.py | 12 +++++--- cirq/sim/clifford/clifford_simulator.py | 13 +++++--- cirq/sim/density_matrix_simulator.py | 25 ++++++++------- cirq/sim/density_matrix_simulator_test.py | 37 +++++++++++++++++------ cirq/sim/sparse_simulator.py | 8 ++--- cirq/sim/sparse_simulator_test.py | 24 +++++++++++---- cirq/sim/state_vector_simulator.py | 5 ++- 7 files changed, 81 insertions(+), 43 deletions(-) diff --git a/cirq/contrib/quimb/mps_simulator.py b/cirq/contrib/quimb/mps_simulator.py index 0518291c2ee..1ac636bc5b2 100644 --- a/cirq/contrib/quimb/mps_simulator.py +++ b/cirq/contrib/quimb/mps_simulator.py @@ -54,7 +54,9 @@ class MPSOptions: class MPSSimulator( simulator.SimulatesSamples, - simulator.SimulatesIntermediateState['MPSSimulatorStepResult', 'MPSTrialResult', 'MPSState', 'MPSState'], + simulator.SimulatesIntermediateState[ + 'MPSSimulatorStepResult', 'MPSTrialResult', 'MPSState', 'MPSState' + ], ): """An efficient simulator for MPS circuits.""" @@ -116,10 +118,10 @@ def create_act_on_args( ) def iterate_circuit( - self, - circuit: circuits.Circuit, - qubit_order: ops.QubitOrderOrList, - state: 'MPSState', + self, + circuit: circuits.Circuit, + qubit_order: ops.QubitOrderOrList, + state: 'MPSState', ): if len(circuit) == 0: yield MPSSimulatorStepResult(measurements=state.log_of_measurement_results, state=state) diff --git a/cirq/sim/clifford/clifford_simulator.py b/cirq/sim/clifford/clifford_simulator.py index 241b51b4d5e..fd8ab213ce8 100644 --- a/cirq/sim/clifford/clifford_simulator.py +++ b/cirq/sim/clifford/clifford_simulator.py @@ -45,7 +45,10 @@ class CliffordSimulator( simulator.SimulatesSamples, simulator.SimulatesIntermediateState[ - 'CliffordSimulatorStepResult', 'CliffordTrialResult', 'CliffordState', clifford.ActOnStabilizerCHFormArgs, + 'CliffordSimulatorStepResult', + 'CliffordTrialResult', + 'CliffordState', + clifford.ActOnStabilizerCHFormArgs, ], ): """An efficient simulator for Clifford circuits.""" @@ -98,10 +101,10 @@ def create_act_on_args( ) def iterate_circuit( - self, - circuit: circuits.Circuit, - qubit_order: ops.QubitOrderOrList, - ch_form_args: clifford.ActOnStabilizerCHFormArgs, + self, + circuit: circuits.Circuit, + qubit_order: ops.QubitOrderOrList, + ch_form_args: clifford.ActOnStabilizerCHFormArgs, ): qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) qubit_map = {q: i for i, q in enumerate(qubits)} diff --git a/cirq/sim/density_matrix_simulator.py b/cirq/sim/density_matrix_simulator.py index 383ef15fe9c..a44dfa05f58 100644 --- a/cirq/sim/density_matrix_simulator.py +++ b/cirq/sim/density_matrix_simulator.py @@ -29,7 +29,10 @@ class DensityMatrixSimulator( simulator.SimulatesSamples, simulator.SimulatesIntermediateState[ - 'DensityMatrixStepResult', 'DensityMatrixTrialResult', 'DensityMatrixSimulatorState', act_on_density_matrix_args.ActOnDensityMatrixArgs, + 'DensityMatrixStepResult', + 'DensityMatrixTrialResult', + 'DensityMatrixSimulatorState', + act_on_density_matrix_args.ActOnDensityMatrixArgs, ], ): """A simulator for density matrices and noisy quantum circuits. @@ -184,9 +187,7 @@ def _run( if general_suffix.are_all_measurements_terminal() and not any( general_suffix.findall_operations(lambda op: isinstance(op, circuits.CircuitOperation)) ): - return self._run_sweep_sample( - general_suffix, repetitions, qubit_order, acton_args - ) + return self._run_sweep_sample(general_suffix, repetitions, qubit_order, acton_args) return self._run_sweep_repeat(general_suffix, repetitions, qubit_order, acton_args) def _run_sweep_sample( @@ -238,10 +239,8 @@ def create_act_on_args( ): qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) qid_shape = protocols.qid_shape(qubits) - initial_matrix = ( - qis.to_valid_density_matrix( - initial_state, len(qid_shape), qid_shape=qid_shape, dtype=self._dtype - ) + initial_matrix = qis.to_valid_density_matrix( + initial_state, len(qid_shape), qid_shape=qid_shape, dtype=self._dtype ) if np.may_share_memory(initial_matrix, initial_state): initial_matrix = initial_matrix.copy() @@ -257,11 +256,11 @@ def create_act_on_args( ) def iterate_circuit( - self, - circuit: circuits.Circuit, - qubit_order: ops.QubitOrderOrList, - sim_state: act_on_density_matrix_args.ActOnDensityMatrixArgs, - all_measurements_are_terminal: bool = False, + self, + circuit: circuits.Circuit, + qubit_order: ops.QubitOrderOrList, + sim_state: act_on_density_matrix_args.ActOnDensityMatrixArgs, + all_measurements_are_terminal: bool = False, ): qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) qubit_map = {q: i for i, q in enumerate(qubits)} diff --git a/cirq/sim/density_matrix_simulator_test.py b/cirq/sim/density_matrix_simulator_test.py index e8bb1240532..3f09706f0d4 100644 --- a/cirq/sim/density_matrix_simulator_test.py +++ b/cirq/sim/density_matrix_simulator_test.py @@ -269,7 +269,9 @@ def _channel_(self): def test_run_measure_at_end_no_repetitions(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.DensityMatrixSimulator(dtype=dtype) - with mock.patch.object(simulator, 'iterate_circuit', wraps=simulator.iterate_circuit) as mock_sim: + with mock.patch.object( + simulator, 'iterate_circuit', wraps=simulator.iterate_circuit + ) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -287,7 +289,9 @@ def test_run_measure_at_end_no_repetitions(dtype): def test_run_repetitions_measure_at_end(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.DensityMatrixSimulator(dtype=dtype) - with mock.patch.object(simulator, 'iterate_circuit', wraps=simulator.iterate_circuit) as mock_sim: + with mock.patch.object( + simulator, 'iterate_circuit', wraps=simulator.iterate_circuit + ) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -303,7 +307,9 @@ def test_run_repetitions_measure_at_end(dtype): def test_run_qudits_repetitions_measure_at_end(dtype): q0, q1 = cirq.LineQid.for_qid_shape((2, 3)) simulator = cirq.DensityMatrixSimulator(dtype=dtype) - with mock.patch.object(simulator, 'iterate_circuit', wraps=simulator.iterate_circuit) as mock_sim: + with mock.patch.object( + simulator, 'iterate_circuit', wraps=simulator.iterate_circuit + ) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1, 2]: circuit = cirq.Circuit( @@ -321,7 +327,9 @@ def test_run_qudits_repetitions_measure_at_end(dtype): def test_run_measurement_not_terminal_no_repetitions(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.DensityMatrixSimulator(dtype=dtype) - with mock.patch.object(simulator, 'iterate_circuit', wraps=simulator.iterate_circuit) as mock_sim: + with mock.patch.object( + simulator, 'iterate_circuit', wraps=simulator.iterate_circuit + ) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -344,7 +352,9 @@ def test_run_measurement_not_terminal_no_repetitions(dtype): def test_run_repetitions_measurement_not_terminal(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.DensityMatrixSimulator(dtype=dtype) - with mock.patch.object(simulator, 'iterate_circuit', wraps=simulator.iterate_circuit) as mock_sim: + with mock.patch.object( + simulator, 'iterate_circuit', wraps=simulator.iterate_circuit + ) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -365,7 +375,9 @@ def test_run_repetitions_measurement_not_terminal(dtype): def test_run_qudits_repetitions_measurement_not_terminal(dtype): q0, q1 = cirq.LineQid.for_qid_shape((2, 3)) simulator = cirq.DensityMatrixSimulator(dtype=dtype) - with mock.patch.object(simulator, 'iterate_circuit', wraps=simulator.iterate_circuit) as mock_sim: + with mock.patch.object( + simulator, 'iterate_circuit', wraps=simulator.iterate_circuit + ) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1, 2]: circuit = cirq.Circuit( @@ -710,7 +722,10 @@ def test_simulate_moment_steps_empty_circuit(dtype): 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={}, ) @@ -1304,7 +1319,9 @@ def test_nonmeasuring_subcircuits_do_not_cause_sweep_repeat(): cirq.measure(q, key='x'), ) simulator = cirq.DensityMatrixSimulator() - with mock.patch.object(simulator, 'iterate_circuit', wraps=simulator.iterate_circuit) as mock_sim: + with mock.patch.object( + simulator, 'iterate_circuit', wraps=simulator.iterate_circuit + ) as mock_sim: simulator.run(circuit, repetitions=10) assert mock_sim.call_count == 2 @@ -1316,7 +1333,9 @@ def test_measuring_subcircuits_cause_sweep_repeat(): cirq.measure(q, key='x'), ) simulator = cirq.DensityMatrixSimulator() - with mock.patch.object(simulator, 'iterate_circuit', wraps=simulator.iterate_circuit) as mock_sim: + with mock.patch.object( + simulator, 'iterate_circuit', wraps=simulator.iterate_circuit + ) as mock_sim: simulator.run(circuit, repetitions=10) assert mock_sim.call_count == 11 diff --git a/cirq/sim/sparse_simulator.py b/cirq/sim/sparse_simulator.py index 2f476c3f6fb..0d8ec9e3153 100644 --- a/cirq/sim/sparse_simulator.py +++ b/cirq/sim/sparse_simulator.py @@ -251,10 +251,10 @@ def create_act_on_args( ) def iterate_circuit( - self, - circuit: circuits.Circuit, - qubit_order: ops.QubitOrderOrList, - sim_state: act_on_state_vector_args.ActOnStateVectorArgs, + self, + circuit: circuits.Circuit, + qubit_order: ops.QubitOrderOrList, + sim_state: act_on_state_vector_args.ActOnStateVectorArgs, ): qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) qubit_map = {q: i for i, q in enumerate(qubits)} diff --git a/cirq/sim/sparse_simulator_test.py b/cirq/sim/sparse_simulator_test.py index 1823ee699c1..4be27de63bb 100644 --- a/cirq/sim/sparse_simulator_test.py +++ b/cirq/sim/sparse_simulator_test.py @@ -89,7 +89,9 @@ def test_run_bit_flips(dtype): def test_run_measure_at_end_no_repetitions(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.Simulator(dtype=dtype) - with mock.patch.object(simulator, 'iterate_circuit', wraps=simulator.iterate_circuit) as mock_sim: + with mock.patch.object( + simulator, 'iterate_circuit', wraps=simulator.iterate_circuit + ) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -114,7 +116,9 @@ def test_run_repetitions_terminal_measurement_stochastic(): def test_run_repetitions_measure_at_end(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.Simulator(dtype=dtype) - with mock.patch.object(simulator, 'iterate_circuit', wraps=simulator.iterate_circuit) as mock_sim: + with mock.patch.object( + simulator, 'iterate_circuit', wraps=simulator.iterate_circuit + ) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -131,7 +135,9 @@ def test_run_repetitions_measure_at_end(dtype): def test_run_invert_mask_measure_not_terminal(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.Simulator(dtype=dtype) - with mock.patch.object(simulator, 'iterate_circuit', wraps=simulator.iterate_circuit) as mock_sim: + with mock.patch.object( + simulator, 'iterate_circuit', wraps=simulator.iterate_circuit + ) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -151,7 +157,9 @@ def test_run_invert_mask_measure_not_terminal(dtype): def test_run_partial_invert_mask_measure_not_terminal(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.Simulator(dtype=dtype) - with mock.patch.object(simulator, 'iterate_circuit', wraps=simulator.iterate_circuit) as mock_sim: + with mock.patch.object( + simulator, 'iterate_circuit', wraps=simulator.iterate_circuit + ) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -171,7 +179,9 @@ def test_run_partial_invert_mask_measure_not_terminal(dtype): def test_run_measurement_not_terminal_no_repetitions(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.Simulator(dtype=dtype) - with mock.patch.object(simulator, 'iterate_circuit', wraps=simulator.iterate_circuit) as mock_sim: + with mock.patch.object( + simulator, 'iterate_circuit', wraps=simulator.iterate_circuit + ) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -194,7 +204,9 @@ def test_run_measurement_not_terminal_no_repetitions(dtype): def test_run_repetitions_measurement_not_terminal(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.Simulator(dtype=dtype) - with mock.patch.object(simulator, 'iterate_circuit', wraps=simulator.iterate_circuit) as mock_sim: + with mock.patch.object( + simulator, 'iterate_circuit', wraps=simulator.iterate_circuit + ) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( diff --git a/cirq/sim/state_vector_simulator.py b/cirq/sim/state_vector_simulator.py index c8a3e102f10..12de1c3a5a3 100644 --- a/cirq/sim/state_vector_simulator.py +++ b/cirq/sim/state_vector_simulator.py @@ -34,7 +34,10 @@ class SimulatesIntermediateStateVector( Generic[TStateVectorStepResult], simulator.SimulatesAmplitudes, simulator.SimulatesIntermediateState[ - TStateVectorStepResult, 'StateVectorTrialResult', 'StateVectorSimulatorState', ActOnStateVectorArgs + TStateVectorStepResult, + 'StateVectorTrialResult', + 'StateVectorSimulatorState', + ActOnStateVectorArgs, ], metaclass=abc.ABCMeta, ): From 0ee652605761e65b9db061de3d1d56e34e7a034f Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Wed, 24 Mar 2021 12:38:42 -0700 Subject: [PATCH 12/44] optimize imports --- cirq/contrib/quimb/mps_simulator.py | 4 ++-- cirq/sim/clifford/clifford_simulator.py | 4 ++-- cirq/sim/density_matrix_simulator.py | 2 +- cirq/sim/sparse_simulator.py | 1 - 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/cirq/contrib/quimb/mps_simulator.py b/cirq/contrib/quimb/mps_simulator.py index 1ac636bc5b2..e85a5d96354 100644 --- a/cirq/contrib/quimb/mps_simulator.py +++ b/cirq/contrib/quimb/mps_simulator.py @@ -17,10 +17,10 @@ https://arxiv.org/abs/2002.07730 """ +import dataclasses import math -from typing import Any, Dict, List, Iterator, Optional, Sequence, Set, TYPE_CHECKING, Iterable +from typing import Any, Dict, List, Optional, Sequence, Set, TYPE_CHECKING, Iterable -import dataclasses import numpy as np import quimb.tensor as qtn diff --git a/cirq/sim/clifford/clifford_simulator.py b/cirq/sim/clifford/clifford_simulator.py index fd8ab213ce8..afc76b0f8ec 100644 --- a/cirq/sim/clifford/clifford_simulator.py +++ b/cirq/sim/clifford/clifford_simulator.py @@ -29,16 +29,16 @@ to state vector amplitudes. """ -from typing import Any, Dict, List, Iterator, Sequence +from typing import Any, Dict, List, Sequence import numpy as np import cirq from cirq import circuits, study, ops, protocols, value +from cirq._compat import deprecated from cirq.ops.dense_pauli_string import DensePauliString from cirq.protocols import act_on from cirq.sim import clifford, simulator -from cirq._compat import deprecated from cirq.sim.simulator import check_all_resolved diff --git a/cirq/sim/density_matrix_simulator.py b/cirq/sim/density_matrix_simulator.py index a44dfa05f58..f211993cfbd 100644 --- a/cirq/sim/density_matrix_simulator.py +++ b/cirq/sim/density_matrix_simulator.py @@ -13,7 +13,7 @@ # limitations under the License. """Simulator for density matrices that simulates noisy quantum circuits.""" import collections -from typing import Any, Dict, Iterator, List, TYPE_CHECKING, Tuple, Type, Union +from typing import Any, Dict, List, TYPE_CHECKING, Tuple, Type, Union import numpy as np diff --git a/cirq/sim/sparse_simulator.py b/cirq/sim/sparse_simulator.py index 0d8ec9e3153..868dff0d387 100644 --- a/cirq/sim/sparse_simulator.py +++ b/cirq/sim/sparse_simulator.py @@ -18,7 +18,6 @@ from typing import ( Any, Dict, - Iterator, List, Type, TYPE_CHECKING, From f7f792a1ce4dec5a6472be30d05ee2c84665a073 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Wed, 24 Mar 2021 22:19:18 -0700 Subject: [PATCH 13/44] Address PR comments --- cirq/contrib/quimb/mps_simulator.py | 26 +++++++++---------- cirq/contrib/quimb/mps_simulator_test.py | 14 +--------- cirq/sim/act_on_args.py | 8 ++++-- cirq/sim/act_on_density_matrix_args.py | 2 +- cirq/sim/act_on_state_vector_args.py | 2 +- .../clifford/act_on_clifford_tableau_args.py | 2 +- .../act_on_stabilizer_ch_form_args.py | 2 +- 7 files changed, 24 insertions(+), 32 deletions(-) diff --git a/cirq/contrib/quimb/mps_simulator.py b/cirq/contrib/quimb/mps_simulator.py index 8fff3f7697e..aec48cbc02a 100644 --- a/cirq/contrib/quimb/mps_simulator.py +++ b/cirq/contrib/quimb/mps_simulator.py @@ -107,9 +107,7 @@ def _base_iterator( measurements={}, state=MPSState( qubit_map, - [], self.prng, - {}, self.simulation_options, self.grouping, initial_state=initial_state, @@ -119,9 +117,7 @@ def _base_iterator( state = MPSState( qubit_map, - [], self.prng, - {}, self.simulation_options, self.grouping, initial_state=initial_state, @@ -291,12 +287,12 @@ class MPSState(ActOnArgs): def __init__( self, qubit_map: Dict['cirq.Qid', int], - axes: Iterable[int], prng: np.random.RandomState, - log_of_measurement_results: Dict[str, Any], simulation_options: MPSOptions = MPSOptions(), grouping: Optional[Dict['cirq.Qid', int]] = None, initial_state: int = 0, + axes: Iterable[int] = None, + log_of_measurement_results: Dict[str, Any] = None, ): """Creates and MPSState @@ -306,8 +302,12 @@ def __init__( simulation_options: Numerical options for the simulation. grouping: How to group qubits together, if None all are individual. initial_state: An integer representing the initial state. + 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. """ - super().__init__(axes, prng, log_of_measurement_results) + super().__init__(prng, axes, log_of_measurement_results) self.qubit_map = qubit_map self.grouping = qubit_map if grouping is None else grouping if self.grouping.keys() != self.qubit_map.keys(): @@ -365,9 +365,7 @@ def _value_equality_values_(self) -> Any: def copy(self) -> 'MPSState': state = MPSState( self.qubit_map, - self.axes, self.prng, - self.log_of_measurement_results, self.simulation_options, self.grouping, ) @@ -430,7 +428,7 @@ def to_numpy(self) -> np.ndarray: """An alias for the state vector.""" return self.state_vector() - def _act_on_fallback_(self, op: Any, allow_decompose: bool): + def apply_op(self, op: 'cirq.Operation', prng: np.random.RandomState): """Applies a unitary operation, mutating the object to represent the new state. op: @@ -445,9 +443,7 @@ def _act_on_fallback_(self, op: Any, allow_decompose: bool): U = protocols.unitary(op) else: mixtures = protocols.mixture(op) - mixture_idx = int( - self.prng.choice(len(mixtures), p=[mixture[0] for mixture in mixtures]) - ) + mixture_idx = int(prng.choice(len(mixtures), p=[mixture[0] for mixture in mixtures])) U = mixtures[mixture_idx][1] U = qtn.Tensor( U.reshape([qubit.dimension for qubit in op.qubits] * 2), inds=(new_inds + old_inds) @@ -513,6 +509,10 @@ def _act_on_fallback_(self, op: Any, allow_decompose: bool): raise ValueError('Can only handle 1 and 2 qubit operations') return True + def _act_on_fallback_(self, op: Any, allow_decompose: bool): + """Delegates the action to self.apply_op""" + return self.apply_op(op, self.prng) + def estimation_stats(self): "Returns some statistics about the memory usage and quality of the approximation." diff --git a/cirq/contrib/quimb/mps_simulator_test.py b/cirq/contrib/quimb/mps_simulator_test.py index cc7ed43ee2f..8c16c5c8385 100644 --- a/cirq/contrib/quimb/mps_simulator_test.py +++ b/cirq/contrib/quimb/mps_simulator_test.py @@ -260,8 +260,6 @@ def test_trial_result_str(): qubit_map={q0: 0}, prng=value.parse_random_state(0), simulation_options=ccq.mps_simulator.MPSOptions(), - axes=[], - log_of_measurement_results={}, ) assert ( str( @@ -280,9 +278,7 @@ def test_trial_result_str(): def test_empty_step_result(): q0 = cirq.LineQubit(0) - state = ccq.mps_simulator.MPSState( - qubit_map={q0: 0}, prng=value.parse_random_state(0), axes=[], log_of_measurement_results={} - ) + state = ccq.mps_simulator.MPSState(qubit_map={q0: 0}, prng=value.parse_random_state(0)) step_result = ccq.mps_simulator.MPSSimulatorStepResult(state, measurements={'0': [1]}) assert ( str(step_result) @@ -299,22 +295,16 @@ def test_state_equal(): qubit_map={q0: 0}, prng=value.parse_random_state(0), simulation_options=ccq.mps_simulator.MPSOptions(cutoff=1e-3, sum_prob_atol=1e-3), - axes=[], - log_of_measurement_results={}, ) state1a = ccq.mps_simulator.MPSState( qubit_map={q1: 0}, prng=value.parse_random_state(0), simulation_options=ccq.mps_simulator.MPSOptions(cutoff=1e-3, sum_prob_atol=1e-3), - axes=[], - log_of_measurement_results={}, ) state1b = ccq.mps_simulator.MPSState( qubit_map={q1: 0}, prng=value.parse_random_state(0), simulation_options=ccq.mps_simulator.MPSOptions(cutoff=1729.0, sum_prob_atol=1e-3), - axes=[], - log_of_measurement_results={}, ) assert state0 == state0 assert state0 != state1a @@ -343,8 +333,6 @@ def test_tensor_index_names(): state = ccq.mps_simulator.MPSState( qubit_map, prng=value.parse_random_state(0), - axes=[], - log_of_measurement_results={}, ) assert state.i_str(0) == "i_00" diff --git a/cirq/sim/act_on_args.py b/cirq/sim/act_on_args.py index a9b2f381292..d0533d0527c 100644 --- a/cirq/sim/act_on_args.py +++ b/cirq/sim/act_on_args.py @@ -26,9 +26,9 @@ class ActOnArgs: def __init__( self, - axes: Iterable[int], prng: np.random.RandomState, - log_of_measurement_results: Dict[str, Any], + axes: Iterable[int] = None, + log_of_measurement_results: Dict[str, Any] = None, ): """ Args: @@ -40,6 +40,10 @@ def __init__( being recorded into. Edit it easily by calling `ActOnStateVectorArgs.record_measurement_result`. """ + if axes is None: + axes = [] + if log_of_measurement_results is None: + log_of_measurement_results = {} self.axes = tuple(axes) self.prng = prng self.log_of_measurement_results = log_of_measurement_results diff --git a/cirq/sim/act_on_density_matrix_args.py b/cirq/sim/act_on_density_matrix_args.py index a1e6bb4e023..cb57f0b3815 100644 --- a/cirq/sim/act_on_density_matrix_args.py +++ b/cirq/sim/act_on_density_matrix_args.py @@ -55,7 +55,7 @@ def __init__( being recorded into. Edit it easily by calling `ActOnStateVectorArgs.record_measurement_result`. """ - super().__init__(axes, prng, log_of_measurement_results) + super().__init__(prng, axes, log_of_measurement_results) self.target_tensor = target_tensor self.available_buffer = available_buffer self.qid_shape = qid_shape diff --git a/cirq/sim/act_on_state_vector_args.py b/cirq/sim/act_on_state_vector_args.py index 001dff7a7e8..5ac08918e7f 100644 --- a/cirq/sim/act_on_state_vector_args.py +++ b/cirq/sim/act_on_state_vector_args.py @@ -62,7 +62,7 @@ def __init__( being recorded into. Edit it easily by calling `ActOnStateVectorArgs.record_measurement_result`. """ - super().__init__(axes, prng, log_of_measurement_results) + super().__init__(prng, axes, log_of_measurement_results) self.target_tensor = target_tensor self.available_buffer = available_buffer diff --git a/cirq/sim/clifford/act_on_clifford_tableau_args.py b/cirq/sim/clifford/act_on_clifford_tableau_args.py index 9b30c7243d1..54d5414604c 100644 --- a/cirq/sim/clifford/act_on_clifford_tableau_args.py +++ b/cirq/sim/clifford/act_on_clifford_tableau_args.py @@ -56,7 +56,7 @@ def __init__( being recorded into. Edit it easily by calling `ActOnCliffordTableauArgs.record_measurement_result`. """ - super().__init__(axes, prng, log_of_measurement_results) + super().__init__(prng, axes, log_of_measurement_results) self.tableau = tableau def _act_on_fallback_(self, action: Any, allow_decompose: bool): diff --git a/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py b/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py index b7362a5be10..b3470f36bb0 100644 --- a/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py +++ b/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py @@ -53,7 +53,7 @@ def __init__( being recorded into. Edit it easily by calling `ActOnStabilizerCHFormArgs.record_measurement_result`. """ - super().__init__(axes, prng, log_of_measurement_results) + super().__init__(prng, axes, log_of_measurement_results) self.state = state def _act_on_fallback_(self, action: Any, allow_decompose: bool): From 3f6d0e62b05a56974e26e720337e39fe32a227f9 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Wed, 24 Mar 2021 22:30:51 -0700 Subject: [PATCH 14/44] Unit test --- cirq/contrib/quimb/mps_simulator_test.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cirq/contrib/quimb/mps_simulator_test.py b/cirq/contrib/quimb/mps_simulator_test.py index 8c16c5c8385..aeccdd2e173 100644 --- a/cirq/contrib/quimb/mps_simulator_test.py +++ b/cirq/contrib/quimb/mps_simulator_test.py @@ -496,3 +496,14 @@ def test_state_copy(): assert len(x) == len(y) for i in range(len(x)): assert not np.shares_memory(x[i], y[i]) + + +def test_state_act_on_args_initializer(): + s = ccq.mps_simulator.MPSState( + qubit_map={cirq.LineQubit(0): 0}, + prng=np.random.RandomState(0), + axes=[2], + log_of_measurement_results={'test': 4} + ) + assert s.axes == (2,) + assert s.log_of_measurement_results == {'test': 4} From 6c91dd80fcd5d1ec4509508ebfa281a6bdd8c5f6 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Wed, 24 Mar 2021 22:32:22 -0700 Subject: [PATCH 15/44] Formatting --- cirq/contrib/quimb/mps_simulator_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq/contrib/quimb/mps_simulator_test.py b/cirq/contrib/quimb/mps_simulator_test.py index aeccdd2e173..ade54e10f98 100644 --- a/cirq/contrib/quimb/mps_simulator_test.py +++ b/cirq/contrib/quimb/mps_simulator_test.py @@ -503,7 +503,7 @@ def test_state_act_on_args_initializer(): qubit_map={cirq.LineQubit(0): 0}, prng=np.random.RandomState(0), axes=[2], - log_of_measurement_results={'test': 4} + log_of_measurement_results={'test': 4}, ) assert s.axes == (2,) assert s.log_of_measurement_results == {'test': 4} From 0c8763e45c6b0a9c190480b08169df601b598c57 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Thu, 25 Mar 2021 09:31:11 -0700 Subject: [PATCH 16/44] Rename, format --- cirq/contrib/quimb/mps_simulator.py | 4 +-- cirq/sim/clifford/clifford_simulator.py | 4 +-- cirq/sim/density_matrix_simulator.py | 12 ++++----- cirq/sim/density_matrix_simulator_test.py | 32 ++++++----------------- cirq/sim/simulator.py | 8 +++--- cirq/sim/sparse_simulator.py | 10 +++---- cirq/sim/sparse_simulator_test.py | 24 +++++------------ 7 files changed, 33 insertions(+), 61 deletions(-) diff --git a/cirq/contrib/quimb/mps_simulator.py b/cirq/contrib/quimb/mps_simulator.py index bd8587249f4..b6b86e94cba 100644 --- a/cirq/contrib/quimb/mps_simulator.py +++ b/cirq/contrib/quimb/mps_simulator.py @@ -84,7 +84,7 @@ def __init__( self.simulation_options = simulation_options self.grouping = grouping - def create_act_on_args( + def _create_act_on_args( self, circuit: circuits.Circuit, qubit_order: ops.QubitOrderOrList, @@ -115,7 +115,7 @@ def create_act_on_args( initial_state=initial_state, ) - def iterate_circuit( + def _core_iterator( self, circuit: circuits.Circuit, qubit_order: ops.QubitOrderOrList, diff --git a/cirq/sim/clifford/clifford_simulator.py b/cirq/sim/clifford/clifford_simulator.py index afc76b0f8ec..a10008524b0 100644 --- a/cirq/sim/clifford/clifford_simulator.py +++ b/cirq/sim/clifford/clifford_simulator.py @@ -68,7 +68,7 @@ def is_supported_operation(op: 'cirq.Operation') -> bool: # TODO: support more general Pauli measurements return protocols.has_stabilizer_effect(op) - def create_act_on_args( + def _create_act_on_args( self, circuit: circuits.Circuit, qubit_order: ops.QubitOrderOrList, @@ -100,7 +100,7 @@ def create_act_on_args( {}, ) - def iterate_circuit( + def _core_iterator( self, circuit: circuits.Circuit, qubit_order: ops.QubitOrderOrList, diff --git a/cirq/sim/density_matrix_simulator.py b/cirq/sim/density_matrix_simulator.py index f211993cfbd..d897cb00873 100644 --- a/cirq/sim/density_matrix_simulator.py +++ b/cirq/sim/density_matrix_simulator.py @@ -174,9 +174,9 @@ def _run( prefix, general_suffix = split_into_matching_protocol_then_general( resolved_circuit, lambda op: not protocols.is_measurement(op) ) - acton_args = self.create_act_on_args(prefix, qubit_order, 0) + acton_args = self._create_act_on_args(prefix, qubit_order, 0) step_result = None - for step_result in self.iterate_circuit( + for step_result in self._core_iterator( circuit=prefix, qubit_order=qubit_order, sim_state=acton_args, @@ -197,7 +197,7 @@ def _run_sweep_sample( qubit_order: ops.QubitOrderOrList, acton_args: act_on_density_matrix_args.ActOnDensityMatrixArgs, ) -> Dict[str, np.ndarray]: - for step_result in self.iterate_circuit( + for step_result in self._core_iterator( circuit=circuit, qubit_order=qubit_order, sim_state=acton_args, @@ -219,7 +219,7 @@ def _run_sweep_repeat( measurements = {} # type: Dict[str, List[np.ndarray]] for _ in range(repetitions): - all_step_results = self.iterate_circuit( + all_step_results = self._core_iterator( circuit, qubit_order=qubit_order, sim_state=acton_args.copy(), @@ -231,7 +231,7 @@ def _run_sweep_repeat( measurements[k].append(np.array(v, dtype=np.uint8)) return {k: np.array(v) for k, v in measurements.items()} - def create_act_on_args( + def _create_act_on_args( self, circuit: circuits.Circuit, qubit_order: ops.QubitOrderOrList, @@ -255,7 +255,7 @@ def create_act_on_args( log_of_measurement_results={}, ) - def iterate_circuit( + def _core_iterator( self, circuit: circuits.Circuit, qubit_order: ops.QubitOrderOrList, diff --git a/cirq/sim/density_matrix_simulator_test.py b/cirq/sim/density_matrix_simulator_test.py index 3f09706f0d4..aa8a1da1f59 100644 --- a/cirq/sim/density_matrix_simulator_test.py +++ b/cirq/sim/density_matrix_simulator_test.py @@ -269,9 +269,7 @@ def _channel_(self): def test_run_measure_at_end_no_repetitions(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.DensityMatrixSimulator(dtype=dtype) - with mock.patch.object( - simulator, 'iterate_circuit', wraps=simulator.iterate_circuit - ) as mock_sim: + with mock.patch.object(simulator, '_core_iterator', wraps=simulator._core_iterator) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -289,9 +287,7 @@ def test_run_measure_at_end_no_repetitions(dtype): def test_run_repetitions_measure_at_end(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.DensityMatrixSimulator(dtype=dtype) - with mock.patch.object( - simulator, 'iterate_circuit', wraps=simulator.iterate_circuit - ) as mock_sim: + with mock.patch.object(simulator, '_core_iterator', wraps=simulator._core_iterator) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -307,9 +303,7 @@ def test_run_repetitions_measure_at_end(dtype): def test_run_qudits_repetitions_measure_at_end(dtype): q0, q1 = cirq.LineQid.for_qid_shape((2, 3)) simulator = cirq.DensityMatrixSimulator(dtype=dtype) - with mock.patch.object( - simulator, 'iterate_circuit', wraps=simulator.iterate_circuit - ) as mock_sim: + with mock.patch.object(simulator, '_core_iterator', wraps=simulator._core_iterator) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1, 2]: circuit = cirq.Circuit( @@ -327,9 +321,7 @@ def test_run_qudits_repetitions_measure_at_end(dtype): def test_run_measurement_not_terminal_no_repetitions(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.DensityMatrixSimulator(dtype=dtype) - with mock.patch.object( - simulator, 'iterate_circuit', wraps=simulator.iterate_circuit - ) as mock_sim: + with mock.patch.object(simulator, '_core_iterator', wraps=simulator._core_iterator) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -352,9 +344,7 @@ def test_run_measurement_not_terminal_no_repetitions(dtype): def test_run_repetitions_measurement_not_terminal(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.DensityMatrixSimulator(dtype=dtype) - with mock.patch.object( - simulator, 'iterate_circuit', wraps=simulator.iterate_circuit - ) as mock_sim: + with mock.patch.object(simulator, '_core_iterator', wraps=simulator._core_iterator) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -375,9 +365,7 @@ def test_run_repetitions_measurement_not_terminal(dtype): def test_run_qudits_repetitions_measurement_not_terminal(dtype): q0, q1 = cirq.LineQid.for_qid_shape((2, 3)) simulator = cirq.DensityMatrixSimulator(dtype=dtype) - with mock.patch.object( - simulator, 'iterate_circuit', wraps=simulator.iterate_circuit - ) as mock_sim: + with mock.patch.object(simulator, '_core_iterator', wraps=simulator._core_iterator) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1, 2]: circuit = cirq.Circuit( @@ -1319,9 +1307,7 @@ def test_nonmeasuring_subcircuits_do_not_cause_sweep_repeat(): cirq.measure(q, key='x'), ) simulator = cirq.DensityMatrixSimulator() - with mock.patch.object( - simulator, 'iterate_circuit', wraps=simulator.iterate_circuit - ) as mock_sim: + with mock.patch.object(simulator, '_core_iterator', wraps=simulator._core_iterator) as mock_sim: simulator.run(circuit, repetitions=10) assert mock_sim.call_count == 2 @@ -1333,9 +1319,7 @@ def test_measuring_subcircuits_cause_sweep_repeat(): cirq.measure(q, key='x'), ) simulator = cirq.DensityMatrixSimulator() - with mock.patch.object( - simulator, 'iterate_circuit', wraps=simulator.iterate_circuit - ) as mock_sim: + with mock.patch.object(simulator, '_core_iterator', wraps=simulator._core_iterator) as mock_sim: simulator.run(circuit, repetitions=10) assert mock_sim.call_count == 11 diff --git a/cirq/sim/simulator.py b/cirq/sim/simulator.py index dbb1631db5c..850ef6af05e 100644 --- a/cirq/sim/simulator.py +++ b/cirq/sim/simulator.py @@ -525,11 +525,11 @@ def _base_iterator( Yields: StepResults from simulating a Moment of the Circuit. """ - acton_args = self.create_act_on_args(circuit, qubit_order, initial_state) - return self.iterate_circuit(circuit, qubit_order, acton_args) + acton_args = self._create_act_on_args(circuit, qubit_order, initial_state) + return self._core_iterator(circuit, qubit_order, acton_args) @abc.abstractmethod - def create_act_on_args( + def _create_act_on_args( self, circuit: circuits.Circuit, qubit_order: ops.QubitOrderOrList, @@ -551,7 +551,7 @@ def create_act_on_args( """ @abc.abstractmethod - def iterate_circuit( + def _core_iterator( self, circuit: circuits.Circuit, qubit_order: ops.QubitOrderOrList, diff --git a/cirq/sim/sparse_simulator.py b/cirq/sim/sparse_simulator.py index 868dff0d387..a277069e0f8 100644 --- a/cirq/sim/sparse_simulator.py +++ b/cirq/sim/sparse_simulator.py @@ -181,9 +181,9 @@ def _run( if protocols.has_unitary(self.noise) else (resolved_circuit[0:0], resolved_circuit) ) - acton_args = self.create_act_on_args(unitary_prefix, qubit_order, 0) + acton_args = self._create_act_on_args(unitary_prefix, qubit_order, 0) step_result = None - for step_result in self.iterate_circuit( + for step_result in self._core_iterator( circuit=unitary_prefix, qubit_order=qubit_order, sim_state=acton_args, @@ -219,7 +219,7 @@ def _brute_force_samples( measurements: DefaultDict[str, List[np.ndarray]] = collections.defaultdict(list) for _ in range(repetitions): - all_step_results = self.iterate_circuit( + all_step_results = self._core_iterator( circuit, sim_state=acton_args.copy(), qubit_order=qubit_order ) @@ -228,7 +228,7 @@ def _brute_force_samples( measurements[k].append(np.array(v, dtype=np.uint8)) return {k: np.array(v) for k, v in measurements.items()} - def create_act_on_args( + def _create_act_on_args( self, circuit: circuits.Circuit, qubit_order: ops.QubitOrderOrList, @@ -249,7 +249,7 @@ def create_act_on_args( log_of_measurement_results={}, ) - def iterate_circuit( + def _core_iterator( self, circuit: circuits.Circuit, qubit_order: ops.QubitOrderOrList, diff --git a/cirq/sim/sparse_simulator_test.py b/cirq/sim/sparse_simulator_test.py index 4be27de63bb..047e75f545a 100644 --- a/cirq/sim/sparse_simulator_test.py +++ b/cirq/sim/sparse_simulator_test.py @@ -89,9 +89,7 @@ def test_run_bit_flips(dtype): def test_run_measure_at_end_no_repetitions(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.Simulator(dtype=dtype) - with mock.patch.object( - simulator, 'iterate_circuit', wraps=simulator.iterate_circuit - ) as mock_sim: + with mock.patch.object(simulator, '_core_iterator', wraps=simulator._core_iterator) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -116,9 +114,7 @@ def test_run_repetitions_terminal_measurement_stochastic(): def test_run_repetitions_measure_at_end(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.Simulator(dtype=dtype) - with mock.patch.object( - simulator, 'iterate_circuit', wraps=simulator.iterate_circuit - ) as mock_sim: + with mock.patch.object(simulator, '_core_iterator', wraps=simulator._core_iterator) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -135,9 +131,7 @@ def test_run_repetitions_measure_at_end(dtype): def test_run_invert_mask_measure_not_terminal(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.Simulator(dtype=dtype) - with mock.patch.object( - simulator, 'iterate_circuit', wraps=simulator.iterate_circuit - ) as mock_sim: + with mock.patch.object(simulator, '_core_iterator', wraps=simulator._core_iterator) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -157,9 +151,7 @@ def test_run_invert_mask_measure_not_terminal(dtype): def test_run_partial_invert_mask_measure_not_terminal(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.Simulator(dtype=dtype) - with mock.patch.object( - simulator, 'iterate_circuit', wraps=simulator.iterate_circuit - ) as mock_sim: + with mock.patch.object(simulator, '_core_iterator', wraps=simulator._core_iterator) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -179,9 +171,7 @@ def test_run_partial_invert_mask_measure_not_terminal(dtype): def test_run_measurement_not_terminal_no_repetitions(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.Simulator(dtype=dtype) - with mock.patch.object( - simulator, 'iterate_circuit', wraps=simulator.iterate_circuit - ) as mock_sim: + with mock.patch.object(simulator, '_core_iterator', wraps=simulator._core_iterator) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( @@ -204,9 +194,7 @@ def test_run_measurement_not_terminal_no_repetitions(dtype): def test_run_repetitions_measurement_not_terminal(dtype): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.Simulator(dtype=dtype) - with mock.patch.object( - simulator, 'iterate_circuit', wraps=simulator.iterate_circuit - ) as mock_sim: + with mock.patch.object(simulator, '_core_iterator', wraps=simulator._core_iterator) as mock_sim: for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit( From ccc294ce130f9d3c4c6067bd11555548a1f44a48 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Thu, 25 Mar 2021 09:43:26 -0700 Subject: [PATCH 17/44] Fix docs --- cirq/contrib/quimb/mps_simulator.py | 19 ++++++++++++++--- cirq/sim/clifford/clifford_simulator.py | 20 ++++++++++++++---- cirq/sim/density_matrix_simulator.py | 28 +++++++++++++++++++++++++ cirq/sim/simulator.py | 6 +++--- cirq/sim/sparse_simulator.py | 26 +++++++++++++++++++++++ 5 files changed, 89 insertions(+), 10 deletions(-) diff --git a/cirq/contrib/quimb/mps_simulator.py b/cirq/contrib/quimb/mps_simulator.py index b6b86e94cba..485e74c33d6 100644 --- a/cirq/contrib/quimb/mps_simulator.py +++ b/cirq/contrib/quimb/mps_simulator.py @@ -90,7 +90,7 @@ def _create_act_on_args( qubit_order: ops.QubitOrderOrList, initial_state: int, ): - """Iterator over MPSSimulatorStepResult from Moments of a Circuit + """Creates MPSState args for simulating the Circuit. Args: circuit: The circuit to simulate. @@ -100,8 +100,8 @@ def _create_act_on_args( initial_state: The initial state for the simulation in the computational basis. Represented as a big endian int. - Yields: - MPSStepResult from simulating a Moment of the Circuit. + Returns: + MPSState args for simulating the Circuit. """ qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) @@ -121,6 +121,19 @@ def _core_iterator( qubit_order: ops.QubitOrderOrList, state: 'MPSState', ): + """Iterator over MPSSimulatorStepResult from Moments of a Circuit + + Args: + circuit: The circuit to simulate. + qubit_order: 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. + state: The initial state args for the simulation in the + computational basis. + + Yields: + MPSStepResult from simulating a Moment of the Circuit. + """ if len(circuit) == 0: yield MPSSimulatorStepResult(measurements=state.log_of_measurement_results, state=state) return diff --git a/cirq/sim/clifford/clifford_simulator.py b/cirq/sim/clifford/clifford_simulator.py index a10008524b0..bb278f461ec 100644 --- a/cirq/sim/clifford/clifford_simulator.py +++ b/cirq/sim/clifford/clifford_simulator.py @@ -74,7 +74,7 @@ def _create_act_on_args( qubit_order: ops.QubitOrderOrList, initial_state: int, ): - """Iterator over CliffordSimulatorStepResult from Moments of a Circuit + """Creates the ActOnStabilizerChFormArgs for a circuit. Args: circuit: The circuit to simulate. @@ -84,9 +84,8 @@ def _create_act_on_args( initial_state: The initial state for the simulation in the computational basis. Represented as a big endian int. - - Yields: - CliffordStepResult from simulating a Moment of the Circuit. + Returns: + ActOnStabilizerChFormArgs for the circuit. """ qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) @@ -106,6 +105,19 @@ def _core_iterator( qubit_order: ops.QubitOrderOrList, ch_form_args: clifford.ActOnStabilizerCHFormArgs, ): + """Iterator over CliffordSimulatorStepResult from Moments of a Circuit + + Args: + circuit: The circuit to simulate. + qubit_order: 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. + ch_form_args: The initial state args for the simulation in the + computational basis. + + Yields: + CliffordStepResult from simulating a Moment of the Circuit. + """ qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) qubit_map = {q: i for i, q in enumerate(qubits)} diff --git a/cirq/sim/density_matrix_simulator.py b/cirq/sim/density_matrix_simulator.py index d897cb00873..a06fb649a33 100644 --- a/cirq/sim/density_matrix_simulator.py +++ b/cirq/sim/density_matrix_simulator.py @@ -237,6 +237,19 @@ def _create_act_on_args( qubit_order: ops.QubitOrderOrList, initial_state: Union[np.ndarray, 'cirq.STATE_VECTOR_LIKE'], ): + """Creates the ActOnDensityMatrixArgs for a circuit. + + Args: + circuit: The circuit to simulate. + qubit_order: 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. + initial_state: The initial state for the simulation in the + computational basis. + + Returns: + ActOnDensityMatrixArgs for the circuit. + """ qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) qid_shape = protocols.qid_shape(qubits) initial_matrix = qis.to_valid_density_matrix( @@ -262,6 +275,21 @@ def _core_iterator( sim_state: act_on_density_matrix_args.ActOnDensityMatrixArgs, all_measurements_are_terminal: bool = False, ): + """Iterator over DensityMatrixStepResult from Moments of a Circuit + + Args: + circuit: The circuit to simulate. + qubit_order: 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 initial state args for the simulation in the + computational basis. + all_measurements_are_terminal: Indicator that all measurements + are terminal, allowing optimization. + + Yields: + DensityMatrixStepResult from simulating a Moment of the Circuit. + """ qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) qubit_map = {q: i for i, q in enumerate(qubits)} if len(circuit) == 0: diff --git a/cirq/sim/simulator.py b/cirq/sim/simulator.py index 850ef6af05e..b9b24dbb78d 100644 --- a/cirq/sim/simulator.py +++ b/cirq/sim/simulator.py @@ -535,7 +535,7 @@ def _create_act_on_args( qubit_order: ops.QubitOrderOrList, initial_state: Any, ) -> TActOnArgs: - """Creates the `act_on` state for a simulator. + """Creates the ActOnArgs state for a simulator. Args: circuit: The circuit to simulate. @@ -547,7 +547,7 @@ def _create_act_on_args( documentation of the implementing class for details. Returns: - `act_on_args` for the simulator. + ActOnArgs for the simulator. """ @abc.abstractmethod @@ -564,7 +564,7 @@ def _core_iterator( qubit_order: 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. - initial_state: The initial state for the simulation. The form of + initial_state: The initial args for the simulation. The form of this state depends on the simulation implementation. See documentation of the implementing class for details. diff --git a/cirq/sim/sparse_simulator.py b/cirq/sim/sparse_simulator.py index a277069e0f8..e087c6a6eba 100644 --- a/cirq/sim/sparse_simulator.py +++ b/cirq/sim/sparse_simulator.py @@ -234,6 +234,19 @@ def _create_act_on_args( qubit_order: ops.QubitOrderOrList, initial_state: 'cirq.STATE_VECTOR_LIKE', ): + """Creates the ActOnStateVectorArgs for a circuit. + + Args: + circuit: The circuit to simulate. + qubit_order: 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. + initial_state: The initial state for the simulation in the + computational basis. + + Returns: + ActOnStateVectorArgs for the circuit. + """ qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) num_qubits = len(qubits) qid_shape = protocols.qid_shape(qubits) @@ -255,6 +268,19 @@ def _core_iterator( qubit_order: ops.QubitOrderOrList, sim_state: act_on_state_vector_args.ActOnStateVectorArgs, ): + """Iterator over SparseSimulatorStep from Moments of a Circuit + + Args: + circuit: The circuit to simulate. + qubit_order: 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 initial state args for the simulation in the + computational basis. + + Yields: + SparseSimulatorStep from simulating a Moment of the Circuit. + """ qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) qubit_map = {q: i for i, q in enumerate(qubits)} if len(circuit) == 0: From 0edf5665a0acebeb147261bff4f017715f45aeab Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Thu, 25 Mar 2021 10:17:48 -0700 Subject: [PATCH 18/44] Update quantum teleportation --- examples/quantum_teleportation.py | 35 +++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/examples/quantum_teleportation.py b/examples/quantum_teleportation.py index 489f4a52020..29b19829ebf 100644 --- a/examples/quantum_teleportation.py +++ b/examples/quantum_teleportation.py @@ -37,10 +37,11 @@ import numpy as np import cirq +msg, alice, bob = cirq.LineQubit.range(3) + def make_quantum_teleportation_circuit(ranX, ranY): circuit = cirq.Circuit() - msg, alice, bob = cirq.LineQubit.range(3) # Creates Bell state to be shared between Alice and Bob. circuit.append([cirq.H(alice), cirq.CNOT(alice, bob)]) @@ -48,10 +49,21 @@ def make_quantum_teleportation_circuit(ranX, ranY): circuit.append([cirq.X(msg) ** ranX, cirq.Y(msg) ** ranY]) # Bell measurement of the Message and Alice's entangled qubit. circuit.append([cirq.CNOT(msg, alice), cirq.H(msg)]) - circuit.append(cirq.measure(msg, alice)) + circuit.append(cirq.measure(alice, key='a')) + circuit.append(cirq.measure(msg, key='m')) + return circuit + + +def make_final_quantum_teleportation_circuit(meas_alice, meas_msg): + circuit = cirq.Circuit() + circuit.append(cirq.identity_each(msg, alice, bob)) # Uses the two classical bits from the Bell measurement to recover the # original quantum Message on Bob's entangled qubit. - circuit.append([cirq.CNOT(alice, bob), cirq.CZ(msg, bob)]) + if meas_alice: + circuit.append(cirq.X(bob)) + + if meas_msg: + circuit.append(cirq.Z(bob)) return circuit @@ -73,13 +85,12 @@ def main(seed=None): sim = cirq.Simulator(seed=seed) + print("\nBloch Sphere of Message After Random X and Y Gates:") + # Prints the Bloch Sphere of the Message after the X and Y gates. # Run a simple simulation that applies the random X and Y gates that # create our message. q0 = cirq.LineQubit(0) message = sim.simulate(cirq.Circuit([cirq.X(q0) ** ranX, cirq.Y(q0) ** ranY])) - - print("\nBloch Sphere of Message After Random X and Y Gates:") - # Prints the Bloch Sphere of the Message after the X and Y gates. expected = cirq.bloch_vector_from_state_vector(message.final_state_vector, 0) print( "x: ", @@ -90,12 +101,18 @@ def main(seed=None): np.around(expected[2], 4), ) - # Records the final state of the simulation. - final_results = sim.simulate(circuit) + args = sim._create_act_on_args(circuit, cirq.QubitOrder.DEFAULT, 0) + result = list(sim._core_iterator(circuit, cirq.QubitOrder.DEFAULT, args)) + meas_alice = result[3].measurements['a'] == [1] + meas_msg = result[4].measurements['m'] == [1] + final_circuit = make_final_quantum_teleportation_circuit(meas_alice, meas_msg) + + final_result = list(sim._core_iterator(final_circuit, cirq.QubitOrder.DEFAULT, args)) + final_r = final_result[len(final_result) - 1] print("\nBloch Sphere of Qubit 2 at Final State:") # Prints the Bloch Sphere of Bob's entangled qubit at the final state. - teleported = cirq.bloch_vector_from_state_vector(final_results.final_state_vector, 2) + teleported = cirq.bloch_vector_from_state_vector(final_r.state_vector(), 2) print( "x: ", np.around(teleported[0], 4), From 31fc327fbef2922bdc3bceccb948491f1db1ce5f Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Thu, 25 Mar 2021 12:32:34 -0700 Subject: [PATCH 19/44] Update quantum teleportation --- examples/quantum_teleportation.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/examples/quantum_teleportation.py b/examples/quantum_teleportation.py index 29b19829ebf..99880a129c9 100644 --- a/examples/quantum_teleportation.py +++ b/examples/quantum_teleportation.py @@ -49,8 +49,8 @@ def make_quantum_teleportation_circuit(ranX, ranY): circuit.append([cirq.X(msg) ** ranX, cirq.Y(msg) ** ranY]) # Bell measurement of the Message and Alice's entangled qubit. circuit.append([cirq.CNOT(msg, alice), cirq.H(msg)]) - circuit.append(cirq.measure(alice, key='a')) - circuit.append(cirq.measure(msg, key='m')) + circuit.append(cirq.measure(alice, key='alice')) + circuit.append(cirq.measure(msg, key='msg')) return circuit @@ -85,13 +85,14 @@ def main(seed=None): sim = cirq.Simulator(seed=seed) - print("\nBloch Sphere of Message After Random X and Y Gates:") - # Prints the Bloch Sphere of the Message after the X and Y gates. # Run a simple simulation that applies the random X and Y gates that # create our message. q0 = cirq.LineQubit(0) message = sim.simulate(cirq.Circuit([cirq.X(q0) ** ranX, cirq.Y(q0) ** ranY])) expected = cirq.bloch_vector_from_state_vector(message.final_state_vector, 0) + + print("\nBloch Sphere of Message After Random X and Y Gates:") + # Prints the Bloch Sphere of the Message after the X and Y gates. print( "x: ", np.around(expected[0], 4), @@ -101,18 +102,19 @@ def main(seed=None): np.around(expected[2], 4), ) + # Run the initial part of the simulation (Alice) and measure. args = sim._create_act_on_args(circuit, cirq.QubitOrder.DEFAULT, 0) - result = list(sim._core_iterator(circuit, cirq.QubitOrder.DEFAULT, args)) - meas_alice = result[3].measurements['a'] == [1] - meas_msg = result[4].measurements['m'] == [1] - final_circuit = make_final_quantum_teleportation_circuit(meas_alice, meas_msg) + initial_results = list(sim._core_iterator(circuit, cirq.QubitOrder.DEFAULT, args)) + meas_alice = initial_results[3].measurements['alice'] == [1] + meas_msg = initial_results[4].measurements['msg'] == [1] - final_result = list(sim._core_iterator(final_circuit, cirq.QubitOrder.DEFAULT, args)) - final_r = final_result[len(final_result) - 1] + # Run the final part of the simulation (Bob). + final_circuit = make_final_quantum_teleportation_circuit(meas_alice, meas_msg) + *_, final_results = sim._core_iterator(final_circuit, cirq.QubitOrder.DEFAULT, args) print("\nBloch Sphere of Qubit 2 at Final State:") # Prints the Bloch Sphere of Bob's entangled qubit at the final state. - teleported = cirq.bloch_vector_from_state_vector(final_r.state_vector(), 2) + teleported = cirq.bloch_vector_from_state_vector(final_results.state_vector(), 2) print( "x: ", np.around(teleported[0], 4), From 838d10374bf783b9cf3eaf6e776373f68dd413e8 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Thu, 25 Mar 2021 12:33:59 -0700 Subject: [PATCH 20/44] Update quantum teleportation --- examples/quantum_teleportation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/quantum_teleportation.py b/examples/quantum_teleportation.py index 99880a129c9..548bd6d1722 100644 --- a/examples/quantum_teleportation.py +++ b/examples/quantum_teleportation.py @@ -89,10 +89,10 @@ def main(seed=None): # create our message. q0 = cirq.LineQubit(0) message = sim.simulate(cirq.Circuit([cirq.X(q0) ** ranX, cirq.Y(q0) ** ranY])) - expected = cirq.bloch_vector_from_state_vector(message.final_state_vector, 0) print("\nBloch Sphere of Message After Random X and Y Gates:") # Prints the Bloch Sphere of the Message after the X and Y gates. + expected = cirq.bloch_vector_from_state_vector(message.final_state_vector, 0) print( "x: ", np.around(expected[0], 4), From f664e9d514fd9e3935aca31b95dd74247aa01c00 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Thu, 25 Mar 2021 12:42:50 -0700 Subject: [PATCH 21/44] Make the QubitOrder arg optional --- cirq/contrib/quimb/mps_simulator.py | 12 ++++++------ cirq/sim/clifford/clifford_simulator.py | 12 ++++++------ cirq/sim/density_matrix_simulator.py | 20 ++++++++++---------- cirq/sim/simulator.py | 20 ++++++++++---------- cirq/sim/sparse_simulator.py | 16 ++++++++-------- examples/quantum_teleportation.py | 6 +++--- 6 files changed, 43 insertions(+), 43 deletions(-) diff --git a/cirq/contrib/quimb/mps_simulator.py b/cirq/contrib/quimb/mps_simulator.py index 485e74c33d6..d9de997ffe6 100644 --- a/cirq/contrib/quimb/mps_simulator.py +++ b/cirq/contrib/quimb/mps_simulator.py @@ -87,18 +87,18 @@ def __init__( def _create_act_on_args( self, circuit: circuits.Circuit, - qubit_order: ops.QubitOrderOrList, initial_state: int, + qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, ): """Creates MPSState args for simulating the Circuit. Args: circuit: The circuit to simulate. + initial_state: The initial state for the simulation in the + computational basis. Represented as a big endian int. qubit_order: 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. - initial_state: The initial state for the simulation in the - computational basis. Represented as a big endian int. Returns: MPSState args for simulating the Circuit. @@ -118,18 +118,18 @@ def _create_act_on_args( def _core_iterator( self, circuit: circuits.Circuit, - qubit_order: ops.QubitOrderOrList, state: 'MPSState', + qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, ): """Iterator over MPSSimulatorStepResult from Moments of a Circuit Args: circuit: The circuit to simulate. + state: The initial state args for the simulation in the + computational basis. qubit_order: 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. - state: The initial state args for the simulation in the - computational basis. Yields: MPSStepResult from simulating a Moment of the Circuit. diff --git a/cirq/sim/clifford/clifford_simulator.py b/cirq/sim/clifford/clifford_simulator.py index bb278f461ec..31416cc6e07 100644 --- a/cirq/sim/clifford/clifford_simulator.py +++ b/cirq/sim/clifford/clifford_simulator.py @@ -71,18 +71,18 @@ def is_supported_operation(op: 'cirq.Operation') -> bool: def _create_act_on_args( self, circuit: circuits.Circuit, - qubit_order: ops.QubitOrderOrList, initial_state: int, + qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, ): """Creates the ActOnStabilizerChFormArgs for a circuit. Args: circuit: The circuit to simulate. + initial_state: The initial state for the simulation in the + computational basis. Represented as a big endian int. qubit_order: 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. - initial_state: The initial state for the simulation in the - computational basis. Represented as a big endian int. Returns: ActOnStabilizerChFormArgs for the circuit. @@ -102,18 +102,18 @@ def _create_act_on_args( def _core_iterator( self, circuit: circuits.Circuit, - qubit_order: ops.QubitOrderOrList, ch_form_args: clifford.ActOnStabilizerCHFormArgs, + qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, ): """Iterator over CliffordSimulatorStepResult from Moments of a Circuit Args: circuit: The circuit to simulate. + ch_form_args: The initial state args for the simulation in the + computational basis. qubit_order: 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. - ch_form_args: The initial state args for the simulation in the - computational basis. Yields: CliffordStepResult from simulating a Moment of the Circuit. diff --git a/cirq/sim/density_matrix_simulator.py b/cirq/sim/density_matrix_simulator.py index a06fb649a33..b2d10b2999b 100644 --- a/cirq/sim/density_matrix_simulator.py +++ b/cirq/sim/density_matrix_simulator.py @@ -174,12 +174,12 @@ def _run( prefix, general_suffix = split_into_matching_protocol_then_general( resolved_circuit, lambda op: not protocols.is_measurement(op) ) - acton_args = self._create_act_on_args(prefix, qubit_order, 0) + acton_args = self._create_act_on_args(prefix, 0, qubit_order) step_result = None for step_result in self._core_iterator( circuit=prefix, - qubit_order=qubit_order, sim_state=acton_args, + qubit_order=qubit_order, ): pass assert step_result is not None @@ -199,8 +199,8 @@ def _run_sweep_sample( ) -> Dict[str, np.ndarray]: for step_result in self._core_iterator( circuit=circuit, - qubit_order=qubit_order, sim_state=acton_args, + qubit_order=qubit_order, all_measurements_are_terminal=True, ): pass @@ -221,8 +221,8 @@ def _run_sweep_repeat( for _ in range(repetitions): all_step_results = self._core_iterator( circuit, - qubit_order=qubit_order, sim_state=acton_args.copy(), + qubit_order=qubit_order, ) for step_result in all_step_results: for k, v in step_result.measurements.items(): @@ -234,18 +234,18 @@ def _run_sweep_repeat( def _create_act_on_args( self, circuit: circuits.Circuit, - qubit_order: ops.QubitOrderOrList, initial_state: Union[np.ndarray, 'cirq.STATE_VECTOR_LIKE'], + qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, ): """Creates the ActOnDensityMatrixArgs for a circuit. Args: circuit: The circuit to simulate. + initial_state: The initial state for the simulation in the + computational basis. qubit_order: 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. - initial_state: The initial state for the simulation in the - computational basis. Returns: ActOnDensityMatrixArgs for the circuit. @@ -271,19 +271,19 @@ def _create_act_on_args( def _core_iterator( self, circuit: circuits.Circuit, - qubit_order: ops.QubitOrderOrList, sim_state: act_on_density_matrix_args.ActOnDensityMatrixArgs, + qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, all_measurements_are_terminal: bool = False, ): """Iterator over DensityMatrixStepResult from Moments of a Circuit Args: circuit: The circuit to simulate. + sim_state: The initial state args for the simulation in the + computational basis. qubit_order: 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 initial state args for the simulation in the - computational basis. all_measurements_are_terminal: Indicator that all measurements are terminal, allowing optimization. diff --git a/cirq/sim/simulator.py b/cirq/sim/simulator.py index b9b24dbb78d..16075164594 100644 --- a/cirq/sim/simulator.py +++ b/cirq/sim/simulator.py @@ -525,26 +525,26 @@ def _base_iterator( Yields: StepResults from simulating a Moment of the Circuit. """ - acton_args = self._create_act_on_args(circuit, qubit_order, initial_state) - return self._core_iterator(circuit, qubit_order, acton_args) + acton_args = self._create_act_on_args(circuit, initial_state, qubit_order) + return self._core_iterator(circuit, acton_args, qubit_order) @abc.abstractmethod def _create_act_on_args( self, circuit: circuits.Circuit, - qubit_order: ops.QubitOrderOrList, initial_state: Any, + qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, ) -> TActOnArgs: """Creates the ActOnArgs state for a simulator. Args: circuit: The circuit to simulate. - qubit_order: 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. initial_state: The initial state for the simulation. The form of this state depends on the simulation implementation. See documentation of the implementing class for details. + qubit_order: 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. Returns: ActOnArgs for the simulator. @@ -554,19 +554,19 @@ def _create_act_on_args( def _core_iterator( self, circuit: circuits.Circuit, - qubit_order: ops.QubitOrderOrList, initial_state: TActOnArgs, + qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, ) -> Iterator[TStepResult]: """Iterator over StepResult from Moments of a Circuit. Args: circuit: The circuit to simulate. - qubit_order: 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. initial_state: The initial args for the simulation. The form of this state depends on the simulation implementation. See documentation of the implementing class for details. + qubit_order: 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. Yields: StepResults from simulating a Moment of the Circuit. diff --git a/cirq/sim/sparse_simulator.py b/cirq/sim/sparse_simulator.py index e087c6a6eba..680dbdf09d0 100644 --- a/cirq/sim/sparse_simulator.py +++ b/cirq/sim/sparse_simulator.py @@ -181,12 +181,12 @@ def _run( if protocols.has_unitary(self.noise) else (resolved_circuit[0:0], resolved_circuit) ) - acton_args = self._create_act_on_args(unitary_prefix, qubit_order, 0) + acton_args = self._create_act_on_args(unitary_prefix, 0, qubit_order) step_result = None for step_result in self._core_iterator( circuit=unitary_prefix, - qubit_order=qubit_order, sim_state=acton_args, + qubit_order=qubit_order, ): pass assert step_result is not None @@ -231,18 +231,18 @@ def _brute_force_samples( def _create_act_on_args( self, circuit: circuits.Circuit, - qubit_order: ops.QubitOrderOrList, initial_state: 'cirq.STATE_VECTOR_LIKE', + qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, ): """Creates the ActOnStateVectorArgs for a circuit. Args: circuit: The circuit to simulate. + initial_state: The initial state for the simulation in the + computational basis. qubit_order: 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. - initial_state: The initial state for the simulation in the - computational basis. Returns: ActOnStateVectorArgs for the circuit. @@ -265,18 +265,18 @@ def _create_act_on_args( def _core_iterator( self, circuit: circuits.Circuit, - qubit_order: ops.QubitOrderOrList, sim_state: act_on_state_vector_args.ActOnStateVectorArgs, + qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, ): """Iterator over SparseSimulatorStep from Moments of a Circuit Args: circuit: The circuit to simulate. + sim_state: The initial state args for the simulation in the + computational basis. qubit_order: 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 initial state args for the simulation in the - computational basis. Yields: SparseSimulatorStep from simulating a Moment of the Circuit. diff --git a/examples/quantum_teleportation.py b/examples/quantum_teleportation.py index 548bd6d1722..242bb9934a5 100644 --- a/examples/quantum_teleportation.py +++ b/examples/quantum_teleportation.py @@ -103,14 +103,14 @@ def main(seed=None): ) # Run the initial part of the simulation (Alice) and measure. - args = sim._create_act_on_args(circuit, cirq.QubitOrder.DEFAULT, 0) - initial_results = list(sim._core_iterator(circuit, cirq.QubitOrder.DEFAULT, args)) + args = sim._create_act_on_args(circuit, 0) + initial_results = list(sim._core_iterator(circuit, args)) meas_alice = initial_results[3].measurements['alice'] == [1] meas_msg = initial_results[4].measurements['msg'] == [1] # Run the final part of the simulation (Bob). final_circuit = make_final_quantum_teleportation_circuit(meas_alice, meas_msg) - *_, final_results = sim._core_iterator(final_circuit, cirq.QubitOrder.DEFAULT, args) + *_, final_results = sim._core_iterator(final_circuit, args) print("\nBloch Sphere of Qubit 2 at Final State:") # Prints the Bloch Sphere of Bob's entangled qubit at the final state. From 5a35ca995442a0f8f9bf47785500eafa7365bd01 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Thu, 25 Mar 2021 12:47:28 -0700 Subject: [PATCH 22/44] Fix the engine_simulator.py overrides --- cirq/google/calibration/engine_simulator.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/cirq/google/calibration/engine_simulator.py b/cirq/google/calibration/engine_simulator.py index 5f0d32fca88..bfc8ff8e8c4 100644 --- a/cirq/google/calibration/engine_simulator.py +++ b/cirq/google/calibration/engine_simulator.py @@ -395,14 +395,23 @@ def _run( converted = _convert_to_circuit_with_drift(self, circuit) return self._simulator._run(converted, param_resolver, repetitions) - def _base_iterator( + def _core_iterator( self, circuit: Circuit, + initial_state: Any, qubit_order: QubitOrderOrList, + ) -> Iterator[StateVectorStepResult]: + converted = _convert_to_circuit_with_drift(self, circuit) + return self._simulator._core_iterator(converted, initial_state, qubit_order) + + def _create_act_on_args( + self, + circuit: Circuit, initial_state: Any, + qubit_order: QubitOrderOrList, ) -> Iterator[StateVectorStepResult]: converted = _convert_to_circuit_with_drift(self, circuit) - return self._simulator._base_iterator(converted, qubit_order, initial_state) + return self._simulator._create_act_on_args(converted, initial_state, qubit_order) class _PhasedFSimConverter(PointOptimizer): From 2d0ee42d9d84d949d1ecd24296653622070c562c Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Thu, 25 Mar 2021 12:53:40 -0700 Subject: [PATCH 23/44] Fix the engine_simulator.py overrides --- cirq/google/calibration/engine_simulator.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cirq/google/calibration/engine_simulator.py b/cirq/google/calibration/engine_simulator.py index bfc8ff8e8c4..94ef6f2c322 100644 --- a/cirq/google/calibration/engine_simulator.py +++ b/cirq/google/calibration/engine_simulator.py @@ -21,6 +21,7 @@ Operation, PhasedFSimGate, Qid, + QubitOrder, QubitOrderOrList, SingleQubitGate, WaitGate, @@ -399,8 +400,8 @@ def _core_iterator( self, circuit: Circuit, initial_state: Any, - qubit_order: QubitOrderOrList, - ) -> Iterator[StateVectorStepResult]: + qubit_order: QubitOrderOrList = QubitOrder.DEFAULT, + ): converted = _convert_to_circuit_with_drift(self, circuit) return self._simulator._core_iterator(converted, initial_state, qubit_order) @@ -408,8 +409,8 @@ def _create_act_on_args( self, circuit: Circuit, initial_state: Any, - qubit_order: QubitOrderOrList, - ) -> Iterator[StateVectorStepResult]: + qubit_order: QubitOrderOrList = QubitOrder.DEFAULT, + ): converted = _convert_to_circuit_with_drift(self, circuit) return self._simulator._create_act_on_args(converted, initial_state, qubit_order) From 22b3b92ba55aa4fa97f21929491fe8a5f91d56ef Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Thu, 25 Mar 2021 12:58:14 -0700 Subject: [PATCH 24/44] Fix the engine_simulator.py overrides --- cirq/google/calibration/engine_simulator.py | 24 +++++++++------------ 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/cirq/google/calibration/engine_simulator.py b/cirq/google/calibration/engine_simulator.py index 94ef6f2c322..af0e75ae916 100644 --- a/cirq/google/calibration/engine_simulator.py +++ b/cirq/google/calibration/engine_simulator.py @@ -3,7 +3,6 @@ Callable, Dict, Iterable, - Iterator, List, Optional, Sequence, @@ -14,6 +13,16 @@ import numpy as np from cirq.circuits import Circuit, PointOptimizer, PointOptimizationSummary +from cirq.google.calibration.phased_fsim import ( + FloquetPhasedFSimCalibrationRequest, + PhaseCalibratedFSimGate, + IncompatibleMomentError, + PhasedFSimCalibrationRequest, + PhasedFSimCalibrationResult, + PhasedFSimCharacterization, + SQRT_ISWAP_PARAMETERS, + try_convert_sqrt_iswap_to_fsim, +) from cirq.ops import ( FSimGate, Gate, @@ -30,23 +39,10 @@ Simulator, SimulatesSamples, SimulatesIntermediateStateVector, - StateVectorStepResult, ) from cirq.study import ParamResolver from cirq.value import RANDOM_STATE_OR_SEED_LIKE, parse_random_state -from cirq.google.calibration.phased_fsim import ( - FloquetPhasedFSimCalibrationRequest, - PhaseCalibratedFSimGate, - IncompatibleMomentError, - PhasedFSimCalibrationRequest, - PhasedFSimCalibrationResult, - PhasedFSimCharacterization, - SQRT_ISWAP_PARAMETERS, - try_convert_sqrt_iswap_to_fsim, -) - - ParametersDriftGenerator = Callable[[Qid, Qid, FSimGate], PhasedFSimCharacterization] PhasedFsimDictParameters = Dict[ Tuple[Qid, Qid], Union[Dict[str, float], PhasedFSimCharacterization] From 5b98a6bc12f826d2b2359d4b7755c31a732007f0 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Thu, 25 Mar 2021 13:30:26 -0700 Subject: [PATCH 25/44] Fix coverage check --- .../act_on_clifford_tableau_args_test.py | 19 +++++++++++++++++++ .../act_on_stabilizer_ch_form_args_test.py | 19 +++++++++++++++++++ examples/quantum_teleportation.py | 4 ++-- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/cirq/sim/clifford/act_on_clifford_tableau_args_test.py b/cirq/sim/clifford/act_on_clifford_tableau_args_test.py index 9fd16de6918..950e0fcaf27 100644 --- a/cirq/sim/clifford/act_on_clifford_tableau_args_test.py +++ b/cirq/sim/clifford/act_on_clifford_tableau_args_test.py @@ -82,3 +82,22 @@ class NoDetailsSingleQubitGate(cirq.SingleQubitGate): with pytest.raises(TypeError, match="Failed to act"): cirq.act_on(NoDetailsSingleQubitGate(), args) + + +def test_copy(): + args = cirq.ActOnCliffordTableauArgs( + tableau=cirq.CliffordTableau(num_qubits=3), + axes=[1], + prng=np.random.RandomState(), + log_of_measurement_results={}, + ) + args1 = args.copy() + assert isinstance(args1, cirq.ActOnCliffordTableauArgs) + assert args is not args1 + assert args.tableau is not args1.tableau + assert args.tableau == args1.tableau + assert args.axes is not args1.axes + assert args.axes == args1.axes + assert args.prng is args1.prng + assert args.log_of_measurement_results is not args1.log_of_measurement_results + assert args.log_of_measurement_results == args.log_of_measurement_results diff --git a/cirq/sim/clifford/act_on_stabilizer_ch_form_args_test.py b/cirq/sim/clifford/act_on_stabilizer_ch_form_args_test.py index 8f3963cd4ce..7e5e9230a7d 100644 --- a/cirq/sim/clifford/act_on_stabilizer_ch_form_args_test.py +++ b/cirq/sim/clifford/act_on_stabilizer_ch_form_args_test.py @@ -106,3 +106,22 @@ def _unitary_(self): ) cirq.act_on(cirq.H, expected_args) np.testing.assert_allclose(args.state.state_vector(), expected_args.state.state_vector()) + + +def test_copy(): + args = cirq.ActOnStabilizerCHFormArgs( + state=cirq.StabilizerStateChForm(num_qubits=3), + axes=[1], + prng=np.random.RandomState(), + log_of_measurement_results={}, + ) + args1 = args.copy() + assert isinstance(args1, cirq.ActOnStabilizerCHFormArgs) + assert args is not args1 + assert args.state is not args1.state + np.testing.assert_equal(args.state.state_vector(), args1.state.state_vector()) + assert args.axes is not args1.axes + assert args.axes == args1.axes + assert args.prng is args1.prng + assert args.log_of_measurement_results is not args1.log_of_measurement_results + assert args.log_of_measurement_results == args.log_of_measurement_results diff --git a/examples/quantum_teleportation.py b/examples/quantum_teleportation.py index 242bb9934a5..9440a4c4c43 100644 --- a/examples/quantum_teleportation.py +++ b/examples/quantum_teleportation.py @@ -60,10 +60,10 @@ def make_final_quantum_teleportation_circuit(meas_alice, meas_msg): # Uses the two classical bits from the Bell measurement to recover the # original quantum Message on Bob's entangled qubit. if meas_alice: - circuit.append(cirq.X(bob)) + circuit.append(cirq.X(bob)) # coverage: ignore if meas_msg: - circuit.append(cirq.Z(bob)) + circuit.append(cirq.Z(bob)) # coverage: ignore return circuit From f970a38a5331c6915befa38120bd98c777f45e54 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Sat, 27 Mar 2021 19:40:55 -0700 Subject: [PATCH 26/44] Change quantum teleportation example to be generic across simulators. --- examples/examples_test.py | 4 +- examples/quantum_teleportation.py | 156 +++++++++++++++++------------- 2 files changed, 92 insertions(+), 68 deletions(-) diff --git a/examples/examples_test.py b/examples/examples_test.py index ccf49d1364c..bc0cf7f26aa 100644 --- a/examples/examples_test.py +++ b/examples/examples_test.py @@ -101,8 +101,8 @@ def test_example_runs_qaoa(): def test_example_runs_quantum_teleportation(): - _, teleported = examples.quantum_teleportation.main(seed=12) - assert np.allclose(np.array([0.07023552, -0.9968105, -0.03788921]), teleported) + exptected, teleported = examples.quantum_teleportation.main(seed=12) + assert np.allclose(np.array([-0.21627635, -0.86642754, -0.45003086]), teleported) def test_example_runs_superdense_coding(): diff --git a/examples/quantum_teleportation.py b/examples/quantum_teleportation.py index 9440a4c4c43..24bb064f130 100644 --- a/examples/quantum_teleportation.py +++ b/examples/quantum_teleportation.py @@ -34,38 +34,88 @@ """ import random +from typing import Tuple + import numpy as np import cirq +from cirq.sim.simulator import ( + TStepResult, + SimulatesIntermediateState, + TSimulationTrialResult, + TSimulatorState, + TActOnArgs, +) -msg, alice, bob = cirq.LineQubit.range(3) - - -def make_quantum_teleportation_circuit(ranX, ranY): - circuit = cirq.Circuit() - # Creates Bell state to be shared between Alice and Bob. - circuit.append([cirq.H(alice), cirq.CNOT(alice, bob)]) - # Creates a random state for the Message. - circuit.append([cirq.X(msg) ** ranX, cirq.Y(msg) ** ranY]) - # Bell measurement of the Message and Alice's entangled qubit. - circuit.append([cirq.CNOT(msg, alice), cirq.H(msg)]) - circuit.append(cirq.measure(alice, key='alice')) - circuit.append(cirq.measure(msg, key='msg')) - return circuit +def _print_bloch(vector): + print( + 'x: ', + np.around(vector[0], 4), + 'y: ', + np.around(vector[1], 4), + 'z: ', + np.around(vector[2], 4), + ) -def make_final_quantum_teleportation_circuit(meas_alice, meas_msg): - circuit = cirq.Circuit() - circuit.append(cirq.identity_each(msg, alice, bob)) - # Uses the two classical bits from the Bell measurement to recover the - # original quantum Message on Bob's entangled qubit. +def _run( + sim: SimulatesIntermediateState[ + TStepResult, TSimulationTrialResult, TSimulatorState, TActOnArgs + ] +) -> Tuple[TSimulationTrialResult, TStepResult]: + # Initialize our qubit state space. + msg, alice, bob = qubits = cirq.LineQubit.range(3) + qubit_order = cirq.QubitOrder.as_qubit_order(qubits) + + # First we create a bell state circuit and simulate it on the qubits. + bell_circuit = cirq.Circuit(cirq.H(alice), cirq.CNOT(alice, bob)) + args = sim._create_act_on_args(bell_circuit, 0, qubit_order) + list(sim._core_iterator(bell_circuit, args, qubit_order)) + print('\nBell Circuit:') + print(bell_circuit) + + # Second we randomize the message qubit. + rand_x = random.random() + rand_y = random.random() + msg_circuit = cirq.Circuit( + cirq.X(msg) ** rand_x, + cirq.Y(msg) ** rand_y, + ) + list(sim._core_iterator(msg_circuit, args, qubit_order)) + print('\nMessage Circuit:') + print(msg_circuit) + + # Now we measure on Alice's side + alice_circuit = cirq.Circuit( + cirq.CNOT(msg, alice), + cirq.H(msg), + cirq.measure(alice, key='alice'), + cirq.measure(msg, key='msg'), + ) + alice_results = list(sim._core_iterator(alice_circuit, args, qubit_order)) + meas_alice = alice_results[1].measurements['alice'] == [1] + meas_msg = alice_results[2].measurements['msg'] == [1] + print('\nAlice Circuit:') + print(alice_circuit) + print(f'meas_alice={meas_alice}') + print(f'meas_msg={meas_msg}') + + # Finally we construct Bob's circuit based on Alice's measurements + bob_circuit = cirq.Circuit() if meas_alice: - circuit.append(cirq.X(bob)) # coverage: ignore + bob_circuit.append(cirq.X(bob)) # coverage: ignore if meas_msg: - circuit.append(cirq.Z(bob)) # coverage: ignore + bob_circuit.append(cirq.Z(bob)) # coverage: ignore + + *_, final_results = sim._core_iterator(bob_circuit, args, qubit_order) + print('\nBob Circuit:') + print(bob_circuit) - return circuit + # We simulate our message circuit separately for comparison + message = sim.simulate(msg_circuit) + + return message, final_results def main(seed=None): @@ -76,53 +126,27 @@ def main(seed=None): """ random.seed(seed) - ranX = random.random() - ranY = random.random() - circuit = make_quantum_teleportation_circuit(ranX, ranY) - - print("Circuit:") - print(circuit) - + # Run with density matrix simulator + print('***Run with density matrix simulator***') + sim = cirq.DensityMatrixSimulator(seed=seed) + message, final_results = _run(sim) + print('\nBloch Sphere of Message After Random X and Y Gates:') + expected = cirq.bloch_vector_from_state_vector(message.final_density_matrix, 0) + _print_bloch(expected) + print('\nBloch Sphere of Qubit 2 at Final State:') + teleported = cirq.bloch_vector_from_state_vector(final_results._density_matrix, 2) + _print_bloch(teleported) + + # Run with sparse simulator + print('\n\n\n\n\n***Run with sparse simulator***') sim = cirq.Simulator(seed=seed) - - # Run a simple simulation that applies the random X and Y gates that - # create our message. - q0 = cirq.LineQubit(0) - message = sim.simulate(cirq.Circuit([cirq.X(q0) ** ranX, cirq.Y(q0) ** ranY])) - - print("\nBloch Sphere of Message After Random X and Y Gates:") - # Prints the Bloch Sphere of the Message after the X and Y gates. + message, final_results = _run(sim) + print('\nBloch Sphere of Message After Random X and Y Gates:') expected = cirq.bloch_vector_from_state_vector(message.final_state_vector, 0) - print( - "x: ", - np.around(expected[0], 4), - "y: ", - np.around(expected[1], 4), - "z: ", - np.around(expected[2], 4), - ) - - # Run the initial part of the simulation (Alice) and measure. - args = sim._create_act_on_args(circuit, 0) - initial_results = list(sim._core_iterator(circuit, args)) - meas_alice = initial_results[3].measurements['alice'] == [1] - meas_msg = initial_results[4].measurements['msg'] == [1] - - # Run the final part of the simulation (Bob). - final_circuit = make_final_quantum_teleportation_circuit(meas_alice, meas_msg) - *_, final_results = sim._core_iterator(final_circuit, args) - - print("\nBloch Sphere of Qubit 2 at Final State:") - # Prints the Bloch Sphere of Bob's entangled qubit at the final state. + _print_bloch(expected) + print('\nBloch Sphere of Qubit 2 at Final State:') teleported = cirq.bloch_vector_from_state_vector(final_results.state_vector(), 2) - print( - "x: ", - np.around(teleported[0], 4), - "y: ", - np.around(teleported[1], 4), - "z: ", - np.around(teleported[2], 4), - ) + _print_bloch(teleported) return expected, teleported From 1fd8e8c609ec595466b8fbc2d3b57900d44a7232 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Sat, 27 Mar 2021 19:46:46 -0700 Subject: [PATCH 27/44] Change quantum teleportation example to be generic across simulators. --- examples/examples_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/examples_test.py b/examples/examples_test.py index bc0cf7f26aa..4c726b5e124 100644 --- a/examples/examples_test.py +++ b/examples/examples_test.py @@ -101,7 +101,7 @@ def test_example_runs_qaoa(): def test_example_runs_quantum_teleportation(): - exptected, teleported = examples.quantum_teleportation.main(seed=12) + _, teleported = examples.quantum_teleportation.main(seed=12) assert np.allclose(np.array([-0.21627635, -0.86642754, -0.45003086]), teleported) From 45cfdbaaacc28b439b5b266a6c793b04cc3b0639 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Sun, 28 Mar 2021 16:43:14 -0700 Subject: [PATCH 28/44] Change qubit_order to an explicit ordering of qubits. --- cirq/contrib/quimb/mps_simulator.py | 14 ++++------ cirq/google/calibration/engine_simulator.py | 14 ++++------ cirq/sim/clifford/clifford_simulator.py | 15 ++++------- cirq/sim/density_matrix_simulator.py | 30 +++++++++------------ cirq/sim/simulator.py | 15 +++++------ cirq/sim/sparse_simulator.py | 25 ++++++++--------- examples/quantum_teleportation.py | 14 +++++----- 7 files changed, 53 insertions(+), 74 deletions(-) diff --git a/cirq/contrib/quimb/mps_simulator.py b/cirq/contrib/quimb/mps_simulator.py index d9de997ffe6..f70e118a949 100644 --- a/cirq/contrib/quimb/mps_simulator.py +++ b/cirq/contrib/quimb/mps_simulator.py @@ -19,7 +19,7 @@ import dataclasses import math -from typing import Any, Dict, List, Optional, Sequence, Set, TYPE_CHECKING, Iterable +from typing import Any, Dict, List, Optional, Sequence, Set, TYPE_CHECKING, Iterable, Tuple import numpy as np import quimb.tensor as qtn @@ -86,25 +86,21 @@ def __init__( def _create_act_on_args( self, - circuit: circuits.Circuit, initial_state: int, - qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, + qubits: Tuple['cirq.Qid', ...], ): """Creates MPSState args for simulating the Circuit. Args: - circuit: The circuit to simulate. initial_state: The initial state for the simulation in the computational basis. Represented as a big endian int. - qubit_order: Determines the canonical ordering of the qubits. This + qubits: 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. Returns: MPSState args for simulating the Circuit. """ - qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) - qubit_map = {q: i for i, q in enumerate(qubits)} return MPSState( @@ -119,7 +115,7 @@ def _core_iterator( self, circuit: circuits.Circuit, state: 'MPSState', - qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, + qubits: Tuple['cirq.Qid', ...], ): """Iterator over MPSSimulatorStepResult from Moments of a Circuit @@ -127,7 +123,7 @@ def _core_iterator( circuit: The circuit to simulate. state: The initial state args for the simulation in the computational basis. - qubit_order: Determines the canonical ordering of the qubits. This + qubits: 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. diff --git a/cirq/google/calibration/engine_simulator.py b/cirq/google/calibration/engine_simulator.py index af0e75ae916..f7aeb7ad83c 100644 --- a/cirq/google/calibration/engine_simulator.py +++ b/cirq/google/calibration/engine_simulator.py @@ -30,8 +30,6 @@ Operation, PhasedFSimGate, Qid, - QubitOrder, - QubitOrderOrList, SingleQubitGate, WaitGate, ) @@ -396,19 +394,17 @@ def _core_iterator( self, circuit: Circuit, initial_state: Any, - qubit_order: QubitOrderOrList = QubitOrder.DEFAULT, + qubits: Tuple[Qid, ...], ): converted = _convert_to_circuit_with_drift(self, circuit) - return self._simulator._core_iterator(converted, initial_state, qubit_order) + return self._simulator._core_iterator(converted, initial_state, qubits) def _create_act_on_args( self, - circuit: Circuit, - initial_state: Any, - qubit_order: QubitOrderOrList = QubitOrder.DEFAULT, + initial_state: int, + qubits: Tuple[Qid, ...], ): - converted = _convert_to_circuit_with_drift(self, circuit) - return self._simulator._create_act_on_args(converted, initial_state, qubit_order) + return self._simulator._create_act_on_args(initial_state, qubits) class _PhasedFSimConverter(PointOptimizer): diff --git a/cirq/sim/clifford/clifford_simulator.py b/cirq/sim/clifford/clifford_simulator.py index 31416cc6e07..3fb89cd53a3 100644 --- a/cirq/sim/clifford/clifford_simulator.py +++ b/cirq/sim/clifford/clifford_simulator.py @@ -29,7 +29,7 @@ to state vector amplitudes. """ -from typing import Any, Dict, List, Sequence +from typing import Any, Dict, List, Sequence, Tuple import numpy as np @@ -70,25 +70,21 @@ def is_supported_operation(op: 'cirq.Operation') -> bool: def _create_act_on_args( self, - circuit: circuits.Circuit, initial_state: int, - qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, + qubits: Tuple['cirq.Qid', ...], ): """Creates the ActOnStabilizerChFormArgs for a circuit. Args: - circuit: The circuit to simulate. initial_state: The initial state for the simulation in the computational basis. Represented as a big endian int. - qubit_order: Determines the canonical ordering of the qubits. This + qubits: 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. Returns: ActOnStabilizerChFormArgs for the circuit. """ - qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) - qubit_map = {q: i for i, q in enumerate(qubits)} state = CliffordState(qubit_map, initial_state=initial_state) @@ -103,7 +99,7 @@ def _core_iterator( self, circuit: circuits.Circuit, ch_form_args: clifford.ActOnStabilizerCHFormArgs, - qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, + qubits: Tuple['cirq.Qid', ...], ): """Iterator over CliffordSimulatorStepResult from Moments of a Circuit @@ -111,14 +107,13 @@ def _core_iterator( circuit: The circuit to simulate. ch_form_args: The initial state args for the simulation in the computational basis. - qubit_order: Determines the canonical ordering of the qubits. This + qubits: 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. Yields: CliffordStepResult from simulating a Moment of the Circuit. """ - qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) qubit_map = {q: i for i, q in enumerate(qubits)} def create_state(): diff --git a/cirq/sim/density_matrix_simulator.py b/cirq/sim/density_matrix_simulator.py index b2d10b2999b..c37d9ca8efc 100644 --- a/cirq/sim/density_matrix_simulator.py +++ b/cirq/sim/density_matrix_simulator.py @@ -169,17 +169,17 @@ def _run( param_resolver = param_resolver or study.ParamResolver({}) resolved_circuit = protocols.resolve_parameters(circuit, param_resolver) check_all_resolved(resolved_circuit) - qubit_order = sorted(resolved_circuit.all_qubits()) + qubits = tuple(sorted(resolved_circuit.all_qubits())) + acton_args = self._create_act_on_args(0, qubits) prefix, general_suffix = split_into_matching_protocol_then_general( resolved_circuit, lambda op: not protocols.is_measurement(op) ) - acton_args = self._create_act_on_args(prefix, 0, qubit_order) step_result = None for step_result in self._core_iterator( circuit=prefix, sim_state=acton_args, - qubit_order=qubit_order, + qubits=qubits, ): pass assert step_result is not None @@ -187,20 +187,20 @@ def _run( if general_suffix.are_all_measurements_terminal() and not any( general_suffix.findall_operations(lambda op: isinstance(op, circuits.CircuitOperation)) ): - return self._run_sweep_sample(general_suffix, repetitions, qubit_order, acton_args) - return self._run_sweep_repeat(general_suffix, repetitions, qubit_order, acton_args) + return self._run_sweep_sample(general_suffix, repetitions, qubits, acton_args) + return self._run_sweep_repeat(general_suffix, repetitions, qubits, acton_args) def _run_sweep_sample( self, circuit: circuits.Circuit, repetitions: int, - qubit_order: ops.QubitOrderOrList, + qubits: Tuple['cirq.Qid', ...], acton_args: act_on_density_matrix_args.ActOnDensityMatrixArgs, ) -> Dict[str, np.ndarray]: for step_result in self._core_iterator( circuit=circuit, sim_state=acton_args, - qubit_order=qubit_order, + qubits=qubits, all_measurements_are_terminal=True, ): pass @@ -213,7 +213,7 @@ def _run_sweep_repeat( self, circuit: circuits.Circuit, repetitions: int, - qubit_order: ops.QubitOrderOrList, + qubits: Tuple['cirq.Qid', ...], acton_args: act_on_density_matrix_args.ActOnDensityMatrixArgs, ) -> Dict[str, np.ndarray]: measurements = {} # type: Dict[str, List[np.ndarray]] @@ -222,7 +222,7 @@ def _run_sweep_repeat( all_step_results = self._core_iterator( circuit, sim_state=acton_args.copy(), - qubit_order=qubit_order, + qubits=qubits, ) for step_result in all_step_results: for k, v in step_result.measurements.items(): @@ -233,24 +233,21 @@ def _run_sweep_repeat( def _create_act_on_args( self, - circuit: circuits.Circuit, initial_state: Union[np.ndarray, 'cirq.STATE_VECTOR_LIKE'], - qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, + qubits: Tuple['cirq.Qid', ...], ): """Creates the ActOnDensityMatrixArgs for a circuit. Args: - circuit: The circuit to simulate. initial_state: The initial state for the simulation in the computational basis. - qubit_order: Determines the canonical ordering of the qubits. This + qubits: 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. Returns: ActOnDensityMatrixArgs for the circuit. """ - qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) qid_shape = protocols.qid_shape(qubits) initial_matrix = qis.to_valid_density_matrix( initial_state, len(qid_shape), qid_shape=qid_shape, dtype=self._dtype @@ -272,7 +269,7 @@ def _core_iterator( self, circuit: circuits.Circuit, sim_state: act_on_density_matrix_args.ActOnDensityMatrixArgs, - qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, + qubits: Tuple['cirq.Qid', ...], all_measurements_are_terminal: bool = False, ): """Iterator over DensityMatrixStepResult from Moments of a Circuit @@ -281,7 +278,7 @@ def _core_iterator( circuit: The circuit to simulate. sim_state: The initial state args for the simulation in the computational basis. - qubit_order: Determines the canonical ordering of the qubits. This + qubits: 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. all_measurements_are_terminal: Indicator that all measurements @@ -290,7 +287,6 @@ def _core_iterator( Yields: DensityMatrixStepResult from simulating a Moment of the Circuit. """ - qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) qubit_map = {q: i for i, q in enumerate(qubits)} if len(circuit) == 0: yield DensityMatrixStepResult( diff --git a/cirq/sim/simulator.py b/cirq/sim/simulator.py index 16075164594..e8f02f1feae 100644 --- a/cirq/sim/simulator.py +++ b/cirq/sim/simulator.py @@ -525,24 +525,23 @@ def _base_iterator( Yields: StepResults from simulating a Moment of the Circuit. """ - acton_args = self._create_act_on_args(circuit, initial_state, qubit_order) - return self._core_iterator(circuit, acton_args, qubit_order) + qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) + acton_args = self._create_act_on_args(initial_state, qubits) + return self._core_iterator(circuit, acton_args, qubits) @abc.abstractmethod def _create_act_on_args( self, - circuit: circuits.Circuit, initial_state: Any, - qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, + qubits: Tuple['cirq.Qid', ...], ) -> TActOnArgs: """Creates the ActOnArgs state for a simulator. Args: - circuit: The circuit to simulate. initial_state: The initial state for the simulation. The form of this state depends on the simulation implementation. See documentation of the implementing class for details. - qubit_order: Determines the canonical ordering of the qubits. This + qubits: 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. @@ -555,7 +554,7 @@ def _core_iterator( self, circuit: circuits.Circuit, initial_state: TActOnArgs, - qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, + qubits: Tuple['cirq.Qid', ...], ) -> Iterator[TStepResult]: """Iterator over StepResult from Moments of a Circuit. @@ -564,7 +563,7 @@ def _core_iterator( initial_state: The initial args for the simulation. The form of this state depends on the simulation implementation. See documentation of the implementing class for details. - qubit_order: Determines the canonical ordering of the qubits. This + qubits: 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. diff --git a/cirq/sim/sparse_simulator.py b/cirq/sim/sparse_simulator.py index 680dbdf09d0..5960b8530d1 100644 --- a/cirq/sim/sparse_simulator.py +++ b/cirq/sim/sparse_simulator.py @@ -24,6 +24,7 @@ DefaultDict, Union, cast, + Tuple, ) import numpy as np @@ -172,7 +173,8 @@ def _run( param_resolver = param_resolver or study.ParamResolver({}) resolved_circuit = protocols.resolve_parameters(circuit, param_resolver) check_all_resolved(resolved_circuit) - qubit_order = sorted(resolved_circuit.all_qubits()) + qubits = tuple(sorted(resolved_circuit.all_qubits())) + acton_args = self._create_act_on_args(0, qubits) # Simulate as many unitary operations as possible before having to # repeat work for each sample. @@ -181,12 +183,11 @@ def _run( if protocols.has_unitary(self.noise) else (resolved_circuit[0:0], resolved_circuit) ) - acton_args = self._create_act_on_args(unitary_prefix, 0, qubit_order) step_result = None for step_result in self._core_iterator( circuit=unitary_prefix, sim_state=acton_args, - qubit_order=qubit_order, + qubits=qubits, ): pass assert step_result is not None @@ -205,14 +206,14 @@ def _run( acton_args=acton_args, circuit=general_suffix, repetitions=repetitions, - qubit_order=qubit_order, + qubits=qubits, ) def _brute_force_samples( self, acton_args: act_on_state_vector_args.ActOnStateVectorArgs, circuit: circuits.Circuit, - qubit_order: 'cirq.QubitOrderOrList', + qubits: Tuple['cirq.Qid', ...], repetitions: int, ) -> Dict[str, np.ndarray]: """Repeatedly simulate a circuit in order to produce samples.""" @@ -220,7 +221,7 @@ def _brute_force_samples( measurements: DefaultDict[str, List[np.ndarray]] = collections.defaultdict(list) for _ in range(repetitions): all_step_results = self._core_iterator( - circuit, sim_state=acton_args.copy(), qubit_order=qubit_order + circuit, sim_state=acton_args.copy(), qubits=qubits ) for step_result in all_step_results: @@ -230,24 +231,21 @@ def _brute_force_samples( def _create_act_on_args( self, - circuit: circuits.Circuit, initial_state: 'cirq.STATE_VECTOR_LIKE', - qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, + qubits: Tuple['cirq.Qid', ...], ): """Creates the ActOnStateVectorArgs for a circuit. Args: - circuit: The circuit to simulate. initial_state: The initial state for the simulation in the computational basis. - qubit_order: Determines the canonical ordering of the qubits. This + qubits: 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. Returns: ActOnStateVectorArgs for the circuit. """ - qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) num_qubits = len(qubits) qid_shape = protocols.qid_shape(qubits) state = qis.to_valid_state_vector( @@ -266,7 +264,7 @@ def _core_iterator( self, circuit: circuits.Circuit, sim_state: act_on_state_vector_args.ActOnStateVectorArgs, - qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, + qubits: Tuple['cirq.Qid', ...], ): """Iterator over SparseSimulatorStep from Moments of a Circuit @@ -274,14 +272,13 @@ def _core_iterator( circuit: The circuit to simulate. sim_state: The initial state args for the simulation in the computational basis. - qubit_order: Determines the canonical ordering of the qubits. This + qubits: 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. Yields: SparseSimulatorStep from simulating a Moment of the Circuit. """ - qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) qubit_map = {q: i for i, q in enumerate(qubits)} if len(circuit) == 0: yield SparseSimulatorStep( diff --git a/examples/quantum_teleportation.py b/examples/quantum_teleportation.py index 24bb064f130..70c37568c2e 100644 --- a/examples/quantum_teleportation.py +++ b/examples/quantum_teleportation.py @@ -64,13 +64,13 @@ def _run( ] ) -> Tuple[TSimulationTrialResult, TStepResult]: # Initialize our qubit state space. - msg, alice, bob = qubits = cirq.LineQubit.range(3) - qubit_order = cirq.QubitOrder.as_qubit_order(qubits) + msg, alice, bob = cirq.LineQubit.range(3) + qubits = (msg, alice, bob) + args = sim._create_act_on_args(0, qubits) # First we create a bell state circuit and simulate it on the qubits. bell_circuit = cirq.Circuit(cirq.H(alice), cirq.CNOT(alice, bob)) - args = sim._create_act_on_args(bell_circuit, 0, qubit_order) - list(sim._core_iterator(bell_circuit, args, qubit_order)) + list(sim._core_iterator(bell_circuit, args, qubits)) print('\nBell Circuit:') print(bell_circuit) @@ -81,7 +81,7 @@ def _run( cirq.X(msg) ** rand_x, cirq.Y(msg) ** rand_y, ) - list(sim._core_iterator(msg_circuit, args, qubit_order)) + list(sim._core_iterator(msg_circuit, args, qubits)) print('\nMessage Circuit:') print(msg_circuit) @@ -92,7 +92,7 @@ def _run( cirq.measure(alice, key='alice'), cirq.measure(msg, key='msg'), ) - alice_results = list(sim._core_iterator(alice_circuit, args, qubit_order)) + alice_results = list(sim._core_iterator(alice_circuit, args, qubits)) meas_alice = alice_results[1].measurements['alice'] == [1] meas_msg = alice_results[2].measurements['msg'] == [1] print('\nAlice Circuit:') @@ -108,7 +108,7 @@ def _run( if meas_msg: bob_circuit.append(cirq.Z(bob)) # coverage: ignore - *_, final_results = sim._core_iterator(bob_circuit, args, qubit_order) + *_, final_results = sim._core_iterator(bob_circuit, args, qubits) print('\nBob Circuit:') print(bob_circuit) From b34ac3a9dcdb29b6f068ad10f705bf46be5efaa9 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Fri, 2 Apr 2021 18:47:23 -0700 Subject: [PATCH 29/44] Clean PR issues --- cirq/contrib/quimb/mps_simulator.py | 2 +- cirq/google/calibration/engine_simulator.py | 4 +-- cirq/sim/clifford/clifford_simulator.py | 2 +- cirq/sim/density_matrix_simulator.py | 4 +-- cirq/sim/simulator.py | 32 +++++++++++++++------ cirq/sim/sparse_simulator.py | 4 +-- examples/quantum_teleportation.py | 26 ++++++++--------- 7 files changed, 45 insertions(+), 29 deletions(-) diff --git a/cirq/contrib/quimb/mps_simulator.py b/cirq/contrib/quimb/mps_simulator.py index f70e118a949..ed39a2dd971 100644 --- a/cirq/contrib/quimb/mps_simulator.py +++ b/cirq/contrib/quimb/mps_simulator.py @@ -84,7 +84,7 @@ def __init__( self.simulation_options = simulation_options self.grouping = grouping - def _create_act_on_args( + def create_act_on_args( self, initial_state: int, qubits: Tuple['cirq.Qid', ...], diff --git a/cirq/google/calibration/engine_simulator.py b/cirq/google/calibration/engine_simulator.py index f7aeb7ad83c..004b74bfaa0 100644 --- a/cirq/google/calibration/engine_simulator.py +++ b/cirq/google/calibration/engine_simulator.py @@ -399,12 +399,12 @@ def _core_iterator( converted = _convert_to_circuit_with_drift(self, circuit) return self._simulator._core_iterator(converted, initial_state, qubits) - def _create_act_on_args( + def create_act_on_args( self, initial_state: int, qubits: Tuple[Qid, ...], ): - return self._simulator._create_act_on_args(initial_state, qubits) + return self._simulator.create_act_on_args(initial_state, qubits) class _PhasedFSimConverter(PointOptimizer): diff --git a/cirq/sim/clifford/clifford_simulator.py b/cirq/sim/clifford/clifford_simulator.py index 3fb89cd53a3..e6ff6a435dd 100644 --- a/cirq/sim/clifford/clifford_simulator.py +++ b/cirq/sim/clifford/clifford_simulator.py @@ -68,7 +68,7 @@ def is_supported_operation(op: 'cirq.Operation') -> bool: # TODO: support more general Pauli measurements return protocols.has_stabilizer_effect(op) - def _create_act_on_args( + def create_act_on_args( self, initial_state: int, qubits: Tuple['cirq.Qid', ...], diff --git a/cirq/sim/density_matrix_simulator.py b/cirq/sim/density_matrix_simulator.py index c37d9ca8efc..391729c75f7 100644 --- a/cirq/sim/density_matrix_simulator.py +++ b/cirq/sim/density_matrix_simulator.py @@ -170,7 +170,7 @@ def _run( resolved_circuit = protocols.resolve_parameters(circuit, param_resolver) check_all_resolved(resolved_circuit) qubits = tuple(sorted(resolved_circuit.all_qubits())) - acton_args = self._create_act_on_args(0, qubits) + acton_args = self.create_act_on_args(0, qubits) prefix, general_suffix = split_into_matching_protocol_then_general( resolved_circuit, lambda op: not protocols.is_measurement(op) @@ -231,7 +231,7 @@ def _run_sweep_repeat( measurements[k].append(np.array(v, dtype=np.uint8)) return {k: np.array(v) for k, v in measurements.items()} - def _create_act_on_args( + def create_act_on_args( self, initial_state: Union[np.ndarray, 'cirq.STATE_VECTOR_LIKE'], qubits: Tuple['cirq.Qid', ...], diff --git a/cirq/sim/simulator.py b/cirq/sim/simulator.py index e8f02f1feae..e059da7895a 100644 --- a/cirq/sim/simulator.py +++ b/cirq/sim/simulator.py @@ -412,8 +412,9 @@ def simulate_sweep( qubit_order: 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. - initial_state: The initial state for the simulation. The form of - this state depends on the simulation implementation. See + initial_state: The initial state for the simulation. This can be + either a raw state or a `TActOnArgs`. The form of the + raw state depends on the simulation implementation. See documentation of the implementing class for details. Returns: @@ -457,8 +458,9 @@ def simulate_moment_steps( qubit_order: 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. - initial_state: The initial state for the simulation. The form of - this state depends on the simulation implementation. See + initial_state: The initial state for the simulation. This can be + either a raw state or a `TActOnArgs`. The form of the + raw state depends on the simulation implementation. See documentation of the implementing class for details. Returns: @@ -513,6 +515,12 @@ def _base_iterator( ) -> Iterator[TStepResult]: """Iterator over StepResult from Moments of a Circuit. + This is a thin wrapper around `create_acton_args` and `_core_iterator`. + Overriding this method was the old way of creating a circuit iterator, + and this method is planned to be formally put on the deprecation path. + Going forward, override the aforementioned two methods in custom + simulators. + Args: circuit: The circuit to simulate. qubit_order: Determines the canonical ordering of the qubits. This @@ -526,17 +534,22 @@ def _base_iterator( StepResults from simulating a Moment of the Circuit. """ qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) - acton_args = self._create_act_on_args(initial_state, qubits) + acton_args = ( + initial_state + if isinstance(initial_state, ActOnArgs) + else self.create_act_on_args(initial_state, qubits) + ) return self._core_iterator(circuit, acton_args, qubits) - @abc.abstractmethod - def _create_act_on_args( + def create_act_on_args( self, initial_state: Any, qubits: Tuple['cirq.Qid', ...], ) -> TActOnArgs: """Creates the ActOnArgs state for a simulator. + Custom simulators should implement this method. + Args: initial_state: The initial state for the simulation. The form of this state depends on the simulation implementation. See @@ -548,8 +561,8 @@ def _create_act_on_args( Returns: ActOnArgs for the simulator. """ + raise NotImplementedError() - @abc.abstractmethod def _core_iterator( self, circuit: circuits.Circuit, @@ -558,6 +571,8 @@ def _core_iterator( ) -> Iterator[TStepResult]: """Iterator over StepResult from Moments of a Circuit. + Custom simulators should implement this method. + Args: circuit: The circuit to simulate. initial_state: The initial args for the simulation. The form of @@ -570,6 +585,7 @@ def _core_iterator( Yields: StepResults from simulating a Moment of the Circuit. """ + raise NotImplementedError() @abc.abstractmethod def _create_simulator_trial_result( diff --git a/cirq/sim/sparse_simulator.py b/cirq/sim/sparse_simulator.py index 5960b8530d1..f9aba66e218 100644 --- a/cirq/sim/sparse_simulator.py +++ b/cirq/sim/sparse_simulator.py @@ -174,7 +174,7 @@ def _run( resolved_circuit = protocols.resolve_parameters(circuit, param_resolver) check_all_resolved(resolved_circuit) qubits = tuple(sorted(resolved_circuit.all_qubits())) - acton_args = self._create_act_on_args(0, qubits) + acton_args = self.create_act_on_args(0, qubits) # Simulate as many unitary operations as possible before having to # repeat work for each sample. @@ -229,7 +229,7 @@ def _brute_force_samples( measurements[k].append(np.array(v, dtype=np.uint8)) return {k: np.array(v) for k, v in measurements.items()} - def _create_act_on_args( + def create_act_on_args( self, initial_state: 'cirq.STATE_VECTOR_LIKE', qubits: Tuple['cirq.Qid', ...], diff --git a/examples/quantum_teleportation.py b/examples/quantum_teleportation.py index 70c37568c2e..c21154c8817 100644 --- a/examples/quantum_teleportation.py +++ b/examples/quantum_teleportation.py @@ -66,11 +66,11 @@ def _run( # Initialize our qubit state space. msg, alice, bob = cirq.LineQubit.range(3) qubits = (msg, alice, bob) - args = sim._create_act_on_args(0, qubits) + args = sim.create_act_on_args(0, qubits) # First we create a bell state circuit and simulate it on the qubits. bell_circuit = cirq.Circuit(cirq.H(alice), cirq.CNOT(alice, bob)) - list(sim._core_iterator(bell_circuit, args, qubits)) + list(sim.simulate_moment_steps(bell_circuit, None, qubits, args)) print('\nBell Circuit:') print(bell_circuit) @@ -81,7 +81,7 @@ def _run( cirq.X(msg) ** rand_x, cirq.Y(msg) ** rand_y, ) - list(sim._core_iterator(msg_circuit, args, qubits)) + list(sim.simulate_moment_steps(msg_circuit, None, qubits, args)) print('\nMessage Circuit:') print(msg_circuit) @@ -89,26 +89,26 @@ def _run( alice_circuit = cirq.Circuit( cirq.CNOT(msg, alice), cirq.H(msg), - cirq.measure(alice, key='alice'), - cirq.measure(msg, key='msg'), + cirq.measure(alice, key='x_fixup'), + cirq.measure(msg, key='z_fixup'), ) - alice_results = list(sim._core_iterator(alice_circuit, args, qubits)) - meas_alice = alice_results[1].measurements['alice'] == [1] - meas_msg = alice_results[2].measurements['msg'] == [1] + alice_results = list(sim.simulate_moment_steps(alice_circuit, None, qubits, args)) + x_fixup = alice_results[1].measurements['x_fixup'] == [1] + z_fixup = alice_results[2].measurements['z_fixup'] == [1] print('\nAlice Circuit:') print(alice_circuit) - print(f'meas_alice={meas_alice}') - print(f'meas_msg={meas_msg}') + print(f'x_fixup={x_fixup}') + print(f'z_fixup={z_fixup}') # Finally we construct Bob's circuit based on Alice's measurements bob_circuit = cirq.Circuit() - if meas_alice: + if x_fixup: bob_circuit.append(cirq.X(bob)) # coverage: ignore - if meas_msg: + if z_fixup: bob_circuit.append(cirq.Z(bob)) # coverage: ignore - *_, final_results = sim._core_iterator(bob_circuit, args, qubits) + *_, final_results = sim.simulate_moment_steps(bob_circuit, None, qubits, args) print('\nBob Circuit:') print(bob_circuit) From 80b420273530cc2a34c06bc6504af2bc11f27637 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Fri, 2 Apr 2021 21:05:18 -0700 Subject: [PATCH 30/44] Fix type check --- cirq/sim/simulator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq/sim/simulator.py b/cirq/sim/simulator.py index 9b11d7e7df4..12eb313f717 100644 --- a/cirq/sim/simulator.py +++ b/cirq/sim/simulator.py @@ -535,7 +535,7 @@ def _base_iterator( """ qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) acton_args = ( - initial_state + cast(TActOnArgs, initial_state) if isinstance(initial_state, ActOnArgs) else self.create_act_on_args(initial_state, qubits) ) From 645988e08209cac0b97c5c8ac5ca4d2356d56124 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Fri, 2 Apr 2021 21:56:31 -0700 Subject: [PATCH 31/44] Fix unit test --- cirq/sim/simulator.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cirq/sim/simulator.py b/cirq/sim/simulator.py index 12eb313f717..e2b58fdc42e 100644 --- a/cirq/sim/simulator.py +++ b/cirq/sim/simulator.py @@ -541,6 +541,7 @@ def _base_iterator( ) return self._core_iterator(circuit, acton_args, qubits) + @abc.abstractmethod def create_act_on_args( self, initial_state: Any, @@ -563,6 +564,7 @@ def create_act_on_args( """ raise NotImplementedError() + @abc.abstractmethod def _core_iterator( self, circuit: circuits.Circuit, From 3ea6c696067f14cc743221db852b73ab555d9354 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Sat, 3 Apr 2021 17:14:57 -0700 Subject: [PATCH 32/44] Spelling --- cirq/sim/simulator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq/sim/simulator.py b/cirq/sim/simulator.py index e2b58fdc42e..3a5a86eca5e 100644 --- a/cirq/sim/simulator.py +++ b/cirq/sim/simulator.py @@ -560,7 +560,7 @@ def create_act_on_args( ordering of the computational basis states. Returns: - ActOnArgs for the simulator. + The ActOnArgs for the simulator. """ raise NotImplementedError() From 2102af3318f34c49918cffc1c112149f5fce5cb1 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Mon, 5 Apr 2021 00:24:30 -0700 Subject: [PATCH 33/44] small changes --- cirq/sim/act_on_density_matrix_args.py | 2 +- cirq/sim/act_on_state_vector_args.py | 2 +- .../sim/clifford/act_on_clifford_tableau_args.py | 2 +- .../act_on_clifford_tableau_args_test.py | 1 - .../clifford/act_on_stabilizer_ch_form_args.py | 2 +- .../act_on_stabilizer_ch_form_args_test.py | 1 - cirq/sim/density_matrix_simulator.py | 16 ++++++++-------- cirq/sim/simulator.py | 6 +++--- cirq/sim/sparse_simulator.py | 10 +++++----- 9 files changed, 20 insertions(+), 22 deletions(-) diff --git a/cirq/sim/act_on_density_matrix_args.py b/cirq/sim/act_on_density_matrix_args.py index 5ac6d3714b9..ed64b6b20f0 100644 --- a/cirq/sim/act_on_density_matrix_args.py +++ b/cirq/sim/act_on_density_matrix_args.py @@ -96,7 +96,7 @@ def copy(self): return ActOnDensityMatrixArgs( self.target_tensor.copy(), [b.copy() for b in self.available_buffer], - [a for a in self.axes], + self.axes, self.qid_shape, self.prng, self.log_of_measurement_results.copy(), diff --git a/cirq/sim/act_on_state_vector_args.py b/cirq/sim/act_on_state_vector_args.py index 70ca8c1cc0c..9df5c10f440 100644 --- a/cirq/sim/act_on_state_vector_args.py +++ b/cirq/sim/act_on_state_vector_args.py @@ -171,7 +171,7 @@ def copy(self): return ActOnStateVectorArgs( self.target_tensor.copy(), self.available_buffer.copy(), - [a for a in self.axes], + self.axes, self.prng, self.log_of_measurement_results.copy(), ) diff --git a/cirq/sim/clifford/act_on_clifford_tableau_args.py b/cirq/sim/clifford/act_on_clifford_tableau_args.py index 463273d8600..dda324baab9 100644 --- a/cirq/sim/clifford/act_on_clifford_tableau_args.py +++ b/cirq/sim/clifford/act_on_clifford_tableau_args.py @@ -80,7 +80,7 @@ def _perform_measurement(self) -> List[int]: def copy(self): return ActOnCliffordTableauArgs( self.tableau.copy(), - [a for a in self.axes], + self.axes, self.prng, self.log_of_measurement_results.copy(), ) diff --git a/cirq/sim/clifford/act_on_clifford_tableau_args_test.py b/cirq/sim/clifford/act_on_clifford_tableau_args_test.py index 950e0fcaf27..fff652f998c 100644 --- a/cirq/sim/clifford/act_on_clifford_tableau_args_test.py +++ b/cirq/sim/clifford/act_on_clifford_tableau_args_test.py @@ -96,7 +96,6 @@ def test_copy(): assert args is not args1 assert args.tableau is not args1.tableau assert args.tableau == args1.tableau - assert args.axes is not args1.axes assert args.axes == args1.axes assert args.prng is args1.prng assert args.log_of_measurement_results is not args1.log_of_measurement_results diff --git a/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py b/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py index 014241d3d65..7996e0e9d1b 100644 --- a/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py +++ b/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py @@ -75,7 +75,7 @@ def _perform_measurement(self) -> List[int]: def copy(self): return ActOnStabilizerCHFormArgs( self.state.copy(), - [a for a in self.axes], + self.axes, self.prng, self.log_of_measurement_results.copy(), ) diff --git a/cirq/sim/clifford/act_on_stabilizer_ch_form_args_test.py b/cirq/sim/clifford/act_on_stabilizer_ch_form_args_test.py index 7e5e9230a7d..803de773eba 100644 --- a/cirq/sim/clifford/act_on_stabilizer_ch_form_args_test.py +++ b/cirq/sim/clifford/act_on_stabilizer_ch_form_args_test.py @@ -120,7 +120,6 @@ def test_copy(): assert args is not args1 assert args.state is not args1.state np.testing.assert_equal(args.state.state_vector(), args1.state.state_vector()) - assert args.axes is not args1.axes assert args.axes == args1.axes assert args.prng is args1.prng assert args.log_of_measurement_results is not args1.log_of_measurement_results diff --git a/cirq/sim/density_matrix_simulator.py b/cirq/sim/density_matrix_simulator.py index 391729c75f7..b9d7b2b7b17 100644 --- a/cirq/sim/density_matrix_simulator.py +++ b/cirq/sim/density_matrix_simulator.py @@ -170,7 +170,7 @@ def _run( resolved_circuit = protocols.resolve_parameters(circuit, param_resolver) check_all_resolved(resolved_circuit) qubits = tuple(sorted(resolved_circuit.all_qubits())) - acton_args = self.create_act_on_args(0, qubits) + act_on_args = self.create_act_on_args(0, qubits) prefix, general_suffix = split_into_matching_protocol_then_general( resolved_circuit, lambda op: not protocols.is_measurement(op) @@ -178,7 +178,7 @@ def _run( step_result = None for step_result in self._core_iterator( circuit=prefix, - sim_state=acton_args, + sim_state=act_on_args, qubits=qubits, ): pass @@ -187,19 +187,19 @@ def _run( if general_suffix.are_all_measurements_terminal() and not any( general_suffix.findall_operations(lambda op: isinstance(op, circuits.CircuitOperation)) ): - return self._run_sweep_sample(general_suffix, repetitions, qubits, acton_args) - return self._run_sweep_repeat(general_suffix, repetitions, qubits, acton_args) + return self._run_sweep_sample(general_suffix, repetitions, qubits, act_on_args) + return self._run_sweep_repeat(general_suffix, repetitions, qubits, act_on_args) def _run_sweep_sample( self, circuit: circuits.Circuit, repetitions: int, qubits: Tuple['cirq.Qid', ...], - acton_args: act_on_density_matrix_args.ActOnDensityMatrixArgs, + act_on_args: act_on_density_matrix_args.ActOnDensityMatrixArgs, ) -> Dict[str, np.ndarray]: for step_result in self._core_iterator( circuit=circuit, - sim_state=acton_args, + sim_state=act_on_args, qubits=qubits, all_measurements_are_terminal=True, ): @@ -214,14 +214,14 @@ def _run_sweep_repeat( circuit: circuits.Circuit, repetitions: int, qubits: Tuple['cirq.Qid', ...], - acton_args: act_on_density_matrix_args.ActOnDensityMatrixArgs, + act_on_args: act_on_density_matrix_args.ActOnDensityMatrixArgs, ) -> Dict[str, np.ndarray]: measurements = {} # type: Dict[str, List[np.ndarray]] for _ in range(repetitions): all_step_results = self._core_iterator( circuit, - sim_state=acton_args.copy(), + sim_state=act_on_args.copy(), qubits=qubits, ) for step_result in all_step_results: diff --git a/cirq/sim/simulator.py b/cirq/sim/simulator.py index e2b58fdc42e..249a7e4fafa 100644 --- a/cirq/sim/simulator.py +++ b/cirq/sim/simulator.py @@ -515,7 +515,7 @@ def _base_iterator( ) -> Iterator[TStepResult]: """Iterator over StepResult from Moments of a Circuit. - This is a thin wrapper around `create_acton_args` and `_core_iterator`. + This is a thin wrapper around `create_act_on_args` and `_core_iterator`. Overriding this method was the old way of creating a circuit iterator, and this method is planned to be formally put on the deprecation path. Going forward, override the aforementioned two methods in custom @@ -534,12 +534,12 @@ def _base_iterator( StepResults from simulating a Moment of the Circuit. """ qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) - acton_args = ( + act_on_args = ( cast(TActOnArgs, initial_state) if isinstance(initial_state, ActOnArgs) else self.create_act_on_args(initial_state, qubits) ) - return self._core_iterator(circuit, acton_args, qubits) + return self._core_iterator(circuit, act_on_args, qubits) @abc.abstractmethod def create_act_on_args( diff --git a/cirq/sim/sparse_simulator.py b/cirq/sim/sparse_simulator.py index 5a2e87891dc..0255a139543 100644 --- a/cirq/sim/sparse_simulator.py +++ b/cirq/sim/sparse_simulator.py @@ -174,7 +174,7 @@ def _run( resolved_circuit = protocols.resolve_parameters(circuit, param_resolver) check_all_resolved(resolved_circuit) qubits = tuple(sorted(resolved_circuit.all_qubits())) - acton_args = self.create_act_on_args(0, qubits) + act_on_args = self.create_act_on_args(0, qubits) # Simulate as many unitary operations as possible before having to # repeat work for each sample. @@ -186,7 +186,7 @@ def _run( step_result = None for step_result in self._core_iterator( circuit=unitary_prefix, - sim_state=acton_args, + sim_state=act_on_args, qubits=qubits, ): pass @@ -203,7 +203,7 @@ def _run( ) return self._brute_force_samples( - acton_args=acton_args, + act_on_args=act_on_args, circuit=general_suffix, repetitions=repetitions, qubits=qubits, @@ -211,7 +211,7 @@ def _run( def _brute_force_samples( self, - acton_args: act_on_state_vector_args.ActOnStateVectorArgs, + act_on_args: act_on_state_vector_args.ActOnStateVectorArgs, circuit: circuits.Circuit, qubits: Tuple['cirq.Qid', ...], repetitions: int, @@ -221,7 +221,7 @@ def _brute_force_samples( measurements: DefaultDict[str, List[np.ndarray]] = collections.defaultdict(list) for _ in range(repetitions): all_step_results = self._core_iterator( - circuit, sim_state=acton_args.copy(), qubits=qubits + circuit, sim_state=act_on_args.copy(), qubits=qubits ) for step_result in all_step_results: From 452a95053a9bc9ab6b0129a40d57d4c61262e140 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Mon, 5 Apr 2021 00:36:10 -0700 Subject: [PATCH 34/44] test change --- cirq/sim/simulator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq/sim/simulator.py b/cirq/sim/simulator.py index 2175e2ccefe..c5f838f01b9 100644 --- a/cirq/sim/simulator.py +++ b/cirq/sim/simulator.py @@ -560,7 +560,7 @@ def create_act_on_args( ordering of the computational basis states. Returns: - The ActOnArgs for the simulator. + The ActOnArgs for this simulator. """ raise NotImplementedError() From d1a42adc2049dcef8e3461d253a07da06e9b3fc5 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Sat, 10 Apr 2021 11:54:39 -0700 Subject: [PATCH 35/44] make _core_iterator.sim_state naming consistent --- cirq/contrib/quimb/mps_simulator.py | 18 +++++++++++------- cirq/google/calibration/engine_simulator.py | 4 ++-- cirq/sim/clifford/clifford_simulator.py | 16 ++++++++-------- cirq/sim/simulator.py | 4 ++-- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/cirq/contrib/quimb/mps_simulator.py b/cirq/contrib/quimb/mps_simulator.py index ed39a2dd971..155841898ab 100644 --- a/cirq/contrib/quimb/mps_simulator.py +++ b/cirq/contrib/quimb/mps_simulator.py @@ -114,14 +114,14 @@ def create_act_on_args( def _core_iterator( self, circuit: circuits.Circuit, - state: 'MPSState', + sim_state: 'MPSState', qubits: Tuple['cirq.Qid', ...], ): """Iterator over MPSSimulatorStepResult from Moments of a Circuit Args: circuit: The circuit to simulate. - state: The initial state args for the simulation in the + sim_state: The initial state args for the simulation in the computational basis. qubits: Determines the canonical ordering of the qubits. This is often used in specifying the initial state, i.e. the @@ -131,20 +131,24 @@ def _core_iterator( MPSStepResult from simulating a Moment of the Circuit. """ if len(circuit) == 0: - yield MPSSimulatorStepResult(measurements=state.log_of_measurement_results, state=state) + yield MPSSimulatorStepResult( + measurements=sim_state.log_of_measurement_results, state=sim_state + ) return noisy_moments = self.noise.noisy_moments(circuit, sorted(circuit.all_qubits())) for op_tree in noisy_moments: for op in flatten_to_ops(op_tree): if protocols.is_measurement(op) or protocols.has_mixture(op): - state.axes = tuple(state.qubit_map[qubit] for qubit in op.qubits) - protocols.act_on(op, state) + sim_state.axes = tuple(sim_state.qubit_map[qubit] for qubit in op.qubits) + protocols.act_on(op, sim_state) else: raise NotImplementedError(f"Unrecognized operation: {op!r}") - yield MPSSimulatorStepResult(measurements=state.log_of_measurement_results, state=state) - state.log_of_measurement_results.clear() + yield MPSSimulatorStepResult( + measurements=sim_state.log_of_measurement_results, state=sim_state + ) + sim_state.log_of_measurement_results.clear() def _create_simulator_trial_result( self, diff --git a/cirq/google/calibration/engine_simulator.py b/cirq/google/calibration/engine_simulator.py index 004b74bfaa0..26041832591 100644 --- a/cirq/google/calibration/engine_simulator.py +++ b/cirq/google/calibration/engine_simulator.py @@ -393,11 +393,11 @@ def _run( def _core_iterator( self, circuit: Circuit, - initial_state: Any, + sim_state: Any, qubits: Tuple[Qid, ...], ): converted = _convert_to_circuit_with_drift(self, circuit) - return self._simulator._core_iterator(converted, initial_state, qubits) + return self._simulator._core_iterator(converted, sim_state, qubits) def create_act_on_args( self, diff --git a/cirq/sim/clifford/clifford_simulator.py b/cirq/sim/clifford/clifford_simulator.py index e6ff6a435dd..adf53c759ec 100644 --- a/cirq/sim/clifford/clifford_simulator.py +++ b/cirq/sim/clifford/clifford_simulator.py @@ -98,14 +98,14 @@ def create_act_on_args( def _core_iterator( self, circuit: circuits.Circuit, - ch_form_args: clifford.ActOnStabilizerCHFormArgs, + sim_state: clifford.ActOnStabilizerCHFormArgs, qubits: Tuple['cirq.Qid', ...], ): """Iterator over CliffordSimulatorStepResult from Moments of a Circuit Args: circuit: The circuit to simulate. - ch_form_args: The initial state args for the simulation in the + sim_state: The initial state args for the simulation in the computational basis. qubits: Determines the canonical ordering of the qubits. This is often used in specifying the initial state, i.e. the @@ -118,29 +118,29 @@ def _core_iterator( def create_state(): state = CliffordState(qubit_map) - state.ch_form = ch_form_args.state.copy() + state.ch_form = sim_state.state.copy() return state if len(circuit) == 0: yield CliffordSimulatorStepResult( - measurements=ch_form_args.log_of_measurement_results, state=create_state() + measurements=sim_state.log_of_measurement_results, state=create_state() ) return for moment in circuit: - ch_form_args.log_of_measurement_results = {} + sim_state.log_of_measurement_results = {} for op in moment: try: - ch_form_args.axes = tuple(qubit_map[i] for i in op.qubits) - act_on(op, ch_form_args) + sim_state.axes = tuple(qubit_map[i] for i in op.qubits) + act_on(op, sim_state) except TypeError: raise NotImplementedError( f"CliffordSimulator doesn't support {op!r}" ) # type: ignore yield CliffordSimulatorStepResult( - measurements=ch_form_args.log_of_measurement_results, state=create_state() + measurements=sim_state.log_of_measurement_results, state=create_state() ) def _create_simulator_trial_result( diff --git a/cirq/sim/simulator.py b/cirq/sim/simulator.py index c5f838f01b9..8d3617cec50 100644 --- a/cirq/sim/simulator.py +++ b/cirq/sim/simulator.py @@ -568,7 +568,7 @@ def create_act_on_args( def _core_iterator( self, circuit: circuits.Circuit, - initial_state: TActOnArgs, + sim_state: TActOnArgs, qubits: Tuple['cirq.Qid', ...], ) -> Iterator[TStepResult]: """Iterator over StepResult from Moments of a Circuit. @@ -577,7 +577,7 @@ def _core_iterator( Args: circuit: The circuit to simulate. - initial_state: The initial args for the simulation. The form of + sim_state: The initial args for the simulation. The form of this state depends on the simulation implementation. See documentation of the implementing class for details. qubits: Determines the canonical ordering of the qubits. This From 6465a23f29187c662c583c4f2d767f9b006ff174 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Sat, 10 Apr 2021 12:08:53 -0700 Subject: [PATCH 36/44] Allow create_act_on_args to accept an ActOnArgs parameter, and return it directly if supplied. --- cirq/contrib/quimb/mps_simulator.py | 9 ++++++--- cirq/google/calibration/engine_simulator.py | 3 ++- cirq/sim/clifford/clifford_simulator.py | 9 ++++++--- cirq/sim/density_matrix_simulator.py | 7 +++++-- cirq/sim/simulator.py | 6 +----- cirq/sim/sparse_simulator.py | 5 ++++- 6 files changed, 24 insertions(+), 15 deletions(-) diff --git a/cirq/contrib/quimb/mps_simulator.py b/cirq/contrib/quimb/mps_simulator.py index 155841898ab..e5e2d91a5b3 100644 --- a/cirq/contrib/quimb/mps_simulator.py +++ b/cirq/contrib/quimb/mps_simulator.py @@ -19,7 +19,7 @@ import dataclasses import math -from typing import Any, Dict, List, Optional, Sequence, Set, TYPE_CHECKING, Iterable, Tuple +from typing import Any, Dict, List, Optional, Sequence, Set, TYPE_CHECKING, Iterable, Tuple, Union import numpy as np import quimb.tensor as qtn @@ -86,9 +86,9 @@ def __init__( def create_act_on_args( self, - initial_state: int, + initial_state: Union[int, 'MPSState'], qubits: Tuple['cirq.Qid', ...], - ): + ) -> 'MPSState': """Creates MPSState args for simulating the Circuit. Args: @@ -101,6 +101,9 @@ def create_act_on_args( Returns: MPSState args for simulating the Circuit. """ + if isinstance(initial_state, MPSState): + return initial_state + qubit_map = {q: i for i, q in enumerate(qubits)} return MPSState( diff --git a/cirq/google/calibration/engine_simulator.py b/cirq/google/calibration/engine_simulator.py index 26041832591..561c0a06ea2 100644 --- a/cirq/google/calibration/engine_simulator.py +++ b/cirq/google/calibration/engine_simulator.py @@ -37,6 +37,7 @@ Simulator, SimulatesSamples, SimulatesIntermediateStateVector, + ActOnStateVectorArgs, ) from cirq.study import ParamResolver from cirq.value import RANDOM_STATE_OR_SEED_LIKE, parse_random_state @@ -401,7 +402,7 @@ def _core_iterator( def create_act_on_args( self, - initial_state: int, + initial_state: Union[int, ActOnStateVectorArgs], qubits: Tuple[Qid, ...], ): return self._simulator.create_act_on_args(initial_state, qubits) diff --git a/cirq/sim/clifford/clifford_simulator.py b/cirq/sim/clifford/clifford_simulator.py index adf53c759ec..50dd264e69e 100644 --- a/cirq/sim/clifford/clifford_simulator.py +++ b/cirq/sim/clifford/clifford_simulator.py @@ -29,7 +29,7 @@ to state vector amplitudes. """ -from typing import Any, Dict, List, Sequence, Tuple +from typing import Any, Dict, List, Sequence, Tuple, Union import numpy as np @@ -70,9 +70,9 @@ def is_supported_operation(op: 'cirq.Operation') -> bool: def create_act_on_args( self, - initial_state: int, + initial_state: Union[int, clifford.ActOnStabilizerCHFormArgs], qubits: Tuple['cirq.Qid', ...], - ): + ) -> clifford.ActOnStabilizerCHFormArgs: """Creates the ActOnStabilizerChFormArgs for a circuit. Args: @@ -85,6 +85,9 @@ def create_act_on_args( Returns: ActOnStabilizerChFormArgs for the circuit. """ + if isinstance(initial_state, clifford.ActOnStabilizerCHFormArgs): + return initial_state + qubit_map = {q: i for i, q in enumerate(qubits)} state = CliffordState(qubit_map, initial_state=initial_state) diff --git a/cirq/sim/density_matrix_simulator.py b/cirq/sim/density_matrix_simulator.py index b9d7b2b7b17..9afd631bc4a 100644 --- a/cirq/sim/density_matrix_simulator.py +++ b/cirq/sim/density_matrix_simulator.py @@ -233,9 +233,9 @@ def _run_sweep_repeat( def create_act_on_args( self, - initial_state: Union[np.ndarray, 'cirq.STATE_VECTOR_LIKE'], + initial_state: Union[np.ndarray, 'cirq.STATE_VECTOR_LIKE', 'cirq.ActOnDensityMatrixArgs'], qubits: Tuple['cirq.Qid', ...], - ): + ) -> 'cirq.ActOnDensityMatrixArgs': """Creates the ActOnDensityMatrixArgs for a circuit. Args: @@ -248,6 +248,9 @@ def create_act_on_args( Returns: ActOnDensityMatrixArgs for the circuit. """ + if isinstance(initial_state, act_on_density_matrix_args.ActOnDensityMatrixArgs): + return initial_state + qid_shape = protocols.qid_shape(qubits) initial_matrix = qis.to_valid_density_matrix( initial_state, len(qid_shape), qid_shape=qid_shape, dtype=self._dtype diff --git a/cirq/sim/simulator.py b/cirq/sim/simulator.py index 8d3617cec50..83c22d1a4ac 100644 --- a/cirq/sim/simulator.py +++ b/cirq/sim/simulator.py @@ -534,11 +534,7 @@ def _base_iterator( StepResults from simulating a Moment of the Circuit. """ qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) - act_on_args = ( - cast(TActOnArgs, initial_state) - if isinstance(initial_state, ActOnArgs) - else self.create_act_on_args(initial_state, qubits) - ) + act_on_args = self.create_act_on_args(initial_state, qubits) return self._core_iterator(circuit, act_on_args, qubits) @abc.abstractmethod diff --git a/cirq/sim/sparse_simulator.py b/cirq/sim/sparse_simulator.py index 0255a139543..816c08c4f3d 100644 --- a/cirq/sim/sparse_simulator.py +++ b/cirq/sim/sparse_simulator.py @@ -231,7 +231,7 @@ def _brute_force_samples( def create_act_on_args( self, - initial_state: 'cirq.STATE_VECTOR_LIKE', + initial_state: Union['cirq.STATE_VECTOR_LIKE', 'cirq.ActOnStateVectorArgs'], qubits: Tuple['cirq.Qid', ...], ): """Creates the ActOnStateVectorArgs for a circuit. @@ -246,6 +246,9 @@ def create_act_on_args( Returns: ActOnStateVectorArgs for the circuit. """ + if isinstance(initial_state, act_on_state_vector_args.ActOnStateVectorArgs): + return initial_state + num_qubits = len(qubits) qid_shape = protocols.qid_shape(qubits) state = qis.to_valid_state_vector( From 2fe1e8a07c6cb747242449dd28d6ca38ea9457e2 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Sat, 10 Apr 2021 18:14:47 -0700 Subject: [PATCH 37/44] Add tests for `.simulate(ActOnArgs)`. --- cirq/sim/clifford/clifford_simulator_test.py | 21 ++++++++++++++++++++ cirq/sim/density_matrix_simulator_test.py | 14 +++++++++++++ cirq/sim/sparse_simulator_test.py | 14 +++++++++++++ 3 files changed, 49 insertions(+) diff --git a/cirq/sim/clifford/clifford_simulator_test.py b/cirq/sim/clifford/clifford_simulator_test.py index ebeebdb4f84..9e5cdb1f516 100644 --- a/cirq/sim/clifford/clifford_simulator_test.py +++ b/cirq/sim/clifford/clifford_simulator_test.py @@ -97,6 +97,27 @@ def test_simulate_initial_state(): ) +def test_simulate_act_on_args(): + q0, q1 = cirq.LineQubit.range(2) + simulator = cirq.CliffordSimulator() + for b0 in [0, 1]: + for b1 in [0, 1]: + circuit = cirq.Circuit() + if b0: + circuit.append(cirq.X(q0)) + if b1: + circuit.append(cirq.X(q1)) + circuit.append(cirq.measure(q0, q1)) + + args = simulator.create_act_on_args(initial_state=1, qubits=(q0, q1)) + result = simulator.simulate(circuit, initial_state=args) + expected_state = np.zeros(shape=(2, 2)) + expected_state[b0][1 - b1] = 1.0 + np.testing.assert_almost_equal( + result.final_state.to_numpy(), np.reshape(expected_state, 4) + ) + + def test_simulate_qubit_order(): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.CliffordSimulator() diff --git a/cirq/sim/density_matrix_simulator_test.py b/cirq/sim/density_matrix_simulator_test.py index aa8a1da1f59..c2e3448535f 100644 --- a/cirq/sim/density_matrix_simulator_test.py +++ b/cirq/sim/density_matrix_simulator_test.py @@ -560,6 +560,20 @@ def test_simulate_initial_state(dtype): np.testing.assert_equal(result.final_density_matrix, expected_density_matrix) +@pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) +def test_simulate_act_on_args(dtype): + q0, q1 = cirq.LineQubit.range(2) + simulator = cirq.DensityMatrixSimulator(dtype=dtype) + for b0 in [0, 1]: + for b1 in [0, 1]: + circuit = cirq.Circuit((cirq.X ** b0)(q0), (cirq.X ** b1)(q1)) + args = simulator.create_act_on_args(initial_state=1, qubits=(q0, q1)) + result = simulator.simulate(circuit, initial_state=args) + expected_density_matrix = np.zeros(shape=(4, 4)) + expected_density_matrix[b0 * 2 + 1 - b1, b0 * 2 + 1 - b1] = 1.0 + np.testing.assert_equal(result.final_density_matrix, expected_density_matrix) + + def test_simulate_tps_initial_state(): q0, q1 = cirq.LineQubit.range(2) simulator = cirq.DensityMatrixSimulator() diff --git a/cirq/sim/sparse_simulator_test.py b/cirq/sim/sparse_simulator_test.py index 047e75f545a..255bffa6e48 100644 --- a/cirq/sim/sparse_simulator_test.py +++ b/cirq/sim/sparse_simulator_test.py @@ -449,6 +449,20 @@ def test_simulate_initial_state(dtype): np.testing.assert_equal(result.final_state_vector, np.reshape(expected_state, 4)) +@pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) +def test_simulate_act_on_args(dtype): + q0, q1 = cirq.LineQubit.range(2) + simulator = cirq.Simulator(dtype=dtype) + for b0 in [0, 1]: + for b1 in [0, 1]: + circuit = cirq.Circuit((cirq.X ** b0)(q0), (cirq.X ** b1)(q1)) + args = simulator.create_act_on_args(initial_state=1, qubits=(q0, q1)) + result = simulator.simulate(circuit, initial_state=args) + expected_state = np.zeros(shape=(2, 2)) + expected_state[b0][1 - b1] = 1.0 + np.testing.assert_equal(result.final_state_vector, np.reshape(expected_state, 4)) + + @pytest.mark.parametrize('dtype', [np.complex64, np.complex128]) def test_simulate_qubit_order(dtype): q0, q1 = cirq.LineQubit.range(2) From cfb711ef2089d922fa4fde172501c739ff1b1d6b Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Sat, 10 Apr 2021 18:24:09 -0700 Subject: [PATCH 38/44] Add tests for `.simulate(ActOnArgs)`. --- cirq/contrib/quimb/mps_simulator_test.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/cirq/contrib/quimb/mps_simulator_test.py b/cirq/contrib/quimb/mps_simulator_test.py index ade54e10f98..92f1f352926 100644 --- a/cirq/contrib/quimb/mps_simulator_test.py +++ b/cirq/contrib/quimb/mps_simulator_test.py @@ -205,6 +205,21 @@ def test_cnot_flipped(): ) +def test_act_on_args(): + q0, q1 = qubit_order = cirq.LineQubit.range(2) + circuit = cirq.Circuit(cirq.CNOT(q1, q0)) + mps_simulator = ccq.mps_simulator.MPSSimulator() + ref_simulator = cirq.Simulator() + for initial_state in range(4): + args = mps_simulator.create_act_on_args(initial_state=initial_state, qubits=(q0, q1)) + actual = mps_simulator.simulate(circuit, qubit_order=qubit_order, initial_state=args) + expected = ref_simulator.simulate(circuit, qubit_order=qubit_order, initial_state=initial_state) + np.testing.assert_allclose( + actual.final_state.to_numpy(), expected.final_state_vector, atol=1e-4 + ) + assert len(actual.measurements) == 0 + + def test_three_qubits(): q0, q1, q2 = cirq.LineQubit.range(3) circuit = cirq.Circuit(cirq.CCX(q0, q1, q2)) From e2063df917977f5a3959ddc8c73f642f4ebff3ad Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Sat, 10 Apr 2021 18:53:05 -0700 Subject: [PATCH 39/44] lint --- cirq/contrib/quimb/mps_simulator_test.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cirq/contrib/quimb/mps_simulator_test.py b/cirq/contrib/quimb/mps_simulator_test.py index 92f1f352926..f6d2ac63d5b 100644 --- a/cirq/contrib/quimb/mps_simulator_test.py +++ b/cirq/contrib/quimb/mps_simulator_test.py @@ -213,7 +213,9 @@ def test_act_on_args(): for initial_state in range(4): args = mps_simulator.create_act_on_args(initial_state=initial_state, qubits=(q0, q1)) actual = mps_simulator.simulate(circuit, qubit_order=qubit_order, initial_state=args) - expected = ref_simulator.simulate(circuit, qubit_order=qubit_order, initial_state=initial_state) + expected = ref_simulator.simulate( + circuit, qubit_order=qubit_order, initial_state=initial_state + ) np.testing.assert_allclose( actual.final_state.to_numpy(), expected.final_state_vector, atol=1e-4 ) From 8913bdad87c4c66d43af02f72e8f9f7b52b126d4 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Sat, 10 Apr 2021 21:16:49 -0700 Subject: [PATCH 40/44] Plop qubits and qubit_map into ActOnArgs instead of passing together everywhere. --- cirq/contrib/quimb/mps_simulator.py | 35 ++++++++----------- cirq/contrib/quimb/mps_simulator_test.py | 12 +++---- cirq/google/calibration/engine_simulator.py | 3 +- cirq/sim/act_on_args.py | 19 +++++++--- cirq/sim/act_on_density_matrix_args.py | 26 +++++++++----- cirq/sim/act_on_state_vector_args.py | 19 ++++++---- .../clifford/act_on_clifford_tableau_args.py | 17 +++++---- .../act_on_stabilizer_ch_form_args.py | 17 +++++---- cirq/sim/clifford/clifford_simulator.py | 18 ++++------ cirq/sim/density_matrix_simulator.py | 21 ++++------- cirq/sim/simulator.py | 6 +--- cirq/sim/sparse_simulator.py | 19 +++------- 12 files changed, 107 insertions(+), 105 deletions(-) diff --git a/cirq/contrib/quimb/mps_simulator.py b/cirq/contrib/quimb/mps_simulator.py index e5e2d91a5b3..a6b6b563a33 100644 --- a/cirq/contrib/quimb/mps_simulator.py +++ b/cirq/contrib/quimb/mps_simulator.py @@ -104,13 +104,11 @@ def create_act_on_args( if isinstance(initial_state, MPSState): return initial_state - qubit_map = {q: i for i, q in enumerate(qubits)} - return MPSState( - qubit_map, - self.prng, - self.simulation_options, - self.grouping, + qubits=qubits, + prng=self.prng, + simulation_options=self.simulation_options, + grouping=self.grouping, initial_state=initial_state, ) @@ -118,7 +116,6 @@ def _core_iterator( self, circuit: circuits.Circuit, sim_state: 'MPSState', - qubits: Tuple['cirq.Qid', ...], ): """Iterator over MPSSimulatorStepResult from Moments of a Circuit @@ -126,9 +123,6 @@ def _core_iterator( circuit: The circuit to simulate. sim_state: The initial state args for the simulation in the computational basis. - qubits: 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. Yields: MPSStepResult from simulating a Moment of the Circuit. @@ -304,7 +298,7 @@ class MPSState(ActOnArgs): def __init__( self, - qubit_map: Dict['cirq.Qid', int], + qubits: Iterable['cirq.Qid'], prng: np.random.RandomState, simulation_options: MPSOptions = MPSOptions(), grouping: Optional[Dict['cirq.Qid', int]] = None, @@ -315,7 +309,9 @@ def __init__( """Creates and MPSState Args: - qubit_map: A map from Qid to an integer that uniquely identifies it. + qubits: 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. prng: A random number generator, used to simulate measurements. simulation_options: Numerical options for the simulation. grouping: How to group qubits together, if None all are individual. @@ -325,8 +321,8 @@ def __init__( log_of_measurement_results: A mutable object that measurements are being recorded into. """ - super().__init__(prng, axes, log_of_measurement_results) - self.qubit_map = qubit_map + super().__init__(prng, qubits, axes, log_of_measurement_results) + qubit_map = self.qubit_map self.grouping = qubit_map if grouping is None else grouping if self.grouping.keys() != self.qubit_map.keys(): raise ValueError('Grouping must cover exactly the qubits.') @@ -382,10 +378,10 @@ def _value_equality_values_(self) -> Any: def copy(self) -> 'MPSState': state = MPSState( - self.qubit_map, - self.prng, - self.simulation_options, - self.grouping, + qubits=self.qubits, + prng=self.prng, + simulation_options=self.simulation_options, + grouping=self.grouping, ) state.M = [x.copy() for x in self.M] state.estimated_gate_error_list = self.estimated_gate_error_list @@ -602,6 +598,5 @@ def perform_measurement( def _perform_measurement(self) -> List[int]: """Measures the axes specified by the simulator.""" - qubit_map_inv = {v: k for k, v in self.qubit_map.items()} - qubits = [qubit_map_inv[key] for key in self.axes] + qubits = [self.qubits[key] for key in self.axes] return self.perform_measurement(qubits, self.prng) diff --git a/cirq/contrib/quimb/mps_simulator_test.py b/cirq/contrib/quimb/mps_simulator_test.py index f6d2ac63d5b..e9780b29e18 100644 --- a/cirq/contrib/quimb/mps_simulator_test.py +++ b/cirq/contrib/quimb/mps_simulator_test.py @@ -274,7 +274,7 @@ def test_measurement_str(): def test_trial_result_str(): q0 = cirq.LineQubit(0) final_simulator_state = ccq.mps_simulator.MPSState( - qubit_map={q0: 0}, + qubits=(q0,), prng=value.parse_random_state(0), simulation_options=ccq.mps_simulator.MPSOptions(), ) @@ -295,7 +295,7 @@ def test_trial_result_str(): def test_empty_step_result(): q0 = cirq.LineQubit(0) - state = ccq.mps_simulator.MPSState(qubit_map={q0: 0}, prng=value.parse_random_state(0)) + state = ccq.mps_simulator.MPSState(qubits=(q0,), prng=value.parse_random_state(0)) step_result = ccq.mps_simulator.MPSSimulatorStepResult(state, measurements={'0': [1]}) assert ( str(step_result) @@ -309,17 +309,17 @@ def test_empty_step_result(): def test_state_equal(): q0, q1 = cirq.LineQubit.range(2) state0 = ccq.mps_simulator.MPSState( - qubit_map={q0: 0}, + qubits=(q0,), prng=value.parse_random_state(0), simulation_options=ccq.mps_simulator.MPSOptions(cutoff=1e-3, sum_prob_atol=1e-3), ) state1a = ccq.mps_simulator.MPSState( - qubit_map={q1: 0}, + qubits=(q1,), prng=value.parse_random_state(0), simulation_options=ccq.mps_simulator.MPSOptions(cutoff=1e-3, sum_prob_atol=1e-3), ) state1b = ccq.mps_simulator.MPSState( - qubit_map={q1: 0}, + qubits=(q1,), prng=value.parse_random_state(0), simulation_options=ccq.mps_simulator.MPSOptions(cutoff=1729.0, sum_prob_atol=1e-3), ) @@ -517,7 +517,7 @@ def test_state_copy(): def test_state_act_on_args_initializer(): s = ccq.mps_simulator.MPSState( - qubit_map={cirq.LineQubit(0): 0}, + qubits=(cirq.LineQubit(0),), prng=np.random.RandomState(0), axes=[2], log_of_measurement_results={'test': 4}, diff --git a/cirq/google/calibration/engine_simulator.py b/cirq/google/calibration/engine_simulator.py index 561c0a06ea2..03c5aae939a 100644 --- a/cirq/google/calibration/engine_simulator.py +++ b/cirq/google/calibration/engine_simulator.py @@ -395,10 +395,9 @@ def _core_iterator( self, circuit: Circuit, sim_state: Any, - qubits: Tuple[Qid, ...], ): converted = _convert_to_circuit_with_drift(self, circuit) - return self._simulator._core_iterator(converted, sim_state, qubits) + return self._simulator._core_iterator(converted, sim_state) def create_act_on_args( self, diff --git a/cirq/sim/act_on_args.py b/cirq/sim/act_on_args.py index c9a54030268..46f7971db38 100644 --- a/cirq/sim/act_on_args.py +++ b/cirq/sim/act_on_args.py @@ -13,7 +13,7 @@ # limitations under the License. """Objects and methods for acting efficiently on a state tensor.""" import abc -from typing import Any, Iterable, Dict, List, TypeVar +from typing import Any, Iterable, Dict, List, TypeVar, TYPE_CHECKING import numpy as np @@ -22,6 +22,9 @@ TSelf = TypeVar('TSelf', bound='ActOnArgs') +if TYPE_CHECKING: + import cirq + class ActOnArgs: """State and context for an operation acting on a state tensor.""" @@ -29,23 +32,31 @@ class ActOnArgs: def __init__( self, prng: np.random.RandomState, + qubits: Iterable['cirq.Qid'] = None, axes: Iterable[int] = None, log_of_measurement_results: Dict[str, Any] = None, ): """ Args: - axes: The indices of axes corresponding to the qubits that the - operation is supposed to act upon. prng: The pseudo random number generator to use for probabilistic effects. + qubits: 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. + 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`. """ + if qubits is None: + qubits = () if axes is None: - axes = [] + axes = () 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.axes = tuple(axes) self.prng = prng self.log_of_measurement_results = log_of_measurement_results diff --git a/cirq/sim/act_on_density_matrix_args.py b/cirq/sim/act_on_density_matrix_args.py index ed64b6b20f0..8263a0a038a 100644 --- a/cirq/sim/act_on_density_matrix_args.py +++ b/cirq/sim/act_on_density_matrix_args.py @@ -13,13 +13,16 @@ # limitations under the License. """Objects and methods for acting efficiently on a density matrix.""" -from typing import Any, Iterable, Dict, List, Tuple +from typing import Any, Iterable, Dict, List, Tuple, TYPE_CHECKING import numpy as np from cirq import protocols, sim from cirq.sim.act_on_args import ActOnArgs, strat_act_on_from_apply_decompose +if TYPE_CHECKING: + import cirq + class ActOnDensityMatrixArgs(ActOnArgs): """State and context for an operation acting on a density matrix. @@ -36,6 +39,7 @@ def __init__( qid_shape: Tuple[int, ...], prng: np.random.RandomState, log_of_measurement_results: Dict[str, Any], + qubits: Iterable['cirq.Qid'] = None, ): """ Args: @@ -46,6 +50,9 @@ def __init__( `target_tensor`. Used by operations that cannot be applied to `target_tensor` inline, in order to avoid unnecessary allocations. + qubits: 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. axes: The indices of axes corresponding to the qubits that the operation is supposed to act upon. qid_shape: The shape of the target tensor. @@ -55,7 +62,7 @@ def __init__( being recorded into. Edit it easily by calling `ActOnStateVectorArgs.record_measurement_result`. """ - super().__init__(prng, axes, log_of_measurement_results) + super().__init__(prng, qubits, axes, log_of_measurement_results) self.target_tensor = target_tensor self.available_buffer = available_buffer self.qid_shape = qid_shape @@ -92,14 +99,15 @@ def _perform_measurement(self) -> List[int]: ) return bits - def copy(self): + def copy(self) -> 'cirq.ActOnDensityMatrixArgs': return ActOnDensityMatrixArgs( - self.target_tensor.copy(), - [b.copy() for b in self.available_buffer], - self.axes, - self.qid_shape, - self.prng, - self.log_of_measurement_results.copy(), + target_tensor=self.target_tensor.copy(), + available_buffer=[b.copy() for b in self.available_buffer], + qubits=self.qubits, + axes=self.axes, + qid_shape=self.qid_shape, + prng=self.prng, + log_of_measurement_results=self.log_of_measurement_results.copy(), ) diff --git a/cirq/sim/act_on_state_vector_args.py b/cirq/sim/act_on_state_vector_args.py index 9df5c10f440..91c84d0e3d9 100644 --- a/cirq/sim/act_on_state_vector_args.py +++ b/cirq/sim/act_on_state_vector_args.py @@ -43,6 +43,7 @@ def __init__( axes: Iterable[int], prng: np.random.RandomState, log_of_measurement_results: Dict[str, Any], + qubits: Iterable['cirq.Qid'] = None, ): """ Args: @@ -54,6 +55,9 @@ def __init__( `target_tensor` inline, in order to avoid unnecessary allocations. Passing `available_buffer` into `swap_target_tensor_for` will swap it for `target_tensor`. + qubits: 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. axes: The indices of axes corresponding to the qubits that the operation is supposed to act upon. prng: The pseudo random number generator to use for probabilistic @@ -62,7 +66,7 @@ def __init__( being recorded into. Edit it easily by calling `ActOnStateVectorArgs.record_measurement_result`. """ - super().__init__(prng, axes, log_of_measurement_results) + super().__init__(prng, qubits, axes, log_of_measurement_results) self.target_tensor = target_tensor self.available_buffer = available_buffer @@ -167,13 +171,14 @@ def _perform_measurement(self) -> List[int]: ) return bits - def copy(self): + def copy(self) -> 'cirq.ActOnStateVectorArgs': return ActOnStateVectorArgs( - self.target_tensor.copy(), - self.available_buffer.copy(), - self.axes, - self.prng, - self.log_of_measurement_results.copy(), + target_tensor=self.target_tensor.copy(), + available_buffer=self.available_buffer.copy(), + qubits=self.qubits, + axes=self.axes, + prng=self.prng, + log_of_measurement_results=self.log_of_measurement_results.copy(), ) diff --git a/cirq/sim/clifford/act_on_clifford_tableau_args.py b/cirq/sim/clifford/act_on_clifford_tableau_args.py index dda324baab9..4dd550dceb4 100644 --- a/cirq/sim/clifford/act_on_clifford_tableau_args.py +++ b/cirq/sim/clifford/act_on_clifford_tableau_args.py @@ -43,11 +43,15 @@ def __init__( axes: Iterable[int], prng: np.random.RandomState, log_of_measurement_results: Dict[str, Any], + qubits: Iterable['cirq.Qid'] = None, ): """ Args: tableau: The CliffordTableau to act on. Operations are expected to perform inplace edits of this object. + qubits: 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. axes: The indices of axes corresponding to the qubits that the operation is supposed to act upon. prng: The pseudo random number generator to use for probabilistic @@ -56,7 +60,7 @@ def __init__( being recorded into. Edit it easily by calling `ActOnCliffordTableauArgs.record_measurement_result`. """ - super().__init__(prng, axes, log_of_measurement_results) + super().__init__(prng, qubits, axes, log_of_measurement_results) self.tableau = tableau def _act_on_fallback_(self, action: Any, allow_decompose: bool): @@ -77,12 +81,13 @@ def _perform_measurement(self) -> List[int]: """Returns the measurement from the tableau.""" return [self.tableau._measure(q, self.prng) for q in self.axes] - def copy(self): + def copy(self) -> 'cirq.ActOnCliffordTableauArgs': return ActOnCliffordTableauArgs( - self.tableau.copy(), - self.axes, - self.prng, - self.log_of_measurement_results.copy(), + tableau=self.tableau.copy(), + qubits=self.qubits, + axes=self.axes, + prng=self.prng, + log_of_measurement_results=self.log_of_measurement_results.copy(), ) diff --git a/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py b/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py index 7996e0e9d1b..06b4e6c2e6e 100644 --- a/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py +++ b/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py @@ -40,11 +40,15 @@ def __init__( axes: Iterable[int], prng: np.random.RandomState, log_of_measurement_results: Dict[str, Any], + qubits: Iterable['cirq.Qid'] = None, ): """Initializes with the given state and the axes for the operation. Args: state: The StabilizerStateChForm to act on. Operations are expected to perform inplace edits of this object. + qubits: 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. axes: The indices of axes corresponding to the qubits that the operation is supposed to act upon. prng: The pseudo random number generator to use for probabilistic @@ -53,7 +57,7 @@ def __init__( being recorded into. Edit it easily by calling `ActOnStabilizerCHFormArgs.record_measurement_result`. """ - super().__init__(prng, axes, log_of_measurement_results) + super().__init__(prng, qubits, axes, log_of_measurement_results) self.state = state def _act_on_fallback_(self, action: Any, allow_decompose: bool): @@ -72,12 +76,13 @@ def _perform_measurement(self) -> List[int]: """Returns the measurement from the stabilizer state form.""" return [self.state._measure(q, self.prng) for q in self.axes] - def copy(self): + def copy(self) -> 'cirq.ActOnStabilizerCHFormArgs': return ActOnStabilizerCHFormArgs( - self.state.copy(), - self.axes, - self.prng, - self.log_of_measurement_results.copy(), + state=self.state.copy(), + qubits=self.qubits, + axes=self.axes, + prng=self.prng, + log_of_measurement_results=self.log_of_measurement_results.copy(), ) diff --git a/cirq/sim/clifford/clifford_simulator.py b/cirq/sim/clifford/clifford_simulator.py index 50dd264e69e..a25b8b67b83 100644 --- a/cirq/sim/clifford/clifford_simulator.py +++ b/cirq/sim/clifford/clifford_simulator.py @@ -92,17 +92,17 @@ def create_act_on_args( state = CliffordState(qubit_map, initial_state=initial_state) return clifford.ActOnStabilizerCHFormArgs( - state.ch_form, - [], - self._prng, - {}, + state=state.ch_form, + axes=[], + prng=self._prng, + log_of_measurement_results={}, + qubits=qubits, ) def _core_iterator( self, circuit: circuits.Circuit, sim_state: clifford.ActOnStabilizerCHFormArgs, - qubits: Tuple['cirq.Qid', ...], ): """Iterator over CliffordSimulatorStepResult from Moments of a Circuit @@ -110,17 +110,13 @@ def _core_iterator( circuit: The circuit to simulate. sim_state: The initial state args for the simulation in the computational basis. - qubits: 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. Yields: CliffordStepResult from simulating a Moment of the Circuit. """ - qubit_map = {q: i for i, q in enumerate(qubits)} def create_state(): - state = CliffordState(qubit_map) + state = CliffordState(sim_state.qubit_map) state.ch_form = sim_state.state.copy() return state @@ -135,7 +131,7 @@ def create_state(): for op in moment: try: - sim_state.axes = tuple(qubit_map[i] for i in op.qubits) + sim_state.axes = tuple(sim_state.qubit_map[i] for i in op.qubits) act_on(op, sim_state) except TypeError: raise NotImplementedError( diff --git a/cirq/sim/density_matrix_simulator.py b/cirq/sim/density_matrix_simulator.py index 15fe5b3046e..f31b6cb5841 100644 --- a/cirq/sim/density_matrix_simulator.py +++ b/cirq/sim/density_matrix_simulator.py @@ -180,7 +180,6 @@ def _run( for step_result in self._core_iterator( circuit=prefix, sim_state=act_on_args, - qubits=qubits, ): pass assert step_result is not None @@ -188,20 +187,18 @@ def _run( if general_suffix.are_all_measurements_terminal() and not any( general_suffix.findall_operations(lambda op: isinstance(op, circuits.CircuitOperation)) ): - return self._run_sweep_sample(general_suffix, repetitions, qubits, act_on_args) - return self._run_sweep_repeat(general_suffix, repetitions, qubits, act_on_args) + return self._run_sweep_sample(general_suffix, repetitions, act_on_args) + return self._run_sweep_repeat(general_suffix, repetitions, act_on_args) def _run_sweep_sample( self, circuit: circuits.Circuit, repetitions: int, - qubits: Tuple['cirq.Qid', ...], act_on_args: act_on_density_matrix_args.ActOnDensityMatrixArgs, ) -> Dict[str, np.ndarray]: for step_result in self._core_iterator( circuit=circuit, sim_state=act_on_args, - qubits=qubits, all_measurements_are_terminal=True, ): pass @@ -214,7 +211,6 @@ def _run_sweep_repeat( self, circuit: circuits.Circuit, repetitions: int, - qubits: Tuple['cirq.Qid', ...], act_on_args: act_on_density_matrix_args.ActOnDensityMatrixArgs, ) -> Dict[str, np.ndarray]: measurements = {} # type: Dict[str, List[np.ndarray]] @@ -223,7 +219,6 @@ def _run_sweep_repeat( all_step_results = self._core_iterator( circuit, sim_state=act_on_args.copy(), - qubits=qubits, ) for step_result in all_step_results: for k, v in step_result.measurements.items(): @@ -263,6 +258,7 @@ def create_act_on_args( return act_on_density_matrix_args.ActOnDensityMatrixArgs( target_tensor=tensor, available_buffer=[np.empty_like(tensor) for _ in range(3)], + qubits=qubits, axes=[], qid_shape=qid_shape, prng=self._prng, @@ -273,7 +269,6 @@ def _core_iterator( self, circuit: circuits.Circuit, sim_state: act_on_density_matrix_args.ActOnDensityMatrixArgs, - qubits: Tuple['cirq.Qid', ...], all_measurements_are_terminal: bool = False, ): """Iterator over DensityMatrixStepResult from Moments of a Circuit @@ -282,21 +277,17 @@ def _core_iterator( circuit: The circuit to simulate. sim_state: The initial state args for the simulation in the computational basis. - qubits: 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. all_measurements_are_terminal: Indicator that all measurements are terminal, allowing optimization. Yields: DensityMatrixStepResult from simulating a Moment of the Circuit. """ - qubit_map = {q: i for i, q in enumerate(qubits)} if len(circuit) == 0: yield DensityMatrixStepResult( density_matrix=sim_state.target_tensor, measurements=dict(sim_state.log_of_measurement_results), - qubit_map=qubit_map, + qubit_map=sim_state.qubit_map, dtype=self._dtype, ) return @@ -315,13 +306,13 @@ def _core_iterator( continue if self._ignore_measurement_results: op = ops.phase_damp(1).on(*op.qubits) - sim_state.axes = tuple(qubit_map[qubit] for qubit in op.qubits) + sim_state.axes = tuple(sim_state.qubit_map[qubit] for qubit in op.qubits) protocols.act_on(op, sim_state) yield DensityMatrixStepResult( density_matrix=sim_state.target_tensor, measurements=dict(sim_state.log_of_measurement_results), - qubit_map=qubit_map, + qubit_map=sim_state.qubit_map, dtype=self._dtype, ) sim_state.log_of_measurement_results.clear() diff --git a/cirq/sim/simulator.py b/cirq/sim/simulator.py index 83c22d1a4ac..567b274d5ff 100644 --- a/cirq/sim/simulator.py +++ b/cirq/sim/simulator.py @@ -535,7 +535,7 @@ def _base_iterator( """ qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) act_on_args = self.create_act_on_args(initial_state, qubits) - return self._core_iterator(circuit, act_on_args, qubits) + return self._core_iterator(circuit, act_on_args) @abc.abstractmethod def create_act_on_args( @@ -565,7 +565,6 @@ def _core_iterator( self, circuit: circuits.Circuit, sim_state: TActOnArgs, - qubits: Tuple['cirq.Qid', ...], ) -> Iterator[TStepResult]: """Iterator over StepResult from Moments of a Circuit. @@ -576,9 +575,6 @@ def _core_iterator( sim_state: The initial args for the simulation. The form of this state depends on the simulation implementation. See documentation of the implementing class for details. - qubits: 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. Yields: StepResults from simulating a Moment of the Circuit. diff --git a/cirq/sim/sparse_simulator.py b/cirq/sim/sparse_simulator.py index 816c08c4f3d..7492ef286f1 100644 --- a/cirq/sim/sparse_simulator.py +++ b/cirq/sim/sparse_simulator.py @@ -187,7 +187,6 @@ def _run( for step_result in self._core_iterator( circuit=unitary_prefix, sim_state=act_on_args, - qubits=qubits, ): pass assert step_result is not None @@ -206,23 +205,19 @@ def _run( act_on_args=act_on_args, circuit=general_suffix, repetitions=repetitions, - qubits=qubits, ) def _brute_force_samples( self, act_on_args: act_on_state_vector_args.ActOnStateVectorArgs, circuit: circuits.Circuit, - qubits: Tuple['cirq.Qid', ...], repetitions: int, ) -> Dict[str, np.ndarray]: """Repeatedly simulate a circuit in order to produce samples.""" measurements: DefaultDict[str, List[np.ndarray]] = collections.defaultdict(list) for _ in range(repetitions): - all_step_results = self._core_iterator( - circuit, sim_state=act_on_args.copy(), qubits=qubits - ) + all_step_results = self._core_iterator(circuit, sim_state=act_on_args.copy()) for step_result in all_step_results: for k, v in step_result.measurements.items(): @@ -258,6 +253,7 @@ def create_act_on_args( return act_on_state_vector_args.ActOnStateVectorArgs( target_tensor=np.reshape(state, qid_shape), available_buffer=np.empty(qid_shape, dtype=self._dtype), + qubits=qubits, axes=[], prng=self._prng, log_of_measurement_results={}, @@ -267,7 +263,6 @@ def _core_iterator( self, circuit: circuits.Circuit, sim_state: act_on_state_vector_args.ActOnStateVectorArgs, - qubits: Tuple['cirq.Qid', ...], ): """Iterator over SparseSimulatorStep from Moments of a Circuit @@ -275,19 +270,15 @@ def _core_iterator( circuit: The circuit to simulate. sim_state: The initial state args for the simulation in the computational basis. - qubits: 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. Yields: SparseSimulatorStep from simulating a Moment of the Circuit. """ - qubit_map = {q: i for i, q in enumerate(qubits)} if len(circuit) == 0: yield SparseSimulatorStep( state_vector=sim_state.target_tensor, measurements=dict(sim_state.log_of_measurement_results), - qubit_map=qubit_map, + qubit_map=sim_state.qubit_map, dtype=self._dtype, ) return @@ -295,13 +286,13 @@ def _core_iterator( noisy_moments = self.noise.noisy_moments(circuit, sorted(circuit.all_qubits())) for op_tree in noisy_moments: for op in flatten_to_ops(op_tree): - sim_state.axes = tuple(qubit_map[qubit] for qubit in op.qubits) + sim_state.axes = tuple(sim_state.qubit_map[qubit] for qubit in op.qubits) protocols.act_on(op, sim_state) yield SparseSimulatorStep( state_vector=sim_state.target_tensor, measurements=dict(sim_state.log_of_measurement_results), - qubit_map=qubit_map, + qubit_map=sim_state.qubit_map, dtype=self._dtype, ) sim_state.log_of_measurement_results.clear() From d195016de60a7cfb81198cbfcc5f3b468bc843d4 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Mon, 12 Apr 2021 13:38:57 -0700 Subject: [PATCH 41/44] Change qubits param to Sequence. Clean up teleportation example. --- cirq/contrib/quimb/mps_simulator.py | 6 +++--- cirq/google/calibration/engine_simulator.py | 2 +- cirq/sim/act_on_args.py | 4 ++-- cirq/sim/act_on_density_matrix_args.py | 4 ++-- cirq/sim/act_on_state_vector_args.py | 4 ++-- .../clifford/act_on_clifford_tableau_args.py | 4 ++-- .../act_on_stabilizer_ch_form_args.py | 4 ++-- cirq/sim/clifford/clifford_simulator.py | 4 ++-- cirq/sim/density_matrix_simulator.py | 4 ++-- cirq/sim/simulator.py | 2 +- cirq/sim/sparse_simulator.py | 4 ++-- examples/quantum_teleportation.py | 21 +++++++++---------- 12 files changed, 31 insertions(+), 32 deletions(-) diff --git a/cirq/contrib/quimb/mps_simulator.py b/cirq/contrib/quimb/mps_simulator.py index a6b6b563a33..79836baad39 100644 --- a/cirq/contrib/quimb/mps_simulator.py +++ b/cirq/contrib/quimb/mps_simulator.py @@ -19,7 +19,7 @@ import dataclasses import math -from typing import Any, Dict, List, Optional, Sequence, Set, TYPE_CHECKING, Iterable, Tuple, Union +from typing import Any, Dict, List, Optional, Sequence, Set, TYPE_CHECKING, Iterable, Union import numpy as np import quimb.tensor as qtn @@ -87,7 +87,7 @@ def __init__( def create_act_on_args( self, initial_state: Union[int, 'MPSState'], - qubits: Tuple['cirq.Qid', ...], + qubits: Sequence['cirq.Qid'], ) -> 'MPSState': """Creates MPSState args for simulating the Circuit. @@ -298,7 +298,7 @@ class MPSState(ActOnArgs): def __init__( self, - qubits: Iterable['cirq.Qid'], + qubits: Sequence['cirq.Qid'], prng: np.random.RandomState, simulation_options: MPSOptions = MPSOptions(), grouping: Optional[Dict['cirq.Qid', int]] = None, diff --git a/cirq/google/calibration/engine_simulator.py b/cirq/google/calibration/engine_simulator.py index 03c5aae939a..10c76ed649f 100644 --- a/cirq/google/calibration/engine_simulator.py +++ b/cirq/google/calibration/engine_simulator.py @@ -402,7 +402,7 @@ def _core_iterator( def create_act_on_args( self, initial_state: Union[int, ActOnStateVectorArgs], - qubits: Tuple[Qid, ...], + qubits: Sequence[Qid], ): return self._simulator.create_act_on_args(initial_state, qubits) diff --git a/cirq/sim/act_on_args.py b/cirq/sim/act_on_args.py index 46f7971db38..33eb2fab7b4 100644 --- a/cirq/sim/act_on_args.py +++ b/cirq/sim/act_on_args.py @@ -13,7 +13,7 @@ # limitations under the License. """Objects and methods for acting efficiently on a state tensor.""" import abc -from typing import Any, Iterable, Dict, List, TypeVar, TYPE_CHECKING +from typing import Any, Iterable, Dict, List, TypeVar, TYPE_CHECKING, Sequence import numpy as np @@ -32,7 +32,7 @@ class ActOnArgs: def __init__( self, prng: np.random.RandomState, - qubits: Iterable['cirq.Qid'] = None, + qubits: Sequence['cirq.Qid'] = None, axes: Iterable[int] = None, log_of_measurement_results: Dict[str, Any] = None, ): diff --git a/cirq/sim/act_on_density_matrix_args.py b/cirq/sim/act_on_density_matrix_args.py index 8263a0a038a..a029772bd61 100644 --- a/cirq/sim/act_on_density_matrix_args.py +++ b/cirq/sim/act_on_density_matrix_args.py @@ -13,7 +13,7 @@ # limitations under the License. """Objects and methods for acting efficiently on a density matrix.""" -from typing import Any, Iterable, Dict, List, Tuple, TYPE_CHECKING +from typing import Any, Iterable, Dict, List, Tuple, TYPE_CHECKING, Sequence import numpy as np @@ -39,7 +39,7 @@ def __init__( qid_shape: Tuple[int, ...], prng: np.random.RandomState, log_of_measurement_results: Dict[str, Any], - qubits: Iterable['cirq.Qid'] = None, + qubits: Sequence['cirq.Qid'] = None, ): """ Args: diff --git a/cirq/sim/act_on_state_vector_args.py b/cirq/sim/act_on_state_vector_args.py index 91c84d0e3d9..9fb5dcaa46f 100644 --- a/cirq/sim/act_on_state_vector_args.py +++ b/cirq/sim/act_on_state_vector_args.py @@ -13,7 +13,7 @@ # limitations under the License. """Objects and methods for acting efficiently on a state vector.""" -from typing import Any, Iterable, Tuple, TYPE_CHECKING, Union, Dict, List +from typing import Any, Iterable, Tuple, TYPE_CHECKING, Union, Dict, List, Sequence import numpy as np @@ -43,7 +43,7 @@ def __init__( axes: Iterable[int], prng: np.random.RandomState, log_of_measurement_results: Dict[str, Any], - qubits: Iterable['cirq.Qid'] = None, + qubits: Sequence['cirq.Qid'] = None, ): """ Args: diff --git a/cirq/sim/clifford/act_on_clifford_tableau_args.py b/cirq/sim/clifford/act_on_clifford_tableau_args.py index 4dd550dceb4..312e4b18430 100644 --- a/cirq/sim/clifford/act_on_clifford_tableau_args.py +++ b/cirq/sim/clifford/act_on_clifford_tableau_args.py @@ -14,7 +14,7 @@ """A protocol for implementing high performance clifford tableau evolutions for Clifford Simulator.""" -from typing import Any, Dict, Iterable, TYPE_CHECKING, List +from typing import Any, Dict, Iterable, TYPE_CHECKING, List, Sequence import numpy as np @@ -43,7 +43,7 @@ def __init__( axes: Iterable[int], prng: np.random.RandomState, log_of_measurement_results: Dict[str, Any], - qubits: Iterable['cirq.Qid'] = None, + qubits: Sequence['cirq.Qid'] = None, ): """ Args: diff --git a/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py b/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py index 06b4e6c2e6e..071a2a6c21b 100644 --- a/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py +++ b/cirq/sim/clifford/act_on_stabilizer_ch_form_args.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Any, Dict, Iterable, TYPE_CHECKING, List +from typing import Any, Dict, Iterable, TYPE_CHECKING, List, Sequence import numpy as np @@ -40,7 +40,7 @@ def __init__( axes: Iterable[int], prng: np.random.RandomState, log_of_measurement_results: Dict[str, Any], - qubits: Iterable['cirq.Qid'] = None, + qubits: Sequence['cirq.Qid'] = None, ): """Initializes with the given state and the axes for the operation. Args: diff --git a/cirq/sim/clifford/clifford_simulator.py b/cirq/sim/clifford/clifford_simulator.py index a25b8b67b83..6730d38f5b7 100644 --- a/cirq/sim/clifford/clifford_simulator.py +++ b/cirq/sim/clifford/clifford_simulator.py @@ -29,7 +29,7 @@ to state vector amplitudes. """ -from typing import Any, Dict, List, Sequence, Tuple, Union +from typing import Any, Dict, List, Sequence, Union import numpy as np @@ -71,7 +71,7 @@ def is_supported_operation(op: 'cirq.Operation') -> bool: def create_act_on_args( self, initial_state: Union[int, clifford.ActOnStabilizerCHFormArgs], - qubits: Tuple['cirq.Qid', ...], + qubits: Sequence['cirq.Qid'], ) -> clifford.ActOnStabilizerCHFormArgs: """Creates the ActOnStabilizerChFormArgs for a circuit. diff --git a/cirq/sim/density_matrix_simulator.py b/cirq/sim/density_matrix_simulator.py index f31b6cb5841..77f6888df9a 100644 --- a/cirq/sim/density_matrix_simulator.py +++ b/cirq/sim/density_matrix_simulator.py @@ -13,7 +13,7 @@ # limitations under the License. """Simulator for density matrices that simulates noisy quantum circuits.""" import collections -from typing import Any, Dict, List, TYPE_CHECKING, Tuple, Union +from typing import Any, Dict, List, TYPE_CHECKING, Tuple, Union, Sequence import numpy as np @@ -230,7 +230,7 @@ def _run_sweep_repeat( def create_act_on_args( self, initial_state: Union[np.ndarray, 'cirq.STATE_VECTOR_LIKE', 'cirq.ActOnDensityMatrixArgs'], - qubits: Tuple['cirq.Qid', ...], + qubits: Sequence['cirq.Qid'], ) -> 'cirq.ActOnDensityMatrixArgs': """Creates the ActOnDensityMatrixArgs for a circuit. diff --git a/cirq/sim/simulator.py b/cirq/sim/simulator.py index 567b274d5ff..fad89c58b54 100644 --- a/cirq/sim/simulator.py +++ b/cirq/sim/simulator.py @@ -541,7 +541,7 @@ def _base_iterator( def create_act_on_args( self, initial_state: Any, - qubits: Tuple['cirq.Qid', ...], + qubits: Sequence['cirq.Qid'], ) -> TActOnArgs: """Creates the ActOnArgs state for a simulator. diff --git a/cirq/sim/sparse_simulator.py b/cirq/sim/sparse_simulator.py index 7492ef286f1..ea46d1e4d80 100644 --- a/cirq/sim/sparse_simulator.py +++ b/cirq/sim/sparse_simulator.py @@ -24,7 +24,7 @@ DefaultDict, Union, cast, - Tuple, + Sequence, ) import numpy as np @@ -227,7 +227,7 @@ def _brute_force_samples( def create_act_on_args( self, initial_state: Union['cirq.STATE_VECTOR_LIKE', 'cirq.ActOnStateVectorArgs'], - qubits: Tuple['cirq.Qid', ...], + qubits: Sequence['cirq.Qid'], ): """Creates the ActOnStateVectorArgs for a circuit. diff --git a/examples/quantum_teleportation.py b/examples/quantum_teleportation.py index c21154c8817..963b542e414 100644 --- a/examples/quantum_teleportation.py +++ b/examples/quantum_teleportation.py @@ -62,15 +62,14 @@ def _run( sim: SimulatesIntermediateState[ TStepResult, TSimulationTrialResult, TSimulatorState, TActOnArgs ] -) -> Tuple[TSimulationTrialResult, TStepResult]: +) -> Tuple[TSimulationTrialResult, TSimulationTrialResult]: # Initialize our qubit state space. - msg, alice, bob = cirq.LineQubit.range(3) - qubits = (msg, alice, bob) + msg, alice, bob = qubits = cirq.LineQubit.range(3) args = sim.create_act_on_args(0, qubits) # First we create a bell state circuit and simulate it on the qubits. bell_circuit = cirq.Circuit(cirq.H(alice), cirq.CNOT(alice, bob)) - list(sim.simulate_moment_steps(bell_circuit, None, qubits, args)) + sim.simulate(bell_circuit, initial_state=args) print('\nBell Circuit:') print(bell_circuit) @@ -81,7 +80,7 @@ def _run( cirq.X(msg) ** rand_x, cirq.Y(msg) ** rand_y, ) - list(sim.simulate_moment_steps(msg_circuit, None, qubits, args)) + sim.simulate(msg_circuit, initial_state=args) print('\nMessage Circuit:') print(msg_circuit) @@ -92,9 +91,9 @@ def _run( cirq.measure(alice, key='x_fixup'), cirq.measure(msg, key='z_fixup'), ) - alice_results = list(sim.simulate_moment_steps(alice_circuit, None, qubits, args)) - x_fixup = alice_results[1].measurements['x_fixup'] == [1] - z_fixup = alice_results[2].measurements['z_fixup'] == [1] + alice_results = sim.simulate(alice_circuit, initial_state=args) + x_fixup = alice_results.measurements['x_fixup'] == [1] + z_fixup = alice_results.measurements['z_fixup'] == [1] print('\nAlice Circuit:') print(alice_circuit) print(f'x_fixup={x_fixup}') @@ -108,7 +107,7 @@ def _run( if z_fixup: bob_circuit.append(cirq.Z(bob)) # coverage: ignore - *_, final_results = sim.simulate_moment_steps(bob_circuit, None, qubits, args) + final_results = sim.simulate(bob_circuit, initial_state=args) print('\nBob Circuit:') print(bob_circuit) @@ -134,7 +133,7 @@ def main(seed=None): expected = cirq.bloch_vector_from_state_vector(message.final_density_matrix, 0) _print_bloch(expected) print('\nBloch Sphere of Qubit 2 at Final State:') - teleported = cirq.bloch_vector_from_state_vector(final_results._density_matrix, 2) + teleported = cirq.bloch_vector_from_state_vector(final_results.final_density_matrix, 2) _print_bloch(teleported) # Run with sparse simulator @@ -145,7 +144,7 @@ def main(seed=None): expected = cirq.bloch_vector_from_state_vector(message.final_state_vector, 0) _print_bloch(expected) print('\nBloch Sphere of Qubit 2 at Final State:') - teleported = cirq.bloch_vector_from_state_vector(final_results.state_vector(), 2) + teleported = cirq.bloch_vector_from_state_vector(final_results.final_state_vector, 2) _print_bloch(teleported) return expected, teleported From 5ad813c43cf146659307a75ffac2f1c98d005686 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Mon, 19 Apr 2021 12:45:59 -0700 Subject: [PATCH 42/44] PR comments: * create_act_on_args private * Allow CliffordState to take ch_form as an initial state * type annotations * revert quantum teleportation example --- cirq/contrib/quimb/mps_simulator.py | 2 +- cirq/contrib/quimb/mps_simulator_test.py | 2 +- cirq/google/calibration/engine_simulator.py | 11 +- cirq/sim/clifford/clifford_simulator.py | 10 +- cirq/sim/clifford/clifford_simulator_test.py | 2 +- cirq/sim/density_matrix_simulator.py | 4 +- cirq/sim/density_matrix_simulator_test.py | 2 +- cirq/sim/simulator.py | 4 +- cirq/sim/sparse_simulator.py | 4 +- cirq/sim/sparse_simulator_test.py | 2 +- examples/examples_test.py | 2 +- examples/quantum_teleportation.py | 144 +++++++------------ 12 files changed, 73 insertions(+), 116 deletions(-) diff --git a/cirq/contrib/quimb/mps_simulator.py b/cirq/contrib/quimb/mps_simulator.py index 79836baad39..db41ce3b5b3 100644 --- a/cirq/contrib/quimb/mps_simulator.py +++ b/cirq/contrib/quimb/mps_simulator.py @@ -84,7 +84,7 @@ def __init__( self.simulation_options = simulation_options self.grouping = grouping - def create_act_on_args( + def _create_act_on_args( self, initial_state: Union[int, 'MPSState'], qubits: Sequence['cirq.Qid'], diff --git a/cirq/contrib/quimb/mps_simulator_test.py b/cirq/contrib/quimb/mps_simulator_test.py index e9780b29e18..f9053d12b5a 100644 --- a/cirq/contrib/quimb/mps_simulator_test.py +++ b/cirq/contrib/quimb/mps_simulator_test.py @@ -211,7 +211,7 @@ def test_act_on_args(): mps_simulator = ccq.mps_simulator.MPSSimulator() ref_simulator = cirq.Simulator() for initial_state in range(4): - args = mps_simulator.create_act_on_args(initial_state=initial_state, qubits=(q0, q1)) + args = mps_simulator._create_act_on_args(initial_state=initial_state, qubits=(q0, q1)) actual = mps_simulator.simulate(circuit, qubit_order=qubit_order, initial_state=args) expected = ref_simulator.simulate( circuit, qubit_order=qubit_order, initial_state=initial_state diff --git a/cirq/google/calibration/engine_simulator.py b/cirq/google/calibration/engine_simulator.py index 10c76ed649f..535b9935a5a 100644 --- a/cirq/google/calibration/engine_simulator.py +++ b/cirq/google/calibration/engine_simulator.py @@ -7,7 +7,7 @@ Optional, Sequence, Tuple, - Union, + Union, Iterator, ) import numpy as np @@ -37,6 +37,7 @@ Simulator, SimulatesSamples, SimulatesIntermediateStateVector, + StateVectorStepResult, ActOnStateVectorArgs, ) from cirq.study import ParamResolver @@ -395,16 +396,16 @@ def _core_iterator( self, circuit: Circuit, sim_state: Any, - ): + ) -> Iterator[StateVectorStepResult]: converted = _convert_to_circuit_with_drift(self, circuit) return self._simulator._core_iterator(converted, sim_state) - def create_act_on_args( + def _create_act_on_args( self, initial_state: Union[int, ActOnStateVectorArgs], qubits: Sequence[Qid], - ): - return self._simulator.create_act_on_args(initial_state, qubits) + ) -> ActOnStateVectorArgs: + return self._simulator._create_act_on_args(initial_state, qubits) class _PhasedFSimConverter(PointOptimizer): diff --git a/cirq/sim/clifford/clifford_simulator.py b/cirq/sim/clifford/clifford_simulator.py index 6730d38f5b7..700b927fb6e 100644 --- a/cirq/sim/clifford/clifford_simulator.py +++ b/cirq/sim/clifford/clifford_simulator.py @@ -68,7 +68,7 @@ def is_supported_operation(op: 'cirq.Operation') -> bool: # TODO: support more general Pauli measurements return protocols.has_stabilizer_effect(op) - def create_act_on_args( + def _create_act_on_args( self, initial_state: Union[int, clifford.ActOnStabilizerCHFormArgs], qubits: Sequence['cirq.Qid'], @@ -116,9 +116,7 @@ def _core_iterator( """ def create_state(): - state = CliffordState(sim_state.qubit_map) - state.ch_form = sim_state.state.copy() - return state + return CliffordState(sim_state.qubit_map, sim_state.state.copy()) if len(circuit) == 0: yield CliffordSimulatorStepResult( @@ -261,11 +259,11 @@ class CliffordState: Gates and measurements are applied to each representation in O(n^2) time. """ - def __init__(self, qubit_map, initial_state=0): + def __init__(self, qubit_map, initial_state: Union[int, clifford.StabilizerStateChForm] = 0): self.qubit_map = qubit_map self.n = len(qubit_map) - self.ch_form = clifford.StabilizerStateChForm(self.n, initial_state) + self.ch_form = initial_state if isinstance(initial_state, clifford.StabilizerStateChForm) else clifford.StabilizerStateChForm(self.n, initial_state) def _json_dict_(self): return { diff --git a/cirq/sim/clifford/clifford_simulator_test.py b/cirq/sim/clifford/clifford_simulator_test.py index 9e5cdb1f516..2b9810370ef 100644 --- a/cirq/sim/clifford/clifford_simulator_test.py +++ b/cirq/sim/clifford/clifford_simulator_test.py @@ -109,7 +109,7 @@ def test_simulate_act_on_args(): circuit.append(cirq.X(q1)) circuit.append(cirq.measure(q0, q1)) - args = simulator.create_act_on_args(initial_state=1, qubits=(q0, q1)) + args = simulator._create_act_on_args(initial_state=1, qubits=(q0, q1)) result = simulator.simulate(circuit, initial_state=args) expected_state = np.zeros(shape=(2, 2)) expected_state[b0][1 - b1] = 1.0 diff --git a/cirq/sim/density_matrix_simulator.py b/cirq/sim/density_matrix_simulator.py index 77f6888df9a..ba311385f94 100644 --- a/cirq/sim/density_matrix_simulator.py +++ b/cirq/sim/density_matrix_simulator.py @@ -171,7 +171,7 @@ def _run( resolved_circuit = protocols.resolve_parameters(circuit, param_resolver) check_all_resolved(resolved_circuit) qubits = tuple(sorted(resolved_circuit.all_qubits())) - act_on_args = self.create_act_on_args(0, qubits) + act_on_args = self._create_act_on_args(0, qubits) prefix, general_suffix = split_into_matching_protocol_then_general( resolved_circuit, lambda op: not protocols.is_measurement(op) @@ -227,7 +227,7 @@ def _run_sweep_repeat( measurements[k].append(np.array(v, dtype=np.uint8)) return {k: np.array(v) for k, v in measurements.items()} - def create_act_on_args( + def _create_act_on_args( self, initial_state: Union[np.ndarray, 'cirq.STATE_VECTOR_LIKE', 'cirq.ActOnDensityMatrixArgs'], qubits: Sequence['cirq.Qid'], diff --git a/cirq/sim/density_matrix_simulator_test.py b/cirq/sim/density_matrix_simulator_test.py index c2e3448535f..3a78a81dbb9 100644 --- a/cirq/sim/density_matrix_simulator_test.py +++ b/cirq/sim/density_matrix_simulator_test.py @@ -567,7 +567,7 @@ def test_simulate_act_on_args(dtype): for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit((cirq.X ** b0)(q0), (cirq.X ** b1)(q1)) - args = simulator.create_act_on_args(initial_state=1, qubits=(q0, q1)) + args = simulator._create_act_on_args(initial_state=1, qubits=(q0, q1)) result = simulator.simulate(circuit, initial_state=args) expected_density_matrix = np.zeros(shape=(4, 4)) expected_density_matrix[b0 * 2 + 1 - b1, b0 * 2 + 1 - b1] = 1.0 diff --git a/cirq/sim/simulator.py b/cirq/sim/simulator.py index fad89c58b54..2feea53bc6c 100644 --- a/cirq/sim/simulator.py +++ b/cirq/sim/simulator.py @@ -534,11 +534,11 @@ def _base_iterator( StepResults from simulating a Moment of the Circuit. """ qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(circuit.all_qubits()) - act_on_args = self.create_act_on_args(initial_state, qubits) + act_on_args = self._create_act_on_args(initial_state, qubits) return self._core_iterator(circuit, act_on_args) @abc.abstractmethod - def create_act_on_args( + def _create_act_on_args( self, initial_state: Any, qubits: Sequence['cirq.Qid'], diff --git a/cirq/sim/sparse_simulator.py b/cirq/sim/sparse_simulator.py index ea46d1e4d80..6058eacff84 100644 --- a/cirq/sim/sparse_simulator.py +++ b/cirq/sim/sparse_simulator.py @@ -174,7 +174,7 @@ def _run( resolved_circuit = protocols.resolve_parameters(circuit, param_resolver) check_all_resolved(resolved_circuit) qubits = tuple(sorted(resolved_circuit.all_qubits())) - act_on_args = self.create_act_on_args(0, qubits) + act_on_args = self._create_act_on_args(0, qubits) # Simulate as many unitary operations as possible before having to # repeat work for each sample. @@ -224,7 +224,7 @@ def _brute_force_samples( measurements[k].append(np.array(v, dtype=np.uint8)) return {k: np.array(v) for k, v in measurements.items()} - def create_act_on_args( + def _create_act_on_args( self, initial_state: Union['cirq.STATE_VECTOR_LIKE', 'cirq.ActOnStateVectorArgs'], qubits: Sequence['cirq.Qid'], diff --git a/cirq/sim/sparse_simulator_test.py b/cirq/sim/sparse_simulator_test.py index 255bffa6e48..3590281d500 100644 --- a/cirq/sim/sparse_simulator_test.py +++ b/cirq/sim/sparse_simulator_test.py @@ -456,7 +456,7 @@ def test_simulate_act_on_args(dtype): for b0 in [0, 1]: for b1 in [0, 1]: circuit = cirq.Circuit((cirq.X ** b0)(q0), (cirq.X ** b1)(q1)) - args = simulator.create_act_on_args(initial_state=1, qubits=(q0, q1)) + args = simulator._create_act_on_args(initial_state=1, qubits=(q0, q1)) result = simulator.simulate(circuit, initial_state=args) expected_state = np.zeros(shape=(2, 2)) expected_state[b0][1 - b1] = 1.0 diff --git a/examples/examples_test.py b/examples/examples_test.py index 4c726b5e124..ccf49d1364c 100644 --- a/examples/examples_test.py +++ b/examples/examples_test.py @@ -102,7 +102,7 @@ def test_example_runs_qaoa(): def test_example_runs_quantum_teleportation(): _, teleported = examples.quantum_teleportation.main(seed=12) - assert np.allclose(np.array([-0.21627635, -0.86642754, -0.45003086]), teleported) + assert np.allclose(np.array([0.07023552, -0.9968105, -0.03788921]), teleported) def test_example_runs_superdense_coding(): diff --git a/examples/quantum_teleportation.py b/examples/quantum_teleportation.py index 963b542e414..489f4a52020 100644 --- a/examples/quantum_teleportation.py +++ b/examples/quantum_teleportation.py @@ -34,87 +34,26 @@ """ import random -from typing import Tuple - import numpy as np import cirq -from cirq.sim.simulator import ( - TStepResult, - SimulatesIntermediateState, - TSimulationTrialResult, - TSimulatorState, - TActOnArgs, -) - - -def _print_bloch(vector): - print( - 'x: ', - np.around(vector[0], 4), - 'y: ', - np.around(vector[1], 4), - 'z: ', - np.around(vector[2], 4), - ) - - -def _run( - sim: SimulatesIntermediateState[ - TStepResult, TSimulationTrialResult, TSimulatorState, TActOnArgs - ] -) -> Tuple[TSimulationTrialResult, TSimulationTrialResult]: - # Initialize our qubit state space. - msg, alice, bob = qubits = cirq.LineQubit.range(3) - args = sim.create_act_on_args(0, qubits) - - # First we create a bell state circuit and simulate it on the qubits. - bell_circuit = cirq.Circuit(cirq.H(alice), cirq.CNOT(alice, bob)) - sim.simulate(bell_circuit, initial_state=args) - print('\nBell Circuit:') - print(bell_circuit) - - # Second we randomize the message qubit. - rand_x = random.random() - rand_y = random.random() - msg_circuit = cirq.Circuit( - cirq.X(msg) ** rand_x, - cirq.Y(msg) ** rand_y, - ) - sim.simulate(msg_circuit, initial_state=args) - print('\nMessage Circuit:') - print(msg_circuit) - - # Now we measure on Alice's side - alice_circuit = cirq.Circuit( - cirq.CNOT(msg, alice), - cirq.H(msg), - cirq.measure(alice, key='x_fixup'), - cirq.measure(msg, key='z_fixup'), - ) - alice_results = sim.simulate(alice_circuit, initial_state=args) - x_fixup = alice_results.measurements['x_fixup'] == [1] - z_fixup = alice_results.measurements['z_fixup'] == [1] - print('\nAlice Circuit:') - print(alice_circuit) - print(f'x_fixup={x_fixup}') - print(f'z_fixup={z_fixup}') - - # Finally we construct Bob's circuit based on Alice's measurements - bob_circuit = cirq.Circuit() - if x_fixup: - bob_circuit.append(cirq.X(bob)) # coverage: ignore - if z_fixup: - bob_circuit.append(cirq.Z(bob)) # coverage: ignore - final_results = sim.simulate(bob_circuit, initial_state=args) - print('\nBob Circuit:') - print(bob_circuit) +def make_quantum_teleportation_circuit(ranX, ranY): + circuit = cirq.Circuit() + msg, alice, bob = cirq.LineQubit.range(3) - # We simulate our message circuit separately for comparison - message = sim.simulate(msg_circuit) + # Creates Bell state to be shared between Alice and Bob. + circuit.append([cirq.H(alice), cirq.CNOT(alice, bob)]) + # Creates a random state for the Message. + circuit.append([cirq.X(msg) ** ranX, cirq.Y(msg) ** ranY]) + # Bell measurement of the Message and Alice's entangled qubit. + circuit.append([cirq.CNOT(msg, alice), cirq.H(msg)]) + circuit.append(cirq.measure(msg, alice)) + # Uses the two classical bits from the Bell measurement to recover the + # original quantum Message on Bob's entangled qubit. + circuit.append([cirq.CNOT(alice, bob), cirq.CZ(msg, bob)]) - return message, final_results + return circuit def main(seed=None): @@ -125,27 +64,46 @@ def main(seed=None): """ random.seed(seed) - # Run with density matrix simulator - print('***Run with density matrix simulator***') - sim = cirq.DensityMatrixSimulator(seed=seed) - message, final_results = _run(sim) - print('\nBloch Sphere of Message After Random X and Y Gates:') - expected = cirq.bloch_vector_from_state_vector(message.final_density_matrix, 0) - _print_bloch(expected) - print('\nBloch Sphere of Qubit 2 at Final State:') - teleported = cirq.bloch_vector_from_state_vector(final_results.final_density_matrix, 2) - _print_bloch(teleported) - - # Run with sparse simulator - print('\n\n\n\n\n***Run with sparse simulator***') + ranX = random.random() + ranY = random.random() + circuit = make_quantum_teleportation_circuit(ranX, ranY) + + print("Circuit:") + print(circuit) + sim = cirq.Simulator(seed=seed) - message, final_results = _run(sim) - print('\nBloch Sphere of Message After Random X and Y Gates:') + + # Run a simple simulation that applies the random X and Y gates that + # create our message. + q0 = cirq.LineQubit(0) + message = sim.simulate(cirq.Circuit([cirq.X(q0) ** ranX, cirq.Y(q0) ** ranY])) + + print("\nBloch Sphere of Message After Random X and Y Gates:") + # Prints the Bloch Sphere of the Message after the X and Y gates. expected = cirq.bloch_vector_from_state_vector(message.final_state_vector, 0) - _print_bloch(expected) - print('\nBloch Sphere of Qubit 2 at Final State:') + print( + "x: ", + np.around(expected[0], 4), + "y: ", + np.around(expected[1], 4), + "z: ", + np.around(expected[2], 4), + ) + + # Records the final state of the simulation. + final_results = sim.simulate(circuit) + + print("\nBloch Sphere of Qubit 2 at Final State:") + # Prints the Bloch Sphere of Bob's entangled qubit at the final state. teleported = cirq.bloch_vector_from_state_vector(final_results.final_state_vector, 2) - _print_bloch(teleported) + print( + "x: ", + np.around(teleported[0], 4), + "y: ", + np.around(teleported[1], 4), + "z: ", + np.around(teleported[2], 4), + ) return expected, teleported From f319a225e8bb3156f3965a6523cb80e9218de486 Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Mon, 19 Apr 2021 12:56:53 -0700 Subject: [PATCH 43/44] format/lint --- cirq/google/calibration/engine_simulator.py | 3 ++- cirq/sim/clifford/clifford_simulator.py | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/cirq/google/calibration/engine_simulator.py b/cirq/google/calibration/engine_simulator.py index 535b9935a5a..cc8c8d2d389 100644 --- a/cirq/google/calibration/engine_simulator.py +++ b/cirq/google/calibration/engine_simulator.py @@ -7,7 +7,8 @@ Optional, Sequence, Tuple, - Union, Iterator, + Union, + Iterator, ) import numpy as np diff --git a/cirq/sim/clifford/clifford_simulator.py b/cirq/sim/clifford/clifford_simulator.py index 700b927fb6e..9f440570fca 100644 --- a/cirq/sim/clifford/clifford_simulator.py +++ b/cirq/sim/clifford/clifford_simulator.py @@ -263,7 +263,11 @@ def __init__(self, qubit_map, initial_state: Union[int, clifford.StabilizerState self.qubit_map = qubit_map self.n = len(qubit_map) - self.ch_form = initial_state if isinstance(initial_state, clifford.StabilizerStateChForm) else clifford.StabilizerStateChForm(self.n, initial_state) + self.ch_form = ( + initial_state + if isinstance(initial_state, clifford.StabilizerStateChForm) + else clifford.StabilizerStateChForm(self.n, initial_state) + ) def _json_dict_(self): return { From 0e7ee06e58c72aca83c3f4bfc38c1cf9aa55bd4a Mon Sep 17 00:00:00 2001 From: Dax Fohl Date: Mon, 19 Apr 2021 21:02:53 -0700 Subject: [PATCH 44/44] format --- cirq-google/cirq_google/calibration/engine_simulator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq-google/cirq_google/calibration/engine_simulator.py b/cirq-google/cirq_google/calibration/engine_simulator.py index 586b85fbfa8..bb1998e43da 100644 --- a/cirq-google/cirq_google/calibration/engine_simulator.py +++ b/cirq-google/cirq_google/calibration/engine_simulator.py @@ -444,4 +444,4 @@ def _convert_to_circuit_with_drift( circuit_with_drift = Circuit(circuit) converter = _PhasedFSimConverter(simulator) converter.optimize_circuit(circuit_with_drift) - return circuit_with_drift \ No newline at end of file + return circuit_with_drift