1use crate::error::Error;
5use crate::lstm::Lstm;
6use crate::model::{ModelConfig, NamModel};
7use crate::wavenet::WaveNet;
8
9#[non_exhaustive]
15#[derive(Debug)]
16pub enum Model {
17 WaveNet(Box<WaveNet>),
21 Lstm(Lstm),
23 Slimmable(Slimmable),
25}
26
27#[derive(Debug)]
35pub struct Slimmable {
36 submodels: Vec<Model>,
37 max_values: Vec<f32>,
38 active: usize,
39}
40
41impl Slimmable {
42 pub fn len(&self) -> usize {
44 self.submodels.len()
45 }
46
47 pub fn is_empty(&self) -> bool {
49 self.submodels.is_empty()
50 }
51
52 pub fn active_index(&self) -> usize {
54 self.active
55 }
56
57 pub fn select(&mut self, index: usize) {
60 self.active = index.min(self.submodels.len() - 1);
61 }
62
63 pub fn set_slim_size(&mut self, value: f32) {
67 self.active = self
68 .max_values
69 .iter()
70 .position(|&m| m > value)
71 .unwrap_or(self.submodels.len() - 1);
72 }
73}
74
75impl Model {
76 pub fn from_nam(model: &NamModel) -> Result<Self, Error> {
78 match &model.config {
79 ModelConfig::WaveNet(_) => Ok(Model::WaveNet(Box::new(WaveNet::new(model)?))),
80 ModelConfig::Lstm(_) => Ok(Model::Lstm(Lstm::new(model)?)),
81 ModelConfig::Slimmable(cfg) => {
82 if cfg.submodels.is_empty() {
83 return Err(Error::UnsupportedFeature("empty SlimmableContainer".into()));
84 }
85 let mut submodels = Vec::with_capacity(cfg.submodels.len());
86 let mut max_values = Vec::with_capacity(cfg.submodels.len());
87 for sm in &cfg.submodels {
88 submodels.push(Model::from_nam(&sm.model)?);
89 max_values.push(sm.max_value);
90 }
91 let active = submodels.len() - 1; Ok(Model::Slimmable(Slimmable {
93 submodels,
94 max_values,
95 active,
96 }))
97 }
98 }
99 }
100
101 pub(crate) fn from_nam_conditioning(model: &NamModel) -> Result<Self, Error> {
106 match &model.config {
107 ModelConfig::WaveNet(_) => {
108 Ok(Model::WaveNet(Box::new(WaveNet::new_conditioning(model)?)))
109 }
110 _ => Model::from_nam(model),
111 }
112 }
113
114 pub fn process_buffer(&mut self, io: &mut [f32]) {
116 match self {
117 Model::WaveNet(w) => w.process_buffer(io),
118 Model::Lstm(l) => l.process_buffer(io),
119 Model::Slimmable(s) => s.submodels[s.active].process_buffer(io),
120 }
121 }
122
123 pub fn process_sample(&mut self, x: f32) -> f32 {
125 match self {
126 Model::WaveNet(w) => w.process_sample(x),
127 Model::Lstm(l) => l.process_sample(x),
128 Model::Slimmable(s) => s.submodels[s.active].process_sample(x),
129 }
130 }
131
132 pub fn reset(&mut self) {
134 match self {
135 Model::WaveNet(w) => w.reset(),
136 Model::Lstm(l) => l.reset(),
137 Model::Slimmable(s) => s.submodels.iter_mut().for_each(Model::reset),
141 }
142 }
143
144 pub fn receptive_field(&self) -> usize {
151 match self {
152 Model::WaveNet(w) => w.receptive_field(),
153 Model::Lstm(_) => 0,
154 Model::Slimmable(s) => s.submodels[s.active].receptive_field(),
155 }
156 }
157
158 pub(crate) fn num_output_channels(&self) -> usize {
162 match self {
163 Model::WaveNet(w) => w.num_output_channels(),
164 Model::Lstm(_) => 1,
165 Model::Slimmable(s) => s.submodels[s.active].num_output_channels(),
166 }
167 }
168
169 pub(crate) fn process_block_multi(&mut self, input: &[f32], out: &mut [f32], n: usize) {
173 match self {
174 Model::WaveNet(w) => w.process_block_multi(input, out, n),
175 Model::Lstm(l) => {
176 out[..n].copy_from_slice(&input[..n]);
178 l.process_buffer(&mut out[..n]);
179 }
180 Model::Slimmable(s) => s.submodels[s.active].process_block_multi(input, out, n),
181 }
182 }
183
184 pub fn as_slimmable(&self) -> Option<&Slimmable> {
188 match self {
189 Model::Slimmable(s) => Some(s),
190 _ => None,
191 }
192 }
193
194 pub fn as_slimmable_mut(&mut self) -> Option<&mut Slimmable> {
196 match self {
197 Model::Slimmable(s) => Some(s),
198 _ => None,
199 }
200 }
201}
202
203const _: () = {
208 fn assert_send_sync<T: Send + Sync>() {}
209 let _ = assert_send_sync::<Model>;
210 let _ = assert_send_sync::<WaveNet>;
211 let _ = assert_send_sync::<Lstm>;
212 let _ = assert_send_sync::<Slimmable>;
213};
214
215#[cfg(test)]
216mod tests {
217 use super::*;
218
219 const TINY_WAVENET: &str = r#"{
220 "version": "0.5.4", "architecture": "WaveNet",
221 "config": { "layers": [{
222 "input_size": 1, "condition_size": 1, "channels": 1, "head_size": 1,
223 "kernel_size": 1, "dilations": [1], "activation": "ReLU",
224 "gated": false, "head_bias": false
225 }], "head": null, "head_scale": 10.0 },
226 "weights": [1.0, 2.0, 0.5, 1.0, 3.0, 0.1, 0.5, 10.0]
227 }"#;
228
229 const TINY_LSTM: &str = r#"{
230 "version": "0.5.4", "architecture": "LSTM",
231 "config": { "input_size": 1, "hidden_size": 1, "num_layers": 1 },
232 "weights": [1.0,0.0, 0.0,0.0, 2.0,0.0, 0.0,0.0, 0.0,0.0,0.0,0.0, 0.0, 0.0, 3.0, 0.5]
233 }"#;
234
235 #[test]
236 fn from_nam_builds_wavenet() {
237 let m = NamModel::from_json_str(TINY_WAVENET).unwrap();
238 let mut model = Model::from_nam(&m).unwrap();
239 assert!(matches!(model, Model::WaveNet(_)));
240 let mut buf = [0.5_f32];
241 model.process_buffer(&mut buf);
242 assert!((buf[0] - 10.0).abs() < 1e-5, "got {}", buf[0]);
243 }
244
245 #[test]
246 fn receptive_field_zero_for_lstm_warmup_for_wavenet() {
247 let wn = Model::from_nam(&NamModel::from_json_str(TINY_WAVENET).unwrap()).unwrap();
249 assert_eq!(wn.receptive_field(), 1);
250 let lstm = Model::from_nam(&NamModel::from_json_str(TINY_LSTM).unwrap()).unwrap();
251 assert_eq!(lstm.receptive_field(), 0);
252 }
253
254 #[test]
255 fn from_nam_builds_lstm() {
256 let m = NamModel::from_json_str(TINY_LSTM).unwrap();
257 let mut model = Model::from_nam(&m).unwrap();
258 assert!(matches!(model, Model::Lstm(_)));
259 let mut buf = [0.5_f32];
260 model.process_buffer(&mut buf);
261 assert!((buf[0] - 1.1623).abs() < 1e-3, "got {}", buf[0]);
262 }
263
264 fn container() -> Model {
265 let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
266 .join("tests/fixtures/slimmable_container.nam");
267 let json = std::fs::read_to_string(path).expect("read container");
268 let m = NamModel::from_json_str(&json).expect("parse container");
269 Model::from_nam(&m).expect("build container")
270 }
271
272 #[test]
273 fn from_nam_builds_slimmable_default_full() {
274 let mut model = container();
275 let s = model.as_slimmable_mut().expect("is slimmable");
276 assert_eq!(s.len(), 3);
277 assert_eq!(s.active_index(), 2, "default = last/full submodel");
278 }
279
280 #[test]
281 fn select_clamps_out_of_range() {
282 let mut model = container();
283 let s = model.as_slimmable_mut().unwrap();
284 s.select(0);
285 assert_eq!(s.active_index(), 0);
286 s.select(99);
287 assert_eq!(s.active_index(), 2, "clamped to last");
288 }
289
290 #[test]
291 fn set_slim_size_picks_first_threshold_above_value() {
292 let mut model = container();
293 let s = model.as_slimmable_mut().unwrap();
294 s.set_slim_size(0.0);
296 assert_eq!(s.active_index(), 0); s.set_slim_size(0.5);
298 assert_eq!(s.active_index(), 1); s.set_slim_size(0.99);
300 assert_eq!(s.active_index(), 2); s.set_slim_size(5.0);
302 assert_eq!(s.active_index(), 2); }
304
305 #[test]
306 fn reset_clears_all_submodels_not_just_active() {
307 let mut model = container();
311
312 let mut fresh = container();
314 fresh.as_slimmable_mut().unwrap().select(0);
315 let mut probe_fresh = vec![0.3_f32; 8];
316 fresh.process_buffer(&mut probe_fresh);
317
318 model.as_slimmable_mut().unwrap().select(0);
320 let mut warm = vec![0.5_f32; 16];
321 model.process_buffer(&mut warm);
322 model.as_slimmable_mut().unwrap().select(2);
323 model.reset();
324 model.as_slimmable_mut().unwrap().select(0);
325 let mut probe = vec![0.3_f32; 8];
326 model.process_buffer(&mut probe);
327
328 for (i, (got, want)) in probe.iter().zip(&probe_fresh).enumerate() {
329 assert!(
330 (got - want).abs() < 1e-6,
331 "reset left submodel 0 dirty at sample {i}: {got} vs fresh {want}"
332 );
333 }
334 }
335
336 #[test]
337 fn slimmable_processes_through_active_submodel() {
338 let mut model = container();
339 model.as_slimmable_mut().unwrap().select(0); let mut a = vec![0.1_f32; 32];
341 model.process_buffer(&mut a);
342 model.as_slimmable_mut().unwrap().select(2); let mut b = vec![0.1_f32; 32];
344 model.process_buffer(&mut b);
345 }
346
347 #[test]
348 fn as_slimmable_none_for_plain_models() {
349 let mut wn = Model::from_nam(&NamModel::from_json_str(TINY_WAVENET).unwrap()).unwrap();
350 assert!(wn.as_slimmable_mut().is_none());
351 }
352}