1use super::config::{SAM_EMBED_HW, SamEncoderConfig};
34use super::preprocess::{SamPreprocessWeights, extract_preprocess_weights};
35use anyhow::{Result, anyhow, ensure};
36use rlx_core::vision_ops_ir::{bhwc_to_nchw, conv2d_no_bias, layer_norm2d_nchw};
37use rlx_core::weight_map::WeightMap;
38use rlx_ir::HirGraphExt;
39use rlx_ir::hir::{HirModule, HirMut, HirNodeId};
40use rlx_ir::*;
41use std::collections::HashMap;
42
43struct SamBuilder {
44 hir: HirModule,
45 params: HashMap<String, Vec<f32>>,
46}
47
48impl SamBuilder {
49 fn new(name: &str) -> Self {
50 Self {
51 hir: HirModule::new(name),
52 params: HashMap::new(),
53 }
54 }
55
56 fn m(&mut self) -> HirMut<'_> {
57 HirMut::new(&mut self.hir)
58 }
59}
60
61#[allow(dead_code)]
62fn lower_hir(hir: HirModule) -> Result<Graph> {
63 Graph::from_hir(hir).map_err(|e| anyhow!("{e}"))
64}
65
66pub fn build_sam_encoder_hir(
73 cfg: &SamEncoderConfig,
74 weights: &mut WeightMap,
75) -> Result<(HirModule, HashMap<String, Vec<f32>>, SamPreprocessWeights)> {
76 let mut b = SamBuilder::new("sam_image_encoder");
77 let f = DType::F32;
78
79 let preprocess = extract_preprocess_weights(weights, cfg)?;
83
84 let e = cfg.embed_dim;
85 let nh = cfg.num_heads;
86 let dh = cfg.head_dim();
87 let scale = 1.0 / (dh as f32).sqrt();
88 let eps = cfg.layer_norm_eps as f32;
89 let hw = SAM_EMBED_HW;
90 let s = hw * hw; let hidden_input = b.m().input("hidden", Shape::new(&[1, s, e], f));
94
95 let mut x = hidden_input;
96 for layer_idx in 0..cfg.depth {
97 let lp = format!("image_encoder.blocks.{layer_idx}");
98 let is_global = cfg.global_attn_indexes.contains(&layer_idx);
99 let ws = if is_global { 0 } else { cfg.window_size };
100
101 let n1_g = load_p(&mut b, weights, &format!("{lp}.norm1.weight"), false)?;
103 let n1_b = load_p(&mut b, weights, &format!("{lp}.norm1.bias"), false)?;
104 let normed = b.m().ln(x, n1_g, n1_b, eps);
105
106 let attn_out = if ws == 0 {
108 attention_global(
109 &mut b,
110 weights,
111 &lp,
112 normed,
113 e,
114 nh,
115 dh,
116 scale,
117 hw,
118 cfg.use_rel_pos,
119 cfg.qkv_bias,
120 )?
121 } else {
122 attention_windowed(
123 &mut b,
124 weights,
125 &lp,
126 normed,
127 e,
128 nh,
129 dh,
130 scale,
131 hw,
132 ws,
133 cfg.use_rel_pos,
134 cfg.qkv_bias,
135 )?
136 };
137
138 x = b.m().add(x, attn_out);
140
141 let n2_g = load_p(&mut b, weights, &format!("{lp}.norm2.weight"), false)?;
143 let n2_b = load_p(&mut b, weights, &format!("{lp}.norm2.bias"), false)?;
144 let normed2 = b.m().ln(x, n2_g, n2_b, eps);
145
146 let fc1_w = load_p(&mut b, weights, &format!("{lp}.mlp.lin1.weight"), true)?;
147 let fc1_b = load_p(&mut b, weights, &format!("{lp}.mlp.lin1.bias"), false)?;
148 let fc2_w = load_p(&mut b, weights, &format!("{lp}.mlp.lin2.weight"), true)?;
149 let fc2_b = load_p(&mut b, weights, &format!("{lp}.mlp.lin2.bias"), false)?;
150
151 let up_mm = b.m().mm(normed2, fc1_w);
152 let up = b.m().add(up_mm, fc1_b);
153 let act = b.m().gelu(up);
157 let down_mm = b.m().mm(act, fc2_w);
158 let ffn = b.m().add(down_mm, fc2_b);
159
160 x = b.m().add(x, ffn);
161 }
162
163 let oc = cfg.out_chans;
169 let nchw = bhwc_to_nchw(&mut b.m(), x, 1, hw, hw, e);
170 let c1_w = load_p(&mut b, weights, "image_encoder.neck.0.weight", false)?;
171 let feat = conv2d_no_bias(&mut b.m(), nchw, c1_w, 1, oc, 1, 1, [1, 1], [0, 0], hw, hw);
172 let ln1_g = load_p(&mut b, weights, "image_encoder.neck.1.weight", false)?;
173 let ln1_b = load_p(&mut b, weights, "image_encoder.neck.1.bias", false)?;
174 let feat = layer_norm2d_nchw(&mut b.m(), feat, ln1_g, ln1_b, eps);
175 let c2_w = load_p(&mut b, weights, "image_encoder.neck.2.weight", false)?;
176 let feat = conv2d_no_bias(&mut b.m(), feat, c2_w, 1, oc, 3, 3, [1, 1], [1, 1], hw, hw);
177 let ln2_g = load_p(&mut b, weights, "image_encoder.neck.3.weight", false)?;
178 let ln2_b = load_p(&mut b, weights, "image_encoder.neck.3.bias", false)?;
179 let out = layer_norm2d_nchw(&mut b.m(), feat, ln2_g, ln2_b, eps);
180
181 b.hir.set_outputs(vec![out]);
182
183 Ok((b.hir, b.params, preprocess))
184}
185
186pub fn build_sam_encoder_graph(
188 cfg: &SamEncoderConfig,
189 weights: &mut WeightMap,
190) -> Result<(Graph, HashMap<String, Vec<f32>>, SamPreprocessWeights)> {
191 let built = super::flow::build_sam_encoder_built(cfg, weights)?;
192 let (graph, params) = rlx_core::flow_util::graph_from_built(built.model)?;
193 Ok((graph, params, built.preprocess))
194}
195
196#[allow(clippy::too_many_arguments)]
198fn attention_global(
199 sb: &mut SamBuilder,
200 w: &mut WeightMap,
201 lp: &str,
202 x: HirNodeId, e: usize,
204 nh: usize,
205 dh: usize,
206 scale: f32,
207 hw: usize,
208 use_rel_pos: bool,
209 qkv_bias: bool,
210) -> Result<HirNodeId> {
211 let s = hw * hw;
212 decomposed_attention(
213 sb,
214 w,
215 lp,
216 x,
217 e,
218 nh,
219 dh,
220 scale,
221 hw,
222 hw,
223 s,
224 1,
225 use_rel_pos,
226 qkv_bias,
227 )
228}
229
230#[allow(clippy::too_many_arguments)]
233fn attention_windowed(
234 sb: &mut SamBuilder,
235 w: &mut WeightMap,
236 lp: &str,
237 x: HirNodeId, e: usize,
239 nh: usize,
240 dh: usize,
241 scale: f32,
242 hw: usize,
243 ws: usize,
244 use_rel_pos: bool,
245 qkv_bias: bool,
246) -> Result<HirNodeId> {
247 let bhwc = sb.m().reshape_(x, vec![1, hw as i64, hw as i64, e as i64]);
249
250 let pad = (ws - hw % ws) % ws;
251 let hw_p = hw + pad;
252 let n_win_per_side = hw_p / ws;
253 let n_win = n_win_per_side * n_win_per_side;
254
255 let padded = if pad > 0 {
257 let z_h = pad_zero_param(sb, &format!("{lp}.attn._pad_h"), &[1, pad, hw, e]);
258 let p1 = sb.m().concat_(vec![bhwc, z_h], 1); let z_w = pad_zero_param(sb, &format!("{lp}.attn._pad_w"), &[1, hw_p, pad, e]);
260 sb.m().concat_(vec![p1, z_w], 2) } else {
262 bhwc
263 };
264
265 let reshaped = sb.m().reshape_(
268 padded,
269 vec![
270 1,
271 n_win_per_side as i64,
272 ws as i64,
273 n_win_per_side as i64,
274 ws as i64,
275 e as i64,
276 ],
277 );
278 let transposed = sb.m().transpose_(reshaped, vec![0, 1, 3, 2, 4, 5]);
279 let windowed = sb.m().reshape_(
280 transposed,
281 vec![n_win as i64, ws as i64, ws as i64, e as i64],
282 );
283 let win_flat = sb
285 .m()
286 .reshape_(windowed, vec![n_win as i64, (ws * ws) as i64, e as i64]);
287
288 let attn_out = decomposed_attention(
291 sb,
292 w,
293 lp,
294 win_flat,
295 e,
296 nh,
297 dh,
298 scale,
299 ws,
300 ws,
301 ws * ws,
302 n_win,
303 use_rel_pos,
304 qkv_bias,
305 )?;
306 let un = sb
311 .m()
312 .reshape_(attn_out, vec![n_win as i64, ws as i64, ws as i64, e as i64]);
313 let un = sb.m().reshape_(
314 un,
315 vec![
316 1,
317 n_win_per_side as i64,
318 n_win_per_side as i64,
319 ws as i64,
320 ws as i64,
321 e as i64,
322 ],
323 );
324 let un = sb.m().transpose_(un, vec![0, 1, 3, 2, 4, 5]);
325 let un = sb
326 .m()
327 .reshape_(un, vec![1, hw_p as i64, hw_p as i64, e as i64]);
328 let un = if pad > 0 {
330 let cropped_h = sb.m().narrow_(un, 1, 0, hw);
331 sb.m().narrow_(cropped_h, 2, 0, hw)
332 } else {
333 un
334 };
335 Ok(sb.m().reshape_(un, vec![1, (hw * hw) as i64, e as i64]))
337}
338
339#[allow(clippy::too_many_arguments)]
346fn decomposed_attention(
347 sb: &mut SamBuilder,
348 w: &mut WeightMap,
349 lp: &str,
350 x: HirNodeId, e: usize,
352 nh: usize,
353 dh: usize,
354 scale: f32,
355 h: usize,
356 w_dim: usize,
357 s: usize, batch: usize,
359 use_rel_pos: bool,
360 qkv_bias: bool,
361) -> Result<HirNodeId> {
362 let qkv_w_node = load_p(sb, w, &format!("{lp}.attn.qkv.weight"), true)?;
367 let qkv_b_node = if qkv_bias {
368 Some(load_p(sb, w, &format!("{lp}.attn.qkv.bias"), false)?)
369 } else {
370 None
371 };
372 let qkv_mm = sb.m().mm(x, qkv_w_node); let qkv = if let Some(b) = qkv_b_node {
374 sb.m().add(qkv_mm, b)
375 } else {
376 qkv_mm
377 };
378
379 let qkv5 = sb
383 .m()
384 .reshape_(qkv, vec![batch as i64, s as i64, 3, nh as i64, dh as i64]);
385 let qkv_perm = sb.m().transpose_(qkv5, vec![2, 0, 3, 1, 4]); let qkv_flat = sb
387 .m()
388 .reshape_(qkv_perm, vec![3, (batch * nh) as i64, s as i64, dh as i64]);
389 let q = sb.m().narrow_(qkv_flat, 0, 0, 1);
390 let q = sb
391 .m()
392 .reshape_(q, vec![(batch * nh) as i64, s as i64, dh as i64]);
393 let k = sb.m().narrow_(qkv_flat, 0, 1, 1);
394 let k = sb
395 .m()
396 .reshape_(k, vec![(batch * nh) as i64, s as i64, dh as i64]);
397 let v = sb.m().narrow_(qkv_flat, 0, 2, 1);
398 let v = sb
399 .m()
400 .reshape_(v, vec![(batch * nh) as i64, s as i64, dh as i64]);
401
402 let scale_node = scalar_param(sb, &format!("{lp}.attn._scale"), scale);
404 let q_scaled = sb.m().mul(q, scale_node);
405 let k_t = sb.m().transpose_(k, vec![0, 2, 1]); let scores = sb.m().mm(q_scaled, k_t); let scores = if use_rel_pos {
410 let (mut r_h_data, mut r_w_data) = extract_rel_pos(w, lp, h, w_dim, dh)?;
414 if rlx_ir::env::flag("RLX_SAM_DEBUG_ZERO_RELPOS") {
419 r_h_data.iter_mut().for_each(|v| *v = 0.0);
420 r_w_data.iter_mut().for_each(|v| *v = 0.0);
421 }
422 if rlx_ir::env::flag("RLX_SAM_DEBUG_ZERO_RELH") {
423 r_h_data.iter_mut().for_each(|v| *v = 0.0);
424 }
425 if rlx_ir::env::flag("RLX_SAM_DEBUG_ZERO_RELW") {
426 r_w_data.iter_mut().for_each(|v| *v = 0.0);
427 }
428 let r_h_node = const_param(
429 sb,
430 &format!("{lp}.attn._rel_h_indexed"),
431 &[h, h, dh],
432 r_h_data,
433 );
434 let r_w_node = const_param(
435 sb,
436 &format!("{lp}.attn._rel_w_indexed"),
437 &[w_dim, w_dim, dh],
438 r_w_data,
439 );
440 add_decomposed_rel_pos(sb, scores, q, r_h_node, r_w_node, batch, nh, h, w_dim, dh)?
441 } else {
442 scores
443 };
444
445 let attn_w = sb.m().sm(scores, -1);
447
448 let attn_v = sb.m().mm(attn_w, v);
450
451 let reshaped = sb
453 .m()
454 .reshape_(attn_v, vec![batch as i64, nh as i64, s as i64, dh as i64]);
455 let perm = sb.m().transpose_(reshaped, vec![0, 2, 1, 3]); let merged = sb
457 .m()
458 .reshape_(perm, vec![batch as i64, s as i64, e as i64]);
459
460 let proj_w = load_p(sb, w, &format!("{lp}.attn.proj.weight"), true)?;
462 let proj_b = load_p(sb, w, &format!("{lp}.attn.proj.bias"), false)?;
463 let proj_mm = sb.m().mm(merged, proj_w);
464 Ok(sb.m().add(proj_mm, proj_b))
465}
466
467#[allow(clippy::too_many_arguments)]
476fn add_decomposed_rel_pos(
477 sb: &mut SamBuilder,
478 scores: HirNodeId, q: HirNodeId, r_h: HirNodeId, r_w: HirNodeId, batch: usize,
483 nh: usize,
484 h: usize,
485 w: usize,
486 dh: usize,
487) -> Result<HirNodeId> {
488 let bh = batch * nh;
489 let r_q = sb
491 .m()
492 .reshape_(q, vec![bh as i64, h as i64, w as i64, dh as i64]);
493
494 let mut rel_h_slices: Vec<HirNodeId> = Vec::with_capacity(h);
503 for h_q in 0..h {
504 let rq_slice = sb.m().narrow_(r_q, 1, h_q, 1); let rq_slice = sb
507 .m()
508 .reshape_(rq_slice, vec![bh as i64, w as i64, dh as i64]);
509 let rh_slice = sb.m().narrow_(r_h, 0, h_q, 1); let rh_slice = sb.m().reshape_(rh_slice, vec![h as i64, dh as i64]); let rh_t = sb.m().transpose_(rh_slice, vec![1, 0]); let mm = sb.m().mm(rq_slice, rh_t); let mm5 = sb.m().reshape_(mm, vec![bh as i64, 1, w as i64, h as i64]);
516 rel_h_slices.push(mm5);
517 }
518 let rel_h_4d = sb.m().concat_(rel_h_slices, 1); let mut rel_w_slices: Vec<HirNodeId> = Vec::with_capacity(w);
522 for w_q in 0..w {
523 let rq_slice = sb.m().narrow_(r_q, 2, w_q, 1); let rq_slice = sb
525 .m()
526 .reshape_(rq_slice, vec![bh as i64, h as i64, dh as i64]);
527 let rw_slice = sb.m().narrow_(r_w, 0, w_q, 1); let rw_slice = sb.m().reshape_(rw_slice, vec![w as i64, dh as i64]); let rw_t = sb.m().transpose_(rw_slice, vec![1, 0]); let mm = sb.m().mm(rq_slice, rw_t); let mm5 = sb.m().reshape_(mm, vec![bh as i64, h as i64, 1, w as i64]);
532 rel_w_slices.push(mm5);
533 }
534 let rel_w_4d = sb.m().concat_(rel_w_slices, 2); let scores_5d = sb.m().reshape_(
547 scores,
548 vec![bh as i64, h as i64, w as i64, h as i64, w as i64],
549 );
550 let rel_h_5d = sb
551 .m()
552 .reshape_(rel_h_4d, vec![bh as i64, h as i64, w as i64, h as i64, 1]);
553 let rel_h_tiled = {
554 let mut copies = Vec::with_capacity(w);
555 for _ in 0..w {
556 copies.push(rel_h_5d);
557 }
558 sb.m().concat_(copies, 4) };
560 let rel_w_5d = sb
561 .m()
562 .reshape_(rel_w_4d, vec![bh as i64, h as i64, w as i64, 1, w as i64]);
563 let rel_w_tiled = {
564 let mut copies = Vec::with_capacity(h);
565 for _ in 0..h {
566 copies.push(rel_w_5d);
567 }
568 sb.m().concat_(copies, 3) };
570 let s1 = sb.m().add(scores_5d, rel_h_tiled);
571 let s2 = sb.m().add(s1, rel_w_tiled);
572 Ok(sb
573 .m()
574 .reshape_(s2, vec![bh as i64, (h * w) as i64, (h * w) as i64]))
575}
576
577fn extract_rel_pos(
584 weights: &mut WeightMap,
585 lp: &str,
586 h: usize,
587 w: usize,
588 dh: usize,
589) -> Result<(Vec<f32>, Vec<f32>)> {
590 let (rel_h_raw, rh_shape) = weights.take(&format!("{lp}.attn.rel_pos_h"))?;
591 let (rel_w_raw, rw_shape) = weights.take(&format!("{lp}.attn.rel_pos_w"))?;
592 ensure!(
593 rh_shape == vec![2 * h - 1, dh],
594 "{lp}.attn.rel_pos_h expected [{}, {dh}], got {rh_shape:?}",
595 2 * h - 1
596 );
597 ensure!(
598 rw_shape == vec![2 * w - 1, dh],
599 "{lp}.attn.rel_pos_w expected [{}, {dh}], got {rw_shape:?}",
600 2 * w - 1
601 );
602
603 let mut r_h = vec![0f32; h * h * dh];
604 for q in 0..h {
605 for k in 0..h {
606 let idx = (q as isize - k as isize + (h as isize - 1)) as usize;
607 let src = &rel_h_raw[idx * dh..(idx + 1) * dh];
608 let dst = &mut r_h[(q * h + k) * dh..(q * h + k + 1) * dh];
609 dst.copy_from_slice(src);
610 }
611 }
612 let mut r_w = vec![0f32; w * w * dh];
613 for q in 0..w {
614 for k in 0..w {
615 let idx = (q as isize - k as isize + (w as isize - 1)) as usize;
616 let src = &rel_w_raw[idx * dh..(idx + 1) * dh];
617 let dst = &mut r_w[(q * w + k) * dh..(q * w + k + 1) * dh];
618 dst.copy_from_slice(src);
619 }
620 }
621 Ok((r_h, r_w))
622}
623
624pub struct NeckWeights {
630 pub conv1_w: Vec<f32>, pub ln1_g: Vec<f32>, pub ln1_b: Vec<f32>,
633 pub conv2_w: Vec<f32>, pub ln2_g: Vec<f32>,
635 pub ln2_b: Vec<f32>,
636 pub embed_dim: usize,
637 pub out_chans: usize,
638 pub eps: f32,
639}
640
641#[allow(dead_code)]
642fn extract_neck_weights(weights: &mut WeightMap, cfg: &SamEncoderConfig) -> Result<NeckWeights> {
643 let (conv1_w_raw, c1_shape) = weights.take("image_encoder.neck.0.weight")?;
644 ensure!(
645 c1_shape == vec![cfg.out_chans, cfg.embed_dim, 1, 1],
646 "neck.0.weight expected [{}, {}, 1, 1], got {c1_shape:?}",
647 cfg.out_chans,
648 cfg.embed_dim
649 );
650 let conv1_w = conv1_w_raw; let (ln1_g, _) = weights.take("image_encoder.neck.1.weight")?;
652 let (ln1_b, _) = weights.take("image_encoder.neck.1.bias")?;
653 let (conv2_w, c2_shape) = weights.take("image_encoder.neck.2.weight")?;
654 ensure!(
655 c2_shape == vec![cfg.out_chans, cfg.out_chans, 3, 3],
656 "neck.2.weight expected [{}, {}, 3, 3], got {c2_shape:?}",
657 cfg.out_chans,
658 cfg.out_chans
659 );
660 let (ln2_g, _) = weights.take("image_encoder.neck.3.weight")?;
661 let (ln2_b, _) = weights.take("image_encoder.neck.3.bias")?;
662 Ok(NeckWeights {
663 conv1_w,
664 ln1_g,
665 ln1_b,
666 conv2_w,
667 ln2_g,
668 ln2_b,
669 embed_dim: cfg.embed_dim,
670 out_chans: cfg.out_chans,
671 eps: cfg.layer_norm_eps as f32,
672 })
673}
674
675pub fn apply_neck_host(neck: &NeckWeights, body_out: &[f32], hw: usize) -> Vec<f32> {
679 let e = neck.embed_dim;
680 let oc = neck.out_chans;
681 let eps = neck.eps;
682
683 let s = hw * hw;
687 let mut feat = vec![0f32; s * oc]; for si in 0..s {
689 for oi in 0..oc {
690 let mut acc = 0f32;
691 for ei in 0..e {
692 acc += body_out[si * e + ei] * neck.conv1_w[oi * e + ei];
693 }
694 feat[si * oc + oi] = acc;
695 }
696 }
697
698 layernorm2d_inplace(&mut feat, s, oc, &neck.ln1_g, &neck.ln1_b, eps);
700
701 let mut nchw = vec![0f32; oc * hw * hw];
704 for y in 0..hw {
705 for x in 0..hw {
706 for c in 0..oc {
707 nchw[c * hw * hw + y * hw + x] = feat[(y * hw + x) * oc + c];
708 }
709 }
710 }
711 let conv2_out = conv2d_3x3_pad1(&nchw, oc, oc, hw, hw, &neck.conv2_w);
712
713 let mut bhwc = vec![0f32; s * oc];
715 for c in 0..oc {
716 for y in 0..hw {
717 for x in 0..hw {
718 bhwc[(y * hw + x) * oc + c] = conv2_out[c * hw * hw + y * hw + x];
719 }
720 }
721 }
722 layernorm2d_inplace(&mut bhwc, s, oc, &neck.ln2_g, &neck.ln2_b, eps);
723
724 let mut out_nchw = vec![0f32; oc * hw * hw];
725 for y in 0..hw {
726 for x in 0..hw {
727 for c in 0..oc {
728 out_nchw[c * hw * hw + y * hw + x] = bhwc[(y * hw + x) * oc + c];
729 }
730 }
731 }
732 out_nchw
733}
734
735fn layernorm2d_inplace(data: &mut [f32], s: usize, c: usize, g: &[f32], b: &[f32], eps: f32) {
737 for si in 0..s {
738 let row = &mut data[si * c..(si + 1) * c];
739 let mean: f32 = row.iter().sum::<f32>() / c as f32;
740 let var: f32 = row.iter().map(|v| (v - mean) * (v - mean)).sum::<f32>() / c as f32;
741 let inv = 1.0 / (var + eps).sqrt();
742 for k in 0..c {
743 row[k] = (row[k] - mean) * inv * g[k] + b[k];
744 }
745 }
746}
747
748fn conv2d_3x3_pad1(
752 input: &[f32],
753 in_c: usize,
754 out_c: usize,
755 h: usize,
756 w: usize,
757 weight: &[f32], ) -> Vec<f32> {
759 let mut out = vec![0f32; out_c * h * w];
760 for oc in 0..out_c {
761 for y in 0..h {
762 for x in 0..w {
763 let mut acc = 0f32;
764 for ic in 0..in_c {
765 for ky in 0..3 {
766 let iy = y as isize + ky as isize - 1;
767 if iy < 0 || iy >= h as isize {
768 continue;
769 }
770 for kx in 0..3 {
771 let ix = x as isize + kx as isize - 1;
772 if ix < 0 || ix >= w as isize {
773 continue;
774 }
775 let v = input[ic * h * w + iy as usize * w + ix as usize];
776 let wi = ((oc * in_c + ic) * 3 + ky) * 3 + kx;
777 acc += v * weight[wi];
778 }
779 }
780 }
781 out[oc * h * w + y * w + x] = acc;
782 }
783 }
784 }
785 out
786}
787
788fn load_p(
791 sb: &mut SamBuilder,
792 weights: &mut WeightMap,
793 key: &str,
794 transpose: bool,
795) -> Result<HirNodeId> {
796 let (data, shape) = if transpose {
797 weights
798 .take_transposed(key)
799 .map_err(|e| anyhow!("transpose-load `{key}`: {e}"))?
800 } else {
801 weights
802 .take(key)
803 .map_err(|e| anyhow!("load `{key}`: {e}"))?
804 };
805 let name = key.to_string();
806 let id = sb.m().param(&name, Shape::new(&shape, DType::F32));
807 sb.params.insert(name, data);
808 Ok(id)
809}
810
811#[allow(dead_code)]
812fn scalar_param(sb: &mut SamBuilder, name: &str, value: f32) -> HirNodeId {
813 let id = sb.m().param(name, Shape::new(&[1], DType::F32));
814 sb.params.insert(name.to_string(), vec![value]);
815 id
816}
817
818fn const_param(sb: &mut SamBuilder, name: &str, shape: &[usize], data: Vec<f32>) -> HirNodeId {
819 let id = sb.m().param(name, Shape::new(shape, DType::F32));
820 sb.params.insert(name.to_string(), data);
821 id
822}
823
824fn pad_zero_param(sb: &mut SamBuilder, name: &str, shape: &[usize]) -> HirNodeId {
825 let n: usize = shape.iter().product();
826 let id = sb.m().param(name, Shape::new(shape, DType::F32));
827 sb.params.insert(name.to_string(), vec![0f32; n]);
828 id
829}