1use std::collections::HashMap;
7use std::time::Duration;
8
9use scirs2_core::random::ChaCha8Rng;
10use scirs2_core::random::{Rng, SeedableRng};
11use scirs2_core::RngExt;
12
13use super::error::{AdvancedQuantumError, AdvancedQuantumResult};
14use crate::ising::IsingModel;
15use crate::simulator::{AnnealingResult, AnnealingSolution};
16
17#[derive(Debug, Clone)]
19pub struct AdiabaticShortcutsOptimizer {
20 pub config: ShortcutsConfig,
22 pub protocols: Vec<ShortcutProtocol>,
24 pub control_optimizer: ControlOptimizer,
26 pub performance_stats: ShortcutsPerformanceStats,
28}
29
30#[derive(Debug, Clone)]
32pub struct ShortcutsConfig {
33 pub shortcut_method: ShortcutMethod,
35 pub control_method: ControlOptimizationMethod,
37 pub time_constraints: TimeConstraints,
39 pub fidelity_targets: FidelityTargets,
41 pub resource_constraints: ResourceConstraints,
43}
44
45impl Default for ShortcutsConfig {
46 fn default() -> Self {
47 Self {
48 shortcut_method: ShortcutMethod::ShortcutsToAdiabaticity,
49 control_method: ControlOptimizationMethod::GRAPE,
50 time_constraints: TimeConstraints::default(),
51 fidelity_targets: FidelityTargets::default(),
52 resource_constraints: ResourceConstraints::default(),
53 }
54 }
55}
56
57#[derive(Debug, Clone, PartialEq, Eq)]
59pub enum ShortcutMethod {
60 ShortcutsToAdiabaticity,
62 FastForward,
64 CounterdiabaticDriving,
66 OptimalControl,
68 MachineLearning,
70}
71
72#[derive(Debug, Clone, PartialEq, Eq)]
74pub enum ControlOptimizationMethod {
75 GRAPE,
77 CRAB,
79 Krotov,
81 Pontryagin,
83 ReinforcementLearning,
85}
86
87#[derive(Debug, Clone)]
89pub struct TimeConstraints {
90 pub min_time: f64,
92 pub max_time: f64,
94 pub time_steps: usize,
96 pub time_tolerance: f64,
98}
99
100impl Default for TimeConstraints {
101 fn default() -> Self {
102 Self {
103 min_time: 0.1,
104 max_time: 10.0,
105 time_steps: 100,
106 time_tolerance: 1e-6,
107 }
108 }
109}
110
111#[derive(Debug, Clone)]
113pub struct FidelityTargets {
114 pub state_fidelity: f64,
116 pub process_fidelity: f64,
118 pub energy_fidelity: f64,
120 pub fidelity_tolerance: f64,
122}
123
124impl Default for FidelityTargets {
125 fn default() -> Self {
126 Self {
127 state_fidelity: 0.99,
128 process_fidelity: 0.95,
129 energy_fidelity: 0.98,
130 fidelity_tolerance: 1e-4,
131 }
132 }
133}
134
135#[derive(Debug, Clone)]
137pub struct ResourceConstraints {
138 pub max_control_amplitude: f64,
140 pub max_control_derivative: f64,
142 pub available_controls: Vec<ControlField>,
144 pub hardware_limitations: HardwareLimitations,
146}
147
148impl Default for ResourceConstraints {
149 fn default() -> Self {
150 Self {
151 max_control_amplitude: 10.0,
152 max_control_derivative: 100.0,
153 available_controls: vec![
154 ControlField::MagneticX,
155 ControlField::MagneticY,
156 ControlField::MagneticZ,
157 ],
158 hardware_limitations: HardwareLimitations::default(),
159 }
160 }
161}
162
163#[derive(Debug, Clone, PartialEq, Eq)]
165pub enum ControlField {
166 MagneticX,
168 MagneticY,
170 MagneticZ,
172 Electric,
174 Microwave,
176 Laser,
178}
179
180#[derive(Debug, Clone)]
182pub struct HardwareLimitations {
183 pub max_field_strength: f64,
185 pub field_rise_time: f64,
187 pub control_bandwidth: f64,
189 pub noise_floor: f64,
191}
192
193impl Default for HardwareLimitations {
194 fn default() -> Self {
195 Self {
196 max_field_strength: 5.0,
197 field_rise_time: 0.01,
198 control_bandwidth: 1000.0,
199 noise_floor: 1e-6,
200 }
201 }
202}
203
204#[derive(Debug, Clone)]
206pub struct ShortcutProtocol {
207 pub name: String,
209 pub time_evolution: Vec<TimePoint>,
211 pub control_fields: Vec<ControlSequence>,
213 pub expected_fidelity: f64,
215 pub protocol_cost: f64,
217}
218
219#[derive(Debug, Clone)]
221pub struct TimePoint {
222 pub time: f64,
224 pub hamiltonian: HamiltonianComponent,
226 pub state_vector: Vec<f64>,
228 pub energy: f64,
230}
231
232#[derive(Debug, Clone)]
234pub struct HamiltonianComponent {
235 pub pauli_string: String,
237 pub coefficient: f64,
239 pub qubit_indices: Vec<usize>,
241}
242
243#[derive(Debug, Clone)]
245pub struct ControlSequence {
246 pub field_type: ControlField,
248 pub amplitude_sequence: Vec<(f64, f64)>,
250 pub interpolation: InterpolationMethod,
252}
253
254#[derive(Debug, Clone, PartialEq, Eq)]
256pub enum InterpolationMethod {
257 Linear,
259 CubicSpline,
261 Fourier,
263 PiecewiseConstant,
265}
266
267#[derive(Debug, Clone)]
269pub struct ControlOptimizer {
270 pub algorithm: ControlOptimizationAlgorithm,
272 pub cost_function: CostFunction,
274 pub gradient_computation: GradientComputation,
276 pub convergence_criteria: ControlConvergenceCriteria,
278}
279
280#[derive(Debug, Clone)]
282pub struct ControlOptimizationAlgorithm {
283 pub algorithm_type: ControlOptimizationMethod,
285 pub parameters: HashMap<String, f64>,
287 pub max_iterations: usize,
289 pub learning_rate: f64,
291}
292
293#[derive(Debug, Clone)]
295pub struct CostFunction {
296 pub fidelity_weight: f64,
298 pub time_weight: f64,
300 pub energy_weight: f64,
302 pub control_effort_weight: f64,
304 pub regularization: Vec<RegularizationTerm>,
306}
307
308#[derive(Debug, Clone)]
310pub struct RegularizationTerm {
311 pub term_type: RegularizationType,
313 pub weight: f64,
315 pub parameters: Vec<f64>,
317}
318
319#[derive(Debug, Clone, PartialEq, Eq)]
321pub enum RegularizationType {
322 L1,
324 L2,
326 TotalVariation,
328 Smoothness,
330 Bandwidth,
332}
333
334#[derive(Debug, Clone, PartialEq, Eq)]
336pub enum GradientComputation {
337 Analytical,
339 FiniteDifferences,
341 AutoDiff,
343 Adjoint,
345}
346
347#[derive(Debug, Clone)]
349pub struct ControlConvergenceCriteria {
350 pub cost_tolerance: f64,
352 pub gradient_tolerance: f64,
354 pub parameter_tolerance: f64,
356 pub max_stagnation: usize,
358}
359
360#[derive(Debug, Clone)]
362pub struct ShortcutsPerformanceStats {
363 pub achieved_fidelity: f64,
365 pub protocol_time: f64,
367 pub optimization_time: Duration,
369 pub control_effort: f64,
371 pub speedup_factor: f64,
373}
374
375impl AdiabaticShortcutsOptimizer {
376 #[must_use]
378 pub fn new(config: ShortcutsConfig) -> Self {
379 Self {
380 config,
381 protocols: Vec::new(),
382 control_optimizer: ControlOptimizer::default(),
383 performance_stats: ShortcutsPerformanceStats::default(),
384 }
385 }
386
387 pub fn solve<P>(&mut self, problem: &P) -> AdvancedQuantumResult<AnnealingResult<Vec<i32>>>
389 where
390 P: Clone + 'static,
391 {
392 if let Ok(ising_problem) = self.convert_to_ising(problem) {
394 let solution = self.optimize(&ising_problem)?;
395 match solution {
396 Ok(annealing_solution) => {
397 let spins: Vec<i32> = annealing_solution
398 .best_spins
399 .iter()
400 .map(|&s| i32::from(s))
401 .collect();
402 Ok(Ok(spins))
403 }
404 Err(err) => Ok(Err(err)),
405 }
406 } else {
407 Err(AdvancedQuantumError::AdiabaticError(
408 "Cannot convert problem to Ising model".to_string(),
409 ))
410 }
411 }
412
413 fn convert_to_ising<P: 'static>(
415 &self,
416 problem: &P,
417 ) -> Result<IsingModel, AdvancedQuantumError> {
418 use std::any::Any;
419
420 if let Some(ising) = (problem as &dyn Any).downcast_ref::<IsingModel>() {
422 return Ok(ising.clone());
423 }
424
425 if let Some(ising_ref) = (problem as &dyn Any).downcast_ref::<&IsingModel>() {
427 return Ok((*ising_ref).clone());
428 }
429
430 let num_qubits = self.estimate_problem_size(problem);
432 let mut ising = IsingModel::new(num_qubits);
433
434 let problem_hash = self.hash_problem(problem);
436 let mut rng = ChaCha8Rng::seed_from_u64(problem_hash);
437
438 match self.config.shortcut_method {
440 ShortcutMethod::ShortcutsToAdiabaticity => {
441 self.generate_smooth_landscape(&mut ising, &mut rng)?;
443 }
444 ShortcutMethod::FastForward => {
445 self.generate_gap_structured_problem(&mut ising, &mut rng)?;
447 }
448 _ => {
449 self.generate_default_problem(&mut ising, &mut rng)?;
451 }
452 }
453
454 Ok(ising)
455 }
456
457 fn generate_smooth_landscape(
459 &self,
460 ising: &mut IsingModel,
461 rng: &mut ChaCha8Rng,
462 ) -> Result<(), AdvancedQuantumError> {
463 let num_qubits = ising.num_qubits;
464
465 for i in 0..num_qubits {
467 let bias = 0.5 * (2.0 * std::f64::consts::PI * i as f64 / num_qubits as f64).sin();
468 ising
469 .set_bias(i, bias)
470 .map_err(AdvancedQuantumError::IsingError)?;
471 }
472
473 for i in 0..(num_qubits - 1) {
475 let coupling = 0.2f64.mul_add(rng.random_range(-1.0..1.0), -0.5);
476 ising
477 .set_coupling(i, i + 1, coupling)
478 .map_err(AdvancedQuantumError::IsingError)?;
479 }
480
481 for _ in 0..(num_qubits / 4) {
483 let i = rng.random_range(0..num_qubits);
484 let j = rng.random_range(0..num_qubits);
485 if i != j {
486 let coupling = 0.1 * rng.random_range(-1.0..1.0);
487 ising
488 .set_coupling(i, j, coupling)
489 .map_err(AdvancedQuantumError::IsingError)?;
490 }
491 }
492
493 Ok(())
494 }
495
496 fn generate_gap_structured_problem(
498 &self,
499 ising: &mut IsingModel,
500 rng: &mut ChaCha8Rng,
501 ) -> Result<(), AdvancedQuantumError> {
502 let num_qubits = ising.num_qubits;
503
504 for i in 0..num_qubits {
506 let bias = if i % 2 == 0 { 0.8 } else { -0.8 };
507 ising
508 .set_bias(i, bias)
509 .map_err(AdvancedQuantumError::IsingError)?;
510 }
511
512 for i in 0..num_qubits {
514 for j in (i + 1)..num_qubits {
515 if (i + j) % 3 == 0 {
516 let coupling = 0.3 * if rng.random_bool(0.5) { 1.0 } else { -1.0 };
517 ising
518 .set_coupling(i, j, coupling)
519 .map_err(AdvancedQuantumError::IsingError)?;
520 }
521 }
522 }
523
524 Ok(())
525 }
526
527 fn generate_default_problem(
529 &self,
530 ising: &mut IsingModel,
531 rng: &mut ChaCha8Rng,
532 ) -> Result<(), AdvancedQuantumError> {
533 let num_qubits = ising.num_qubits;
534
535 for i in 0..num_qubits {
537 let bias = rng.random_range(-1.0..1.0);
538 ising
539 .set_bias(i, bias)
540 .map_err(AdvancedQuantumError::IsingError)?;
541 }
542
543 let coupling_probability = 0.3;
545 for i in 0..num_qubits {
546 for j in (i + 1)..num_qubits {
547 if rng.random::<f64>() < coupling_probability {
548 let coupling = rng.random_range(-1.0..1.0);
549 ising
550 .set_coupling(i, j, coupling)
551 .map_err(AdvancedQuantumError::IsingError)?;
552 }
553 }
554 }
555
556 Ok(())
557 }
558
559 const fn estimate_problem_size<P>(&self, _problem: &P) -> usize {
561 12
564 }
565
566 const fn hash_problem<P>(&self, _problem: &P) -> u64 {
568 54_321
571 }
572
573 pub fn optimize(
575 &mut self,
576 problem: &IsingModel,
577 ) -> AdvancedQuantumResult<AnnealingResult<AnnealingSolution>> {
578 println!("Starting Adiabatic Shortcuts optimization");
579 let start_time = std::time::Instant::now();
580
581 let protocol = self.generate_shortcut_protocol(problem)?;
583 self.protocols.push(protocol.clone());
584
585 let result = self.execute_protocol(&protocol, problem)?;
587
588 self.performance_stats.optimization_time = start_time.elapsed();
590 self.performance_stats.achieved_fidelity = self.calculate_achieved_fidelity(&result)?;
591 self.performance_stats.protocol_time =
592 protocol.time_evolution.last().map_or(0.0, |tp| tp.time);
593 self.performance_stats.control_effort = self.calculate_control_effort(&protocol)?;
594 self.performance_stats.speedup_factor = self.calculate_speedup_factor(&protocol)?;
595
596 println!(
597 "Adiabatic shortcuts completed. Energy: {:.6}, Fidelity: {:.6}",
598 result.best_energy, self.performance_stats.achieved_fidelity
599 );
600
601 Ok(Ok(result))
602 }
603
604 fn generate_shortcut_protocol(
606 &self,
607 problem: &IsingModel,
608 ) -> AdvancedQuantumResult<ShortcutProtocol> {
609 let protocol_name = format!("{:?}_protocol", self.config.shortcut_method);
610
611 let time_evolution = self.generate_time_evolution(problem)?;
613
614 let control_fields = self.generate_control_sequences(problem)?;
616
617 let expected_fidelity =
619 self.estimate_protocol_fidelity(&time_evolution, &control_fields)?;
620
621 let protocol_cost = self.calculate_protocol_cost(&control_fields)?;
623
624 Ok(ShortcutProtocol {
625 name: protocol_name,
626 time_evolution,
627 control_fields,
628 expected_fidelity,
629 protocol_cost,
630 })
631 }
632
633 fn generate_time_evolution(
635 &self,
636 problem: &IsingModel,
637 ) -> AdvancedQuantumResult<Vec<TimePoint>> {
638 let mut time_points = Vec::new();
639 let dt = (self.config.time_constraints.max_time - self.config.time_constraints.min_time)
640 / self.config.time_constraints.time_steps as f64;
641
642 for i in 0..=self.config.time_constraints.time_steps {
643 let time = (i as f64).mul_add(dt, self.config.time_constraints.min_time);
644
645 let hamiltonian = HamiltonianComponent {
647 pauli_string: format!("Z_{}", problem.num_qubits),
648 coefficient: 1.0 - time / self.config.time_constraints.max_time,
649 qubit_indices: (0..problem.num_qubits).collect(),
650 };
651
652 let state_vector = self.compute_instantaneous_state(time, problem)?;
654
655 let energy = self.compute_instantaneous_energy(time, problem, &state_vector)?;
657
658 time_points.push(TimePoint {
659 time,
660 hamiltonian,
661 state_vector,
662 energy,
663 });
664 }
665
666 Ok(time_points)
667 }
668
669 fn compute_instantaneous_state(
671 &self,
672 time: f64,
673 problem: &IsingModel,
674 ) -> AdvancedQuantumResult<Vec<f64>> {
675 let num_qubits = problem.num_qubits;
676 let state_size = 1 << num_qubits;
677 let mut state = vec![0.0; state_size];
678
679 let s = time / self.config.time_constraints.max_time;
681
682 if s < 1e-8 {
683 let amplitude = 1.0 / (state_size as f64).sqrt();
685 for i in 0..state_size {
686 state[i] = amplitude;
687 }
688 } else {
689 state[0] = s.sqrt();
691 let remaining = (1.0 - s) / (state_size - 1) as f64;
692 for i in 1..state_size {
693 state[i] = remaining.sqrt();
694 }
695 }
696
697 Ok(state)
698 }
699
700 fn compute_instantaneous_energy(
702 &self,
703 _time: f64,
704 problem: &IsingModel,
705 _state: &[f64],
706 ) -> AdvancedQuantumResult<f64> {
707 let mut energy = 0.0;
709
710 for i in 0..problem.num_qubits {
712 if let Ok(bias) = problem.get_bias(i) {
713 energy += bias;
714 }
715 }
716
717 for i in 0..problem.num_qubits {
719 for j in (i + 1)..problem.num_qubits {
720 if let Ok(coupling) = problem.get_coupling(i, j) {
721 energy += coupling;
722 }
723 }
724 }
725
726 Ok(energy)
727 }
728
729 fn generate_control_sequences(
731 &self,
732 problem: &IsingModel,
733 ) -> AdvancedQuantumResult<Vec<ControlSequence>> {
734 let mut control_sequences = Vec::new();
735
736 for control_field in &self.config.resource_constraints.available_controls {
737 let sequence = match self.config.shortcut_method {
738 ShortcutMethod::ShortcutsToAdiabaticity => {
739 self.generate_sta_control_sequence(control_field.clone(), problem)?
740 }
741 ShortcutMethod::FastForward => {
742 self.generate_fastforward_control_sequence(control_field.clone(), problem)?
743 }
744 _ => self.generate_default_control_sequence(control_field.clone(), problem)?,
745 };
746
747 control_sequences.push(sequence);
748 }
749
750 Ok(control_sequences)
751 }
752
753 fn generate_sta_control_sequence(
755 &self,
756 field_type: ControlField,
757 _problem: &IsingModel,
758 ) -> AdvancedQuantumResult<ControlSequence> {
759 let mut amplitude_sequence = Vec::new();
760 let dt = (self.config.time_constraints.max_time - self.config.time_constraints.min_time)
761 / self.config.time_constraints.time_steps as f64;
762
763 for i in 0..=self.config.time_constraints.time_steps {
764 let time = (i as f64).mul_add(dt, self.config.time_constraints.min_time);
765
766 let s = time / self.config.time_constraints.max_time;
768 let amplitude = match field_type {
769 ControlField::MagneticX => s * (1.0 - s) * 4.0, ControlField::MagneticY => {
771 0.5 * 2.0f64
772 .mul_add(s, -1.0)
773 .mul_add(-2.0f64.mul_add(s, -1.0), 1.0)
774 } ControlField::MagneticZ => 1.0 - s, _ => 0.1 * (time * std::f64::consts::PI).sin(), };
778
779 amplitude_sequence.push((time, amplitude));
780 }
781
782 Ok(ControlSequence {
783 field_type,
784 amplitude_sequence,
785 interpolation: InterpolationMethod::CubicSpline,
786 })
787 }
788
789 fn generate_fastforward_control_sequence(
791 &self,
792 field_type: ControlField,
793 _problem: &IsingModel,
794 ) -> AdvancedQuantumResult<ControlSequence> {
795 let mut amplitude_sequence = Vec::new();
796 let dt = (self.config.time_constraints.max_time - self.config.time_constraints.min_time)
797 / self.config.time_constraints.time_steps as f64;
798
799 for i in 0..=self.config.time_constraints.time_steps {
800 let time = (i as f64).mul_add(dt, self.config.time_constraints.min_time);
801
802 let amplitude = match field_type {
804 ControlField::MagneticX => 2.0 * time.exp() / self.config.time_constraints.max_time,
805 ControlField::MagneticY => (time * 2.0 * std::f64::consts::PI).cos(),
806 _ => 0.5,
807 };
808
809 amplitude_sequence.push((time, amplitude));
810 }
811
812 Ok(ControlSequence {
813 field_type,
814 amplitude_sequence,
815 interpolation: InterpolationMethod::Linear,
816 })
817 }
818
819 fn generate_default_control_sequence(
821 &self,
822 field_type: ControlField,
823 _problem: &IsingModel,
824 ) -> AdvancedQuantumResult<ControlSequence> {
825 let mut amplitude_sequence = Vec::new();
826 let dt = (self.config.time_constraints.max_time - self.config.time_constraints.min_time)
827 / self.config.time_constraints.time_steps as f64;
828
829 for i in 0..=self.config.time_constraints.time_steps {
830 let time = (i as f64).mul_add(dt, self.config.time_constraints.min_time);
831 let amplitude = 0.1; amplitude_sequence.push((time, amplitude));
833 }
834
835 Ok(ControlSequence {
836 field_type,
837 amplitude_sequence,
838 interpolation: InterpolationMethod::PiecewiseConstant,
839 })
840 }
841
842 fn execute_protocol(
844 &self,
845 protocol: &ShortcutProtocol,
846 problem: &IsingModel,
847 ) -> AdvancedQuantumResult<AnnealingSolution> {
848 let start_time = std::time::Instant::now();
849
850 let final_time_point = protocol
852 .time_evolution
853 .last()
854 .ok_or_else(|| AdvancedQuantumError::AdiabaticError("Empty protocol".to_string()))?;
855
856 let final_energy = final_time_point.energy;
857
858 let best_spins = self.extract_spin_configuration(&final_time_point.state_vector)?;
860
861 Ok(AnnealingSolution {
862 best_energy: final_energy,
863 best_spins,
864 repetitions: 1,
865 total_sweeps: protocol.time_evolution.len(),
866 runtime: start_time.elapsed(),
867 info: format!("Adiabatic shortcuts: {}", protocol.name),
868 })
869 }
870
871 fn extract_spin_configuration(&self, state_vector: &[f64]) -> AdvancedQuantumResult<Vec<i8>> {
873 let max_amplitude_index = state_vector
875 .iter()
876 .enumerate()
877 .max_by(|(_, a), (_, b)| {
878 a.abs()
879 .partial_cmp(&b.abs())
880 .unwrap_or(std::cmp::Ordering::Equal)
881 })
882 .map_or(0, |(i, _)| i);
883
884 let num_qubits = (state_vector.len() as f64).log2() as usize;
886 let mut spins = Vec::new();
887
888 for qubit in 0..num_qubits {
889 let bit = (max_amplitude_index >> qubit) & 1;
890 spins.push(if bit == 1 { 1 } else { -1 });
891 }
892
893 Ok(spins)
894 }
895
896 const fn estimate_protocol_fidelity(
898 &self,
899 _time_evolution: &[TimePoint],
900 _control_fields: &[ControlSequence],
901 ) -> AdvancedQuantumResult<f64> {
902 Ok(0.95) }
905
906 fn calculate_protocol_cost(
908 &self,
909 control_fields: &[ControlSequence],
910 ) -> AdvancedQuantumResult<f64> {
911 let mut total_cost = 0.0;
912
913 for sequence in control_fields {
914 let control_effort = sequence
915 .amplitude_sequence
916 .iter()
917 .map(|(_, amplitude)| amplitude.powi(2))
918 .sum::<f64>();
919 total_cost += control_effort;
920 }
921
922 Ok(total_cost)
923 }
924
925 const fn calculate_achieved_fidelity(
927 &self,
928 _result: &AnnealingSolution,
929 ) -> AdvancedQuantumResult<f64> {
930 Ok(0.93)
932 }
933
934 const fn calculate_control_effort(
936 &self,
937 protocol: &ShortcutProtocol,
938 ) -> AdvancedQuantumResult<f64> {
939 Ok(protocol.protocol_cost)
940 }
941
942 fn calculate_speedup_factor(&self, protocol: &ShortcutProtocol) -> AdvancedQuantumResult<f64> {
944 let adiabatic_time = 100.0; let shortcut_time = protocol.time_evolution.last().map_or(1.0, |tp| tp.time);
946
947 Ok(adiabatic_time / shortcut_time)
948 }
949}
950
951impl Default for ControlOptimizer {
952 fn default() -> Self {
953 Self {
954 algorithm: ControlOptimizationAlgorithm {
955 algorithm_type: ControlOptimizationMethod::GRAPE,
956 parameters: HashMap::new(),
957 max_iterations: 1000,
958 learning_rate: 0.01,
959 },
960 cost_function: CostFunction {
961 fidelity_weight: 1.0,
962 time_weight: 0.1,
963 energy_weight: 0.5,
964 control_effort_weight: 0.01,
965 regularization: Vec::new(),
966 },
967 gradient_computation: GradientComputation::FiniteDifferences,
968 convergence_criteria: ControlConvergenceCriteria {
969 cost_tolerance: 1e-6,
970 gradient_tolerance: 1e-6,
971 parameter_tolerance: 1e-8,
972 max_stagnation: 50,
973 },
974 }
975 }
976}
977
978impl Default for ShortcutsPerformanceStats {
979 fn default() -> Self {
980 Self {
981 achieved_fidelity: 0.0,
982 protocol_time: 0.0,
983 optimization_time: Duration::from_secs(0),
984 control_effort: 0.0,
985 speedup_factor: 1.0,
986 }
987 }
988}
989
990#[must_use]
992pub fn create_adiabatic_shortcuts_optimizer() -> AdiabaticShortcutsOptimizer {
993 AdiabaticShortcutsOptimizer::new(ShortcutsConfig::default())
994}
995
996#[must_use]
998pub fn create_custom_adiabatic_shortcuts_optimizer(
999 shortcut_method: ShortcutMethod,
1000 control_method: ControlOptimizationMethod,
1001 max_time: f64,
1002) -> AdiabaticShortcutsOptimizer {
1003 let mut config = ShortcutsConfig::default();
1004 config.shortcut_method = shortcut_method;
1005 config.control_method = control_method;
1006 config.time_constraints.max_time = max_time;
1007
1008 AdiabaticShortcutsOptimizer::new(config)
1009}
1010
1011#[cfg(test)]
1012mod tests {
1013 use super::*;
1014
1015 #[test]
1016 fn test_adiabatic_shortcuts_creation() {
1017 let optimizer = create_adiabatic_shortcuts_optimizer();
1018 assert!(matches!(
1019 optimizer.config.shortcut_method,
1020 ShortcutMethod::ShortcutsToAdiabaticity
1021 ));
1022 assert!(matches!(
1023 optimizer.config.control_method,
1024 ControlOptimizationMethod::GRAPE
1025 ));
1026 }
1027
1028 #[test]
1029 fn test_time_evolution_generation() {
1030 let optimizer = create_adiabatic_shortcuts_optimizer();
1031 let ising = IsingModel::new(2);
1032
1033 let time_evolution = optimizer
1034 .generate_time_evolution(&ising)
1035 .expect("should generate time evolution");
1036 assert!(!time_evolution.is_empty());
1037 assert!(time_evolution.len() > 10);
1038
1039 for i in 1..time_evolution.len() {
1041 assert!(time_evolution[i].time > time_evolution[i - 1].time);
1042 }
1043 }
1044
1045 #[test]
1046 fn test_control_sequence_generation() {
1047 let optimizer = create_adiabatic_shortcuts_optimizer();
1048 let ising = IsingModel::new(2);
1049
1050 let control_sequences = optimizer
1051 .generate_control_sequences(&ising)
1052 .expect("should generate control sequences");
1053 assert!(!control_sequences.is_empty());
1054
1055 for sequence in &control_sequences {
1056 assert!(!sequence.amplitude_sequence.is_empty());
1057 for i in 1..sequence.amplitude_sequence.len() {
1059 assert!(sequence.amplitude_sequence[i].0 > sequence.amplitude_sequence[i - 1].0);
1060 }
1061 }
1062 }
1063
1064 #[test]
1065 fn test_spin_configuration_extraction() {
1066 let optimizer = create_adiabatic_shortcuts_optimizer();
1067
1068 let state_vector = vec![0.1, 0.9, 0.3, 0.2];
1070 let spins = optimizer
1071 .extract_spin_configuration(&state_vector)
1072 .expect("should extract spin configuration");
1073
1074 assert_eq!(spins.len(), 2);
1075 assert_eq!(spins[0], 1); assert_eq!(spins[1], -1); }
1078}