use crate::imports::*;
use bincode::{deserialize, serialize};
use std::ffi::OsStr;
use std::fs::File;
use std::path::{Path, PathBuf};
#[macro_export]
macro_rules! time_it {
($thing: expr) => {{
let t0 = Instant::now();
$thing;
let t_elapsed = Instant::now() - t0;
t_elapsed
}};
}
#[macro_export]
macro_rules! zip {
($x: expr) => ($x);
($x: expr, $($y: expr), +) => (
$x.iter().zip(
zip!($($y), +))
)
}
#[macro_export]
macro_rules! connect_states {
($sys: ident, $(($s0: ident, $s1: ident, $c: ident)), +) => {
$(
$sys.$c.set_flow(&$sys.$s0, &$sys.$s1);
)+
};
}
#[macro_export]
macro_rules! update_derivs {
($sys: ident, $(($s0: ident, $s1: ident, $c: ident)), +) => {
$(
$sys.$s0.step_deriv(-$sys.$c.flow() / $sys.$s0.storage());
$sys.$s1.step_deriv($sys.$c.flow() / $sys.$s1.storage());
)+
};
}
#[macro_export]
macro_rules! print_to_py {
( $( $x:expr, $y:expr ),* ) => {
pyo3::Python::with_gil(|py| {
let locals = pyo3::types::PyDict::new(py);
$(
locals.set_item($x, $y).unwrap();
py.run(
&format!("print(f\"{}: {{{}:.3g}}\")", $x, $x),
None,
Some(locals),
)
.expect(&format!("printing `{}` failed", $x));
)*
});
};
( $x:expr ) => {
pyo3::Python::with_gil(|py| {
py.run(
&format!("print({})", $x),
None,
None,
)
.expect(&format!("printing `{}` failed", $x));
});
}
}
pub trait HasState {
fn state(&self) -> f64;
fn set_state(&mut self, val: f64);
fn step_state_by_dt(&mut self, dt: &f64) {
self.set_state(self.state() + dt * self.deriv());
}
fn step_state(&mut self, val: f64) {
self.set_state(self.state() + val);
}
fn deriv(&self) -> f64;
fn set_deriv(&mut self, val: f64);
fn step_deriv(&mut self, val: f64) {
self.set_deriv(self.deriv() + val)
}
fn storage(&self) -> f64;
}
pub trait HasStates: BareClone {
fn states(&self) -> Vec<f64>;
fn set_states(&mut self, val: Vec<f64>);
fn step_states_by_dt(&mut self, dt: &f64);
fn step_states(&mut self, val: Vec<f64>);
fn derivs(&self) -> Vec<f64>;
fn set_derivs(&mut self, val: &[f64]);
fn step_derivs(&mut self, val: Vec<f64>);
fn storages(&self) -> Vec<f64>;
}
pub trait Flow {
fn set_flow(&mut self, p0: &dyn HasState, p1: &dyn HasState);
fn flow(&self) -> f64;
}
pub trait Diff {
fn diff(&self) -> Vec<f64>;
}
impl Diff for Vec<f64> {
fn diff(&self) -> Vec<f64> {
self.windows(2)
.map(|vs| {
let [x, y] = vs else {unreachable!()};
y - x
})
.collect()
}
}
pub trait SerdeAPI: Serialize + for<'a> Deserialize<'a> {
#[allow(clippy::wrong_self_convention)]
fn to_file(&self, filename: &str) -> Result<(), anyhow::Error> {
let file = PathBuf::from(filename);
let extension = Path::new(filename)
.extension()
.and_then(OsStr::to_str)
.unwrap_or("");
match extension {
"json" => {
serde_json::to_writer(&File::create(file)?, self)?;
Ok(())
}
"yaml" => {
serde_yaml::to_writer(&File::create(file)?, self)?;
Ok(())
}
_ => Err(anyhow!("Unsupported file extension {}", extension)),
}
}
fn from_file(filename: &str) -> Result<Self, anyhow::Error>
where
Self: std::marker::Sized,
for<'de> Self: Deserialize<'de>,
{
let extension = Path::new(filename)
.extension()
.and_then(OsStr::to_str)
.unwrap_or("");
let file = File::open(filename)?;
match extension {
"yaml" => Ok(serde_yaml::from_reader(file)?),
"json" => Ok(serde_json::from_reader(file)?),
_ => Err(anyhow!("Unsupported file extension {}", extension)),
}
}
fn to_json(&self) -> String {
serde_json::to_string(&self).unwrap()
}
fn from_json(json_str: &str) -> Result<Self, anyhow::Error> {
Ok(serde_json::from_str(json_str)?)
}
fn to_yaml(&self) -> String {
serde_yaml::to_string(&self).unwrap()
}
fn from_yaml(yaml_str: &str) -> Result<Self, anyhow::Error> {
Ok(serde_yaml::from_str(yaml_str)?)
}
fn to_bincode(&self) -> Vec<u8> {
serialize(&self).unwrap()
}
fn from_bincode(encoded: &[u8]) -> Result<Self, anyhow::Error> {
Ok(deserialize(encoded)?)
}
}
impl<T> SerdeAPI for T where T: Serialize + for<'a> Deserialize<'a> {}
pub trait Linspace {
fn linspace(start: f64, stop: f64, n_elements: usize) -> Vec<f64> {
let n_steps = n_elements - 1;
let step_size = (stop - start) / n_steps as f64;
let v_norm: Vec<f64> = (0..=n_steps)
.collect::<Vec<usize>>()
.iter()
.map(|x| *x as f64)
.collect();
let v = v_norm.iter().map(|x| (x * step_size) + start).collect();
v
}
}
impl Linspace for Vec<f64> {}
pub trait BareClone {
fn bare_clone(&self) -> Self;
}