sim_lib_discrete_search/
receipt.rs1use crate::SearchControl;
4
5#[derive(Clone, Debug, PartialEq, Eq)]
7pub enum SearchStatus {
8 Complete,
10 Partial,
12 Cancelled,
14 Infeasible,
16}
17
18#[derive(Clone, Debug, PartialEq, Eq)]
20pub struct SearchReceipt {
21 pub status: SearchStatus,
23 pub reason: Option<String>,
25 pub work_used: u64,
27 pub expanded: u64,
29 pub scored: u64,
31 pub propagated: u64,
33 pub emitted: u64,
35 pub pruned: u64,
37 pub max_frontier: usize,
39 pub result_count: usize,
41 pub seed: u64,
43 pub policy_digest: String,
45 pub digest: String,
47}
48
49impl SearchReceipt {
50 pub(crate) fn invalid(control: &SearchControl, reason: String) -> Self {
51 Self::finalize(
52 SearchStatus::Partial,
53 Some(format!("invalid control: {reason}")),
54 Metrics::default(),
55 control,
56 &[],
57 )
58 }
59
60 pub(crate) fn finalize(
61 status: SearchStatus,
62 reason: Option<String>,
63 metrics: Metrics,
64 control: &SearchControl,
65 output_material: &[String],
66 ) -> Self {
67 let policy_material = control.policy_material();
68 let policy_digest = stable_digest(&[policy_material.as_str()]);
69 let status_material = format!("{status:?}");
70 let reason_material = reason.clone().unwrap_or_default();
71 let metrics_material = format!(
72 "work={};expanded={};scored={};propagated={};emitted={};pruned={};max_frontier={};result_count={}",
73 metrics.work_used,
74 metrics.expanded,
75 metrics.scored,
76 metrics.propagated,
77 metrics.emitted,
78 metrics.pruned,
79 metrics.max_frontier,
80 metrics.result_count,
81 );
82 let mut parts = vec![
83 policy_material.as_str(),
84 status_material.as_str(),
85 reason_material.as_str(),
86 metrics_material.as_str(),
87 ];
88 parts.extend(output_material.iter().map(String::as_str));
89 let digest = stable_digest(&parts);
90
91 Self {
92 status,
93 reason,
94 work_used: metrics.work_used,
95 expanded: metrics.expanded,
96 scored: metrics.scored,
97 propagated: metrics.propagated,
98 emitted: metrics.emitted,
99 pruned: metrics.pruned,
100 max_frontier: metrics.max_frontier,
101 result_count: metrics.result_count,
102 seed: control.seed,
103 policy_digest,
104 digest,
105 }
106 }
107}
108
109#[derive(Clone, Debug, PartialEq, Eq)]
111pub struct SearchRun<Output> {
112 pub outputs: Vec<Output>,
114 pub receipt: SearchReceipt,
116}
117
118#[derive(Clone, Debug, Default, PartialEq, Eq)]
120pub(crate) struct Metrics {
121 pub(crate) work_used: u64,
122 pub(crate) expanded: u64,
123 pub(crate) scored: u64,
124 pub(crate) propagated: u64,
125 pub(crate) emitted: u64,
126 pub(crate) pruned: u64,
127 pub(crate) max_frontier: usize,
128 pub(crate) result_count: usize,
129}
130
131pub(crate) fn stable_digest(parts: &[&str]) -> String {
132 let mut hash = 0xcbf29ce484222325u64;
133 for part in parts {
134 for byte in part.as_bytes() {
135 hash ^= u64::from(*byte);
136 hash = hash.wrapping_mul(0x100000001b3);
137 }
138 hash ^= 0xff;
139 hash = hash.wrapping_mul(0x100000001b3);
140 }
141 format!("{hash:016x}")
142}