1use crate::error::OptimizeResult;
12use crate::result::OptimizeResults;
13use scirs2_core::ndarray::{Array1, Array2, ArrayView1};
14use scirs2_core::numeric::Zero;
15use scirs2_core::random::{rng, Rng};
16use std::collections::VecDeque;
17use std::f64::consts::PI;
18
19#[derive(Debug, Clone, Copy)]
21pub struct Complex {
22 pub real: f64,
23 pub imag: f64,
24}
25
26impl Complex {
27 pub fn new(real: f64, imag: f64) -> Self {
28 Self { real, imag }
29 }
30
31 pub fn magnitude(&self) -> f64 {
32 (self.real * self.real + self.imag * self.imag).sqrt()
33 }
34
35 pub fn phase(&self) -> f64 {
36 self.imag.atan2(self.real)
37 }
38
39 pub fn conjugate(&self) -> Complex {
40 Complex::new(self.real, -self.imag)
41 }
42}
43
44impl std::ops::Add for Complex {
45 type Output = Complex;
46
47 fn add(self, other: Complex) -> Complex {
48 Complex::new(self.real + other.real, self.imag + other.imag)
49 }
50}
51
52impl std::ops::Mul for Complex {
53 type Output = Complex;
54
55 fn mul(self, other: Complex) -> Complex {
56 Complex::new(
57 self.real * other.real - self.imag * other.imag,
58 self.real * other.imag + self.imag * other.real,
59 )
60 }
61}
62
63impl std::ops::Mul<f64> for Complex {
64 type Output = Complex;
65
66 fn mul(self, scalar: f64) -> Complex {
67 Complex::new(self.real * scalar, self.imag * scalar)
68 }
69}
70
71impl Zero for Complex {
72 fn zero() -> Self {
73 Complex::new(0.0, 0.0)
74 }
75
76 fn is_zero(&self) -> bool {
77 self.real == 0.0 && self.imag == 0.0
78 }
79}
80
81#[derive(Debug, Clone)]
83pub struct QuantumState {
84 pub amplitudes: Array1<Complex>,
86 pub basis_states: Array2<f64>,
88 pub num_qubits: usize,
90 pub entanglement_matrix: Array2<Complex>,
92 pub decoherence_time: f64,
94 pub evolution_time: f64,
96}
97
98impl QuantumState {
99 pub fn new(num_params: usize, num_basis_states: usize) -> Self {
101 let num_qubits = (num_basis_states as f64).log2().ceil() as usize;
102 let actual_states = 1 << num_qubits;
103
104 let mut amplitudes = Array1::from_shape_fn(actual_states, |_| {
106 Complex::new(
107 scirs2_core::random::rng().random_range(-1.0..1.0),
108 scirs2_core::random::rng().random_range(-1.0..1.0),
109 )
110 });
111
112 let norm: f64 = amplitudes
114 .iter()
115 .map(|c| c.magnitude().powi(2))
116 .sum::<f64>()
117 .sqrt();
118 for amp in amplitudes.iter_mut() {
119 *amp = *amp * (1.0 / norm);
120 }
121
122 let basis_states = Array2::from_shape_fn((actual_states, num_params), |_| {
124 scirs2_core::random::rng().random_range(-2.0..5.0)
125 });
126
127 let entanglement_matrix = Array2::from_shape_fn((num_params, num_params), |(i, j)| {
129 if i == j {
130 Complex::new(1.0, 0.0)
131 } else {
132 let correlation = scirs2_core::random::rng().random_range(-0.1..0.1);
133 Complex::new(correlation, correlation * 0.1)
134 }
135 });
136
137 Self {
138 amplitudes,
139 basis_states,
140 num_qubits,
141 entanglement_matrix,
142 decoherence_time: 1000.0,
143 evolution_time: 0.0,
144 }
145 }
146
147 pub fn measure(&self) -> Array1<f64> {
149 let probabilities: Vec<f64> = self
151 .amplitudes
152 .iter()
153 .map(|c| c.magnitude().powi(2))
154 .collect();
155
156 let mut cumulative = 0.0;
158 let random_value = scirs2_core::random::rng().random_range(0.0..1.0);
159
160 for (i, &prob) in probabilities.iter().enumerate() {
161 cumulative += prob;
162 if random_value <= cumulative {
163 return self.basis_states.row(i).to_owned();
164 }
165 }
166
167 let max_prob_idx = probabilities
169 .iter()
170 .enumerate()
171 .max_by(|a, b| a.1.partial_cmp(b.1).unwrap())
172 .map(|(i, _)| i)
173 .unwrap_or(0);
174
175 self.basis_states.row(max_prob_idx).to_owned()
176 }
177
178 pub fn evolve(&mut self, objective_gradients: &Array1<f64>, dt: f64) -> OptimizeResult<()> {
180 self.evolution_time += dt;
181
182 let hamiltonian = self.compute_hamiltonian(objective_gradients)?;
184
185 self.apply_time_evolution(&hamiltonian, dt)?;
187
188 self.apply_decoherence(dt)?;
190
191 self.update_entanglement(objective_gradients)?;
193
194 Ok(())
195 }
196
197 fn compute_hamiltonian(
198 &self,
199 objective_gradients: &Array1<f64>,
200 ) -> OptimizeResult<Array2<Complex>> {
201 let n_states = self.amplitudes.len();
202 let mut hamiltonian = Array2::zeros((n_states, n_states));
203
204 for i in 0..n_states {
205 for j in 0..n_states {
206 if i == j {
207 let params = self.basis_states.row(i);
209 let potential = params
210 .iter()
211 .zip(objective_gradients.iter())
212 .map(|(&p, &g)| p * g)
213 .sum::<f64>();
214
215 hamiltonian[[i, j]] = Complex::new(potential, 0.0);
216 } else {
217 let distance = (&self.basis_states.row(i) - &self.basis_states.row(j))
219 .mapv(|x| x * x)
220 .sum()
221 .sqrt();
222
223 let tunneling_amplitude = (-distance * 0.1).exp();
224 hamiltonian[[i, j]] = Complex::new(0.0, tunneling_amplitude);
225 }
226 }
227 }
228
229 Ok(hamiltonian)
230 }
231
232 fn apply_time_evolution(
233 &mut self,
234 hamiltonian: &Array2<Complex>,
235 dt: f64,
236 ) -> OptimizeResult<()> {
237 let n_states = self.amplitudes.len();
241 let mut new_amplitudes = self.amplitudes.clone();
242
243 for i in 0..n_states {
244 let mut evolved_amp = Complex::new(0.0, 0.0);
245
246 for j in 0..n_states {
247 let h_element = hamiltonian[[i, j]];
248 let evolution_factor = if i == j {
249 Complex::new(1.0, 0.0) + Complex::new(0.0, -h_element.real * dt)
251 } else {
252 Complex::new(0.0, -h_element.real * dt) + Complex::new(h_element.imag * dt, 0.0)
254 };
255
256 evolved_amp = evolved_amp + evolution_factor * self.amplitudes[j];
257 }
258
259 new_amplitudes[i] = evolved_amp;
260 }
261
262 let norm: f64 = new_amplitudes
264 .iter()
265 .map(|c| c.magnitude().powi(2))
266 .sum::<f64>()
267 .sqrt();
268 for amp in new_amplitudes.iter_mut() {
269 *amp = *amp * (1.0 / norm.max(1e-12));
270 }
271
272 self.amplitudes = new_amplitudes;
273 Ok(())
274 }
275
276 fn apply_decoherence(&mut self, dt: f64) -> OptimizeResult<()> {
277 let decoherence_factor = (-dt / self.decoherence_time).exp();
279
280 for amp in self.amplitudes.iter_mut() {
281 *amp = *amp * decoherence_factor;
282 }
283
284 let noise_strength = 1.0 - decoherence_factor;
286 for amp in self.amplitudes.iter_mut() {
287 let noise_real =
288 scirs2_core::random::rng().random_range(-0.5..0.5) * noise_strength * 0.01;
289 let noise_imag =
290 scirs2_core::random::rng().random_range(-0.5..0.5) * noise_strength * 0.01;
291 *amp = *amp + Complex::new(noise_real, noise_imag);
292 }
293
294 let norm: f64 = self
296 .amplitudes
297 .iter()
298 .map(|c| c.magnitude().powi(2))
299 .sum::<f64>()
300 .sqrt();
301 for amp in self.amplitudes.iter_mut() {
302 *amp = *amp * (1.0 / norm.max(1e-12));
303 }
304
305 Ok(())
306 }
307
308 fn update_entanglement(&mut self, objective_gradients: &Array1<f64>) -> OptimizeResult<()> {
309 let n_params = self.entanglement_matrix.nrows();
310
311 for i in 0..n_params {
313 for j in i + 1..n_params {
314 if i < objective_gradients.len() && j < objective_gradients.len() {
315 let correlation = objective_gradients[i] * objective_gradients[j];
316 let phase_update = correlation * 0.01;
317
318 let current = self.entanglement_matrix[[i, j]];
319 let new_phase = current.phase() + phase_update;
320 let magnitude = current.magnitude() * 0.99 + correlation.abs() * 0.01;
321
322 self.entanglement_matrix[[i, j]] =
323 Complex::new(magnitude * new_phase.cos(), magnitude * new_phase.sin());
324 self.entanglement_matrix[[j, i]] = self.entanglement_matrix[[i, j]].conjugate();
325 }
326 }
327 }
328
329 Ok(())
330 }
331
332 pub fn create_superposition(&mut self, exploration_radius: f64) -> OptimizeResult<()> {
334 let n_states = self.basis_states.nrows();
335 let n_params = self.basis_states.ncols();
336
337 for i in 0..n_states {
339 for j in 0..n_params {
340 let perturbation = scirs2_core::random::rng()
341 .random_range(-exploration_radius..exploration_radius);
342 self.basis_states[[i, j]] += perturbation;
343 }
344 }
345
346 let equal_amplitude = Complex::new(1.0 / (n_states as f64).sqrt(), 0.0);
348 for i in 0..n_states {
349 self.amplitudes[i] = equal_amplitude;
350 }
351
352 Ok(())
353 }
354
355 pub fn quantum_tunnel(
357 &mut self,
358 barrier_height: f64,
359 tunnel_probability: f64,
360 ) -> OptimizeResult<()> {
361 if scirs2_core::random::rng().random_range(0.0..1.0) < tunnel_probability {
362 let n_states = self.basis_states.nrows();
364 let n_params = self.basis_states.ncols();
365
366 let source_state = scirs2_core::random::rng().random_range(0..n_states);
368
369 for j in 0..n_params {
371 let tunnel_distance =
372 barrier_height * scirs2_core::random::rng().random_range(-0.5..0.5);
373 self.basis_states[[source_state, j]] += tunnel_distance;
374 }
375
376 let tunnel_amplitude_factor = (-barrier_height * 0.1).exp();
378 self.amplitudes[source_state] = self.amplitudes[source_state] * tunnel_amplitude_factor;
379
380 let norm: f64 = self
382 .amplitudes
383 .iter()
384 .map(|c| c.magnitude().powi(2))
385 .sum::<f64>()
386 .sqrt();
387 for amp in self.amplitudes.iter_mut() {
388 *amp = *amp * (1.0 / norm.max(1e-12));
389 }
390 }
391
392 Ok(())
393 }
394}
395
396#[derive(Debug, Clone)]
398pub struct QuantumAnnealingSchedule {
399 pub initial_temperature: f64,
401 pub final_temperature: f64,
403 pub current_temperature: f64,
405 pub progress: f64,
407 pub schedule_type: CoolingSchedule,
409 pub quantum_fluctuation: f64,
411}
412
413#[derive(Debug, Clone)]
414pub enum CoolingSchedule {
415 Linear,
416 Exponential,
417 Logarithmic,
418 QuantumAdaptive,
419}
420
421impl QuantumAnnealingSchedule {
422 pub fn new(initial_temp: f64, final_temp: f64, schedule: CoolingSchedule) -> Self {
423 Self {
424 initial_temperature: initial_temp,
425 final_temperature: final_temp,
426 current_temperature: initial_temp,
427 progress: 0.0,
428 schedule_type: schedule,
429 quantum_fluctuation: 1.0,
430 }
431 }
432
433 pub fn update(&mut self, iteration: usize, max_nit: usize, energy_change: f64) {
434 self.progress = iteration as f64 / max_nit as f64;
435
436 self.current_temperature = match self.schedule_type {
437 CoolingSchedule::Linear => {
438 self.initial_temperature * (1.0 - self.progress)
439 + self.final_temperature * self.progress
440 }
441 CoolingSchedule::Exponential => {
442 self.initial_temperature
443 * (self.final_temperature / self.initial_temperature).powf(self.progress)
444 }
445 CoolingSchedule::Logarithmic => self.initial_temperature / (1.0 + self.progress).ln(),
446 CoolingSchedule::QuantumAdaptive => {
447 let base_temp = self.initial_temperature * (0.1_f64).powf(self.progress);
449 let quantum_correction = self.quantum_fluctuation * (-energy_change.abs()).exp();
450 base_temp * (1.0 + quantum_correction * 0.1)
451 }
452 };
453
454 self.quantum_fluctuation =
456 1.0 - self.progress + 0.1 * (2.0 * PI * self.progress * 10.0).sin();
457 }
458
459 pub fn should_accept(&self, energy_delta: f64) -> bool {
460 if energy_delta <= 0.0 {
461 true } else {
463 let classical_prob = (-energy_delta / self.current_temperature).exp();
465 let quantum_prob = classical_prob * (1.0 + self.quantum_fluctuation * 0.1);
466
467 scirs2_core::random::rng().random_range(0.0..1.0) < quantum_prob.min(1.0)
468 }
469 }
470}
471
472#[derive(Debug, Clone)]
474pub struct QuantumInspiredOptimizer {
475 pub quantum_state: QuantumState,
477 pub annealing_schedule: QuantumAnnealingSchedule,
479 pub best_solution: Array1<f64>,
481 pub best_objective: f64,
483 pub iteration: usize,
485 pub max_nit: usize,
487 pub function_evaluations: usize,
489 pub interference_history: VecDeque<f64>,
491 pub entanglement_strength: f64,
493 pub tunneling_events: usize,
495 pub tolerance: f64,
497}
498
499impl QuantumInspiredOptimizer {
500 pub fn new(
502 initial_params: &ArrayView1<f64>,
503 max_nit: usize,
504 num_quantum_states: usize,
505 ) -> Self {
506 let n_params = initial_params.len();
507 let quantum_state = QuantumState::new(n_params, num_quantum_states);
508 let annealing_schedule =
509 QuantumAnnealingSchedule::new(10.0, 0.01, CoolingSchedule::QuantumAdaptive);
510
511 Self {
512 quantum_state,
513 annealing_schedule,
514 best_solution: initial_params.to_owned(),
515 best_objective: f64::INFINITY,
516 iteration: 0,
517 max_nit,
518 function_evaluations: 0,
519 interference_history: VecDeque::with_capacity(1000),
520 entanglement_strength: 1.0,
521 tunneling_events: 0,
522 tolerance: 1e-8,
523 }
524 }
525
526 pub fn optimize<F>(&mut self, objective: F) -> OptimizeResult<OptimizeResults<f64>>
528 where
529 F: Fn(&ArrayView1<f64>) -> f64,
530 {
531 if self.best_objective == f64::INFINITY {
533 self.best_objective = objective(&self.best_solution.view());
534 self.function_evaluations += 1;
535 }
536
537 let mut prev_objective = self.best_objective;
538 let mut stagnation_counter = 0;
539
540 for iteration in 0..self.max_nit {
541 self.iteration = iteration;
542
543 let candidate_params = self.quantum_state.measure();
545 let candidate_objective = objective(&candidate_params.view());
546 self.function_evaluations += 1;
547
548 if candidate_objective < self.best_objective {
550 self.best_objective = candidate_objective;
551 self.best_solution = candidate_params.clone();
552 stagnation_counter = 0;
553 } else {
554 stagnation_counter += 1;
555 }
556
557 let energy_change = candidate_objective - prev_objective;
559 let gradients =
560 self.compute_finite_difference_gradient(&objective, &candidate_params.view())?;
561
562 self.quantum_state.evolve(&gradients, 0.1)?;
564
565 self.annealing_schedule
567 .update(iteration, self.max_nit, energy_change);
568
569 if stagnation_counter > 20 {
571 let barrier_height = self.annealing_schedule.current_temperature;
572 let tunnel_prob = 0.1 * self.annealing_schedule.quantum_fluctuation;
573 self.quantum_state
574 .quantum_tunnel(barrier_height, tunnel_prob)?;
575
576 if scirs2_core::random::rng().random_range(0.0..1.0) < tunnel_prob {
577 self.tunneling_events += 1;
578 stagnation_counter = 0;
579 }
580 }
581
582 if iteration % 50 == 0 {
584 let exploration_radius = self.annealing_schedule.current_temperature * 0.1;
585 self.quantum_state
586 .create_superposition(exploration_radius)?;
587 }
588
589 let interference = self.compute_quantum_interference();
591 self.interference_history.push_back(interference);
592 if self.interference_history.len() > 1000 {
593 self.interference_history.pop_front();
594 }
595
596 self.entanglement_strength = self.compute_entanglement_strength();
598
599 if (prev_objective - candidate_objective).abs() < self.tolerance && iteration > 100 {
601 break;
602 }
603
604 prev_objective = candidate_objective;
605 }
606
607 Ok(OptimizeResults::<f64> {
608 x: self.best_solution.clone(),
609 fun: self.best_objective,
610 success: self.best_objective < f64::INFINITY,
611 nit: self.iteration,
612 message: format!(
613 "Quantum optimization completed. Tunneling events: {}, Final entanglement: {:.3}",
614 self.tunneling_events, self.entanglement_strength
615 ),
616 jac: None,
617 hess: None,
618 constr: None,
619 nfev: self.iteration * self.quantum_state.num_qubits, njev: 0,
621 nhev: 0,
622 maxcv: 0,
623 status: if self.best_objective < f64::INFINITY {
624 0
625 } else {
626 1
627 },
628 })
629 }
630
631 fn compute_finite_difference_gradient<F>(
632 &self,
633 objective: &F,
634 params: &ArrayView1<f64>,
635 ) -> OptimizeResult<Array1<f64>>
636 where
637 F: Fn(&ArrayView1<f64>) -> f64,
638 {
639 let eps = 1e-8;
640 let mut gradient = Array1::zeros(params.len());
641 let base_value = objective(params);
642
643 for i in 0..params.len() {
644 let mut params_plus = params.to_owned();
645 params_plus[i] += eps;
646 let value_plus = objective(¶ms_plus.view());
647
648 gradient[i] = (value_plus - base_value) / eps;
649 }
650
651 Ok(gradient)
652 }
653
654 fn compute_quantum_interference(&self) -> f64 {
655 let mut interference = 0.0;
657 let n_states = self.quantum_state.amplitudes.len();
658
659 for i in 0..n_states {
660 for j in i + 1..n_states {
661 let amp_i = self.quantum_state.amplitudes[i];
662 let amp_j = self.quantum_state.amplitudes[j];
663
664 let interference_term = (amp_i.conjugate() * amp_j).real;
666 interference += interference_term.abs();
667 }
668 }
669
670 interference / (n_states * (n_states - 1) / 2) as f64
671 }
672
673 fn compute_entanglement_strength(&self) -> f64 {
674 let mut total_entanglement = 0.0;
676 let n_params = self.quantum_state.entanglement_matrix.nrows();
677 let mut count = 0;
678
679 for i in 0..n_params {
680 for j in i + 1..n_params {
681 total_entanglement += self.quantum_state.entanglement_matrix[[i, j]].magnitude();
682 count += 1;
683 }
684 }
685
686 if count > 0 {
687 total_entanglement / count as f64
688 } else {
689 0.0
690 }
691 }
692
693 pub fn get_quantum_stats(&self) -> QuantumOptimizationStats {
695 QuantumOptimizationStats {
696 function_evaluations: self.function_evaluations,
697 tunneling_events: self.tunneling_events,
698 current_temperature: self.annealing_schedule.current_temperature,
699 entanglement_strength: self.entanglement_strength,
700 quantum_interference: self.interference_history.back().copied().unwrap_or(0.0),
701 superposition_dimension: self.quantum_state.amplitudes.len(),
702 evolution_time: self.quantum_state.evolution_time,
703 decoherence_level: 1.0
704 - (-self.quantum_state.evolution_time / self.quantum_state.decoherence_time).exp(),
705 }
706 }
707}
708
709#[derive(Debug, Clone)]
711pub struct QuantumOptimizationStats {
712 pub function_evaluations: usize,
713 pub tunneling_events: usize,
714 pub current_temperature: f64,
715 pub entanglement_strength: f64,
716 pub quantum_interference: f64,
717 pub superposition_dimension: usize,
718 pub evolution_time: f64,
719 pub decoherence_level: f64,
720}
721
722#[allow(dead_code)]
724pub fn quantum_optimize<F>(
725 objective: F,
726 initial_params: &ArrayView1<f64>,
727 max_nit: Option<usize>,
728 num_quantum_states: Option<usize>,
729) -> OptimizeResult<OptimizeResults<f64>>
730where
731 F: Fn(&ArrayView1<f64>) -> f64,
732{
733 let max_iter = max_nit.unwrap_or(1000);
734 let num_states = num_quantum_states.unwrap_or(32);
735
736 let mut optimizer = QuantumInspiredOptimizer::new(initial_params, max_iter, num_states);
737 optimizer.optimize(objective)
738}
739
740#[allow(dead_code)]
742pub fn quantum_particle_swarm_optimize<F>(
743 objective: F,
744 initial_params: &ArrayView1<f64>,
745 numparticles: usize,
746 max_nit: usize,
747) -> OptimizeResult<OptimizeResults<f64>>
748where
749 F: Fn(&ArrayView1<f64>) -> f64,
750{
751 let mut particles = Vec::new();
752
753 for _ in 0..numparticles {
755 let particle_optimizer = QuantumInspiredOptimizer::new(initial_params, max_nit, 8);
756 particles.push(particle_optimizer);
757 }
758
759 let mut global_best_solution = initial_params.to_owned();
760 let mut global_best_objective = f64::INFINITY;
761
762 for iteration in 0..max_nit {
763 for particle in &mut particles {
764 for _ in 0..5 {
766 let candidate = particle.quantum_state.measure();
767 let obj_value = objective(&candidate.view());
768
769 if obj_value < particle.best_objective {
770 particle.best_objective = obj_value;
771 particle.best_solution = candidate.clone();
772 }
773
774 if obj_value < global_best_objective {
775 global_best_objective = obj_value;
776 global_best_solution = candidate.clone();
777 }
778
779 let gradients =
781 particle.compute_finite_difference_gradient(&objective, &candidate.view())?;
782 particle.quantum_state.evolve(&gradients, 0.01)?;
783
784 if scirs2_core::random::rng().random_range(0.0..1.0) < 0.1 {
786 let n_params = initial_params.len();
787 for i in 0..n_params.min(particle.quantum_state.basis_states.ncols()) {
788 let entanglement_strength = 0.1;
789 for state_idx in 0..particle.quantum_state.basis_states.nrows() {
790 particle.quantum_state.basis_states[[state_idx, i]] = (1.0
791 - entanglement_strength)
792 * particle.quantum_state.basis_states[[state_idx, i]]
793 + entanglement_strength * global_best_solution[i];
794 }
795 }
796 }
797 }
798 }
799
800 if iteration % 100 == 0 {
802 for particle in &mut particles {
803 particle.quantum_state.quantum_tunnel(1.0, 0.05)?;
804 }
805 }
806 }
807
808 Ok(OptimizeResults::<f64> {
809 x: global_best_solution,
810 fun: global_best_objective,
811 success: global_best_objective < f64::INFINITY,
812 nit: max_nit,
813 message: format!(
814 "Quantum particle swarm optimization completed with {} particles",
815 numparticles
816 ),
817 jac: None,
818 hess: None,
819 constr: None,
820 nfev: max_nit * numparticles, njev: 0,
822 nhev: 0,
823 maxcv: 0,
824 status: if global_best_objective < f64::INFINITY {
825 0
826 } else {
827 1
828 },
829 })
830}
831
832#[cfg(test)]
833mod tests {
834 use super::*;
835
836 #[test]
837 fn test_complex_arithmetic() {
838 let c1 = Complex::new(1.0, 2.0);
839 let c2 = Complex::new(3.0, 4.0);
840
841 let sum = c1 + c2;
842 assert_eq!(sum.real, 4.0);
843 assert_eq!(sum.imag, 6.0);
844
845 let product = c1 * c2;
846 assert_eq!(product.real, -5.0); assert_eq!(product.imag, 10.0); assert!((c1.magnitude() - (5.0_f64).sqrt()).abs() < 1e-10);
850 }
851
852 #[test]
853 fn test_quantum_state_creation() {
854 let state = QuantumState::new(3, 8);
855 assert_eq!(state.num_qubits, 3);
856 assert_eq!(state.amplitudes.len(), 8);
857 assert_eq!(state.basis_states.nrows(), 8);
858 assert_eq!(state.basis_states.ncols(), 3);
859
860 let norm_squared: f64 = state.amplitudes.iter().map(|c| c.magnitude().powi(2)).sum();
862 assert!((norm_squared - 1.0).abs() < 1e-10);
863 }
864
865 #[test]
866 fn test_quantum_measurement() {
867 let state = QuantumState::new(2, 4);
868 let measured_params = state.measure();
869 assert_eq!(measured_params.len(), 2);
870 }
871
872 #[test]
873 fn test_quantum_annealing_schedule() {
874 let mut schedule = QuantumAnnealingSchedule::new(10.0, 0.1, CoolingSchedule::Linear);
875
876 schedule.update(0, 100, 0.0);
877 assert!((schedule.current_temperature - 10.0).abs() < 1e-10);
878
879 schedule.update(50, 100, -1.0);
880 assert!(schedule.current_temperature < 10.0);
881 assert!(schedule.current_temperature > 0.1);
882
883 schedule.update(100, 100, 0.0);
884 assert!((schedule.current_temperature - 0.1).abs() < 1e-10);
885 }
886
887 #[test]
888 fn test_quantum_optimizer_creation() {
889 let initial_params = Array1::from(vec![1.0, 2.0]);
890 let optimizer = QuantumInspiredOptimizer::new(&initial_params.view(), 100, 16);
891
892 assert_eq!(optimizer.best_solution.len(), 2);
893 assert_eq!(optimizer.max_nit, 100);
894 assert_eq!(optimizer.quantum_state.amplitudes.len(), 16);
895 }
896
897 #[test]
898 fn test_quantum_optimization() {
899 let objective = |x: &ArrayView1<f64>| (x[0] - 1.0).powi(2) + (x[1] - 2.0).powi(2);
900 let initial = Array1::from(vec![0.0, 0.0]);
901
902 let result = quantum_optimize(objective, &initial.view(), Some(200), Some(16)).unwrap();
903
904 assert!(result.nit > 0);
905 assert!(result.fun.is_finite());
907 assert!(result.success);
908 assert_eq!(result.x.len(), 2);
909
910 assert!(result.x[0].abs() < 10.0);
912 assert!(result.x[1].abs() < 10.0);
913 }
914
915 #[test]
916 fn test_quantum_tunneling() {
917 let mut state = QuantumState::new(2, 4);
918 let original_states = state.basis_states.clone();
919
920 state.quantum_tunnel(5.0, 1.0).unwrap(); let mut changed = false;
924 for i in 0..state.basis_states.nrows() {
925 for j in 0..state.basis_states.ncols() {
926 if (state.basis_states[[i, j]] - original_states[[i, j]]).abs() > 1e-10 {
927 changed = true;
928 break;
929 }
930 }
931 }
932
933 assert!(changed);
934 }
935
936 #[test]
937 fn test_quantum_particle_swarm() {
938 let objective = |x: &ArrayView1<f64>| x[0].powi(2) + x[1].powi(2);
939 let initial = Array1::from(vec![5.0, 5.0]);
940
941 let result = quantum_particle_swarm_optimize(objective, &initial.view(), 5, 20).unwrap();
942
943 assert!(result.nit > 0);
944 assert!(result.fun < objective(&initial.view()));
945 assert!(result.success);
946 }
947
948 #[test]
949 fn test_quantum_superposition() {
950 let mut state = QuantumState::new(2, 4);
951 state.create_superposition(1.0).unwrap();
952
953 let n_states = state.amplitudes.len();
955 let expected_magnitude = 1.0 / (n_states as f64).sqrt();
956
957 for amp in &state.amplitudes {
958 assert!((amp.magnitude() - expected_magnitude).abs() < 0.1);
959 }
960 }
961
962 #[test]
963 fn test_quantum_evolution() {
964 let mut state = QuantumState::new(2, 4);
965 let gradients = Array1::from(vec![1.0, -1.0]);
966
967 let original_amplitudes = state.amplitudes.clone();
968 state.evolve(&gradients, 0.1).unwrap();
969
970 let mut evolved = false;
972 for i in 0..state.amplitudes.len() {
973 if (state.amplitudes[i].real - original_amplitudes[i].real).abs() > 1e-10
974 || (state.amplitudes[i].imag - original_amplitudes[i].imag).abs() > 1e-10
975 {
976 evolved = true;
977 break;
978 }
979 }
980
981 assert!(evolved);
982 }
983}