Janus 2.0.0
High-performance C++20 dual-mode numerical framework
Loading...
Searching...
No Matches
Janus 👥

Documentation Janus CI Clang-Format Check codecov

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:

#include <janus/janus.hpp>
// Generic model -- works with double or CasADi symbolic types
auto drag(auto rho, auto v, auto S, auto Cd) {
return 0.5 * rho * janus::pow(v, 2) * S * Cd;
}
int main() {
// Numeric mode -- compiles to native arithmetic
double D = drag(1.225, 50.0, 10.0, 0.02);
// Symbolic mode -- same function builds a CasADi graph
auto v = opti.variable(50.0);
auto D_sym = drag(1.225, v, 10.0, 0.02);
opti.minimize(D_sym);
opti.subject_to_bounds(v, 10, 100);
auto sol = opti.solve(); // IPOPT under the hood
}
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.