use crate::error::{NeuralError, Result};
use crate::optimizers::Optimizer;
use scirs2_core::ndarray::Array;
use scirs2_core::numeric::Float;
use std::fmt::Debug;
use scirs2_optim::optimizers as optim_optimizers;
pub struct Adam<F: Float + Debug + NumAssign> {
inner: optim, optimizers: Adam<F>,
weight_decay: F,
}
impl<F: Float + Debug + NumAssign> Adam<F> {
pub fn new(_learningrate: F, beta1: F, beta2: F, epsilon: F) -> Self {
Self {
inner: optim, optimizers: Adam::new_with_config(
learning_rate,
beta1,
beta2,
epsilon,
F::zero() ),
weight_decay: F::zero(),
}
}
pub fn default_with_lr(_learningrate: F) -> Result<Self> {
let beta1 = F::from(0.9).ok_or_else(||
NeuralError::InvalidArchitecture("Failed to convert beta1".to_string()))?;
let beta2 = F::from(0.999).ok_or_else(||
NeuralError::InvalidArchitecture("Failed to convert beta2".to_string()))?;
let epsilon = F::from(1e-8).ok_or_else(||
NeuralError::InvalidArchitecture("Failed to convert epsilon".to_string()))?;
Ok(Self::new(_learning_rate, beta1, beta2, epsilon))
pub fn new_with_decay(
learning_rate: F,
beta1: F,
beta2: F,
epsilon: F,
weight_decay: F
) -> Self {
weight_decay
weight_decay,
pub fn get_beta1(&self) -> F {
self.inner.get_beta1()
pub fn set_beta1(&mut self, beta1: F) -> &mut Self {
self.inner.set_beta1(beta1);
self
pub fn get_beta2(&self) -> F {
self.inner.get_beta2()
pub fn set_beta2(&mut self, beta2: F) -> &mut Self {
self.inner.set_beta2(beta2);
pub fn get_epsilon(&self) -> F {
self.inner.get_epsilon()
pub fn set_epsilon(&mut self, epsilon: F) -> &mut Self {
self.inner.set_epsilon(epsilon);
pub fn get_weight_decay(&self) -> F {
self.weight_decay
pub fn set_weight_decay(&mut self, weightdecay: F) -> &mut Self {
self.weight_decay = weight_decay;
self.inner.set_weight_decay(weight_decay);
pub fn reset(&mut self) {
self.inner.reset();
impl<F: Float + Debug + NumAssign> Default for Adam<F> {
fn default() -> Self {
let _learning_rate = F::from(0.001).unwrap_or(F::one() / F::from(1000.0).unwrap_or(F::one()));
let beta1 = F::from(0.9).unwrap_or(F::one());
let beta2 = F::from(0.999).unwrap_or(F::one());
let epsilon = F::from(1e-8).unwrap_or(F::zero());
Self::new(_learning_rate, beta1, beta2, epsilon)
impl<F: Float + Debug + NumAssign> Optimizer<F> for Adam<F> {
fn update(&mut self, params: &mut [Array<F, scirs2_core::ndarray::IxDyn>],
grads: &[Array<F, scirs2_core::ndarray::IxDyn>]) -> Result<()> {
if params.len() != grads.len() {
return Err(NeuralError::TrainingError(format!(
"Parameter and gradient counts do not match: {} vs {}",
params.len(), grads.len()
)));
for (param, grad) in params.iter_mut().zip(grads.iter()) {
let mut param_copy = param.clone();
match self.inner.step(¶m_copy, grad) {
Ok(updated_param) => {
*param = updated_param;
},
Err(e) => {
return Err(NeuralError::TrainingError(format!(
"Failed to update parameter: {}", e
)));
}
}
Ok(())
fn get_learning_rate(&self) -> F {
self.inner.get_learning_rate()
fn set_learning_rate(&mut self, lr: F) {
self.inner.set_learning_rate(lr);