Skip to main content

Learning-augmented optimization: differentiable APQP/APLP layers on CVXPY and PyTorch.

Project description

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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

diffapqp-0.2.0.tar.gz (46.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

diffapqp-0.2.0-py3-none-any.whl (37.8 kB view details)

Uploaded Python 3

File details

Details for the file diffapqp-0.2.0.tar.gz.

File metadata

  • Download URL: diffapqp-0.2.0.tar.gz
  • Upload date:
  • Size: 46.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.11.15

File hashes

Hashes for diffapqp-0.2.0.tar.gz
Algorithm Hash digest
SHA256 7a98e746434489e0e0210c9feafb5717e4b5da4528bbd1b380ee7a7db4117f78
MD5 5112335f78dce4754fd90ab0007889c6
BLAKE2b-256 261fb14d84d628ead6a2fbb60bff5ff0d8ab41b30b594c62dbe1ccd1466033b5

See more details on using hashes here.

File details

Details for the file diffapqp-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: diffapqp-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 37.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.11.15

File hashes

Hashes for diffapqp-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0699569949815f3597d569ffe0dc975ead50794c725d1926c1536ae53a4290aa
MD5 31e68279daf6c91415b8b30372407978
BLAKE2b-256 903a9f7abb792f5ec6baeb4b3c1b56753e14bae2197b583ed5374853fc6cf505

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page