use crate::models::lazy_mimi_conv::{LazyPadMode, StreamConv1dState, StreamableConv1dWeights};
use crate::models::lazy_mimi_conv_transpose::{
StreamConvTranspose1dState, StreamableConvTranspose1dWeights,
};
use fuel_ir::error::Result;
use fuel_tensor::lazy::Tensor;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct ConvDownsample1dWeights {
pub conv: StreamableConv1dWeights,
}
impl ConvDownsample1dWeights {
pub fn new(stride: usize, dim: usize, causal: bool, raw_weight: Arc<[f32]>) -> Result<Self> {
let conv = StreamableConv1dWeights::new(
raw_weight,
None,
dim,
dim,
2 * stride,
stride,
1,
causal,
LazyPadMode::Replicate,
)?;
Ok(Self { conv })
}
pub fn forward(&self, xs: &Tensor) -> Result<Tensor> {
self.conv.forward(xs)
}
pub fn step(
&self,
state: StreamConv1dState,
xs: &Tensor,
) -> Result<(StreamConv1dState, Option<Tensor>)> {
self.conv.step(state, xs)
}
}
#[derive(Debug, Clone)]
pub struct ConvTrUpsample1dWeights {
pub convtr: StreamableConvTranspose1dWeights,
}
impl ConvTrUpsample1dWeights {
pub fn new(stride: usize, dim: usize, causal: bool, raw_weight: Arc<[f32]>) -> Result<Self> {
let convtr = StreamableConvTranspose1dWeights::new(
raw_weight,
None,
dim,
dim,
2 * stride,
stride,
dim,
causal,
)?;
Ok(Self { convtr })
}
pub fn forward(&self, xs: &Tensor) -> Result<Tensor> {
self.convtr.forward(xs)
}
pub fn step(
&self,
state: StreamConvTranspose1dState,
xs: &Tensor,
) -> Result<(StreamConvTranspose1dState, Option<Tensor>)> {
self.convtr.step(state, xs)
}
}
#[cfg(test)]
mod tests {
use super::*;
use fuel_ir::Shape;
use fuel_tensor::Device;
fn const_xs(b: usize, c: usize, t: usize, src: &[f32]) -> Tensor {
assert_eq!(src.len(), b * c * t);
Tensor::from_f32(
Arc::from(src.to_vec()),
Shape::from_dims(&[b, c, t]),
&Device::cpu(),
)
.unwrap()
}
fn ramp_weight(out_c: usize, in_per_group: usize, k: usize) -> Arc<[f32]> {
let n = out_c * in_per_group * k;
Arc::from(
(0..n)
.map(|i| 0.01 + (i as f32) * 0.03)
.collect::<Vec<f32>>(),
)
}
fn ramp_xs(b: usize, c: usize, t: usize) -> Tensor {
let n = b * c * t;
let data: Vec<f32> = (0..n).map(|i| 0.05 + (i as f32) * 0.017).collect();
const_xs(b, c, t, &data)
}
#[test]
fn downsample_forward_shape_stride_2() {
let dim = 2;
let stride = 2;
let w = ramp_weight(dim, dim, 2 * stride);
let down = ConvDownsample1dWeights::new(stride, dim, true, w).unwrap();
let t_in = 16;
let xs = ramp_xs(1, dim, t_in);
let y = down.forward(&xs).unwrap();
assert_eq!(y.shape().dims(), &[1, dim, t_in / stride]);
for v in &y.realize_f32() {
assert!(v.is_finite(), "non-finite: {v}");
}
}
#[test]
fn downsample_forward_shape_stride_4() {
let dim = 3;
let stride = 4;
let w = ramp_weight(dim, dim, 2 * stride);
let down = ConvDownsample1dWeights::new(stride, dim, true, w).unwrap();
let t_in = 24;
let xs = ramp_xs(1, dim, t_in);
let y = down.forward(&xs).unwrap();
assert_eq!(y.shape().dims(), &[1, dim, t_in / stride]);
}
#[test]
fn upsample_forward_shape_stride_2() {
let dim = 2;
let stride = 2;
let w = ramp_weight(dim, 1, 2 * stride);
let up = ConvTrUpsample1dWeights::new(stride, dim, true, w).unwrap();
let t_in = 8;
let xs = ramp_xs(1, dim, t_in);
let y = up.forward(&xs).unwrap();
assert_eq!(y.shape().dims(), &[1, dim, t_in * stride]);
for v in &y.realize_f32() {
assert!(v.is_finite(), "non-finite: {v}");
}
}
#[test]
fn upsample_forward_shape_stride_4() {
let dim = 2;
let stride = 4;
let w = ramp_weight(dim, 1, 2 * stride);
let up = ConvTrUpsample1dWeights::new(stride, dim, true, w).unwrap();
let t_in = 5;
let xs = ramp_xs(1, dim, t_in);
let y = up.forward(&xs).unwrap();
assert_eq!(y.shape().dims(), &[1, dim, t_in * stride]);
}
#[test]
fn downsample_streaming_equals_one_shot() {
let dim = 2;
let stride = 2;
let w = ramp_weight(dim, dim, 2 * stride);
let down = ConvDownsample1dWeights::new(stride, dim, true, w).unwrap();
let t_in = 12;
let xs = ramp_xs(1, dim, t_in);
let one_shot = down.forward(&xs).unwrap().realize_f32();
let chunk = 3;
let mut state = StreamConv1dState::empty();
let mut pieces: Vec<Tensor> = Vec::new();
let mut t = 0;
while t < t_in {
let len = chunk.min(t_in - t);
let xc = xs.narrow(2_usize, t, len).unwrap();
let (s2, y) = down.step(state, &xc).unwrap();
state = s2;
if let Some(y) = y {
pieces.push(y);
}
t += len;
}
let mut streamed = pieces[0].clone();
for p in pieces.iter().skip(1) {
streamed = streamed.concat(p, 2_usize).unwrap();
}
let streamed = streamed.realize_f32();
let cout = dim;
let t_one = one_shot.len() / cout;
let t_str = streamed.len() / cout;
let common = t_one.min(t_str);
assert!(common >= 1, "expected shared frames, got {common}");
for c in 0..cout {
for k in 0..common {
let a = streamed[c * t_str + k];
let b = one_shot[c * t_one + k];
assert!(
(a - b).abs() < 1e-5,
"ch={c} t={k}: streamed={a} one_shot={b}",
);
}
}
}
#[test]
fn upsample_streaming_equals_one_shot() {
let dim = 2;
let stride = 2;
let w = ramp_weight(dim, 1, 2 * stride);
let up = ConvTrUpsample1dWeights::new(stride, dim, true, w).unwrap();
let t_in = 8;
let xs = ramp_xs(1, dim, t_in);
let one_shot = up.forward(&xs).unwrap().realize_f32();
let chunk = 2;
let mut state = StreamConvTranspose1dState::empty();
let mut pieces: Vec<Tensor> = Vec::new();
let mut t = 0;
while t < t_in {
let len = chunk.min(t_in - t);
let xc = xs.narrow(2_usize, t, len).unwrap();
let (s2, y) = up.step(state, &xc).unwrap();
state = s2;
if let Some(y) = y {
pieces.push(y);
}
t += len;
}
let mut streamed = pieces[0].clone();
for p in pieces.iter().skip(1) {
streamed = streamed.concat(p, 2_usize).unwrap();
}
let streamed = streamed.realize_f32();
let cout = dim;
let t_one = one_shot.len() / cout;
let t_str = streamed.len() / cout;
let common = t_one.min(t_str);
assert!(
common >= stride,
"expected at least {stride} shared per-ch samples, got {common}"
);
for c in 0..cout {
for k in 0..common {
let a = streamed[c * t_str + k];
let b = one_shot[c * t_one + k];
assert!(
(a - b).abs() < 1e-5,
"ch={c} t={k}: streamed={a} one_shot={b}",
);
}
}
}
}