The Partition Problem — QAOA Worked Example
A concrete, end-to-end derivation of QAOA applied to the partition problem: given a set of numbers, split them into two groups whose sums are as close as possible. It’s NP-hard classically (verifying a candidate split is trivial — sum both sides — but finding one requires checking splits in the worst case) yet easy to verify, making it a textbook “classically verifiable problem” for demonstrating quantum advantage. The derivation goes QUBO → Ising in three steps: encode each element with a binary variable (which subset it’s in), write the imbalance cost , then swap to spin variables via and minimize instead of to avoid absolute values. Expanding and dropping the constant diagonal term (using ) leaves — replacing spins with Pauli-Z operators gives the cost Hamiltonian , with each coupling weighted by the product of the two numbers it connects. Key insight: this is the same graph-based recipe as MaxCut — the partition problem is MaxCut on a complete graph whose edge weights are — which is why the implementation reaches for MaxCut tooling rather than writing a bespoke encoder.
from qiskit_addon_opt_mapper.applications import Maxcut
from qiskit_addon_opt_mapper.translators import to_ising
from qopt_best_practices.circuit_library import annotated_qaoa_ansatz
# graph: networkx.Graph with edge weights = a_i * a_j
maxcut = Maxcut(graph)
partition_hamiltonian = to_ising(maxcut.to_quadratic_program()) # SparsePauliOp
circuit = annotated_qaoa_ansatz(partition_hamiltonian, reps=layers)API notes
to_isingtakes anOptimizationProblem, not aMaxcutobject directly — call.to_quadratic_program()(or equivalent) on theMaxcutapplication first.annotated_qaoa_ansatz(fromqopt_best_practices) produces a circuit with the QAOA cost/mixer layers annotated (boxed), which the transpiler can then unroll withUnrollBoxes()— this preserves layer structure throughgenerate_preset_qaoa_pass_managerfor SWAP-strategy-aware routing, instead of losing it to generic optimization passes.- For a hardware-native graph (one that already matches the backend’s coupling map after a greedy edge-coloring),
SwapStrategy(cmap, ())— an empty strategy — is valid: no SWAPs are needed because the problem graph and the hardware graph coincide after coloring.
Bonus: scaling to larger instances
At 1600 nodes, brute-force partition assignment from raw sampling accumulates bit-flip errors. A local-search refinement pass (swap_partitions) improves the best sampled bitstring by trying single-node swaps between the two partitions and keeping any swap that reduces the imbalance — cheap classical post-processing layered on top of the quantum result rather than an alternative to it. Because this refinement is itself a graph operation over thousands of edges, the notebook converts the NetworkX graph to Rustworkx first — Rustworkx is a Rust-backed graph library with the same conceptual API as NetworkX but built for exactly this kind of large-graph performance case.
Related
- QAOA — Quantum Approximate Optimization Algorithm — the algorithm this note is a worked instance of
- Pauli Correlation Encoding (PCE) — the qubit-reduction technique used to scale this same partition problem from ~160 to ~1600 nodes
- Transpiling QAOA Circuits — SWAP Strategies and SAT Mapping — how the annotated ansatz here gets mapped onto real hardware connectivity
- Quantum Chemistry — QPE on H2 (Worked Example) — another worked-example note in the same style, for a different algorithm family
Self-Check
- Why does minimizing avoid the need to handle an absolute value, and what step of the derivation drops the constant term that produces?
- Why is the partition problem a good “classically verifiable” candidate for demonstrating quantum advantage, when finding the answer is NP-hard?
- What would go wrong if you passed a
Maxcutobject directly toto_isinginstead of first converting it to a quadratic program?