Use OSQP as custom solver for Matlab MPC

I already implemented some simplified OSQP-based MPC controllers using S-functions in Simulink.

I was wondering if anyone was able to use OSQP as the costume solver for Matlab MPC or it is not doable due to setup->update->solve procedure of the solver. Matlab MPC class and blocks have nice and mature design capabilities and enable the user to call custom QP solver for simulation and/or code generation as per the following link.
https://www.mathworks.com/help/mpc/ug/qp-solver.html#bu7wrmu

We have implemented support for simulink in this branch of the matlab interface:
https://github.com/oxfordcontrol/osqp-matlab/tree/pg/simulink

It has not been merged to the main matlab interface though, nor has it been heavily tested.

I don’t know of anyone who has implemented an interface to use OSQP as a custom solver with the mathworks MPC tools, but I am sure it would be possible. Some trickery would perhaps be required in order to detect when a problem has been posed for the first time or when the data matrices have changed, but it’s definitely doable.

OSQP does codegen as well, so we could also support that functionality through the MPC tools.

You could make a feature request / issue for it in github if you like.

Yes I saw that branch and it is nice but cannot be code-generated and used for embedded applications. I created a CMEX version myself using OSQP codegen which can be used but it is limited and does not have all the design capabilities and formulations of MathWorks MPC tools.

Yes all needed is a function [x,status] = mpcCustomSolverCodeGen(H,f,A,b,x0) script which calls OSQP MEX file at each sample time. However, the main challenge is the setup->update->solve structure of the solver and I am not really sure how we can trick this without touching MPC tools.

Sure, I will create a feature request in github. That would be really nice if you can implement an interface to use OSQP as a custom solver for MPC tools.

On a separate note: with OSQP codegen feature you cannot solve a problem in which there are sparsity changes, right? because you create the workspace only once and then update the matrices/vectors as needed (assuming sparsity does not change) afterwards but you cannot set up the problem again during simulations.

This branch should have better Simulink codegen support than the original one Paul linked above: GitHub - imciner2/osqp-matlab at im/simulink_cg. I have used that branch to generate solvers inside the OpalRT system (which does code generation from Simulink to their devices).

Sure, I will create a feature request in github. That would be really nice if you can implement an interface to use OSQP as a custom solver for MPC tools.

Please open an issue requesting this, and if you can, it would be great to link to the documentation/spec that describes how to interface with these tools - because I don’t think I have seen any documentation describing it yet.

On a separate note: with OSQP codegen feature you cannot solve a problem in which there are sparsity changes, right? because you create the workspace only once and then update the matrices/vectors as needed (assuming sparsity is not changed) afterwards but you cannot set up the problem again during simulations.

Correct, for the code generation the sparsity pattern is set by the matrices in the problem when it is created. If you know exactly what all the sparsity patterns look like you could pass in a matrix that is simply the union of all of them (e.g. put values at all possible non-zero entries) and then online you just set the unneeded entries to 0 when you update it.

Yes all needed is a function [x,status] = mpcCustomSolverCodeGen(H,f,A,b,x0) script which calls OSQP MEX file at each sample time. However, the main challenge is the setup->update->solve structure of the solver and I am not really sure how we can trick this without touching MPC tools.

I think that this could be managed via a persistent variable internal to the OSQP interface wrapper. On the first call the persistent variable would be empty and you would initialize the solver, taking a copy of all the initialization data. On subsequent calls you could check whether H or A have changed. If not, you could just update the linear terms in OSQP and solve again without reinitializing.

Note that if Matlab keeps passing the exact same H and A between calls then the above would be very fast because the internally stored versions would be lazy copies and there is nothing to check. If Matlab rebuilds H and A at every step (which would be a poor choice) then it will still work, but at the cost of an elementwise check for equality.

Fancier things could be done if H or A change entries but not sparsity, etc.

Note that none of this will work well if you have multiple MPC instances running in parallel unless you get very fancy.

I just checked this branch and it seems you cannot simply generate code using embedded coder, right? I am getting some errors.

I opened an issue in github and provided some links/documents which explains the process.

So, if you have an adaptive MPC for example you cannot use OSQP codegen? since the sparsity may change and it is not necessarily known pattern in advance.

Yes, that makes sense and will require some works

If you have an MPC problem in which :

  1. The P or A matrices change, but
  2. the sparsity doesn’t change

then you can use OSQP as an embedded solver still. We provide update methods that allow you to modify the entries of these matrices. In this case the KKT matrix will not change its sparsity pattern, so although it needs to be refactored it does not require memory reallocation.

If P and A do change, then you can still do the following:

  1. Work out all of the entries that could conceivably be nonzero at any point (hopefully it’s not all of them).
  2. Do setup using (P,A) with all potential non-zeros flagged in the input data as logical non-zeros (even if they are numerically zero).

Then everything will work still. It is only a good idea to do this if there is some kind of overally sparsity to your problem, i.e. many of the entries of P and A remain zero throughout.

Thanks Paul! The first case (sparsity does not change) is straightforward and can be readily handled using different “update” methods of the solver.

The 2nd scenario is still not clear to me. My understanding was when sparsity changes, we should set up the problem again at that specific time which is not doable with the OSQP codegen version for embedded applications since the workspace is created only once. Am I right or no we can still update the problem with some manipulations without a need to reinitialize? Is there an example for this?

During the problem setup OSQP takes as input the sparse matrices A and P. The compressed sparse column format used for these matrices has four parts:

  1. A row index showing the row number of each entry.
  2. A column index with n+1 entries, indicating the index of first element that appears in each column.
  3. A vector of values for the entries.
  4. the row and column dimension of the matrix

The first two parts define the logical pattern of non-zeros, and only this pattern is used to determine the amount of memory that is required in the solver. There is nothing to prevent you from setting one of those values numerically to zero though.

For example, you might define a 2x2 matrix A to have the sparsity pattern:
[X X
X 0]

during setup. It doesn’t matter what value you place in each ‘X’, what matters is that they are included in the index of (possible) nonzeros. Later on you would then be free to assign A values like:
[1 2
3 0]

or

[0 0
3 0]

but not

[1 2
3 4]