1use rustc_hash::{FxHashMap, FxHashSet};
2
3use crate::config::selection::boltzmann;
4use crate::select::{SelectionReason, SelectionResult};
5use crate::types::{Fragment, FragmentId};
6
7fn boltzmann_weight(rel_score: f64, token_count: u32, beta: f64) -> f64 {
8 rel_score * (-beta * token_count as f64).exp()
9}
10
11fn nonoverlapping(selected: &[Fragment], frag: &Fragment) -> bool {
12 selected.iter().all(|s| {
13 if s.path() != frag.path() {
14 return true;
15 }
16 frag.end_line() < s.start_line() || frag.start_line() > s.end_line()
17 })
18}
19
20pub fn boltzmann_select(
21 fragments: &[Fragment],
22 core_ids: &FxHashSet<FragmentId>,
23 rel: &FxHashMap<FragmentId, f64>,
24 budget_tokens: u32,
25 beta: f64,
26) -> SelectionResult {
27 if fragments.is_empty() {
28 return SelectionResult {
29 selected: Vec::new(),
30 reason: SelectionReason::NoCandidates,
31 used_tokens: 0,
32 utility: 0.0,
33 greedy_iters: 0,
34 stopping_certificate: 0.0,
35 };
36 }
37
38 let mut core: Vec<Fragment> = fragments
39 .iter()
40 .filter(|f| core_ids.contains(&f.id))
41 .cloned()
42 .collect();
43 core.sort_by_key(|f| f.token_count);
44
45 let mut selected: Vec<Fragment> = Vec::new();
46 let mut used: u32 = 0;
47 for f in core {
48 if used + f.token_count > budget_tokens {
49 continue;
50 }
51 if !nonoverlapping(&selected, &f) {
52 continue;
53 }
54 used += f.token_count;
55 selected.push(f);
56 }
57
58 let mut ranked: Vec<(f64, Fragment)> = fragments
59 .iter()
60 .filter(|f| !core_ids.contains(&f.id) && f.token_count > 0)
61 .map(|f| {
62 let r = rel.get(&f.id).copied().unwrap_or(0.0);
63 (boltzmann_weight(r, f.token_count, beta), f.clone())
64 })
65 .filter(|(w, _)| *w > 0.0)
66 .collect();
67 ranked.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(std::cmp::Ordering::Equal));
68
69 let mut total_utility = 0.0;
70 let mut reason = SelectionReason::TopK;
71 for (w, f) in ranked {
72 if used + f.token_count > budget_tokens {
73 reason = SelectionReason::BudgetExhausted;
74 continue;
75 }
76 if !nonoverlapping(&selected, &f) {
77 continue;
78 }
79 used += f.token_count;
80 total_utility += w;
81 selected.push(f);
82 }
83
84 let final_count = selected.len();
85 SelectionResult {
86 selected,
87 reason,
88 used_tokens: used,
89 utility: total_utility,
90 greedy_iters: final_count,
91 stopping_certificate: 0.0,
92 }
93}
94
95pub fn calibrate_beta(
96 fragments: &[Fragment],
97 core_ids: &FxHashSet<FragmentId>,
98 rel: &FxHashMap<FragmentId, f64>,
99 budget_tokens: u32,
100 epsilon: f64,
101) -> f64 {
102 let cfg = boltzmann();
103 let (mut lo, mut hi) = (cfg.beta_lo, cfg.beta_hi);
104 let target = budget_tokens as f64;
105 let tol = (target * epsilon).max(1.0);
106
107 let cost_lo = boltzmann_select(fragments, core_ids, rel, budget_tokens, lo).used_tokens as f64;
117 if cost_lo < target - tol {
118 return lo;
119 }
120 let cost_hi = boltzmann_select(fragments, core_ids, rel, budget_tokens, hi).used_tokens as f64;
121 if cost_hi >= target - tol {
122 return hi;
123 }
124
125 for _ in 0..cfg.bisect_iters {
126 let mid = (lo * hi).sqrt();
127 let cost =
128 boltzmann_select(fragments, core_ids, rel, budget_tokens, mid).used_tokens as f64;
129 if cost >= target - tol {
130 lo = mid;
131 } else {
132 hi = mid;
133 }
134 }
135 lo
136}
137
138#[cfg(test)]
139mod paper_claim_tests {
140 use super::*;
147 use crate::types::{FragmentId, FragmentKind};
148 use std::sync::Arc;
149
150 fn frag(name: &str, tokens: u32) -> Fragment {
151 Fragment {
152 id: FragmentId::new(Arc::from(format!("syn/{name}.rs")), 1, 10),
153 kind: FragmentKind::Function,
154 content: Arc::from(""),
155 identifiers: FxHashSet::default(),
156 token_count: tokens,
157 symbol_name: Some(name.to_lowercase()),
158 }
159 }
160
161 fn tilde_utility(set: &[usize], fragments: &[Fragment], rels: &[f64], beta: f64) -> f64 {
165 set.iter()
166 .map(|&i| rels[i] * (-beta * fragments[i].token_count as f64).exp())
167 .sum()
168 }
169
170 fn xorshift(state: &mut u64) -> u64 {
171 *state ^= *state << 13;
172 *state ^= *state >> 7;
173 *state ^= *state << 17;
174 *state
175 }
176
177 fn random_subset(n: usize, fraction: f64, rng: &mut u64) -> Vec<usize> {
178 (0..n)
179 .filter(|_| (xorshift(rng) % 1000) as f64 / 1000.0 < fraction)
180 .collect()
181 }
182
183 #[test]
184 fn claim_3a_boltzmann_reweighted_utility_is_modular() {
185 let fragments: Vec<Fragment> = (0..10)
186 .map(|i| frag(&format!("f_{i}"), 80 + i * 40))
187 .collect();
188 let rels: Vec<f64> = (0..10).map(|i| 0.2 + 0.07 * i as f64).collect();
189
190 let mut rng = 0xBADCAFE_u64;
191 for &beta in &[1e-4_f64, 1e-2, 0.1, 1.0, 10.0] {
192 for _ in 0..1000 {
193 let a = random_subset(10, 0.5, &mut rng);
194 let b = random_subset(10, 0.5, &mut rng);
195 let union: Vec<usize> = {
196 let s: FxHashSet<usize> = a.iter().chain(b.iter()).copied().collect();
197 let mut v: Vec<usize> = s.into_iter().collect();
198 v.sort();
199 v
200 };
201 let intersection: Vec<usize> = {
202 let sa: FxHashSet<usize> = a.iter().copied().collect();
203 let mut v: Vec<usize> = b.iter().copied().filter(|i| sa.contains(i)).collect();
204 v.sort();
205 v
206 };
207 let lhs = tilde_utility(&union, &fragments, &rels, beta);
208 let rhs = tilde_utility(&a, &fragments, &rels, beta)
209 + tilde_utility(&b, &fragments, &rels, beta)
210 - tilde_utility(&intersection, &fragments, &rels, beta);
211 assert!(
212 (lhs - rhs).abs() < 1e-9,
213 "Boltzmann tilde-utility not modular at β={beta}: \
214 lhs={lhs}, rhs={rhs}, |Δ|={}",
215 (lhs - rhs).abs()
216 );
217 }
218 }
219 }
220
221 #[test]
222 fn claim_8_boltzmann_cost_is_monotone_in_beta() {
223 let fragments: Vec<Fragment> = (0..12)
224 .map(|i| frag(&format!("f_{i}"), 50 + i * 60))
225 .collect();
226 let mut rels: FxHashMap<FragmentId, f64> = FxHashMap::default();
227 for (i, f) in fragments.iter().enumerate() {
228 rels.insert(f.id.clone(), 0.2 + 0.05 * i as f64);
229 }
230 let core_ids = FxHashSet::default();
231 let budget: u32 = 4096;
232
233 let betas = [1e-6, 1e-4, 1e-2, 1.0, 100.0];
234 let costs: Vec<u32> = betas
235 .iter()
236 .map(|&b| boltzmann_select(&fragments, &core_ids, &rels, budget, b).used_tokens)
237 .collect();
238
239 for i in 0..costs.len() - 1 {
240 assert!(
241 costs[i] >= costs[i + 1],
242 "Boltzmann cost not monotone in β: at β={}→{} cost {}→{} (sequence: {costs:?})",
243 betas[i],
244 betas[i + 1],
245 costs[i],
246 costs[i + 1]
247 );
248 }
249
250 assert!(
251 costs[0] > costs[costs.len() - 1],
252 "β sweep produced no cost variation; sweep range may be too narrow (costs: {costs:?})"
253 );
254 }
255}