mindquantum.algorithm.library.qudit_mapping 源代码

# Copyright 2024 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.
# ============================================================================
"""Qudit symmetric mapping module."""

from typing import List
import numpy as np
import scipy as sp
from scipy.sparse import csr_matrix
from mindquantum.utils.f import is_power_of_two
from mindquantum.core import QubitOperator

from mindquantum.core.circuit import Circuit
from mindquantum.core.gates import X, RX, RY, RZ, U3, GlobalPhase, UnivMathGate
from mindquantum.utils.type_value_check import _check_input_type, _check_int_type, _check_value_should_not_less

optional_basis = ["zyz", "u3"]


def _symmetric_state_index(dim: int, n_qudits: int) -> dict:
    """
    The index of the qudit state or matrix element corresponding to the qubit symmetric state or matrix during mapping.

    Args:
        dim (int): the dimension of qudit state or matrix.
        n_qudits (int): the number fo qudit state or matrix.

    Returns:
        dict, which keys are the index of the qudit state or matrix,
        values are the corresponding index of qubit symmetric state or matrix.

    Examples:
        >>> from mindquantum.algorithm.library.qudit_mapping import _symmetric_state_index
        >>> _symmetric_state_index(dim=3, n_qudits=1)
        {0: [0], 1: [1, 2], 2: [3]}
        >>> _symmetric_state_index(dim=4, n_qudits=1)
        {0: [0], 1: [1, 2, 4], 2: [3, 5, 6], 3: [7]}
        >>> _symmetric_state_index(dim=3, n_qudits=2)
        {0: [0], 1: [1, 2], 2: [3], 3: [4, 8], 4: [5, 6, 9, 10], 5: [7, 11], 6: [12], 7: [13, 14], 8: [15]}
    """
    _check_int_type("dim", dim)
    _check_int_type("n_qudits", n_qudits)
    _check_value_should_not_less("dim", 2, dim)
    _check_value_should_not_less("n_qudits", 1, n_qudits)
    if n_qudits == 1:
        ind = {}
        for i in range(2 ** (dim - 1)):
            num = bin(i).count("1")
            if num in ind:
                ind[num].append(i)
            else:
                ind[num] = [i]
    else:
        ind, ind_ = {}, {}
        for i in range(2 ** (dim - 1)):
            num = bin(i).count("1")
            i_ = bin(i)[2::].zfill(dim - 1)
            if num in ind_:
                ind_[num].append(i_)
            else:
                ind_[num] = [i_]
        for i in range(dim**n_qudits):
            multi = [""]
            base = np.base_repr(i, dim).zfill(n_qudits)
            for j in range(n_qudits):
                multi = [x + y for x in multi for y in ind_[int(base[j])]]
            ind[i] = [int(x, 2) for x in multi]
    return ind


def _is_symmetric(qubit: np.ndarray, n_qubits: int = 1) -> bool:
    """
    Check whether the qubit state or matrix is symmetric.

    Args:
        qubit (np.ndarray): the qubit state or matrix that needs to be checked whether it is symmetric.
        n_qubits (int): the number of qubits in the qubit symmetric state or matrix. Default: ``1``.

    Returns:
        bool, whether the qubit state or matrix is symmetric.
    """
    _check_input_type("qubit", np.ndarray, qubit)
    _check_int_type("n_qubits", n_qubits)
    _check_value_should_not_less("n_qubits", 1, n_qubits)
    if qubit.ndim == 2 and (qubit.shape[0] == 1 or qubit.shape[1] == 1):
        qubit = qubit.flatten()
    if qubit.ndim == 2 and qubit.shape[0] != qubit.shape[1]:
        raise ValueError(f"Wrong qubit matrix shape {qubit.shape}.")
    if qubit.ndim != 1 and qubit.ndim != 2:
        raise ValueError(f"Wrong qubit matrix shape {qubit.shape}.")
    is_sym = True
    n = qubit.shape[0]
    if not is_power_of_two(n):
        raise ValueError(f"Wrong qubit matrix size {n} is not a power of 2.")
    nq = int(np.log2(n))
    dim = nq // n_qubits + 1
    if nq % n_qubits == 0 and nq != n_qubits:
        ind = _symmetric_state_index(dim, n_qubits)
    else:
        raise ValueError(f"Wrong qubit matrix shape {qubit.shape} or number of qubits {n_qubits}.")
    if qubit.ndim == 1:
        for i in range(dim**n_qubits):
            i_ = ind[i]
            if len(i_) != 1:
                a = qubit[i_]
                is_sym = is_sym & np.allclose(a, a[0])
    elif qubit.ndim == 2:
        for i in range(dim**n_qubits):
            i_ = ind[i]
            for j in range(dim**n_qubits):
                j_ = ind[j]
                if len(i_) != 1 or len(j_) != 1:
                    a = qubit[np.ix_(i_, j_)]
                    is_sym = is_sym & np.allclose(a, a[0][0])
    return is_sym


[文档]def qudit_symmetric_decoding(qubit: np.ndarray, n_qubits: int = 1) -> np.ndarray: r""" Qudit symmetric decoding, decodes a qubit symmetric state or matrix into a qudit state or matrix. The input qubit state/matrix must preserve the symmetry required by the qudit-qubit mapping. For example, in a qutrit(d=3) to two-qubit mapping: .. math:: \begin{align} \ket{00\cdots00}&\to\ket{0} \\[.5ex] \frac{\ket{0\cdots01}+\ket{0\cdots010}+\ket{10\cdots0}}{\sqrt{d-1}}&\to\ket{1} \\ \frac{\ket{0\cdots011}+\ket{0\cdots0101}+\ket{110\cdots0}}{\sqrt{d-1}}&\to\ket{2} \\ \vdots&\qquad\vdots \\[.5ex] \ket{11\cdots11}&\to\ket{d-1} \end{align} The symmetry requires that states in the same symmetric subspace must have equal amplitudes. For example, states ¦01⟩ and ¦10⟩ belong to the same symmetric subspace and must have equal amplitudes. Args: qubit (np.ndarray): the qubit symmetric state or matrix that needs to be decoded, where the qubit state or matrix must preserve symmetry. n_qubits (int): the number of qubits in the qubit symmetric state or matrix. Default: ``1``. Returns: np.ndarray, the qudit state or matrix obtained after the qudit symmetric decoding. Raises: ValueError: If the input qubit state/matrix does not preserve the required symmetry. Examples: >>> import numpy as np >>> from mindquantum.algorithm.library.qudit_mapping import qudit_symmetric_decoding >>> # A symmetric qubit state where amplitudes in |01⟩ and |10⟩ are equal >>> qubit = np.array([1., 2., 2., 3.]) >>> qubit /= np.linalg.norm(qubit) >>> print(qubit) [0.23570226 0.47140452 0.47140452 0.70710678] >>> print(qudit_symmetric_decoding(qubit)) [0.23570226+0.j 0.66666667+0.j 0.70710678+0.j] """ _check_input_type("qubit", np.ndarray, qubit) _check_int_type("n_qubits", n_qubits) _check_value_should_not_less("n_qubits", 1, n_qubits) if qubit.ndim == 2 and (qubit.shape[0] == 1 or qubit.shape[1] == 1): qubit = qubit.flatten() if qubit.ndim == 2 and qubit.shape[0] != qubit.shape[1]: raise ValueError(f"Wrong qubit matrix shape {qubit.shape}.") if qubit.ndim != 1 and qubit.ndim != 2: raise ValueError(f"Wrong qubit matrix shape {qubit.shape}.") n = qubit.shape[0] if not is_power_of_two(n): raise ValueError(f"Wrong qubit matrix size {n} is not a power of 2.") nq = int(np.log2(n)) dim = nq // n_qubits + 1 if nq % n_qubits == 0 and nq != n_qubits: ind = _symmetric_state_index(dim, n_qubits) else: raise ValueError(f"Wrong qubit matrix shape {qubit.shape} or number of qubits {n_qubits}.") if qubit.ndim == 1: qudit = np.zeros(dim**n_qubits, dtype=np.complex128) for i in range(dim**n_qubits): i_ = ind[i] qubit_i = qubit[i_] if np.allclose(qubit_i, qubit_i[0]): qudit[i] = qubit_i[0] * np.sqrt(len(i_)) else: raise ValueError("Qubit matrix is not symmetric.") elif qubit.ndim == 2: qudit = np.zeros([dim**n_qubits, dim**n_qubits], dtype=np.complex128) for i in range(dim**n_qubits): i_ = ind[i] for j in range(dim**n_qubits): j_ = ind[j] qubit_ij = qubit[np.ix_(i_, j_)] if np.allclose(qubit_ij, qubit_ij[0][0]): div = np.sqrt(len(i_)) * np.sqrt(len(j_)) qudit[i, j] = qubit_ij[0][0] * div else: raise ValueError("Qubit matrix is not symmetric.") return qudit
[文档]def qudit_symmetric_encoding(qudit: np.ndarray, n_qudits: int = 1, is_csr: bool = False) -> np.ndarray: r""" Qudit symmetric encoding, encodes a qudit state or matrix into a qubit symmetric state or matrix. .. math:: \begin{align} \ket{0}&\to\ket{00\cdots00} \\[.5ex] \ket{1}&\to\frac{\ket{0\cdots01}+\ket{0\cdots010}+\ket{10\cdots0}}{\sqrt{d-1}} \\ \ket{2}&\to\frac{\ket{0\cdots011}+\ket{0\cdots0101}+\ket{110\cdots0}}{\sqrt{d-1}} \\ \vdots&\qquad\vdots \\[.5ex] \ket{d-1}&\to\ket{11\cdots11} \end{align} Args: qudit (np.ndarray): the qudit state or matrix that needs to be encoded. n_qudits (int): the number of qudits in the qudit state or matrix. Default: ``1``. is_csr (bool): whether to return the matrix in CSR (Compressed Sparse Row) format. Default: False. Returns: np.ndarray, the qubit symmetric state or matrix obtained after the qudit symmetric encoding. Examples: >>> import numpy as np >>> from mindquantum.algorithm.library.qudit_mapping import qudit_symmetric_encoding >>> qudit = np.array([1., 2., 3.]) >>> qudit /= np.linalg.norm(qudit) >>> print(qudit) [0.26726124 0.53452248 0.80178373] >>> print(qudit_symmetric_encoding(qudit)) [0.26726124+0.j 0.37796447+0.j 0.37796447+0.j 0.80178373+0.j] """ _check_input_type("qudit", np.ndarray, qudit) _check_int_type("n_qudits", n_qudits) _check_value_should_not_less("n_qudits", 1, n_qudits) _check_input_type("is_csr", bool, is_csr) if qudit.ndim == 2 and (qudit.shape[0] == 1 or qudit.shape[1] == 1): qudit = qudit.flatten() if qudit.ndim == 2 and qudit.shape[0] != qudit.shape[1]: raise ValueError(f"Wrong qudit matrix shape {qudit.shape}.") if qudit.ndim != 1 and qudit.ndim != 2: raise ValueError(f"Wrong qudit matrix shape {qudit.shape}.") dim = round(qudit.shape[0] ** (1 / n_qudits), 12) if dim % 1 == 0: dim = int(dim) n = 2 ** ((dim - 1) * n_qudits) ind = _symmetric_state_index(dim, n_qudits) else: raise ValueError(f"Wrong qudit matrix shape {qudit.shape} or number of qudits {n_qudits}.") if qudit.ndim == 1: qubit = csr_matrix((n, 1), dtype=np.complex128) for i in range(dim**n_qudits): ind_i = ind[i] num_i = len(ind_i) data = np.ones(num_i) * qudit[i] / np.sqrt(num_i) i_ = (ind_i, np.zeros(num_i)) qubit += csr_matrix((data, i_), shape=(n, 1)) if not is_csr: qubit = qubit.toarray().flatten() elif qudit.ndim == 2: qubit = csr_matrix((n, n), dtype=np.complex128) for i in range(dim**n_qudits): ind_i = ind[i] num_i = len(ind_i) for j in range(dim**n_qudits): ind_j = ind[j] num_j = len(ind_j) i_ = np.repeat(ind_i, num_j) j_ = np.tile(ind_j, num_i) div = np.sqrt(num_i) * np.sqrt(num_j) data = np.ones(num_i * num_j) * qudit[i, j] / div qubit += csr_matrix((data, (i_, j_)), shape=(n, n)) if not is_csr: qubit = qubit.toarray() return qubit
def _two_level_unitary_synthesis(basis: str, ind: List[int], pr_str: List[str], obj: List[int]) -> Circuit: """ Synthesize a qutrit two-level unitary gate with qubit circuit. Args: basis (str): decomposition basis, can be one of ``"zyz"`` or ``"u3"``. ind (List[int]): the subspace index of the qutrit two-level unitary gate. pr_str (List[str]): the params name of the qutrit two-level unitary gate. obj (List[int]): object qubits. Returns: :class:`~.core.circuit.Circuit`, qubit circuit that can synthesize a qutrit two-level unitary gate. """ if len(ind) != 2: raise ValueError(f"The qutrit unitary index length {len(ind)} should be 2.") if len(set(ind)) != len(ind): raise ValueError(f"The qutrit unitary index {ind} cannot be repeated") if min(ind) < 0 or max(ind) >= 3: raise ValueError(f"The qutrit unitary index {ind} should in 0 to 2.") if len(pr_str) != 3: raise ValueError(f"The qutrit unitary params length {len(pr_str)} should be 3.") circ = Circuit() if ind == [0, 1]: corr = Circuit() + X(obj[1], obj[0]) + RY(np.pi / 2).on(obj[0], obj[1]) + X(obj[1], obj[0]) + X(obj[1]) elif ind == [0, 2]: corr = Circuit() + X(obj[0]) + X(obj[1], obj[0]) + X(obj[0]) elif ind == [1, 2]: corr = Circuit() + X(obj[1], obj[0]) + RY(-np.pi / 2).on(obj[0], obj[1]) + X(obj[1], obj[0]) circ += corr if basis == "zyz": circ += RZ(pr_str[0]).on(obj[0], obj[1]) circ += RY(pr_str[1]).on(obj[0], obj[1]) circ += RZ(pr_str[2]).on(obj[0], obj[1]) elif basis == "u3": theta, phi, lam = pr_str circ += U3(theta, phi, lam).on(obj[0], obj[1]) else: raise ValueError(f"{basis} is not a supported decomposition method of {optional_basis}.") circ += corr.hermitian() return circ def _single_qutrit_unitary_synthesis(basis: str, name: str, obj: List[int]) -> Circuit: """ Synthesize a single qutrit unitary gate with qubit circuit. Args: basis (str): decomposition basis, can be one of ``"zyz"`` or ``"u3"``. name (str): the name of the single qutrit unitary gate. obj (List[int]): object qubits. Returns: :class:`~.core.circuit.Circuit`, qubit circuit that can synthesize a single qutrit unitary gate. """ circ = Circuit() index = [[0, 1], [0, 2], [1, 2]] if basis == "zyz": for i, ind in enumerate(index): pr_ind = f"{''.join(str(i) for i in ind)}_{i}" pr_str = [f"{name}RZ{pr_ind}", f"{name}RY{pr_ind}", f"{name}Rz{pr_ind}"] circ += _two_level_unitary_synthesis(basis, ind, pr_str, obj) elif basis == "u3": for i, ind in enumerate(index): pr_ind = f"{''.join(str(i) for i in ind)}_{i}" pr_str = [f"{name}𝜃{pr_ind}", f"{name}𝜑{pr_ind}", f"{name}𝜆{pr_ind}"] circ += _two_level_unitary_synthesis(basis, ind, pr_str, obj) else: raise ValueError(f"{basis} is not a supported decomposition method of {optional_basis}.") return circ def _controlled_rotation_synthesis(ind: List[int], name: str, obj: int, ctrl: List[int], state: int) -> Circuit: """ Synthesize a qutrit controlled rotation gate with qubit circuit. Args: ind (List[int]): the subspace index of the qutrit controlled rotation gate. name (str): the name of the qutrit controlled rotation gate. obj (int): object qubit. ctrl (List[int]): control qubits. state (int): the control state of the qutrit controlled rotation gate. Returns: :class:`~.core.circuit.Circuit`, qubit circuit that can synthesize a qutrit controlled rotation gate. """ circ = Circuit() if state == 0: if ind == [0, 1]: corr = ( Circuit() + X(ctrl[1]) + X(ctrl[2]) + X(ctrl[0], ctrl[1:] + [obj]) + RY(np.pi / 2).on(obj, ctrl) + X(ctrl[0], ctrl[1:] + [obj]) + X(ctrl[0], ctrl[1:]) ) elif ind == [0, 2]: corr = ( Circuit() + X(ctrl[1]) + X(ctrl[2]) + X(obj, ctrl[1:]) + X(ctrl[0], ctrl[1:] + [obj]) + X(obj, ctrl[1:]) ) elif ind == [1, 2]: corr = ( Circuit() + X(ctrl[1]) + X(ctrl[2]) + X(ctrl[0], ctrl[1:] + [obj]) + RY(-np.pi / 2).on(obj, ctrl) + X(ctrl[0], ctrl[1:] + [obj]) ) elif state == 1: if ind == [0, 1]: corr = ( Circuit() + X(ctrl[1], ctrl[2]) + RY(np.pi / 2).on(ctrl[2]) + X(ctrl[0], ctrl[1:] + [obj]) + RY(np.pi / 2).on(obj, ctrl) + X(ctrl[0], ctrl[1:] + [obj]) + X(ctrl[0], ctrl[1:]) ) elif ind == [0, 2]: corr = ( Circuit() + X(ctrl[1], ctrl[2]) + RY(np.pi / 2).on(ctrl[2]) + X(obj, ctrl[1:]) + X(ctrl[0], ctrl[1:] + [obj]) + X(obj, ctrl[1:]) ) elif ind == [1, 2]: corr = ( Circuit() + X(ctrl[1], ctrl[2]) + RY(np.pi / 2).on(ctrl[2]) + X(ctrl[0], ctrl[1:] + [obj]) + RY(-np.pi / 2).on(obj, ctrl) + X(ctrl[0], ctrl[1:] + [obj]) ) elif state == 2: if ind == [0, 1]: corr = ( Circuit() + X(ctrl[0], ctrl[1:] + [obj]) + RY(np.pi / 2).on(obj, ctrl) + X(ctrl[0], ctrl[1:] + [obj]) + X(ctrl[0], ctrl[1:]) ) elif ind == [0, 2]: corr = Circuit() + X(obj, ctrl[1:]) + X(ctrl[0], ctrl[1:] + [obj]) + X(obj, ctrl[1:]) elif ind == [1, 2]: corr = ( Circuit() + X(ctrl[0], ctrl[1:] + [obj]) + RY(-np.pi / 2).on(obj, ctrl) + X(ctrl[0], ctrl[1:] + [obj]) ) circ += corr if "RX" in name: circ = circ + RX(name).on(obj, ctrl) elif "RY" in name: circ = circ + RY(name).on(obj, ctrl) elif "RZ" in name: circ = circ + RZ(name).on(obj, ctrl) elif "GP" in name: circ = circ + GlobalPhase(name).on(obj, ctrl) circ += corr.hermitian() return circ def _controlled_diagonal_synthesis(name: str, obj: int, ctrl: List[int], state: int) -> Circuit: """ Synthesize a qutrit controlled diagonal gate with qubit circuit. Args: name (str): the name of the qutrit controlled diagonal gate. obj (int): object qubit. ctrl (List[int]): control qubits. state (int): the control state of the qutrit controlled diagonal gate. Returns: :class:`~.core.circuit.Circuit`, qubit circuit that can synthesize a qutrit controlled diagonal gate. """ circ = Circuit() circ += _controlled_rotation_synthesis([0, 1], f"{name}RZ01", obj, ctrl, state) circ += _controlled_rotation_synthesis([0, 2], f"{name}RZ02", obj, ctrl, state) circ += _controlled_rotation_synthesis([0, 1], f"{name}GP", obj, ctrl, state) circ += _controlled_rotation_synthesis([0, 2], f"{name}GP", obj, ctrl, state) circ += _controlled_rotation_synthesis([1, 2], f"{name}GP", obj, ctrl, state) return circ
[文档]def qutrit_symmetric_ansatz(gate: UnivMathGate, basis: str = "zyz", with_phase: bool = False) -> Circuit: r""" Construct a qubit ansatz that preserves the symmetry of encoding for arbitrary qutrit gate. This function constructs a parameterized quantum circuit (ansatz) that can implement any qutrit gate while preserving the symmetry required by the qutrit-qubit mapping. The symmetry preservation means that states in the same symmetric subspace will maintain equal amplitudes after the gate operation. For a single qutrit (mapped to 2 qubits), the symmetric subspaces are: - {¦00⟩} for qutrit state ¦0⟩ - {(¦01⟩+¦10⟩)/√2} for qutrit state ¦1⟩ - {¦11⟩} for qutrit state ¦2⟩ Reference: `Synthesis of multivalued quantum logic circuits by elementary gates <https://journals.aps.org/pra/abstract/10.1103/PhysRevA.87.012325>`_, `Optimal synthesis of multivalued quantum circuits <https://journals.aps.org/pra/abstract/10.1103/PhysRevA.92.062317>`_. Args: gate (:class:`~.core.gates.UnivMathGate`): symmetry-preserving qubit gate encoded by qutrit gate. basis (str): decomposition basis, can be one of ``"zyz"`` or ``"u3"``. The ZYZ basis uses RZ and RY rotations, while the U3 basis uses the U3 gate. Default: ``"zyz"``. with_phase (bool): whether return global phase in form of a :class:`~.core.gates.GlobalPhase` gate on the qubit circuit. Default: ``False``. Returns: :class:`~.core.circuit.Circuit`, qubit ansatz that preserves the symmetry of qutrit encoding. Raises: ValueError: If the input gate is not symmetric or if the number of qubits is not compatible with qutrit encoding (must be 2 or 4 qubits). Examples: >>> from scipy.stats import unitary_group >>> from mindquantum.core.circuit import Circuit >>> from mindquantum.core.gates import UnivMathGate >>> from mindquantum.algorithm import qutrit_symmetric_ansatz, qudit_symmetric_encoding >>> qutrit_unitary = unitary_group.rvs(3) >>> qubit_unitary = qudit_symmetric_encoding(qutrit_unitary) >>> qubit_gate = UnivMathGate('U', qubit_unitary).on([0, 1]) >>> ansatz_circ = qutrit_symmetric_ansatz(qubit_gate) """ _check_input_type("gate", UnivMathGate, gate) _check_input_type("basis", str, basis) _check_input_type("with_phase", bool, with_phase) if gate.ctrl_qubits: raise ValueError(f"Currently not applicable for a controlled gate {gate}.") basis = basis.lower() if basis not in optional_basis: raise ValueError(f"{basis} is not a supported decomposition method of {optional_basis}.") circ = Circuit() obj = gate.obj_qubits name = f"{gate.name}_" if not _is_symmetric(gate.matrix(), int(len(obj) / 2)): raise ValueError(f"{gate} is not a symmetric gate.") if len(obj) == 2: circ += _single_qutrit_unitary_synthesis(basis, f"{name}", obj) elif len(obj) == 4: circ += _single_qutrit_unitary_synthesis(basis, f"{name}U1_", obj[:2]) circ += _controlled_diagonal_synthesis(f"{name}CD1_", obj[0], obj[1:], 1) circ += _single_qutrit_unitary_synthesis(basis, f"{name}U2_", obj[:2]) circ += _controlled_diagonal_synthesis(f"{name}CD2_", obj[0], obj[1:], 2) circ += _single_qutrit_unitary_synthesis(basis, f"{name}U3_", obj[:2]) circ += _controlled_rotation_synthesis([1, 2], f"{name}RY1_2", obj[-1], obj[::-1][1:], 2) circ += _controlled_rotation_synthesis([1, 2], f"{name}RY1_1", obj[-1], obj[::-1][1:], 1) circ += _single_qutrit_unitary_synthesis(basis, f"{name}U4_", obj[:2]) circ += _controlled_diagonal_synthesis(f"{name}CD3_", obj[0], obj[1:], 2) circ += _single_qutrit_unitary_synthesis(basis, f"{name}U5_", obj[:2]) circ += _controlled_rotation_synthesis([0, 1], f"{name}RY2_2", obj[-1], obj[::-1][1:], 2) circ += _controlled_rotation_synthesis([0, 1], f"{name}RY2_1", obj[-1], obj[::-1][1:], 1) circ += _single_qutrit_unitary_synthesis(basis, f"{name}U6_", obj[:2]) circ += _controlled_diagonal_synthesis(f"{name}CD4_", obj[0], obj[1:], 0) circ += _single_qutrit_unitary_synthesis(basis, f"{name}U7_", obj[:2]) circ += _controlled_rotation_synthesis([1, 2], f"{name}RY3_2", obj[-1], obj[::-1][1:], 2) circ += _controlled_rotation_synthesis([1, 2], f"{name}RY3_1", obj[-1], obj[::-1][1:], 1) circ += _single_qutrit_unitary_synthesis(basis, f"{name}U8_", obj[:2]) circ += _controlled_diagonal_synthesis(f"{name}CD5_", obj[0], obj[1:], 2) circ += _single_qutrit_unitary_synthesis(basis, f"{name}U9_", obj[:2]) else: raise ValueError( "Currently only applicable when the n_qutrits is 1 or 2, which means the n_qubits must be 2 or 4." ) if with_phase: for i in obj: circ += GlobalPhase(f"{name}phase").on(i) return circ
[文档]def mat_to_op(mat, little_endian: bool = True) -> QubitOperator: """ Convert a matrix to a QubitOperator. Default output is in little endian. Args: mat: the qubit matrix that needs to be converted to a QubitOperator. little_endian (bool): whether the qubit order is little endian. This means the leftmost qubit is the qubit with the highest index. Default: ``True``. Returns: :class:`~.core.operators.QubitOperator`, the QubitOperator obtained after the matrix conversion. Examples: >>> import numpy as np >>> from mindquantum.algorithm.library.qudit_mapping import mat_to_op >>> mat = np.array([[1, 0, 0, 1], [0, 1, 1, 0], [0, 1, 1, 0], [1, 0, 0, 1]]) >>> print(mat_to_op(mat, 2)) 1 [] + 1 [X0 X1] """ _check_input_type("mat", (np.ndarray, sp.sparse.spmatrix), mat) _check_input_type("little_endian", bool, little_endian) def pairs_to_op(i, j): bin_i = bin(i)[2:].zfill(n_qubits) bin_j = bin(j)[2:].zfill(n_qubits) if little_endian: bin_i = bin_i[::-1] bin_j = bin_j[::-1] term = QubitOperator('') for ind, (b1, b2) in enumerate(zip(bin_i, bin_j)): if b1 + b2 == '00': term *= QubitOperator(f'I{ind}', 1 / 2) + QubitOperator(f'Z{ind}', 1 / 2) elif b1 + b2 == '11': term *= QubitOperator(f'I{ind}', 1 / 2) + QubitOperator(f'Z{ind}', -1 / 2) elif b1 + b2 == '01': term *= QubitOperator(f'X{ind}', 1 / 2) + QubitOperator(f'Y{ind}', 1 / 2 * 1j) elif b1 + b2 == '10': term *= QubitOperator(f'X{ind}', 1 / 2) + QubitOperator(f'Y{ind}', -1 / 2 * 1j) return term if np.shape(mat)[0] != np.shape(mat)[1]: raise ValueError(f"Not a legal qubit matrix {mat}.") if np.ceil(np.log2(np.shape(mat)[0])) != np.floor(np.log2(np.shape(mat)[0])): raise ValueError(f"Not a legal qubit matrix {mat}.") n_qubits = int(np.ceil(np.log2(np.shape(mat)[0]))) res = QubitOperator() if sp.sparse.issparse(mat): coo_mat = sp.sparse.coo_matrix(mat) for i, j, value in zip(coo_mat.row, coo_mat.col, coo_mat.data): if np.abs(value) == 0: continue term = pairs_to_op(i, j) res += term * value else: for i in range(2**n_qubits): for j in range(2**n_qubits): if np.abs(mat[i][j]) == 0: continue term = pairs_to_op(i, j) res += term * mat[i][j] return res.compress()