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,
71
72 pub margin: f64,
74
75 pub time_limit_ms: u64,
81
82 pub target_utilization: Option<f64>,
84
85 pub threads: usize,
87
88 pub population_size: usize,
91
92 pub max_generations: u32,
94
95 pub crossover_rate: f64,
97
98 pub mutation_rate: f64,
100
101 pub elite_count: usize,
103
104 pub seed: Option<u64>,
107}
108
109impl Default for Config {
110 fn default() -> Self {
111 Self {
112 strategy: Strategy::default(),
113 spacing: 0.0,
114 margin: 0.0,
115 time_limit_ms: 30000,
116 target_utilization: None,
117 threads: 0,
118 population_size: 100,
119 max_generations: 500,
120 crossover_rate: 0.85,
121 mutation_rate: 0.05,
122 elite_count: 5,
123 seed: None,
124 }
125 }
126}
127
128impl Config {
129 pub fn new() -> Self {
131 Self::default()
132 }
133
134 pub fn with_strategy(mut self, strategy: Strategy) -> Self {
136 self.strategy = strategy;
137 self
138 }
139
140 pub fn with_spacing(mut self, spacing: f64) -> Self {
142 self.spacing = spacing;
143 self
144 }
145
146 pub fn with_margin(mut self, margin: f64) -> Self {
148 self.margin = margin;
149 self
150 }
151
152 pub fn with_time_limit(mut self, ms: u64) -> Self {
154 self.time_limit_ms = ms;
155 self
156 }
157
158 pub fn with_target_utilization(mut self, util: f64) -> Self {
160 self.target_utilization = Some(util.clamp(0.0, 1.0));
161 self
162 }
163
164 pub fn with_seed(mut self, seed: u64) -> Self {
166 self.seed = Some(seed);
167 self
168 }
169}
170
171pub type ProgressCallback = Box<dyn Fn(ProgressInfo) + Send + Sync>;
173
174#[derive(Debug, Clone, Default)]
176pub struct ProgressInfo {
177 pub iteration: u32,
179 pub total_iterations: u32,
181 pub utilization: f64,
183 pub best_fitness: f64,
185 pub items_placed: usize,
187 pub total_items: usize,
189 pub elapsed_ms: u64,
191 pub phase: String,
193 pub running: bool,
195}
196
197impl ProgressInfo {
198 pub fn new() -> Self {
200 Self {
201 running: true,
202 ..Default::default()
203 }
204 }
205
206 pub fn with_iteration(mut self, current: u32, total: u32) -> Self {
208 self.iteration = current;
209 self.total_iterations = total;
210 self
211 }
212
213 pub fn with_utilization(mut self, utilization: f64) -> Self {
215 self.utilization = utilization;
216 self
217 }
218
219 pub fn with_fitness(mut self, fitness: f64) -> Self {
221 self.best_fitness = fitness;
222 self
223 }
224
225 pub fn with_items(mut self, placed: usize, total: usize) -> Self {
227 self.items_placed = placed;
228 self.total_items = total;
229 self
230 }
231
232 pub fn with_elapsed(mut self, elapsed_ms: u64) -> Self {
234 self.elapsed_ms = elapsed_ms;
235 self
236 }
237
238 pub fn with_phase(mut self, phase: impl Into<String>) -> Self {
240 self.phase = phase.into();
241 self
242 }
243
244 pub fn finished(mut self) -> Self {
246 self.running = false;
247 self
248 }
249
250 pub fn progress_percent(&self) -> f64 {
252 if self.total_iterations > 0 {
253 self.iteration as f64 / self.total_iterations as f64
254 } else {
255 0.0
256 }
257 }
258}
259
260pub trait Solver {
262 type Geometry: Geometry;
264 type Boundary: Boundary;
266 type Scalar;
268
269 fn solve(
271 &self,
272 geometries: &[Self::Geometry],
273 boundary: &Self::Boundary,
274 ) -> Result<SolveResult<Self::Scalar>>;
275
276 fn solve_with_progress(
278 &self,
279 geometries: &[Self::Geometry],
280 boundary: &Self::Boundary,
281 callback: ProgressCallback,
282 ) -> Result<SolveResult<Self::Scalar>>;
283
284 fn cancel(&self);
286}