MiniSolver is a professional-grade, header-only C++17 library for solving Nonlinear Model Predictive Control (NMPC) problems.
Designed for real-time robotics and autonomous-driving research, it combines the flexibility of Python-based symbolic modeling with the raw performance of highly optimized, template-generated C++ code. The default real-time configuration is designed for Zero Dynamic Memory Allocation (Zero-Malloc) during the solve phase; profiling and iteration logging are opt-in diagnostics and should stay disabled in hard real-time loops.
-
Riccati Recursion: Utilizes a specialized block-tridiagonal linear solver (
$O(N)$ complexity) tailored for optimal control structures. - SQP-RTI Support: Real-Time Iteration (SQP-RTI) mode allows for >1 kHz control loops by performing a single quadratic programming sub-step per control tick.
- Analytical Derivatives: Uses SymPy to generate flattened, algebraically simplified C++ code for Jacobians and Hessians at compile-time, including true Gauss-Newton Hessians for explicit least-squares residuals.
-
π₯ Fused Riccati Kernels: Unlike solvers that use generic matrix libraries, MiniSolver uses Python (SymPy) to symbolically fuse the Riccati backward pass (
Q + A'PA) into a single, flattened C++ function. This eliminates all loop overhead and explicitly bypasses multiplication by zero, achieving perfect sparsity exploitation for small-to-medium systems ($N_x < 20$ ).
- Zero-Malloc Solve Path: The default solver configuration performs no
new/malloccalls duringsolve(). Keep profiling and iteration logging disabled for hard real-time use. - Robust Interior Point Method (IPM):
- Mehrotra Predictor-Corrector: Drastically reduces iteration counts by utilizing higher-order corrections.
- Filter Line Search: Ensures global convergence without the tedious tuning of merit function parameters.
- Feasibility Restoration: Automatically triggers a minimum-norm recovery phase if the solver encounters infeasible warm starts.
- Second Order Correction (SOC): Handles highly nonlinear constraints (e.g., tight obstacle avoidance) by curving the search step.
- Native Soft Constraints: Supports L1 (Exact) and L2 (Quadratic) soft constraints via Dual Regularization, allowing for relaxation without increasing the problem dimensions (slack variables are handled implicitly).
- Defect Rollout Refinement: Optional correction pass for linearized dynamics defects after the Riccati solve. Configure it with
direction_refinement = DirectionRefinementMode::DYNAMICS_DEFECT_ROLLOUT; it is not a full KKT iterative-refinement method.
Historical local benchmark snapshot on an Intel Core i7 (single thread) for a
Kinematic Bicycle Model with Obstacle Avoidance (
| Strategy | Integrator | Line Search | Avg Time | Iterations | Status |
|---|---|---|---|---|---|
| TURBO | Euler | Filter | ~0.8 ms | 10-15 | Approx |
| ROBUST (Rec) | RK4 | Filter | ~2.4 ms | 23 | Optimal |
| STABLE | RK4 | Merit | ~3.0 ms | 46 | Optimal |
| ADAPTIVE | RK4 | Filter | ~25.0 ms | 300* | Feasible |
> Adaptive strategy may stagnate on feasible but high-cost solutions in scenarios with bad initial guesses (e.g., straight-line initialization).
- CMake >= 3.10
- C++17 Compiler (GCC/Clang)
- Python 3 + SymPy (
pip install sympy) - (Optional) Eigen3 (Default backend, can be swapped for built-in
MiniMatrix)
Define your Optimal Control Problem (OCP) using the Python DSL. This generates the optimized C++ headers.
# my_model.py
from minisolver.MiniModel import Dot, OptimalControlModel
import sympy as sp
model = OptimalControlModel(name="DroneModel")
# 1. Define Variables
px, py, vx, vy, vz = model.state("px", "py", "vx", "vy", "vz")
thrust = model.control("thrust")
# 2. Dynamics (f(x,u))
model.subject_to(Dot(px) == vx)
model.subject_to(Dot(py) == vy)
model.subject_to(Dot(vz) == thrust - 9.81)
# 3. Objective
model.add_residual(px - 0.0, weight=20.0) # 0.5 * 20 * (px - 0)^2
model.add_residual(thrust, weight=0.2) # 0.5 * 0.2 * thrust^2
# 4. Constraints
model.subject_to( thrust <= 20.0 ) # Hard Constraint
# Soft Constraint (L1 Penalty via Dual Regularization)
model.subject_to( vz <= 5.0, weight=100.0, loss='L1' )
# 5. Generate C++ Code
model.generate("include/model")Include the generated header and the solver.
#include "model/drone_model.h"
#include "minisolver/solver/solver.h"
using namespace minisolver;
int main() {
// Instantiate solver with Max Horizon N=100
MiniSolver<DroneModel, 100> solver(50, Backend::CPU_SERIAL);
// Set Initial Condition
solver.set_initial_state("px", -10.0);
// Configure for Robustness
SolverConfig config = solver.get_config();
config.barrier_strategy = BarrierStrategy::MEHROTRA;
config.enable_soc = true;
solver.set_config(config);
// Solve
SolverStatus status = solver.solve();
// Retrieve Optimal Control
std::vector<double> u_opt = solver.get_control(0);
}MiniSolver includes a one-click build script that handles dependency checking, code generation, and compilation.
./build.sh- Generate your model headers on a development machine:
python3 generate_model.py. - Copy the
minisolver/include folder and your generated headers into the target project. - Define
USE_CUSTOM_MATRIXto remove the Eigen dependency when the built-in fixed-size matrix backend is appropriate. - Keep profiling, iteration logging, snapshots, and code generation out of hard real-time loops.
- Validate the target toolchain and runtime profile. MiniSolver is embedded-oriented, but it is not yet documented as a freestanding C++ or no-exceptions embedded profile.
include/minisolver/core/: Core types (KnotPoint), memory-safe containers (Trajectory), and solver configuration/state.include/minisolver/matrix/: Fixed-size matrix abstraction layer (MiniMatrix/Eigen).include/minisolver/solver/: The mainMiniSolverclass orchestrating the IPM loop.include/minisolver/algorithms/: Numerical engines:RiccatiSolver: Block-tridiagonal linear system solver.LineSearch: Filter and Merit function strategies.
python/minisolver/: TheMiniModelDSL and C++ code generator.examples/: Runnable generated-model examples, including the car tutorial and advanced bicycle case.tools/:auto_tuner.cpp: Monte-Carlo search for optimal solver configurations.replay_solver.cpp: Debugging tool to replay solver snapshot states.benchmark_suite/: MiniSolver configuration benchmark suite.
docs/: Design notes, ADRs, review ledgers, and testing plans.
MiniSolver is developed with a multi-agent engineering workflow. AI coding and review agents are used for implementation, independent review, benchmark investigation, debugging, and documentation research. Final architecture decisions, validation criteria, and releases are maintained by Edward Qu.
Non-trivial solver changes are expected to be evidence-driven: reproduce the issue, add a focused test or benchmark, make the smallest defensible change, and record validation results before trusting the change.
MiniSolver is licensed under the Apache 2.0 License.
If you use this software in academic work, please refer to CITATION.cff.
Maintained by Edward Qu.