Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Local-first QAOA

Maintained by the JuliaQUBO organization
SECQUOIA  ·  PSR Energy

Open In Colab

A reproducible Julia tutorial for local optimization, circuit auditing, and optional execution on IBM quantum hardware.

Setup

Local installation

From the repository root, instantiate the shared Julia environment before opening this notebook:

julia --project=notebooks_jl -e 'using Pkg; Pkg.instantiate()'

QiskitOpt.jl manages its compatible local Qiskit stack through PythonCall and CondaPkg. The default path below is credential-free: it uses a local Aer simulator and does not contact IBM Runtime.

Google Colab

Open the badge above, select a Julia runtime, and run the setup cells. The bootstrap clones this repository only when Colab does not already have it, then activates the same checked-in notebook project used locally.

Notebook Cell
Notebook Cell

Learning objectives

By the end of this notebook you will be able to:

  1. map a minimization QUBO to a cost Hamiltonian with an explicit bit/spin convention;

  2. configure a bounded, reproducible local QAOA run through QiskitOpt.jl;

  3. distinguish raw QUBO energy, application score, sample probability, and the most-frequent sample;

  4. decode number-partitioning, Max-Cut, and minimum-vertex-cover samples and compare them with exact baselines;

  5. audit parameter order, bit order, circuit resources, objective metadata, and the effect of QAOA depth p; and

  6. submit a fixed QAOA circuit to IBM quantum hardware through an explicit, secret-safe opt-in.

Prerequisites

Prior notebooks: Notebook 2 introduces QUBO modeling, and Notebook 7 derives and independently checks the three canonical models used here.

Mathematical background: Binary variables, Pauli operators, expectation values, and basic probability.

Software: Julia 1.10+ with the shared notebook project instantiated.

Accounts required: None for the default path. An IBM Quantum account is required only for the explicitly enabled hardware-submission cell.

QiskitOpt runtime diagnostics: OK
  Julia package: OK (0.7.1)
    Julia version: 1.10.11
  PythonCall: OK (0.9.35)
    Python executable: <notebook-environment>/bin/python
  numpy: OK (2.4.6)
    Python executable: <notebook-environment>/bin/python
  qiskit: OK (2.3.1)
    Python executable: <notebook-environment>/bin/python
  qiskit_aer: OK (0.17.2)
    Python executable: <notebook-environment>/bin/python
  qiskit_optimization: OK (0.7.0)
    Python executable: <notebook-environment>/bin/python
  scipy: OK (1.15.3)
    Python executable: <notebook-environment>/bin/python
  Local backend: OK
    Usable backend: aer_simulator_matrix_product_state. QAOA/VQE local solves still use qiskit_ibm_runtime EstimatorV2/SamplerV2; pass ibm=true to verify those primitives.
Local runtime ready: QiskitOpt 0.7.1, qiskit 2.3.1, qiskit-aer 0.17.2

QAOA in one convention

Write a minimization QUBO as

E(x)=c+∑iℓixi+∑i<jqijxixj,xi∈{0,1}.E(x)=c+\sum_i \ell_i x_i+\sum_{i<j}q_{ij}x_ix_j,\qquad x_i\in\{0,1\}.

We use zi=1−2xiz_i=1-2x_i, or equivalently xi=(1−zi)/2x_i=(1-z_i)/2. Replacing each ziz_i by the Pauli-ZiZ_i operator gives a diagonal cost Hamiltonian HCH_C whose computational-basis eigenvalue is the raw QUBO energy. QiskitOpt performs this conversion and records the objective sense, scale, sign, and offset so we can audit the convention instead of recreating the bridge.

QAOA starts from the uniform state ∣+⟩⊗n|+\rangle^{\otimes n}. At depth pp, it alternates a cost unitary exp⁡(−iγkHC)\exp(-i\gamma_k H_C) with a mixer unitary exp⁡(−iβkHM)\exp(-i\beta_k H_M), where the default mixer is based on Pauli XX. A classical optimizer changes the angles to reduce an estimated expected energy. Qiskit/QiskitOpt bind the vector as all beta angles followed by all gamma angles:

[β0,…,βp−1,γ0,…,γp−1].[\beta_0,\ldots,\beta_{p-1},\gamma_0,\ldots,\gamma_{p-1}].

The optimizer uses finite-shot expectation estimates; a separate final sampling stage draws bit strings from the optimized circuit. More shots reduce sampling noise but do not turn QAOA into an exact algorithm.

Default budget: p=1, optimizer shots=256, final shots=512, maximum evaluations=8

Four quantities that should not be conflated

  • Raw QUBO energy is the minimized polynomial value returned with a sample.

  • Application score is the decoded quantity we care about: partition imbalance, cut weight, or cover size.

  • Probability is the sample’s read count divided by the final shot count.

  • Most-frequent sample has the largest observed probability. It is not automatically the minimum-energy sample.

The exact baseline below is computed by independent enumeration. The stochastic assertions require the sampled set to contain an exact-energy feasible state, but they never pin one complete histogram.

Three canonical models

Notebook 7 gives the full derivations. Here we rebuild the same four-variable tutorial instances directly in QiskitOpt-backed JuMP models. Each application score is independent of the model expression used by the solver.

Number partitioning

With xi=0x_i=0 and xi=1x_i=1 naming the two groups, the raw energy is the squared signed imbalance. The decoded application score is the absolute imbalance, so a raw energy of 0 means a perfect partition.

Number partitioning
  exact optimum raw energy = 0.0
  sampled best: bits=[0, 0, 0, 1], raw energy=0.0, absolute imbalance=0, probability=0.135
  most frequent: bits=[0, 0, 0, 1], raw energy=0.0, probability=0.135
  decoded best = (group_zero = [1, 3, 4], group_one = [8])

Weighted Max-Cut

The application maximizes cut weight, while the QUBO minimizes its negative. Therefore a raw energy of -7 corresponds to the exact cut weight 7.

Weighted Max-Cut
  exact optimum raw energy = -7.0
  sampled best: bits=[0, 1, 1, 0], raw energy=-7.0, cut weight=7, probability=0.17
  most frequent: bits=[0, 1, 1, 0], raw energy=-7.0, probability=0.17
  decoded best = (side_zero = [1, 4], side_one = [2, 3])

Minimum vertex cover

The raw energy adds the selected-vertex count to a penalty for each uncovered edge. With penalty 2 on this instance, every exact optimum is feasible and selects two vertices.

Minimum vertex cover
  exact optimum raw energy = 2.0
  sampled best: bits=[0, 1, 1, 0], raw energy=2.0, cover size=2, probability=0.178
  most frequent: bits=[1, 1, 0, 1], raw energy=3.0, probability=0.195
  decoded best = (selected = [2, 3], uncovered = Tuple{Int64, Int64}[])
Exact-versus-sampled energy summary
  Number partitioning      exact=0.0, sampled best=0.0, most-frequent energy=0.0
  Weighted Max-Cut         exact=-7.0, sampled best=-7.0, most-frequent energy=-7.0
  Minimum vertex cover     exact=2.0, sampled best=2.0, most-frequent energy=3.0

The exact and sampled-best energies agree for all three small instances. This says the finite sample included an optimum; it does not prove the optimized quantum state assigns the optimum the highest probability, and it does not justify a performance or quantum-advantage claim. Inspect the separately reported most-frequent row to see why energy and frequency are different questions.

Fixed-parameter circuits and resource audit

The trained Max-Cut angles give a representative p=1p=1 fixed circuit. We also construct a deterministic p=2p=2 circuit for the same QUBO without running another optimizer. fixed_parameter_circuit records the binding convention and objective metadata; resource_audit transpiles locally and reports depth, size, operation counts, and two-qubit counts without submitting a job.

p=1 parameter order: 
["β[0]", "γ[0]"]
p=2 parameter order: ["β[0]", "β[1]", "γ[0]", "γ[1]"]
Variable order: ["1", "2", "3", "4"]
Measurement convention: QAOA.count_key_bits(key) returns bits in variable order
p=1 resources: depth=8, size=17, two-qubit operations=5, operations=Dict("barrier" => 1, "rzz" => 5, "measure" => 4, "rx" => 4, "h" => 4)
p=2 resources: depth=13, size=26, two-qubit operations=10, operations=Dict("barrier" => 1, "rzz" => 10, "measure" => 4, "rx" => 8, "h" => 4)
Qiskit key 0101 is printed highest classical bit first; QAOA.count_key_bits returns variable order [1, 0, 1, 0].

Run on IBM quantum hardware

The next two cells follow the same local-fallback pattern as the D-Wave notebook: first prepare and inspect a dry run, then optionally submit the measured p=1p=1 Max-Cut circuit to a real IBM backend. The live path calls ibm_runtime_handoff(...; dry_run=false), which resolves the configured backend, transpiles for that hardware, and submits a SamplerV2 job. On success, ibm_hardware_job is the real Runtime job handle.

Before starting Jupyter, choose a currently available backend in your IBM Quantum account and set these values outside the notebook:

  • QUBONOTEBOOKS_QAOA_ENABLE_IBM=1 explicitly requests a hardware job;

  • QUBONOTEBOOKS_QAOA_IBM_BACKEND names the backend selected for this run;

  • QISKIT_IBM_TOKEN comes from an environment variable or secret store;

  • optionally QISKIT_IBM_INSTANCE and QISKIT_IBM_CHANNEL

After a successful submission, use ibm_hardware_job.status() to monitor it and ibm_hardware_job.result() after completion. Decode returned Qiskit count keys with QiskitOpt.QAOA.count_key_bits before evaluating the application score.

With the opt-in disabled, configuration missing, or IBM Runtime unavailable, no hardware job is submitted and the local Aer results above remain the fallback. The notebook never calls save_account, writes credentials, prints the token, or embeds a live backend name.

IBM handoff dry run: backend configured=false, shots=512, credentials recorded=false
IBM quantum hardware submission is disabled. Set QUBONOTEBOOKS_QAOA_ENABLE_IBM=1 with a backend and token to submit. The local Aer results above remain available.

Practice checkpoints

  1. Find a problem above where the most-frequent sample differs from the first minimum-energy sample. Why does that not invalidate the exact-energy check?

  2. Compare the transpiled p=1p=1 and p=2p=2 resource dictionaries. Which counts scale directly with the number of repeated cost layers on this instance?

  3. Convert the rendered Qiskit key 1100 to variable order before evaluating an application score.

Notebook Cell
Problems whose selected minimum-energy row differs from the most-frequent row: ["Minimum vertex cover"]
Notebook Cell
Increasing p from 1 to 2 changes depth 8→13, size 17→26, and two-qubit operations 5→10.
Notebook Cell
Rendered key 1100 becomes variable-order bits [0, 0, 1, 1].

Summary

Learning objectives met:

  • The convention zi=1−2xiz_i=1-2x_i turns a minimization QUBO into a diagonal cost Hamiltonian whose computational-basis values are raw QUBO energies.

  • A local Aer backend, bounded pp/shot/iteration budget, and explicit sampler/simulator/transpiler seeds make the tutorial path reproducible and credential-free.

  • Independent enumeration verifies the sampled best energy and decoded feasibility for number partitioning, weighted Max-Cut, and minimum vertex cover.

  • Sample probability and most-frequent sample are reported separately from raw energy and application score.

  • Public QiskitOpt APIs expose beta-then-gamma binding, variable/count-key order, objective scale/sign/offset, and transpiled circuit resources.

  • The IBM section performs a safe dry run by default and can submit the fixed circuit to real quantum hardware when the backend, secret, and explicit environment opt-in are configured.

Expected runtime class: after the Julia and Python environments are instantiated, the three four-qubit local solves and two circuit audits are a short tutorial run (normally well under two minutes on a laptop-class CPU). First-time package installation is separate.

Next steps: Increase one budget dimension at a time, retain the exact baseline, and compare distributions across several seeds before drawing statistical conclusions.

Further reading:

  • The original QAOA paper defines the alternating cost/mixer construction and its depth parameter.

  • The QiskitOpt.jl best-practices guide documents the maintained local-Aer, fixed-circuit, resource-audit, and Runtime-handoff APIs used here.

References

  1. E. Farhi, J. Goldstone, and S. Gutmann, A Quantum Approximate Optimization Algorithm, arXiv:1411.4028 (2014), https://arxiv.org/abs/1411.4028.

  2. A. R. Mazumder and S. Tayur, Five Starter Problems: Solving Quadratic Unconstrained Binary Optimization Models on Quantum Computers, TutORials in Operations Research (2025), pp. 145–183, Mazumder & Tayur (2025).

  3. QiskitOpt.jl public QAOA interface and best-practices guide: https://github.com/JuliaQUBO/QiskitOpt.jl.

  4. Companion materials cited for pedagogical context: https://github.com/arulrhikm/Solving-QUBOs-on-Quantum-Computers.

This notebook contains original Julia code and prose. The companion repository is cited as context; no source cells, prose, saved output, or assets were copied from it.

References
  1. Mazumder, A. R., & Tayur, S. (2025). Five Starter Problems: Solving Quadratic Unconstrained Binary Optimization Models on Quantum Computers. In Tutorials in Operations Research: Advances in Analytics and Operations Research: Improving Decisions to Secure the Future (pp. 145–183). INFORMS. 10.1287/educ.2025.0288