2026-02-09 17:27:35 +01:00
# Mean Field Games Tutorial (Production)
2026-02-16 16:42:24 +01:00
This page summarizes the full MFG tutorial notebook ([`mean_field_games_tutorial.ipynb` ](https://github.com/ThotDjehuty/optimiz-r/blob/main/examples/notebooks/mean_field_games_tutorial.ipynb )) and the accompanying audit in [`docs/MFG_TUTORIAL_COMPLETE.md` ](https://github.com/ThotDjehuty/optimiz-r/blob/main/docs/MFG_TUTORIAL_COMPLETE.md ).
2026-02-09 17:27:35 +01:00
## What the notebook demonstrates
- Rust-backed 1D MFG solver (`solve_mfg_1d_rust` ) with PyO3 bindings
- Coupled HJB– Fokker-Planck fixed-point iteration with congestion term
- Execution time: ~0.4 s for a 100× 100 grid (agents × time)
- Stable mass conservation and no NaNs across iterations
- Visual outputs: convergence plot, 3D density evolution, 3D value surface, time-slice snapshots
## Problem setup
- Spatial grid: $x \in [0, 1]$, 100 points; time grid: 100 steps, $T = 1.0$
- Viscosity $\nu = 0.01$, relaxation $\alpha = 0.5$, congestion penalty $\lambda = 0.5$
- Initial distribution $m_0$: Gaussian centered at $x=0.3$
- Terminal cost $u_T(x) = 0.5(x - 0.7)^2$ (agents target $x=0.7$)
### Core equations
.. math::
-\partial_t u - \nu\,\partial_{xx} u + H\big(x, \partial_x u, m\big) = 0,\\
\partial_t m - \nu\,\partial_{xx} m - \operatorname{div}\big(m\, \partial_p H\big) = 0.
We iterate between backward $u$ and forward $m$ with mass renormalization to keep $\int m \, dx = 1$.
## Usage snippet
```python
import numpy as np
from optimizr import MFGConfig , solve_mfg_1d_rust
x = np . linspace ( 0 , 1 , 100 )
m0 = np . exp ( - 50 * ( x - 0.3 ) ** 2 )
m0 /= np . trapz ( m0 , x )
u_terminal = 0.5 * ( x - 0.7 ) ** 2
config = MFGConfig ( nx = 100 , nt = 100 , x_min = 0.0 , x_max = 1.0 , T = 1.0 , nu = 0.01 , max_iter = 50 , tol = 1e-5 , alpha = 0.5 )
u , m , iters = solve_mfg_1d_rust ( m0 , u_terminal , config , lambda_congestion = 0.5 )
print ( f "converged in { iters } iterations: u { u . shape } , m { m . shape } " )
```
## Key observations
- Agents split and migrate toward the target region; congestion prevents collapse into a single spike.
- Value function decreases smoothly over time, capturing optimal cost-to-go.
- Convergence is monotone in practice; fixed-point loop hits tolerance within ~50 iterations.
## Why the Rust backend matters
- Implicit diffusion step and upwind transport improve stability over the reference Python solver.
- Rayon parallelism speeds up 2D grids; OpenBLAS accelerates dense linear algebra where applicable.
- Safe bindings via PyO3 with abi3 wheels keep installation friction low.
## Reproducing visuals
- Run the notebook end-to-end to generate 3D surfaces and time-slice plots.
- Export figures from the notebook if you need static assets for papers or presentations.
## Next steps (tracked)
- Add 2D MFG example with separable costs.
- Extend congestion models (e.g., polynomial costs) and compare convergence rates.
- Log convergence metrics to CSV for batch sweeps.