#![feature(test)]
extern crate blas;
extern crate freude;
#[macro_use(azip)]
extern crate ndarray;
extern crate ndarray_rand;
extern crate rand;
extern crate test;
use blas::c as cblas;
use ndarray::prelude::*;
use ndarray_rand::RandomExt;
use rand::distributions::IndependentSample;
use rand::distributions::{Normal, Range};
use std::f64;
use std::marker::PhantomData;
use freude::{Ode,RungeKutta4,Stepper};
use test::black_box;
struct NullSystem<T> {
_phantom: PhantomData<T>,
}
impl Ode for NullSystem<Array1<f64>> {
type State = Array1<f64>;
fn differentiate_into(&mut self, state: &Self::State, derivative: &mut Self::State) {
derivative.assign(state);
}
}
impl Ode for NullSystem<Vec<f64>> {
type State = Vec<f64>;
fn differentiate_into(&mut self, state: &Self::State, derivative: &mut Self::State) {
derivative.clone_from(state);
}
}
struct Kuramoto {
frequencies: Vec<f64>,
size: usize,
temp_sin: Vec<f64>,
temp_cos: Vec<f64>,
}
impl Ode for Kuramoto {
type State = Vec<f64>;
fn differentiate_into(&mut self, state: &Vec<f64>, derivative: &mut Vec<f64>) {
for (t, s) in self.temp_sin.iter_mut().zip(state.iter()) {
*t = f64::sin(*s);
}
for (t, c) in self.temp_cos.iter_mut().zip(state.iter()) {
*t = f64::cos(*c);
}
let mut sin_sum = self.temp_sin.iter().fold(0.0, |acc, s| { acc + s });
let mut cos_sum = self.temp_cos.iter().fold(0.0, |acc, c| { acc + c });
sin_sum /= self.size as f64;
cos_sum /= self.size as f64;
azip!(mut d (derivative), f (&self.frequencies), sin (&self.temp_sin), cos (&self.temp_cos) in {
*d = f + sin_sum * cos - cos_sum * sin
})
}
}
struct ChaoticNeuralNet {
coupling: Array2<f64>,
nonlinearity: f64,
size: usize,
temp_tanh: Array1<f64>,
}
impl Ode for ChaoticNeuralNet {
type State = Array1<f64>;
fn differentiate_into(&mut self, state: &Array1<f64>, derivative: &mut Array1<f64>) {
let g = self.nonlinearity;
self.temp_tanh.zip_mut_with(&state, |t, x| { *t = f64::tanh(g * *x); });
unsafe {
cblas::dgemv(cblas::Layout::RowMajor, cblas::Transpose::None, self.size as i32, self.size as i32, 1.0, self.coupling.as_slice_memory_order().unwrap(), self.size as i32, self.temp_tanh.as_slice_memory_order().unwrap(), 1, 0.0, derivative.as_slice_memory_order_mut().unwrap(), 1, );
}
let mut derivative = derivative.view_mut();
derivative -= state;
}
}
#[bench]
fn rk4_freude(bench: &mut test::Bencher) {
#[derive(Clone)]
struct SimpleODE {
a: f64,
}
impl Ode for SimpleODE {
type State = f64;
fn differentiate_into(&mut self, x: &f64, into: &mut f64) {
*into = *x + x.sin();
}
}
let mut x = 1.0;
let mut sys = SimpleODE { a: 1. };
let mut rk4 = RungeKutta4::new(&x, 0.1);
bench.iter(|| { rk4.do_step(&mut sys, &mut x); });
let _x = black_box(rk4);
}
#[bench]
fn rk4_manual(bench: &mut test::Bencher) {
fn f(x: f64) -> f64 {
1f64 + x.sin()
}
fn rk4(x: &mut f64, dt: f64) {
let k1 = f(*x);
let k2 = f(*x + dt/2.0 * k1);
let k3 = f(*x + dt/2.0 * k2);
let k4 = f(*x + dt * k3);
*x = *x + dt/6.0 * (k1 + 2.0 * k2 + 2.0 * k3 + k4);
}
let mut x = black_box(1.0);
bench.iter(|| { rk4(&mut x, 0.1); });
let _x = black_box(x); }
#[bench]
fn chaotic_net_derivative(bench: &mut test::Bencher) {
let size = 8192;
let mean = 0.0;
let nonlinearity = 1.5;
let std_dev = 1.1;
let dist = Normal::new(mean, std_dev / f64::sqrt(size as f64));
let mut coupling = Array2::random([size,size], dist);
coupling.diag_mut().map_inplace(|c| { *c = 0.0; });
let temp_tanh = Array1::zeros(size);
let mut chaotic_net = ChaoticNeuralNet {
coupling: coupling,
nonlinearity: nonlinearity,
size: size,
temp_tanh: temp_tanh,
};
let between = Range::new(-1.0, 1.0);
let state = Array1::random(size, between);
let derivative = Array1::zeros(size);
let mut derivative = black_box(derivative);
bench.iter(|| { chaotic_net.differentiate_into(&state, &mut derivative)});
}
#[bench]
fn chaotic_net_rk4(bench: &mut test::Bencher) {
let size = 8192;
let mean = 0.0;
let nonlinearity = 1.5;
let std_dev = 1.1;
let dist = Normal::new(mean, std_dev / f64::sqrt(size as f64));
let mut coupling = Array2::random([size,size], dist);
coupling.diag_mut().map_inplace(|c| { *c = 0.0; });
let temp_tanh = Array1::zeros(size);
let mut chaotic_net = ChaoticNeuralNet {
coupling: coupling,
nonlinearity: nonlinearity,
size: size,
temp_tanh: temp_tanh,
};
let between = Range::new(-1.0, 1.0);
let mut state = Array1::random(size, between);
let rk4 = RungeKutta4::new(&state, 0.1);
let mut rk4 = black_box(rk4);
bench.iter(|| { rk4.do_step(&mut chaotic_net, &mut state); });
}
#[bench]
fn kuramoto_vec(bench: &mut test::Bencher) {
let size = 8192;
let mut rng = ::rand::thread_rng();
let dist = Normal::new(0.0, 1.0);
let frequencies: Vec<_> = ::std::iter::repeat(()).take(size).map(|_| {
dist.ind_sample(&mut rng)
}).collect();
let temp_sin = vec![0.0; size];
let temp_cos = vec![0.0; size];
let mut kuramoto = Kuramoto {
frequencies: frequencies,
size: size,
temp_sin: temp_sin,
temp_cos: temp_cos,
};
let pi = ::std::f64::consts::PI;
let between = Range::new(-pi, pi);
let mut state: Vec<_> = ::std::iter::repeat(()).take(size).map(|_| {
between.ind_sample(&mut rng)
}).collect();
let rk4 = RungeKutta4::new(&state, 0.1);
let mut rk4 = black_box(rk4);
bench.iter(|| { rk4.do_step(&mut kuramoto, &mut state); });
}
#[bench]
fn rk4_array_speed(bench: &mut test::Bencher) {
let size = 8192;
let mut state = Array1::zeros(size);
let mut system = NullSystem { _phantom: PhantomData::<Array1<f64>> };
let rk4 = RungeKutta4::new(&state, 0.1);
let mut rk4 = black_box(rk4);
bench.iter(|| {rk4.do_step(&mut system, &mut state); });
}
#[bench]
fn rk4_vec_speed(bench: &mut test::Bencher) {
let size = 8192;
let mut state = vec![0.0; size];
let mut system = NullSystem { _phantom: PhantomData::<Vec<f64>> };
let rk4 = RungeKutta4::new(&state, 0.1);
let mut rk4 = black_box(rk4);
bench.iter(|| {rk4.do_step(&mut system, &mut state); });
}