measure.py 9.0 KB
Newer Older
Q
Quleaf 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
# !/usr/bin/env python3
# Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

r"""
The source file of the class for the measurement.
"""

import paddle
import paddle_quantum
from ..backend import quleaf
Q
Quleaf 已提交
23 24 25
from ..backend import Backend, state_vector
from ..intrinsic import _get_float_dtype, _perm_of_list, _base_transpose, _base_transpose_for_dm
from typing import Optional, Union, Iterable, List
Q
Quleaf 已提交
26 27 28 29 30 31 32 33 34 35 36


class ExpecVal(paddle_quantum.Operator):
    r"""The class of the loss function to compute the expectation value for the observable.

    This interface can make you using the expectation value for the observable as the loss function.

    Args:
        hamiltonian: The input observable.
        shots: The number of measurement shots. Defaults to ``0``. Now it just need to be input when the backend is QuLeaf.
    """
Q
Quleaf 已提交
37

Q
Quleaf 已提交
38 39 40 41 42 43 44 45 46
    def __init__(self, hamiltonian: paddle_quantum.Hamiltonian, shots: Optional[int] = 0):
        super().__init__()
        self.hamiltonian = hamiltonian
        self.shots = shots
        self.num_terms = self.hamiltonian.n_terms
        self.coeffs = paddle.to_tensor(self.hamiltonian.coefficients)
        self.sites = self.hamiltonian.sites
        self.matrices = self.hamiltonian.pauli_words_matrix

Q
Quleaf 已提交
47 48
    def forward(self, state: paddle_quantum.State, decompose: Optional[bool] = False) -> Union[
        paddle.Tensor, List[paddle.Tensor]]:
Q
Quleaf 已提交
49 50 51 52 53 54
        r"""Compute the expectation value of the observable with respect to the input state.

        The value computed by this function can be used as a loss function to optimize.

        Args:
            state: The input state which will be used to compute the expectation value.
Q
Quleaf 已提交
55
            decompose: Defaults to ``False``.  If decompose is ``True``, it will return the expectation value of each term.
Q
Quleaf 已提交
56 57 58 59 60 61 62 63

        Raises:
            NotImplementedError: The backend is wrong or not supported.

        Returns:
            The expectation value. If the backend is QuLeaf, it is computed by sampling.
        """
        if self.backend == Backend.QuLeaf:
Q
Quleaf 已提交
64
            param_list = [history['param'] for history in filter(lambda x: 'param' in x, state.oper_history)]
Q
Quleaf 已提交
65
            expec_val = quleaf.ExpecValOp.apply(
Q
Quleaf 已提交
66 67 68 69
                state,
                self.hamiltonian,
                paddle.to_tensor([self.shots], dtype=paddle.get_default_dtype()),
                *param_list
Q
Quleaf 已提交
70 71 72 73
            )
            return expec_val

        num_qubits = state.num_qubits
Q
Quleaf 已提交
74
        float_dtype = _get_float_dtype(paddle_quantum.get_dtype())
Q
Quleaf 已提交
75
        origin_seq = list(range(num_qubits))
Q
Quleaf 已提交
76
        state_data = state.data
Q
Quleaf 已提交
77 78 79 80 81 82 83 84 85
        if self.backend == Backend.StateVector:
            expec_val = paddle.zeros([state_data.reshape([-1, 2 ** num_qubits]).shape[0]], dtype=float_dtype)
        elif self.backend == Backend.DensityMatrix:
            expec_val = paddle.zeros([state_data.reshape([-1, 2 ** num_qubits, 2 ** num_qubits]).shape[0]],
                                     dtype=float_dtype)
        else:
            raise NotImplementedError
        expec_val_each_term = []
        for i in range(self.num_terms):
Q
Quleaf 已提交
86 87 88
            pauli_site = self.sites[i]
            if pauli_site == ['']:
                expec_val += self.coeffs[i]
Q
Quleaf 已提交
89
                expec_val_each_term.append(self.coeffs[i])
Q
Quleaf 已提交
90 91 92
                continue
            num_applied_qubits = len(pauli_site)
            matrix = self.matrices[i]
Q
Quleaf 已提交
93

Q
Quleaf 已提交
94
            if self.backend == Backend.StateVector:
Q
Quleaf 已提交
95 96 97 98 99 100 101 102 103 104
                output_state, seq_for_acted = state_vector.unitary_transformation_without_swapback(
                    state_data, [matrix], pauli_site, num_qubits, origin_seq)
                perm_map = _perm_of_list(seq_for_acted, origin_seq)
                output_state = _base_transpose(output_state, perm_map).reshape([-1, 2 ** num_qubits, 1])
                state_data_bra = paddle.conj(state_data.reshape([-1, 1, 2 ** num_qubits]))
                batch_values = paddle.squeeze(paddle.real(paddle.matmul(state_data_bra, output_state)), axis=[-2, -1]) * \
                               self.coeffs[i]
                expec_val_each_term.append(batch_values)
                expec_val += batch_values

Q
Quleaf 已提交
105
            elif self.backend == Backend.DensityMatrix:
Q
Quleaf 已提交
106 107 108 109 110 111 112 113 114 115
                seq_for_acted = pauli_site + [x for x in origin_seq if x not in pauli_site]
                perm_map = _perm_of_list(origin_seq, seq_for_acted)
                output_state = _base_transpose_for_dm(state_data, perm=perm_map).reshape(
                    [-1, 2 ** num_applied_qubits, 2 ** (2 * num_qubits - num_applied_qubits)])
                output_state = paddle.matmul(matrix, output_state).reshape(
                    [-1, 2 ** num_qubits, 2 ** num_qubits])
                batch_values = paddle.real(paddle.trace(output_state, axis1=-2, axis2=-1)) * self.coeffs[i]
                expec_val_each_term.append(batch_values)
                expec_val += batch_values

Q
Quleaf 已提交
116 117
            else:
                raise NotImplementedError
Q
Quleaf 已提交
118 119 120

        if decompose:
            return expec_val_each_term
Q
Quleaf 已提交
121 122 123 124 125 126 127 128 129 130 131 132
        return expec_val


class Measure(paddle_quantum.Operator):
    r"""Compute the probability of the specified measurement result.

    Args:
        measure_basis: Specify the basis of the measurement. Defaults to ``'z'``.

    Raises:
        NotImplementedError: Currently we just support the z basis.
    """
Q
Quleaf 已提交
133

Q
Quleaf 已提交
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
    def __init__(self, measure_basis: Optional[Union[Iterable[paddle.Tensor], str]] = 'z'):
        super().__init__()
        if measure_basis == 'z' or measure_basis == 'computational_basis':
            self.measure_basis = 'z'
        else:
            raise NotImplementedError

    def forward(
            self, state: paddle_quantum.State, qubits_idx: Optional[Union[Iterable[int], int, str]] = 'full',
            desired_result: Optional[Union[Iterable[str], str]] = None
    ) -> paddle.Tensor:
        r"""Compute the probability of measurement to the input state.

        Args:
            state: The quantum state to be measured.
            qubits_idx: The index of the qubits to be measured. Defaults to ``'full'`` which means measure all the qubits.
            desired_result: Specify the results of the measurement to return. Defaults to ``None`` which means return the probability of all the results.

Q
Quleaf 已提交
152 153 154 155
        Raises:
            NotImplementedError: The backend is wrong or not supported.
            NotImplementedError: The qubit index is wrong or not supported.
            NotImplementedError: Currently we just support the z basis.
Q
Quleaf 已提交
156

Q
Quleaf 已提交
157 158 159
        Returns:
            The probability of the measurement.
        """
Q
Quleaf 已提交
160
        float_dtype = _get_float_dtype(paddle_quantum.get_dtype())
Q
Quleaf 已提交
161 162 163 164 165
        num_qubits = state.num_qubits
        if self.measure_basis == 'z':
            if state.backend == paddle_quantum.Backend.StateVector:
                prob_amplitude = paddle.multiply(paddle.conj(state.data), state.data).real()
            elif state.backend == paddle_quantum.Backend.DensityMatrix:
Q
Quleaf 已提交
166
                prob_amplitude = paddle.zeros([2 ** num_qubits], dtype=float_dtype)
Q
Quleaf 已提交
167 168 169
                for idx in range(0, 2 ** num_qubits):
                    prob_amplitude[idx] += state.data[idx, idx].real()
            else:
Q
Quleaf 已提交
170
                raise NotImplementedError("The backend is wrong or not supported.")
Q
Quleaf 已提交
171 172 173 174 175 176

            if isinstance(qubits_idx, int):
                qubits_idx = [qubits_idx]
            if isinstance(qubits_idx, Iterable) and all((isinstance(idx, int) for idx in qubits_idx)):
                qubits_idx = list(qubits_idx)
                measured_num = len(qubits_idx)
Q
Quleaf 已提交
177
                prob_array = paddle.zeros([2 ** measured_num], dtype=float_dtype)
Q
Quleaf 已提交
178 179 180 181 182 183 184 185 186 187
                for idx in range(0, 2 ** num_qubits):
                    binary = bin(idx)[2:]
                    binary = '0' * (num_qubits - len(binary)) + binary
                    target_qubits = ''
                    for qubit_idx in qubits_idx:
                        target_qubits += binary[qubit_idx]
                    prob_array[int(target_qubits, base=2)] += prob_amplitude[idx]
            elif qubits_idx == 'full':
                prob_array = prob_amplitude
            else:
Q
Quleaf 已提交
188 189
                raise NotImplementedError("The qubit index is wrong or not supported.")

Q
Quleaf 已提交
190
            prob_array = prob_array / paddle.sum(prob_array)  # normalize calculation error
Q
Quleaf 已提交
191 192 193 194
            if desired_result is None:
                return prob_array
            if isinstance(desired_result, str):
                desired_result = [desired_result]
Q
Quleaf 已提交
195 196
                prob_array = paddle.concat([prob_array[int(res, base=2)] for res in desired_result])
            return prob_array
Q
Quleaf 已提交
197
        else:
Q
Quleaf 已提交
198
            raise NotImplementedError("Currently we just support the z basis.")