1use crate::geometry::{Boundary, Geometry};
4use crate::result::SolveResult;
5use crate::Result;
6
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
12#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
13pub enum Strategy {
14 #[default]
16 BottomLeftFill,
17 NfpGuided,
19 GeneticAlgorithm,
21 Brkga,
23 SimulatedAnnealing,
25 ExtremePoint,
27 Gdrr,
29 Alns,
31 MilpExact,
33 HybridExact,
35}
36
37impl Strategy {
38 pub fn parse(name: &str) -> Option<Self> {
45 match name.trim().to_lowercase().as_str() {
46 "blf" | "bottomleftfill" => Some(Self::BottomLeftFill),
47 "nfp" | "nfpguided" => Some(Self::NfpGuided),
48 "ga" | "genetic" | "geneticalgorithm" => Some(Self::GeneticAlgorithm),
49 "brkga" => Some(Self::Brkga),
50 "sa" | "simulatedannealing" => Some(Self::SimulatedAnnealing),
51 "ep" | "extremepoint" => Some(Self::ExtremePoint),
52 "gdrr" => Some(Self::Gdrr),
53 "alns" => Some(Self::Alns),
54 "milpexact" | "exact" => Some(Self::MilpExact),
55 "hybridexact" | "hybrid" => Some(Self::HybridExact),
56 _ => None,
57 }
58 }
59}
60
61#[derive(Debug, Clone)]
63#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
64pub struct Config {
65 pub strategy: Strategy,
67
68 pub spacing: f64,
70
71 pub margin: f64,
73
74 pub time_limit_ms: u64,
76
77 pub target_utilization: Option<f64>,
79
80 pub threads: usize,
82
83 pub population_size: usize,
86
87 pub max_generations: u32,
89
90 pub crossover_rate: f64,
92
93 pub mutation_rate: f64,
95
96 pub elite_count: usize,
98
99 pub seed: Option<u64>,
102}
103
104impl Default for Config {
105 fn default() -> Self {
106 Self {
107 strategy: Strategy::default(),
108 spacing: 0.0,
109 margin: 0.0,
110 time_limit_ms: 30000,
111 target_utilization: None,
112 threads: 0,
113 population_size: 100,
114 max_generations: 500,
115 crossover_rate: 0.85,
116 mutation_rate: 0.05,
117 elite_count: 5,
118 seed: None,
119 }
120 }
121}
122
123impl Config {
124 pub fn new() -> Self {
126 Self::default()
127 }
128
129 pub fn with_strategy(mut self, strategy: Strategy) -> Self {
131 self.strategy = strategy;
132 self
133 }
134
135 pub fn with_spacing(mut self, spacing: f64) -> Self {
137 self.spacing = spacing;
138 self
139 }
140
141 pub fn with_margin(mut self, margin: f64) -> Self {
143 self.margin = margin;
144 self
145 }
146
147 pub fn with_time_limit(mut self, ms: u64) -> Self {
149 self.time_limit_ms = ms;
150 self
151 }
152
153 pub fn with_target_utilization(mut self, util: f64) -> Self {
155 self.target_utilization = Some(util.clamp(0.0, 1.0));
156 self
157 }
158
159 pub fn with_seed(mut self, seed: u64) -> Self {
161 self.seed = Some(seed);
162 self
163 }
164}
165
166pub type ProgressCallback = Box<dyn Fn(ProgressInfo) + Send + Sync>;
168
169#[derive(Debug, Clone, Default)]
171pub struct ProgressInfo {
172 pub iteration: u32,
174 pub total_iterations: u32,
176 pub utilization: f64,
178 pub best_fitness: f64,
180 pub items_placed: usize,
182 pub total_items: usize,
184 pub elapsed_ms: u64,
186 pub phase: String,
188 pub running: bool,
190}
191
192impl ProgressInfo {
193 pub fn new() -> Self {
195 Self {
196 running: true,
197 ..Default::default()
198 }
199 }
200
201 pub fn with_iteration(mut self, current: u32, total: u32) -> Self {
203 self.iteration = current;
204 self.total_iterations = total;
205 self
206 }
207
208 pub fn with_utilization(mut self, utilization: f64) -> Self {
210 self.utilization = utilization;
211 self
212 }
213
214 pub fn with_fitness(mut self, fitness: f64) -> Self {
216 self.best_fitness = fitness;
217 self
218 }
219
220 pub fn with_items(mut self, placed: usize, total: usize) -> Self {
222 self.items_placed = placed;
223 self.total_items = total;
224 self
225 }
226
227 pub fn with_elapsed(mut self, elapsed_ms: u64) -> Self {
229 self.elapsed_ms = elapsed_ms;
230 self
231 }
232
233 pub fn with_phase(mut self, phase: impl Into<String>) -> Self {
235 self.phase = phase.into();
236 self
237 }
238
239 pub fn finished(mut self) -> Self {
241 self.running = false;
242 self
243 }
244
245 pub fn progress_percent(&self) -> f64 {
247 if self.total_iterations > 0 {
248 self.iteration as f64 / self.total_iterations as f64
249 } else {
250 0.0
251 }
252 }
253}
254
255pub trait Solver {
257 type Geometry: Geometry;
259 type Boundary: Boundary;
261 type Scalar;
263
264 fn solve(
266 &self,
267 geometries: &[Self::Geometry],
268 boundary: &Self::Boundary,
269 ) -> Result<SolveResult<Self::Scalar>>;
270
271 fn solve_with_progress(
273 &self,
274 geometries: &[Self::Geometry],
275 boundary: &Self::Boundary,
276 callback: ProgressCallback,
277 ) -> Result<SolveResult<Self::Scalar>>;
278
279 fn cancel(&self);
281}