1#![allow(dead_code)]
16
17use anyhow::Result;
18use parking_lot::{Mutex, RwLock};
19use serde::{Deserialize, Serialize};
20use std::collections::HashMap;
21use std::sync::atomic::{AtomicUsize, Ordering};
22use std::sync::Arc;
23use std::time::{Duration, Instant};
24use trustformers_core::tensor::Tensor;
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct AsyncSGDConfig {
29 pub learning_rate: f32,
31 pub momentum: f32,
33 pub weight_decay: f32,
35 pub max_staleness: usize,
37 pub staleness_factor: f32,
39}
40
41impl Default for AsyncSGDConfig {
42 fn default() -> Self {
43 Self {
44 learning_rate: 1e-3,
45 momentum: 0.9,
46 weight_decay: 0.0,
47 max_staleness: 10,
48 staleness_factor: 0.9,
49 }
50 }
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct HogwildConfig {
56 pub learning_rate: f32,
58 pub sparse_ratio: f32,
60 pub max_workers: usize,
62}
63
64impl Default for HogwildConfig {
65 fn default() -> Self {
66 Self {
67 learning_rate: 1e-3,
68 sparse_ratio: 0.1,
69 max_workers: 4,
70 }
71 }
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct DelayedGradientConfig {
77 pub learning_rate: f32,
79 pub max_delay: usize,
81 pub compensation_method: DelayCompensationMethod,
83 pub compensation_factor: f32,
85}
86
87impl Default for DelayedGradientConfig {
88 fn default() -> Self {
89 Self {
90 learning_rate: 1e-3,
91 max_delay: 20,
92 compensation_method: DelayCompensationMethod::LinearDecay,
93 compensation_factor: 0.5,
94 }
95 }
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
100pub enum DelayCompensationMethod {
101 None,
103 LinearDecay,
105 ExponentialDecay,
107 Adaptive,
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct ElasticAveragingConfig {
114 pub learning_rate: f32,
116 pub alpha: f32,
118 pub tau: usize,
120 pub beta: f32,
122}
123
124impl Default for ElasticAveragingConfig {
125 fn default() -> Self {
126 Self {
127 learning_rate: 1e-3,
128 alpha: 0.6,
129 tau: 10,
130 beta: 0.9,
131 }
132 }
133}
134
135pub struct ParameterServer {
137 parameters: Arc<RwLock<Vec<Tensor>>>,
139 global_step: AtomicUsize,
141 version_counters: Arc<Mutex<Vec<usize>>>,
143 worker_timestamps: Arc<Mutex<HashMap<usize, Instant>>>,
145}
146
147impl ParameterServer {
148 pub fn new(initial_parameters: Vec<Tensor>) -> Self {
150 let param_count = initial_parameters.len();
151 Self {
152 parameters: Arc::new(RwLock::new(initial_parameters)),
153 global_step: AtomicUsize::new(0),
154 version_counters: Arc::new(Mutex::new(vec![0; param_count])),
155 worker_timestamps: Arc::new(Mutex::new(HashMap::new())),
156 }
157 }
158
159 pub fn get_parameters(&self, worker_id: usize) -> Result<(Vec<Tensor>, Vec<usize>)> {
161 let params = self.parameters.read().clone();
162 let versions = self.version_counters.lock().clone();
163
164 let mut timestamps = self.worker_timestamps.lock();
166 timestamps.insert(worker_id, Instant::now());
167
168 Ok((params, versions))
169 }
170
171 pub fn update_parameters(
173 &self,
174 worker_id: usize,
175 gradients: Vec<Tensor>,
176 param_versions: Vec<usize>,
177 learning_rate: f32,
178 ) -> Result<()> {
179 let _current_step = self.global_step.load(Ordering::SeqCst);
180
181 let staleness = self.compute_staleness(worker_id, ¶m_versions)?;
183 if staleness > 10 {
184 return Ok(());
186 }
187
188 let compensated_lr = learning_rate * (1.0 / (1.0 + staleness as f32 * 0.1));
190
191 {
193 let mut params = self.parameters.write();
194 let mut versions = self.version_counters.lock();
195
196 for (i, gradient) in gradients.iter().enumerate() {
197 if i < params.len() {
198 let update = gradient.mul_scalar(compensated_lr)?;
199 params[i] = params[i].sub(&update)?;
200 versions[i] += 1;
201 }
202 }
203 }
204
205 self.global_step.fetch_add(1, Ordering::SeqCst);
206 Ok(())
207 }
208
209 fn compute_staleness(&self, _worker_id: usize, param_versions: &[usize]) -> Result<usize> {
210 let current_versions = self.version_counters.lock();
211 let max_staleness = param_versions
212 .iter()
213 .zip(current_versions.iter())
214 .map(|(old, new)| new.saturating_sub(*old))
215 .max()
216 .unwrap_or(0);
217 Ok(max_staleness)
218 }
219
220 pub fn get_global_step(&self) -> usize {
222 self.global_step.load(Ordering::SeqCst)
223 }
224}
225
226pub struct AsyncSGD {
228 config: AsyncSGDConfig,
229 worker_id: usize,
230 parameter_server: Arc<ParameterServer>,
231 momentum_buffers: Vec<Tensor>,
232 local_parameters: Vec<Tensor>,
233 param_versions: Vec<usize>,
234 last_sync_step: usize,
235}
236
237impl AsyncSGD {
238 pub fn new(
240 config: AsyncSGDConfig,
241 worker_id: usize,
242 parameter_server: Arc<ParameterServer>,
243 ) -> Result<Self> {
244 let (params, versions) = parameter_server.get_parameters(worker_id)?;
245 let param_count = params.len();
246
247 Ok(Self {
248 config,
249 worker_id,
250 parameter_server,
251 momentum_buffers: (0..param_count)
252 .map(|i| Tensor::zeros(¶ms[i].shape()).map_err(anyhow::Error::from))
253 .collect::<Result<Vec<_>>>()?,
254 local_parameters: params,
255 param_versions: versions,
256 last_sync_step: 0,
257 })
258 }
259
260 pub fn step(&mut self, gradients: &[Tensor]) -> Result<()> {
262 let current_step = self.parameter_server.get_global_step();
264 let staleness = current_step - self.last_sync_step;
265
266 if staleness > self.config.max_staleness {
267 self.sync_with_server()?;
268 }
269
270 for (i, gradient) in gradients.iter().enumerate() {
272 if i < self.local_parameters.len() {
273 let effective_grad = if self.config.weight_decay > 0.0 {
275 gradient.add(&self.local_parameters[i].mul_scalar(self.config.weight_decay)?)?
276 } else {
277 gradient.clone()
278 };
279
280 self.momentum_buffers[i] = self.momentum_buffers[i]
282 .mul_scalar(self.config.momentum)?
283 .add(&effective_grad)?;
284
285 let staleness_factor = self.config.staleness_factor.powi(staleness as i32);
287 let compensated_lr = self.config.learning_rate * staleness_factor;
288
289 let update = self.momentum_buffers[i].mul_scalar(compensated_lr)?;
291 self.local_parameters[i] = self.local_parameters[i].sub(&update)?;
292 }
293 }
294
295 if current_step.is_multiple_of(5) {
297 self.push_to_server(gradients)?;
298 }
299
300 Ok(())
301 }
302
303 fn sync_with_server(&mut self) -> Result<()> {
304 let (params, versions) = self.parameter_server.get_parameters(self.worker_id)?;
305 self.local_parameters = params;
306 self.param_versions = versions;
307 self.last_sync_step = self.parameter_server.get_global_step();
308 Ok(())
309 }
310
311 fn push_to_server(&self, gradients: &[Tensor]) -> Result<()> {
312 self.parameter_server.update_parameters(
313 self.worker_id,
314 gradients.to_vec(),
315 self.param_versions.clone(),
316 self.config.learning_rate,
317 )
318 }
319
320 pub fn get_parameters(&self) -> &[Tensor] {
322 &self.local_parameters
323 }
324}
325
326pub struct Hogwild {
328 config: HogwildConfig,
329 worker_id: usize,
330 shared_parameters: Arc<RwLock<Vec<Tensor>>>,
331 local_step: usize,
332}
333
334impl Hogwild {
335 pub fn new(
337 config: HogwildConfig,
338 worker_id: usize,
339 shared_parameters: Arc<RwLock<Vec<Tensor>>>,
340 ) -> Self {
341 Self {
342 config,
343 worker_id,
344 shared_parameters,
345 local_step: 0,
346 }
347 }
348
349 pub fn sparse_step(&mut self, sparse_gradients: &[(usize, Tensor)]) -> Result<()> {
351 for &(param_idx, ref gradient) in sparse_gradients {
355 {
356 let params = self.shared_parameters.read();
357 if param_idx >= params.len() {
358 continue;
359 }
360 } let mut params_write = self.shared_parameters.write();
364 let update = gradient.mul_scalar(self.config.learning_rate)?;
365 params_write[param_idx] = params_write[param_idx].sub(&update)?;
366 }
367
368 self.local_step += 1;
369 Ok(())
370 }
371
372 pub fn select_sparse_indices(&self, total_params: usize) -> Vec<usize> {
374 use scirs2_core::random::*; let num_sparse = (total_params as f32 * self.config.sparse_ratio) as usize;
377 let mut indices: Vec<usize> = (0..total_params).collect();
378 let mut rng = thread_rng();
379 indices.shuffle(rng.rng_mut());
380 indices.truncate(num_sparse);
381 indices
382 }
383}
384
385pub struct DelayedGradient {
387 config: DelayedGradientConfig,
388 parameters: Vec<Tensor>,
389 gradient_buffer: Vec<(Tensor, usize, Instant)>, current_step: usize,
391}
392
393impl DelayedGradient {
394 pub fn new(config: DelayedGradientConfig, initial_parameters: Vec<Tensor>) -> Self {
396 Self {
397 config,
398 parameters: initial_parameters,
399 gradient_buffer: Vec::new(),
400 current_step: 0,
401 }
402 }
403
404 pub fn add_delayed_gradient(&mut self, gradient: Tensor, delay: usize) {
406 self.gradient_buffer.push((gradient, delay, Instant::now()));
407 }
408
409 pub fn step(&mut self) -> Result<()> {
411 self.current_step += 1;
412
413 let mut i = 0;
415 while i < self.gradient_buffer.len() {
416 let (ref gradient, delay, timestamp) = &self.gradient_buffer[i];
417 let age = timestamp.elapsed();
418
419 if age >= Duration::from_millis((*delay as u64) * 10) {
420 let compensation = self.compute_delay_compensation(*delay)?;
422 let compensated_lr = self.config.learning_rate * compensation;
423
424 for (j, param) in self.parameters.iter_mut().enumerate() {
426 if j < 1 {
427 let update = gradient.mul_scalar(compensated_lr)?;
429 *param = param.sub(&update)?;
430 }
431 }
432
433 self.gradient_buffer.remove(i);
434 } else {
435 i += 1;
436 }
437 }
438
439 Ok(())
440 }
441
442 fn compute_delay_compensation(&self, delay: usize) -> Result<f32> {
443 if delay > self.config.max_delay {
444 return Ok(0.0); }
446
447 let delay_ratio = delay as f32 / self.config.max_delay as f32;
448
449 let compensation = match self.config.compensation_method {
450 DelayCompensationMethod::None => 1.0,
451 DelayCompensationMethod::LinearDecay => {
452 1.0 - delay_ratio * self.config.compensation_factor
453 },
454 DelayCompensationMethod::ExponentialDecay => {
455 (-delay_ratio * self.config.compensation_factor).exp()
456 },
457 DelayCompensationMethod::Adaptive => {
458 1.0 / (1.0 + delay_ratio * self.config.compensation_factor)
460 },
461 };
462
463 Ok(compensation.max(0.1)) }
465
466 pub fn get_parameters(&self) -> &[Tensor] {
468 &self.parameters
469 }
470}
471
472pub struct ElasticAveraging {
474 config: ElasticAveragingConfig,
475 worker_id: usize,
476 local_parameters: Vec<Tensor>,
477 global_parameters: Arc<RwLock<Vec<Tensor>>>,
478 elastic_force: Vec<Tensor>,
479 local_step: usize,
480 last_communication: usize,
481}
482
483impl ElasticAveraging {
484 pub fn new(
486 config: ElasticAveragingConfig,
487 worker_id: usize,
488 global_parameters: Arc<RwLock<Vec<Tensor>>>,
489 ) -> Result<Self> {
490 let global_params = global_parameters.read().clone();
491 let param_count = global_params.len();
492
493 Ok(Self {
494 config,
495 worker_id,
496 local_parameters: global_params.clone(),
497 global_parameters,
498 elastic_force: (0..param_count)
499 .map(|i| Tensor::zeros(&global_params[i].shape()).map_err(anyhow::Error::from))
500 .collect::<Result<Vec<_>>>()?,
501 local_step: 0,
502 last_communication: 0,
503 })
504 }
505
506 pub fn step(&mut self, gradients: &[Tensor]) -> Result<()> {
508 for (i, gradient) in gradients.iter().enumerate() {
510 if i < self.local_parameters.len() {
511 let update = gradient.mul_scalar(self.config.learning_rate)?;
512 self.local_parameters[i] = self.local_parameters[i].sub(&update)?;
513 }
514 }
515
516 let global_params = self.global_parameters.read();
518 for i in 0..self.local_parameters.len() {
519 let diff = self.local_parameters[i].sub(&global_params[i])?;
520 self.elastic_force[i] = diff.mul_scalar(self.config.alpha)?;
521 let elastic_update = self.elastic_force[i].mul_scalar(self.config.learning_rate)?;
522 self.local_parameters[i] = self.local_parameters[i].sub(&elastic_update)?;
523 }
524 drop(global_params);
525
526 self.local_step += 1;
527
528 if self.local_step - self.last_communication >= self.config.tau {
530 self.communicate_with_global()?;
531 self.last_communication = self.local_step;
532 }
533
534 Ok(())
535 }
536
537 fn communicate_with_global(&mut self) -> Result<()> {
538 let mut global_params = self.global_parameters.write();
539
540 for i in 0..global_params.len() {
542 let local_contrib = self.local_parameters[i].mul_scalar(1.0 - self.config.beta)?;
543 let global_contrib = global_params[i].mul_scalar(self.config.beta)?;
544 global_params[i] = local_contrib.add(&global_contrib)?;
545 }
546
547 self.local_parameters = global_params.clone();
549
550 Ok(())
551 }
552
553 pub fn get_parameters(&self) -> &[Tensor] {
555 &self.local_parameters
556 }
557}
558
559#[cfg(test)]
560mod tests {
561 use super::*;
562
563 #[test]
564 fn test_async_sgd_config() {
565 let config = AsyncSGDConfig::default();
566 assert_eq!(config.learning_rate, 1e-3);
567 assert_eq!(config.momentum, 0.9);
568 assert_eq!(config.max_staleness, 10);
569 }
570
571 #[test]
572 fn test_hogwild_config() {
573 let config = HogwildConfig::default();
574 assert_eq!(config.learning_rate, 1e-3);
575 assert_eq!(config.sparse_ratio, 0.1);
576 assert_eq!(config.max_workers, 4);
577 }
578
579 #[test]
580 fn test_delayed_gradient_config() {
581 let config = DelayedGradientConfig::default();
582 assert_eq!(config.learning_rate, 1e-3);
583 assert_eq!(config.max_delay, 20);
584 assert!(matches!(
585 config.compensation_method,
586 DelayCompensationMethod::LinearDecay
587 ));
588 }
589
590 #[test]
591 fn test_parameter_server_creation() {
592 let params = vec![Tensor::zeros(&[10]).expect("Failed to create tensor")];
593 let server = ParameterServer::new(params);
594 assert_eq!(server.get_global_step(), 0);
595 }
596
597 #[test]
598 fn test_elastic_averaging_config() {
599 let config = ElasticAveragingConfig::default();
600 assert_eq!(config.learning_rate, 1e-3);
601 assert_eq!(config.alpha, 0.6);
602 assert_eq!(config.tau, 10);
603 assert_eq!(config.beta, 0.9);
604 }
605}