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)]
34#[serde(try_from = "MinimumCardinalityKeyData")]
35pub struct MinimumCardinalityKey {
36 num_attributes: usize,
38 dependencies: Vec<(Vec<usize>, Vec<usize>)>,
40}
41
42#[derive(Deserialize)]
43struct MinimumCardinalityKeyData {
44 num_attributes: usize,
45 dependencies: Vec<(Vec<usize>, Vec<usize>)>,
46}
47
48impl TryFrom<MinimumCardinalityKeyData> for MinimumCardinalityKey {
49 type Error = crate::registry::ConstructionError;
50
51 fn try_from(data: MinimumCardinalityKeyData) -> Result<Self, Self::Error> {
52 Self::try_new(data.num_attributes, data.dependencies)
53 }
54}
55
56impl MinimumCardinalityKey {
57 pub fn new(num_attributes: usize, dependencies: Vec<(Vec<usize>, Vec<usize>)>) -> Self {
63 Self::try_new(num_attributes, dependencies).unwrap_or_else(|error| panic!("{error}"))
64 }
65
66 fn try_new(
67 num_attributes: usize,
68 mut dependencies: Vec<(Vec<usize>, Vec<usize>)>,
69 ) -> Result<Self, crate::registry::ConstructionError> {
70 for (index, (lhs, rhs)) in dependencies.iter_mut().enumerate() {
71 lhs.sort_unstable();
72 lhs.dedup();
73 rhs.sort_unstable();
74 rhs.dedup();
75 if let Some(attribute) = lhs
76 .iter()
77 .chain(rhs.iter())
78 .find(|&&attribute| attribute >= num_attributes)
79 {
80 return Err(format!("dependency {index} contains attribute {attribute} outside attribute set of size {num_attributes}").into());
81 }
82 }
83 Ok(Self {
84 num_attributes,
85 dependencies,
86 })
87 }
88
89 pub fn num_attributes(&self) -> usize {
91 self.num_attributes
92 }
93
94 pub fn num_dependencies(&self) -> usize {
96 self.dependencies.len()
97 }
98
99 pub fn dependencies(&self) -> &[(Vec<usize>, Vec<usize>)] {
101 &self.dependencies
102 }
103
104 fn compute_closure(&self, selected: &[bool]) -> Vec<bool> {
109 let mut closure = selected.to_vec();
110 loop {
111 let mut changed = false;
112 for (lhs, rhs) in &self.dependencies {
113 if lhs.iter().all(|&a| closure[a]) {
114 for &a in rhs {
115 if !closure[a] {
116 closure[a] = true;
117 changed = true;
118 }
119 }
120 }
121 }
122 if !changed {
123 break;
124 }
125 }
126 closure
127 }
128
129 fn is_key(&self, selected: &[bool]) -> bool {
132 let closure = self.compute_closure(selected);
133 closure.iter().all(|&v| v)
134 }
135}
136
137impl Problem for MinimumCardinalityKey {
138 const NAME: &'static str = "MinimumCardinalityKey";
139 type Solution = Vec<bool>;
140 type Value = Min<i64>;
141
142 crate::problem_parameters![
143 ("num_attributes", num_attributes),
144 ("num_dependencies", num_dependencies),
145 ];
146
147 fn evaluate(
148 &self,
149 config: &Self::Solution,
150 ) -> Result<Min<i64>, crate::traits::EvaluationError> {
151 Ok({
152 if config.len() != self.num_attributes {
153 return Err(crate::traits::EvaluationError::InvalidConfiguration(
154 "attribute-selection length does not match the relation".into(),
155 ));
156 }
157
158 if self.is_key(config) {
159 let count = config.iter().filter(|&&v| v).count();
160 Min(Some(i64::try_from(count).map_err(|_| {
161 crate::traits::EvaluationError::IntegerOverflow(
162 "converting selected-attribute count to i64".into(),
163 )
164 })?))
165 } else {
166 Min(None)
167 }
168 })
169 }
170
171 fn variant() -> Vec<(&'static str, &'static str)> {
172 crate::variant_params![]
173 }
174}
175
176impl crate::solvers::BruteForceProblem for MinimumCardinalityKey {
177 fn dimensions(&self) -> Vec<usize> {
178 vec![2; self.num_attributes]
179 }
180}
181
182crate::declare_variants! {
183 default MinimumCardinalityKey => "2^num_attributes",
184}
185
186crate::register_brute_force! {
187 MinimumCardinalityKey decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
188}
189
190#[cfg(feature = "example-db")]
191pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
192 vec![crate::example_db::specs::ModelExampleSpec {
193 id: "minimum_cardinality_key",
194 instance: Box::new(MinimumCardinalityKey::new(
195 6,
196 vec![
197 (vec![0, 1], vec![2]),
198 (vec![0, 2], vec![3]),
199 (vec![1, 3], vec![4]),
200 (vec![2, 4], vec![5]),
201 ],
202 )),
203 optimal_config: serde_json::json!(vec![true, true, false, false, false, false]),
204 optimal_value: serde_json::json!(2),
205 }]
206}
207
208#[cfg(test)]
209#[path = "../../unit_tests/models/set/minimum_cardinality_key.rs"]
210mod tests;