1use std::collections::{HashMap, VecDeque};
18use std::fmt;
19
20use crate::advanced_quantum_algorithms::{
21 AdiabaticShortcutsOptimizer, AdvancedAlgorithmConfig, AdvancedQuantumAlgorithms,
22 AlgorithmSelectionStrategy, InfiniteDepthQAOA, InfiniteQAOAConfig, QuantumZenoAnnealer,
23 ShortcutsConfig, ZenoConfig,
24};
25use crate::applications::{
26 ApplicationError, ApplicationResult, IndustrySolution, OptimizationProblem,
27};
28use crate::bayesian_hyperopt::{optimize_annealing_parameters, BayesianHyperoptimizer};
29use crate::ising::IsingModel;
30use crate::neural_annealing_schedules::{NeuralAnnealingScheduler, NeuralSchedulerConfig};
31use crate::non_stoquastic::{HamiltonianType, NonStoquasticHamiltonian};
32use crate::quantum_error_correction::{
33 ErrorCorrectionCode, ErrorMitigationConfig, ErrorMitigationManager, LogicalAnnealingEncoder,
34 NoiseResilientAnnealingProtocol, SyndromeDetector,
35};
36use crate::simulator::{AnnealingParams, QuantumAnnealingSimulator};
37use std::fmt::Write;
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub enum LatticeType {
42 SimpleCubic,
44 BodyCenteredCubic,
46 FaceCenteredCubic,
48 HexagonalClosePacked,
50 Diamond,
52 Graphene,
54 Perovskite,
56 Custom {
58 coordination: usize,
59 dimension: usize,
60 },
61}
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
65pub struct LatticePosition {
66 pub x: i32,
67 pub y: i32,
68 pub z: i32,
69}
70
71impl LatticePosition {
72 #[must_use]
73 pub const fn new(x: i32, y: i32, z: i32) -> Self {
74 Self { x, y, z }
75 }
76
77 #[must_use]
78 pub fn distance(&self, other: &Self) -> f64 {
79 let dx = f64::from(self.x - other.x);
80 let dy = f64::from(self.y - other.y);
81 let dz = f64::from(self.z - other.z);
82 dz.mul_add(dz, dx.mul_add(dx, dy * dy)).sqrt()
83 }
84
85 #[must_use]
86 pub fn neighbors(&self, lattice_type: LatticeType) -> Vec<Self> {
87 match lattice_type {
88 LatticeType::SimpleCubic => {
89 vec![
90 Self::new(self.x + 1, self.y, self.z),
91 Self::new(self.x - 1, self.y, self.z),
92 Self::new(self.x, self.y + 1, self.z),
93 Self::new(self.x, self.y - 1, self.z),
94 Self::new(self.x, self.y, self.z + 1),
95 Self::new(self.x, self.y, self.z - 1),
96 ]
97 }
98 LatticeType::Graphene => {
99 vec![
101 Self::new(self.x + 1, self.y, self.z),
102 Self::new(self.x, self.y + 1, self.z),
103 Self::new(self.x - 1, self.y + 1, self.z),
104 ]
105 }
106 _ => {
107 self.neighbors(LatticeType::SimpleCubic)
109 }
110 }
111 }
112}
113
114#[derive(Debug, Clone, PartialEq)]
116pub struct AtomicSpecies {
117 pub symbol: String,
118 pub atomic_number: u32,
119 pub mass: f64,
120 pub magnetic_moment: Option<f64>,
121 pub charge: f64,
122 pub radius: f64,
123}
124
125impl AtomicSpecies {
126 #[must_use]
127 pub const fn new(symbol: String, atomic_number: u32, mass: f64) -> Self {
128 Self {
129 symbol,
130 atomic_number,
131 mass,
132 magnetic_moment: None,
133 charge: 0.0,
134 radius: 1.0,
135 }
136 }
137
138 #[must_use]
139 pub const fn with_magnetic_moment(mut self, moment: f64) -> Self {
140 self.magnetic_moment = Some(moment);
141 self
142 }
143
144 #[must_use]
145 pub const fn with_charge(mut self, charge: f64) -> Self {
146 self.charge = charge;
147 self
148 }
149}
150
151#[derive(Debug, Clone)]
153pub struct LatticeSite {
154 pub position: LatticePosition,
155 pub species: Option<AtomicSpecies>,
156 pub occupation: bool,
157 pub spin: Option<[f64; 3]>, pub defect_type: Option<DefectType>,
159 pub local_energy: f64,
160}
161
162impl LatticeSite {
163 #[must_use]
164 pub const fn new(position: LatticePosition) -> Self {
165 Self {
166 position,
167 species: None,
168 occupation: false,
169 spin: None,
170 defect_type: None,
171 local_energy: 0.0,
172 }
173 }
174
175 #[must_use]
176 pub fn with_species(mut self, species: AtomicSpecies) -> Self {
177 self.species = Some(species);
178 self.occupation = true;
179 self
180 }
181
182 #[must_use]
183 pub const fn with_spin(mut self, spin: [f64; 3]) -> Self {
184 self.spin = Some(spin);
185 self
186 }
187
188 #[must_use]
189 pub fn with_defect(mut self, defect: DefectType) -> Self {
190 self.defect_type = Some(defect);
191 self
192 }
193}
194
195#[derive(Debug, Clone, PartialEq)]
197pub enum DefectType {
198 Vacancy,
200 Interstitial(AtomicSpecies),
202 Substitutional(AtomicSpecies),
204 GrainBoundary,
206 Dislocation { burgers_vector: [f64; 3] },
208 Surface,
210}
211
212#[derive(Debug, Clone)]
214pub struct MaterialsLattice {
215 pub lattice_type: LatticeType,
216 pub dimensions: [usize; 3], pub lattice_constants: [f64; 3], pub sites: HashMap<LatticePosition, LatticeSite>,
219 pub temperature: f64,
220 pub external_field: Option<[f64; 3]>, }
222
223impl MaterialsLattice {
224 #[must_use]
225 pub fn new(lattice_type: LatticeType, dimensions: [usize; 3]) -> Self {
226 let mut sites = HashMap::new();
227
228 for x in 0..(dimensions[0] as i32) {
230 for y in 0..(dimensions[1] as i32) {
231 for z in 0..(dimensions[2] as i32) {
232 let pos = LatticePosition::new(x, y, z);
233 sites.insert(pos, LatticeSite::new(pos));
234 }
235 }
236 }
237
238 Self {
239 lattice_type,
240 dimensions,
241 lattice_constants: [1.0, 1.0, 1.0],
242 sites,
243 temperature: 300.0, external_field: None,
245 }
246 }
247
248 pub const fn set_lattice_constants(&mut self, constants: [f64; 3]) {
249 self.lattice_constants = constants;
250 }
251
252 pub fn add_atom(
253 &mut self,
254 position: LatticePosition,
255 species: AtomicSpecies,
256 ) -> ApplicationResult<()> {
257 if let Some(site) = self.sites.get_mut(&position) {
258 site.species = Some(species);
259 site.occupation = true;
260 Ok(())
261 } else {
262 Err(ApplicationError::InvalidConfiguration(format!(
263 "Position {position:?} not found in lattice"
264 )))
265 }
266 }
267
268 pub fn create_defect(
269 &mut self,
270 position: LatticePosition,
271 defect: DefectType,
272 ) -> ApplicationResult<()> {
273 if let Some(site) = self.sites.get_mut(&position) {
274 site.defect_type = Some(defect);
275 match &site.defect_type {
276 Some(DefectType::Vacancy) => {
277 site.occupation = false;
278 site.species = None;
279 }
280 Some(DefectType::Interstitial(species)) => {
281 site.species = Some(species.clone());
282 site.occupation = true;
283 }
284 Some(DefectType::Substitutional(species)) => {
285 site.species = Some(species.clone());
286 site.occupation = true;
287 }
288 _ => {}
289 }
290 Ok(())
291 } else {
292 Err(ApplicationError::InvalidConfiguration(format!(
293 "Position {position:?} not found in lattice"
294 )))
295 }
296 }
297
298 #[must_use]
299 pub fn calculate_total_energy(&self) -> f64 {
300 let mut total_energy = 0.0;
301
302 for (pos, site) in &self.sites {
303 total_energy += site.local_energy;
305
306 let neighbors = pos.neighbors(self.lattice_type);
308 for neighbor_pos in neighbors {
309 if let Some(neighbor_site) = self.sites.get(&neighbor_pos) {
310 if site.occupation && neighbor_site.occupation {
311 total_energy += self.interaction_energy(site, neighbor_site);
312 }
313 }
314 }
315
316 if let Some(ref defect) = site.defect_type {
318 total_energy += self.defect_energy(defect);
319 }
320 }
321
322 total_energy / 2.0 }
324
325 fn interaction_energy(&self, site1: &LatticeSite, site2: &LatticeSite) -> f64 {
326 let mut energy = 0.0;
327
328 if let (Some(ref species1), Some(ref species2)) = (&site1.species, &site2.species) {
330 let distance = site1.position.distance(&site2.position) * self.lattice_constants[0];
331 let k_coulomb = 8.99e9; energy += k_coulomb * species1.charge * species2.charge / distance;
333 }
334
335 if let (Some(spin1), Some(spin2)) = (site1.spin, site2.spin) {
337 let j_exchange = -1.0; energy += j_exchange
339 * spin1[2].mul_add(spin2[2], spin1[0].mul_add(spin2[0], spin1[1] * spin2[1]));
340 }
341
342 energy
343 }
344
345 const fn defect_energy(&self, defect: &DefectType) -> f64 {
346 match defect {
347 DefectType::Vacancy => 2.0, DefectType::Interstitial(_) => 3.0, DefectType::Substitutional(_) => 0.5, DefectType::GrainBoundary => 1.0,
351 DefectType::Dislocation { .. } => 5.0,
352 DefectType::Surface => 0.8,
353 }
354 }
355
356 #[must_use]
357 pub fn calculate_order_parameter(&self) -> f64 {
358 let mut magnetization = [0.0, 0.0, 0.0];
359 let mut count = 0;
360
361 for site in self.sites.values() {
362 if let Some(spin) = site.spin {
363 magnetization[0] += spin[0];
364 magnetization[1] += spin[1];
365 magnetization[2] += spin[2];
366 count += 1;
367 }
368 }
369
370 if count > 0 {
371 let mag = magnetization[2].mul_add(
372 magnetization[2],
373 magnetization[0].mul_add(magnetization[0], magnetization[1] * magnetization[1]),
374 );
375 mag.sqrt() / f64::from(count)
376 } else {
377 0.0
378 }
379 }
380}
381
382#[derive(Debug, Clone)]
384pub enum MaterialsObjective {
385 MinimizeEnergy,
387 MaximizeStability,
389 MinimizeDefects,
391 OptimizeMagnetism,
393 MinimizeFormationEnergy,
395 MaximizeOrder,
397 MultiObjective(Vec<(Self, f64)>),
399}
400
401#[derive(Debug, Clone)]
403pub struct MaterialsOptimizationProblem {
404 pub lattice: MaterialsLattice,
405 pub objectives: Vec<MaterialsObjective>,
406 pub constraints: Vec<MaterialsConstraint>,
407 pub qec_framework: Option<String>,
408 pub advanced_config: AdvancedAlgorithmConfig,
409 pub neural_config: Option<NeuralSchedulerConfig>,
410}
411
412#[derive(Debug, Clone)]
414pub enum MaterialsConstraint {
415 Stoichiometry { elements: HashMap<String, f64> },
417 ChargeNeutrality,
419 MaxDefectDensity(f64),
421 TemperatureRange { min: f64, max: f64 },
423 MagneticMomentConservation,
425 StructuralStability { min_coordination: usize },
427}
428
429impl MaterialsOptimizationProblem {
430 #[must_use]
431 pub fn new(lattice: MaterialsLattice) -> Self {
432 Self {
433 lattice,
434 objectives: vec![MaterialsObjective::MinimizeEnergy],
435 constraints: vec![],
436 qec_framework: None,
437 advanced_config: AdvancedAlgorithmConfig {
438 enable_infinite_qaoa: true,
439 enable_zeno_annealing: true,
440 enable_adiabatic_shortcuts: true,
441 enable_counterdiabatic: true,
442 selection_strategy: AlgorithmSelectionStrategy::ProblemSpecific,
443 track_performance: true,
444 },
445 neural_config: None,
446 }
447 }
448
449 #[must_use]
450 pub fn with_quantum_error_correction(mut self, config: String) -> Self {
451 self.qec_framework = Some(config);
452 self
453 }
454
455 #[must_use]
456 pub fn with_neural_annealing(mut self, config: NeuralSchedulerConfig) -> Self {
457 self.neural_config = Some(config);
458 self
459 }
460
461 #[must_use]
462 pub fn add_objective(mut self, objective: MaterialsObjective) -> Self {
463 self.objectives.push(objective);
464 self
465 }
466
467 #[must_use]
468 pub fn add_constraint(mut self, constraint: MaterialsConstraint) -> Self {
469 self.constraints.push(constraint);
470 self
471 }
472
473 pub fn solve_with_infinite_qaoa(&self) -> ApplicationResult<MaterialsLattice> {
475 println!("Starting materials optimization with infinite-depth QAOA");
476
477 let (ising_model, variable_map) = self.to_ising_model()?;
478
479 let qaoa_config = InfiniteQAOAConfig {
480 max_depth: 50,
481 initial_depth: 3,
482 optimization_tolerance: 1e-8,
483 ..Default::default()
484 };
485
486 let mut qaoa = InfiniteDepthQAOA::new(qaoa_config);
487 let result = qaoa.solve(&ising_model).map_err(|e| {
488 ApplicationError::OptimizationError(format!("Infinite QAOA failed: {e:?}"))
489 })?;
490
491 let solution = result
492 .map_err(|e| ApplicationError::OptimizationError(format!("QAOA solver error: {e}")))?;
493
494 self.solution_to_lattice(&solution, &variable_map)
495 }
496
497 pub fn solve_with_zeno_annealing(&self) -> ApplicationResult<MaterialsLattice> {
499 println!("Starting materials optimization with Quantum Zeno annealing");
500
501 let (ising_model, variable_map) = self.to_ising_model()?;
502
503 let zeno_config = ZenoConfig {
504 measurement_frequency: 2.0,
505 total_evolution_time: 20.0,
506 evolution_time_step: 0.05,
507 ..Default::default()
508 };
509
510 let mut zeno_annealer = QuantumZenoAnnealer::new(zeno_config);
511 let result = zeno_annealer.solve(&ising_model).map_err(|e| {
512 ApplicationError::OptimizationError(format!("Zeno annealing failed: {e:?}"))
513 })?;
514
515 let solution = result
516 .map_err(|e| ApplicationError::OptimizationError(format!("Zeno solver error: {e}")))?;
517
518 self.solution_to_lattice(&solution, &variable_map)
519 }
520
521 pub fn solve_with_adiabatic_shortcuts(&self) -> ApplicationResult<MaterialsLattice> {
523 println!("Starting materials optimization with adiabatic shortcuts");
524
525 let (ising_model, variable_map) = self.to_ising_model()?;
526
527 let shortcuts_config = ShortcutsConfig::default();
528 let mut shortcuts_optimizer = AdiabaticShortcutsOptimizer::new(shortcuts_config);
529
530 let result = shortcuts_optimizer.solve(&ising_model).map_err(|e| {
531 ApplicationError::OptimizationError(format!("Adiabatic shortcuts failed: {e:?}"))
532 })?;
533
534 let solution = result.map_err(|e| {
535 ApplicationError::OptimizationError(format!("Shortcuts solver error: {e}"))
536 })?;
537
538 self.solution_to_lattice(&solution, &variable_map)
539 }
540
541 pub fn solve_with_error_correction(&self) -> ApplicationResult<MaterialsLattice> {
543 if let Some(ref qec_framework) = self.qec_framework {
544 println!("Starting noise-resilient materials optimization");
545
546 let (ising_model, variable_map) = self.to_ising_model()?;
547
548 let error_config = ErrorMitigationConfig::default();
550 let mut error_manager = ErrorMitigationManager::new(error_config).map_err(|e| {
551 ApplicationError::OptimizationError(format!(
552 "Failed to create error manager: {e:?}"
553 ))
554 })?;
555
556 let params = AnnealingParams::default();
558 let annealer = QuantumAnnealingSimulator::new(params.clone()).map_err(|e| {
559 ApplicationError::OptimizationError(format!("Failed to create annealer: {e:?}"))
560 })?;
561 let annealing_result = annealer.solve(&ising_model).map_err(|e| {
562 ApplicationError::OptimizationError(format!("Annealing failed: {e:?}"))
563 })?;
564
565 let error_mitigation_result =
567 crate::quantum_error_correction::error_mitigation::AnnealingResult {
568 solution: annealing_result
569 .best_spins
570 .iter()
571 .map(|&x| i32::from(x))
572 .collect(),
573 energy: annealing_result.best_energy,
574 num_occurrences: 1,
575 chain_break_fraction: 0.0,
576 timing: std::collections::HashMap::new(),
577 info: std::collections::HashMap::new(),
578 };
579
580 let mitigation_result = error_manager
582 .apply_mitigation(&ising_model, error_mitigation_result, ¶ms)
583 .map_err(|e| {
584 ApplicationError::OptimizationError(format!("Error mitigation failed: {e:?}"))
585 })?;
586
587 let solution = &mitigation_result.mitigated_result.solution;
588
589 self.solution_to_lattice(&solution, &variable_map)
590 } else {
591 Err(ApplicationError::InvalidConfiguration(
592 "Quantum error correction not enabled".to_string(),
593 ))
594 }
595 }
596
597 pub fn optimize_lattice_parameters(&self) -> ApplicationResult<HashMap<String, f64>> {
599 println!("Optimizing lattice parameters with Bayesian optimization");
600
601 let objective = |params: &[f64]| -> f64 {
602 let temperature = params[0];
604 let lattice_constant = params[1];
605 let field_strength = params[2];
606
607 let thermal_energy = temperature * 0.001; let strain_energy = (lattice_constant - 1.0).powi(2) * 10.0; let field_energy = field_strength * 0.1; thermal_energy + strain_energy + field_energy
614 };
615
616 let best_params = optimize_annealing_parameters(objective, Some(40)).map_err(|e| {
617 ApplicationError::OptimizationError(format!("Bayesian optimization failed: {e:?}"))
618 })?;
619
620 let mut result = HashMap::new();
621 result.insert("optimal_temperature".to_string(), best_params[0]);
622 result.insert("optimal_lattice_constant".to_string(), best_params[1]);
623 result.insert("optimal_field_strength".to_string(), best_params[2]);
624
625 Ok(result)
626 }
627
628 fn to_ising_model(&self) -> ApplicationResult<(IsingModel, HashMap<String, usize>)> {
630 let total_sites = self.lattice.sites.len();
631 let mut ising = IsingModel::new(total_sites);
632 let mut variable_map = HashMap::new();
633
634 let site_positions: Vec<_> = self.lattice.sites.keys().copied().collect();
636 for (i, pos) in site_positions.iter().enumerate() {
637 variable_map.insert(format!("site_{}_{}_{}_{}", pos.x, pos.y, pos.z, i), i);
638 }
639
640 for (i, (pos, site)) in self.lattice.sites.iter().enumerate() {
642 let mut bias = site.local_energy;
643
644 for objective in &self.objectives {
646 match objective {
647 MaterialsObjective::MinimizeEnergy => {
648 bias += site.local_energy;
649 }
650 MaterialsObjective::MinimizeDefects => {
651 if site.defect_type.is_some() {
652 bias += 10.0; }
654 }
655 MaterialsObjective::OptimizeMagnetism => {
656 if let Some(spin) = site.spin {
657 let spin_magnitude = spin[2]
658 .mul_add(spin[2], spin[0].mul_add(spin[0], spin[1] * spin[1]))
659 .sqrt();
660 bias -= spin_magnitude; }
662 }
663 _ => {
664 bias += 0.1;
666 }
667 }
668 }
669
670 ising
671 .set_bias(i, bias)
672 .map_err(|e| ApplicationError::OptimizationError(e.to_string()))?;
673 }
674
675 for (i, (pos1, site1)) in self.lattice.sites.iter().enumerate() {
677 let neighbors = pos1.neighbors(self.lattice.lattice_type);
678 for neighbor_pos in neighbors {
679 if let Some((j, (_, site2))) = self
680 .lattice
681 .sites
682 .iter()
683 .enumerate()
684 .find(|(_, (pos, _))| **pos == neighbor_pos)
685 {
686 if i < j {
687 let coupling = self.lattice.interaction_energy(site1, site2);
689 ising
690 .set_coupling(i, j, coupling)
691 .map_err(|e| ApplicationError::OptimizationError(e.to_string()))?;
692 }
693 }
694 }
695 }
696
697 Ok((ising, variable_map))
698 }
699
700 fn solution_to_lattice(
702 &self,
703 solution: &[i32],
704 variable_map: &HashMap<String, usize>,
705 ) -> ApplicationResult<MaterialsLattice> {
706 let mut optimized_lattice = self.lattice.clone();
707
708 for (pos, site) in &mut optimized_lattice.sites {
710 if let Some(&var_index) =
711 variable_map.get(&format!("site_{}_{}_{}_0", pos.x, pos.y, pos.z))
712 {
713 if var_index < solution.len() {
714 let spin_value = solution[var_index];
715
716 if site.spin.is_some() {
718 let spin_direction = if spin_value > 0 { 1.0 } else { -1.0 };
719 site.spin = Some([0.0, 0.0, spin_direction]);
720 }
721
722 site.occupation = spin_value > 0;
724 }
725 }
726 }
727
728 let positions: Vec<_> = optimized_lattice.sites.keys().copied().collect();
730 for pos in positions {
731 let local_energy = self.calculate_local_energy(&pos, &optimized_lattice);
732 if let Some(site) = optimized_lattice.sites.get_mut(&pos) {
733 site.local_energy = local_energy;
734 }
735 }
736
737 Ok(optimized_lattice)
738 }
739
740 fn calculate_local_energy(&self, pos: &LatticePosition, lattice: &MaterialsLattice) -> f64 {
741 let mut energy = 0.0;
742
743 if let Some(site) = lattice.sites.get(pos) {
744 if let Some(ref defect) = site.defect_type {
746 energy += lattice.defect_energy(defect);
747 }
748
749 let neighbors = pos.neighbors(lattice.lattice_type);
751 for neighbor_pos in neighbors {
752 if let Some(neighbor_site) = lattice.sites.get(&neighbor_pos) {
753 energy += lattice.interaction_energy(site, neighbor_site) / 2.0;
754 }
756 }
757 }
758
759 energy
760 }
761}
762
763impl OptimizationProblem for MaterialsOptimizationProblem {
764 type Solution = MaterialsLattice;
765 type ObjectiveValue = f64;
766
767 fn description(&self) -> String {
768 format!(
769 "Materials science lattice optimization: {:?} lattice, dimensions {:?}, {} sites",
770 self.lattice.lattice_type,
771 self.lattice.dimensions,
772 self.lattice.sites.len()
773 )
774 }
775
776 fn size_metrics(&self) -> HashMap<String, usize> {
777 let mut metrics = HashMap::new();
778 metrics.insert("total_sites".to_string(), self.lattice.sites.len());
779 metrics.insert("x_dimension".to_string(), self.lattice.dimensions[0]);
780 metrics.insert("y_dimension".to_string(), self.lattice.dimensions[1]);
781 metrics.insert("z_dimension".to_string(), self.lattice.dimensions[2]);
782
783 let occupied_sites = self.lattice.sites.values().filter(|s| s.occupation).count();
784 metrics.insert("occupied_sites".to_string(), occupied_sites);
785
786 let defect_sites = self
787 .lattice
788 .sites
789 .values()
790 .filter(|s| s.defect_type.is_some())
791 .count();
792 metrics.insert("defect_sites".to_string(), defect_sites);
793
794 metrics
795 }
796
797 fn validate(&self) -> ApplicationResult<()> {
798 if self.lattice.sites.is_empty() {
799 return Err(ApplicationError::DataValidationError(
800 "Lattice must have at least one site".to_string(),
801 ));
802 }
803
804 let total_sites =
805 self.lattice.dimensions[0] * self.lattice.dimensions[1] * self.lattice.dimensions[2];
806 if total_sites > 10_000 {
807 return Err(ApplicationError::ResourceLimitExceeded(
808 "Lattice too large for current implementation".to_string(),
809 ));
810 }
811
812 for constraint in &self.constraints {
814 match constraint {
815 MaterialsConstraint::ChargeNeutrality => {
816 let total_charge: f64 = self
817 .lattice
818 .sites
819 .values()
820 .filter_map(|s| s.species.as_ref().map(|sp| sp.charge))
821 .sum();
822 if total_charge.abs() > 1e-6 {
823 return Err(ApplicationError::ConstraintViolation(format!(
824 "Charge neutrality violated: total charge = {total_charge}"
825 )));
826 }
827 }
828 MaterialsConstraint::MaxDefectDensity(max_density) => {
829 let defect_count = self
830 .lattice
831 .sites
832 .values()
833 .filter(|s| s.defect_type.is_some())
834 .count();
835 let defect_density = defect_count as f64 / self.lattice.sites.len() as f64;
836 if defect_density > *max_density {
837 return Err(ApplicationError::ConstraintViolation(format!(
838 "Defect density {defect_density} exceeds maximum {max_density}"
839 )));
840 }
841 }
842 _ => {
843 }
845 }
846 }
847
848 Ok(())
849 }
850
851 fn to_qubo(&self) -> ApplicationResult<(crate::ising::QuboModel, HashMap<String, usize>)> {
852 let (ising, variable_map) = self.to_ising_model()?;
854 let qubo = ising.to_qubo();
855 Ok((qubo, variable_map))
856 }
857
858 fn evaluate_solution(
859 &self,
860 solution: &Self::Solution,
861 ) -> ApplicationResult<Self::ObjectiveValue> {
862 Ok(solution.calculate_total_energy())
863 }
864
865 fn is_feasible(&self, solution: &Self::Solution) -> bool {
866 for constraint in &self.constraints {
868 match constraint {
869 MaterialsConstraint::ChargeNeutrality => {
870 let total_charge: f64 = solution
871 .sites
872 .values()
873 .filter_map(|s| s.species.as_ref().map(|sp| sp.charge))
874 .sum();
875 if total_charge.abs() > 1e-6 {
876 return false;
877 }
878 }
879 MaterialsConstraint::MaxDefectDensity(max_density) => {
880 let defect_count = solution
881 .sites
882 .values()
883 .filter(|s| s.defect_type.is_some())
884 .count();
885 let defect_density = defect_count as f64 / solution.sites.len() as f64;
886 if defect_density > *max_density {
887 return false;
888 }
889 }
890 _ => {
891 }
893 }
894 }
895
896 true
897 }
898}
899
900impl IndustrySolution for MaterialsLattice {
901 type Problem = MaterialsOptimizationProblem;
902
903 fn from_binary(problem: &Self::Problem, binary_solution: &[i8]) -> ApplicationResult<Self> {
904 let solution_i32: Vec<i32> = binary_solution.iter().map(|&x| i32::from(x)).collect();
905 let variable_map = HashMap::new(); problem.solution_to_lattice(&solution_i32, &variable_map)
907 }
908
909 fn summary(&self) -> HashMap<String, String> {
910 let mut summary = HashMap::new();
911 summary.insert(
912 "lattice_type".to_string(),
913 format!("{:?}", self.lattice_type),
914 );
915 summary.insert("dimensions".to_string(), format!("{:?}", self.dimensions));
916 summary.insert("total_sites".to_string(), self.sites.len().to_string());
917 summary.insert(
918 "temperature".to_string(),
919 format!("{:.2} K", self.temperature),
920 );
921 summary.insert(
922 "total_energy".to_string(),
923 format!("{:.6}", self.calculate_total_energy()),
924 );
925 summary.insert(
926 "order_parameter".to_string(),
927 format!("{:.6}", self.calculate_order_parameter()),
928 );
929
930 let occupied_sites = self.sites.values().filter(|s| s.occupation).count();
931 summary.insert("occupied_sites".to_string(), occupied_sites.to_string());
932
933 let defect_sites = self
934 .sites
935 .values()
936 .filter(|s| s.defect_type.is_some())
937 .count();
938 summary.insert("defect_sites".to_string(), defect_sites.to_string());
939
940 summary
941 }
942
943 fn metrics(&self) -> HashMap<String, f64> {
944 let mut metrics = HashMap::new();
945 metrics.insert("total_energy".to_string(), self.calculate_total_energy());
946 metrics.insert(
947 "order_parameter".to_string(),
948 self.calculate_order_parameter(),
949 );
950 metrics.insert("temperature".to_string(), self.temperature);
951
952 let occupied_fraction =
953 self.sites.values().filter(|s| s.occupation).count() as f64 / self.sites.len() as f64;
954 metrics.insert("occupation_fraction".to_string(), occupied_fraction);
955
956 let defect_density = self
957 .sites
958 .values()
959 .filter(|s| s.defect_type.is_some())
960 .count() as f64
961 / self.sites.len() as f64;
962 metrics.insert("defect_density".to_string(), defect_density);
963
964 metrics
965 }
966
967 fn export_format(&self) -> ApplicationResult<String> {
968 let mut output = String::new();
969
970 output.push_str("# Materials Science Lattice Configuration\n");
971 let _ = writeln!(output, "Lattice Type: {:?}", self.lattice_type);
972 let _ = writeln!(output, "Dimensions: {:?}", self.dimensions);
973 let _ = writeln!(output, "Lattice Constants: {:?}", self.lattice_constants);
974 let _ = writeln!(output, "Temperature: {:.2} K", self.temperature);
975 let _ = write!(
976 output,
977 "Total Energy: {:.6}\n",
978 self.calculate_total_energy()
979 );
980 let _ = write!(
981 output,
982 "Order Parameter: {:.6}\n",
983 self.calculate_order_parameter()
984 );
985
986 if let Some(field) = self.external_field {
987 let _ = writeln!(output, "External Field: {field:?}");
988 }
989
990 output.push_str("\n# Site Details\n");
991 for (pos, site) in &self.sites {
992 if site.occupation {
993 let _ = write!(output, "Site ({}, {}, {}): ", pos.x, pos.y, pos.z);
994
995 if let Some(ref species) = site.species {
996 let _ = write!(output, "{} ", species.symbol);
997 }
998
999 if let Some(spin) = site.spin {
1000 let _ = write!(
1001 output,
1002 "spin=({:.3}, {:.3}, {:.3}) ",
1003 spin[0], spin[1], spin[2]
1004 );
1005 }
1006
1007 if let Some(ref defect) = site.defect_type {
1008 let _ = write!(output, "defect={defect:?} ");
1009 }
1010
1011 let _ = write!(output, "energy={:.6}", site.local_energy);
1012 output.push('\n');
1013 }
1014 }
1015
1016 Ok(output)
1017 }
1018}
1019
1020pub fn create_benchmark_problems(
1022 size: usize,
1023) -> ApplicationResult<
1024 Vec<Box<dyn OptimizationProblem<Solution = MaterialsLattice, ObjectiveValue = f64>>>,
1025> {
1026 let mut problems = Vec::new();
1027
1028 let dimensions = match size {
1029 s if s <= 10 => [4, 4, 1], s if s <= 50 => [5, 5, 2], _ => [8, 8, 2], };
1033
1034 let lattice_types = [
1036 LatticeType::SimpleCubic,
1037 LatticeType::FaceCenteredCubic,
1038 LatticeType::Graphene,
1039 ];
1040
1041 for (i, &lattice_type) in lattice_types.iter().enumerate() {
1042 let mut lattice = MaterialsLattice::new(lattice_type, dimensions);
1043
1044 let fe_atom = AtomicSpecies::new("Fe".to_string(), 26, 55.845)
1046 .with_magnetic_moment(2.2)
1047 .with_charge(0.0);
1048
1049 for (pos, site) in &mut lattice.sites {
1051 if (pos.x + pos.y + pos.z) % 2 == 0 {
1052 site.species = Some(fe_atom.clone());
1054 site.occupation = true;
1055 site.spin = Some([0.0, 0.0, 1.0]); }
1057 }
1058
1059 if size > 10 {
1061 let defect_pos = LatticePosition::new(2, 2, 0);
1062 lattice.create_defect(defect_pos, DefectType::Vacancy).ok();
1063 }
1064
1065 let mut problem = MaterialsOptimizationProblem::new(lattice);
1066
1067 match i {
1069 0 => problem = problem.add_objective(MaterialsObjective::MinimizeEnergy),
1070 1 => problem = problem.add_objective(MaterialsObjective::OptimizeMagnetism),
1071 2 => problem = problem.add_objective(MaterialsObjective::MinimizeDefects),
1072 _ => problem = problem.add_objective(MaterialsObjective::MaximizeStability),
1073 }
1074
1075 problems.push(Box::new(problem)
1077 as Box<
1078 dyn OptimizationProblem<Solution = MaterialsLattice, ObjectiveValue = f64>,
1079 >);
1080 }
1081
1082 Ok(problems)
1083}
1084
1085#[cfg(test)]
1086mod tests {
1087 use super::*;
1088
1089 #[test]
1090 fn test_lattice_creation() {
1091 let lattice = MaterialsLattice::new(LatticeType::SimpleCubic, [3, 3, 3]);
1092 assert_eq!(lattice.sites.len(), 27);
1093 assert_eq!(lattice.dimensions, [3, 3, 3]);
1094 }
1095
1096 #[test]
1097 fn test_atomic_species() {
1098 let fe_atom = AtomicSpecies::new("Fe".to_string(), 26, 55.845)
1099 .with_magnetic_moment(2.2)
1100 .with_charge(2.0);
1101
1102 assert_eq!(fe_atom.symbol, "Fe");
1103 assert_eq!(fe_atom.atomic_number, 26);
1104 assert_eq!(fe_atom.magnetic_moment, Some(2.2));
1105 assert_eq!(fe_atom.charge, 2.0);
1106 }
1107
1108 #[test]
1109 fn test_lattice_neighbors() {
1110 let pos = LatticePosition::new(1, 1, 1);
1111 let neighbors = pos.neighbors(LatticeType::SimpleCubic);
1112 assert_eq!(neighbors.len(), 6); }
1114
1115 #[test]
1116 fn test_defect_creation() {
1117 let mut lattice = MaterialsLattice::new(LatticeType::SimpleCubic, [2, 2, 2]);
1118 let pos = LatticePosition::new(0, 0, 0);
1119
1120 let result = lattice.create_defect(pos, DefectType::Vacancy);
1121 assert!(result.is_ok());
1122
1123 let site = lattice
1124 .sites
1125 .get(&pos)
1126 .expect("site should exist at position after defect creation");
1127 assert!(!site.occupation);
1128 assert!(site.defect_type.is_some());
1129 }
1130
1131 #[test]
1132 fn test_energy_calculation() {
1133 let mut lattice = MaterialsLattice::new(LatticeType::SimpleCubic, [2, 2, 2]);
1134
1135 let fe_atom = AtomicSpecies::new("Fe".to_string(), 26, 55.845);
1136 let pos = LatticePosition::new(0, 0, 0);
1137
1138 lattice
1139 .add_atom(pos, fe_atom)
1140 .expect("should add Fe atom at position");
1141
1142 let energy = lattice.calculate_total_energy();
1143 assert!(energy >= 0.0); }
1145
1146 #[test]
1147 fn test_problem_validation() {
1148 let lattice = MaterialsLattice::new(LatticeType::SimpleCubic, [3, 3, 3]);
1149 let problem = MaterialsOptimizationProblem::new(lattice);
1150
1151 assert!(problem.validate().is_ok());
1152 }
1153
1154 #[test]
1155 fn test_ising_conversion() {
1156 let lattice = MaterialsLattice::new(LatticeType::SimpleCubic, [2, 2, 2]);
1157 let problem = MaterialsOptimizationProblem::new(lattice);
1158
1159 let (ising, variable_map) = problem
1160 .to_ising_model()
1161 .expect("should convert lattice problem to Ising model");
1162 assert_eq!(ising.num_qubits, 8); assert!(!variable_map.is_empty());
1164 }
1165}