problemreductions/rules/
minimumfaultdetectiontestset_ilp.rs1use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
9use crate::models::misc::MinimumFaultDetectionTestSet;
10use crate::reduction;
11use crate::rules::traits::{ReduceTo, ReductionResult};
12use std::collections::VecDeque;
13
14#[derive(Debug, Clone)]
16pub struct ReductionMFDTSToILP {
17 target: ILP<bool>,
18 num_inputs: usize,
19 num_outputs: usize,
20}
21
22impl ReductionResult for ReductionMFDTSToILP {
23 type Source = MinimumFaultDetectionTestSet;
24 type Target = ILP<bool>;
25
26 fn target_problem(&self) -> &ILP<bool> {
27 &self.target
28 }
29
30 fn extract_solution(
31 &self,
32 target_solution: &<Self::Target as crate::traits::Problem>::Solution,
33 ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
34 crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
35
36 Ok((0..self.num_inputs)
37 .map(|input| {
38 (0..self.num_outputs)
39 .map(|output| target_solution[input * self.num_outputs + output] == 1)
40 .collect()
41 })
42 .collect())
43 }
44}
45
46#[reduction(transform = {
51 exact {
52 num_vars = "num_inputs * num_outputs",
53 },
54 upper_bound {
55 max_constraint_magnitude_bits = "1",
56 num_constraints = "num_vertices - 1",
57 num_nonzeros = "num_inputs * num_outputs * (num_vertices - 1)",
58 },
59})]
60impl ReduceTo<ILP<bool>> for MinimumFaultDetectionTestSet {
61 type Result = ReductionMFDTSToILP;
62
63 fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
64 fn reachable(adj: &[Vec<usize>], start: usize) -> Vec<bool> {
65 let mut seen = vec![false; adj.len()];
66 let mut queue = VecDeque::new();
67 seen[start] = true;
68 queue.push_back(start);
69
70 while let Some(vertex) = queue.pop_front() {
71 for &next in &adj[vertex] {
72 if !seen[next] {
73 seen[next] = true;
74 queue.push_back(next);
75 }
76 }
77 }
78
79 seen
80 }
81
82 let mut adj = vec![Vec::new(); self.num_vertices()];
83 let mut rev_adj = vec![Vec::new(); self.num_vertices()];
84 for &(tail, head) in self.arcs() {
85 adj[tail].push(head);
86 rev_adj[head].push(tail);
87 }
88
89 let input_reachability: Vec<Vec<bool>> = self
90 .inputs()
91 .iter()
92 .map(|&input| reachable(&adj, input))
93 .collect();
94 let output_reachability: Vec<Vec<bool>> = self
95 .outputs()
96 .iter()
97 .map(|&output| reachable(&rev_adj, output))
98 .collect();
99
100 let mut boundary = vec![false; self.num_vertices()];
101 for &input in self.inputs() {
102 boundary[input] = true;
103 }
104 for &output in self.outputs() {
105 boundary[output] = true;
106 }
107
108 let num_pairs = self.num_inputs() * self.num_outputs();
109 let internal_vertices: Vec<usize> = (0..self.num_vertices())
110 .filter(|&vertex| !boundary[vertex])
111 .collect();
112
113 let constraints: Vec<LinearConstraint> = internal_vertices
114 .into_iter()
115 .map(|vertex| {
116 let mut terms = Vec::new();
117 for (input_idx, input_cov) in input_reachability.iter().enumerate() {
118 for (output_idx, output_cov) in output_reachability.iter().enumerate() {
119 if input_cov[vertex] && output_cov[vertex] {
120 let pair_idx = input_idx * self.num_outputs() + output_idx;
121 terms.push((pair_idx, 1));
122 }
123 }
124 }
125 LinearConstraint::ge(terms, 1)
126 })
127 .collect();
128
129 let objective = (0..num_pairs).map(|pair_idx| (pair_idx, 1)).collect();
130
131 Ok(ReductionMFDTSToILP {
132 target: ILP::new(num_pairs, constraints, objective, ObjectiveSense::Minimize)
133 .map_err(Self::target_construction)?,
134 num_inputs: self.num_inputs(),
135 num_outputs: self.num_outputs(),
136 })
137 }
138}
139
140#[cfg(feature = "example-db")]
141pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
142 vec![crate::example_db::specs::RuleExampleSpec {
143 id: "minimumfaultdetectiontestset_to_ilp",
144 build: || {
145 let source = MinimumFaultDetectionTestSet::new(
146 7,
147 vec![
148 (0, 2),
149 (0, 3),
150 (1, 3),
151 (1, 4),
152 (2, 5),
153 (3, 5),
154 (3, 6),
155 (4, 6),
156 ],
157 vec![0, 1],
158 vec![5, 6],
159 );
160 crate::example_db::specs::rule_example_via_ilp::<_, bool>(source)
161 },
162 }]
163}
164
165#[cfg(test)]
166#[path = "../unit_tests/rules/minimumfaultdetectiontestset_ilp.rs"]
167mod tests;