A small, efficient class for solving convex optimisation problems. A generic quadratic programming (QP) problem is of the form:
where:
-
$\mathbf{x}\in\mathbb{R}^\mathrm{n}$ is the decision variable, -
$\mathbf{H = H^\mathrm{T}}\in\mathbb{R}^\mathrm{n\times n}$ is a weighting matrix, -
$\mathbf{f}\in\mathbb{R}^\mathrm{n}$ is the linear component of the quadratic equation, -
$\mathbf{B}\in\mathbb{R}^\mathrm{c\times n}$ is a constraint matrix, and -
$\mathbf{z}\in\mathbb{R}^\mathrm{c}$ is a constraint vector.
There are also several functions for handling linear least squares problems with equality and inequality constraints. It is fast, and effective. It has been used to control the upper body of a humanoid robot subject to grasp constraints and joint limits.
Note
Simple QP Solver is free to use under the GNU General Public License v3.0. If you find this software useful, citing it would be appreciated.
Jump To:
The SimpleQPSolver
requires the Eigen
libraries. If you're using Linux you can install it from the command line:
sudo apt install libeigen3-dev
Otherwise, you can head over to the main page to see how you can install it.
SimpleQPSolver is a template class contained in a single header file. There is no need to clone this repository and build any packages (unless you want to, of course). All you need to do is download the header file QPSolver.h
and include it in your package:
software_simple_qp/include/QPSolver.h
That is all!
Note
If you want to build the package for some reason, there is a simple test.cpp
file you can run that demonstrates the use of the QPSolver
class.
It is possible to automatically download the QPSolver.h
header file as part of another package. In your CMakeLists.txt
file you can add something like:
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/your_package/include/QPSolver.h")
file(DOWNLOAD
https://raw.githubusercontent.com/Woolfrey/software_simple_qp/master/include/QPSolver.h
${CMAKE_SOURCE_DIR}/your_package/include/QPSolver.h)
endif()
You can use float
or double
with this class. Input arguments for the Eigen
classes must match:
Eigen::MatrixXf
andEigen::VectorXf
when usingQPSolver<float>
, orEigen::MatrixXd
andEigen::VectorXd
when usingQPSolver<double>
.
For problems without inequality constraints, you can call static
methods without creating a QPSolver
object:
QPSolver<float>::solve(H,f)
QPSolver<double>::least_squares(y,A,W)
QPSolver<float>::redundant_least_squares(xd,W,A,y)
Methods with inequality constraints, e.g.
Some examples are below.
Assuming
Eigen::VectorXd x = QPSolver<double>::solve(H,f);
Tip
You can actually solve this yourself by calling something like x = H.ldlt().solve(-f)
, but I put this function in for completeness.
where:
-
$\mathbf{y}\in\mathbb{R}^\mathrm{m}$ , -
$\mathbf{A}\in\mathbb{R}^\mathrm{m\times n}$ , -
$\mathbf{x}\in\mathbb{R}^\mathrm{n}$ , -
$\mathbf{W}\in\mathbb{R}^\mathrm{m\times m}$ weights the error, and - m
$\ge$ n (more outputs than inputs)
Call:
Eigen::VectorXf x = QPSolver<float>::least_squares(y,A,W);
where:
-
$\mathbf{y}\in\mathbb{R}^\mathrm{m}$ , -
$\mathbf{A}\in\mathbb{R}^\mathrm{m\times n}$ , -
$\mathbf{x}\in\mathbb{R}^\mathrm{n}$ , -
$\mathbf{x}_\mathrm{d}\in\mathbb{R}^\mathrm{n}$ is a desired value for$\mathbf{x}$ , -
$\mathbf{W}\in\mathbb{R}^\mathrm{n\times n}$ weights the solution, - m < n (more inputs than outputs)
In this situation the
Eigen::VectorXd x = QPSolver<double>::redundant_least_squares(xd,W,A,y);
Note
For problems like this with inequality constraints, the solver uses an interior point algorithm. This uses Newton's method to iteratively minimize the objective function whilst satisfying the inequality. It therefore requires a start point or initial guess.
First create an object, then call the function:
QPSolver<double> solver;
Eigen::VectorXd x = solver.solve(H,f,B,z,x0);
where x0
is the start point argument. It can have a huge influence on the result, so it is good to give the solver an approximate solution if you can.
If you're repeatedly solving a QP problem you can get the last solution and use that as the input:
Eigen::VectorXd x0 = solver.last_solution();
There are several functions conveniently written for least squares type problems:
Linear least squares with upper and lower bounds:
use:
Eigen::VectorXf x = solver.constrained_least_squares(y,A,W,xMin,xMax,x0);
Redundant least squares with upper and lower bounds:
use:
Eigen::VectorXd x = solver.constrained_least_squares(xd,W,A,y,xMin,xMax,x0);
Redundant least squares with inequality constraints:
use:
Eigen::VectorXd x = solver.constrained_least_squares(xd,W,A,y,B,z,x0);
Warning
When using this particular function the desired value
There are several parameters that can be set when solving for inequality constaints:
-
set_step_size(const DataType &size)
: The parameter$\alpha$ scales the step size$\alpha\cdot\Delta\mathbf{x}$ . Default value is 1; a smaller size will mean slower convergence. -
set_tolerance(const DataType &tolerance)
: The algorithm terminates when$|\alpha\cdot\Delta\mathbf{x}|$ is less than this value. A smaller value means a more accurate solution, but slower solution time. -
set_num_steps(const unsigned int &numer)
: The algorithm terminates if this number of steps is reached. A higher value means a more accurate solution, but it might take longer to solve. -
set_barrier_scalar(const DataType &scalar)
: The inequality constraints are converted to a log-barrier function. This parameter determines how steep the slope of the barrier is. A smaller value means a faster solution, but you may prematurely run in to the constraint and terminate the algorithm. -
set_barrier_reduction_rate(const DataType &rate)
: Every loop the barrier slope is decreased. This determines how fast it decreases. A smaller value means the barrier effect will shrink quickly. This will make the algorithm faster, but then it may not find a solution if it hits the constraints prematurely.
First navigate to your working directory:
cd ~/MyWorkspace
Then clone the repository:
git clone https://github.com/Woolfrey/software_simple_qp.git
Create a build folder and navigate to it:
cd software_simple_qp/ && mkdir build && cd build
Generate the build tools using:
cmake ../
Then build the package with:
make
You can then run
./test
which prints information about the use of different class methods, as well as the accuracy and speed of solutions.
If you use SimpleQPSolver
and find it useful, I'd appreciate it if you could cite me. Here is a BibTeX
format:
@software{Woolfrey_SimpleQPSolver_2023,
author = {Woolfrey, Jon},
month = aug,
title = {{S}imple {QP} {S}olver},
url = {https://github.com/Woolfrey/software_simple_qp},
version = {1.0.0},
year = {2023}
}
Here's the automatically generated APA format:
Woolfrey, J. (2023). SimpleQPSolver (Version 1.0.0) [Computer software]. https://github.com/Woolfrey/software_simple_qp
Alternatively, click on Cite this repository
on the top-right corner of this page.