Unexpected behavior of update()?

Hi OSQP team,

We are using the Python OSQP package to solve convex quadratic problems sequentially. The P matrix is constant through all these iterations, while constraints matrix A and vectors q, l and u depend on some time dependent information, hence need to be modified at each iteration.

In our first implementation, at each iteration we do something like:

P = compute_P()
for t in timesteps:
    A, q, l, u = compute_A_q_l_u()
    solver = osqp.OSQP()
    solver.setup(P=P, q=q, A=A, l=l, u=u)
    solution = solver.solve()

It yields the expected solutions.

For performance purposes, we are trying a second implementation where we take advantage of the update function to efficiently update the quadratic problem and resolve it. At each iteration we do something like:

P = compute_P()
solver = osqp.OSQP()
first_use = True
for t in timesteps:
    A, q, l, u = compute_A_q_l_u()
    if first_use:
        solver.setup(P=P, q=q, A=A, l=l, u=u)
        first_use = False
    else:
        solver.update(q=q, l=l, u=u)
        solver.update(Ax=A.data)
    solution = solver.solve()

However this sometimes yield inaccurate solves (not on the first use at t=0), which never happens when using implementation 1.

I have uploaded here a minimal reproducible example to exhibit the problem we are facing. At this point, we are struggling to figure out whether we are doing something wrong and misusing the update function or if this could be due to a bug in the OSQP library.

OSQP package version:

osqp 0.6.2.post0 py37he8f5f7f_3 conda-forge

Your feedback would be much appreciated !

Best regards and happy holidays,
Quentin Vanderlinden

I think the problem is that your A matrices are of type ndarray, rather than in compressed sparse column format. When you call setup on the first use it is likely that the osqp python wrapper is converting the matrices to CSC format for you (NB:I didn’t check to confirm).

When you call the update function it’s not clear what should happen, since you are just providing an ndarray rather than an array of the nonzero values only in A. The update function wants you to provide an array containing only those entries that are (structurally) non-zero, and those non-zeros should be in the same place as when you called setup.

Good morning Paul,

Thanks for your reply. In the simplified snippets I shared in my post it doesn’t show it explicitly but if you have a look at the MWE I shared, you will see that we do use csc_matrix for A. Following your message though, I investigated a bit more the sparsity structures of A_0 and A_1 and they are very slightly different. The number of non-zero elements for A_0 and A_1 are 1004 and 1002, respectively. Consequently, the call to update() is misused, I see what’s wrong now.

Again, thank you for your fast reply.

Best regards & happy holidays,
Quentin Vanderlinden

OK, I am glad it worked out. Note that OSQP allows some of the entries in the CSC matrix to be structural zeros, i.e. flagged as a nonzero entry but with numerical value 0.0. This is useful, for example, when updating data as you have done here and you want to reserve the option of setting some values to zero or not - just mark them as structural zeros on initialization.

This can be a problem though when automatically converting from other formats in python (or some other language), since the zero values tend to get squeezed out completely.

thanks for the awesome information.