1use scirs2_core::ndarray::{Array1, Array2};
9use scirs2_core::Complex64;
10use serde::{Deserialize, Serialize};
11
12use crate::error::{Result, SimulatorError};
13use crate::scirs2_integration::SciRS2Backend;
14use crate::trotter::{Hamiltonian, HamiltonianTerm};
15
16#[derive(Debug, Clone)]
18pub struct AdiabaticConfig {
19 pub total_time: f64,
21 pub time_steps: usize,
23 pub schedule_type: ScheduleType,
25 pub initial_hamiltonian: Hamiltonian,
27 pub final_hamiltonian: Hamiltonian,
29 pub gap_tracking: GapTrackingConfig,
31 pub energy_tolerance: f64,
33 pub max_iterations: usize,
35 pub adaptive_stepping: bool,
37 pub monitor_diabatic_transitions: bool,
39}
40
41impl Default for AdiabaticConfig {
42 fn default() -> Self {
43 Self {
44 total_time: 100.0,
45 time_steps: 1000,
46 schedule_type: ScheduleType::Linear,
47 initial_hamiltonian: Hamiltonian::new(1), final_hamiltonian: Hamiltonian::new(1),
49 gap_tracking: GapTrackingConfig::default(),
50 energy_tolerance: 1e-12,
51 max_iterations: 1000,
52 adaptive_stepping: true,
53 monitor_diabatic_transitions: false,
54 }
55 }
56}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60pub enum ScheduleType {
61 Linear,
63 Quadratic,
65 Cubic,
67 Exponential,
69 Optimal,
71 Polynomial(u32),
73 LandauZener,
75}
76
77#[derive(Debug, Clone)]
79pub struct GapTrackingConfig {
80 pub enabled: bool,
82 pub min_gap_threshold: f64,
84 pub num_eigenvalues: usize,
86 pub smoothing_window: usize,
88 pub detect_diabatic_transitions: bool,
90 pub lookahead_steps: usize,
92}
93
94impl Default for GapTrackingConfig {
95 fn default() -> Self {
96 Self {
97 enabled: true,
98 min_gap_threshold: 1e-6,
99 num_eigenvalues: 10,
100 smoothing_window: 5,
101 detect_diabatic_transitions: true,
102 lookahead_steps: 3,
103 }
104 }
105}
106
107pub struct AdiabaticQuantumComputer {
109 config: AdiabaticConfig,
111 state: Array1<Complex64>,
113 current_time: f64,
115 evolution_history: Vec<AdiabaticSnapshot>,
117 gap_history: Vec<GapMeasurement>,
119 backend: Option<SciRS2Backend>,
121 stats: AdiabaticStats,
123}
124
125#[derive(Debug, Clone)]
127pub struct AdiabaticSnapshot {
128 pub time: f64,
130 pub schedule_parameter: f64,
132 pub state: Array1<Complex64>,
134 pub energy: f64,
136 pub gap: Option<f64>,
138 pub instantaneous_ground_state: Option<Array1<Complex64>>,
140 pub ground_state_fidelity: Option<f64>,
142 pub adiabatic_parameter: Option<f64>,
144}
145
146#[derive(Debug, Clone)]
148pub struct GapMeasurement {
149 pub time: f64,
151 pub schedule_parameter: f64,
153 pub gap: f64,
155 pub ground_energy: f64,
157 pub first_excited_energy: f64,
159 pub gap_derivative: Option<f64>,
161 pub predicted_min_gap: Option<f64>,
163}
164
165#[derive(Debug, Clone, Default, Serialize, Deserialize)]
167pub struct AdiabaticStats {
168 pub total_evolution_time_ms: f64,
170 pub steps_completed: usize,
172 pub eigenvalue_computations: usize,
174 pub avg_eigenvalue_time_ms: f64,
176 pub min_gap: f64,
178 pub max_gap: f64,
180 pub avg_gap: f64,
182 pub diabatic_transitions: usize,
184 pub final_ground_state_fidelity: f64,
186 pub success_probability: f64,
188}
189
190impl AdiabaticQuantumComputer {
191 pub fn new(config: AdiabaticConfig) -> Result<Self> {
193 let num_qubits = config.initial_hamiltonian.get_num_qubits();
195 let state_size = 1 << num_qubits;
196
197 let mut state = Array1::zeros(state_size);
198 state[0] = Complex64::new(1.0, 0.0); Ok(Self {
201 config,
202 state,
203 current_time: 0.0,
204 evolution_history: Vec::new(),
205 gap_history: Vec::new(),
206 backend: None,
207 stats: AdiabaticStats::default(),
208 })
209 }
210
211 pub fn with_backend(mut self) -> Result<Self> {
213 self.backend = Some(SciRS2Backend::new());
214 Ok(self)
215 }
216
217 pub fn initialize_ground_state(&mut self) -> Result<()> {
219 let initial_matrix = self.build_hamiltonian_matrix(&self.config.initial_hamiltonian)?;
220 let (eigenvalues, eigenvectors) = self.compute_eigendecomposition(&initial_matrix)?;
221
222 let ground_idx = eigenvalues
224 .iter()
225 .enumerate()
226 .min_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap())
227 .map(|(idx, _)| idx)
228 .unwrap_or(0);
229
230 self.state = eigenvectors.column(ground_idx).to_owned();
232
233 Ok(())
234 }
235
236 pub fn evolve(&mut self) -> Result<AdiabaticResult> {
238 let start_time = std::time::Instant::now();
239
240 self.initialize_ground_state()?;
242
243 let initial_snapshot = self.take_snapshot(0.0)?;
245 self.evolution_history.push(initial_snapshot);
246
247 let dt = self.config.total_time / self.config.time_steps as f64;
248
249 for step in 1..=self.config.time_steps {
250 let step_start = std::time::Instant::now();
251
252 let t = step as f64 * dt;
253 let s = self.schedule_function(t);
254
255 let actual_dt = if self.config.adaptive_stepping {
257 self.calculate_adaptive_timestep(t, dt)?
258 } else {
259 dt
260 };
261
262 let hamiltonian = self.interpolate_hamiltonian(s)?;
264
265 self.evolve_step(&hamiltonian, actual_dt)?;
267
268 if self.config.gap_tracking.enabled {
270 let gap_measurement = self.measure_gap(t, s, &hamiltonian)?;
271
272 if self.config.monitor_diabatic_transitions {
274 self.check_diabatic_transition(&gap_measurement)?;
275 }
276
277 self.gap_history.push(gap_measurement);
278 }
279
280 if step % 10 == 0 || step == self.config.time_steps {
282 let snapshot = self.take_snapshot(t)?;
283 self.evolution_history.push(snapshot);
284 }
285
286 self.current_time = t;
287 self.stats.steps_completed += 1;
288
289 let step_time = step_start.elapsed().as_secs_f64() * 1000.0;
290 println!(
291 "Step {}/{}: t={:.3}, s={:.3}, time={:.2}ms",
292 step, self.config.time_steps, t, s, step_time
293 );
294 }
295
296 self.compute_final_statistics()?;
298
299 let total_time = start_time.elapsed().as_secs_f64() * 1000.0;
300 self.stats.total_evolution_time_ms = total_time;
301
302 Ok(AdiabaticResult {
303 final_state: self.state.clone(),
304 evolution_history: self.evolution_history.clone(),
305 gap_history: self.gap_history.clone(),
306 total_time_ms: total_time,
307 success_probability: self.stats.success_probability,
308 min_gap: self.stats.min_gap,
309 final_energy: self.calculate_current_energy()?,
310 })
311 }
312
313 fn schedule_function(&self, t: f64) -> f64 {
315 let s = t / self.config.total_time;
316
317 match self.config.schedule_type {
318 ScheduleType::Linear => s,
319 ScheduleType::Quadratic => s * s,
320 ScheduleType::Cubic => s * s * s,
321 ScheduleType::Exponential => (s.exp() - 1.0) / (1f64.exp() - 1.0),
322 ScheduleType::Polynomial(n) => s.powi(n as i32),
323 ScheduleType::LandauZener => {
324 if s < 0.5 {
326 2.0 * s * s
327 } else {
328 1.0 - 2.0 * (1.0 - s) * (1.0 - s)
329 }
330 }
331 ScheduleType::Optimal => {
332 s }
335 }
336 }
337
338 fn interpolate_hamiltonian(&self, s: f64) -> Result<Hamiltonian> {
340 let num_qubits = self
341 .config
342 .initial_hamiltonian
343 .get_num_qubits()
344 .max(self.config.final_hamiltonian.get_num_qubits());
345 let mut interpolated = Hamiltonian::new(num_qubits);
346
347 for term in &self.config.initial_hamiltonian.terms {
349 let scaled_term = match term {
350 HamiltonianTerm::SinglePauli {
351 qubit,
352 pauli,
353 coefficient,
354 } => HamiltonianTerm::SinglePauli {
355 qubit: *qubit,
356 pauli: pauli.clone(),
357 coefficient: coefficient * (1.0 - s),
358 },
359 HamiltonianTerm::TwoPauli {
360 qubit1,
361 qubit2,
362 pauli1,
363 pauli2,
364 coefficient,
365 } => HamiltonianTerm::TwoPauli {
366 qubit1: *qubit1,
367 qubit2: *qubit2,
368 pauli1: pauli1.clone(),
369 pauli2: pauli2.clone(),
370 coefficient: coefficient * (1.0 - s),
371 },
372 HamiltonianTerm::PauliString {
373 qubits,
374 paulis,
375 coefficient,
376 } => HamiltonianTerm::PauliString {
377 qubits: qubits.clone(),
378 paulis: paulis.clone(),
379 coefficient: coefficient * (1.0 - s),
380 },
381 HamiltonianTerm::Custom {
382 qubits,
383 matrix,
384 coefficient,
385 } => HamiltonianTerm::Custom {
386 qubits: qubits.clone(),
387 matrix: matrix.clone(),
388 coefficient: coefficient * (1.0 - s),
389 },
390 };
391 interpolated.add_term(scaled_term);
392 }
393
394 for term in &self.config.final_hamiltonian.terms {
395 let scaled_term = match term {
396 HamiltonianTerm::SinglePauli {
397 qubit,
398 pauli,
399 coefficient,
400 } => HamiltonianTerm::SinglePauli {
401 qubit: *qubit,
402 pauli: pauli.clone(),
403 coefficient: coefficient * s,
404 },
405 HamiltonianTerm::TwoPauli {
406 qubit1,
407 qubit2,
408 pauli1,
409 pauli2,
410 coefficient,
411 } => HamiltonianTerm::TwoPauli {
412 qubit1: *qubit1,
413 qubit2: *qubit2,
414 pauli1: pauli1.clone(),
415 pauli2: pauli2.clone(),
416 coefficient: coefficient * s,
417 },
418 HamiltonianTerm::PauliString {
419 qubits,
420 paulis,
421 coefficient,
422 } => HamiltonianTerm::PauliString {
423 qubits: qubits.clone(),
424 paulis: paulis.clone(),
425 coefficient: coefficient * s,
426 },
427 HamiltonianTerm::Custom {
428 qubits,
429 matrix,
430 coefficient,
431 } => HamiltonianTerm::Custom {
432 qubits: qubits.clone(),
433 matrix: matrix.clone(),
434 coefficient: coefficient * s,
435 },
436 };
437 interpolated.add_term(scaled_term);
438 }
439
440 Ok(interpolated)
441 }
442
443 fn evolve_step(&mut self, hamiltonian: &Hamiltonian, dt: f64) -> Result<()> {
445 let h_matrix = self.build_hamiltonian_matrix(hamiltonian)?;
447
448 let evolution_operator = self.compute_evolution_operator(&h_matrix, dt)?;
450
451 self.state = evolution_operator.dot(&self.state);
453
454 let norm: f64 = self.state.iter().map(|x| x.norm_sqr()).sum::<f64>().sqrt();
456 if norm > 1e-15 {
457 self.state.mapv_inplace(|x| x / norm);
458 }
459
460 Ok(())
461 }
462
463 fn build_hamiltonian_matrix(&self, hamiltonian: &Hamiltonian) -> Result<Array2<Complex64>> {
465 let num_qubits = hamiltonian.get_num_qubits();
466 let dim = 1 << num_qubits;
467 let mut matrix = Array2::zeros((dim, dim));
468
469 for term in &hamiltonian.terms {
470 let (term_matrix, coefficient) = self.build_term_matrix(term, num_qubits)?;
471 matrix = matrix + term_matrix.mapv(|x| x * coefficient);
472 }
473
474 Ok(matrix)
475 }
476
477 fn build_term_matrix(
479 &self,
480 term: &HamiltonianTerm,
481 num_qubits: usize,
482 ) -> Result<(Array2<Complex64>, f64)> {
483 let dim = 1 << num_qubits;
484
485 match term {
486 HamiltonianTerm::SinglePauli {
487 qubit,
488 pauli,
489 coefficient,
490 } => {
491 let mut matrix = Array2::eye(dim);
492 let pauli_matrix = self.get_pauli_matrix(pauli)?;
493 matrix = self.apply_single_qubit_to_full_matrix(
494 &matrix,
495 &pauli_matrix,
496 *qubit,
497 num_qubits,
498 )?;
499 Ok((matrix, *coefficient))
500 }
501 HamiltonianTerm::TwoPauli {
502 qubit1,
503 qubit2,
504 pauli1,
505 pauli2,
506 coefficient,
507 } => {
508 let mut matrix = Array2::eye(dim);
509 let pauli1_matrix = self.get_pauli_matrix(pauli1)?;
510 let pauli2_matrix = self.get_pauli_matrix(pauli2)?;
511
512 matrix = self.apply_single_qubit_to_full_matrix(
513 &matrix,
514 &pauli1_matrix,
515 *qubit1,
516 num_qubits,
517 )?;
518 matrix = self.apply_single_qubit_to_full_matrix(
519 &matrix,
520 &pauli2_matrix,
521 *qubit2,
522 num_qubits,
523 )?;
524 Ok((matrix, *coefficient))
525 }
526 HamiltonianTerm::PauliString {
527 qubits,
528 paulis,
529 coefficient,
530 } => {
531 let mut matrix = Array2::eye(dim);
532
533 for (qubit, pauli) in qubits.iter().zip(paulis.iter()) {
534 let pauli_matrix = self.get_pauli_matrix(pauli)?;
535 matrix = self.apply_single_qubit_to_full_matrix(
536 &matrix,
537 &pauli_matrix,
538 *qubit,
539 num_qubits,
540 )?;
541 }
542 Ok((matrix, *coefficient))
543 }
544 HamiltonianTerm::Custom {
545 qubits: _,
546 matrix: _,
547 coefficient,
548 } => {
549 Ok((Array2::eye(dim), *coefficient))
551 }
552 }
553 }
554
555 fn get_pauli_matrix(&self, pauli: &str) -> Result<Array2<Complex64>> {
557 match pauli.to_uppercase().as_str() {
558 "I" => Ok(Array2::eye(2)),
559 "X" => Ok(Array2::from_shape_vec(
560 (2, 2),
561 vec![
562 Complex64::new(0.0, 0.0),
563 Complex64::new(1.0, 0.0),
564 Complex64::new(1.0, 0.0),
565 Complex64::new(0.0, 0.0),
566 ],
567 )
568 .unwrap()),
569 "Y" => Ok(Array2::from_shape_vec(
570 (2, 2),
571 vec![
572 Complex64::new(0.0, 0.0),
573 Complex64::new(0.0, -1.0),
574 Complex64::new(0.0, 1.0),
575 Complex64::new(0.0, 0.0),
576 ],
577 )
578 .unwrap()),
579 "Z" => Ok(Array2::from_shape_vec(
580 (2, 2),
581 vec![
582 Complex64::new(1.0, 0.0),
583 Complex64::new(0.0, 0.0),
584 Complex64::new(0.0, 0.0),
585 Complex64::new(-1.0, 0.0),
586 ],
587 )
588 .unwrap()),
589 _ => Err(SimulatorError::InvalidInput(format!(
590 "Unknown Pauli operator: {}",
591 pauli
592 ))),
593 }
594 }
595
596 fn apply_single_qubit_to_full_matrix(
598 &self,
599 full_matrix: &Array2<Complex64>,
600 single_qubit_op: &Array2<Complex64>,
601 target_qubit: usize,
602 num_qubits: usize,
603 ) -> Result<Array2<Complex64>> {
604 let dim = 1 << num_qubits;
605 let mut result = Array2::zeros((dim, dim));
606
607 for i in 0..dim {
608 for j in 0..dim {
609 let i_bit = (i >> target_qubit) & 1;
610 let j_bit = (j >> target_qubit) & 1;
611
612 result[[i, j]] = full_matrix[[i, j]] * single_qubit_op[[i_bit, j_bit]];
613 }
614 }
615
616 Ok(result)
617 }
618
619 fn compute_evolution_operator(
621 &self,
622 hamiltonian: &Array2<Complex64>,
623 dt: f64,
624 ) -> Result<Array2<Complex64>> {
625 let dim = hamiltonian.dim().0;
629 if dim <= 64 {
630 self.matrix_exponential(hamiltonian, -Complex64::new(0.0, dt))
632 } else {
633 self.trotter_evolution(hamiltonian, dt)
635 }
636 }
637
638 fn matrix_exponential(
640 &self,
641 matrix: &Array2<Complex64>,
642 factor: Complex64,
643 ) -> Result<Array2<Complex64>> {
644 let dim = matrix.dim().0;
645
646 let scaled_matrix = matrix.mapv(|x| x * factor);
648
649 let mut result = Array2::eye(dim);
651 let mut term = Array2::eye(dim);
652
653 for n in 1..=20 {
654 term = term.dot(&scaled_matrix) / (n as f64);
656 let term_norm: f64 = term.iter().map(|x| x.norm_sqr()).sum::<f64>().sqrt();
657
658 result = result + &term;
659
660 if term_norm < 1e-15 {
662 break;
663 }
664 }
665
666 Ok(result)
667 }
668
669 fn trotter_evolution(
671 &self,
672 hamiltonian: &Array2<Complex64>,
673 dt: f64,
674 ) -> Result<Array2<Complex64>> {
675 self.matrix_exponential(hamiltonian, -Complex64::new(0.0, dt))
677 }
678
679 fn measure_gap(&mut self, t: f64, s: f64, hamiltonian: &Hamiltonian) -> Result<GapMeasurement> {
681 let start_time = std::time::Instant::now();
682
683 let h_matrix = self.build_hamiltonian_matrix(hamiltonian)?;
684 let (eigenvalues, _) = self.compute_eigendecomposition(&h_matrix)?;
685
686 let mut sorted_eigenvalues = eigenvalues;
688 sorted_eigenvalues.sort_by(|a, b| a.partial_cmp(b).unwrap());
689
690 let ground_energy = sorted_eigenvalues[0];
691 let first_excited_energy = sorted_eigenvalues.get(1).copied().unwrap_or(ground_energy);
692 let gap = first_excited_energy - ground_energy;
693
694 if self.stats.min_gap == 0.0 || gap < self.stats.min_gap {
696 self.stats.min_gap = gap;
697 }
698 if gap > self.stats.max_gap {
699 self.stats.max_gap = gap;
700 }
701
702 let gap_count = self.gap_history.len() as f64;
703 self.stats.avg_gap = (self.stats.avg_gap * gap_count + gap) / (gap_count + 1.0);
704
705 let computation_time = start_time.elapsed().as_secs_f64() * 1000.0;
706 self.stats.avg_eigenvalue_time_ms = (self.stats.avg_eigenvalue_time_ms
707 * self.stats.eigenvalue_computations as f64
708 + computation_time)
709 / (self.stats.eigenvalue_computations + 1) as f64;
710 self.stats.eigenvalue_computations += 1;
711
712 Ok(GapMeasurement {
713 time: t,
714 schedule_parameter: s,
715 gap,
716 ground_energy,
717 first_excited_energy,
718 gap_derivative: self.estimate_gap_derivative(gap),
719 predicted_min_gap: self.predict_minimum_gap(),
720 })
721 }
722
723 fn estimate_gap_derivative(&self, current_gap: f64) -> Option<f64> {
725 if self.gap_history.len() < 2 {
726 return None;
727 }
728
729 let prev_gap = self.gap_history.last().unwrap().gap;
730 let prev_time = self.gap_history.last().unwrap().time;
731 let dt = self.current_time - prev_time;
732
733 if dt > 1e-15 {
734 Some((current_gap - prev_gap) / dt)
735 } else {
736 None
737 }
738 }
739
740 fn predict_minimum_gap(&self) -> Option<f64> {
742 if self.gap_history.len() < 3 {
743 return None;
744 }
745
746 let n = self.gap_history.len();
748 let recent_gaps: Vec<f64> = self.gap_history[n - 3..].iter().map(|g| g.gap).collect();
749 let recent_times: Vec<f64> = self.gap_history[n - 3..].iter().map(|g| g.time).collect();
750
751 recent_gaps.into_iter().fold(f64::INFINITY, f64::min).into()
754 }
755
756 fn check_diabatic_transition(&mut self, gap_measurement: &GapMeasurement) -> Result<()> {
758 if let Some(gap_derivative) = gap_measurement.gap_derivative {
760 let gap = gap_measurement.gap;
761 let dt = self.config.total_time / self.config.time_steps as f64;
762
763 let diabatic_prob = if gap_derivative.abs() > 1e-15 {
765 (-2.0 * std::f64::consts::PI * gap * gap / gap_derivative.abs()).exp()
766 } else {
767 0.0
768 };
769
770 if diabatic_prob > 0.01 {
772 self.stats.diabatic_transitions += 1;
774 println!(
775 "Warning: Potential diabatic transition detected at t={:.3}, P_diabatic={:.4}",
776 gap_measurement.time, diabatic_prob
777 );
778 }
779 }
780
781 Ok(())
782 }
783
784 fn calculate_adaptive_timestep(&self, t: f64, default_dt: f64) -> Result<f64> {
786 if self.gap_history.is_empty() {
787 return Ok(default_dt);
788 }
789
790 let current_gap = self.gap_history.last().unwrap().gap;
791
792 let gap_factor = (current_gap / self.config.gap_tracking.min_gap_threshold).sqrt();
794 let adaptive_dt = default_dt * gap_factor.min(2.0).max(0.1); Ok(adaptive_dt)
797 }
798
799 fn take_snapshot(&mut self, t: f64) -> Result<AdiabaticSnapshot> {
801 let s = self.schedule_function(t);
802 let energy = self.calculate_current_energy()?;
803
804 let gap = self.gap_history.last().map(|g| g.gap);
806
807 let (instantaneous_ground_state, ground_state_fidelity, adiabatic_parameter) =
809 if self.config.gap_tracking.enabled {
810 let hamiltonian = self.interpolate_hamiltonian(s)?;
811 let h_matrix = self.build_hamiltonian_matrix(&hamiltonian)?;
812 let (eigenvalues, eigenvectors) = self.compute_eigendecomposition(&h_matrix)?;
813
814 let ground_idx = eigenvalues
816 .iter()
817 .enumerate()
818 .min_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap())
819 .map(|(idx, _)| idx)
820 .unwrap_or(0);
821
822 let ground_state = eigenvectors.column(ground_idx).to_owned();
823
824 let fidelity = self.calculate_fidelity(&self.state, &ground_state);
826
827 let adiabatic_param = gap.map(|g| g * g * self.config.total_time);
829
830 (Some(ground_state), Some(fidelity), adiabatic_param)
831 } else {
832 (None, None, None)
833 };
834
835 Ok(AdiabaticSnapshot {
836 time: t,
837 schedule_parameter: s,
838 state: self.state.clone(),
839 energy,
840 gap,
841 instantaneous_ground_state,
842 ground_state_fidelity,
843 adiabatic_parameter,
844 })
845 }
846
847 fn calculate_current_energy(&self) -> Result<f64> {
849 let s = self.schedule_function(self.current_time);
850 let hamiltonian = self.interpolate_hamiltonian(s)?;
851 let h_matrix = self.build_hamiltonian_matrix(&hamiltonian)?;
852
853 let h_psi = h_matrix.dot(&self.state);
855 let energy: Complex64 = self
856 .state
857 .iter()
858 .zip(h_psi.iter())
859 .map(|(psi, h_psi)| psi.conj() * h_psi)
860 .sum();
861
862 Ok(energy.re)
863 }
864
865 fn calculate_fidelity(&self, state1: &Array1<Complex64>, state2: &Array1<Complex64>) -> f64 {
867 let overlap: Complex64 = state1
868 .iter()
869 .zip(state2.iter())
870 .map(|(a, b)| a.conj() * b)
871 .sum();
872
873 overlap.norm_sqr()
874 }
875
876 fn compute_eigendecomposition(
878 &self,
879 matrix: &Array2<Complex64>,
880 ) -> Result<(Vec<f64>, Array2<Complex64>)> {
881 let dim = matrix.dim().0;
885 if dim > 16 {
886 return self.compute_approximate_eigenvalues(matrix);
888 }
889
890 let mut eigenvalues = Vec::new();
892 let mut eigenvectors = Array2::eye(dim);
893
894 for i in 0..dim {
896 eigenvalues.push(matrix[[i, i]].re);
897 }
898
899 eigenvalues.sort_by(|a, b| a.partial_cmp(b).unwrap());
901
902 Ok((eigenvalues, eigenvectors))
903 }
904
905 fn compute_approximate_eigenvalues(
907 &self,
908 matrix: &Array2<Complex64>,
909 ) -> Result<(Vec<f64>, Array2<Complex64>)> {
910 let dim = matrix.dim().0;
911
912 let mut eigenvalues = Vec::new();
915 for i in 0..dim.min(self.config.gap_tracking.num_eigenvalues) {
916 eigenvalues.push(matrix[[i, i]].re);
917 }
918
919 eigenvalues.sort_by(|a, b| a.partial_cmp(b).unwrap());
920 let eigenvectors = Array2::eye(dim);
921
922 Ok((eigenvalues, eigenvectors))
923 }
924
925 fn compute_final_statistics(&mut self) -> Result<()> {
927 if let Some(final_snapshot) = self.evolution_history.last() {
929 if let Some(fidelity) = final_snapshot.ground_state_fidelity {
930 self.stats.final_ground_state_fidelity = fidelity;
931 }
932 }
933
934 self.stats.success_probability = self.stats.final_ground_state_fidelity;
937
938 Ok(())
939 }
940
941 pub fn get_state(&self) -> &Array1<Complex64> {
943 &self.state
944 }
945
946 pub fn get_evolution_history(&self) -> &[AdiabaticSnapshot] {
948 &self.evolution_history
949 }
950
951 pub fn get_gap_history(&self) -> &[GapMeasurement] {
953 &self.gap_history
954 }
955
956 pub fn get_stats(&self) -> &AdiabaticStats {
958 &self.stats
959 }
960
961 pub fn reset(&mut self) -> Result<()> {
963 let num_qubits = self.config.initial_hamiltonian.get_num_qubits();
964 let state_size = 1 << num_qubits;
965
966 self.state = Array1::zeros(state_size);
967 self.state[0] = Complex64::new(1.0, 0.0);
968 self.current_time = 0.0;
969 self.evolution_history.clear();
970 self.gap_history.clear();
971 self.stats = AdiabaticStats::default();
972
973 Ok(())
974 }
975}
976
977#[derive(Debug, Clone)]
979pub struct AdiabaticResult {
980 pub final_state: Array1<Complex64>,
982 pub evolution_history: Vec<AdiabaticSnapshot>,
984 pub gap_history: Vec<GapMeasurement>,
986 pub total_time_ms: f64,
988 pub success_probability: f64,
990 pub min_gap: f64,
992 pub final_energy: f64,
994}
995
996pub struct AdiabaticUtils;
998
999impl AdiabaticUtils {
1000 pub fn create_max_cut_hamiltonian(
1002 graph_edges: &[(usize, usize)],
1003 weights: &[f64],
1004 ) -> Hamiltonian {
1005 let max_vertex = graph_edges
1006 .iter()
1007 .flat_map(|&(u, v)| [u, v])
1008 .max()
1009 .unwrap_or(0)
1010 + 1;
1011 let mut hamiltonian = Hamiltonian::new(max_vertex);
1012
1013 for (i, &(u, v)) in graph_edges.iter().enumerate() {
1014 let weight = weights.get(i).copied().unwrap_or(1.0);
1015
1016 hamiltonian
1021 .add_two_pauli(u, v, "Z", "Z", weight / 2.0)
1022 .unwrap();
1023 }
1024
1025 hamiltonian
1026 }
1027
1028 pub fn create_3sat_hamiltonian(clauses: &[Vec<i32>]) -> Hamiltonian {
1030 let max_var = clauses
1031 .iter()
1032 .flat_map(|clause| clause.iter())
1033 .map(|&lit| lit.abs() as usize)
1034 .max()
1035 .unwrap_or(0)
1036 + 1;
1037 let mut hamiltonian = Hamiltonian::new(max_var);
1038
1039 for clause in clauses {
1040 if clause.len() != 3 {
1041 continue; }
1043
1044 for i in 0..clause.len() {
1050 for j in i + 1..clause.len() {
1051 let var1 = clause[i].abs() as usize;
1052 let var2 = clause[j].abs() as usize;
1053
1054 if var1 < max_var && var2 < max_var {
1056 hamiltonian
1057 .add_two_pauli(var1, var2, "Z", "Z", 0.1)
1058 .unwrap();
1059 }
1060 }
1061 }
1062 }
1063
1064 hamiltonian
1065 }
1066
1067 pub fn create_tfim_hamiltonian(
1069 num_qubits: usize,
1070 j_coupling: f64,
1071 h_field: f64,
1072 ) -> Hamiltonian {
1073 let mut hamiltonian = Hamiltonian::new(num_qubits);
1074
1075 for i in 0..num_qubits - 1 {
1077 hamiltonian
1078 .add_two_pauli(i, i + 1, "Z", "Z", -j_coupling)
1079 .unwrap();
1080 }
1081
1082 for i in 0..num_qubits {
1084 hamiltonian.add_single_pauli(i, "X", -h_field).unwrap();
1085 }
1086
1087 hamiltonian
1088 }
1089
1090 pub fn create_mixing_hamiltonian(num_qubits: usize) -> Hamiltonian {
1092 let mut hamiltonian = Hamiltonian::new(num_qubits);
1093
1094 for i in 0..num_qubits {
1095 hamiltonian.add_single_pauli(i, "X", 1.0).unwrap();
1096 }
1097
1098 hamiltonian
1099 }
1100
1101 pub fn benchmark_adiabatic_qc() -> Result<AdiabaticBenchmarkResults> {
1103 let mut results = AdiabaticBenchmarkResults::default();
1104
1105 let problem_sizes = vec![4, 6, 8];
1107 let schedule_types = vec![
1108 ScheduleType::Linear,
1109 ScheduleType::Quadratic,
1110 ScheduleType::LandauZener,
1111 ];
1112
1113 for &num_qubits in &problem_sizes {
1114 for &schedule_type in &schedule_types {
1115 let initial_h = Self::create_mixing_hamiltonian(num_qubits);
1117 let final_h = Self::create_tfim_hamiltonian(num_qubits, 1.0, 0.1);
1118
1119 let config = AdiabaticConfig {
1120 total_time: 10.0,
1121 time_steps: 100,
1122 schedule_type,
1123 initial_hamiltonian: initial_h,
1124 final_hamiltonian: final_h,
1125 gap_tracking: GapTrackingConfig {
1126 enabled: true,
1127 min_gap_threshold: 1e-3,
1128 ..Default::default()
1129 },
1130 ..Default::default()
1131 };
1132
1133 let mut adiabatic_qc = AdiabaticQuantumComputer::new(config)?;
1134
1135 let start = std::time::Instant::now();
1136 let result = adiabatic_qc.evolve()?;
1137 let execution_time = start.elapsed().as_secs_f64() * 1000.0;
1138
1139 let key = format!("{}q_{:?}", num_qubits, schedule_type);
1140 results.execution_times.push((key.clone(), execution_time));
1141 results
1142 .success_probabilities
1143 .push((key.clone(), result.success_probability));
1144 results.min_gaps.push((key, result.min_gap));
1145 }
1146 }
1147
1148 Ok(results)
1149 }
1150}
1151
1152#[derive(Debug, Clone, Default)]
1154pub struct AdiabaticBenchmarkResults {
1155 pub execution_times: Vec<(String, f64)>,
1157 pub success_probabilities: Vec<(String, f64)>,
1159 pub min_gaps: Vec<(String, f64)>,
1161}
1162
1163#[cfg(test)]
1164mod tests {
1165 use super::*;
1166 use approx::assert_abs_diff_eq;
1167
1168 #[test]
1169 fn test_adiabatic_qc_creation() {
1170 let config = AdiabaticConfig::default();
1171 let adiabatic_qc = AdiabaticQuantumComputer::new(config);
1172 assert!(adiabatic_qc.is_ok());
1173 }
1174
1175 #[test]
1176 fn test_schedule_functions() {
1177 let config = AdiabaticConfig {
1178 total_time: 10.0,
1179 schedule_type: ScheduleType::Linear,
1180 ..Default::default()
1181 };
1182 let adiabatic_qc = AdiabaticQuantumComputer::new(config).unwrap();
1183
1184 assert_abs_diff_eq!(adiabatic_qc.schedule_function(0.0), 0.0, epsilon = 1e-10);
1185 assert_abs_diff_eq!(adiabatic_qc.schedule_function(5.0), 0.5, epsilon = 1e-10);
1186 assert_abs_diff_eq!(adiabatic_qc.schedule_function(10.0), 1.0, epsilon = 1e-10);
1187 }
1188
1189 #[test]
1190 fn test_hamiltonian_interpolation() {
1191 let mut initial_h = Hamiltonian::new(1);
1192 initial_h.add_pauli_term(1.0, &[(0, 'X')]).unwrap();
1193
1194 let mut final_h = Hamiltonian::new(1);
1195 final_h.add_pauli_term(1.0, &[(0, 'Z')]).unwrap();
1196
1197 let config = AdiabaticConfig {
1198 initial_hamiltonian: initial_h,
1199 final_hamiltonian: final_h,
1200 ..Default::default()
1201 };
1202
1203 let adiabatic_qc = AdiabaticQuantumComputer::new(config).unwrap();
1204
1205 let h_mid = adiabatic_qc.interpolate_hamiltonian(0.5).unwrap();
1206 assert_eq!(h_mid.terms.len(), 2); }
1208
1209 #[test]
1210 fn test_tfim_hamiltonian() {
1211 let hamiltonian = AdiabaticUtils::create_tfim_hamiltonian(3, 1.0, 0.5);
1212
1213 let num_zz_terms = hamiltonian.terms.iter().filter(|t| {
1215 matches!(t, HamiltonianTerm::TwoPauli { pauli1, pauli2, .. } if pauli1 == "Z" && pauli2 == "Z")
1216 }).count();
1217
1218 let num_x_terms = hamiltonian
1219 .terms
1220 .iter()
1221 .filter(|t| matches!(t, HamiltonianTerm::SinglePauli { pauli, .. } if pauli == "X"))
1222 .count();
1223
1224 assert_eq!(num_zz_terms, 2); assert_eq!(num_x_terms, 3); }
1227
1228 #[test]
1229 fn test_max_cut_hamiltonian() {
1230 let edges = vec![(0, 1), (1, 2), (2, 0)]; let weights = vec![1.0, 1.0, 1.0];
1232
1233 let hamiltonian = AdiabaticUtils::create_max_cut_hamiltonian(&edges, &weights);
1234 assert!(!hamiltonian.terms.is_empty());
1235 }
1236
1237 #[test]
1238 fn test_mixing_hamiltonian() {
1239 let hamiltonian = AdiabaticUtils::create_mixing_hamiltonian(2);
1240
1241 let num_x_terms = hamiltonian
1242 .terms
1243 .iter()
1244 .filter(|t| matches!(t, HamiltonianTerm::SinglePauli { pauli, .. } if pauli == "X"))
1245 .count();
1246
1247 assert_eq!(num_x_terms, 2); }
1249
1250 #[test]
1251 fn test_adiabatic_evolution() {
1252 let initial_h = AdiabaticUtils::create_mixing_hamiltonian(2);
1253 let final_h = AdiabaticUtils::create_tfim_hamiltonian(2, 1.0, 0.1);
1254
1255 let config = AdiabaticConfig {
1256 total_time: 1.0,
1257 time_steps: 10,
1258 initial_hamiltonian: initial_h,
1259 final_hamiltonian: final_h,
1260 gap_tracking: GapTrackingConfig {
1261 enabled: false, ..Default::default()
1263 },
1264 ..Default::default()
1265 };
1266
1267 let mut adiabatic_qc = AdiabaticQuantumComputer::new(config).unwrap();
1268 let result = adiabatic_qc.evolve();
1269 assert!(result.is_ok());
1270
1271 let evolution_result = result.unwrap();
1272 assert_eq!(evolution_result.evolution_history.len(), 2); }
1274
1275 #[test]
1276 fn test_gap_tracking() {
1277 let initial_h = AdiabaticUtils::create_mixing_hamiltonian(2);
1278 let final_h = AdiabaticUtils::create_tfim_hamiltonian(2, 1.0, 0.1);
1279
1280 let config = AdiabaticConfig {
1281 total_time: 1.0,
1282 time_steps: 5,
1283 initial_hamiltonian: initial_h,
1284 final_hamiltonian: final_h,
1285 gap_tracking: GapTrackingConfig {
1286 enabled: true,
1287 ..Default::default()
1288 },
1289 ..Default::default()
1290 };
1291
1292 let mut adiabatic_qc = AdiabaticQuantumComputer::new(config).unwrap();
1293 let result = adiabatic_qc.evolve();
1294 assert!(result.is_ok());
1295
1296 let evolution_result = result.unwrap();
1297 assert!(!evolution_result.gap_history.is_empty());
1298 assert!(evolution_result.min_gap >= 0.0);
1299 }
1300
1301 #[test]
1302 fn test_energy_calculation() {
1303 let initial_h = AdiabaticUtils::create_mixing_hamiltonian(1);
1304 let final_h = AdiabaticUtils::create_tfim_hamiltonian(1, 1.0, 0.1);
1305
1306 let config = AdiabaticConfig {
1307 initial_hamiltonian: initial_h,
1308 final_hamiltonian: final_h,
1309 ..Default::default()
1310 };
1311
1312 let adiabatic_qc = AdiabaticQuantumComputer::new(config).unwrap();
1313 let energy = adiabatic_qc.calculate_current_energy();
1314 assert!(energy.is_ok());
1315 }
1316
1317 #[test]
1318 fn test_fidelity_calculation() {
1319 let config = AdiabaticConfig::default();
1320 let adiabatic_qc = AdiabaticQuantumComputer::new(config).unwrap();
1321
1322 let state1 = Array1::from_vec(vec![Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)]);
1323 let state2 = Array1::from_vec(vec![Complex64::new(1.0, 0.0), Complex64::new(0.0, 0.0)]);
1324
1325 let fidelity = adiabatic_qc.calculate_fidelity(&state1, &state2);
1326 assert_abs_diff_eq!(fidelity, 1.0, epsilon = 1e-10);
1327 }
1328}