Skip to main content

optirs_core/plugin/
core.rs

1// Core plugin traits and interfaces for optimizer development
2//
3// This module defines the fundamental traits and structures that custom optimizers
4// must implement to integrate with the plugin system.
5
6use 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
15/// Main trait for optimizer plugins
16pub trait OptimizerPlugin<A: Float>: Debug + Send + Sync {
17    /// Perform a single optimization step
18    fn step(&mut self, params: &Array1<A>, gradients: &Array1<A>) -> Result<Array1<A>>;
19
20    /// Get optimizer name
21    fn name(&self) -> &str;
22
23    /// Get optimizer version
24    fn version(&self) -> &str;
25
26    /// Get plugin information
27    fn plugin_info(&self) -> PluginInfo;
28
29    /// Get optimizer capabilities
30    fn capabilities(&self) -> PluginCapabilities;
31
32    /// Initialize optimizer with parameters
33    fn initialize(&mut self, paramshape: &[usize]) -> Result<()>;
34
35    /// Reset optimizer state
36    fn reset(&mut self) -> Result<()>;
37
38    /// Get optimizer configuration
39    fn get_config(&self) -> OptimizerConfig;
40
41    /// Set optimizer configuration
42    fn set_config(&mut self, config: OptimizerConfig) -> Result<()>;
43
44    /// Get optimizer state for serialization
45    fn get_state(&self) -> Result<OptimizerState>;
46
47    /// Set optimizer state from deserialization
48    fn set_state(&mut self, state: OptimizerState) -> Result<()>;
49
50    /// Clone the optimizer plugin
51    fn clone_plugin(&self) -> Box<dyn OptimizerPlugin<A>>;
52
53    /// Get memory usage information
54    fn memory_usage(&self) -> MemoryUsage {
55        MemoryUsage::default()
56    }
57
58    /// Get performance metrics
59    fn performance_metrics(&self) -> PerformanceMetrics {
60        PerformanceMetrics::default()
61    }
62}
63
64/// Extended plugin trait for optimizers with advanced features
65pub trait ExtendedOptimizerPlugin<A: Float>: OptimizerPlugin<A> {
66    /// Perform batch optimization step
67    fn batch_step(&mut self, params: &Array2<A>, gradients: &Array2<A>) -> Result<Array2<A>>;
68
69    /// Compute adaptive learning rate
70    fn adaptive_learning_rate(&self, gradients: &Array1<A>) -> A;
71
72    /// Gradient preprocessing
73    fn preprocess_gradients(&self, gradients: &Array1<A>) -> Result<Array1<A>>;
74
75    /// Parameter postprocessing
76    fn postprocess_parameters(&self, params: &Array1<A>) -> Result<Array1<A>>;
77
78    /// Get optimization trajectory
79    fn get_trajectory(&self) -> Vec<Array1<A>>;
80
81    /// Compute convergence metrics
82    fn convergence_metrics(&self) -> ConvergenceMetrics;
83}
84
85/// Plugin information and metadata
86#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct PluginInfo {
88    /// Plugin name
89    pub name: String,
90    /// Plugin version
91    pub version: String,
92    /// Plugin author
93    pub author: String,
94    /// Plugin description
95    pub description: String,
96    /// Plugin homepage/repository
97    pub homepage: Option<String>,
98    /// Plugin license
99    pub license: String,
100    /// Supported data types
101    pub supported_types: Vec<DataType>,
102    /// Plugin category
103    pub category: PluginCategory,
104    /// Plugin tags for search/filtering
105    pub tags: Vec<String>,
106    /// Minimum SDK version required
107    pub min_sdk_version: String,
108    /// Plugin dependencies
109    pub dependencies: Vec<PluginDependency>,
110}
111
112/// Plugin capabilities and features
113#[derive(Debug, Clone, Serialize, Deserialize, Default)]
114pub struct PluginCapabilities {
115    /// Supports sparse gradients
116    pub sparse_gradients: bool,
117    /// Supports parameter groups
118    pub parameter_groups: bool,
119    /// Supports momentum
120    pub momentum: bool,
121    /// Supports adaptive learning rates
122    pub adaptive_learning_rate: bool,
123    /// Supports weight decay
124    pub weight_decay: bool,
125    /// Supports gradient clipping
126    pub gradient_clipping: bool,
127    /// Supports batch processing
128    pub batch_processing: bool,
129    /// Supports state serialization
130    pub state_serialization: bool,
131    /// Thread safety
132    pub thread_safe: bool,
133    /// Memory efficient
134    pub memory_efficient: bool,
135    /// GPU acceleration support
136    pub gpu_support: bool,
137    /// SIMD optimization
138    pub simd_optimized: bool,
139    /// Supports custom loss functions
140    pub custom_loss_functions: bool,
141    /// Supports regularization
142    pub regularization: bool,
143}
144
145impl PluginCapabilities {
146    /// Look up a capability by its field name (as used in
147    /// `PluginQuery::required_capabilities`). Unknown names return `false`
148    /// rather than matching everything -- a query for a capability this
149    /// type has never heard of must never silently pass.
150    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/// Supported data types
172#[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/// Plugin categories
184#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
185pub enum PluginCategory {
186    /// First-order optimizers (SGD, Adam, etc.)
187    FirstOrder,
188    /// Second-order optimizers (Newton, BFGS, etc.)
189    SecondOrder,
190    /// Specialized optimizers (domain-specific)
191    Specialized,
192    /// Meta-learning optimizers
193    MetaLearning,
194    /// Experimental optimizers
195    Experimental,
196    /// Utility/helper plugins
197    Utility,
198}
199
200/// Plugin dependency information
201#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
202pub struct PluginDependency {
203    /// Dependency name
204    pub name: String,
205    /// Version requirement
206    pub version: String,
207    /// Whether dependency is optional
208    pub optional: bool,
209    /// Dependency type
210    pub dependency_type: DependencyType,
211}
212
213/// Types of plugin dependencies
214#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
215pub enum DependencyType {
216    /// Another plugin
217    Plugin,
218    /// System library
219    SystemLibrary,
220    /// Rust crate
221    Crate,
222    /// Runtime requirement
223    Runtime,
224}
225
226/// Optimizer configuration
227#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
228pub struct OptimizerConfig {
229    /// Learning rate
230    pub learning_rate: f64,
231    /// Weight decay
232    pub weight_decay: f64,
233    /// Momentum coefficient
234    pub momentum: f64,
235    /// Gradient clipping threshold
236    pub gradient_clip: Option<f64>,
237    /// Custom parameters
238    pub custom_params: HashMap<String, ConfigValue>,
239}
240
241/// Configuration value types
242#[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/// Optimizer state for serialization
252#[derive(Debug, Clone, Serialize, Deserialize, Default)]
253pub struct OptimizerState {
254    /// Internal state vectors
255    pub state_vectors: HashMap<String, Vec<f64>>,
256    /// Step count
257    pub step_count: usize,
258    /// Custom state data
259    pub custom_state: HashMap<String, StateValue>,
260}
261
262/// State value types
263#[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/// Memory usage information
274#[derive(Debug, Clone, Default)]
275pub struct MemoryUsage {
276    /// Current memory usage (bytes)
277    pub current_usage: usize,
278    /// Peak memory usage (bytes)
279    pub peak_usage: usize,
280    /// Memory efficiency score (0.0 to 1.0)
281    pub efficiency_score: f64,
282}
283
284/// Performance metrics
285#[derive(Debug, Clone, Default)]
286pub struct PerformanceMetrics {
287    /// Average step time (seconds)
288    pub avg_step_time: f64,
289    /// Total steps performed
290    pub total_steps: usize,
291    /// Throughput (steps per second)
292    pub throughput: f64,
293    /// CPU utilization (0.0 to 1.0)
294    pub cpu_utilization: f64,
295}
296
297/// Convergence metrics
298#[derive(Debug, Clone, Default)]
299pub struct ConvergenceMetrics {
300    /// Gradient norm
301    pub gradient_norm: f64,
302    /// Parameter change norm
303    pub parameter_change_norm: f64,
304    /// Loss improvement rate
305    pub loss_improvement_rate: f64,
306    /// Convergence score (0.0 to 1.0)
307    pub convergence_score: f64,
308}
309
310/// Plugin validation result
311#[derive(Debug, Clone)]
312pub struct PluginValidationResult {
313    /// Whether plugin is valid
314    pub is_valid: bool,
315    /// Validation errors
316    pub errors: Vec<String>,
317    /// Validation warnings
318    pub warnings: Vec<String>,
319    /// Performance benchmark results
320    pub benchmark_results: Option<BenchmarkResults>,
321}
322
323/// Benchmark results for plugin validation
324#[derive(Debug, Clone)]
325pub struct BenchmarkResults {
326    /// Execution time benchmarks
327    pub execution_times: Vec<Duration>,
328    /// Memory usage benchmarks
329    pub memory_usage: Vec<usize>,
330    /// Accuracy benchmarks
331    pub accuracy_scores: Vec<f64>,
332    /// Convergence benchmarks
333    pub convergence_rates: Vec<f64>,
334}
335
336/// Plugin factory trait for creating optimizer instances
337pub trait OptimizerPluginFactory<A: Float>: Debug + Send + Sync {
338    /// Create a new optimizer instance
339    fn create_optimizer(&self, config: OptimizerConfig) -> Result<Box<dyn OptimizerPlugin<A>>>;
340
341    /// Get factory information
342    fn factory_info(&self) -> PluginInfo;
343
344    /// Validate configuration
345    fn validate_config(&self, config: &OptimizerConfig) -> Result<()>;
346
347    /// Get default configuration
348    fn default_config(&self) -> OptimizerConfig;
349
350    /// Get configuration schema
351    fn config_schema(&self) -> ConfigSchema;
352}
353
354/// Configuration schema for validation and UI generation
355#[derive(Debug, Clone, Serialize, Deserialize)]
356pub struct ConfigSchema {
357    /// Schema fields
358    pub fields: HashMap<String, FieldSchema>,
359    /// Required fields
360    pub required_fields: Vec<String>,
361    /// Schema version
362    pub version: String,
363}
364
365/// Individual field schema
366#[derive(Debug, Clone, Serialize, Deserialize)]
367pub struct FieldSchema {
368    /// Field type
369    pub field_type: FieldType,
370    /// Field description
371    pub description: String,
372    /// Default value
373    pub default_value: Option<ConfigValue>,
374    /// Validation constraints
375    pub constraints: Vec<ValidationConstraint>,
376    /// Whether field is required
377    pub required: bool,
378}
379
380/// Field types for schema
381#[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/// Validation constraints
405#[derive(Debug, Clone, Serialize, Deserialize)]
406pub enum ValidationConstraint {
407    /// Minimum value
408    Min(f64),
409    /// Maximum value
410    Max(f64),
411    /// Value must be positive
412    Positive,
413    /// Value must be non-negative
414    NonNegative,
415    /// Value must be in range
416    Range(f64, f64),
417    /// String must match regex pattern
418    Pattern(String),
419    /// Custom validation function name
420    Custom(String),
421}
422
423/// Plugin lifecycle hooks
424pub trait PluginLifecycle {
425    /// Called when plugin is loaded
426    fn on_load(&mut self) -> Result<()> {
427        Ok(())
428    }
429
430    /// Called when plugin is unloaded
431    fn on_unload(&mut self) -> Result<()> {
432        Ok(())
433    }
434
435    /// Called when plugin is enabled
436    fn on_enable(&mut self) -> Result<()> {
437        Ok(())
438    }
439
440    /// Called when plugin is disabled
441    fn on_disable(&mut self) -> Result<()> {
442        Ok(())
443    }
444
445    /// Called periodically for maintenance
446    fn on_maintenance(&mut self) -> Result<()> {
447        Ok(())
448    }
449}
450
451/// Plugin event system
452///
453/// `Send + Sync` because an event handler is stored inside
454/// `Box<dyn PluginEventHandler>` on `BaseOptimizerPlugin`, and
455/// `OptimizerPlugin<A>: Debug + Send + Sync` requires every field of any
456/// implementor to satisfy the same bound.
457pub trait PluginEventHandler: Send + Sync {
458    /// Handle optimization step event
459    fn on_step(&mut self, _step: usize, _params: &Array1<f64>, _gradients: &Array1<f64>) {}
460
461    /// Handle convergence event
462    fn on_convergence(&mut self, _finalparams: &Array1<f64>) {}
463
464    /// Handle error event
465    fn on_error(&mut self, _error: &OptimError) {}
466
467    /// Handle custom event
468    fn on_custom_event(&mut self, _event_name: &str, _data: &dyn Any) {}
469}
470
471/// Plugin metadata provider
472pub trait PluginMetadata {
473    /// Get plugin documentation
474    fn documentation(&self) -> String {
475        String::new()
476    }
477
478    /// Get plugin examples
479    fn examples(&self) -> Vec<PluginExample> {
480        Vec::new()
481    }
482
483    /// Get plugin changelog
484    fn changelog(&self) -> String {
485        String::new()
486    }
487
488    /// Get plugin compatibility information
489    fn compatibility(&self) -> CompatibilityInfo {
490        CompatibilityInfo::default()
491    }
492}
493
494/// Plugin example
495#[derive(Debug, Clone)]
496pub struct PluginExample {
497    /// Example title
498    pub title: String,
499    /// Example description
500    pub description: String,
501    /// Example code
502    pub code: String,
503    /// Expected output
504    pub expected_output: String,
505}
506
507/// Compatibility information
508#[derive(Debug, Clone, Default)]
509pub struct CompatibilityInfo {
510    /// Supported Rust versions
511    pub rust_versions: Vec<String>,
512    /// Supported platforms
513    pub platforms: Vec<String>,
514    /// Known issues
515    pub known_issues: Vec<String>,
516    /// Breaking changes
517    pub breaking_changes: Vec<String>,
518}
519
520// Default implementations
521
522impl 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/// Utility functions for plugin development
553/// Create a basic plugin info structure
554#[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/// Create basic plugin capabilities
565#[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/// Validate plugin configuration against schema
575#[allow(dead_code)]
576pub fn validate_config_against_schema(
577    config: &OptimizerConfig,
578    schema: &ConfigSchema,
579) -> Result<()> {
580    // Check required fields
581    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    // Validate field constraints
609    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/// Validate individual field value against schema
631#[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            _ => {} // Other constraint types can be added as needed
666        }
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}