Initial commit
This commit is contained in:
commit
aaa212b9a9
22 changed files with 4778 additions and 0 deletions
9
examples/epidemics/Cargo.toml
Normal file
9
examples/epidemics/Cargo.toml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "epidemics"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
nalgebra = "0.32.3"
|
||||
rand = "0.8.5"
|
||||
rustmodel = { path = "../..", features = ["plot"] }
|
||||
107
examples/epidemics/src/main.rs
Normal file
107
examples/epidemics/src/main.rs
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
use nalgebra::vector;
|
||||
use rustmodel::prelude::*;
|
||||
|
||||
fn main() {
|
||||
// Start with 1% infected
|
||||
let x0 = vector![0.99, 0.01];
|
||||
let dt = 0.1;
|
||||
let nsamples: usize = 400;
|
||||
|
||||
let settings = sir::SirSettings {
|
||||
beta: 0.6,
|
||||
gamma: 0.1,
|
||||
pop: 1.0,
|
||||
};
|
||||
|
||||
let model = sir::Sir {
|
||||
s: settings.clone(),
|
||||
};
|
||||
let solver = ImplicitEuler {
|
||||
dt,
|
||||
tol: 0.000001,
|
||||
niters: 100,
|
||||
};
|
||||
let mut xlist = Vec::with_capacity(nsamples);
|
||||
xlist.push(x0);
|
||||
let mut x = x0;
|
||||
for _ in 1..nsamples {
|
||||
x = solver.f(&model, x);
|
||||
xlist.push(x);
|
||||
}
|
||||
|
||||
xlist.iter().for_each(|x| println!("{}\t{}", x[0], x[1]));
|
||||
|
||||
PlotBuilder::default()
|
||||
.dt(dt)
|
||||
.title(Some(String::from("Epidemics Compartmental Model")))
|
||||
.y_min(Some(0.0))
|
||||
.y_max(Some(1.0))
|
||||
.x_label(Some(String::from("Time")))
|
||||
.build()
|
||||
.unwrap()
|
||||
.plot(
|
||||
"target/sir.png",
|
||||
&xlist,
|
||||
&[("Susceptible", colors::BLUE), ("Infected", colors::RED)],
|
||||
);
|
||||
}
|
||||
|
||||
/// SIR model without vital dynamics
|
||||
pub mod sir {
|
||||
use nalgebra::{base::*, matrix, vector};
|
||||
use rustmodel::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SirSettings<T> {
|
||||
/// Transmission probability
|
||||
pub beta: T,
|
||||
/// Removal probability
|
||||
pub gamma: T,
|
||||
/// Total population
|
||||
pub pop: T,
|
||||
}
|
||||
|
||||
impl<T> Settings for SirSettings<T> {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Sir<T> {
|
||||
pub s: SirSettings<T>,
|
||||
}
|
||||
|
||||
impl Model<f64, SirSettings<f64>, 2> for Sir<f64> {
|
||||
fn f(&self, x: Vector2<f64>) -> Vector2<f64> {
|
||||
vector![
|
||||
-self.s.beta * x[0] * x[1] / self.s.pop,
|
||||
self.s.beta * x[0] * x[1] / self.s.pop - self.s.gamma * x[1]
|
||||
]
|
||||
}
|
||||
fn df(&self, x: Vector2<f64>) -> Matrix2<f64> {
|
||||
matrix![
|
||||
-self.s.beta*x[1]/self.s.pop, -self.s.beta*x[0]/self.s.pop;
|
||||
self.s.beta*x[1]/self.s.pop, self.s.beta*x[0]/self.s.pop-self.s.gamma
|
||||
]
|
||||
}
|
||||
fn get_settings(&self) -> &SirSettings<f64> {
|
||||
&self.s
|
||||
}
|
||||
fn get_settings_mut(&mut self) -> &mut SirSettings<f64> {
|
||||
&mut self.s
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<SirSettings<T>> for Vect<T, 3> {
|
||||
fn from(s: SirSettings<T>) -> Self {
|
||||
vector![s.beta, s.gamma, s.pop]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar + Copy> From<Vect<T, 3>> for SirSettings<T> {
|
||||
fn from(v: Vect<T, 3>) -> Self {
|
||||
Self {
|
||||
beta: v[0],
|
||||
gamma: v[1],
|
||||
pop: v[2],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
608
examples/old/charts.rs
Normal file
608
examples/old/charts.rs
Normal file
|
|
@ -0,0 +1,608 @@
|
|||
use crate::{
|
||||
model::{self, Model},
|
||||
opti::GradientDescentOptimizer,
|
||||
solver::Solver,
|
||||
utils::*,
|
||||
};
|
||||
|
||||
use plotters::prelude::*;
|
||||
use rayon::prelude::*;
|
||||
|
||||
const CHART_SIZE: (u32, u32) = (1000, 800); //(500, 400);
|
||||
const CHART_SIZE_OBJ: (u32, u32) = (960, 960); //(480, 480);
|
||||
|
||||
pub fn draw_chart(filename: &str, title: Option<&str>, pop: f64, xlist: &[Vect<f64, 2>], dt: f64) {
|
||||
let filepath = format!("target/{}.png", filename);
|
||||
let root = BitMapBackend::new(&filepath, CHART_SIZE).into_drawing_area();
|
||||
root.fill(&WHITE).unwrap();
|
||||
let mut chart = ChartBuilder::on(&root);
|
||||
if let Some(title) = title {
|
||||
chart.caption(
|
||||
title,
|
||||
FontDesc::new(FontFamily::Name("cantarell"), 28.0, FontStyle::Normal),
|
||||
);
|
||||
}
|
||||
let mut chart = chart
|
||||
.margin_right(12)
|
||||
.y_label_area_size(30)
|
||||
.x_label_area_size(30)
|
||||
.build_cartesian_2d(0.0f64..xlist.len() as f64 * dt, 0.0f64..1.)
|
||||
.unwrap();
|
||||
|
||||
chart.configure_mesh().x_desc("Time").draw().unwrap();
|
||||
|
||||
chart
|
||||
.draw_series(LineSeries::new(
|
||||
xlist.iter().enumerate().map(|(i, x)| (i as f64 * dt, x[0])),
|
||||
BLUE,
|
||||
))
|
||||
.unwrap()
|
||||
.label("Susceptible")
|
||||
.legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], BLUE));
|
||||
|
||||
chart
|
||||
.draw_series(LineSeries::new(
|
||||
xlist.iter().enumerate().map(|(i, x)| (i as f64 * dt, x[1])),
|
||||
RED,
|
||||
))
|
||||
.unwrap()
|
||||
.label("Infected")
|
||||
.legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], RED));
|
||||
|
||||
chart
|
||||
.draw_series(LineSeries::new(
|
||||
xlist
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, x)| (i as f64 * dt, pop - x[0] - x[1])),
|
||||
GREEN,
|
||||
))
|
||||
.unwrap()
|
||||
.label("Removed")
|
||||
.legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], GREEN));
|
||||
|
||||
chart
|
||||
.configure_series_labels()
|
||||
.background_style(WHITE.mix(0.8))
|
||||
.border_style(BLACK)
|
||||
.draw()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn draw_error_chart(filename: &str, title: Option<&str>, xlist: &[f64]) {
|
||||
let filepath = format!("target/{}.png", filename);
|
||||
let root = BitMapBackend::new(&filepath, CHART_SIZE).into_drawing_area();
|
||||
root.fill(&WHITE).unwrap();
|
||||
let mut chart = ChartBuilder::on(&root);
|
||||
if let Some(title) = title {
|
||||
chart.caption(
|
||||
title,
|
||||
FontDesc::new(FontFamily::Name("cantarell"), 28.0, FontStyle::Normal),
|
||||
);
|
||||
}
|
||||
let mut chart = chart
|
||||
.margin_right(12)
|
||||
.y_label_area_size(50)
|
||||
.x_label_area_size(30)
|
||||
.build_cartesian_2d(0..xlist.len(), (0.0f64..max(xlist)).log_scale())
|
||||
.unwrap();
|
||||
|
||||
let printer = plotters::data::float::FloatPrettyPrinter {
|
||||
allow_scientific: true,
|
||||
min_decimal: 0,
|
||||
max_decimal: 2,
|
||||
};
|
||||
chart
|
||||
.configure_mesh()
|
||||
.x_desc("Iterations")
|
||||
.y_desc("Mean error")
|
||||
.y_label_formatter(&|y| printer.print(*y))
|
||||
.draw()
|
||||
.unwrap();
|
||||
|
||||
chart
|
||||
.draw_series(LineSeries::new(xlist.iter().copied().enumerate(), BLACK))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn draw_error_chart2(
|
||||
filename: &str,
|
||||
title: Option<&str>,
|
||||
xlist_batch: &[f64],
|
||||
xlist_sto: &[f64],
|
||||
) {
|
||||
let filepath = format!("target/{}.png", filename);
|
||||
let root = BitMapBackend::new(&filepath, CHART_SIZE).into_drawing_area();
|
||||
root.fill(&WHITE).unwrap();
|
||||
let mut chart = ChartBuilder::on(&root);
|
||||
if let Some(title) = title {
|
||||
chart.caption(
|
||||
title,
|
||||
FontDesc::new(FontFamily::Name("cantarell"), 28.0, FontStyle::Normal),
|
||||
);
|
||||
}
|
||||
let mut chart = chart
|
||||
.margin_right(12)
|
||||
.y_label_area_size(50)
|
||||
.x_label_area_size(30)
|
||||
.build_cartesian_2d(
|
||||
0..xlist_batch.len().max(xlist_sto.len()),
|
||||
(0.0f64..max(xlist_batch).max(max(xlist_sto))).log_scale(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let printer = plotters::data::float::FloatPrettyPrinter {
|
||||
allow_scientific: true,
|
||||
min_decimal: 0,
|
||||
max_decimal: 2,
|
||||
};
|
||||
chart
|
||||
.configure_mesh()
|
||||
.x_desc("Iterations")
|
||||
.y_desc("Mean error")
|
||||
.y_label_formatter(&|y| printer.print(*y))
|
||||
.draw()
|
||||
.unwrap();
|
||||
|
||||
chart
|
||||
.draw_series(LineSeries::new(
|
||||
xlist_batch.iter().copied().enumerate(),
|
||||
BLACK,
|
||||
))
|
||||
.unwrap()
|
||||
.label("Batch")
|
||||
.legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], BLACK));
|
||||
|
||||
chart
|
||||
.draw_series(LineSeries::new(xlist_sto.iter().copied().enumerate(), RED))
|
||||
.unwrap()
|
||||
.label("Stochastic")
|
||||
.legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], RED));
|
||||
|
||||
chart
|
||||
.configure_series_labels()
|
||||
.background_style(WHITE.mix(0.8))
|
||||
.border_style(BLACK)
|
||||
.position(SeriesLabelPosition::MiddleLeft)
|
||||
.draw()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn plot_objective<
|
||||
R: Solver<f64, model::sir::SirSettings<f64>, model::sir::Sir<f64>, 2> + Clone + Sync,
|
||||
>(
|
||||
filename: &str,
|
||||
title: Option<&str>,
|
||||
optimizer: GradientDescentOptimizer<
|
||||
f64,
|
||||
model::sir::SirSettings<f64>,
|
||||
model::sir::Sir<f64>,
|
||||
R,
|
||||
2,
|
||||
3,
|
||||
>,
|
||||
ylist_true: &[Vect<f64, 2>],
|
||||
path_batch: Option<&[(f64, f64)]>,
|
||||
path_sto: Option<&[(f64, f64)]>,
|
||||
) {
|
||||
let filepath = format!("target/{}.png", filename);
|
||||
let root = BitMapBackend::new(&filepath, CHART_SIZE_OBJ).into_drawing_area();
|
||||
root.fill(&WHITE).unwrap();
|
||||
let mut chart = ChartBuilder::on(&root);
|
||||
if let Some(title) = title {
|
||||
chart.caption(
|
||||
title,
|
||||
FontDesc::new(FontFamily::Name("cantarell"), 28.0, FontStyle::Normal),
|
||||
);
|
||||
}
|
||||
let mut chart = chart
|
||||
.margin_right(12)
|
||||
.x_label_area_size(30)
|
||||
.y_label_area_size(40)
|
||||
.build_cartesian_2d(0.0f64..1., 0.0f64..1.)
|
||||
.unwrap();
|
||||
|
||||
chart
|
||||
.configure_mesh()
|
||||
.x_desc("beta")
|
||||
.y_desc("gamma")
|
||||
.draw()
|
||||
.unwrap();
|
||||
|
||||
let area = chart.plotting_area();
|
||||
|
||||
let range = area.get_pixel_range();
|
||||
let (pw, ph) = (range.0.end - range.0.start, range.1.end - range.1.start);
|
||||
let (xr, yr) = (chart.x_range(), chart.y_range());
|
||||
let step = (
|
||||
(xr.end - xr.start) / pw as f64,
|
||||
(yr.end - yr.start) / ph as f64,
|
||||
);
|
||||
|
||||
let mut min = f64::MAX;
|
||||
let mut max = f64::MIN;
|
||||
let vals: Vec<(f64, f64, f64)> = (0..pw * ph)
|
||||
.into_par_iter()
|
||||
.map(|i| {
|
||||
let (x, y) = (
|
||||
xr.start + step.0 * (i % pw) as f64,
|
||||
yr.start + step.1 * (i / pw) as f64,
|
||||
);
|
||||
let mut optimizer = optimizer.clone();
|
||||
let s = optimizer.model.get_settings_mut();
|
||||
s.beta = x;
|
||||
s.gamma = y;
|
||||
let val = optimizer.objective_batch(&optimizer.model, ylist_true);
|
||||
(x, y, val)
|
||||
})
|
||||
.collect();
|
||||
vals.iter().for_each(|(_, _, val)| {
|
||||
if *val > max {
|
||||
max = *val;
|
||||
}
|
||||
if *val < min {
|
||||
min = *val;
|
||||
}
|
||||
});
|
||||
let ampl = 0.825 / (max - min);
|
||||
|
||||
for (x, y, c) in vals {
|
||||
area.draw_pixel((x, y), &HSLColor((c - min) * ampl, 1.0, 0.5))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if let Some(path_sto) = path_sto {
|
||||
chart
|
||||
.draw_series(std::iter::once(PathElement::new(
|
||||
path_sto,
|
||||
RGBColor(128, 128, 128),
|
||||
)))
|
||||
.unwrap()
|
||||
.label("Stochastic")
|
||||
.legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], RGBColor(128, 128, 128)));
|
||||
}
|
||||
|
||||
if let Some(path_batch) = path_batch {
|
||||
chart
|
||||
.draw_series(std::iter::once(PathElement::new(path_batch, BLACK)))
|
||||
.unwrap()
|
||||
.label("Batch")
|
||||
.legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], BLACK));
|
||||
}
|
||||
|
||||
chart
|
||||
.configure_series_labels()
|
||||
.background_style(WHITE.mix(0.8))
|
||||
.border_style(BLACK)
|
||||
.position(SeriesLabelPosition::UpperRight)
|
||||
.draw()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn draw_comparison_chart(
|
||||
filename: &str,
|
||||
title: Option<&str>,
|
||||
s: &model::sir::SirSettings<f64>,
|
||||
xlist_explicit: &[Vect<f64, 2>],
|
||||
xlist_implicit: &[Vect<f64, 2>],
|
||||
xlist_true: &[Vect<f64, 2>],
|
||||
dt_explicit: f64,
|
||||
dt_implicit: f64,
|
||||
dt_true: f64,
|
||||
) {
|
||||
let filepath = format!("target/{}.png", filename);
|
||||
let root = BitMapBackend::new(&filepath, CHART_SIZE).into_drawing_area();
|
||||
root.fill(&WHITE).unwrap();
|
||||
let mut chart = ChartBuilder::on(&root);
|
||||
if let Some(title) = title {
|
||||
chart.caption(
|
||||
title,
|
||||
FontDesc::new(FontFamily::Name("cantarell"), 28.0, FontStyle::Normal),
|
||||
);
|
||||
}
|
||||
let mut chart = chart
|
||||
.margin_right(12)
|
||||
.y_label_area_size(30)
|
||||
.x_label_area_size(30)
|
||||
.build_cartesian_2d(0.0f64..xlist_true.len() as f64 * dt_true, 0.0f64..1.)
|
||||
.unwrap();
|
||||
|
||||
chart.configure_mesh().x_desc("Time").draw().unwrap();
|
||||
|
||||
chart
|
||||
.draw_series(LineSeries::new(
|
||||
xlist_explicit
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, x)| (i as f64 * dt_explicit, x[0])),
|
||||
ShapeStyle::from(BLUE).stroke_width(3),
|
||||
))
|
||||
.unwrap()
|
||||
.label("Susceptible (explicit)")
|
||||
.legend(|(x, y)| {
|
||||
PathElement::new(
|
||||
vec![(x, y), (x + 20, y)],
|
||||
ShapeStyle::from(BLUE).stroke_width(3),
|
||||
)
|
||||
});
|
||||
|
||||
chart
|
||||
.draw_series(LineSeries::new(
|
||||
xlist_explicit
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, x)| (i as f64 * dt_explicit, x[1])),
|
||||
ShapeStyle::from(RED).stroke_width(3),
|
||||
))
|
||||
.unwrap()
|
||||
.label("Infected (explicit)")
|
||||
.legend(|(x, y)| {
|
||||
PathElement::new(
|
||||
vec![(x, y), (x + 20, y)],
|
||||
ShapeStyle::from(RED).stroke_width(3),
|
||||
)
|
||||
});
|
||||
|
||||
chart
|
||||
.draw_series(LineSeries::new(
|
||||
xlist_explicit
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, x)| (i as f64 * dt_explicit, s.pop - x[0] - x[1])),
|
||||
ShapeStyle::from(GREEN).stroke_width(3),
|
||||
))
|
||||
.unwrap()
|
||||
.label("Removed (explicit)")
|
||||
.legend(|(x, y)| {
|
||||
PathElement::new(
|
||||
vec![(x, y), (x + 20, y)],
|
||||
ShapeStyle::from(GREEN).stroke_width(3),
|
||||
)
|
||||
});
|
||||
|
||||
chart
|
||||
.draw_series(LineSeries::new(
|
||||
xlist_implicit
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, x)| (i as f64 * dt_implicit, x[0])),
|
||||
BLUE,
|
||||
))
|
||||
.unwrap()
|
||||
.label("Susceptible (implicit)")
|
||||
.legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], BLUE));
|
||||
|
||||
chart
|
||||
.draw_series(LineSeries::new(
|
||||
xlist_implicit
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, x)| (i as f64 * dt_implicit, x[1])),
|
||||
RED,
|
||||
))
|
||||
.unwrap()
|
||||
.label("Infected (implicit)")
|
||||
.legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], RED));
|
||||
|
||||
chart
|
||||
.draw_series(LineSeries::new(
|
||||
xlist_implicit
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, x)| (i as f64 * dt_implicit, s.pop - x[0] - x[1])),
|
||||
GREEN,
|
||||
))
|
||||
.unwrap()
|
||||
.label("Removed (implicit)")
|
||||
.legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], GREEN));
|
||||
|
||||
chart
|
||||
.draw_series(LineSeries::new(
|
||||
xlist_true
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, x)| (i as f64 * dt_true, x[0])),
|
||||
RGBColor(0, 0, 128),
|
||||
))
|
||||
.unwrap()
|
||||
.label("Susceptible (true)")
|
||||
.legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], RGBColor(0, 0, 128)));
|
||||
|
||||
chart
|
||||
.draw_series(LineSeries::new(
|
||||
xlist_true
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, x)| (i as f64 * dt_true, x[1])),
|
||||
RGBColor(128, 0, 0),
|
||||
))
|
||||
.unwrap()
|
||||
.label("Infected (true)")
|
||||
.legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], RGBColor(128, 0, 0)));
|
||||
|
||||
chart
|
||||
.draw_series(LineSeries::new(
|
||||
xlist_true
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, x)| (i as f64 * dt_true, s.pop - x[0] - x[1])),
|
||||
RGBColor(0, 128, 0),
|
||||
))
|
||||
.unwrap()
|
||||
.label("Removed (true)")
|
||||
.legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], RGBColor(0, 128, 0)));
|
||||
|
||||
chart
|
||||
.configure_series_labels()
|
||||
.background_style(WHITE.mix(0.8))
|
||||
.border_style(BLACK)
|
||||
.draw()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn draw_bike_chart(filename: &str, title: Option<&str>, xlists: &[(&str, &[f64])], dt: f64) {
|
||||
let max_x = xlists
|
||||
.iter()
|
||||
.map(|(_, xlist)| xlist.len())
|
||||
.max()
|
||||
.expect("at least one series expected");
|
||||
let max_y = *xlists
|
||||
.iter()
|
||||
.map(|(_, xlist)| {
|
||||
xlist
|
||||
.iter()
|
||||
.max_by(|a, b| a.total_cmp(b))
|
||||
.expect("at least one sample per series expected")
|
||||
})
|
||||
.max_by(|a, b| a.total_cmp(b))
|
||||
.unwrap();
|
||||
let filepath = format!("target/{}.png", filename);
|
||||
let root = BitMapBackend::new(&filepath, (640, 480)).into_drawing_area();
|
||||
root.fill(&WHITE).unwrap();
|
||||
let mut chart = ChartBuilder::on(&root);
|
||||
if let Some(title) = title {
|
||||
chart.caption(
|
||||
title,
|
||||
FontDesc::new(FontFamily::Name("cantarell"), 28.0, FontStyle::Normal),
|
||||
);
|
||||
}
|
||||
let mut chart = chart
|
||||
.margin_right(12)
|
||||
.margin_top(12)
|
||||
.y_label_area_size(30)
|
||||
.x_label_area_size(30)
|
||||
.build_cartesian_2d(0.0f64..max_x as f64 * dt, 0.0f64..max_y)
|
||||
.unwrap();
|
||||
|
||||
chart.configure_mesh().x_desc("Temps (s)").draw().unwrap();
|
||||
|
||||
for (list_i, (label, xlist)) in xlists.into_iter().enumerate() {
|
||||
chart
|
||||
.draw_series(LineSeries::new(
|
||||
xlist.iter().enumerate().map(|(i, x)| (i as f64 * dt, *x)),
|
||||
Palette100::pick(list_i + 1).stroke_width(2),
|
||||
))
|
||||
.unwrap()
|
||||
.label(*label)
|
||||
.legend(move |(x, y)| {
|
||||
PathElement::new(
|
||||
vec![(x, y), (x + 20, y)],
|
||||
Palette100::pick(list_i + 1).stroke_width(2),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
chart
|
||||
.configure_series_labels()
|
||||
.border_style(BLACK)
|
||||
.background_style(WHITE.mix(0.8))
|
||||
.label_font(("Libertinus Serif", 20))
|
||||
.draw()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn draw_bike_chart2(
|
||||
filename: &str,
|
||||
title: Option<&str>,
|
||||
xlists1: &[(&str, &[f64])],
|
||||
xlists2: &[(&str, &[f64])],
|
||||
dt: f64,
|
||||
) {
|
||||
let max_x = xlists1
|
||||
.iter()
|
||||
.chain(xlists2.iter())
|
||||
.map(|(_, xlist)| xlist.len())
|
||||
.max()
|
||||
.expect("at least one series expected");
|
||||
let max_y1 = *xlists1
|
||||
.iter()
|
||||
.map(|(_, xlist)| {
|
||||
xlist
|
||||
.iter()
|
||||
.max_by(|a, b| a.total_cmp(b))
|
||||
.expect("at least one sample per series expected")
|
||||
})
|
||||
.max_by(|a, b| a.total_cmp(b))
|
||||
.unwrap();
|
||||
let max_y2 = *xlists2
|
||||
.iter()
|
||||
.map(|(_, xlist)| {
|
||||
xlist
|
||||
.iter()
|
||||
.max_by(|a, b| a.total_cmp(b))
|
||||
.expect("at least one sample per series expected")
|
||||
})
|
||||
.max_by(|a, b| a.total_cmp(b))
|
||||
.unwrap();
|
||||
let filepath = format!("target/{}.png", filename);
|
||||
let root = BitMapBackend::new(&filepath, (640, 480)).into_drawing_area();
|
||||
root.fill(&WHITE).unwrap();
|
||||
let mut chart = ChartBuilder::on(&root);
|
||||
if let Some(title) = title {
|
||||
chart.caption(
|
||||
title,
|
||||
FontDesc::new(FontFamily::Name("cantarell"), 28.0, FontStyle::Normal),
|
||||
);
|
||||
}
|
||||
let mut chart = chart
|
||||
.margin_right(12)
|
||||
.margin_top(12)
|
||||
.y_label_area_size(50)
|
||||
.x_label_area_size(30)
|
||||
.right_y_label_area_size(50)
|
||||
.build_cartesian_2d(0.0f64..max_x as f64 * dt, 0.0f64..max_y1)
|
||||
.unwrap()
|
||||
.set_secondary_coord(0.0f64..max_x as f64 * dt, 0.0f64..max_y2);
|
||||
|
||||
chart
|
||||
.configure_mesh()
|
||||
.x_desc("Temps (s)")
|
||||
.y_desc("Vitesse (m/s)")
|
||||
.draw()
|
||||
.unwrap();
|
||||
chart
|
||||
.configure_secondary_axes()
|
||||
.y_desc("Freinage")
|
||||
.draw()
|
||||
.unwrap();
|
||||
|
||||
for (list_i, (label, xlist)) in xlists1.into_iter().enumerate() {
|
||||
chart
|
||||
.draw_series(LineSeries::new(
|
||||
xlist.iter().enumerate().map(|(i, x)| (i as f64 * dt, *x)),
|
||||
Palette100::pick(list_i + 1).stroke_width(2),
|
||||
))
|
||||
.unwrap()
|
||||
.label(*label)
|
||||
.legend(move |(x, y)| {
|
||||
PathElement::new(
|
||||
vec![(x, y), (x + 20, y)],
|
||||
Palette100::pick(list_i + 1).stroke_width(2),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
for (list_i, (label, xlist)) in (xlists1.len()..).zip(xlists2.into_iter()) {
|
||||
chart
|
||||
.draw_secondary_series(LineSeries::new(
|
||||
xlist.iter().enumerate().map(|(i, x)| (i as f64 * dt, *x)),
|
||||
Palette100::pick(list_i + 1).stroke_width(2),
|
||||
))
|
||||
.unwrap()
|
||||
.label(*label)
|
||||
.legend(move |(x, y)| {
|
||||
PathElement::new(
|
||||
vec![(x, y), (x + 20, y)],
|
||||
Palette100::pick(list_i + 1).stroke_width(2),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
chart
|
||||
.configure_series_labels()
|
||||
.border_style(BLACK)
|
||||
.background_style(WHITE.mix(0.8))
|
||||
.label_font(("Libertinus Serif", 20))
|
||||
.draw()
|
||||
.unwrap();
|
||||
}
|
||||
91
examples/old/live.rs
Normal file
91
examples/old/live.rs
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
use crate::{
|
||||
model::{Coloring, Model, Settings},
|
||||
solver::Solver,
|
||||
space::Space,
|
||||
};
|
||||
|
||||
use sdl2::{event::Event, keyboard::Keycode, pixels::PixelFormatEnum};
|
||||
use std::{
|
||||
sync::{Arc, RwLock, RwLockReadGuard},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
const FRAMEDUR: u64 = 30;
|
||||
|
||||
pub fn run<
|
||||
T: Copy,
|
||||
P,
|
||||
S: Settings,
|
||||
M: Model<T, S, D> + Coloring<T, P, [u8; 3], D>,
|
||||
V: Solver<T, S, M, D>,
|
||||
const D: usize,
|
||||
>(
|
||||
space: Arc<RwLock<Space<T, S, M, V, D>>>,
|
||||
) {
|
||||
let sdl_context = sdl2::init().unwrap();
|
||||
let video_subsystem = sdl_context.video().unwrap();
|
||||
|
||||
let size = space.read().unwrap().size;
|
||||
|
||||
let window = video_subsystem
|
||||
.window("Model", size.0 as u32, size.1 as u32)
|
||||
.resizable()
|
||||
.position_centered()
|
||||
.opengl()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let mut canvas = window.into_canvas().build().unwrap();
|
||||
let texture_creator = canvas.texture_creator();
|
||||
|
||||
let mut event_pump = sdl_context.event_pump().unwrap();
|
||||
let interval = Duration::from_millis(FRAMEDUR);
|
||||
|
||||
let mut image = vec![0u8; size.0 * size.1 * 3];
|
||||
|
||||
'running: loop {
|
||||
for event in event_pump.poll_iter() {
|
||||
match event {
|
||||
Event::Quit { .. }
|
||||
| Event::KeyDown {
|
||||
keycode: Some(Keycode::Escape),
|
||||
..
|
||||
} => {
|
||||
break 'running;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
render(space.read().unwrap(), &mut image);
|
||||
|
||||
let mut texture = texture_creator
|
||||
.create_texture_streaming(PixelFormatEnum::RGB24, size.0 as u32, size.1 as u32)
|
||||
.unwrap();
|
||||
texture.update(None, &image, size.0 * 3).unwrap();
|
||||
canvas.copy(&texture, None, None).unwrap();
|
||||
|
||||
canvas.present();
|
||||
|
||||
std::thread::sleep(interval);
|
||||
}
|
||||
}
|
||||
|
||||
fn render<
|
||||
T: Copy,
|
||||
P,
|
||||
S: Settings,
|
||||
M: Model<T, S, D> + Coloring<T, P, [u8; 3], D>,
|
||||
V: Solver<T, S, M, D>,
|
||||
const D: usize,
|
||||
>(
|
||||
space: RwLockReadGuard<Space<T, S, M, V, D>>,
|
||||
image: &mut Vec<u8>,
|
||||
) {
|
||||
image.resize(space.points.len() * 3, 0);
|
||||
if let Some(pre) = M::prepare(space.points.iter().map(|point| point.pos)) {
|
||||
for (i, point) in space.points.iter().enumerate() {
|
||||
image[i * 3..i * 3 + 3].copy_from_slice(&M::color(&pre, point.pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
676
examples/old/main.rs
Normal file
676
examples/old/main.rs
Normal file
|
|
@ -0,0 +1,676 @@
|
|||
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::<Vec<_>>()),
|
||||
Some(&path_sto.iter().map(|v| (v[0], v[1])).collect::<Vec<_>>()),
|
||||
);
|
||||
charts::plot_objective(
|
||||
"obj",
|
||||
None,
|
||||
optimizer.clone(),
|
||||
&xlist_true,
|
||||
Some(&path.iter().map(|v| (v[0], v[1])).collect::<Vec<_>>()),
|
||||
Some(&path_sto.iter().map(|v| (v[0], v[1])).collect::<Vec<_>>()),
|
||||
);
|
||||
|
||||
// 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::<f64> {
|
||||
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::<Vec<_>>(),
|
||||
)],
|
||||
dt,
|
||||
);
|
||||
charts::draw_bike_chart(
|
||||
"bike_v",
|
||||
None,
|
||||
&[(
|
||||
"v (m/s)",
|
||||
&xlist_true.iter().map(|x| x[1]).collect::<Vec<_>>(),
|
||||
)],
|
||||
dt,
|
||||
);
|
||||
charts::draw_bike_chart2(
|
||||
"bike_opti1",
|
||||
None,
|
||||
&[(
|
||||
"v (m/s)",
|
||||
&xlist_opti1.iter().map(|x| x[1]).collect::<Vec<_>>(),
|
||||
)],
|
||||
&[("b", &blist_opti1)],
|
||||
dt,
|
||||
);
|
||||
charts::draw_bike_chart(
|
||||
"bike_opti2",
|
||||
None,
|
||||
&[
|
||||
(
|
||||
"v (m/s) (arrière)",
|
||||
&xlist_opti1.iter().map(|x| x[1]).collect::<Vec<_>>(),
|
||||
),
|
||||
(
|
||||
"v (m/s) (arrière+avant)",
|
||||
&xlist_opti2.iter().map(|x| x[1]).collect::<Vec<_>>(),
|
||||
),
|
||||
],
|
||||
dt,
|
||||
);
|
||||
charts::draw_bike_chart(
|
||||
"bike_var_theta",
|
||||
None,
|
||||
&[
|
||||
(
|
||||
&format!("v (θ={}, B<0)", settings_true.th),
|
||||
&xlist_true.iter().map(|x| x[1]).collect::<Vec<_>>(),
|
||||
),
|
||||
(
|
||||
&format!("v (θ={}, B>0)", settings_greater_th.th),
|
||||
&xlist_greater_th.iter().map(|x| x[1]).collect::<Vec<_>>(),
|
||||
),
|
||||
],
|
||||
dt,
|
||||
);
|
||||
charts::draw_bike_chart(
|
||||
"bike_ode_v",
|
||||
None,
|
||||
&[(
|
||||
"v (m/s)",
|
||||
&xlist_ode.iter().map(|x| x[1]).collect::<Vec<_>>(),
|
||||
)],
|
||||
dt,
|
||||
);
|
||||
}
|
||||
595
examples/old/model.rs
Normal file
595
examples/old/model.rs
Normal file
|
|
@ -0,0 +1,595 @@
|
|||
use crate::utils::*;
|
||||
|
||||
use nalgebra::{base::*, matrix, vector};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub trait Settings {}
|
||||
|
||||
pub trait Model<T, S: Settings, const D: usize> {
|
||||
/// Returns f(x)
|
||||
fn f(&self, x: Vect<T, D>) -> Vect<T, D>;
|
||||
/// Returns df(x)/dx
|
||||
fn df(&self, x: Vect<T, D>) -> Mat<T, D, D>;
|
||||
fn get_settings(&self) -> &S;
|
||||
fn get_settings_mut(&mut self) -> &mut S;
|
||||
}
|
||||
|
||||
pub trait Coloring<T, P, C, const D: usize> {
|
||||
fn prepare<I: Iterator<Item = Vect<T, D>>>(iter: I) -> Option<P>;
|
||||
fn color(pre: &P, val: Vect<T, D>) -> C;
|
||||
}
|
||||
|
||||
pub struct MinMaxColoringInfo<T, const D: usize> {
|
||||
pub min: [T; D],
|
||||
pub max: [T; D],
|
||||
}
|
||||
|
||||
pub trait Constraint<T, const D: usize> {
|
||||
fn constrain_mut(&self, val: &mut Vect<T, D>);
|
||||
fn constrain(&self, mut val: Vect<T, D>) -> Vect<T, D> {
|
||||
self.constrain_mut(&mut val);
|
||||
val
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MinMaxConstraint<T, const D: usize> {
|
||||
pub min: [T; D],
|
||||
pub max: [T; D],
|
||||
}
|
||||
|
||||
impl<T: Copy + PartialOrd, const D: usize> Constraint<T, D> for MinMaxConstraint<T, D> {
|
||||
fn constrain_mut(&self, val: &mut Vect<T, D>) {
|
||||
for ((comp, min), max) in val.iter_mut().zip(self.min.iter()).zip(self.max.iter()) {
|
||||
*comp = num_traits::clamp(*comp, *min, *max)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Models
|
||||
|
||||
pub mod constrained {
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ConstrainedSettings<T, O, M, S> {
|
||||
pub constraint: O,
|
||||
pub model: M,
|
||||
pub _p: PhantomData<(T, S)>,
|
||||
}
|
||||
|
||||
impl<T, O, M, S> Settings for ConstrainedSettings<T, O, M, S> {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Constrained<T, O, M, S> {
|
||||
pub s: ConstrainedSettings<T, O, M, S>,
|
||||
}
|
||||
|
||||
impl<T, O: Constraint<T, D>, M: Model<T, S, D>, S: Settings, const D: usize>
|
||||
Model<T, ConstrainedSettings<T, O, M, S>, D> for Constrained<T, O, M, S>
|
||||
{
|
||||
fn f(&self, x: Vect<T, D>) -> Vect<T, D> {
|
||||
self.s.constraint.constrain(self.s.model.f(x))
|
||||
}
|
||||
fn df(&self, x: Vect<T, D>) -> Mat<T, D, D> {
|
||||
self.s.model.df(x)
|
||||
}
|
||||
fn get_settings(&self) -> &ConstrainedSettings<T, O, M, S> {
|
||||
&self.s
|
||||
}
|
||||
fn get_settings_mut(&mut self) -> &mut ConstrainedSettings<T, O, M, S> {
|
||||
&mut self.s
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, O, M: Coloring<T, P, C, D>, S, P, C, const D: usize> Coloring<T, P, C, D>
|
||||
for Constrained<T, O, M, S>
|
||||
{
|
||||
fn prepare<I: Iterator<Item = Vect<T, D>>>(iter: I) -> Option<P> {
|
||||
M::prepare(iter)
|
||||
}
|
||||
fn color(pre: &P, val: Vect<T, D>) -> C {
|
||||
M::color(pre, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// SIR model without vital dynamics
|
||||
pub mod sir {
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SirSettings<T> {
|
||||
/// Transmission probability
|
||||
pub beta: T,
|
||||
/// Removal probability
|
||||
pub gamma: T,
|
||||
pub pop: T,
|
||||
}
|
||||
|
||||
impl<T> Settings for SirSettings<T> {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Sir<T> {
|
||||
pub s: SirSettings<T>,
|
||||
}
|
||||
|
||||
impl Model<f64, SirSettings<f64>, 2> for Sir<f64> {
|
||||
fn f(&self, x: Vector2<f64>) -> Vector2<f64> {
|
||||
vector![
|
||||
-self.s.beta * x[0] * x[1] / self.s.pop,
|
||||
self.s.beta * x[0] * x[1] / self.s.pop - self.s.gamma * x[1]
|
||||
]
|
||||
}
|
||||
fn df(&self, x: Vector2<f64>) -> Matrix2<f64> {
|
||||
matrix![
|
||||
-self.s.beta*x[1]/self.s.pop, -self.s.beta*x[0]/self.s.pop;
|
||||
self.s.beta*x[1]/self.s.pop, self.s.beta*x[0]/self.s.pop-self.s.gamma
|
||||
]
|
||||
}
|
||||
fn get_settings(&self) -> &SirSettings<f64> {
|
||||
&self.s
|
||||
}
|
||||
fn get_settings_mut(&mut self) -> &mut SirSettings<f64> {
|
||||
&mut self.s
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<SirSettings<T>> for Vect<T, 3> {
|
||||
fn from(s: SirSettings<T>) -> Self {
|
||||
vector![s.beta, s.gamma, s.pop]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar + Copy> From<Vect<T, 3>> for SirSettings<T> {
|
||||
fn from(v: Vect<T, 3>) -> Self {
|
||||
Self {
|
||||
beta: v[0],
|
||||
gamma: v[1],
|
||||
pop: v[2],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// SIR model with vital dynamics, constant population
|
||||
pub mod sirv {
|
||||
use super::*;
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SirvSettings<T> {
|
||||
/// Transmission probability
|
||||
pub beta: T,
|
||||
/// Removal probability
|
||||
pub gamma: T,
|
||||
/// Birth rate
|
||||
pub lambda: T,
|
||||
/// Death rate
|
||||
pub mu: T,
|
||||
pub pop: T,
|
||||
}
|
||||
|
||||
impl<T> Settings for SirvSettings<T> {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Sirv<T> {
|
||||
pub s: SirvSettings<T>,
|
||||
}
|
||||
|
||||
impl Model<f64, SirvSettings<f64>, 2> for Sirv<f64> {
|
||||
fn f(&self, x: Vector2<f64>) -> Vector2<f64> {
|
||||
vector![
|
||||
self.s.lambda - self.s.beta * x[0] * x[1] / self.s.pop - self.s.mu * x[0],
|
||||
self.s.beta * x[0] * x[1] / self.s.pop - self.s.gamma * x[1] - self.s.mu * x[1]
|
||||
]
|
||||
}
|
||||
fn df(&self, x: Vector2<f64>) -> Matrix2<f64> {
|
||||
matrix![
|
||||
-self.s.beta*x[1]/self.s.pop - self.s.mu, -self.s.beta*x[0]/self.s.pop;
|
||||
self.s.beta*x[1]/self.s.pop, self.s.beta*x[0]/self.s.pop-self.s.gamma - self.s.mu
|
||||
]
|
||||
}
|
||||
fn get_settings(&self) -> &SirvSettings<f64> {
|
||||
&self.s
|
||||
}
|
||||
fn get_settings_mut(&mut self) -> &mut SirvSettings<f64> {
|
||||
&mut self.s
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<SirvSettings<T>> for Vect<T, 5> {
|
||||
fn from(s: SirvSettings<T>) -> Self {
|
||||
vector![s.beta, s.gamma, s.lambda, s.mu, s.pop]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar + Copy> From<Vect<T, 5>> for SirvSettings<T> {
|
||||
fn from(v: Vect<T, 5>) -> Self {
|
||||
Self {
|
||||
beta: v[0],
|
||||
gamma: v[1],
|
||||
lambda: v[2],
|
||||
mu: v[3],
|
||||
pop: v[4],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Giraffe
|
||||
pub mod giraffe {
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GiraffeSettings<T> {
|
||||
pub a_a: T,
|
||||
pub a_b: T,
|
||||
pub b_a: T,
|
||||
pub b_b: T,
|
||||
}
|
||||
|
||||
impl<T> Settings for GiraffeSettings<T> {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Giraffe<T> {
|
||||
pub s: GiraffeSettings<T>,
|
||||
}
|
||||
|
||||
impl Model<f64, GiraffeSettings<f64>, 2> for Giraffe<f64> {
|
||||
fn f(&self, x: Vector2<f64>) -> Vector2<f64> {
|
||||
vector![
|
||||
self.s.a_a * x[0] + self.s.b_a * x[1],
|
||||
self.s.a_b * x[0] + self.s.b_b * x[1]
|
||||
]
|
||||
}
|
||||
fn df(&self, _x: Vector2<f64>) -> Matrix2<f64> {
|
||||
matrix![
|
||||
self.s.a_a, self.s.b_a;
|
||||
self.s.a_b, self.s.b_b
|
||||
]
|
||||
}
|
||||
fn get_settings(&self) -> &GiraffeSettings<f64> {
|
||||
&self.s
|
||||
}
|
||||
fn get_settings_mut(&mut self) -> &mut GiraffeSettings<f64> {
|
||||
&mut self.s
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<GiraffeSettings<T>> for Vect<T, 4> {
|
||||
fn from(s: GiraffeSettings<T>) -> Self {
|
||||
vector![s.a_a, s.a_b, s.b_a, s.b_b]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar + Copy> From<Vect<T, 4>> for GiraffeSettings<T> {
|
||||
fn from(v: Vect<T, 4>) -> Self {
|
||||
Self {
|
||||
a_a: v[0],
|
||||
a_b: v[1],
|
||||
b_a: v[2],
|
||||
b_b: v[3],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*impl Coloring<f64, MinMaxColoringInfo<f64, 2>, [u8; 3], 2> for Giraffe<f64> {
|
||||
fn prepare<I: Iterator<Item = Vect<f64, 2>>>(
|
||||
iter: I,
|
||||
) -> Option<MinMaxColoringInfo<f64, 2>> {
|
||||
let mut r = MinMaxColoringInfo {
|
||||
min: [1.0, 1.0],
|
||||
max: [0.0, 0.0],
|
||||
};
|
||||
for val in iter {
|
||||
if val[0] < r.min[0] {
|
||||
r.min[0] = val[0];
|
||||
}
|
||||
if val[0] > r.max[0] {
|
||||
r.max[0] = val[0];
|
||||
}
|
||||
if val[1] < r.min[1] {
|
||||
r.min[1] = val[1];
|
||||
}
|
||||
if val[1] > r.max[1] {
|
||||
r.max[1] = val[1];
|
||||
}
|
||||
}
|
||||
if r.min[0] == r.max[0] || r.min[1] == r.max[1] {
|
||||
None
|
||||
} else {
|
||||
Some(r)
|
||||
}
|
||||
}
|
||||
fn color(pre: &MinMaxColoringInfo<f64, 2>, val: Vect<f64, 2>) -> [u8; 3] {
|
||||
[
|
||||
((val[1] - pre.min[1]).abs() / (pre.max[1] - pre.min[1]).abs() * 255.0) as u8,
|
||||
0,
|
||||
((val[0] - pre.min[0]).abs() / (pre.max[0] - pre.min[0]).abs() * 255.0) as u8,
|
||||
]
|
||||
}*/
|
||||
|
||||
impl Coloring<f64, (), [u8; 3], 2> for Giraffe<f64> {
|
||||
fn prepare<I: Iterator<Item = Vect<f64, 2>>>(_iter: I) -> Option<()> {
|
||||
Some(())
|
||||
}
|
||||
fn color(_pre: &(), val: Vect<f64, 2>) -> [u8; 3] {
|
||||
[
|
||||
(val[0] * 255.0) as u8,
|
||||
((val[0] + val[1]) * 127.5) as u8,
|
||||
255 - (val[1] * 255.0) as u8,
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// https://arxiv.org/abs/2210.05227
|
||||
pub mod lyfe {
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct LyfeSettings<T> {
|
||||
pub da: T,
|
||||
pub db: T,
|
||||
pub f: T,
|
||||
pub r: T,
|
||||
}
|
||||
|
||||
impl<T> Settings for LyfeSettings<T> {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Lyfe<T> {
|
||||
pub s: LyfeSettings<T>,
|
||||
}
|
||||
|
||||
impl Model<f64, LyfeSettings<f64>, 2> for Lyfe<f64> {
|
||||
fn f(&self, x: Vector2<f64>) -> Vector2<f64> {
|
||||
vector![
|
||||
x[0] * x[1].powi(2) + self.s.f * (1.0 - x[0]),
|
||||
x[0] * x[1].powi(2) - (self.s.f + self.s.r) * x[1]
|
||||
]
|
||||
}
|
||||
fn df(&self, x: Vector2<f64>) -> Matrix2<f64> {
|
||||
matrix![
|
||||
x[1].powi(2)-self.s.f, 2.0*x[0]*x[1];
|
||||
x[1].powi(2), 2.0*x[0]*x[1] - (self.s.f+self.s.r)
|
||||
]
|
||||
}
|
||||
fn get_settings(&self) -> &LyfeSettings<f64> {
|
||||
&self.s
|
||||
}
|
||||
fn get_settings_mut(&mut self) -> &mut LyfeSettings<f64> {
|
||||
&mut self.s
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<LyfeSettings<T>> for Vect<T, 4> {
|
||||
fn from(s: LyfeSettings<T>) -> Self {
|
||||
vector![s.da, s.db, s.f, s.r]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar + Copy> From<Vect<T, 4>> for LyfeSettings<T> {
|
||||
fn from(v: Vect<T, 4>) -> Self {
|
||||
Self {
|
||||
da: v[0],
|
||||
db: v[1],
|
||||
f: v[2],
|
||||
r: v[3],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*impl Coloring<f64, MinMaxColoringInfo<f64, 2>, [u8; 3], 2> for Lyfe<f64> {
|
||||
fn prepare<I: Iterator<Item = Vect<f64, 2>>>(
|
||||
iter: I,
|
||||
) -> Option<MinMaxColoringInfo<f64, 2>> {
|
||||
let mut r = MinMaxColoringInfo {
|
||||
min: [1.0, 1.0],
|
||||
max: [0.0, 0.0],
|
||||
};
|
||||
for val in iter {
|
||||
if val[0] < r.min[0] {
|
||||
r.min[0] = val[0];
|
||||
}
|
||||
if val[0] > r.max[0] {
|
||||
r.max[0] = val[0];
|
||||
}
|
||||
if val[1] < r.min[1] {
|
||||
r.min[1] = val[1];
|
||||
}
|
||||
if val[1] > r.max[1] {
|
||||
r.max[1] = val[1];
|
||||
}
|
||||
}
|
||||
if r.min[0] == r.max[0] || r.min[1] == r.max[1] {
|
||||
None
|
||||
} else {
|
||||
Some(r)
|
||||
}
|
||||
}
|
||||
fn color(pre: &MinMaxColoringInfo<f64, 2>, val: Vect<f64, 2>) -> [u8; 3] {
|
||||
[
|
||||
((val[1] - pre.min[1]).abs() / (pre.max[1] - pre.min[1]).abs() * 255.0) as u8,
|
||||
0,
|
||||
((val[0] - pre.min[0]).abs() / (pre.max[0] - pre.min[0]).abs() * 255.0) as u8,
|
||||
]
|
||||
}*/
|
||||
|
||||
impl Coloring<f64, (), [u8; 3], 2> for Lyfe<f64> {
|
||||
fn prepare<I: Iterator<Item = Vect<f64, 2>>>(_iter: I) -> Option<()> {
|
||||
Some(())
|
||||
}
|
||||
fn color(_pre: &(), val: Vect<f64, 2>) -> [u8; 3] {
|
||||
[
|
||||
(val[0] * 255.0) as u8,
|
||||
((val[0] + val[1]) * 127.5) as u8,
|
||||
255 - (val[1] * 255.0) as u8,
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod bike {
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BikeSettings<T> {
|
||||
/// Pénétration dans l'air
|
||||
pub cx: T,
|
||||
/// Gravité
|
||||
pub g: T,
|
||||
/// Masse
|
||||
pub m: T,
|
||||
/// Pente
|
||||
pub th: T,
|
||||
}
|
||||
|
||||
impl<T> Settings for BikeSettings<T> {}
|
||||
|
||||
fn b(x: f64, v: f64) -> f64 {
|
||||
80.
|
||||
}
|
||||
|
||||
fn db(x: f64, v: f64) -> Vector2<f64> {
|
||||
vector![0., 0.]
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Bike<T> {
|
||||
pub s: BikeSettings<T>,
|
||||
}
|
||||
|
||||
impl Model<f64, BikeSettings<f64>, 2> for Bike<f64> {
|
||||
fn f(&self, x: Vector2<f64>) -> Vector2<f64> {
|
||||
vector![
|
||||
x[1],
|
||||
self.s.g * self.s.th.sin() - (self.s.cx * x[1].powi(2) + b(x[0], x[1])) / self.s.m
|
||||
]
|
||||
}
|
||||
fn df(&self, x: Vector2<f64>) -> Matrix2<f64> {
|
||||
let dbx = db(x[0], x[1]);
|
||||
matrix![
|
||||
0., 1.;
|
||||
-dbx[0]/self.s.m, -(2.*self.s.cx*x[1]+dbx[1])/self.s.m
|
||||
]
|
||||
}
|
||||
fn get_settings(&self) -> &BikeSettings<f64> {
|
||||
&self.s
|
||||
}
|
||||
fn get_settings_mut(&mut self) -> &mut BikeSettings<f64> {
|
||||
&mut self.s
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<BikeSettings<T>> for Vect<T, 4> {
|
||||
fn from(s: BikeSettings<T>) -> Self {
|
||||
vector![s.cx, s.g, s.m, s.th]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Scalar + Copy> From<Vect<T, 4>> for BikeSettings<T> {
|
||||
fn from(v: Vect<T, 4>) -> Self {
|
||||
Self {
|
||||
cx: v[0],
|
||||
g: v[1],
|
||||
m: v[2],
|
||||
th: v[3],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*impl Coloring<f64, MinMaxColoringInfo<f64, 2>, [u8; 3], 2> for Lyfe<f64> {
|
||||
fn prepare<I: Iterator<Item = Vect<f64, 2>>>(
|
||||
iter: I,
|
||||
) -> Option<MinMaxColoringInfo<f64, 2>> {
|
||||
let mut r = MinMaxColoringInfo {
|
||||
min: [1.0, 1.0],
|
||||
max: [0.0, 0.0],
|
||||
};
|
||||
for val in iter {
|
||||
if val[0] < r.min[0] {
|
||||
r.min[0] = val[0];
|
||||
}
|
||||
if val[0] > r.max[0] {
|
||||
r.max[0] = val[0];
|
||||
}
|
||||
if val[1] < r.min[1] {
|
||||
r.min[1] = val[1];
|
||||
}
|
||||
if val[1] > r.max[1] {
|
||||
r.max[1] = val[1];
|
||||
}
|
||||
}
|
||||
if r.min[0] == r.max[0] || r.min[1] == r.max[1] {
|
||||
None
|
||||
} else {
|
||||
Some(r)
|
||||
}
|
||||
}
|
||||
fn color(pre: &MinMaxColoringInfo<f64, 2>, val: Vect<f64, 2>) -> [u8; 3] {
|
||||
[
|
||||
((val[1] - pre.min[1]).abs() / (pre.max[1] - pre.min[1]).abs() * 255.0) as u8,
|
||||
0,
|
||||
((val[0] - pre.min[0]).abs() / (pre.max[0] - pre.min[0]).abs() * 255.0) as u8,
|
||||
]
|
||||
}*/
|
||||
|
||||
impl Coloring<f64, (), [u8; 3], 2> for Bike<f64> {
|
||||
fn prepare<I: Iterator<Item = Vect<f64, 2>>>(_iter: I) -> Option<()> {
|
||||
Some(())
|
||||
}
|
||||
fn color(_pre: &(), val: Vect<f64, 2>) -> [u8; 3] {
|
||||
[
|
||||
(val[0] * 255.0) as u8,
|
||||
((val[0] + val[1]) * 127.5) as u8,
|
||||
255 - (val[1] * 255.0) as u8,
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod bike2 {
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BikeSettings<T> {
|
||||
/// Pénétration dans l'air
|
||||
pub cx: T,
|
||||
/// Gravité
|
||||
pub g: T,
|
||||
/// Masse
|
||||
pub m: T,
|
||||
/// Pente
|
||||
pub th: T,
|
||||
/// (x, v, settings) -> ((b1, b2), (b1', b2'))
|
||||
pub b: fn(T, T, &BikeSettings<T>) -> ((T, T), (T, T)),
|
||||
}
|
||||
impl<T> Settings for BikeSettings<T> {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Bike<T> {
|
||||
pub s: BikeSettings<T>,
|
||||
}
|
||||
|
||||
impl Model<f64, BikeSettings<f64>, 2> for Bike<f64> {
|
||||
fn f(&self, x: Vector2<f64>) -> Vector2<f64> {
|
||||
let ((b1, b2), (_db1, _db2)) = (self.s.b)(x[0], x[1], &self.s);
|
||||
vector![
|
||||
x[1],
|
||||
self.s.g * self.s.th.sin() - (self.s.cx * x[1].powi(2) + b1 + b2) / self.s.m
|
||||
]
|
||||
}
|
||||
fn df(&self, x: Vector2<f64>) -> Matrix2<f64> {
|
||||
let ((_b1, _b2), (db1, db2)) = (self.s.b)(x[0], x[1], &self.s);
|
||||
matrix![
|
||||
0., 1.;
|
||||
0., -(2.*self.s.cx*x[1]+db1+db2)/self.s.m
|
||||
]
|
||||
}
|
||||
fn get_settings(&self) -> &BikeSettings<f64> {
|
||||
&self.s
|
||||
}
|
||||
fn get_settings_mut(&mut self) -> &mut BikeSettings<f64> {
|
||||
&mut self.s
|
||||
}
|
||||
}
|
||||
}
|
||||
399
examples/old/opti.rs
Normal file
399
examples/old/opti.rs
Normal file
|
|
@ -0,0 +1,399 @@
|
|||
use crate::{model::*, solver::*, utils::*};
|
||||
|
||||
use nalgebra::{base::*, ComplexField};
|
||||
use num_traits::{FromPrimitive, Zero};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub fn newton<
|
||||
T: Copy + Scalar + ComplexField<RealField = T> + PartialOrd,
|
||||
S: Settings,
|
||||
const D: usize,
|
||||
>(
|
||||
model: &impl Model<T, S, D>,
|
||||
x0: Vect<T, D>,
|
||||
dt: T,
|
||||
tol: T,
|
||||
niters: usize,
|
||||
) -> Vect<T, D>
|
||||
where
|
||||
Const<D>: ToTypenum + DimMin<Const<D>, Output = Const<D>>,
|
||||
{
|
||||
let mut x = x0;
|
||||
|
||||
for _ in 0..niters {
|
||||
if let Some(m) = (Mat::<T, D, D>::identity() - model.df(x) * dt).try_inverse() {
|
||||
let dx = m * (x - x0 - model.f(x) * dt);
|
||||
if dx.norm() < tol {
|
||||
break;
|
||||
}
|
||||
x -= dx;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
x
|
||||
}
|
||||
|
||||
/// Slower version using a linear system.
|
||||
pub fn _newton_slow<
|
||||
T: Copy + Scalar + ComplexField<RealField = T> + PartialOrd,
|
||||
S: Settings,
|
||||
const D: usize,
|
||||
>(
|
||||
model: &impl Model<T, S, D>,
|
||||
x0: Vect<T, D>,
|
||||
dt: T,
|
||||
tol: T,
|
||||
niters: usize,
|
||||
) -> Vect<T, D>
|
||||
where
|
||||
Const<D>: ToTypenum + DimMin<Const<D>, Output = Const<D>>,
|
||||
{
|
||||
let mut x = x0;
|
||||
|
||||
for _ in 0..niters {
|
||||
let fi = model.f(x);
|
||||
let dfi = model.df(x);
|
||||
let g = x0 + fi * dt - x;
|
||||
let dgdx = dfi * dt - Mat::<T, D, D>::identity();
|
||||
if let Some(dx) = dgdx.lu().solve(&g) {
|
||||
if dx.norm() < tol {
|
||||
break;
|
||||
}
|
||||
x -= dx;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
x
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GradientDescentOptimizer<
|
||||
T,
|
||||
S: Settings,
|
||||
M: Model<T, S, D> + Clone,
|
||||
R: Solver<T, S, M, D>,
|
||||
const D: usize,
|
||||
const DS: usize,
|
||||
> {
|
||||
pub model: M,
|
||||
pub solver: R,
|
||||
_ph: PhantomData<(T, S)>,
|
||||
}
|
||||
|
||||
impl<
|
||||
T: Copy + ComplexField<RealField = T> + FromPrimitive,
|
||||
S: Settings + Clone + Into<Vect<T, DS>> + From<Vect<T, DS>>,
|
||||
M: Model<T, S, D> + Clone,
|
||||
R: Solver<T, S, M, D>,
|
||||
const D: usize,
|
||||
const DS: usize,
|
||||
> GradientDescentOptimizer<T, S, M, R, D, DS>
|
||||
where
|
||||
Vect<T, DS>: std::ops::DivAssign<T> + std::ops::Mul<T, Output = Vect<T, DS>>,
|
||||
{
|
||||
pub fn new(model: M, solver: R) -> Self {
|
||||
Self {
|
||||
model,
|
||||
solver,
|
||||
_ph: PhantomData {},
|
||||
}
|
||||
}
|
||||
|
||||
/// Distance between f(x) and y_true, that we want to minimize
|
||||
pub fn objective(&self, model: &M, x: Vect<T, D>, y_true: Vect<T, D>) -> T {
|
||||
(self.solver.f(model, x) - y_true).norm()
|
||||
}
|
||||
|
||||
/// Return gradient of the objective function
|
||||
/// (opposite direction for Settings in order to make f(x) closer to y_true)
|
||||
///
|
||||
/// `free_settings` gives the indices of the settings we can change.
|
||||
/// For example, if all the settings can change, set it to `0..DS`.
|
||||
pub fn objective_gradient(
|
||||
&self,
|
||||
x: Vect<T, D>,
|
||||
y_true: Vect<T, D>,
|
||||
ep: T,
|
||||
free_settings: impl Iterator<Item = usize>,
|
||||
) -> Vect<T, DS> {
|
||||
let diff = self.objective(&self.model, x, y_true);
|
||||
let mut model = self.model.clone();
|
||||
let s: Vect<T, DS> = model.get_settings().clone().into();
|
||||
let mut si = s;
|
||||
let mut ds = Vect::<T, DS>::zero();
|
||||
for i in free_settings {
|
||||
si[i] += ep;
|
||||
*model.get_settings_mut() = si.into();
|
||||
ds[i] = (self.objective(&model, x, y_true) - diff) / ep;
|
||||
si[i] = s[i];
|
||||
}
|
||||
ds
|
||||
}
|
||||
|
||||
pub fn objective_gradient_batch(
|
||||
&self,
|
||||
ylist_true: &[Vect<T, D>],
|
||||
ep: T,
|
||||
free_settings: impl Iterator<Item = usize> + Clone,
|
||||
) -> Vect<T, DS> {
|
||||
let nsamples = T::from_usize(ylist_true.len() - 1).unwrap();
|
||||
|
||||
let mut ds_batch = Vect::<T, DS>::zero();
|
||||
for (x, y_true) in ylist_true.iter().zip(ylist_true.iter().skip(1)) {
|
||||
let ds = self.objective_gradient(*x, *y_true, ep, free_settings.clone());
|
||||
ds_batch += ds;
|
||||
}
|
||||
ds_batch / nsamples
|
||||
}
|
||||
|
||||
/// Calibrate settings using batch GD
|
||||
pub fn calibrate_batch(
|
||||
&mut self,
|
||||
ylist_true: &[Vect<T, D>],
|
||||
ep: T,
|
||||
rate: T,
|
||||
niters: usize,
|
||||
free_settings: impl Iterator<Item = usize> + Clone,
|
||||
) {
|
||||
for _ in 0..niters {
|
||||
let ds_batch = self.objective_gradient_batch(ylist_true, ep, free_settings.clone());
|
||||
|
||||
// ds_batch is the mean of the gradient of the objective function for each sample
|
||||
let mut s: Vect<T, DS> = self.model.get_settings().clone().into();
|
||||
s -= ds_batch * rate;
|
||||
*self.model.get_settings_mut() = s.into();
|
||||
}
|
||||
}
|
||||
|
||||
/// Calibrate settings using stochastic GD
|
||||
pub fn calibrate_stochastic(
|
||||
&mut self,
|
||||
ylist_true: &[Vect<T, D>],
|
||||
ep: T,
|
||||
rate: T,
|
||||
niters: usize,
|
||||
free_settings: impl Iterator<Item = usize> + Clone,
|
||||
) {
|
||||
for _ in 0..niters {
|
||||
for (x, y_true) in ylist_true.iter().zip(ylist_true.iter().skip(1)) {
|
||||
let ds = self.objective_gradient(*x, *y_true, ep, free_settings.clone());
|
||||
|
||||
let mut s: Vect<T, DS> = self.model.get_settings().clone().into();
|
||||
s -= ds * rate;
|
||||
*self.model.get_settings_mut() = s.into();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Calibrate settings using batch GD and record path and error
|
||||
///
|
||||
/// Returns (path, error)
|
||||
pub fn calibrate_batch_record(
|
||||
&mut self,
|
||||
ylist_true: &[Vect<T, D>],
|
||||
ep: T,
|
||||
rate: T,
|
||||
niters: usize,
|
||||
free_settings: impl Iterator<Item = usize> + Clone,
|
||||
) -> (Vec<Vect<T, DS>>, Vec<T>)
|
||||
where
|
||||
T: PartialOrd,
|
||||
{
|
||||
let mut path = Vec::with_capacity(niters + 1);
|
||||
path.push(self.model.get_settings().clone().into());
|
||||
let mut errorlist = Vec::with_capacity(niters + 1);
|
||||
errorlist.push(self.objective_batch(&self.model, ylist_true));
|
||||
|
||||
for _ in 0..niters {
|
||||
let ds_batch = self.objective_gradient_batch(ylist_true, ep, free_settings.clone());
|
||||
|
||||
// ds_batch is the mean of the gradient of the objective function for each sample
|
||||
let mut s: Vect<T, DS> = self.model.get_settings().clone().into();
|
||||
s -= ds_batch * rate;
|
||||
path.push(s);
|
||||
*self.model.get_settings_mut() = s.into();
|
||||
|
||||
let error = self.objective_batch(&self.model, ylist_true);
|
||||
if error > *errorlist.last().unwrap() {
|
||||
path.pop();
|
||||
*self.model.get_settings_mut() = (*path.last().unwrap()).into();
|
||||
break;
|
||||
}
|
||||
errorlist.push(error);
|
||||
}
|
||||
(path, errorlist)
|
||||
}
|
||||
|
||||
/// Calibrate settings using stochastic GD and record path and error
|
||||
///
|
||||
/// Returns (path, error)
|
||||
pub fn calibrate_stochastic_record(
|
||||
&mut self,
|
||||
ylist_true: &[Vect<T, D>],
|
||||
ep: T,
|
||||
rate: T,
|
||||
niters: usize,
|
||||
free_settings: impl Iterator<Item = usize> + Clone,
|
||||
) -> (Vec<Vect<T, DS>>, Vec<T>)
|
||||
where
|
||||
T: PartialOrd,
|
||||
{
|
||||
let mut path = Vec::with_capacity(niters * (niters - 1) + 1);
|
||||
path.push(self.model.get_settings().clone().into());
|
||||
let mut errorlist = Vec::with_capacity(niters * (niters - 1) + 1);
|
||||
errorlist.push(self.objective_batch(&self.model, ylist_true));
|
||||
|
||||
for _ in 0..niters {
|
||||
for (x, y_true) in ylist_true.iter().zip(ylist_true.iter().skip(1)) {
|
||||
let ds = self.objective_gradient(*x, *y_true, ep, free_settings.clone());
|
||||
|
||||
let mut s: Vect<T, DS> = self.model.get_settings().clone().into();
|
||||
s -= ds * rate;
|
||||
path.push(s);
|
||||
*self.model.get_settings_mut() = s.into();
|
||||
|
||||
let error = self.objective_batch(&self.model, ylist_true);
|
||||
if error > *errorlist.last().unwrap() {
|
||||
path.pop();
|
||||
*self.model.get_settings_mut() = (*path.last().unwrap()).into();
|
||||
continue;
|
||||
}
|
||||
errorlist.push(error);
|
||||
}
|
||||
}
|
||||
(path, errorlist)
|
||||
}
|
||||
|
||||
/// Mean of the objective function on all the samples
|
||||
pub fn objective_batch(&self, model: &M, ylist_true: &[Vect<T, D>]) -> T {
|
||||
let nsamples = T::from_usize(ylist_true.len() - 1).unwrap();
|
||||
|
||||
let mut obj_batch = T::zero();
|
||||
for (x, y_true) in ylist_true.iter().zip(ylist_true.iter().skip(1)) {
|
||||
obj_batch += self.objective(model, *x, *y_true);
|
||||
}
|
||||
obj_batch / nsamples
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use nalgebra::vector;
|
||||
use rand::Rng;
|
||||
|
||||
/// This test can fail, but should succeed most of the time
|
||||
#[test]
|
||||
fn test_objective_gradient_convergence() {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let ep0 = 0.01;
|
||||
let nep = 20;
|
||||
let ntests = 1000;
|
||||
|
||||
let mut fail_spikes = 0;
|
||||
let mut fail_d = 0;
|
||||
|
||||
for _ in 0..ntests {
|
||||
let model = sir::Sir {
|
||||
s: sir::SirSettings {
|
||||
beta: rng.gen(),
|
||||
gamma: rng.gen(),
|
||||
pop: 1.0,
|
||||
},
|
||||
};
|
||||
let solver = ImplicitEulerSolver {
|
||||
dt: 0.1,
|
||||
tol: 0.000001,
|
||||
niters: 100,
|
||||
};
|
||||
let optimizer = GradientDescentOptimizer::new(model, solver);
|
||||
let x = rng.gen();
|
||||
let y_true = rng.gen();
|
||||
|
||||
let mut g = optimizer.objective_gradient(x, y_true, ep0, 0..2);
|
||||
let mut d = f64::MAX;
|
||||
let mut spikes = 5;
|
||||
for ep in (1..nep).map(|i| ep0 / 2.0.powi(i)) {
|
||||
let ng = optimizer.objective_gradient(x, y_true, dbg!(ep), 0..2);
|
||||
let nd = (dbg!(ng) - g).norm();
|
||||
if nd.is_zero() {
|
||||
break;
|
||||
}
|
||||
// Allow obj' having a local minimum between s and s+ep
|
||||
if dbg!(nd) >= dbg!(d) {
|
||||
if spikes == 0 {
|
||||
fail_spikes += 1;
|
||||
break;
|
||||
}
|
||||
spikes -= 1;
|
||||
}
|
||||
g = ng;
|
||||
d = nd;
|
||||
}
|
||||
// d should be very small
|
||||
if d > 10.0 * ep0 / 2.0.powi(nep - 1) {
|
||||
fail_d += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let prop_fail_spikes = fail_spikes as f64 / ntests as f64;
|
||||
let prop_fail_d = fail_d as f64 / ntests as f64;
|
||||
println!("Fail spikes: {} %", prop_fail_spikes * 100.0);
|
||||
println!("Fail d: {} %", prop_fail_d * 100.0);
|
||||
assert!(prop_fail_spikes < 0.015);
|
||||
assert!(prop_fail_d < 0.0015);
|
||||
}
|
||||
|
||||
// TODO fix
|
||||
//#[test]
|
||||
fn _test_objective_gradient_direction() {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let niters: usize = 1000;
|
||||
let x0 = vector![0.99, 0.01];
|
||||
let dt = 0.1;
|
||||
|
||||
// Generate "true" data
|
||||
let settings_true = sir::SirSettings {
|
||||
beta: rng.gen(),
|
||||
gamma: rng.gen(),
|
||||
pop: 1.0,
|
||||
};
|
||||
let model = sir::Sir {
|
||||
s: dbg!(settings_true),
|
||||
};
|
||||
let solver = ImplicitEulerSolver {
|
||||
dt,
|
||||
tol: 0.000001,
|
||||
niters: 100,
|
||||
};
|
||||
let mut optimizer = GradientDescentOptimizer::new(model, solver);
|
||||
let mut xlist_true = Vec::with_capacity(niters + 1);
|
||||
xlist_true.push(x0);
|
||||
let mut x = x0;
|
||||
for _ in 0..niters {
|
||||
x = optimizer.solver.f(&optimizer.model, x);
|
||||
xlist_true.push(x);
|
||||
}
|
||||
|
||||
// Start with random settings
|
||||
*optimizer.model.get_settings_mut() = dbg!(sir::SirSettings {
|
||||
beta: rng.gen(),
|
||||
gamma: rng.gen(),
|
||||
pop: 1.0,
|
||||
});
|
||||
|
||||
// Compute descent direction
|
||||
let dir = dbg!(optimizer.objective_gradient(x0, xlist_true[1], 0.0000001, 0..2));
|
||||
|
||||
// Check that this direction leads to smaller error
|
||||
let s: Vect<f64, 3> = optimizer.model.get_settings().clone().into();
|
||||
let y = optimizer.model.f(x0);
|
||||
*optimizer.model.get_settings_mut() = (s - dir).into(); // Apply direction
|
||||
let y_new = optimizer.model.f(x0);
|
||||
assert!((y - xlist_true[1]).norm() >= (y_new - xlist_true[1]).norm());
|
||||
}
|
||||
}
|
||||
41
examples/old/solver.rs
Normal file
41
examples/old/solver.rs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
use crate::{model::*, opti::*, utils::*};
|
||||
|
||||
use nalgebra::{base::*, ComplexField};
|
||||
|
||||
pub trait Solver<T, S: Settings, M: Model<T, S, D>, const D: usize> {
|
||||
fn f(&self, model: &M, x: Vect<T, D>) -> Vect<T, D>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ExplicitEulerSolver<T: Copy + ComplexField<RealField = T>> {
|
||||
pub dt: T,
|
||||
}
|
||||
|
||||
impl<T: Copy + ComplexField<RealField = T>, S: Settings, M: Model<T, S, D>, const D: usize>
|
||||
Solver<T, S, M, D> for ExplicitEulerSolver<T>
|
||||
{
|
||||
fn f(&self, model: &M, x: Vect<T, D>) -> Vect<T, D> {
|
||||
model.f(x) * self.dt + x
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ImplicitEulerSolver<T: Clone + ComplexField<RealField = T>> {
|
||||
pub dt: T,
|
||||
pub tol: T,
|
||||
pub niters: usize,
|
||||
}
|
||||
|
||||
impl<
|
||||
T: Copy + ComplexField<RealField = T> + PartialOrd,
|
||||
S: Settings,
|
||||
M: Model<T, S, D>,
|
||||
const D: usize,
|
||||
> Solver<T, S, M, D> for ImplicitEulerSolver<T>
|
||||
where
|
||||
Const<D>: ToTypenum + DimMin<Const<D>, Output = Const<D>>,
|
||||
{
|
||||
fn f(&self, model: &M, x: Vect<T, D>) -> Vect<T, D> {
|
||||
newton(model, x, self.dt, self.tol, self.niters)
|
||||
}
|
||||
}
|
||||
94
examples/old/space.rs
Normal file
94
examples/old/space.rs
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
use crate::{
|
||||
model::{Model, Settings},
|
||||
solver::Solver,
|
||||
utils::*,
|
||||
};
|
||||
|
||||
use nalgebra::ComplexField;
|
||||
use std::{collections::HashMap, marker::PhantomData};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Point<T: Copy, const D: usize> {
|
||||
pub pos: Vect<T, D>,
|
||||
pub diffusion: Vect<T, D>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Source<T: Copy, const D: usize> {
|
||||
pub amplitude: T,
|
||||
pub fonction: fn(T) -> Vect<T, D>,
|
||||
pub freq: T,
|
||||
pub phase: T,
|
||||
}
|
||||
|
||||
pub struct Space<T: Copy, S: Settings, M: Model<T, S, D>, V: Solver<T, S, M, D>, const D: usize> {
|
||||
pub model: M,
|
||||
pub old_points: Vec<Point<T, D>>,
|
||||
pub points: Vec<Point<T, D>>,
|
||||
pub size: (usize, usize),
|
||||
pub solver: V,
|
||||
pub sources: HashMap<usize, Source<T, D>>,
|
||||
pub time: T,
|
||||
pub _p: PhantomData<S>,
|
||||
}
|
||||
|
||||
impl<
|
||||
T: Copy + ComplexField<RealField = T>,
|
||||
S: Settings,
|
||||
M: Model<T, S, D>,
|
||||
V: Solver<T, S, M, D>,
|
||||
const D: usize,
|
||||
> Space<T, S, M, V, D>
|
||||
{
|
||||
pub fn simulate(&mut self, delta_time: T) {
|
||||
std::mem::swap(&mut self.old_points, &mut self.points);
|
||||
for (i, (point, old_point)) in self
|
||||
.points
|
||||
.iter_mut()
|
||||
.zip(self.old_points.iter())
|
||||
.enumerate()
|
||||
{
|
||||
if let Some(source) = self.sources.get(&i) {
|
||||
let t = self.time * source.freq + source.phase;
|
||||
//Point{pos:t.sin()*source.amplitude, speed: t.cos()*source.amplitude}
|
||||
point.pos = (source.fonction)(t) * source.amplitude;
|
||||
} else {
|
||||
*point = old_point.clone()
|
||||
};
|
||||
point.pos = self.solver.f(&self.model, point.pos);
|
||||
}
|
||||
|
||||
let mut i = 0usize;
|
||||
for y in 0..self.size.1 {
|
||||
for x in 0..self.size.0 {
|
||||
let old_point = self.old_points[i].clone();
|
||||
let point = &mut self.points[i];
|
||||
|
||||
if y > 0 {
|
||||
point.pos += (self.old_points[i - self.size.0].pos - old_point.pos)
|
||||
.component_mul(&point.diffusion)
|
||||
* delta_time;
|
||||
}
|
||||
if y < self.size.1 - 1 {
|
||||
point.pos += (self.old_points[i + self.size.0].pos - old_point.pos)
|
||||
.component_mul(&point.diffusion)
|
||||
* delta_time;
|
||||
}
|
||||
if x > 0 {
|
||||
point.pos += (self.old_points[i - 1].pos - old_point.pos)
|
||||
.component_mul(&point.diffusion)
|
||||
* delta_time;
|
||||
}
|
||||
if x < self.size.0 - 1 {
|
||||
point.pos += (self.old_points[i + 1].pos - old_point.pos)
|
||||
.component_mul(&point.diffusion)
|
||||
* delta_time;
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
self.time += delta_time;
|
||||
}
|
||||
}
|
||||
16
examples/old/utils.rs
Normal file
16
examples/old/utils.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
use nalgebra::base::*;
|
||||
|
||||
/// `<Type, Rows, Columns>`
|
||||
pub type Mat<T, const R: usize, const C: usize> =
|
||||
Matrix<T, Const<R>, Const<C>, ArrayStorage<T, R, C>>;
|
||||
pub type Vect<T, const R: usize> = Matrix<T, Const<R>, U1, ArrayStorage<T, R, 1>>;
|
||||
|
||||
pub fn max(l: &[f64]) -> f64 {
|
||||
let mut m = l[0];
|
||||
for &i in &l[1..] {
|
||||
if i > m {
|
||||
m = i;
|
||||
}
|
||||
}
|
||||
m
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue