Quadratic programming solvers in Python with a unified API
—
The main interface functions for solving quadratic programming problems with qpsolvers. These functions provide a unified API that abstracts different solver backends and handles matrix format conversions automatically.
The main function for solving quadratic programs with automatic solver selection and format handling.
def solve_qp(
P: Union[np.ndarray, spa.csc_matrix],
q: np.ndarray,
G: Optional[Union[np.ndarray, spa.csc_matrix]] = None,
h: Optional[np.ndarray] = None,
A: Optional[Union[np.ndarray, spa.csc_matrix]] = None,
b: Optional[np.ndarray] = None,
lb: Optional[np.ndarray] = None,
ub: Optional[np.ndarray] = None,
solver: Optional[str] = None,
initvals: Optional[np.ndarray] = None,
verbose: bool = False,
**kwargs
) -> Optional[np.ndarray]:
"""
Solve a quadratic program using the specified solver.
The QP is formulated as:
minimize 1/2 x^T P x + q^T x
subject to G x <= h
A x = b
lb <= x <= ub
Parameters:
- P: Symmetric cost matrix (positive semi-definite)
- q: Cost vector
- G: Linear inequality constraint matrix
- h: Linear inequality constraint vector
- A: Linear equality constraint matrix
- b: Linear equality constraint vector
- lb: Lower bound constraint vector (can contain -np.inf)
- ub: Upper bound constraint vector (can contain +np.inf)
- solver: Solver name from available_solvers (required)
- initvals: Initial values for warm-starting
- verbose: Enable solver output
- **kwargs: Solver-specific parameters
Returns:
Optimal solution vector if found, None if infeasible/unbounded
Raises:
- NoSolverSelected: If solver parameter is not specified
- SolverNotFound: If specified solver is not available
- SolverError: If solver encounters an error
"""Usage example:
import numpy as np
from qpsolvers import solve_qp, available_solvers
# Check available solvers
print("Available solvers:", available_solvers)
# Define QP matrices
P = np.array([[2.0, 0.0], [0.0, 2.0]])
q = np.array([1.0, 1.0])
G = np.array([[-1.0, 0.0], [0.0, -1.0]])
h = np.array([0.0, 0.0])
# Solve with different solvers
x1 = solve_qp(P, q, G, h, solver="osqp")
x2 = solve_qp(P, q, G, h, solver="cvxopt")
x3 = solve_qp(P, q, G, h, solver="proxqp", verbose=True)Alternative interface that uses Problem objects and returns complete Solution objects with dual multipliers.
def solve_problem(
problem: Problem,
solver: str,
initvals: Optional[np.ndarray] = None,
verbose: bool = False,
**kwargs
) -> Solution:
"""
Solve a quadratic program using a Problem object.
Parameters:
- problem: Problem instance containing QP formulation
- solver: Solver name from available_solvers
- initvals: Initial values for warm-starting
- verbose: Enable solver output
- **kwargs: Solver-specific parameters
Returns:
Solution object with primal/dual variables and optimality information
Raises:
- SolverNotFound: If specified solver is not available
- ValueError: If problem is malformed or non-convex
"""Usage example:
from qpsolvers import Problem, solve_problem
# Create a problem instance
problem = Problem(P, q, G, h, A, b)
problem.check_constraints() # Validate problem formulation
# Solve and get complete solution
solution = solve_problem(problem, solver="osqp")
if solution.found:
print(f"Optimal value: {solution.obj}")
print(f"Primal solution: {solution.x}")
print(f"Dual multipliers (inequalities): {solution.z}")
print(f"Dual multipliers (equalities): {solution.y}")
print(f"Is optimal: {solution.is_optimal(eps_abs=1e-6)}")Specialized function for solving linear least squares problems as quadratic programs.
def solve_ls(
R: Union[np.ndarray, spa.csc_matrix],
s: np.ndarray,
G: Optional[Union[np.ndarray, spa.csc_matrix]] = None,
h: Optional[np.ndarray] = None,
A: Optional[Union[np.ndarray, spa.csc_matrix]] = None,
b: Optional[np.ndarray] = None,
lb: Optional[np.ndarray] = None,
ub: Optional[np.ndarray] = None,
W: Optional[Union[np.ndarray, spa.csc_matrix]] = None,
solver: Optional[str] = None,
initvals: Optional[np.ndarray] = None,
verbose: bool = False,
sparse_conversion: Optional[bool] = None,
**kwargs
) -> Optional[np.ndarray]:
"""
Solve a linear least squares problem with optional constraints.
Formulated as:
minimize 1/2 ||R x - s||^2_W
subject to G x <= h
A x = b
lb <= x <= ub
Where ||·||_W is the weighted norm with weight matrix W.
Parameters:
- R: Regression matrix
- s: Target vector
- G: Linear inequality constraint matrix
- h: Linear inequality constraint vector
- A: Linear equality constraint matrix
- b: Linear equality constraint vector
- lb: Lower bound constraint vector
- ub: Upper bound constraint vector
- W: Weight matrix for the least squares objective
- solver: Solver name from available_solvers
- initvals: Initial values for warm-starting
- verbose: Enable solver output
- sparse_conversion: Force sparse (True) or dense (False) conversion, auto if None
- **kwargs: Solver-specific parameters
Returns:
Optimal solution vector if found, None if infeasible
"""Usage example:
import numpy as np
from qpsolvers import solve_ls
# Define least squares problem: minimize ||Rx - s||^2
R = np.random.randn(100, 10) # 100 observations, 10 variables
s = np.random.randn(100) # target values
# Solve unconstrained least squares
x_unconstrained = solve_ls(R, s, solver="osqp")
# Solve with non-negativity constraints
lb = np.zeros(10)
x_nonneg = solve_ls(R, s, lb=lb, solver="osqp")
# Solve with weighted least squares
W = np.diag(np.random.rand(100)) # diagonal weight matrix
x_weighted = solve_ls(R, s, W=W, solver="osqp")Specialized function for solving unconstrained quadratic programs using SciPy's LSQR.
def solve_unconstrained(problem: Problem) -> Solution:
"""
Solve an unconstrained quadratic program with SciPy's LSQR.
Only works for problems with no constraints (G, h, A, b, lb, ub all None).
Parameters:
- problem: Unconstrained Problem instance
Returns:
Solution object with the optimal point
Raises:
- ProblemError: If the problem is unbounded below or has constraints
"""Usage example:
from qpsolvers import Problem, solve_unconstrained
import numpy as np
# Define unconstrained QP: minimize 1/2 x^T P x + q^T x
P = np.array([[2.0, 1.0], [1.0, 2.0]])
q = np.array([1.0, -1.0])
# Create unconstrained problem
problem = Problem(P, q) # No constraints specified
# Solve directly
solution = solve_unconstrained(problem)
print(f"Unconstrained optimum: {solution.x}")All solving functions accept both dense and sparse matrix formats:
numpy.ndarray for smaller problems or when dense solvers are preferredscipy.sparse.csc_matrix for large sparse problemsThe functions automatically handle format conversions based on the selected solver's requirements.
import numpy as np
import scipy.sparse as spa
from qpsolvers import solve_qp
# Dense format
P_dense = np.eye(3)
solution1 = solve_qp(P_dense, q, solver="quadprog") # dense solver
# Sparse format
P_sparse = spa.eye(3, format="csc")
solution2 = solve_qp(P_sparse, q, solver="osqp") # sparse solverInstall with Tessl CLI
npx tessl i tessl/pypi-qpsolvers