1use std::collections::{HashMap, VecDeque};
17use std::fmt;
18
19use crate::advanced_quantum_algorithms::{
20 AdvancedAlgorithmConfig, AdvancedQuantumAlgorithms, AlgorithmSelectionStrategy,
21 InfiniteDepthQAOA, InfiniteQAOAConfig, QuantumZenoAnnealer, ZenoConfig,
22};
23use crate::applications::{
24 ApplicationError, ApplicationResult, IndustrySolution, OptimizationProblem,
25};
26use crate::bayesian_hyperopt::{optimize_annealing_parameters, BayesianHyperoptimizer};
27use crate::ising::{IsingModel, QuboModel};
28use crate::neural_annealing_schedules::{NeuralAnnealingScheduler, NeuralSchedulerConfig};
29use crate::quantum_error_correction::{
30 ErrorCorrectionCode, ErrorMitigationConfig, ErrorMitigationManager, LogicalAnnealingEncoder,
31 NoiseResilientAnnealingProtocol, SyndromeDetector,
32};
33use crate::simulator::{AnnealingParams, QuantumAnnealingSimulator};
34use std::fmt::Write;
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
38pub enum AminoAcidType {
39 Hydrophobic,
41 Polar,
43}
44
45impl fmt::Display for AminoAcidType {
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 match self {
48 Self::Hydrophobic => write!(f, "H"),
49 Self::Polar => write!(f, "P"),
50 }
51 }
52}
53
54#[derive(Debug, Clone)]
56pub struct ProteinSequence {
57 pub sequence: Vec<AminoAcidType>,
59 pub id: String,
61 pub metadata: HashMap<String, String>,
63}
64
65impl ProteinSequence {
66 #[must_use]
68 pub fn new(sequence: Vec<AminoAcidType>, id: String) -> Self {
69 Self {
70 sequence,
71 id,
72 metadata: HashMap::new(),
73 }
74 }
75
76 pub fn from_string(sequence: &str, id: String) -> ApplicationResult<Self> {
78 let mut amino_acids = Vec::new();
79
80 for ch in sequence.chars() {
81 match ch.to_ascii_uppercase() {
82 'H' => amino_acids.push(AminoAcidType::Hydrophobic),
83 'P' => amino_acids.push(AminoAcidType::Polar),
84 _ => {
85 return Err(ApplicationError::DataValidationError(format!(
86 "Invalid amino acid character: {ch}"
87 )))
88 }
89 }
90 }
91
92 Ok(Self::new(amino_acids, id))
93 }
94
95 #[must_use]
97 pub fn length(&self) -> usize {
98 self.sequence.len()
99 }
100
101 #[must_use]
103 pub fn hydrophobic_count(&self) -> usize {
104 self.sequence
105 .iter()
106 .filter(|&&aa| aa == AminoAcidType::Hydrophobic)
107 .count()
108 }
109
110 #[must_use]
112 pub fn polar_count(&self) -> usize {
113 self.sequence
114 .iter()
115 .filter(|&&aa| aa == AminoAcidType::Polar)
116 .count()
117 }
118}
119
120#[derive(Debug, Clone, Copy, PartialEq, Eq)]
122pub enum LatticeType {
123 Square2D,
125 Triangular2D,
127 Cubic3D,
129 FCC3D,
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
135pub struct LatticePosition {
136 pub x: i32,
137 pub y: i32,
138 pub z: i32,
139}
140
141impl LatticePosition {
142 #[must_use]
143 pub const fn new(x: i32, y: i32, z: i32) -> Self {
144 Self { x, y, z }
145 }
146
147 #[must_use]
148 pub fn distance(&self, other: &Self) -> f64 {
149 let dx = f64::from(self.x - other.x);
150 let dy = f64::from(self.y - other.y);
151 let dz = f64::from(self.z - other.z);
152 dz.mul_add(dz, dx.mul_add(dx, dy * dy)).sqrt()
153 }
154
155 #[must_use]
156 pub const fn manhattan_distance(&self, other: &Self) -> i32 {
157 (self.x - other.x).abs() + (self.y - other.y).abs() + (self.z - other.z).abs()
158 }
159}
160
161#[derive(Debug, Clone)]
163pub struct ProteinFolding {
164 pub sequence: ProteinSequence,
166 pub lattice_type: LatticeType,
168 pub positions: Vec<LatticePosition>,
170}
171
172impl ProteinFolding {
173 #[must_use]
175 pub fn new(sequence: ProteinSequence, lattice_type: LatticeType) -> Self {
176 let positions = vec![LatticePosition::new(0, 0, 0); sequence.length()];
177 Self {
178 sequence,
179 lattice_type,
180 positions,
181 }
182 }
183
184 pub fn set_position(
186 &mut self,
187 index: usize,
188 position: LatticePosition,
189 ) -> ApplicationResult<()> {
190 if index >= self.sequence.length() {
191 return Err(ApplicationError::InvalidConfiguration(format!(
192 "Index {} out of bounds for sequence length {}",
193 index,
194 self.sequence.length()
195 )));
196 }
197 self.positions[index] = position;
198 Ok(())
199 }
200
201 #[must_use]
203 pub fn is_valid(&self) -> bool {
204 let mut position_set = std::collections::HashSet::new();
206 for &pos in &self.positions {
207 if !position_set.insert(pos) {
208 return false; }
210 }
211
212 for i in 1..self.positions.len() {
214 let dist = self.positions[i - 1].manhattan_distance(&self.positions[i]);
215 if dist != 1 {
216 return false; }
218 }
219
220 true
221 }
222
223 #[must_use]
225 pub fn hydrophobic_contacts(&self) -> i32 {
226 let mut contacts = 0;
227
228 for i in 0..self.sequence.length() {
229 if self.sequence.sequence[i] != AminoAcidType::Hydrophobic {
230 continue;
231 }
232
233 for j in (i + 2)..self.sequence.length() {
234 if self.sequence.sequence[j] != AminoAcidType::Hydrophobic {
236 continue;
237 }
238
239 if self.positions[i].manhattan_distance(&self.positions[j]) == 1 {
240 contacts += 1;
241 }
242 }
243 }
244
245 contacts
246 }
247
248 #[must_use]
250 pub fn radius_of_gyration(&self) -> f64 {
251 let n = self.positions.len() as f64;
252
253 let cx = self.positions.iter().map(|p| f64::from(p.x)).sum::<f64>() / n;
255 let cy = self.positions.iter().map(|p| f64::from(p.y)).sum::<f64>() / n;
256 let cz = self.positions.iter().map(|p| f64::from(p.z)).sum::<f64>() / n;
257
258 let rg_sq = self
260 .positions
261 .iter()
262 .map(|p| {
263 let dx = f64::from(p.x) - cx;
264 let dy = f64::from(p.y) - cy;
265 let dz = f64::from(p.z) - cz;
266 dz.mul_add(dz, dx.mul_add(dx, dy * dy))
267 })
268 .sum::<f64>()
269 / n;
270
271 rg_sq.sqrt()
272 }
273
274 #[must_use]
276 pub fn total_energy(&self) -> f64 {
277 let hh_contacts = f64::from(self.hydrophobic_contacts());
278 let compactness_penalty = self.radius_of_gyration();
279
280 0.1f64.mul_add(compactness_penalty, -hh_contacts)
282 }
283}
284
285#[derive(Debug, Clone)]
287pub struct ProteinFoldingProblem {
288 pub sequence: ProteinSequence,
290 pub lattice_type: LatticeType,
292 pub objectives: Vec<FoldingObjective>,
294 pub qec_framework: Option<String>,
296 pub advanced_config: AdvancedAlgorithmConfig,
298 pub neural_config: Option<NeuralSchedulerConfig>,
300}
301
302#[derive(Debug, Clone)]
304pub enum FoldingObjective {
305 MaximizeHHContacts,
307 MinimizeRadiusOfGyration,
309 MinimizeTotalEnergy,
311 MaximizeStability,
313}
314
315impl ProteinFoldingProblem {
316 #[must_use]
318 pub fn new(sequence: ProteinSequence, lattice_type: LatticeType) -> Self {
319 Self {
320 sequence,
321 lattice_type,
322 objectives: vec![FoldingObjective::MaximizeHHContacts],
323 qec_framework: None,
324 advanced_config: AdvancedAlgorithmConfig {
325 enable_infinite_qaoa: true,
326 enable_zeno_annealing: true,
327 enable_adiabatic_shortcuts: true,
328 enable_counterdiabatic: true,
329 selection_strategy: AlgorithmSelectionStrategy::ProblemSpecific,
330 track_performance: true,
331 },
332 neural_config: None,
333 }
334 }
335
336 #[must_use]
338 pub fn with_quantum_error_correction(mut self, config: String) -> Self {
339 self.qec_framework = Some(config);
340 self
341 }
342
343 #[must_use]
345 pub fn with_neural_annealing(mut self, config: NeuralSchedulerConfig) -> Self {
346 self.neural_config = Some(config);
347 self
348 }
349
350 #[must_use]
352 pub fn add_objective(mut self, objective: FoldingObjective) -> Self {
353 self.objectives.push(objective);
354 self
355 }
356
357 pub fn solve_with_advanced_algorithms(&self) -> ApplicationResult<ProteinFolding> {
359 println!("Starting protein folding optimization with advanced quantum algorithms");
360
361 let (qubo, variable_map) = self.to_qubo()?;
363
364 let algorithms = AdvancedQuantumAlgorithms::with_config(self.advanced_config.clone());
366
367 let result = algorithms.solve(&qubo, None).map_err(|e| {
369 ApplicationError::OptimizationError(format!("Advanced algorithm failed: {e:?}"))
370 })?;
371
372 let solution = result
373 .map_err(|e| ApplicationError::OptimizationError(format!("Solver error: {e}")))?;
374
375 self.solution_from_binary(&solution, &variable_map)
377 }
378
379 pub fn solve_with_qec(&self) -> ApplicationResult<ProteinFolding> {
381 if let Some(ref qec_framework) = self.qec_framework {
382 println!("Starting noise-resilient protein folding optimization");
383
384 let (qubo, variable_map) = self.to_qubo()?;
386 let ising_model = qubo.to_ising();
387
388 let error_config = ErrorMitigationConfig::default();
390 let mut error_manager = ErrorMitigationManager::new(error_config).map_err(|e| {
391 ApplicationError::OptimizationError(format!(
392 "Failed to create error manager: {e:?}"
393 ))
394 })?;
395
396 let params = AnnealingParams::default();
398 let annealer = QuantumAnnealingSimulator::new(params.clone()).map_err(|e| {
399 ApplicationError::OptimizationError(format!("Failed to create annealer: {e:?}"))
400 })?;
401 let annealing_result = annealer.solve(&ising_model.0).map_err(|e| {
402 ApplicationError::OptimizationError(format!("Annealing failed: {e:?}"))
403 })?;
404
405 let error_mitigation_result =
407 crate::quantum_error_correction::error_mitigation::AnnealingResult {
408 solution: annealing_result
409 .best_spins
410 .iter()
411 .map(|&x| i32::from(x))
412 .collect(),
413 energy: annealing_result.best_energy,
414 num_occurrences: 1,
415 chain_break_fraction: 0.0,
416 timing: std::collections::HashMap::new(),
417 info: std::collections::HashMap::new(),
418 };
419
420 let mitigation_result = error_manager
422 .apply_mitigation(&ising_model.0, error_mitigation_result, ¶ms)
423 .map_err(|e| {
424 ApplicationError::OptimizationError(format!("Error mitigation failed: {e:?}"))
425 })?;
426
427 let solution = &mitigation_result.mitigated_result.solution;
428
429 self.solution_from_binary(&solution, &variable_map)
431 } else {
432 Err(ApplicationError::InvalidConfiguration(
433 "Quantum error correction not enabled".to_string(),
434 ))
435 }
436 }
437
438 pub fn solve_with_neural_annealing(&self) -> ApplicationResult<ProteinFolding> {
440 if let Some(ref neural_config) = self.neural_config {
441 println!("Starting protein folding with neural-guided annealing");
442
443 let (qubo, variable_map) = self.to_qubo()?;
444
445 let mut neural_scheduler = NeuralAnnealingScheduler::new(neural_config.clone())
447 .map_err(|e| {
448 ApplicationError::OptimizationError(format!(
449 "Failed to create neural scheduler: {e:?}"
450 ))
451 })?;
452
453 let result = neural_scheduler.optimize(&qubo).map_err(|e| {
455 ApplicationError::OptimizationError(format!("Neural annealing failed: {e:?}"))
456 })?;
457
458 let solution = result.map_err(|e| {
459 ApplicationError::OptimizationError(format!("Neural solver error: {e}"))
460 })?;
461
462 self.solution_from_binary(&solution, &variable_map)
463 } else {
464 Err(ApplicationError::InvalidConfiguration(
465 "Neural annealing not enabled".to_string(),
466 ))
467 }
468 }
469
470 pub fn optimize_hyperparameters(&self) -> ApplicationResult<HashMap<String, f64>> {
472 println!("Optimizing folding hyperparameters with Bayesian optimization");
473
474 let objective = |params: &[f64]| -> f64 {
476 let temperature = params[0];
478 let steps = params[1] as usize;
479 let length = self.sequence.length() as f64;
483 let hydrophobic_ratio = self.sequence.hydrophobic_count() as f64 / length;
484
485 let energy_scale = -hydrophobic_ratio * length / 4.0; let thermal_noise = temperature * 0.1;
488 let convergence_factor = (steps as f64 / 1000.0).min(1.0);
489
490 -(energy_scale * convergence_factor - thermal_noise)
492 };
493
494 let best_params = optimize_annealing_parameters(objective, Some(30)).map_err(|e| {
496 ApplicationError::OptimizationError(format!("Bayesian optimization failed: {e:?}"))
497 })?;
498
499 let mut result = HashMap::new();
500 result.insert("optimal_temperature".to_string(), best_params[0]);
501 result.insert("optimal_steps".to_string(), best_params[1]);
502 result.insert("optimal_algorithm".to_string(), best_params[2]);
503
504 Ok(result)
505 }
506
507 fn solution_from_binary(
509 &self,
510 solution: &[i32],
511 variable_map: &HashMap<String, usize>,
512 ) -> ApplicationResult<ProteinFolding> {
513 let mut folding = ProteinFolding::new(self.sequence.clone(), self.lattice_type);
514
515 let seq_len = self.sequence.length();
518
519 folding.set_position(0, LatticePosition::new(0, 0, 0))?;
521
522 for i in 1..seq_len {
524 let move_var_base = (i - 1) * 2; if move_var_base + 1 < solution.len() {
527 let move_x = i32::from(solution[move_var_base] > 0);
528 let move_y = i32::from(solution[move_var_base + 1] > 0);
529
530 let prev_pos = folding.positions[i - 1];
531 let new_pos = match (move_x, move_y) {
532 (0, 0) => LatticePosition::new(prev_pos.x + 1, prev_pos.y, prev_pos.z),
533 (0, 1) => LatticePosition::new(prev_pos.x, prev_pos.y + 1, prev_pos.z),
534 (1, 0) => LatticePosition::new(prev_pos.x - 1, prev_pos.y, prev_pos.z),
535 (1, 1) => LatticePosition::new(prev_pos.x, prev_pos.y - 1, prev_pos.z),
536 _ => LatticePosition::new(prev_pos.x + 1, prev_pos.y, prev_pos.z), };
538
539 folding.set_position(i, new_pos)?;
540 }
541 }
542
543 if !folding.is_valid() {
545 self.fix_invalid_folding(&mut folding)?;
547 }
548
549 Ok(folding)
550 }
551
552 fn fix_invalid_folding(&self, folding: &mut ProteinFolding) -> ApplicationResult<()> {
554 let mut positions = vec![LatticePosition::new(0, 0, 0)];
556 let mut used_positions = std::collections::HashSet::new();
557 used_positions.insert(positions[0]);
558
559 for i in 1..self.sequence.length() {
561 let prev_pos = positions[i - 1];
562
563 let directions = [
565 LatticePosition::new(1, 0, 0),
566 LatticePosition::new(-1, 0, 0),
567 LatticePosition::new(0, 1, 0),
568 LatticePosition::new(0, -1, 0),
569 ];
570
571 let mut placed = false;
572 for &dir in &directions {
573 let new_pos = LatticePosition::new(
574 prev_pos.x + dir.x,
575 prev_pos.y + dir.y,
576 prev_pos.z + dir.z,
577 );
578
579 if used_positions.insert(new_pos) {
580 positions.push(new_pos);
581 placed = true;
582 break;
583 }
584 }
585
586 if !placed {
587 return Err(ApplicationError::OptimizationError(
588 "Cannot generate valid folding configuration".to_string(),
589 ));
590 }
591 }
592
593 folding.positions = positions;
594 Ok(())
595 }
596}
597
598impl OptimizationProblem for ProteinFoldingProblem {
599 type Solution = ProteinFolding;
600 type ObjectiveValue = f64;
601
602 fn description(&self) -> String {
603 format!(
604 "Protein folding optimization for sequence {} (length: {}) on {:?} lattice",
605 self.sequence.id,
606 self.sequence.length(),
607 self.lattice_type
608 )
609 }
610
611 fn size_metrics(&self) -> HashMap<String, usize> {
612 let mut metrics = HashMap::new();
613 metrics.insert("sequence_length".to_string(), self.sequence.length());
614 metrics.insert(
615 "hydrophobic_count".to_string(),
616 self.sequence.hydrophobic_count(),
617 );
618 metrics.insert("polar_count".to_string(), self.sequence.polar_count());
619 metrics.insert("variables".to_string(), (self.sequence.length() - 1) * 2); metrics
621 }
622
623 fn validate(&self) -> ApplicationResult<()> {
624 if self.sequence.length() < 2 {
625 return Err(ApplicationError::DataValidationError(
626 "Sequence must have at least 2 amino acids".to_string(),
627 ));
628 }
629
630 if self.sequence.length() > 200 {
631 return Err(ApplicationError::ResourceLimitExceeded(
632 "Sequence too long for current implementation".to_string(),
633 ));
634 }
635
636 Ok(())
637 }
638
639 fn to_qubo(&self) -> ApplicationResult<(QuboModel, HashMap<String, usize>)> {
640 self.validate()?;
641
642 let seq_len = self.sequence.length();
643 let num_vars = (seq_len - 1) * 2; let mut qubo = QuboModel::new(num_vars);
646 let mut variable_map = HashMap::new();
647
648 for i in 0..(seq_len - 1) {
650 variable_map.insert(format!("move_{i}_x"), i * 2);
651 variable_map.insert(format!("move_{i}_y"), i * 2 + 1);
652 }
653
654 for obj in &self.objectives {
656 match obj {
657 FoldingObjective::MaximizeHHContacts => {
658 self.add_contact_terms(&mut qubo)?;
659 }
660 FoldingObjective::MinimizeRadiusOfGyration => {
661 self.add_compactness_terms(&mut qubo)?;
662 }
663 _ => {
664 self.add_contact_terms(&mut qubo)?;
666 self.add_compactness_terms(&mut qubo)?;
667 }
668 }
669 }
670
671 Ok((qubo, variable_map))
672 }
673
674 fn evaluate_solution(
675 &self,
676 solution: &Self::Solution,
677 ) -> ApplicationResult<Self::ObjectiveValue> {
678 if !solution.is_valid() {
679 return Err(ApplicationError::DataValidationError(
680 "Invalid folding configuration".to_string(),
681 ));
682 }
683
684 Ok(solution.total_energy())
685 }
686
687 fn is_feasible(&self, solution: &Self::Solution) -> bool {
688 solution.is_valid()
689 }
690}
691
692impl ProteinFoldingProblem {
693 fn add_contact_terms(&self, qubo: &mut QuboModel) -> ApplicationResult<()> {
695 let weight = -1.0; for i in 0..qubo.num_variables {
699 for j in (i + 1)..qubo.num_variables {
700 qubo.set_quadratic(i, j, weight * 0.1)?;
702 }
703 }
704
705 Ok(())
706 }
707
708 fn add_compactness_terms(&self, qubo: &mut QuboModel) -> ApplicationResult<()> {
710 let penalty = 0.5;
712
713 for i in 0..qubo.num_variables {
714 qubo.set_linear(i, penalty)?;
715 }
716
717 Ok(())
718 }
719}
720
721impl IndustrySolution for ProteinFolding {
722 type Problem = ProteinFoldingProblem;
723
724 fn from_binary(problem: &Self::Problem, binary_solution: &[i8]) -> ApplicationResult<Self> {
725 let solution_i32: Vec<i32> = binary_solution.iter().map(|&x| i32::from(x)).collect();
726 let variable_map = HashMap::new(); problem.solution_from_binary(&solution_i32, &variable_map)
728 }
729
730 fn summary(&self) -> HashMap<String, String> {
731 let mut summary = HashMap::new();
732 summary.insert("sequence_id".to_string(), self.sequence.id.clone());
733 summary.insert(
734 "sequence_length".to_string(),
735 self.sequence.length().to_string(),
736 );
737 summary.insert(
738 "lattice_type".to_string(),
739 format!("{:?}", self.lattice_type),
740 );
741 summary.insert(
742 "hydrophobic_contacts".to_string(),
743 self.hydrophobic_contacts().to_string(),
744 );
745 summary.insert(
746 "radius_of_gyration".to_string(),
747 format!("{:.3}", self.radius_of_gyration()),
748 );
749 summary.insert(
750 "total_energy".to_string(),
751 format!("{:.3}", self.total_energy()),
752 );
753 summary.insert("is_valid".to_string(), self.is_valid().to_string());
754 summary
755 }
756
757 fn metrics(&self) -> HashMap<String, f64> {
758 let mut metrics = HashMap::new();
759 metrics.insert(
760 "hydrophobic_contacts".to_string(),
761 f64::from(self.hydrophobic_contacts()),
762 );
763 metrics.insert("radius_of_gyration".to_string(), self.radius_of_gyration());
764 metrics.insert("total_energy".to_string(), self.total_energy());
765 metrics.insert(
766 "compactness_score".to_string(),
767 1.0 / (1.0 + self.radius_of_gyration()),
768 );
769 metrics
770 }
771
772 fn export_format(&self) -> ApplicationResult<String> {
773 let mut output = String::new();
774
775 let _ = writeln!(output, "# Protein Folding Result");
776 let _ = writeln!(output, "Sequence ID: {}", self.sequence.id);
777 let _ = writeln!(
778 output,
779 "Sequence: {}",
780 self.sequence
781 .sequence
782 .iter()
783 .map(|aa| format!("{aa}"))
784 .collect::<String>()
785 );
786 let _ = writeln!(output, "Lattice Type: {:?}", self.lattice_type);
787 let _ = write!(output, "Length: {}\n", self.sequence.length());
788 let _ = write!(
789 output,
790 "Hydrophobic Contacts: {}\n",
791 self.hydrophobic_contacts()
792 );
793 let _ = write!(
794 output,
795 "Radius of Gyration: {:.3}\n",
796 self.radius_of_gyration()
797 );
798 let _ = write!(output, "Total Energy: {:.3}\n", self.total_energy());
799 let _ = write!(output, "Valid Configuration: {}\n", self.is_valid());
800
801 output.push_str("\n# Positions\n");
802 for (i, pos) in self.positions.iter().enumerate() {
803 let _ = write!(
804 output,
805 "{}: {} ({}, {}, {})\n",
806 i, self.sequence.sequence[i], pos.x, pos.y, pos.z
807 );
808 }
809
810 Ok(output)
811 }
812}
813
814pub fn create_benchmark_problems(
816 size: usize,
817) -> ApplicationResult<
818 Vec<Box<dyn OptimizationProblem<Solution = ProteinFolding, ObjectiveValue = f64>>>,
819> {
820 let mut problems = Vec::new();
821
822 let sequences = match size {
824 s if s <= 10 => {
825 vec!["HPHPPHHPHH", "HPHHHPPHPH", "HPPHHPPHPP"]
826 }
827 s if s <= 25 => {
828 vec![
829 "HPHPPHHPHHHPPHPPHHHPHPH",
830 "HHHPPHPPHHPPHPPHHHPHPH",
831 "HPHPPHPPHHHPPHPPHHHPHP",
832 "HPPHHPPHPPHHHPPHPPHHPH",
833 ]
834 }
835 _ => {
836 vec![
837 "HPHPPHHPHHHPPHPPHHHPHPHHPHPPHHPHHHPPHPPHHHPHPH",
838 "HHHPPHPPHHPPHPPHHHPHPHHHHPPHPPHHPPHPPHHHPHPH",
839 "HPHPPHPPHHHPPHPPHHHPHPHPHPPHPPHHHPPHPPHHHPHP",
840 ]
841 }
842 };
843
844 for (i, seq_str) in sequences.iter().enumerate() {
845 let sequence = ProteinSequence::from_string(seq_str, format!("benchmark_{i}"))?;
846 let problem = ProteinFoldingProblem::new(sequence, LatticeType::Square2D);
847
848 problems.push(Box::new(problem)
851 as Box<
852 dyn OptimizationProblem<Solution = ProteinFolding, ObjectiveValue = f64>,
853 >);
854 }
855
856 Ok(problems)
857}
858
859#[cfg(test)]
860mod tests {
861 use super::*;
862
863 #[test]
864 fn test_protein_sequence_creation() {
865 let sequence = ProteinSequence::from_string("HPHPPHHPHH", "test".to_string())
866 .expect("Failed to create protein sequence from valid string");
867 assert_eq!(sequence.length(), 10);
868 assert_eq!(sequence.hydrophobic_count(), 6);
869 assert_eq!(sequence.polar_count(), 4);
870 }
871
872 #[test]
873 fn test_lattice_position() {
874 let pos1 = LatticePosition::new(0, 0, 0);
875 let pos2 = LatticePosition::new(1, 0, 0);
876
877 assert_eq!(pos1.manhattan_distance(&pos2), 1);
878 assert!((pos1.distance(&pos2) - 1.0).abs() < 1e-10);
879 }
880
881 #[test]
882 fn test_protein_folding_validation() {
883 let sequence = ProteinSequence::from_string("HPHH", "test".to_string())
884 .expect("Failed to create protein sequence");
885 let mut folding = ProteinFolding::new(sequence, LatticeType::Square2D);
886
887 folding
889 .set_position(0, LatticePosition::new(0, 0, 0))
890 .expect("Failed to set position 0");
891 folding
892 .set_position(1, LatticePosition::new(1, 0, 0))
893 .expect("Failed to set position 1");
894 folding
895 .set_position(2, LatticePosition::new(2, 0, 0))
896 .expect("Failed to set position 2");
897 folding
898 .set_position(3, LatticePosition::new(2, 1, 0))
899 .expect("Failed to set position 3");
900
901 assert!(folding.is_valid());
902 }
903
904 #[test]
905 fn test_hydrophobic_contacts() {
906 let sequence = ProteinSequence::from_string("HPHH", "test".to_string())
907 .expect("Failed to create protein sequence");
908 let mut folding = ProteinFolding::new(sequence, LatticeType::Square2D);
909
910 folding
912 .set_position(0, LatticePosition::new(0, 0, 0))
913 .expect("Failed to set position 0"); folding
915 .set_position(1, LatticePosition::new(1, 0, 0))
916 .expect("Failed to set position 1"); folding
918 .set_position(2, LatticePosition::new(2, 0, 0))
919 .expect("Failed to set position 2"); folding
921 .set_position(3, LatticePosition::new(2, 1, 0))
922 .expect("Failed to set position 3"); assert_eq!(folding.hydrophobic_contacts(), 0);
926
927 folding
929 .set_position(3, LatticePosition::new(1, 1, 0))
930 .expect("Failed to update position 3"); folding
935 .set_position(0, LatticePosition::new(0, 0, 0))
936 .expect("Failed to set folded position 0"); folding
938 .set_position(1, LatticePosition::new(1, 0, 0))
939 .expect("Failed to set folded position 1"); folding
941 .set_position(2, LatticePosition::new(1, 1, 0))
942 .expect("Failed to set folded position 2"); folding
944 .set_position(3, LatticePosition::new(0, 1, 0))
945 .expect("Failed to set folded position 3"); assert_eq!(folding.hydrophobic_contacts(), 1);
949 }
950
951 #[test]
952 fn test_problem_creation() {
953 let sequence = ProteinSequence::from_string("HPHPPHHPHH", "test".to_string())
954 .expect("Failed to create protein sequence");
955 let problem = ProteinFoldingProblem::new(sequence, LatticeType::Square2D);
956
957 assert!(problem.validate().is_ok());
958
959 let metrics = problem.size_metrics();
960 assert_eq!(metrics["sequence_length"], 10);
961 }
962
963 #[test]
964 fn test_qubo_conversion() {
965 let sequence = ProteinSequence::from_string("HPHH", "test".to_string())
966 .expect("Failed to create protein sequence");
967 let problem = ProteinFoldingProblem::new(sequence, LatticeType::Square2D);
968
969 let (qubo, variable_map) = problem
970 .to_qubo()
971 .expect("Failed to convert problem to QUBO");
972 assert_eq!(qubo.num_variables, 6); assert!(!variable_map.is_empty());
974 }
975}