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 module_path: module_path!(),
18 description: "Find a candidate key of minimum cardinality in a relational system",
19 fields: &[
20 FieldInfo { name: "num_attributes", type_name: "usize", description: "Number of attributes in the relation" },
21 FieldInfo { name: "dependencies", type_name: "Vec<(Vec<usize>, Vec<usize>)>", description: "Functional dependencies as (lhs, rhs) pairs" },
22 ],
23 }
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct MinimumCardinalityKey {
34 num_attributes: usize,
36 dependencies: Vec<(Vec<usize>, Vec<usize>)>,
38}
39
40impl MinimumCardinalityKey {
41 pub fn new(num_attributes: usize, dependencies: Vec<(Vec<usize>, Vec<usize>)>) -> Self {
47 let mut dependencies = dependencies;
48 for (dep_index, (lhs, rhs)) in dependencies.iter_mut().enumerate() {
49 lhs.sort_unstable();
50 lhs.dedup();
51 rhs.sort_unstable();
52 rhs.dedup();
53 for &attr in lhs.iter().chain(rhs.iter()) {
54 assert!(
55 attr < num_attributes,
56 "Dependency {} contains attribute {} which is outside attribute set of size {}",
57 dep_index,
58 attr,
59 num_attributes
60 );
61 }
62 }
63
64 Self {
65 num_attributes,
66 dependencies,
67 }
68 }
69
70 pub fn num_attributes(&self) -> usize {
72 self.num_attributes
73 }
74
75 pub fn num_dependencies(&self) -> usize {
77 self.dependencies.len()
78 }
79
80 pub fn dependencies(&self) -> &[(Vec<usize>, Vec<usize>)] {
82 &self.dependencies
83 }
84
85 fn compute_closure(&self, selected: &[bool]) -> Vec<bool> {
90 let mut closure = selected.to_vec();
91 loop {
92 let mut changed = false;
93 for (lhs, rhs) in &self.dependencies {
94 if lhs.iter().all(|&a| closure[a]) {
95 for &a in rhs {
96 if !closure[a] {
97 closure[a] = true;
98 changed = true;
99 }
100 }
101 }
102 }
103 if !changed {
104 break;
105 }
106 }
107 closure
108 }
109
110 fn is_key(&self, selected: &[bool]) -> bool {
113 let closure = self.compute_closure(selected);
114 closure.iter().all(|&v| v)
115 }
116}
117
118impl Problem for MinimumCardinalityKey {
119 const NAME: &'static str = "MinimumCardinalityKey";
120 type Value = Min<i64>;
121
122 fn dims(&self) -> Vec<usize> {
123 vec![2; self.num_attributes]
124 }
125
126 fn evaluate(&self, config: &[usize]) -> Min<i64> {
127 if config.len() != self.num_attributes || config.iter().any(|&v| v > 1) {
128 return Min(None);
129 }
130
131 let selected: Vec<bool> = config.iter().map(|&v| v == 1).collect();
132
133 if self.is_key(&selected) {
134 let count = selected.iter().filter(|&&v| v).count();
135 Min(Some(count as i64))
136 } else {
137 Min(None)
138 }
139 }
140
141 fn variant() -> Vec<(&'static str, &'static str)> {
142 crate::variant_params![]
143 }
144}
145
146crate::declare_variants! {
147 default MinimumCardinalityKey => "2^num_attributes",
148}
149
150#[cfg(feature = "example-db")]
151pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
152 vec![crate::example_db::specs::ModelExampleSpec {
153 id: "minimum_cardinality_key",
154 instance: Box::new(MinimumCardinalityKey::new(
155 6,
156 vec![
157 (vec![0, 1], vec![2]),
158 (vec![0, 2], vec![3]),
159 (vec![1, 3], vec![4]),
160 (vec![2, 4], vec![5]),
161 ],
162 )),
163 optimal_config: vec![1, 1, 0, 0, 0, 0],
164 optimal_value: serde_json::json!(2),
165 }]
166}
167
168#[cfg(test)]
169#[path = "../../unit_tests/models/set/minimum_cardinality_key.rs"]
170mod tests;