problemreductions/solvers/customized/
solver.rs1use super::fd_subset_search::{
7 self, compute_closure, find_essential_attributes, find_essential_attributes_restricted,
8 is_minimal_key, is_superkey, BranchDecision,
9};
10use crate::models::graph::{PartialFeedbackEdgeSet, RootedTreeArrangement};
11use crate::models::misc::{AdditionalKey, BoyceCoddNormalFormViolation};
12use crate::models::set::{MinimumCardinalityKey, PrimeAttributeName};
13use crate::topology::SimpleGraph;
14use std::collections::HashSet;
15
16#[derive(Default)]
24pub struct CustomizedSolver;
25
26impl CustomizedSolver {
27 pub fn new() -> Self {
29 Self
30 }
31
32 pub fn supports_problem(any: &dyn std::any::Any) -> bool {
34 any.is::<MinimumCardinalityKey>()
35 || any.is::<AdditionalKey>()
36 || any.is::<PrimeAttributeName>()
37 || any.is::<BoyceCoddNormalFormViolation>()
38 || any.is::<PartialFeedbackEdgeSet<SimpleGraph>>()
39 || any.is::<RootedTreeArrangement<SimpleGraph>>()
40 }
41
42 pub fn solve_dyn(&self, any: &dyn std::any::Any) -> Option<Vec<usize>> {
47 if let Some(p) = any.downcast_ref::<MinimumCardinalityKey>() {
48 return solve_minimum_cardinality_key(p);
49 }
50 if let Some(p) = any.downcast_ref::<AdditionalKey>() {
51 return solve_additional_key(p);
52 }
53 if let Some(p) = any.downcast_ref::<PrimeAttributeName>() {
54 return solve_prime_attribute_name(p);
55 }
56 if let Some(p) = any.downcast_ref::<BoyceCoddNormalFormViolation>() {
57 return solve_bcnf_violation(p);
58 }
59 if let Some(p) = any.downcast_ref::<PartialFeedbackEdgeSet<SimpleGraph>>() {
60 return super::partial_feedback_edge_set::find_witness(p);
61 }
62 if let Some(p) = any.downcast_ref::<RootedTreeArrangement<SimpleGraph>>() {
63 return super::rooted_tree_arrangement::find_witness(p);
64 }
65 None
66 }
67}
68
69fn solve_minimum_cardinality_key(problem: &MinimumCardinalityKey) -> Option<Vec<usize>> {
74 let n = problem.num_attributes();
75 let deps = problem.dependencies().to_vec();
76
77 let essential = find_essential_attributes(n, &deps);
78 let essential_count = essential.len();
79
80 let essential_set: HashSet<usize> = essential.iter().copied().collect();
82 let branch_order: Vec<usize> = (0..n).filter(|i| !essential_set.contains(i)).collect();
83
84 for max_total in essential_count..=n {
86 let result = fd_subset_search::search_fd_subset(
87 n,
88 &essential,
89 &branch_order,
90 |selected, _depth| {
91 let count = selected.iter().filter(|&&v| v).count();
92 if count > max_total {
93 BranchDecision::Prune
94 } else {
95 BranchDecision::Continue
96 }
97 },
98 |selected| {
99 selected.iter().filter(|&&v| v).count() == max_total
100 && is_minimal_key(selected, &deps)
101 },
102 );
103
104 if let Some(indices) = result {
105 let mut config = vec![0; n];
106 for i in indices {
107 config[i] = 1;
108 }
109 return Some(config);
110 }
111 }
112 None
113}
114
115fn solve_additional_key(problem: &AdditionalKey) -> Option<Vec<usize>> {
117 let n_attrs = problem.num_attributes();
118 let deps = problem.dependencies().to_vec();
119 let relation_attrs = problem.relation_attrs();
120 let known_keys = problem.known_keys();
121
122 let essential = find_essential_attributes_restricted(n_attrs, &deps, relation_attrs);
123
124 let essential_set: HashSet<usize> = essential.iter().copied().collect();
126 let branch_indices: Vec<usize> = relation_attrs
127 .iter()
128 .copied()
129 .filter(|a| !essential_set.contains(a))
130 .collect();
131
132 let result = fd_subset_search::search_fd_subset(
134 n_attrs,
135 &essential,
136 &branch_indices,
137 |_selected, _depth| BranchDecision::Continue,
138 |selected| {
139 let closure = compute_closure(selected, &deps);
141 if !relation_attrs.iter().all(|&a| closure[a]) {
142 return false;
143 }
144 let selected_ra: Vec<usize> = relation_attrs
146 .iter()
147 .copied()
148 .filter(|&a| selected[a])
149 .collect();
150 if selected_ra.is_empty() {
151 return false;
152 }
153 for &a in &selected_ra {
154 let mut reduced = selected.to_vec();
155 reduced[a] = false;
156 let reduced_closure = compute_closure(&reduced, &deps);
157 if relation_attrs.iter().all(|&ra| reduced_closure[ra]) {
158 return false; }
160 }
161 let mut sorted_selected: Vec<usize> = selected_ra;
163 sorted_selected.sort_unstable();
164 !known_keys.contains(&sorted_selected)
165 },
166 );
167
168 result.map(|indices| {
170 let index_set: HashSet<usize> = indices.into_iter().collect();
171 relation_attrs
172 .iter()
173 .map(|&attr| if index_set.contains(&attr) { 1 } else { 0 })
174 .collect()
175 })
176}
177
178fn solve_prime_attribute_name(problem: &PrimeAttributeName) -> Option<Vec<usize>> {
180 let n = problem.num_attributes();
181 let deps = problem.dependencies().to_vec();
182 let query = problem.query_attribute();
183
184 let essential = find_essential_attributes(n, &deps);
185
186 let mut forced: Vec<usize> = essential.clone();
188 if !forced.contains(&query) {
189 forced.push(query);
190 }
191 forced.sort_unstable();
192 forced.dedup();
193
194 let forced_set: HashSet<usize> = forced.iter().copied().collect();
195 let branch_order: Vec<usize> = (0..n).filter(|i| !forced_set.contains(i)).collect();
196
197 let result = fd_subset_search::search_fd_subset(
198 n,
199 &forced,
200 &branch_order,
201 |selected, _depth| {
202 if is_superkey(selected, &deps) {
204 return BranchDecision::Continue;
206 }
207 BranchDecision::Continue
208 },
209 |selected| selected[query] && is_minimal_key(selected, &deps),
210 );
211
212 result.map(|indices| {
213 let mut config = vec![0; n];
214 for i in indices {
215 config[i] = 1;
216 }
217 config
218 })
219}
220
221fn solve_bcnf_violation(problem: &BoyceCoddNormalFormViolation) -> Option<Vec<usize>> {
224 let n_attrs = problem.num_attributes();
225 let deps = problem.functional_deps().to_vec();
226 let target = problem.target_subset();
227
228 let branch_order: Vec<usize> = target.to_vec();
230
231 let result = fd_subset_search::search_fd_subset(
232 n_attrs,
233 &[],
234 &branch_order,
235 |_selected, _depth| BranchDecision::Continue,
236 |selected| {
237 let x: HashSet<usize> = target.iter().copied().filter(|&a| selected[a]).collect();
238 let closure = compute_closure(selected, &deps);
239 let mut has_in_closure = false;
241 let mut has_not_in_closure = false;
242 for &a in target {
243 if !x.contains(&a) {
244 if closure[a] {
245 has_in_closure = true;
246 } else {
247 has_not_in_closure = true;
248 }
249 }
250 }
251 has_in_closure && has_not_in_closure
252 },
253 );
254
255 result.map(|indices| {
257 let index_set: HashSet<usize> = indices.into_iter().collect();
258 target
259 .iter()
260 .map(|&attr| if index_set.contains(&attr) { 1 } else { 0 })
261 .collect()
262 })
263}
264
265#[cfg(test)]
266#[path = "../../unit_tests/solvers/customized/solver.rs"]
267mod tests;