sim_lib_discrete_search/
control.rs1use std::time::Duration;
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub enum SearchOrder {
8 DepthFirst,
10 BreadthFirst,
12 BestFirst,
14 AStar,
16 Beam {
19 width: usize,
21 },
22}
23
24impl SearchOrder {
25 pub fn label(self) -> &'static str {
27 match self {
28 Self::DepthFirst => "depth-first",
29 Self::BreadthFirst => "breadth-first",
30 Self::BestFirst => "best-first",
31 Self::AStar => "a-star",
32 Self::Beam { .. } => "beam",
33 }
34 }
35
36 pub(crate) fn policy_material(self) -> String {
37 match self {
38 Self::Beam { width } => format!("order=beam,width={width}"),
39 other => format!("order={}", other.label()),
40 }
41 }
42}
43
44#[derive(Clone, Copy, Debug, PartialEq, Eq)]
46pub struct WorkCosts {
47 pub expand: u64,
49 pub score: u64,
51 pub propagate: u64,
53 pub emit: u64,
55}
56
57impl Default for WorkCosts {
58 fn default() -> Self {
59 Self {
60 expand: 1,
61 score: 1,
62 propagate: 1,
63 emit: 1,
64 }
65 }
66}
67
68#[derive(Clone, Debug, PartialEq, Eq)]
70pub struct SearchControl {
71 pub order: SearchOrder,
73 pub seed: u64,
75 pub max_work: Option<u64>,
77 pub max_results: Option<usize>,
79 pub max_frontier: Option<usize>,
81 pub max_memory_nodes: Option<usize>,
83 pub max_time: Option<Duration>,
85 pub branch_and_bound: bool,
87 pub costs: WorkCosts,
89}
90
91impl Default for SearchControl {
92 fn default() -> Self {
93 Self {
94 order: SearchOrder::DepthFirst,
95 seed: 0,
96 max_work: None,
97 max_results: None,
98 max_frontier: None,
99 max_memory_nodes: None,
100 max_time: None,
101 branch_and_bound: false,
102 costs: WorkCosts::default(),
103 }
104 }
105}
106
107impl SearchControl {
108 pub fn with_order(mut self, order: SearchOrder) -> Self {
110 self.order = order;
111 self
112 }
113
114 pub fn with_seed(mut self, seed: u64) -> Self {
116 self.seed = seed;
117 self
118 }
119
120 pub fn with_max_work(mut self, max_work: u64) -> Self {
122 self.max_work = Some(max_work);
123 self
124 }
125
126 pub fn with_max_results(mut self, max_results: usize) -> Self {
128 self.max_results = Some(max_results);
129 self
130 }
131
132 pub fn with_max_frontier(mut self, max_frontier: usize) -> Self {
134 self.max_frontier = Some(max_frontier);
135 self
136 }
137
138 pub fn with_max_memory_nodes(mut self, max_memory_nodes: usize) -> Self {
140 self.max_memory_nodes = Some(max_memory_nodes);
141 self
142 }
143
144 pub fn with_max_time(mut self, max_time: Duration) -> Self {
146 self.max_time = Some(max_time);
147 self
148 }
149
150 pub fn with_branch_and_bound(mut self, enabled: bool) -> Self {
152 self.branch_and_bound = enabled;
153 self
154 }
155
156 pub fn with_costs(mut self, costs: WorkCosts) -> Self {
158 self.costs = costs;
159 self
160 }
161
162 pub(crate) fn validate(&self) -> Result<(), String> {
163 if matches!(self.order, SearchOrder::Beam { width: 0 }) {
164 return Err("beam width must be greater than zero".to_string());
165 }
166 if self.costs.expand == 0
167 || self.costs.score == 0
168 || self.costs.propagate == 0
169 || self.costs.emit == 0
170 {
171 return Err("work costs must be positive".to_string());
172 }
173 Ok(())
174 }
175
176 pub(crate) fn policy_material(&self) -> String {
177 format!(
178 "{};seed={};max_work={};max_results={};max_frontier={};max_memory_nodes={};max_time_ns={};branch_and_bound={};costs={},{},{},{}",
179 self.order.policy_material(),
180 self.seed,
181 option_u64(self.max_work),
182 option_usize(self.max_results),
183 option_usize(self.max_frontier),
184 option_usize(self.max_memory_nodes),
185 self.max_time
186 .map(|duration| duration.as_nanos().to_string())
187 .unwrap_or_else(|| "none".to_string()),
188 self.branch_and_bound,
189 self.costs.expand,
190 self.costs.score,
191 self.costs.propagate,
192 self.costs.emit,
193 )
194 }
195}
196
197fn option_u64(value: Option<u64>) -> String {
198 value
199 .map(|value| value.to_string())
200 .unwrap_or_else(|| "none".to_string())
201}
202
203fn option_usize(value: Option<usize>) -> String {
204 value
205 .map(|value| value.to_string())
206 .unwrap_or_else(|| "none".to_string())
207}