1#[allow(dead_code)]
8use crate::coordination::monitoring::anomaly_detection::{AnomalyConfig, AnomalyResult};
9use crate::coordination::monitoring::performance_tracking::{
10 DashboardConfiguration, TrackerConfiguration,
11};
12use crate::coordination::orchestration::pipeline_orchestrator::{
13 ExecutionState, OrchestratorConfiguration,
14};
15use crate::coordination::scheduling::task_scheduler::SchedulerConfig;
16use crate::research::experiments::ResourceUsage;
17use scirs2_core::numeric::Float;
18use std::collections::HashMap;
19use std::fmt::Debug;
20use std::marker::PhantomData;
21use std::time::{Duration, Instant, SystemTime};
22
23pub mod monitoring;
25pub mod orchestration;
26pub mod scheduling;
27
28pub use scheduling::{
30 PriorityLevel, PriorityManager, PriorityQueue, PriorityUpdateStrategy,
31 ResourceAllocationStrategy, ResourceAllocationTracker, ResourceManager, ResourcePool,
32 ScheduledTask, SchedulingStrategy, StaticPriorityStrategy, TaskPriority, TaskScheduler,
33};
34
35pub type OptimizationTask<T> = ScheduledTask<T>;
37
38pub use orchestration::{
39 AlertConfiguration, Checkpoint, CheckpointConfiguration, CheckpointManager, CheckpointMetadata,
40 CheckpointStorage, Experiment, ExperimentConfiguration, ExperimentExecution, ExperimentManager,
41 ExperimentResult, ExperimentStatus, FileCheckpointStorage, InMemoryCheckpointStorage,
42 MonitoringConfiguration, OptimizationPipeline, PipelineConfiguration, PipelineExecution,
43 PipelineOrchestrator, PipelineStage, RecoveryManager, RecoveryOptions, RecoveryStrategy,
44 RecoveryTarget, ResourceLimits, StageResult, StateType, StorageConfiguration, TimeoutSettings,
45 ValidationRule,
46};
47
48pub use monitoring::{
49 AlertManager, AnomalyAlert, AnomalyAnalyzer, AnomalyClassifier, AnomalyDetector,
50 AnomalyReporter, ConvergenceAnalyzer, ConvergenceCriteria, ConvergenceDetector,
51 ConvergenceIndicator, ConvergenceMonitor, ConvergenceResult, MetricCollector, OutlierDetector,
52 PerformanceAlert, PerformanceMetrics, PerformanceTracker,
53};
54
55pub struct OptimizationCoordinator<T: Float + Debug + Send + Sync + 'static> {
57 scheduler: TaskScheduler<T>,
58 orchestrator: PipelineOrchestrator<T>,
59 performance_tracker: PerformanceTracker<T>,
60 convergence_detector: ConvergenceDetector<T>,
61 anomaly_detector: AnomalyDetector<T>,
62 config: CoordinatorConfig<T>,
63 state: CoordinatorState<T>,
64 metrics: CoordinatorMetrics<T>,
65 _phantom: PhantomData<T>,
66}
67
68#[derive(Debug)]
70pub struct CoordinatorConfig<T: Float + Debug + Send + Sync + 'static> {
71 pub max_concurrent_tasks: usize,
72 pub default_timeout: Duration,
73 pub monitoring_interval: Duration,
74 pub checkpoint_interval: Duration,
75 pub resource_allocation_strategy: ResourceAllocationStrategy,
76 pub priority_strategy: Box<dyn PriorityUpdateStrategy<T>>,
77 pub convergence_criteria: ConvergenceCriteria<T>,
78 pub enable_anomaly_detection: bool,
79 pub enable_auto_scaling: bool,
80 pub enable_fault_tolerance: bool,
81 pub performance_threshold: T,
82}
83
84impl<T: Float + Debug + Send + Sync + 'static> Default for CoordinatorConfig<T> {
85 fn default() -> Self {
86 Self {
87 max_concurrent_tasks: 10,
88 default_timeout: Duration::from_secs(3600),
89 monitoring_interval: Duration::from_secs(10),
90 checkpoint_interval: Duration::from_secs(300),
91 resource_allocation_strategy: ResourceAllocationStrategy::FairShare,
92 priority_strategy: Box::new(StaticPriorityStrategy),
93 convergence_criteria: ConvergenceCriteria::default(),
94 enable_anomaly_detection: true,
95 enable_auto_scaling: true,
96 enable_fault_tolerance: true,
97 performance_threshold: T::from(0.01).unwrap_or_else(|| T::zero()),
98 }
99 }
100}
101
102#[derive(Debug)]
104pub struct CoordinatorState<T: Float + Debug + Send + Sync + 'static> {
105 pub active_tasks: HashMap<String, OptimizationTask<T>>,
106 pub active_pipelines: HashMap<String, OptimizationPipeline<T>>,
107 pub pipeline_execution_ids: HashMap<String, String>,
110 pub experiment_pipeline_ids: HashMap<String, String>,
114 pub active_experiments: HashMap<String, Experiment<T>>,
115 pub resource_usage: ResourceUsage,
116 pub last_checkpoint: Option<Instant>,
117 pub last_monitoring_update: Option<Instant>,
118 pub coordination_start_time: Instant,
119 pub total_tasks_processed: usize,
120 pub total_experiments_completed: usize,
121 pub convergence_checks_total: usize,
123 pub convergence_checks_passed: usize,
125 pub anomaly_checks_total: usize,
127 pub anomaly_checks_flagged: usize,
129}
130
131impl<T: Float + Debug + Send + Sync + 'static> Default for CoordinatorState<T> {
132 fn default() -> Self {
133 Self::new()
134 }
135}
136
137impl<T: Float + Debug + Send + Sync + 'static> CoordinatorState<T> {
138 pub fn new() -> Self {
139 Self {
140 active_tasks: HashMap::new(),
141 active_pipelines: HashMap::new(),
142 pipeline_execution_ids: HashMap::new(),
143 experiment_pipeline_ids: HashMap::new(),
144 active_experiments: HashMap::new(),
145 resource_usage: ResourceUsage::default(),
146 last_checkpoint: None,
147 last_monitoring_update: None,
148 coordination_start_time: Instant::now(),
149 total_tasks_processed: 0,
150 total_experiments_completed: 0,
151 convergence_checks_total: 0,
152 convergence_checks_passed: 0,
153 anomaly_checks_total: 0,
154 anomaly_checks_flagged: 0,
155 }
156 }
157}
158
159#[derive(Debug, Clone)]
161pub struct CoordinatorMetrics<T: Float + Debug + Send + Sync + 'static> {
162 pub average_task_completion_time: T,
163 pub throughput: T,
164 pub resource_utilization: T,
165 pub error_rate: T,
166 pub convergence_rate: T,
167 pub anomaly_detection_rate: T,
168 pub uptime: Duration,
169 pub total_processed_tasks: usize,
170}
171
172impl<T: Float + Debug + Send + Sync + 'static> Default for CoordinatorMetrics<T> {
173 fn default() -> Self {
174 Self {
175 average_task_completion_time: T::zero(),
176 throughput: T::zero(),
177 resource_utilization: T::zero(),
178 error_rate: T::zero(),
179 convergence_rate: T::zero(),
180 anomaly_detection_rate: T::zero(),
181 uptime: Duration::new(0, 0),
182 total_processed_tasks: 0,
183 }
184 }
185}
186
187#[derive(Debug, Clone)]
189pub struct CoordinationResult<T: Float + Debug + Send + Sync + 'static> {
190 pub success: bool,
191 pub task_id: String,
192 pub execution_time: Duration,
193 pub resource_usage: ResourceUsage,
194 pub performance_metrics: PerformanceMetrics<T>,
195 pub convergence_result: Option<ConvergenceResult<T>>,
196 pub anomaly_alerts: Vec<AnomalyAlert<T>>,
197 pub errors: Vec<String>,
198}
199
200impl<T: Float + Debug + Send + Sync + 'static + Default> OptimizationCoordinator<T> {
201 pub fn new(config: CoordinatorConfig<T>) -> Result<Self, String> {
209 let estimation_threshold = T::from(0.9)
210 .ok_or_else(|| "failed to represent 0.9 in target float type".to_string())?;
211
212 let scheduler = TaskScheduler::new(SchedulerConfig {
213 max_concurrent_tasks: config.max_concurrent_tasks,
214 queue_size_limit: 1000,
215 task_timeout: config.default_timeout,
216 priority_update_interval: Duration::from_secs(5),
217 load_balance_interval: Duration::from_secs(10),
218 estimation_threshold,
219 enable_adaptive_scheduling: true,
220 enable_performance_learning: true,
221 })
222 .map_err(|e| format!("Failed to create task scheduler: {e}"))?;
223
224 let orchestrator = PipelineOrchestrator::new(OrchestratorConfiguration {
225 max_concurrent_pipelines: config.max_concurrent_tasks,
226 default_resource_limits: ResourceLimits::default(),
227 default_timeouts: TimeoutSettings::default(),
228 monitoring: MonitoringConfiguration::default(),
229 })
230 .map_err(|e| format!("Failed to create pipeline orchestrator: {e}"))?;
231
232 let performance_tracker = PerformanceTracker::new(TrackerConfiguration {
233 collection_interval: config.monitoring_interval,
234 enabled_collectors: vec!["default".to_string()],
235 enabled_analyzers: vec!["default".to_string()],
236 storage_config: StorageConfiguration::default(),
237 alert_config: AlertConfiguration::default(),
238 dashboard_config: DashboardConfiguration {
239 theme: String::from("default"),
240 auto_refresh: true,
241 default_time_range: Duration::from_secs(3600),
242 custom_params: HashMap::new(),
243 },
244 })
245 .map_err(|e| format!("Failed to create performance tracker: {e}"))?;
246
247 let convergence_detector = ConvergenceDetector::new(config.convergence_criteria.clone());
248
249 let anomaly_detector = AnomalyDetector::new(AnomalyConfig::default());
250
251 Ok(Self {
252 scheduler,
253 orchestrator,
254 performance_tracker,
255 convergence_detector,
256 anomaly_detector,
257 state: CoordinatorState::new(),
258 metrics: CoordinatorMetrics::default(),
259 config,
260 _phantom: PhantomData,
261 })
262 }
263
264 pub fn submit_task(&mut self, mut task: OptimizationTask<T>) -> Result<String, String> {
266 let task_id = format!(
268 "task_{}_{}",
269 self.state.total_tasks_processed,
270 Instant::now().elapsed().as_nanos()
271 );
272 task.task_id = task_id.clone();
273
274 match self.scheduler.submit_task(task.clone()) {
276 Ok(()) => {
277 self.state.active_tasks.insert(task_id.clone(), task);
278 self.state.total_tasks_processed += 1;
279
280 Ok(task_id)
284 }
285 Err(e) => Err(format!("Failed to schedule task: {}", e)),
286 }
287 }
288
289 pub fn submit_pipeline(
291 &mut self,
292 mut pipeline: OptimizationPipeline<T>,
293 ) -> Result<String, String> {
294 let pipeline_id = format!(
295 "pipeline_{}_{}",
296 self.state.active_pipelines.len(),
297 Instant::now().elapsed().as_nanos()
298 );
299
300 pipeline.pipeline_id = pipeline_id.clone();
301
302 let execution_id = self
310 .orchestrator
311 .execute_pipeline(pipeline.clone())
312 .map_err(|err| format!("Failed to execute pipeline: {err}"))?;
313 self.state
314 .active_pipelines
315 .insert(pipeline_id.clone(), pipeline);
316 self.state
317 .pipeline_execution_ids
318 .insert(pipeline_id.clone(), execution_id);
319 Ok(pipeline_id)
320 }
321
322 pub fn pipeline_execution_id(&self, pipeline_id: &str) -> Option<&str> {
324 self.state
325 .pipeline_execution_ids
326 .get(pipeline_id)
327 .map(String::as_str)
328 }
329
330 pub fn pipeline_execution_status(&self, pipeline_id: &str) -> Option<ExecutionState> {
332 let execution_id = self.state.pipeline_execution_ids.get(pipeline_id)?;
333 self.orchestrator.get_execution_status(execution_id)
334 }
335
336 pub fn submit_experiment(&mut self, mut experiment: Experiment<T>) -> Result<String, String> {
338 let experiment_id = format!(
339 "experiment_{}_{}",
340 self.state.active_experiments.len(),
341 Instant::now().elapsed().as_nanos()
342 );
343
344 experiment.experiment_id = experiment_id.clone();
345
346 let pipeline = self.experiment_to_pipeline(&experiment)?;
348 let pipeline_id = self.submit_pipeline(pipeline)?;
349
350 self.state
351 .active_experiments
352 .insert(experiment_id.clone(), experiment);
353 self.state
354 .experiment_pipeline_ids
355 .insert(experiment_id.clone(), pipeline_id);
356
357 Ok(experiment_id)
358 }
359
360 pub fn experiment_pipeline_id(&self, experiment_id: &str) -> Option<&str> {
362 self.state
363 .experiment_pipeline_ids
364 .get(experiment_id)
365 .map(String::as_str)
366 }
367
368 pub fn execute_cycle(&mut self) -> Vec<CoordinationResult<T>> {
370 let mut results = Vec::new();
371
372 self.update_monitoring();
374
375 let task_results = self.process_scheduled_tasks();
377 results.extend(task_results);
378
379 self.update_pipeline_executions();
381
382 self.perform_maintenance();
384
385 self.update_metrics();
387
388 results
389 }
390
391 pub fn monitor_optimization_value(&mut self, task_id: &str, value: T) -> MonitoringResult<T> {
393 let mut alerts = Vec::new();
394
395 let _ = self.performance_tracker.collect_metrics();
397
398 let convergence_result = self.convergence_detector.check_convergence(value);
400
401 let anomaly_result = if self.config.enable_anomaly_detection {
403 Some(self.anomaly_detector.detect_anomaly(value))
404 } else {
405 None
406 };
407
408 if let Some(ref anomaly) = anomaly_result {
410 self.state.anomaly_checks_total += 1;
411 if anomaly.is_anomaly {
412 self.state.anomaly_checks_flagged += 1;
413 alerts.push(MonitoringAlert::Anomaly(anomaly.clone()));
414 }
415 }
416
417 self.state.convergence_checks_total += 1;
418 if convergence_result.converged {
419 self.state.convergence_checks_passed += 1;
420 alerts.push(MonitoringAlert::Convergence(convergence_result.clone()));
421 }
422
423 MonitoringResult {
424 task_id: task_id.to_string(),
425 value,
426 convergence_result,
427 anomaly_result,
428 alerts,
429 timestamp: Instant::now(),
430 }
431 }
432
433 pub fn get_status(&self) -> CoordinationStatus<T> {
435 CoordinationStatus {
436 active_tasks: self.state.active_tasks.len(),
437 active_pipelines: self.state.active_pipelines.len(),
438 active_experiments: self.state.active_experiments.len(),
439 resource_utilization: self.state.resource_usage.clone(),
440 metrics: self.metrics.clone(),
441 uptime: self.state.coordination_start_time.elapsed(),
442 health_status: self.assess_health_status(),
443 }
444 }
445
446 pub fn update_resource_allocation(&mut self, allocation: ResourceUsage) -> Result<(), String> {
448 self.state.resource_usage = allocation;
449
450 Ok(())
459 }
460
461 pub fn shutdown(&mut self) -> Result<(), String> {
463 self.complete_active_tasks()?;
465
466 self.save_final_checkpoints()?;
468
469 let report = self.generate_final_report();
471 println!("Coordination shutdown report:\n{}", report);
472
473 Ok(())
474 }
475
476 fn update_monitoring(&mut self) {
479 let now = Instant::now();
480
481 if let Some(last_update) = self.state.last_monitoring_update {
482 if now.duration_since(last_update) < self.config.monitoring_interval {
483 return;
484 }
485 }
486
487 let _ = self.performance_tracker.collect_metrics();
489
490 if self.config.enable_anomaly_detection {
492 let system_metrics = self.collect_system_metrics();
493 for metric in system_metrics {
494 let _ = self.anomaly_detector.detect_anomaly(metric);
495 }
496 }
497
498 self.state.last_monitoring_update = Some(now);
499 }
500
501 fn process_scheduled_tasks(&mut self) -> Vec<CoordinationResult<T>> {
502 let mut results = Vec::new();
503
504 while let Some(task) = self.scheduler.get_next_task() {
508 let task_id = task.task_id.clone();
509 match self.execute_task(&task) {
510 Ok(result) => {
511 results.push(result);
512 self.state.active_tasks.remove(&task_id);
513 }
514 Err(e) => {
515 self.state.active_tasks.remove(&task_id);
516 results.push(CoordinationResult {
517 success: false,
518 task_id,
519 execution_time: Duration::new(0, 0),
520 resource_usage: ResourceUsage::default(),
521 performance_metrics: PerformanceMetrics::default(),
522 convergence_result: None,
523 anomaly_alerts: Vec::new(),
524 errors: vec![e],
525 });
526 }
527 }
528 }
529
530 results
531 }
532
533 fn execute_task(
534 &mut self,
535 task: &OptimizationTask<T>,
536 ) -> Result<CoordinationResult<T>, String> {
537 let start_time = Instant::now();
538 let task_id = task.task_id.clone();
539
540 let assigned_resources =
545 crate::coordination::scheduling::task_scheduler::AssignedResources {
546 cpu_cores: (0..task.resource_requirements.cpu_cores).collect(),
547 memory_mb: task.resource_requirements.memory_mb,
548 gpu_devices: (0..task.resource_requirements.gpu_devices).collect(),
549 storage_gb: task.resource_requirements.storage_gb,
550 network_bandwidth: task.resource_requirements.network_bandwidth,
551 };
552 self.scheduler
553 .start_task_execution(task.clone(), assigned_resources)
554 .map_err(|e| format!("Failed to start task execution: {e}"))?;
555
556 let (success, performance_metrics, error_info) =
560 match self.performance_tracker.collect_metrics() {
561 Ok(metrics) => (true, metrics, None),
562 Err(e) => (false, PerformanceMetrics::default(), Some(e.to_string())),
563 };
564
565 let execution_time = start_time.elapsed();
566
567 self.scheduler
568 .complete_task(&task_id, success, error_info.clone())
569 .map_err(|e| format!("Failed to complete task in scheduler: {e}"))?;
570
571 Ok(CoordinationResult {
572 success,
573 task_id,
574 execution_time,
575 resource_usage: self.state.resource_usage.clone(),
576 performance_metrics,
577 convergence_result: None,
578 anomaly_alerts: Vec::new(),
579 errors: error_info.into_iter().collect(),
580 })
581 }
582
583 fn update_pipeline_executions(&mut self) {
584 }
593
594 fn perform_maintenance(&mut self) {
595 let now = Instant::now();
596
597 if let Some(last_checkpoint) = self.state.last_checkpoint {
599 if now.duration_since(last_checkpoint) >= self.config.checkpoint_interval {
600 let _ = self.create_checkpoint();
601 }
602 } else {
603 let _ = self.create_checkpoint();
604 }
605
606 self.cleanup_completed_items();
608
609 if self.config.enable_auto_scaling {
611 self.update_adaptive_parameters();
612 }
613 }
614
615 fn create_checkpoint(&mut self) -> Result<(), String> {
616 self.state.last_checkpoint = Some(Instant::now());
619 Ok(())
620 }
621
622 fn cleanup_completed_items(&mut self) {
623 let threshold = Duration::from_secs(3600); let now = SystemTime::now();
633
634 self.state.active_tasks.retain(|_, task| {
635 now.duration_since(task.created_at)
636 .map(|age| age < threshold)
637 .unwrap_or(true)
638 });
639 }
640
641 fn update_adaptive_parameters(&mut self) {
642 let current_metrics = &self.metrics;
644
645 if current_metrics.resource_utilization > T::from(0.9).unwrap_or_else(|| T::zero()) {
646 let _ = self.request_additional_resources();
648 } else if current_metrics.resource_utilization < T::from(0.3).unwrap_or_else(|| T::zero()) {
649 let _ = self.release_excess_resources();
651 }
652 }
653
654 fn request_additional_resources(&mut self) -> Result<(), String> {
655 Ok(())
657 }
658
659 fn release_excess_resources(&mut self) -> Result<(), String> {
660 Ok(())
662 }
663
664 fn update_metrics(&mut self) {
665 let current_time = Instant::now();
666 let uptime = current_time.duration_since(self.state.coordination_start_time);
667
668 self.metrics.uptime = uptime;
670 self.metrics.total_processed_tasks = self.state.total_tasks_processed;
671
672 if uptime.as_secs() > 0 {
674 self.metrics.throughput = T::from(self.state.total_tasks_processed)
675 .unwrap_or_else(|| T::zero())
676 / T::from(uptime.as_secs()).unwrap_or_else(|| T::one());
677 }
678
679 self.metrics.resource_utilization = if self.config.max_concurrent_tasks > 0 {
686 (T::from(self.state.active_tasks.len()).unwrap_or_else(|| T::zero())
687 / T::from(self.config.max_concurrent_tasks).unwrap_or_else(|| T::one()))
688 .min(T::one())
689 } else {
690 T::zero()
691 };
692
693 self.metrics.convergence_rate = self.calculate_convergence_rate();
695 self.metrics.anomaly_detection_rate = self.calculate_anomaly_rate();
696 }
697
698 fn calculate_convergence_rate(&self) -> T {
703 if self.state.convergence_checks_total == 0 {
704 return T::zero();
705 }
706 T::from(self.state.convergence_checks_passed).unwrap_or_else(|| T::zero())
707 / T::from(self.state.convergence_checks_total).unwrap_or_else(|| T::one())
708 }
709
710 fn calculate_anomaly_rate(&self) -> T {
714 if self.state.anomaly_checks_total == 0 {
715 return T::zero();
716 }
717 T::from(self.state.anomaly_checks_flagged).unwrap_or_else(|| T::zero())
718 / T::from(self.state.anomaly_checks_total).unwrap_or_else(|| T::one())
719 }
720
721 fn collect_system_metrics(&self) -> Vec<T> {
722 vec![
724 self.metrics.resource_utilization,
725 self.metrics.throughput,
726 T::from(self.state.active_tasks.len()).unwrap_or_else(|| T::zero()),
727 T::from(self.state.active_pipelines.len()).unwrap_or_else(|| T::zero()),
728 ]
729 }
730
731 fn experiment_to_pipeline(
732 &self,
733 experiment: &Experiment<T>,
734 ) -> Result<OptimizationPipeline<T>, String> {
735 let pipeline = OptimizationPipeline {
737 pipeline_id: experiment.experiment_id.clone(),
738 name: format!("Experiment {}", experiment.experiment_id),
739 description: "Auto-generated pipeline from experiment".to_string(),
740 stages: Vec::new(), dependencies: HashMap::new(),
742 configuration: PipelineConfiguration::default(),
743 global_parameters: HashMap::new(),
744 metadata: crate::coordination::orchestration::pipeline_orchestrator::PipelineMetadata {
745 created_by: "system".to_string(),
746 created_at: std::time::SystemTime::now(),
747 updated_at: std::time::SystemTime::now(),
748 tags: vec!["experiment".to_string()],
749 description: "Pipeline from experiment".to_string(),
750 },
751 version: "1.0.0".to_string(),
752 };
753 Ok(pipeline)
754 }
755
756 fn assess_health_status(&self) -> HealthStatus {
757 let error_rate = self.metrics.error_rate;
758 let resource_utilization = self.metrics.resource_utilization;
759
760 if error_rate > T::from(0.1).unwrap_or_else(|| T::zero()) {
761 HealthStatus::Unhealthy
762 } else if resource_utilization > T::from(0.95).unwrap_or_else(|| T::zero()) {
763 HealthStatus::Degraded
764 } else if error_rate > T::from(0.05).unwrap_or_else(|| T::zero())
765 || resource_utilization > T::from(0.8).unwrap_or_else(|| T::zero())
766 {
767 HealthStatus::Warning
768 } else {
769 HealthStatus::Healthy
770 }
771 }
772
773 fn complete_active_tasks(&mut self) -> Result<(), String> {
774 let timeout = Duration::from_secs(60);
776 let start = Instant::now();
777
778 while !self.state.active_tasks.is_empty() && start.elapsed() < timeout {
779 let results = self.process_scheduled_tasks();
780 if results.is_empty() {
781 std::thread::sleep(Duration::from_millis(100));
782 }
783 }
784
785 if !self.state.active_tasks.is_empty() {
786 return Err(format!(
787 "Timeout waiting for {} tasks to complete",
788 self.state.active_tasks.len()
789 ));
790 }
791
792 Ok(())
793 }
794
795 fn save_final_checkpoints(&mut self) -> Result<(), String> {
796 self.create_checkpoint()
797 }
798
799 fn generate_final_report(&self) -> String {
800 format!(
801 "Optimization Coordination Final Report:\n\
802 - Total Uptime: {:?}\n\
803 - Tasks Processed: {}\n\
804 - Experiments Completed: {}\n\
805 - Average Throughput: {:.2}\n\
806 - Resource Utilization: {:.2}%\n\
807 - Error Rate: {:.2}%\n\
808 - Convergence Rate: {:.2}%\n\
809 - Health Status: {:?}",
810 self.metrics.uptime,
811 self.metrics.total_processed_tasks,
812 self.state.total_experiments_completed,
813 self.metrics.throughput.to_f64().unwrap_or(0.0),
814 (self.metrics.resource_utilization * T::from(100.0).unwrap_or_else(|| T::zero()))
815 .to_f64()
816 .unwrap_or(0.0),
817 (self.metrics.error_rate * T::from(100.0).unwrap_or_else(|| T::zero()))
818 .to_f64()
819 .unwrap_or(0.0),
820 (self.metrics.convergence_rate * T::from(100.0).unwrap_or_else(|| T::zero()))
821 .to_f64()
822 .unwrap_or(0.0),
823 self.assess_health_status(),
824 )
825 }
826}
827
828#[derive(Debug, Clone)]
830pub struct MonitoringResult<T: Float + Debug + Send + Sync + 'static> {
831 pub task_id: String,
832 pub value: T,
833 pub convergence_result: ConvergenceResult<T>,
834 pub anomaly_result: Option<AnomalyResult<T>>,
835 pub alerts: Vec<MonitoringAlert<T>>,
836 pub timestamp: Instant,
837}
838
839#[derive(Debug, Clone)]
841pub enum MonitoringAlert<T: Float + Debug + Send + Sync + 'static> {
842 Convergence(ConvergenceResult<T>),
843 Anomaly(AnomalyResult<T>),
844 Performance(Box<PerformanceAlert<T>>),
845 Resource(String),
846}
847
848#[derive(Debug, Clone)]
850pub struct CoordinationStatus<T: Float + Debug + Send + Sync + 'static> {
851 pub active_tasks: usize,
852 pub active_pipelines: usize,
853 pub active_experiments: usize,
854 pub resource_utilization: ResourceUsage,
855 pub metrics: CoordinatorMetrics<T>,
856 pub uptime: Duration,
857 pub health_status: HealthStatus,
858}
859
860#[derive(Debug, Clone, PartialEq)]
862pub enum HealthStatus {
863 Healthy,
864 Warning,
865 Degraded,
866 Unhealthy,
867}
868
869pub struct CoordinatorBuilder<T: Float + Debug + Send + Sync + 'static> {
871 config: CoordinatorConfig<T>,
872}
873
874impl<T: Float + Debug + Send + Sync + 'static + Default> CoordinatorBuilder<T> {
875 pub fn new() -> Self {
876 Self {
877 config: CoordinatorConfig::default(),
878 }
879 }
880
881 pub fn max_concurrent_tasks(mut self, max: usize) -> Self {
882 self.config.max_concurrent_tasks = max;
883 self
884 }
885
886 pub fn monitoring_interval(mut self, interval: Duration) -> Self {
887 self.config.monitoring_interval = interval;
888 self
889 }
890
891 pub fn enable_anomaly_detection(mut self, enable: bool) -> Self {
892 self.config.enable_anomaly_detection = enable;
893 self
894 }
895
896 pub fn enable_fault_tolerance(mut self, enable: bool) -> Self {
897 self.config.enable_fault_tolerance = enable;
898 self
899 }
900
901 pub fn convergence_criteria(mut self, criteria: ConvergenceCriteria<T>) -> Self {
902 self.config.convergence_criteria = criteria;
903 self
904 }
905
906 pub fn build(self) -> Result<OptimizationCoordinator<T>, String> {
907 OptimizationCoordinator::new(self.config)
908 }
909}
910
911impl<T: Float + Debug + Send + Sync + 'static + Default> Default for CoordinatorBuilder<T> {
912 fn default() -> Self {
913 Self::new()
914 }
915}
916
917#[cfg(test)]
918mod tests {
919 use super::*;
920
921 #[test]
922 fn test_coordinator_creation() {
923 let coordinator = CoordinatorBuilder::<f64>::new()
924 .max_concurrent_tasks(5)
925 .enable_anomaly_detection(true)
926 .build()
927 .expect("unwrap failed");
928
929 let status = coordinator.get_status();
930 assert_eq!(status.active_tasks, 0);
931 assert_eq!(status.health_status, HealthStatus::Healthy);
932 }
933
934 #[test]
935 fn test_task_submission() {
936 let mut coordinator = OptimizationCoordinator::<f64>::new(CoordinatorConfig::default())
937 .expect("unwrap failed");
938 let task = OptimizationTask::new("test_task".to_string());
939
940 let task_id = coordinator.submit_task(task).expect("unwrap failed");
941 assert!(!task_id.is_empty());
942
943 let status = coordinator.get_status();
944 assert!(status.active_tasks > 0);
945 }
946
947 #[test]
948 fn test_monitoring() {
949 let mut coordinator = OptimizationCoordinator::<f64>::new(CoordinatorConfig::default())
950 .expect("unwrap failed");
951 let result = coordinator.monitor_optimization_value("test_task", 1.0);
952
953 assert_eq!(result.task_id, "test_task");
954 assert_eq!(result.value, 1.0);
955 }
956
957 #[test]
958 fn convergence_and_anomaly_rates_reflect_real_history() {
959 let mut coordinator = OptimizationCoordinator::<f64>::new(CoordinatorConfig::default())
962 .expect("unwrap failed");
963
964 assert_eq!(coordinator.calculate_convergence_rate(), 0.0);
967 assert_eq!(coordinator.calculate_anomaly_rate(), 0.0);
968
969 for _ in 0..4 {
970 coordinator.monitor_optimization_value("t", 1.0);
971 }
972 assert_eq!(coordinator.state.convergence_checks_total, 4);
975 }
976
977 #[test]
978 fn execute_cycle_processes_submitted_tasks_through_the_scheduler() {
979 let mut coordinator = OptimizationCoordinator::<f64>::new(CoordinatorConfig::default())
983 .expect("unwrap failed");
984 let task = OptimizationTask::new("cycle_task".to_string());
985 coordinator.submit_task(task).expect("unwrap failed");
986
987 let results = coordinator.execute_cycle();
988 assert!(
989 !results.is_empty(),
990 "execute_cycle must dispatch the submitted task instead of processing nothing"
991 );
992 let stats = coordinator.scheduler.get_statistics();
996 assert_eq!(
997 stats.total_tasks_completed + stats.total_tasks_failed,
998 1,
999 "the dispatched task must be reflected in scheduler statistics"
1000 );
1001 }
1002
1003 #[test]
1011 fn resource_utilization_reflects_real_active_task_load() {
1012 let mut coordinator = CoordinatorBuilder::<f64>::new()
1013 .max_concurrent_tasks(4)
1014 .build()
1015 .expect("unwrap failed");
1016
1017 coordinator.update_metrics();
1018 assert_eq!(coordinator.metrics.resource_utilization, 0.0);
1019
1020 for i in 0..2 {
1021 let task = OptimizationTask::new(format!("util_task_{i}"));
1022 coordinator
1023 .submit_task(task)
1024 .expect("submit should succeed");
1025 }
1026 coordinator.update_metrics();
1027
1028 assert_eq!(
1029 coordinator.metrics.resource_utilization, 0.5,
1030 "2 active tasks out of a configured max of 4 must report 50% utilization, \
1031 not the old hardcoded 0.0"
1032 );
1033 }
1034
1035 #[test]
1040 fn cleanup_completed_items_removes_only_stale_tasks() {
1041 let mut coordinator = OptimizationCoordinator::<f64>::new(CoordinatorConfig::default())
1042 .expect("unwrap failed");
1043
1044 let mut old_task = OptimizationTask::new("old_task".to_string());
1045 old_task.created_at = SystemTime::now() - Duration::from_secs(7200); coordinator
1047 .state
1048 .active_tasks
1049 .insert(old_task.task_id.clone(), old_task);
1050
1051 let fresh_task = OptimizationTask::new("fresh_task".to_string());
1052 let fresh_id = fresh_task.task_id.clone();
1053 coordinator
1054 .state
1055 .active_tasks
1056 .insert(fresh_id.clone(), fresh_task);
1057
1058 assert_eq!(coordinator.state.active_tasks.len(), 2);
1059 coordinator.cleanup_completed_items();
1060
1061 assert_eq!(
1062 coordinator.state.active_tasks.len(),
1063 1,
1064 "the stale (>1h old) task must be removed"
1065 );
1066 assert!(
1067 coordinator.state.active_tasks.contains_key(&fresh_id),
1068 "the fresh task must be retained"
1069 );
1070 }
1071}