problemreductions/models/set/
minimum_cardinality_key.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
7use crate::traits::Problem;
8use crate::types::Min;
9use serde::{Deserialize, Serialize};
10
11inventory::submit! {
12 ProblemSchemaEntry {
13 name: "MinimumCardinalityKey",
14 display_name: "Minimum Cardinality Key",
15 aliases: &[],
16 dimensions: &[],
17 category: crate::registry::ProblemCategory::Set,
18 module_path: module_path!(),
19 description: "Find a candidate key of minimum cardinality in a relational system",
20 fields: &[
21 FieldInfo { name: "num_attributes", type_name: "usize", description: "Number of attributes in the relation" },
22 FieldInfo { name: "dependencies", type_name: "Vec<(Vec<usize>, Vec<usize>)>", description: "Functional dependencies as (lhs, rhs) pairs" },
23 ],
24 }
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct MinimumCardinalityKey {
35 num_attributes: usize,
37 dependencies: Vec<(Vec<usize>, Vec<usize>)>,
39}
40
41impl MinimumCardinalityKey {
42 pub fn new(num_attributes: usize, dependencies: Vec<(Vec<usize>, Vec<usize>)>) -> Self {
48 let mut dependencies = dependencies;
49 for (dep_index, (lhs, rhs)) in dependencies.iter_mut().enumerate() {
50 lhs.sort_unstable();
51 lhs.dedup();
52 rhs.sort_unstable();
53 rhs.dedup();
54 for &attr in lhs.iter().chain(rhs.iter()) {
55 assert!(
56 attr < num_attributes,
57 "Dependency {} contains attribute {} which is outside attribute set of size {}",
58 dep_index,
59 attr,
60 num_attributes
61 );
62 }
63 }
64
65 Self {
66 num_attributes,
67 dependencies,
68 }
69 }
70
71 pub fn num_attributes(&self) -> usize {
73 self.num_attributes
74 }
75
76 pub fn num_dependencies(&self) -> usize {
78 self.dependencies.len()
79 }
80
81 pub fn dependencies(&self) -> &[(Vec<usize>, Vec<usize>)] {
83 &self.dependencies
84 }
85
86 fn compute_closure(&self, selected: &[bool]) -> Vec<bool> {
91 let mut closure = selected.to_vec();
92 loop {
93 let mut changed = false;
94 for (lhs, rhs) in &self.dependencies {
95 if lhs.iter().all(|&a| closure[a]) {
96 for &a in rhs {
97 if !closure[a] {
98 closure[a] = true;
99 changed = true;
100 }
101 }
102 }
103 }
104 if !changed {
105 break;
106 }
107 }
108 closure
109 }
110
111 fn is_key(&self, selected: &[bool]) -> bool {
114 let closure = self.compute_closure(selected);
115 closure.iter().all(|&v| v)
116 }
117}
118
119impl Problem for MinimumCardinalityKey {
120 const NAME: &'static str = "MinimumCardinalityKey";
121 type Solution = Vec<bool>;
122 type Value = Min<i64>;
123
124 crate::problem_parameters![
125 ("num_attributes", num_attributes),
126 ("num_dependencies", num_dependencies),
127 ];
128
129 fn evaluate(
130 &self,
131 config: &Self::Solution,
132 ) -> Result<Min<i64>, crate::traits::EvaluationError> {
133 Ok({
134 if config.len() != self.num_attributes {
135 return Err(crate::traits::EvaluationError::InvalidConfiguration(
136 "attribute-selection length does not match the relation".into(),
137 ));
138 }
139
140 if self.is_key(config) {
141 let count = config.iter().filter(|&&v| v).count();
142 Min(Some(i64::try_from(count).map_err(|_| {
143 crate::traits::EvaluationError::IntegerOverflow(
144 "converting selected-attribute count to i64".into(),
145 )
146 })?))
147 } else {
148 Min(None)
149 }
150 })
151 }
152
153 fn variant() -> Vec<(&'static str, &'static str)> {
154 crate::variant_params![]
155 }
156}
157
158impl crate::solvers::BruteForceProblem for MinimumCardinalityKey {
159 fn dimensions(&self) -> Vec<usize> {
160 vec![2; self.num_attributes]
161 }
162}
163
164crate::declare_variants! {
165 default MinimumCardinalityKey => "2^num_attributes",
166}
167
168crate::register_brute_force! {
169 MinimumCardinalityKey decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
170}
171
172#[cfg(feature = "example-db")]
173pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
174 vec![crate::example_db::specs::ModelExampleSpec {
175 id: "minimum_cardinality_key",
176 instance: Box::new(MinimumCardinalityKey::new(
177 6,
178 vec![
179 (vec![0, 1], vec![2]),
180 (vec![0, 2], vec![3]),
181 (vec![1, 3], vec![4]),
182 (vec![2, 4], vec![5]),
183 ],
184 )),
185 optimal_config: serde_json::json!(vec![true, true, false, false, false, false]),
186 optimal_value: serde_json::json!(2),
187 }]
188}
189
190#[cfg(test)]
191#[path = "../../unit_tests/models/set/minimum_cardinality_key.rs"]
192mod tests;