1use std::collections::HashMap;
8use std::fmt;
9use std::path::{Path, PathBuf};
10
11use chrono::{DateTime, Utc};
12use num_traits::Float;
13use serde::{Deserialize, Serialize};
14
15use crate::{
16 config_error,
17 error::{ErrorBuilder, NeuroDivergentError, NeuroDivergentResult},
18 traits::{ConfigBuilder, ConfigParameter, ExogenousConfig, ModelConfig},
19};
20
21#[derive(Debug, Clone)]
23pub struct GenericModelConfig<T: Float + Send + Sync + 'static> {
24 pub model_type: String,
26 pub horizon: usize,
28 pub input_size: usize,
30 pub output_size: usize,
32 pub exogenous_config: ExogenousConfig,
34 pub parameters: HashMap<String, ConfigParameter<T>>,
36 pub metadata: ConfigMetadata,
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct ConfigMetadata {
43 pub name: Option<String>,
45 pub description: Option<String>,
47 pub version: String,
49 pub created_at: DateTime<Utc>,
51 pub modified_at: DateTime<Utc>,
53 pub author: Option<String>,
55 pub tags: Vec<String>,
57 pub custom_fields: HashMap<String, String>,
59}
60
61pub struct ModelConfigBuilder<T: Float + Send + Sync + 'static> {
63 model_type: Option<String>,
64 horizon: Option<usize>,
65 input_size: Option<usize>,
66 output_size: Option<usize>,
67 exogenous_config: ExogenousConfig,
68 parameters: HashMap<String, ConfigParameter<T>>,
69 metadata: ConfigMetadata,
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct SystemConfig {
75 pub logging: LoggingConfig,
77 pub performance: PerformanceConfig,
79 pub memory: MemoryConfig,
81 pub parallel: ParallelConfig,
83 pub io: IoConfig,
85 pub debug: DebugConfig,
87 pub features: FeatureFlags,
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct LoggingConfig {
94 pub level: String,
96 pub format: LogFormat,
98 pub file_path: Option<PathBuf>,
100 pub max_file_size_mb: Option<usize>,
102 pub max_files: Option<usize>,
104 pub structured: bool,
106 pub timestamps: bool,
108}
109
110#[derive(Debug, Clone, Serialize, Deserialize)]
112pub enum LogFormat {
113 Text,
115 Json,
117 Compact,
119 Pretty,
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize)]
125pub struct PerformanceConfig {
126 pub enable_simd: bool,
128 pub enable_gpu: bool,
130 pub gpu_device_id: Option<usize>,
132 pub enable_amp: bool,
134 pub cpu_optimization: CpuOptimization,
136 pub enable_profiling: bool,
138 pub profiling_output_dir: Option<PathBuf>,
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize)]
144pub enum CpuOptimization {
145 None,
147 Native,
149 Features(Vec<String>),
151 Conservative,
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize)]
157pub struct MemoryConfig {
158 pub pool_size_mb: Option<usize>,
160 pub enable_pool: bool,
162 pub allocation_strategy: AllocationStrategy,
164 pub enable_monitoring: bool,
166 pub warning_threshold: Option<f64>,
168 pub gc_hints: bool,
170}
171
172#[derive(Debug, Clone, Serialize, Deserialize)]
174pub enum AllocationStrategy {
175 System,
177 Pool,
179 Arena,
181 Custom(String),
183}
184
185#[derive(Debug, Clone, Serialize, Deserialize)]
187pub struct ParallelConfig {
188 pub num_threads: Option<usize>,
190 pub thread_pool: ThreadPoolConfig,
192 pub enable_parallel_training: bool,
194 pub enable_parallel_prediction: bool,
196 pub enable_data_parallelism: bool,
198 pub enable_model_parallelism: bool,
200}
201
202#[derive(Debug, Clone, Serialize, Deserialize)]
204pub struct ThreadPoolConfig {
205 pub pool_type: ThreadPoolType,
207 pub stack_size_kb: Option<usize>,
209 pub thread_name_prefix: Option<String>,
211 pub thread_priority: Option<ThreadPriority>,
213}
214
215#[derive(Debug, Clone, Serialize, Deserialize)]
217pub enum ThreadPoolType {
218 Global,
220 Custom,
222 WorkStealing,
224}
225
226#[derive(Debug, Clone, Serialize, Deserialize)]
228pub enum ThreadPriority {
229 Low,
231 Normal,
233 High,
235}
236
237#[derive(Debug, Clone, Serialize, Deserialize)]
239pub struct IoConfig {
240 pub default_format: DataFormat,
242 pub enable_compression: bool,
244 pub compression_level: Option<u8>,
246 pub buffer_size_kb: usize,
248 pub enable_async_io: bool,
250 pub network_timeout_secs: Option<u64>,
252 pub retry_config: RetryConfig,
254}
255
256#[derive(Debug, Clone, Serialize, Deserialize)]
258pub enum DataFormat {
259 Json,
261 Bson,
263 MessagePack,
265 Binary,
267 Parquet,
269 Csv,
271}
272
273#[derive(Debug, Clone, Serialize, Deserialize)]
275pub struct RetryConfig {
276 pub max_retries: usize,
278 pub initial_delay_ms: u64,
280 pub max_delay_ms: u64,
282 pub backoff_multiplier: f64,
284 pub enable_jitter: bool,
286}
287
288#[derive(Debug, Clone, Serialize, Deserialize)]
290pub struct DebugConfig {
291 pub enabled: bool,
293 pub output_dir: Option<PathBuf>,
295 pub save_intermediates: bool,
297 pub detailed_timing: bool,
299 pub memory_tracking: bool,
301 pub network_visualization: bool,
303 pub verbosity: DebugVerbosity,
305}
306
307#[derive(Debug, Clone, Serialize, Deserialize)]
309pub enum DebugVerbosity {
310 Minimal,
312 Normal,
314 Verbose,
316 VeryVerbose,
318}
319
320#[derive(Debug, Clone, Serialize, Deserialize)]
322pub struct FeatureFlags {
323 pub experimental_gpu_kernels: bool,
325 pub experimental_optimizations: bool,
327 pub experimental_models: bool,
329 pub experimental_data_formats: bool,
331 pub auto_hyperparameter_tuning: bool,
333 pub auto_model_selection: bool,
335 pub distributed_training: bool,
337}
338
339#[derive(Debug, Clone)]
341pub struct ValidationResult {
342 pub is_valid: bool,
344 pub errors: Vec<ValidationError>,
346 pub warnings: Vec<ValidationWarning>,
348}
349
350#[derive(Debug, Clone)]
352pub struct ValidationError {
353 pub field: String,
355 pub message: String,
357 pub code: Option<String>,
359}
360
361#[derive(Debug, Clone)]
363pub struct ValidationWarning {
364 pub field: String,
366 pub message: String,
368 pub code: Option<String>,
370}
371
372pub struct ConfigManager {
374 search_paths: Vec<PathBuf>,
376 current_config: Option<SystemConfig>,
378 file_format: DataFormat,
380}
381
382impl<T: Float + Send + Sync + 'static> GenericModelConfig<T> {
383 pub fn new(model_type: impl Into<String>) -> Self {
385 Self {
386 model_type: model_type.into(),
387 horizon: 1,
388 input_size: 1,
389 output_size: 1,
390 exogenous_config: ExogenousConfig::default(),
391 parameters: HashMap::new(),
392 metadata: ConfigMetadata::new(),
393 }
394 }
395
396 pub fn with_parameter(mut self, key: impl Into<String>, value: ConfigParameter<T>) -> Self {
398 self.parameters.insert(key.into(), value);
399 self
400 }
401
402 pub fn with_parameters(mut self, params: HashMap<String, ConfigParameter<T>>) -> Self {
404 self.parameters.extend(params);
405 self
406 }
407
408 pub fn with_metadata(mut self, metadata: ConfigMetadata) -> Self {
410 self.metadata = metadata;
411 self
412 }
413
414 pub fn get_parameter(&self, key: &str) -> Option<&ConfigParameter<T>> {
416 self.parameters.get(key)
417 }
418
419 pub fn get_float_parameter(&self, key: &str) -> Option<T> {
421 match self.parameters.get(key) {
422 Some(ConfigParameter::Float(val)) => Some(*val),
423 _ => None,
424 }
425 }
426
427 pub fn get_integer_parameter(&self, key: &str) -> Option<i64> {
429 match self.parameters.get(key) {
430 Some(ConfigParameter::Integer(val)) => Some(*val),
431 _ => None,
432 }
433 }
434
435 pub fn get_string_parameter(&self, key: &str) -> Option<&String> {
437 match self.parameters.get(key) {
438 Some(ConfigParameter::String(val)) => Some(val),
439 _ => None,
440 }
441 }
442
443 pub fn get_boolean_parameter(&self, key: &str) -> Option<bool> {
445 match self.parameters.get(key) {
446 Some(ConfigParameter::Boolean(val)) => Some(*val),
447 _ => None,
448 }
449 }
450
451 #[allow(dead_code)]
454 pub fn save<P: AsRef<Path>>(&self, _path: P) -> NeuroDivergentResult<()> {
455 todo!("Configuration serialization needs to be implemented without generic type constraints")
456 }
457
458 #[allow(dead_code)]
461 pub fn load<P: AsRef<Path>>(_path: P) -> NeuroDivergentResult<Self> {
462 todo!("Configuration deserialization needs to be implemented without generic type constraints")
463 }
464
465 pub fn merge(mut self, other: Self) -> Self {
467 self.parameters.extend(other.parameters);
469
470 if other.horizon != 1 {
472 self.horizon = other.horizon;
473 }
474 if other.input_size != 1 {
475 self.input_size = other.input_size;
476 }
477 if other.output_size != 1 {
478 self.output_size = other.output_size;
479 }
480
481 if !other.exogenous_config.static_features.is_empty() {
483 self.exogenous_config.static_features = other.exogenous_config.static_features;
484 }
485 if !other.exogenous_config.historical_features.is_empty() {
486 self.exogenous_config.historical_features = other.exogenous_config.historical_features;
487 }
488 if !other.exogenous_config.future_features.is_empty() {
489 self.exogenous_config.future_features = other.exogenous_config.future_features;
490 }
491
492 self.metadata.modified_at = Utc::now();
493 self
494 }
495}
496
497impl<T: Float + Send + Sync + 'static> ModelConfig<T> for GenericModelConfig<T> {
498 fn validate(&self) -> NeuroDivergentResult<()> {
499 if self.horizon == 0 {
500 return Err(config_error!("Horizon must be greater than 0"));
501 }
502
503 if self.input_size == 0 {
504 return Err(config_error!("Input size must be greater than 0"));
505 }
506
507 if self.output_size == 0 {
508 return Err(config_error!("Output size must be greater than 0"));
509 }
510
511 if self.model_type.is_empty() {
512 return Err(config_error!("Model type cannot be empty"));
513 }
514
515 Ok(())
516 }
517
518 fn horizon(&self) -> usize {
519 self.horizon
520 }
521
522 fn input_size(&self) -> usize {
523 self.input_size
524 }
525
526 fn output_size(&self) -> usize {
527 self.output_size
528 }
529
530 fn exogenous_config(&self) -> &ExogenousConfig {
531 &self.exogenous_config
532 }
533
534 fn model_type(&self) -> &str {
535 &self.model_type
536 }
537
538 fn to_parameters(&self) -> HashMap<String, ConfigParameter<T>> {
539 let mut params = self.parameters.clone();
540
541 params.insert("horizon".to_string(), ConfigParameter::Integer(self.horizon as i64));
543 params.insert("input_size".to_string(), ConfigParameter::Integer(self.input_size as i64));
544 params.insert("output_size".to_string(), ConfigParameter::Integer(self.output_size as i64));
545 params.insert("model_type".to_string(), ConfigParameter::String(self.model_type.clone()));
546
547 params
548 }
549
550 fn from_parameters(params: HashMap<String, ConfigParameter<T>>) -> NeuroDivergentResult<Self> {
551 let model_type = params.get("model_type")
552 .and_then(|p| match p {
553 ConfigParameter::String(s) => Some(s.clone()),
554 _ => None,
555 })
556 .ok_or_else(|| config_error!("Missing required parameter: model_type"))?;
557
558 let horizon = params.get("horizon")
559 .and_then(|p| match p {
560 ConfigParameter::Integer(i) => Some(*i as usize),
561 _ => None,
562 })
563 .unwrap_or(1);
564
565 let input_size = params.get("input_size")
566 .and_then(|p| match p {
567 ConfigParameter::Integer(i) => Some(*i as usize),
568 _ => None,
569 })
570 .unwrap_or(1);
571
572 let output_size = params.get("output_size")
573 .and_then(|p| match p {
574 ConfigParameter::Integer(i) => Some(*i as usize),
575 _ => None,
576 })
577 .unwrap_or(1);
578
579 let mut config = Self::new(model_type);
580 config.horizon = horizon;
581 config.input_size = input_size;
582 config.output_size = output_size;
583
584 let mut remaining_params = params;
586 remaining_params.remove("model_type");
587 remaining_params.remove("horizon");
588 remaining_params.remove("input_size");
589 remaining_params.remove("output_size");
590
591 config.parameters = remaining_params;
592 Ok(config)
593 }
594
595 fn builder() -> impl ConfigBuilder<Self, T> {
596 ModelConfigBuilder::new()
597 }
598}
599
600impl<T: Float + Send + Sync + 'static> ModelConfigBuilder<T> {
601 pub fn new() -> Self {
603 Self {
604 model_type: None,
605 horizon: None,
606 input_size: None,
607 output_size: None,
608 exogenous_config: ExogenousConfig::default(),
609 parameters: HashMap::new(),
610 metadata: ConfigMetadata::new(),
611 }
612 }
613
614 pub fn with_model_type(mut self, model_type: impl Into<String>) -> Self {
616 self.model_type = Some(model_type.into());
617 self
618 }
619
620 pub fn with_parameter(mut self, key: impl Into<String>, value: ConfigParameter<T>) -> Self {
622 self.parameters.insert(key.into(), value);
623 self
624 }
625
626 pub fn with_metadata(mut self, metadata: ConfigMetadata) -> Self {
628 self.metadata = metadata;
629 self
630 }
631}
632
633impl<T: Float + Send + Sync + 'static> ConfigBuilder<GenericModelConfig<T>, T> for ModelConfigBuilder<T> {
634 fn build(self) -> NeuroDivergentResult<GenericModelConfig<T>> {
635 let model_type = self.model_type.ok_or_else(|| {
636 config_error!("Model type is required")
637 })?;
638
639 let horizon = self.horizon.unwrap_or(1);
640 let input_size = self.input_size.unwrap_or(1);
641 let output_size = self.output_size.unwrap_or(horizon);
642
643 let mut config = GenericModelConfig::new(model_type);
644 config.horizon = horizon;
645 config.input_size = input_size;
646 config.output_size = output_size;
647 config.exogenous_config = self.exogenous_config;
648 config.parameters = self.parameters;
649 config.metadata = self.metadata;
650
651 config.validate()?;
652 Ok(config)
653 }
654
655 fn with_horizon(mut self, horizon: usize) -> Self {
656 self.horizon = Some(horizon);
657 self
658 }
659
660 fn with_input_size(mut self, input_size: usize) -> Self {
661 self.input_size = Some(input_size);
662 self
663 }
664
665 fn with_exogenous_config(mut self, config: ExogenousConfig) -> Self {
666 self.exogenous_config = config;
667 self
668 }
669}
670
671impl ConfigMetadata {
672 pub fn new() -> Self {
674 let now = Utc::now();
675 Self {
676 name: None,
677 description: None,
678 version: "1.0.0".to_string(),
679 created_at: now,
680 modified_at: now,
681 author: None,
682 tags: Vec::new(),
683 custom_fields: HashMap::new(),
684 }
685 }
686
687 pub fn with_name(mut self, name: impl Into<String>) -> Self {
689 self.name = Some(name.into());
690 self
691 }
692
693 pub fn with_description(mut self, description: impl Into<String>) -> Self {
695 self.description = Some(description.into());
696 self
697 }
698
699 pub fn with_version(mut self, version: impl Into<String>) -> Self {
701 self.version = version.into();
702 self
703 }
704
705 pub fn with_author(mut self, author: impl Into<String>) -> Self {
707 self.author = Some(author.into());
708 self
709 }
710
711 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
713 self.tags = tags;
714 self
715 }
716
717 pub fn with_custom_field(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
719 self.custom_fields.insert(key.into(), value.into());
720 self
721 }
722}
723
724impl ConfigManager {
725 pub fn new() -> Self {
727 let mut search_paths = Vec::new();
728
729 if let Ok(home) = std::env::var("HOME") {
731 let home_dir = PathBuf::from(home);
732 search_paths.push(home_dir.join(".config").join("neuro-divergent"));
733 }
734
735 search_paths.push(PathBuf::from("/etc/neuro-divergent"));
736 search_paths.push(PathBuf::from("./config"));
737
738 Self {
739 search_paths,
740 current_config: None,
741 file_format: DataFormat::Json,
742 }
743 }
744
745 pub fn add_search_path<P: AsRef<Path>>(&mut self, path: P) {
747 self.search_paths.push(path.as_ref().to_path_buf());
748 }
749
750 pub fn load_config(&mut self) -> NeuroDivergentResult<&SystemConfig> {
752 for search_path in &self.search_paths {
753 let config_file = search_path.join("config.json");
754 if config_file.exists() {
755 let config = self.load_from_file(&config_file)?;
756 self.current_config = Some(config);
757 return Ok(self.current_config.as_ref().unwrap());
758 }
759 }
760
761 self.current_config = Some(SystemConfig::default());
763 Ok(self.current_config.as_ref().unwrap())
764 }
765
766 pub fn load_from_file<P: AsRef<Path>>(&self, path: P) -> NeuroDivergentResult<SystemConfig> {
768 let content = std::fs::read_to_string(path)
769 .map_err(|e| ErrorBuilder::config(format!("Failed to read config file: {}", e)).build())?;
770
771 let config: SystemConfig = serde_json::from_str(&content)
772 .map_err(|e| ErrorBuilder::config(format!("Failed to parse config: {}", e)).build())?;
773
774 Ok(config)
775 }
776
777 pub fn save_config<P: AsRef<Path>>(&self, path: P) -> NeuroDivergentResult<()> {
779 let config = self.current_config.as_ref()
780 .ok_or_else(|| config_error!("No configuration loaded"))?;
781
782 let content = serde_json::to_string_pretty(config)
783 .map_err(|e| ErrorBuilder::config(format!("Failed to serialize config: {}", e)).build())?;
784
785 std::fs::write(path, content)
786 .map_err(|e| ErrorBuilder::config(format!("Failed to write config file: {}", e)).build())?;
787
788 Ok(())
789 }
790
791 pub fn config(&self) -> Option<&SystemConfig> {
793 self.current_config.as_ref()
794 }
795
796 pub fn config_mut(&mut self) -> Option<&mut SystemConfig> {
798 self.current_config.as_mut()
799 }
800
801 pub fn validate(&self) -> ValidationResult {
803 let mut result = ValidationResult {
804 is_valid: true,
805 errors: Vec::new(),
806 warnings: Vec::new(),
807 };
808
809 if let Some(config) = &self.current_config {
810 self.validate_system_config(config, &mut result);
811 } else {
812 result.is_valid = false;
813 result.errors.push(ValidationError {
814 field: "config".to_string(),
815 message: "No configuration loaded".to_string(),
816 code: Some("NO_CONFIG".to_string()),
817 });
818 }
819
820 result
821 }
822
823 fn validate_system_config(&self, _config: &SystemConfig, _result: &mut ValidationResult) {
824 }
826}
827
828impl Default for SystemConfig {
829 fn default() -> Self {
830 Self {
831 logging: LoggingConfig::default(),
832 performance: PerformanceConfig::default(),
833 memory: MemoryConfig::default(),
834 parallel: ParallelConfig::default(),
835 io: IoConfig::default(),
836 debug: DebugConfig::default(),
837 features: FeatureFlags::default(),
838 }
839 }
840}
841
842impl Default for LoggingConfig {
843 fn default() -> Self {
844 Self {
845 level: "info".to_string(),
846 format: LogFormat::Text,
847 file_path: None,
848 max_file_size_mb: Some(100),
849 max_files: Some(5),
850 structured: false,
851 timestamps: true,
852 }
853 }
854}
855
856impl Default for PerformanceConfig {
857 fn default() -> Self {
858 Self {
859 enable_simd: true,
860 enable_gpu: false,
861 gpu_device_id: None,
862 enable_amp: false,
863 cpu_optimization: CpuOptimization::Native,
864 enable_profiling: false,
865 profiling_output_dir: None,
866 }
867 }
868}
869
870impl Default for MemoryConfig {
871 fn default() -> Self {
872 Self {
873 pool_size_mb: None,
874 enable_pool: false,
875 allocation_strategy: AllocationStrategy::System,
876 enable_monitoring: false,
877 warning_threshold: Some(80.0),
878 gc_hints: true,
879 }
880 }
881}
882
883impl Default for ParallelConfig {
884 fn default() -> Self {
885 Self {
886 num_threads: None,
887 thread_pool: ThreadPoolConfig::default(),
888 enable_parallel_training: true,
889 enable_parallel_prediction: true,
890 enable_data_parallelism: true,
891 enable_model_parallelism: false,
892 }
893 }
894}
895
896impl Default for ThreadPoolConfig {
897 fn default() -> Self {
898 Self {
899 pool_type: ThreadPoolType::Global,
900 stack_size_kb: None,
901 thread_name_prefix: Some("neuro-divergent".to_string()),
902 thread_priority: Some(ThreadPriority::Normal),
903 }
904 }
905}
906
907impl Default for IoConfig {
908 fn default() -> Self {
909 Self {
910 default_format: DataFormat::Json,
911 enable_compression: false,
912 compression_level: Some(6),
913 buffer_size_kb: 64,
914 enable_async_io: false,
915 network_timeout_secs: Some(30),
916 retry_config: RetryConfig::default(),
917 }
918 }
919}
920
921impl Default for RetryConfig {
922 fn default() -> Self {
923 Self {
924 max_retries: 3,
925 initial_delay_ms: 100,
926 max_delay_ms: 5000,
927 backoff_multiplier: 2.0,
928 enable_jitter: true,
929 }
930 }
931}
932
933impl Default for DebugConfig {
934 fn default() -> Self {
935 Self {
936 enabled: false,
937 output_dir: None,
938 save_intermediates: false,
939 detailed_timing: false,
940 memory_tracking: false,
941 network_visualization: false,
942 verbosity: DebugVerbosity::Normal,
943 }
944 }
945}
946
947impl Default for FeatureFlags {
948 fn default() -> Self {
949 Self {
950 experimental_gpu_kernels: false,
951 experimental_optimizations: false,
952 experimental_models: false,
953 experimental_data_formats: false,
954 auto_hyperparameter_tuning: false,
955 auto_model_selection: false,
956 distributed_training: false,
957 }
958 }
959}
960
961impl Default for ConfigMetadata {
962 fn default() -> Self {
963 Self::new()
964 }
965}
966
967impl Default for ConfigManager {
968 fn default() -> Self {
969 Self::new()
970 }
971}
972
973impl<T: Float + Send + Sync + 'static> Default for ModelConfigBuilder<T> {
974 fn default() -> Self {
975 Self::new()
976 }
977}
978
979impl fmt::Display for LogFormat {
980 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
981 match self {
982 LogFormat::Text => write!(f, "text"),
983 LogFormat::Json => write!(f, "json"),
984 LogFormat::Compact => write!(f, "compact"),
985 LogFormat::Pretty => write!(f, "pretty"),
986 }
987 }
988}
989
990impl fmt::Display for DataFormat {
991 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
992 match self {
993 DataFormat::Json => write!(f, "json"),
994 DataFormat::Bson => write!(f, "bson"),
995 DataFormat::MessagePack => write!(f, "msgpack"),
996 DataFormat::Binary => write!(f, "binary"),
997 DataFormat::Parquet => write!(f, "parquet"),
998 DataFormat::Csv => write!(f, "csv"),
999 }
1000 }
1001}
1002
1003#[cfg(test)]
1004mod tests {
1005 use super::*;
1006
1007 #[test]
1008 fn test_generic_config_creation() {
1009 let config = GenericModelConfig::<f64>::new("test_model")
1010 .with_parameter("learning_rate".to_string(), ConfigParameter::Float(0.01))
1011 .with_parameter("epochs".to_string(), ConfigParameter::Integer(100));
1012
1013 assert_eq!(config.model_type, "test_model");
1014 assert_eq!(config.get_float_parameter("learning_rate"), Some(0.01));
1015 assert_eq!(config.get_integer_parameter("epochs"), Some(100));
1016 }
1017
1018 #[test]
1019 fn test_config_builder() {
1020 let config = ModelConfigBuilder::<f64>::new()
1021 .with_model_type("lstm")
1022 .with_horizon(12)
1023 .with_input_size(24)
1024 .with_parameter("hidden_size".to_string(), ConfigParameter::Integer(64))
1025 .build()
1026 .unwrap();
1027
1028 assert_eq!(config.model_type, "lstm");
1029 assert_eq!(config.horizon, 12);
1030 assert_eq!(config.input_size, 24);
1031 assert_eq!(config.get_integer_parameter("hidden_size"), Some(64));
1032 }
1033
1034 #[test]
1035 fn test_config_validation() {
1036 let valid_config = GenericModelConfig::<f64>::new("test")
1037 .with_parameter("horizon".to_string(), ConfigParameter::Integer(12));
1038
1039 valid_config.horizon = 12;
1040 assert!(valid_config.validate().is_ok());
1041
1042 let mut invalid_config = GenericModelConfig::<f64>::new("test");
1043 invalid_config.horizon = 0;
1044 assert!(invalid_config.validate().is_err());
1045 }
1046
1047 #[test]
1048 fn test_config_serialization() {
1049 let config = GenericModelConfig::<f64>::new("test_model")
1050 .with_parameter("param1".to_string(), ConfigParameter::Float(1.0))
1051 .with_parameter("param2".to_string(), ConfigParameter::String("value".to_string()));
1052
1053 let serialized = serde_json::to_string(&config).unwrap();
1054 let deserialized: GenericModelConfig<f64> = serde_json::from_str(&serialized).unwrap();
1055
1056 assert_eq!(config.model_type, deserialized.model_type);
1057 assert_eq!(config.parameters.len(), deserialized.parameters.len());
1058 }
1059
1060 #[test]
1061 fn test_system_config_defaults() {
1062 let config = SystemConfig::default();
1063
1064 assert_eq!(config.logging.level, "info");
1065 assert!(config.performance.enable_simd);
1066 assert!(!config.debug.enabled);
1067 assert!(!config.features.experimental_gpu_kernels);
1068 }
1069
1070 #[test]
1071 fn test_config_manager() {
1072 let mut manager = ConfigManager::new();
1073
1074 let config = manager.load_config().unwrap();
1076 assert_eq!(config.logging.level, "info");
1077
1078 assert!(manager.config().is_some());
1080 }
1081
1082 #[test]
1083 fn test_metadata_builder() {
1084 let metadata = ConfigMetadata::new()
1085 .with_name("test_config")
1086 .with_description("Test configuration")
1087 .with_author("test_author")
1088 .with_tags(vec!["test".to_string(), "config".to_string()])
1089 .with_custom_field("custom_key", "custom_value");
1090
1091 assert_eq!(metadata.name, Some("test_config".to_string()));
1092 assert_eq!(metadata.description, Some("Test configuration".to_string()));
1093 assert_eq!(metadata.author, Some("test_author".to_string()));
1094 assert_eq!(metadata.tags, vec!["test", "config"]);
1095 assert_eq!(metadata.custom_fields.get("custom_key"), Some(&"custom_value".to_string()));
1096 }
1097}