utils.py 9.4 KB
Newer Older
Q
Quleaf 已提交
1
# Copyright (c) 2020 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.
Q
Quleaf 已提交
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
#
# 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.

from functools import reduce

Q
Quleaf 已提交
17 18
import numpy 
from numpy import absolute, log
Q
Quleaf 已提交
19 20
from numpy import diag, dot, identity
from numpy import kron as np_kron
Q
Quleaf 已提交
21 22 23
from numpy import trace as np_trace
from numpy import matmul as np_matmul
from numpy import random as np_random
Q
Quleaf 已提交
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
from numpy import linalg, sqrt
from numpy import sum as np_sum
from numpy import transpose as np_transpose
from numpy import zeros as np_zeros

from paddle.complex import elementwise_add
from paddle.complex import kron as pp_kron
from paddle.complex import matmul
from paddle.complex import transpose as pp_transpose

from paddle.fluid.dygraph import to_variable
from paddle.fluid.framework import ComplexVariable
from paddle.fluid.layers import concat, cos, ones, reshape, sin
from paddle.fluid.layers import zeros as pp_zeros

Q
Quleaf 已提交
39 40
from scipy.linalg import logm, sqrtm

Q
Quleaf 已提交
41 42
__all__ = [
    "partial_trace",
Q
Quleaf 已提交
43 44 45 46 47
    "state_fidelity",
    "gate_fidelity",
    "purity",
    "von_neumann_entropy",
    "relative_entropy",
Q
Quleaf 已提交
48
    "NKron",
Q
Quleaf 已提交
49
    "dagger",
Q
Quleaf 已提交
50 51
    "random_pauli_str_generator",
    "pauli_str_to_matrix"
Q
Quleaf 已提交
52 53 54
]


Q
Quleaf 已提交
55 56 57 58 59 60 61 62 63 64 65 66
def partial_trace(rho_AB, dim1, dim2, A_or_B):
    r"""计算量子态的偏迹。

    Args:
        rho_AB (ComplexVariable): 输入的量子态
        dim1 (int): 系统A的维数
        dim2 (int): 系统B的维数
        A_or_B (int): 1或者2,1表示去除A,2表示去除B

    Returns:
        ComplexVariable: 量子态的偏迹

Q
Quleaf 已提交
67
    """
Q
Quleaf 已提交
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
    if A_or_B == 2:
        dim1, dim2 = dim2, dim1

    idty_np = identity(dim2).astype("complex128")
    idty_B = to_variable(idty_np)

    zero_np = np_zeros([dim2, dim2], "complex128")
    res = to_variable(zero_np)

    for dim_j in range(dim1):
        row_top = pp_zeros([1, dim_j], dtype="float64")
        row_mid = ones([1, 1], dtype="float64")
        row_bot = pp_zeros([1, dim1 - dim_j - 1], dtype="float64")
        bra_j_re = concat([row_top, row_mid, row_bot], axis=1)
        bra_j_im = pp_zeros([1, dim1], dtype="float64")
        bra_j = ComplexVariable(bra_j_re, bra_j_im)
Q
Quleaf 已提交
84

Q
Quleaf 已提交
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
        if A_or_B == 1:
            row_tmp = pp_kron(bra_j, idty_B)
            res = elementwise_add(res, matmul(matmul(row_tmp, rho_AB),
                                              pp_transpose(ComplexVariable(row_tmp.real, -row_tmp.imag),
                                                           perm=[1, 0]), ), )

        if A_or_B == 2:
            row_tmp = pp_kron(idty_B, bra_j)
            res = elementwise_add(res, matmul(matmul(row_tmp, rho_AB),
                                              pp_transpose(ComplexVariable(row_tmp.real, -row_tmp.imag),
                                                           perm=[1, 0]), ), )

    return res


def state_fidelity(rho, sigma):
    r"""计算两个量子态的保真度。
Q
Quleaf 已提交
102

Q
Quleaf 已提交
103 104
    .. math::
        F(\rho, \sigma) = \text{tr}(\sqrt{\sqrt{\rho}\sigma\sqrt{\rho}})
Q
Quleaf 已提交
105

Q
Quleaf 已提交
106 107 108
    Args:
        rho (numpy.ndarray): 量子态的密度矩阵形式
        sigma (numpy.ndarray): 量子态的密度矩阵形式
Q
Quleaf 已提交
109

Q
Quleaf 已提交
110 111
    Returns:
        float: 输入的量子态之间的保真度
Q
Quleaf 已提交
112
    """
Q
Quleaf 已提交
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
    assert rho.shape == sigma.shape, 'The shape of two quantum states are different'
    fidelity = numpy.trace(sqrtm(sqrtm(rho) @ sigma @ sqrtm(rho))).real

    return fidelity


def gate_fidelity(U, V):
    r"""计算两个量子门的保真度。

    .. math::

        F(U, V) = |\text{tr}(UV^\dagger)|/2^n

    :math:`U` 是一个 :math:`2^n\times 2^n` 的 Unitary 矩阵。

    Args:
        U (numpy.ndarray): 量子门的酉矩阵形式
        V (numpy.ndarray): 量子门的酉矩阵形式

    Returns:
        float: 输入的量子门之间的保真度
Q
Quleaf 已提交
134
    """
Q
Quleaf 已提交
135 136 137 138 139
    assert U.shape == V.shape, 'The shape of two unitary matrices are different'
    dim = U.shape[0]
    fidelity = absolute(np_trace(np_matmul(U, V.conj().T)))/dim
    
    return fidelity
Q
Quleaf 已提交
140 141


Q
Quleaf 已提交
142 143
def purity(rho):
    r"""计算量子态的纯度。
Q
Quleaf 已提交
144

Q
Quleaf 已提交
145 146 147 148 149 150 151 152 153
    .. math::

        P = \text{tr}(\rho^2)

    Args:
        rho (numpy.ndarray): 量子态的密度矩阵形式

    Returns:
        float: 输入的量子态的纯度
Q
Quleaf 已提交
154
    """
Q
Quleaf 已提交
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
    gamma = np_trace(np_matmul(rho, rho))
    
    return gamma.real
    

def von_neumann_entropy(rho):
    r"""计算量子态的冯诺依曼熵。

    .. math::

        S = -\text{tr}(\rho \log(\rho))

    Args:
        rho(numpy.ndarray): 量子态的密度矩阵形式

    Returns:
        float: 输入的量子态的冯诺依曼熵
Q
Quleaf 已提交
172
    """
Q
Quleaf 已提交
173 174 175 176
    rho_eigenvalue, _ = linalg.eig(rho)
    entropy = -np_sum(rho_eigenvalue*log(rho_eigenvalue))
    
    return entropy.real
Q
Quleaf 已提交
177 178


Q
Quleaf 已提交
179 180
def relative_entropy(rho, sig):
    r"""计算两个量子态的相对熵。
Q
Quleaf 已提交
181

Q
Quleaf 已提交
182
    .. math::
Q
Quleaf 已提交
183

Q
Quleaf 已提交
184 185 186 187 188 189 190 191
        S(\rho \| \sigma)=\text{tr} \rho(\log \rho-\log \sigma)

    Args:
        rho (numpy.ndarray): 量子态的密度矩阵形式
        sig (numpy.ndarray): 量子态的密度矩阵形式

    Returns:
        float: 输入的量子态之间的相对熵
Q
Quleaf 已提交
192
    """
Q
Quleaf 已提交
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
    assert rho.shape == sig.shape, 'The shape of two quantum states are different'
    res = numpy.trace(rho @ logm(rho) - rho @ logm(sig))
    return res.real


def NKron(matrix_A, matrix_B, *args):
    r"""计算两个及以上的矩阵的Kronecker积。

    Args:
        matrix_A (numpy.ndarray): 一个矩阵
        matrix_B (numpy.ndarray): 一个矩阵
        *args (numpy.ndarray): 其余矩阵

    Returns:
        ComplexVariable: 输入矩阵的Kronecker积

    .. code-block:: python
    
        from paddle_quantum.state import density_op_random
        from paddle_quantum.utils import NKron
        A = density_op_random(2)
        B = density_op_random(2)
        C = density_op_random(2)
        result = NKron(A, B, C)

    ``result`` 应为: :math:`A \otimes B \otimes C`
Q
Quleaf 已提交
219
    """
Q
Quleaf 已提交
220
    return reduce(lambda result, index: np_kron(result, index), args, np_kron(matrix_A, matrix_B), )
Q
Quleaf 已提交
221 222


Q
Quleaf 已提交
223
def dagger(matrix):
Q
Quleaf 已提交
224
    r"""计算矩阵的埃尔米特转置,即Hermitian transpose。
Q
Quleaf 已提交
225

Q
Quleaf 已提交
226 227
    Args:
        matrix (ComplexVariable): 需要埃尔米特转置的矩阵
Q
Quleaf 已提交
228

Q
Quleaf 已提交
229 230
    Returns:
        ComplexVariable: 输入矩阵的埃尔米特转置
Q
Quleaf 已提交
231

Q
Quleaf 已提交
232
    代码示例:
Q
Quleaf 已提交
233

Q
Quleaf 已提交
234 235
    .. code-block:: python
    
Q
Quleaf 已提交
236
        from paddle_quantum.utils import dagger
Q
Quleaf 已提交
237 238 239 240
        from paddle import fluid
        import numpy as np
        with fluid.dygraph.guard():
            rho = fluid.dygraph.to_variable(np.array([[1+1j, 2+2j], [3+3j, 4+4j]]))
Q
Quleaf 已提交
241
            print(dagger(rho).numpy())
Q
Quleaf 已提交
242

Q
Quleaf 已提交
243
    ::
Q
Quleaf 已提交
244

Q
Quleaf 已提交
245 246
        [[1.-1.j 3.-3.j]
        [2.-2.j 4.-4.j]]
Q
Quleaf 已提交
247
    """
Q
Quleaf 已提交
248 249 250
    matrix_dagger = pp_transpose(ComplexVariable(matrix.real, -matrix.imag), perm=[1, 0])

    return matrix_dagger
Q
Quleaf 已提交
251 252


Q
Quleaf 已提交
253 254
def random_pauli_str_generator(n, terms=3):
    r"""随机生成一个可观测量(observable)的列表( ``list`` )形式。
Q
Quleaf 已提交
255

Q
Quleaf 已提交
256 257 258
    一个可观测量 :math:`O=0.3X\otimes I\otimes I+0.5Y\otimes I\otimes Z` 的
    列表形式为 ``[[0.3, 'x0'], [0.5, 'y0,z2']]`` 。这样一个可观测量是由
    调用 ``random_pauli_str_generator(3, terms=2)`` 生成的。
Q
Quleaf 已提交
259

Q
Quleaf 已提交
260 261 262 263 264 265
    Args:
        n (int): 量子比特数量
        terms (int, optional): 可观测量的项数

    Returns:
        list: 随机生成的可观测量的列表形式
Q
Quleaf 已提交
266
    """
Q
Quleaf 已提交
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
    pauli_str = []
    for sublen in np_random.randint(1, high=n+1, size=terms):
        # Tips: -1 <= coef < 1
        coef = np_random.rand()*2-1
        ops = np_random.choice(['x', 'y', 'z'], size=sublen)
        pos = np_random.choice(range(n), size=sublen, replace=False)
        op_list = [ops[i]+str(pos[i]) for i in range(sublen)]
        pauli_str.append([coef, ','.join(op_list)])
    return pauli_str


def pauli_str_to_matrix(pauli_str, n):
    r"""将输入的可观测量(observable)的列表( ``list`` )形式转换为其矩阵形式。

    如输入的 ``pauli_str`` 为 ``[[0.7, 'z0,x1'], [0.2, 'z1']]`` 且 ``n=3`` ,
    则此函数返回可观测量 :math:`0.7Z\otimes X\otimes I+0.2I\otimes Z\otimes I` 的
    矩阵形式。

    Args:
        pauli_str (list): 一个可观测量的列表形式
        n (int): 量子比特数量

    Returns:
        numpy.ndarray: 输入列表对应的可观测量的矩阵形式
Q
Quleaf 已提交
291
    """
Q
Quleaf 已提交
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
    pauli_dict = {'i': numpy.eye(2) + 0j, 'x': numpy.array([[0, 1], [1, 0]]) + 0j,
                  'y': numpy.array([[0, -1j], [1j, 0]]), 'z': numpy.array([[1, 0], [0, -1]]) + 0j}

    # Parse pauli_str; 'x0,z1,y4' to 'xziiy'
    new_pauli_str = []
    for coeff, op_str in pauli_str:
        init = list('i'*n)
        op_list = op_str.split(',')
        for op in op_list:
            pos = int(op[1:])
            assert pos < n, 'n is too small'
            init[pos] = op[0]
        new_pauli_str.append([coeff, ''.join(init)])

    # Convert new_pauli_str to matrix; 'xziiy' to NKron(x, z, i, i, y)
    matrices = []
    for coeff, op_str in new_pauli_str:
        sub_matrices = []
        for op in op_str:
            sub_matrices.append(pauli_dict[op])
        if len(op_str) == 1:
            matrices.append(coeff * sub_matrices[0])
        else:
            matrices.append(coeff * NKron(sub_matrices[0], sub_matrices[1], *sub_matrices[2:]))

    return sum(matrices)