numerics 0.1.0
Loading...
Searching...
No Matches
solve.hpp
Go to the documentation of this file.
1/// @file solve/solve.hpp
2/// @brief Unified solve() dispatcher -- the single entry point for all solvers.
3///
4/// Dispatches on (problem type, algorithm type) via C++20 concepts, mirroring
5/// Julia's SciML ecosystem where the algorithm is a swappable plug:
6///
7/// // Explicit ODE (Lorenz, nbody, quantum, ...)
8/// auto sol = num::solve(ODEProblem{f, u0, t0, tf}, RK45{.rtol=1e-8}, obs);
9///
10/// // Implicit ODE (heat, diffusion, ...)
11/// num::solve(u, BackwardEuler{.solver=cg, .dt=dt, .nstep=n});
12///
13/// // MCMC (Ising, spin glass, ...)
14/// double m = num::solve(MCMCProblem{accept, flip, N}, Metropolis{}, measure, rng);
15///
16/// Problems carry the mathematics. Algorithms carry the numerics.
17/// Swapping the algorithm never touches the problem definition.
18#pragma once
19
20#include "solve/problems.hpp"
21#include "solve/algorithms.hpp"
22#include "ode/ode.hpp"
23#include "ode/implicit.hpp"
24#include "stochastic/mcmc.hpp"
25#include <concepts>
26#include <random>
27
28namespace num {
29
30// --- Concepts ----------------------------------------------------------------
31
32template<typename P>
33concept IsODEProblem = requires(const P& p, double t, const Vector& y, Vector& dy) {
34 p.f(t, y, dy);
35 { p.u0 } -> std::convertible_to<const Vector&>;
36 { p.t0 } -> std::convertible_to<double>;
37 { p.tf } -> std::convertible_to<double>;
38};
39
40template<typename A>
42 std::same_as<std::remove_cvref_t<A>, Euler> ||
43 std::same_as<std::remove_cvref_t<A>, RK4> ||
44 std::same_as<std::remove_cvref_t<A>, RK45>;
45
46template<typename A>
47concept IsImplicitODEAlg = requires(const A& a) {
48 { a.solver } -> std::convertible_to<LinearSolver>;
49 { a.dt } -> std::convertible_to<double>;
50 { a.nstep } -> std::convertible_to<int>;
51};
52
53template<typename A>
54concept IsMCMCAlg = std::same_as<std::remove_cvref_t<A>, Metropolis>;
55
56// --- Explicit ODE ------------------------------------------------------------
57
58template<IsODEProblem P>
59ODEResult solve(const P& prob, const RK45& alg, ObserverFn obs = nullptr) {
60 ODEParams p{.t0=prob.t0, .tf=prob.tf, .h=alg.h,
61 .rtol=alg.rtol, .atol=alg.atol, .max_steps=alg.max_steps};
62 return ode_rk45(prob.f, prob.u0, p, obs);
63}
64
65template<IsODEProblem P>
66ODEResult solve(const P& prob, const RK4& alg, ObserverFn obs = nullptr) {
67 return ode_rk4(prob.f, prob.u0, {.t0=prob.t0, .tf=prob.tf, .h=alg.h}, obs);
68}
69
70template<IsODEProblem P>
71ODEResult solve(const P& prob, const Euler& alg, ObserverFn obs = nullptr) {
72 return ode_euler(prob.f, prob.u0, {.t0=prob.t0, .tf=prob.tf, .h=alg.h}, obs);
73}
74
75// --- Implicit ODE ------------------------------------------------------------
76
77template<VecField F>
78void solve(F& u, const BackwardEuler& alg) {
79 ode::advance(u, alg.solver, {alg.nstep, alg.dt});
80}
81
82template<VecField F, typename Observer>
83void solve(F& u, const BackwardEuler& alg, Observer&& obs) {
84 ode::advance(u, alg.solver, {alg.nstep, alg.dt}, std::forward<Observer>(obs));
85}
86
87// --- MCMC --------------------------------------------------------------------
88
89/// Run equilibration + measurement sweeps; return the mean of measure() over
90/// measurement sweeps. The rng is passed by the caller so its state persists
91/// across successive solve() calls (e.g. a temperature loop).
92template<IsMCMCAlg A, typename MeasureFn, typename RNG>
93double solve(const MCMCProblem& prob, const A& alg, MeasureFn&& measure, RNG& rng) {
94 for (int s = 0; s < alg.equilibration; ++s)
96 double acc = 0.0;
97 for (int s = 0; s < alg.measurements; ++s) {
99 acc += measure();
100 }
101 return acc / alg.measurements;
102}
103
104} // namespace num
Algorithm tags: carry the numerics, not the mathematics.
Implicit time integration via a user-supplied LinearSolver.
MetropolisStats metropolis_sweep_prob(idx n_sites, ProbFn acceptance_prob, Apply apply_flip, RNG &rng)
Metropolis sweep with caller-supplied acceptance probabilities.
Definition mcmc_impl.hpp:54
void advance(Field &u, const LinearSolver &solver, ImplicitParams p, Observer &&obs)
Definition implicit.hpp:34
ODEResult ode_rk4(ODERhsFn f, Vector y0, ODEParams p={}, ObserverFn obs=nullptr)
Classic 4th-order Runge-Kutta, fixed step.
Definition ode.cpp:339
std::function< void(real t, const Vector &y)> ObserverFn
Definition ode.hpp:34
ODEResult solve(const P &prob, const RK45 &alg, ObserverFn obs=nullptr)
Definition solve.hpp:59
BasicVector< real > Vector
Real-valued dense vector with full backend dispatch (CPU + GPU)
Definition vector.hpp:130
ODEResult ode_euler(ODERhsFn f, Vector y0, ODEParams p={}, ObserverFn obs=nullptr)
Forward Euler, 1st-order, fixed step.
Definition ode.cpp:332
ODEResult ode_rk45(ODERhsFn f, Vector y0, ODEParams p={}, ObserverFn obs=nullptr)
Adaptive Dormand-Prince RK45 with FSAL and PI step-size control.
Definition ode.cpp:346
ODE integrators: Euler, RK4, adaptive RK45 (Dormand-Prince), and symplectic Velocity Verlet / Yoshida...
Problem types: carry the mathematics, not the numerics.
LinearSolver solver
std::function< double(int)> accept_prob
Definition problems.hpp:30
std::function< void(int)> propose
Definition problems.hpp:31
Parameters for all ODE integrators. Set only the fields you need.
Definition ode.hpp:64
double atol
double rtol