Skip to content

Solve a QP

The simplest thing qpax does: take the data of a convex quadratic program and return its primal solution along with convergence diagnostics. Reach for this recipe when you have a single QP and just need the answer.

The problem

The example builds a random feasible convex QP with \(n=3\) variables, \(m=1\) equality constraint and \(p=2\) inequality constraints, then solves it:

\[ \begin{aligned} \min_{x}\;& \tfrac{1}{2}\, x^{\mathsf T} Q x + q^{\mathsf T} x \\ \text{s.t.}\;& A x = b \\ & G x \le h. \end{aligned} \]

qpax.solve_qp returns the tuple (x, s, z, y, converged, iters): the primal x, the inequality slack s and dual z, the equality dual y, a convergence flag, and the iteration count.

Code

"""Solve one random convex QP with qpax.

The problem is

    minimize_x 0.5 x^T Q x + q^T x
    subject to A x = b
               G x <= h
"""

import jax
import jax.numpy as jnp
import numpy as np

import qpax

USE_F64 = False
if USE_F64:
    jax.config.update("jax_enable_x64", True)

dtype = jnp.float64 if USE_F64 else jnp.float32


def generate_random_qp(n, m, p, dtype):
    rng = np.random.default_rng(0)

    M = rng.normal(size=(n, n))
    Q = M.T @ M + 1e-2 * np.eye(n)
    q = rng.normal(size=n)

    A = rng.normal(size=(m, n))
    x_ref = rng.normal(size=n)
    b = A @ x_ref

    G = rng.normal(size=(p, n))
    h = G @ x_ref + rng.uniform(0.5, 1.5, size=p)

    return (jnp.asarray(arr, dtype=dtype) for arr in (Q, q, A, b, G, h))


# Build one random feasible QP.
Q, q, A, b, G, h = generate_random_qp(n=3, m=1, p=2, dtype=dtype)

# Solve it and keep the convergence diagnostics.
x, _, _, _, converged, iters = qpax.solve_qp(Q, q, A, b, G, h, verbose=True)

print("converged:", int(converged))
print("iterations:", int(iters))
print("x:", x)