quantrs2_anneal/advanced_quantum_algorithms/
mod.rs1pub mod adiabatic_shortcuts;
13pub mod counterdiabatic;
14pub mod error;
15pub mod infinite_qaoa;
16pub mod utils;
17pub mod zeno_annealing;
18
19pub use adiabatic_shortcuts::*;
21pub use counterdiabatic::*;
22pub use error::*;
23pub use infinite_qaoa::*;
24pub use utils::*;
25pub use zeno_annealing::*;
26
27use scirs2_core::ndarray::{Array1, Array2};
28use scirs2_core::Complex64;
29use std::collections::HashMap;
30use std::sync::Arc;
31
32use crate::{
33 ising::{IsingModel, QuboModel},
34 AnnealingResult, EmbeddingConfig,
35};
36
37#[derive(Debug, Clone)]
42pub struct AdvancedQuantumAlgorithms {
43 pub default_config: AdvancedAlgorithmConfig,
45}
46
47#[derive(Debug, Clone)]
49pub struct AdvancedAlgorithmConfig {
50 pub enable_infinite_qaoa: bool,
52 pub enable_zeno_annealing: bool,
54 pub enable_adiabatic_shortcuts: bool,
56 pub enable_counterdiabatic: bool,
58 pub selection_strategy: AlgorithmSelectionStrategy,
60 pub track_performance: bool,
62}
63
64#[derive(Debug, Clone, PartialEq, Eq)]
66pub enum AlgorithmSelectionStrategy {
67 FirstAvailable,
69 BestPerformance,
71 ProblemSpecific,
73 Ensemble,
75 Manual(String),
77}
78
79impl AdvancedQuantumAlgorithms {
80 #[must_use]
82 pub fn new() -> Self {
83 Self {
84 default_config: AdvancedAlgorithmConfig::default(),
85 }
86 }
87
88 #[must_use]
90 pub const fn with_config(config: AdvancedAlgorithmConfig) -> Self {
91 Self {
92 default_config: config,
93 }
94 }
95
96 pub fn solve<P>(
98 &self,
99 problem: &P,
100 config: Option<AdvancedAlgorithmConfig>,
101 ) -> AdvancedQuantumResult<AnnealingResult<Vec<i32>>>
102 where
103 P: Clone + 'static,
104 {
105 let config = config.unwrap_or_else(|| self.default_config.clone());
106
107 match config.selection_strategy {
108 AlgorithmSelectionStrategy::FirstAvailable => {
109 self.solve_with_first_available(problem, &config)
110 }
111 AlgorithmSelectionStrategy::BestPerformance => {
112 self.solve_with_best_performance(problem, &config)
113 }
114 AlgorithmSelectionStrategy::ProblemSpecific => {
115 self.solve_with_problem_specific(problem, &config)
116 }
117 AlgorithmSelectionStrategy::Ensemble => self.solve_with_ensemble(problem, &config),
118 AlgorithmSelectionStrategy::Manual(ref algorithm_name) => {
119 self.solve_with_manual_selection(problem, &config, algorithm_name)
120 }
121 }
122 }
123
124 fn solve_with_first_available<P>(
126 &self,
127 problem: &P,
128 config: &AdvancedAlgorithmConfig,
129 ) -> AdvancedQuantumResult<AnnealingResult<Vec<i32>>>
130 where
131 P: Clone + 'static,
132 {
133 if config.enable_infinite_qaoa {
134 let qaoa_config = InfiniteQAOAConfig::default();
135 let mut qaoa = InfiniteDepthQAOA::new(qaoa_config);
136 return qaoa.solve(problem);
137 }
138
139 if config.enable_zeno_annealing {
140 let zeno_config = ZenoConfig::default();
141 let mut annealer = QuantumZenoAnnealer::new(zeno_config);
142 return annealer.solve(problem);
143 }
144
145 if config.enable_adiabatic_shortcuts {
146 let shortcuts_config = ShortcutsConfig::default();
147 let mut optimizer = AdiabaticShortcutsOptimizer::new(shortcuts_config);
148 return optimizer.solve(problem);
149 }
150
151 if config.enable_counterdiabatic {
152 let cd_config = CounterdiabaticConfig::default();
153 let mut optimizer = CounterdiabaticDrivingOptimizer::new(cd_config);
154 return optimizer.solve(problem);
155 }
156
157 Err(AdvancedQuantumError::NoAlgorithmAvailable)
158 }
159
160 pub fn optimize_problem(
162 &self,
163 problem: &crate::ising::QuboModel,
164 ) -> AdvancedQuantumResult<crate::simulator::AnnealingResult<crate::simulator::AnnealingSolution>>
165 {
166 use crate::simulator::AnnealingSolution;
167 use std::time::Instant;
168
169 let start_time = Instant::now();
170
171 let ising = IsingModel::from_qubo(problem);
173
174 let best_solution = match self.default_config.selection_strategy {
176 AlgorithmSelectionStrategy::FirstAvailable => {
177 self.optimize_with_first_available(&ising)?
178 }
179 AlgorithmSelectionStrategy::BestPerformance => {
180 self.optimize_with_best_performance(&ising)?
181 }
182 AlgorithmSelectionStrategy::ProblemSpecific => {
183 self.optimize_with_problem_specific(&ising)?
184 }
185 AlgorithmSelectionStrategy::Ensemble => self.optimize_with_ensemble(&ising)?,
186 AlgorithmSelectionStrategy::Manual(ref algo_name) => {
187 self.optimize_with_manual_selection(&ising, algo_name)?
188 }
189 };
190
191 let runtime = start_time.elapsed();
192
193 let qubo_solution: Vec<i8> = best_solution.iter().map(|&s| i8::from(s == 1)).collect();
195
196 let mut energy = 0.0;
198 for (var, coeff) in problem.linear_terms() {
199 if qubo_solution[var] == 1 {
200 energy += coeff;
201 }
202 }
203 for (var1, var2, coeff) in problem.quadratic_terms() {
204 if qubo_solution[var1] == 1 && qubo_solution[var2] == 1 {
205 energy += coeff;
206 }
207 }
208
209 let solution = AnnealingSolution {
210 best_spins: qubo_solution,
211 best_energy: energy,
212 repetitions: 1,
213 total_sweeps: 1000,
214 runtime,
215 info: format!(
216 "Optimized using advanced quantum algorithms (strategy: {:?})",
217 self.default_config.selection_strategy
218 ),
219 };
220
221 Ok(Ok(solution))
222 }
223
224 fn optimize_with_first_available(&self, ising: &IsingModel) -> AdvancedQuantumResult<Vec<i32>> {
226 if self.default_config.enable_infinite_qaoa {
227 return self.optimize_with_infinite_qaoa(ising);
228 }
229 if self.default_config.enable_zeno_annealing {
230 return self.optimize_with_zeno(ising);
231 }
232 if self.default_config.enable_adiabatic_shortcuts {
233 return self.optimize_with_adiabatic_shortcuts(ising);
234 }
235 if self.default_config.enable_counterdiabatic {
236 return self.optimize_with_counterdiabatic(ising);
237 }
238 Err(AdvancedQuantumError::NoAlgorithmAvailable)
239 }
240
241 fn optimize_with_best_performance(
243 &self,
244 ising: &IsingModel,
245 ) -> AdvancedQuantumResult<Vec<i32>> {
246 if self.default_config.enable_infinite_qaoa {
249 self.optimize_with_infinite_qaoa(ising)
250 } else {
251 self.optimize_with_first_available(ising)
252 }
253 }
254
255 fn optimize_with_problem_specific(
257 &self,
258 ising: &IsingModel,
259 ) -> AdvancedQuantumResult<Vec<i32>> {
260 let num_qubits = ising.num_qubits;
262 let num_couplings = ising.couplings().len();
263 let coupling_density = num_couplings as f64 / (num_qubits * num_qubits) as f64;
264
265 if num_qubits < 20 && coupling_density > 0.5 {
267 if self.default_config.enable_infinite_qaoa {
269 return self.optimize_with_infinite_qaoa(ising);
270 }
271 }
272
273 if coupling_density < 0.1 {
274 if self.default_config.enable_zeno_annealing {
276 return self.optimize_with_zeno(ising);
277 }
278 }
279
280 self.optimize_with_first_available(ising)
282 }
283
284 fn optimize_with_ensemble(&self, ising: &IsingModel) -> AdvancedQuantumResult<Vec<i32>> {
286 let mut results = Vec::new();
287 let mut energies = Vec::new();
288
289 if self.default_config.enable_infinite_qaoa {
291 if let Ok(sol) = self.optimize_with_infinite_qaoa(ising) {
292 let energy = self.calculate_ising_energy(ising, &sol);
293 results.push(sol);
294 energies.push(energy);
295 }
296 }
297
298 if self.default_config.enable_zeno_annealing {
299 if let Ok(sol) = self.optimize_with_zeno(ising) {
300 let energy = self.calculate_ising_energy(ising, &sol);
301 results.push(sol);
302 energies.push(energy);
303 }
304 }
305
306 if self.default_config.enable_adiabatic_shortcuts {
307 if let Ok(sol) = self.optimize_with_adiabatic_shortcuts(ising) {
308 let energy = self.calculate_ising_energy(ising, &sol);
309 results.push(sol);
310 energies.push(energy);
311 }
312 }
313
314 if self.default_config.enable_counterdiabatic {
315 if let Ok(sol) = self.optimize_with_counterdiabatic(ising) {
316 let energy = self.calculate_ising_energy(ising, &sol);
317 results.push(sol);
318 energies.push(energy);
319 }
320 }
321
322 if let Some(best_idx) = energies
324 .iter()
325 .enumerate()
326 .min_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
327 .map(|(idx, _)| idx)
328 {
329 Ok(results[best_idx].clone())
330 } else {
331 Err(AdvancedQuantumError::NoAlgorithmAvailable)
332 }
333 }
334
335 fn optimize_with_manual_selection(
337 &self,
338 ising: &IsingModel,
339 algo_name: &str,
340 ) -> AdvancedQuantumResult<Vec<i32>> {
341 match algo_name {
342 "infinite_qaoa" | "qaoa" => self.optimize_with_infinite_qaoa(ising),
343 "zeno" | "quantum_zeno" => self.optimize_with_zeno(ising),
344 "adiabatic_shortcuts" | "shortcuts" => self.optimize_with_adiabatic_shortcuts(ising),
345 "counterdiabatic" | "cd" => self.optimize_with_counterdiabatic(ising),
346 _ => Err(AdvancedQuantumError::InvalidAlgorithm(
347 algo_name.to_string(),
348 )),
349 }
350 }
351
352 fn optimize_with_infinite_qaoa(&self, ising: &IsingModel) -> AdvancedQuantumResult<Vec<i32>> {
354 let config = InfiniteQAOAConfig::default();
355 let mut qaoa = InfiniteDepthQAOA::new(config);
356 let result = qaoa.solve(ising)?;
357 result.map_err(|e| AdvancedQuantumError::ConvergenceError(format!("QAOA failed: {e:?}")))
358 }
359
360 fn optimize_with_zeno(&self, ising: &IsingModel) -> AdvancedQuantumResult<Vec<i32>> {
362 let config = ZenoConfig::default();
363 let mut zeno = QuantumZenoAnnealer::new(config);
364 let result = zeno.solve(ising)?;
365 result.map_err(|e| AdvancedQuantumError::ZenoError(format!("Zeno annealing failed: {e:?}")))
366 }
367
368 fn optimize_with_adiabatic_shortcuts(
370 &self,
371 ising: &IsingModel,
372 ) -> AdvancedQuantumResult<Vec<i32>> {
373 let config = ShortcutsConfig::default();
374 let mut shortcuts = AdiabaticShortcutsOptimizer::new(config);
375 let result = shortcuts.solve(ising)?;
376 result.map_err(|e| {
377 AdvancedQuantumError::ConvergenceError(format!("Adiabatic shortcuts failed: {e:?}"))
378 })
379 }
380
381 fn optimize_with_counterdiabatic(&self, ising: &IsingModel) -> AdvancedQuantumResult<Vec<i32>> {
383 let mut config = ShortcutsConfig::default();
385 config.shortcut_method = ShortcutMethod::CounterdiabaticDriving;
386 let mut optimizer = AdiabaticShortcutsOptimizer::new(config);
387 let result = optimizer.solve(ising)?;
388 result.map_err(|e| {
389 AdvancedQuantumError::ConvergenceError(format!("Counterdiabatic driving failed: {e:?}"))
390 })
391 }
392
393 fn calculate_ising_energy(&self, ising: &IsingModel, solution: &[i32]) -> f64 {
395 let mut energy = 0.0;
396
397 for (i, bias) in ising.biases() {
399 energy += bias * f64::from(solution[i]);
400 }
401
402 for coupling in ising.couplings() {
404 energy += coupling.strength
405 * f64::from(solution[coupling.i])
406 * f64::from(solution[coupling.j]);
407 }
408
409 energy
410 }
411
412 fn solve_with_best_performance<P>(
414 &self,
415 problem: &P,
416 config: &AdvancedAlgorithmConfig,
417 ) -> AdvancedQuantumResult<AnnealingResult<Vec<i32>>>
418 where
419 P: Clone + 'static,
420 {
421 self.solve_with_first_available(problem, config)
424 }
425
426 fn solve_with_problem_specific<P>(
428 &self,
429 problem: &P,
430 config: &AdvancedAlgorithmConfig,
431 ) -> AdvancedQuantumResult<AnnealingResult<Vec<i32>>>
432 where
433 P: Clone + 'static,
434 {
435 let problem_size = if let Ok(ising_problem) = self.convert_to_ising(problem) {
438 ising_problem.num_qubits
439 } else {
440 100 };
442 let density = self.estimate_problem_density(problem, problem_size);
443
444 if problem_size <= 50 && density > 0.7 {
445 if config.enable_infinite_qaoa {
447 let qaoa_config = InfiniteQAOAConfig::default();
448 let mut qaoa = InfiniteDepthQAOA::new(qaoa_config);
449 return qaoa.solve(problem);
450 }
451 } else if problem_size > 100 {
452 if config.enable_zeno_annealing {
454 let zeno_config = ZenoConfig::default();
455 let mut annealer = QuantumZenoAnnealer::new(zeno_config);
456 return annealer.solve(problem);
457 }
458 }
459
460 self.solve_with_first_available(problem, config)
462 }
463
464 fn solve_with_ensemble<P>(
466 &self,
467 problem: &P,
468 config: &AdvancedAlgorithmConfig,
469 ) -> AdvancedQuantumResult<AnnealingResult<Vec<i32>>>
470 where
471 P: Clone + 'static,
472 {
473 let mut results = Vec::new();
474
475 if config.enable_infinite_qaoa {
477 let qaoa_config = InfiniteQAOAConfig::default();
478 let mut qaoa = InfiniteDepthQAOA::new(qaoa_config);
479 if let Ok(result) = qaoa.solve(problem) {
480 results.push(result);
481 }
482 }
483
484 if config.enable_zeno_annealing {
485 let zeno_config = ZenoConfig::default();
486 let mut annealer = QuantumZenoAnnealer::new(zeno_config);
487 if let Ok(result) = annealer.solve(problem) {
488 results.push(result);
489 }
490 }
491
492 if let Some(best_result) = results.into_iter().next() {
494 Ok(best_result)
495 } else {
496 Err(AdvancedQuantumError::EnsembleFailed)
497 }
498 }
499
500 fn solve_with_manual_selection<P>(
502 &self,
503 problem: &P,
504 config: &AdvancedAlgorithmConfig,
505 algorithm_name: &str,
506 ) -> AdvancedQuantumResult<AnnealingResult<Vec<i32>>>
507 where
508 P: Clone + 'static,
509 {
510 match algorithm_name {
511 "infinite_qaoa" if config.enable_infinite_qaoa => {
512 let qaoa_config = InfiniteQAOAConfig::default();
513 let mut qaoa = InfiniteDepthQAOA::new(qaoa_config);
514 qaoa.solve(problem)
515 }
516 "zeno_annealing" if config.enable_zeno_annealing => {
517 let zeno_config = ZenoConfig::default();
518 let mut annealer = QuantumZenoAnnealer::new(zeno_config);
519 annealer.solve(problem)
520 }
521 "adiabatic_shortcuts" if config.enable_adiabatic_shortcuts => {
522 let shortcuts_config = ShortcutsConfig::default();
523 let mut optimizer = AdiabaticShortcutsOptimizer::new(shortcuts_config);
524 optimizer.solve(problem)
525 }
526 "counterdiabatic" if config.enable_counterdiabatic => {
527 let cd_config = CounterdiabaticConfig::default();
528 let mut optimizer = CounterdiabaticDrivingOptimizer::new(cd_config);
529 optimizer.solve(problem)
530 }
531 _ => Err(AdvancedQuantumError::AlgorithmNotFound(
532 algorithm_name.to_string(),
533 )),
534 }
535 }
536
537 fn estimate_problem_density<P>(&self, problem: &P, num_vars: usize) -> f64
543 where
544 P: Clone + 'static,
545 {
546 let max_interactions = num_vars * num_vars.saturating_sub(1) / 2;
547
548 if max_interactions == 0 {
549 return 0.0;
550 }
551
552 if let Ok(ising) = self.convert_to_ising(problem) {
554 let actual_interactions = ising
555 .couplings()
556 .iter()
557 .filter(|coupling| coupling.strength.abs() > 1e-12)
558 .count();
559 return actual_interactions as f64 / max_interactions as f64;
560 }
561
562 let estimated_interactions = num_vars * 2;
564 estimated_interactions as f64 / max_interactions as f64
565 }
566
567 fn convert_to_ising<P>(&self, problem: &P) -> Result<IsingModel, String>
573 where
574 P: Clone + 'static,
575 {
576 use std::any::Any;
577 let any_problem = problem as &dyn Any;
578
579 if let Some(ising) = any_problem.downcast_ref::<IsingModel>() {
580 return Ok(ising.clone());
581 }
582 if let Some(ising_ref) = any_problem.downcast_ref::<&IsingModel>() {
583 return Ok((*ising_ref).clone());
584 }
585 if let Some(qubo) = any_problem.downcast_ref::<QuboModel>() {
586 return Ok(qubo.to_ising().0);
587 }
588 if let Some(qubo_ref) = any_problem.downcast_ref::<&QuboModel>() {
589 return Ok((*qubo_ref).to_ising().0);
590 }
591
592 Err("Unsupported problem type: expected IsingModel or QuboModel".to_string())
593 }
594}
595
596impl Default for AdvancedAlgorithmConfig {
597 fn default() -> Self {
598 Self {
599 enable_infinite_qaoa: true,
600 enable_zeno_annealing: true,
601 enable_adiabatic_shortcuts: true,
602 enable_counterdiabatic: true,
603 selection_strategy: AlgorithmSelectionStrategy::ProblemSpecific,
604 track_performance: true,
605 }
606 }
607}
608
609impl Default for AdvancedQuantumAlgorithms {
610 fn default() -> Self {
611 Self::new()
612 }
613}
614
615#[cfg(test)]
616mod tests {
617 use super::*;
618
619 #[test]
620 fn test_coordinator_convert_to_ising_real() {
621 let algorithms = AdvancedQuantumAlgorithms::default();
622
623 let mut ising = IsingModel::new(4);
625 ising.set_bias(0, 0.5).expect("set bias");
626 ising.set_coupling(0, 1, -1.0).expect("set coupling");
627 ising.set_coupling(2, 3, 0.25).expect("set coupling");
628 let converted = algorithms
629 .convert_to_ising(&ising)
630 .expect("Ising input should convert");
631 assert_eq!(converted.num_qubits, 4);
632
633 let density = algorithms.estimate_problem_density(&ising, converted.num_qubits);
635 let expected = 2.0 / 6.0;
636 assert!(
637 (density - expected).abs() < 1e-12,
638 "density {density} != {expected}"
639 );
640 }
641
642 #[test]
643 fn test_coordinator_convert_to_ising_rejects_unsupported() {
644 let algorithms = AdvancedQuantumAlgorithms::default();
645 let bogus: Vec<f64> = vec![1.0, 2.0, 3.0];
646 assert!(
647 algorithms.convert_to_ising(&bogus).is_err(),
648 "unsupported types must error rather than return a fabricated empty model"
649 );
650 }
651}