Install
snn_opt is on PyPI. Wheels ship for Linux (x86_64, aarch64), macOS (Apple Silicon), and Windows across CPython 3.9–3.13, with a compiled C++ kernel pre-built. No compiler needed on your side.
pip install snn-opt # core
pip install "snn-opt[examples]" # also matplotlib, for the figures
The PyPI distribution name is
snn-opt(hyphenated, lowercase, per PEP 503); the Python import name issnn_opt. So youpip install snn-optand thenimport snn_opt.
Only NumPy and SciPy are required at runtime.
Developer install
For a checkout that lets you edit the source:
git clone https://github.com/ahkhan03/SNN_opt.git
cd SNN_opt
pip install -e . # editable
pip install -e ".[examples]" # + matplotlib for the figures
For a specific commit (reproducibility for papers):
pip install "git+https://github.com/ahkhan03/SNN_opt.git@<commit-sha>"
Your first QP
We’ll solve the smallest QP that does anything interesting:
Without the constraint, the optimum is the origin and the trajectory is uneventful. The constraint cuts off a half-plane, and the solver has to figure out that the optimum sits on the line.
import numpy as np
from snn_opt import solve_qp
# Hessian, linear cost, constraint matrix, constraint offset.
A = np.eye(2)
b = np.zeros(2)
C = np.array([[1.0, 2.0]])
d = np.array([-1.0])
x0 = np.array([1.0, 1.0]) # starts feasible
result = solve_qp(A, b, C, d, x0, max_iterations=1000)
print(result.summary())
print("x* =", result.final_x)
print("f* =", result.final_objective)
Run it. You should see an answer very close to with f* ≈ 0, plus a one-line-per-stat summary including the iteration count and the number of projection events.
Compiled C++ backend
The PyPI wheels include a compiled kernel (snn_opt._kernel, built via pybind11) that accelerates the inner adaptive-projection loop by roughly an order of magnitude. Opt in with the backend keyword:
result = solve_qp(A, b, C, d, x0, backend='c') # compiled kernel
result = solve_qp(A, b, C, d, x0, backend='python') # reference (default)
Both backends are kept in lockstep by a parity test suite. The C kernel supports dense problems with projection_method='adaptive'; sparse inputs and other projection methods are rejected with a clear error rather than silently falling back, so stick with backend='python' for those. Convergence is decided by the same host-side certificate on every backend, so converged means one thing everywhere. The same kernel source is HLS-compatible, and the repository ships a physically qualified Kria KV260 FPGA reference under fpga/.
What the result tells you
SolverResult is a dataclass with everything the solver knows about the run, not just the final answer:
result.final_x # the solution
result.final_objective # f(x*)
result.converged # bool
result.convergence_reason # which criterion fired
result.iterations_used # how many Euler steps
result.n_projections # how many constraint corrections
result.t, result.X # full trajectory: time, state
result.objective_values # f(x_t) per iteration
result.constraint_violations # max violation per iteration
result.spike_times # iterations at which a spike fired
result.spike_constraints # which constraint(s) fired each time
result.spike_norms # how big each spike was
The last four fields are the raw material for the spike raster you’ll see throughout this site; they’re how you learn what your solver is doing.
Checking the answer
Since v0.6.0, converged=True is a scale-invariant KKT certificate: it means the final point passed a KKT-cone test relative to the problem’s own gradient scale, together with joint feasibility and a plateau check, at three consecutive checkpoints. Three fields carry the detail:
result.joint_feasible # rows AND bounds feasible together
result.kkt_residual # KKT certificate residual
result.projection_budget_exhausted
joint_feasibleis the honest feasibility flag. Before v0.5.0 the convergence test looked at the rows ofConly, so a bound violation could not fail it.kkt_residualis the certificate itself: a nonnegative least-squares fit of the gradient onto the cone of facet normals, with a complementarity guard so slack facets cannot absorb the gradient. It is accepted relative to the problem’s own gradient scale (kkt_scale), so the decision is invariant under objective rescaling, constraint row order, and per-row scaling; readkkt_residual / kkt_scaleas the cross-problem comparable defect. The pre-0.6 test compared an absolute projected-gradient norm against1e-6and could never fire on large-scale problems; if you need it, it survives asoptimality_test="legacy_projected_gradient".projection_budget_exhaustedmeans the inner projection sweep hit its watchdog. The solve aborts rather than returning a knowingly infeasible point as a success.
If converged stays False on a problem you believe in, the certificate may simply be measuring the accuracy floor of the default step size honestly. Lower k0_scale and raise max_iterations together to shrink the floor, or loosen kkt_rel_tol if the default 1e-4 is tighter than you need. The accuracy figure on the home page shows why the two knobs have to move together.
Next
- An SVM is a QP: turn a real machine-learning problem into something
solve_qpcan take. - Reading the spike raster: interpret the spike-time outputs above as a diagnostic plot.
- See
examples/in the repo for nine more worked QPs: 3-D polytopes, infeasible recovery, equality constraints, warm starting, and a full SVM.