This package provides a thin wrapper of OSQP.jl, but also provides the ability to differentiate through the quadratic progam, based on the equations in OptNet.
The package exports 2 commands: solve
and solve_and_jac
.
Activate your environment and simply add DifferentialOSQP
:
] add https://github.com/dev10110/DifferentiableOSQP.jl
x = solve(P, q, A, u; kwargs...)
Solves the quadratic program:
where kwargs
are keyword arguments passed into OSQP.setup!
To include equality constraints, for example G x = h
, modify A, u
matrices as:
A = [A ; G ; -G]
u = [u ; h; -h]
This introduces the constraints G x \leq h
and G x \geq h
, allowing equalities to be handled. Yes, this is rather inefficient, but the easiest way to solve the problem I think.
x = solve(θ, P_fn, q_fn, A_fn, u_fn; kwargs)
Solves the quadratic program:
i.e., assumes the P_fn, q_fn, A_fn, u_fn
are functions of θ
. Note, the shape and size of each output must be correct - for example, A_fn(θ)
must return a matrix, and q_fn(θ)
must return a vector.
Thinking about interface 2, notice that a QP solver is essentially a function
Therefore, the Jacobian of the QP
If we want the jacobian of QP
, we can call it as the following:
x, J = solve_and_jac(θ, P_fn, q_fn, A_fn, u_fn; kwargs)
which gives the optimal solution x
, and the jacobian J
For convenience, we overloaded solve
to handle Dual numbers. This means we can directly use ForwardDiff.jacobian
, as in the following example:
J = ForwardDiff.jacobian(θ -> solve(θ, P_fn, q_fn, A_fn, u_fn), θ0)
or more explictly
function parameteric_qp(θ)
return solve(θ, P_fn, q_fn, A_fn, u_fn)
end
J = ForwardDiff.jacobian(parametric_qp, θ0)
Naturally, not all QPs are differentiable. This library will always return a derivative, but doesnt check/warn if the derivative doesnt exist. I want to add this functionality in the future. See this or this paper for some results on existence of derivatives/Lipschitz continuity of QPs.