native_neural_network_std 0.2.1

Ergonomic std wrapper for the `native_neural_network` crate (no_std) — std-friendly re-exports and utilities.
Documentation
use native_neural_network_std::std::activations_std::ActivationKind;
use native_neural_network_std::std::engine_std::forward_plan;
use native_neural_network_std::std::layers_std::{DenseLayerDesc, LayerPlanStd, LayerSpec};
use native_neural_network_std::std::model_format_std::{decode_model, encode_model, encoded_size};
use native_neural_network_std::std::model_std::ModelStd;
use native_neural_network_std::std::visualization_std::get_network_from_rnn;

fn lcg_u32(mut s: u32) -> impl Iterator<Item = u8> {
    std::iter::from_fn(move || {
        s = s.wrapping_mul(1664525).wrapping_add(1013904223);
        Some((s & 0xff) as u8)
    })
}

#[test]
fn cpu_backend_abstraction_real() {
    let previous = native_neural_network_std::std::engine_std::get_compute_backend();
    native_neural_network_std::std::engine_std::set_compute_backend(
        native_neural_network_std::std::engine_std::ComputeBackend::Cpu,
    );
    assert_eq!(
        native_neural_network_std::std::engine_std::get_compute_backend(),
        native_neural_network_std::std::engine_std::ComputeBackend::Cpu
    );
    let layers = vec![
        native_neural_network_std::std::layers_std::LayerSpec::Dense(
            native_neural_network_std::std::layers_std::DenseLayerDesc {
                input_size: 2,
                output_size: 1,
                weight_offset: 0,
                bias_offset: 0,
                activation:
                    native_neural_network_std::std::activations_std::ActivationKind::Identity,
            },
        ),
    ];
    let plan = native_neural_network_std::std::layers_std::LayerPlanStd::new(
        layers,
        vec![2.0f32, 3.0f32],
        vec![1.0f32],
    );
    let input = vec![1.0f32, 2.0f32];
    let mut output = vec![0.0f32; 1];
    let mut scratch =
        vec![
            0.0f32;
            native_neural_network_std::std::engine_std::required_batch_scratch_len(&plan, 1)
                .unwrap_or(0)
        ];
    native_neural_network_std::std::engine_std::forward_plan_big_kernel(
        &plan,
        &input,
        &mut output,
        1,
        &mut scratch,
    )
    .expect("cpu forward");
    assert!((output[0] - 9.0).abs() < 1e-6);
    native_neural_network_std::std::engine_std::set_compute_backend(previous);
}

#[test]
fn fuzz_decode_model_no_panic() {
    let mut gen = lcg_u32(0x1234_5678);
    for len in 0..200 {
        let mut buf = Vec::with_capacity(len);
        for _ in 0..len {
            buf.push(gen.next().unwrap());
        }
        let mut layers_out = vec![
            LayerSpec::Dense(DenseLayerDesc {
                input_size: 0,
                output_size: 0,
                weight_offset: 0,
                bias_offset: 0,
                activation: ActivationKind::Identity
            });
            1
        ];
        let mut weights_out = vec![0f32; 4];
        let mut biases_out = vec![0f32; 2];
        let res = decode_model(&buf, &mut layers_out, &mut weights_out, &mut biases_out);
        assert!(res.is_ok() || res.is_err());
    }
}

#[test]
fn decode_empty_zero_sizes_roundtrip() {
    let size = encoded_size(0usize, 0usize, 0usize).expect("size for zero counts");
    let mut buf = vec![0u8; size];
    let used = encode_model(&[], &[], &[], &mut buf).expect("encode empty");
    assert!(used <= buf.len());
    let mut out_layers = vec![
        LayerSpec::Dense(DenseLayerDesc {
            input_size: 0,
            output_size: 0,
            weight_offset: 0,
            bias_offset: 0,
            activation: ActivationKind::Identity
        });
        1
    ];
    let mut weights_out = vec![0f32; 0];
    let mut biases_out = vec![0f32; 0];
    let res = decode_model(
        &buf[..used],
        &mut out_layers,
        &mut weights_out,
        &mut biases_out,
    );
    assert!(res.is_err());
}

#[test]
fn nan_and_inf_propagate_safely() {
    let layers = vec![LayerSpec::Dense(DenseLayerDesc {
        input_size: 2,
        output_size: 1,
        weight_offset: 0,
        bias_offset: 0,
        activation: ActivationKind::Identity,
    })];
    let weights = vec![f32::NAN, f32::INFINITY];
    let biases = vec![0.0f32];
    let size = encoded_size(layers.len(), weights.len(), biases.len()).unwrap();
    let mut buf = vec![0u8; size];
    let used = encode_model(&layers, &weights, &biases, &mut buf).expect("encode");
    buf.truncate(used);
    let m = ModelStd::from_bytes(&buf).expect("from_bytes");
    let out = m.run_single(&[1.0f32, 1.0f32]).expect("run");
    assert!(!out[0].is_finite());
}

#[test]
fn forward_plan_matches_model_run() {
    let layers = vec![LayerSpec::Dense(DenseLayerDesc {
        input_size: 2,
        output_size: 1,
        weight_offset: 0,
        bias_offset: 0,
        activation: ActivationKind::Identity,
    })];
    let weights = vec![1.5f32, -0.5f32];
    let biases = vec![0.25f32];
    let size = encoded_size(layers.len(), weights.len(), biases.len()).unwrap();
    let mut buf = vec![0u8; size];
    let used = encode_model(&layers, &weights, &biases, &mut buf).unwrap();
    buf.truncate(used);
    let m = ModelStd::from_bytes(&buf).unwrap();
    let single = m.run_single(&[2.0f32, 3.0f32]).unwrap();
    let plan = LayerPlanStd::new(m.layers.clone(), m.weights.clone(), m.biases.clone());
    let mut out = vec![0f32; m.output_size];
    let mut scratch = vec![0f32; m.infer_scratch_len.max(16)];
    forward_plan(&plan, &[2.0f32, 3.0f32], &mut out, &mut scratch).unwrap();
    assert_eq!(single, out);
}

#[test]
fn ffi_create_null_pointer_returns_null() {
    let h = native_neural_network_std::std::ffi_api_std::rnn_ffi_model_create_from_bytes(&[]);
    assert_eq!(h, 0usize);
}

#[test]
fn ffi_destroy_null_is_noop() {
    native_neural_network_std::std::ffi_api_std::rnn_ffi_model_destroy(0usize);
    assert!(true);
}

#[test]
fn parse_rnn_fuzz_no_panic() {
    let mut gen = lcg_u32(0xfeed_f00d);
    for len in 0..150 {
        let mut buf = Vec::with_capacity(len);
        for _ in 0..len {
            buf.push(gen.next().unwrap());
        }
        let res = get_network_from_rnn(&buf);
        assert!(res.is_ok() || res.is_err());
    }
}

#[test]
fn batch_large_sanity() {
    let layers = vec![LayerSpec::Dense(DenseLayerDesc {
        input_size: 2,
        output_size: 1,
        weight_offset: 0,
        bias_offset: 0,
        activation: ActivationKind::Identity,
    })];
    let weights = vec![1.0f32, 1.0f32];
    let biases = vec![0.0f32];
    let size = encoded_size(layers.len(), weights.len(), biases.len()).unwrap();
    let mut buf = vec![0u8; size];
    let used = encode_model(&layers, &weights, &biases, &mut buf).unwrap();
    buf.truncate(used);
    let mut input = Vec::with_capacity(2000);
    for i in 0..1000 {
        input.push(i as f32);
        input.push((i + 1) as f32);
    }
    let m = ModelStd::from_bytes(&buf).expect("from_bytes");
    let out = m.run_batch(&input, 1000).expect("batch");
    assert_eq!(out.len(), 1000 * m.output_size);
}

#[test]
fn concurrent_init_no_panic() {
    native_neural_network_std::shutdown();
    let mut th = Vec::new();
    for _ in 0..32 {
        th.push(std::thread::spawn(|| {
            let res = native_neural_network_std::init();
            match res {
                Ok(()) => {}
                Err(_) => {}
            }
        }));
    }
    for t in th {
        let r = t.join();
        assert!(r.is_ok());
    }
    assert!(native_neural_network_std::is_initialized());
    native_neural_network_std::shutdown();
}