metrale_model_layers/circuit_exec/
spec_key.rs1use std::collections::BTreeMap;
18
19use anyhow::{Context, Result, bail};
20use metrale_circuit::digest::plans_digest;
21use metrale_circuit::fuser::{fuse, fuse_table};
22use metrale_circuit::runtime::route_arms;
23use metrale_circuit::{AvailableKernels, Instance, Loaded, Mode};
24
25use super::sources::{instance, sources};
26
27pub const KEY_MODES: [Mode; 3] = [Mode::Verify, Mode::VerifyBatch, Mode::Draft];
29
30pub fn spec_cost_plan_digests(recipe: &str) -> Result<BTreeMap<String, String>> {
32 let inst = instance(recipe).context("the speculative cost model needs a circuit instance")?;
33 let loaded = metrale_circuit::load(&inst, sources(&inst)?)?;
34 let mut key = BTreeMap::new();
35 for mode in KEY_MODES {
36 let digests = mode_plan_digests(&inst, &loaded, mode)?;
37 if digests.is_empty() {
38 bail!(
39 "recipe `{recipe}`: its circuit instance declares no {} plan, so the speculative \
40 cost of that mode would be unkeyed (kernels/circuits/INSTANCES.toml)",
41 mode.name()
42 );
43 }
44 key.insert(
45 mode.name().to_string(),
46 plans_digest(digests.iter().map(String::as_str)),
47 );
48 }
49 Ok(key)
50}
51
52fn mode_plan_digests(inst: &Instance, loaded: &Loaded, mode: Mode) -> Result<Vec<String>> {
56 let avail = AvailableKernels::all_named_by(&loaded.rules);
58 let (circuit, rules, policy) = (&loaded.circuit, loaded.rules.as_slice(), &inst.policy);
59 let mut out = Vec::new();
60 if mode == Mode::VerifyBatch {
61 for table in &inst.verify_batch {
63 out.push(fuse_table(circuit, rules, &avail, policy, table)?.digest);
64 }
65 return Ok(out);
66 }
67 for &rows in inst.plans.get(&mode).into_iter().flatten() {
68 let plan = fuse(circuit, rules, &avail, policy, mode, rows)?;
69 let arms = route_arms(circuit, (rules, &loaded.runtime), &avail, policy, &plan)?;
70 out.push(plan.digest);
71 out.extend(arms.into_iter().map(|(_, arm)| arm.digest));
72 }
73 Ok(out)
74}
75
76#[cfg(test)]
77#[path = "spec_key_tests.rs"]
78mod tests;