use std::collections::HashMap;
use crate::Optimizer;
use crate::common::zeros_entry;
#[derive(Debug, Clone)]
pub struct Stiefel {
pub lr: f32,
pub momentum: f32,
m: HashMap<String, Vec<f32>>,
}
impl Stiefel {
pub fn new(lr: f32) -> Self {
Self {
lr,
momentum: 0.0,
m: HashMap::new(),
}
}
pub fn with_momentum(mut self, momentum: f32) -> Self {
self.momentum = momentum;
self
}
}
impl Optimizer for Stiefel {
fn set_lr(&mut self, lr: f32) {
self.lr = lr;
}
fn step(&mut self, name: &str, shape: &[usize], param: &mut [f32], grad: &[f32]) {
debug_assert_eq!(param.len(), grad.len());
let lr = self.lr;
let mu = self.momentum;
let is_stiefel = shape.len() == 2 && shape[0] <= shape[1];
if !is_stiefel {
if mu == 0.0 {
for i in 0..param.len() {
param[i] -= lr * grad[i];
}
} else {
let m = zeros_entry(&mut self.m, name, param.len());
for i in 0..param.len() {
m[i] = mu * m[i] + grad[i];
param[i] -= lr * m[i];
}
}
return;
}
let (rows, cols) = (shape[0], shape[1]);
debug_assert_eq!(rows * cols, param.len());
let a = mat_mul_bt(param, grad, rows, cols); let mut symm = vec![0.0f32; rows * rows];
for i in 0..rows {
for j in 0..rows {
symm[i * rows + j] = 0.5 * (a[i * rows + j] + a[j * rows + i]);
}
}
let wsym = mat_mul(&symm, param, rows, rows, cols);
let mut g_riem = vec![0.0f32; rows * cols];
for i in 0..rows * cols {
g_riem[i] = grad[i] - wsym[i];
}
let mut y = vec![0.0f32; rows * cols];
if mu == 0.0 {
for i in 0..rows * cols {
y[i] = param[i] - lr * g_riem[i];
}
} else {
let m = zeros_entry(&mut self.m, name, rows * cols);
for i in 0..rows * cols {
m[i] = mu * m[i] + g_riem[i];
y[i] = param[i] - lr * m[i];
}
}
qf_rows_in_place(&mut y, rows, cols);
param.copy_from_slice(&y);
}
}
fn mat_mul(a: &[f32], b: &[f32], m: usize, k: usize, n: usize) -> Vec<f32> {
debug_assert_eq!(a.len(), m * k);
debug_assert_eq!(b.len(), k * n);
let mut c = vec![0.0f32; m * n];
for i in 0..m {
for j in 0..n {
let mut s = 0.0f32;
for p in 0..k {
s += a[i * k + p] * b[p * n + j];
}
c[i * n + j] = s;
}
}
c
}
fn mat_mul_bt(a: &[f32], b: &[f32], m: usize, n: usize) -> Vec<f32> {
debug_assert_eq!(a.len(), m * n);
debug_assert_eq!(b.len(), m * n);
let mut c = vec![0.0f32; m * m];
for i in 0..m {
for j in 0..m {
let mut s = 0.0f32;
for p in 0..n {
s += a[i * n + p] * b[j * n + p];
}
c[i * m + j] = s;
}
}
c
}
fn qf_rows_in_place(y: &mut [f32], rows: usize, cols: usize) {
debug_assert_eq!(y.len(), rows * cols);
for i in 0..rows {
for j in 0..i {
let mut dot = 0.0f32;
for c in 0..cols {
dot += y[i * cols + c] * y[j * cols + c];
}
for c in 0..cols {
y[i * cols + c] -= dot * y[j * cols + c];
}
}
let mut nrm = 0.0f64;
for c in 0..cols {
let v = y[i * cols + c] as f64;
nrm += v * v;
}
let nrm = (nrm.sqrt() as f32).max(1e-12);
let inv = 1.0 / nrm;
for c in 0..cols {
y[i * cols + c] *= inv;
}
}
}