mindquantum.core.circuit.qfi 源代码

# Copyright 2021 Huawei Technologies Co., Ltd
#
# 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.
# ============================================================================
"""Calculate quantum fisher information."""

import numpy as np

from mindquantum.core.gates.basicgate import MultiParamsGate

from ...utils.type_value_check import _check_and_generate_pr_type, _check_input_type
from ..parameterresolver import ParameterResolver
from .circuit import Circuit


def pr_converter(pr_map, origin: ParameterResolver):
    """Convert original parameters to redefined parameters."""
    part_a = {}
    for k, v in pr_map.items():
        part_a[k] = v.combination(origin).const
    return ParameterResolver(part_a)


def apply_gate(sim, gate, g_cpp, pr_cpp, diff):
    """Apply a gate."""
    if gate.parameterized:
        sim.apply_gate(g_cpp, pr_cpp, diff)
    else:
        sim.apply_gate(g_cpp)


# pylint: disable=too-many-statements,too-many-locals
def _qfi_matrix_base(circuit: Circuit, which_part='both', backend='mqvector'):
    """Calculate Quantum Fisher Information (QFI)."""
    from ...simulator import (  # pylint: disable=import-outside-toplevel,cyclic-import
        Simulator,
        inner_product,
    )

    _check_input_type('circuit', Circuit, circuit)
    if which_part not in ['A', 'B', 'both']:
        raise ValueError(f"which part should be 'A', 'B' or 'both', but get {which_part}.")
    circuit = circuit.remove_barrier()
    if circuit.has_measure_gate:
        raise ValueError("circuit can not has measure gate for calculate qfi similar value.")
    if circuit.is_noise_circuit:
        raise ValueError("circuit can not be noise circuit for calculate qfi similar value.")
    if not circuit.params_name:
        raise ValueError("circuit need a parameterized quantum circuit, but get non-parameterized one.")

    pure_circ = Circuit()
    n_params = 0
    jac = {}
    pr_map = {}
    for gate in circuit:
        if isinstance(gate, MultiParamsGate):
            raise ValueError(f"qfi doesn't support multi parameters gate: {gate}")
        if gate.parameterized:
            n_params += 1
            new_p = f'p{n_params}'
            pure_circ += gate(ParameterResolver(new_p)).on(gate.obj_qubits, gate.ctrl_qubits)
            jac[new_p] = dict(gate.coeff.items())
            pr_map[new_p] = gate.coeff
        else:
            pure_circ += gate
    old_idx_map = {p: idx for idx, p in enumerate(circuit.params_name)}
    new_idx_map = {p: idx for idx, p in enumerate(pure_circ.params_name)}
    tmp = np.zeros((len(new_idx_map), len(old_idx_map)), np.complex128)
    for new_p, matrix in jac.items():
        for old_p, v in matrix.items():
            tmp[new_idx_map[new_p], old_idx_map[old_p]] = v
    jac = tmp
    cpp_obj = pure_circ.get_cpp_obj()
    c_len = len(pure_circ)
    ket = Simulator(backend, pure_circ.n_qubits)

    # pylint: disable=too-many-branches
    def qfi_ops(pr: ParameterResolver):
        pr = _check_and_generate_pr_type(pr, circuit.params_name)
        ket.reset()
        pr_cpp = pr_converter(pr_map, pr).to_real_obj()
        if which_part != 'B':
            part_a = np.zeros((len(new_idx_map), len(new_idx_map)), np.complex128)
        if which_part != 'A':
            part_b = np.zeros(len(new_idx_map), np.complex128)
        for i in range(c_len):
            gate = pure_circ[i]
            g_cpp = cpp_obj[i]
            if gate.parameterized:
                idx_i = new_idx_map[gate.coeff.params_name[0]]
                bra = Simulator(backend, pure_circ.n_qubits)
                ket_tmp = ket.copy()
                ket_tmp.backend.sim.apply_gate(g_cpp, pr_cpp, True)
                if which_part != 'B':
                    grad_current = inner_product(ket_tmp, ket_tmp)
                    part_a[idx_i, idx_i] = grad_current
                for j in range(i):
                    g_j = pure_circ[j]
                    g_cpp_j = cpp_obj[j]
                    if which_part != 'B':
                        bra_tmp = bra.copy()
                        for k in range(j, i + 1):
                            g_k = pure_circ[k]
                            g_cpp_k = cpp_obj[k]
                            apply_gate(bra_tmp.backend.sim, g_k, g_cpp_k, pr_cpp, (k == j))

                    if g_j.parameterized:
                        bra.backend.sim.apply_gate(g_cpp_j, pr_cpp, False)
                        if which_part != 'B':
                            idx_j = new_idx_map[g_j.coeff.params_name[0]]
                            part_a[idx_j, idx_i] = inner_product(bra_tmp, ket_tmp)
                            part_a[idx_i, idx_j] = np.conj(part_a[idx_j, idx_i])
                    else:
                        bra.backend.sim.apply_gate(g_cpp_j)
                bra.backend.sim.apply_gate(g_cpp, pr_cpp, False)
                if which_part != 'A':
                    part_b[idx_i] = np.conj(inner_product(bra, ket_tmp))
                ket.backend.sim.apply_gate(g_cpp, pr_cpp, False)
            else:
                ket.backend.sim.apply_gate(g_cpp)
        if which_part != 'B':
            first_part = jac.T @ part_a @ jac
        if which_part != 'A':
            second_part = jac.T @ part_b
        if which_part == 'A':
            return first_part
        if which_part == 'B':
            return second_part
        return first_part, second_part

    return qfi_ops


[文档]def qfi(circuit: Circuit, backend='mqvector'): r""" Calculate the quantum fisher information of the given parameterized circuit with given parameters. The quantum fisher information of a parameterized circuit is defined as: .. math:: \text{QFI}_{i,j} = 4\text{Re}(A_{i,j} - B_{i,j}) where .. math:: A_{i,j} = \frac{\partial \left<\psi\right| }{\partial x_{i}} \frac{\partial \left|\psi\right> }{\partial x_{j}} and .. math:: B_{i,j} = \frac{\partial \left<\psi\right| }{\partial x_i}\left|\psi\right> \left<\psi\right|\frac{\partial \left|\psi\right> }{\partial x_{j}} Args: circuit (Circuit): A parameterized quantum circuit. backend (str): A supported simulator backend. Please refer description of :class:`~.simulator.Simulator`. Default: ``'mqvector'``. Returns: Function, a function that can calculate quantum fisher information. Examples: >>> import numpy as np >>> from mindquantum.core.circuit import qfi, Circuit >>> circ = Circuit().rx('a', 0).ry('b', 0).rz('c', 0) >>> qfi_ops = qfi(circ) >>> qfi_ops(np.array([1, 2, 3])) array([[ 1. , 0. , -0.90929743], [ 0. , 0.29192658, -0.18920062], [-0.90929743, -0.18920062, 0.94944468]]) """ qfi_ops_tmp = _qfi_matrix_base(circuit, backend=backend) def qfi_ops(pr): # pylint: disable=invalid-name a, b = qfi_ops_tmp(pr) b = np.outer(b, np.conj(b)) return np.real(a - b) * 4 return qfi_ops
[文档]def partial_psi_partial_psi(circuit: Circuit, backend='mqvector'): r""" Calculate the following value of the given parameterized quantum circuit. .. math:: A_{i,j} = \frac{\partial \left<\psi\right| }{\partial x_{i}} \frac{\partial \left|\psi\right> }{\partial x_{j}} Args: circuit (Circuit): A parameterized quantum circuit. backend (str): A supported simulator backend. Please refer description of :class:`~.simulator.Simulator`. Default: ``'mqvector'``. Returns: Function, a function that can calculate inner product of partial psi and partial psi. Examples: >>> import numpy as np >>> from mindquantum.core.circuit import partial_psi_partial_psi, Circuit >>> circ = Circuit().rx('a', 0).ry('b', 0).rz('c', 0) >>> pppp_ops = partial_psi_partial_psi(circ) >>> pppp_ops(np.array([1, 2, 3])) array([[ 0.25 +0.j , 0. +0.13507558j, -0.22732436-0.08754387j], [ 0. -0.13507558j, 0.25 +0.j , 0. +0.12282387j], [-0.22732436+0.08754387j, 0. -0.12282387j, 0.25 +0.j ]]) """ return _qfi_matrix_base(circuit, 'A', backend=backend)
[文档]def partial_psi_psi(circuit: Circuit, backend='mqvector'): r""" Calculate the following value of the given parameterized quantum circuit. .. math:: B_i = \frac{\partial \left<\psi\right| }{\partial x_i}\left|\psi\right> Args: circuit (Circuit): A parameterized quantum circuit. backend (str): A supported simulator backend. Please refer description of :class:`~.simulator.Simulator`. Default: ``'mqvector'``. Returns: Function, a function that can calculate inner product of partial psi and psi. Examples: >>> import numpy as np >>> from mindquantum.core.circuit import partial_psi_psi, Circuit >>> circ = Circuit().rx('a', 0).ry('b', 0).rz('c', 0) >>> ppp = partial_psi_psi(circ) >>> ppp(np.array([1, 2, 3])) array([0.+0.j , 0.-0.42073549j, 0.-0.11242255j]) """ return _qfi_matrix_base(circuit, 'B', backend=backend)