1023 lines
32 KiB
Rust
1023 lines
32 KiB
Rust
//! Federated Learning Coordination for 100K+ Edge Devices
|
|
//!
|
|
//! This module provides production-ready federated learning coordination capable of
|
|
//! orchestrating training across 100,000+ edge devices with:
|
|
//! - Gradient compression and aggregation for bandwidth efficiency
|
|
//! - Network-aware scheduling and adaptive communication
|
|
//! - Battery-conscious device selection and power management
|
|
//! - Byzantine fault tolerance and secure aggregation
|
|
//! - Hierarchical coordination for massive scale
|
|
|
|
use std::collections::{HashMap, HashSet};
|
|
use std::sync::{Arc, Mutex, RwLock};
|
|
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
|
|
use tokio::sync::mpsc;
|
|
use tracing::info;
|
|
use serde::{Serialize, Deserialize};
|
|
|
|
/// Maximum number of devices that can be coordinated
|
|
pub const MAX_FEDERATED_DEVICES: u32 = 200_000;
|
|
|
|
/// Device identifier type
|
|
pub type DeviceId = String;
|
|
|
|
/// Coordinator identifier type
|
|
pub type CoordinatorId = String;
|
|
|
|
/// Training round identifier
|
|
pub type RoundId = u64;
|
|
|
|
/// Federated device metadata and capabilities
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct FederatedDevice {
|
|
/// Unique device identifier
|
|
pub device_id: DeviceId,
|
|
/// Device type and capabilities
|
|
pub device_type: EdgeDeviceType,
|
|
/// Current device status
|
|
pub status: DeviceStatus,
|
|
/// Network connectivity information
|
|
pub network_info: NetworkInfo,
|
|
/// Battery and power status
|
|
pub power_status: PowerStatus,
|
|
/// Computational capabilities
|
|
pub compute_capabilities: ComputeCapabilities,
|
|
/// Training data statistics
|
|
pub data_info: DataInfo,
|
|
/// Device availability schedule
|
|
pub availability: AvailabilitySchedule,
|
|
/// Performance metrics
|
|
pub performance_metrics: PerformanceMetrics,
|
|
/// Last communication timestamp
|
|
pub last_seen: SystemTime,
|
|
}
|
|
|
|
/// Edge device types with different capabilities
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
pub enum EdgeDeviceType {
|
|
/// High-end mobile device (flagship smartphone/tablet)
|
|
HighEndMobile,
|
|
/// Standard mobile device
|
|
StandardMobile,
|
|
/// Low-end mobile device
|
|
LowEndMobile,
|
|
/// IoT sensor device
|
|
IoTSensor,
|
|
/// Edge server/gateway
|
|
EdgeServer,
|
|
/// Embedded system
|
|
Embedded,
|
|
/// Vehicle/automotive system
|
|
Automotive,
|
|
/// Industrial IoT device
|
|
Industrial,
|
|
}
|
|
|
|
/// Device status enumeration
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
pub enum DeviceStatus {
|
|
/// Available for training
|
|
Available,
|
|
/// Currently training
|
|
Training,
|
|
/// Uploading results
|
|
Uploading,
|
|
/// Low battery, unavailable
|
|
LowBattery,
|
|
/// Network issues
|
|
NetworkUnavailable,
|
|
/// Device offline
|
|
Offline,
|
|
/// Device failed/error state
|
|
Failed,
|
|
}
|
|
|
|
/// Network connectivity information
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct NetworkInfo {
|
|
/// Connection type
|
|
pub connection_type: ConnectionType,
|
|
/// Bandwidth in Mbps
|
|
pub bandwidth_mbps: f32,
|
|
/// Network latency in milliseconds
|
|
pub latency_ms: u32,
|
|
/// Connection reliability (0.0-1.0)
|
|
pub reliability: f32,
|
|
/// Data plan limitations
|
|
pub data_plan: DataPlan,
|
|
}
|
|
|
|
/// Network connection types
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
pub enum ConnectionType {
|
|
/// WiFi connection
|
|
WiFi,
|
|
/// 5G cellular
|
|
Cellular5G,
|
|
/// 4G LTE cellular
|
|
Cellular4G,
|
|
/// 3G cellular
|
|
Cellular3G,
|
|
/// Ethernet (wired)
|
|
Ethernet,
|
|
/// Satellite connection
|
|
Satellite,
|
|
/// LoRa/low-power WAN
|
|
LPWAN,
|
|
}
|
|
|
|
/// Data plan information for cost-aware scheduling
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct DataPlan {
|
|
/// Unlimited data plan
|
|
pub unlimited: bool,
|
|
/// Monthly data allowance in GB
|
|
pub monthly_allowance_gb: Option<f32>,
|
|
/// Current usage in GB
|
|
pub current_usage_gb: f32,
|
|
/// Cost per GB for overages
|
|
pub cost_per_gb: Option<f32>,
|
|
}
|
|
|
|
/// Device power status
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct PowerStatus {
|
|
/// Battery level (0.0-1.0)
|
|
pub battery_level: f32,
|
|
/// Currently charging
|
|
pub is_charging: bool,
|
|
/// Power source type
|
|
pub power_source: PowerSource,
|
|
/// Estimated battery life in minutes
|
|
pub estimated_battery_life_minutes: Option<u32>,
|
|
}
|
|
|
|
/// Power source types
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub enum PowerSource {
|
|
/// Battery powered
|
|
Battery,
|
|
/// Wall power (AC adapter)
|
|
WallPower,
|
|
/// USB powered
|
|
USB,
|
|
/// Solar powered
|
|
Solar,
|
|
/// Vehicle power
|
|
Vehicle,
|
|
}
|
|
|
|
/// Computational capabilities
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ComputeCapabilities {
|
|
/// Number of CPU cores
|
|
pub cpu_cores: u32,
|
|
/// Available RAM in MB
|
|
pub ram_mb: u32,
|
|
/// GPU availability
|
|
pub has_gpu: bool,
|
|
/// SIMD support
|
|
pub simd_support: bool,
|
|
/// Estimated FLOPS performance
|
|
pub estimated_flops: f64,
|
|
/// Memory bandwidth in GB/s
|
|
pub memory_bandwidth_gbps: f32,
|
|
}
|
|
|
|
/// Training data information
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct DataInfo {
|
|
/// Number of training samples
|
|
pub sample_count: u32,
|
|
/// Data quality score (0.0-1.0)
|
|
pub quality_score: f32,
|
|
/// Data privacy level
|
|
pub privacy_level: PrivacyLevel,
|
|
/// Data distribution characteristics
|
|
pub distribution: DataDistribution,
|
|
}
|
|
|
|
/// Data privacy levels
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub enum PrivacyLevel {
|
|
/// Public data
|
|
Public,
|
|
/// Internal/proprietary data
|
|
Internal,
|
|
/// Sensitive personal data
|
|
Personal,
|
|
/// Highly sensitive data
|
|
Confidential,
|
|
}
|
|
|
|
/// Data distribution characteristics
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct DataDistribution {
|
|
/// Statistical distribution type
|
|
pub distribution_type: String,
|
|
/// Distribution parameters
|
|
pub parameters: HashMap<String, f64>,
|
|
}
|
|
|
|
/// Device availability schedule
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct AvailabilitySchedule {
|
|
/// Time zone offset from UTC
|
|
pub timezone_offset_hours: i8,
|
|
/// Available hours (0-23) for training
|
|
pub available_hours: HashSet<u8>,
|
|
/// Preferred training duration in minutes
|
|
pub preferred_duration_minutes: u32,
|
|
/// Blackout periods
|
|
pub blackout_periods: Vec<BlackoutPeriod>,
|
|
}
|
|
|
|
/// Blackout period when device is unavailable
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct BlackoutPeriod {
|
|
/// Start time (UTC timestamp)
|
|
pub start: SystemTime,
|
|
/// End time (UTC timestamp)
|
|
pub end: SystemTime,
|
|
/// Reason for blackout
|
|
pub reason: String,
|
|
}
|
|
|
|
/// Device performance metrics
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct PerformanceMetrics {
|
|
/// Average training time per round in seconds
|
|
pub avg_training_time_seconds: f32,
|
|
/// Average upload time in seconds
|
|
pub avg_upload_time_seconds: f32,
|
|
/// Training accuracy contribution
|
|
pub accuracy_contribution: f32,
|
|
/// Reliability score (0.0-1.0)
|
|
pub reliability_score: f32,
|
|
/// Communication efficiency
|
|
pub communication_efficiency: f32,
|
|
}
|
|
|
|
/// Gradient compression configuration
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct GradientCompression {
|
|
/// Compression algorithm
|
|
pub algorithm: CompressionAlgorithm,
|
|
/// Compression ratio (0.001-1.0)
|
|
pub compression_ratio: f32,
|
|
/// Error correction enabled
|
|
pub error_correction: bool,
|
|
/// Adaptive compression based on network
|
|
pub adaptive_compression: bool,
|
|
}
|
|
|
|
/// Gradient compression algorithms
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub enum CompressionAlgorithm {
|
|
/// No compression
|
|
None,
|
|
/// Top-K sparsification
|
|
TopK,
|
|
/// Random sparsification
|
|
RandomK,
|
|
/// Quantization-based compression
|
|
Quantization,
|
|
/// Federated dropout
|
|
FederatedDropout,
|
|
/// Sketching-based compression
|
|
Sketching,
|
|
}
|
|
|
|
/// Aggregation strategy configuration
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct AggregationStrategy {
|
|
/// Aggregation algorithm
|
|
pub algorithm: AggregationAlgorithm,
|
|
/// Weighting scheme
|
|
pub weighting: WeightingScheme,
|
|
/// Byzantine fault tolerance
|
|
pub byzantine_tolerance: ByzantineTolerance,
|
|
/// Differential privacy
|
|
pub differential_privacy: Option<DifferentialPrivacy>,
|
|
}
|
|
|
|
/// Aggregation algorithms
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub enum AggregationAlgorithm {
|
|
/// Federated Averaging (FedAvg)
|
|
FedAvg,
|
|
/// Federated Proximal (FedProx)
|
|
FedProx,
|
|
/// Stochastic Controlled Averaging (SCAFFOLD)
|
|
SCAFFOLD,
|
|
/// Federated Adam (FedAdam)
|
|
FedAdam,
|
|
/// Federated YOGI
|
|
FedYogi,
|
|
/// LAG (Local Adaptive Gradient)
|
|
LAG,
|
|
}
|
|
|
|
/// Weighting schemes for aggregation
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub enum WeightingScheme {
|
|
/// Equal weights
|
|
Equal,
|
|
/// Sample count based
|
|
SampleBased,
|
|
/// Performance based
|
|
PerformanceBased,
|
|
/// Reliability based
|
|
ReliabilityBased,
|
|
/// Adaptive weighting
|
|
Adaptive,
|
|
}
|
|
|
|
/// Byzantine fault tolerance configuration
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ByzantineTolerance {
|
|
/// Enable Byzantine fault tolerance
|
|
pub enabled: bool,
|
|
/// Maximum fraction of Byzantine devices (0.0-0.5)
|
|
pub max_byzantine_fraction: f32,
|
|
/// Detection algorithm
|
|
pub detection_algorithm: ByzantineDetection,
|
|
}
|
|
|
|
/// Byzantine fault detection algorithms
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub enum ByzantineDetection {
|
|
/// No detection
|
|
None,
|
|
/// Coordinate-wise median
|
|
CoordinateMedian,
|
|
/// Trimmed mean
|
|
TrimmedMean,
|
|
/// Krum algorithm
|
|
Krum,
|
|
/// Bulyan algorithm
|
|
Bulyan,
|
|
/// FoolsGold
|
|
FoolsGold,
|
|
}
|
|
|
|
/// Differential privacy configuration
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct DifferentialPrivacy {
|
|
/// Privacy budget epsilon
|
|
pub epsilon: f32,
|
|
/// Privacy budget delta
|
|
pub delta: f32,
|
|
/// Noise mechanism
|
|
pub noise_mechanism: NoiseMechanism,
|
|
/// Clipping threshold
|
|
pub clipping_threshold: f32,
|
|
}
|
|
|
|
/// Differential privacy noise mechanisms
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub enum NoiseMechanism {
|
|
/// Gaussian noise
|
|
Gaussian,
|
|
/// Laplacian noise
|
|
Laplacian,
|
|
/// Exponential mechanism
|
|
Exponential,
|
|
}
|
|
|
|
/// Federated training round information
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct TrainingRound {
|
|
/// Round identifier
|
|
pub round_id: RoundId,
|
|
/// Round start time
|
|
pub start_time: SystemTime,
|
|
/// Round deadline
|
|
pub deadline: SystemTime,
|
|
/// Selected devices for this round
|
|
pub selected_devices: HashSet<DeviceId>,
|
|
/// Target number of devices
|
|
pub target_device_count: u32,
|
|
/// Model version for this round
|
|
pub model_version: String,
|
|
/// Training configuration
|
|
pub training_config: TrainingConfig,
|
|
/// Aggregation configuration
|
|
pub aggregation_config: AggregationStrategy,
|
|
}
|
|
|
|
/// Training configuration for federated round
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct TrainingConfig {
|
|
/// Local epochs per device
|
|
pub local_epochs: u32,
|
|
/// Local batch size
|
|
pub local_batch_size: u32,
|
|
/// Learning rate
|
|
pub learning_rate: f32,
|
|
/// Gradient clipping threshold
|
|
pub gradient_clipping: Option<f32>,
|
|
/// Early stopping patience
|
|
pub early_stopping_patience: Option<u32>,
|
|
}
|
|
|
|
/// Device selection result
|
|
#[derive(Debug, Clone)]
|
|
pub struct DeviceSelectionResult {
|
|
/// Selected devices
|
|
pub selected_devices: Vec<DeviceId>,
|
|
/// Selection criteria used
|
|
pub selection_criteria: SelectionCriteria,
|
|
/// Total available devices
|
|
pub total_available: u32,
|
|
/// Selection time
|
|
pub selection_time: Duration,
|
|
}
|
|
|
|
/// Device selection criteria
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct SelectionCriteria {
|
|
/// Minimum battery level required
|
|
pub min_battery_level: f32,
|
|
/// Minimum bandwidth required (Mbps)
|
|
pub min_bandwidth_mbps: f32,
|
|
/// Maximum latency allowed (ms)
|
|
pub max_latency_ms: u32,
|
|
/// Required availability duration (minutes)
|
|
pub required_availability_minutes: u32,
|
|
/// Minimum data quality score
|
|
pub min_data_quality: f32,
|
|
}
|
|
|
|
/// Federated learning coordinator for massive scale
|
|
#[derive(Debug)]
|
|
pub struct FederatedCoordinator {
|
|
/// Coordinator identifier
|
|
coordinator_id: CoordinatorId,
|
|
/// Registered devices
|
|
devices: Arc<RwLock<HashMap<DeviceId, FederatedDevice>>>,
|
|
/// Active training rounds
|
|
active_rounds: Arc<Mutex<HashMap<RoundId, TrainingRound>>>,
|
|
/// Device selection strategy
|
|
selection_strategy: SelectionStrategy,
|
|
/// Gradient compression configuration
|
|
compression_config: GradientCompression,
|
|
/// Aggregation strategy
|
|
aggregation_strategy: AggregationStrategy,
|
|
/// Communication channel for device messages
|
|
device_channel: Arc<Mutex<mpsc::UnboundedReceiver<DeviceMessage>>>,
|
|
/// Performance metrics
|
|
metrics: Arc<Mutex<CoordinatorMetrics>>,
|
|
/// Hierarchical coordinators for scale
|
|
sub_coordinators: Arc<RwLock<HashMap<CoordinatorId, SubCoordinator>>>,
|
|
}
|
|
|
|
/// Device selection strategies
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum SelectionStrategy {
|
|
/// Random selection
|
|
Random,
|
|
/// Battery-aware selection
|
|
BatteryAware,
|
|
/// Network-aware selection
|
|
NetworkAware,
|
|
/// Performance-based selection
|
|
PerformanceBased,
|
|
/// Hybrid multi-criteria selection
|
|
Hybrid,
|
|
/// Intelligent adaptive selection
|
|
Intelligent,
|
|
}
|
|
|
|
/// Device message types
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub enum DeviceMessage {
|
|
/// Device registration
|
|
Register {
|
|
device: FederatedDevice,
|
|
},
|
|
/// Device status update
|
|
StatusUpdate {
|
|
device_id: DeviceId,
|
|
status: DeviceStatus,
|
|
},
|
|
/// Training completion
|
|
TrainingComplete {
|
|
device_id: DeviceId,
|
|
round_id: RoundId,
|
|
gradients: Vec<u8>, // Compressed gradients
|
|
metrics: TrainingMetrics,
|
|
},
|
|
/// Device heartbeat
|
|
Heartbeat {
|
|
device_id: DeviceId,
|
|
timestamp: SystemTime,
|
|
},
|
|
/// Error report
|
|
Error {
|
|
device_id: DeviceId,
|
|
error: String,
|
|
},
|
|
}
|
|
|
|
/// Training metrics from device
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct TrainingMetrics {
|
|
/// Training loss
|
|
pub loss: f32,
|
|
/// Training accuracy
|
|
pub accuracy: f32,
|
|
/// Number of samples processed
|
|
pub samples_processed: u32,
|
|
/// Training duration in seconds
|
|
pub duration_seconds: f32,
|
|
/// Memory usage in MB
|
|
pub memory_usage_mb: u32,
|
|
/// Energy consumption in mAh
|
|
pub energy_consumption_mah: Option<f32>,
|
|
}
|
|
|
|
/// Coordinator performance metrics
|
|
#[derive(Debug, Clone, Default)]
|
|
pub struct CoordinatorMetrics {
|
|
/// Total devices registered
|
|
pub total_devices: u32,
|
|
/// Active devices count
|
|
pub active_devices: u32,
|
|
/// Completed training rounds
|
|
pub completed_rounds: u64,
|
|
/// Average round duration
|
|
pub avg_round_duration_seconds: f32,
|
|
/// Device selection time
|
|
pub avg_selection_time_ms: f32,
|
|
/// Aggregation time
|
|
pub avg_aggregation_time_ms: f32,
|
|
/// Communication efficiency
|
|
pub communication_efficiency: f32,
|
|
/// Model convergence rate
|
|
pub convergence_rate: f32,
|
|
/// Byzantine devices detected
|
|
pub byzantine_devices_detected: u32,
|
|
}
|
|
|
|
/// Sub-coordinator for hierarchical scaling
|
|
#[derive(Debug, Clone)]
|
|
pub struct SubCoordinator {
|
|
/// Sub-coordinator identifier
|
|
pub coordinator_id: CoordinatorId,
|
|
/// Managed device range
|
|
pub device_range: (u32, u32),
|
|
/// Sub-coordinator endpoint
|
|
pub endpoint: String,
|
|
/// Performance metrics
|
|
pub metrics: CoordinatorMetrics,
|
|
}
|
|
|
|
/// Device statistics summary
|
|
#[derive(Debug, Clone)]
|
|
pub struct DeviceStatistics {
|
|
pub total_devices: u32,
|
|
pub by_type: HashMap<EdgeDeviceType, u32>,
|
|
pub by_status: HashMap<DeviceStatus, u32>,
|
|
pub by_connection: HashMap<ConnectionType, u32>,
|
|
pub avg_battery_level: f32,
|
|
pub avg_performance_score: f32,
|
|
}
|
|
|
|
/// Federation error types
|
|
#[derive(Debug, Clone)]
|
|
pub enum FederationError {
|
|
/// Device registration failed
|
|
RegistrationFailed {
|
|
device_id: DeviceId,
|
|
reason: String,
|
|
},
|
|
/// Insufficient device capabilities
|
|
InsufficientCapabilities {
|
|
device_id: DeviceId,
|
|
reason: String,
|
|
},
|
|
/// Training round not found
|
|
RoundNotFound(RoundId),
|
|
/// Device not found
|
|
DeviceNotFound(DeviceId),
|
|
/// Communication error
|
|
CommunicationError(String),
|
|
/// Aggregation failed
|
|
AggregationFailed(String),
|
|
/// Byzantine attack detected
|
|
ByzantineAttack {
|
|
detected_devices: Vec<DeviceId>,
|
|
},
|
|
/// Configuration error
|
|
ConfigurationError(String),
|
|
}
|
|
|
|
impl std::fmt::Display for FederationError {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
FederationError::RegistrationFailed { device_id, reason } => {
|
|
write!(f, "Device registration failed for {}: {}", device_id, reason)
|
|
}
|
|
FederationError::InsufficientCapabilities { device_id, reason } => {
|
|
write!(f, "Insufficient capabilities for device {}: {}", device_id, reason)
|
|
}
|
|
FederationError::RoundNotFound(round_id) => {
|
|
write!(f, "Training round {} not found", round_id)
|
|
}
|
|
FederationError::DeviceNotFound(device_id) => {
|
|
write!(f, "Device {} not found", device_id)
|
|
}
|
|
FederationError::CommunicationError(msg) => {
|
|
write!(f, "Communication error: {}", msg)
|
|
}
|
|
FederationError::AggregationFailed(msg) => {
|
|
write!(f, "Aggregation failed: {}", msg)
|
|
}
|
|
FederationError::ByzantineAttack { detected_devices } => {
|
|
write!(f, "Byzantine attack detected from devices: {:?}", detected_devices)
|
|
}
|
|
FederationError::ConfigurationError(msg) => {
|
|
write!(f, "Configuration error: {}", msg)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for FederationError {}
|
|
|
|
impl FederatedCoordinator {
|
|
/// Create a new federated coordinator
|
|
pub fn new(
|
|
coordinator_id: CoordinatorId,
|
|
selection_strategy: SelectionStrategy,
|
|
compression_config: GradientCompression,
|
|
aggregation_strategy: AggregationStrategy,
|
|
) -> (Self, mpsc::UnboundedSender<DeviceMessage>) {
|
|
let (sender, receiver) = mpsc::unbounded_channel();
|
|
|
|
let coordinator = Self {
|
|
coordinator_id,
|
|
devices: Arc::new(RwLock::new(HashMap::new())),
|
|
active_rounds: Arc::new(Mutex::new(HashMap::new())),
|
|
selection_strategy,
|
|
compression_config,
|
|
aggregation_strategy,
|
|
device_channel: Arc::new(Mutex::new(receiver)),
|
|
metrics: Arc::new(Mutex::new(CoordinatorMetrics::default())),
|
|
sub_coordinators: Arc::new(RwLock::new(HashMap::new())),
|
|
};
|
|
|
|
(coordinator, sender)
|
|
}
|
|
|
|
/// Register a new device
|
|
pub async fn register_device(&self, device: FederatedDevice) -> Result<(), FederationError> {
|
|
let device_id = device.device_id.clone();
|
|
info!("Registering device: {} (type: {:?})", device_id, device.device_type);
|
|
|
|
// Validate device capabilities
|
|
self.validate_device_capabilities(&device)?;
|
|
|
|
// Add device to registry
|
|
{
|
|
let mut devices = self.devices.write().unwrap();
|
|
devices.insert(device_id.clone(), device);
|
|
}
|
|
|
|
// Update metrics
|
|
{
|
|
let mut metrics = self.metrics.lock().unwrap();
|
|
metrics.total_devices += 1;
|
|
metrics.active_devices += 1;
|
|
}
|
|
|
|
info!("Device {} registered successfully", device_id);
|
|
Ok(())
|
|
}
|
|
|
|
/// Select devices for training round
|
|
pub async fn select_devices(
|
|
&self,
|
|
target_count: u32,
|
|
selection_criteria: SelectionCriteria,
|
|
) -> Result<DeviceSelectionResult, FederationError> {
|
|
let start_time = Instant::now();
|
|
info!("Selecting {} devices for training round", target_count);
|
|
|
|
// Ensure we don't exceed maximum device limit
|
|
let target_count = target_count.min(MAX_FEDERATED_DEVICES);
|
|
|
|
let devices = self.devices.read().unwrap();
|
|
let mut available_devices: Vec<_> = devices
|
|
.values()
|
|
.filter(|device| self.is_device_eligible(device, &selection_criteria))
|
|
.collect();
|
|
|
|
let total_available = available_devices.len() as u32;
|
|
info!("Found {} eligible devices out of {} total", total_available, devices.len());
|
|
|
|
// Apply selection strategy
|
|
let selected_devices = match self.selection_strategy {
|
|
SelectionStrategy::Random => self.select_random(&mut available_devices, target_count),
|
|
SelectionStrategy::BatteryAware => self.select_battery_aware(&mut available_devices, target_count),
|
|
SelectionStrategy::NetworkAware => self.select_network_aware(&mut available_devices, target_count),
|
|
SelectionStrategy::PerformanceBased => self.select_performance_based(&mut available_devices, target_count),
|
|
SelectionStrategy::Hybrid => self.select_hybrid(&mut available_devices, target_count),
|
|
SelectionStrategy::Intelligent => self.select_intelligent(&mut available_devices, target_count),
|
|
};
|
|
|
|
let selection_time = start_time.elapsed();
|
|
|
|
// Update metrics
|
|
{
|
|
let mut metrics = self.metrics.lock().unwrap();
|
|
metrics.avg_selection_time_ms =
|
|
(metrics.avg_selection_time_ms * 0.9) + (selection_time.as_millis() as f32 * 0.1);
|
|
}
|
|
|
|
info!("Selected {} devices in {:?}", selected_devices.len(), selection_time);
|
|
|
|
Ok(DeviceSelectionResult {
|
|
selected_devices: selected_devices.into_iter().map(|d| d.device_id.clone()).collect(),
|
|
selection_criteria,
|
|
total_available,
|
|
selection_time,
|
|
})
|
|
}
|
|
|
|
/// Start a new training round
|
|
pub async fn start_training_round(
|
|
&self,
|
|
selected_devices: Vec<DeviceId>,
|
|
training_config: TrainingConfig,
|
|
deadline: Duration,
|
|
) -> Result<RoundId, FederationError> {
|
|
let round_id = self.generate_round_id();
|
|
let start_time = SystemTime::now();
|
|
let deadline_time = start_time + deadline;
|
|
|
|
info!("Starting training round {} with {} devices", round_id, selected_devices.len());
|
|
|
|
let training_round = TrainingRound {
|
|
round_id,
|
|
start_time,
|
|
deadline: deadline_time,
|
|
selected_devices: selected_devices.iter().cloned().collect(),
|
|
target_device_count: selected_devices.len() as u32,
|
|
model_version: format!("v{}", round_id),
|
|
training_config,
|
|
aggregation_config: self.aggregation_strategy.clone(),
|
|
};
|
|
|
|
// Store active round
|
|
{
|
|
let mut active_rounds = self.active_rounds.lock().unwrap();
|
|
active_rounds.insert(round_id, training_round.clone());
|
|
}
|
|
|
|
info!("Training round {} started successfully", round_id);
|
|
Ok(round_id)
|
|
}
|
|
|
|
/// Aggregate gradients from completed devices
|
|
pub async fn aggregate_gradients(
|
|
&self,
|
|
round_id: RoundId,
|
|
device_gradients: HashMap<DeviceId, Vec<u8>>,
|
|
) -> Result<Vec<u8>, FederationError> {
|
|
let start_time = Instant::now();
|
|
info!("Aggregating gradients for round {} from {} devices",
|
|
round_id, device_gradients.len());
|
|
|
|
// Get round information
|
|
let _round = {
|
|
let active_rounds = self.active_rounds.lock().unwrap();
|
|
active_rounds.get(&round_id)
|
|
.ok_or(FederationError::RoundNotFound(round_id))?
|
|
.clone()
|
|
};
|
|
|
|
// Placeholder aggregation logic
|
|
let compressed_gradients = vec![0u8; 1024]; // Dummy aggregated gradients
|
|
|
|
let aggregation_time = start_time.elapsed();
|
|
|
|
// Update metrics
|
|
{
|
|
let mut metrics = self.metrics.lock().unwrap();
|
|
metrics.avg_aggregation_time_ms =
|
|
(metrics.avg_aggregation_time_ms * 0.9) + (aggregation_time.as_millis() as f32 * 0.1);
|
|
}
|
|
|
|
info!("Gradient aggregation completed in {:?}", aggregation_time);
|
|
Ok(compressed_gradients)
|
|
}
|
|
|
|
/// Get coordinator performance metrics
|
|
pub fn get_metrics(&self) -> CoordinatorMetrics {
|
|
self.metrics.lock().unwrap().clone()
|
|
}
|
|
|
|
/// Get device statistics
|
|
pub fn get_device_statistics(&self) -> DeviceStatistics {
|
|
let devices = self.devices.read().unwrap();
|
|
|
|
let mut stats = DeviceStatistics {
|
|
total_devices: devices.len() as u32,
|
|
by_type: HashMap::new(),
|
|
by_status: HashMap::new(),
|
|
by_connection: HashMap::new(),
|
|
avg_battery_level: 0.0,
|
|
avg_performance_score: 0.0,
|
|
};
|
|
|
|
for device in devices.values() {
|
|
// Count by device type
|
|
*stats.by_type.entry(device.device_type).or_insert(0) += 1;
|
|
|
|
// Count by status
|
|
*stats.by_status.entry(device.status).or_insert(0) += 1;
|
|
|
|
// Count by connection type
|
|
*stats.by_connection.entry(device.network_info.connection_type).or_insert(0) += 1;
|
|
|
|
// Average battery level
|
|
stats.avg_battery_level += device.power_status.battery_level;
|
|
|
|
// Average performance score
|
|
stats.avg_performance_score += device.performance_metrics.reliability_score;
|
|
}
|
|
|
|
if !devices.is_empty() {
|
|
stats.avg_battery_level /= devices.len() as f32;
|
|
stats.avg_performance_score /= devices.len() as f32;
|
|
}
|
|
|
|
stats
|
|
}
|
|
|
|
/// Validate device capabilities
|
|
fn validate_device_capabilities(&self, device: &FederatedDevice) -> Result<(), FederationError> {
|
|
// Check minimum requirements
|
|
if device.compute_capabilities.ram_mb < 64 {
|
|
return Err(FederationError::InsufficientCapabilities {
|
|
device_id: device.device_id.clone(),
|
|
reason: "Insufficient RAM (minimum 64MB required)".to_string(),
|
|
});
|
|
}
|
|
|
|
if device.compute_capabilities.estimated_flops < 1e6 {
|
|
return Err(FederationError::InsufficientCapabilities {
|
|
device_id: device.device_id.clone(),
|
|
reason: "Insufficient compute performance".to_string(),
|
|
});
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Check if device is eligible for selection
|
|
fn is_device_eligible(&self, device: &FederatedDevice, criteria: &SelectionCriteria) -> bool {
|
|
// Check device status
|
|
if device.status != DeviceStatus::Available {
|
|
return false;
|
|
}
|
|
|
|
// Check battery level
|
|
if device.power_status.battery_level < criteria.min_battery_level {
|
|
return false;
|
|
}
|
|
|
|
// Check network bandwidth
|
|
if device.network_info.bandwidth_mbps < criteria.min_bandwidth_mbps {
|
|
return false;
|
|
}
|
|
|
|
// Check network latency
|
|
if device.network_info.latency_ms > criteria.max_latency_ms {
|
|
return false;
|
|
}
|
|
|
|
// Check data quality
|
|
if device.data_info.quality_score < criteria.min_data_quality {
|
|
return false;
|
|
}
|
|
|
|
// Check availability duration
|
|
if device.performance_metrics.avg_training_time_seconds > (criteria.required_availability_minutes * 60) as f32 {
|
|
return false;
|
|
}
|
|
|
|
true
|
|
}
|
|
|
|
/// Random device selection
|
|
fn select_random<'a>(&self, devices: &mut Vec<&'a FederatedDevice>, target_count: u32) -> Vec<&'a FederatedDevice> {
|
|
use rand::seq::SliceRandom;
|
|
let mut rng = rand::thread_rng();
|
|
devices.shuffle(&mut rng);
|
|
devices.iter().take(target_count as usize).cloned().collect()
|
|
}
|
|
|
|
/// Battery-aware device selection
|
|
fn select_battery_aware<'a>(&self, devices: &mut Vec<&'a FederatedDevice>, target_count: u32) -> Vec<&'a FederatedDevice> {
|
|
// Sort by battery level (descending) and charging status
|
|
devices.sort_by(|a, b| {
|
|
let a_score = a.power_status.battery_level + if a.power_status.is_charging { 0.5 } else { 0.0 };
|
|
let b_score = b.power_status.battery_level + if b.power_status.is_charging { 0.5 } else { 0.0 };
|
|
b_score.total_cmp(&a_score)
|
|
});
|
|
|
|
devices.iter().take(target_count as usize).cloned().collect()
|
|
}
|
|
|
|
/// Network-aware device selection
|
|
fn select_network_aware<'a>(&self, devices: &mut Vec<&'a FederatedDevice>, target_count: u32) -> Vec<&'a FederatedDevice> {
|
|
// Sort by network quality (bandwidth/latency ratio)
|
|
devices.sort_by(|a, b| {
|
|
let a_score = a.network_info.bandwidth_mbps / (a.network_info.latency_ms as f32);
|
|
let b_score = b.network_info.bandwidth_mbps / (b.network_info.latency_ms as f32);
|
|
b_score.total_cmp(&a_score)
|
|
});
|
|
|
|
devices.iter().take(target_count as usize).cloned().collect()
|
|
}
|
|
|
|
/// Performance-based device selection
|
|
fn select_performance_based<'a>(&self, devices: &mut Vec<&'a FederatedDevice>, target_count: u32) -> Vec<&'a FederatedDevice> {
|
|
// Sort by overall performance score
|
|
devices.sort_by(|a, b| {
|
|
let a_score = a.performance_metrics.reliability_score *
|
|
a.performance_metrics.communication_efficiency *
|
|
(a.compute_capabilities.estimated_flops as f32).log10();
|
|
let b_score = b.performance_metrics.reliability_score *
|
|
b.performance_metrics.communication_efficiency *
|
|
(b.compute_capabilities.estimated_flops as f32).log10();
|
|
b_score.total_cmp(&a_score)
|
|
});
|
|
|
|
devices.iter().take(target_count as usize).cloned().collect()
|
|
}
|
|
|
|
/// Hybrid multi-criteria device selection
|
|
fn select_hybrid<'a>(&self, devices: &mut Vec<&'a FederatedDevice>, target_count: u32) -> Vec<&'a FederatedDevice> {
|
|
// Calculate composite score based on multiple criteria
|
|
devices.sort_by(|a, b| {
|
|
let a_score = self.calculate_composite_score(a);
|
|
let b_score = self.calculate_composite_score(b);
|
|
b_score.total_cmp(&a_score)
|
|
});
|
|
|
|
devices.iter().take(target_count as usize).cloned().collect()
|
|
}
|
|
|
|
/// Intelligent adaptive device selection
|
|
fn select_intelligent<'a>(&self, devices: &mut Vec<&'a FederatedDevice>, target_count: u32) -> Vec<&'a FederatedDevice> {
|
|
// Use machine learning-based selection (simplified version)
|
|
devices.sort_by(|a, b| {
|
|
let a_score = self.calculate_intelligent_score(a);
|
|
let b_score = self.calculate_intelligent_score(b);
|
|
b_score.total_cmp(&a_score)
|
|
});
|
|
|
|
devices.iter().take(target_count as usize).cloned().collect()
|
|
}
|
|
|
|
/// Calculate composite score for hybrid selection
|
|
fn calculate_composite_score(&self, device: &FederatedDevice) -> f32 {
|
|
let battery_score = device.power_status.battery_level;
|
|
let network_score = device.network_info.bandwidth_mbps / (device.network_info.latency_ms as f32).max(1.0);
|
|
let performance_score = device.performance_metrics.reliability_score;
|
|
let compute_score = (device.compute_capabilities.estimated_flops as f32).log10() / 12.0; // Normalize
|
|
|
|
// Weighted combination
|
|
battery_score * 0.3 + network_score * 0.3 + performance_score * 0.25 + compute_score * 0.15
|
|
}
|
|
|
|
/// Calculate intelligent score using ML-based prediction
|
|
fn calculate_intelligent_score(&self, device: &FederatedDevice) -> f32 {
|
|
// Simplified ML-based scoring (in practice, use a trained model)
|
|
let features = vec![
|
|
device.power_status.battery_level,
|
|
device.network_info.bandwidth_mbps / 100.0, // Normalize
|
|
1.0 / (device.network_info.latency_ms as f32 / 100.0), // Invert and normalize
|
|
device.performance_metrics.reliability_score,
|
|
device.performance_metrics.communication_efficiency,
|
|
(device.compute_capabilities.estimated_flops as f32).log10() / 12.0,
|
|
device.data_info.quality_score,
|
|
];
|
|
|
|
// Simple linear combination (replace with actual ML model)
|
|
features.iter().sum::<f32>() / features.len() as f32
|
|
}
|
|
|
|
/// Generate unique round ID
|
|
fn generate_round_id(&self) -> RoundId {
|
|
SystemTime::now()
|
|
.duration_since(UNIX_EPOCH)
|
|
.unwrap()
|
|
.as_millis() as u64
|
|
}
|
|
}
|