Unit commitment — scheduling power generators on/off to meet demand at minimum cost — is NP-hard
and central to grid operations. Quantum optimization (QAOA, VQE) offers a potential path for
combinatorial problems of this type. This project implements both quantum and classical solvers
on the same real-data instances, providing an honest assessment of where quantum approaches currently
stand and where they break down.
PennyLane
QAOA
VQE
Google OR-Tools
ENTSO-E API
PyTorch
Key questions this project answers
- At what problem size does QAOA/VQE match classical MILP quality?
- How does solution quality degrade as generators exceed simulator qubit capacity?
- What's the optimization landscape? (convergence, local minima)
- How sensitive is QUBO performance to constraint penalty weights?
- Can VQE capture continuous OPF better than discretized QAOA?
Technical approach
- Data: Real load and generation data from ENTSO-E Transparency Platform (France/Germany, hourly resolution, 1 year).
- QUBO encoding: Unit commitment binary decisions encoded as Ising Hamiltonian. Constraints embedded as quadratic penalty terms.
- QAOA: p-layer alternating cost/mixer unitaries optimized with COBYLA. PennyLane
default.qubitsimulator. - VQE: Hardware-efficient ansatz with Ry/Rz rotations + CNOT entanglement for continuous OPF relaxation.
- Classical baselines: MILP via OR-Tools (exact), simulated annealing (heuristic), greedy priority list (fast heuristic).
- Scaling study: N = 4 to 20 generators, T = 6 to 48 hours, QAOA layers p = 1 to 5.
Scaling benchmarks
All solvers run on the same problem instances derived from ENTSO-E data.
| Problem size | MILP (exact) | Sim. Annealing | QAOA | VQE |
|---|---|---|---|---|
| 4 gen, 6h | — | — | — | — |
| 8 gen, 12h | — | — | — | — |
| 12 gen, 24h | — | — | — | — |
| 16 gen, 24h | — | — | — | N/A |
| 20 gen, 48h | — | — | N/A | N/A |
Cost in $/MWh. N/A = exceeds simulator capacity. Results from real benchmark runs.
QAOA circuit structure
Python · PennyLane
QAOA cost layer
@qml.qnode(dev)
def qaoa_circuit(gammas, betas, cost_h, n_layers):
# Equal superposition
for i in range(n_qubits):
qml.Hadamard(wires=i)
# Alternating cost + mixer layers
for p in range(n_layers):
qml.ApproxTimeEvolution(cost_h, gammas[p], 1)
for i in range(n_qubits):
qml.RX(2 * betas[p], wires=i)
return qml.expval(cost_h)
System diagram
flowchart TB
Data["ENTSO-E Grid Data
Load, generation, costs"] --> UC["Unit Commitment
MILP formulation"] UC --> QUBO["QUBO Encoding
Binary → Ising Hamiltonian"] QUBO --> QAOA["QAOA Solver
PennyLane circuit"] QUBO --> VQE["VQE Solver
Variational ansatz"] UC --> MILP["MILP Solver
OR-Tools (exact)"] UC --> SA["Simulated Annealing
Classical heuristic"] QAOA --> Compare["Benchmark
Cost, time, quality"] VQE --> Compare MILP --> Compare SA --> Compare style QAOA fill:#eff6ff,stroke:#2563eb,color:#0f172a style VQE fill:#eff6ff,stroke:#2563eb,color:#0f172a style MILP fill:#eff6ff,stroke:#2563eb,color:#0f172a style SA fill:#eff6ff,stroke:#2563eb,color:#0f172a
Load, generation, costs"] --> UC["Unit Commitment
MILP formulation"] UC --> QUBO["QUBO Encoding
Binary → Ising Hamiltonian"] QUBO --> QAOA["QAOA Solver
PennyLane circuit"] QUBO --> VQE["VQE Solver
Variational ansatz"] UC --> MILP["MILP Solver
OR-Tools (exact)"] UC --> SA["Simulated Annealing
Classical heuristic"] QAOA --> Compare["Benchmark
Cost, time, quality"] VQE --> Compare MILP --> Compare SA --> Compare style QAOA fill:#eff6ff,stroke:#2563eb,color:#0f172a style VQE fill:#eff6ff,stroke:#2563eb,color:#0f172a style MILP fill:#eff6ff,stroke:#2563eb,color:#0f172a style SA fill:#eff6ff,stroke:#2563eb,color:#0f172a