Skip to content

Commit

Permalink
Merge branch 'release/0.16.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
lucidfrontier45 committed Aug 27, 2024
2 parents fdefe85 + 94f10bf commit 5c66041
Show file tree
Hide file tree
Showing 22 changed files with 133 additions and 153 deletions.
2 changes: 1 addition & 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.15.0"
version = "0.16.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,12 @@ fn main() {
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>| {
let mut 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));
let res = opt.run_with_callback(&model, None, n_iter, time_limit, &mut callback);
pb.finish();
dbg!(res.unwrap());
}
Expand All @@ -122,4 +122,4 @@ If initial solution is not supplied, `generate_initial_solution` is called and t
`postprocess_final_solution` is called after the optimization iteration.


Further details can be found at API document, example and test codes.
Further details can be found at API document, example and test codes.
4 changes: 2 additions & 2 deletions examples/quadratic_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,12 @@ fn main() {
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>| {
let mut 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));
let res = opt.run_with_callback(&model, None, n_iter, time_limit, &mut callback);
pb.finish();
dbg!(res.unwrap());
}
30 changes: 15 additions & 15 deletions examples/tsp_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,15 +186,15 @@ impl Default for DequeTabuList {
}

impl TabuList<TSPModel> for DequeTabuList {
fn contains(&self, item: &(SolutionType, TransitionType)) -> bool {
let (_, (_, inserted_edges)) = item;
fn contains(&self, transition: &TransitionType) -> bool {
let (_, inserted_edges) = transition;
inserted_edges
.iter()
.any(|edge| self.buff.iter().any(|e| *e == *edge))
}

fn append(&mut self, item: (SolutionType, TransitionType)) {
let (_, (removed_edges, _)) = item;
fn append(&mut self, transition: TransitionType) {
let (removed_edges, _) = transition;
for edge in removed_edges {
if self.buff.iter().all(|e| *e != edge) {
self.buff.append(edge);
Expand Down Expand Up @@ -256,7 +256,7 @@ fn main() {
let initial_solution = tsp_model.generate_random_solution(&mut rng).ok();

let pb = create_pbar(n_iter as u64);
let callback = |op: OptProgress<SolutionType, ScoreType>| {
let mut callback = |op: OptProgress<SolutionType, ScoreType>| {
let ratio = op.accepted_count as f64 / op.iter as f64;
pb.set_message(format!(
"best score {:.4e}, count = {}, acceptance ratio {:.2e}",
Expand All @@ -270,12 +270,12 @@ fn main() {
println!("run hill climbing");
let optimizer = HillClimbingOptimizer::new(1000, 200);
let (final_solution, final_score) = optimizer
.run(
.run_with_callback(
&tsp_model,
initial_solution.clone(),
n_iter,
time_limit,
Some(&callback),
&mut callback,
)
.unwrap();
println!(
Expand All @@ -289,12 +289,12 @@ fn main() {
println!("run tabu search");
let optimizer = TabuSearchOptimizer::<TSPModel, DequeTabuList>::new(patience, 200, 10, 20);
let (final_solution, final_score) = optimizer
.run(
.run_with_callback(
&tsp_model,
initial_solution.clone(),
n_iter,
time_limit,
Some(&callback),
&mut callback,
)
.unwrap();
println!(
Expand All @@ -308,12 +308,12 @@ fn main() {
println!("run annealing");
let optimizer = SimulatedAnnealingOptimizer::new(patience, 200, 200.0, 50.0);
let (final_solution, final_score) = optimizer
.run(
.run_with_callback(
&tsp_model,
initial_solution.clone(),
n_iter,
time_limit,
Some(&callback),
&mut callback,
)
.unwrap();
println!(
Expand All @@ -327,12 +327,12 @@ fn main() {
println!("run epsilon greedy");
let optimizer = EpsilonGreedyOptimizer::new(patience, 200, 10, 0.3);
let (final_solution, final_score) = optimizer
.run(
.run_with_callback(
&tsp_model,
initial_solution.clone(),
n_iter,
time_limit,
Some(&callback),
&mut callback,
)
.unwrap();
println!(
Expand All @@ -346,12 +346,12 @@ fn main() {
println!("run relative annealing");
let optimizer = RelativeAnnealingOptimizer::new(patience, 200, 10, 1e1);
let (final_solution, final_score) = optimizer
.run(
.run_with_callback(
&tsp_model,
initial_solution,
n_iter,
time_limit,
Some(&callback),
&mut callback,
)
.unwrap();
println!(
Expand Down
6 changes: 3 additions & 3 deletions src/callback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ impl<S, SC: Ord> OptProgress<S, SC> {
/// pb.set_draw_target(ProgressDrawTarget::stderr_with_hz(10));
/// pb
/// };
/// let callback = |op: OptProgress<SolutionType, ScoreType>| {
/// let mut callback = |op: OptProgress<SolutionType, ScoreType>| {
/// let ratio = op.accepted_count as f64 / op.iter as f64;
/// pb.set_message(format!(
/// "best score {:.4e}, count = {}, acceptance ratio {:.2e}",
Expand All @@ -55,6 +55,6 @@ impl<S, SC: Ord> OptProgress<S, SC> {
/// pb.set_position(op.iter as u64);
/// };
/// ```
pub trait OptCallbackFn<S, SC: PartialOrd>: Fn(OptProgress<S, SC>) {}
pub trait OptCallbackFn<S, SC: PartialOrd>: FnMut(OptProgress<S, SC>) {}

impl<F, S, SC: PartialOrd> OptCallbackFn<S, SC> for F where F: Fn(OptProgress<S, SC>) {}
impl<F, S, SC: PartialOrd> OptCallbackFn<S, SC> for F where F: FnMut(OptProgress<S, SC>) {}
38 changes: 24 additions & 14 deletions src/optim/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,42 @@ use crate::{callback::OptCallbackFn, Duration, OptModel};
#[auto_impl(&, Box, Rc, Arc)]
pub trait LocalSearchOptimizer<M: OptModel> {
/// Start optimization
fn optimize<F>(
fn optimize(
&self,
model: &M,
initial_solution: M::SolutionType,
initial_score: M::ScoreType,
n_iter: usize,
time_limit: Duration,
callback: Option<&F>,
) -> (M::SolutionType, M::ScoreType)
where
M: OptModel,
F: OptCallbackFn<M::SolutionType, M::ScoreType>;
callback: &mut dyn OptCallbackFn<M::SolutionType, M::ScoreType>,
) -> (M::SolutionType, M::ScoreType);

/// Start optimization
fn run<F>(
/// generate initial solution if not given and run optimization
fn run(
&self,
model: &M,
initial_solution_and_score: Option<(M::SolutionType, M::ScoreType)>,
n_iter: usize,
time_limit: Duration,
) -> AnyResult<(M::SolutionType, M::ScoreType)> {
self.run_with_callback(
model,
initial_solution_and_score,
n_iter,
time_limit,
&mut |_| {},
)
}

/// generate initial solution if not given and run optimization with callback
fn run_with_callback(
&self,
model: &M,
initial_solution_and_score: Option<(M::SolutionType, M::ScoreType)>,
n_iter: usize,
time_limit: Duration,
callback: Option<&F>,
) -> AnyResult<(M::SolutionType, M::ScoreType)>
where
M: OptModel,
F: OptCallbackFn<M::SolutionType, M::ScoreType>,
{
callback: &mut dyn OptCallbackFn<M::SolutionType, M::ScoreType>,
) -> AnyResult<(M::SolutionType, M::ScoreType)> {
let (initial_solution, initial_score) = match initial_solution_and_score {
Some((solution, score)) => (solution, score),
None => {
Expand Down
10 changes: 3 additions & 7 deletions src/optim/epsilon_greedy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,15 @@ impl<M: OptModel> LocalSearchOptimizer<M> for EpsilonGreedyOptimizer {
/// - `n_iter`: maximum iterations
/// - `time_limit`: maximum iteration time
/// - `callback` : callback function that will be invoked at the end of each iteration
fn optimize<F>(
fn optimize(
&self,
model: &M,
initial_solution: M::SolutionType,
initial_score: M::ScoreType,
n_iter: usize,
time_limit: Duration,
callback: Option<&F>,
) -> (M::SolutionType, M::ScoreType)
where
M: OptModel + Sync + Send,
F: OptCallbackFn<M::SolutionType, M::ScoreType>,
{
callback: &mut dyn OptCallbackFn<M::SolutionType, M::ScoreType>,
) -> (M::SolutionType, M::ScoreType) {
let optimizer = GenericLocalSearchOptimizer::new(
self.patience,
self.n_trials,
Expand Down
17 changes: 6 additions & 11 deletions src/optim/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,18 +63,15 @@ where
/// - `n_iter`: maximum iterations
/// - `time_limit`: maximum iteration time
/// - `callback` : callback function that will be invoked at the end of each iteration
fn optimize<F>(
fn optimize(
&self,
model: &M,
initial_solution: M::SolutionType,
initial_score: M::ScoreType,
n_iter: usize,
time_limit: Duration,
callback: Option<&F>,
) -> (M::SolutionType, M::ScoreType)
where
F: OptCallbackFn<M::SolutionType, M::ScoreType>,
{
callback: &mut dyn OptCallbackFn<M::SolutionType, M::ScoreType>,
) -> (M::SolutionType, M::ScoreType) {
let start_time = Instant::now();
let mut rng = rand::thread_rng();
let mut current_solution = initial_solution;
Expand Down Expand Up @@ -129,11 +126,9 @@ where
break;
}

if let Some(f) = callback {
let progress =
OptProgress::new(it, accepted_counter, best_solution.clone(), best_score);
f(progress);
}
let progress =
OptProgress::new(it, accepted_counter, best_solution.clone(), best_score);
callback(progress);
}

let best_solution = (*best_solution.borrow()).clone();
Expand Down
10 changes: 3 additions & 7 deletions src/optim/hill_climbing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,15 @@ impl<M: OptModel> LocalSearchOptimizer<M> for HillClimbingOptimizer {
/// - `n_iter`: maximum iterations
/// - `time_limit`: maximum iteration time
/// - `callback` : callback function that will be invoked at the end of each iteration
/// - `_extra_in` : not used
fn optimize<F>(
fn optimize(
&self,
model: &M,
initial_solution: M::SolutionType,
initial_score: M::ScoreType,
n_iter: usize,
time_limit: Duration,
callback: Option<&F>,
) -> (M::SolutionType, M::ScoreType)
where
F: OptCallbackFn<M::SolutionType, M::ScoreType>,
{
callback: &mut dyn OptCallbackFn<M::SolutionType, M::ScoreType>,
) -> (M::SolutionType, M::ScoreType) {
let optimizer = EpsilonGreedyOptimizer::new(self.patience, self.n_trials, usize::MAX, 0.0);
optimizer.optimize(
model,
Expand Down
9 changes: 3 additions & 6 deletions src/optim/logistic_annealing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,15 @@ impl<M: OptModel<ScoreType = NotNan<f64>>> LocalSearchOptimizer<M> for LogisticA
/// - `n_iter`: maximum iterations
/// - `time_limit`: maximum iteration time
/// - `callback` : callback function that will be invoked at the end of each iteration
fn optimize<F>(
fn optimize(
&self,
model: &M,
initial_solution: M::SolutionType,
initial_score: M::ScoreType,
n_iter: usize,
time_limit: Duration,
callback: Option<&F>,
) -> (M::SolutionType, M::ScoreType)
where
F: OptCallbackFn<M::SolutionType, M::ScoreType>,
{
callback: &mut dyn OptCallbackFn<M::SolutionType, M::ScoreType>,
) -> (M::SolutionType, M::ScoreType) {
let optimizer = GenericLocalSearchOptimizer::new(
self.patience,
self.n_trials,
Expand Down
10 changes: 3 additions & 7 deletions src/optim/relative_annealing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,19 +52,15 @@ impl<M: OptModel<ScoreType = NotNan<f64>>> LocalSearchOptimizer<M> for RelativeA
/// - `n_iter`: maximum iterations
/// - `time_limit`: maximum iteration time
/// - `callback` : callback function that will be invoked at the end of each iteration
/// - `_extra_in` : not used
fn optimize<F>(
fn optimize(
&self,
model: &M,
initial_solution: M::SolutionType,
initial_score: M::ScoreType,
n_iter: usize,
time_limit: Duration,
callback: Option<&F>,
) -> (M::SolutionType, M::ScoreType)
where
F: OptCallbackFn<M::SolutionType, M::ScoreType>,
{
callback: &mut dyn OptCallbackFn<M::SolutionType, M::ScoreType>,
) -> (M::SolutionType, M::ScoreType) {
let optimizer = GenericLocalSearchOptimizer::new(
self.patience,
self.n_trials,
Expand Down
Loading

0 comments on commit 5c66041

Please sign in to comment.