Skip to content

Commit

Permalink
Merge branch 'release/0.14.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
lucidfrontier45 committed Aug 12, 2024
2 parents 9a2e914 + eea59aa commit ce0592a
Show file tree
Hide file tree
Showing 20 changed files with 267 additions and 202 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ categories = ["algorithms"]
repository = "https://github.com/lucidfrontier45/localsearch"
license-file = "LICENSE"
readme = "README.md"
version = "0.13.1"
version = "0.14.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand All @@ -17,6 +17,7 @@ rand = "0.8.5"
ordered-float = "4.2.2"
rayon = "1.10.0"
auto_impl = "1.2.0"
anyhow = "1.0.86"

[target.'cfg(target_family = "wasm")'.dependencies]
web-time = "1.1.0"
Expand Down
41 changes: 21 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ All of the algorithms are parallelized with Rayon.
You need to implement your own model that implements `OptModel` trait. Actual optimization is handled by each algorithm functions. Here is a simple example to optimize a quadratic function with Hill Climbing algorithm.

```rust
use std::{error::Error, time::Duration};
use std::time::Duration;

use anyhow::Result as AnyResult;
use indicatif::{ProgressBar, ProgressDrawTarget, ProgressStyle};
use localsearch::{
optim::{HillClimbingOptimizer, LocalSearchOptimizer},
Expand All @@ -27,6 +28,9 @@ use localsearch::{
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,
Expand All @@ -40,10 +44,15 @@ impl QuadraticModel {
let dist = Uniform::new(low, high);
Self { k, centers, dist }
}
}

type SolutionType = Vec<f64>;
type ScoreType = NotNan<f64>;
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;
Expand All @@ -52,32 +61,25 @@ impl OptModel for QuadraticModel {
fn generate_random_solution<R: rand::Rng>(
&self,
rng: &mut R,
) -> Result<Self::SolutionType, Box<dyn Error>> {
) -> AnyResult<(Self::SolutionType, Self::ScoreType)> {
let solution = self.dist.sample_iter(rng).take(self.k).collect::<Vec<_>>();
Ok(solution)
let score = self.evaluate_solution(&solution);
Ok((solution, score))
}

fn generate_trial_solution<R: rand::Rng>(
&self,
current_solution: &Self::SolutionType,
current_solution: Self::SolutionType,
_current_score: Self::ScoreType,
rng: &mut R,
_current_score: Option<NotNan<f64>>,
) -> (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.clone();
let mut new_solution = current_solution;
new_solution[k] = v;
let score = self.evaluate_solution(&new_solution);
(new_solution, (), score)
}

fn evaluate_solution(&self, solution: &Self::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()
}
}

fn create_pbar(n_iter: u64) -> ProgressBar {
Expand All @@ -98,7 +100,7 @@ fn main() {

println!("running Hill Climbing optimizer");
let n_iter = 10000;
let time_limit = Duration::from_secs(60);
let time_limit = Duration::from_secs_f32(1.0);
let patiance = 1000;
let n_trials = 50;
let opt = HillClimbingOptimizer::new(patiance, n_trials);
Expand All @@ -110,9 +112,8 @@ fn main() {

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

```

In addition you can also add `preprocess_initial_solution` and `postprocess_final_solution` to your model.
Expand Down
38 changes: 20 additions & 18 deletions examples/quadratic_model.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{error::Error, time::Duration};
use std::time::Duration;

use anyhow::Result as AnyResult;
use indicatif::{ProgressBar, ProgressDrawTarget, ProgressStyle};
use localsearch::{
optim::{HillClimbingOptimizer, LocalSearchOptimizer},
Expand All @@ -8,6 +9,9 @@ use localsearch::{
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,
Expand All @@ -21,10 +25,15 @@ impl QuadraticModel {
let dist = Uniform::new(low, high);
Self { k, centers, dist }
}
}

type SolutionType = Vec<f64>;
type ScoreType = NotNan<f64>;
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;
Expand All @@ -33,32 +42,25 @@ impl OptModel for QuadraticModel {
fn generate_random_solution<R: rand::Rng>(
&self,
rng: &mut R,
) -> Result<Self::SolutionType, Box<dyn Error>> {
) -> AnyResult<(Self::SolutionType, Self::ScoreType)> {
let solution = self.dist.sample_iter(rng).take(self.k).collect::<Vec<_>>();
Ok(solution)
let score = self.evaluate_solution(&solution);
Ok((solution, score))
}

fn generate_trial_solution<R: rand::Rng>(
&self,
current_solution: &Self::SolutionType,
current_solution: Self::SolutionType,
_current_score: Self::ScoreType,
rng: &mut R,
_current_score: Option<NotNan<f64>>,
) -> (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.clone();
let mut new_solution = current_solution;
new_solution[k] = v;
let score = self.evaluate_solution(&new_solution);
(new_solution, (), score)
}

fn evaluate_solution(&self, solution: &Self::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()
}
}

fn create_pbar(n_iter: u64) -> ProgressBar {
Expand Down Expand Up @@ -91,5 +93,5 @@ fn main() {

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

0 comments on commit ce0592a

Please sign in to comment.