Coverage for lpsolvers / __init__.py: 69%
52 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-20 15:19 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-20 15:19 +0000
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4# SPDX-License-Identifier: LGPL-3.0-or-later
5# Copyright (C) 2016-2022 Stéphane Caron
7"""Linear programming solvers in Python with a unified API."""
9from typing import Optional
11import numpy as np
13from .exceptions import NoSolverSelected, SolverNotFound
15__version__ = "2.2.0"
17available_solvers = []
20# cdd
21# ===
23try:
24 from .cdd_ import cdd_solve_lp
26 available_solvers.append("cdd")
27except ImportError:
29 def cdd_solve_lp(
30 c: np.ndarray,
31 G: np.ndarray,
32 h: np.ndarray,
33 A: Optional[np.ndarray] = None,
34 b: Optional[np.ndarray] = None,
35 ) -> np.ndarray:
36 """Error function defined when cdd is not available."""
37 raise ImportError("cdd not found")
40# CVXOPT
41# ======
43try:
44 from .cvxopt_ import cvxopt_solve_lp
46 available_solvers.append("cvxopt")
47except ImportError:
49 def cvxopt_solve_lp(
50 c: np.ndarray,
51 G: np.ndarray,
52 h: np.ndarray,
53 A: Optional[np.ndarray] = None,
54 b: Optional[np.ndarray] = None,
55 solver: Optional[str] = None,
56 **kwargs,
57 ) -> np.ndarray:
58 """Error function defined when CVXOPT is not available."""
59 raise ImportError("CVXOPT not found")
62# CVXPY
63# =====
65try:
66 from .cvxpy_ import cvxpy_solve_lp
68 available_solvers.append("cvxpy")
69except ImportError:
71 def cvxpy_solve_lp(
72 c: np.ndarray,
73 G: np.ndarray,
74 h: np.ndarray,
75 A: Optional[np.ndarray] = None,
76 b: Optional[np.ndarray] = None,
77 solver: Optional[str] = None,
78 verbose: bool = False,
79 **kwargs,
80 ) -> np.ndarray:
81 """Error function defined when CVXPY is not available."""
82 raise ImportError("CVXPY not found")
85# PDLP
86# ====
88try:
89 from .pdlp_ import pdlp_solve_lp
91 available_solvers.append("pdlp")
92except ImportError:
94 def pdlp_solve_lp(
95 c: np.ndarray,
96 G: np.ndarray,
97 h: np.ndarray,
98 A: Optional[np.ndarray] = None,
99 b: Optional[np.ndarray] = None,
100 verbose: bool = False,
101 eps_optimal_absolute: Optional[float] = None,
102 eps_optimal_relative: Optional[float] = None,
103 time_sec_limits: Optional[float] = None,
104 **kwargs,
105 ) -> np.ndarray:
106 """Error function defined when PDLP is not available."""
107 raise ImportError("PDLP not found")
110# ProxQP
111# ======
113try:
114 from .proxqp_ import proxqp_solve_lp
116 available_solvers.append("proxqp")
117except ImportError:
119 def proxqp_solve_lp(
120 c: np.ndarray,
121 G: np.ndarray,
122 h: np.ndarray,
123 A: Optional[np.ndarray] = None,
124 b: Optional[np.ndarray] = None,
125 verbose: bool = False,
126 backend: Optional[str] = None,
127 **kwargs,
128 ) -> np.ndarray:
129 """Error function defined when ProxQP is not available."""
130 raise ImportError("ProxQP not found")
133def solve_lp(
134 c: np.ndarray,
135 G: np.ndarray,
136 h: np.ndarray,
137 A: Optional[np.ndarray] = None,
138 b: Optional[np.ndarray] = None,
139 solver: Optional[str] = None,
140 **kwargs,
141) -> np.ndarray:
142 r"""Solve a linear program using one of the available LP solvers.
144 The linear program is defined as:
146 .. math::
148 \begin{split}\begin{array}{ll}
149 \mbox{minimize} &
150 c^T x \\
151 \mbox{subject to}
152 & G x \leq h \\
153 & A x = b
154 \end{array}\end{split}
156 Parameters
157 ----------
158 c :
159 Linear cost vector.
160 G :
161 Linear inequality constraint matrix.
162 h :
163 Linear inequality constraint vector.
164 A :
165 Linear equality constraint matrix.
166 b :
167 Linear equality constraint vector.
168 solver :
169 Name of the LP solver to choose in :data:`lpsolvers.available_solvers`.
171 Returns
172 -------
173 :
174 Optimal solution if found, ``None`` otherwise.
176 Raises
177 ------
178 ValueError
179 If the LP is not feasible.
180 SolverNotFound
181 If the requested LP solver is not found.
183 Notes
184 -----
185 Extra keyword arguments given to this function are forwarded to the
186 underlying solver. For example, we can call ProxQP with a custom absolute
187 feasibility tolerance by ``solve_lp(c, G, h, solver='proxqp',
188 eps_abs=1e-8)``.
189 """
190 if solver is None:
191 raise NoSolverSelected(
192 "Set the `solver` keyword argument to one of the "
193 f"available solvers in {available_solvers}"
194 )
195 if isinstance(G, np.ndarray) and G.ndim == 1:
196 G = G.reshape((1, G.shape[0]))
197 if solver == "cdd":
198 return cdd_solve_lp(c, G, h, A, b)
199 if solver == "cvxopt":
200 return cvxopt_solve_lp(c, G, h, A, b, **kwargs)
201 if solver == "cvxpy":
202 return cvxpy_solve_lp(c, G, h, A, b, **kwargs)
203 if solver == "pdlp":
204 return pdlp_solve_lp(c, G, h, A, b, **kwargs)
205 if solver == "proxqp":
206 return proxqp_solve_lp(c, G, h, A, b, **kwargs)
207 raise SolverNotFound(f"solver '{solver}' is not available")
210__all__ = [
211 "__version__",
212 "available_solvers",
213 "cdd_solve_lp",
214 "cvxopt_solve_lp",
215 "cvxpy_solve_lp",
216 "pdlp_solve_lp",
217 "proxqp_solve_lp",
218 "solve_lp",
219]