1use crate::error::{Result, SimulatorError};
4use crate::scirs2_integration::SciRS2Backend;
5use scirs2_core::ndarray::{Array1, Array2};
6use scirs2_core::random::prelude::*;
7use scirs2_core::Complex64;
8use std::collections::HashMap;
9use std::f64::consts::PI;
10use std::sync::{Arc, Mutex};
11
12use super::types::*;
13
14impl QuantumInspiredFramework {
15 pub fn new(config: QuantumInspiredConfig) -> Result<Self> {
17 let state = QuantumInspiredState {
18 variables: Array1::zeros(config.num_variables),
19 objective_value: f64::INFINITY,
20 iteration: 0,
21 best_solution: Array1::zeros(config.num_variables),
22 best_objective: f64::INFINITY,
23 convergence_history: Vec::new(),
24 runtime_stats: RuntimeStats::default(),
25 };
26 let rng = thread_rng();
27 Ok(Self {
28 config,
29 state,
30 backend: None,
31 stats: QuantumInspiredStats::default(),
32 rng: Arc::new(Mutex::new(rng)),
33 })
34 }
35 pub fn set_backend(&mut self, backend: SciRS2Backend) {
37 self.backend = Some(backend);
38 }
39 pub fn optimize(&mut self) -> Result<OptimizationResult> {
41 let start_time = std::time::Instant::now();
42 match self.config.optimization_config.algorithm_type {
43 OptimizationAlgorithm::QuantumGeneticAlgorithm => self.quantum_genetic_algorithm(),
44 OptimizationAlgorithm::QuantumParticleSwarm => {
45 self.quantum_particle_swarm_optimization()
46 }
47 OptimizationAlgorithm::QuantumSimulatedAnnealing => self.quantum_simulated_annealing(),
48 OptimizationAlgorithm::QuantumDifferentialEvolution => {
49 self.quantum_differential_evolution()
50 }
51 OptimizationAlgorithm::ClassicalQAOA => self.classical_qaoa_simulation(),
52 OptimizationAlgorithm::ClassicalVQE => self.classical_vqe_simulation(),
53 OptimizationAlgorithm::QuantumAntColony => self.quantum_ant_colony_optimization(),
54 OptimizationAlgorithm::QuantumHarmonySearch => self.quantum_harmony_search(),
55 }
56 }
57 pub(super) fn quantum_genetic_algorithm(&mut self) -> Result<OptimizationResult> {
59 let pop_size = self.config.algorithm_config.population_size;
60 let num_vars = self.config.num_variables;
61 let max_iterations = self.config.algorithm_config.max_iterations;
62 let mut population = self.initialize_quantum_population(pop_size, num_vars)?;
63 let mut fitness_values = vec![0.0; pop_size];
64 for (i, individual) in population.iter().enumerate() {
65 fitness_values[i] = self.evaluate_objective(individual)?;
66 self.state.runtime_stats.function_evaluations += 1;
67 }
68 for generation in 0..max_iterations {
69 self.state.iteration = generation;
70 let parents = self.quantum_selection(&population, &fitness_values)?;
71 let mut offspring = self.quantum_crossover(&parents)?;
72 self.quantum_mutation(&mut offspring)?;
73 let mut offspring_fitness = vec![0.0; offspring.len()];
74 for (i, individual) in offspring.iter().enumerate() {
75 offspring_fitness[i] = self.evaluate_objective(individual)?;
76 self.state.runtime_stats.function_evaluations += 1;
77 }
78 self.quantum_replacement(
79 &mut population,
80 &mut fitness_values,
81 offspring,
82 offspring_fitness,
83 )?;
84 let best_idx = fitness_values
85 .iter()
86 .enumerate()
87 .min_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
88 .map(|(idx, _)| idx)
89 .unwrap_or(0);
90 if fitness_values[best_idx] < self.state.best_objective {
91 self.state.best_objective = fitness_values[best_idx];
92 self.state.best_solution = population[best_idx].clone();
93 }
94 self.state
95 .convergence_history
96 .push(self.state.best_objective);
97 if self.check_convergence()? {
98 break;
99 }
100 }
101 Ok(OptimizationResult {
102 solution: self.state.best_solution.clone(),
103 objective_value: self.state.best_objective,
104 iterations: self.state.iteration,
105 converged: self.check_convergence()?,
106 runtime_stats: self.state.runtime_stats.clone(),
107 metadata: HashMap::new(),
108 })
109 }
110 pub(super) fn initialize_quantum_population(
112 &self,
113 pop_size: usize,
114 num_vars: usize,
115 ) -> Result<Vec<Array1<f64>>> {
116 let mut population = Vec::with_capacity(pop_size);
117 let bounds = &self.config.optimization_config.bounds;
118 let quantum_params = &self.config.algorithm_config.quantum_parameters;
119 for _ in 0..pop_size {
120 let mut individual = Array1::zeros(num_vars);
121 for j in 0..num_vars {
122 let (min_bound, max_bound) = if j < bounds.len() {
123 bounds[j]
124 } else {
125 (-1.0, 1.0)
126 };
127 let mut rng = self.rng.lock().expect("RNG lock poisoned");
128 let base_value = rng
129 .random::<f64>()
130 .mul_add(max_bound - min_bound, min_bound);
131 let superposition_noise = (rng.random::<f64>() - 0.5)
132 * quantum_params.superposition_strength
133 * (max_bound - min_bound);
134 individual[j] = (base_value + superposition_noise).clamp(min_bound, max_bound);
135 }
136 population.push(individual);
137 }
138 Ok(population)
139 }
140 pub(super) fn quantum_selection(
142 &self,
143 population: &[Array1<f64>],
144 fitness: &[f64],
145 ) -> Result<Vec<Array1<f64>>> {
146 let pop_size = population.len();
147 let elite_size = (self.config.algorithm_config.elite_ratio * pop_size as f64) as usize;
148 let quantum_params = &self.config.algorithm_config.quantum_parameters;
149 let mut indexed_fitness: Vec<(usize, f64)> =
150 fitness.iter().enumerate().map(|(i, &f)| (i, f)).collect();
151 indexed_fitness.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
152 let mut parents = Vec::new();
153 for i in 0..elite_size {
154 parents.push(population[indexed_fitness[i].0].clone());
155 }
156 let mut rng = self.rng.lock().expect("RNG lock poisoned");
157 while parents.len() < pop_size {
158 let tournament_size = 3;
159 let mut tournament_indices = Vec::new();
160 for _ in 0..tournament_size {
161 tournament_indices.push(rng.random_range(0..pop_size));
162 }
163 let mut selection_probabilities = vec![0.0; tournament_size];
164 for (i, &idx) in tournament_indices.iter().enumerate() {
165 let normalized_fitness = 1.0 / (1.0 + fitness[idx]);
166 let interference_factor = (quantum_params.interference_strength
167 * (i as f64 * PI / tournament_size as f64))
168 .cos()
169 .abs();
170 selection_probabilities[i] = normalized_fitness * (1.0 + interference_factor);
171 }
172 let sum: f64 = selection_probabilities.iter().sum();
173 for prob in &mut selection_probabilities {
174 *prob /= sum;
175 }
176 let mut cumulative = 0.0;
177 let random_val = rng.random::<f64>();
178 for (i, &prob) in selection_probabilities.iter().enumerate() {
179 cumulative += prob;
180 if random_val <= cumulative {
181 parents.push(population[tournament_indices[i]].clone());
182 break;
183 }
184 }
185 }
186 Ok(parents)
187 }
188 pub(super) fn quantum_crossover(&self, parents: &[Array1<f64>]) -> Result<Vec<Array1<f64>>> {
190 let mut offspring = Vec::new();
191 let crossover_rate = self.config.algorithm_config.crossover_rate;
192 let quantum_params = &self.config.algorithm_config.quantum_parameters;
193 let mut rng = self.rng.lock().expect("RNG lock poisoned");
194 for i in (0..parents.len()).step_by(2) {
195 if i + 1 < parents.len() && rng.random::<f64>() < crossover_rate {
196 let parent1 = &parents[i];
197 let parent2 = &parents[i + 1];
198 let mut child1 = parent1.clone();
199 let mut child2 = parent2.clone();
200 for j in 0..parent1.len() {
201 let entanglement_strength = quantum_params.entanglement_strength;
202 let alpha = rng.random::<f64>();
203 let entangled_val1 = alpha.mul_add(parent1[j], (1.0 - alpha) * parent2[j]);
204 let entangled_val2 = (1.0 - alpha).mul_add(parent1[j], alpha * parent2[j]);
205 let correlation = entanglement_strength
206 * (parent1[j] - parent2[j]).abs()
207 * (rng.random::<f64>() - 0.5);
208 child1[j] = entangled_val1 + correlation;
209 child2[j] = entangled_val2 - correlation;
210 }
211 offspring.push(child1);
212 offspring.push(child2);
213 } else {
214 offspring.push(parents[i].clone());
215 if i + 1 < parents.len() {
216 offspring.push(parents[i + 1].clone());
217 }
218 }
219 }
220 Ok(offspring)
221 }
222 pub(super) fn quantum_mutation(&mut self, population: &mut [Array1<f64>]) -> Result<()> {
224 let mutation_rate = self.config.algorithm_config.mutation_rate;
225 let quantum_params = &self.config.algorithm_config.quantum_parameters;
226 let bounds = &self.config.optimization_config.bounds;
227 let mut rng = self.rng.lock().expect("RNG lock poisoned");
228 for individual in population.iter_mut() {
229 for j in 0..individual.len() {
230 if rng.random::<f64>() < mutation_rate {
231 let (min_bound, max_bound) = if j < bounds.len() {
232 bounds[j]
233 } else {
234 (-1.0, 1.0)
235 };
236 let current_val = individual[j];
237 let range = max_bound - min_bound;
238 let gaussian_mutation =
239 rng.random::<f64>() * 0.1 * range * (rng.random::<f64>() - 0.5);
240 let tunneling_prob = quantum_params.tunneling_probability;
241 let tunneling_mutation = if rng.random::<f64>() < tunneling_prob {
242 (rng.random::<f64>() - 0.5) * range
243 } else {
244 0.0
245 };
246 individual[j] = (current_val + gaussian_mutation + tunneling_mutation)
247 .clamp(min_bound, max_bound);
248 }
249 }
250 }
251 self.state.runtime_stats.quantum_operations += population.len();
252 Ok(())
253 }
254 pub(super) fn quantum_replacement(
256 &self,
257 population: &mut Vec<Array1<f64>>,
258 fitness: &mut Vec<f64>,
259 offspring: Vec<Array1<f64>>,
260 offspring_fitness: Vec<f64>,
261 ) -> Result<()> {
262 let quantum_params = &self.config.algorithm_config.quantum_parameters;
263 let measurement_prob = quantum_params.measurement_probability;
264 let mut rng = self.rng.lock().expect("RNG lock poisoned");
265 let mut combined_population = population.clone();
266 combined_population.extend(offspring);
267 let mut combined_fitness = fitness.clone();
268 combined_fitness.extend(offspring_fitness);
269 let pop_size = population.len();
270 let mut new_population = Vec::with_capacity(pop_size);
271 let mut new_fitness = Vec::with_capacity(pop_size);
272 let mut indexed_combined: Vec<(usize, f64)> = combined_fitness
273 .iter()
274 .enumerate()
275 .map(|(i, &f)| (i, f))
276 .collect();
277 indexed_combined.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
278 for i in 0..pop_size {
279 if i < indexed_combined.len() {
280 let idx = indexed_combined[i].0;
281 let acceptance_prob = if rng.random::<f64>() < measurement_prob {
282 1.0
283 } else {
284 1.0 / (1.0 + (i as f64 / pop_size as f64))
285 };
286 if rng.random::<f64>() < acceptance_prob {
287 new_population.push(combined_population[idx].clone());
288 new_fitness.push(combined_fitness[idx]);
289 }
290 }
291 }
292 while new_population.len() < pop_size {
293 for i in 0..indexed_combined.len() {
294 if new_population.len() >= pop_size {
295 break;
296 }
297 let idx = indexed_combined[i].0;
298 if !new_population.iter().any(|x| {
299 x.iter()
300 .zip(combined_population[idx].iter())
301 .all(|(a, b)| (a - b).abs() < 1e-10)
302 }) {
303 new_population.push(combined_population[idx].clone());
304 new_fitness.push(combined_fitness[idx]);
305 }
306 }
307 }
308 new_population.truncate(pop_size);
309 new_fitness.truncate(pop_size);
310 *population = new_population;
311 *fitness = new_fitness;
312 Ok(())
313 }
314 pub(super) fn quantum_particle_swarm_optimization(&mut self) -> Result<OptimizationResult> {
316 let pop_size = self.config.algorithm_config.population_size;
317 let num_vars = self.config.num_variables;
318 let max_iterations = self.config.algorithm_config.max_iterations;
319 let quantum_params = self.config.algorithm_config.quantum_parameters.clone();
320 let bounds = self.config.optimization_config.bounds.clone();
321 let mut particles = self.initialize_quantum_population(pop_size, num_vars)?;
322 let mut velocities: Vec<Array1<f64>> = vec![Array1::zeros(num_vars); pop_size];
323 let mut personal_best = particles.clone();
324 let mut personal_best_fitness = vec![f64::INFINITY; pop_size];
325 let mut global_best = Array1::zeros(num_vars);
326 let mut global_best_fitness = f64::INFINITY;
327 for (i, particle) in particles.iter().enumerate() {
328 let fitness = self.evaluate_objective(particle)?;
329 personal_best_fitness[i] = fitness;
330 if fitness < global_best_fitness {
331 global_best_fitness = fitness;
332 global_best = particle.clone();
333 }
334 self.state.runtime_stats.function_evaluations += 1;
335 }
336 let w = 0.7;
337 let c1 = 2.0;
338 let c2 = 2.0;
339 for iteration in 0..max_iterations {
340 self.state.iteration = iteration;
341 for i in 0..pop_size {
342 let mut rng = self.rng.lock().expect("RNG lock poisoned");
343 for j in 0..num_vars {
344 let r1 = rng.random::<f64>();
345 let r2 = rng.random::<f64>();
346 let cognitive_term = c1 * r1 * (personal_best[i][j] - particles[i][j]);
347 let social_term = c2 * r2 * (global_best[j] - particles[i][j]);
348 let quantum_fluctuation =
349 quantum_params.superposition_strength * (rng.random::<f64>() - 0.5);
350 let quantum_tunneling =
351 if rng.random::<f64>() < quantum_params.tunneling_probability {
352 (rng.random::<f64>() - 0.5) * 2.0
353 } else {
354 0.0
355 };
356 velocities[i][j] = w * velocities[i][j]
357 + cognitive_term
358 + social_term
359 + quantum_fluctuation
360 + quantum_tunneling;
361 }
362 for j in 0..num_vars {
363 particles[i][j] += velocities[i][j];
364 let (min_bound, max_bound) = if j < bounds.len() {
365 bounds[j]
366 } else {
367 (-10.0, 10.0)
368 };
369 particles[i][j] = particles[i][j].clamp(min_bound, max_bound);
370 }
371 drop(rng);
372 let fitness = self.evaluate_objective(&particles[i])?;
373 self.state.runtime_stats.function_evaluations += 1;
374 if fitness < personal_best_fitness[i] {
375 personal_best_fitness[i] = fitness;
376 personal_best[i] = particles[i].clone();
377 }
378 if fitness < global_best_fitness {
379 global_best_fitness = fitness;
380 global_best = particles[i].clone();
381 }
382 }
383 self.state.best_objective = global_best_fitness;
384 self.state.best_solution = global_best.clone();
385 self.state.convergence_history.push(global_best_fitness);
386 if self.check_convergence()? {
387 break;
388 }
389 }
390 Ok(OptimizationResult {
391 solution: global_best,
392 objective_value: global_best_fitness,
393 iterations: self.state.iteration,
394 converged: self.check_convergence()?,
395 runtime_stats: self.state.runtime_stats.clone(),
396 metadata: HashMap::new(),
397 })
398 }
399 pub(super) fn quantum_simulated_annealing(&mut self) -> Result<OptimizationResult> {
401 let max_iterations = self.config.algorithm_config.max_iterations;
402 let temperature_schedule = self.config.algorithm_config.temperature_schedule;
403 let quantum_parameters = self.config.algorithm_config.quantum_parameters.clone();
404 let bounds = self.config.optimization_config.bounds.clone();
405 let num_vars = self.config.num_variables;
406 let mut current_solution = Array1::zeros(num_vars);
407 let mut rng = self.rng.lock().expect("RNG lock poisoned");
408 for i in 0..num_vars {
409 let (min_bound, max_bound) = if i < bounds.len() {
410 bounds[i]
411 } else {
412 (-10.0, 10.0)
413 };
414 current_solution[i] = rng
415 .random::<f64>()
416 .mul_add(max_bound - min_bound, min_bound);
417 }
418 drop(rng);
419 let mut current_energy = self.evaluate_objective(¤t_solution)?;
420 let mut best_solution = current_solution.clone();
421 let mut best_energy = current_energy;
422 self.state.runtime_stats.function_evaluations += 1;
423 let initial_temp: f64 = 100.0;
424 let final_temp: f64 = 0.01;
425 for iteration in 0..max_iterations {
426 self.state.iteration = iteration;
427 let temp = match temperature_schedule {
428 TemperatureSchedule::Exponential => {
429 initial_temp
430 * (final_temp / initial_temp).powf(iteration as f64 / max_iterations as f64)
431 }
432 TemperatureSchedule::Linear => (initial_temp - final_temp)
433 .mul_add(-(iteration as f64 / max_iterations as f64), initial_temp),
434 TemperatureSchedule::Logarithmic => initial_temp / (1.0 + (iteration as f64).ln()),
435 TemperatureSchedule::QuantumAdiabatic => {
436 let s = iteration as f64 / max_iterations as f64;
437 initial_temp.mul_add(1.0 - s, final_temp * s * (1.0 - (1.0 - s).powi(3)))
438 }
439 TemperatureSchedule::Custom => initial_temp * 0.95_f64.powi(iteration as i32),
440 };
441 let mut neighbor = current_solution.clone();
442 let quantum_params = &quantum_parameters;
443 let mut rng = self.rng.lock().expect("RNG lock poisoned");
444 for i in 0..num_vars {
445 if rng.random::<f64>() < 0.5 {
446 let (min_bound, max_bound) = if i < bounds.len() {
447 bounds[i]
448 } else {
449 (-10.0, 10.0)
450 };
451 let step_size = temp / initial_temp;
452 let gaussian_step =
453 rng.random::<f64>() * step_size * (max_bound - min_bound) * 0.1;
454 let tunneling_move =
455 if rng.random::<f64>() < quantum_params.tunneling_probability {
456 (rng.random::<f64>() - 0.5) * (max_bound - min_bound) * 0.5
457 } else {
458 0.0
459 };
460 neighbor[i] = (current_solution[i] + gaussian_step + tunneling_move)
461 .clamp(min_bound, max_bound);
462 }
463 }
464 drop(rng);
465 let neighbor_energy = self.evaluate_objective(&neighbor)?;
466 self.state.runtime_stats.function_evaluations += 1;
467 let delta_energy = neighbor_energy - current_energy;
468 let acceptance_prob = if delta_energy < 0.0 {
469 1.0
470 } else {
471 let boltzmann_factor = (-delta_energy / temp).exp();
472 let quantum_correction = quantum_params.interference_strength
473 * (2.0 * PI * iteration as f64 / max_iterations as f64).cos()
474 * 0.1;
475 (boltzmann_factor + quantum_correction).clamp(0.0, 1.0)
476 };
477 let mut rng = self.rng.lock().expect("RNG lock poisoned");
478 if rng.random::<f64>() < acceptance_prob {
479 current_solution = neighbor;
480 current_energy = neighbor_energy;
481 if current_energy < best_energy {
482 best_solution = current_solution.clone();
483 best_energy = current_energy;
484 }
485 }
486 drop(rng);
487 self.state.best_objective = best_energy;
488 self.state.best_solution = best_solution.clone();
489 self.state.convergence_history.push(best_energy);
490 if temp < final_temp || self.check_convergence()? {
491 break;
492 }
493 }
494 Ok(OptimizationResult {
495 solution: best_solution,
496 objective_value: best_energy,
497 iterations: self.state.iteration,
498 converged: self.check_convergence()?,
499 runtime_stats: self.state.runtime_stats.clone(),
500 metadata: HashMap::new(),
501 })
502 }
503 pub(super) fn quantum_differential_evolution(&mut self) -> Result<OptimizationResult> {
505 let pop_size = self.config.algorithm_config.population_size.max(4);
506 let num_vars = self.config.num_variables;
507 let max_iterations = self.config.algorithm_config.max_iterations;
508 let mutation_factor = 0.5_f64;
510 let crossover_rate = self.config.algorithm_config.crossover_rate;
511 let bounds = self.config.optimization_config.bounds.clone();
512 let quantum_params = self.config.algorithm_config.quantum_parameters.clone();
513
514 let mut population = self.initialize_quantum_population(pop_size, num_vars)?;
516 let mut fitness: Vec<f64> = Vec::with_capacity(pop_size);
517 let mut best_solution = population[0].clone();
518 let mut best_fitness = f64::INFINITY;
519
520 for individual in &population {
522 let f = self.evaluate_objective(individual)?;
523 fitness.push(f);
524 self.state.runtime_stats.function_evaluations += 1;
525 if f < best_fitness {
526 best_fitness = f;
527 best_solution = individual.clone();
528 }
529 }
530
531 for iteration in 0..max_iterations {
532 self.state.iteration = iteration;
533
534 for i in 0..pop_size {
535 let mut rng = self.rng.lock().expect("RNG lock poisoned");
537 let mut indices: Vec<usize> = Vec::with_capacity(3);
538 let mut attempts = 0usize;
539 while indices.len() < 3 && attempts < 1000 {
540 let idx = (rng.random::<f64>() * pop_size as f64) as usize % pop_size;
541 if idx != i && !indices.contains(&idx) {
542 indices.push(idx);
543 }
544 attempts += 1;
545 }
546 while indices.len() < 3 {
548 let candidate = (i + indices.len() + 1) % pop_size;
549 if !indices.contains(&candidate) {
550 indices.push(candidate);
551 } else {
552 indices.push((candidate + 1) % pop_size);
553 }
554 }
555 let (r1, r2, r3) = (indices[0], indices[1], indices[2]);
556
557 let mut mutant = population[r1].clone();
559 for j in 0..num_vars {
560 mutant[j] =
561 mutation_factor.mul_add(population[r2][j] - population[r3][j], mutant[j]);
562 }
563
564 let mut trial = population[i].clone();
567 let guaranteed_j = (rng.random::<f64>() * num_vars as f64) as usize % num_vars;
568 for j in 0..num_vars {
569 if j == guaranteed_j || rng.random::<f64>() < crossover_rate {
570 trial[j] = mutant[j];
571 }
572 if rng.random::<f64>() < quantum_params.tunneling_probability {
574 let (min_b, max_b) = if j < bounds.len() {
575 bounds[j]
576 } else {
577 (-10.0, 10.0)
578 };
579 trial[j] = rng.random::<f64>().mul_add(max_b - min_b, min_b);
580 }
581 let (min_b, max_b) = if j < bounds.len() {
583 bounds[j]
584 } else {
585 (-10.0, 10.0)
586 };
587 trial[j] = trial[j].clamp(min_b, max_b);
588 }
589 drop(rng);
590
591 let trial_fitness = self.evaluate_objective(&trial)?;
593 self.state.runtime_stats.function_evaluations += 1;
594 if trial_fitness < fitness[i] {
595 fitness[i] = trial_fitness;
596 population[i] = trial;
597 if trial_fitness < best_fitness {
598 best_fitness = trial_fitness;
599 best_solution = population[i].clone();
600 }
601 }
602 }
603
604 self.state.best_objective = best_fitness;
605 self.state.best_solution = best_solution.clone();
606 self.state.convergence_history.push(best_fitness);
607
608 if self.check_convergence()? {
609 break;
610 }
611 }
612
613 Ok(OptimizationResult {
614 solution: best_solution,
615 objective_value: best_fitness,
616 iterations: self.state.iteration,
617 converged: self.check_convergence()?,
618 runtime_stats: self.state.runtime_stats.clone(),
619 metadata: HashMap::new(),
620 })
621 }
622 pub(super) fn classical_qaoa_simulation(&mut self) -> Result<OptimizationResult> {
627 let num_vars = self.config.num_variables;
628 let max_iterations = self.config.algorithm_config.max_iterations;
629 let bounds = self.config.optimization_config.bounds.clone();
630 let quantum_params = self.config.algorithm_config.quantum_parameters.clone();
631
632 let mut current = {
634 let mut rng = self.rng.lock().expect("RNG lock poisoned");
635 let mut v = Array1::zeros(num_vars);
636 for j in 0..num_vars {
637 let (lo, hi) = if j < bounds.len() {
638 bounds[j]
639 } else {
640 (-10.0, 10.0)
641 };
642 v[j] = rng.random::<f64>().mul_add(hi - lo, lo);
643 }
644 v
645 };
646 let mut current_energy = self.evaluate_objective(¤t)?;
647 let mut best = current.clone();
648 let mut best_energy = current_energy;
649 self.state.runtime_stats.function_evaluations += 1;
650
651 for iteration in 0..max_iterations {
652 self.state.iteration = iteration;
653 let layers = iteration as f64 / max_iterations.max(1) as f64;
654 let gamma = PI * layers;
656 let beta = std::f64::consts::FRAC_PI_2 * (1.0 - layers);
658
659 let mut next = current.clone();
660 let mut rng = self.rng.lock().expect("RNG lock poisoned");
661 for j in 0..num_vars {
662 let (lo, hi) = if j < bounds.len() {
663 bounds[j]
664 } else {
665 (-10.0, 10.0)
666 };
667 let range = hi - lo;
668 let shift = range * 0.05;
670 let cost_shift = (gamma.sin() * shift).mul_add(rng.random::<f64>() - 0.5, 0.0);
671 let mix_shift = if rng.random::<f64>() < beta.sin().powi(2) {
673 (rng.random::<f64>() - 0.5) * range * quantum_params.superposition_strength
674 } else {
675 0.0
676 };
677 let tunnel_shift = if rng.random::<f64>() < quantum_params.tunneling_probability {
679 (rng.random::<f64>() - 0.5) * range
680 } else {
681 0.0
682 };
683 next[j] = (current[j] + cost_shift + mix_shift + tunnel_shift).clamp(lo, hi);
684 }
685 drop(rng);
686
687 let next_energy = self.evaluate_objective(&next)?;
688 self.state.runtime_stats.function_evaluations += 1;
689
690 if next_energy < current_energy {
692 current = next;
693 current_energy = next_energy;
694 if current_energy < best_energy {
695 best_energy = current_energy;
696 best = current.clone();
697 }
698 }
699
700 self.state.best_objective = best_energy;
701 self.state.best_solution = best.clone();
702 self.state.convergence_history.push(best_energy);
703 if self.check_convergence()? {
704 break;
705 }
706 }
707
708 Ok(OptimizationResult {
709 solution: best,
710 objective_value: best_energy,
711 iterations: self.state.iteration,
712 converged: self.check_convergence()?,
713 runtime_stats: self.state.runtime_stats.clone(),
714 metadata: HashMap::new(),
715 })
716 }
717
718 pub(super) fn classical_vqe_simulation(&mut self) -> Result<OptimizationResult> {
723 let num_vars = self.config.num_variables;
724 let max_iterations = self.config.algorithm_config.max_iterations;
725 let bounds = self.config.optimization_config.bounds.clone();
726 let quantum_params = self.config.algorithm_config.quantum_parameters.clone();
727
728 let mut theta = {
730 let mut rng = self.rng.lock().expect("RNG lock poisoned");
731 let mut v = Array1::zeros(num_vars);
732 for j in 0..num_vars {
733 let (lo, hi) = if j < bounds.len() {
734 bounds[j]
735 } else {
736 (-10.0, 10.0)
737 };
738 v[j] = rng.random::<f64>().mul_add(hi - lo, lo);
739 }
740 v
741 };
742 let mut current_energy = self.evaluate_objective(&theta)?;
743 let mut best = theta.clone();
744 let mut best_energy = current_energy;
745 self.state.runtime_stats.function_evaluations += 1;
746
747 for iteration in 0..max_iterations {
748 self.state.iteration = iteration;
749 let phase = 2.0 * PI * iteration as f64 / max_iterations.max(1) as f64;
751 let interference = quantum_params.interference_strength * phase.cos();
752
753 for j in 0..num_vars {
755 let (lo, hi) = if j < bounds.len() {
756 bounds[j]
757 } else {
758 (-10.0, 10.0)
759 };
760 let delta = 0.1 * (hi - lo) + interference.abs() * 0.01 * (hi - lo);
761
762 let mut plus = theta.clone();
763 plus[j] = (plus[j] + delta).clamp(lo, hi);
764 let mut minus = theta.clone();
765 minus[j] = (minus[j] - delta).clamp(lo, hi);
766
767 let e_plus = self.evaluate_objective(&plus)?;
768 let e_minus = self.evaluate_objective(&minus)?;
769 self.state.runtime_stats.function_evaluations += 2;
770
771 if e_plus < current_energy && e_plus <= e_minus {
772 theta = plus;
773 current_energy = e_plus;
774 } else if e_minus < current_energy {
775 theta = minus;
776 current_energy = e_minus;
777 }
778 }
779
780 {
782 let mut rng = self.rng.lock().expect("RNG lock poisoned");
783 if rng.random::<f64>() < quantum_params.tunneling_probability {
784 let j = (rng.random::<f64>() * num_vars as f64) as usize % num_vars;
785 let (lo, hi) = if j < bounds.len() {
786 bounds[j]
787 } else {
788 (-10.0, 10.0)
789 };
790 theta[j] = rng.random::<f64>().mul_add(hi - lo, lo);
791 current_energy = self.evaluate_objective(&theta)?;
792 self.state.runtime_stats.function_evaluations += 1;
793 }
794 }
795
796 if current_energy < best_energy {
797 best_energy = current_energy;
798 best = theta.clone();
799 }
800
801 self.state.best_objective = best_energy;
802 self.state.best_solution = best.clone();
803 self.state.convergence_history.push(best_energy);
804 if self.check_convergence()? {
805 break;
806 }
807 }
808
809 Ok(OptimizationResult {
810 solution: best,
811 objective_value: best_energy,
812 iterations: self.state.iteration,
813 converged: self.check_convergence()?,
814 runtime_stats: self.state.runtime_stats.clone(),
815 metadata: HashMap::new(),
816 })
817 }
818
819 pub(super) fn quantum_ant_colony_optimization(&mut self) -> Result<OptimizationResult> {
824 const N_LEVELS: usize = 10;
825 let n_ants = self.config.algorithm_config.population_size.max(5);
826 let num_vars = self.config.num_variables;
827 let max_iterations = self.config.algorithm_config.max_iterations;
828 let bounds = self.config.optimization_config.bounds.clone();
829 let quantum_params = self.config.algorithm_config.quantum_parameters.clone();
830 let evaporation_rate = 1.0 - self.config.algorithm_config.mutation_rate.clamp(0.01, 0.99);
831
832 let mut pheromones = vec![vec![1.0f64; N_LEVELS]; num_vars];
834
835 let mut best_solution = {
836 let mut rng = self.rng.lock().expect("RNG lock poisoned");
837 let mut v = Array1::zeros(num_vars);
838 for j in 0..num_vars {
839 let (lo, hi) = if j < bounds.len() {
840 bounds[j]
841 } else {
842 (-10.0, 10.0)
843 };
844 v[j] = rng.random::<f64>().mul_add(hi - lo, lo);
845 }
846 v
847 };
848 let mut best_energy = self.evaluate_objective(&best_solution)?;
849 self.state.runtime_stats.function_evaluations += 1;
850
851 for iteration in 0..max_iterations {
852 self.state.iteration = iteration;
853 let q_phase = 2.0 * PI * iteration as f64 / max_iterations.max(1) as f64;
855 let q_boost = 1.0 + quantum_params.interference_strength * q_phase.cos();
856
857 let mut ant_solutions: Vec<Array1<f64>> = Vec::with_capacity(n_ants);
858 let mut ant_energies: Vec<f64> = Vec::with_capacity(n_ants);
859
860 for _ in 0..n_ants {
862 let mut solution = Array1::zeros(num_vars);
863 {
864 let mut rng = self.rng.lock().expect("RNG lock poisoned");
865 for j in 0..num_vars {
866 let (lo, hi) = if j < bounds.len() {
867 bounds[j]
868 } else {
869 (-10.0, 10.0)
870 };
871 if rng.random::<f64>() < quantum_params.tunneling_probability {
873 solution[j] = rng.random::<f64>().mul_add(hi - lo, lo);
874 continue;
875 }
876 let total: f64 = pheromones[j]
878 .iter()
879 .map(|&p| (p * q_boost).max(1e-10))
880 .sum();
881 let mut pick = rng.random::<f64>() * total;
882 let mut chosen_level = N_LEVELS - 1;
883 for (k, &p) in pheromones[j].iter().enumerate() {
884 pick -= (p * q_boost).max(1e-10);
885 if pick <= 0.0 {
886 chosen_level = k;
887 break;
888 }
889 }
890 solution[j] =
891 lo + (chosen_level as f64 + 0.5) / N_LEVELS as f64 * (hi - lo);
892 }
893 }
894
895 let energy = self.evaluate_objective(&solution)?;
896 self.state.runtime_stats.function_evaluations += 1;
897 if energy < best_energy {
898 best_energy = energy;
899 best_solution = solution.clone();
900 }
901 ant_solutions.push(solution);
902 ant_energies.push(energy);
903 }
904
905 for j in 0..num_vars {
907 for k in 0..N_LEVELS {
908 pheromones[j][k] *= evaporation_rate;
909 }
910 }
911
912 let min_e = ant_energies.iter().cloned().fold(f64::INFINITY, f64::min);
914 let max_e = ant_energies
915 .iter()
916 .cloned()
917 .fold(f64::NEG_INFINITY, f64::max);
918 let range = (max_e - min_e).max(1e-10);
919 for (ant_idx, solution) in ant_solutions.iter().enumerate() {
920 let deposit = 1.0 - (ant_energies[ant_idx] - min_e) / range;
921 for j in 0..num_vars {
922 let (lo, hi) = if j < bounds.len() {
923 bounds[j]
924 } else {
925 (-10.0, 10.0)
926 };
927 let level = ((solution[j] - lo) / (hi - lo) * N_LEVELS as f64) as usize;
928 let level = level.min(N_LEVELS - 1);
929 pheromones[j][level] += deposit;
930 }
931 }
932
933 self.state.best_objective = best_energy;
934 self.state.best_solution = best_solution.clone();
935 self.state.convergence_history.push(best_energy);
936 if self.check_convergence()? {
937 break;
938 }
939 }
940
941 Ok(OptimizationResult {
942 solution: best_solution,
943 objective_value: best_energy,
944 iterations: self.state.iteration,
945 converged: self.check_convergence()?,
946 runtime_stats: self.state.runtime_stats.clone(),
947 metadata: HashMap::new(),
948 })
949 }
950 pub(super) fn quantum_harmony_search(&mut self) -> Result<OptimizationResult> {
952 let hm_size = self.config.algorithm_config.population_size.max(5);
953 let num_vars = self.config.num_variables;
954 let max_iterations = self.config.algorithm_config.max_iterations;
955 let hmcr = 0.9_f64;
957 let par = 0.3_f64;
959 let bounds = self.config.optimization_config.bounds.clone();
960 let quantum_params = self.config.algorithm_config.quantum_parameters.clone();
961
962 let mut harmony_memory = self.initialize_quantum_population(hm_size, num_vars)?;
964 let mut hm_fitness: Vec<f64> = Vec::with_capacity(hm_size);
965 let mut best_solution = harmony_memory[0].clone();
966 let mut best_fitness = f64::INFINITY;
967
968 for harmony in &harmony_memory {
970 let f = self.evaluate_objective(harmony)?;
971 hm_fitness.push(f);
972 self.state.runtime_stats.function_evaluations += 1;
973 if f < best_fitness {
974 best_fitness = f;
975 best_solution = harmony.clone();
976 }
977 }
978
979 for iteration in 0..max_iterations {
980 self.state.iteration = iteration;
981
982 let mut new_harmony = Array1::zeros(num_vars);
984 let mut rng = self.rng.lock().expect("RNG lock poisoned");
985
986 for j in 0..num_vars {
987 let (min_b, max_b) = if j < bounds.len() {
988 bounds[j]
989 } else {
990 (-10.0, 10.0)
991 };
992
993 if rng.random::<f64>() < hmcr {
994 let hm_idx = (rng.random::<f64>() * hm_size as f64) as usize % hm_size;
996 new_harmony[j] = harmony_memory[hm_idx][j];
997
998 if rng.random::<f64>() < par {
1000 let bw = 0.05 * (max_b - min_b);
1002 let perturbation = (rng.random::<f64>() - 0.5) * 2.0 * bw;
1003 new_harmony[j] = (new_harmony[j] + perturbation).clamp(min_b, max_b);
1004 }
1005 } else {
1006 new_harmony[j] = rng.random::<f64>().mul_add(max_b - min_b, min_b);
1008 }
1009
1010 if rng.random::<f64>() < quantum_params.tunneling_probability {
1012 new_harmony[j] = rng.random::<f64>().mul_add(max_b - min_b, min_b);
1013 }
1014 }
1015 drop(rng);
1016
1017 let new_fitness = self.evaluate_objective(&new_harmony)?;
1019 self.state.runtime_stats.function_evaluations += 1;
1020
1021 if let Some((worst_idx, &worst_fitness)) = hm_fitness
1023 .iter()
1024 .enumerate()
1025 .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
1026 {
1027 if new_fitness < worst_fitness {
1028 hm_fitness[worst_idx] = new_fitness;
1029 harmony_memory[worst_idx] = new_harmony;
1030 if new_fitness < best_fitness {
1031 best_fitness = new_fitness;
1032 best_solution = harmony_memory[worst_idx].clone();
1033 }
1034 }
1035 }
1036
1037 self.state.best_objective = best_fitness;
1038 self.state.best_solution = best_solution.clone();
1039 self.state.convergence_history.push(best_fitness);
1040
1041 if self.check_convergence()? {
1042 break;
1043 }
1044 }
1045
1046 Ok(OptimizationResult {
1047 solution: best_solution,
1048 objective_value: best_fitness,
1049 iterations: self.state.iteration,
1050 converged: self.check_convergence()?,
1051 runtime_stats: self.state.runtime_stats.clone(),
1052 metadata: HashMap::new(),
1053 })
1054 }
1055 pub(super) fn evaluate_objective(&self, solution: &Array1<f64>) -> Result<f64> {
1057 let result = match self.config.optimization_config.objective_function {
1058 ObjectiveFunction::Quadratic => solution.iter().map(|&x| x * x).sum(),
1059 ObjectiveFunction::Rastrigin => {
1060 let n = solution.len() as f64;
1061 let a = 10.0;
1062 a * n
1063 + solution
1064 .iter()
1065 .map(|&x| x.mul_add(x, -(a * (2.0 * PI * x).cos())))
1066 .sum::<f64>()
1067 }
1068 ObjectiveFunction::Rosenbrock => {
1069 if solution.len() < 2 {
1070 return Ok(0.0);
1071 }
1072 let mut result = 0.0;
1073 for i in 0..solution.len() - 1 {
1074 let x = solution[i];
1075 let y = solution[i + 1];
1076 result += (1.0 - x).mul_add(1.0 - x, 100.0 * x.mul_add(-x, y).powi(2));
1077 }
1078 result
1079 }
1080 ObjectiveFunction::Ackley => {
1081 let n = solution.len() as f64;
1082 let a: f64 = 20.0;
1083 let b: f64 = 0.2;
1084 let c: f64 = 2.0 * PI;
1085 let sum1 = solution.iter().map(|&x| x * x).sum::<f64>() / n;
1086 let sum2 = solution.iter().map(|&x| (c * x).cos()).sum::<f64>() / n;
1087 (-a).mul_add((-b * sum1.sqrt()).exp(), -sum2.exp()) + a + std::f64::consts::E
1088 }
1089 ObjectiveFunction::Sphere => solution.iter().map(|&x| x * x).sum(),
1090 ObjectiveFunction::Griewank => {
1091 let sum_sq = solution.iter().map(|&x| x * x).sum::<f64>() / 4000.0;
1092 let prod_cos = solution
1093 .iter()
1094 .enumerate()
1095 .map(|(i, &x)| (x / ((i + 1) as f64).sqrt()).cos())
1096 .product::<f64>();
1097 1.0 + sum_sq - prod_cos
1098 }
1099 ObjectiveFunction::Custom => solution.iter().map(|&x| x * x).sum(),
1100 };
1101 Ok(result)
1102 }
1103 pub(super) fn check_convergence(&self) -> Result<bool> {
1105 if self.state.convergence_history.len() < 2 {
1106 return Ok(false);
1107 }
1108 let tolerance = self.config.algorithm_config.tolerance;
1109 let recent_improvements = &self.state.convergence_history
1110 [self.state.convergence_history.len().saturating_sub(10)..];
1111 if recent_improvements.len() < 2 {
1112 return Ok(false);
1113 }
1114 let last_value = recent_improvements
1115 .last()
1116 .expect("recent_improvements has at least 2 elements");
1117 let second_last_value = recent_improvements[recent_improvements.len() - 2];
1118 let change = (last_value - second_last_value).abs();
1119 Ok(change < tolerance)
1120 }
1121 pub fn train_ml_model(
1127 &mut self,
1128 training_data: &[(Array1<f64>, Array1<f64>)],
1129 ) -> Result<MLTrainingResult> {
1130 if training_data.is_empty() {
1131 return Err(SimulatorError::InvalidInput(
1132 "Training data is empty".to_string(),
1133 ));
1134 }
1135
1136 let input_dim = training_data[0].0.len();
1137 let output_dim = training_data[0].1.len();
1138 let n_params = (input_dim + 1) * output_dim;
1142
1143 let lr = self
1144 .config
1145 .algorithm_config
1146 .quantum_parameters
1147 .superposition_strength
1148 .clamp(0.001, 0.1);
1149 let epochs = self.config.algorithm_config.max_iterations;
1150 let tunneling_prob = self
1151 .config
1152 .algorithm_config
1153 .quantum_parameters
1154 .tunneling_probability;
1155
1156 let mut params: Array1<f64> = {
1158 let mut rng = self.rng.lock().expect("RNG lock poisoned");
1159 let mut p = Array1::zeros(n_params);
1160 for v in p.iter_mut() {
1161 *v = (rng.random::<f64>() - 0.5) * 0.1;
1162 }
1163 p
1164 };
1165
1166 let mut loss_history: Vec<f64> = Vec::with_capacity(epochs);
1167
1168 for epoch in 0..epochs {
1169 let mut total_loss = 0.0_f64;
1170 let mut grad: Array1<f64> = Array1::zeros(n_params);
1171
1172 for (x, y_true) in training_data {
1173 let mut y_pred: Array1<f64> = Array1::zeros(output_dim);
1175 for o in 0..output_dim {
1176 let w_start = o * (input_dim + 1);
1177 for i in 0..input_dim {
1178 y_pred[o] += params[w_start + i] * x[i];
1179 }
1180 y_pred[o] += params[o * (input_dim + 1) + input_dim];
1181 }
1182
1183 let residual: Array1<f64> =
1185 Array1::from_iter(y_pred.iter().zip(y_true.iter()).map(|(p, t)| *p - *t));
1186 total_loss += residual.iter().map(|r| r * r).sum::<f64>() / output_dim as f64;
1187
1188 for o in 0..output_dim {
1190 let w_start = o * (input_dim + 1);
1191 let err: f64 = residual[o] * 2.0 / output_dim as f64;
1192 for i in 0..input_dim {
1193 grad[w_start + i] += err * x[i];
1194 }
1195 grad[o * (input_dim + 1) + input_dim] += err;
1196 }
1197 }
1198
1199 let n: f64 = training_data.len() as f64;
1200 total_loss /= n;
1201
1202 for i in 0..n_params {
1204 params[i] -= lr * grad[i] / n;
1205 }
1206
1207 {
1209 let mut rng = self.rng.lock().expect("RNG lock poisoned");
1210 if rng.random::<f64>() < tunneling_prob {
1211 let j = (rng.random::<f64>() * n_params as f64) as usize % n_params;
1212 params[j] += (rng.random::<f64>() - 0.5) * 0.01;
1213 }
1214 }
1215
1216 loss_history.push(total_loss);
1217
1218 if epoch >= 2 && (loss_history[epoch - 1] - total_loss).abs() < 1e-8 {
1220 break;
1221 }
1222 }
1223
1224 let validation_accuracy = {
1227 let mut correct = 0_usize;
1228 for (x, y_true) in training_data {
1229 let mut y_pred: Array1<f64> = Array1::zeros(output_dim);
1230 for o in 0..output_dim {
1231 let w_start = o * (input_dim + 1);
1232 for i in 0..input_dim {
1233 y_pred[o] += params[w_start + i] * x[i];
1234 }
1235 y_pred[o] += params[o * (input_dim + 1) + input_dim];
1236 }
1237 let err: f64 = y_pred
1238 .iter()
1239 .zip(y_true.iter())
1240 .map(|(p, t)| (p - t).abs())
1241 .sum::<f64>()
1242 / output_dim as f64;
1243 let mag: f64 = y_true.iter().map(|t| t.abs()).sum::<f64>() / output_dim as f64;
1244 if err < 0.1 * mag.max(1.0) {
1245 correct += 1;
1246 }
1247 }
1248 correct as f64 / training_data.len() as f64
1249 };
1250
1251 Ok(MLTrainingResult {
1252 parameters: params,
1253 loss_history,
1254 validation_accuracy,
1255 training_time: 0.0,
1256 complexity_metrics: HashMap::new(),
1257 })
1258 }
1259
1260 pub fn sample(&mut self) -> Result<SamplingResult> {
1266 let n_samples = self.config.algorithm_config.max_iterations.max(100);
1267 let num_vars = self.config.num_variables;
1268 let bounds = self.config.optimization_config.bounds.clone();
1269 let tunneling_prob = self
1270 .config
1271 .algorithm_config
1272 .quantum_parameters
1273 .tunneling_probability;
1274
1275 let mut samples: Vec<Array1<f64>> = Vec::with_capacity(n_samples);
1276 let mut accepted = 0_usize;
1277
1278 let mut current: Array1<f64> = {
1280 let mut rng = self.rng.lock().expect("RNG lock poisoned");
1281 let mut v = Array1::zeros(num_vars);
1282 for j in 0..num_vars {
1283 let (lo, hi) = if j < bounds.len() {
1284 bounds[j]
1285 } else {
1286 (-10.0, 10.0)
1287 };
1288 v[j] = rng.random::<f64>().mul_add(hi - lo, lo);
1289 }
1290 v
1291 };
1292 let mut current_energy = self.evaluate_objective(¤t)?;
1293
1294 let temperature = 1.0_f64;
1296
1297 for _ in 0..n_samples {
1298 let proposed: Array1<f64> = {
1300 let mut rng = self.rng.lock().expect("RNG lock poisoned");
1301 let mut prop = current.clone();
1302 for j in 0..num_vars {
1303 let (lo, hi) = if j < bounds.len() {
1304 bounds[j]
1305 } else {
1306 (-10.0, 10.0)
1307 };
1308 let step = (hi - lo) * 0.1;
1309 if rng.random::<f64>() < tunneling_prob {
1310 prop[j] = rng.random::<f64>().mul_add(hi - lo, lo);
1312 } else {
1313 prop[j] = (current[j] + (rng.random::<f64>() - 0.5) * step).clamp(lo, hi);
1314 }
1315 }
1316 prop
1317 };
1318
1319 let proposed_energy = self.evaluate_objective(&proposed)?;
1320 let delta = proposed_energy - current_energy;
1321
1322 let accept = {
1323 let mut rng = self.rng.lock().expect("RNG lock poisoned");
1324 delta < 0.0 || rng.random::<f64>() < (-delta / temperature).exp()
1325 };
1326
1327 if accept {
1328 current = proposed;
1329 current_energy = proposed_energy;
1330 accepted += 1;
1331 }
1332 samples.push(current.clone());
1333 }
1334
1335 let acceptance_rate = accepted as f64 / n_samples as f64;
1336
1337 let mut samples_array = Array2::zeros((n_samples, num_vars));
1339 for (i, s) in samples.iter().enumerate() {
1340 samples_array.row_mut(i).assign(s);
1341 }
1342
1343 let mean: Array1<f64> = samples_array
1345 .mean_axis(scirs2_core::ndarray::Axis(0))
1346 .unwrap_or_else(|| Array1::zeros(num_vars));
1347 let variance: Array1<f64> = samples_array.var_axis(scirs2_core::ndarray::Axis(0), 0.0);
1348
1349 let skewness: Array1<f64> = Array1::from_iter((0..num_vars).map(|j| {
1351 let mu = mean[j];
1352 let sigma = variance[j].sqrt();
1353 if sigma < 1e-12 {
1354 return 0.0;
1355 }
1356 let m3: f64 = samples_array
1357 .column(j)
1358 .iter()
1359 .map(|&v| (v - mu).powi(3))
1360 .sum::<f64>()
1361 / n_samples as f64;
1362 m3 / sigma.powi(3)
1363 }));
1364
1365 let kurtosis: Array1<f64> = Array1::from_iter((0..num_vars).map(|j| {
1367 let mu = mean[j];
1368 let sigma = variance[j].sqrt();
1369 if sigma < 1e-12 {
1370 return 0.0;
1371 }
1372 let m4: f64 = samples_array
1373 .column(j)
1374 .iter()
1375 .map(|&v| (v - mu).powi(4))
1376 .sum::<f64>()
1377 / n_samples as f64;
1378 m4 / sigma.powi(4) - 3.0
1379 }));
1380
1381 let mut correlation_matrix = Array2::zeros((num_vars, num_vars));
1383 for i in 0..num_vars {
1384 for j in 0..num_vars {
1385 if i == j {
1386 correlation_matrix[[i, j]] = 1.0;
1387 continue;
1388 }
1389 let mu_i = mean[i];
1390 let mu_j = mean[j];
1391 let si = variance[i].sqrt();
1392 let sj = variance[j].sqrt();
1393 if si < 1e-12 || sj < 1e-12 {
1394 continue;
1395 }
1396 let cov: f64 = samples_array
1397 .column(i)
1398 .iter()
1399 .zip(samples_array.column(j).iter())
1400 .map(|(&a, &b)| (a - mu_i) * (b - mu_j))
1401 .sum::<f64>()
1402 / n_samples as f64;
1403 correlation_matrix[[i, j]] = cov / (si * sj);
1404 }
1405 }
1406
1407 let effective_sample_size = (n_samples as f64 * acceptance_rate).max(1.0) as usize;
1408 let autocorr_times: Array1<f64> =
1410 Array1::from_elem(num_vars, (1.0_f64 / acceptance_rate.max(1e-6)).max(1.0));
1411
1412 Ok(SamplingResult {
1413 samples: samples_array,
1414 statistics: SampleStatistics {
1415 mean,
1416 variance,
1417 skewness,
1418 kurtosis,
1419 correlation_matrix,
1420 },
1421 acceptance_rate,
1422 effective_sample_size,
1423 autocorr_times,
1424 })
1425 }
1426
1427 pub fn solve_linear_algebra(
1434 &mut self,
1435 matrix: &Array2<Complex64>,
1436 rhs: &Array1<Complex64>,
1437 ) -> Result<LinalgResult> {
1438 let n = matrix.nrows();
1439 if n != matrix.ncols() {
1440 return Err(SimulatorError::InvalidInput(format!(
1441 "Matrix must be square: got {}×{}",
1442 n,
1443 matrix.ncols()
1444 )));
1445 }
1446 if n != rhs.len() {
1447 return Err(SimulatorError::InvalidInput(format!(
1448 "Matrix dimension {} incompatible with rhs length {}",
1449 n,
1450 rhs.len()
1451 )));
1452 }
1453 if n == 0 {
1454 return Ok(LinalgResult {
1455 solution: Array1::zeros(0),
1456 eigenvalues: None,
1457 eigenvectors: None,
1458 singular_values: None,
1459 residual_norm: 0.0,
1460 iterations: 0,
1461 });
1462 }
1463
1464 let max_iter = self.config.algorithm_config.max_iterations.max(1000);
1465 let tol = self.config.algorithm_config.tolerance.max(1e-10);
1466
1467 let mut x: Array1<Complex64> = Array1::from_elem(n, Complex64::new(0.0, 0.0));
1468
1469 let mut converged_iter = max_iter;
1470
1471 for iter in 0..max_iter {
1472 let x_old = x.clone();
1473
1474 for i in 0..n {
1476 let diag = matrix[[i, i]];
1477 if diag.norm() < 1e-14 {
1478 continue;
1480 }
1481 let mut sigma = Complex64::new(0.0, 0.0);
1482 for j in 0..n {
1483 if j != i {
1484 sigma += matrix[[i, j]] * x[j];
1485 }
1486 }
1487 x[i] = (rhs[i] - sigma) / diag;
1488 }
1489
1490 let delta: f64 = x
1492 .iter()
1493 .zip(x_old.iter())
1494 .map(|(a, b)| (a - b).norm())
1495 .sum::<f64>();
1496
1497 if delta < tol {
1498 converged_iter = iter + 1;
1499 break;
1500 }
1501 }
1502
1503 let ax: Array1<Complex64> = Array1::from_iter((0..n).map(|i| {
1505 (0..n)
1506 .map(|j| matrix[[i, j]] * x[j])
1507 .fold(Complex64::new(0.0, 0.0), |acc, v| acc + v)
1508 }));
1509 let residual_norm: f64 = ax
1510 .iter()
1511 .zip(rhs.iter())
1512 .map(|(a, b)| (a - b).norm())
1513 .sum::<f64>();
1514
1515 Ok(LinalgResult {
1516 solution: x,
1517 eigenvalues: None,
1518 eigenvectors: None,
1519 singular_values: None,
1520 residual_norm,
1521 iterations: converged_iter,
1522 })
1523 }
1524
1525 pub fn solve_graph_problem(&mut self, adjacency_matrix: &Array2<f64>) -> Result<GraphResult> {
1533 let n = adjacency_matrix.nrows();
1534 if n == 0 {
1535 return Ok(GraphResult {
1536 solution: vec![],
1537 objective_value: 0.0,
1538 graph_metrics: GraphMetrics {
1539 modularity: 0.0,
1540 clustering_coefficient: 0.0,
1541 average_path_length: 0.0,
1542 diameter: 0,
1543 },
1544 walk_stats: None,
1545 });
1546 }
1547
1548 let tunneling_prob = self
1552 .config
1553 .algorithm_config
1554 .quantum_parameters
1555 .tunneling_probability;
1556
1557 let mut coloring = vec![0_usize; n];
1558 let mut max_color = 0_usize;
1559
1560 for v in 0..n {
1561 let mut neighbor_colors: std::collections::HashSet<usize> =
1562 std::collections::HashSet::new();
1563 for u in 0..n {
1564 if adjacency_matrix[[v, u]] > 0.0 {
1565 neighbor_colors.insert(coloring[u]);
1566 }
1567 }
1568
1569 let mut color = 0;
1570 while neighbor_colors.contains(&color) {
1571 color += 1;
1572 }
1573
1574 {
1576 let mut rng = self.rng.lock().expect("RNG lock poisoned");
1577 if rng.random::<f64>() < tunneling_prob && color <= max_color {
1578 color = (color + 1).min(max_color + 1);
1579 }
1580 }
1581
1582 coloring[v] = color;
1583 if color > max_color {
1584 max_color = color;
1585 }
1586 }
1587
1588 let mut cut_value = 0.0_f64;
1590 for u in 0..n {
1591 for v in (u + 1)..n {
1592 if coloring[u] % 2 != coloring[v] % 2 {
1593 cut_value += adjacency_matrix[[u, v]];
1594 }
1595 }
1596 }
1597
1598 let degrees: Vec<f64> = (0..n)
1600 .map(|v| (0..n).map(|u| adjacency_matrix[[v, u]].abs()).sum::<f64>())
1601 .collect();
1602
1603 let clustering_coefficient = {
1605 let mut total_cc = 0.0_f64;
1606 for v in 0..n {
1607 let neighbors: Vec<usize> = (0..n)
1608 .filter(|&u| u != v && adjacency_matrix[[v, u]] > 0.0)
1609 .collect();
1610 let k = neighbors.len();
1611 if k < 2 {
1612 continue;
1613 }
1614 let mut triangles = 0_usize;
1615 for i in 0..k {
1616 for j in (i + 1)..k {
1617 if adjacency_matrix[[neighbors[i], neighbors[j]]] > 0.0 {
1618 triangles += 1;
1619 }
1620 }
1621 }
1622 total_cc += triangles as f64 / (k * (k - 1) / 2) as f64;
1623 }
1624 total_cc / n as f64
1625 };
1626
1627 let (average_path_length, diameter) = {
1629 let mut total_dist = 0_usize;
1630 let mut max_dist = 0_usize;
1631 let mut reachable_pairs = 0_usize;
1632
1633 for src in 0..n {
1634 let mut dist = vec![usize::MAX; n];
1635 dist[src] = 0;
1636 let mut queue = std::collections::VecDeque::new();
1637 queue.push_back(src);
1638 while let Some(u) = queue.pop_front() {
1639 for v in 0..n {
1640 if adjacency_matrix[[u, v]] > 0.0 && dist[v] == usize::MAX {
1641 dist[v] = dist[u] + 1;
1642 queue.push_back(v);
1643 }
1644 }
1645 }
1646 for dst in 0..n {
1647 if dst != src && dist[dst] != usize::MAX {
1648 total_dist += dist[dst];
1649 reachable_pairs += 1;
1650 if dist[dst] > max_dist {
1651 max_dist = dist[dst];
1652 }
1653 }
1654 }
1655 }
1656
1657 let avg = if reachable_pairs > 0 {
1658 total_dist as f64 / reachable_pairs as f64
1659 } else {
1660 0.0
1661 };
1662 (avg, max_dist)
1663 };
1664
1665 let total_weight: f64 = degrees.iter().sum::<f64>() / 2.0;
1667 let modularity = if total_weight > 1e-12 {
1668 let two_m = 2.0 * total_weight;
1669 let mut q = 0.0_f64;
1670 for u in 0..n {
1671 for v in 0..n {
1672 if coloring[u] == coloring[v] {
1673 let a_uv = adjacency_matrix[[u, v]];
1674 let expected = degrees[u] * degrees[v] / two_m;
1675 q += a_uv - expected;
1676 }
1677 }
1678 }
1679 q / two_m
1680 } else {
1681 0.0
1682 };
1683
1684 Ok(GraphResult {
1685 solution: coloring,
1686 objective_value: cut_value,
1687 graph_metrics: GraphMetrics {
1688 modularity,
1689 clustering_coefficient,
1690 average_path_length,
1691 diameter,
1692 },
1693 walk_stats: None,
1694 })
1695 }
1696 #[must_use]
1698 pub const fn get_stats(&self) -> &QuantumInspiredStats {
1699 &self.stats
1700 }
1701 #[must_use]
1703 pub const fn get_state(&self) -> &QuantumInspiredState {
1704 &self.state
1705 }
1706 pub const fn get_state_mut(&mut self) -> &mut QuantumInspiredState {
1708 &mut self.state
1709 }
1710 pub fn evaluate_objective_public(&mut self, solution: &Array1<f64>) -> Result<f64> {
1712 self.evaluate_objective(solution)
1713 }
1714 pub fn check_convergence_public(&self) -> Result<bool> {
1716 self.check_convergence()
1717 }
1718 pub fn reset(&mut self) {
1720 self.state = QuantumInspiredState {
1721 variables: Array1::zeros(self.config.num_variables),
1722 objective_value: f64::INFINITY,
1723 iteration: 0,
1724 best_solution: Array1::zeros(self.config.num_variables),
1725 best_objective: f64::INFINITY,
1726 convergence_history: Vec::new(),
1727 runtime_stats: RuntimeStats::default(),
1728 };
1729 self.stats = QuantumInspiredStats::default();
1730 }
1731}