Native Solver Plugin System
GAT uses a unique subprocess-based plugin architecture for native solvers. This design provides crash isolation, version flexibility, and seamless fallback to pure-Rust implementations when native solvers aren't available.
Architecture Overview
┌─────────────────────────────────────────────────────────────────┐
│ GAT CLI │
│ (gat opf ac grid.arrow) │
└──────────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Solver Dispatcher │
│ • Detects problem type (LP, SOCP, NLP, MIP) │
│ • Checks installed native solvers │
│ • Falls back to pure-Rust if needed │
└──────────────────────────┬──────────────────────────────────────┘
│
┌───────────────┴───────────────┐
│ │
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│ Pure-Rust Path │ │ Native Solver │
│ │ │ Subprocess │
│ • L-BFGS (AC-OPF) │ │ │
│ • Clarabel (SOCP) │ │ ┌───────────────┐ │
│ • Always available │ │ │ IPOPT/HiGHS │ │
│ │ │ │ CBC/CLP │ │
└─────────────────────┘ │ └───────────────┘ │
│ │ │
│ Arrow IPC │
│ │ │
└─────────┴──────────┘
Key Design Principles
1. Crash Isolation
Native libraries (especially C/C++ solvers like IPOPT) can crash due to numerical issues, memory corruption, or library bugs. By running them in a separate process:
- Main process stays alive: A solver crash doesn't take down the CLI
- Clean error recovery: The dispatcher can retry with a different solver
- Resource cleanup: OS handles memory/file cleanup on subprocess exit
2. Version Flexibility
Different users may need different solver versions:
# Install specific IPOPT version
# Multiple versions can coexist
3. Arrow IPC Protocol
All solver communication uses Apache Arrow IPC:
┌────────────────────┐ Arrow IPC ┌────────────────────┐
│ GAT Process │ ──────────────► │ Solver Process │
│ │ │ │
│ Problem data: │ │ Receives: │
│ • Bounds │ Multi-batch │ • Structured data │
│ • Constraints │ streaming │ • Type-safe │
│ • Objectives │ │ • Zero-copy │
│ │ ◄────────────── │ │
│ Solution: │ │ Returns: │
│ • Primal vars │ │ • Status code │
│ • Dual vars │ │ • Iteration count │
│ • Status │ │ • Solve time │
└────────────────────┘ └────────────────────┘
Benefits of Arrow IPC:
- Type safety: Schema validation catches bugs early
- Efficient: Zero-copy reads where possible
- Language-agnostic: Works with C, C++, Python solvers
Available Backends
| Backend | Type | Problem Classes | Status |
|---|---|---|---|
| L-BFGS | Pure Rust | NLP | Always available |
| Clarabel | Pure Rust | LP, SOCP | Always available |
| IPOPT | Native (C++) | NLP | Optional |
| HiGHS | Native (C++) | LP, MIP | Optional |
| CBC | Native (C) | MIP | Optional |
| CLP | Native (C) | LP | Optional |
Automatic Selection
The dispatcher automatically selects the best available solver:
// Problem classification
match problem_type
Installing Native Solvers
GAT supports two methods for installing native solvers: system packages (quickest) or vendored builds (fully offline, reproducible).
Method 1: System Packages (Quick)
# Ubuntu/Debian - IPOPT
# Ubuntu/Debian - CBC/CLP
# macOS
Method 2: Vendored Build (Offline, Reproducible)
GAT includes vendored sources for the complete COIN-OR solver stack. This enables fully offline builds with reproducible results.
Prerequisites:
# Ubuntu/Debian
# macOS
Build from vendored sources:
# LP/SOCP solver stack: CoinUtils → Osi → Clp
# MIP solver stack: Cgl → Cbc (requires CLP)
# NLP solver stack: Metis → MUMPS → IPOPT
What gets built:
| Stack | Script | Components | Output Libraries |
|---|---|---|---|
| LP | build-clp.sh | CoinUtils, Osi, Clp | libCoinUtils.a, libOsi.a, libClp.a |
| MIP | build-cbc.sh | Cgl, Cbc | libCgl.a, libCbc.a |
| NLP | build-ipopt.sh | Metis 4.0, MUMPS 5.8, IPOPT 3.14 | libcoinmetis.a, libcoinmumps.a, libipopt.so |
All libraries install to vendor/local/. MUMPS is built with OpenMP for parallel factorization; Metis provides graph-based ordering for better fill-in reduction.
Use with Cargo:
# Set paths and build
# Or use the wrapper script
IPOPT (Recommended for AC-OPF)
IPOPT provides the fastest and most accurate AC-OPF solutions:
# From vendored sources (recommended for CI/reproducibility)
# Or system package (quick setup)
CBC/CLP (Recommended for LP/MIP)
CBC provides branch-and-cut MIP solving; CLP provides simplex LP:
# From vendored sources
# Or system package
Managing Solvers
# List installed solvers
# Uninstall a solver
# Check solver health
Implementation Details
Subprocess Communication
Each native solver wrapper implements the SolverProcess trait:
Error Handling
The dispatcher handles solver failures gracefully:
match native_solver.solve
Build System Integration
Vendored Build Chain:
The shell scripts in scripts/ handle the native solver build:
- Extract vendored source archives from
vendor/ - Apply COIN-OR patches for compatibility
- Configure with proper dependency ordering
- Build with parallel make (
-j$(nproc)) - Install static libraries to
vendor/local/ - Generate pkg-config files for Cargo build.rs detection
Cargo Build Integration:
Each native solver crate (gat-cbc, gat-clp, gat-ipopt) has a build.rs that:
- Tries system pkg-config first (fastest)
- Falls back to
vendor/local/pre-built libraries - Can build from source as last resort (slowest)
// Priority order in gat-cbc/build.rs
CI Integration:
The .github/workflows/native-solvers.yml workflow:
- Caches
vendor/local/between builds - Runs vendored build scripts if cache misses
- Tests all solver features with proper
LD_LIBRARY_PATH
Performance Characteristics
| Solver | Startup | Memory | Best For |
|---|---|---|---|
| L-BFGS | < 1ms | Low | Small NLP |
| Clarabel | < 1ms | Low | SOCP |
| IPOPT | ~50ms | Medium | Large NLP |
| HiGHS | ~10ms | Medium | LP/MIP |
Note: Subprocess startup overhead (~50-100ms) is amortized over solve time. For large problems (> 1000 buses), IPOPT's superior convergence more than compensates.
Troubleshooting
"Solver not found"
# Check if solver is installed
# Check system dependencies
# Rebuild solver
"Arrow IPC error"
Usually indicates version mismatch:
# Rebuild with current GAT version
"Solver crashed"
Check solver logs:
# Enable verbose logging
GAT_LOG=debug
# Check solver-specific logs
Related Documentation
- OPF Guide — Choosing the right solver tier
- Benchmarks — Complete PGLib validation results
- Building from Source — Compiling with native solver support