problemreductions/models/misc/
minimum_fault_detection_test_set.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
8use crate::traits::Problem;
9use crate::types::Min;
10use serde::{Deserialize, Deserializer, Serialize};
11use std::collections::{HashSet, VecDeque};
12
13inventory::submit! {
14 ProblemSchemaEntry {
15 name: "MinimumFaultDetectionTestSet",
16 display_name: "Minimum Fault Detection Test Set",
17 aliases: &[],
18 dimensions: &[],
19 module_path: module_path!(),
20 description: "Find minimum set of input-output paths covering all internal DAG vertices",
21 fields: &[
22 FieldInfo { name: "num_vertices", type_name: "usize", description: "Number of vertices in the DAG" },
23 FieldInfo { name: "arcs", type_name: "Vec<(usize, usize)>", description: "Directed arcs (u, v)" },
24 FieldInfo { name: "inputs", type_name: "Vec<usize>", description: "Input vertex indices" },
25 FieldInfo { name: "outputs", type_name: "Vec<usize>", description: "Output vertex indices" },
26 ],
27 }
28}
29
30#[derive(Debug, Clone, Serialize)]
61pub struct MinimumFaultDetectionTestSet {
62 num_vertices: usize,
64 arcs: Vec<(usize, usize)>,
66 inputs: Vec<usize>,
68 outputs: Vec<usize>,
70 #[serde(skip)]
73 coverage: Vec<HashSet<usize>>,
74}
75
76#[derive(Deserialize)]
77struct MinimumFaultDetectionTestSetData {
78 num_vertices: usize,
79 arcs: Vec<(usize, usize)>,
80 inputs: Vec<usize>,
81 outputs: Vec<usize>,
82}
83
84impl<'de> Deserialize<'de> for MinimumFaultDetectionTestSet {
85 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
86 where
87 D: Deserializer<'de>,
88 {
89 let data = MinimumFaultDetectionTestSetData::deserialize(deserializer)?;
90 let coverage =
91 Self::build_coverage(data.num_vertices, &data.arcs, &data.inputs, &data.outputs);
92 Ok(Self {
93 num_vertices: data.num_vertices,
94 arcs: data.arcs,
95 inputs: data.inputs,
96 outputs: data.outputs,
97 coverage,
98 })
99 }
100}
101
102impl MinimumFaultDetectionTestSet {
103 pub fn new(
110 num_vertices: usize,
111 arcs: Vec<(usize, usize)>,
112 inputs: Vec<usize>,
113 outputs: Vec<usize>,
114 ) -> Self {
115 assert!(!inputs.is_empty(), "Inputs must not be empty");
116 assert!(!outputs.is_empty(), "Outputs must not be empty");
117 for (i, &(u, v)) in arcs.iter().enumerate() {
118 assert!(
119 u < num_vertices && v < num_vertices,
120 "Arc {} ({}, {}) out of bounds for {} vertices",
121 i,
122 u,
123 v,
124 num_vertices
125 );
126 }
127 for &inp in &inputs {
128 assert!(
129 inp < num_vertices,
130 "Input vertex {} out of bounds for {} vertices",
131 inp,
132 num_vertices
133 );
134 }
135 for &out in &outputs {
136 assert!(
137 out < num_vertices,
138 "Output vertex {} out of bounds for {} vertices",
139 out,
140 num_vertices
141 );
142 }
143 let coverage = Self::build_coverage(num_vertices, &arcs, &inputs, &outputs);
144 Self {
145 num_vertices,
146 arcs,
147 inputs,
148 outputs,
149 coverage,
150 }
151 }
152
153 fn forward_reachable(num_vertices: usize, adj: &[Vec<usize>], start: usize) -> HashSet<usize> {
155 let mut visited = HashSet::new();
156 let mut queue = VecDeque::new();
157 visited.insert(start);
158 queue.push_back(start);
159 while let Some(v) = queue.pop_front() {
160 if v < adj.len() {
161 for &w in &adj[v] {
162 if visited.insert(w) {
163 queue.push_back(w);
164 }
165 }
166 }
167 }
168 let _ = num_vertices; visited
170 }
171
172 fn backward_reachable(
174 num_vertices: usize,
175 rev_adj: &[Vec<usize>],
176 start: usize,
177 ) -> HashSet<usize> {
178 let mut visited = HashSet::new();
179 let mut queue = VecDeque::new();
180 visited.insert(start);
181 queue.push_back(start);
182 while let Some(v) = queue.pop_front() {
183 if v < rev_adj.len() {
184 for &w in &rev_adj[v] {
185 if visited.insert(w) {
186 queue.push_back(w);
187 }
188 }
189 }
190 }
191 let _ = num_vertices;
192 visited
193 }
194
195 fn build_coverage(
197 num_vertices: usize,
198 arcs: &[(usize, usize)],
199 inputs: &[usize],
200 outputs: &[usize],
201 ) -> Vec<HashSet<usize>> {
202 let mut adj = vec![vec![]; num_vertices];
204 let mut rev_adj = vec![vec![]; num_vertices];
205 for &(u, v) in arcs {
206 adj[u].push(v);
207 rev_adj[v].push(u);
208 }
209
210 let fwd: Vec<HashSet<usize>> = inputs
212 .iter()
213 .map(|&inp| Self::forward_reachable(num_vertices, &adj, inp))
214 .collect();
215
216 let bwd: Vec<HashSet<usize>> = outputs
218 .iter()
219 .map(|&out| Self::backward_reachable(num_vertices, &rev_adj, out))
220 .collect();
221
222 let num_outputs = outputs.len();
223 let mut coverage = Vec::with_capacity(inputs.len() * num_outputs);
224 for (i_idx, _) in inputs.iter().enumerate() {
225 for (o_idx, _) in outputs.iter().enumerate() {
226 let cov: HashSet<usize> = fwd[i_idx].intersection(&bwd[o_idx]).copied().collect();
228 coverage.push(cov);
229 }
230 }
231 coverage
232 }
233
234 pub fn num_vertices(&self) -> usize {
236 self.num_vertices
237 }
238
239 pub fn num_arcs(&self) -> usize {
241 self.arcs.len()
242 }
243
244 pub fn arcs(&self) -> &[(usize, usize)] {
246 &self.arcs
247 }
248
249 pub fn inputs(&self) -> &[usize] {
251 &self.inputs
252 }
253
254 pub fn outputs(&self) -> &[usize] {
256 &self.outputs
257 }
258
259 pub fn num_inputs(&self) -> usize {
261 self.inputs.len()
262 }
263
264 pub fn num_outputs(&self) -> usize {
266 self.outputs.len()
267 }
268}
269
270impl Problem for MinimumFaultDetectionTestSet {
271 const NAME: &'static str = "MinimumFaultDetectionTestSet";
272 type Value = Min<usize>;
273
274 fn variant() -> Vec<(&'static str, &'static str)> {
275 crate::variant_params![]
276 }
277
278 fn dims(&self) -> Vec<usize> {
279 vec![2; self.inputs.len() * self.outputs.len()]
280 }
281
282 fn evaluate(&self, config: &[usize]) -> Min<usize> {
283 let num_pairs = self.inputs.len() * self.outputs.len();
284 if config.len() != num_pairs {
285 return Min(None);
286 }
287 if config.iter().any(|&c| c > 1) {
288 return Min(None);
289 }
290
291 let mut boundary = vec![false; self.num_vertices];
292 for &input in &self.inputs {
293 boundary[input] = true;
294 }
295 for &output in &self.outputs {
296 boundary[output] = true;
297 }
298 let required_internal_vertices =
299 boundary.iter().filter(|&&is_boundary| !is_boundary).count();
300
301 let mut covered: HashSet<usize> = HashSet::new();
303 let mut count = 0usize;
304 for (idx, &sel) in config.iter().enumerate() {
305 if sel == 1 {
306 count += 1;
307 covered.extend(
308 self.coverage[idx]
309 .iter()
310 .copied()
311 .filter(|&vertex| !boundary[vertex]),
312 );
313 }
314 }
315
316 if covered.len() == required_internal_vertices {
318 Min(Some(count))
319 } else {
320 Min(None)
321 }
322 }
323}
324
325crate::declare_variants! {
326 default MinimumFaultDetectionTestSet => "2^(num_inputs * num_outputs)",
327}
328
329#[cfg(feature = "example-db")]
330pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
331 vec![crate::example_db::specs::ModelExampleSpec {
337 id: "minimum_fault_detection_test_set",
338 instance: Box::new(MinimumFaultDetectionTestSet::new(
339 7,
340 vec![
341 (0, 2),
342 (0, 3),
343 (1, 3),
344 (1, 4),
345 (2, 5),
346 (3, 5),
347 (3, 6),
348 (4, 6),
349 ],
350 vec![0, 1],
351 vec![5, 6],
352 )),
353 optimal_config: vec![1, 0, 0, 1],
354 optimal_value: serde_json::json!(2),
355 }]
356}
357
358#[cfg(test)]
359#[path = "../../unit_tests/models/misc/minimum_fault_detection_test_set.rs"]
360mod tests;