Esempio n. 1
0
    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)
Esempio n. 2
0
    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
Esempio n. 3
0
    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()}
Esempio n. 4
0
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))