Skip to content

Solving MaxCut with QAOA

This tutorial shows how to solve the maximum cut (MaxCut) combinatorial optimization problem on a graph using the Quantum Approximate Optimization Algorithm (QAOA), first introduced by Farhi et al. in 2014 1.

Given an arbitrary graph, the MaxCut problem consists in finding a graph cut which partitions the nodes into two disjoint sets, such that the number of edges in the cut is maximized. This is a very common combinatorial optimization problem known to be computationally hard (NP-hard).

The graph used for this tutorial is an unweighted graph randomly generated using the networkx library with a certain probability \(p\) of having an edge between two arbitrary nodes (known as Erdős–Rényi graph).

import numpy as np
import networkx as nx
import matplotlib.pyplot as plt
import random

# ensure reproducibility
seed = 42
np.random.seed(seed)
random.seed(seed)

# Create random graph
n_nodes = 4
edge_prob = 0.8
graph = nx.gnp_random_graph(n_nodes, edge_prob)

nx.draw(graph)
2024-06-17T17:44:49.283812 image/svg+xml Matplotlib v3.7.5, https://matplotlib.org/

The goal of the MaxCut algorithm is to maximize the following cost function:

\[\mathcal{C}(p) = \sum_{\alpha}^m \mathcal{C}_{\alpha}(p)\]

where \(p\) is a given cut of the graph, \(\alpha\) is an index over the edges and \(\mathcal{C}_{\alpha}(p)\) is written such that if the nodes connected by the \(\alpha\) edge are in the same set, it returns \(0\), otherwise it returns \(1\). We will represent a cut \(p\) as a bitstring of length \(N\), where \(N\) is the number of nodes, and where the bit in position \(i\) shows to which partition node \(i\) belongs. We assign value 0 to one of the partitions defined by the cut and 1 to the other. Since this choice is arbitrary, every cut is represented by two bitstrings, e.g. "0011" and "1100" are equivalent.

Since in this tutorial we are only dealing with small graphs, we can find the maximum cut by brute force to make sure QAOA works as intended.

# Function to calculate the cost associated with a cut
def calculate_cost(cut: str, graph: nx.graph) -> float:
    """Returns the cost of a given cut (represented by a bitstring)"""
    cost = 0
    for edge in graph.edges():
        (i, j) = edge
        if cut[i] != cut[j]:
            cost += 1
    return cost


# Function to get a binary representation of an int
get_binary = lambda x, n: format(x, "b").zfill(n)

# List of all possible cuts
all_possible_cuts = [bin(k)[2:].rjust(n_nodes, "0") for k in range(2**n_nodes)]

# List with the costs associated to each cut
all_costs = [calculate_cost(cut, graph) for cut in all_possible_cuts]

# Get the maximum cost
maxcost = max(all_costs)

# Get all cuts that correspond to the maximum cost
maxcuts = [get_binary(i, n_nodes) for i, j in enumerate(all_costs) if j == maxcost]
print(f"The maximum cut is represented by the bitstrings {maxcuts}, with a cost of {maxcost}")
The maximum cut is represented by the bitstrings ['0011', '0101', '0110', '1001', '1010', '1100'], with a cost of 4

The QAOA quantum circuit

The Max-Cut problem can be solved by using the QAOA algorithm. QAOA belongs to the class of Variational Quantum Algorithms (VQAs), which means that its quantum circuit contains a certain number of parametrized quantum gates that need to be optimized with a classical optimizer. The QAOA circuit is composed of two operators:

  • The cost operator \(U_c\): a circuit generated by the cost Hamiltonian which encodes the cost function described above into a quantum circuit. The solution to the optimization problem is encoded in the ground state of the cost Hamiltonian \(H_c\). The cost operator is simply the evolution of the cost Hamiltonian parametrized by a variational parameter \(\gamma\) so that \(U_c = e^{i\gamma H_c}.\)
  • The mixing operator \(U_b\): a simple set of single-qubit rotations with adjustable angles which are tuned during the classical optimization loop to minimize the cost

The cost Hamiltonian of the MaxCut problem can be written as:

\[H_c = \frac12 \sum_{\langle i,j\rangle} (\mathbb{1} - Z_iZ_j)\]

where \(\langle i,j\rangle\) represents the edge between nodes \(i\) and \(j\). The solution of the MaxCut problem is encoded in the ground state of the above Hamiltonian.

The QAOA quantum circuit consists of a number of layers, each layer containing a cost and a mixing operator. Below, the QAOA quantum circuit is defined using qadence operations. First, a layer of Hadamard gates is applied to all qubits to prepare the initial state \(|+\rangle ^{\otimes n}\). The cost operator of each layer can be built "manually", implementing the \(e^{iZZ\gamma}\) terms with CNOTs and a \(\rm{RZ}(2\gamma)\) rotation, or it can also be automatically decomposed into digital single and two-qubits operations via the .digital_decomposition() method. The decomposition is exact since the Hamiltonian generator is diagonal.

from qadence import tag, kron, chain, RX, RZ, Z, H, CNOT, I, add
from qadence import HamEvo, QuantumCircuit, Parameter

n_qubits = graph.number_of_nodes()
n_edges = graph.number_of_edges()
n_layers = 6

# Generate the cost Hamiltonian
zz_ops = add(Z(edge[0]) @ Z(edge[1]) for edge in graph.edges)
cost_ham = 0.5 * (n_edges * kron(I(i) for i in range(n_qubits)) - zz_ops)


# QAOA circuit
def build_qaoa_circuit(n_qubits, n_layers, graph):
    layers = []
    # Layer of Hadamards
    initial_layer = kron(H(i) for i in range(n_qubits))
    layers.append(initial_layer)
    for layer in range(n_layers):

        # cost layer with digital decomposition
        # cost_layer = HamEvo(cost_ham, f"g{layer}").digital_decomposition(approximation="basic")
        cost_layer = []
        for edge in graph.edges():
            (q0, q1) = edge
            zz_term = chain(
                CNOT(q0, q1),
                RZ(q1, Parameter(f"g{layer}")),
                CNOT(q0, q1),
            )
            cost_layer.append(zz_term)
        cost_layer = chain(*cost_layer)
        cost_layer = tag(cost_layer, "cost")

        # mixing layer with single qubit rotations
        mixing_layer = kron(RX(i, f"b{layer}") for i in range(n_qubits))
        mixing_layer = tag(mixing_layer, "mixing")

        # putting all together in a single ChainBlock
        layers.append(chain(cost_layer, mixing_layer))

    final_b = chain(*layers)
    return QuantumCircuit(n_qubits, final_b)


circuit = build_qaoa_circuit(n_qubits, n_layers, graph)

# Print a single layer of the circuit
%3 cluster_d49b58dbba1243e0a44ba366078bd8f2 mixing cluster_f35febf688eb4fd48dc5a7e4b74732e4 cost a925c54cc0254093b5533b1f2aae4a35 0 8e2ebed7e3314e92ac6f967f20aa67ab H a925c54cc0254093b5533b1f2aae4a35--8e2ebed7e3314e92ac6f967f20aa67ab a95e0479cba04b11bfe7789d0400db92 1 fb174c80a2fb4a84b18bb815cc8e4740 8e2ebed7e3314e92ac6f967f20aa67ab--fb174c80a2fb4a84b18bb815cc8e4740 8a70fc10a51b48d8988144c6b5d290d7 fb174c80a2fb4a84b18bb815cc8e4740--8a70fc10a51b48d8988144c6b5d290d7 ded9a5d6522843d3924ec0a6f6247c09 8a70fc10a51b48d8988144c6b5d290d7--ded9a5d6522843d3924ec0a6f6247c09 f23f3858860f41589b3bd545e42e12dd ded9a5d6522843d3924ec0a6f6247c09--f23f3858860f41589b3bd545e42e12dd 70967c05b0ed4e50949e18c79bdaf61a f23f3858860f41589b3bd545e42e12dd--70967c05b0ed4e50949e18c79bdaf61a 0b36da37e772437bbcbb6cff3c8db465 70967c05b0ed4e50949e18c79bdaf61a--0b36da37e772437bbcbb6cff3c8db465 45a1a6cf482f494da83242b8bac341d6 0b36da37e772437bbcbb6cff3c8db465--45a1a6cf482f494da83242b8bac341d6 707324bd59c8418c89b34230f3ff5238 45a1a6cf482f494da83242b8bac341d6--707324bd59c8418c89b34230f3ff5238 33cb46bca57b4803a9be80071c2845aa 707324bd59c8418c89b34230f3ff5238--33cb46bca57b4803a9be80071c2845aa 6cbdf52bae5a4cf2b3afb153e302b1c1 33cb46bca57b4803a9be80071c2845aa--6cbdf52bae5a4cf2b3afb153e302b1c1 130554bf05214418adfd1f4042503ad7 6cbdf52bae5a4cf2b3afb153e302b1c1--130554bf05214418adfd1f4042503ad7 71ff7b7470744554936e36d9f230f191 130554bf05214418adfd1f4042503ad7--71ff7b7470744554936e36d9f230f191 ec9f7610aa814fc4a609d35cf42afff7 71ff7b7470744554936e36d9f230f191--ec9f7610aa814fc4a609d35cf42afff7 cbd3af8b43094de8bf84f2d0179c4573 ec9f7610aa814fc4a609d35cf42afff7--cbd3af8b43094de8bf84f2d0179c4573 657a37fe8a4645d29e3838042c18482e cbd3af8b43094de8bf84f2d0179c4573--657a37fe8a4645d29e3838042c18482e 0d38784bb91f4f02ac886a2238174bd3 657a37fe8a4645d29e3838042c18482e--0d38784bb91f4f02ac886a2238174bd3 bfdb9034c565453fbe72305a0ac75bf8 0d38784bb91f4f02ac886a2238174bd3--bfdb9034c565453fbe72305a0ac75bf8 fa6cb5bc44e9474d8b240c2b77d00525 bfdb9034c565453fbe72305a0ac75bf8--fa6cb5bc44e9474d8b240c2b77d00525 af7c29c5a2714753a514643ae7e9778f RX(b0) fa6cb5bc44e9474d8b240c2b77d00525--af7c29c5a2714753a514643ae7e9778f f4edbf7c45fe4037aaf7e2ac567ee83f af7c29c5a2714753a514643ae7e9778f--f4edbf7c45fe4037aaf7e2ac567ee83f f8be36168a074bf98e086a3a3ae3d1a1 dda05b38837541b4a002c5be10be67ee H a95e0479cba04b11bfe7789d0400db92--dda05b38837541b4a002c5be10be67ee 5fc5f3465dc24852bb31409a3d992351 2 5d842f5746ca4e83b401c3c6d5deb8a3 X dda05b38837541b4a002c5be10be67ee--5d842f5746ca4e83b401c3c6d5deb8a3 5d842f5746ca4e83b401c3c6d5deb8a3--fb174c80a2fb4a84b18bb815cc8e4740 f2a25431fc5a43efbfea628b44d6b1ea RZ(g0) 5d842f5746ca4e83b401c3c6d5deb8a3--f2a25431fc5a43efbfea628b44d6b1ea 4f5dd582f95d4bad80b779d6a23ad5b4 X f2a25431fc5a43efbfea628b44d6b1ea--4f5dd582f95d4bad80b779d6a23ad5b4 4f5dd582f95d4bad80b779d6a23ad5b4--ded9a5d6522843d3924ec0a6f6247c09 f8e3ce37739047c08f37d1a7feb70407 4f5dd582f95d4bad80b779d6a23ad5b4--f8e3ce37739047c08f37d1a7feb70407 693cd17df2fa4768962ebfffeff3b346 f8e3ce37739047c08f37d1a7feb70407--693cd17df2fa4768962ebfffeff3b346 ed81b54c204c416ea284b71cea7b6d4b 693cd17df2fa4768962ebfffeff3b346--ed81b54c204c416ea284b71cea7b6d4b 99d55e35015a4405b081acb2ebcdd052 ed81b54c204c416ea284b71cea7b6d4b--99d55e35015a4405b081acb2ebcdd052 a27e9bfb5ab846d482e8c282091bd23c 99d55e35015a4405b081acb2ebcdd052--a27e9bfb5ab846d482e8c282091bd23c 25a063a43c32493ca480fd547aa07548 a27e9bfb5ab846d482e8c282091bd23c--25a063a43c32493ca480fd547aa07548 9ff4ce8235c84c71892188495c1f4166 25a063a43c32493ca480fd547aa07548--9ff4ce8235c84c71892188495c1f4166 22d893e8091b45e58a028db61a1e4165 9ff4ce8235c84c71892188495c1f4166--22d893e8091b45e58a028db61a1e4165 cb3c29f4f9aa44a995be85a7238a4efe 22d893e8091b45e58a028db61a1e4165--cb3c29f4f9aa44a995be85a7238a4efe e133ac712e2a4c8e84ec2e594c498f55 cb3c29f4f9aa44a995be85a7238a4efe--e133ac712e2a4c8e84ec2e594c498f55 f97e8e666ab4415a9c2fafd902f5a7d4 e133ac712e2a4c8e84ec2e594c498f55--f97e8e666ab4415a9c2fafd902f5a7d4 d2d9b506a14a41dba94d15cccbe2eec2 f97e8e666ab4415a9c2fafd902f5a7d4--d2d9b506a14a41dba94d15cccbe2eec2 706f425565494eee8349a54f8876c87f d2d9b506a14a41dba94d15cccbe2eec2--706f425565494eee8349a54f8876c87f 9e61e5873dd241a6bb27a740a2f76b71 706f425565494eee8349a54f8876c87f--9e61e5873dd241a6bb27a740a2f76b71 fa2826fe7a9d46d99e3f811446f47709 9e61e5873dd241a6bb27a740a2f76b71--fa2826fe7a9d46d99e3f811446f47709 1f3aa5453fc14a448cebaee7edaade62 RX(b0) fa2826fe7a9d46d99e3f811446f47709--1f3aa5453fc14a448cebaee7edaade62 1f3aa5453fc14a448cebaee7edaade62--f8be36168a074bf98e086a3a3ae3d1a1 c118bf3f2aff4276b433cbbdc672c115 7bdfffaaf8d544139a8d6318b82860c6 H 5fc5f3465dc24852bb31409a3d992351--7bdfffaaf8d544139a8d6318b82860c6 731af4179a8f4ecd8bc3a74ed33e113c 3 fbe8d1fded294ac988ad9e7ea19df2c0 7bdfffaaf8d544139a8d6318b82860c6--fbe8d1fded294ac988ad9e7ea19df2c0 9c22355872d14420bb9995fafe742902 fbe8d1fded294ac988ad9e7ea19df2c0--9c22355872d14420bb9995fafe742902 98db8aa2e3234dd1a00d587f0670e2b3 9c22355872d14420bb9995fafe742902--98db8aa2e3234dd1a00d587f0670e2b3 6c0a5e0f85c04cfa801d18a094becd16 X 98db8aa2e3234dd1a00d587f0670e2b3--6c0a5e0f85c04cfa801d18a094becd16 6c0a5e0f85c04cfa801d18a094becd16--f23f3858860f41589b3bd545e42e12dd 7e44fb4de4f140169d50b96eead5f23a RZ(g0) 6c0a5e0f85c04cfa801d18a094becd16--7e44fb4de4f140169d50b96eead5f23a ad950ec57c314ecd8395a3623f6e700b X 7e44fb4de4f140169d50b96eead5f23a--ad950ec57c314ecd8395a3623f6e700b ad950ec57c314ecd8395a3623f6e700b--0b36da37e772437bbcbb6cff3c8db465 b330c1a1546b43f0b2cd88de135d2ad8 ad950ec57c314ecd8395a3623f6e700b--b330c1a1546b43f0b2cd88de135d2ad8 b7977bfa292f4f3ebfa0dc9c2603d73f b330c1a1546b43f0b2cd88de135d2ad8--b7977bfa292f4f3ebfa0dc9c2603d73f fb70a0675d8e4699a7a5e5ab56e0e40d b7977bfa292f4f3ebfa0dc9c2603d73f--fb70a0675d8e4699a7a5e5ab56e0e40d 522df83d231e41e490d1223cc66f053b X fb70a0675d8e4699a7a5e5ab56e0e40d--522df83d231e41e490d1223cc66f053b 522df83d231e41e490d1223cc66f053b--9ff4ce8235c84c71892188495c1f4166 051d0190816848b7957777bda1573097 RZ(g0) 522df83d231e41e490d1223cc66f053b--051d0190816848b7957777bda1573097 3cd73afdcfb644048d899eed271d0c8e X 051d0190816848b7957777bda1573097--3cd73afdcfb644048d899eed271d0c8e 3cd73afdcfb644048d899eed271d0c8e--cb3c29f4f9aa44a995be85a7238a4efe aea8996dd82a4ae991eeb159eef3cfd8 3cd73afdcfb644048d899eed271d0c8e--aea8996dd82a4ae991eeb159eef3cfd8 7e14595865d8457eb28bcd470ae6aeed aea8996dd82a4ae991eeb159eef3cfd8--7e14595865d8457eb28bcd470ae6aeed 5df849e42804445c86dbc5742dfa9bae 7e14595865d8457eb28bcd470ae6aeed--5df849e42804445c86dbc5742dfa9bae a0d1fc46970f4a1fa56b3c74ec7e01ee 5df849e42804445c86dbc5742dfa9bae--a0d1fc46970f4a1fa56b3c74ec7e01ee b9bdf1655828452aa596db7d5b43d529 a0d1fc46970f4a1fa56b3c74ec7e01ee--b9bdf1655828452aa596db7d5b43d529 9131f1c1079f44edaebb72cb97edb250 b9bdf1655828452aa596db7d5b43d529--9131f1c1079f44edaebb72cb97edb250 2e793972a0c7461898b48605eaa0f947 RX(b0) 9131f1c1079f44edaebb72cb97edb250--2e793972a0c7461898b48605eaa0f947 2e793972a0c7461898b48605eaa0f947--c118bf3f2aff4276b433cbbdc672c115 0ed5da562ca74b10a05c8ce8284532bd a85b207c46ea40dca3377e9c73278824 H 731af4179a8f4ecd8bc3a74ed33e113c--a85b207c46ea40dca3377e9c73278824 03df0d828ced449fbf44d920702b4810 a85b207c46ea40dca3377e9c73278824--03df0d828ced449fbf44d920702b4810 0d144bd479764dbba0a210abfcb763fc 03df0d828ced449fbf44d920702b4810--0d144bd479764dbba0a210abfcb763fc 2fd19b6e4ab741a9b7c2e4663c7eeddb 0d144bd479764dbba0a210abfcb763fc--2fd19b6e4ab741a9b7c2e4663c7eeddb 8fa3cf9921c54f82a81a3fc0a660dbf0 2fd19b6e4ab741a9b7c2e4663c7eeddb--8fa3cf9921c54f82a81a3fc0a660dbf0 3773414ef8e2425e81ab1c1951e0d236 8fa3cf9921c54f82a81a3fc0a660dbf0--3773414ef8e2425e81ab1c1951e0d236 1a8da3da69294d6da0aad401fe9fda51 3773414ef8e2425e81ab1c1951e0d236--1a8da3da69294d6da0aad401fe9fda51 6baf4503b8f24a8696873e8cf74d9780 X 1a8da3da69294d6da0aad401fe9fda51--6baf4503b8f24a8696873e8cf74d9780 6baf4503b8f24a8696873e8cf74d9780--45a1a6cf482f494da83242b8bac341d6 aea140c0153345ccb75c28ef571d6d1e RZ(g0) 6baf4503b8f24a8696873e8cf74d9780--aea140c0153345ccb75c28ef571d6d1e efa36c45f510488385aa2613b8275ee7 X aea140c0153345ccb75c28ef571d6d1e--efa36c45f510488385aa2613b8275ee7 efa36c45f510488385aa2613b8275ee7--33cb46bca57b4803a9be80071c2845aa 90812d42682f4e7ca8759ccc3e992a7f efa36c45f510488385aa2613b8275ee7--90812d42682f4e7ca8759ccc3e992a7f 4b2512f5242a4a29b91443abe3d263ab 90812d42682f4e7ca8759ccc3e992a7f--4b2512f5242a4a29b91443abe3d263ab 9dcac1ce0f54425190e6ff499cb62bad 4b2512f5242a4a29b91443abe3d263ab--9dcac1ce0f54425190e6ff499cb62bad 0aa2fdb055674e09951ac242b9ba7b16 X 9dcac1ce0f54425190e6ff499cb62bad--0aa2fdb055674e09951ac242b9ba7b16 0aa2fdb055674e09951ac242b9ba7b16--e133ac712e2a4c8e84ec2e594c498f55 a9bfaa73d4c643b39864d3dce56e7751 RZ(g0) 0aa2fdb055674e09951ac242b9ba7b16--a9bfaa73d4c643b39864d3dce56e7751 c48f8622f5b643f58869e960f54c8fe7 X a9bfaa73d4c643b39864d3dce56e7751--c48f8622f5b643f58869e960f54c8fe7 c48f8622f5b643f58869e960f54c8fe7--d2d9b506a14a41dba94d15cccbe2eec2 a10d120f40dd4b18b7c25d7bb27e9d44 X c48f8622f5b643f58869e960f54c8fe7--a10d120f40dd4b18b7c25d7bb27e9d44 a10d120f40dd4b18b7c25d7bb27e9d44--a0d1fc46970f4a1fa56b3c74ec7e01ee d8a66b8e2439459898a6248261ce02c8 RZ(g0) a10d120f40dd4b18b7c25d7bb27e9d44--d8a66b8e2439459898a6248261ce02c8 18b9be4595a74473bf719154d3a1553b X d8a66b8e2439459898a6248261ce02c8--18b9be4595a74473bf719154d3a1553b 18b9be4595a74473bf719154d3a1553b--9131f1c1079f44edaebb72cb97edb250 866a16b9dcfa46b58d8395fd610bbd89 RX(b0) 18b9be4595a74473bf719154d3a1553b--866a16b9dcfa46b58d8395fd610bbd89 866a16b9dcfa46b58d8395fd610bbd89--0ed5da562ca74b10a05c8ce8284532bd

Train the QAOA circuit to solve MaxCut

Given the QAOA circuit above, one can construct the associated Qadence QuantumModel and train it using standard gradient based optimization.

The loss function to be minimized reads:

\[\mathcal{L} =-\langle \psi | H_c| \psi \rangle= -\frac12 \sum_{\langle i,j\rangle} \left(1 - \langle \psi | Z_i Z_j | \psi \rangle \right)\]

where \(|\psi\rangle(\beta, \gamma)\) is the wavefunction obtained by running the QAQA quantum circuit and the sum runs over the edges of the graph \(\langle i,j\rangle\).

import torch
from qadence import QuantumModel

torch.manual_seed(seed)


def loss_function(model: QuantumModel):
    # The loss corresponds to the expectation
    # value of the cost Hamiltonian
    return -1.0 * model.expectation().squeeze()


# initialize the parameters to random values
model = QuantumModel(circuit, observable=cost_ham)
model.reset_vparams(torch.rand(model.num_vparams))
initial_loss = loss_function(model)
print(f"Initial loss: {initial_loss}")

# train the model
n_epochs = 100
lr = 0.1

optimizer = torch.optim.Adam(model.parameters(), lr=lr)

for i in range(n_epochs):
    optimizer.zero_grad()
    loss = loss_function(model)
    loss.backward()
    optimizer.step()
    if (i + 1) % (n_epochs // 10) == 0:
        print(f"MaxCut cost at iteration {i+1}: {-loss.item()}")
Initial loss: -2.1782381363858794
MaxCut cost at iteration 10: 3.7470706807026373
MaxCut cost at iteration 20: 3.8378810288930207
MaxCut cost at iteration 30: 3.9424197899236146
MaxCut cost at iteration 40: 3.998125625576603
MaxCut cost at iteration 50: 3.9964705285082114
MaxCut cost at iteration 60: 3.9991374608876598
MaxCut cost at iteration 70: 3.999467854291969
MaxCut cost at iteration 80: 3.999872558672824
MaxCut cost at iteration 90: 3.999947583412106
MaxCut cost at iteration 100: 3.9999793311641065

Qadence offers some convenience functions to implement this training loop with advanced logging and metrics track features. You can refer to this tutorial for more details.

Results

Given the trained quantum model, one needs to sample the resulting quantum state to recover the bitstring with the highest probability which corresponds to the maximum cut of the graph.

samples = model.sample(n_shots=100)[0]
most_frequent = max(samples, key=samples.get)

print(f"Most frequently sampled bitstring corresponding to the maximum cut: {most_frequent}")

# let's now draw the cut obtained with the QAOA procedure
colors = []
labels = {}
for node, b in zip(graph.nodes(), most_frequent):
    colors.append("green") if int(b) == 0 else colors.append("red")
    labels[node] = "A" if int(b) == 0 else "B"

nx.draw_networkx(graph, node_color=colors, with_labels=True, labels=labels)
Most frequently sampled bitstring corresponding to the maximum cut: 1001 2024-06-17T17:44:53.591335 image/svg+xml Matplotlib v3.7.5, https://matplotlib.org/

References


  1. Farhi et al. - A Quantum Approximate Optimization Algorithm