Developer notes¶
Adding a new solver¶
Let’s imagine we want to add a new solver called AwesomeQP. The solver keyword, the string passed via the solver
keyword argument, is the lowercase version of the vernacular name of a QP solver. For our imaginary solver, the keyword is therefore "awesomeqp"
.
The process to add AwesomeQP to qpsolvers goes as follows:
Create a new file
qpsolvers/solvers/awesomeqp_.py
(named after the solver keyword, with a trailing underscore)Implement in this file a function
awesomeqp_solve_problem
that returns aSolution
Implement in the same file a function
awesomeqp_solve_qp
to connect it to the historical API, typically as follows:
def awesomeqp_solve_qp(P, q, G, h, A, b, lb, ub, initvals=None, verbose=False, **kwargs):
) -> Optional[np.ndarray]:
r"""Solve a quadratic program using AwesomeQP.
[document parameters and return values here]
"""
problem = Problem(P, q, G, h, A, b, lb, ub)
solution = awesomeqp_solve_problem(
problem, initvals, verbose, backend, **kwargs
)
return solution.x if solution.found else None
Define the two function prototypes for
awesomeqp_solve_problem
andawesomeqp_solve_qp
inqpsolvers/solvers/__init__.py
:
# AwesomeQP
# ========
awesome_solve_qp: Optional[
Callable[
[
ndarray,
ndarray,
Optional[ndarray],
Optional[ndarray],
Optional[ndarray],
Optional[ndarray],
Optional[ndarray],
Optional[str],
bool,
],
Optional[ndarray],
]
] = None
Note
The prototype needs to match the actual function. You can check its correctness by running tox -e py
in the repository.
Below the prototype, import the function into the
solve_function
dictionary:
try:
from .awesomeqp_ import awesomeqp_solve_qp
solve_function["awesomeqp"] = awesomeqp_solve_qp
available_solvers.append("awesomeqp")
# dense_solvers.append("awesomeqp") if applicable
# sparse_solvers.append("awesomeqp") if applicable
except ImportError:
pass
Append the solver identifier to
dense_solvers
orsparse_solvers
, if applicableImport
awesomeqp_solve_qp
fromqpsolvers/__init__.py
and add it to__all__
Add the solver to
doc/src/supported-solvers.rst
Add the solver to the Solvers section of the README
Assuming AwesomeQP is distributed on PyPI, add it to the
[testenv]
and[testenv:coverage]
environments oftox.ini
for unit testingAssuming AwesomeQP is distributed on Conda or PyPI, add it to the list of dependencies in
doc/environment.yml
Log the new solver as an addition in the changelog
If you are a new contributor, feel free to add your name to
CITATION.cff
.
Problem conversions¶
Convert problems from and to standard QP form.
- qpsolvers.conversions.combine_linear_box_inequalities(G, h, lb, ub, n, use_csc)¶
Combine linear and box inequalities into double-sided linear format.
Input format:
\[\begin{split}\begin{split}\begin{array}{ll} G x & \leq h \\ lb & \leq x \leq ub \end{array}\end{split}\end{split}\]Output format:
\[l \leq C \leq u\]- Parameters:
G – Linear inequality constraint matrix. Must be two-dimensional.
h – Linear inequality constraint vector.
lb – Lower bound constraint vector.
ub – Upper bound constraint vector.
n (
int
) – Number of optimization variables.use_csc (
bool
) – IfTrue
, use sparse rather than dense matrices.
- Returns:
Linear inequality matrix \(C\) and vectors \(u\), \(l\). The two vector will contain \(\pm\infty\) values on coordinates where there is no corresponding constraint.
- Raises:
ProblemError – If the inequality matrix and vector are not consistent.
- qpsolvers.conversions.ensure_sparse_matrices(P, G, A)¶
Make sure problem matrices are sparse.
- Parameters:
P (
Union
[ndarray
,csc_matrix
]) – Cost matrix.G (
Union
[ndarray
,csc_matrix
,None
]) – Inequality constraint matrix, if any.A (
Union
[ndarray
,csc_matrix
,None
]) – Equality constraint matrix, if any.
- Return type:
Tuple
[csc_matrix
,Optional
[csc_matrix
],Optional
[csc_matrix
]]- Returns:
Tuple of all three matrices as sparse matrices.
- qpsolvers.conversions.linear_from_box_inequalities(G, h, lb, ub, use_sparse)¶
Append lower or upper bound vectors to inequality constraints.
- Parameters:
G (
Union
[ndarray
,csc_matrix
,None
]) – Linear inequality matrix.h (
Optional
[ndarray
]) – Linear inequality vector.lb (
Optional
[ndarray
]) – Lower bound constraint vector.ub (
Optional
[ndarray
]) – Upper bound constraint vector.use_sparse (
bool
) – Use sparse matrices if true, dense matrices otherwise.
- Return type:
Tuple
[Union
[ndarray
,csc_matrix
,None
],Optional
[ndarray
]]- Returns:
G (np.ndarray, spa.csc_matrix or None) – Updated linear inequality matrix.
h (np.ndarray or None) – Updated linear inequality vector.
- qpsolvers.conversions.socp_from_qp(P, q, G, h)¶
Convert a quadratic program to a second-order cone program.
The quadratic program is defined by:
\[\begin{split}\begin{split}\begin{array}{ll} \underset{x}{\mbox{minimize}} & \frac{1}{2} x^T P x + q^T x \\ \mbox{subject to} & G x \leq h \end{array}\end{split}\end{split}\]The equivalent second-order cone program is:
\[\begin{split}\begin{split}\begin{array}{ll} \underset{x}{\mbox{minimize}} & c^T_s y \\ \mbox{subject to} & G_s y \leq_{\cal K} h_s \end{array}\end{split}\end{split}\]This function is adapted from
ecosqp.m
in the ecos-matlab repository. See the documentation in that script for details on this reformulation.- Parameters:
P (
ndarray
) – Primal quadratic cost matrix.q (
ndarray
) – Primal quadratic cost vector.G (
Optional
[ndarray
]) – Linear inequality constraint matrix.h (
Optional
[ndarray
]) – Linear inequality constraint vector.
- Return type:
Tuple
[ndarray
,ndarray
,ndarray
,Dict
[str
,Any
]]- Returns:
c_socp (array) – SOCP cost vector.
G_socp (array) – SOCP inequality matrix.
h_socp (array) – SOCP inequality vector.
dims (dict) – Dimension dictionary used by SOCP solvers, where
dims["l"]
is the number of inequality constraints.
- Raises:
ValueError : – If the cost matrix is not positive definite.
- qpsolvers.conversions.split_dual_linear_box(z_stacked, lb, ub)¶
Separate linear and box multipliers from a stacked dual vector.
This function assumes linear and box inequalities were combined using
qpsolvers.conversions.linear_from_box_inequalities()
.- Parameters:
z_stacked (
ndarray
) – Stacked vector of dual multipliers.lb (
Optional
[ndarray
]) – Lower bound constraint vector.ub (
Optional
[ndarray
]) – Upper bound constraint vector.
- Return type:
Tuple
[Optional
[ndarray
],Optional
[ndarray
]]- Returns:
Pair
z, z_box
of linear and box multipliers. Both can be empty arrays if there is no corresponding constraint.
Testing locally¶
To run all CI checks locally, go to the repository folder and run
tox -e py
This will run linters and unit tests.