use rand::distributions::Standard;
use rand::rngs::SmallRng;
use rand::Rng;
use super::{StopCondition, TransientBehavior};
use std::f32::consts::PI;
const DEFAULT_PERIOD: usize = 5000;
#[derive(Clone, Debug)]
pub struct TransientModel {
num_variables: usize,
num_points: usize,
period: usize,
momentums: Vec<f64>,
motion_coeffs: Vec<Vec<f64>>,
rand_magnitudes: Vec<f32>,
}
impl TransientModel {
pub fn new(
behavior: &TransientBehavior,
stop_condition: &StopCondition,
num_variables: usize,
motion_coefficients: Vec<f64>,
momentum_magnitude: f64,
) -> Self {
let period = match stop_condition {
StopCondition::Iterations(iters) => *iters,
StopCondition::Cost(_) => DEFAULT_PERIOD,
StopCondition::Both(iters, _) => *iters,
};
let num_directions = motion_coefficients.len();
let num_points = num_directions * num_variables;
let momentums = Self::gen_momentum_vals(period, behavior.momentum_mode, momentum_magnitude);
let motion_coeffs = match num_directions {
2 => Self::motion_coeffs_sol(period, behavior.motion_mode, &motion_coefficients),
3 => Self::motion_coeffs_syn(period, behavior.motion_mode, &motion_coefficients),
_ => panic!("Motion Coefficients vector must have a length of 2 or 3!"),
};
let rand_magnitudes = Self::gen_rand_mags(period, behavior.stochastic_mode);
Self {
num_variables,
num_points,
period,
momentums,
motion_coeffs,
rand_magnitudes,
}
}
pub fn rand_vecs(&self, iter: usize, rng: &mut SmallRng) -> Vec<Vec<f64>> {
let index = iter % self.period;
let rand_mag = self.rand_magnitudes[index];
let samples: Vec<f32> = rng.sample_iter(Standard).take(self.num_points).collect();
samples
.chunks_exact(self.num_variables)
.map(|dir_chunk| dir_chunk.iter().map(|s| (s * rand_mag) as f64).collect())
.collect()
}
pub fn momentum(&self, iter: usize) -> f64 {
let index = iter % self.period;
self.momentums[index]
}
pub fn motion_coeffs(&self, iter: usize) -> &[f64] {
let index = iter % self.period;
&self.motion_coeffs[index][..]
}
fn gen_momentum_vals(
period: usize,
fund_momentum_mode: u8,
momentum_magnitude: f64,
) -> Vec<f64> {
let period_float = period as f32;
let frequency = PI * (1.0 + ((fund_momentum_mode as f32) * 2.0));
(0..period)
.map(|i| {
let t = (i as f32) / period_float;
(((frequency * t).cos() * 0.45) + 0.55) as f64 * momentum_magnitude
})
.collect()
}
fn gen_rand_mags(period: usize, fund_rand_mode: u8) -> Vec<f32> {
let period_float = period as f32;
let frequency = PI * (1.0 + ((fund_rand_mode as f32) * 2.0));
(0..period)
.map(|i| {
let t = (i as f32) / period_float;
((frequency * t).cos() * 0.95) + 1.05
})
.collect()
}
fn motion_coeffs_syn(
period: usize,
fund_motion_mode: u8,
base_coeffs: &Vec<f64>,
) -> Vec<Vec<f64>> {
let period_float = period as f32;
let fund_mode_float = fund_motion_mode as f32;
let loc_freq = PI * (3.0 + (fund_mode_float * 2.0)); let tri_freq = PI * (2.0 + (fund_mode_float * 2.0)); let glo_freq = PI * (3.0 + (fund_mode_float * 2.0));
(0..period)
.map(|i| {
let t = (i as f32) / period_float;
let loc: f32 = ((loc_freq * t).cos() * 0.45) + 0.55;
let tri: f32 = ((tri_freq * t).sin() * 0.53) + 0.47;
let glo: f32 = ((glo_freq * (t + 1.0)).cos() * 0.53) + 0.47;
vec![
(loc as f64) * base_coeffs[0],
(tri as f64) * base_coeffs[1],
(glo as f64) * base_coeffs[2],
]
})
.collect()
}
fn motion_coeffs_sol(
period: usize,
fund_motion_mode: u8,
base_coeffs: &Vec<f64>,
) -> Vec<Vec<f64>> {
let period_float = period as f32;
let fund_mode_float = fund_motion_mode as f32;
let loc_freq = PI * (3.0 + (fund_mode_float * 2.0)); let tri_freq = PI * (3.0 + (fund_mode_float * 2.0));
(0..period)
.map(|i| {
let t = (i as f32) / period_float;
let loc: f32 = ((loc_freq * t).cos() * 0.45) + 0.55;
let tri: f32 = ((tri_freq * (t + 1.0)).cos() * 0.55) + 0.45;
vec![(loc as f64) * base_coeffs[0], (tri as f64) * base_coeffs[1]]
})
.collect()
}
}