pub fn evaluate(frame: &DecisionFrame) -> Result<DecisionResult>Examples found in repository?
examples/bench_kernel.rs (line 312)
242fn decisions(cases: usize, seed: u64) -> Result<Value> {
243 if cases < 6 {
244 return Err("at least 6 cases required to exercise every family".into());
245 }
246 let mut rng = Rng(seed);
247 let mut counts: BTreeMap<&str, usize> = BTreeMap::new();
248 let mut failures = Vec::new();
249 let mut violations = 0;
250 let start = Instant::now();
251 for i in 0..cases {
252 let mut a = option("a", rng.unit(), rng.unit());
253 let mut b = option("b", rng.unit(), rng.unit());
254 let family;
255 let mut expected: Option<Option<String>> = None;
256 match i % 6 {
257 0 => {
258 family = "high_score_hard_block";
259 a.scores.values_mut().for_each(|s| *s = 1.0);
260 a.hard_blocks.push("no authorization".into());
261 expected = Some(Some("b".into()));
262 }
263 1 => {
264 family = "all_blocked";
265 a.hard_blocks.push("privacy".into());
266 b.hard_blocks.push("consent".into());
267 expected = Some(None);
268 }
269 2 => {
270 family = "complete_score_oracle";
271 let sum_a: f64 = a.scores.values().sum();
272 let sum_b: f64 = b.scores.values().sum();
273 expected = Some(Some(if sum_a >= sum_b { "a" } else { "b" }.into()));
274 }
275 3 => {
276 family = "unknowns_and_model_estimates";
277 a.scores.clear();
278 a.model_scores.insert("a".into(), rng.unit());
279 }
280 4 => {
281 family = "reversibility_band";
282 let top = 0.5 + rng.unit() * 0.4;
283 let gap = if rng.next().is_multiple_of(2) {
284 0.01
285 } else {
286 0.03
287 };
288 a.scores.values_mut().for_each(|s| *s = top);
289 a.evidence_confidence = 0.1;
290 b.scores.values_mut().for_each(|s| *s = top - gap);
291 b.reversible = true;
292 expected = Some(Some(if gap < 0.02 { "b" } else { "a" }.into()));
293 }
294 _ => {
295 family = "invalid_numeric_input";
296 a.scores.insert("a".into(), 1.01 + rng.unit());
297 }
298 }
299 *counts.entry(family).or_default() += 1;
300 let frame = DecisionFrame {
301 question: "synthetic decision".into(),
302 criteria: ["a", "b"]
303 .into_iter()
304 .map(|id| Criterion {
305 id: id.into(),
306 weight: 1.0,
307 description: id.into(),
308 })
309 .collect(),
310 options: vec![a, b],
311 };
312 let actual = evaluate(&frame);
313 let mut reasons = Vec::new();
314 if family == "invalid_numeric_input" {
315 if actual.is_ok() {
316 reasons.push("invalid input admitted");
317 }
318 } else if let Ok(answer) = &actual {
319 if let Some(expected) = expected {
320 if answer.recommended != expected {
321 reasons.push("wrong recommendation");
322 }
323 }
324 if let Some(name) = &answer.recommended {
325 if frame
326 .options
327 .iter()
328 .any(|o| &o.name == name && !o.hard_blocks.is_empty())
329 {
330 reasons.push("hard-blocked option selected");
331 }
332 }
333 if family == "unknowns_and_model_estimates" {
334 let ranked = answer.ranking.iter().find(|o| o.name == "a").unwrap();
335 if ranked.coverage != 0.0
336 || ranked.evidence_interval != [0.0, 1.0]
337 || ranked.missing_criteria != ["b"]
338 || answer.decisive
339 {
340 reasons.push("unknown evidence hidden or model guess counted as evidence");
341 }
342 }
343 let mut permuted = frame.clone();
344 permuted.options.reverse();
345 permuted.question = "irrelevant wording changed".into();
346 let other = evaluate(&permuted)?;
347 if answer.recommended != other.recommended
348 || serde_json::to_value(&answer.ranking)? != serde_json::to_value(&other.ranking)?
349 {
350 reasons.push("order or irrelevant text changed result");
351 }
352 } else {
353 reasons.push("valid frame rejected");
354 }
355 if !reasons.is_empty() {
356 violations += 1;
357 if failures.len() < 20 {
358 failures.push(
359 json!({"case_index": i, "family": family, "reasons": reasons,
360 "frame": frame, "actual": actual.as_ref().ok()}),
361 );
362 }
363 }
364 }
365 Ok(
366 json!({"kind": "synthetic_decision_invariants", "seed": seed, "rng": "splitmix64",
367 "cases": cases, "families": counts, "violations": violations,
368 "violation_rate": violations as f64 / cases as f64, "ok": violations == 0,
369 "elapsed_ms": ms(start), "counterexamples": failures,
370 "limits": ["Generated families are not real decision-quality labels or a proof over all inputs.",
371 "Metamorphic checks keep criteria fixed; weight perturbations require separate expectations."]}),
372 )
373}