problemreductions/models/set/
prime_attribute_name.rs1use crate::registry::{CreateSpec, ProblemSchemaEntry};
7use crate::traits::Problem;
8use serde::{Deserialize, Serialize};
9
10inventory::submit! {
11 ProblemSchemaEntry {
12 name: "PrimeAttributeName",
13 display_name: "Prime Attribute Name",
14 aliases: &[],
15 dimensions: &[],
16 category: crate::registry::ProblemCategory::Set,
17 module_path: module_path!(),
18 description: "Determine if an attribute belongs to any candidate key under functional dependencies",
19 fields: PrimeAttributeNameCreateSpec::FIELDS,
20 }
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
63#[serde(try_from = "PrimeAttributeNameData")]
64pub struct PrimeAttributeName {
65 num_attributes: usize,
67 dependencies: Vec<(Vec<usize>, Vec<usize>)>,
69 query_attribute: usize,
71}
72
73#[derive(Deserialize)]
74struct PrimeAttributeNameData {
75 num_attributes: usize,
76 dependencies: Vec<(Vec<usize>, Vec<usize>)>,
77 query_attribute: usize,
78}
79
80impl TryFrom<PrimeAttributeNameData> for PrimeAttributeName {
81 type Error = crate::registry::ConstructionError;
82
83 fn try_from(data: PrimeAttributeNameData) -> Result<Self, Self::Error> {
84 Self::try_new(data.num_attributes, data.dependencies, data.query_attribute)
85 }
86}
87
88#[derive(Debug, Deserialize, crate::CreateSpec)]
89struct PrimeAttributeNameCreateSpec {
90 universe_size: usize,
92 dependencies: Vec<(Vec<usize>, Vec<usize>)>,
94 query_attribute: usize,
96}
97
98impl TryFrom<PrimeAttributeNameCreateSpec> for PrimeAttributeName {
99 type Error = crate::registry::ConstructionError;
100
101 fn try_from(spec: PrimeAttributeNameCreateSpec) -> Result<Self, Self::Error> {
102 Self::try_new(spec.universe_size, spec.dependencies, spec.query_attribute)
103 }
104}
105
106impl PrimeAttributeName {
107 pub fn new(
114 num_attributes: usize,
115 dependencies: Vec<(Vec<usize>, Vec<usize>)>,
116 query_attribute: usize,
117 ) -> Self {
118 Self::try_new(num_attributes, dependencies, query_attribute)
119 .unwrap_or_else(|error| panic!("{error}"))
120 }
121
122 fn try_new(
123 num_attributes: usize,
124 dependencies: Vec<(Vec<usize>, Vec<usize>)>,
125 query_attribute: usize,
126 ) -> Result<Self, crate::registry::ConstructionError> {
127 if query_attribute >= num_attributes {
128 return Err(format!("Query attribute {query_attribute} is outside attribute set of size {num_attributes}").into());
129 }
130 for (index, (lhs, rhs)) in dependencies.iter().enumerate() {
131 if lhs.is_empty() {
132 return Err(format!("Dependency {index} has empty LHS").into());
133 }
134 if let Some(attribute) = lhs
135 .iter()
136 .chain(rhs.iter())
137 .find(|&&attribute| attribute >= num_attributes)
138 {
139 return Err(format!("dependency {index} contains attribute {attribute} outside attribute set of size {num_attributes}").into());
140 }
141 }
142 Ok(Self {
143 num_attributes,
144 dependencies,
145 query_attribute,
146 })
147 }
148
149 pub fn num_attributes(&self) -> usize {
151 self.num_attributes
152 }
153
154 pub fn num_dependencies(&self) -> usize {
156 self.dependencies.len()
157 }
158
159 pub fn query_attribute(&self) -> usize {
161 self.query_attribute
162 }
163
164 pub fn dependencies(&self) -> &[(Vec<usize>, Vec<usize>)] {
166 &self.dependencies
167 }
168
169 pub fn compute_closure(&self, attrs: &[bool]) -> Vec<bool> {
174 let mut closure = attrs.to_vec();
175 loop {
176 let mut changed = false;
177 for (lhs, rhs) in &self.dependencies {
178 if lhs.iter().all(|&a| closure[a]) {
179 for &a in rhs {
180 if !closure[a] {
181 closure[a] = true;
182 changed = true;
183 }
184 }
185 }
186 }
187 if !changed {
188 break;
189 }
190 }
191 closure
192 }
193}
194
195impl Problem for PrimeAttributeName {
196 const NAME: &'static str = "PrimeAttributeName";
197 type Solution = Vec<bool>;
198 type Value = crate::types::Or;
199
200 crate::problem_parameters![
201 ("num_attributes", num_attributes),
202 ("num_dependencies", num_dependencies),
203 ];
204
205 fn evaluate(
206 &self,
207 config: &Self::Solution,
208 ) -> Result<crate::types::Or, crate::traits::EvaluationError> {
209 Ok({
210 crate::types::Or({
211 if config.len() != self.num_attributes {
212 return Err(crate::traits::EvaluationError::InvalidConfiguration(
213 "attribute-selection length does not match the relation".into(),
214 ));
215 }
216
217 if !config[self.query_attribute] {
219 return Ok(crate::types::Or(false));
220 }
221
222 let closure = self.compute_closure(config);
224 if closure.iter().any(|&v| !v) {
225 return Ok(crate::types::Or(false));
226 }
227
228 for i in 0..self.num_attributes {
230 if config[i] {
231 let mut reduced = config.clone();
232 reduced[i] = false;
233 let reduced_closure = self.compute_closure(&reduced);
234 if reduced_closure.iter().all(|&v| v) {
235 return Ok(crate::types::Or(false));
237 }
238 }
239 }
240
241 true
242 })
243 })
244 }
245
246 fn variant() -> Vec<(&'static str, &'static str)> {
247 crate::variant_params![]
248 }
249}
250
251impl crate::solvers::BruteForceProblem for PrimeAttributeName {
252 fn dimensions(&self) -> Vec<usize> {
253 vec![2; self.num_attributes]
254 }
255}
256
257crate::declare_variants! {
258 default PrimeAttributeName => "2^num_attributes * num_dependencies * num_attributes" create PrimeAttributeNameCreateSpec,
259}
260
261crate::register_brute_force! {
262 PrimeAttributeName decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
263}
264
265#[cfg(feature = "example-db")]
266pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
267 vec![crate::example_db::specs::ModelExampleSpec {
268 id: "prime_attribute_name",
269 instance: Box::new(PrimeAttributeName::new(
271 6,
272 vec![
273 (vec![0, 1], vec![2, 3, 4, 5]),
274 (vec![2, 3], vec![0, 1, 4, 5]),
275 (vec![0, 3], vec![1, 2, 4, 5]),
276 ],
277 3,
278 )),
279 optimal_config: serde_json::json!(vec![false, false, true, true, false, false]),
281 optimal_value: serde_json::json!(true),
282 }]
283}
284
285#[cfg(test)]
286#[path = "../../unit_tests/models/set/prime_attribute_name.rs"]
287mod tests;