1use scirs2_core::random::prelude::*;
10use scirs2_core::random::ChaCha8Rng;
11use scirs2_core::random::{Rng, SeedableRng};
12use std::collections::HashMap;
13use std::time::{Duration, Instant};
14use thiserror::Error;
15
16use crate::ising::{IsingError, IsingModel};
17
18#[derive(Error, Debug)]
20pub enum CimError {
21 #[error("Ising error: {0}")]
23 IsingError(#[from] IsingError),
24
25 #[error("Invalid optical parameters: {0}")]
27 InvalidOpticalParameters(String),
28
29 #[error("Simulation error: {0}")]
31 SimulationError(String),
32
33 #[error("Network topology error: {0}")]
35 TopologyError(String),
36
37 #[error("Convergence error: {0}")]
39 ConvergenceError(String),
40}
41
42pub type CimResult<T> = Result<T, CimError>;
44
45#[derive(Debug, Clone)]
47pub struct OpticalParametricOscillator {
48 pub index: usize,
50
51 pub amplitude: Complex,
53
54 pub gain: f64,
56
57 pub loss: f64,
59
60 pub saturation: f64,
62
63 pub injection: Complex,
65
66 pub threshold: f64,
68}
69
70#[derive(Debug, Clone, Copy, PartialEq)]
72pub struct Complex {
73 pub re: f64,
75 pub im: f64,
77}
78
79impl Complex {
80 #[must_use]
82 pub const fn new(re: f64, im: f64) -> Self {
83 Self { re, im }
84 }
85
86 #[must_use]
88 pub fn from_polar(magnitude: f64, phase: f64) -> Self {
89 Self {
90 re: magnitude * phase.cos(),
91 im: magnitude * phase.sin(),
92 }
93 }
94
95 #[must_use]
97 pub fn magnitude_squared(&self) -> f64 {
98 self.re.mul_add(self.re, self.im * self.im)
99 }
100
101 #[must_use]
103 pub fn magnitude(&self) -> f64 {
104 self.magnitude_squared().sqrt()
105 }
106
107 #[must_use]
109 pub fn phase(&self) -> f64 {
110 self.im.atan2(self.re)
111 }
112
113 #[must_use]
115 pub fn conjugate(&self) -> Self {
116 Self {
117 re: self.re,
118 im: -self.im,
119 }
120 }
121}
122
123impl std::ops::Add for Complex {
124 type Output = Self;
125
126 fn add(self, other: Self) -> Self {
127 Self {
128 re: self.re + other.re,
129 im: self.im + other.im,
130 }
131 }
132}
133
134impl std::ops::Sub for Complex {
135 type Output = Self;
136
137 fn sub(self, other: Self) -> Self {
138 Self {
139 re: self.re - other.re,
140 im: self.im - other.im,
141 }
142 }
143}
144
145impl std::ops::Mul for Complex {
146 type Output = Self;
147
148 fn mul(self, other: Self) -> Self {
149 Self {
150 re: self.re.mul_add(other.re, -(self.im * other.im)),
151 im: self.re.mul_add(other.im, self.im * other.re),
152 }
153 }
154}
155
156impl std::ops::Mul<f64> for Complex {
157 type Output = Self;
158
159 fn mul(self, scalar: f64) -> Self {
160 Self {
161 re: self.re * scalar,
162 im: self.im * scalar,
163 }
164 }
165}
166
167#[derive(Debug, Clone)]
169pub struct OpticalCoupling {
170 pub from: usize,
172
173 pub to: usize,
175
176 pub strength: Complex,
178
179 pub delay: f64,
181}
182
183#[derive(Debug, Clone)]
185pub struct CimConfig {
186 pub num_oscillators: usize,
188
189 pub dt: f64,
191
192 pub total_time: f64,
194
195 pub pump_schedule: PumpSchedule,
197
198 pub topology: NetworkTopology,
200
201 pub noise_config: NoiseConfig,
203
204 pub measurement_config: MeasurementConfig,
206
207 pub convergence_config: ConvergenceConfig,
209
210 pub seed: Option<u64>,
212
213 pub detailed_logging: bool,
215
216 pub output_file: Option<String>,
218}
219
220impl Default for CimConfig {
221 fn default() -> Self {
222 Self {
223 num_oscillators: 4,
224 dt: 0.001,
225 total_time: 10.0,
226 pump_schedule: PumpSchedule::Linear {
227 initial_power: 0.5,
228 final_power: 2.0,
229 },
230 topology: NetworkTopology::FullyConnected,
231 noise_config: NoiseConfig::default(),
232 measurement_config: MeasurementConfig::default(),
233 convergence_config: ConvergenceConfig::default(),
234 seed: None,
235 detailed_logging: false,
236 output_file: None,
237 }
238 }
239}
240
241pub enum PumpSchedule {
243 Linear {
245 initial_power: f64,
246 final_power: f64,
247 },
248
249 Exponential {
251 initial_power: f64,
252 final_power: f64,
253 time_constant: f64,
254 },
255
256 Sigmoid {
258 initial_power: f64,
259 final_power: f64,
260 steepness: f64,
261 midpoint: f64,
262 },
263
264 Custom {
266 power_function: Box<dyn Fn(f64) -> f64 + Send + Sync>,
267 },
268}
269
270impl std::fmt::Debug for PumpSchedule {
271 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
272 match self {
273 Self::Linear {
274 initial_power,
275 final_power,
276 } => f
277 .debug_struct("Linear")
278 .field("initial_power", initial_power)
279 .field("final_power", final_power)
280 .finish(),
281 Self::Exponential {
282 initial_power,
283 final_power,
284 time_constant,
285 } => f
286 .debug_struct("Exponential")
287 .field("initial_power", initial_power)
288 .field("final_power", final_power)
289 .field("time_constant", time_constant)
290 .finish(),
291 Self::Sigmoid {
292 initial_power,
293 final_power,
294 steepness,
295 midpoint,
296 } => f
297 .debug_struct("Sigmoid")
298 .field("initial_power", initial_power)
299 .field("final_power", final_power)
300 .field("steepness", steepness)
301 .field("midpoint", midpoint)
302 .finish(),
303 Self::Custom { .. } => f
304 .debug_struct("Custom")
305 .field("power_function", &"<function>")
306 .finish(),
307 }
308 }
309}
310
311impl Clone for PumpSchedule {
312 fn clone(&self) -> Self {
313 match self {
314 Self::Linear {
315 initial_power,
316 final_power,
317 } => Self::Linear {
318 initial_power: *initial_power,
319 final_power: *final_power,
320 },
321 Self::Exponential {
322 initial_power,
323 final_power,
324 time_constant,
325 } => Self::Exponential {
326 initial_power: *initial_power,
327 final_power: *final_power,
328 time_constant: *time_constant,
329 },
330 Self::Sigmoid {
331 initial_power,
332 final_power,
333 steepness,
334 midpoint,
335 } => Self::Sigmoid {
336 initial_power: *initial_power,
337 final_power: *final_power,
338 steepness: *steepness,
339 midpoint: *midpoint,
340 },
341 Self::Custom { .. } => {
342 Self::Linear {
344 initial_power: 0.0,
345 final_power: 1.0,
346 }
347 }
348 }
349 }
350}
351
352#[derive(Debug, Clone)]
354pub enum NetworkTopology {
355 FullyConnected,
357
358 Ring,
360
361 Lattice2D { width: usize, height: usize },
363
364 Random { connectivity: f64 },
366
367 SmallWorld {
369 ring_connectivity: usize,
370 rewiring_probability: f64,
371 },
372
373 Custom { couplings: Vec<OpticalCoupling> },
375}
376
377#[derive(Debug, Clone)]
379pub struct NoiseConfig {
380 pub quantum_noise: f64,
382
383 pub phase_noise: f64,
385
386 pub amplitude_noise: f64,
388
389 pub temperature: f64,
391
392 pub decoherence_rate: f64,
394}
395
396impl Default for NoiseConfig {
397 fn default() -> Self {
398 Self {
399 quantum_noise: 0.01,
400 phase_noise: 0.001,
401 amplitude_noise: 0.001,
402 temperature: 0.01,
403 decoherence_rate: 0.001,
404 }
405 }
406}
407
408#[derive(Debug, Clone)]
410pub struct MeasurementConfig {
411 pub detection_efficiency: f64,
413
414 pub measurement_window: f64,
416
417 pub reference_phase: f64,
419
420 pub measurement_repetitions: usize,
422
423 pub decision_threshold: f64,
425}
426
427impl Default for MeasurementConfig {
428 fn default() -> Self {
429 Self {
430 detection_efficiency: 0.95,
431 measurement_window: 1.0,
432 reference_phase: 0.0,
433 measurement_repetitions: 100,
434 decision_threshold: 0.0,
435 }
436 }
437}
438
439#[derive(Debug, Clone)]
441pub struct ConvergenceConfig {
442 pub energy_tolerance: f64,
444
445 pub stagnation_time: f64,
447
448 pub oscillation_threshold: f64,
450
451 pub phase_stability: f64,
453}
454
455impl Default for ConvergenceConfig {
456 fn default() -> Self {
457 Self {
458 energy_tolerance: 1e-6,
459 stagnation_time: 1.0,
460 oscillation_threshold: 0.1,
461 phase_stability: 0.01,
462 }
463 }
464}
465
466#[derive(Debug, Clone)]
468pub struct CimResults {
469 pub best_solution: Vec<i8>,
471
472 pub best_energy: f64,
474
475 pub final_optical_state: Vec<Complex>,
477
478 pub energy_history: Vec<f64>,
480
481 pub optical_evolution: Vec<Vec<Complex>>,
483
484 pub time_points: Vec<f64>,
486
487 pub converged: bool,
489
490 pub convergence_time: f64,
492
493 pub total_simulation_time: Duration,
495
496 pub optical_stats: OpticalStatistics,
498
499 pub performance_metrics: CimPerformanceMetrics,
501}
502
503#[derive(Debug, Clone)]
505pub struct OpticalStatistics {
506 pub average_power: f64,
508
509 pub power_variance: f64,
511
512 pub phase_coherence: Vec<f64>,
514
515 pub cross_correlations: Vec<Vec<f64>>,
517
518 pub oscillation_frequencies: Vec<f64>,
520
521 pub pump_efficiency: f64,
523}
524
525#[derive(Debug, Clone)]
527pub struct CimPerformanceMetrics {
528 pub solution_quality: f64,
530
531 pub time_to_convergence: f64,
533
534 pub phase_transitions: usize,
536
537 pub power_efficiency: f64,
539
540 pub noise_resilience: f64,
542}
543
544pub struct CoherentIsingMachine {
546 config: CimConfig,
548
549 oscillators: Vec<OpticalParametricOscillator>,
551
552 couplings: Vec<OpticalCoupling>,
554
555 rng: ChaCha8Rng,
557
558 current_time: f64,
560
561 evolution_history: Vec<(f64, Vec<Complex>)>,
563
564 energy_history: Vec<f64>,
566}
567
568impl CoherentIsingMachine {
569 pub fn new(config: CimConfig) -> CimResult<Self> {
571 let rng = match config.seed {
572 Some(seed) => ChaCha8Rng::seed_from_u64(seed),
573 None => ChaCha8Rng::seed_from_u64(thread_rng().gen()),
574 };
575
576 let mut cim = Self {
577 oscillators: Vec::new(),
578 couplings: Vec::new(),
579 rng,
580 current_time: 0.0,
581 evolution_history: Vec::new(),
582 energy_history: Vec::new(),
583 config,
584 };
585
586 cim.initialize_system()?;
587 Ok(cim)
588 }
589
590 fn initialize_system(&mut self) -> CimResult<()> {
592 for i in 0..self.config.num_oscillators {
594 let osc = OpticalParametricOscillator {
595 index: i,
596 amplitude: Complex::new(
597 self.rng.gen_range(-0.1..0.1),
598 self.rng.gen_range(-0.1..0.1),
599 ),
600 gain: 1.0,
601 loss: 0.1,
602 saturation: 1.0,
603 injection: Complex::new(0.0, 0.0),
604 threshold: 1.0,
605 };
606 self.oscillators.push(osc);
607 }
608
609 self.initialize_topology()?;
611
612 Ok(())
613 }
614
615 fn initialize_topology(&mut self) -> CimResult<()> {
617 self.couplings.clear();
618
619 let topology = self.config.topology.clone();
620 match topology {
621 NetworkTopology::FullyConnected => {
622 for i in 0..self.config.num_oscillators {
623 for j in (i + 1)..self.config.num_oscillators {
624 self.couplings.push(OpticalCoupling {
625 from: i,
626 to: j,
627 strength: Complex::new(0.1, 0.0),
628 delay: 0.0,
629 });
630 self.couplings.push(OpticalCoupling {
631 from: j,
632 to: i,
633 strength: Complex::new(0.1, 0.0),
634 delay: 0.0,
635 });
636 }
637 }
638 }
639
640 NetworkTopology::Ring => {
641 for i in 0..self.config.num_oscillators {
642 let j = (i + 1) % self.config.num_oscillators;
643 self.couplings.push(OpticalCoupling {
644 from: i,
645 to: j,
646 strength: Complex::new(0.2, 0.0),
647 delay: 0.0,
648 });
649 self.couplings.push(OpticalCoupling {
650 from: j,
651 to: i,
652 strength: Complex::new(0.2, 0.0),
653 delay: 0.0,
654 });
655 }
656 }
657
658 NetworkTopology::Lattice2D { width, height } => {
659 if width * height != self.config.num_oscillators {
660 return Err(CimError::TopologyError(
661 "Lattice dimensions don't match number of oscillators".to_string(),
662 ));
663 }
664
665 for i in 0..width {
667 for j in 0..height {
668 let idx = i * height + j;
669
670 if i + 1 < width {
672 let neighbor = (i + 1) * height + j;
673 self.add_bidirectional_coupling(idx, neighbor, 0.15);
674 }
675
676 if j + 1 < height {
678 let neighbor = i * height + (j + 1);
679 self.add_bidirectional_coupling(idx, neighbor, 0.15);
680 }
681 }
682 }
683 }
684
685 NetworkTopology::Random { connectivity } => {
686 let num_possible_edges =
687 self.config.num_oscillators * (self.config.num_oscillators - 1) / 2;
688 let num_edges = (num_possible_edges as f64 * connectivity) as usize;
689
690 let mut added_edges = std::collections::HashSet::new();
691
692 while added_edges.len() < num_edges {
693 let i = self.rng.gen_range(0..self.config.num_oscillators);
694 let j = self.rng.gen_range(0..self.config.num_oscillators);
695
696 if i != j {
697 let edge = if i < j { (i, j) } else { (j, i) };
698 if added_edges.insert(edge) {
699 let strength = self.rng.gen_range(0.05..0.25);
700 self.add_bidirectional_coupling(edge.0, edge.1, strength);
701 }
702 }
703 }
704 }
705
706 NetworkTopology::SmallWorld {
707 ring_connectivity,
708 rewiring_probability,
709 } => {
710 for i in 0..self.config.num_oscillators {
712 for k in 1..=ring_connectivity {
713 let j = (i + k) % self.config.num_oscillators;
714
715 if self.rng.gen_bool(rewiring_probability) {
717 let new_target = self.rng.gen_range(0..self.config.num_oscillators);
718 if new_target != i {
719 self.add_bidirectional_coupling(i, new_target, 0.1);
720 }
721 } else {
722 self.add_bidirectional_coupling(i, j, 0.1);
723 }
724 }
725 }
726 }
727
728 NetworkTopology::Custom { couplings } => {
729 self.couplings = couplings;
730 }
731 }
732
733 Ok(())
734 }
735
736 fn add_bidirectional_coupling(&mut self, i: usize, j: usize, strength: f64) {
738 self.couplings.push(OpticalCoupling {
739 from: i,
740 to: j,
741 strength: Complex::new(strength, 0.0),
742 delay: 0.0,
743 });
744 self.couplings.push(OpticalCoupling {
745 from: j,
746 to: i,
747 strength: Complex::new(strength, 0.0),
748 delay: 0.0,
749 });
750 }
751
752 pub fn solve(&mut self, problem: &IsingModel) -> CimResult<CimResults> {
754 if problem.num_qubits != self.config.num_oscillators {
755 return Err(CimError::SimulationError(format!(
756 "Problem size {} doesn't match CIM size {}",
757 problem.num_qubits, self.config.num_oscillators
758 )));
759 }
760
761 println!("Starting coherent Ising machine simulation...");
762 let start_time = Instant::now();
763
764 self.map_ising_to_optical(problem)?;
766
767 self.current_time = 0.0;
769 self.evolution_history.clear();
770 self.energy_history.clear();
771
772 let num_steps = (self.config.total_time / self.config.dt) as usize;
774 let mut best_energy = f64::INFINITY;
775 let mut best_solution = vec![0; problem.num_qubits];
776 let mut converged = false;
777 let mut convergence_time = self.config.total_time;
778
779 for step in 0..num_steps {
780 self.current_time = step as f64 * self.config.dt;
781
782 let pump_power = self.get_pump_power(self.current_time);
784 self.update_pump_power(pump_power);
785
786 self.evolve_system()?;
788
789 self.add_noise();
791
792 if step % 100 == 0 || step == num_steps - 1 {
794 self.record_state();
795 }
796
797 let current_solution = self.measure_solution()?;
799 let current_energy = self.evaluate_energy(problem, ¤t_solution)?;
800 self.energy_history.push(current_energy);
801
802 if current_energy < best_energy {
804 best_energy = current_energy;
805 best_solution = current_solution;
806 }
807
808 if !converged && self.check_convergence()? {
810 converged = true;
811 convergence_time = self.current_time;
812 if self.config.detailed_logging {
813 println!("Converged at time {convergence_time:.3}");
814 }
815 }
816
817 if step % 1000 == 0 && self.config.detailed_logging {
819 println!(
820 "Step {}: Time = {:.3}, Energy = {:.6}, Power = {:.3}",
821 step, self.current_time, current_energy, pump_power
822 );
823 }
824 }
825
826 let total_simulation_time = start_time.elapsed();
827
828 let optical_stats = self.calculate_optical_statistics();
830 let performance_metrics = self.calculate_performance_metrics(best_energy, convergence_time);
831
832 let results = CimResults {
834 best_solution,
835 best_energy,
836 final_optical_state: self.oscillators.iter().map(|osc| osc.amplitude).collect(),
837 energy_history: self.energy_history.clone(),
838 optical_evolution: self
839 .evolution_history
840 .iter()
841 .map(|(_, state)| state.clone())
842 .collect(),
843 time_points: self.evolution_history.iter().map(|(t, _)| *t).collect(),
844 converged,
845 convergence_time,
846 total_simulation_time,
847 optical_stats,
848 performance_metrics,
849 };
850
851 println!("CIM simulation completed in {total_simulation_time:.2?}");
852 println!("Best energy: {best_energy:.6}, Converged: {converged}");
853
854 Ok(results)
855 }
856
857 fn map_ising_to_optical(&mut self, problem: &IsingModel) -> CimResult<()> {
859 for i in 0..problem.num_qubits {
861 let bias = problem.get_bias(i).unwrap_or(0.0);
862 self.oscillators[i].injection = Complex::new(bias * 0.1, 0.0);
863 }
864
865 for coupling in &mut self.couplings {
867 if let Ok(ising_coupling) = problem.get_coupling(coupling.from, coupling.to) {
868 if ising_coupling != 0.0 {
869 let optical_strength = ising_coupling * 0.1;
871 coupling.strength = Complex::new(optical_strength, 0.0);
872 }
873 }
874 }
875
876 Ok(())
877 }
878
879 fn get_pump_power(&self, time: f64) -> f64 {
881 let normalized_time = time / self.config.total_time;
882
883 match &self.config.pump_schedule {
884 PumpSchedule::Linear {
885 initial_power,
886 final_power,
887 } => initial_power + (final_power - initial_power) * normalized_time,
888
889 PumpSchedule::Exponential {
890 initial_power,
891 final_power,
892 time_constant,
893 } => {
894 initial_power
895 + (final_power - initial_power) * (1.0 - (-time / time_constant).exp())
896 }
897
898 PumpSchedule::Sigmoid {
899 initial_power,
900 final_power,
901 steepness,
902 midpoint,
903 } => {
904 let sigmoid = 1.0 / (1.0 + (-(normalized_time - midpoint) * steepness).exp());
905 initial_power + (final_power - initial_power) * sigmoid
906 }
907
908 PumpSchedule::Custom { power_function } => power_function(time),
909 }
910 }
911
912 fn update_pump_power(&mut self, pump_power: f64) {
914 for osc in &mut self.oscillators {
915 osc.gain = pump_power;
916 }
917 }
918
919 fn evolve_system(&mut self) -> CimResult<()> {
921 let dt = self.config.dt;
922 let mut new_amplitudes = Vec::new();
923
924 for i in 0..self.oscillators.len() {
926 let osc = &self.oscillators[i];
927 let mut derivative = Complex::new(0.0, 0.0);
928
929 let power = osc.amplitude.magnitude_squared();
931 if osc.gain > osc.threshold {
932 let net_gain = osc.gain - osc.threshold - osc.loss;
933 derivative = derivative + osc.amplitude * net_gain * (1.0 - power / osc.saturation);
934 } else {
935 derivative = derivative + osc.amplitude * (-osc.loss);
937 }
938
939 derivative = derivative + osc.injection;
941
942 for coupling in &self.couplings {
944 if coupling.to == i {
945 let source_amplitude = self.oscillators[coupling.from].amplitude;
946 derivative = derivative + source_amplitude * coupling.strength;
947 }
948 }
949
950 let new_amplitude = osc.amplitude + derivative * dt;
952 new_amplitudes.push(new_amplitude);
953 }
954
955 for (i, new_amplitude) in new_amplitudes.into_iter().enumerate() {
957 self.oscillators[i].amplitude = new_amplitude;
958 }
959
960 Ok(())
961 }
962
963 fn add_noise(&mut self) {
965 let noise_config = &self.config.noise_config;
966
967 for osc in &mut self.oscillators {
968 let quantum_noise_re = self.rng.gen_range(-1.0..1.0) * noise_config.quantum_noise;
970 let quantum_noise_im = self.rng.gen_range(-1.0..1.0) * noise_config.quantum_noise;
971
972 let phase_noise = self.rng.gen_range(-1.0..1.0) * noise_config.phase_noise;
974 let current_phase = osc.amplitude.phase();
975 let new_phase = current_phase + phase_noise;
976
977 let amplitude_noise = self
979 .rng
980 .gen_range(-1.0_f64..1.0)
981 .mul_add(noise_config.amplitude_noise, 1.0);
982 let current_magnitude = osc.amplitude.magnitude();
983 let new_magnitude = current_magnitude * amplitude_noise;
984
985 osc.amplitude = Complex::from_polar(new_magnitude, new_phase)
987 + Complex::new(quantum_noise_re, quantum_noise_im);
988
989 osc.amplitude =
991 osc.amplitude * noise_config.decoherence_rate.mul_add(-self.config.dt, 1.0);
992 }
993 }
994
995 fn record_state(&mut self) {
997 let state: Vec<Complex> = self.oscillators.iter().map(|osc| osc.amplitude).collect();
998 self.evolution_history.push((self.current_time, state));
999 }
1000
1001 fn measure_solution(&self) -> CimResult<Vec<i8>> {
1003 let mut solution = Vec::new();
1004
1005 for osc in &self.oscillators {
1006 let measurement =
1008 osc.amplitude.re * self.config.measurement_config.detection_efficiency;
1009
1010 let spin = if measurement > self.config.measurement_config.decision_threshold {
1012 1
1013 } else {
1014 -1
1015 };
1016
1017 solution.push(spin);
1018 }
1019
1020 Ok(solution)
1021 }
1022
1023 fn evaluate_energy(&self, problem: &IsingModel, solution: &[i8]) -> CimResult<f64> {
1025 let mut energy = 0.0;
1026
1027 for i in 0..solution.len() {
1029 energy += problem.get_bias(i).unwrap_or(0.0) * f64::from(solution[i]);
1030 }
1031
1032 for i in 0..solution.len() {
1034 for j in (i + 1)..solution.len() {
1035 energy += problem.get_coupling(i, j).unwrap_or(0.0)
1036 * f64::from(solution[i])
1037 * f64::from(solution[j]);
1038 }
1039 }
1040
1041 Ok(energy)
1042 }
1043
1044 fn check_convergence(&self) -> CimResult<bool> {
1046 if self.energy_history.len() < 100 {
1047 return Ok(false);
1048 }
1049
1050 let recent_window = 50;
1052 let recent_energies = &self.energy_history[self.energy_history.len() - recent_window..];
1053 let energy_variance = {
1054 let mean = recent_energies.iter().sum::<f64>() / recent_energies.len() as f64;
1055 recent_energies
1056 .iter()
1057 .map(|&e| (e - mean).powi(2))
1058 .sum::<f64>()
1059 / recent_energies.len() as f64
1060 };
1061
1062 let energy_stable = energy_variance < self.config.convergence_config.energy_tolerance;
1063
1064 let oscillation_stable = self.oscillators.iter().all(|osc| {
1066 osc.amplitude.magnitude() > self.config.convergence_config.oscillation_threshold
1067 });
1068
1069 Ok(energy_stable && oscillation_stable)
1070 }
1071
1072 fn calculate_optical_statistics(&self) -> OpticalStatistics {
1074 let num_oscillators = self.oscillators.len();
1075
1076 let total_power: f64 = self
1078 .oscillators
1079 .iter()
1080 .map(|osc| osc.amplitude.magnitude_squared())
1081 .sum();
1082 let average_power = total_power / num_oscillators as f64;
1083
1084 let power_variance = {
1086 let powers: Vec<f64> = self
1087 .oscillators
1088 .iter()
1089 .map(|osc| osc.amplitude.magnitude_squared())
1090 .collect();
1091 let mean = powers.iter().sum::<f64>() / powers.len() as f64;
1092 powers.iter().map(|&p| (p - mean).powi(2)).sum::<f64>() / powers.len() as f64
1093 };
1094
1095 let phase_coherence: Vec<f64> = self
1097 .oscillators
1098 .iter()
1099 .map(|osc| osc.amplitude.magnitude().min(1.0))
1100 .collect();
1101
1102 let mut cross_correlations = vec![vec![0.0; num_oscillators]; num_oscillators];
1104 for i in 0..num_oscillators {
1105 for j in 0..num_oscillators {
1106 if i == j {
1107 cross_correlations[i][j] = 1.0;
1108 } else {
1109 let phase_diff = (self.oscillators[i].amplitude.phase()
1111 - self.oscillators[j].amplitude.phase())
1112 .abs();
1113 cross_correlations[i][j] = (phase_diff / std::f64::consts::PI).cos().abs();
1114 }
1115 }
1116 }
1117
1118 let oscillation_frequencies = vec![1.0; num_oscillators]; OpticalStatistics {
1122 average_power,
1123 power_variance,
1124 phase_coherence,
1125 cross_correlations,
1126 oscillation_frequencies,
1127 pump_efficiency: 0.8, }
1129 }
1130
1131 fn calculate_performance_metrics(
1133 &self,
1134 best_energy: f64,
1135 convergence_time: f64,
1136 ) -> CimPerformanceMetrics {
1137 let solution_quality = 1.0; let time_to_convergence = convergence_time / self.config.total_time;
1142
1143 let average_power = self
1145 .oscillators
1146 .iter()
1147 .map(|osc| osc.amplitude.magnitude_squared())
1148 .sum::<f64>()
1149 / self.oscillators.len() as f64;
1150 let power_efficiency = 1.0 / (1.0 + average_power);
1151
1152 CimPerformanceMetrics {
1153 solution_quality,
1154 time_to_convergence,
1155 phase_transitions: 0, power_efficiency,
1157 noise_resilience: 0.8, }
1159 }
1160}
1161
1162#[must_use]
1166pub fn create_standard_cim_config(num_oscillators: usize, simulation_time: f64) -> CimConfig {
1167 CimConfig {
1168 num_oscillators,
1169 dt: 0.001,
1170 total_time: simulation_time,
1171 pump_schedule: PumpSchedule::Linear {
1172 initial_power: 0.5,
1173 final_power: 1.5,
1174 },
1175 topology: NetworkTopology::FullyConnected,
1176 ..Default::default()
1177 }
1178}
1179
1180#[must_use]
1182pub fn create_low_noise_cim_config(num_oscillators: usize) -> CimConfig {
1183 let mut config = create_standard_cim_config(num_oscillators, 5.0);
1184 config.noise_config = NoiseConfig {
1185 quantum_noise: 0.001,
1186 phase_noise: 0.0001,
1187 amplitude_noise: 0.0001,
1188 temperature: 0.001,
1189 decoherence_rate: 0.0001,
1190 };
1191 config
1192}
1193
1194#[must_use]
1196pub fn create_realistic_cim_config(num_oscillators: usize) -> CimConfig {
1197 let mut config = create_standard_cim_config(num_oscillators, 10.0);
1198 config.noise_config = NoiseConfig {
1199 quantum_noise: 0.05,
1200 phase_noise: 0.01,
1201 amplitude_noise: 0.01,
1202 temperature: 0.1,
1203 decoherence_rate: 0.01,
1204 };
1205 config.detailed_logging = true;
1206 config
1207}
1208
1209#[cfg(test)]
1210mod tests {
1211 use super::*;
1212
1213 #[test]
1214 fn test_complex_operations() {
1215 let c1 = Complex::new(3.0, 4.0);
1216 let c2 = Complex::new(1.0, 2.0);
1217
1218 assert_eq!(c1.magnitude(), 5.0);
1219 assert!((c1.phase() - 4.0_f64.atan2(3.0)).abs() < 1e-10);
1220
1221 let sum = c1 + c2;
1222 assert_eq!(sum.re, 4.0);
1223 assert_eq!(sum.im, 6.0);
1224
1225 let product = c1 * c2;
1226 assert_eq!(product.re, -5.0); assert_eq!(product.im, 10.0); }
1229
1230 #[test]
1231 fn test_cim_config_creation() {
1232 let config = create_standard_cim_config(4, 5.0);
1233 assert_eq!(config.num_oscillators, 4);
1234 assert_eq!(config.total_time, 5.0);
1235
1236 match config.topology {
1237 NetworkTopology::FullyConnected => {}
1238 _ => panic!("Expected fully connected topology"),
1239 }
1240 }
1241
1242 #[test]
1243 fn test_optical_oscillator() {
1244 let osc = OpticalParametricOscillator {
1245 index: 0,
1246 amplitude: Complex::new(1.0, 0.0),
1247 gain: 1.5,
1248 loss: 0.1,
1249 saturation: 1.0,
1250 injection: Complex::new(0.1, 0.0),
1251 threshold: 1.0,
1252 };
1253
1254 assert_eq!(osc.amplitude.magnitude(), 1.0);
1255 assert_eq!(osc.gain, 1.5);
1256 assert!(osc.gain > osc.threshold);
1257 }
1258}