use strided_nn::{scratch_len, softmax_axis0_f32, softmax_axis0_f64};
use tenferro_tensor::{DType, Tensor, TensorRead};
use crate::activation::fill_pooled;
use crate::buffer_pool::BufferPool;
use crate::materialize_tensor_read;
use threadteam::Exec;
pub(crate) fn softmax_read_with_pool(
buffers: &mut BufferPool,
input: TensorRead<'_>,
axis: usize,
mut exec: Exec<'_>,
) -> crate::Result<Option<Tensor>> {
let shape = input.shape().to_vec();
if axis != 0 || shape.is_empty() || shape[0] == 0 {
return Ok(None);
}
let workers = exec.budget().get();
match input.dtype() {
DType::F32 => {
if let Ok(data) = input.as_slice::<f32>() {
return softmax_f32(workers, buffers, &shape, data, exec).map(Some);
}
let tensor = materialize_tensor_read(buffers, "softmax", input, exec.reborrow())?;
softmax_f32(workers, buffers, &shape, tensor.as_slice::<f32>()?, exec).map(Some)
}
DType::F64 => {
if let Ok(data) = input.as_slice::<f64>() {
return softmax_f64(workers, buffers, &shape, data, exec).map(Some);
}
let tensor = materialize_tensor_read(buffers, "softmax", input, exec.reborrow())?;
softmax_f64(workers, buffers, &shape, tensor.as_slice::<f64>()?, exec).map(Some)
}
_ => Ok(None),
}
}
fn softmax_f32(
workers: usize,
buffers: &BufferPool,
shape: &[usize],
data: &[f32],
exec: Exec<'_>,
) -> crate::Result<Tensor> {
fill_pooled(buffers, shape, |out| {
let scratch_len = if data.is_empty() {
0
} else {
scratch_len::<f32>(shape[0], data.len(), workers)
};
let mut scratch = vec![0.0f32; scratch_len];
softmax_axis0_f32(shape[0], data, out, &mut scratch, exec)
.expect("validated softmax contract");
})
}
fn softmax_f64(
workers: usize,
buffers: &BufferPool,
shape: &[usize],
data: &[f64],
exec: Exec<'_>,
) -> crate::Result<Tensor> {
fill_pooled(buffers, shape, |out| {
let scratch_len = if data.is_empty() {
0
} else {
scratch_len::<f64>(shape[0], data.len(), workers)
};
let mut scratch = vec![0.0f64; scratch_len];
softmax_axis0_f64(shape[0], data, out, &mut scratch, exec)
.expect("validated softmax contract");
})
}
#[cfg(test)]
mod tests;