Example #1
0
    def forward(self, adjacency_matrix, N, P, METHOD):
        """
        This function constructs the loss function for the QAOA circuit.

        Args:
            adjacency_matrix: the adjacency matrix generated from the graph encoding the classical problem
            N: number of qubits
            P: number of layers
            METHOD: which version of QAOA is chosen to solve the problem, i.e., standard version labeled by 1 or
            extended version by 2.
        Returns:
            the loss function for the parameterized QAOA circuit and the circuit itself
        """

        # Generate the problem_based quantum Hamiltonian H_problem based on the classical problem in paddle
        H_problem = H_generator(N, adjacency_matrix)

        # The standard QAOA circuit: the function circuit_QAOA is used to construct the circuit, indexed by METHOD 1.
        if METHOD == 1:
            cir = circuit_QAOA(self.theta, adjacency_matrix, N, P)
        # The extended QAOA circuit: the function circuit_extend_QAOA is used to construct the net, indexed by METHOD 2.
        elif METHOD == 2:
            cir = circuit_extend_QAOA(self.theta, adjacency_matrix, N, P)
        else:
            raise ValueError("Wrong method called!")

        cir.run_state_vector()
        loss = cir.expecval(H_problem)

        return loss, cir
Example #2
0
def benchmark_QAOA(classical_graph_adjacency=None, N=None):
    """
     This function benchmarks the performance of QAOA. Indeed, it compares its approximate solution obtained
     from QAOA with predetermined parameters, such as iteration step = 120 and learning rate = 0.1, to the exact solution
     to the classical problem.
    """
    # Generate the graph and its adjacency matrix from the classical problem, such as the Max-Cut problem
    if all(var is None for var in (classical_graph_adjacency, N)):
        N = 4
        _, classical_graph_adjacency = generate_graph(N, 1)

    # Convert the Hamiltonian's list form to matrix form
    H_matrix = pauli_str_to_matrix(H_generator(N, classical_graph_adjacency),
                                   N)
    H_diag = diag(H_matrix).real
    # Compute the exact solution of the original problem to benchmark the performance of QAOA
    H_max = max(H_diag)
    H_min = min(H_diag)

    print('H_max:', H_max, '  H_min:', H_min)

    # Load the data of QAOA
    x1 = load('./output/summary_data.npz')

    H_min = ones([len(x1['iter'])]) * H_min

    # Plot it
    pyplot.figure(1)
    loss_QAOA, = pyplot.plot(x1['iter'],
                             x1['energy'],
                             alpha=0.7,
                             marker='',
                             linestyle="--",
                             linewidth=2,
                             color='m')
    benchmark, = pyplot.plot(x1['iter'],
                             H_min,
                             alpha=0.7,
                             marker='',
                             linestyle=":",
                             linewidth=2,
                             color='b')
    pyplot.xlabel('Number of iteration')
    pyplot.ylabel('Performance of the loss function for QAOA')

    pyplot.legend(
        handles=[loss_QAOA, benchmark],
        labels=[
            r'Loss function $\left\langle {\psi \left( {\bf{\theta }} \right)} '
            r'\right|H\left| {\psi \left( {\bf{\theta }} \right)} \right\rangle $',
            'The benchmark result',
        ],
        loc='best')

    # Show the picture
    pyplot.show()
Example #3
0
    def forward(self, input_state, adjacency_matrix, out_state_store, N, P,
                METHOD):
        """
        This function constructs the loss function for the QAOA circuit.

        Args:
            self: the free parameters to be optimized in the QAOA circuit and defined in the above function
            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 generated from the graph encoding the classical problem
            out_state_store: the output state of the QAOA circuit
            N: number of qubits
            P: number of layers
            METHOD: which version of QAOA is chosen to solve the problem, i.e., standard version labeled by 1 or
            extended version by 2.
        Returns:
            The loss function for the parameterized QAOA circuit.
        """

        # Generate the problem_based quantum Hamiltonian H_problem based on the classical problem in paddle
        H, _ = H_generator(N, adjacency_matrix)
        H_problem = fluid.dygraph.to_variable(H)

        # The standard QAOA circuit: the function circuit_QAOA is used to construct the circuit, indexed by METHOD 1.
        if METHOD == 1:
            out_state = circuit_QAOA(self.theta, input_state, adjacency_matrix,
                                     N, P)
        # The extended QAOA circuit: the function circuit_extend_QAOA is used to construct the net, indexed by METHOD 2.
        elif METHOD == 2:
            out_state = circuit_extend_QAOA(self.theta, input_state,
                                            adjacency_matrix, N, P)
        else:
            raise ValueError("Wrong method called!")

        out_state_store.append(out_state.numpy())
        loss = pp_matmul(
            pp_matmul(out_state, H_problem),
            transpose(
                fluid.framework.ComplexVariable(out_state.real,
                                                -out_state.imag),
                perm=[1, 0],
            ),
        )

        return loss.real
Example #4
0
def main(N=4):
    # number of qubits or number of nodes in the graph
    N = 4
    classical_graph, classical_graph_adjacency = generate_graph(N,
                                                                GRAPHMETHOD=1)
    print(classical_graph_adjacency)

    # Convert the Hamiltonian's list form to matrix form
    H_matrix = pauli_str_to_matrix(H_generator(N, classical_graph_adjacency),
                                   N)

    H_diag = np.diag(H_matrix).real
    H_max = np.max(H_diag)
    H_min = np.min(H_diag)

    print(H_diag)
    print('H_max:', H_max, '  H_min:', H_min)

    pos = nx.circular_layout(classical_graph)
    nx.draw(classical_graph,
            pos,
            width=4,
            with_labels=True,
            font_weight='bold')
    plt.show()

    classical_graph, classical_graph_adjacency = generate_graph(N, 1)

    opt_cir = Paddle_QAOA(classical_graph_adjacency,
                          N=4,
                          P=4,
                          METHOD=1,
                          ITR=120,
                          LR=0.1)

    # Load the data of QAOA
    x1 = np.load('./output/summary_data.npz')

    H_min = np.ones([len(x1['iter'])]) * H_min

    # Plot loss
    loss_QAOA, = plt.plot(x1['iter'],
                          x1['energy'],
                          alpha=0.7,
                          marker='',
                          linestyle="--",
                          linewidth=2,
                          color='m')
    benchmark, = plt.plot(x1['iter'],
                          H_min,
                          alpha=0.7,
                          marker='',
                          linestyle=":",
                          linewidth=2,
                          color='b')
    plt.xlabel('Number of iteration')
    plt.ylabel('Performance of the loss function for QAOA')

    plt.legend(
        handles=[loss_QAOA, benchmark],
        labels=[
            r'Loss function $\left\langle {\psi \left( {\bf{\theta }} \right)} '
            r'\right|H\left| {\psi \left( {\bf{\theta }} \right)} \right\rangle $',
            'The benchmark result',
        ],
        loc='best')

    # Show the plot
    plt.show()

    with fluid.dygraph.guard():
        # Measure the output state of the QAOA circuit for 1024 shots by default
        prob_measure = opt_cir.measure(plot=True)

    # Find the max value in measured probability of bitstrings
    max_prob = max(prob_measure.values())
    # Find the bitstring with max probability
    solution_list = [
        result[0] for result in prob_measure.items() if result[1] == max_prob
    ]
    print("The output bitstring:", solution_list)

    # Draw the graph representing the first bitstring in the solution_list to the MaxCut-like problem
    head_bitstring = solution_list[0]

    node_cut = [
        "blue" if head_bitstring[node] == "1" else "red"
        for node in classical_graph
    ]

    edge_cut = [
        "solid"
        if head_bitstring[node_row] == head_bitstring[node_col] else "dashed"
        for node_row, node_col in classical_graph.edges()
    ]
    nx.draw(
        classical_graph,
        pos,
        node_color=node_cut,
        style=edge_cut,
        width=4,
        with_labels=True,
        font_weight="bold",
    )
    plt.show()