Skip to content

EdwardOptimization/MiniSolver

Repository files navigation

MiniSolver: High-Performance NMPC Library

C++17 License Platform Zero-Malloc

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.


πŸš€ Key Features

⚑ Blazing Fast Performance

  • 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$).

πŸ›‘οΈ Real-Time Robustness

  • Zero-Malloc Solve Path: The default solver configuration performs no new/malloc calls during solve(). 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.

πŸ”§ Advanced Solver Capabilities

  • 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.

πŸ“Š Performance Benchmarks

Historical local benchmark snapshot on an Intel Core i7 (single thread) for a Kinematic Bicycle Model with Obstacle Avoidance ($N_x=6, N_u=2, N=50$). For release claims or solver-to-solver comparisons, use the dated reports in the benchmark repository with the exact MiniSolver commit, command line, backend, and hardware recorded.

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).


πŸ› οΈ Quick Start

Prerequisites

  • 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)

1. Define Your Model (Python)

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")

2. Solve in C++

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);
}

3. Build & Run

MiniSolver includes a one-click build script that handles dependency checking, code generation, and compilation.

./build.sh

πŸ“¦ Embedded-Oriented Deployment Notes

  1. Generate your model headers on a development machine: python3 generate_model.py.
  2. Copy the minisolver/ include folder and your generated headers into the target project.
  3. Define USE_CUSTOM_MATRIX to remove the Eigen dependency when the built-in fixed-size matrix backend is appropriate.
  4. Keep profiling, iteration logging, snapshots, and code generation out of hard real-time loops.
  5. 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.

πŸ“‚ Project Structure

  • 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 main MiniSolver class orchestrating the IPM loop.
  • include/minisolver/algorithms/: Numerical engines:
    • RiccatiSolver: Block-tridiagonal linear system solver.
    • LineSearch: Filter and Merit function strategies.
  • python/minisolver/: The MiniModel DSL 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.

🧭 Development Workflow

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.

🀝 License & Citation

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.

About

Fast fixed-size nonlinear MPC solver with Python/SymPy codegen, primal-dual IPM, Riccati kernels, and zero-malloc C++ solve paths.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors