Skip to main content

Differentiation Through Black-Box Quadratic Programming Solvers

Project description

dQP

Differentiation Through Black-Box Quadratic Programming Solvers [Paper]
Magoon*, Yang*, Aigerman, Kovalsky
Accepted. NeurIPS (2025)

teaser teaser

Installation

pip install libdqp

This [PyPI package] includes PyTorch, open-source python interfaces to various QP and linear solvers, and tools for sparsity. Some QP solvers such as Gurobi are commercial, but offer [academic licenses]. Experiment-specific packages are detailed in the experiment section.

Introduction

dQP is a modular framework for differentiating the solution to a quadratic programming problem (QP) with respect to its parameters, enabling the seamless integration of QPs into machine learning architectures and bilevel optimization. dQP supports over 15 state-of-the-art QP solvers by attaching a custom PyTorch layer to the open-source QP interface [qpsolvers]. It solves problems of the form,

QP

and is used as in the following example which uses QP solver [OSQP] and sparse symmetric indefinite linear solver [QDLDL],

import torch
import numpy as np
from scipy.sparse import csc_matrix

from dqp import dQP
from dqp.sparse_helper import csc_scipy_to_torch

# Define a simple QP: minimize x1^2 + x2^2 s.t. x1 + x2 >= 1, x >= 0
P = csc_matrix(np.array([[2.0, 0.0], [0.0, 2.0]]))
q = np.array([0.0, 0.0])
C = csc_matrix(np.array([[-1.0, -1.0], [-1.0, 0.0], [0.0, -1.0]]))
d = np.array([-1.0, 0.0, 0.0])

# Convert to PyTorch tensors
P_torch = csc_scipy_to_torch(P)
q_torch = torch.tensor(q, dtype=torch.float64, requires_grad=True)
C_torch = csc_scipy_to_torch(C)
d_torch = torch.tensor(d, dtype=torch.float64, requires_grad=True)

# Build settings with OSQP forward solver and qdldl backward solver
settings = dQP.build_settings(
    solve_type="sparse",
    qp_solver="osqp",
    lin_solver="qdldl",
)

# Create layer and solve
layer = dQP.dQP_layer(settings=settings)
x_star, lambda_star, mu_star, _, _ = layer(P_torch, q_torch, C_torch, d_torch)

# Backpropagate (differentiate) through scalar loss L(x^*) = sum(x^*)
x_star.sum().backward()
print(f"Solution: {x_star.detach().numpy()}")  # [0.5, 0.5]
print(f"Gradient w.r.t. d: {d_torch.grad.numpy()}")

The key mathematical structure we use to aid modularity is the special polyhedral geometry of a QP. This is illustrated below, where a QP is locally equivalent to a purely equality constrained problem, i.e., both their solutions and derivatives match.

teaser

Options

While dQP described in the snippet above suppresses default options, dQP has the following tunable settings.

Option Meaning
qp_solver QP solver (supported by [qpsolvers]).
lin_solver Linear solver for the derivative (algorithm 1).
solve_type Dense or sparse (CSC format).
eps_abs Absolute tolerance on feasibility, optimality, etc.
eps_rel Relative tolerance.
eps_active Tolerance to decide the active set of constraints.
dual_available Assert whether a given solver outputs duals, if not, solve for them.
normalize_constraints Normalize each row of the constraints, so residuals are distances.
refine_active Use heuristic active set refinement (Fig. 7)
warm_start_from_previous Save previous solution and use to warm-start (useful in bilevel optimization)
omp_parallel Use a simple parallel for loop for the forward over batches.
empty_batch Include an empty batch dimension, even for batch 1.
qp_solver_keywords Additional keywords for qpsolvers.
verbose Display additional information.
time Display timings.
check_PSD Verify that Q is positive semi-definite (dense only), is costly.

Which solver do I choose for my problem? First, we suggest perusing open-source benchmarks and the basic classes of QP solver. For more information, we include a simple diagnostic tool which iterates through available QP solvers and times the forward/backward solves of your example QP (Fig. 6).

Development Setup and Experiments

This script sets up the environment distributed with PyPI:
git clone https://github.com/cwmagoon/dQP
cd dQP
conda create -y --name dQP python=3.9
conda activate dQP
pip install -e .

This script includes this and provides additional dependencies used in the experiments:

git clone https://github.com/cwmagoon/dQP
cd dQP
conda create -y --name dQP python=3.9
conda activate dQP

pip install torch==2.3.0+cpu -f https://download.pytorch.org/whl/torch_stable.html scipy numpy qpsolvers 
pip install clarabel cvxopt daqp ecos gurobipy highspy mosek osqp piqp proxsuite qpalm quadprog scs 
pip install qdldl pypardiso 

pip install optnet qpth cvxpylayers proxsuite
    
pip install matplotlib tensorboard pandas

pip install setproctitle

pip install torch_geometric torch_scatter torch_sparse -f https://data.pyg.org/whl/torch-2.3.0+cpu.html
pip install libigl polyscope shapely robust_laplacian torchvision==0.18
conda install -c conda-forge ffmpeg


We provide the code for our experiments in the experiments folder, including some additional directions on running them at the following:

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

libdqp-0.1.1.tar.gz (31.8 kB view details)

Uploaded Source

Built Distribution

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

libdqp-0.1.1-py3-none-any.whl (30.6 kB view details)

Uploaded Python 3

File details

Details for the file libdqp-0.1.1.tar.gz.

File metadata

  • Download URL: libdqp-0.1.1.tar.gz
  • Upload date:
  • Size: 31.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.7

File hashes

Hashes for libdqp-0.1.1.tar.gz
Algorithm Hash digest
SHA256 385a77721af544141c2a2716ea84804b6e8b28976e160f3f74e6596aee397f7d
MD5 90e0ec52547884743ee7431b563c39cd
BLAKE2b-256 89e8cab3576c96e201b948d89607d2a41c4a58ad7a1d1c1370f4459c07b0964e

See more details on using hashes here.

File details

Details for the file libdqp-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: libdqp-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 30.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.7

File hashes

Hashes for libdqp-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 31d899e291a6faa93e253c2d08f02dc1b79c009ec5cbcf2b7d9f8a94a164d90c
MD5 fe82d2b101ea94c273f2a982ce3cf617
BLAKE2b-256 b883b0a84b1a671449916adaafcc6baba8a8138c53d39188013f500837f6c1d5

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