Skip to content

OpenQuantumComputing/QAOA

Repository files navigation

QAOA

PyPI version Python License: GPL v3

A flexible, modular Python library for the Quantum Approximate Optimization Algorithm / Quantum Alternating Operator Ansatz (QAOA), designed for research and experimentation. Swap problems, mixers, initial states, optimizers, and backends without rewriting your code.


Table of Contents


Installation

pip install qaoa

Or install in development mode from source:

git clone https://github.com/OpenQuantumComputing/QAOA.git
cd QAOA
pip install -e .

Requirements

Optional (for the agent):

  • langchain, langchain_community, langchain_chroma, langchain_openai, streamlit

Quick Example

import networkx as nx
from qaoa import QAOA, problems, mixers, initialstates

# Build a random graph
G = nx.random_regular_graph(3, 8, seed=42)

# Define QAOA components
qaoa = QAOA(
    problem=problems.MaxCut(G),
    mixer=mixers.X(),
    initialstate=initialstates.Plus()
)

# Sample cost landscape at depth p=1
qaoa.sample_cost_landscape()

# Optimize to depth p=3
qaoa.optimize(depth=3)

# Extract results
print("Optimal expectation value:", qaoa.get_Exp(depth=3))
print("Optimal parameters (gamma):", qaoa.get_gamma(depth=3))
print("Optimal parameters (beta):", qaoa.get_beta(depth=3))

See examples/ for more complete worked examples.


Background

Given a cost function $$c: \lbrace 0, 1\rbrace^n \rightarrow \mathbb{R}$$ one defines a problem Hamiltonian $H_P$ through the action on computational basis states via

$$ H_P |x\rangle = c(x) |x\rangle,$$

which means that ground states minimize the cost function $c$. Given a parametrized ansatz $| \gamma, \beta \rangle$, a classical optimizer is used to minimize the energy

$$ \langle \gamma, \beta | H_P | \gamma, \beta \rangle.$$

QAOA of depth $p$ consists of the following ansatz:

$$ |\gamma, \beta \rangle = \prod_{l=1}^p \left( U_M(\beta_l) U_P(\gamma_l)\right) | s\rangle, $$

where

  • $U_P$ is a family of phase-separating operators,
  • $U_M$ is a family of mixing operators, and
  • $|s\rangle$ is a "simple" initial state.

In plain vanilla QAOA these have the form $U_M(\beta_l)=e^{-i\beta_l X^{\otimes n}}$, $U_P(\gamma_l)=e^{-i\gamma_l H_P}$, and the uniform superposition $| s \rangle = |+\rangle^{\otimes n}$ as initial state.


Custom Ansatz

To create a custom QAOA ansatz, specify a problem, a mixer, and an initial state. These base classes each have an abstract method def create_circuit: that must be implemented. The problem base class additionally requires def cost:.

This library already contains several standard implementations.

It is very easy to extend this list by implementing the abstract methods of the base classes above. Feel free to fork the repo and open a pull request!

For example, to set up QAOA for MaxCut using the X-mixer and $|+\rangle^{\otimes n}$ as the initial state:

qaoa = QAOA(
    problem=problems.MaxCut(G),
    mixer=mixers.X(),
    initialstate=initialstates.Plus()
)

Running Optimization at Depth $p$

For depth $p=1$ the expectation value can be sampled on an $n\times m$ Cartesian grid over the domain $[0,\gamma_\text{max}]\times[0,\beta_\text{max}]$ with:

qaoa.sample_cost_landscape()

Energy landscape

Sampling high-dimensional target functions quickly becomes intractable for depth $p>1$. The library therefore iteratively increases the depth. At each depth a local optimization algorithm (e.g. COBYLA) finds a local minimum, using the following initial guess:

  • At depth $p=1$: parameters $(\gamma, \beta)$ are taken from the minimum of the sampled cost landscape.
  • At depth $p>1$: parameters are seeded via an interpolation-based heuristic from the optimal values at the previous depth.
qaoa.optimize(depth=p)

This will call sample_cost_landscape automatically if it has not been run yet.


Further Parameters

qaoa = QAOA(
    ...,
    backend=,
    noisemodel=,
    optimizer=,
    precision=,
    shots=,
    cvar=
)
  • backend: the backend to use, defaults to AerSimulator() from qiskit_aer
  • noisemodel: noise model to apply, defaults to None
  • optimizer: optimizer from qiskit-algorithms with options, defaults to [COBYLA, {}]
  • precision: sample until a certain precision of the expectation value is reached, based on $\text{error}=\frac{\text{variance}}{\sqrt{\text{shots}}}$, defaults to None
  • shots: number of measurement shots, defaults to 1024
  • cvar: value for Conditional Value at Risk (CVaR), defaults to 1 (standard expectation value)

Extract Results

Once qaoa.optimize(depth=p) is run, extract the expectation value, variance, and parameters for each depth $1\leq i \leq p$:

qaoa.get_Exp(depth=i)
qaoa.get_Var(depth=i)
qaoa.get_gamma(depth=i)
qaoa.get_beta(depth=i)

Additionally, for every optimizer call at each depth, the angles, expectation value, variance, maximum cost, minimum cost, and number of shots are stored in:

qaoa.optimization_results[i]

Multi-Angle QAOA

Multi-angle QAOA allows components to use multiple parameters per layer, increasing expressibility:

  • Multi-angle mixer (XMultiAngle): each qubit gets its own independent β parameter.
  • Parameterized initial state (PlusParameterized): the initial state |+⟩ with optimizable per-qubit phase rotations.
qaoa = QAOA(
    problem=problems.MaxCut(G),
    mixer=mixers.XMultiAngle(),   # N_qubits beta parameters per layer
    initialstate=initialstates.Plus()
)

The flat angle array format used by hist(), getParametersToBind(), and interp() is:

[init_0, ..., init_{n-1},          # initial state params (0 for Plus)
 gamma_{0,0}, ..., beta_{0,n-1},   # layer 0 params
 gamma_{1,0}, ..., beta_{1,n-1},   # layer 1 params
 ...]

For the standard single-parameter case this reduces to [gamma_0, beta_0, gamma_1, beta_1, ...].

Implement get_num_parameters() in a custom component to enable multi-angle support. See examples/MultiAngle for a complete example.


Minimizing Depth of Phase Separating Operator

Assuming all-to-all connectivity of qubits, one can minimize the circuit depth of the phase separating operator by solving the minimum edge colouring problem. This is implemented in GraphHandler and is invoked automatically. An example output is shown below:

Edge Coloring


Building Circuits like "Lego"

Components can be freely composed ("lego style") to build more complex circuits.

A typical workflow is:

  1. Define a feasible-state preparation circuit (e.g. Dicke).
  2. Build a mixer acting on that feasible space (e.g. Grover).
  3. Replicate the resulting block across independent registers using a tensor product.

For example, construct a Dicke state with Hamming weight $k=2$ on 4 qubits:

from qaoa import initialstates, mixers

dicke = initialstates.Dicke(2, 4)     # k=2 excitations on N=4 qubits

Next, build a Grover mixer that operates on the feasible space prepared by the Dicke circuit:

grover = mixers.Grover(dicke)
grover.create_circuit()
grover.circuit.draw('mpl')

Grover circuit

The Grover mixer implements

$$U_M(\beta) = U_S^\dagger , X^{\otimes n} , C^{n-1}P(\beta) , X^{\otimes n} , U_S,$$

where $U_S$ is the state-preparation circuit (here, Dicke). In the circuit diagram, $U_S$ and $U_S^\dagger$ appear as labelled blocks (Dicke / Dicke†).

Finally, use Tensor to replicate the block across independent registers:

tensor = initialstates.Tensor(grover, 3)   # 3 copies → 12 qubits total
tensor.create_circuit()
tensor.circuit.draw('mpl')

The Grover mixer automatically inherits the qubit count from the Dicke circuit, and Tensor replicates the full block without manual qubit bookkeeping.


Lego-like circuit: three Grover blocks on 12 qubits

Annotating circuits

Every component (initial state or mixer) carries a label attribute used as the circuit name when create_circuit() is called. The label defaults to the class name but can be customised at construction time (for Dicke, Grover, and Tensor) or by setting the attribute before calling create_circuit():

dicke = initialstates.Dicke(2, 4, label="Dicke-2")
dicke.create_circuit()
print(dicke.circuit.name)   # → "Dicke-2"

xy = mixers.XY()
xy.label = "XY-ring"
xy.setNumQubits(4)
xy.create_circuit()
print(xy.circuit.name)      # → "XY-ring"

Repository Structure

QAOA/
├── qaoa/                    # Core library
│   ├── qaoa.py              # Main QAOA class
│   ├── problems/            # Problem Hamiltonians (MaxCut, QUBO, Portfolio, …)
│   ├── mixers/              # Mixing operators (X, XY, Grover, …)
│   ├── initialstates/       # Initial state circuits (Plus, Dicke, Tensor, …)
│   └── util/                # Graph utilities and helpers
├── examples/                # Jupyter notebook examples
│   ├── MaxCut/
│   ├── ExactCover/
│   └── PortfolioOptimization/
├── agent/                   # LLM-powered QAOA assistant
├── unittests/               # Unit tests
├── images/                  # Figures used in documentation
└── setup.py

Talk to an Agent

The agent/ folder contains a specialized QAOA assistant that can answer questions about the library or generate example code.

Run from the terminal:

cd agent
python planner.py

Run as a web interface:

cd agent
streamlit run interface.py

QAOA Agent interface startup page



Example interaction:

QAOA Agent interface with question page

Dependencies for the agent: langchain, langchain_community, langchain_chroma, langchain_openai, and optionally streamlit for the web interface. An OpenAI API key is also required.


Citation

If you use this library in your research, please cite:

@software{fuchs2024qaoa,
  author       = {Franz Georg Fuchs},
  title        = {{QAOA}: A Modular Python Library for the Quantum Approximate Optimization Algorithm},
  year         = {2024},
  url          = {https://github.com/OpenQuantumComputing/QAOA},
  note         = {Version 1.2.3}
}

Acknowledgement

This work was funded by the Research Council of Norway through project number 33202.

About

This package is a flexible python implementation of the Quantum Approximate Optimization Algorithm /Quantum Alternating Operator ansatz (QAOA) aimed at researchers to readily test the performance of a new ansatz, a new classical optimizers, etc.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors