DifferentiableOSQP

Documentation for DifferentiableOSQP.

    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.

    Interface 1: Solve a QP

       x = solve(P, q, A, u; kwargs...)

    Solves the quadratic program:

    \[\begin{aligned} \min_x &\quad \frac{1}{2} x^T P x + q^T x \\ s.t. &\quad A x \leq u \end{aligned}\]

    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.

    Interface 2: Solve a Parameteric QP

      x = solve(θ, P_fn, q_fn, A_fn, u_fn; kwargs)

    Solves the quadratic program:

    \[\begin{aligned} \min_x &\quad \frac{1}{2} x^T P(\theta) x + q(\theta)^T x \\ s.t. &\quad A(\theta) x \leq u(\theta) \end{aligned}\]

    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.

    Interface 3: Jacobians of a QP

    Thinking about interface 2, notice that a QP solver is essentially a function

    \[QP : \mathbb{R}^p \to \mathbb{R}^n\\ x = QP(θ)\]

    Therefore, the Jacobian of the QP

    \[J = \frac{\partial x}{\partial \theta}\]

    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

    Interaface 4: ForwardDiff

    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)