Ejemplo n.º 1
0
    def expectation_from_density_matrix(
        self,
        state: np.ndarray,
        qubit_map: Mapping[raw_types.Qid, int],
        *,
        atol: float = 1e-7,
        check_preconditions: bool = True,
    ) -> float:
        """Evaluate the expectation of this PauliSum given a density matrix.

        See `PauliString.expectation_from_density_matrix`.

        Args:
            state: An array representing a valid  density matrix.
            qubit_map: A map from all qubits used in this PauliSum to the
                indices of the qubits that `state` is defined over.
            atol: Absolute numerical tolerance.
            check_preconditions: Whether to check that `state` represents a
                valid density matrix.

        Returns:
            The expectation value of the input state.

        Raises:
            NotImplementedError: If any of the coefficients are imaginary,
                so that this is not Hermitian.
            TypeError: If the input state is not a complex type.
            ValueError: If the input vector is not the correct size or shape.
        """
        if any(abs(p.coefficient.imag) > 0.0001 for p in self):
            raise NotImplementedError(
                "Cannot compute expectation value of a non-Hermitian "
                "PauliString <{}>. Coefficient must be real.".format(self))

        # FIXME: Avoid enforce specific complex type. This is necessary to
        # prevent an `apply_unitary` bug (Issue #2041).
        if state.dtype.kind != 'c':
            raise TypeError(
                "Input state dtype must be np.complex64 or np.complex128")

        size = state.size
        num_qubits = int(np.sqrt(size)).bit_length() - 1
        _validate_qubit_mapping(qubit_map, self.qubits, num_qubits)

        dim = int(np.sqrt(size))
        if state.shape != (dim, dim) and state.shape != (2, 2) * num_qubits:
            raise ValueError("Input array does not represent a density matrix "
                             "with shape `(2 ** n, 2 ** n)` or `(2, ..., 2)`.")

        if check_preconditions:
            # Do not enforce reshaping if the state all axes are dimension 2.
            _ = qis.to_valid_density_matrix(
                density_matrix_rep=state.reshape(dim, dim),
                num_qubits=num_qubits,
                dtype=state.dtype,
                atol=atol,
            )
        return sum(
            p._expectation_from_density_matrix_no_validation(state, qubit_map)
            for p in self)
    def create(
        cls,
        *,
        initial_state: Union[np.ndarray, 'cirq.STATE_VECTOR_LIKE'] = 0,
        qid_shape: Optional[Tuple[int, ...]] = None,
        dtype: Optional['DTypeLike'] = None,
        buffer: Optional[List[np.ndarray]] = None,
    ):
        """Creates a buffered density matrix with the requested state.

        Args:
            initial_state: The initial state for the simulation in the computational basis.
            qid_shape: The shape of the density matrix, if the initial state is provided as an int.
            dtype: The desired dtype of the density matrix.
            buffer: Optional, must be length 3 and same shape as the density matrix. If not
                provided, a buffer will be created automatically.
        Raises:
            ValueError: If initial state is provided as integer, but qid_shape is not provided.
        """
        if not isinstance(initial_state, np.ndarray):
            if qid_shape is None:
                raise ValueError(
                    'qid_shape must be provided if initial_state is not ndarray'
                )
            density_matrix = qis.to_valid_density_matrix(initial_state,
                                                         len(qid_shape),
                                                         qid_shape=qid_shape,
                                                         dtype=dtype).reshape(
                                                             qid_shape * 2)
        else:
            if qid_shape is not None:
                if dtype and initial_state.dtype != dtype:
                    initial_state = initial_state.astype(dtype)
                density_matrix = qis.to_valid_density_matrix(
                    initial_state,
                    len(qid_shape),
                    qid_shape=qid_shape,
                    dtype=dtype).reshape(qid_shape * 2)
            else:
                density_matrix = initial_state  # coverage: ignore
            if np.may_share_memory(density_matrix, initial_state):
                density_matrix = density_matrix.copy()
        density_matrix = density_matrix.astype(dtype, copy=False)
        return cls(density_matrix, buffer)
Ejemplo n.º 3
0
    def set_density_matrix(self, density_matrix_repr: Union[int, np.ndarray]):
        """Set the density matrix to a new density matrix.

        Args:
            density_matrix_repr: If this is an int, the density matrix is set to
            the computational basis state corresponding to this state. Otherwise
            if this is a np.ndarray it is the full state, either a pure state
            or the full density matrix.  If it is the pure state it must be the
            correct size, be normalized (an L2 norm of 1), and be safely
            castable to an appropriate dtype for the simulator.  If it is a
            mixed state it must be correctly sized and positive semidefinite
            with trace one.
        """
        density_matrix = qis.to_valid_density_matrix(
            density_matrix_repr, len(self._qubit_map), qid_shape=self._qid_shape, dtype=self._dtype
        )
        sim_state_matrix = self._simulator_state().density_matrix
        density_matrix = np.reshape(density_matrix, sim_state_matrix.shape)
        np.copyto(dst=sim_state_matrix, src=density_matrix)
Ejemplo n.º 4
0
    def _create_partial_act_on_args(
        self,
        initial_state: Union[np.ndarray, 'cirq.STATE_VECTOR_LIKE',
                             'cirq.ActOnDensityMatrixArgs'],
        qubits: Sequence['cirq.Qid'],
        logs: Dict[str, Any],
    ) -> 'cirq.ActOnDensityMatrixArgs':
        """Creates the ActOnDensityMatrixArgs for a circuit.

        Args:
            initial_state: The initial state 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.
            logs: The log of measurement results that is added into.

        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)
        if np.may_share_memory(initial_matrix, initial_state):
            initial_matrix = initial_matrix.copy()

        tensor = initial_matrix.reshape(qid_shape * 2)
        return act_on_density_matrix_args.ActOnDensityMatrixArgs(
            target_tensor=tensor,
            available_buffer=[np.empty_like(tensor) for _ in range(3)],
            qubits=qubits,
            qid_shape=qid_shape,
            prng=self._prng,
            log_of_measurement_results=logs,
        )
Ejemplo n.º 5
0
    def _base_iterator(self,
                       circuit: circuits.Circuit,
                       qubit_order: ops.QubitOrderOrList,
                       initial_state: Union[int, np.ndarray],
                       all_measurements_are_terminal=False) -> Iterator:
        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)
        measured = collections.defaultdict(
            bool)  # type: Dict[Tuple[cirq.Qid, ...], bool]
        if len(circuit) == 0:
            yield DensityMatrixStepResult(initial_matrix, {}, qubit_map,
                                          self._dtype)
            return

        state = _StateAndBuffers(len(qid_shape),
                                 initial_matrix.reshape(qid_shape * 2))

        def on_stuck(bad_op: ops.Operation):
            return TypeError(
                "Can't simulate operations that don't implement "
                "SupportsUnitary, SupportsConsistentApplyUnitary, "
                "SupportsMixture, SupportsChannel or is a measurement: {!r}".
                format(bad_op))

        def keep(potential_op: ops.Operation) -> bool:
            return (protocols.has_channel(potential_op) or
                    isinstance(potential_op.gate, ops.MeasurementGate))

        noisy_moments = self.noise.noisy_moments(circuit,
                                                 sorted(circuit.all_qubits()))

        for moment in noisy_moments:
            measurements = collections.defaultdict(
                list)  # type: Dict[str, List[int]]

            channel_ops_and_measurements = protocols.decompose(
                moment, keep=keep, on_stuck_raise=on_stuck)

            for op in channel_ops_and_measurements:
                indices = [qubit_map[qubit] for qubit in op.qubits]
                # TODO: support more general measurements.
                if all_measurements_are_terminal and measured[op.qubits]:
                    continue
                if isinstance(op.gate, ops.MeasurementGate):
                    measured[op.qubits] = True
                    meas = op.gate
                    if all_measurements_are_terminal:
                        continue
                    if self._ignore_measurement_results:
                        for i, q in enumerate(op.qubits):
                            self._apply_op_channel(
                                ops.phase_damp(1).on(q), state, [indices[i]])
                    else:
                        invert_mask = meas.full_invert_mask()
                        # Measure updates inline.
                        bits, _ = (density_matrix_utils.measure_density_matrix(
                            state.tensor,
                            indices,
                            qid_shape=qid_shape,
                            out=state.tensor,
                            seed=self._prng))
                        corrected = [
                            bit ^ (bit < 2 and mask)
                            for bit, mask in zip(bits, invert_mask)
                        ]
                        key = protocols.measurement_key(meas)
                        measurements[key].extend(corrected)
                else:
                    # TODO: Use apply_channel similar to apply_unitary.
                    self._apply_op_channel(op, state, indices)
            yield DensityMatrixStepResult(density_matrix=state.tensor,
                                          measurements=measurements,
                                          qubit_map=qubit_map,
                                          dtype=self._dtype)
Ejemplo n.º 6
0
    def expectation_from_density_matrix(
            self,
            state: np.ndarray,
            qubit_map: Mapping[raw_types.Qid, int],
            *,
            atol: float = 1e-7,
            check_preconditions: bool = True) -> float:
        r"""Evaluate the expectation of this PauliString given a density matrix.

        Compute the expectation value of this PauliString with respect to an
        array representing a density matrix. By convention expectation values
        are defined for Hermitian operators, and so this method will fail if
        this PauliString is non-Hermitian.

        `state` must be an array representation of a density matrix and have
        shape `(2 ** n, 2 ** n)` or `(2, 2, ..., 2)` (2*n entries), where
        `state` is expressed over n qubits.

        `qubit_map` must assign an integer index to each qubit in this
        PauliString that determines which bit position of a computational basis
        state that qubit corresponds to. For example if `state` represents
        $|0\rangle |+\rangle$ and `q0, q1 = cirq.LineQubit.range(2)` then:

            cirq.X(q0).expectation(state, qubit_map={q0: 0, q1: 1}) = 0
            cirq.X(q0).expectation(state, qubit_map={q0: 1, q1: 0}) = 1

        Args:
            state: An array representing a valid  density matrix.
            qubit_map: A map from all qubits used in this PauliString to the
                indices of the qubits that `state` is defined over.
            atol: Absolute numerical tolerance.
            check_preconditions: Whether to check that `state` represents a
                valid density matrix.

        Returns:
            The expectation value of the input state.

        Raises:
            NotImplementedError if this PauliString is non-Hermitian.
        """
        if abs(self.coefficient.imag) > 0.0001:
            raise NotImplementedError(
                'Cannot compute expectation value of a non-Hermitian '
                f'PauliString <{self}>. Coefficient must be real.')

        # FIXME: Avoid enforcing specific complex type. This is necessary to
        # prevent an `apply_unitary` bug (Issue #2041).
        if state.dtype.kind != 'c':
            raise TypeError("Input state dtype must be np.complex64 or "
                            "np.complex128")

        size = state.size
        num_qubits = int(np.sqrt(size)).bit_length() - 1
        dim = 1 << num_qubits
        if state.shape != (dim, dim) and state.shape != (2, 2) * num_qubits:
            raise ValueError("Input array does not represent a density matrix "
                             "with shape `(2 ** n, 2 ** n)` or `(2, ..., 2)`.")

        _validate_qubit_mapping(qubit_map, self.qubits, num_qubits)
        if check_preconditions:
            # Do not enforce reshaping if the state all axes are dimension 2.
            _ = qis.to_valid_density_matrix(density_matrix_rep=state.reshape(
                dim, dim),
                                            num_qubits=num_qubits,
                                            dtype=state.dtype,
                                            atol=atol)
        return self._expectation_from_density_matrix_no_validation(
            state, qubit_map)
Ejemplo n.º 7
0
    def __init__(
        self,
        target_tensor: Optional[np.ndarray] = None,
        available_buffer: Optional[List[np.ndarray]] = None,
        qid_shape: Optional[Tuple[int, ...]] = None,
        prng: Optional[np.random.RandomState] = None,
        log_of_measurement_results: Optional[Dict[str, List[int]]] = None,
        qubits: Optional[Sequence['cirq.Qid']] = None,
        ignore_measurement_results: bool = False,
        initial_state: Union[np.ndarray, 'cirq.STATE_VECTOR_LIKE'] = 0,
        dtype: Type[np.number] = np.complex64,
        classical_data: Optional['cirq.ClassicalDataStore'] = None,
    ):
        """Inits ActOnDensityMatrixArgs.

        Args:
            target_tensor: The state vector to act on, stored as a numpy array
                with one dimension for each qubit in the system. Operations are
                expected to perform inplace edits of this object.
            available_buffer: A workspace with the same shape and dtype as
                `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.
            qid_shape: The shape of the target tensor.
            prng: The pseudo random number generator to use for probabilistic
                effects.
            log_of_measurement_results: A mutable object that measurements are
                being recorded into.
            ignore_measurement_results: If True, then the simulation
                will treat measurement as dephasing instead of collapsing
                process. This is only applicable to simulators that can
                model dephasing.
            initial_state: The initial state for the simulation in the
                computational basis.
            dtype: The `numpy.dtype` of the inferred state vector. One of
                `numpy.complex64` or `numpy.complex128`. Only used when
                `target_tenson` is None.
            classical_data: The shared classical data container for this
                simulation.

        Raises:
            ValueError: The dimension of `target_tensor` is not divisible by 2
                and `qid_shape` is not provided.
        """
        if ignore_measurement_results:
            super().__init__(
                prng=prng,
                qubits=qubits,
                log_of_measurement_results=log_of_measurement_results,
                ignore_measurement_results=ignore_measurement_results,
                classical_data=classical_data,
            )
        else:
            super().__init__(
                prng=prng,
                qubits=qubits,
                log_of_measurement_results=log_of_measurement_results,
                classical_data=classical_data,
            )
        if target_tensor is None:
            qubits_qid_shape = protocols.qid_shape(self.qubits)
            initial_matrix = qis.to_valid_density_matrix(
                initial_state,
                len(qubits_qid_shape),
                qid_shape=qubits_qid_shape,
                dtype=dtype)
            if np.may_share_memory(initial_matrix, initial_state):
                initial_matrix = initial_matrix.copy()
            target_tensor = initial_matrix.reshape(qubits_qid_shape * 2)
        self.target_tensor = target_tensor

        if available_buffer is None:
            available_buffer = [np.empty_like(target_tensor) for _ in range(3)]
        self.available_buffer = available_buffer

        if qid_shape is None:
            target_shape = target_tensor.shape
            if len(target_shape) % 2 != 0:
                raise ValueError(
                    'The dimension of target_tensor is not divisible by 2.'
                    ' Require explicit qid_shape.')
            qid_shape = target_shape[:len(target_shape) // 2]
        self.qid_shape = qid_shape
Ejemplo n.º 8
0
    def _base_iterator(
        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(
            target_tensor=tensor,
            available_buffer=[np.empty_like(tensor) for _ in range(3)],
            axes=[],
            qid_shape=qid_shape,
            prng=self._prng,
            log_of_measurement_results={},
        )

        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:
            for op in flatten_to_ops(moment):
                # TODO: support more general measurements.
                # Github issue: https://github.com/quantumlib/Cirq/issues/3566
                if all_measurements_are_terminal and measured[op.qubits]:
                    continue
                if protocols.is_measurement(op):
                    measured[op.qubits] = True
                    if all_measurements_are_terminal:
                        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)
                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,
                dtype=self._dtype,
            )
            sim_state.log_of_measurement_results.clear()
Ejemplo n.º 9
0
def to_valid_density_matrix(*args, **kwargs):
    return qis.to_valid_density_matrix(*args, **kwargs)