use super::super::WeightCursor;
use super::super::checked_arith;
use super::traits::{ConvWeightsOutput, DenseWeightsOutput};
use crate::math::common::AlignedVec;
pub(crate) fn read_conv1d_weights_typed<T: ConvWeightsOutput>(
cursor: &mut WeightCursor<'_>,
in_size: usize,
out_size: usize,
k_size: usize,
dilation: usize,
do_bias: bool,
) -> anyhow::Result<T> {
let width = select_interleave_width(out_size);
let num_blocks = out_size.div_ceil(width);
let padded_total =
checked_arith::checked_conv_padded_total(num_blocks, width, in_size, k_size)?;
let interleaved = cursor.is_interleaved4();
let f32_weights = if interleaved {
let total_4wide = checked_arith::checked_mul4(out_size.div_ceil(4), 4, in_size, k_size)?;
let raw = cursor.read_slice(total_4wide)?;
let mut weights = AlignedVec::new(padded_total, 0.0f32)?;
if width != 4 {
let mut tmp = AlignedVec::new(total_4wide, 0.0f32)?;
tmp.copy_from_slice(raw);
match width {
16 => transpose_4wide_to_16wide(&tmp, &mut weights, in_size, out_size, k_size),
8 => transpose_4wide_to_8wide(&tmp, &mut weights, in_size, out_size, k_size),
_ => transpose_conv1d_interleaved_4wide(
&tmp,
&mut weights,
in_size,
out_size,
k_size,
),
}
} else {
weights.copy_from_slice(raw);
}
weights
} else {
let total = checked_arith::checked_conv_total(out_size, in_size, k_size)?;
let raw = cursor.read_slice(total)?;
let mut weights = AlignedVec::new(padded_total, 0.0f32)?;
match width {
16 => transpose_conv1d_interleaved_16wide(raw, &mut weights, in_size, out_size, k_size),
8 => transpose_conv1d_interleaved_8wide(raw, &mut weights, in_size, out_size, k_size),
_ => transpose_conv1d_interleaved_4wide(raw, &mut weights, in_size, out_size, k_size),
}
weights
};
let bias = if do_bias {
AlignedVec::from_vec(cursor.read_slice(out_size)?.to_vec())?
} else {
AlignedVec::new(out_size, 0.0)?
};
Ok(T::from_parts(
f32_weights,
bias,
do_bias,
dilation,
in_size,
out_size,
k_size,
))
}
pub(crate) fn read_dense_weights_typed<T: DenseWeightsOutput>(
cursor: &mut WeightCursor<'_>,
in_size: usize,
out_size: usize,
do_bias: bool,
) -> anyhow::Result<T> {
let total = checked_arith::checked_dense_total(out_size, in_size)?;
let raw = cursor.read_slice(total)?;
let mut f32_weights = AlignedVec::new(total, 0.0f32)?;
let interleaved = cursor.is_interleaved4();
if interleaved {
f32_weights.copy_from_slice(raw);
} else {
transpose_dense_layer_f32(raw, &mut f32_weights, in_size, out_size);
}
let bias = if do_bias {
AlignedVec::from_vec(cursor.read_slice(out_size)?.to_vec())?
} else {
AlignedVec::new(out_size, 0.0)?
};
Ok(T::from_parts(f32_weights, bias, do_bias, in_size, out_size))
}
pub(crate) fn read_dense_head_weights_typed<T: DenseWeightsOutput>(
cursor: &mut WeightCursor<'_>,
in_size: usize,
out_size: usize,
do_bias: bool,
) -> anyhow::Result<T> {
let total = checked_arith::checked_dense_total(out_size, in_size)?;
let raw = cursor.read_slice(total)?;
let mut f32_weights = AlignedVec::new(total, 0.0f32)?;
let interleaved = cursor.is_interleaved4();
if interleaved {
f32_weights.copy_from_slice(raw);
} else {
transpose_dense_layer_f32(raw, &mut f32_weights, in_size, out_size);
}
let bias = if do_bias {
AlignedVec::from_vec(cursor.read_slice(out_size)?.to_vec())?
} else {
AlignedVec::new(out_size, 0.0)?
};
Ok(T::from_parts(f32_weights, bias, do_bias, in_size, out_size))
}
pub fn transpose_conv1d_interleaved_4wide(
raw: &[f32],
weights: &mut [f32],
in_ch: usize,
out_ch: usize,
kernel: usize,
) {
debug_assert!(
raw.len() >= out_ch * in_ch * kernel,
"raw slice too short for transposition"
);
let num_blocks = out_ch.div_ceil(4);
let padded_total = num_blocks * 4 * in_ch * kernel;
debug_assert!(
weights.len() >= padded_total,
"weights slice too short for interleaved output"
);
for b in 0..num_blocks {
for k in 0..kernel {
for in_c in 0..in_ch {
for lane in 0..4 {
let out_c = b * 4 + lane;
let target_idx = b * (kernel * in_ch * 4) + k * (in_ch * 4) + in_c * 4 + lane;
if out_c < out_ch {
let raw_idx = (out_c * in_ch + in_c) * kernel + k;
weights[target_idx] = raw[raw_idx];
} else {
weights[target_idx] = 0.0;
}
}
}
}
}
}
pub fn transpose_conv1d_interleaved_8wide(
raw: &[f32],
weights: &mut [f32],
in_ch: usize,
out_ch: usize,
kernel: usize,
) {
debug_assert!(
raw.len() >= out_ch * in_ch * kernel,
"raw slice too short for transposition"
);
let num_blocks = out_ch.div_ceil(8);
let padded_total = num_blocks * 8 * in_ch * kernel;
debug_assert!(
weights.len() >= padded_total,
"weights slice too short for interleaved output"
);
for b in 0..num_blocks {
for k in 0..kernel {
for in_c in 0..in_ch {
for lane in 0..8 {
let out_c = b * 8 + lane;
let target_idx = b * (kernel * in_ch * 8) + k * (in_ch * 8) + in_c * 8 + lane;
if out_c < out_ch {
let raw_idx = (out_c * in_ch + in_c) * kernel + k;
weights[target_idx] = raw[raw_idx];
} else {
weights[target_idx] = 0.0;
}
}
}
}
}
}
pub fn transpose_conv1d_interleaved_16wide(
raw: &[f32],
weights: &mut [f32],
in_ch: usize,
out_ch: usize,
kernel: usize,
) {
debug_assert!(
raw.len() >= out_ch * in_ch * kernel,
"raw slice too short for transposition"
);
let num_blocks = out_ch.div_ceil(16);
let padded_total = num_blocks * 16 * in_ch * kernel;
debug_assert!(
weights.len() >= padded_total,
"weights slice too short for interleaved output"
);
for b in 0..num_blocks {
for k in 0..kernel {
for in_c in 0..in_ch {
for lane in 0..16 {
let out_c = b * 16 + lane;
let target_idx =
b * (kernel * in_ch * 16) + k * (in_ch * 16) + in_c * 16 + lane;
if out_c < out_ch {
let raw_idx = (out_c * in_ch + in_c) * kernel + k;
weights[target_idx] = raw[raw_idx];
} else {
weights[target_idx] = 0.0;
}
}
}
}
}
}
pub(crate) fn transpose_4wide_to_8wide(
src: &[f32],
dst: &mut [f32],
in_ch: usize,
out_ch: usize,
kernel: usize,
) {
let num_blocks_4 = out_ch.div_ceil(4);
for b in 0..out_ch.div_ceil(8) {
for k in 0..kernel {
for in_c in 0..in_ch {
let dst_base = b * kernel * in_ch * 8 + k * in_ch * 8 + in_c * 8;
for lane in 0..4 {
let src_b = 2 * b;
if src_b < num_blocks_4 {
let src_idx = src_b * kernel * in_ch * 4 + k * in_ch * 4 + in_c * 4 + lane;
dst[dst_base + lane] = src[src_idx];
} else {
dst[dst_base + lane] = 0.0;
}
}
for lane in 0..4 {
let src_b = 2 * b + 1;
if src_b < num_blocks_4 {
let src_idx = src_b * kernel * in_ch * 4 + k * in_ch * 4 + in_c * 4 + lane;
dst[dst_base + 4 + lane] = src[src_idx];
} else {
dst[dst_base + 4 + lane] = 0.0;
}
}
}
}
}
}
pub(crate) fn transpose_4wide_to_16wide(
src: &[f32],
dst: &mut [f32],
in_ch: usize,
out_ch: usize,
kernel: usize,
) {
let num_blocks_4 = out_ch.div_ceil(4);
for b in 0..out_ch.div_ceil(16) {
for k in 0..kernel {
for in_c in 0..in_ch {
let dst_base = b * kernel * in_ch * 16 + k * in_ch * 16 + in_c * 16;
for lane in 0..4 {
let src_b = 4 * b;
if src_b < num_blocks_4 {
let src_idx = src_b * kernel * in_ch * 4 + k * in_ch * 4 + in_c * 4 + lane;
dst[dst_base + lane] = src[src_idx];
} else {
dst[dst_base + lane] = 0.0;
}
}
for lane in 0..4 {
let src_b = 4 * b + 1;
if src_b < num_blocks_4 {
let src_idx = src_b * kernel * in_ch * 4 + k * in_ch * 4 + in_c * 4 + lane;
dst[dst_base + 4 + lane] = src[src_idx];
} else {
dst[dst_base + 4 + lane] = 0.0;
}
}
for lane in 0..4 {
let src_b = 4 * b + 2;
if src_b < num_blocks_4 {
let src_idx = src_b * kernel * in_ch * 4 + k * in_ch * 4 + in_c * 4 + lane;
dst[dst_base + 8 + lane] = src[src_idx];
} else {
dst[dst_base + 8 + lane] = 0.0;
}
}
for lane in 0..4 {
let src_b = 4 * b + 3;
if src_b < num_blocks_4 {
let src_idx = src_b * kernel * in_ch * 4 + k * in_ch * 4 + in_c * 4 + lane;
dst[dst_base + 12 + lane] = src[src_idx];
} else {
dst[dst_base + 12 + lane] = 0.0;
}
}
}
}
}
}
#[inline]
pub fn select_interleave_width(out_ch: usize) -> usize {
if out_ch.is_multiple_of(16) || out_ch == 12 {
16
} else if out_ch.is_multiple_of(8) {
8
} else {
4
}
}
fn transpose_dense_layer_f32(raw: &[f32], weights: &mut [f32], in_size: usize, out_size: usize) {
debug_assert!(
raw.len() >= out_size * in_size,
"raw slice too short for dense transposition"
);
debug_assert!(
weights.len() >= in_size * out_size,
"weights slice too short for dense output"
);
for out_c in 0..out_size {
for in_c in 0..in_size {
weights[in_c * out_size + out_c] = raw[out_c * in_size + in_c];
}
}
}
#[cfg(test)]
#[path = "layout_test.rs"]
mod layout_test;