1use crate::infer::GraphExt as _;
15use crate::{Graph, NodeId};
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub enum InterpMode {
20 Nearest,
22 Linear,
24}
25
26impl Graph {
27 #[allow(clippy::too_many_arguments)]
36 pub fn conv_transpose1d(
37 &mut self,
38 input: NodeId,
39 weight: NodeId,
40 kernel: usize,
41 stride: usize,
42 padding: usize,
43 dilation: usize,
44 output_padding: usize,
45 groups: usize,
46 ) -> NodeId {
47 let in_s = self.shape(input).clone();
48 let w_s = self.shape(weight).clone();
49 assert_eq!(
50 in_s.rank(),
51 3,
52 "conv_transpose1d: input must be [N, C_in, L]"
53 );
54 assert_eq!(
55 w_s.rank(),
56 3,
57 "conv_transpose1d: weight must be [C_in, C_out/groups, K]"
58 );
59 let n = in_s.dim(0).unwrap_static();
60 let c_in = in_s.dim(1).unwrap_static();
61 let l = in_s.dim(2).unwrap_static();
62 let c_out_g = w_s.dim(1).unwrap_static();
63
64 let input4 = self.reshape_(input, vec![n as i64, c_in as i64, 1, l as i64]);
67 let weight4 = self.reshape_(weight, vec![c_in as i64, c_out_g as i64, 1, kernel as i64]);
68 let out4 = self.conv_transpose2d(
69 input4,
70 weight4,
71 [1, kernel],
72 [1, stride],
73 [0, padding],
74 [1, dilation],
75 [0, output_padding],
76 groups,
77 );
78 let o = self.shape(out4).clone();
79 let c_out = o.dim(1).unwrap_static();
80 let l_out = o.dim(3).unwrap_static();
81 self.reshape_(out4, vec![n as i64, c_out as i64, l_out as i64])
82 }
83
84 pub fn interpolate3d_nearest(&mut self, x: NodeId, size: [usize; 3]) -> NodeId {
92 let xs = self.shape(x).clone();
93 assert_eq!(xs.rank(), 5, "interpolate3d_nearest: NCDHW only");
94 assert!(
95 size.iter().all(|&s| s > 0),
96 "interpolate3d_nearest: size dims must be positive"
97 );
98 let out = crate::Shape::new(
99 &[
100 xs.dim(0).unwrap_static(),
101 xs.dim(1).unwrap_static(),
102 size[0],
103 size[1],
104 size[2],
105 ],
106 xs.dtype(),
107 );
108 self.push(
109 crate::Op::Interpolate3d {
110 size: size.to_vec(),
111 },
112 vec![x],
113 out,
114 None,
115 )
116 }
117
118 pub fn interpolate1d(&mut self, x: NodeId, l_out: usize, mode: InterpMode) -> NodeId {
124 assert!(l_out > 0, "interpolate1d: l_out must be positive");
125 let xs = self.shape(x).clone();
126 let rank = xs.rank();
127 let l_in = xs.dim(rank - 1).unwrap_static();
128 if l_in == l_out {
129 return x;
130 }
131 let batch: usize = (0..rank - 1).map(|i| xs.dim(i).unwrap_static()).product();
132
133 let mut w = vec![0f32; l_in * l_out];
136 for j in 0..l_out {
137 let pos = if l_out == 1 {
138 0.0
139 } else {
140 j as f32 * (l_in as f32 - 1.0) / (l_out as f32 - 1.0)
141 };
142 match mode {
143 InterpMode::Nearest => {
144 let i = (pos + 0.5) as usize;
145 let i = i.min(l_in - 1);
146 w[i * l_out + j] = 1.0;
147 }
148 InterpMode::Linear => {
149 let i0 = pos.floor() as usize;
150 let i0 = i0.min(l_in - 1);
151 let i1 = (i0 + 1).min(l_in - 1);
152 let frac = pos - i0 as f32;
153 w[i0 * l_out + j] += 1.0 - frac;
154 w[i1 * l_out + j] += frac;
155 }
156 }
157 }
158 let w_node = self.const_f32_tensor(w, &[l_in, l_out]);
159
160 let x2 = self.reshape_(x, vec![batch as i64, l_in as i64]);
162 let y2 = self.mm(x2, w_node); let mut out_dims: Vec<i64> = (0..rank - 1)
164 .map(|i| xs.dim(i).unwrap_static() as i64)
165 .collect();
166 out_dims.push(l_out as i64);
167 self.reshape_(y2, out_dims)
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174 use crate::{DType, Shape};
175
176 fn dims(g: &Graph, id: NodeId) -> Vec<usize> {
177 g.shape(id)
178 .dims()
179 .iter()
180 .map(|d| d.unwrap_static())
181 .collect()
182 }
183 fn input(g: &mut Graph, shape: &[usize]) -> NodeId {
184 g.input("x", Shape::new(shape, DType::F32))
185 }
186
187 #[test]
188 fn conv_transpose1d_upsamples_by_stride() {
189 let mut g = Graph::new("ct1d");
190 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);
193 assert_eq!(dims(&g, y), vec![2, 5, 16]);
195 }
196
197 #[test]
198 fn interpolate1d_linear_shape() {
199 let mut g = Graph::new("interp");
200 let x = input(&mut g, &[2, 4, 10]);
201 let y = g.interpolate1d(x, 25, InterpMode::Linear);
202 assert_eq!(dims(&g, y), vec![2, 4, 25]);
203 }
204
205 #[test]
206 fn interpolate1d_nearest_identity() {
207 let mut g = Graph::new("interp_id");
208 let x = input(&mut g, &[3, 7]);
209 let y = g.interpolate1d(x, 7, InterpMode::Nearest);
210 assert_eq!(y, x); }
212}