10 个版本 (破坏性更新)

0.14.0 2024年8月12日
0.13.0 2024年7月26日
0.12.0 2023年12月26日
0.11.0 2023年11月4日
0.9.0 2023年2月13日

#240算法

Download history 8/week @ 2024-05-19 169/week @ 2024-05-26 13/week @ 2024-06-02 3/week @ 2024-06-09 1/week @ 2024-06-16 3/week @ 2024-06-30 108/week @ 2024-07-21 72/week @ 2024-07-28 234/week @ 2024-08-11

每月414 次下载

自定义许可证

50KB
962

localsearch

Rust本地搜索优化库

实现算法

所有算法都使用Rayon并行化。

  1. 爬山法。
  2. 禁忌搜索。
  3. 模拟退火
  4. ε贪婪搜索,爬山法的一个变体,即使试验解的得分比之前的一个更差,也会以恒定的概率接受试验解。
  5. 相对退火,模拟退火的一个变体,它使用相对得分差来计算转换概率。
  6. 逻辑退火,相对退火的一个变体,它使用逻辑函数而不是简单的指数。

如何使用

您需要实现自己的模型,该模型实现了OptModel特质。实际的优化由每个算法函数处理。以下是一个使用爬山算法优化二次函数的简单示例。

use std::time::Duration;

use anyhow::Result as AnyResult;
use indicatif::{ProgressBar, ProgressDrawTarget, ProgressStyle};
use localsearch::{
    optim::{HillClimbingOptimizer, LocalSearchOptimizer},
    OptModel, OptProgress,
};
use ordered_float::NotNan;
use rand::{self, distributions::Uniform, prelude::Distribution};

type SolutionType = Vec<f64>;
type ScoreType = NotNan<f64>;

#[derive(Clone)]
struct QuadraticModel {
    k: usize,
    centers: Vec<f64>,
    dist: Uniform<f64>,
}

impl QuadraticModel {
    fn new(k: usize, centers: Vec<f64>, value_range: (f64, f64)) -> Self {
        let (low, high) = value_range;
        let dist = Uniform::new(low, high);
        Self { k, centers, dist }
    }

    fn evaluate_solution(&self, solution: &SolutionType) -> NotNan<f64> {
        let score = (0..self.k)
            .into_iter()
            .map(|i| (solution[i] - self.centers[i]).powf(2.0))
            .sum();
        NotNan::new(score).unwrap()
    }
}

impl OptModel for QuadraticModel {
    type SolutionType = SolutionType;
    type TransitionType = ();
    type ScoreType = ScoreType;
    fn generate_random_solution<R: rand::Rng>(
        &self,
        rng: &mut R,
    ) -> AnyResult<(Self::SolutionType, Self::ScoreType)> {
        let solution = self.dist.sample_iter(rng).take(self.k).collect::<Vec<_>>();
        let score = self.evaluate_solution(&solution);
        Ok((solution, score))
    }

    fn generate_trial_solution<R: rand::Rng>(
        &self,
        current_solution: Self::SolutionType,
        _current_score: Self::ScoreType,
        rng: &mut R,
    ) -> (Self::SolutionType, Self::TransitionType, NotNan<f64>) {
        let k = rng.gen_range(0..self.k);
        let v = self.dist.sample(rng);
        let mut new_solution = current_solution;
        new_solution[k] = v;
        let score = self.evaluate_solution(&new_solution);
        (new_solution, (), score)
    }
}

fn create_pbar(n_iter: u64) -> ProgressBar {
    let pb = ProgressBar::new(n_iter);
    pb.set_style(
        ProgressStyle::default_bar()
            .template(
                "{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {pos}/{len} (eta={eta}) {msg} ",
            ).unwrap()
            .progress_chars("#>-")
    );
    pb.set_draw_target(ProgressDrawTarget::stderr_with_hz(10));
    pb
}

fn main() {
    let model = QuadraticModel::new(3, vec![2.0, 0.0, -3.5], (-10.0, 10.0));

    println!("running Hill Climbing optimizer");
    let n_iter = 10000;
    let time_limit = Duration::from_secs_f32(1.0);
    let patiance = 1000;
    let n_trials = 50;
    let opt = HillClimbingOptimizer::new(patiance, n_trials);
    let pb = create_pbar(n_iter as u64);
    let callback = |op: OptProgress<SolutionType, ScoreType>| {
        pb.set_message(format!("best score {:e}", op.score.into_inner()));
        pb.set_position(op.iter as u64);
    };

    let res = opt.run(&model, None, n_iter, time_limit, Some(&callback), ());
    pb.finish();
    dbg!(res.unwrap());
}

此外,您还可以向您的模型添加preprocess_initial_solutionpostprocess_final_solution。在优化迭代开始之前调用preprocess_initial_solution。如果没有提供初始解,则调用generate_initial_solution,然后传递生成的解给preprocess_initial_solution。在优化迭代之后调用postprocess_final_solution

更多详细信息可以在API文档、示例和测试代码中找到。

依赖项

~2–2.8MB
~56K SLoC