Skip to content

xuwkk/diffapqp

Repository files navigation

DiffAPQP

DiffAPQP connects continuous affine-parametric quadratic and linear programs written in CVXPY to PyTorch. It provides differentiable layers for two common learning targets:

  • SolMapLayer differentiates losses that depend on the optimal decision $\hat z^\star$.
  • ValueFuncLayer differentiates losses that depend on the optimal objective value $\alpha(\hat y)$.

Write the optimization model naturally in CVXPY and pass the original cvxpy.Problem to either layer. DiffAPQP validates and canonicalizes compatible problems internally, so no manual standard-form construction is required.

The package is developed by Dr. Wangkun Xu from Imperial College London, partially funded by EPSRC under Grant EP/Y025946/1.

  • More details can be found in the documentation.
  • Please also refer to our preprint paper: Structured Differentiable Optimization for Efficient Decision-focused Learning in Power Systems and
  • power system application repository: diffapqp-power-system.

DiffAPQP has been tested on

  • Ubuntu 20.04.6 LTS; two Intel Xeon Gold 6230R processors; 256 GB RAM based on DiffAPQP-v0.1.0 (the benchmark results can be found in the paper)
  • macOS Tahoe 26.5.2; Apple M1 Pro processor; 32 GB RAM based on DiffAPQP-v0.2.0 (the benchmark results can be found here).

Highlights

  • Native CVXPY modeling: parameter and variable names become the keys of the layer's input and output dictionaries.
  • Batched execution: independent parameter instances can be solved concurrently.
  • Flexible forward solvers: CVXPY-compatible QP and conic backends can be used independently of the backward method.
  • Repeated-solve acceleration: solver warm starts and in-place data updates are available where the backend and CVXPY interface support them.
  • Specialized differentiation: solution-map losses use full- or reduced-KKT adjoints, while value-function losses use a direct value derivative without solving an adjoint system.

Installation

Install the core package from PyPI:

python -m pip install diffapqp

This installs CVXPY and its standard dependencies, which normally provide Clarabel, OSQP, and SCS. Optional solver packages can be installed with:

python -m pip install "diffapqp[gurobi]"  # Gurobi; a licence is required
python -m pip install "diffapqp[proxqp]"  # PROXQP

DiffAPQP's explicit sample-indexed warm starts and in-place solver-data updates for OSQP, SCS, QPALM, and PROXQP require the pinned custom CVXPY fork:

python -m pip install \
  "cvxpy @ git+https://github.com/xuwkk/cvxpy.git@v1.9.0-lapso.7"
python -m pip install diffapqp

The custom fork requires Python 3.11 or later. The core DiffAPQP package supports Python 3.9 or later. Install PyTorch separately first when a particular CUDA build is required.

See the installation guide for editable installs, compatibility imports, and additional solver backends.

Quick Start

The following example defines a parameterized QP, solves two instances as a batch, and differentiates a loss through the optimal decisions:

import cvxpy as cp
import torch

from diffapqp import SolMapLayer

z = cp.Variable(2, name="z")
q = cp.Parameter(2, name="q")

problem = cp.Problem(
    cp.Minimize(0.5 * cp.sum_squares(z) + q @ z),
    [z >= 0, z <= 10],
)

layer = SolMapLayer(problem, max_n_cores=1)

q_batch = torch.tensor(
    [[-1.0, -2.0], [0.5, -1.5]],
    dtype=torch.double,
    requires_grad=True,
)

primal_dict, *_ = layer(
    {"q": q_batch},
    solver="OSQP",
    reduced_kkt=True,
)

loss = primal_dict["z"].square().mean()
loss.backward()

print(primal_dict["z"])
print(q_batch.grad)

The leading tensor dimension is the batch dimension. Input keys must match the names of CVXPY parameters, and primal_dict restores the names and shapes of the original CVXPY variables.

Parallel Execution

max_n_cores controls both forward sample workers and solution-map backward workers. 1 selects serial execution, -1 selects all available CPUs, and requested worker counts are capped at os.cpu_count().

Forward samples use threads on every platform. Solution-map adjoint solves keep the existing fork-based process pool on Linux and use a shared-memory thread pool on Windows and macOS. Each backward worker limits nested BLAS/OpenMP work to one native thread to avoid oversubscription. See the solution-map constructor for details.

Choose a Layer

Training loss depends on Layer Differentiable output Backward method
Optimal decisions $\hat z^\star$ SolMapLayer Named variables in primal_dict Full- or reduced-KKT adjoint
Optimal value $\alpha(\hat y)$ ValueFuncLayer value_batch Direct value-function derivative

Supported Problem Class

DiffAPQP targets continuous affine-parametric QPs and LPs whose canonical form is

$$ \begin{aligned} \underset{\tilde z}{\operatorname{minimize}}\quad & \frac{1}{2}\tilde z^\top P\tilde z

  • q(\hat y)^\top\tilde z + c(\hat y) \ \text{subject to}\quad & A\tilde z = b(\hat y), \ & G\tilde z \le h(\hat y). \end{aligned} $$

The matrices $P$, $A$, and $G$ are constant, while $q$, $c$, $b$, and $h$ may depend affinely on the parameter vector $\hat y$. Linear programs are the special case $P=0$.

The original CVXPY problem must:

  • be continuous, DCP compliant, and reducible through CVXPY's QP canonicalization;
  • satisfy CVXPY's DPP rules when parameterized;
  • assign explicit names to all parameters and decision variables.

Parameterized quadratic or constraint matrices, nonlinear constraints, and integer or Boolean decisions are outside the current layer contract. The problem setup explains the accepted model and internal canonicalization in detail.

Warm Start and Update

With the custom CVXPY fork, repeated solves can associate warm-start iterates with stable sample identifiers and reuse a solver workspace for each batch slot:

primal_dict, *_ = layer(
    param_dict,
    idx_list=sample_ids,
    solver="OSQP",
    warm_start=True,
    update=True,
)

The first call initializes the relevant state; later calls reuse it. Warm start and update are distinct mechanisms, and their exact behavior is solver dependent. See Efficient Forward Pass for the lifecycle and Solvers for backend coverage and default arguments.

Documentation

Tutorials, Tests, and Benchmarks

  • Tutorials provide runnable examples of both differentiable layers.
  • Tests cover canonicalization, forward solves, gradients, and package compatibility.
  • Benchmarks compare runtime, gradient agreement, CPU usage, and memory usage.

Development

git clone https://github.com/xuwkk/diffapqp.git
cd diffapqp
python -m pip install -e ".[dev,docs]"
python -m pytest
python -m mkdocs build --strict

Install the custom CVXPY fork before the editable package when developing or testing sample-indexed warm-start/update behavior.

License

DiffAPQP is released under the Apache License 2.0.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages