1个不稳定版本
0.1.0 | 2023年2月2日 |
---|
#1212 在 算法
18KB
274 行
PSO-Rust
这是什么?
这是一个专注于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