pub mod native_rmd_rnn {
pub use native_neural_network::rnn_api::*;
}
#[derive(Debug)]
pub enum RnnStdError {
Truncated,
BadMagic,
BadHeader,
BadBounds,
ScratchFull,
BlobNotFound,
}
#[derive(Debug, Clone)]
pub struct BlobMetaStd {
pub name: String,
pub dtype: u8,
pub dims: Vec<u32>,
pub offset: usize,
pub length: usize,
}
#[derive(Debug)]
pub struct RnnStd {
pub bytes: Vec<u8>,
pub blobs: Vec<BlobMetaStd>,
layer_meta: Option<Vec<u8>>,
weights: Option<Vec<u8>>,
biases: Option<Vec<u8>>,
benchmark: Option<Vec<u8>>,
}
pub type BlobMeta = BlobMetaStd;
pub type RnnHandle = RnnStd;
impl RnnStd {
pub fn from_bytes(bytes: Vec<u8>) -> Result<Self, RnnStdError> {
let view = native_neural_network::rnn_api::read_rnn(&bytes, None).map_err(map_rnn_error)?;
let layer_meta = view.layer_meta.map(|v| v.to_vec());
let weights = view.weights.map(|v| v.to_vec());
let biases = view.biases.map(|v| v.to_vec());
let benchmark = view.benchmark.map(|v| v.to_vec());
let mut blobs = Vec::new();
if let Some(v) = view.layer_meta {
blobs.push(BlobMetaStd {
name: "layer.meta".to_string(),
dtype: view.scan.header.map(|h| h.dtype).unwrap_or(0),
dims: vec![u32::try_from(v.len()).unwrap_or(u32::MAX)],
offset: 0,
length: v.len(),
});
}
if let Some(v) = view.weights {
blobs.push(BlobMetaStd {
name: "weights".to_string(),
dtype: view.scan.header.map(|h| h.dtype).unwrap_or(0),
dims: vec![u32::try_from(v.len()).unwrap_or(u32::MAX)],
offset: 0,
length: v.len(),
});
}
if let Some(v) = view.biases {
blobs.push(BlobMetaStd {
name: "biases".to_string(),
dtype: view.scan.header.map(|h| h.dtype).unwrap_or(0),
dims: vec![u32::try_from(v.len()).unwrap_or(u32::MAX)],
offset: 0,
length: v.len(),
});
}
if let Some(v) = view.benchmark {
blobs.push(BlobMetaStd {
name: "benchmark".to_string(),
dtype: 0,
dims: vec![u32::try_from(v.len()).unwrap_or(u32::MAX)],
offset: 0,
length: v.len(),
});
}
Ok(Self {
bytes,
blobs,
layer_meta,
weights,
biases,
benchmark,
})
}
pub fn blob_count(&self) -> usize {
self.blobs.len()
}
pub fn blob_names(&self) -> Vec<String> {
self.blobs.iter().map(|b| b.name.clone()).collect()
}
pub fn get_blob_bytes(&self, name: &str) -> Option<&[u8]> {
match name {
"layer.meta" => self.layer_meta.as_deref(),
"weights" => self.weights.as_deref(),
"biases" => self.biases.as_deref(),
"benchmark" => self.benchmark.as_deref(),
_ => None,
}
}
}
impl core::fmt::Display for RnnStdError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "RnnStdError::{:?}", self)
}
}
impl std::error::Error for RnnStdError {}
pub fn parse_rnn_from_bytes(bytes: &[u8]) -> Result<RnnStd, RnnStdError> {
RnnStd::from_bytes(bytes.to_vec())
}
#[derive(Debug, Clone, Copy)]
pub struct DenseCounts {
pub layers: usize,
pub weights: usize,
pub biases: usize,
}
type DenseUnpackResult = (Vec<crate::std::layers_std::LayerSpec>, Vec<f32>, Vec<f32>);
type DenseUnpackResultF64 = (Vec<crate::std::layers_std::LayerSpec>, Vec<f64>, Vec<f64>);
#[derive(Clone, Copy, Debug)]
pub struct RnnDenseCapacityStd {
pub layer_specs: usize,
pub weights: usize,
pub biases: usize,
pub infer_scratch: usize,
}
pub fn rnn_required_from_bytes_v1_std(
bytes: &[u8],
) -> Result<DenseCounts, native_neural_network::rnn_api::RnnApiError> {
let (layers, weights, biases, _) = decode_header_any(bytes)?;
Ok(DenseCounts {
layers,
weights,
biases,
})
}
pub fn rnn_unpack_v1_std(
bytes: &[u8],
) -> Result<DenseUnpackResult, native_neural_network::rnn_api::RnnApiError> {
if let Ok((layers, weights, biases)) = decode_f32_from_container(bytes) {
return Ok((layers, weights, biases));
}
let counts = parse_rmd1_counts_f32(bytes)?;
let mut layers_out =
vec![
crate::std::layers_std::LayerSpec::Dense(crate::std::layers_std::DenseLayerDesc {
input_size: 0,
output_size: 0,
weight_offset: 0,
bias_offset: 0,
activation: native_neural_network::activations::ActivationKind::Identity,
});
counts.layers
];
let mut weights_out = vec![0f32; counts.weights];
let mut biases_out = vec![0f32; counts.biases];
let decoded = crate::std::model_format_std::decode_model_v1(
bytes,
&mut layers_out,
&mut weights_out,
&mut biases_out,
)
.map_err(|_| native_neural_network::rnn_api::RnnApiError::BadBytes)?;
Ok((
layers_out[..decoded.layers].to_vec(),
weights_out[..decoded.weights].to_vec(),
biases_out[..decoded.biases].to_vec(),
))
}
pub fn rnn_unpack_v1_f64_std(
bytes: &[u8],
) -> Result<DenseUnpackResultF64, native_neural_network::rnn_api::RnnApiError> {
decode_f64_from_container(bytes)
}
pub fn rnn_extract_blob_bytes_std(bytes: &[u8], name: &str) -> Result<Vec<u8>, RnnStdError> {
let parsed = parse_rnn_from_bytes(bytes)?;
parsed
.get_blob_bytes(name)
.map(|v| v.to_vec())
.ok_or(RnnStdError::BlobNotFound)
}
pub fn rnn_unpack_from_container_std(
bytes: &[u8],
blob_name: &str,
) -> Result<DenseUnpackResult, native_neural_network::rnn_api::RnnApiError> {
let blob = rnn_extract_blob_bytes_std(bytes, blob_name)
.map_err(|_| native_neural_network::rnn_api::RnnApiError::BadBytes)?;
rnn_unpack_v1_std(&blob)
}
pub fn rnn_unpack_from_container_f64_std(
bytes: &[u8],
blob_name: &str,
) -> Result<DenseUnpackResultF64, native_neural_network::rnn_api::RnnApiError> {
let blob = rnn_extract_blob_bytes_std(bytes, blob_name)
.map_err(|_| native_neural_network::rnn_api::RnnApiError::BadBytes)?;
rnn_unpack_v1_f64_std(&blob)
}
#[allow(clippy::too_many_arguments)]
pub fn rnn_run_from_container_v1(
bytes: &[u8],
blob_name: &str,
input: &[f32],
output: &mut [f32],
layer_specs_scratch: &mut [crate::std::layers_std::LayerSpec],
weights_scratch: &mut [f32],
biases_scratch: &mut [f32],
infer_scratch: &mut [f32],
) -> Result<(), native_neural_network::rnn_api::RnnApiError> {
let blob = rnn_extract_blob_bytes_std(bytes, blob_name)
.map_err(|_| native_neural_network::rnn_api::RnnApiError::BadBytes)?;
rnn_run_v1(
&blob,
input,
output,
layer_specs_scratch,
weights_scratch,
biases_scratch,
infer_scratch,
)
}
#[allow(clippy::too_many_arguments)]
pub fn rnn_run_from_container_v1_f64(
bytes: &[u8],
blob_name: &str,
input: &[f64],
output: &mut [f64],
layer_specs_scratch: &mut [crate::std::layers_std::LayerSpec],
weights_scratch: &mut [f64],
biases_scratch: &mut [f64],
infer_scratch: &mut [f64],
) -> Result<(), native_neural_network::rnn_api::RnnApiError> {
let blob = rnn_extract_blob_bytes_std(bytes, blob_name)
.map_err(|_| native_neural_network::rnn_api::RnnApiError::BadBytes)?;
rnn_run_v1_f64(
&blob,
input,
output,
layer_specs_scratch,
weights_scratch,
biases_scratch,
infer_scratch,
)
}
pub fn rnn_required_infer_scratch_from_specs_std(
specs: &[crate::std::layers_std::LayerSpec],
) -> Result<usize, native_neural_network::rnn_api::RnnApiError> {
let native = crate::std::layers_std::to_native_vec(specs);
let (weights_len, biases_len) = lengths_from_specs(&native)?;
let weights = vec![0f32; weights_len];
let biases = vec![0f32; biases_len];
let plan = native_neural_network::layers::LayerPlan {
layers: &native,
weights: &weights,
biases: &biases,
};
native_neural_network::engine::required_single_infer_scratch(&plan)
.ok_or(native_neural_network::rnn_api::RnnApiError::InvalidTopology)
}
pub fn rnn_required_from_topology(
topology: &[usize],
) -> Result<
crate::std::model_format_std::DecodedCountsStd,
native_neural_network::rnn_api::RnnApiError,
> {
let (weights, biases) =
native_neural_network::initializers::expected_parameter_counts(topology)
.ok_or(native_neural_network::rnn_api::RnnApiError::InvalidTopology)?;
Ok(crate::std::model_format_std::DecodedCountsStd {
layers: topology.len().saturating_sub(1),
weights,
biases,
})
}
pub fn rnn_required_buffers(
topology: &[usize],
) -> Result<RnnDenseCapacityStd, native_neural_network::rnn_api::RnnApiError> {
let decoded = rnn_required_from_topology(topology)?;
let mut native_specs = vec![
native_neural_network::layers::LayerSpec::Dense(
native_neural_network::layers::LayerDesc {
input_size: 0,
output_size: 0,
weight_offset: 0,
bias_offset: 0,
activation: native_neural_network::activations::ActivationKind::Identity,
}
);
decoded.layers
];
let used = native_neural_network::layers::build_from_layers(
topology,
native_neural_network::activations::ActivationKind::Identity,
native_neural_network::activations::ActivationKind::Identity,
decoded.weights,
decoded.biases,
&mut native_specs,
)
.map_err(|_| native_neural_network::rnn_api::RnnApiError::InvalidTopology)?;
let plan = native_neural_network::layers::LayerPlan {
layers: &native_specs[..used],
weights: &vec![0f32; decoded.weights],
biases: &vec![0f32; decoded.biases],
};
let infer = native_neural_network::engine::required_single_infer_scratch(&plan)
.ok_or(native_neural_network::rnn_api::RnnApiError::InvalidTopology)?;
Ok(RnnDenseCapacityStd {
layer_specs: decoded.layers,
weights: decoded.weights,
biases: decoded.biases,
infer_scratch: infer,
})
}
pub fn rnn_pack_v1(
topology: &[usize],
hidden_activation: native_neural_network::activations::ActivationKind,
output_activation: native_neural_network::activations::ActivationKind,
weights: &[f32],
biases: &[f32],
layer_specs_scratch: &mut [crate::std::layers_std::LayerSpec],
out_bytes: &mut [u8],
) -> Result<usize, native_neural_network::rnn_api::RnnApiError> {
let mut native_specs = vec![
native_neural_network::layers::LayerSpec::Dense(
native_neural_network::layers::LayerDesc {
input_size: 0,
output_size: 0,
weight_offset: 0,
bias_offset: 0,
activation: native_neural_network::activations::ActivationKind::Identity,
}
);
layer_specs_scratch.len()
];
let used = native_neural_network::engine::pack_rnn_f32(
topology,
hidden_activation,
output_activation,
weights,
biases,
&mut native_specs,
out_bytes,
)
.map_err(map_rnn_flow_error)?;
let used_specs = native_neural_network::layers::build_from_layers(
topology,
hidden_activation,
output_activation,
weights.len(),
biases.len(),
&mut native_specs,
)
.map_err(|_| native_neural_network::rnn_api::RnnApiError::InvalidTopology)?;
crate::std::layers_std::fill_std_slice_from_native(
&native_specs[..used_specs],
layer_specs_scratch,
);
Ok(used)
}
pub fn rnn_pack_v1_f64(
topology: &[usize],
hidden_activation: native_neural_network::activations::ActivationKind,
output_activation: native_neural_network::activations::ActivationKind,
weights: &[f64],
biases: &[f64],
layer_specs_scratch: &mut [crate::std::layers_std::LayerSpec],
out_bytes: &mut [u8],
) -> Result<usize, native_neural_network::rnn_api::RnnApiError> {
let mut native_specs = vec![
native_neural_network::layers::LayerSpec::Dense(
native_neural_network::layers::LayerDesc {
input_size: 0,
output_size: 0,
weight_offset: 0,
bias_offset: 0,
activation: native_neural_network::activations::ActivationKind::Identity,
}
);
layer_specs_scratch.len()
];
let used = native_neural_network::engine::pack_rnn_f64(
topology,
hidden_activation,
output_activation,
weights,
biases,
&mut native_specs,
out_bytes,
)
.map_err(map_rnn_flow_error)?;
let used_specs = native_neural_network::layers::build_from_layers(
topology,
hidden_activation,
output_activation,
weights.len(),
biases.len(),
&mut native_specs,
)
.map_err(|_| native_neural_network::rnn_api::RnnApiError::InvalidTopology)?;
crate::std::layers_std::fill_std_slice_from_native(
&native_specs[..used_specs],
layer_specs_scratch,
);
Ok(used)
}
#[allow(clippy::too_many_arguments)]
pub fn rnn_run_v1_f64(
bytes: &[u8],
input: &[f64],
output: &mut [f64],
layer_specs_scratch: &mut [crate::std::layers_std::LayerSpec],
weights_scratch: &mut [f64],
biases_scratch: &mut [f64],
infer_scratch: &mut [f64],
) -> Result<(), native_neural_network::rnn_api::RnnApiError> {
let (layers, weights, biases) = rnn_unpack_v1_f64_std(bytes)?;
if weights_scratch.len() < weights.len()
|| biases_scratch.len() < biases.len()
|| layer_specs_scratch.len() < layers.len()
{
return Err(native_neural_network::rnn_api::RnnApiError::CapacityTooSmall);
}
weights_scratch[..weights.len()].copy_from_slice(&weights);
biases_scratch[..biases.len()].copy_from_slice(&biases);
layer_specs_scratch[..layers.len()].copy_from_slice(&layers);
let native = crate::std::layers_std::to_native_vec(&layers);
let plan = native_neural_network::layers::LayerPlanF64 {
layers: &native,
weights: &weights_scratch[..weights.len()],
biases: &biases_scratch[..biases.len()],
};
native_neural_network::engine::forward_plan_f64(&plan, input, output, infer_scratch)
.map_err(|_| native_neural_network::rnn_api::RnnApiError::Model)
}
#[allow(clippy::too_many_arguments)]
pub fn rnn_run_v1(
bytes: &[u8],
input: &[f32],
output: &mut [f32],
layer_specs_scratch: &mut [crate::std::layers_std::LayerSpec],
weights_scratch: &mut [f32],
biases_scratch: &mut [f32],
infer_scratch: &mut [f32],
) -> Result<(), native_neural_network::rnn_api::RnnApiError> {
let (layers, weights, biases) = rnn_unpack_v1_std(bytes)?;
if weights_scratch.len() < weights.len()
|| biases_scratch.len() < biases.len()
|| layer_specs_scratch.len() < layers.len()
{
return Err(native_neural_network::rnn_api::RnnApiError::CapacityTooSmall);
}
weights_scratch[..weights.len()].copy_from_slice(&weights);
biases_scratch[..biases.len()].copy_from_slice(&biases);
layer_specs_scratch[..layers.len()].copy_from_slice(&layers);
let native = crate::std::layers_std::to_native_vec(&layers);
let plan = native_neural_network::layers::LayerPlan {
layers: &native,
weights: &weights_scratch[..weights.len()],
biases: &biases_scratch[..biases.len()],
};
native_neural_network::engine::forward_plan(&plan, input, output, infer_scratch)
.map_err(|_| native_neural_network::rnn_api::RnnApiError::Model)
}
pub fn rnn_validate_topology(
topology: &[usize],
) -> Result<(), native_neural_network::rnn_api::RnnApiError> {
let _ = native_neural_network::initializers::expected_parameter_counts(topology)
.ok_or(native_neural_network::rnn_api::RnnApiError::InvalidTopology)?;
Ok(())
}
pub fn rnn_validate_counts(
topology: &[usize],
weights_len: usize,
biases_len: usize,
) -> Result<(), native_neural_network::rnn_api::RnnApiError> {
let (w, b) = native_neural_network::initializers::expected_parameter_counts(topology)
.ok_or(native_neural_network::rnn_api::RnnApiError::InvalidTopology)?;
if w == weights_len && b == biases_len {
Ok(())
} else {
Err(native_neural_network::rnn_api::RnnApiError::InvalidTopology)
}
}
pub fn rnn_wrap_payload_in_container_std(payload: &[u8], blob_name: &str) -> Vec<u8> {
let _ = blob_name;
payload.to_vec()
}
fn map_rnn_error(err: native_neural_network::rnn_api::RnnApiError) -> RnnStdError {
match err {
native_neural_network::rnn_api::RnnApiError::InvalidTopology => RnnStdError::BadHeader,
native_neural_network::rnn_api::RnnApiError::CapacityTooSmall => RnnStdError::ScratchFull,
native_neural_network::rnn_api::RnnApiError::BadBytes => RnnStdError::BadMagic,
native_neural_network::rnn_api::RnnApiError::FormatMismatch => RnnStdError::BadHeader,
native_neural_network::rnn_api::RnnApiError::Layer
| native_neural_network::rnn_api::RnnApiError::Model => RnnStdError::BadBounds,
}
}
fn map_rnn_flow_error(
err: native_neural_network::engine::RnnFlowError,
) -> native_neural_network::rnn_api::RnnApiError {
match err {
native_neural_network::engine::RnnFlowError::InvalidTopology => {
native_neural_network::rnn_api::RnnApiError::InvalidTopology
}
native_neural_network::engine::RnnFlowError::CapacityTooSmall => {
native_neural_network::rnn_api::RnnApiError::CapacityTooSmall
}
native_neural_network::engine::RnnFlowError::BadBytes => {
native_neural_network::rnn_api::RnnApiError::BadBytes
}
native_neural_network::engine::RnnFlowError::Model => {
native_neural_network::rnn_api::RnnApiError::Model
}
}
}
fn parse_rmd1_counts_f32(
bytes: &[u8],
) -> Result<DenseCounts, native_neural_network::rnn_api::RnnApiError> {
if bytes.len() < 20 || &bytes[0..4] != b"RMD1" {
return Err(native_neural_network::rnn_api::RnnApiError::BadBytes);
}
if bytes[6] != 0 {
return Err(native_neural_network::rnn_api::RnnApiError::FormatMismatch);
}
let layers = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]) as usize;
let weights = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]) as usize;
let biases = u32::from_le_bytes([bytes[16], bytes[17], bytes[18], bytes[19]]) as usize;
Ok(DenseCounts {
layers,
weights,
biases,
})
}
fn decode_header_any(
bytes: &[u8],
) -> Result<(usize, usize, usize, u8), native_neural_network::rnn_api::RnnApiError> {
if bytes.len() >= 20 && &bytes[0..4] == b"RMD1" {
let dtype = bytes[6];
let layers = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]) as usize;
let weights = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]) as usize;
let biases = u32::from_le_bytes([bytes[16], bytes[17], bytes[18], bytes[19]]) as usize;
return Ok((layers, weights, biases, dtype));
}
let view = native_neural_network::rnn_api::read_rnn(bytes, None)?;
let header = view
.scan
.header
.ok_or(native_neural_network::rnn_api::RnnApiError::BadBytes)?;
Ok((
header.layer_count as usize,
header.weights_len as usize,
header.biases_len as usize,
header.dtype,
))
}
fn decode_f32_from_container(
bytes: &[u8],
) -> Result<DenseUnpackResult, native_neural_network::rnn_api::RnnApiError> {
let view = native_neural_network::rnn_api::read_rnn(bytes, None)?;
let header = view
.scan
.header
.ok_or(native_neural_network::rnn_api::RnnApiError::BadBytes)?;
if header.dtype != 0 {
return Err(native_neural_network::rnn_api::RnnApiError::FormatMismatch);
}
let layer_meta = view
.layer_meta
.ok_or(native_neural_network::rnn_api::RnnApiError::BadBytes)?;
let weights_b = view
.weights
.ok_or(native_neural_network::rnn_api::RnnApiError::BadBytes)?;
let biases_b = view
.biases
.ok_or(native_neural_network::rnn_api::RnnApiError::BadBytes)?;
let layers = decode_layer_specs(layer_meta)?;
let weights = decode_f32_slice(weights_b, header.weights_len as usize)?;
let biases = decode_f32_slice(biases_b, header.biases_len as usize)?;
let out_layers = layers
.into_iter()
.map(crate::std::layers_std::LayerSpec::from)
.collect();
Ok((out_layers, weights, biases))
}
fn decode_f64_from_container(
bytes: &[u8],
) -> Result<DenseUnpackResultF64, native_neural_network::rnn_api::RnnApiError> {
let view = native_neural_network::rnn_api::read_rnn(bytes, None)?;
let header = view
.scan
.header
.ok_or(native_neural_network::rnn_api::RnnApiError::BadBytes)?;
if header.dtype != 1 {
return Err(native_neural_network::rnn_api::RnnApiError::FormatMismatch);
}
let layer_meta = view
.layer_meta
.ok_or(native_neural_network::rnn_api::RnnApiError::BadBytes)?;
let weights_b = view
.weights
.ok_or(native_neural_network::rnn_api::RnnApiError::BadBytes)?;
let biases_b = view
.biases
.ok_or(native_neural_network::rnn_api::RnnApiError::BadBytes)?;
let layers = decode_layer_specs(layer_meta)?;
let weights = decode_f64_slice(weights_b, header.weights_len as usize)?;
let biases = decode_f64_slice(biases_b, header.biases_len as usize)?;
let out_layers = layers
.into_iter()
.map(crate::std::layers_std::LayerSpec::from)
.collect();
Ok((out_layers, weights, biases))
}
fn decode_layer_specs(
bytes: &[u8],
) -> Result<
Vec<native_neural_network::layers::LayerSpec>,
native_neural_network::rnn_api::RnnApiError,
> {
if !bytes.len().is_multiple_of(20) {
return Err(native_neural_network::rnn_api::RnnApiError::BadBytes);
}
let layer_count = bytes.len() / 20;
let mut out = Vec::with_capacity(layer_count);
for i in 0..layer_count {
let off = i * 20;
let input_size =
u32::from_le_bytes([bytes[off], bytes[off + 1], bytes[off + 2], bytes[off + 3]])
as usize;
let output_size = u32::from_le_bytes([
bytes[off + 4],
bytes[off + 5],
bytes[off + 6],
bytes[off + 7],
]) as usize;
let weight_offset = u32::from_le_bytes([
bytes[off + 8],
bytes[off + 9],
bytes[off + 10],
bytes[off + 11],
]) as usize;
let bias_offset = u32::from_le_bytes([
bytes[off + 12],
bytes[off + 13],
bytes[off + 14],
bytes[off + 15],
]) as usize;
let activation =
native_neural_network::activations::ActivationKind::from_u8(bytes[off + 16])
.ok_or(native_neural_network::rnn_api::RnnApiError::BadBytes)?;
out.push(native_neural_network::layers::LayerSpec::Dense(
native_neural_network::layers::LayerDesc {
input_size,
output_size,
weight_offset,
bias_offset,
activation,
},
));
}
Ok(out)
}
fn decode_f32_slice(
bytes: &[u8],
expected_len: usize,
) -> Result<Vec<f32>, native_neural_network::rnn_api::RnnApiError> {
if bytes.len() != expected_len.saturating_mul(4) {
return Err(native_neural_network::rnn_api::RnnApiError::BadBytes);
}
let mut out = vec![0f32; expected_len];
for (i, slot) in out.iter_mut().enumerate() {
let o = i * 4;
*slot = f32::from_le_bytes([bytes[o], bytes[o + 1], bytes[o + 2], bytes[o + 3]]);
}
Ok(out)
}
fn decode_f64_slice(
bytes: &[u8],
expected_len: usize,
) -> Result<Vec<f64>, native_neural_network::rnn_api::RnnApiError> {
if bytes.len() != expected_len.saturating_mul(8) {
return Err(native_neural_network::rnn_api::RnnApiError::BadBytes);
}
let mut out = vec![0f64; expected_len];
for (i, slot) in out.iter_mut().enumerate() {
let o = i * 8;
*slot = f64::from_le_bytes([
bytes[o],
bytes[o + 1],
bytes[o + 2],
bytes[o + 3],
bytes[o + 4],
bytes[o + 5],
bytes[o + 6],
bytes[o + 7],
]);
}
Ok(out)
}
fn lengths_from_specs(
specs: &[native_neural_network::layers::LayerSpec],
) -> Result<(usize, usize), native_neural_network::rnn_api::RnnApiError> {
let mut weights = 0usize;
let mut biases = 0usize;
for spec in specs {
let native_neural_network::layers::LayerSpec::Dense(d) = *spec;
let w = d
.input_size
.checked_mul(d.output_size)
.ok_or(native_neural_network::rnn_api::RnnApiError::InvalidTopology)?;
let w_end = d
.weight_offset
.checked_add(w)
.ok_or(native_neural_network::rnn_api::RnnApiError::InvalidTopology)?;
let b_end = d
.bias_offset
.checked_add(d.output_size)
.ok_or(native_neural_network::rnn_api::RnnApiError::InvalidTopology)?;
weights = weights.max(w_end);
biases = biases.max(b_end);
}
Ok((weights, biases))
}
fn rnn_init_hook() -> Result<(), crate::InitError> {
Ok(())
}
pub fn register_module_init_hooks() -> Result<(), crate::RegisterError> {
crate::register_init_hook("rnn", crate::InitSubsystem::Rnn, rnn_init_hook)
}