Initial commit

This commit is contained in:
Pascal Engélibert 2023-11-16 23:28:16 +01:00
commit aaa212b9a9
22 changed files with 4778 additions and 0 deletions

View 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"] }

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
}