opt_solver 0.1.1

Common optimization algorithms
Documentation
mod min;
mod min_par;
mod min_vec;
mod min_vec_par;

use std::cmp::min;

use crate::*;
use crate::particle_swarm_optimization::min_par::min_par;
use crate::particle_swarm_optimization::min_vec_par::min_vec_par;

#[doc=include_str!("PSO.md")]
#[derive(Clone, Copy, Debug, PartialEq, Default)]
#[pyclass(get_all, set_all)]
pub struct PSO {
    pub n_iters: usize,
    pub n_particles: usize,
    pub c1: f64,
    pub c2: f64,
    pub inertia: f64,
    pub max_v: f64,
}

impl Opti for PSO {
    fn min<
        const I8: usize,
        const I16: usize,
        const I32: usize,
        const I64: usize,
        const I128: usize,
        const I: usize,
        const F32: usize,
        const F64: usize,
        const C64: usize,
        const C128: usize,
        const D: usize,
        O: PartialOrd + Clone,
    >(
        &self,
        f: impl Fn(X<I8, I16, I32, I64, I128, I, F32, F64, C64, C128, D>) -> O,
        x_min: X<I8, I16, I32, I64, I128, I, F32, F64, C64, C128, D>,
        x_max: X<I8, I16, I32, I64, I128, I, F32, F64, C64, C128, D>,
    ) -> (O, X<I8, I16, I32, I64, I128, I, F32, F64, C64, C128, D>) {
        min::min(self, f, x_min, x_max)
    }

    fn min_vec<O: PartialOrd + Clone>(
        &self,
        f: impl Fn(XVec) -> O,
        x_min: XVec,
        x_max: XVec,
    ) -> (O, XVec) {
        min_vec::min_vec(self, f, x_min, x_max)
    }

    fn min_par<
        const I8: usize,
        const I16: usize,
        const I32: usize,
        const I64: usize,
        const I128: usize,
        const I: usize,
        const F32: usize,
        const F64: usize,
        const C64: usize,
        const C128: usize,
        const D: usize,
        O: PartialOrd + Clone + Sync + Send,
    >(
        &self,
        f: impl Fn(X<I8, I16, I32, I64, I128, I, F32, F64, C64, C128, D>) -> O + Sync,
        x_min: X<I8, I16, I32, I64, I128, I, F32, F64, C64, C128, D>,
        x_max: X<I8, I16, I32, I64, I128, I, F32, F64, C64, C128, D>,
    ) -> (O, X<I8, I16, I32, I64, I128, I, F32, F64, C64, C128, D>) {
        min_par(self, f, x_min, x_max)
    }

    fn min_vec_par<O: PartialOrd + Clone + Sync + Send>(
        &self,
        f: impl Fn(XVec) -> O + Sync,
        x_min: XVec,
        x_max: XVec,
    ) -> (O, XVec) {
        min_vec_par(self, f, x_min, x_max)
    }
}

#[pymethods]
impl PSO {
    #[new]
    #[pyo3(signature = (n_iters = 1000, n_particles = 30, c1 = 2.0, c2 = 2.0,   inertia = 0.5 , max_v = 0.2, ))]
    pub fn new(
        n_iters: usize,
        n_particles: usize,
        c1: f64,
        c2: f64,
        inertia: f64,
        max_v: f64,
    ) -> Self {
        Self {
            n_iters,
            n_particles,
            c1,
            c2,

            inertia,
            max_v,
        }
    }

    #[pyo3(name = "min", signature = (
	f,
	i8_min = vec ! [], i8_max = vec ! [],
	i16_min = vec ! [], i16_max = vec ! [],
	i32_min = vec ! [], i32_max = vec ! [],
	i64_min = vec ! [], i64_max = vec ! [],
	i128_min = vec ! [], i128_max = vec ! [],
	i_min = vec ! [], i_max = vec ! [],
	f32_min = vec ! [], f32_max = vec ! [],
	f64_min = vec ! [], f64_max = vec ! [],
	c64_min = vec ! [], c64_max = vec ! [],
	c128_min = vec ! [], c128_max = vec ! [],
	d_min = vec ! [], d_max = vec ! []
	))]
    fn py_min(
        &self,
        f: PyObject,
        i8_min: Vec<i8>,
        i8_max: Vec<i8>,
        i16_min: Vec<i16>,
        i16_max: Vec<i16>,
        i32_min: Vec<i32>,
        i32_max: Vec<i32>,
        i64_min: Vec<i64>,
        i64_max: Vec<i64>,
        i128_min: Vec<i128>,
        i128_max: Vec<i128>,
        i_min: Vec<BigInt>,
        i_max: Vec<BigInt>,
        f32_min: Vec<f32>,
        f32_max: Vec<f32>,
        f64_min: Vec<f64>,
        f64_max: Vec<f64>,
        c64_min: Vec<Complex<f32>>,
        c64_max: Vec<Complex<f32>>,
        c128_min: Vec<Complex<f64>>,
        c128_max: Vec<Complex<f64>>,
        d_min: Vec<Decimal>,
        d_max: Vec<Decimal>,
    ) -> (PyObject, Py<PyDict>) {
        let x_min = XVec {
            i8: i8_min,
            i16: i16_min,
            i32: i32_min,
            i64: i64_min,
            i128: i128_min,
            i: i_min,
            f32: f32_min,
            f64: f64_min,
            c64: c64_min,
            c128: c128_min,
            d: d_min,
        };

        let x_max = XVec {
            i8: i8_max,
            i16: i16_max,
            i32: i32_max,
            i64: i64_max,
            i128: i128_max,
            i: i_max,
            f32: f32_max,
            f64: f64_max,
            c64: c64_max,
            c128: c128_max,
            d: d_max,
        };

        let (f_val, x) = Python::with_gil(|py| {
            let (f_val, x) = self.min_vec(
                move |x| {
                    let binding = x.into_py(py);
                    let x_py = binding.as_ref(py);
                    let res = f.call(py, (), Some(x_py)).expect("failed to call f");
                    PyWrapperObject(res, py)
                },
                x_min,
                x_max,
            );
            (f_val.0, x.into_py(py))
        });

        (f_val, x)
    }

    #[pyo3(name = "max", signature = (
        f,
        i8_min = vec ! [], i8_max = vec ! [],
        i16_min = vec ! [], i16_max = vec ! [],
        i32_min = vec ! [], i32_max = vec ! [],
        i64_min = vec ! [], i64_max = vec ! [],
        i128_min = vec ! [], i128_max = vec ! [],
        i_min = vec ! [], i_max = vec ! [],
        f32_min = vec ! [], f32_max = vec ! [],
        f64_min = vec ! [], f64_max = vec ! [],
        c64_min = vec ! [], c64_max = vec ! [],
        c128_min = vec ! [], c128_max = vec ! [],
        d_min = vec ! [], d_max = vec ! []
	))]
    fn py_max<'py>(
        &self,
        f: PyObject,
        i8_min: Vec<i8>,
        i8_max: Vec<i8>,
        i16_min: Vec<i16>,
        i16_max: Vec<i16>,
        i32_min: Vec<i32>,
        i32_max: Vec<i32>,
        i64_min: Vec<i64>,
        i64_max: Vec<i64>,
        i128_min: Vec<i128>,
        i128_max: Vec<i128>,
        i_min: Vec<BigInt>,
        i_max: Vec<BigInt>,
        f32_min: Vec<f32>,
        f32_max: Vec<f32>,
        f64_min: Vec<f64>,
        f64_max: Vec<f64>,
        c64_min: Vec<Complex<f32>>,
        c64_max: Vec<Complex<f32>>,
        c128_min: Vec<Complex<f64>>,
        c128_max: Vec<Complex<f64>>,
        d_min: Vec<Decimal>,
        d_max: Vec<Decimal>,
    ) -> (PyObject, Py<PyDict>) {
        let x_min = XVec {
            i8: i8_min,
            i16: i16_min,
            i32: i32_min,
            i64: i64_min,
            i128: i128_min,
            i: i_min,
            f32: f32_min,
            f64: f64_min,
            c64: c64_min,
            c128: c128_min,
            d: d_min,
        };

        let x_max = XVec {
            i8: i8_max,
            i16: i16_max,
            i32: i32_max,
            i64: i64_max,
            i128: i128_max,
            i: i_max,
            f32: f32_max,
            f64: f64_max,
            c64: c64_max,
            c128: c128_max,
            d: d_max,
        };

        let (f_val, x) = Python::with_gil(|py| {
            let (f_val, x) = self.max_vec(
                move |x| {
                    let binding = x.into_py(py);
                    let x_py = binding.as_ref(py);
                    let res = f.call(py, (), Some(x_py)).expect("failed to call f");
                    PyWrapperObject(res, py)
                },
                x_min,
                x_max,
            );
            (f_val.0, x.into_py(py))
        });

        (f_val, x)
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    extern crate test;

    use test::Bencher;

    fn f(x: X<0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0>) -> f64 {
        (x.f64[0] - 7.0).powi(2) + (x.f64[1] + 3.0).powi(2) + (x.f64[2] + 5.0).powi(2)
    }

    #[test]
    fn it_works() {
        let x_min = x_new().set_f64([-10.0, -10.0, -10.0]);
        let x_max = x_new().set_f64([10.0, 10.0, 10.0]);

        let pso = PSO::new(1000, 10000, 2.0, 2.0, 0.2, 0.2);

        let (out_best, x_best) = pso.min(f, x_min.clone(), x_max.clone());

        println!("{:?}\n{:?}", out_best, x_best);

        assert!(out_best < 0.5);
        assert!((x_best.f64[0] - 7.0).abs() < 1.0);
        assert!((x_best.f64[1] + 3.0).abs() < 1.0);
        assert!((x_best.f64[2] + 5.0).abs() < 1.0);
        let (out_best, x_best) = pso.min_par(f, x_min, x_max);

        println!("{:?}\n{:?}", out_best, x_best);

        assert!(out_best < 0.5);
        assert!((x_best.f64[0] - 7.0).abs() < 1.0);
        assert!((x_best.f64[1] + 3.0).abs() < 1.0);
        assert!((x_best.f64[2] + 5.0).abs() < 1.0);
    }

    #[bench]
    fn bencher(b: &mut Bencher) {
        let pso = PSO::new(1000, 10000, 2.0, 2.0, 0.2, 0.2);

        b.iter(|| {
            pso.min(
                f,
                x_new().set_f64([-10.0, -10.0, -10.0]),
                x_new().set_f64([10.0, 10.0, 10.0]),
            );
        });
    }

    #[bench]
    fn bencher_par(b: &mut Bencher) {
        let pso = PSO::new(1000, 10000, 2.0, 2.0, 0.2, 0.2);

        b.iter(|| {
            pso.min_par(
                f,
                x_new().set_f64([-10.0, -10.0, -10.0]),
                x_new().set_f64([10.0, 10.0, 10.0]),
            );
        });
    }
}