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
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()
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
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()