graphmind_optimization/algorithms/
hs.rs1use crate::common::{Individual, OptimizationResult, Problem, SolverConfig};
2use ndarray::Array1;
3use rand::prelude::*;
4
5pub struct HSSolver {
6 pub config: SolverConfig,
7 pub hmcr: f64, pub par: f64, pub bw: f64, }
11
12impl HSSolver {
13 pub fn new(config: SolverConfig) -> Self {
14 Self {
15 config,
16 hmcr: 0.9,
17 par: 0.3,
18 bw: 0.01,
19 }
20 }
21
22 pub fn solve<P: Problem>(&self, problem: &P) -> OptimizationResult {
23 let mut rng = thread_rng();
24 let dim = problem.dim();
25 let (lower, upper) = problem.bounds();
26
27 let mut hm: Vec<Individual> = (0..self.config.population_size)
29 .map(|_| {
30 let mut vars = Array1::zeros(dim);
31 for i in 0..dim {
32 vars[i] = rng.gen_range(lower[i]..upper[i]);
33 }
34 let fitness = problem.fitness(&vars);
35 Individual::new(vars, fitness)
36 })
37 .collect();
38
39 let mut history = Vec::with_capacity(self.config.max_iterations);
40
41 for _iter in 0..self.config.max_iterations {
42 let mut best_idx = 0;
44 let mut worst_idx = 0;
45 for i in 1..hm.len() {
46 if hm[i].fitness < hm[best_idx].fitness {
47 best_idx = i;
48 }
49 if hm[i].fitness > hm[worst_idx].fitness {
50 worst_idx = i;
51 }
52 }
53 history.push(hm[best_idx].fitness);
54
55 let mut new_vars = Array1::zeros(dim);
57 for j in 0..dim {
58 if rng.gen::<f64>() < self.hmcr {
59 let r_idx = rng.gen_range(0..self.config.population_size);
61 new_vars[j] = hm[r_idx].variables[j];
62
63 if rng.gen::<f64>() < self.par {
65 let range = upper[j] - lower[j];
66 let delta = (rng.gen::<f64>() - 0.5) * 2.0 * self.bw * range;
67 new_vars[j] = (new_vars[j] + delta).clamp(lower[j], upper[j]);
68 }
69 } else {
70 new_vars[j] = rng.gen_range(lower[j]..upper[j]);
72 }
73 }
74
75 let new_fitness = problem.fitness(&new_vars);
76
77 if new_fitness < hm[worst_idx].fitness {
79 hm[worst_idx] = Individual::new(new_vars, new_fitness);
80 }
81 }
82
83 hm.sort_by(|a, b| a.fitness.partial_cmp(&b.fitness).unwrap());
85 let best = &hm[0];
86
87 OptimizationResult {
88 best_variables: best.variables.clone(),
89 best_fitness: best.fitness,
90 history,
91 }
92 }
93}