PyTorch API¶
The PyTorch layer extends torch.nn.Module for seamless integration with PyTorch models.
CvxpyLayer¶
- class cvxpylayers.torch.CvxpyLayer[source]¶
Bases:
ModuleA differentiable convex optimization layer for PyTorch.
This layer wraps a parametrized CVXPY problem, solving it in the forward pass and computing gradients via implicit differentiation in the backward pass.
Example
>>> import cvxpy as cp >>> import torch >>> from cvxpylayers.torch import CvxpyLayer >>> >>> # Define a simple QP >>> x = cp.Variable(2) >>> A = cp.Parameter((3, 2)) >>> b = cp.Parameter(3) >>> problem = cp.Problem(cp.Minimize(cp.sum_squares(A @ x - b)), [x >= 0]) >>> >>> # Create the layer >>> layer = CvxpyLayer(problem, parameters=[A, b], variables=[x]) >>> >>> # Solve with gradients >>> A_t = torch.randn(3, 2, requires_grad=True) >>> b_t = torch.randn(3, requires_grad=True) >>> (solution,) = layer(A_t, b_t) >>> solution.sum().backward()
- __init__(problem, parameters, variables, solver=None, gp=False, verbose=False, canon_backend=None, solver_args=None)[source]¶
Initialize the differentiable optimization layer.
- Parameters:
problem (Problem) – A CVXPY Problem. Must be DPP-compliant (
problem.is_dpp()must return True).parameters (list[Parameter]) – List of CVXPY Parameters that will be filled with values at runtime. Order must match the order of tensors passed to forward().
variables (list[Variable]) – List of CVXPY Variables whose optimal values will be returned by forward(). Order determines the order of returned tensors.
solver (str | None) – CVXPY solver to use (e.g.,
cp.CLARABEL,cp.SCS). If None, uses the default diffcp solver.gp (bool) – If True, problem is a geometric program. Parameters will be log-transformed before solving.
verbose (bool) – If True, print solver output.
canon_backend (str | None) – Backend for canonicalization. Options are ‘diffcp’, ‘cuclarabel’, or None (auto-select).
solver_args (dict[str, Any] | None) – Default keyword arguments passed to the solver. Can be overridden per-call in forward().
- Raises:
AssertionError – If problem is not DPP-compliant.
ValueError – If parameters or variables are not part of the problem.
- Return type:
None
- forward(*params, solver_args=None)[source]¶
Solve the optimization problem and return optimal variable values.
- Parameters:
*params (Tensor) – Tensor values for each CVXPY Parameter, in the same order as the
parametersargument to __init__. Each tensor shape must match the corresponding Parameter shape, optionally with a batch dimension prepended. Batched and unbatched parameters can be mixed; unbatched parameters are broadcast.solver_args (dict[str, Any] | None) – Keyword arguments passed to the solver, overriding any defaults set in __init__.
- Returns:
Tuple of tensors containing optimal values for each CVXPY Variable specified in the
variablesargument to __init__. If inputs are batched, outputs will have matching batch dimensions.- Raises:
RuntimeError – If the solver fails to find a solution.
- Return type:
Example
>>> # Single problem >>> (x_opt,) = layer(A_tensor, b_tensor) >>> >>> # Batched: solve 10 problems in parallel >>> A_batch = torch.randn(10, 3, 2) >>> b_batch = torch.randn(10, 3) >>> (x_batch,) = layer(A_batch, b_batch) # x_batch.shape = (10, 2)
Usage Example¶
import cvxpy as cp
import torch
from cvxpylayers.torch import CvxpyLayer
# Define problem
n, m = 2, 3
x = cp.Variable(n)
A = cp.Parameter((m, n))
b = cp.Parameter(m)
problem = cp.Problem(
cp.Minimize(cp.sum_squares(A @ x - b)),
[x >= 0]
)
# Create layer
layer = CvxpyLayer(problem, parameters=[A, b], variables=[x])
# Solve with gradients
A_t = torch.randn(m, n, requires_grad=True)
b_t = torch.randn(m, requires_grad=True)
(x_sol,) = layer(A_t, b_t)
# Backpropagate
x_sol.sum().backward()
GPU Usage¶
For GPU acceleration with CuClarabel:
import cvxpy as cp
import torch
from cvxpylayers.torch import CvxpyLayer
device = torch.device("cuda")
layer = CvxpyLayer(
problem,
parameters=[A, b],
variables=[x],
solver=cp.CUCLARABEL
).to(device)
A_gpu = torch.randn(m, n, device=device, requires_grad=True)
b_gpu = torch.randn(m, device=device, requires_grad=True)
(x_sol,) = layer(A_gpu, b_gpu)
Integration with nn.Module¶
import torch.nn as nn
class OptNet(nn.Module):
def __init__(self):
super().__init__()
self.fc = nn.Linear(10, 6)
x = cp.Variable(2)
A = cp.Parameter((3, 2))
b = cp.Parameter(3)
problem = cp.Problem(cp.Minimize(cp.sum_squares(A @ x - b)))
self.cvx = CvxpyLayer(problem, parameters=[A, b], variables=[x])
def forward(self, features, b):
A = self.fc(features).view(-1, 3, 2)
(x,) = self.cvx(A, b)
return x