1use ferric_core::{demo, matmul_cpu, max_abs_diff, Context};
4use wasm_bindgen::prelude::*;
5
6fn gen(m: u32, k: u32, n: u32) -> (Vec<f32>, Vec<f32>) {
8 let a: Vec<f32> = (0..(m * k) as usize).map(|i| ((i * 7 % 13) as f32 - 6.0) * 0.1).collect();
9 let b: Vec<f32> = (0..(k * n) as usize).map(|i| ((i * 5 % 11) as f32 - 5.0) * 0.1).collect();
10 (a, b)
11}
12
13#[wasm_bindgen]
16pub async fn ferric_matmul_demo(m: u32, k: u32, n: u32) -> std::result::Result<String, JsValue> {
17 console_error_panic_hook::set_once();
18 let ctx = Context::new().await.map_err(|e| JsValue::from_str(&e))?;
19 let (a, b) = gen(m, k, n);
20 let gpu = ctx.matmul(&a, &b, m, k, n).await.map_err(|e| JsValue::from_str(&e))?;
21 let cpu = matmul_cpu(&a, &b, m as usize, k as usize, n as usize);
22 let diff = max_abs_diff(&gpu, &cpu);
23 Ok(format!("{:?}|{:.3e}|{:?}", ctx.backend, diff, &gpu[..6.min(gpu.len())]))
24}
25
26#[wasm_bindgen]
30pub async fn ferric_lm_demo(prompt: String, steps: usize) -> std::result::Result<String, JsValue> {
31 console_error_panic_hook::set_once();
32 let ids: Vec<u32> = prompt.split(',').filter_map(|s| s.trim().parse().ok()).map(|v: u32| v % demo::VOCAB as u32).collect();
33 if ids.is_empty() {
34 return Err(JsValue::from_str("no valid token ids in prompt"));
35 }
36 let ctx = Context::new().await.map_err(|e| JsValue::from_str(&e))?;
37 let t0 = js_sys::Date::now();
38 let generated = demo::generate(&ctx, &ids, steps).await.map_err(|e| JsValue::from_str(&e))?;
39 let ms = js_sys::Date::now() - t0;
40 let gpu = demo::logits(&ctx, &ids).await.map_err(|e| JsValue::from_str(&e))?;
42 let diff = max_abs_diff(&gpu, &demo::logits_cpu(&ids));
43 let js = |v: &[u32]| v.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(",");
44 Ok(format!(
45 "{{\"backend\":\"{:?}\",\"adapter\":\"{}\",\"prompt\":[{}],\"generated\":[{}],\"layers\":{},\"vocab\":{},\"logit_diff\":{:.3e},\"ms\":{:.1}}}",
46 ctx.backend, ctx.adapter_name, js(&ids), js(&generated), demo::N_LAYERS, demo::VOCAB, diff, ms
47 ))
48}