
Janus is a C++20 header-only numerical framework that implements a dual-mode code-transformations paradigm inspired by AeroSandbox. You write physics models once using generic templates; the same code compiles to optimized native arithmetic (Eigen) for simulation and builds a symbolic computational graph (CasADi) for automatic differentiation and gradient-based optimization. No code duplication, no runtime dispatch overhead.
Building & Development
Prerequisites
Recommended: Nix with flakes enabled – the flake.nix pins every dependency.
Manual: CMake 3.20+, a C++20 compiler (Clang recommended), Eigen 3.4+, CasADi, GoogleTest, Ninja.
Dev environment
# Enter the Nix dev shell (sets up compiler, Eigen, CasADi, gtest, ccache, doxygen, lcov, etc.)
nix develop
# Or use the convenience wrapper
./scripts/dev.sh
Build, test, run
./scripts/build.sh # CMake + Ninja debug build
./scripts/build.sh --release # Release build
./scripts/test.sh # Build (if needed) + ctest
./scripts/coverage.sh # Build with coverage, run tests, generate lcov report
./scripts/run_examples.sh # Build and run all 30 example programs
./scripts/verify.sh # Full pre-push check: build + test + examples
Formatting is enforced via treefmt (clang-format, cmake-format, nixfmt):
nix fmt # Format everything
./scripts/install-hooks.sh # Install pre-commit formatting hook
Project Structure
janus/
├── docs/ # Documentation
│ ├── user_guides/ # 18 topic guides (see Documentation below)
│ ├── patterns/ # Reusable patterns (branching, loops, hybrid optimization)
│ └── design_overview.md # Architecture deep-dive
├── examples/ # 30 runnable demos
│ ├── intro/ # Getting started (numeric, energy, sparsity, printing)
│ ├── math/ # Branching, graphs, loops, sensitivity, PCE, structural analysis
│ ├── interpolation/ # N-D tables, scattered interpolation, root finding, file-based tables
│ ├── simulation/ # ODE integration, drag, brachistochrone, hybrid, attitudes
│ └── optimization/ # Rosenbrock, drag, beam, brachistochrone, sweeps, transcription comparison
├── include/
│ └── janus/
│ ├── core/ # Type system, concepts, diagnostics, sparsity, structural analysis, IO
│ ├── math/ # Trig, calculus, autodiff, interpolation, integration, root finding,
│ │ # quadrature, PCE, linalg, quaternions, rotations, surrogates
│ ├── optimization/ # Opti interface, scaling, collocation, multiple shooting,
│ │ # pseudospectral, Birkhoff pseudospectral
│ ├── utils/ # Utility helpers (JSON)
│ ├── janus.hpp # Main umbrella include
│ └── using.hpp # Convenience aliases/imports
├── scripts/ # Build, test, CI, coverage, formatting, doc generation
├── tests/ # GoogleTest suites (core/, math/, optimization/)
├── CMakeLists.txt
└── flake.nix # Nix dev environment and package definition
Architecture Overview
Janus is built on a template-first traceability paradigm. User models are templated on a generic scalar type; the framework provides two backends that satisfy the same concept constraints. The numeric backend maps to double and Eigen::MatrixXd – the compiler generates assembly identical to hand-written C++. The symbolic backend maps to casadi::MX and Eigen::Matrix<casadi::MX> – the same code constructs a static computational graph suitable for automatic differentiation, sparsity detection, and NLP solvers.
A dispatch layer in the janus:: namespace shadows std:: math functions and uses C++20 concepts to route calls (janus::sin, janus::pow, etc.) to the correct backend at compile time. The framework draws a strict line between structural logic (integers, booleans, loop bounds – these shape the graph) and value logic (floating-point quantities that flow through the graph). Standard if/else cannot branch on symbolic values; janus::where(condition, true_val, false_val) compiles to a ternary in numeric mode and a casadi::if_else switch node in symbolic mode.
For the full design rationale, see docs/design_overview.md.
Documentation
Organization
- docs/user_guides/ – 18 standalone guides, each covering one subsystem end-to-end.
- docs/patterns/ – Reusable coding patterns (branching, loops, hybrid optimization).
- docs/design_overview.md – Architecture principles and type system.
- Doxygen API docs – Generated from source comments.
Generating docs
./scripts/generate_docs.sh # Or: doxygen Doxyfile
Hosted on GitHub Pages.
User guides
| Guide | Description |
| numeric_computing | Numeric backend basics – templates, Eigen, optimized machine code |
| symbolic_computing | Symbolic backend – CasADi graph construction, derivatives, code generation |
| math_functions | Dispatch layer and ADL for dual-mode math (janus::sin, janus::pow, etc.) |
| interpolation | N-dimensional gridded interpolation in numeric and symbolic modes |
| integration | ODE solvers – RK4, RK45, Stormer-Verlet, mass-matrix, second-order systems |
| root_finding | Newton-Raphson and bracketing solvers with globalization |
| optimization | janus::Opti interface for constrained NLP (IPOPT/SNOPT/QPOASES) |
| collocation | Direct collocation transcription for optimal control |
| multiple_shooting | Multiple shooting transcription via CasADi integrators (CVODES/IDAS) |
| pseudospectral | Global polynomial pseudospectral transcription |
| birkhoff_pseudospectral | Birkhoff-form pseudospectral with derivative collocation |
| transcription_methods | Comparison and selection guide for all transcription methods |
| graph_visualization | Visualizing computational graphs for debugging |
| polynomial_chaos | Polynomial chaos expansion for uncertainty quantification |
| stochastic_quadrature | Probability-measure quadrature rules for PCE workflows |
| sparsity | Jacobian/Hessian sparsity detection and exploitation |
| structural_transforms | Alias elimination and BLT decomposition for residual systems |
| structural_diagnostics | Structural observability analysis for state estimation |
Features
- 🎭 Dual-Mode Physics: Write once, run as Numeric (Fast C++) or Symbolic (CasADi Graph).
- 🔢 Unified Math: Std/CasADi agnostic math functions (janus::sin, janus::pow, janus::where).
- ⚡ Linear Algebra: Eigen-based matrix operations compatible with symbolic types, policy-driven linear solvers.
- 📉 Optimization: High-level Opti interface for NLP solvers (IPOPT/SNOPT/QPOASES) with Direct Collocation, Multiple Shooting, Pseudospectral, and Birkhoff Pseudospectral transcriptions. Variable/constraint scaling diagnostics, parametric sweeps.
- 🔁 Differentiation: Automatic differentiation (Forward/Reverse) via CasADi, sparse Jacobian/Hessian pipelines, sensitivity regime selection, matrix-free HVPs.
- ⏱️ Integration: ODE solvers (solve_ivp), Runge-Kutta methods, Stormer-Verlet (symplectic), second-order and mass-matrix integrators, discrete integration.
- 📈 Interpolation: 1D, 2D, Sparse, and N-D table lookups with B-spline support.
- 📐 Geometry: Quaternions, Rotation Matrices, Euler Angles.
- 🔍 Root Finding: Multi-method globalization stack (trust-region, line-search, Broyden, pseudo-transient), implicit function builders.
- 🧠 Surrogates: Differentiable approximations (sigmoid, softmax, smooth abs/max/min/clamp) for discontinuous functions.
- 📊 Stochastic Analysis: Polynomial chaos expansion, stochastic quadrature (Gauss, Clenshaw-Curtis, Smolyak sparse grids).
- 🔬 Structural Analysis: Sparsity detection, alias elimination, BLT decomposition, structural observability/identifiability analysis.
- 💾 IO: Graph visualization, serialization, HTML export.
Examples
The examples/ directory contains 30 runnable demos organized by topic:
- intro/ (4) – Numeric basics, energy model, sparsity intro, printing
- math/ (10) – Branching logic, graph visualization, loops, sensitivity, PCE, linear solve policies, structural analysis
- interpolation/ (4) – N-D tables, scattered data, root finding, file-based table loading
- simulation/ (5) – Drag model, brachistochrone, hybrid systems, aircraft attitudes, smooth trajectories
- optimization/ (6) – Rosenbrock, drag optimization, beam deflection, brachistochrone, parametric sweeps, transcription comparison
- integration (1) – RK4, RK45, Stormer-Verlet, mass-matrix integrators
Build and run all examples:
./scripts/run_examples.sh
Quick Look
Write a model once, evaluate it numerically and symbolically:
auto drag(auto rho, auto v, auto S, auto Cd) {
}
int main() {
double D = drag(1.225, 50.0, 10.0, 0.02);
auto D_sym = drag(1.225, v, 10.0, 0.02);
}
Main optimization environment class.
Definition Opti.hpp:167
void minimize(const SymbolicScalar &objective)
Set objective to minimize.
Definition Opti.hpp:555
void subject_to_bounds(const SymbolicScalar &scalar, double lower_bound, double upper_bound)
Apply both lower and upper bounds to a scalar variable.
Definition Opti.hpp:491
OptiSol solve(const OptiOptions &options={})
Solve the optimization problem.
Definition Opti.hpp:603
SymbolicScalar variable(double init_guess=0.0, std::optional< double > scale=std::nullopt, std::optional< double > lower_bound=std::nullopt, std::optional< double > upper_bound=std::nullopt)
Create a scalar decision variable.
Definition Opti.hpp:200
Umbrella header that includes the entire Janus public API.
T pow(const T &base, const T &exponent)
Computes the power function: base^exponent.
Definition Arithmetic.hpp:72
Contributing
Language and style: C++20, header-only, heavily templated. Formatting is enforced by nix fmt (clang-format). Run ./scripts/install-hooks.sh to auto-format on commit.
Adding tests: Test files live in tests/ and mirror the include/janus/ directory layout (tests/core/, tests/math/, tests/optimization/). Use GoogleTest. Run ./scripts/test.sh to verify.
Adding examples: Drop a .cpp file in the appropriate examples/ subdirectory and add it to examples/CMakeLists.txt. Run ./scripts/run_examples.sh to verify.
PR workflow: Fork, branch, make changes, ensure ./scripts/verify.sh passes (build + test + examples), open a PR against main.
Inspiration & Credits
Janus is heavily inspired by AeroSandbox, Peter Sharpe's Python-based design optimization framework. Janus serves as a C++ implementation and extension of the "Code Transformations" paradigm pioneered by Sharpe.
- Primary Inspiration: AeroSandbox by Peter Sharpe.
- Theoretical Foundation: Sharpe, Peter D. AeroSandbox: A Differentiable Framework for Aircraft Design Optimization. PhD Thesis, MIT, 2024. Read Thesis
Janus is built upon the shoulders of giants:
- Eigen: For high-performance linear algebra and numeric storage.
- CasADi: For symbolic graph generation, automatic differentiation, and optimization interfaces.