pso 0.2.0

Particle Swarm Optimizer
Documentation
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);

        // println!("momentums: {:?}", momentums);

        // println!("motions: {:?}", motion_coeffs);

        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 progress = (iter as f32) / self.period;
        let period_float = period as f32;
        // PI, 3*PI, 5*PI, 7*PI, ...
        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;
        // PI, 3*PI, 5*PI, 7*PI, ...
        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)); // 3*PI, 5*PI, 7*PI, ...
        let tri_freq = PI * (2.0 + (fund_mode_float * 2.0)); // 5*PI, 7*PI, 9*PI, ...
        let glo_freq = PI * (3.0 + (fund_mode_float * 2.0)); // 3*PI, 5*PI, 7*PI, ...

        (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)); // 3*PI, 5*PI, 7*PI, ...
        let tri_freq = PI * (3.0 + (fund_mode_float * 2.0)); // 5*PI, 7*PI, 9*PI, ...

        (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()
    }
}