1use crate::error::{OptimError, Result};
7use scirs2_core::ndarray::{Array1, Array2};
8use scirs2_core::numeric::Float;
9use serde::{Deserialize, Serialize};
10use std::any::Any;
11use std::collections::HashMap;
12use std::fmt::Debug;
13use std::time::Duration;
14
15pub trait OptimizerPlugin<A: Float>: Debug + Send + Sync {
17 fn step(&mut self, params: &Array1<A>, gradients: &Array1<A>) -> Result<Array1<A>>;
19
20 fn name(&self) -> &str;
22
23 fn version(&self) -> &str;
25
26 fn plugin_info(&self) -> PluginInfo;
28
29 fn capabilities(&self) -> PluginCapabilities;
31
32 fn initialize(&mut self, paramshape: &[usize]) -> Result<()>;
34
35 fn reset(&mut self) -> Result<()>;
37
38 fn get_config(&self) -> OptimizerConfig;
40
41 fn set_config(&mut self, config: OptimizerConfig) -> Result<()>;
43
44 fn get_state(&self) -> Result<OptimizerState>;
46
47 fn set_state(&mut self, state: OptimizerState) -> Result<()>;
49
50 fn clone_plugin(&self) -> Box<dyn OptimizerPlugin<A>>;
52
53 fn memory_usage(&self) -> MemoryUsage {
55 MemoryUsage::default()
56 }
57
58 fn performance_metrics(&self) -> PerformanceMetrics {
60 PerformanceMetrics::default()
61 }
62}
63
64pub trait ExtendedOptimizerPlugin<A: Float>: OptimizerPlugin<A> {
66 fn batch_step(&mut self, params: &Array2<A>, gradients: &Array2<A>) -> Result<Array2<A>>;
68
69 fn adaptive_learning_rate(&self, gradients: &Array1<A>) -> A;
71
72 fn preprocess_gradients(&self, gradients: &Array1<A>) -> Result<Array1<A>>;
74
75 fn postprocess_parameters(&self, params: &Array1<A>) -> Result<Array1<A>>;
77
78 fn get_trajectory(&self) -> Vec<Array1<A>>;
80
81 fn convergence_metrics(&self) -> ConvergenceMetrics;
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct PluginInfo {
88 pub name: String,
90 pub version: String,
92 pub author: String,
94 pub description: String,
96 pub homepage: Option<String>,
98 pub license: String,
100 pub supported_types: Vec<DataType>,
102 pub category: PluginCategory,
104 pub tags: Vec<String>,
106 pub min_sdk_version: String,
108 pub dependencies: Vec<PluginDependency>,
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize, Default)]
114pub struct PluginCapabilities {
115 pub sparse_gradients: bool,
117 pub parameter_groups: bool,
119 pub momentum: bool,
121 pub adaptive_learning_rate: bool,
123 pub weight_decay: bool,
125 pub gradient_clipping: bool,
127 pub batch_processing: bool,
129 pub state_serialization: bool,
131 pub thread_safe: bool,
133 pub memory_efficient: bool,
135 pub gpu_support: bool,
137 pub simd_optimized: bool,
139 pub custom_loss_functions: bool,
141 pub regularization: bool,
143}
144
145impl PluginCapabilities {
146 pub fn has_capability(&self, name: &str) -> bool {
151 match name {
152 "sparse_gradients" => self.sparse_gradients,
153 "parameter_groups" => self.parameter_groups,
154 "momentum" => self.momentum,
155 "adaptive_learning_rate" => self.adaptive_learning_rate,
156 "weight_decay" => self.weight_decay,
157 "gradient_clipping" => self.gradient_clipping,
158 "batch_processing" => self.batch_processing,
159 "state_serialization" => self.state_serialization,
160 "thread_safe" => self.thread_safe,
161 "memory_efficient" => self.memory_efficient,
162 "gpu_support" => self.gpu_support,
163 "simd_optimized" => self.simd_optimized,
164 "custom_loss_functions" => self.custom_loss_functions,
165 "regularization" => self.regularization,
166 _ => false,
167 }
168 }
169}
170
171#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
173pub enum DataType {
174 F32,
175 F64,
176 I32,
177 I64,
178 Complex32,
179 Complex64,
180 Custom(String),
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
185pub enum PluginCategory {
186 FirstOrder,
188 SecondOrder,
190 Specialized,
192 MetaLearning,
194 Experimental,
196 Utility,
198}
199
200#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
202pub struct PluginDependency {
203 pub name: String,
205 pub version: String,
207 pub optional: bool,
209 pub dependency_type: DependencyType,
211}
212
213#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
215pub enum DependencyType {
216 Plugin,
218 SystemLibrary,
220 Crate,
222 Runtime,
224}
225
226#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
228pub struct OptimizerConfig {
229 pub learning_rate: f64,
231 pub weight_decay: f64,
233 pub momentum: f64,
235 pub gradient_clip: Option<f64>,
237 pub custom_params: HashMap<String, ConfigValue>,
239}
240
241#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
243pub enum ConfigValue {
244 Float(f64),
245 Integer(i64),
246 Boolean(bool),
247 String(String),
248 Array(Vec<f64>),
249}
250
251#[derive(Debug, Clone, Serialize, Deserialize, Default)]
253pub struct OptimizerState {
254 pub state_vectors: HashMap<String, Vec<f64>>,
256 pub step_count: usize,
258 pub custom_state: HashMap<String, StateValue>,
260}
261
262#[derive(Debug, Clone, Serialize, Deserialize)]
264pub enum StateValue {
265 Float(f64),
266 Integer(i64),
267 Boolean(bool),
268 String(String),
269 Array(Vec<f64>),
270 Matrix(Vec<Vec<f64>>),
271}
272
273#[derive(Debug, Clone, Default)]
275pub struct MemoryUsage {
276 pub current_usage: usize,
278 pub peak_usage: usize,
280 pub efficiency_score: f64,
282}
283
284#[derive(Debug, Clone, Default)]
286pub struct PerformanceMetrics {
287 pub avg_step_time: f64,
289 pub total_steps: usize,
291 pub throughput: f64,
293 pub cpu_utilization: f64,
295}
296
297#[derive(Debug, Clone, Default)]
299pub struct ConvergenceMetrics {
300 pub gradient_norm: f64,
302 pub parameter_change_norm: f64,
304 pub loss_improvement_rate: f64,
306 pub convergence_score: f64,
308}
309
310#[derive(Debug, Clone)]
312pub struct PluginValidationResult {
313 pub is_valid: bool,
315 pub errors: Vec<String>,
317 pub warnings: Vec<String>,
319 pub benchmark_results: Option<BenchmarkResults>,
321}
322
323#[derive(Debug, Clone)]
325pub struct BenchmarkResults {
326 pub execution_times: Vec<Duration>,
328 pub memory_usage: Vec<usize>,
330 pub accuracy_scores: Vec<f64>,
332 pub convergence_rates: Vec<f64>,
334}
335
336pub trait OptimizerPluginFactory<A: Float>: Debug + Send + Sync {
338 fn create_optimizer(&self, config: OptimizerConfig) -> Result<Box<dyn OptimizerPlugin<A>>>;
340
341 fn factory_info(&self) -> PluginInfo;
343
344 fn validate_config(&self, config: &OptimizerConfig) -> Result<()>;
346
347 fn default_config(&self) -> OptimizerConfig;
349
350 fn config_schema(&self) -> ConfigSchema;
352}
353
354#[derive(Debug, Clone, Serialize, Deserialize)]
356pub struct ConfigSchema {
357 pub fields: HashMap<String, FieldSchema>,
359 pub required_fields: Vec<String>,
361 pub version: String,
363}
364
365#[derive(Debug, Clone, Serialize, Deserialize)]
367pub struct FieldSchema {
368 pub field_type: FieldType,
370 pub description: String,
372 pub default_value: Option<ConfigValue>,
374 pub constraints: Vec<ValidationConstraint>,
376 pub required: bool,
378}
379
380#[derive(Debug, Clone, Serialize, Deserialize)]
382pub enum FieldType {
383 Float {
384 min: Option<f64>,
385 max: Option<f64>,
386 },
387 Integer {
388 min: Option<i64>,
389 max: Option<i64>,
390 },
391 Boolean,
392 String {
393 max_length: Option<usize>,
394 },
395 Array {
396 element_type: Box<FieldType>,
397 max_length: Option<usize>,
398 },
399 Choice {
400 options: Vec<String>,
401 },
402}
403
404#[derive(Debug, Clone, Serialize, Deserialize)]
406pub enum ValidationConstraint {
407 Min(f64),
409 Max(f64),
411 Positive,
413 NonNegative,
415 Range(f64, f64),
417 Pattern(String),
419 Custom(String),
421}
422
423pub trait PluginLifecycle {
425 fn on_load(&mut self) -> Result<()> {
427 Ok(())
428 }
429
430 fn on_unload(&mut self) -> Result<()> {
432 Ok(())
433 }
434
435 fn on_enable(&mut self) -> Result<()> {
437 Ok(())
438 }
439
440 fn on_disable(&mut self) -> Result<()> {
442 Ok(())
443 }
444
445 fn on_maintenance(&mut self) -> Result<()> {
447 Ok(())
448 }
449}
450
451pub trait PluginEventHandler: Send + Sync {
458 fn on_step(&mut self, _step: usize, _params: &Array1<f64>, _gradients: &Array1<f64>) {}
460
461 fn on_convergence(&mut self, _finalparams: &Array1<f64>) {}
463
464 fn on_error(&mut self, _error: &OptimError) {}
466
467 fn on_custom_event(&mut self, _event_name: &str, _data: &dyn Any) {}
469}
470
471pub trait PluginMetadata {
473 fn documentation(&self) -> String {
475 String::new()
476 }
477
478 fn examples(&self) -> Vec<PluginExample> {
480 Vec::new()
481 }
482
483 fn changelog(&self) -> String {
485 String::new()
486 }
487
488 fn compatibility(&self) -> CompatibilityInfo {
490 CompatibilityInfo::default()
491 }
492}
493
494#[derive(Debug, Clone)]
496pub struct PluginExample {
497 pub title: String,
499 pub description: String,
501 pub code: String,
503 pub expected_output: String,
505}
506
507#[derive(Debug, Clone, Default)]
509pub struct CompatibilityInfo {
510 pub rust_versions: Vec<String>,
512 pub platforms: Vec<String>,
514 pub known_issues: Vec<String>,
516 pub breaking_changes: Vec<String>,
518}
519
520impl Default for PluginInfo {
523 fn default() -> Self {
524 Self {
525 name: "Unknown".to_string(),
526 version: "0.1.0".to_string(),
527 author: "Unknown".to_string(),
528 description: "No description provided".to_string(),
529 homepage: None,
530 license: "MIT".to_string(),
531 supported_types: vec![DataType::F32, DataType::F64],
532 category: PluginCategory::FirstOrder,
533 tags: Vec::new(),
534 min_sdk_version: "0.1.0".to_string(),
535 dependencies: Vec::new(),
536 }
537 }
538}
539
540impl Default for OptimizerConfig {
541 fn default() -> Self {
542 Self {
543 learning_rate: 0.001,
544 weight_decay: 0.0,
545 momentum: 0.0,
546 gradient_clip: None,
547 custom_params: HashMap::new(),
548 }
549 }
550}
551
552#[allow(dead_code)]
555pub fn create_plugin_info(name: &str, version: &str, author: &str) -> PluginInfo {
556 PluginInfo {
557 name: name.to_string(),
558 version: version.to_string(),
559 author: author.to_string(),
560 ..Default::default()
561 }
562}
563
564#[allow(dead_code)]
566pub fn create_basic_capabilities() -> PluginCapabilities {
567 PluginCapabilities {
568 state_serialization: true,
569 thread_safe: true,
570 ..Default::default()
571 }
572}
573
574#[allow(dead_code)]
576pub fn validate_config_against_schema(
577 config: &OptimizerConfig,
578 schema: &ConfigSchema,
579) -> Result<()> {
580 for required_field in &schema.required_fields {
582 match required_field.as_str() {
583 "learning_rate" => {
584 if config.learning_rate <= 0.0 {
585 return Err(OptimError::InvalidConfig(
586 "Learning rate must be positive".to_string(),
587 ));
588 }
589 }
590 "weight_decay" => {
591 if config.weight_decay < 0.0 {
592 return Err(OptimError::InvalidConfig(
593 "Weight decay must be non-negative".to_string(),
594 ));
595 }
596 }
597 _ => {
598 if !config.custom_params.contains_key(required_field) {
599 return Err(OptimError::InvalidConfig(format!(
600 "Required field '{}' is missing",
601 required_field
602 )));
603 }
604 }
605 }
606 }
607
608 for (field_name, field_schema) in &schema.fields {
610 let value = match field_name.as_str() {
611 "learning_rate" => Some(ConfigValue::Float(config.learning_rate)),
612 "weight_decay" => Some(ConfigValue::Float(config.weight_decay)),
613 "momentum" => Some(ConfigValue::Float(config.momentum)),
614 _ => config.custom_params.get(field_name).cloned(),
615 };
616
617 if let Some(value) = value {
618 validate_field_value(&value, field_schema)?;
619 } else if field_schema.required {
620 return Err(OptimError::InvalidConfig(format!(
621 "Required field '{}' is missing",
622 field_name
623 )));
624 }
625 }
626
627 Ok(())
628}
629
630#[allow(dead_code)]
632fn validate_field_value(value: &ConfigValue, schema: &FieldSchema) -> Result<()> {
633 for constraint in &schema.constraints {
634 match (value, constraint) {
635 (ConfigValue::Float(v), ValidationConstraint::Min(min)) if v < min => {
636 return Err(OptimError::InvalidConfig(format!(
637 "Value {} is below minimum {}",
638 v, min
639 )));
640 }
641 (ConfigValue::Float(v), ValidationConstraint::Max(max)) if v > max => {
642 return Err(OptimError::InvalidConfig(format!(
643 "Value {} is above maximum {}",
644 v, max
645 )));
646 }
647 (ConfigValue::Float(v), ValidationConstraint::Positive) if *v <= 0.0 => {
648 return Err(OptimError::InvalidConfig(
649 "Value must be positive".to_string(),
650 ));
651 }
652 (ConfigValue::Float(v), ValidationConstraint::NonNegative) if *v < 0.0 => {
653 return Err(OptimError::InvalidConfig(
654 "Value must be non-negative".to_string(),
655 ));
656 }
657 (ConfigValue::Float(v), ValidationConstraint::Range(min, max))
658 if (v < min || v > max) =>
659 {
660 return Err(OptimError::InvalidConfig(format!(
661 "Value {} is outside range [{}, {}]",
662 v, min, max
663 )));
664 }
665 _ => {} }
667 }
668 Ok(())
669}
670
671#[cfg(test)]
672mod tests {
673 use super::*;
674
675 #[test]
676 fn test_plugin_info_default() {
677 let info = PluginInfo::default();
678 assert_eq!(info.name, "Unknown");
679 assert_eq!(info.version, "0.1.0");
680 }
681
682 #[test]
683 fn test_plugin_capabilities_default() {
684 let caps = PluginCapabilities::default();
685 assert!(!caps.sparse_gradients);
686 assert!(!caps.gpu_support);
687 }
688
689 #[test]
690 fn test_config_validation() {
691 let mut schema = ConfigSchema {
692 fields: HashMap::new(),
693 required_fields: vec!["learning_rate".to_string()],
694 version: "1.0".to_string(),
695 };
696
697 schema.fields.insert(
698 "learning_rate".to_string(),
699 FieldSchema {
700 field_type: FieldType::Float {
701 min: Some(0.0),
702 max: None,
703 },
704 description: "Learning rate".to_string(),
705 default_value: Some(ConfigValue::Float(0.001)),
706 constraints: vec![ValidationConstraint::Positive],
707 required: true,
708 },
709 );
710
711 let config = OptimizerConfig {
712 learning_rate: 0.001,
713 ..Default::default()
714 };
715
716 assert!(validate_config_against_schema(&config, &schema).is_ok());
717
718 let config = OptimizerConfig {
719 learning_rate: -0.001,
720 ..Default::default()
721 };
722 assert!(validate_config_against_schema(&config, &schema).is_err());
723 }
724}