use crate::infer::GraphExt as _;
use crate::{Graph, NodeId};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum InterpMode {
Nearest,
Linear,
}
impl Graph {
#[allow(clippy::too_many_arguments)]
pub fn conv_transpose1d(
&mut self,
input: NodeId,
weight: NodeId,
kernel: usize,
stride: usize,
padding: usize,
dilation: usize,
output_padding: usize,
groups: usize,
) -> NodeId {
let in_s = self.shape(input).clone();
let w_s = self.shape(weight).clone();
assert_eq!(
in_s.rank(),
3,
"conv_transpose1d: input must be [N, C_in, L]"
);
assert_eq!(
w_s.rank(),
3,
"conv_transpose1d: weight must be [C_in, C_out/groups, K]"
);
let n = in_s.dim(0).unwrap_static();
let c_in = in_s.dim(1).unwrap_static();
let l = in_s.dim(2).unwrap_static();
let c_out_g = w_s.dim(1).unwrap_static();
let input4 = self.reshape_(input, vec![n as i64, c_in as i64, 1, l as i64]);
let weight4 = self.reshape_(weight, vec![c_in as i64, c_out_g as i64, 1, kernel as i64]);
let out4 = self.conv_transpose2d(
input4,
weight4,
[1, kernel],
[1, stride],
[0, padding],
[1, dilation],
[0, output_padding],
groups,
);
let o = self.shape(out4).clone();
let c_out = o.dim(1).unwrap_static();
let l_out = o.dim(3).unwrap_static();
self.reshape_(out4, vec![n as i64, c_out as i64, l_out as i64])
}
pub fn interpolate1d(&mut self, x: NodeId, l_out: usize, mode: InterpMode) -> NodeId {
assert!(l_out > 0, "interpolate1d: l_out must be positive");
let xs = self.shape(x).clone();
let rank = xs.rank();
let l_in = xs.dim(rank - 1).unwrap_static();
if l_in == l_out {
return x;
}
let batch: usize = (0..rank - 1).map(|i| xs.dim(i).unwrap_static()).product();
let mut w = vec![0f32; l_in * l_out];
for j in 0..l_out {
let pos = if l_out == 1 {
0.0
} else {
j as f32 * (l_in as f32 - 1.0) / (l_out as f32 - 1.0)
};
match mode {
InterpMode::Nearest => {
let i = (pos + 0.5) as usize;
let i = i.min(l_in - 1);
w[i * l_out + j] = 1.0;
}
InterpMode::Linear => {
let i0 = pos.floor() as usize;
let i0 = i0.min(l_in - 1);
let i1 = (i0 + 1).min(l_in - 1);
let frac = pos - i0 as f32;
w[i0 * l_out + j] += 1.0 - frac;
w[i1 * l_out + j] += frac;
}
}
}
let w_node = self.const_f32_tensor(w, &[l_in, l_out]);
let x2 = self.reshape_(x, vec![batch as i64, l_in as i64]);
let y2 = self.mm(x2, w_node); let mut out_dims: Vec<i64> = (0..rank - 1)
.map(|i| xs.dim(i).unwrap_static() as i64)
.collect();
out_dims.push(l_out as i64);
self.reshape_(y2, out_dims)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{DType, Shape};
fn dims(g: &Graph, id: NodeId) -> Vec<usize> {
g.shape(id)
.dims()
.iter()
.map(|d| d.unwrap_static())
.collect()
}
fn input(g: &mut Graph, shape: &[usize]) -> NodeId {
g.input("x", Shape::new(shape, DType::F32))
}
#[test]
fn conv_transpose1d_upsamples_by_stride() {
let mut g = Graph::new("ct1d");
let x = input(&mut g, &[2, 3, 8]); let w = g.param("w", Shape::new(&[3, 5, 4], DType::F32)); let y = g.conv_transpose1d(x, w, 4, 2, 1, 1, 0, 1);
assert_eq!(dims(&g, y), vec![2, 5, 16]);
}
#[test]
fn interpolate1d_linear_shape() {
let mut g = Graph::new("interp");
let x = input(&mut g, &[2, 4, 10]);
let y = g.interpolate1d(x, 25, InterpMode::Linear);
assert_eq!(dims(&g, y), vec![2, 4, 25]);
}
#[test]
fn interpolate1d_nearest_identity() {
let mut g = Graph::new("interp_id");
let x = input(&mut g, &[3, 7]);
let y = g.interpolate1d(x, 7, InterpMode::Nearest);
assert_eq!(y, x); }
}