1use crate::gradient_free::{GradientFreeConfig, ObjectiveFunction};
12use crate::{OptimizerError, OptimizerResult};
13use scirs2_core::random::{Random, Rng};
14use scirs2_core::RngExt;
15use std::f32::consts::PI;
16
17#[derive(Debug, Clone)]
25pub struct QuantumPSO {
26 pub num_particles: usize,
28 pub alpha: f32,
30 pub adaptive_alpha: bool,
32 pub alpha_initial: f32,
34 pub alpha_final: f32,
36 pub config: GradientFreeConfig,
38}
39
40impl QuantumPSO {
41 pub fn new(num_particles: usize, alpha: f32) -> Self {
42 Self {
43 num_particles,
44 alpha,
45 adaptive_alpha: false,
46 alpha_initial: 1.0,
47 alpha_final: 0.5,
48 config: GradientFreeConfig::default(),
49 }
50 }
51
52 pub fn with_adaptive_alpha(mut self, alpha_initial: f32, alpha_final: f32) -> Self {
54 self.adaptive_alpha = true;
55 self.alpha_initial = alpha_initial;
56 self.alpha_final = alpha_final;
57 self
58 }
59
60 pub fn with_config(mut self, config: GradientFreeConfig) -> Self {
61 self.config = config;
62 self
63 }
64
65 pub fn optimize<F: ObjectiveFunction>(
67 &self,
68 objective: &F,
69 initial_bounds: &[(f32, f32)],
70 ) -> OptimizerResult<QuantumOptimizationResult> {
71 use scirs2_core::random::{Random, Rng};
72 let mut rng = Random::seed(self.config.seed.unwrap_or(42));
73
74 let dimension = initial_bounds.len();
75 let mut positions = Vec::with_capacity(self.num_particles);
76 let mut personal_best_positions = Vec::with_capacity(self.num_particles);
77 let mut personal_best_values = Vec::with_capacity(self.num_particles);
78
79 for _ in 0..self.num_particles {
81 let mut position = Vec::with_capacity(dimension);
82 for i in 0..dimension {
83 let (min_bound, max_bound) = initial_bounds[i];
84 position.push(rng.random::<f32>() * (max_bound - min_bound) + min_bound);
85 }
86 positions.push(position);
87 }
88
89 let mut global_best_position = vec![0.0; dimension];
91 let mut global_best_value = f32::INFINITY;
92 let mut evaluations = 0;
93 let mut history = Vec::new();
94
95 for i in 0..self.num_particles {
96 let value = objective.evaluate(&positions[i])?;
97 personal_best_positions.push(positions[i].clone());
98 personal_best_values.push(value);
99 evaluations += 1;
100 history.push((positions[i].clone(), value));
101
102 if value < global_best_value {
103 global_best_value = value;
104 global_best_position = positions[i].clone();
105 }
106 }
107
108 let mut iterations = 0;
109 let mut stagnation_count = 0;
110 let max_iterations = self.config.max_evaluations / self.num_particles;
111
112 while evaluations < self.config.max_evaluations
113 && stagnation_count < self.config.max_stagnation
114 {
115 let old_global_best = global_best_value;
116
117 let current_alpha = if self.adaptive_alpha {
119 let progress = iterations as f32 / max_iterations as f32;
120 self.alpha_initial - (self.alpha_initial - self.alpha_final) * progress
121 } else {
122 self.alpha
123 };
124
125 let mut mbest = vec![0.0; dimension];
127 for pbest in &personal_best_positions {
128 for j in 0..dimension {
129 mbest[j] += pbest[j];
130 }
131 }
132 for j in 0..dimension {
133 mbest[j] /= self.num_particles as f32;
134 }
135
136 for i in 0..self.num_particles {
138 for j in 0..dimension {
139 let phi = rng.random::<f32>();
141 let p =
142 phi * personal_best_positions[i][j] + (1.0 - phi) * global_best_position[j];
143
144 let u = rng.random::<f32>();
146 let sign = if rng.random::<f32>() < 0.5 { 1.0 } else { -1.0 };
147
148 let delta = current_alpha * (mbest[j] - positions[i][j]).abs() * (-u.ln());
150 positions[i][j] = p + sign * delta;
151
152 let (min_bound, max_bound) = initial_bounds[j];
154 positions[i][j] = positions[i][j].max(min_bound).min(max_bound);
155 }
156
157 let value = objective.evaluate(&positions[i])?;
159 evaluations += 1;
160 history.push((positions[i].clone(), value));
161
162 if value < personal_best_values[i] {
164 personal_best_values[i] = value;
165 personal_best_positions[i] = positions[i].clone();
166
167 if value < global_best_value {
169 global_best_value = value;
170 global_best_position = positions[i].clone();
171 }
172 }
173 }
174
175 if (global_best_value - old_global_best).abs() < self.config.tolerance {
177 stagnation_count += 1;
178 } else {
179 stagnation_count = 0;
180 }
181
182 iterations += 1;
183 }
184
185 Ok(QuantumOptimizationResult {
186 best_parameters: global_best_position,
187 best_value: global_best_value,
188 evaluations,
189 iterations,
190 history,
191 converged: stagnation_count >= self.config.max_stagnation
192 || evaluations >= self.config.max_evaluations,
193 })
194 }
195}
196
197#[derive(Debug, Clone)]
204pub struct QuantumGeneticAlgorithm {
205 pub population_size: usize,
207 pub theta: f32,
209 pub max_generations: usize,
211 pub config: GradientFreeConfig,
213}
214
215impl QuantumGeneticAlgorithm {
216 pub fn new(population_size: usize, theta: f32, max_generations: usize) -> Self {
217 Self {
218 population_size,
219 theta,
220 max_generations,
221 config: GradientFreeConfig::default(),
222 }
223 }
224
225 pub fn optimize<F: ObjectiveFunction>(
227 &self,
228 objective: &F,
229 initial_bounds: &[(f32, f32)],
230 ) -> OptimizerResult<QuantumOptimizationResult> {
231 let mut rng = Random::seed(self.config.seed.unwrap_or(42));
232 let dimension = initial_bounds.len();
233
234 let mut q_population: Vec<Vec<(f32, f32)>> = Vec::with_capacity(self.population_size);
238 for _ in 0..self.population_size {
239 let mut q_individual = Vec::with_capacity(dimension);
240 for _ in 0..dimension {
241 let alpha = 1.0 / 2.0_f32.sqrt();
243 let beta = 1.0 / 2.0_f32.sqrt();
244 q_individual.push((alpha, beta));
245 }
246 q_population.push(q_individual);
247 }
248
249 let mut best_parameters = vec![0.0; dimension];
250 let mut best_value = f32::INFINITY;
251 let mut evaluations = 0;
252 let mut history = Vec::new();
253
254 for generation in 0..self.max_generations {
255 let mut classical_population = Vec::with_capacity(self.population_size);
257 let mut fitnesses = Vec::with_capacity(self.population_size);
258
259 for q_individual in &q_population {
260 let mut classical_solution = Vec::with_capacity(dimension);
261
262 for (j, &(alpha, _beta)) in q_individual.iter().enumerate() {
263 let prob_one = alpha * alpha;
265 let bit = if rng.random::<f32>() < prob_one {
266 1.0
267 } else {
268 0.0
269 };
270
271 let (min_bound, max_bound) = initial_bounds[j];
273 let value = min_bound + bit * (max_bound - min_bound);
274 classical_solution.push(value);
275 }
276
277 let fitness = objective.evaluate(&classical_solution)?;
278 evaluations += 1;
279 history.push((classical_solution.clone(), fitness));
280
281 if fitness < best_value {
282 best_value = fitness;
283 best_parameters = classical_solution.clone();
284 }
285
286 classical_population.push(classical_solution);
287 fitnesses.push(fitness);
288 }
289
290 let best_idx = fitnesses
292 .iter()
293 .enumerate()
294 .min_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
295 .map(|(idx, _)| idx)
296 .expect("population should not be empty");
297
298 for i in 0..self.population_size {
300 for j in 0..dimension {
301 let (alpha, beta) = q_population[i][j];
302
303 let sign = if fitnesses[i] > fitnesses[best_idx] {
305 if classical_population[i][j] < classical_population[best_idx][j] {
307 1.0
308 } else {
309 -1.0
310 }
311 } else {
312 0.0 };
314
315 let theta = sign * self.theta;
318 let cos_theta = theta.cos();
319 let sin_theta = theta.sin();
320
321 let new_alpha = cos_theta * alpha - sin_theta * beta;
322 let new_beta = sin_theta * alpha + cos_theta * beta;
323
324 q_population[i][j] = (new_alpha, new_beta);
325 }
326 }
327
328 if evaluations >= self.config.max_evaluations {
330 break;
331 }
332 }
333
334 Ok(QuantumOptimizationResult {
335 best_parameters,
336 best_value,
337 evaluations,
338 iterations: self.max_generations,
339 history,
340 converged: true,
341 })
342 }
343}
344
345#[derive(Debug, Clone)]
350pub struct QuantumAnnealing {
351 pub num_replicas: usize,
353 pub temperature_initial: f32,
355 pub temperature_final: f32,
357 pub gamma_initial: f32,
359 pub gamma_final: f32,
361 pub num_steps: usize,
363 pub config: GradientFreeConfig,
365}
366
367impl QuantumAnnealing {
368 pub fn new(num_replicas: usize, num_steps: usize) -> Self {
369 Self {
370 num_replicas,
371 temperature_initial: 10.0,
372 temperature_final: 0.01,
373 gamma_initial: 5.0,
374 gamma_final: 0.01,
375 num_steps,
376 config: GradientFreeConfig::default(),
377 }
378 }
379
380 pub fn optimize<F: ObjectiveFunction>(
382 &self,
383 objective: &F,
384 initial_bounds: &[(f32, f32)],
385 ) -> OptimizerResult<QuantumOptimizationResult> {
386 let mut rng = Random::seed(self.config.seed.unwrap_or(42));
387 let dimension = initial_bounds.len();
388
389 let mut replicas: Vec<Vec<f32>> = Vec::with_capacity(self.num_replicas);
391 for _ in 0..self.num_replicas {
392 let mut replica = Vec::with_capacity(dimension);
393 for i in 0..dimension {
394 let (min_bound, max_bound) = initial_bounds[i];
395 replica.push(rng.random::<f32>() * (max_bound - min_bound) + min_bound);
396 }
397 replicas.push(replica);
398 }
399
400 let mut best_parameters = replicas[0].clone();
401 let mut best_value = objective.evaluate(&best_parameters)?;
402 let mut evaluations = 1;
403 let mut history = vec![(best_parameters.clone(), best_value)];
404
405 for step in 0..self.num_steps {
406 let progress = step as f32 / self.num_steps as f32;
408 let temperature = self.temperature_initial
409 - (self.temperature_initial - self.temperature_final) * progress;
410 let gamma = self.gamma_initial - (self.gamma_initial - self.gamma_final) * progress;
411
412 for r in 0..self.num_replicas {
414 let current_energy = objective.evaluate(&replicas[r])?;
415 evaluations += 1;
416
417 let mut candidate = replicas[r].clone();
419 for j in 0..dimension {
420 let (min_bound, max_bound) = initial_bounds[j];
421 let perturbation = (rng.random::<f32>() - 0.5) * gamma;
422 candidate[j] = (candidate[j] + perturbation).max(min_bound).min(max_bound);
423 }
424
425 let candidate_energy = objective.evaluate(&candidate)?;
426 evaluations += 1;
427
428 let delta_classical = candidate_energy - current_energy;
430
431 let r_next = (r + 1) % self.num_replicas;
433 let r_prev = if r == 0 { self.num_replicas - 1 } else { r - 1 };
434
435 let mut delta_quantum = 0.0;
436 for j in 0..dimension {
437 let coupling = -temperature / 2.0
438 * ((candidate[j] - replicas[r_next][j]).powi(2)
439 + (candidate[j] - replicas[r_prev][j]).powi(2)
440 - (replicas[r][j] - replicas[r_next][j]).powi(2)
441 - (replicas[r][j] - replicas[r_prev][j]).powi(2));
442 delta_quantum += coupling;
443 }
444
445 let delta_total = delta_classical + delta_quantum;
446
447 let accept_prob = if delta_total < 0.0 {
449 1.0
450 } else {
451 (-delta_total / temperature).exp()
452 };
453
454 if rng.random::<f32>() < accept_prob {
455 replicas[r] = candidate.clone();
456
457 if candidate_energy < best_value {
458 best_value = candidate_energy;
459 best_parameters = candidate.clone();
460 history.push((best_parameters.clone(), best_value));
461 }
462 }
463 }
464
465 if evaluations >= self.config.max_evaluations {
467 break;
468 }
469 }
470
471 Ok(QuantumOptimizationResult {
472 best_parameters,
473 best_value,
474 evaluations,
475 iterations: self.num_steps,
476 history,
477 converged: true,
478 })
479 }
480}
481
482#[derive(Debug, Clone)]
484pub struct QuantumOptimizationResult {
485 pub best_parameters: Vec<f32>,
486 pub best_value: f32,
487 pub evaluations: usize,
488 pub iterations: usize,
489 pub history: Vec<(Vec<f32>, f32)>,
490 pub converged: bool,
491}
492
493#[cfg(test)]
494mod tests {
495 use super::*;
496 use std::sync::Arc;
497 use torsh_core::device::CpuDevice;
498
499 struct SphereFunction;
500 impl ObjectiveFunction for SphereFunction {
501 fn evaluate(&self, x: &[f32]) -> OptimizerResult<f32> {
502 Ok(x.iter().map(|&xi| xi * xi).sum())
503 }
504
505 fn dimension(&self) -> usize {
506 10
507 }
508
509 fn bounds(&self) -> Option<(Vec<f32>, Vec<f32>)> {
510 Some((vec![-5.0; 10], vec![5.0; 10]))
511 }
512 }
513
514 #[test]
515 fn test_quantum_pso() -> OptimizerResult<()> {
516 let qpso = QuantumPSO::new(20, 0.7)
517 .with_adaptive_alpha(1.0, 0.5)
518 .with_config(GradientFreeConfig {
519 max_evaluations: 2000,
520 tolerance: 1e-6,
521 max_stagnation: 50,
522 device: Arc::new(CpuDevice::new()),
523 seed: Some(42),
524 verbose: false,
525 });
526
527 let objective = SphereFunction;
528 let bounds = vec![(-5.0, 5.0); 10];
529
530 let result = qpso.optimize(&objective, &bounds)?;
531
532 assert!(result.best_value < 0.1);
533 assert!(result.converged);
534 assert!(result.evaluations <= 2000);
535
536 Ok(())
537 }
538
539 #[test]
540 fn test_quantum_ga() -> OptimizerResult<()> {
541 let qga = QuantumGeneticAlgorithm::new(50, 0.05 * std::f32::consts::PI, 200);
542
543 let objective = SphereFunction;
544 let bounds = vec![(-5.0, 5.0); 5];
545
546 let result = qga.optimize(&objective, &bounds)?;
547
548 assert!(result.best_value.is_finite());
551 assert!(result.evaluations > 0);
552
553 Ok(())
554 }
555
556 #[test]
557 fn test_quantum_annealing() -> OptimizerResult<()> {
558 let qa = QuantumAnnealing::new(20, 1000);
559
560 let objective = SphereFunction;
561 let bounds = vec![(-5.0, 5.0); 5];
562
563 let result = qa.optimize(&objective, &bounds)?;
564
565 assert!(result.best_value < 50.0); assert!(result.converged);
569
570 Ok(())
571 }
572
573 struct RosenbrockFunction;
574 impl ObjectiveFunction for RosenbrockFunction {
575 fn evaluate(&self, x: &[f32]) -> OptimizerResult<f32> {
576 let mut sum = 0.0;
577 for i in 0..x.len() - 1 {
578 sum += 100.0 * (x[i + 1] - x[i] * x[i]).powi(2) + (1.0 - x[i]).powi(2);
579 }
580 Ok(sum)
581 }
582
583 fn dimension(&self) -> usize {
584 5
585 }
586
587 fn bounds(&self) -> Option<(Vec<f32>, Vec<f32>)> {
588 Some((vec![-2.0; 5], vec![2.0; 5]))
589 }
590 }
591
592 #[test]
593 fn test_qpso_rosenbrock() -> OptimizerResult<()> {
594 let qpso = QuantumPSO::new(30, 0.8)
595 .with_adaptive_alpha(1.2, 0.4)
596 .with_config(GradientFreeConfig {
597 max_evaluations: 5000,
598 tolerance: 1e-5,
599 max_stagnation: 100,
600 device: Arc::new(CpuDevice::new()),
601 seed: Some(42),
602 verbose: false,
603 });
604
605 let objective = RosenbrockFunction;
606 let bounds = vec![(-2.0, 2.0); 5];
607
608 let result = qpso.optimize(&objective, &bounds)?;
609
610 assert!(result.best_value < 10.0);
612
613 Ok(())
614 }
615}