use native_neural_network_std::std::activations_std::ActivationKind;
use native_neural_network_std::std::layers_std::{DenseLayerDesc, LayerSpec};
use native_neural_network_std::std::model_format_std::{
decode_model_v1, encode_model_v1, encoded_size_v1, DecodedCounts,
};
use native_neural_network_std::std::model_std::ModelStd;
use std::sync::Arc;
use std::thread;
fn lcg(seed: u32) -> impl Iterator<Item = f32> {
let mut s = seed as u64;
std::iter::from_fn(move || {
s = (s.wrapping_mul(1664525).wrapping_add(1013904223)) & 0xffff_ffff;
Some(((s as u32) as f32) / (u32::MAX as f32))
})
}
fn make_bytes_for_simple_model() -> Vec<u8> {
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, 2.0f32];
let biases = vec![0.5f32];
let size = encoded_size_v1(layers.len(), weights.len(), biases.len()).expect("encoded size");
let mut buf = vec![0u8; size];
let used = encode_model_v1(&layers, &weights, &biases, &mut buf).expect("encode");
buf.truncate(used);
buf
}
#[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
);
native_neural_network_std::std::engine_std::set_compute_backend(previous);
}
#[test]
fn load_and_run_minimal_model() {
let bytes = make_bytes_for_simple_model();
let model = ModelStd::from_bytes(&bytes).expect("from_bytes");
let out = model.run_single(&[1.0f32, 1.0f32]).expect("run_single");
assert_eq!(out.len(), 1);
assert!(
(out[0] - 3.5f32).abs() < 1e-6,
"expected 3.5, got {}",
out[0]
);
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; 2];
let mut biases_out = vec![0f32; 1];
let counts: DecodedCounts =
decode_model_v1(&bytes, &mut layers_out, &mut weights_out, &mut biases_out)
.expect("decode");
assert_eq!(counts.layers, 1);
assert_eq!(weights_out, vec![1.0f32, 2.0f32]);
assert_eq!(biases_out, vec![0.5f32]);
}
#[test]
fn decode_truncated_bytes_returns_err_in_load_model() {
let mut bytes = make_bytes_for_simple_model();
if bytes.len() > 0 {
bytes.truncate(bytes.len() - 1);
}
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; 2];
let mut biases_out = vec![0f32; 1];
assert!(decode_model_v1(&bytes, &mut layers_out, &mut weights_out, &mut biases_out).is_err());
}
#[test]
fn repeated_runs_are_deterministic_and_batch_like() {
let bytes = make_bytes_for_simple_model();
let model = ModelStd::from_bytes(&bytes).expect("from_bytes");
let input = [1.0f32, 1.0f32];
let first = model.run_single(&input).expect("run_single");
for _ in 0..20 {
let cur = model.run_single(&input).expect("run_single");
assert_eq!(cur, first);
}
}
#[test]
fn heavy_concurrent_loads_and_runs() {
let bytes = Arc::new(make_bytes_for_simple_model());
let mut handles = Vec::new();
for _ in 0..8 {
let b = Arc::clone(&bytes);
handles.push(thread::spawn(move || {
for _ in 0..100 {
let m = ModelStd::from_bytes(&b).expect("from_bytes");
let out = m.run_single(&[2.0f32, 3.0f32]).expect("inference");
assert!(
(out[0] - 8.5f32).abs() < 1e-6,
"unexpected output {}",
out[0]
);
}
}));
}
for h in handles {
h.join().unwrap();
}
}
#[test]
fn multi_layer_serialization_roundtrip_and_infer() {
let layers = vec![
LayerSpec::Dense(DenseLayerDesc {
input_size: 2,
output_size: 3,
weight_offset: 0,
bias_offset: 0,
activation: ActivationKind::Identity,
}),
LayerSpec::Dense(DenseLayerDesc {
input_size: 3,
output_size: 1,
weight_offset: 0,
bias_offset: 0,
activation: ActivationKind::Identity,
}),
];
let weights = vec![1.0f32, 0.0, 0.0, 1.0, 0.5, 0.5, 1.0, 1.0, 1.0];
let biases = vec![0.0f32, 0.0, 0.0, 0.5f32];
let size = encoded_size_v1(layers.len(), weights.len(), biases.len()).expect("encoded size");
let mut buf = vec![0u8; size];
let used = encode_model_v1(&layers, &weights, &biases, &mut buf).expect("encode");
buf.truncate(used);
let model = ModelStd::from_bytes(&buf).expect("from_bytes");
let out = model.run_single(&[1.0f32, 2.0f32]).expect("run");
let mut layers_out = vec![
LayerSpec::Dense(DenseLayerDesc {
input_size: 0,
output_size: 0,
weight_offset: 0,
bias_offset: 0,
activation: ActivationKind::Identity
});
layers.len()
];
let mut weights_out = vec![0f32; weights.len()];
let mut biases_out = vec![0f32; biases.len()];
let counts =
decode_model_v1(&buf, &mut layers_out, &mut weights_out, &mut biases_out).expect("decode");
assert_eq!(counts.layers, layers.len());
assert_eq!(weights_out, weights);
assert_eq!(biases_out, biases);
let infer_scratch_len =
native_neural_network_std::std::rnn_std::rnn_required_infer_scratch_from_specs_std(
&layers_out,
)
.unwrap_or(0);
let reconstructed = ModelStd {
layers: layers_out.clone(),
weights: weights_out.clone(),
biases: biases_out.clone(),
input_size: 2,
output_size: 1,
infer_scratch_len,
};
let out2 = reconstructed.run_single(&[1.0f32, 2.0f32]).expect("run2");
assert_eq!(out, out2);
}
#[test]
fn randomized_inputs_produce_stable_results() {
let bytes = make_bytes_for_simple_model();
let model = ModelStd::from_bytes(&bytes).expect("from_bytes");
let mut rng = lcg(0xdead_beef);
let mut samples = Vec::new();
for _ in 0..50 {
let a = (rng.next().unwrap() * 10.0) - 5.0;
let b = (rng.next().unwrap() * 10.0) - 5.0;
samples.push([a, b]);
}
let first: Vec<Vec<f32>> = samples
.iter()
.map(|s| model.run_single(s).unwrap())
.collect();
let second: Vec<Vec<f32>> = samples
.iter()
.map(|s| model.run_single(s).unwrap())
.collect();
assert_eq!(first, second);
}
#[test]
fn decode_into_too_small_buffers_returns_err() {
let bytes = make_bytes_for_simple_model();
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; 1];
let mut biases_out = vec![0f32; 0];
assert!(decode_model_v1(&bytes, &mut layers_out, &mut weights_out, &mut biases_out).is_err());
}
#[test]
fn concurrent_loads_no_conflict() {
let bytes = Arc::new(make_bytes_for_simple_model());
let mut handles = Vec::new();
for _ in 0..4 {
let b = Arc::clone(&bytes);
handles.push(thread::spawn(move || {
let m = ModelStd::from_bytes(&b).expect("from_bytes");
m.run_single(&[2.0f32, 3.0f32]).expect("inference");
}));
}
for h in handles {
h.join().unwrap();
}
}