1#![allow(dead_code)]
4
5use anyhow::Result;
6use std::collections::HashMap;
7use trustformers_core::parallel::CommunicationBackend;
8use trustformers_core::tensor::Tensor;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17pub enum AggregationStrategy {
18 BinaryTree,
20 Ring,
22 Butterfly,
24 Adaptive,
26}
27
28#[derive(Debug, Clone)]
29pub struct HierarchicalConfig {
30 pub num_nodes: usize,
32 pub devices_per_node: usize,
34 pub node_rank: usize,
36 pub local_rank: usize,
38 pub global_rank: usize,
40 pub strategy: AggregationStrategy,
42 pub comm_backend: CommunicationBackend,
44 pub enable_compression: bool,
46 pub compression_threshold: f32,
48 pub enable_fault_tolerance: bool,
50 pub comm_timeout_ms: u64,
52}
53
54impl Default for HierarchicalConfig {
55 fn default() -> Self {
56 Self {
57 num_nodes: 1,
58 devices_per_node: 1,
59 node_rank: 0,
60 local_rank: 0,
61 global_rank: 0,
62 strategy: AggregationStrategy::Adaptive,
63 comm_backend: CommunicationBackend::Mpi,
64 enable_compression: true,
65 compression_threshold: 0.1,
66 enable_fault_tolerance: true,
67 comm_timeout_ms: 30000,
68 }
69 }
70}
71
72impl HierarchicalConfig {
73 pub fn new(
74 num_nodes: usize,
75 devices_per_node: usize,
76 node_rank: usize,
77 local_rank: usize,
78 ) -> Self {
79 let global_rank = node_rank * devices_per_node + local_rank;
80 Self {
81 num_nodes,
82 devices_per_node,
83 node_rank,
84 local_rank,
85 global_rank,
86 ..Default::default()
87 }
88 }
89
90 pub fn world_size(&self) -> usize {
91 self.num_nodes * self.devices_per_node
92 }
93
94 pub fn is_master(&self) -> bool {
95 self.global_rank == 0
96 }
97
98 pub fn is_node_master(&self) -> bool {
99 self.local_rank == 0
100 }
101}
102
103pub struct HierarchicalAggregator {
105 config: HierarchicalConfig,
106 node_topology: NodeTopology,
107 communication_groups: CommunicationGroups,
108 aggregation_stats: AggregationStats,
109 fault_detector: Option<FaultDetector>,
110}
111
112#[derive(Debug, Clone)]
114pub struct NodeTopology {
115 pub node_adjacency: Vec<Vec<bool>>,
117 pub node_bandwidth: Vec<Vec<f32>>,
119 pub node_latency: Vec<Vec<f32>>,
121 pub intra_node_bandwidth: f32,
123 pub intra_node_latency: f32,
125}
126
127#[derive(Debug, Clone)]
129pub struct CommunicationGroups {
130 pub node_local_group: Vec<usize>,
132 pub cross_node_group: Vec<usize>,
134 pub tree_structure: TreeStructure,
136 pub ring_structure: RingStructure,
138 pub butterfly_structure: ButterflyStructure,
140}
141
142#[derive(Debug, Clone)]
143pub struct TreeStructure {
144 pub parent: Option<usize>,
146 pub children: Vec<usize>,
148 pub depth: usize,
150 pub height: usize,
152}
153
154#[derive(Debug, Clone)]
155pub struct RingStructure {
156 pub next_rank: usize,
158 pub prev_rank: usize,
160 pub ring_size: usize,
162}
163
164#[derive(Debug, Clone)]
165pub struct ButterflyStructure {
166 pub connections: Vec<Vec<usize>>,
168 pub num_stages: usize,
170}
171
172#[derive(Debug, Clone)]
174pub struct AggregationStats {
175 pub total_operations: usize,
177 pub avg_aggregation_time: f32,
179 pub total_bytes_transferred: usize,
181 pub compression_ratio: f32,
183 pub failed_operations: usize,
185 pub strategy_history: HashMap<AggregationStrategy, usize>,
187}
188
189#[derive(Debug)]
191pub struct FaultDetector {
192 pub failed_nodes: Vec<usize>,
194 pub timeout_threshold: u64,
196 pub recovery_strategy: RecoveryStrategy,
198}
199
200#[derive(Debug, Clone)]
201pub enum RecoveryStrategy {
202 Skip,
204 Retry,
206 Abort,
208}
209
210impl Default for AggregationStats {
211 fn default() -> Self {
212 Self {
213 total_operations: 0,
214 avg_aggregation_time: 0.0,
215 total_bytes_transferred: 0,
216 compression_ratio: 1.0,
217 failed_operations: 0,
218 strategy_history: HashMap::new(),
219 }
220 }
221}
222
223impl HierarchicalAggregator {
224 pub fn new(config: HierarchicalConfig) -> Result<Self> {
225 let node_topology = Self::detect_network_topology(&config)?;
226 let communication_groups = Self::build_communication_groups(&config, &node_topology)?;
227 let aggregation_stats = AggregationStats::default();
228
229 let fault_detector = if config.enable_fault_tolerance {
230 Some(FaultDetector {
231 failed_nodes: Vec::new(),
232 timeout_threshold: config.comm_timeout_ms,
233 recovery_strategy: RecoveryStrategy::Skip,
234 })
235 } else {
236 None
237 };
238
239 Ok(Self {
240 config,
241 node_topology,
242 communication_groups,
243 aggregation_stats,
244 fault_detector,
245 })
246 }
247
248 fn detect_network_topology(config: &HierarchicalConfig) -> Result<NodeTopology> {
250 let num_nodes = config.num_nodes;
251
252 let mut node_adjacency = vec![vec![false; num_nodes]; num_nodes];
254 let mut node_bandwidth = vec![vec![0.0; num_nodes]; num_nodes];
255 let mut node_latency = vec![vec![0.0; num_nodes]; num_nodes];
256
257 for i in 0..num_nodes {
260 for j in 0..num_nodes {
261 if i != j {
262 node_adjacency[i][j] = true;
263 node_bandwidth[i][j] = if (i as i32 - j as i32).abs() == 1 {
265 10000.0 } else {
267 1000.0 };
269 node_latency[i][j] = if (i as i32 - j as i32).abs() == 1 {
271 0.1 } else {
273 1.0 };
275 } else {
276 node_adjacency[i][j] = false;
277 node_bandwidth[i][j] = f32::INFINITY;
278 node_latency[i][j] = 0.0;
279 }
280 }
281 }
282
283 Ok(NodeTopology {
284 node_adjacency,
285 node_bandwidth,
286 node_latency,
287 intra_node_bandwidth: 80000.0, intra_node_latency: 0.01, })
290 }
291
292 fn build_communication_groups(
294 config: &HierarchicalConfig,
295 topology: &NodeTopology,
296 ) -> Result<CommunicationGroups> {
297 let node_local_group: Vec<usize> = (0..config.devices_per_node)
299 .map(|i| config.node_rank * config.devices_per_node + i)
300 .collect();
301
302 let cross_node_group: Vec<usize> =
304 (0..config.num_nodes).map(|i| i * config.devices_per_node).collect();
305
306 let tree_structure = Self::build_tree_structure(config, topology)?;
308
309 let ring_structure = Self::build_ring_structure(config)?;
311
312 let butterfly_structure = Self::build_butterfly_structure(config)?;
314
315 Ok(CommunicationGroups {
316 node_local_group,
317 cross_node_group,
318 tree_structure,
319 ring_structure,
320 butterfly_structure,
321 })
322 }
323
324 fn build_tree_structure(
326 config: &HierarchicalConfig,
327 _topology: &NodeTopology,
328 ) -> Result<TreeStructure> {
329 let world_size = config.world_size();
330 let rank = config.global_rank;
331
332 let parent = if rank == 0 { None } else { Some((rank - 1) / 2) };
334
335 let mut children = Vec::new();
336 let left_child = 2 * rank + 1;
337 let right_child = 2 * rank + 2;
338
339 if left_child < world_size {
340 children.push(left_child);
341 }
342 if right_child < world_size {
343 children.push(right_child);
344 }
345
346 let depth = (rank as f32).log2().floor() as usize;
348 let height = (world_size as f32).log2().ceil() as usize;
349
350 Ok(TreeStructure {
351 parent,
352 children,
353 depth,
354 height,
355 })
356 }
357
358 fn build_ring_structure(config: &HierarchicalConfig) -> Result<RingStructure> {
360 let world_size = config.world_size();
361 let rank = config.global_rank;
362
363 let next_rank = (rank + 1) % world_size;
364 let prev_rank = (rank + world_size - 1) % world_size;
365
366 Ok(RingStructure {
367 next_rank,
368 prev_rank,
369 ring_size: world_size,
370 })
371 }
372
373 fn build_butterfly_structure(config: &HierarchicalConfig) -> Result<ButterflyStructure> {
375 let world_size = config.world_size();
376 let rank = config.global_rank;
377 let num_stages = (world_size as f32).log2().ceil() as usize;
378
379 let mut connections = Vec::new();
380
381 for stage in 0..num_stages {
382 let mut stage_connections = Vec::new();
383 let distance = 1 << stage;
384
385 let partner = rank ^ distance;
387 if partner < world_size {
388 stage_connections.push(partner);
389 }
390
391 connections.push(stage_connections);
392 }
393
394 Ok(ButterflyStructure {
395 connections,
396 num_stages,
397 })
398 }
399
400 pub fn hierarchical_all_reduce(
402 &mut self,
403 gradients: &mut HashMap<String, Tensor>,
404 ) -> Result<()> {
405 let start_time = std::time::Instant::now();
406
407 let strategy = self.select_optimal_strategy(gradients)?;
409
410 match strategy {
412 AggregationStrategy::BinaryTree => {
413 self.tree_based_all_reduce(gradients)?;
414 },
415 AggregationStrategy::Ring => {
416 self.ring_based_all_reduce(gradients)?;
417 },
418 AggregationStrategy::Butterfly => {
419 self.butterfly_based_all_reduce(gradients)?;
420 },
421 AggregationStrategy::Adaptive => {
422 let optimal_strategy = self.adaptive_strategy_selection(gradients)?;
424 match optimal_strategy {
425 AggregationStrategy::BinaryTree => self.tree_based_all_reduce(gradients)?,
426 AggregationStrategy::Ring => self.ring_based_all_reduce(gradients)?,
427 AggregationStrategy::Butterfly => self.butterfly_based_all_reduce(gradients)?,
428 AggregationStrategy::Adaptive => {
429 return Err(anyhow::anyhow!(
430 "Invalid adaptive strategy selection: recursive Adaptive strategy returned"
431 ));
432 },
433 }
434 },
435 }
436
437 let elapsed = start_time.elapsed().as_millis() as f32;
439 self.update_aggregation_stats(strategy, elapsed, gradients)?;
440
441 Ok(())
442 }
443
444 fn select_optimal_strategy(
446 &self,
447 gradients: &HashMap<String, Tensor>,
448 ) -> Result<AggregationStrategy> {
449 match self.config.strategy {
450 AggregationStrategy::Adaptive => self.adaptive_strategy_selection(gradients),
451 strategy => Ok(strategy),
452 }
453 }
454
455 fn adaptive_strategy_selection(
457 &self,
458 gradients: &HashMap<String, Tensor>,
459 ) -> Result<AggregationStrategy> {
460 let world_size = self.config.world_size();
461 let num_nodes = self.config.num_nodes;
462
463 let total_data_size: usize = gradients.values().map(|tensor| tensor.memory_usage()).sum();
465
466 if world_size <= 8 {
468 Ok(AggregationStrategy::BinaryTree)
470 } else if total_data_size > 100 * 1024 * 1024 {
471 Ok(AggregationStrategy::Ring)
473 } else if num_nodes > 16 {
474 Ok(AggregationStrategy::Butterfly)
476 } else {
477 Ok(AggregationStrategy::BinaryTree)
479 }
480 }
481
482 fn tree_based_all_reduce(&mut self, gradients: &mut HashMap<String, Tensor>) -> Result<()> {
484 let tree = self.communication_groups.tree_structure.clone();
485
486 self.tree_reduce_up(gradients, &tree)?;
488
489 self.tree_broadcast_down(gradients, &tree)?;
491
492 Ok(())
493 }
494
495 fn ring_based_all_reduce(&mut self, gradients: &mut HashMap<String, Tensor>) -> Result<()> {
497 let ring = self.communication_groups.ring_structure.clone();
498
499 self.ring_reduce_scatter(gradients, &ring)?;
501
502 self.ring_all_gather(gradients, &ring)?;
504
505 Ok(())
506 }
507
508 fn butterfly_based_all_reduce(
510 &mut self,
511 gradients: &mut HashMap<String, Tensor>,
512 ) -> Result<()> {
513 let butterfly = self.communication_groups.butterfly_structure.clone();
514
515 for stage in 0..butterfly.num_stages {
517 self.butterfly_stage_operation(gradients, &butterfly, stage)?;
518 }
519
520 Ok(())
521 }
522
523 fn tree_reduce_up(
525 &mut self,
526 gradients: &mut HashMap<String, Tensor>,
527 tree: &TreeStructure,
528 ) -> Result<()> {
529 for &child_rank in &tree.children {
531 for (name, gradient) in gradients.iter_mut() {
532 let child_gradient = self.simulate_receive_gradient(child_rank, name)?;
534 *gradient = gradient.add(&child_gradient)?;
535 }
536 }
537
538 if let Some(parent_rank) = tree.parent {
540 for (name, gradient) in gradients.iter() {
541 self.simulate_send_gradient(parent_rank, name, gradient)?;
542 }
543 }
544
545 Ok(())
546 }
547
548 fn tree_broadcast_down(
550 &mut self,
551 gradients: &mut HashMap<String, Tensor>,
552 tree: &TreeStructure,
553 ) -> Result<()> {
554 if let Some(parent_rank) = tree.parent {
556 for (name, gradient) in gradients.iter_mut() {
557 *gradient = self.simulate_receive_gradient(parent_rank, name)?;
558 }
559 }
560
561 for &child_rank in &tree.children {
563 for (name, gradient) in gradients.iter() {
564 self.simulate_send_gradient(child_rank, name, gradient)?;
565 }
566 }
567
568 Ok(())
569 }
570
571 fn ring_reduce_scatter(
573 &mut self,
574 gradients: &mut HashMap<String, Tensor>,
575 ring: &RingStructure,
576 ) -> Result<()> {
577 let num_chunks = ring.ring_size;
578 let rank = self.config.global_rank;
579
580 for step in 0..num_chunks - 1 {
581 let _send_chunk = (rank + ring.ring_size - step) % ring.ring_size;
582 let _recv_chunk = (rank + ring.ring_size - step - 1) % ring.ring_size;
583
584 for (name, gradient) in gradients.iter_mut() {
586 let chunk_gradient = self.simulate_receive_gradient(ring.prev_rank, name)?;
587 *gradient = gradient.add(&chunk_gradient)?;
588 self.simulate_send_gradient(ring.next_rank, name, gradient)?;
589 }
590 }
591
592 Ok(())
593 }
594
595 fn ring_all_gather(
597 &mut self,
598 gradients: &mut HashMap<String, Tensor>,
599 ring: &RingStructure,
600 ) -> Result<()> {
601 let num_chunks = ring.ring_size;
602
603 for _step in 0..num_chunks - 1 {
604 for (name, gradient) in gradients.iter_mut() {
606 let chunk_gradient = self.simulate_receive_gradient(ring.prev_rank, name)?;
607 *gradient = gradient.add(&chunk_gradient)?;
608 self.simulate_send_gradient(ring.next_rank, name, gradient)?;
609 }
610 }
611
612 Ok(())
613 }
614
615 fn butterfly_stage_operation(
617 &mut self,
618 gradients: &mut HashMap<String, Tensor>,
619 butterfly: &ButterflyStructure,
620 stage: usize,
621 ) -> Result<()> {
622 if stage < butterfly.connections.len() {
623 for &partner_rank in &butterfly.connections[stage] {
624 for (name, gradient) in gradients.iter_mut() {
625 let partner_gradient = self.simulate_receive_gradient(partner_rank, name)?;
627 *gradient = gradient.add(&partner_gradient)?;
628 self.simulate_send_gradient(partner_rank, name, gradient)?;
629 }
630 }
631 }
632
633 Ok(())
634 }
635
636 fn simulate_receive_gradient(&self, _from_rank: usize, _name: &str) -> Result<Tensor> {
638 Ok(Tensor::zeros(&[1])?)
641 }
642
643 fn simulate_send_gradient(
645 &self,
646 _to_rank: usize,
647 _name: &str,
648 _gradient: &Tensor,
649 ) -> Result<()> {
650 Ok(())
652 }
653
654 fn update_aggregation_stats(
656 &mut self,
657 strategy: AggregationStrategy,
658 elapsed_ms: f32,
659 gradients: &HashMap<String, Tensor>,
660 ) -> Result<()> {
661 let stats = &mut self.aggregation_stats;
662
663 stats.total_operations += 1;
664 stats.avg_aggregation_time =
665 (stats.avg_aggregation_time * (stats.total_operations - 1) as f32 + elapsed_ms)
666 / stats.total_operations as f32;
667
668 let bytes_transferred: usize = gradients.values().map(|tensor| tensor.memory_usage()).sum();
669 stats.total_bytes_transferred += bytes_transferred;
670
671 *stats.strategy_history.entry(strategy).or_insert(0) += 1;
672
673 Ok(())
674 }
675
676 pub fn get_stats(&self) -> &AggregationStats {
678 &self.aggregation_stats
679 }
680
681 pub fn reset_stats(&mut self) {
683 self.aggregation_stats = AggregationStats::default();
684 }
685
686 pub fn get_recommended_strategy(&self) -> AggregationStrategy {
688 let world_size = self.config.world_size();
689 let num_nodes = self.config.num_nodes;
690
691 if world_size <= 8 {
692 AggregationStrategy::BinaryTree
693 } else if num_nodes > 16 {
694 AggregationStrategy::Butterfly
695 } else {
696 AggregationStrategy::Ring
697 }
698 }
699}
700
701#[cfg(test)]
702mod tests {
703 use super::*;
704
705 #[test]
706 fn test_hierarchical_config() {
707 let config = HierarchicalConfig::new(4, 8, 2, 3);
708 assert_eq!(config.num_nodes, 4);
709 assert_eq!(config.devices_per_node, 8);
710 assert_eq!(config.node_rank, 2);
711 assert_eq!(config.local_rank, 3);
712 assert_eq!(config.global_rank, 19);
713 assert_eq!(config.world_size(), 32);
714 assert!(!config.is_master());
715 assert!(!config.is_node_master());
716 }
717
718 #[test]
719 fn test_tree_structure_building() {
720 let config = HierarchicalConfig::new(2, 4, 0, 0);
721 let topology = HierarchicalAggregator::detect_network_topology(&config)
722 .expect("Operation failed in test");
723 let tree = HierarchicalAggregator::build_tree_structure(&config, &topology)
724 .expect("Operation failed in test");
725
726 assert_eq!(tree.parent, None); assert_eq!(tree.children, vec![1, 2]);
728 assert_eq!(tree.depth, 0);
729 }
730
731 #[test]
732 fn test_ring_structure_building() {
733 let config = HierarchicalConfig::new(2, 4, 0, 1);
734 let ring = HierarchicalAggregator::build_ring_structure(&config)
735 .expect("Operation failed in test");
736
737 assert_eq!(ring.next_rank, 2);
738 assert_eq!(ring.prev_rank, 0);
739 assert_eq!(ring.ring_size, 8);
740 }
741
742 #[test]
743 fn test_adaptive_strategy_selection() {
744 let config = HierarchicalConfig::new(4, 4, 0, 0);
745 let aggregator = HierarchicalAggregator::new(config).expect("Construction failed");
746
747 let mut gradients = HashMap::new();
748 gradients.insert(
750 "param1".to_string(),
751 Tensor::zeros(&[8000, 8000]).expect("Failed to create tensor"),
752 );
753
754 let strategy = aggregator
755 .adaptive_strategy_selection(&gradients)
756 .expect("Operation failed in test");
757 assert!(matches!(strategy, AggregationStrategy::Ring));
759 }
760
761 #[test]
762 fn test_aggregation_stats_update() {
763 let config = HierarchicalConfig::new(2, 2, 0, 0);
764 let mut aggregator = HierarchicalAggregator::new(config).expect("Construction failed");
765
766 let mut gradients = HashMap::new();
767 gradients.insert(
768 "param1".to_string(),
769 Tensor::zeros(&[10, 10]).expect("Failed to create tensor"),
770 );
771
772 aggregator
773 .update_aggregation_stats(AggregationStrategy::BinaryTree, 100.0, &gradients)
774 .expect("Operation failed in test");
775
776 let stats = aggregator.get_stats();
777 assert_eq!(stats.total_operations, 1);
778 assert_eq!(stats.avg_aggregation_time, 100.0);
779 assert_eq!(
780 stats.strategy_history.get(&AggregationStrategy::BinaryTree),
781 Some(&1)
782 );
783 }
784
785 #[test]
786 fn test_recommended_strategy() {
787 let small_config = HierarchicalConfig::new(2, 2, 0, 0);
788 let small_aggregator =
789 HierarchicalAggregator::new(small_config).expect("Construction failed");
790 assert!(matches!(
791 small_aggregator.get_recommended_strategy(),
792 AggregationStrategy::BinaryTree
793 ));
794
795 let large_config = HierarchicalConfig::new(20, 1, 0, 0);
796 let large_aggregator =
797 HierarchicalAggregator::new(large_config).expect("Construction failed");
798 assert!(matches!(
799 large_aggregator.get_recommended_strategy(),
800 AggregationStrategy::Butterfly
801 ));
802 }
803
804 #[test]
805 fn test_butterfly_structure() {
806 let config = HierarchicalConfig::new(1, 8, 0, 0);
807 let butterfly = HierarchicalAggregator::build_butterfly_structure(&config)
808 .expect("Operation failed in test");
809
810 assert_eq!(butterfly.num_stages, 3); assert_eq!(butterfly.connections.len(), 3);
812 }
813
814 #[test]
815 fn test_network_topology_detection() {
816 let config = HierarchicalConfig::new(3, 2, 0, 0);
817 let topology = HierarchicalAggregator::detect_network_topology(&config)
818 .expect("Operation failed in test");
819
820 assert_eq!(topology.node_adjacency.len(), 3);
821 assert_eq!(topology.node_bandwidth.len(), 3);
822 assert_eq!(topology.node_latency.len(), 3);
823 assert!(topology.intra_node_bandwidth > 0.0);
824 assert!(topology.intra_node_latency > 0.0);
825 }
826}