Пример #1
0
    def compute_displays_sweep(
        self,
        program: Union[circuits.Circuit, schedules.Schedule],
        params: Optional[study.Sweepable] = None,
        qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT,
        initial_state: Union[int, np.ndarray] = 0,
    ) -> List[study.ComputeDisplaysResult]:
        """Computes displays in the supplied Circuit or Schedule.

        In contrast to `compute_displays`, this allows for sweeping
        over different parameter values.

        Args:
            program: The circuit or schedule to simulate.
            params: Parameters to run with the program.
            qubit_order: Determines the canonical ordering of the qubits used to
                define the order of amplitudes in the wave function.
            initial_state: If an int, the state is set to the computational
                basis state corresponding to this state. Otherwise if it is a
                np.ndarray it is the full initial 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.

        Returns:
            List of ComputeDisplaysResults for this run, one for each
            possible parameter resolver.
        """
        circuit = (program.to_circuit()
                   if isinstance(program, schedules.Schedule) else program)
        qubit_order = ops.QubitOrder.as_qubit_order(qubit_order)
        qubits = qubit_order.order_for(circuit.all_qubits())

        compute_displays_results: List[study.ComputeDisplaysResult] = []
        for param_resolver in study.to_resolvers(params):
            display_values: Dict[Hashable, Any] = {}

            # Compute the displays in the first Moment
            moment = circuit[0]
            matrix = density_matrix_utils.to_valid_density_matrix(
                initial_state, num_qubits=len(qubits), dtype=self._dtype)
            qubit_map = {q: i for i, q in enumerate(qubits)}
            _enter_moment_display_values_into_dictionary(
                display_values, moment, matrix, qubits, qubit_map)

            # Compute the displays in the rest of the Moments
            all_step_results = self.simulate_moment_steps(
                circuit, param_resolver, qubits, initial_state)
            for step_result, moment in zip(all_step_results, circuit[1:]):
                _enter_moment_display_values_into_dictionary(
                    display_values, moment, step_result.density_matrix(),
                    qubits, step_result._qubit_map)

            compute_displays_results.append(
                study.ComputeDisplaysResult(params=param_resolver,
                                            display_values=display_values))

        return compute_displays_results
Пример #2
0
    def expectation_from_density_matrix(
            self, state: np.ndarray, qubit_map: Mapping[raw_types.Qid,
                                                        int]) -> 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.

        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 "
                "PauliString <{}>. Coefficient must be real.".format(self))

        # 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)
        # HACK: avoid circular import
        from cirq.sim.density_matrix_utils import to_valid_density_matrix
        # Do not enforce reshaping if the state all axes are dimension 2.
        _ = to_valid_density_matrix(density_matrix_rep=state.reshape(dim, dim),
                                    num_qubits=num_qubits,
                                    dtype=state.dtype)
        return self._expectation_from_density_matrix_no_validation(
            state, qubit_map)
    def _base_iterator(self,
                       circuit: circuits.Circuit,
                       qubit_order: ops.QubitOrderOrList,
                       initial_state: Union[int, np.ndarray],
                       perform_measurements: bool = True) -> Iterator:

        qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(
            circuit.all_qubits())
        num_qubits = len(qubits)
        qubit_map = {q: i for i, q in enumerate(qubits)}
        matrix = density_matrix_utils.to_valid_density_matrix(
            initial_state, num_qubits, dtype=self._dtype)
        if len(circuit) == 0:
            yield DensityMatrixStepResult(matrix, {},
                                          qubit_map,
                                          dtype=self._dtype)

        matrix = np.reshape(matrix, (2**num_qubits, 2**num_qubits))
        noisy_moments = self.noise.noisy_moments(circuit,
                                                 sorted(circuit.all_qubits()))

        state = qulacs.DensityMatrix(num_qubits)
        state.load(matrix)

        for moment in noisy_moments:
            measurements = collections.defaultdict(
                list)  # type: Dict[str, List[bool]]
            operations = moment.operations
            for op in operations:
                indices = [
                    num_qubits - 1 - qubit_map[qubit] for qubit in op.qubits
                ]
                indices.reverse()

                if isinstance(op, ops.MeasurementGate):
                    # Not implemented
                    raise NotImplementedError(
                        "Measurement is not supported in qulacs simulator")

                else:
                    gate = cast(ops.GateOperation, op).gate
                    channel = protocols.channel(gate)

                    qulacs_gates = []
                    for krauss in channel:
                        krauss = krauss.astype(np.complex128)
                        qulacs_gate = qulacs.gate.DenseMatrix(indices, krauss)
                        qulacs_gates.append(qulacs_gate)
                    qulacs_cptp_map = qulacs.gate.CPTP(qulacs_gates)
                    qulacs_cptp_map.update_quantum_state(state)

            matrix = state.get_matrix()
            matrix = np.reshape(matrix, (2, ) * num_qubits * 2)

            yield DensityMatrixStepResult(density_matrix=matrix,
                                          measurements=measurements,
                                          qubit_map=qubit_map,
                                          dtype=self._dtype)
Пример #4
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.
        """
        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:
            # HACK: avoid circular import
            from cirq.sim.density_matrix_utils import to_valid_density_matrix
            # Do not enforce reshaping if the state all axes are dimension 2.
            _ = 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)
Пример #5
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 = density_matrix_utils.to_valid_density_matrix(
            density_matrix_repr, len(self._qubit_map), self._dtype)
        density_matrix = np.reshape(density_matrix,
                                    self.simulator_state().density_matrix.shape)
        np.copyto(dst=self.simulator_state().density_matrix, src=density_matrix)
Пример #6
0
    def _base_iterator(self,
                       circuit: circuits.Circuit,
                       qubit_order: ops.QubitOrderOrList,
                       initial_state: Union[int, np.ndarray],
                       perform_measurements: bool = True) -> 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 = density_matrix_utils.to_valid_density_matrix(
            initial_state,
            len(qid_shape),
            qid_shape=qid_shape,
            dtype=self._dtype)
        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 (ops.op_gate_of_type(potential_op, ops.MeasurementGate)
                        is not None)
                    or isinstance(potential_op,
                                  (ops.SamplesDisplay, ops.WaveFunctionDisplay,
                                   ops.DensityMatrixDisplay)))

        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]
                if isinstance(op, (ops.SamplesDisplay, ops.WaveFunctionDisplay,
                                   ops.DensityMatrixDisplay)):
                    continue
                # TODO: support more general measurements.
                meas = ops.op_gate_of_type(op, ops.MeasurementGate)
                if meas:
                    if perform_measurements:
                        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)
                        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)
Пример #7
0
    def _base_iterator(
            self,
            circuit: circuits.Circuit,
            qubit_order: ops.QubitOrderOrList,
            initial_state: Union[int, np.ndarray],
            perform_measurements: bool = True) -> Iterator:
        qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(
            circuit.all_qubits())
        num_qubits = len(qubits)
        qubit_map = {q: i for i, q in enumerate(qubits)}
        matrix = density_matrix_utils.to_valid_density_matrix(
            initial_state, num_qubits, self._dtype)
        if len(circuit) == 0:
            yield DensityMatrixStepResult(matrix, {}, qubit_map, self._dtype)
        matrix = np.reshape(matrix, (2,) * num_qubits * 2)

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

        def keep(potential_op: ops.Operation) -> bool:
            return (protocols.has_channel(potential_op)
                    or (ops.op_gate_of_type(potential_op,
                                            ops.MeasurementGate) is not None)
                    or isinstance(potential_op,
                                  (ops.SamplesDisplay,
                                   ops.WaveFunctionDisplay,
                                   ops.DensityMatrixDisplay))
                    )

        matrix = np.reshape(matrix, (2,) * num_qubits * 2)
        noisy_moments = self.noise.noisy_moments(circuit,
                                                 sorted(circuit.all_qubits()))

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

            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]
                if isinstance(op,
                              (ops.SamplesDisplay,
                                  ops.WaveFunctionDisplay,
                                  ops.DensityMatrixDisplay)):
                    continue
                # TODO: support more general measurements.
                meas = ops.op_gate_of_type(op, ops.MeasurementGate)
                if meas:
                    if perform_measurements:
                        invert_mask = meas.invert_mask or num_qubits * (False,)
                        # Measure updates inline.
                        bits, _ = density_matrix_utils.measure_density_matrix(
                            matrix, indices, matrix)
                        corrected = [bit ^ 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.
                    gate = cast(ops.GateOperation, op).gate
                    channel = protocols.channel(gate)
                    sum_buffer = np.zeros((2,) * 2 * num_qubits,
                                          dtype=self._dtype)
                    buffer = np.empty((2,) * 2 * num_qubits, dtype=self._dtype)
                    out = np.empty((2,) * 2 * num_qubits, dtype=self._dtype)
                    for krauss in channel:
                        krauss_tensor = np.reshape(krauss.astype(self._dtype),
                                                   (2,) * gate.num_qubits() * 2)
                        result = linalg.targeted_conjugate_about(krauss_tensor,
                                                                 matrix,
                                                                 indices,
                                                                 buffer=buffer,
                                                                 out=out)
                        sum_buffer += result
                    np.copyto(dst=matrix, src=sum_buffer)
            yield DensityMatrixStepResult(
                    density_matrix=matrix,
                    measurements=measurements,
                    qubit_map=qubit_map,
                    dtype=self._dtype)
    def _base_iterator(self,
                       circuit: circuits.Circuit,
                       qubit_order: ops.QubitOrderOrList,
                       initial_state: Union[int, np.ndarray],
                       perform_measurements: bool = True) -> Iterator:

        qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for(
            circuit.all_qubits())
        num_qubits = len(qubits)
        qubit_map = {q: i for i, q in enumerate(qubits)}
        matrix = density_matrix_utils.to_valid_density_matrix(
            initial_state, num_qubits, self._dtype)
        if len(circuit) == 0:
            yield DensityMatrixStepResult(matrix, {}, qubit_map, self._dtype)

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

        def keep(potential_op: ops.Operation) -> bool:
            return (protocols.has_channel(potential_op)
                    or (ops.op_gate_of_type(potential_op, ops.MeasurementGate)
                        is not None)
                    or isinstance(potential_op,
                                  (ops.SamplesDisplay, ops.WaveFunctionDisplay,
                                   ops.DensityMatrixDisplay)))

        matrix = np.reshape(matrix, (2**num_qubits, 2**num_qubits))
        noisy_moments = self.noise.noisy_moments(circuit,
                                                 sorted(circuit.all_qubits()))

        state = qulacs.DensityMatrix(num_qubits)
        state.load(matrix)

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

            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]
                indices = [
                    num_qubits - 1 - qubit_map[qubit] for qubit in op.qubits
                ]
                indices.reverse()
                if isinstance(op, (ops.SamplesDisplay, ops.WaveFunctionDisplay,
                                   ops.DensityMatrixDisplay)):
                    continue
                # TODO: support more general measurements.

                meas = ops.op_gate_of_type(op, ops.MeasurementGate)
                if meas:
                    # Not implemented
                    raise NotImplementedError(
                        "Measurement is not supported in qulacs simulator")

                else:
                    # TODO: Use apply_channel similar to apply_unitary.
                    gate = cast(ops.GateOperation, op).gate
                    channel = protocols.channel(gate)

                    qulacs_gates = []
                    for krauss in channel:
                        krauss = krauss.astype(np.complex128)
                        qulacs_gate = qulacs.gate.DenseMatrix(indices, krauss)
                        qulacs_gates.append(qulacs_gate)
                    qulacs_cptp_map = qulacs.gate.CPTP(qulacs_gates)
                    qulacs_cptp_map.update_quantum_state(state)

            matrix = state.get_matrix()
            matrix = np.reshape(matrix, (2, ) * num_qubits * 2)

            yield DensityMatrixStepResult(density_matrix=matrix,
                                          measurements=measurements,
                                          qubit_map=qubit_map,
                                          dtype=self._dtype)