Skip to main content

batuta/experiment/
types.rs

1//! Core type definitions for experiment tracking.
2//!
3//! This module contains the fundamental types for compute devices, architectures,
4//! and model paradigms used throughout the experiment tracking system.
5
6use serde::{Deserialize, Serialize};
7use thiserror::Error;
8
9/// Errors that can occur during experiment tracking operations
10#[derive(Error, Debug, Clone, PartialEq)]
11pub enum ExperimentError {
12    #[error("Invalid compute device configuration: {0}")]
13    InvalidComputeDevice(String),
14
15    #[error("Metrics collection failed: {0}")]
16    MetricsCollectionFailed(String),
17
18    #[error("Pareto frontier calculation failed: {0}")]
19    ParetoFrontierFailed(String),
20
21    #[error("Invalid ORCID format: {0}")]
22    InvalidOrcid(String),
23
24    #[error("Citation generation failed: {0}")]
25    CitationGenerationFailed(String),
26
27    #[error("Sovereign distribution validation failed: {0}")]
28    SovereignValidationFailed(String),
29
30    #[error("Experiment storage error: {0}")]
31    StorageError(String),
32}
33
34/// Compute device abstraction for heterogeneous hardware
35#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
36pub enum ComputeDevice {
37    /// Standard CPU execution
38    Cpu { cores: u32, threads_per_core: u32, architecture: CpuArchitecture },
39    /// NVIDIA/AMD GPU acceleration
40    Gpu { name: String, memory_gb: f32, compute_capability: Option<String>, vendor: GpuVendor },
41    /// Google TPU accelerator
42    Tpu { version: TpuVersion, cores: u32 },
43    /// Apple Silicon unified memory
44    AppleSilicon { chip: AppleChip, neural_engine_cores: u32, gpu_cores: u32, memory_gb: u32 },
45    /// Edge/embedded devices
46    Edge { name: String, power_budget_watts: f32 },
47}
48
49/// CPU architecture variants
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
51pub enum CpuArchitecture {
52    X86_64,
53    Aarch64,
54    Riscv64,
55    Wasm32,
56}
57
58/// GPU vendor identification
59#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
60pub enum GpuVendor {
61    Nvidia,
62    Amd,
63    Intel,
64    Apple,
65}
66
67/// TPU version variants
68#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
69pub enum TpuVersion {
70    V2,
71    V3,
72    V4,
73    V5e,
74    V5p,
75}
76
77/// Apple Silicon chip variants
78#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
79pub enum AppleChip {
80    M1,
81    M1Pro,
82    M1Max,
83    M1Ultra,
84    M2,
85    M2Pro,
86    M2Max,
87    M2Ultra,
88    M3,
89    M3Pro,
90    M3Max,
91    M4,
92    M4Pro,
93    M4Max,
94}
95
96impl ComputeDevice {
97    /// Calculate theoretical FLOPS for the device
98    pub fn theoretical_flops(&self) -> f64 {
99        match self {
100            ComputeDevice::Cpu { cores, threads_per_core, architecture } => {
101                let base_flops = match architecture {
102                    CpuArchitecture::X86_64 => 32.0,  // AVX2: 8 FP32 * 4 ops
103                    CpuArchitecture::Aarch64 => 16.0, // NEON: 4 FP32 * 4 ops
104                    CpuArchitecture::Riscv64 => 8.0,
105                    CpuArchitecture::Wasm32 => 4.0,
106                };
107                (*cores as f64) * (*threads_per_core as f64) * base_flops * 1e9
108            }
109            ComputeDevice::Gpu { memory_gb, vendor, .. } => {
110                // Rough estimate based on memory bandwidth
111                let bandwidth_factor = match vendor {
112                    GpuVendor::Nvidia => 15.0,
113                    GpuVendor::Amd => 12.0,
114                    GpuVendor::Intel => 8.0,
115                    GpuVendor::Apple => 10.0,
116                };
117                (*memory_gb as f64) * bandwidth_factor * 1e12
118            }
119            ComputeDevice::Tpu { version, cores } => {
120                let flops_per_core = match version {
121                    TpuVersion::V2 => 45e12,
122                    TpuVersion::V3 => 90e12,
123                    TpuVersion::V4 => 275e12,
124                    TpuVersion::V5e => 197e12,
125                    TpuVersion::V5p => 459e12,
126                };
127                (*cores as f64) * flops_per_core
128            }
129            ComputeDevice::AppleSilicon { chip, gpu_cores, .. } => {
130                let flops_per_gpu_core = match chip {
131                    AppleChip::M1 | AppleChip::M1Pro | AppleChip::M1Max | AppleChip::M1Ultra => {
132                        128e9
133                    }
134                    AppleChip::M2 | AppleChip::M2Pro | AppleChip::M2Max | AppleChip::M2Ultra => {
135                        150e9
136                    }
137                    AppleChip::M3 | AppleChip::M3Pro | AppleChip::M3Max => 180e9,
138                    AppleChip::M4 | AppleChip::M4Pro | AppleChip::M4Max => 200e9,
139                };
140                (*gpu_cores as f64) * flops_per_gpu_core
141            }
142            ComputeDevice::Edge { power_budget_watts, .. } => {
143                // Assume ~10 GFLOPS per watt for edge devices
144                (*power_budget_watts as f64) * 10e9
145            }
146        }
147    }
148
149    /// Estimate power consumption in watts
150    pub fn estimated_power_watts(&self) -> f32 {
151        match self {
152            ComputeDevice::Cpu { cores, .. } => (*cores as f32) * 15.0,
153            ComputeDevice::Gpu { memory_gb, vendor, .. } => {
154                let base = match vendor {
155                    GpuVendor::Nvidia => 30.0,
156                    GpuVendor::Amd => 35.0,
157                    GpuVendor::Intel => 25.0,
158                    GpuVendor::Apple => 20.0,
159                };
160                *memory_gb * base
161            }
162            ComputeDevice::Tpu { version, cores } => {
163                let per_core = match version {
164                    TpuVersion::V2 => 40.0,
165                    TpuVersion::V3 => 50.0,
166                    TpuVersion::V4 => 60.0,
167                    TpuVersion::V5e => 45.0,
168                    TpuVersion::V5p => 70.0,
169                };
170                (*cores as f32) * per_core
171            }
172            ComputeDevice::AppleSilicon { chip, .. } => match chip {
173                AppleChip::M1 => 20.0,
174                AppleChip::M1Pro => 30.0,
175                AppleChip::M1Max => 40.0,
176                AppleChip::M1Ultra => 60.0,
177                AppleChip::M2 => 22.0,
178                AppleChip::M2Pro => 32.0,
179                AppleChip::M2Max => 45.0,
180                AppleChip::M2Ultra => 65.0,
181                AppleChip::M3 => 24.0,
182                AppleChip::M3Pro => 35.0,
183                AppleChip::M3Max => 50.0,
184                AppleChip::M4 => 25.0,
185                AppleChip::M4Pro => 38.0,
186                AppleChip::M4Max => 55.0,
187            },
188            ComputeDevice::Edge { power_budget_watts, .. } => *power_budget_watts,
189        }
190    }
191}
192
193/// Model training paradigm classification
194#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
195pub enum ModelParadigm {
196    /// Traditional ML (sklearn-style)
197    TraditionalML,
198    /// Deep Learning from scratch
199    DeepLearning,
200    /// Fine-tuning pretrained models
201    FineTuning,
202    /// Knowledge distillation
203    Distillation,
204    /// Mixture of Experts
205    MoE,
206    /// Reinforcement Learning
207    ReinforcementLearning,
208    /// Federated Learning
209    FederatedLearning,
210    /// Neural Architecture Search
211    Nas,
212    /// Continual/Lifelong Learning
213    ContinualLearning,
214    /// Meta-Learning
215    MetaLearning,
216}
217
218impl ModelParadigm {
219    /// Get typical compute intensity for this paradigm
220    pub fn compute_intensity(&self) -> ComputeIntensity {
221        match self {
222            ModelParadigm::TraditionalML => ComputeIntensity::Low,
223            ModelParadigm::DeepLearning => ComputeIntensity::High,
224            ModelParadigm::FineTuning => ComputeIntensity::Medium,
225            ModelParadigm::Distillation => ComputeIntensity::Medium,
226            ModelParadigm::MoE => ComputeIntensity::VeryHigh,
227            ModelParadigm::ReinforcementLearning => ComputeIntensity::High,
228            ModelParadigm::FederatedLearning => ComputeIntensity::Medium,
229            ModelParadigm::Nas => ComputeIntensity::VeryHigh,
230            ModelParadigm::ContinualLearning => ComputeIntensity::Medium,
231            ModelParadigm::MetaLearning => ComputeIntensity::High,
232        }
233    }
234
235    /// Check if paradigm typically benefits from GPU
236    pub fn benefits_from_gpu(&self) -> bool {
237        matches!(
238            self,
239            ModelParadigm::DeepLearning
240                | ModelParadigm::FineTuning
241                | ModelParadigm::MoE
242                | ModelParadigm::ReinforcementLearning
243                | ModelParadigm::Nas
244        )
245    }
246}
247
248/// Compute intensity levels
249#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
250pub enum ComputeIntensity {
251    Low,
252    Medium,
253    High,
254    VeryHigh,
255}
256
257/// Platform efficiency classification
258#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
259pub enum PlatformEfficiency {
260    /// Server-grade hardware (datacenter)
261    Server,
262    /// Desktop workstation
263    Workstation,
264    /// Laptop
265    Laptop,
266    /// Edge device
267    Edge,
268    /// Mobile device
269    Mobile,
270    /// Embedded system
271    Embedded,
272}
273
274impl PlatformEfficiency {
275    /// Get typical power budget for platform
276    pub fn typical_power_budget_watts(&self) -> f32 {
277        match self {
278            PlatformEfficiency::Server => 500.0,
279            PlatformEfficiency::Workstation => 350.0,
280            PlatformEfficiency::Laptop => 65.0,
281            PlatformEfficiency::Edge => 15.0,
282            PlatformEfficiency::Mobile => 5.0,
283            PlatformEfficiency::Embedded => 1.0,
284        }
285    }
286}
287
288#[cfg(test)]
289mod tests {
290    use super::*;
291
292    // =========================================================================
293    // ExperimentError Tests
294    // =========================================================================
295
296    #[test]
297    fn test_experiment_error_display() {
298        let err = ExperimentError::InvalidComputeDevice("test".into());
299        assert!(err.to_string().contains("Invalid compute device"));
300
301        let err = ExperimentError::MetricsCollectionFailed("test".into());
302        assert!(err.to_string().contains("Metrics collection failed"));
303
304        let err = ExperimentError::ParetoFrontierFailed("test".into());
305        assert!(err.to_string().contains("Pareto frontier"));
306
307        let err = ExperimentError::InvalidOrcid("test".into());
308        assert!(err.to_string().contains("Invalid ORCID"));
309
310        let err = ExperimentError::CitationGenerationFailed("test".into());
311        assert!(err.to_string().contains("Citation generation"));
312
313        let err = ExperimentError::SovereignValidationFailed("test".into());
314        assert!(err.to_string().contains("Sovereign distribution"));
315
316        let err = ExperimentError::StorageError("test".into());
317        assert!(err.to_string().contains("storage error"));
318    }
319
320    #[test]
321    fn test_experiment_error_equality() {
322        let err1 = ExperimentError::InvalidComputeDevice("a".into());
323        let err2 = ExperimentError::InvalidComputeDevice("a".into());
324        assert_eq!(err1, err2);
325    }
326
327    // =========================================================================
328    // ComputeDevice Tests
329    // =========================================================================
330
331    #[test]
332    fn test_cpu_theoretical_flops_x86() {
333        let cpu = ComputeDevice::Cpu {
334            cores: 8,
335            threads_per_core: 2,
336            architecture: CpuArchitecture::X86_64,
337        };
338        let flops = cpu.theoretical_flops();
339        assert!(flops > 0.0);
340        assert_eq!(flops, 8.0 * 2.0 * 32.0 * 1e9);
341    }
342
343    #[test]
344    fn test_cpu_theoretical_flops_aarch64() {
345        let cpu = ComputeDevice::Cpu {
346            cores: 8,
347            threads_per_core: 1,
348            architecture: CpuArchitecture::Aarch64,
349        };
350        let flops = cpu.theoretical_flops();
351        assert_eq!(flops, 8.0 * 1.0 * 16.0 * 1e9);
352    }
353
354    #[test]
355    fn test_cpu_theoretical_flops_riscv() {
356        let cpu = ComputeDevice::Cpu {
357            cores: 4,
358            threads_per_core: 1,
359            architecture: CpuArchitecture::Riscv64,
360        };
361        let flops = cpu.theoretical_flops();
362        assert_eq!(flops, 4.0 * 1.0 * 8.0 * 1e9);
363    }
364
365    #[test]
366    fn test_cpu_theoretical_flops_wasm() {
367        let cpu = ComputeDevice::Cpu {
368            cores: 1,
369            threads_per_core: 1,
370            architecture: CpuArchitecture::Wasm32,
371        };
372        let flops = cpu.theoretical_flops();
373        assert_eq!(flops, 1.0 * 1.0 * 4.0 * 1e9);
374    }
375
376    #[test]
377    fn test_gpu_theoretical_flops_nvidia() {
378        let gpu = ComputeDevice::Gpu {
379            name: "RTX 4090".into(),
380            memory_gb: 24.0,
381            compute_capability: Some("8.9".into()),
382            vendor: GpuVendor::Nvidia,
383        };
384        let flops = gpu.theoretical_flops();
385        assert_eq!(flops, 24.0 * 15.0 * 1e12);
386    }
387
388    #[test]
389    fn test_gpu_theoretical_flops_amd() {
390        let gpu = ComputeDevice::Gpu {
391            name: "RX 7900".into(),
392            memory_gb: 20.0,
393            compute_capability: None,
394            vendor: GpuVendor::Amd,
395        };
396        let flops = gpu.theoretical_flops();
397        assert_eq!(flops, 20.0 * 12.0 * 1e12);
398    }
399
400    #[test]
401    fn test_gpu_theoretical_flops_intel() {
402        let gpu = ComputeDevice::Gpu {
403            name: "Arc A770".into(),
404            memory_gb: 16.0,
405            compute_capability: None,
406            vendor: GpuVendor::Intel,
407        };
408        let flops = gpu.theoretical_flops();
409        assert_eq!(flops, 16.0 * 8.0 * 1e12);
410    }
411
412    #[test]
413    fn test_gpu_theoretical_flops_apple() {
414        let gpu = ComputeDevice::Gpu {
415            name: "Apple GPU".into(),
416            memory_gb: 32.0,
417            compute_capability: None,
418            vendor: GpuVendor::Apple,
419        };
420        let flops = gpu.theoretical_flops();
421        assert_eq!(flops, 32.0 * 10.0 * 1e12);
422    }
423
424    #[test]
425    fn test_tpu_theoretical_flops() {
426        let tpu_v2 = ComputeDevice::Tpu { version: TpuVersion::V2, cores: 1 };
427        assert_eq!(tpu_v2.theoretical_flops(), 45e12);
428
429        let tpu_v3 = ComputeDevice::Tpu { version: TpuVersion::V3, cores: 2 };
430        assert_eq!(tpu_v3.theoretical_flops(), 2.0 * 90e12);
431
432        let tpu_v4 = ComputeDevice::Tpu { version: TpuVersion::V4, cores: 1 };
433        assert_eq!(tpu_v4.theoretical_flops(), 275e12);
434
435        let tpu_v5e = ComputeDevice::Tpu { version: TpuVersion::V5e, cores: 1 };
436        assert_eq!(tpu_v5e.theoretical_flops(), 197e12);
437
438        let tpu_v5p = ComputeDevice::Tpu { version: TpuVersion::V5p, cores: 1 };
439        assert_eq!(tpu_v5p.theoretical_flops(), 459e12);
440    }
441
442    #[test]
443    fn test_apple_silicon_theoretical_flops() {
444        let m1 = ComputeDevice::AppleSilicon {
445            chip: AppleChip::M1,
446            neural_engine_cores: 16,
447            gpu_cores: 8,
448            memory_gb: 16,
449        };
450        assert_eq!(m1.theoretical_flops(), 8.0 * 128e9);
451
452        let m2_max = ComputeDevice::AppleSilicon {
453            chip: AppleChip::M2Max,
454            neural_engine_cores: 16,
455            gpu_cores: 38,
456            memory_gb: 96,
457        };
458        assert_eq!(m2_max.theoretical_flops(), 38.0 * 150e9);
459
460        let m3_pro = ComputeDevice::AppleSilicon {
461            chip: AppleChip::M3Pro,
462            neural_engine_cores: 16,
463            gpu_cores: 18,
464            memory_gb: 36,
465        };
466        assert_eq!(m3_pro.theoretical_flops(), 18.0 * 180e9);
467
468        let m4_max = ComputeDevice::AppleSilicon {
469            chip: AppleChip::M4Max,
470            neural_engine_cores: 16,
471            gpu_cores: 40,
472            memory_gb: 128,
473        };
474        assert_eq!(m4_max.theoretical_flops(), 40.0 * 200e9);
475    }
476
477    #[test]
478    fn test_edge_device_theoretical_flops() {
479        let edge = ComputeDevice::Edge { name: "Jetson Nano".into(), power_budget_watts: 10.0 };
480        assert_eq!(edge.theoretical_flops(), 10.0 * 10e9);
481    }
482
483    #[test]
484    fn test_cpu_estimated_power() {
485        let cpu = ComputeDevice::Cpu {
486            cores: 8,
487            threads_per_core: 2,
488            architecture: CpuArchitecture::X86_64,
489        };
490        assert_eq!(cpu.estimated_power_watts(), 8.0 * 15.0);
491    }
492
493    #[test]
494    fn test_gpu_estimated_power() {
495        let nvidia = ComputeDevice::Gpu {
496            name: "RTX".into(),
497            memory_gb: 24.0,
498            compute_capability: None,
499            vendor: GpuVendor::Nvidia,
500        };
501        assert_eq!(nvidia.estimated_power_watts(), 24.0 * 30.0);
502
503        let amd = ComputeDevice::Gpu {
504            name: "RX".into(),
505            memory_gb: 16.0,
506            compute_capability: None,
507            vendor: GpuVendor::Amd,
508        };
509        assert_eq!(amd.estimated_power_watts(), 16.0 * 35.0);
510    }
511
512    #[test]
513    fn test_tpu_estimated_power() {
514        let tpu = ComputeDevice::Tpu { version: TpuVersion::V4, cores: 4 };
515        assert_eq!(tpu.estimated_power_watts(), 4.0 * 60.0);
516    }
517
518    #[test]
519    fn test_apple_silicon_estimated_power() {
520        let m1 = ComputeDevice::AppleSilicon {
521            chip: AppleChip::M1,
522            neural_engine_cores: 16,
523            gpu_cores: 8,
524            memory_gb: 16,
525        };
526        assert_eq!(m1.estimated_power_watts(), 20.0);
527
528        let m2_ultra = ComputeDevice::AppleSilicon {
529            chip: AppleChip::M2Ultra,
530            neural_engine_cores: 32,
531            gpu_cores: 76,
532            memory_gb: 192,
533        };
534        assert_eq!(m2_ultra.estimated_power_watts(), 65.0);
535
536        let m4 = ComputeDevice::AppleSilicon {
537            chip: AppleChip::M4,
538            neural_engine_cores: 16,
539            gpu_cores: 10,
540            memory_gb: 24,
541        };
542        assert_eq!(m4.estimated_power_watts(), 25.0);
543    }
544
545    #[test]
546    fn test_edge_device_estimated_power() {
547        let edge = ComputeDevice::Edge { name: "Pi".into(), power_budget_watts: 5.0 };
548        assert_eq!(edge.estimated_power_watts(), 5.0);
549    }
550
551    // =========================================================================
552    // ModelParadigm Tests
553    // =========================================================================
554
555    #[test]
556    fn test_model_paradigm_compute_intensity() {
557        assert_eq!(ModelParadigm::TraditionalML.compute_intensity(), ComputeIntensity::Low);
558        assert_eq!(ModelParadigm::DeepLearning.compute_intensity(), ComputeIntensity::High);
559        assert_eq!(ModelParadigm::FineTuning.compute_intensity(), ComputeIntensity::Medium);
560        assert_eq!(ModelParadigm::Distillation.compute_intensity(), ComputeIntensity::Medium);
561        assert_eq!(ModelParadigm::MoE.compute_intensity(), ComputeIntensity::VeryHigh);
562        assert_eq!(
563            ModelParadigm::ReinforcementLearning.compute_intensity(),
564            ComputeIntensity::High
565        );
566        assert_eq!(ModelParadigm::FederatedLearning.compute_intensity(), ComputeIntensity::Medium);
567        assert_eq!(ModelParadigm::Nas.compute_intensity(), ComputeIntensity::VeryHigh);
568        assert_eq!(ModelParadigm::ContinualLearning.compute_intensity(), ComputeIntensity::Medium);
569        assert_eq!(ModelParadigm::MetaLearning.compute_intensity(), ComputeIntensity::High);
570    }
571
572    #[test]
573    fn test_model_paradigm_benefits_from_gpu() {
574        assert!(ModelParadigm::DeepLearning.benefits_from_gpu());
575        assert!(ModelParadigm::FineTuning.benefits_from_gpu());
576        assert!(ModelParadigm::MoE.benefits_from_gpu());
577        assert!(ModelParadigm::ReinforcementLearning.benefits_from_gpu());
578        assert!(ModelParadigm::Nas.benefits_from_gpu());
579
580        assert!(!ModelParadigm::TraditionalML.benefits_from_gpu());
581        assert!(!ModelParadigm::Distillation.benefits_from_gpu());
582        assert!(!ModelParadigm::FederatedLearning.benefits_from_gpu());
583        assert!(!ModelParadigm::ContinualLearning.benefits_from_gpu());
584        assert!(!ModelParadigm::MetaLearning.benefits_from_gpu());
585    }
586
587    // =========================================================================
588    // PlatformEfficiency Tests
589    // =========================================================================
590
591    #[test]
592    fn test_platform_efficiency_power_budget() {
593        assert_eq!(PlatformEfficiency::Server.typical_power_budget_watts(), 500.0);
594        assert_eq!(PlatformEfficiency::Workstation.typical_power_budget_watts(), 350.0);
595        assert_eq!(PlatformEfficiency::Laptop.typical_power_budget_watts(), 65.0);
596        assert_eq!(PlatformEfficiency::Edge.typical_power_budget_watts(), 15.0);
597        assert_eq!(PlatformEfficiency::Mobile.typical_power_budget_watts(), 5.0);
598        assert_eq!(PlatformEfficiency::Embedded.typical_power_budget_watts(), 1.0);
599    }
600
601    // =========================================================================
602    // Serialization Tests
603    // =========================================================================
604
605    #[test]
606    fn test_compute_device_serialization() {
607        let cpu = ComputeDevice::Cpu {
608            cores: 8,
609            threads_per_core: 2,
610            architecture: CpuArchitecture::X86_64,
611        };
612        let json = serde_json::to_string(&cpu).expect("json serialize failed");
613        let deserialized: ComputeDevice =
614            serde_json::from_str(&json).expect("json deserialize failed");
615        assert_eq!(cpu, deserialized);
616    }
617
618    #[test]
619    fn test_model_paradigm_serialization() {
620        let paradigm = ModelParadigm::DeepLearning;
621        let json = serde_json::to_string(&paradigm).expect("json serialize failed");
622        let deserialized: ModelParadigm =
623            serde_json::from_str(&json).expect("json deserialize failed");
624        assert_eq!(paradigm, deserialized);
625    }
626}