def _to_target_circuit_type(circuit: circuits.AbstractCircuit, target_circuit: CIRCUIT_TYPE) -> CIRCUIT_TYPE: return cast( CIRCUIT_TYPE, circuit.unfreeze(copy=False) if isinstance(target_circuit, circuits.Circuit) else circuit.freeze(), )
def _base_iterator( self, circuit: circuits.AbstractCircuit, qubit_order: ops.QubitOrderOrList, initial_state: Any, ) -> Iterator[TStepResult]: """Iterator over StepResult from Moments of a Circuit. 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 simulators. 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. """ 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)
def _core_iterator( self, circuit: circuits.AbstractCircuit, sim_state: OperationTarget[TActOnArgs], all_measurements_are_terminal: bool = False, ) -> Iterator[TStepResultBase]: """Standard iterator over StepResult from Moments of a Circuit. Args: circuit: The circuit to simulate. 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. all_measurements_are_terminal: Whether all measurements in the given circuit are terminal. Yields: StepResults from simulating a Moment of the Circuit. Raises: TypeError: The simulator encounters an op it does not support. """ if len(circuit) == 0: yield self._create_step_result(sim_state) return noisy_moments = self.noise.noisy_moments(circuit, sorted(circuit.all_qubits())) measured: Dict[Tuple['cirq.Qid', ...], bool] = collections.defaultdict(bool) for moment in noisy_moments: for op in ops.flatten_to_ops(moment): try: # TODO: support more general measurements. # Github issue: https://github.com/quantumlib/Cirq/issues/3566 # Preprocess measurements if all_measurements_are_terminal and measured[op.qubits]: continue if isinstance(op.gate, ops.MeasurementGate): measured[op.qubits] = True if all_measurements_are_terminal: continue if self._ignore_measurement_results: op = ops.phase_damp(1).on(*op.qubits) # Simulate the operation protocols.act_on(op, sim_state) except TypeError: raise TypeError( f"{self.__class__.__name__} doesn't support {op!r}") step_result = self._create_step_result(sim_state) yield step_result sim_state = step_result._sim_state
def _run(self, circuit: circuits.AbstractCircuit, repetitions: int) -> Dict[str, np.ndarray]: measurements: Dict[str, List[int]] = { key: [] for key in protocols.measurement_key_names(circuit) } qubits = circuit.all_qubits() for _ in range(repetitions): state = ActOnCliffordTableauArgs( CliffordTableau(num_qubits=len(qubits)), qubits=list(qubits), prng=self._prng, log_of_measurement_results={}, ) for op in circuit.all_operations(): protocols.act_on(op, state) for k, v in state.log_of_measurement_results.items(): measurements[k].append(v) return {k: np.array(v) for k, v in measurements.items()}
def assert_circuits_with_terminal_measurements_are_equivalent( actual: circuits.AbstractCircuit, reference: circuits.AbstractCircuit, atol: float) -> None: """Determines if two circuits have equivalent effects. The circuits can contain measurements, but the measurements must be at the end of the circuit. Circuits are equivalent if, for all possible inputs, their outputs (classical bits for lines terminated with measurement and qubits for lines without measurement) are observationally indistinguishable up to a tolerance. Note that under this definition of equivalence circuits that differ solely in the overall phase of the post-measurement state of measured qubits are considered equivalent. For example, applying an extra Z gate to an unmeasured qubit changes the effect of a circuit. But inserting a Z gate operation just before a measurement does not. Args: actual: The circuit that was actually computed by some process. reference: A circuit with the correct function. atol: Absolute error tolerance. """ # pylint: disable=unused-variable __tracebackhide__ = True # pylint: enable=unused-variable measured_qubits_actual = { qubit for op in actual.all_operations() if protocols.is_measurement(op) for qubit in op.qubits } measured_qubits_reference = { qubit for op in reference.all_operations() if protocols.is_measurement(op) for qubit in op.qubits } assert actual.are_all_measurements_terminal() assert reference.are_all_measurements_terminal() assert measured_qubits_actual == measured_qubits_reference all_qubits = actual.all_qubits().union(reference.all_qubits()) matrix_actual = actual.unitary(qubits_that_should_be_present=all_qubits) matrix_reference = reference.unitary( qubits_that_should_be_present=all_qubits) n_qubits = len(all_qubits) n = matrix_actual.shape[0] assert n == 1 << n_qubits assert matrix_actual.shape == matrix_reference.shape == (n, n) # Consider the action of the two circuits Ca and Cr on state |x>: # # |ya> = Ca|x> # |yr> = Cr|x> # # Ca and Cr are equivalent according to the definition above iff # for each |x>: # - probability of each measurement outcome is the same for |ya> # and |yr> (across measured qubits), # - amplitudes of each post-measurement state are the same for |ya> # and |yr> except perhaps for an overall phase factor. # # These conditions are satisfied iff the matrices of the two circuits # are identical except perhaps for an overall phase factor for each # rectangular block spanning rows corresponding to the measurement # subspaces and all columns. # # Note two special cases of the rule above: # - if no qubits are measured then the circuits are equivalent if # their matrices are identical except for the global phase factor, # - if all qubits are measured then the circuits are equivalent if # their matrices differ by a diagonal unitary factor. subspaces = _measurement_subspaces(measured_qubits_actual, n_qubits) for subspace in subspaces: block_actual = matrix_actual[subspace, :] block_reference = matrix_reference[subspace, :] assert linalg.allclose_up_to_global_phase( block_actual, block_reference, atol=atol), ( "Circuit's effect differs from the reference circuit.\n" '\n' 'Diagram of actual circuit:\n' '{}\n' '\n' 'Diagram of reference circuit with desired function:\n' '{}\n'.format(actual, reference))
def zip( *circuits: 'cirq.AbstractCircuit', align: Union['cirq.Alignment', str] = Alignment.LEFT ) -> 'cirq.FrozenCircuit': return AbstractCircuit.zip(*circuits, align=align).freeze()
def validate_circuit(self, circuit: circuits.AbstractCircuit): super().validate_circuit(circuit) _verify_unique_measurement_keys(circuit.all_operations())