1个不稳定版本

0.1.0 2023年2月2日

#1212算法

MIT 许可证

18KB
274

PSO-Rust

author GitHub GitHub top language crates

这是什么?

这是一个专注于PSO方法(或粒子群优化器)的Rust库。

有什么新功能?

与crates.io上可找到的现有包不同,这个库不关心输入的内容。通过各种特性,你可以通过实现这些特性来获取任何类型的数据结构。

尽管实现这些特性可能看起来更复杂,但它们支持的能力绝对值得。

如何使用?

毕竟这是一个新的库,所以现在只支持最基础的PSO案例——原始的PSO方法。其他变体,如二分搜索或离散搜索将在不久的将来得到支持。

示例

问题

在$y = x_1^2 + x_2^2$中寻找最小值$y$,其中$-10 \le x_1, x_2 \le 10$

代码

use pso_rust::basic::BasePsoNode;
use pso_rust::traits::{PsoAcc, PsoHandler, PsoParticle};

// Here define the basic problem into solution-formed structure.
struct Particle {
    x1: f64,
    x2: f64,
}
impl PsoParticle<f64> for Particle {
    fn get_performance(&self) -> f64 {
        self.performance
    }
    fn init(position: Vec<f64>) -> Self {
        let mut p = Particle {
            x1: position[0],
            x2: position[1],
            performance: 0.0,
        };
        p.performance = -(p.x1.powi(2) + p.x2.powi(2));
        p
    }
    fn update_position(&mut self, position: &Vec<f64>) -> Option<Vec<f64>> {
        let mut flag = false;
        if position[0] > 10.0 || position[0] < -10.0 {
            self.x1 = 0.0;
            flag = true;
        } else if position[1] > 10.0 || position[1] < -10.0 {
            self.x2 = 0.0;
            flag = true;
        }
        if flag == true {
            self.performance = -(self.x1.powi(2) + self.x2.powi(2));
            Some(vec![self.x1, self.x2])
        } else {
            self.x1 = position[0];
            self.x2 = position[1];
            self.performance = -(self.x1.powi(2) + self.x2.powi(2));
            None
        }
    }
}

// Here to save time and to optimize the length of the code, I defined a versatile data structure to represent those parameters like inertia and just keep them.
// You can define them separately and give them more functions like dynamic value controlled by generations and modified by the node.
// Advanced usage please see below.
#[derive(Debug, Clone)]
struct DATA<T>
where
    T: Debug + Clone
{
    value: T,
}
impl<T> DATA<T>
where
    T: Debug + Clone
{
    fn new(v: T) -> DATA<T> {
        DATA { value: v }
    }
}
impl<T> PsoAcc<BasePsoNode<Particle>, f64, T> for DATA<T>
where
    T: Debug + Clone
{
    fn get_value(&self, generation: &usize, this_node: &BasePsoNode<Particle>) -> T {
        self.value.clone()
    }
    fn init_value(&self) -> T {
        self.value.clone()
    }
}
let speed_field = DATA::new(vec![(-2.0, 2.0), (-2.0, 2.0)]);
let position_field = vec![(-10.0, 10.0), (-10.0, 10.0)];
let inertia = DATA::new(0.5);
let lfactor1 = DATA::new(2.0);
let lfactor2 = DATA::new(2.0);
let mut handler = pso_rust::basic::BasicPsoHandler::new(
    15,             // node amount
    2,              // dimension
    speed_field,    // limit of speed
    position_field, // limit of position for initialize
    inertia,        // inertia of the method
    lfactor1,       // learning factor 1
    lfactor2,       // learning factor 2
    -100.0          // initial best performance
).unwrap();
handler.start(200); // number of generation
println!("Best Performance{}", handler.get_global_best_performance());
let pos = handler.get_global_best_position();
println!("x1: {}, x2: {}", pos[0], pos[1]);

结果

Best Performance-0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000026331326256897253
x1: -0.00000000000000000000000000000000000000000000000037074530548407006, x2: 0.0000000000000000000000000000000000000000000000015797723076922348

当然,结果不可能完全准确。这是一台计算机和一个优化算法。然而,当忽略浮点精度时,结果也非常令人满意。

高级使用

正如上面所示,有两个核心部分是你需要做的:问题和参数。

表示一个问题

你想解决的任何问题都必须实现PsoParticle<T>,其中T表示你的位置数据是如何保存和使用的。在基本解决方案中,它应该是f64,并且泛型为其他解决方案做好了准备。

控制参数

对于参数,它们必须实现 PsoAcc<N, T, X> 特性,其中 N 代表 PsoNode<T>(在基本版本中,BasePsoNode<T>,其中 T 是您上述问题结构体中的类型),T 代表保存和使用位置数据的方式。这种方式在上述示例代码中没有展示。它用于 get_value 函数中的 this_node 参数,作为对您方法参数的高级控制。此外,X 代表特性中定义的函数的返回值,即参数的类型。

通过这种方式,您可以在优化过程中对参数有更多的控制。

依赖关系

~310KB