native_neural_network 0.3.1

Lib no_std Rust for native neural network (.rnn)
Documentation
use super::qtypes::QuantError;

pub fn quantize_i8_symmetric_f32(input: &[f32], output: &mut [i8]) -> Result<f32, QuantError> {
    if input.is_empty() {
        return Err(QuantError::Empty);
    }
    if output.len() < input.len() {
        return Err(QuantError::ShapeMismatch);
    }

    let mut scale_from_contract = 0.0f32;
    if crate::engine::try_invoke_gpu_quantize_i8_f32(input, output, &mut scale_from_contract) {
        if !scale_from_contract.is_finite() || scale_from_contract <= 0.0 {
            return Err(QuantError::InvalidScale);
        }
        return Ok(scale_from_contract);
    }

    let mut max_abs = 0.0f32;
    for &v in input {
        let a = v.abs();
        if a > max_abs {
            max_abs = a;
        }
    }

    let scale = if max_abs <= 0.0 { 1.0 } else { max_abs / 127.0 };
    if !scale.is_finite() || scale <= 0.0 {
        return Err(QuantError::InvalidScale);
    }

    for i in 0..input.len() {
        let v = crate::math::roundf(input[i] / scale);
        output[i] = v.clamp(-127.0, 127.0) as i8;
    }

    Ok(scale)
}

pub fn quantize_i8_symmetric(input: &[f32], output: &mut [i8]) -> Result<f32, QuantError> {
    quantize_i8_symmetric_f32(input, output)
}

pub fn dequantize_i8_symmetric_f32(
    input: &[i8],
    output: &mut [f32],
    scale: f32,
) -> Result<(), QuantError> {
    if input.is_empty() {
        return Err(QuantError::Empty);
    }
    if output.len() < input.len() {
        return Err(QuantError::ShapeMismatch);
    }
    if !scale.is_finite() || scale <= 0.0 {
        return Err(QuantError::InvalidScale);
    }

    if crate::engine::try_invoke_gpu_dequantize_i8_f32(input, output, scale) {
        return Ok(());
    }

    for i in 0..input.len() {
        output[i] = input[i] as f32 * scale;
    }
    Ok(())
}

pub fn dequantize_i8_symmetric(
    input: &[i8],
    output: &mut [f32],
    scale: f32,
) -> Result<(), QuantError> {
    dequantize_i8_symmetric_f32(input, output, scale)
}

pub fn dequantize_i8_symmetric_f64(
    input: &[i8],
    output: &mut [f64],
    scale: f64,
) -> Result<(), QuantError> {
    if input.is_empty() {
        return Err(QuantError::Empty);
    }
    if output.len() < input.len() {
        return Err(QuantError::ShapeMismatch);
    }
    if !scale.is_finite() || scale <= 0.0 {
        return Err(QuantError::InvalidScale);
    }

    if crate::engine::try_invoke_gpu_dequantize_i8_f64(input, output, scale) {
        return Ok(());
    }

    for i in 0..input.len() {
        output[i] = input[i] as f64 * scale;
    }
    Ok(())
}

pub fn quantize_i8_symmetric_f64(input: &[f64], output: &mut [i8]) -> Result<f64, QuantError> {
    if input.is_empty() {
        return Err(QuantError::Empty);
    }
    if output.len() < input.len() {
        return Err(QuantError::ShapeMismatch);
    }

    let mut scale_from_contract = 0.0f64;
    if crate::engine::try_invoke_gpu_quantize_i8_f64(input, output, &mut scale_from_contract) {
        if !scale_from_contract.is_finite() || scale_from_contract <= 0.0 {
            return Err(QuantError::InvalidScale);
        }
        return Ok(scale_from_contract);
    }

    let mut max_abs = 0.0f64;
    for &v in input {
        let a = v.abs();
        if a > max_abs {
            max_abs = a;
        }
    }

    let scale = if max_abs <= 0.0 { 1.0 } else { max_abs / 127.0 };
    if !scale.is_finite() || scale <= 0.0 {
        return Err(QuantError::InvalidScale);
    }

    for i in 0..input.len() {
        let v = crate::math::roundd(input[i] / scale);
        output[i] = v.clamp(-127.0, 127.0) as i8;
    }

    Ok(scale)
}