use crate::dispatch_simd;
use crate::math::activations::ActivationPrecision;
use crate::math::activations::activation_precision;
use crate::math::activations::scalar_minimax_sigmoid;
use crate::math::activations::sigmoid::high_fidelity::scalar_sigmoid_poly;
use crate::math::activations::tanh::high_fidelity::scalar_tanh_poly;
use crate::math::activations::tanh::scalar_pade_tanh;
use core::arch::x86_64::*;
use super::layer_dyn::LstmLayerDyn;
impl LstmLayerDyn {
#[inline(always)]
pub fn process_sample_scalar(&mut self, input: &[f32]) {
let i = self.input_size;
let h = self.hidden_size;
let ih = i + h;
self.state[..i].copy_from_slice(&input[..i]);
for k in 0..4 {
let target_gate_offset = k * h;
let w_start = k * ih * h;
for hi in 0..h {
let mut sum = 0.0;
for j in 0..ih {
let s = self.state[j];
let w = self.input_hidden_weights[w_start + j * h + hi];
sum += w * s;
}
self.gates[target_gate_offset + hi] = sum + self.bias[target_gate_offset + hi];
}
}
let is_hf = activation_precision() == ActivationPrecision::Standard;
for j in 0..h {
let gf = self.gates[j + h];
let gi = self.gates[j];
let gg = self.gates[j + 2 * h];
let go = self.gates[j + 3 * h];
let cs = self.cell_state[j];
let cs_err = self.cell_error[j];
if is_hf {
let f = scalar_sigmoid_poly(gf);
let in_gate = scalar_sigmoid_poly(gi);
let g = scalar_tanh_poly(gg);
let o = scalar_sigmoid_poly(go);
let f_cs = f * cs;
let f_err = f * cs_err;
let i_g = in_gate * g;
let y = i_g - f_err;
let new_cs = f_cs + y;
let new_cs_err = (new_cs - f_cs) - y;
self.cell_state[j] = new_cs;
self.cell_error[j] = new_cs_err;
self.state[i + j] = o * scalar_tanh_poly(new_cs);
} else {
let f = scalar_minimax_sigmoid(gf);
let in_gate = scalar_minimax_sigmoid(gi);
let g = scalar_pade_tanh(gg);
let o = scalar_minimax_sigmoid(go);
let f_cs = f * cs;
let f_err = f * cs_err;
let i_g = in_gate * g;
let y = i_g - f_err;
let new_cs = f_cs + y;
let new_cs_err = (new_cs - f_cs) - y;
self.cell_state[j] = new_cs;
self.cell_error[j] = new_cs_err;
self.state[i + j] = o * scalar_pade_tanh(new_cs);
}
}
}
}
impl LstmLayerDyn {
#[target_feature(enable = "avx2,fma,f16c")]
pub unsafe fn process_sample_avx2(&mut self, input: &[f32]) {
let i = self.input_size;
let h = self.hidden_size;
let ih = i + h;
let stride = ih * h;
self.state[..i].copy_from_slice(&input[..i]);
_mm_prefetch::<_MM_HINT_T0>(self.state.as_ptr().cast::<i8>());
unsafe {
crate::math::gemm::gemv_4gate_avx2(
&self.state[..ih],
&self.input_hidden_weights[0..stride],
&self.input_hidden_weights[stride..2 * stride],
&self.input_hidden_weights[2 * stride..3 * stride],
&self.input_hidden_weights[3 * stride..4 * stride],
&self.bias[..4 * h],
&mut self.gates[..4 * h],
true,
);
}
let (gates_slice, _rest) = &mut self.gates.split_at_mut(4 * h);
let (cell_slice, _) = &mut self.cell_state.split_at_mut(h);
let (cell_err_slice, _) = &mut self.cell_error.split_at_mut(h);
let hidden_slice = &mut self.state[i..];
unsafe {
crate::math::lstm::fused_lstm_gates_dyn_avx2(
gates_slice,
cell_slice,
cell_err_slice,
hidden_slice,
h,
);
}
}
}
impl LstmLayerDyn {
#[target_feature(enable = "avx512f,avx512vl")]
pub unsafe fn process_sample_avx512(&mut self, input: &[f32]) {
let i = self.input_size;
let h = self.hidden_size;
let ih = i + h;
let stride = ih * h;
self.state[..i].copy_from_slice(&input[..i]);
_mm_prefetch::<_MM_HINT_T0>(self.state.as_ptr().cast::<i8>());
unsafe {
crate::math::gemm::gemv_4gate_avx512(
&self.state[..ih],
&self.input_hidden_weights[0..stride],
&self.input_hidden_weights[stride..2 * stride],
&self.input_hidden_weights[2 * stride..3 * stride],
&self.input_hidden_weights[3 * stride..4 * stride],
&self.bias[..4 * h],
&mut self.gates[..4 * h],
true,
);
}
let (gates_slice, _rest) = &mut self.gates.split_at_mut(4 * h);
let (cell_slice, _) = &mut self.cell_state.split_at_mut(h);
let (cell_err_slice, _) = &mut self.cell_error.split_at_mut(h);
let hidden_slice = &mut self.state[i..];
unsafe {
crate::math::lstm::fused_lstm_gates_dyn_avx512(
gates_slice,
cell_slice,
cell_err_slice,
hidden_slice,
h,
);
}
}
}
impl LstmLayerDyn {
#[inline]
pub fn process(&mut self, input: &[f32]) {
unsafe {
dispatch_simd!(
@self,
process_sample_avx512,
process_sample_avx512,
process_sample_avx2,
input
);
}
}
}