1use super::types_and_defaults::*;
4use crate::quantum_network::distributed_protocols::{
5 self, CircuitPartition, DistributedComputationError, ExecutionRequirements, LoadBalancer,
6 LoadBalancerMetrics, NodeId, NodeInfo, PerformanceHistory, PerformanceMetrics,
7 ResourceRequirements, Result as DistributedResult, TrainingDataPoint,
8};
9use crate::quantum_network::network_optimization::{
10 self as netopt, FeatureVector, FeedbackData, MLModel, ModelMetrics,
11 NetworkOptimizationError as OptimizationError, PredictionResult, Priority, TrainingResult,
12};
13use async_trait::async_trait;
14use chrono::{DateTime, Datelike, Duration as ChronoDuration, Timelike, Utc};
15use serde::{Deserialize, Serialize};
16use std::collections::{BTreeMap, HashMap, VecDeque};
17use std::sync::{Arc, Mutex, RwLock};
18use std::time::Duration;
19use tokio::sync::{mpsc, Semaphore};
20use uuid::Uuid;
21
22impl QuantumLoadBalancingMetricsCollector {
23 pub fn new() -> Self {
24 Self::default()
25 }
26}
27
28impl MLOptimizedQuantumLoadBalancer {
30 pub fn new() -> Self {
32 Self {
33 base_strategy: Arc::new(CapabilityBasedQuantumBalancer::new()),
34 ml_predictor: Arc::new(QuantumLoadPredictionModel::new()),
35 quantum_scheduler: Arc::new(QuantumAwareScheduler::new()),
36 performance_learner: Arc::new(QuantumPerformanceLearner::new()),
37 adaptive_weights: Arc::new(Mutex::new(QuantumLoadBalancingWeights::default())),
38 entanglement_tracker: Arc::new(EntanglementQualityTracker::new()),
39 coherence_monitor: Arc::new(CoherenceTimeMonitor::new()),
40 fidelity_preserver: Arc::new(FidelityPreservationSystem::new()),
41 metrics_collector: Arc::new(QuantumLoadBalancingMetricsCollector::new()),
42 }
43 }
44
45 pub async fn select_optimal_node(
47 &self,
48 available_nodes: &[NodeInfo],
49 circuit_partition: &CircuitPartition,
50 quantum_requirements: &QuantumResourceRequirements,
51 ) -> Result<NodeId> {
52 let features = self
54 .extract_quantum_features(available_nodes, circuit_partition, quantum_requirements)
55 .await?;
56
57 let ml_prediction = self
59 .ml_predictor
60 .predict_optimal_node(available_nodes, circuit_partition, &features)
61 .await?;
62
63 let quantum_constraints = self
65 .evaluate_quantum_constraints(available_nodes, circuit_partition, quantum_requirements)
66 .await?;
67
68 let optimal_node = self
70 .combine_ml_and_quantum_decisions(
71 &ml_prediction,
72 &quantum_constraints,
73 available_nodes,
74 circuit_partition,
75 )
76 .await?;
77
78 self.update_performance_learning(&optimal_node, circuit_partition, quantum_requirements)
80 .await?;
81
82 self.update_quantum_metrics(&optimal_node, &features, &ml_prediction)
84 .await?;
85
86 Ok(optimal_node)
87 }
88
89 async fn extract_quantum_features(
91 &self,
92 available_nodes: &[NodeInfo],
93 circuit_partition: &CircuitPartition,
94 quantum_requirements: &QuantumResourceRequirements,
95 ) -> Result<FeatureVector> {
96 let mut features = HashMap::new();
97
98 features.insert(
100 "circuit_depth".to_string(),
101 circuit_partition.gates.len() as f64,
102 );
103 features.insert(
104 "entanglement_pairs_needed".to_string(),
105 quantum_requirements.entanglement_pairs as f64,
106 );
107 features.insert(
108 "fidelity_requirement".to_string(),
109 quantum_requirements.fidelity_requirement,
110 );
111
112 for (i, node) in available_nodes.iter().enumerate() {
114 let node_prefix = format!("node_{i}");
115
116 features.insert(
118 format!("{node_prefix}_max_qubits"),
119 node.capabilities.max_qubits as f64,
120 );
121 features.insert(
122 format!("{node_prefix}_readout_fidelity"),
123 node.capabilities.readout_fidelity,
124 );
125
126 features.insert(
128 format!("{node_prefix}_qubits_in_use"),
129 node.current_load.qubits_in_use as f64,
130 );
131 features.insert(
132 format!("{node_prefix}_queue_length"),
133 node.current_load.queue_length as f64,
134 );
135
136 if let Some(entanglement_quality) =
138 self.get_node_entanglement_quality(&node.node_id).await?
139 {
140 features.insert(
141 format!("{node_prefix}_entanglement_quality"),
142 entanglement_quality,
143 );
144 }
145
146 if let Some(coherence_metrics) = self.get_node_coherence_metrics(&node.node_id).await? {
147 features.insert(
148 format!("{node_prefix}_avg_coherence_time"),
149 coherence_metrics.average_coherence_time.as_secs_f64(),
150 );
151 }
152 }
153
154 let now = Utc::now();
156 features.insert("hour_of_day".to_string(), now.hour() as f64);
157 features.insert(
158 "day_of_week".to_string(),
159 now.weekday().number_from_monday() as f64,
160 );
161
162 Ok(FeatureVector {
163 features,
164 timestamp: now,
165 context: self
166 .extract_quantum_context(circuit_partition, quantum_requirements)
167 .await?,
168 })
169 }
170
171 async fn evaluate_quantum_constraints(
173 &self,
174 available_nodes: &[NodeInfo],
175 circuit_partition: &CircuitPartition,
176 quantum_requirements: &QuantumResourceRequirements,
177 ) -> Result<QuantumSchedulingConstraints> {
178 let mut constraints = QuantumSchedulingConstraints {
179 entanglement_constraints: HashMap::new(),
180 coherence_constraints: HashMap::new(),
181 fidelity_constraints: HashMap::new(),
182 error_correction_constraints: HashMap::new(),
183 deadline_constraints: HashMap::new(),
184 };
185
186 for node in available_nodes {
187 let entanglement_constraint = self
189 .evaluate_entanglement_constraint(
190 &node.node_id,
191 circuit_partition,
192 quantum_requirements,
193 )
194 .await?;
195
196 constraints
197 .entanglement_constraints
198 .insert(node.node_id.clone(), entanglement_constraint);
199
200 let coherence_constraint = self
202 .evaluate_coherence_constraint(
203 &node.node_id,
204 circuit_partition,
205 quantum_requirements,
206 )
207 .await?;
208
209 constraints
210 .coherence_constraints
211 .insert(node.node_id.clone(), coherence_constraint);
212
213 let fidelity_constraint = self
215 .evaluate_fidelity_constraint(
216 &node.node_id,
217 circuit_partition,
218 quantum_requirements,
219 )
220 .await?;
221
222 constraints
223 .fidelity_constraints
224 .insert(node.node_id.clone(), fidelity_constraint);
225 }
226
227 Ok(constraints)
228 }
229
230 async fn combine_ml_and_quantum_decisions(
232 &self,
233 ml_prediction: &QuantumPredictionResult,
234 quantum_constraints: &QuantumSchedulingConstraints,
235 available_nodes: &[NodeInfo],
236 circuit_partition: &CircuitPartition,
237 ) -> Result<NodeId> {
238 let weights = self
239 .adaptive_weights
240 .lock()
241 .unwrap_or_else(|e| e.into_inner())
242 .clone();
243
244 let mut node_scores: HashMap<NodeId, f64> = HashMap::new();
245
246 for node in available_nodes {
247 let mut score = 0.0;
248
249 let ml_score = if node.node_id == ml_prediction.predicted_node {
251 ml_prediction.confidence
252 } else {
253 0.0
254 };
255
256 let entanglement_score = quantum_constraints
258 .entanglement_constraints
259 .get(&node.node_id)
260 .map_or(0.0, |c| c.quality_score);
261
262 let coherence_score = quantum_constraints
264 .coherence_constraints
265 .get(&node.node_id)
266 .map_or(0.0, |c| c.adequacy_score);
267
268 let fidelity_score = quantum_constraints
270 .fidelity_constraints
271 .get(&node.node_id)
272 .map_or(0.0, |c| c.preservation_score);
273
274 let classical_score = self
276 .calculate_classical_resource_score(node, circuit_partition)
277 .await?;
278
279 score += ml_score * 0.3; score += entanglement_score * weights.entanglement_quality_weight;
282 score += coherence_score * weights.coherence_time_weight;
283 score += fidelity_score * weights.fidelity_preservation_weight;
284 score += classical_score * weights.classical_resources_weight;
285
286 node_scores.insert(node.node_id.clone(), score);
287 }
288
289 let optimal_node = node_scores
291 .into_iter()
292 .max_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal))
293 .map(|(node_id, _)| node_id)
294 .ok_or_else(|| {
295 QuantumLoadBalancingError::QuantumSchedulingConflict(
296 "No suitable node found ".to_string(),
297 )
298 })?;
299
300 Ok(optimal_node)
301 }
302
303 async fn get_node_entanglement_quality(&self, node_id: &NodeId) -> Result<Option<f64>> {
305 let entanglement_states = self
306 .entanglement_tracker
307 .entanglement_states
308 .read()
309 .unwrap_or_else(|e| e.into_inner());
310
311 let quality: f64 = entanglement_states
312 .iter()
313 .filter(|((n1, n2), _)| n1 == node_id || n2 == node_id)
314 .map(|(_, state)| state.current_fidelity)
315 .sum::<f64>()
316 / entanglement_states.len() as f64;
317
318 Ok(if quality > 0.0 { Some(quality) } else { None })
319 }
320
321 async fn get_node_coherence_metrics(
323 &self,
324 node_id: &NodeId,
325 ) -> Result<Option<NodeCoherenceMetrics>> {
326 let coherence_states = self
327 .coherence_monitor
328 .coherence_times
329 .read()
330 .unwrap_or_else(|e| e.into_inner());
331
332 let node_coherence_data: Vec<_> = coherence_states
333 .iter()
334 .filter(|((n, _), _)| n == node_id)
335 .collect();
336
337 if node_coherence_data.is_empty() {
338 return Ok(None);
339 }
340
341 let total_t1: Duration = node_coherence_data
342 .iter()
343 .map(|(_, state)| state.t1_time)
344 .sum();
345
346 let total_t2: Duration = node_coherence_data
347 .iter()
348 .map(|(_, state)| state.t2_time)
349 .sum();
350
351 let count = node_coherence_data.len();
352
353 Ok(Some(NodeCoherenceMetrics {
354 average_coherence_time: total_t1 / count as u32,
355 average_dephasing_time: total_t2 / count as u32,
356 coherence_stability: 0.95, }))
358 }
359
360 async fn extract_quantum_context(
362 &self,
363 circuit_partition: &CircuitPartition,
364 quantum_requirements: &QuantumResourceRequirements,
365 ) -> Result<crate::quantum_network::network_optimization::ContextInfo> {
366 Ok(crate::quantum_network::network_optimization::ContextInfo {
367 network_state: "quantum_active".to_string(),
368 time_of_day: Utc::now().hour() as u8,
369 day_of_week: Utc::now().weekday().number_from_monday() as u8,
370 quantum_experiment_type: Some(
371 self.classify_quantum_experiment(circuit_partition).await?,
372 ),
373 user_priority: Some("high".to_string()), })
375 }
376
377 async fn classify_quantum_experiment(
379 &self,
380 circuit_partition: &CircuitPartition,
381 ) -> Result<String> {
382 let gate_types: Vec<&str> = circuit_partition
384 .gates
385 .iter()
386 .map(|g| g.gate_type.as_str())
387 .collect();
388
389 let experiment_type = if gate_types.contains(&"H") && gate_types.contains(&"CNOT") {
390 "entanglement_experiment"
391 } else if gate_types.iter().any(|&g| g.starts_with('R')) {
392 "variational_algorithm"
393 } else if gate_types.contains(&"QFT") {
394 "quantum_fourier_transform"
395 } else {
396 "general_quantum_computation"
397 };
398
399 Ok(experiment_type.to_string())
400 }
401
402 async fn evaluate_entanglement_constraint(
404 &self,
405 node_id: &NodeId,
406 _circuit_partition: &CircuitPartition,
407 quantum_requirements: &QuantumResourceRequirements,
408 ) -> Result<EntanglementConstraint> {
409 let entanglement_states = self
410 .entanglement_tracker
411 .entanglement_states
412 .read()
413 .unwrap_or_else(|e| e.into_inner());
414
415 let available_quality: f64 = entanglement_states
417 .iter()
418 .filter(|((n1, n2), _)| n1 == node_id || n2 == node_id)
419 .map(|(_, state)| state.current_fidelity)
420 .sum::<f64>()
421 / (entanglement_states.len().max(1) as f64);
422
423 let quality_score = if available_quality >= quantum_requirements.fidelity_requirement {
425 1.0
426 } else {
427 available_quality / quantum_requirements.fidelity_requirement
428 };
429
430 Ok(EntanglementConstraint {
431 available_pairs: entanglement_states.len() as u32,
432 required_pairs: quantum_requirements.entanglement_pairs,
433 quality_score,
434 is_feasible: quality_score >= 0.8, })
436 }
437
438 async fn evaluate_coherence_constraint(
440 &self,
441 node_id: &NodeId,
442 circuit_partition: &CircuitPartition,
443 quantum_requirements: &QuantumResourceRequirements,
444 ) -> Result<CoherenceConstraint> {
445 let coherence_states = self
446 .coherence_monitor
447 .coherence_times
448 .read()
449 .unwrap_or_else(|e| e.into_inner());
450
451 let min_coherence_time = coherence_states
453 .iter()
454 .filter(|((n, _), _)| n == node_id)
455 .map(|(_, state)| state.t2_time.min(state.t1_time))
456 .min()
457 .unwrap_or(Duration::from_secs(0));
458
459 let estimated_execution_time = circuit_partition.estimated_execution_time;
461 let required_coherence_time = quantum_requirements
462 .coherence_time_needed
463 .max(estimated_execution_time);
464
465 let adequacy_score = if min_coherence_time >= required_coherence_time {
467 1.0
468 } else {
469 min_coherence_time.as_secs_f64() / required_coherence_time.as_secs_f64()
470 };
471
472 Ok(CoherenceConstraint {
473 available_coherence_time: min_coherence_time,
474 required_coherence_time,
475 adequacy_score,
476 is_adequate: adequacy_score >= 0.9, })
478 }
479
480 async fn evaluate_fidelity_constraint(
482 &self,
483 node_id: &NodeId,
484 circuit_partition: &CircuitPartition,
485 quantum_requirements: &QuantumResourceRequirements,
486 ) -> Result<FidelityConstraint> {
487 let performance_history = self
489 .performance_learner
490 .performance_history
491 .read()
492 .unwrap_or_else(|e| e.into_inner());
493
494 let fidelity_history = performance_history
495 .get(node_id)
496 .map(|h| &h.fidelity_history)
497 .cloned()
498 .unwrap_or_default();
499
500 let circuit_complexity = circuit_partition.gates.len() as f64;
502 let base_fidelity = if fidelity_history.is_empty() {
503 0.95 } else {
505 fidelity_history
506 .iter()
507 .map(|m| m.process_fidelity)
508 .sum::<f64>()
509 / fidelity_history.len() as f64
510 };
511
512 let expected_fidelity = base_fidelity * (0.99_f64).powf(circuit_complexity / 10.0);
514
515 let preservation_score = if expected_fidelity >= quantum_requirements.fidelity_requirement {
517 1.0
518 } else {
519 expected_fidelity / quantum_requirements.fidelity_requirement
520 };
521
522 Ok(FidelityConstraint {
523 expected_fidelity,
524 required_fidelity: quantum_requirements.fidelity_requirement,
525 preservation_score,
526 can_preserve: preservation_score >= 0.95, })
528 }
529
530 async fn calculate_classical_resource_score(
532 &self,
533 node: &NodeInfo,
534 circuit_partition: &CircuitPartition,
535 ) -> Result<f64> {
536 let cpu_score = 1.0 - node.current_load.cpu_utilization;
537 let memory_score = 1.0 - node.current_load.memory_utilization;
538 let network_score = 1.0 - node.current_load.network_utilization;
539
540 let queue_score = if node.current_load.queue_length == 0 {
542 1.0
543 } else {
544 1.0 / (1.0 + node.current_load.queue_length as f64 / 10.0)
545 };
546
547 let resource_adequacy = if node.capabilities.max_qubits
549 >= circuit_partition.resource_requirements.qubits_needed
550 {
551 1.0
552 } else {
553 node.capabilities.max_qubits as f64
554 / circuit_partition.resource_requirements.qubits_needed as f64
555 };
556
557 let combined_score =
559 (cpu_score + memory_score + network_score + queue_score + resource_adequacy) / 5.0;
560
561 Ok(combined_score)
562 }
563
564 async fn update_performance_learning(
566 &self,
567 selected_node: &NodeId,
568 circuit_partition: &CircuitPartition,
569 quantum_requirements: &QuantumResourceRequirements,
570 ) -> Result<()> {
571 let learning_data = QuantumLearningDataPoint {
573 timestamp: Utc::now(),
574 selected_node: selected_node.clone(),
575 circuit_partition: circuit_partition.clone(),
576 quantum_requirements: quantum_requirements.clone(),
577 context_features: HashMap::new(), };
579
580 let training_data = TrainingDataPoint {
582 features: learning_data.context_features.clone(),
583 target_node: learning_data.selected_node.clone(),
584 actual_performance: PerformanceMetrics {
585 execution_time: Duration::from_millis(100), fidelity: 0.95, success: true, resource_utilization: 0.75, },
590 timestamp: learning_data.timestamp,
591 };
592
593 self.performance_learner
595 .learning_algorithm
596 .add_training_data(training_data)
597 .await?;
598
599 Ok(())
600 }
601
602 async fn update_quantum_metrics(
604 &self,
605 selected_node: &NodeId,
606 features: &FeatureVector,
607 prediction: &QuantumPredictionResult,
608 ) -> Result<()> {
609 let mut quantum_metrics = self
610 .metrics_collector
611 .quantum_metrics
612 .lock()
613 .unwrap_or_else(|e| e.into_inner());
614
615 quantum_metrics.total_quantum_decisions += 1;
616
617 if selected_node == &prediction.predicted_node {
619 quantum_metrics.quantum_advantage_achieved += 0.01; }
622
623 if let Some(fidelity) = features.features.get("fidelity_requirement") {
625 quantum_metrics.fidelity_improvement_factor =
626 (quantum_metrics.fidelity_improvement_factor + fidelity) / 2.0;
627 }
628
629 Ok(())
630 }
631}
632
633#[derive(Debug, Clone)]
635pub struct QuantumSchedulingConstraints {
636 pub entanglement_constraints: HashMap<NodeId, EntanglementConstraint>,
637 pub coherence_constraints: HashMap<NodeId, CoherenceConstraint>,
638 pub fidelity_constraints: HashMap<NodeId, FidelityConstraint>,
639 pub error_correction_constraints: HashMap<NodeId, ErrorCorrectionConstraint>,
640 pub deadline_constraints: HashMap<NodeId, DeadlineConstraint>,
641}
642
643#[derive(Debug, Clone, Serialize, Deserialize)]
645pub struct EntanglementConstraint {
646 pub available_pairs: u32,
647 pub required_pairs: u32,
648 pub quality_score: f64,
649 pub is_feasible: bool,
650}
651
652#[derive(Debug, Clone, Serialize, Deserialize)]
654pub struct CoherenceConstraint {
655 pub available_coherence_time: Duration,
656 pub required_coherence_time: Duration,
657 pub adequacy_score: f64,
658 pub is_adequate: bool,
659}
660
661#[derive(Debug, Clone, Serialize, Deserialize)]
663pub struct FidelityConstraint {
664 pub expected_fidelity: f64,
665 pub required_fidelity: f64,
666 pub preservation_score: f64,
667 pub can_preserve: bool,
668}
669
670#[derive(Debug, Clone, Serialize, Deserialize)]
672pub struct ErrorCorrectionConstraint {
673 pub available_schemes: Vec<String>,
674 pub required_schemes: Vec<String>,
675 pub overhead_factor: f64,
676 pub is_compatible: bool,
677}
678
679#[derive(Debug, Clone, Serialize, Deserialize)]
681pub struct DeadlineConstraint {
682 pub hard_deadline: Option<DateTime<Utc>>,
683 pub soft_deadline: Option<DateTime<Utc>>,
684 pub estimated_completion: DateTime<Utc>,
685 pub can_meet_deadline: bool,
686}
687
688#[derive(Debug, Clone, Serialize, Deserialize)]
690pub struct NodeCoherenceMetrics {
691 pub average_coherence_time: Duration,
692 pub average_dephasing_time: Duration,
693 pub coherence_stability: f64,
694}
695
696#[derive(Debug, Clone, Serialize, Deserialize)]
698pub struct QuantumResourceRequirements {
699 pub qubits_needed: u32,
700 pub gate_count_estimate: u32,
701 pub circuit_depth: u32,
702 pub fidelity_requirement: f64,
703 pub coherence_time_needed: Duration,
704 pub entanglement_pairs: u32,
705}
706
707#[derive(Debug, Clone, Serialize, Deserialize)]
709pub struct QuantumLearningDataPoint {
710 pub timestamp: DateTime<Utc>,
711 pub selected_node: NodeId,
712 pub circuit_partition: CircuitPartition,
713 pub quantum_requirements: QuantumResourceRequirements,
714 pub context_features: HashMap<String, f64>,
715}
716
717#[derive(Debug)]
719pub struct CapabilityBasedQuantumBalancer {
720 pub quantum_capability_weights: HashMap<String, f64>,
721 pub quantum_performance_history: Arc<RwLock<HashMap<NodeId, QuantumPerformanceHistory>>>,
722}
723
724impl Default for CapabilityBasedQuantumBalancer {
725 fn default() -> Self {
726 Self::new()
727 }
728}
729
730impl CapabilityBasedQuantumBalancer {
731 pub fn new() -> Self {
732 let mut weights = HashMap::new();
733 weights.insert("qubit_count".to_string(), 0.3);
734 weights.insert("gate_fidelity".to_string(), 0.4);
735 weights.insert("coherence_time".to_string(), 0.3);
736
737 Self {
738 quantum_capability_weights: weights,
739 quantum_performance_history: Arc::new(RwLock::new(HashMap::new())),
740 }
741 }
742}
743
744#[async_trait]
745impl LoadBalancer for CapabilityBasedQuantumBalancer {
746 async fn select_node(
747 &self,
748 available_nodes: &[NodeInfo],
749 requirements: &ResourceRequirements,
750 ) -> std::result::Result<
751 NodeId,
752 crate::quantum_network::distributed_protocols::DistributedComputationError,
753 > {
754 let mut best_node = None;
756 let mut best_score = 0.0;
757
758 for node in available_nodes {
759 let mut score = 0.0;
760
761 let qubit_score = if node.capabilities.max_qubits >= requirements.qubits_needed {
763 1.0
764 } else {
765 node.capabilities.max_qubits as f64 / requirements.qubits_needed as f64
766 };
767
768 let fidelity_score = node.capabilities.readout_fidelity;
769
770 let load_score = 1.0
772 - (node.current_load.qubits_in_use as f64 / node.capabilities.max_qubits as f64);
773
774 score = qubit_score * self.quantum_capability_weights["qubit_count"]
775 + fidelity_score * self.quantum_capability_weights["gate_fidelity"]
776 + load_score * 0.3; if score > best_score {
779 best_score = score;
780 best_node = Some(node.node_id.clone());
781 }
782 }
783
784 best_node.ok_or_else(||
785 crate::quantum_network::distributed_protocols::DistributedComputationError::ResourceAllocation(
786 "No suitable node found ".to_string()
787 )
788 )
789 }
790
791 async fn update_node_metrics(
792 &self,
793 node_id: &NodeId,
794 metrics: &PerformanceMetrics,
795 ) -> std::result::Result<
796 (),
797 crate::quantum_network::distributed_protocols::DistributedComputationError,
798 > {
799 let mut history = self
801 .quantum_performance_history
802 .write()
803 .unwrap_or_else(|e| e.into_inner());
804 if !history.contains_key(node_id) {
805 history.insert(node_id.clone(), QuantumPerformanceHistory::default());
806 }
807
808 let Some(node_history) = history.get_mut(node_id) else {
810 return Ok(());
811 };
812
813 node_history
815 .classical_metrics
816 .execution_times
817 .push_back(metrics.execution_time);
818 if node_history.classical_metrics.execution_times.len() > 100 {
819 node_history.classical_metrics.execution_times.pop_front();
820 }
821
822 node_history.classical_metrics.success_rate = node_history
823 .classical_metrics
824 .success_rate
825 .mul_add(0.9, if metrics.success { 1.0 } else { 0.0 } * 0.1);
826
827 Ok(())
828 }
829
830 fn get_balancer_metrics(&self) -> LoadBalancerMetrics {
831 LoadBalancerMetrics {
832 total_decisions: 0, average_decision_time: Duration::from_millis(10),
834 prediction_accuracy: 0.85,
835 load_distribution_variance: 0.15,
836 total_requests: 0,
837 successful_allocations: 0,
838 failed_allocations: 0,
839 average_response_time: Duration::from_millis(5),
840 node_utilization: HashMap::new(),
841 }
842 }
843
844 fn select_nodes(
852 &self,
853 partitions: &[CircuitPartition],
854 available_nodes: &HashMap<NodeId, NodeInfo>,
855 _requirements: &ExecutionRequirements,
856 ) -> std::result::Result<HashMap<Uuid, NodeId>, DistributedComputationError> {
857 if available_nodes.is_empty() {
858 return Err(DistributedComputationError::ResourceAllocation(
859 "no available nodes for partition assignment".to_string(),
860 ));
861 }
862
863 let qubit_weight = self
864 .quantum_capability_weights
865 .get("qubit_count")
866 .copied()
867 .unwrap_or(0.3);
868 let fidelity_weight = self
869 .quantum_capability_weights
870 .get("gate_fidelity")
871 .copied()
872 .unwrap_or(0.4);
873
874 let mut allocation = HashMap::new();
875 let mut assigned_qubits: HashMap<NodeId, u32> = HashMap::new();
876
877 for partition in partitions {
878 let required_qubits = partition.resource_requirements.qubits_needed.max(1);
879 let mut best_node: Option<NodeId> = None;
880 let mut best_score = f64::NEG_INFINITY;
881
882 for (node_id, node) in available_nodes {
883 if node.capabilities.max_qubits < required_qubits {
884 continue;
885 }
886 let already_assigned = *assigned_qubits.get(node_id).unwrap_or(&0);
887 let projected_load = already_assigned + required_qubits;
888 let max_qubits = node.capabilities.max_qubits.max(1);
889 let utilization = projected_load as f64 / max_qubits as f64;
890 if utilization > 1.0 {
891 continue;
894 }
895
896 let qubit_score =
897 (node.capabilities.max_qubits as f64 / required_qubits as f64).min(1.0);
898 let fidelity_score = node.capabilities.readout_fidelity;
899 let load_score = 1.0 - utilization;
900
901 let score = qubit_score * qubit_weight
902 + fidelity_score * fidelity_weight
903 + load_score * 0.3;
904
905 if score > best_score {
906 best_score = score;
907 best_node = Some(node_id.clone());
908 }
909 }
910
911 let Some(node_id) = best_node else {
912 return Err(DistributedComputationError::ResourceAllocation(format!(
913 "no node has sufficient remaining capacity ({required_qubits} qubits) for partition {}",
914 partition.partition_id
915 )));
916 };
917
918 *assigned_qubits.entry(node_id.clone()).or_insert(0) += required_qubits;
919 allocation.insert(partition.partition_id, node_id);
920 }
921
922 Ok(allocation)
923 }
924
925 fn rebalance_load(
926 &self,
927 current_allocation: &HashMap<Uuid, NodeId>,
928 nodes: &HashMap<NodeId, NodeInfo>,
929 ) -> Option<HashMap<Uuid, NodeId>> {
930 None }
932
933 fn predict_execution_time(&self, partition: &CircuitPartition, node: &NodeInfo) -> Duration {
934 Duration::from_millis(partition.gates.len() as u64 * 15) }
936}
937
938impl Default for QuantumLoadBalancingWeights {
939 fn default() -> Self {
940 Self {
941 entanglement_quality_weight: 0.25,
942 coherence_time_weight: 0.25,
943 fidelity_preservation_weight: 0.20,
944 classical_resources_weight: 0.15,
945 network_latency_weight: 0.10,
946 error_correction_weight: 0.03,
947 fairness_weight: 0.02,
948 dynamic_adjustment_enabled: true,
949 }
950 }
951}
952
953impl Default for QuantumPerformanceHistory {
954 fn default() -> Self {
955 Self {
956 classical_metrics: PerformanceHistory {
957 execution_times: VecDeque::new(),
958 success_rate: 0.95,
959 average_fidelity: 0.90,
960 last_updated: Utc::now(),
961 },
962 fidelity_history: VecDeque::new(),
963 coherence_measurements: VecDeque::new(),
964 entanglement_measurements: VecDeque::new(),
965 error_rate_history: VecDeque::new(),
966 gate_statistics: HashMap::new(),
967 }
968 }
969}
970
971impl Default for QuantumLoadPredictionModel {
975 fn default() -> Self {
976 Self {
977 model: Arc::new(Mutex::new(Box::new(SimpleMLModel::new()))),
978 feature_extractor: Arc::new(QuantumFeatureExtractor::default()),
979 prediction_cache: Arc::new(RwLock::new(HashMap::new())),
980 training_collector: Arc::new(QuantumTrainingDataCollector::default()),
981 performance_tracker: Arc::new(ModelPerformanceTracker::default()),
982 }
983 }
984}
985
986impl Default for QuantumAwareScheduler {
987 fn default() -> Self {
988 Self {
989 entanglement_aware_scheduling: true,
990 coherence_time_optimization: true,
991 fidelity_preservation_priority: true,
992 error_correction_scheduler: Arc::new(ErrorCorrectionScheduler::default()),
993 deadline_scheduler: Arc::new(QuantumDeadlineScheduler::default()),
994 urgency_evaluator: Arc::new(QuantumUrgencyEvaluator::default()),
995 entanglement_resolver: Arc::new(EntanglementDependencyResolver::default()),
996 gate_conflict_resolver: Arc::new(QuantumGateConflictResolver::default()),
997 }
998 }
999}
1000
1001impl QuantumAwareScheduler {
1002 pub fn new() -> Self {
1003 Self::default()
1004 }
1005}
1006
1007impl Default for QuantumPerformanceLearner {
1008 fn default() -> Self {
1009 Self {
1010 performance_history: Arc::new(RwLock::new(HashMap::new())),
1011 learning_algorithm: Arc::new(QuantumReinforcementLearning::default()),
1012 adaptation_strategy: Arc::new(QuantumAdaptationStrategy::default()),
1013 feedback_processor: Arc::new(QuantumFeedbackProcessor::default()),
1014 }
1015 }
1016}
1017
1018impl Default for EntanglementQualityTracker {
1019 fn default() -> Self {
1020 Self {
1021 entanglement_states: Arc::new(RwLock::new(HashMap::new())),
1022 quality_thresholds: Arc::new(EntanglementQualityThresholds::default()),
1023 quality_predictor: Arc::new(EntanglementQualityPredictor::default()),
1024 quality_optimizer: Arc::new(EntanglementQualityOptimizer::default()),
1025 }
1026 }
1027}
1028
1029impl EntanglementQualityTracker {
1030 pub fn new() -> Self {
1031 Self::default()
1032 }
1033}
1034
1035impl Default for CoherenceTimeMonitor {
1036 fn default() -> Self {
1037 Self {
1038 coherence_times: Arc::new(RwLock::new(HashMap::new())),
1039 coherence_predictor: Arc::new(CoherenceTimePredictor::default()),
1040 coherence_optimizer: Arc::new(CoherenceTimeOptimizer::default()),
1041 real_time_monitor: Arc::new(RealTimeCoherenceMonitor::default()),
1042 }
1043 }
1044}
1045
1046impl CoherenceTimeMonitor {
1047 pub fn new() -> Self {
1048 Self::default()
1049 }
1050}
1051
1052impl Default for FidelityPreservationSystem {
1053 fn default() -> Self {
1054 Self {
1055 fidelity_tracker: Arc::new(FidelityTracker::default()),
1056 preservation_strategies: Arc::new(FidelityPreservationStrategies::default()),
1057 error_mitigation: Arc::new(ErrorMitigationCoordinator::default()),
1058 optimization_scheduler: Arc::new(FidelityOptimizationScheduler::default()),
1059 }
1060 }
1061}
1062
1063impl FidelityPreservationSystem {
1064 pub fn new() -> Self {
1065 Self::default()
1066 }
1067}
1068
1069impl Default for QuantumLoadBalancingMetricsCollector {
1070 fn default() -> Self {
1071 Self {
1072 classical_metrics: Arc::new(Mutex::new(LoadBalancerMetrics {
1073 total_decisions: 0,
1074 average_decision_time: Duration::from_millis(10),
1075 prediction_accuracy: 0.95,
1076 load_distribution_variance: 0.1,
1077 total_requests: 0,
1078 successful_allocations: 0,
1079 failed_allocations: 0,
1080 average_response_time: Duration::from_millis(5),
1081 node_utilization: HashMap::new(),
1082 })),
1083 quantum_metrics: Arc::new(Mutex::new(QuantumLoadBalancingMetrics::default())),
1084 performance_tracker: Arc::new(RealTimeQuantumPerformanceTracker::default()),
1085 metrics_aggregator: Arc::new(QuantumMetricsAggregator::default()),
1086 }
1087 }
1088}
1089
1090impl Default for QuantumLoadBalancingMetrics {
1091 fn default() -> Self {
1092 Self {
1093 total_quantum_decisions: 0,
1094 average_quantum_decision_time: Duration::from_millis(15),
1095 entanglement_preservation_rate: 0.9,
1096 coherence_utilization_efficiency: 0.85,
1097 fidelity_improvement_factor: 1.1,
1098 quantum_advantage_achieved: 0.2,
1099 error_correction_overhead_ratio: 0.15,
1100 quantum_fairness_index: 0.95,
1101 }
1102 }
1103}
1104
1105impl Default for EntanglementQualityThresholds {
1106 fn default() -> Self {
1107 Self {
1108 min_fidelity: 0.8,
1109 warning_fidelity: 0.85,
1110 optimal_fidelity: 0.95,
1111 max_decay_rate: 0.05,
1112 min_lifetime: Duration::from_millis(100),
1113 }
1114 }
1115}
1116
1117#[derive(Debug)]
1119pub struct SimpleMLModel {
1120 pub model_type: String,
1121}
1122
1123impl Default for SimpleMLModel {
1124 fn default() -> Self {
1125 Self::new()
1126 }
1127}
1128
1129impl SimpleMLModel {
1130 pub fn new() -> Self {
1131 Self {
1132 model_type: "simple_stub".to_string(),
1133 }
1134 }
1135}
1136
1137#[async_trait]
1138impl crate::quantum_network::network_optimization::MLModel for SimpleMLModel {
1139 async fn predict(
1140 &self,
1141 _features: &FeatureVector,
1142 ) -> std::result::Result<PredictionResult, OptimizationError> {
1143 Ok(PredictionResult {
1144 predicted_values: HashMap::new(),
1145 confidence_intervals: HashMap::new(),
1146 uncertainty_estimate: 0.1,
1147 prediction_timestamp: Utc::now(),
1148 })
1149 }
1150
1151 async fn train(
1152 &mut self,
1153 _training_data: &[TrainingDataPoint],
1154 ) -> std::result::Result<TrainingResult, OptimizationError> {
1155 Ok(TrainingResult {
1156 training_accuracy: 0.85,
1157 validation_accuracy: 0.8,
1158 loss_value: 0.2,
1159 training_duration: Duration::from_secs(10),
1160 model_size_bytes: 1024,
1161 })
1162 }
1163
1164 async fn update_weights(
1165 &mut self,
1166 _feedback: &FeedbackData,
1167 ) -> std::result::Result<(), OptimizationError> {
1168 Ok(())
1169 }
1170
1171 fn get_model_metrics(&self) -> ModelMetrics {
1172 ModelMetrics {
1173 accuracy: 0.85,
1174 precision: 0.8,
1175 recall: 0.9,
1176 f1_score: 0.84,
1177 mae: 0.15,
1178 rmse: 0.2,
1179 }
1180 }
1181}
1182
1183impl QuantumLoadPredictionModel {
1187 pub fn new() -> Self {
1188 Self::default()
1189 }
1190
1191 pub async fn predict_optimal_node(
1201 &self,
1202 available_nodes: &[NodeInfo],
1203 circuit_partition: &CircuitPartition,
1204 features: &FeatureVector,
1205 ) -> Result<QuantumPredictionResult> {
1206 if available_nodes.is_empty() {
1207 return Err(QuantumLoadBalancingError::QuantumSchedulingConflict(
1208 "no available nodes to predict from".to_string(),
1209 ));
1210 }
1211 let required_qubits = circuit_partition.input_qubits.len().max(1) as f64;
1212
1213 let mut best_index: Option<usize> = None;
1214 let mut best_score = f64::NEG_INFINITY;
1215 let mut best_fidelity = 0.95_f64;
1216
1217 for (i, node) in available_nodes.iter().enumerate() {
1218 let prefix = format!("node_{i}");
1219 let get = |suffix: &str, default: f64| -> f64 {
1220 features
1221 .features
1222 .get(&format!("{prefix}_{suffix}"))
1223 .copied()
1224 .unwrap_or(default)
1225 };
1226
1227 let max_qubits = get("max_qubits", node.capabilities.max_qubits as f64);
1228 if max_qubits < required_qubits {
1229 continue;
1230 }
1231 let readout_fidelity = get("readout_fidelity", node.capabilities.readout_fidelity);
1232 let qubits_in_use = get("qubits_in_use", node.current_load.qubits_in_use as f64);
1233 let queue_length = get("queue_length", node.current_load.queue_length as f64);
1234 let entanglement_quality = get("entanglement_quality", 0.5);
1238 let coherence_time = get("avg_coherence_time", 50e-6);
1239
1240 let utilization = if max_qubits > 0.0 {
1241 (qubits_in_use / max_qubits).clamp(0.0, 1.0)
1242 } else {
1243 1.0
1244 };
1245 let queue_penalty = 1.0 / (1.0 + queue_length.max(0.0));
1246 let coherence_score = (coherence_time / 100e-6).clamp(0.0, 1.0);
1247
1248 let score = readout_fidelity * 0.35
1249 + entanglement_quality * 0.25
1250 + coherence_score * 0.15
1251 + queue_penalty * 0.15
1252 + (1.0 - utilization) * 0.10;
1253
1254 if score > best_score {
1255 best_score = score;
1256 best_index = Some(i);
1257 best_fidelity = readout_fidelity;
1258 }
1259 }
1260
1261 let Some(best_index) = best_index else {
1262 return Err(QuantumLoadBalancingError::QuantumSchedulingConflict(
1263 format!(
1264 "no node has sufficient qubit capacity ({required_qubits}) for this partition"
1265 ),
1266 ));
1267 };
1268
1269 let predicted_node = available_nodes[best_index].node_id.clone();
1270 let confidence = best_score.clamp(0.0, 1.0);
1271 let predicted_entanglement_overhead = (required_qubits / 2.0).ceil() as u32;
1272
1273 Ok(QuantumPredictionResult {
1274 predicted_node,
1275 predicted_execution_time: Duration::from_millis((100.0 / confidence.max(0.1)) as u64),
1276 predicted_fidelity: best_fidelity,
1277 predicted_entanglement_overhead,
1278 confidence,
1279 quantum_uncertainty: QuantumUncertaintyFactors {
1280 decoherence_uncertainty: (1.0 - confidence) * 0.1,
1281 entanglement_uncertainty: (1.0 - confidence) * 0.06,
1282 measurement_uncertainty: (1.0 - confidence) * 0.04,
1283 calibration_uncertainty: (1.0 - confidence) * 0.02,
1284 },
1285 prediction_timestamp: Utc::now(),
1286 })
1287 }
1288}
1289
1290impl QuantumPerformanceLearner {
1291 pub fn new() -> Self {
1292 Self::default()
1293 }
1294
1295 pub async fn add_training_data(&self, data: QuantumLearningDataPoint) -> Result<()> {
1300 let mut history = self
1301 .performance_history
1302 .write()
1303 .unwrap_or_else(std::sync::PoisonError::into_inner);
1304 let node_history = history
1305 .entry(data.selected_node.clone())
1306 .or_insert_with(QuantumPerformanceHistory::default);
1307
1308 node_history
1309 .classical_metrics
1310 .execution_times
1311 .push_back(data.circuit_partition.estimated_execution_time);
1312 if node_history.classical_metrics.execution_times.len() > 100 {
1313 node_history.classical_metrics.execution_times.pop_front();
1314 }
1315
1316 node_history
1317 .fidelity_history
1318 .push_back(FidelityMeasurement {
1319 timestamp: data.timestamp,
1320 process_fidelity: data.quantum_requirements.fidelity_requirement,
1321 state_fidelity: data.quantum_requirements.fidelity_requirement,
1322 gate_fidelities: HashMap::new(),
1323 measurement_context: FidelityMeasurementContext {
1324 temperature: data
1325 .context_features
1326 .get("temperature")
1327 .copied()
1328 .unwrap_or(0.0),
1329 time_since_calibration: Duration::from_secs(0),
1330 circuit_depth: data.circuit_partition.gates.len() as u32,
1331 concurrent_operations: 0,
1332 },
1333 });
1334 if node_history.fidelity_history.len() > 100 {
1335 node_history.fidelity_history.pop_front();
1336 }
1337
1338 Ok(())
1339 }
1340}
1341
1342#[cfg(test)]
1344mod tests {
1345 use super::*;
1346
1347 #[tokio::test]
1348 async fn test_quantum_load_balancer_creation() {
1349 let balancer = MLOptimizedQuantumLoadBalancer::new();
1350 assert!(
1351 !balancer
1352 .adaptive_weights
1353 .lock()
1354 .expect("Mutex should not be poisoned")
1355 .dynamic_adjustment_enabled
1356 || balancer
1357 .adaptive_weights
1358 .lock()
1359 .expect("Mutex should not be poisoned")
1360 .dynamic_adjustment_enabled
1361 );
1362 }
1363
1364 #[tokio::test]
1365 async fn test_quantum_feature_extraction() {
1366 let balancer = MLOptimizedQuantumLoadBalancer::new();
1367
1368 let nodes = vec![NodeInfo {
1369 node_id: NodeId("test_node".to_string()),
1370 capabilities: crate::quantum_network::distributed_protocols::NodeCapabilities {
1371 max_qubits: 10,
1372 supported_gates: vec!["H".to_string(), "CNOT".to_string()],
1373 connectivity_graph: vec![(0, 1), (1, 2)],
1374 gate_fidelities: HashMap::new(),
1375 readout_fidelity: 0.95,
1376 coherence_times: HashMap::new(),
1377 classical_compute_power: 1000.0,
1378 memory_capacity_gb: 8,
1379 network_bandwidth_mbps: 1000.0,
1380 },
1381 current_load: crate::quantum_network::distributed_protocols::NodeLoad {
1382 qubits_in_use: 3,
1383 active_circuits: 2,
1384 cpu_utilization: 0.4,
1385 memory_utilization: 0.3,
1386 network_utilization: 0.2,
1387 queue_length: 1,
1388 estimated_completion_time: Duration::from_secs(30),
1389 },
1390 network_info: crate::quantum_network::distributed_protocols::NetworkInfo {
1391 ip_address: "192.168.1.100".to_string(),
1392 port: 8080,
1393 latency_to_nodes: HashMap::new(),
1394 bandwidth_to_nodes: HashMap::new(),
1395 connection_quality: HashMap::new(),
1396 },
1397 status: crate::quantum_network::distributed_protocols::NodeStatus::Active,
1398 last_heartbeat: Utc::now(),
1399 }];
1400
1401 let circuit_partition = CircuitPartition {
1402 partition_id: Uuid::new_v4(),
1403 node_id: NodeId("test".to_string()),
1404 gates: vec![],
1405 dependencies: vec![],
1406 input_qubits: vec![],
1407 output_qubits: vec![],
1408 classical_inputs: vec![],
1409 estimated_execution_time: Duration::from_millis(100),
1410 resource_requirements: ResourceRequirements {
1411 qubits_needed: 5,
1412 gates_count: 10,
1413 memory_mb: 50,
1414 execution_time_estimate: Duration::from_millis(100),
1415 entanglement_pairs_needed: 2,
1416 classical_communication_bits: 100,
1417 },
1418 };
1419
1420 let quantum_requirements = QuantumResourceRequirements {
1421 qubits_needed: 5,
1422 gate_count_estimate: 10,
1423 circuit_depth: 5,
1424 fidelity_requirement: 0.9,
1425 coherence_time_needed: Duration::from_micros(100),
1426 entanglement_pairs: 2,
1427 };
1428
1429 let features = balancer
1430 .extract_quantum_features(&nodes, &circuit_partition, &quantum_requirements)
1431 .await;
1432 assert!(features.is_ok());
1433
1434 let feature_vector = features.expect("Feature extraction should succeed");
1435 assert!(!feature_vector.features.is_empty());
1436 assert!(feature_vector.features.contains_key("circuit_depth"));
1437 assert!(feature_vector
1438 .features
1439 .contains_key("entanglement_pairs_needed"));
1440 }
1441
1442 #[tokio::test]
1443 async fn test_capability_based_quantum_balancer() {
1444 let balancer = CapabilityBasedQuantumBalancer::new();
1445
1446 let nodes = vec![
1447 NodeInfo {
1448 node_id: NodeId("high_capability_node".to_string()),
1449 capabilities: crate::quantum_network::distributed_protocols::NodeCapabilities {
1450 max_qubits: 20,
1451 supported_gates: vec!["H".to_string(), "CNOT".to_string(), "T".to_string()],
1452 connectivity_graph: vec![],
1453 gate_fidelities: HashMap::new(),
1454 readout_fidelity: 0.98,
1455 coherence_times: HashMap::new(),
1456 classical_compute_power: 2000.0,
1457 memory_capacity_gb: 16,
1458 network_bandwidth_mbps: 2000.0,
1459 },
1460 current_load: crate::quantum_network::distributed_protocols::NodeLoad {
1461 qubits_in_use: 5,
1462 active_circuits: 1,
1463 cpu_utilization: 0.2,
1464 memory_utilization: 0.1,
1465 network_utilization: 0.1,
1466 queue_length: 0,
1467 estimated_completion_time: Duration::from_secs(10),
1468 },
1469 network_info: crate::quantum_network::distributed_protocols::NetworkInfo {
1470 ip_address: "192.168.1.101".to_string(),
1471 port: 8080,
1472 latency_to_nodes: HashMap::new(),
1473 bandwidth_to_nodes: HashMap::new(),
1474 connection_quality: HashMap::new(),
1475 },
1476 status: crate::quantum_network::distributed_protocols::NodeStatus::Active,
1477 last_heartbeat: Utc::now(),
1478 },
1479 NodeInfo {
1480 node_id: NodeId("low_capability_node".to_string()),
1481 capabilities: crate::quantum_network::distributed_protocols::NodeCapabilities {
1482 max_qubits: 5,
1483 supported_gates: vec!["H".to_string(), "CNOT".to_string()],
1484 connectivity_graph: vec![],
1485 gate_fidelities: HashMap::new(),
1486 readout_fidelity: 0.90,
1487 coherence_times: HashMap::new(),
1488 classical_compute_power: 500.0,
1489 memory_capacity_gb: 4,
1490 network_bandwidth_mbps: 500.0,
1491 },
1492 current_load: crate::quantum_network::distributed_protocols::NodeLoad {
1493 qubits_in_use: 4,
1494 active_circuits: 2,
1495 cpu_utilization: 0.8,
1496 memory_utilization: 0.7,
1497 network_utilization: 0.6,
1498 queue_length: 3,
1499 estimated_completion_time: Duration::from_secs(60),
1500 },
1501 network_info: crate::quantum_network::distributed_protocols::NetworkInfo {
1502 ip_address: "192.168.1.102".to_string(),
1503 port: 8080,
1504 latency_to_nodes: HashMap::new(),
1505 bandwidth_to_nodes: HashMap::new(),
1506 connection_quality: HashMap::new(),
1507 },
1508 status: crate::quantum_network::distributed_protocols::NodeStatus::Active,
1509 last_heartbeat: Utc::now(),
1510 },
1511 ];
1512
1513 let requirements = ResourceRequirements {
1514 qubits_needed: 10,
1515 gates_count: 20,
1516 memory_mb: 100,
1517 execution_time_estimate: Duration::from_millis(200),
1518 entanglement_pairs_needed: 3,
1519 classical_communication_bits: 500,
1520 };
1521
1522 let selected_node = balancer.select_node(&nodes, &requirements).await;
1523 assert!(selected_node.is_ok());
1524
1525 let node_id = selected_node.expect("Node selection should succeed");
1527 assert_eq!(node_id.0, "high_capability_node");
1528 }
1529
1530 fn make_test_node(
1531 id: &str,
1532 max_qubits: u32,
1533 readout_fidelity: f64,
1534 qubits_in_use: u32,
1535 queue_length: u32,
1536 ) -> NodeInfo {
1537 NodeInfo {
1538 node_id: NodeId(id.to_string()),
1539 capabilities: crate::quantum_network::distributed_protocols::NodeCapabilities {
1540 max_qubits,
1541 supported_gates: vec!["H".to_string(), "CNOT".to_string()],
1542 connectivity_graph: vec![],
1543 gate_fidelities: HashMap::new(),
1544 readout_fidelity,
1545 coherence_times: HashMap::new(),
1546 classical_compute_power: 1000.0,
1547 memory_capacity_gb: 8,
1548 network_bandwidth_mbps: 1000.0,
1549 },
1550 current_load: crate::quantum_network::distributed_protocols::NodeLoad {
1551 qubits_in_use,
1552 active_circuits: 1,
1553 cpu_utilization: 0.2,
1554 memory_utilization: 0.2,
1555 network_utilization: 0.2,
1556 queue_length,
1557 estimated_completion_time: Duration::from_secs(10),
1558 },
1559 network_info: crate::quantum_network::distributed_protocols::NetworkInfo {
1560 ip_address: "127.0.0.1".to_string(),
1561 port: 8080,
1562 latency_to_nodes: HashMap::new(),
1563 bandwidth_to_nodes: HashMap::new(),
1564 connection_quality: HashMap::new(),
1565 },
1566 status: crate::quantum_network::distributed_protocols::NodeStatus::Active,
1567 last_heartbeat: Utc::now(),
1568 }
1569 }
1570
1571 fn make_test_partition(qubits_needed: u32) -> CircuitPartition {
1572 let node_id = NodeId("unassigned".to_string());
1573 CircuitPartition {
1574 partition_id: Uuid::new_v4(),
1575 node_id: node_id.clone(),
1576 gates: vec![],
1577 dependencies: vec![],
1578 input_qubits: (0..qubits_needed)
1579 .map(|i| crate::quantum_network::distributed_protocols::QubitId {
1580 node_id: node_id.clone(),
1581 local_id: i,
1582 global_id: Uuid::new_v4(),
1583 })
1584 .collect(),
1585 output_qubits: vec![],
1586 classical_inputs: vec![],
1587 estimated_execution_time: Duration::from_millis(100),
1588 resource_requirements: ResourceRequirements {
1589 qubits_needed,
1590 gates_count: 10,
1591 memory_mb: 50,
1592 execution_time_estimate: Duration::from_millis(100),
1593 entanglement_pairs_needed: 1,
1594 classical_communication_bits: 100,
1595 },
1596 }
1597 }
1598
1599 #[tokio::test]
1600 async fn test_predict_optimal_node_not_fixed_to_node1() {
1601 let predictor = QuantumLoadPredictionModel::new();
1602 let balancer = MLOptimizedQuantumLoadBalancer::new();
1603
1604 let nodes = vec![
1610 make_test_node("best_node", 20, 0.99, 0, 0),
1611 make_test_node("weak_node", 20, 0.5, 18, 20),
1612 ];
1613 let partition = make_test_partition(2);
1614 let quantum_requirements = QuantumResourceRequirements {
1615 qubits_needed: 2,
1616 gate_count_estimate: 5,
1617 circuit_depth: 5,
1618 fidelity_requirement: 0.9,
1619 coherence_time_needed: Duration::from_micros(50),
1620 entanglement_pairs: 1,
1621 };
1622 let features = balancer
1623 .extract_quantum_features(&nodes, &partition, &quantum_requirements)
1624 .await
1625 .expect("feature extraction should succeed");
1626
1627 let prediction = predictor
1628 .predict_optimal_node(&nodes, &partition, &features)
1629 .await
1630 .expect("prediction should succeed");
1631 assert_eq!(prediction.predicted_node.0, "best_node");
1632
1633 let nodes_swapped = vec![
1636 make_test_node("weak_node", 20, 0.5, 18, 20),
1637 make_test_node("best_node", 20, 0.99, 0, 0),
1638 ];
1639 let features_swapped = balancer
1640 .extract_quantum_features(&nodes_swapped, &partition, &quantum_requirements)
1641 .await
1642 .expect("feature extraction should succeed");
1643 let prediction_swapped = predictor
1644 .predict_optimal_node(&nodes_swapped, &partition, &features_swapped)
1645 .await
1646 .expect("prediction should succeed");
1647 assert_eq!(prediction_swapped.predicted_node.0, "best_node");
1648 }
1649
1650 #[tokio::test]
1651 async fn test_predict_optimal_node_respects_qubit_capacity() {
1652 let predictor = QuantumLoadPredictionModel::new();
1653 let balancer = MLOptimizedQuantumLoadBalancer::new();
1654
1655 let nodes = vec![
1658 make_test_node("tiny_node", 2, 0.999, 0, 0),
1659 make_test_node("adequate_node", 10, 0.8, 2, 1),
1660 ];
1661 let partition = make_test_partition(8);
1662 let quantum_requirements = QuantumResourceRequirements {
1663 qubits_needed: 8,
1664 gate_count_estimate: 20,
1665 circuit_depth: 10,
1666 fidelity_requirement: 0.7,
1667 coherence_time_needed: Duration::from_micros(50),
1668 entanglement_pairs: 2,
1669 };
1670 let features = balancer
1671 .extract_quantum_features(&nodes, &partition, &quantum_requirements)
1672 .await
1673 .expect("feature extraction should succeed");
1674
1675 let prediction = predictor
1676 .predict_optimal_node(&nodes, &partition, &features)
1677 .await
1678 .expect("prediction should succeed");
1679 assert_eq!(prediction.predicted_node.0, "adequate_node");
1680 }
1681
1682 #[tokio::test]
1683 async fn test_add_training_data_is_actually_recorded() {
1684 let learner = QuantumPerformanceLearner::new();
1685 let node_id = NodeId("trained_node".to_string());
1686 let data_point = QuantumLearningDataPoint {
1687 timestamp: Utc::now(),
1688 selected_node: node_id.clone(),
1689 circuit_partition: make_test_partition(4),
1690 quantum_requirements: QuantumResourceRequirements {
1691 qubits_needed: 4,
1692 gate_count_estimate: 8,
1693 circuit_depth: 4,
1694 fidelity_requirement: 0.92,
1695 coherence_time_needed: Duration::from_micros(40),
1696 entanglement_pairs: 1,
1697 },
1698 context_features: HashMap::new(),
1699 };
1700
1701 learner
1702 .add_training_data(data_point)
1703 .await
1704 .expect("add_training_data should succeed");
1705
1706 let history = learner
1709 .performance_history
1710 .read()
1711 .expect("history lock should not be poisoned");
1712 let node_history = history
1713 .get(&node_id)
1714 .expect("training data should have created a history entry for the node");
1715 assert_eq!(node_history.fidelity_history.len(), 1);
1716 assert_eq!(node_history.classical_metrics.execution_times.len(), 1);
1717 }
1718
1719 #[tokio::test]
1720 async fn test_select_nodes_load_balances_across_partitions() {
1721 let balancer = CapabilityBasedQuantumBalancer::new();
1722 let mut nodes = HashMap::new();
1723 nodes.insert(
1724 NodeId("node_a".to_string()),
1725 make_test_node("node_a", 8, 0.9, 0, 0),
1726 );
1727 nodes.insert(
1728 NodeId("node_b".to_string()),
1729 make_test_node("node_b", 8, 0.9, 0, 0),
1730 );
1731
1732 let partitions = vec![make_test_partition(6), make_test_partition(6)];
1738 let requirements = ExecutionRequirements {
1739 min_fidelity: 0.5,
1740 max_latency: Duration::from_secs(10),
1741 fault_tolerance: false,
1742 preferred_nodes: vec![],
1743 excluded_nodes: vec![],
1744 resource_constraints:
1745 crate::quantum_network::distributed_protocols::ResourceConstraints {
1746 max_cost: None,
1747 max_execution_time: Duration::from_secs(60),
1748 max_memory_usage: 1024,
1749 preferred_providers: vec![],
1750 },
1751 };
1752
1753 let allocation = balancer
1754 .select_nodes(&partitions, &nodes, &requirements)
1755 .expect("at least some partitions should be assignable");
1756
1757 let assigned_node_ids: std::collections::HashSet<_> = allocation.values().collect();
1758 assert!(
1759 assigned_node_ids.len() > 1,
1760 "expected partitions to be spread across more than one node, got {assigned_node_ids:?}"
1761 );
1762 }
1763}