#![no_std]
#![forbid(unsafe_code)]
use embedded_exp::exp_q15;
const ONE_Q15: i32 = 32768;
#[inline]
pub fn sigmoid_q15(x: i16) -> i16 {
if x == 0 {
return 16384;
}
if x > 0 {
sigmoid_positive(x)
} else {
let neg_x = if x == i16::MIN {
i16::MAX
} else {
-x
};
let s_neg_x = sigmoid_positive(neg_x) as i32;
let result = ONE_Q15 - s_neg_x;
result.max(1).min(32767) as i16
}
}
#[inline]
fn sigmoid_positive(x: i16) -> i16 {
debug_assert!(x > 0);
let neg_x = -x;
let exp_neg_x = exp_q15(neg_x) as i32;
let denom = ONE_Q15 + exp_neg_x;
let result = (ONE_Q15 * ONE_Q15) / denom;
result.max(1).min(32767) as i16
}
#[inline]
pub fn sigmoid_slice_q15(data: &mut [i16]) {
for val in data.iter_mut() {
*val = sigmoid_q15(*val);
}
}
#[cfg(test)]
mod tests {
use super::*;
const TOLERANCE: i32 = 30;
#[test]
fn test_sigmoid_zero_is_half() {
assert_eq!(sigmoid_q15(0), 16384);
}
#[test]
fn test_sigmoid_negative_one() {
let res = sigmoid_q15(-32768);
assert!(
(res as i32 - 8808).abs() < TOLERANCE,
"sigmoid(-1.0): attendu ≈8808, reçu {}",
res
);
}
#[test]
fn test_sigmoid_positive_one() {
let res = sigmoid_q15(i16::MAX);
assert!(
(res as i32 - 23960).abs() < TOLERANCE,
"sigmoid(≈1.0): attendu ≈23960, reçu {}",
res
);
}
#[test]
fn test_sigmoid_negative_half() {
let res = sigmoid_q15(-16384);
assert!(
(res as i32 - 12372).abs() < TOLERANCE,
"sigmoid(-0.5): attendu ≈12372, reçu {}",
res
);
}
#[test]
fn test_sigmoid_positive_half() {
let res = sigmoid_q15(16384);
assert!(
(res as i32 - 20396).abs() < TOLERANCE,
"sigmoid(0.5): attendu ≈20396, reçu {}",
res
);
}
#[test]
fn test_sigmoid_symmetry() {
for x in [-16384i16, -8192, -4096, -1] {
let s_pos = sigmoid_q15(-x) as i32;
let s_neg = sigmoid_q15(x) as i32;
let sum = s_pos + s_neg;
assert!(
(sum - 32768).abs() < TOLERANCE * 2,
"symétrie brisée pour x={}: sigmoid({})={} + sigmoid({})={} = {}",
x, -x, s_pos, x, s_neg, sum
);
}
}
#[test]
fn test_sigmoid_always_positive() {
for x in [i16::MIN, -16384, -1, 0, 1, 16384, i16::MAX] {
assert!(sigmoid_q15(x) > 0, "sigmoid({}) doit être > 0", x);
}
}
#[test]
fn test_sigmoid_always_below_one() {
for x in [i16::MIN, -16384, -1, 0, 1, 16384, i16::MAX] {
assert!(
(sigmoid_q15(x) as i32) < 32768,
"sigmoid({}) doit être < 32768",
x
);
}
}
#[test]
fn test_sigmoid_monotone() {
let a = sigmoid_q15(-32768); let b = sigmoid_q15(-16384); let c = sigmoid_q15(0); let d = sigmoid_q15(16384); let e = sigmoid_q15(32767); assert!(a < b, "sigmoid(-1.0)={} < sigmoid(-0.5)={}", a, b);
assert!(b < c, "sigmoid(-0.5)={} < sigmoid(0.0)={}", b, c);
assert!(c < d, "sigmoid(0.0)={} < sigmoid(0.5)={}", c, d);
assert!(d < e, "sigmoid(0.5)={} < sigmoid(1.0)={}", d, e);
}
#[test]
fn test_sigmoid_min_no_panic() {
let res = sigmoid_q15(i16::MIN);
assert!(res > 0, "sigmoid(i16::MIN)={} doit être > 0", res);
}
#[test]
fn test_sigmoid_output_in_range() {
for x in [i16::MIN, -32000, -16384, -1, 0, 1, 16384, 32000, i16::MAX] {
let res = sigmoid_q15(x);
assert!(
res >= 1,
"sigmoid({})={} doit être >= 1",
x, res
);
}
}
#[test]
fn test_slice_empty() {
let mut empty: [i16; 0] = [];
sigmoid_slice_q15(&mut empty);
}
#[test]
fn test_slice_zero_gives_half() {
let mut data = [0i16];
sigmoid_slice_q15(&mut data);
assert_eq!(data[0], 16384);
}
#[test]
fn test_slice_mixed() {
let mut data = [-32768i16, 0, 32767];
sigmoid_slice_q15(&mut data);
assert!(data[0] > 0 && data[0] < 16384); assert_eq!(data[1], 16384); assert!(data[2] > 16384); }
#[test]
fn test_slice_all_positive_above_half() {
let mut data = [1i16, 8192, 16384, 32767];
sigmoid_slice_q15(&mut data);
for val in data {
assert!(val > 16384, "sigmoid positif doit être > 16384, reçu {}", val);
}
}
#[test]
fn test_slice_all_negative_below_half() {
let mut data = [-1i16, -8192, -16384, -32768];
sigmoid_slice_q15(&mut data);
for val in data {
assert!(val < 16384, "sigmoid négatif doit être < 16384, reçu {}", val);
}
}
#[test]
fn test_slice_idempotent_after_double_pass() {
let mut data = [-32768i16, -16384, 0, 16384, 32767];
sigmoid_slice_q15(&mut data);
sigmoid_slice_q15(&mut data);
for val in data {
assert!(val >= 1, "hors plage après double passe: {}", val);
}
}
#[test]
fn test_slice_boundary_values() {
let mut data = [i16::MIN, i16::MAX];
sigmoid_slice_q15(&mut data);
assert!(data[0] > 0 && data[0] < 16384);
assert!(data[1] > 16384);
}
}