Beispiel #1
0
def U_theta(theta, N):
    """
    Quantum Neural Network
    """

    # Initialize the quantum neural network by the number of qubits (width of the network)
    cir = UAnsatz(N)

    # Use a built-in QNN template
    cir.universal_2_qubit_gate(theta, [0, 1])

    # Return the Unitary matrix simulated by QNN
    return cir.U
Beispiel #2
0
def U_theta(theta, N):
    """
    Quantum Neural Network
    """

    # 按照量子比特数量/网络宽度初始化量子神经网络
    cir = UAnsatz(N)

    # 调用内置的量子神经网络模板
    cir.universal_2_qubit_gate(theta)

    # 返回量子神经网络所模拟的酉矩阵 U
    return cir.U
def Encoder(theta):

    # use UAnsatz to initialize the network
    cir = UAnsatz(N)

    for layer_num in range(cir_depth):

        for which_qubit in range(N):
            cir.ry(theta[block_len * layer_num * N + which_qubit], which_qubit)
            cir.rz(theta[(block_len * layer_num + 1) * N + which_qubit],
                   which_qubit)

        for which_qubit in range(N - 1):
            cir.cnot([which_qubit, which_qubit + 1])
        cir.cnot([N - 1, 0])

    return cir.U
def Encoder(theta):

    # 用 UAnsatz 初始化网络
    cir = UAnsatz(N)

    # 搭建层级结构:
    for layer_num in range(cir_depth):

        for which_qubit in range(N):
            cir.ry(theta[block_len * layer_num * N + which_qubit], which_qubit)
            cir.rz(theta[(block_len * layer_num + 1) * N + which_qubit],
                   which_qubit)

        for which_qubit in range(N - 1):
            cir.cnot([which_qubit, which_qubit + 1])
        cir.cnot([N - 1, 0])

    return cir.U
Beispiel #5
0
    def forward(self, H_C_ls):
        """
        Forward propagation
        """
        cir = UAnsatz(self.num_qubits)
        cir.complex_entangled_layer(self.theta, self.p)
        cir.run_state_vector()
        loss = cir.expecval(H_C_ls)

        return loss, cir
def circuit_QAOA(theta, input_state, adjacency_matrix, N, P):
    """
    This function constructs the parameterized QAOA circuit which is composed of P layers of two blocks:
    one block is U_theta[layer][0] based on the problem Hamiltonian H which encodes the classical problem,
    and the other is U_theta[layer][1] constructed from the driving Hamiltonian describing the rotation around Pauli X
    acting on each qubit. It finally outputs the final state of the QAOA circuit.

    Args:
         theta: parameters to be optimized in the QAOA circuit
         input_state: initial state of the QAOA circuit which usually is the uniform superposition of 2^N bit-strings
                    in the computational basis $|0\rangle, |1\rangle$
         adjacency_matrix:  the adjacency matrix of the graph encoding the classical problem
         N: number of qubits, or equivalently, the number of nodes in the given graph
         P: number of layers of two blocks in the QAOA circuit
    Returns:
        the final state of the QAOA circuit: cir.state

    """

    cir = UAnsatz(N, input_state=input_state)
    # The first loop defines the QAOA circuit with P layers of two blocks
    for layer in range(P):
        # The second and third loops aim to construct the first block U_theta[layer][0] which involves
        # two-qubit operation e^{-i\beta Z_iZ_j} acting on a pair of qubits or nodes i and j in the circuit.
        for row in range(N):
            for col in range(N):
                if abs(adjacency_matrix[row, col]) and row < col:
                    cir.cnot([row + 1, col + 1])
                    cir.rz(
                        theta=theta[layer][0] * adjacency_matrix[row, col],
                        which_qubit=col + 1,
                    )
                    cir.cnot([row + 1, col + 1])
        # This loops constructs the second block U_theta only involving the single-qubit operation e^{-i\beta X}.
        for i in range(1, N + 1):
            cir.rx(theta=theta[layer][1], which_qubit=i)

    return cir.state
Beispiel #7
0
def U_theta(theta, Hamiltonian, N, D):
    """
    Quantum Neural Network
    """
    # Initialize the quantum neural network by the number of qubits (width of the network)
    cir = UAnsatz(N)

    # Use built-in template (R_y + CNOT)
    cir.real_entangled_layer(theta[:D], D)

    # Add a layer of R_y rotation gates
    for i in range(N):
        cir.ry(theta=theta[D][i][0], which_qubit=i)

    # Act QNN on the default initial state |0000>
    cir.run_state_vector()

    # Calculate the expectation value of the given Hamiltonian
    expectation_val = cir.expecval(Hamiltonian)

    return expectation_val
Beispiel #8
0
def U_theta(theta, Hamiltonian, N, D):
    """
    Quantum Neural Network
    """

    # 按照量子比特数量/网络宽度初始化量子神经网络
    cir = UAnsatz(N)

    # 内置的 {R_y + CNOT} 电路模板
    cir.real_entangled_layer(theta[:D], D)

    # 铺上最后一列 R_y 旋转门
    for i in range(N):
        cir.ry(theta=theta[D][i][0], which_qubit=i)

    # 量子神经网络作用在默认的初始态 |0000>上
    cir.run_state_vector()

    # 计算给定哈密顿量的期望值
    expectation_val = cir.expecval(Hamiltonian)

    return expectation_val
Beispiel #9
0
def U_theta(initial_state, theta, N, D):
    """
    Quantum Neural Network
    """

    # Initialize the quantum neural network by the number of qubits (width of the network)
    cir = UAnsatz(N)

    # Use in-built template (R_y + CNOT)
    cir.real_entangled_layer(theta[:D], D)

    # Add a layer of R_y rotation gate
    for i in range(N):
        cir.ry(theta=theta[D][i][0], which_qubit=i)

    # Act quantum neural network on initilized state
    final_state = cir.run_density_matrix(initial_state)

    return final_state
Beispiel #10
0
def U_theta(initial_state, theta, N, D):
    """
    Quantum Neural Network
    """

    # 按照量子比特数量/网络宽度初始化量子神经网络
    cir = UAnsatz(N)

    # 内置的 {R_y + CNOT} 电路模板
    cir.real_entangled_layer(theta[:D], D)

    # 铺上最后一列 R_y 旋转门
    for i in range(N):
        cir.ry(theta=theta[D][i][0], which_qubit=i)

    # 量子神经网络作用在给定的初始态上
    final_state = cir.run_density_matrix(initial_state)

    return final_state
Beispiel #11
0
    def reset_state(self, status, state, which_qubits):
        r"""用于重置你想要的量子比特上的部分量子态。

        Args:
            status (LoccStatus or list): 输入的 LOCC 态节点,其类型应该为 ``LoccStatus`` 或者由其组成的 ``list``
            state (ComplexVariable): 输入的量子态的矩阵形式
            which_qubits (tuple or list): 指定需要被重置的量子比特编号,其形式为 ``(party_id, qubit_id)`` 的 ``tuple``,或者由其组成的 ``list``

        Returns:
            LoccStatus or list: 重置部分量子比特的量子态后的 LOCC 态节点,其类型为 ``LoccStatus`` 或者由其组成的 ``list``
        """
        if isinstance(which_qubits, tuple):
            which_qubits = [which_qubits]
        qubits_list = list()
        for party_id, qubit_id in which_qubits:
            if isinstance(party_id, str):
                qubits_list.append(self.parties_by_name[party_id][qubit_id])
            elif isinstance(party_id, int):
                qubits_list.append(self.parties_by_number[party_id][qubit_id])
            else:
                raise ValueError
        m = len(qubits_list)
        if isinstance(status, LoccStatus):
            n = int(log2(sqrt(status.state.numpy().size)))
        elif isinstance(status, list):
            n = int(log2(sqrt(status[0].state.numpy().size)))
        else:
            raise ValueError("can't recognize the input status")
        assert max(qubits_list) <= n, "qubit index out of range"

        qubit2idx = list(range(0, n))
        idx2qubit = list(range(0, n))
        swap_history = list()

        cir0 = UAnsatz(n)
        for i, ele in enumerate(qubits_list):
            if qubit2idx[
                    ele] != i:  # if qubit2idx[ele] is i, then swap([ele, i]) is identity
                swap_history.append((i, qubit2idx[ele]))
                cir0.swap([i, qubit2idx[ele]])
                qubit2idx[idx2qubit[i]] = qubit2idx[ele]
                idx2qubit[qubit2idx[ele]] = idx2qubit[i]
                idx2qubit[i] = ele
                qubit2idx[ele] = i

        cir1 = UAnsatz(n)
        swap_cnt = len(swap_history)
        for idx in range(0, swap_cnt):
            a, b = swap_history[swap_cnt - 1 - idx]
            cir1.swap([b, a])

        if isinstance(status, LoccStatus):
            _state = cir0.run_density_matrix(status.state)
            _state = partial_trace(_state, 2**m, 2**(n - m), 1)
            _state = kron(state, _state)
            _state = cir1.run_density_matrix(_state)
            new_status = LoccStatus(_state, status.prob,
                                    status.measured_result)
        elif isinstance(status, list):
            new_status = list()
            for each_status in status:
                _state = cir0.run_density_matrix(each_status.state)
                _state = partial_trace(_state, 2**m, 2**(n - m), 1)
                _state = kron(state, _state)
                _state = cir1.run_density_matrix(_state)
                new_status.append(
                    LoccStatus(_state, each_status.prob,
                               each_status.measured_result))
        else:
            raise ValueError("can't recognize the input status")

        return new_status
Beispiel #12
0
def circuit_QAOA(theta, adjacency_matrix, N, P):
    """
    This function constructs the parameterized QAOA circuit which is composed of P layers of two blocks:
    one block is based on the problem Hamiltonian H which encodes the classical problem,
    and the other is constructed from the driving Hamiltonian describing the rotation around Pauli X
    acting on each qubit. It outputs the final state of the QAOA circuit.

    Args:
        theta: parameters to be optimized in the QAOA circuit
        adjacency_matrix:  the adjacency matrix of the graph encoding the classical problem
        N: number of qubits, or equivalently, the number of parameters in the original classical problem
        P: number of layers of two blocks in the QAOA circuit
    Returns:
        the QAOA circuit
    """

    cir = UAnsatz(N)

    # prepare the input state in the uniform superposition of 2^N bit-strings in the computational basis
    cir.superposition_layer()
    # This loop defines the QAOA circuit with P layers of two blocks
    for layer in range(P):
        # The second and third loops construct the first block which involves two-qubit operation
        #  e^{-i\gamma Z_iZ_j} acting on a pair of qubits or nodes i and j in the circuit in each layer.
        for row in range(N):
            for col in range(N):
                if adjacency_matrix[row, col] and row < col:
                    cir.cnot([row, col])
                    cir.rz(theta[layer][0], col)
                    cir.cnot([row, col])
        # This loop constructs the second block only involving the single-qubit operation e^{-i\beta X}.
        for i in range(N):
            cir.rx(theta[layer][1], i)

    return cir
Beispiel #13
0
def circuit_extend_QAOA(theta, adjacency_matrix, N, P):
    """
    This is an extended version of the QAOA circuit, and the main difference is the block constructed
    from the driving Hamiltonian describing the rotation around an arbitrary direction on each qubit.

    Args:
        theta: parameters to be optimized in the QAOA circuit
        input_state: input state of the QAOA circuit which usually is the uniform superposition of 2^N bit-strings
                     in the computational basis
        adjacency_matrix:  the adjacency matrix of the problem graph encoding the original problem
        N: number of qubits, or equivalently, the number of parameters in the original classical problem
        P: number of layers of two blocks in the QAOA circuit
    Returns:
        the extended QAOA circuit

    Note:
        If this circuit_extend_QAOA function is used to construct QAOA circuit, then we need to change the parameter layer
        in the Net function defined below from the Net(shape=[D, 2]) for circuit_QAOA function to Net(shape=[D, 4])
        because the number of parameters doubles in each layer in this QAOA circuit.
    """
    cir = UAnsatz(N)

    # prepare the input state in the uniform superposition of 2^N bit-strings in the computational basis
    cir.superposition_layer()
    for layer in range(P):
        for row in range(N):
            for col in range(N):
                if adjacency_matrix[row, col] and row < col:
                    cir.cnot([row, col])
                    cir.rz(theta[layer][0], col)
                    cir.cnot([row, col])

        for i in range(N):
            cir.u3(*theta[layer][1:], i)

    return cir
def U_theta(theta, input_state, N, D):  # definition of U_theta
    """
    :param theta:
    :param input_state:
    :return:
    """

    cir = UAnsatz(N, input_state=input_state)
    for i in range(N):
        cir.rx(theta=theta[0][0][i], which_qubit=i + 1)
        cir.ry(theta=theta[0][1][i], which_qubit=i + 1)
        cir.rx(theta=theta[0][2][i], which_qubit=i + 1)

    for repeat in range(D):
        for i in range(1, N):
            cir.cnot(control=[i, i + 1])

        for i in range(N):
            cir.ry(theta=theta[repeat][0][i], which_qubit=i + 1)
            # cir.ry(theta=theta[repeat][1][i], which_qubit=i + 1)
            # cir.ry(theta=theta[repeat][2][i], which_qubit=i + 1)

    return cir.state
Beispiel #15
0
def circuit_QAOA(E, V, n, p, gamma, beta):
    """
    This function constructs the parameterized QAOA circuit which is composed of P layers of two blocks:
    one block is based on the problem Hamiltonian H which encodes the classical problem,
    and the other is constructed from the driving Hamiltonian describing the rotation around Pauli X
    acting on each qubit. It outputs the final state of the QAOA circuit.
    Args:
        E: edges of the graph
        V: vertices of the graph
        n: number of qubits in th QAOA circuit
        p: number of layers of two blocks in the QAOA circuit
        gamma: parameter to be optimized in the QAOA circuit, parameter for the first block
        beta: parameter to be optimized in the QAOA circui, parameter for the second block
    Returns:
        the QAOA circuit
    """
    cir = UAnsatz(n)
    cir.superposition_layer()
    for layer in range(p):
        for (u, v) in E:
            cir.cnot([u, v])
            cir.rz(gamma[layer], v)
            cir.cnot([u, v])
        for v in V:
            cir.rx(beta[layer], v)
    return cir
Beispiel #16
0
def U_theta(theta, n, depth):  
    """
    :param theta: dim: [n, depth + 3]
    :param n: number of qubits
    :param depth: circuit depth
    :return: U_theta
    """
    # 初始化网络
    cir = UAnsatz(n)
    
    # 先搭建广义的旋转层
    for i in range(n):
        cir.rz(theta[i][0], i)
        cir.ry(theta[i][1], i)
        cir.rz(theta[i][2], i)

    # 默认深度为 depth = 1
    # 搭建纠缠层和 Ry旋转层
    for d in range(3, depth + 3):
        for i in range(n-1):
            cir.cnot([i, i + 1])
        cir.cnot([n-1, 0])
        for i in range(n):
            cir.ry(theta[i][d], i)

    return cir.U
Beispiel #17
0
def U_theta(theta, input_state, N, D):
    """
    Circuit
    """

    cir = UAnsatz(N, input_state=input_state)
    for i in range(N):
        cir.rz(theta=theta[0][0][i], which_qubit=i + 1)
        cir.ry(theta=theta[0][1][i], which_qubit=i + 1)
        cir.rz(theta=theta[0][2][i], which_qubit=i + 1)

    for repeat in range(D):
        for i in range(1, N):
            cir.cnot(control=[i, i + 1])

        for i in range(N):
            cir.ry(theta=theta[repeat][0][i], which_qubit=i + 1)
            cir.ry(theta=theta[repeat][1][i], which_qubit=i + 1)
            cir.rz(theta=theta[repeat][2][i], which_qubit=i + 1)

    return cir.state
Beispiel #18
0
    def partial_state(self, status, which_qubits, is_desired=True):
        r"""得到你想要的部分量子比特上的量子态。

        Args:
            status (LoccStatus or list): 输入的 LOCC 态节点,其类型应该为 ``LoccStatus`` 或者由其组成的 ``list``
            which_qubits (tuple or list): 指定的量子比特编号,其形式为 ``(party_id, qubit_id)`` 的 ``tuple`` ,或者由其组成的 ``list``
            is_desired (bool, optional): 默认是 ``True`` ,即返回量子比特上的部分量子态。如果为 ``False`` ,则抛弃这部分量子比特上的量子态,返回剩下的部分量子态

        Returns:
            LoccStatus or list: 得到部分量子态后的 LOCC 态节点,其类型为 ``LoccStatus`` 或者由其组成的 ``list``
        """
        if isinstance(which_qubits, tuple):
            which_qubits = [which_qubits]
        qubits_list = list()
        for party_id, qubit_id in which_qubits:
            if isinstance(party_id, str):
                qubits_list.append(self.parties_by_name[party_id][qubit_id])
            elif isinstance(party_id, int):
                qubits_list.append(self.parties_by_number[party_id][qubit_id])
            else:
                raise ValueError
        m = len(qubits_list)
        if isinstance(status, LoccStatus):
            n = int(log2(sqrt(status.state.numpy().size)))
        elif isinstance(status, list):
            n = int(log2(sqrt(status[0].state.numpy().size)))
        else:
            raise ValueError("can't recognize the input status")

        assert max(qubits_list) <= n, "qubit index out of range"
        origin_seq = list(range(0, n))
        target_seq = [idx for idx in origin_seq if idx not in qubits_list]
        target_seq = qubits_list + target_seq

        swaped = [False] * n
        swap_list = []
        for idx in range(0, n):
            if not swaped[idx]:
                next_idx = idx
                swaped[next_idx] = True
                while not swaped[target_seq[next_idx]]:
                    swaped[target_seq[next_idx]] = True
                    swap_list.append((next_idx, target_seq[next_idx]))
                    next_idx = target_seq[next_idx]

        cir = UAnsatz(n)
        for a, b in swap_list:
            cir.swap([a, b])

        if isinstance(status, LoccStatus):
            state = cir.run_density_matrix(status.state)
            if is_desired:
                state = partial_trace(state, 2**m, 2**(n - m), 2)
            else:
                state = partial_trace(state, 2**m, 2**(n - m), 1)
            new_status = LoccStatus(state, status.prob, status.measured_result)
        elif isinstance(status, list):
            new_status = list()
            for each_status in status:
                state = cir.run_density_matrix(each_status.state)
                if is_desired:
                    state = partial_trace(state, 2**m, 2**(n - m), 2)
                else:
                    state = partial_trace(state, 2**m, 2**(n - m), 1)
                new_status.append(
                    LoccStatus(state, each_status.prob,
                               each_status.measured_result))
        else:
            raise ValueError("can't recognize the input status")

        return new_status
Beispiel #19
0
    def reset_state(self, status, state, which_qubits):
        r"""用于重置你想要的量子比特上的部分量子态。

        Args:
            status (LoccStatus or list): 输入的 LOCC 态节点,其类型应该为 ``LoccStatus`` 或者由其组成的 ``list``
            state (Tensor): 输入的量子态的矩阵形式
            which_qubits (tuple or list): 指定需要被重置的量子比特编号,其形式为 ``(party_id, qubit_id)`` 的 ``tuple``,或者由其组成的 ``list``

        Returns:
            LoccStatus or list: 重置部分量子比特的量子态后的 LOCC 态节点,其类型为 ``LoccStatus`` 或者由其组成的 ``list``
        """
        if isinstance(which_qubits, tuple):
            which_qubits = [which_qubits]
        qubits_list = list()
        for party_id, qubit_id in which_qubits:
            if isinstance(party_id, str):
                qubits_list.append(self.parties_by_name[party_id][qubit_id])
            elif isinstance(party_id, int):
                qubits_list.append(self.parties_by_number[party_id][qubit_id])
            else:
                raise ValueError
        m = len(qubits_list)
        if isinstance(status, LoccStatus):
            n = int(log2(sqrt(status.state.numpy().size)))
        elif isinstance(status, list):
            n = int(log2(sqrt(status[0].state.numpy().size)))
        else:
            raise ValueError("can't recognize the input status")
        assert max(qubits_list) <= n, "qubit index out of range"

        origin_seq = list(range(0, n))
        target_seq = [idx for idx in origin_seq if idx not in qubits_list]
        target_seq = qubits_list + target_seq

        swaped = [False] * n
        swap_list = []
        for idx in range(0, n):
            if not swaped[idx]:
                next_idx = idx
                swaped[next_idx] = True
                while not swaped[target_seq[next_idx]]:
                    swaped[target_seq[next_idx]] = True
                    swap_list.append((next_idx, target_seq[next_idx]))
                    next_idx = target_seq[next_idx]

        cir0 = UAnsatz(n)
        for a, b in swap_list:
            cir0.swap([a, b])

        cir1 = UAnsatz(n)
        swap_list.reverse()
        for a, b in swap_list:
            cir1.swap([a, b])

        if isinstance(status, LoccStatus):
            _state = cir0.run_density_matrix(status.state)
            _state = partial_trace(_state, 2**m, 2**(n - m), 1)
            _state = kron(state, _state)
            _state = cir1.run_density_matrix(_state)
            new_status = LoccStatus(_state, status.prob,
                                    status.measured_result)
        elif isinstance(status, list):
            new_status = list()
            for each_status in status:
                _state = cir0.run_density_matrix(each_status.state)
                _state = partial_trace(_state, 2**m, 2**(n - m), 1)
                _state = kron(state, _state)
                _state = cir1.run_density_matrix(_state)
                new_status.append(
                    LoccStatus(_state, each_status.prob,
                               each_status.measured_result))
        else:
            raise ValueError("can't recognize the input status")

        return new_status
def U_theta(theta, n, depth):
    """
    :param theta: dim: [n, depth + 3]
    :param n: number of qubits
    :param depth: circuit depth
    :return: U_theta
    """
    cir = UAnsatz(n)

    for i in range(n):
        cir.rz(theta[i][0], i)
        cir.ry(theta[i][1], i)
        cir.rz(theta[i][2], i)

    for d in range(3, depth + 3):
        for i in range(n - 1):
            cir.cnot([i, i + 1])
        cir.cnot([n - 1, 0])
        for i in range(n):
            cir.ry(theta[i][d], i)

    return cir.U
Beispiel #21
0
def circuit_maxcut(E, V, p, gamma, beta):
    r"""构建用于最大割问题的 QAOA 参数化电路。

    Args:
        E: 图的边
        V: 图的顶点
        p: QAOA 电路的层数
        gamma: 与最大割问题哈密顿量相关的电路参数
        beta: 与混合哈密顿量相关的电路参数

    Returns:
        UAnsatz: 构建好的 QAOA 电路
    """
    # Number of qubits needed
    n = len(V)
    cir = UAnsatz(n)
    cir.superposition_layer()
    for layer in range(p):
        for (u, v) in E:
            cir.cnot([u, v])
            cir.rz(gamma[layer], v)
            cir.cnot([u, v])
        for i in V:
            cir.rx(beta[layer], i)

    return cir
def U_theta(theta, N):
    """
    U_theta
    """

    cir = UAnsatz(N)
    # ============== D1=2 ==============
    cir.ry(theta[0], 2)
    cir.rz(theta[1], 2)
    cir.cnot([2, 1])
    cir.ry(theta[2], 2)
    cir.rz(theta[3], 2)
    cir.cnot([2, 1])

    # ============== D2=2 ==============
    cir.ry(theta[4], 1)
    cir.ry(theta[5], 2)
    cir.rz(theta[6], 1)
    cir.rz(theta[7], 2)
    cir.cnot([1, 2])

    cir.ry(theta[8], 1)
    cir.ry(theta[9], 2)
    cir.rz(theta[10], 1)
    cir.rz(theta[11], 2)
    cir.cnot([1, 2])

    return cir.state
def circuit_extend_QAOA(theta, input_state, adjacency_matrix, N, P):
    """
    This is an extended version of the QAOA circuit, and the main difference is U_theta[layer]([1]-[3]) constructed
    from the driving Hamiltonian describing the rotation around an arbitrary direction on each qubit.

    Args:
        theta: parameters to be optimized in the QAOA circuit
        input_state: input state of the QAOA circuit which usually is the uniform superposition of 2^N bit-strings
                     in the computational basis
        adjacency_matrix:  the adjacency matrix of the problem graph encoding the original problem
        N: number of qubits, or equivalently, the number of parameters in the original classical problem
        P: number of layers of two blocks in the QAOA circuit
    Returns:
        final state of the QAOA circuit: cir.state

    Note: If this U_extend_theta function is used to construct QAOA circuit, then we need to change the parameter layer
           in the Net function defined below from the Net(shape=[D, 2]) for U_theta function to Net(shape=[D, 4])
           because the number of parameters doubles in each layer in this QAOA circuit.
    """

    cir = UAnsatz(N, input_state=input_state)

    # The first loop defines the QAOA circuit with P layers of two blocks
    for layer in range(P):
        # The second and third loops aim to construct the first block U_theta[layer][0] which involves
        # two-qubit operation e^{-i\beta Z_iZ_j} acting on a pair of qubits or nodes i and j in the circuit.
        for row in range(N):
            for col in range(N):
                if abs(adjacency_matrix[row, col]) and row < col:
                    cir.cnot([row + 1, col + 1])
                    cir.rz(
                        theta=theta[layer][0] * adjacency_matrix[row, col],
                        which_qubit=col + 1,
                    )
                    cir.cnot([row + 1, col + 1])
        # This loops constructs the second block U_theta[layer][1]-[3] composed of three single-qubit operation
        #  e^{-i\beta[1] Z}e^{-i\beta[2] X}e^{-i\beta[3] X} sequentially acting on single qubits.
        for i in range(1, N + 1):
            cir.rz(theta=theta[layer][1], which_qubit=i)
            cir.rx(theta=theta[layer][2], which_qubit=i)
            cir.rz(theta=theta[layer][3], which_qubit=i)

    return cir.state
Beispiel #24
0
    def partial_state(self, status, which_qubits, is_desired=True):
        r"""得到你想要的部分量子比特上的量子态。

        Args:
            status (LoccStatus or list): 输入的 LOCC 态节点,其类型应该为 ``LoccStatus`` 或者由其组成的 ``list``
            which_qubits (tuple or list): 指定的量子比特编号,其形式为 ``(party_id, qubit_id)`` 的 ``tuple`` ,或者由其组成的 ``list``
            is_desired (bool, optional): 默认是 ``True`` ,即返回量子比特上的部分量子态。如果为 ``False`` ,则抛弃这部分量子比特上的量子态,返回剩下的部分量子态

        Returns:
            LoccStatus or list: 得到部分量子态后的 LOCC 态节点,其类型为 ``LoccStatus`` 或者由其组成的 ``list``
        """
        if isinstance(which_qubits, tuple):
            which_qubits = [which_qubits]
        qubits_list = list()
        for party_id, qubit_id in which_qubits:
            if isinstance(party_id, str):
                qubits_list.append(self.parties_by_name[party_id][qubit_id])
            elif isinstance(party_id, int):
                qubits_list.append(self.parties_by_number[party_id][qubit_id])
            else:
                raise ValueError
        m = len(qubits_list)
        if isinstance(status, LoccStatus):
            n = int(log2(sqrt(status.state.numpy().size)))
        elif isinstance(status, list):
            n = int(log2(sqrt(status[0].state.numpy().size)))
        else:
            raise ValueError("can't recognize the input status")

        assert max(qubits_list) <= n, "qubit index out of range"
        qubit2idx = list(range(0, n))
        idx2qubit = list(range(0, n))

        cir = UAnsatz(n)
        for i, ele in enumerate(qubits_list):
            if qubit2idx[ele] != i:
                # if qubit2idx[ele] is i, then swap([ele, i]) is identity
                cir.swap([i, qubit2idx[ele]])
                qubit2idx[idx2qubit[i]] = qubit2idx[ele]
                idx2qubit[qubit2idx[ele]] = idx2qubit[i]
                idx2qubit[i] = ele
                qubit2idx[ele] = i

        if isinstance(status, LoccStatus):
            state = cir.run_density_matrix(status.state)
            if is_desired:
                state = partial_trace(state, 2**m, 2**(n - m), 2)
            else:
                state = partial_trace(state, 2**m, 2**(n - m), 1)
            new_status = LoccStatus(state, status.prob, status.measured_result)
        elif isinstance(status, list):
            new_status = list()
            for each_status in status:
                state = cir.run_density_matrix(each_status.state)
                if is_desired:
                    state = partial_trace(state, 2**m, 2**(n - m), 2)
                else:
                    state = partial_trace(state, 2**m, 2**(n - m), 1)
                new_status.append(
                    LoccStatus(state, each_status.prob,
                               each_status.measured_result))
        else:
            raise ValueError("can't recognize the input status")

        return new_status