use ruvector_attention::training::{Adam, AdamW, InfoNCELoss, Loss, Optimizer, SGD};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct WasmInfoNCELoss {
inner: InfoNCELoss,
}
#[wasm_bindgen]
impl WasmInfoNCELoss {
#[wasm_bindgen(constructor)]
pub fn new(temperature: f32) -> WasmInfoNCELoss {
Self {
inner: InfoNCELoss::new(temperature),
}
}
pub fn compute(
&self,
anchor: &[f32],
positive: &[f32],
negatives: JsValue,
) -> Result<f32, JsError> {
let negatives_vec: Vec<Vec<f32>> = serde_wasm_bindgen::from_value(negatives)?;
let negatives_refs: Vec<&[f32]> = negatives_vec.iter().map(|n| n.as_slice()).collect();
Ok(self.inner.compute(anchor, positive, &negatives_refs))
}
}
#[wasm_bindgen]
pub struct WasmAdam {
inner: Adam,
}
#[wasm_bindgen]
impl WasmAdam {
#[wasm_bindgen(constructor)]
pub fn new(param_count: usize, learning_rate: f32) -> WasmAdam {
Self {
inner: Adam::new(param_count, learning_rate),
}
}
pub fn step(&mut self, params: &mut [f32], gradients: &[f32]) {
self.inner.step(params, gradients);
}
pub fn reset(&mut self) {
self.inner.reset();
}
#[wasm_bindgen(getter)]
pub fn learning_rate(&self) -> f32 {
self.inner.learning_rate()
}
#[wasm_bindgen(setter)]
pub fn set_learning_rate(&mut self, lr: f32) {
self.inner.set_learning_rate(lr);
}
}
#[wasm_bindgen]
pub struct WasmAdamW {
inner: AdamW,
wd: f32,
}
#[wasm_bindgen]
impl WasmAdamW {
#[wasm_bindgen(constructor)]
pub fn new(param_count: usize, learning_rate: f32, weight_decay: f32) -> WasmAdamW {
let optimizer = AdamW::new(param_count, learning_rate).with_weight_decay(weight_decay);
Self {
inner: optimizer,
wd: weight_decay,
}
}
pub fn step(&mut self, params: &mut [f32], gradients: &[f32]) {
self.inner.step(params, gradients);
}
pub fn reset(&mut self) {
self.inner.reset();
}
#[wasm_bindgen(getter)]
pub fn learning_rate(&self) -> f32 {
self.inner.learning_rate()
}
#[wasm_bindgen(setter)]
pub fn set_learning_rate(&mut self, lr: f32) {
self.inner.set_learning_rate(lr);
}
#[wasm_bindgen(getter)]
pub fn weight_decay(&self) -> f32 {
self.wd
}
}
#[wasm_bindgen]
pub struct WasmSGD {
inner: SGD,
}
#[wasm_bindgen]
impl WasmSGD {
#[wasm_bindgen(constructor)]
pub fn new(param_count: usize, learning_rate: f32, momentum: Option<f32>) -> WasmSGD {
let mut optimizer = SGD::new(param_count, learning_rate);
if let Some(m) = momentum {
optimizer = optimizer.with_momentum(m);
}
Self { inner: optimizer }
}
pub fn step(&mut self, params: &mut [f32], gradients: &[f32]) {
self.inner.step(params, gradients);
}
pub fn reset(&mut self) {
self.inner.reset();
}
#[wasm_bindgen(getter)]
pub fn learning_rate(&self) -> f32 {
self.inner.learning_rate()
}
#[wasm_bindgen(setter)]
pub fn set_learning_rate(&mut self, lr: f32) {
self.inner.set_learning_rate(lr);
}
}
#[wasm_bindgen]
pub struct WasmLRScheduler {
initial_lr: f32,
current_step: usize,
warmup_steps: usize,
total_steps: usize,
}
#[wasm_bindgen]
impl WasmLRScheduler {
#[wasm_bindgen(constructor)]
pub fn new(initial_lr: f32, warmup_steps: usize, total_steps: usize) -> WasmLRScheduler {
Self {
initial_lr,
current_step: 0,
warmup_steps,
total_steps,
}
}
pub fn get_lr(&self) -> f32 {
if self.current_step < self.warmup_steps {
self.initial_lr * (self.current_step as f32 / self.warmup_steps as f32)
} else {
let progress = (self.current_step - self.warmup_steps) as f32
/ (self.total_steps - self.warmup_steps) as f32;
let cosine = 0.5 * (1.0 + (std::f32::consts::PI * progress).cos());
self.initial_lr * cosine
}
}
pub fn step(&mut self) {
self.current_step += 1;
}
pub fn reset(&mut self) {
self.current_step = 0;
}
}