mod charts; mod live; mod model; mod opti; mod solver; mod space; mod utils; use model::Model; use opti::GradientDescentOptimizer; use solver::*; use nalgebra::vector; use rand::Rng; use std::{ collections::HashMap, sync::{Arc, RwLock}, thread, time::Duration, }; fn main() { bike(); //lyfe(); //stage(); } #[allow(dead_code)] fn lyfe() { let size = (800, 800); let diffusion = vector![0.2, 0.2]; let model = model::constrained::Constrained { s: model::constrained::ConstrainedSettings { model: model::lyfe::Lyfe { s: model::lyfe::LyfeSettings { da: 0.2, db: 0.3, f: 0.03, r: 0.061, }, }, constraint: model::MinMaxConstraint { min: [0.0, 0.0], max: [1.0, 1.0], }, _p: Default::default(), }, }; /*let solver = ImplicitEulerSolver { dt: 0.1, tol: 0.000001, niters: 100, };*/ let solver = ExplicitEulerSolver { dt: 0.01 }; let mut space = space::Space { model, solver, old_points: vec![ space::Point { pos: vector![0.0, 0.0], diffusion, }; size.0 * size.1 ], points: vec![ space::Point { pos: vector![0.0, 0.0], diffusion, }; size.0 * size.1 ], size, sources: HashMap::new(), time: 0.0, _p: Default::default(), }; let mut rng = rand::thread_rng(); for _ in 0..100 { space.points[rng.gen_range(0..space.old_points.len())].pos[0] = 0.8; } //space.points[size.0 * size.1 / 2 + size.0 / 2].pos[0] = 0.1; //space.points[size.0 * size.1 / 2 + size.0 / 2 + 100].pos[0] = 0.05; let space = Arc::new(RwLock::new(space)); thread::spawn({ let space = space.clone(); let interval = Duration::from_millis(1); move || loop { space.write().unwrap().simulate(0.1); std::thread::sleep(interval); } }); thread::spawn(move || live::run(space)).join().unwrap(); } #[allow(dead_code)] fn giraffe() { let size = (800, 800); let diffusion = vector![0.1, 0.1]; let model = model::constrained::Constrained { s: model::constrained::ConstrainedSettings { model: model::giraffe::Giraffe { s: model::giraffe::GiraffeSettings { a_a: 0.7, a_b: 0.2, b_a: -0.5, b_b: 0.1, }, }, constraint: model::MinMaxConstraint { min: [0.0, 0.0], max: [1.0, 1.0], }, _p: Default::default(), }, }; /*let solver = ImplicitEulerSolver { dt: 0.1, tol: 0.000001, niters: 100, };*/ let solver = ExplicitEulerSolver { dt: 0.1 }; let mut space = space::Space { model, solver, old_points: vec![ space::Point { pos: vector![0.0, 0.0], diffusion, }; size.0 * size.1 ], points: vec![ space::Point { pos: vector![0.0, 0.0], diffusion, }; size.0 * size.1 ], size, sources: HashMap::new(), time: 0.0, _p: Default::default(), }; let mut rng = rand::thread_rng(); for _ in 0..100 { space.points[rng.gen_range(0..space.old_points.len())].pos[0] = 0.5; } //space.points[size.0 * size.1 / 2 + size.0 / 2].pos[0] = 0.1; //space.points[size.0 * size.1 / 2 + size.0 / 2 + 100].pos[0] = 0.05; let space = Arc::new(RwLock::new(space)); thread::spawn({ let space = space.clone(); let interval = Duration::from_millis(1); move || loop { space.write().unwrap().simulate(0.1); std::thread::sleep(interval); } }); thread::spawn(move || live::run(space)).join().unwrap(); } #[allow(dead_code)] fn stage() { let mut rng = rand::thread_rng(); // ---- Initialization let x0 = vector![0.99, 0.01]; let dt = 0.1; let nsamples: usize = 400; let nsamples_partial: usize = 40; // ---- True data generation let settings_true = model::sir::SirSettings { beta: 0.6, gamma: 0.1, pop: 1.0, }; let model = model::sir::Sir { s: settings_true.clone(), }; let solver = ImplicitEulerSolver { dt: 0.1, tol: 0.000001, niters: 100, }; let mut xlist_true = Vec::with_capacity(nsamples); xlist_true.push(x0); let mut x = x0; for _ in 0..nsamples - 1 { x = solver.f(&model, x); xlist_true.push(x); } // ---- Calibration let mut optimizer = GradientDescentOptimizer::new(model, solver); let settings_random = model::sir::SirSettings { beta: rng.gen(), gamma: rng.gen(), pop: 1.0, }; *optimizer.model.get_settings_mut() = model::sir::SirSettings { beta: 0.38960491052564317, gamma: 0.6549130899826807, pop: 1.0, }; //settings_random.clone(); let mut optimizer_sto = optimizer.clone(); let mut xlist_random = Vec::with_capacity(nsamples); xlist_random.push(x0); let mut x = x0; for _ in 0..nsamples - 1 { x = optimizer.solver.f(&optimizer.model, x); xlist_random.push(x); } // Batch let mut path = Vec::new(); let mut error = Vec::new(); for rate in [1.0, 0.1, 0.01, 0.001] { let (path_, error_) = &mut optimizer.calibrate_batch_record( &xlist_true[..nsamples_partial], 0.00001, rate, 1000, 0..2, ); path.append(path_); error.append(error_); } let mut xlist = Vec::with_capacity(nsamples); xlist.push(x0); let mut x = x0; for _ in 0..nsamples - 1 { x = optimizer.solver.f(&optimizer.model, x); xlist.push(x); } // Stochastic let mut path_sto = Vec::new(); let mut error_sto = Vec::new(); for rate in [1.0, 0.1, 0.01, 0.001] { let (path_, error_) = &mut optimizer_sto.calibrate_stochastic_record( &xlist_true[..nsamples_partial], 0.00001, rate, 10, 0..2, ); path_sto.append(path_); error_sto.append(error_); } let mut xlist_sto = Vec::with_capacity(nsamples); xlist_sto.push(x0); let mut x = x0; for _ in 0..nsamples - 1 { x = optimizer_sto.solver.f(&optimizer_sto.model, x); xlist_sto.push(x); } // ---- Printing println!("Random settings:\n{:?}", settings_random); println!("Calibrated settings:\n{:?}", optimizer.model.get_settings()); println!("True settings:\n{:?}", settings_true); // ---- Drawing // Main plots charts::draw_chart("sir_true", None, settings_true.pop, &xlist_true, dt); charts::draw_chart("sir_random", None, settings_random.pop, &xlist_random, dt); charts::draw_chart( "sir_calibrated", None, optimizer.model.get_settings().pop, &xlist, dt, ); charts::draw_error_chart2("error", None, &error, &error_sto); charts::plot_objective( "obj_partial", None, optimizer.clone(), &xlist_true[..nsamples_partial], Some(&path.iter().map(|v| (v[0], v[1])).collect::>()), Some(&path_sto.iter().map(|v| (v[0], v[1])).collect::>()), ); charts::plot_objective( "obj", None, optimizer.clone(), &xlist_true, Some(&path.iter().map(|v| (v[0], v[1])).collect::>()), Some(&path_sto.iter().map(|v| (v[0], v[1])).collect::>()), ); // Implicit/explicit Euler comparison { let dur = 40f64; let settings = model::sir::SirSettings { beta: 0.999, gamma: 0.5, pop: 1.0, }; let model = model::sir::Sir { s: settings.clone(), }; let solver_explicit = ExplicitEulerSolver { dt: 1.0 }; let solver_implicit = ImplicitEulerSolver { dt: 1.0, tol: 0.000001, niters: 100, }; let solver_true = ImplicitEulerSolver { dt: 0.001, tol: 0.000001, niters: 100, }; let nsamples_explicit = (dur / solver_explicit.dt) as usize; let nsamples_implicit = (dur / solver_implicit.dt) as usize; let nsamples_true = (dur / solver_true.dt) as usize; let mut xlist_explicit = Vec::with_capacity(nsamples_explicit); xlist_explicit.push(x0); let mut xlist_implicit = Vec::with_capacity(nsamples_implicit); xlist_implicit.push(x0); let mut xlist_true = Vec::with_capacity(nsamples_true); xlist_true.push(x0); let mut x = x0; for _ in 1..nsamples_explicit { x = solver_explicit.f(&model, x); xlist_explicit.push(x); } x = x0; for _ in 1..nsamples_implicit { x = solver_implicit.f(&model, x); xlist_implicit.push(x); } x = x0; for _ in 1..nsamples_true { x = solver_true.f(&model, x); xlist_true.push(x); } charts::draw_comparison_chart( "comp_euler", None, &settings, &xlist_explicit, &xlist_implicit, &xlist_true, solver_explicit.dt, solver_implicit.dt, solver_true.dt, ); } // SIRV charts { let nsamples = 1000; let settings = model::sirv::SirvSettings { beta: 0.8, gamma: 0.2, lambda: 0.025, mu: 0.02, pop: 1.0, }; let model = model::sirv::Sirv { s: settings }; let mut xlist = Vec::with_capacity(nsamples); xlist.push(x0); let mut x = x0; for _ in 1..nsamples { x = optimizer.solver.f(&model, x); xlist.push(x); } charts::draw_chart("sirv", None, model.get_settings().pop, &xlist, dt); } } #[allow(dead_code)] fn bike() { let mut rng = rand::thread_rng(); // ---- Initialization let x0 = vector![0.0, 60. / 3.6]; let dt = 0.1; let nsamples: usize = 2000; // ---- Data generation let settings_true = model::bike::BikeSettings:: { cx: 0.25, g: 9.81, m: 70.0, th: 0.11, }; println!( "true: A={} ; B={}", -settings_true.cx / settings_true.m, settings_true.g * settings_true.th.sin() - 80. / settings_true.m ); let model = model::bike::Bike { s: settings_true.clone(), }; /*let solver = ImplicitEulerSolver { dt, tol: 0.000001, niters: 100, };*/ let solver = ExplicitEulerSolver { dt }; let mut xlist_true = Vec::with_capacity(nsamples); xlist_true.push(x0); let mut x = x0; for _ in 0..nsamples - 1 { x = solver.f(&model, x); if x[1] < 0. { x[1] = 0.; } xlist_true.push(x); } // -- Alternative settings // Greater theta let mut settings_greater_th = settings_true.clone(); settings_greater_th.th = 0.12; println!( "gtth: A={} ; B={}", -settings_greater_th.cx / settings_greater_th.m, settings_greater_th.g * settings_greater_th.th.sin() - 80. / settings_greater_th.m ); let xlist_greater_th = { let model = model::bike::Bike { s: settings_greater_th.clone(), }; let mut xlist = Vec::with_capacity(nsamples); xlist.push(x0); let mut x = x0; for _ in 0..nsamples - 1 { x = solver.f(&model, x); if x[1] < 0. { x[1] = 0.; } xlist.push(x); } xlist }; // Optimal braking let mut settings_opti1 = model::bike2::BikeSettings { cx: settings_true.cx, g: settings_true.g, m: settings_true.m, th: std::f64::consts::PI / 180. * 15., b: |x, v, s| { let mu = 0.1; let gx = 0.65; let gy = 1.05; let magic = 1.0; ( ( s.m * s.g * (mu * s.th.cos() + s.th.sin()) - s.cx * v * v, 0., ), (-2. * s.cx * v, 0.), ) }, }; let (xlist_opti1, blist_opti1) = { let model = model::bike2::Bike { s: settings_opti1.clone(), }; let mut xlist = Vec::with_capacity(nsamples); xlist.push(x0); let mut blist = Vec::with_capacity(nsamples); let mut x = x0; for _ in 0..nsamples - 1 { blist.push((settings_opti1.b)(x[0], x[1], &settings_opti1).0 .0); x = solver.f(&model, x); if x[1] < 0. { x[1] = 0.; break; } xlist.push(x); } (xlist, blist) }; let mut settings_opti2 = model::bike2::BikeSettings { cx: settings_true.cx, g: settings_true.g, m: settings_true.m, th: std::f64::consts::PI / 180. * 15., b: |x, v, s| { let mu = 0.1; let gx = 0.65; let gy = 1.05; let magic = 1.0; ( ( s.m * s.g * (mu * s.th.cos() + s.th.sin()) - s.cx * v * v, s.m * s.g * s.th.cos() * (mu + gx / gy), ), (-2. * s.cx * v, 0.), ) }, }; let xlist_opti2 = { let model = model::bike2::Bike { s: settings_opti2.clone(), }; let mut xlist = Vec::with_capacity(nsamples); xlist.push(x0); let mut x = x0; for _ in 0..nsamples - 1 { x = solver.f(&model, x); if x[1] < 0. { x[1] = 0.; break; } xlist.push(x); } xlist }; // -- ODE solution let xlist_ode = { let settings = settings_true.clone(); let a = -settings.cx / settings.m; let b = settings.g * settings.th.sin() - 80. / settings.m; /*let r = (-a*b).sqrt(); assert!(!r.is_nan()); let alpha = (1.+x0[1]*a/r)/(1.-x0[1]*a/r);*/ let r = (a * b).sqrt(); assert!(!r.is_nan()); let alpha = -r / (a * x0[1]); println!("alpha: {alpha}"); let stop = (1. / alpha).atan() / r; println!("Stop: {stop}"); let bmax = settings.m * settings.g * (settings.th.cos() + settings.th.sin()) - settings.cx * 27.; println!("bmax: {bmax}"); let mut xlist = Vec::with_capacity(nsamples); xlist.push(x0); let mut x = x0; for t in 0..nsamples - 1 { let t = t as f64 * dt; //dbg!((beta*c*(-t*c).exp()-alpha*c*(t*c).exp())); //dbg!((alpha*(t*c).exp()+beta*(-t*c).exp())); //let v = (beta*c*(-t*c).exp()-alpha*c*(t*c).exp())/a/(alpha*(t*c).exp()+beta*(-t*c).exp()); //let v = r/a*(alpha*(-t*r).exp()-(t*r).exp())/(alpha*(-t*r).exp()+(t*r).exp()); let mut v = r / a * (alpha * (t * r).sin() - (t * r).cos()) / (alpha * (t * r).cos() + (t * r).sin()); if v.is_nan() { panic!("NaN"); } v = v.max(0.).min(100.); x = vector![0., v]; xlist.push(x); if t > stop { break; } } xlist }; // ---- Drawing // Main plots charts::draw_bike_chart( "bike_x", None, &[( "x (m)", &xlist_true.iter().map(|x| x[0]).collect::>(), )], dt, ); charts::draw_bike_chart( "bike_v", None, &[( "v (m/s)", &xlist_true.iter().map(|x| x[1]).collect::>(), )], dt, ); charts::draw_bike_chart2( "bike_opti1", None, &[( "v (m/s)", &xlist_opti1.iter().map(|x| x[1]).collect::>(), )], &[("b", &blist_opti1)], dt, ); charts::draw_bike_chart( "bike_opti2", None, &[ ( "v (m/s) (arrière)", &xlist_opti1.iter().map(|x| x[1]).collect::>(), ), ( "v (m/s) (arrière+avant)", &xlist_opti2.iter().map(|x| x[1]).collect::>(), ), ], dt, ); charts::draw_bike_chart( "bike_var_theta", None, &[ ( &format!("v (θ={}, B<0)", settings_true.th), &xlist_true.iter().map(|x| x[1]).collect::>(), ), ( &format!("v (θ={}, B>0)", settings_greater_th.th), &xlist_greater_th.iter().map(|x| x[1]).collect::>(), ), ], dt, ); charts::draw_bike_chart( "bike_ode_v", None, &[( "v (m/s)", &xlist_ode.iter().map(|x| x[1]).collect::>(), )], dt, ); }