1use scirs2_core::random::prelude::*;
10use scirs2_core::random::ChaCha8Rng;
11use scirs2_core::random::{Rng, SeedableRng};
12use scirs2_core::RngExt;
13use std::collections::HashMap;
14use std::time::{Duration, Instant};
15use thiserror::Error;
16
17use crate::ising::{IsingError, IsingModel};
18
19#[derive(Error, Debug)]
21pub enum CimError {
22 #[error("Ising error: {0}")]
24 IsingError(#[from] IsingError),
25
26 #[error("Invalid optical parameters: {0}")]
28 InvalidOpticalParameters(String),
29
30 #[error("Simulation error: {0}")]
32 SimulationError(String),
33
34 #[error("Network topology error: {0}")]
36 TopologyError(String),
37
38 #[error("Convergence error: {0}")]
40 ConvergenceError(String),
41}
42
43pub type CimResult<T> = Result<T, CimError>;
45
46#[derive(Debug, Clone)]
48pub struct OpticalParametricOscillator {
49 pub index: usize,
51
52 pub amplitude: Complex,
54
55 pub gain: f64,
57
58 pub loss: f64,
60
61 pub saturation: f64,
63
64 pub injection: Complex,
66
67 pub threshold: f64,
69}
70
71#[derive(Debug, Clone, Copy, PartialEq)]
73pub struct Complex {
74 pub re: f64,
76 pub im: f64,
78}
79
80impl Complex {
81 #[must_use]
83 pub const fn new(re: f64, im: f64) -> Self {
84 Self { re, im }
85 }
86
87 #[must_use]
89 pub fn from_polar(magnitude: f64, phase: f64) -> Self {
90 Self {
91 re: magnitude * phase.cos(),
92 im: magnitude * phase.sin(),
93 }
94 }
95
96 #[must_use]
98 pub fn magnitude_squared(&self) -> f64 {
99 self.re.mul_add(self.re, self.im * self.im)
100 }
101
102 #[must_use]
104 pub fn magnitude(&self) -> f64 {
105 self.magnitude_squared().sqrt()
106 }
107
108 #[must_use]
110 pub fn phase(&self) -> f64 {
111 self.im.atan2(self.re)
112 }
113
114 #[must_use]
116 pub fn conjugate(&self) -> Self {
117 Self {
118 re: self.re,
119 im: -self.im,
120 }
121 }
122}
123
124impl std::ops::Add for Complex {
125 type Output = Self;
126
127 fn add(self, other: Self) -> Self {
128 Self {
129 re: self.re + other.re,
130 im: self.im + other.im,
131 }
132 }
133}
134
135impl std::ops::Sub for Complex {
136 type Output = Self;
137
138 fn sub(self, other: Self) -> Self {
139 Self {
140 re: self.re - other.re,
141 im: self.im - other.im,
142 }
143 }
144}
145
146impl std::ops::Mul for Complex {
147 type Output = Self;
148
149 fn mul(self, other: Self) -> Self {
150 Self {
151 re: self.re.mul_add(other.re, -(self.im * other.im)),
152 im: self.re.mul_add(other.im, self.im * other.re),
153 }
154 }
155}
156
157impl std::ops::Mul<f64> for Complex {
158 type Output = Self;
159
160 fn mul(self, scalar: f64) -> Self {
161 Self {
162 re: self.re * scalar,
163 im: self.im * scalar,
164 }
165 }
166}
167
168#[derive(Debug, Clone)]
170pub struct OpticalCoupling {
171 pub from: usize,
173
174 pub to: usize,
176
177 pub strength: Complex,
179
180 pub delay: f64,
182}
183
184#[derive(Debug, Clone)]
186pub struct CimConfig {
187 pub num_oscillators: usize,
189
190 pub dt: f64,
192
193 pub total_time: f64,
195
196 pub pump_schedule: PumpSchedule,
198
199 pub topology: NetworkTopology,
201
202 pub noise_config: NoiseConfig,
204
205 pub measurement_config: MeasurementConfig,
207
208 pub convergence_config: ConvergenceConfig,
210
211 pub seed: Option<u64>,
213
214 pub detailed_logging: bool,
216
217 pub output_file: Option<String>,
219}
220
221impl Default for CimConfig {
222 fn default() -> Self {
223 Self {
224 num_oscillators: 4,
225 dt: 0.001,
226 total_time: 10.0,
227 pump_schedule: PumpSchedule::Linear {
228 initial_power: 0.5,
229 final_power: 2.0,
230 },
231 topology: NetworkTopology::FullyConnected,
232 noise_config: NoiseConfig::default(),
233 measurement_config: MeasurementConfig::default(),
234 convergence_config: ConvergenceConfig::default(),
235 seed: None,
236 detailed_logging: false,
237 output_file: None,
238 }
239 }
240}
241
242pub enum PumpSchedule {
244 Linear {
246 initial_power: f64,
247 final_power: f64,
248 },
249
250 Exponential {
252 initial_power: f64,
253 final_power: f64,
254 time_constant: f64,
255 },
256
257 Sigmoid {
259 initial_power: f64,
260 final_power: f64,
261 steepness: f64,
262 midpoint: f64,
263 },
264
265 Custom {
267 power_function: Box<dyn Fn(f64) -> f64 + Send + Sync>,
268 },
269}
270
271impl std::fmt::Debug for PumpSchedule {
272 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
273 match self {
274 Self::Linear {
275 initial_power,
276 final_power,
277 } => f
278 .debug_struct("Linear")
279 .field("initial_power", initial_power)
280 .field("final_power", final_power)
281 .finish(),
282 Self::Exponential {
283 initial_power,
284 final_power,
285 time_constant,
286 } => f
287 .debug_struct("Exponential")
288 .field("initial_power", initial_power)
289 .field("final_power", final_power)
290 .field("time_constant", time_constant)
291 .finish(),
292 Self::Sigmoid {
293 initial_power,
294 final_power,
295 steepness,
296 midpoint,
297 } => f
298 .debug_struct("Sigmoid")
299 .field("initial_power", initial_power)
300 .field("final_power", final_power)
301 .field("steepness", steepness)
302 .field("midpoint", midpoint)
303 .finish(),
304 Self::Custom { .. } => f
305 .debug_struct("Custom")
306 .field("power_function", &"<function>")
307 .finish(),
308 }
309 }
310}
311
312impl Clone for PumpSchedule {
313 fn clone(&self) -> Self {
314 match self {
315 Self::Linear {
316 initial_power,
317 final_power,
318 } => Self::Linear {
319 initial_power: *initial_power,
320 final_power: *final_power,
321 },
322 Self::Exponential {
323 initial_power,
324 final_power,
325 time_constant,
326 } => Self::Exponential {
327 initial_power: *initial_power,
328 final_power: *final_power,
329 time_constant: *time_constant,
330 },
331 Self::Sigmoid {
332 initial_power,
333 final_power,
334 steepness,
335 midpoint,
336 } => Self::Sigmoid {
337 initial_power: *initial_power,
338 final_power: *final_power,
339 steepness: *steepness,
340 midpoint: *midpoint,
341 },
342 Self::Custom { .. } => {
343 Self::Linear {
345 initial_power: 0.0,
346 final_power: 1.0,
347 }
348 }
349 }
350 }
351}
352
353#[derive(Debug, Clone)]
355pub enum NetworkTopology {
356 FullyConnected,
358
359 Ring,
361
362 Lattice2D { width: usize, height: usize },
364
365 Random { connectivity: f64 },
367
368 SmallWorld {
370 ring_connectivity: usize,
371 rewiring_probability: f64,
372 },
373
374 Custom { couplings: Vec<OpticalCoupling> },
376}
377
378#[derive(Debug, Clone)]
380pub struct NoiseConfig {
381 pub quantum_noise: f64,
383
384 pub phase_noise: f64,
386
387 pub amplitude_noise: f64,
389
390 pub temperature: f64,
392
393 pub decoherence_rate: f64,
395}
396
397impl Default for NoiseConfig {
398 fn default() -> Self {
399 Self {
400 quantum_noise: 0.01,
401 phase_noise: 0.001,
402 amplitude_noise: 0.001,
403 temperature: 0.01,
404 decoherence_rate: 0.001,
405 }
406 }
407}
408
409#[derive(Debug, Clone)]
411pub struct MeasurementConfig {
412 pub detection_efficiency: f64,
414
415 pub measurement_window: f64,
417
418 pub reference_phase: f64,
420
421 pub measurement_repetitions: usize,
423
424 pub decision_threshold: f64,
426}
427
428impl Default for MeasurementConfig {
429 fn default() -> Self {
430 Self {
431 detection_efficiency: 0.95,
432 measurement_window: 1.0,
433 reference_phase: 0.0,
434 measurement_repetitions: 100,
435 decision_threshold: 0.0,
436 }
437 }
438}
439
440#[derive(Debug, Clone)]
442pub struct ConvergenceConfig {
443 pub energy_tolerance: f64,
445
446 pub stagnation_time: f64,
448
449 pub oscillation_threshold: f64,
451
452 pub phase_stability: f64,
454}
455
456impl Default for ConvergenceConfig {
457 fn default() -> Self {
458 Self {
459 energy_tolerance: 1e-6,
460 stagnation_time: 1.0,
461 oscillation_threshold: 0.1,
462 phase_stability: 0.01,
463 }
464 }
465}
466
467#[derive(Debug, Clone)]
469pub struct CimResults {
470 pub best_solution: Vec<i8>,
472
473 pub best_energy: f64,
475
476 pub final_optical_state: Vec<Complex>,
478
479 pub energy_history: Vec<f64>,
481
482 pub optical_evolution: Vec<Vec<Complex>>,
484
485 pub time_points: Vec<f64>,
487
488 pub converged: bool,
490
491 pub convergence_time: f64,
493
494 pub total_simulation_time: Duration,
496
497 pub optical_stats: OpticalStatistics,
499
500 pub performance_metrics: CimPerformanceMetrics,
502}
503
504#[derive(Debug, Clone)]
506pub struct OpticalStatistics {
507 pub average_power: f64,
509
510 pub power_variance: f64,
512
513 pub phase_coherence: Vec<f64>,
515
516 pub cross_correlations: Vec<Vec<f64>>,
518
519 pub oscillation_frequencies: Vec<f64>,
521
522 pub pump_efficiency: f64,
524}
525
526#[derive(Debug, Clone)]
528pub struct CimPerformanceMetrics {
529 pub solution_quality: f64,
531
532 pub time_to_convergence: f64,
534
535 pub phase_transitions: usize,
537
538 pub power_efficiency: f64,
540
541 pub noise_resilience: f64,
543}
544
545pub struct CoherentIsingMachine {
547 config: CimConfig,
549
550 oscillators: Vec<OpticalParametricOscillator>,
552
553 couplings: Vec<OpticalCoupling>,
555
556 rng: ChaCha8Rng,
558
559 current_time: f64,
561
562 evolution_history: Vec<(f64, Vec<Complex>)>,
564
565 energy_history: Vec<f64>,
567}
568
569impl CoherentIsingMachine {
570 pub fn new(config: CimConfig) -> CimResult<Self> {
572 let rng = match config.seed {
573 Some(seed) => ChaCha8Rng::seed_from_u64(seed),
574 None => {
575 let seed = std::time::SystemTime::now()
578 .duration_since(std::time::UNIX_EPOCH)
579 .map(|d| {
580 d.subsec_nanos() as u64 ^ (d.as_secs().wrapping_mul(0x9e37_79b9_7f4a_7c15))
581 })
582 .unwrap_or(0x1234_5678_9abc_def0);
583 ChaCha8Rng::seed_from_u64(seed)
584 }
585 };
586
587 let mut cim = Self {
588 oscillators: Vec::new(),
589 couplings: Vec::new(),
590 rng,
591 current_time: 0.0,
592 evolution_history: Vec::new(),
593 energy_history: Vec::new(),
594 config,
595 };
596
597 cim.initialize_system()?;
598 Ok(cim)
599 }
600
601 fn initialize_system(&mut self) -> CimResult<()> {
603 for i in 0..self.config.num_oscillators {
605 let osc = OpticalParametricOscillator {
606 index: i,
607 amplitude: Complex::new(
608 self.rng.random_range(-0.1..0.1),
609 self.rng.random_range(-0.1..0.1),
610 ),
611 gain: 1.0,
612 loss: 0.1,
613 saturation: 1.0,
614 injection: Complex::new(0.0, 0.0),
615 threshold: 1.0,
616 };
617 self.oscillators.push(osc);
618 }
619
620 self.initialize_topology()?;
622
623 Ok(())
624 }
625
626 fn initialize_topology(&mut self) -> CimResult<()> {
628 self.couplings.clear();
629
630 let topology = self.config.topology.clone();
631 match topology {
632 NetworkTopology::FullyConnected => {
633 for i in 0..self.config.num_oscillators {
634 for j in (i + 1)..self.config.num_oscillators {
635 self.couplings.push(OpticalCoupling {
636 from: i,
637 to: j,
638 strength: Complex::new(0.1, 0.0),
639 delay: 0.0,
640 });
641 self.couplings.push(OpticalCoupling {
642 from: j,
643 to: i,
644 strength: Complex::new(0.1, 0.0),
645 delay: 0.0,
646 });
647 }
648 }
649 }
650
651 NetworkTopology::Ring => {
652 for i in 0..self.config.num_oscillators {
653 let j = (i + 1) % self.config.num_oscillators;
654 self.couplings.push(OpticalCoupling {
655 from: i,
656 to: j,
657 strength: Complex::new(0.2, 0.0),
658 delay: 0.0,
659 });
660 self.couplings.push(OpticalCoupling {
661 from: j,
662 to: i,
663 strength: Complex::new(0.2, 0.0),
664 delay: 0.0,
665 });
666 }
667 }
668
669 NetworkTopology::Lattice2D { width, height } => {
670 if width * height != self.config.num_oscillators {
671 return Err(CimError::TopologyError(
672 "Lattice dimensions don't match number of oscillators".to_string(),
673 ));
674 }
675
676 for i in 0..width {
678 for j in 0..height {
679 let idx = i * height + j;
680
681 if i + 1 < width {
683 let neighbor = (i + 1) * height + j;
684 self.add_bidirectional_coupling(idx, neighbor, 0.15);
685 }
686
687 if j + 1 < height {
689 let neighbor = i * height + (j + 1);
690 self.add_bidirectional_coupling(idx, neighbor, 0.15);
691 }
692 }
693 }
694 }
695
696 NetworkTopology::Random { connectivity } => {
697 let num_possible_edges =
698 self.config.num_oscillators * (self.config.num_oscillators - 1) / 2;
699 let num_edges = (num_possible_edges as f64 * connectivity) as usize;
700
701 let mut added_edges = std::collections::HashSet::new();
702
703 while added_edges.len() < num_edges {
704 let i = self.rng.random_range(0..self.config.num_oscillators);
705 let j = self.rng.random_range(0..self.config.num_oscillators);
706
707 if i != j {
708 let edge = if i < j { (i, j) } else { (j, i) };
709 if added_edges.insert(edge) {
710 let strength = self.rng.random_range(0.05..0.25);
711 self.add_bidirectional_coupling(edge.0, edge.1, strength);
712 }
713 }
714 }
715 }
716
717 NetworkTopology::SmallWorld {
718 ring_connectivity,
719 rewiring_probability,
720 } => {
721 for i in 0..self.config.num_oscillators {
723 for k in 1..=ring_connectivity {
724 let j = (i + k) % self.config.num_oscillators;
725
726 if self.rng.random_bool(rewiring_probability) {
728 let new_target = self.rng.random_range(0..self.config.num_oscillators);
729 if new_target != i {
730 self.add_bidirectional_coupling(i, new_target, 0.1);
731 }
732 } else {
733 self.add_bidirectional_coupling(i, j, 0.1);
734 }
735 }
736 }
737 }
738
739 NetworkTopology::Custom { couplings } => {
740 self.couplings = couplings;
741 }
742 }
743
744 Ok(())
745 }
746
747 fn add_bidirectional_coupling(&mut self, i: usize, j: usize, strength: f64) {
749 self.couplings.push(OpticalCoupling {
750 from: i,
751 to: j,
752 strength: Complex::new(strength, 0.0),
753 delay: 0.0,
754 });
755 self.couplings.push(OpticalCoupling {
756 from: j,
757 to: i,
758 strength: Complex::new(strength, 0.0),
759 delay: 0.0,
760 });
761 }
762
763 pub fn solve(&mut self, problem: &IsingModel) -> CimResult<CimResults> {
765 if problem.num_qubits != self.config.num_oscillators {
766 return Err(CimError::SimulationError(format!(
767 "Problem size {} doesn't match CIM size {}",
768 problem.num_qubits, self.config.num_oscillators
769 )));
770 }
771
772 println!("Starting coherent Ising machine simulation...");
773 let start_time = Instant::now();
774
775 self.map_ising_to_optical(problem)?;
777
778 self.current_time = 0.0;
780 self.evolution_history.clear();
781 self.energy_history.clear();
782
783 let num_steps = (self.config.total_time / self.config.dt) as usize;
785 let mut best_energy = f64::INFINITY;
786 let mut best_solution = vec![0; problem.num_qubits];
787 let mut converged = false;
788 let mut convergence_time = self.config.total_time;
789
790 for step in 0..num_steps {
791 self.current_time = step as f64 * self.config.dt;
792
793 let pump_power = self.get_pump_power(self.current_time);
795 self.update_pump_power(pump_power);
796
797 self.evolve_system()?;
799
800 self.add_noise();
802
803 if step % 100 == 0 || step == num_steps - 1 {
805 self.record_state();
806 }
807
808 let current_solution = self.measure_solution()?;
810 let current_energy = self.evaluate_energy(problem, ¤t_solution)?;
811 self.energy_history.push(current_energy);
812
813 if current_energy < best_energy {
815 best_energy = current_energy;
816 best_solution = current_solution;
817 }
818
819 if !converged && self.check_convergence()? {
821 converged = true;
822 convergence_time = self.current_time;
823 if self.config.detailed_logging {
824 println!("Converged at time {convergence_time:.3}");
825 }
826 }
827
828 if step % 1000 == 0 && self.config.detailed_logging {
830 println!(
831 "Step {}: Time = {:.3}, Energy = {:.6}, Power = {:.3}",
832 step, self.current_time, current_energy, pump_power
833 );
834 }
835 }
836
837 let total_simulation_time = start_time.elapsed();
838
839 let optical_stats = self.calculate_optical_statistics();
841 let performance_metrics = self.calculate_performance_metrics(best_energy, convergence_time);
842
843 let results = CimResults {
845 best_solution,
846 best_energy,
847 final_optical_state: self.oscillators.iter().map(|osc| osc.amplitude).collect(),
848 energy_history: self.energy_history.clone(),
849 optical_evolution: self
850 .evolution_history
851 .iter()
852 .map(|(_, state)| state.clone())
853 .collect(),
854 time_points: self.evolution_history.iter().map(|(t, _)| *t).collect(),
855 converged,
856 convergence_time,
857 total_simulation_time,
858 optical_stats,
859 performance_metrics,
860 };
861
862 println!("CIM simulation completed in {total_simulation_time:.2?}");
863 println!("Best energy: {best_energy:.6}, Converged: {converged}");
864
865 Ok(results)
866 }
867
868 fn map_ising_to_optical(&mut self, problem: &IsingModel) -> CimResult<()> {
870 for i in 0..problem.num_qubits {
872 let bias = problem.get_bias(i).unwrap_or(0.0);
873 self.oscillators[i].injection = Complex::new(bias * 0.1, 0.0);
874 }
875
876 for coupling in &mut self.couplings {
878 if let Ok(ising_coupling) = problem.get_coupling(coupling.from, coupling.to) {
879 if ising_coupling != 0.0 {
880 let optical_strength = ising_coupling * 0.1;
882 coupling.strength = Complex::new(optical_strength, 0.0);
883 }
884 }
885 }
886
887 Ok(())
888 }
889
890 fn get_pump_power(&self, time: f64) -> f64 {
892 let normalized_time = time / self.config.total_time;
893
894 match &self.config.pump_schedule {
895 PumpSchedule::Linear {
896 initial_power,
897 final_power,
898 } => initial_power + (final_power - initial_power) * normalized_time,
899
900 PumpSchedule::Exponential {
901 initial_power,
902 final_power,
903 time_constant,
904 } => {
905 initial_power
906 + (final_power - initial_power) * (1.0 - (-time / time_constant).exp())
907 }
908
909 PumpSchedule::Sigmoid {
910 initial_power,
911 final_power,
912 steepness,
913 midpoint,
914 } => {
915 let sigmoid = 1.0 / (1.0 + (-(normalized_time - midpoint) * steepness).exp());
916 initial_power + (final_power - initial_power) * sigmoid
917 }
918
919 PumpSchedule::Custom { power_function } => power_function(time),
920 }
921 }
922
923 fn update_pump_power(&mut self, pump_power: f64) {
925 for osc in &mut self.oscillators {
926 osc.gain = pump_power;
927 }
928 }
929
930 fn evolve_system(&mut self) -> CimResult<()> {
932 let dt = self.config.dt;
933 let mut new_amplitudes = Vec::new();
934
935 for i in 0..self.oscillators.len() {
937 let osc = &self.oscillators[i];
938 let mut derivative = Complex::new(0.0, 0.0);
939
940 let power = osc.amplitude.magnitude_squared();
942 if osc.gain > osc.threshold {
943 let net_gain = osc.gain - osc.threshold - osc.loss;
944 derivative = derivative + osc.amplitude * net_gain * (1.0 - power / osc.saturation);
945 } else {
946 derivative = derivative + osc.amplitude * (-osc.loss);
948 }
949
950 derivative = derivative + osc.injection;
952
953 for coupling in &self.couplings {
955 if coupling.to == i {
956 let source_amplitude = self.oscillators[coupling.from].amplitude;
957 derivative = derivative + source_amplitude * coupling.strength;
958 }
959 }
960
961 let new_amplitude = osc.amplitude + derivative * dt;
963 new_amplitudes.push(new_amplitude);
964 }
965
966 for (i, new_amplitude) in new_amplitudes.into_iter().enumerate() {
968 self.oscillators[i].amplitude = new_amplitude;
969 }
970
971 Ok(())
972 }
973
974 fn add_noise(&mut self) {
976 let noise_config = &self.config.noise_config;
977
978 for osc in &mut self.oscillators {
979 let quantum_noise_re = self.rng.random_range(-1.0..1.0) * noise_config.quantum_noise;
981 let quantum_noise_im = self.rng.random_range(-1.0..1.0) * noise_config.quantum_noise;
982
983 let phase_noise = self.rng.random_range(-1.0..1.0) * noise_config.phase_noise;
985 let current_phase = osc.amplitude.phase();
986 let new_phase = current_phase + phase_noise;
987
988 let amplitude_noise = self
990 .rng
991 .random_range(-1.0_f64..1.0)
992 .mul_add(noise_config.amplitude_noise, 1.0);
993 let current_magnitude = osc.amplitude.magnitude();
994 let new_magnitude = current_magnitude * amplitude_noise;
995
996 osc.amplitude = Complex::from_polar(new_magnitude, new_phase)
998 + Complex::new(quantum_noise_re, quantum_noise_im);
999
1000 osc.amplitude =
1002 osc.amplitude * noise_config.decoherence_rate.mul_add(-self.config.dt, 1.0);
1003 }
1004 }
1005
1006 fn record_state(&mut self) {
1008 let state: Vec<Complex> = self.oscillators.iter().map(|osc| osc.amplitude).collect();
1009 self.evolution_history.push((self.current_time, state));
1010 }
1011
1012 fn measure_solution(&self) -> CimResult<Vec<i8>> {
1014 let mut solution = Vec::new();
1015
1016 for osc in &self.oscillators {
1017 let measurement =
1019 osc.amplitude.re * self.config.measurement_config.detection_efficiency;
1020
1021 let spin = if measurement > self.config.measurement_config.decision_threshold {
1023 1
1024 } else {
1025 -1
1026 };
1027
1028 solution.push(spin);
1029 }
1030
1031 Ok(solution)
1032 }
1033
1034 fn evaluate_energy(&self, problem: &IsingModel, solution: &[i8]) -> CimResult<f64> {
1036 let mut energy = 0.0;
1037
1038 for i in 0..solution.len() {
1040 energy += problem.get_bias(i).unwrap_or(0.0) * f64::from(solution[i]);
1041 }
1042
1043 for i in 0..solution.len() {
1045 for j in (i + 1)..solution.len() {
1046 energy += problem.get_coupling(i, j).unwrap_or(0.0)
1047 * f64::from(solution[i])
1048 * f64::from(solution[j]);
1049 }
1050 }
1051
1052 Ok(energy)
1053 }
1054
1055 fn check_convergence(&self) -> CimResult<bool> {
1057 if self.energy_history.len() < 100 {
1058 return Ok(false);
1059 }
1060
1061 let recent_window = 50;
1063 let recent_energies = &self.energy_history[self.energy_history.len() - recent_window..];
1064 let energy_variance = {
1065 let mean = recent_energies.iter().sum::<f64>() / recent_energies.len() as f64;
1066 recent_energies
1067 .iter()
1068 .map(|&e| (e - mean).powi(2))
1069 .sum::<f64>()
1070 / recent_energies.len() as f64
1071 };
1072
1073 let energy_stable = energy_variance < self.config.convergence_config.energy_tolerance;
1074
1075 let oscillation_stable = self.oscillators.iter().all(|osc| {
1077 osc.amplitude.magnitude() > self.config.convergence_config.oscillation_threshold
1078 });
1079
1080 Ok(energy_stable && oscillation_stable)
1081 }
1082
1083 fn calculate_optical_statistics(&self) -> OpticalStatistics {
1085 let num_oscillators = self.oscillators.len();
1086
1087 let total_power: f64 = self
1089 .oscillators
1090 .iter()
1091 .map(|osc| osc.amplitude.magnitude_squared())
1092 .sum();
1093 let average_power = total_power / num_oscillators as f64;
1094
1095 let power_variance = {
1097 let powers: Vec<f64> = self
1098 .oscillators
1099 .iter()
1100 .map(|osc| osc.amplitude.magnitude_squared())
1101 .collect();
1102 let mean = powers.iter().sum::<f64>() / powers.len() as f64;
1103 powers.iter().map(|&p| (p - mean).powi(2)).sum::<f64>() / powers.len() as f64
1104 };
1105
1106 let phase_coherence: Vec<f64> = self
1108 .oscillators
1109 .iter()
1110 .map(|osc| osc.amplitude.magnitude().min(1.0))
1111 .collect();
1112
1113 let mut cross_correlations = vec![vec![0.0; num_oscillators]; num_oscillators];
1115 for i in 0..num_oscillators {
1116 for j in 0..num_oscillators {
1117 if i == j {
1118 cross_correlations[i][j] = 1.0;
1119 } else {
1120 let phase_diff = (self.oscillators[i].amplitude.phase()
1122 - self.oscillators[j].amplitude.phase())
1123 .abs();
1124 cross_correlations[i][j] = (phase_diff / std::f64::consts::PI).cos().abs();
1125 }
1126 }
1127 }
1128
1129 let oscillation_frequencies = vec![1.0; num_oscillators]; OpticalStatistics {
1133 average_power,
1134 power_variance,
1135 phase_coherence,
1136 cross_correlations,
1137 oscillation_frequencies,
1138 pump_efficiency: 0.8, }
1140 }
1141
1142 fn calculate_performance_metrics(
1144 &self,
1145 best_energy: f64,
1146 convergence_time: f64,
1147 ) -> CimPerformanceMetrics {
1148 let solution_quality = 1.0; let time_to_convergence = convergence_time / self.config.total_time;
1153
1154 let average_power = self
1156 .oscillators
1157 .iter()
1158 .map(|osc| osc.amplitude.magnitude_squared())
1159 .sum::<f64>()
1160 / self.oscillators.len() as f64;
1161 let power_efficiency = 1.0 / (1.0 + average_power);
1162
1163 CimPerformanceMetrics {
1164 solution_quality,
1165 time_to_convergence,
1166 phase_transitions: 0, power_efficiency,
1168 noise_resilience: 0.8, }
1170 }
1171}
1172
1173#[must_use]
1177pub fn create_standard_cim_config(num_oscillators: usize, simulation_time: f64) -> CimConfig {
1178 CimConfig {
1179 num_oscillators,
1180 dt: 0.001,
1181 total_time: simulation_time,
1182 pump_schedule: PumpSchedule::Linear {
1183 initial_power: 0.5,
1184 final_power: 1.5,
1185 },
1186 topology: NetworkTopology::FullyConnected,
1187 ..Default::default()
1188 }
1189}
1190
1191#[must_use]
1193pub fn create_low_noise_cim_config(num_oscillators: usize) -> CimConfig {
1194 let mut config = create_standard_cim_config(num_oscillators, 5.0);
1195 config.noise_config = NoiseConfig {
1196 quantum_noise: 0.001,
1197 phase_noise: 0.0001,
1198 amplitude_noise: 0.0001,
1199 temperature: 0.001,
1200 decoherence_rate: 0.0001,
1201 };
1202 config
1203}
1204
1205#[must_use]
1207pub fn create_realistic_cim_config(num_oscillators: usize) -> CimConfig {
1208 let mut config = create_standard_cim_config(num_oscillators, 10.0);
1209 config.noise_config = NoiseConfig {
1210 quantum_noise: 0.05,
1211 phase_noise: 0.01,
1212 amplitude_noise: 0.01,
1213 temperature: 0.1,
1214 decoherence_rate: 0.01,
1215 };
1216 config.detailed_logging = true;
1217 config
1218}
1219
1220#[cfg(test)]
1221mod tests {
1222 use super::*;
1223
1224 #[test]
1225 fn test_complex_operations() {
1226 let c1 = Complex::new(3.0, 4.0);
1227 let c2 = Complex::new(1.0, 2.0);
1228
1229 assert_eq!(c1.magnitude(), 5.0);
1230 assert!((c1.phase() - 4.0_f64.atan2(3.0)).abs() < 1e-10);
1231
1232 let sum = c1 + c2;
1233 assert_eq!(sum.re, 4.0);
1234 assert_eq!(sum.im, 6.0);
1235
1236 let product = c1 * c2;
1237 assert_eq!(product.re, -5.0); assert_eq!(product.im, 10.0); }
1240
1241 #[test]
1242 fn test_cim_config_creation() {
1243 let config = create_standard_cim_config(4, 5.0);
1244 assert_eq!(config.num_oscillators, 4);
1245 assert_eq!(config.total_time, 5.0);
1246
1247 match config.topology {
1248 NetworkTopology::FullyConnected => {}
1249 _ => panic!("Expected fully connected topology"),
1250 }
1251 }
1252
1253 #[test]
1254 fn test_optical_oscillator() {
1255 let osc = OpticalParametricOscillator {
1256 index: 0,
1257 amplitude: Complex::new(1.0, 0.0),
1258 gain: 1.5,
1259 loss: 0.1,
1260 saturation: 1.0,
1261 injection: Complex::new(0.1, 0.0),
1262 threshold: 1.0,
1263 };
1264
1265 assert_eq!(osc.amplitude.magnitude(), 1.0);
1266 assert_eq!(osc.gain, 1.5);
1267 assert!(osc.gain > osc.threshold);
1268 }
1269}