qualia_core_db/inference/lab/
ablate.rs1use std::path::Path;
4
5use crate::hardware_passport::measure_decode_proxy_tok_s;
6use crate::inference_modes::{set_inference_mode, InferenceMode};
7use crate::lab::experiment_log::{append_run_csv, ExperimentRun};
8use crate::llm_bench::{set_coop_gemv, set_ffn_fusion, set_kv_int8, set_resident_decode};
9
10#[derive(Debug, Clone)]
11pub struct AblationRow {
12 pub label: String,
13 pub tok_s: Option<f64>,
14 pub toggles: String,
15}
16
17pub fn run_ablation_matrix(model: &Path, tokens: u32, csv_out: Option<&Path>) -> Vec<AblationRow> {
19 let tokens = tokens.max(4).min(32);
20 let model_id = model
21 .file_name()
22 .and_then(|s| s.to_str())
23 .unwrap_or("model")
24 .to_string();
25
26 let cases: [(&str, bool, bool, bool, bool, InferenceMode); 6] = [
28 (
29 "baseline_resident",
30 true,
31 true,
32 true,
33 true,
34 InferenceMode::Portable,
35 ),
36 ("no_coop", true, false, true, true, InferenceMode::Portable),
37 (
38 "no_resident",
39 false,
40 true,
41 true,
42 true,
43 InferenceMode::Portable,
44 ),
45 ("kv_f32", true, true, true, false, InferenceMode::Portable),
46 (
47 "fast_verify",
48 true,
49 true,
50 true,
51 true,
52 InferenceMode::FastVerify,
53 ),
54 ("cuda_mode", true, true, true, true, InferenceMode::CudaTc),
55 ];
56
57 let mut rows = Vec::new();
58 let passport_key =
59 crate::hardware_passport::read_passport(&crate::hardware_passport::default_cache_path())
60 .map(|p| p.key)
61 .unwrap_or_default();
62
63 for (label, res, coop, ffn, kv, mode) in cases {
64 set_resident_decode(res);
65 set_coop_gemv(coop);
66 set_ffn_fusion(ffn);
67 set_kv_int8(kv);
68 set_inference_mode(mode);
69 std::env::set_var("QUALIA_INFERENCE_MODE", mode.as_str());
70
71 let toggles = format!(
72 "{{\"resident\":{res},\"coop\":{coop},\"ffn_fusion\":{ffn},\"kv_int8\":{kv},\"mode\":\"{}\"}}",
73 mode.as_str()
74 );
75 let tok_s = measure_decode_proxy_tok_s(model, tokens);
76 rows.push(AblationRow {
77 label: label.into(),
78 tok_s,
79 toggles: toggles.clone(),
80 });
81
82 if let Some(path) = csv_out {
83 let mut run = ExperimentRun {
84 run_id: ExperimentRun::new_id(),
85 utc: ExperimentRun::utc_now(),
86 git_sha: String::new(),
87 host_passport_key: passport_key.clone(),
88 adapter: String::new(),
89 backend: std::env::var("QUALIA_WGPU_BACKEND").unwrap_or_else(|_| "auto".into()),
90 model_id: model_id.clone(),
91 model_hash: String::new(),
92 layout: guess_layout(&model_id),
93 mode: mode.as_str().into(),
94 profile: "lab-ablate".into(),
95 toggles_json: toggles,
96 qualia_decode_tok_s: tok_s,
97 ollama_decode_tok_s: None,
98 a_gap: None,
99 prefill_tok_s: None,
100 phase_ns_json: "{}".into(),
101 n_ulp_max: None,
102 c_score: None,
103 notes: label.into(),
104 };
105 run.compute_a_gap();
106 let _ = append_run_csv(path, &run);
107 }
108 }
109
110 set_resident_decode(true);
112 set_coop_gemv(true);
113 set_ffn_fusion(true);
114 set_kv_int8(true);
115 set_inference_mode(InferenceMode::Portable);
116 std::env::remove_var("QUALIA_INFERENCE_MODE");
117
118 let forge_backends: &[(&str, &str)] = &[
124 ("forge_wgsl", "wgsl"),
125 ("forge_hlsl", "hlsl"),
126 ("forge_spirv", "spirv"),
127 ("forge_ptx", "ptx"),
128 ];
129
130 for &(label, backend) in forge_backends {
131 set_resident_decode(true);
132 set_coop_gemv(true);
133 set_ffn_fusion(true);
134 set_kv_int8(true);
135 set_inference_mode(InferenceMode::Portable);
136 std::env::set_var("QUALIA_INFERENCE_MODE", "portable");
137 std::env::set_var("QUALIA_FORGE_BACKEND", backend);
138
139 let toggles = format!(
140 "{{\"forge_backend\":\"{backend}\",\"resident\":true,\"coop\":true,\"ffn_fusion\":true,\"kv_int8\":true,\"mode\":\"portable\"}}"
141 );
142 let tok_s = measure_decode_proxy_tok_s(model, tokens);
143 rows.push(AblationRow {
144 label: label.into(),
145 tok_s,
146 toggles: toggles.clone(),
147 });
148
149 if let Some(path) = csv_out {
150 let mut run = ExperimentRun {
151 run_id: ExperimentRun::new_id(),
152 utc: ExperimentRun::utc_now(),
153 git_sha: String::new(),
154 host_passport_key: passport_key.clone(),
155 adapter: String::new(),
156 backend: format!("forge-{backend}"),
157 model_id: model_id.clone(),
158 model_hash: String::new(),
159 layout: guess_layout(&model_id),
160 mode: "portable".into(),
161 profile: "lab-ablate".into(),
162 toggles_json: toggles,
163 qualia_decode_tok_s: tok_s,
164 ollama_decode_tok_s: None,
165 a_gap: None,
166 prefill_tok_s: None,
167 phase_ns_json: "{}".into(),
168 n_ulp_max: None,
169 c_score: None,
170 notes: label.into(),
171 };
172 run.compute_a_gap();
173 let _ = append_run_csv(path, &run);
174 }
175 }
176
177 std::env::remove_var("QUALIA_FORGE_BACKEND");
179
180 rows
181}
182
183fn guess_layout(name: &str) -> String {
184 let n = name.to_ascii_lowercase();
185 if n.contains(".soa.") || n.contains("soa") {
186 "soa".into()
187 } else if n.contains("f16") {
188 "f16".into()
189 } else {
190 "verbatim".into()
191 }
192}
193
194pub fn format_ablation_report(rows: &[AblationRow]) -> String {
195 let mut s = String::from("Ablation matrix (decode-proxy tok/s)\n");
196 for r in rows {
197 s.push_str(&format!(
198 " {:20} {:>8}\n",
199 r.label,
200 r.tok_s
201 .map(|t| format!("{t:.3}"))
202 .unwrap_or_else(|| "fail".into())
203 ));
204 }
205 s
206}