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)]
63pub struct PrimeAttributeName {
64 num_attributes: usize,
66 dependencies: Vec<(Vec<usize>, Vec<usize>)>,
68 query_attribute: usize,
70}
71
72#[derive(Debug, Deserialize, crate::CreateSpec)]
73struct PrimeAttributeNameCreateSpec {
74 universe_size: usize,
76 dependencies: Vec<(Vec<usize>, Vec<usize>)>,
78 query_attribute: usize,
80}
81
82impl TryFrom<PrimeAttributeNameCreateSpec> for PrimeAttributeName {
83 type Error = crate::registry::ConstructionError;
84
85 fn try_from(spec: PrimeAttributeNameCreateSpec) -> Result<Self, Self::Error> {
86 if spec.query_attribute >= spec.universe_size {
87 return Err(format!(
88 "query_attribute {} is outside universe of size {}",
89 spec.query_attribute, spec.universe_size
90 )
91 .into());
92 }
93 for (dependency_index, (lhs, rhs)) in spec.dependencies.iter().enumerate() {
94 if lhs.is_empty() {
95 return Err(
96 format!("dependencies[{dependency_index}] has an empty left side").into(),
97 );
98 }
99 if let Some(&attribute) = lhs
100 .iter()
101 .chain(rhs)
102 .find(|&&attribute| attribute >= spec.universe_size)
103 {
104 return Err(format!(
105 "dependencies[{dependency_index}] contains attribute {attribute} outside universe of size {}",
106 spec.universe_size
107 ).into());
108 }
109 }
110 Ok(Self::new(
111 spec.universe_size,
112 spec.dependencies,
113 spec.query_attribute,
114 ))
115 }
116}
117
118impl PrimeAttributeName {
119 pub fn new(
126 num_attributes: usize,
127 dependencies: Vec<(Vec<usize>, Vec<usize>)>,
128 query_attribute: usize,
129 ) -> Self {
130 assert!(
131 query_attribute < num_attributes,
132 "Query attribute {} is outside attribute set of size {}",
133 query_attribute,
134 num_attributes
135 );
136 for (i, (lhs, rhs)) in dependencies.iter().enumerate() {
137 assert!(!lhs.is_empty(), "Dependency {} has empty LHS", i);
138 for &attr in lhs.iter().chain(rhs.iter()) {
139 assert!(
140 attr < num_attributes,
141 "Dependency {} references attribute {} which is outside attribute set of size {}",
142 i,
143 attr,
144 num_attributes
145 );
146 }
147 }
148 Self {
149 num_attributes,
150 dependencies,
151 query_attribute,
152 }
153 }
154
155 pub fn num_attributes(&self) -> usize {
157 self.num_attributes
158 }
159
160 pub fn num_dependencies(&self) -> usize {
162 self.dependencies.len()
163 }
164
165 pub fn query_attribute(&self) -> usize {
167 self.query_attribute
168 }
169
170 pub fn dependencies(&self) -> &[(Vec<usize>, Vec<usize>)] {
172 &self.dependencies
173 }
174
175 pub fn compute_closure(&self, attrs: &[bool]) -> Vec<bool> {
180 let mut closure = attrs.to_vec();
181 loop {
182 let mut changed = false;
183 for (lhs, rhs) in &self.dependencies {
184 if lhs.iter().all(|&a| closure[a]) {
185 for &a in rhs {
186 if !closure[a] {
187 closure[a] = true;
188 changed = true;
189 }
190 }
191 }
192 }
193 if !changed {
194 break;
195 }
196 }
197 closure
198 }
199}
200
201impl Problem for PrimeAttributeName {
202 const NAME: &'static str = "PrimeAttributeName";
203 type Solution = Vec<bool>;
204 type Value = crate::types::Or;
205
206 crate::problem_parameters![
207 ("num_attributes", num_attributes),
208 ("num_dependencies", num_dependencies),
209 ];
210
211 fn evaluate(
212 &self,
213 config: &Self::Solution,
214 ) -> Result<crate::types::Or, crate::traits::EvaluationError> {
215 Ok({
216 crate::types::Or({
217 if config.len() != self.num_attributes {
218 return Err(crate::traits::EvaluationError::InvalidConfiguration(
219 "attribute-selection length does not match the relation".into(),
220 ));
221 }
222
223 if !config[self.query_attribute] {
225 return Ok(crate::types::Or(false));
226 }
227
228 let closure = self.compute_closure(config);
230 if closure.iter().any(|&v| !v) {
231 return Ok(crate::types::Or(false));
232 }
233
234 for i in 0..self.num_attributes {
236 if config[i] {
237 let mut reduced = config.clone();
238 reduced[i] = false;
239 let reduced_closure = self.compute_closure(&reduced);
240 if reduced_closure.iter().all(|&v| v) {
241 return Ok(crate::types::Or(false));
243 }
244 }
245 }
246
247 true
248 })
249 })
250 }
251
252 fn variant() -> Vec<(&'static str, &'static str)> {
253 crate::variant_params![]
254 }
255}
256
257impl crate::solvers::BruteForceProblem for PrimeAttributeName {
258 fn dimensions(&self) -> Vec<usize> {
259 vec![2; self.num_attributes]
260 }
261}
262
263crate::declare_variants! {
264 default PrimeAttributeName => "2^num_attributes * num_dependencies * num_attributes" create PrimeAttributeNameCreateSpec,
265}
266
267crate::register_brute_force! {
268 PrimeAttributeName decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
269}
270
271#[cfg(feature = "example-db")]
272pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
273 vec![crate::example_db::specs::ModelExampleSpec {
274 id: "prime_attribute_name",
275 instance: Box::new(PrimeAttributeName::new(
277 6,
278 vec![
279 (vec![0, 1], vec![2, 3, 4, 5]),
280 (vec![2, 3], vec![0, 1, 4, 5]),
281 (vec![0, 3], vec![1, 2, 4, 5]),
282 ],
283 3,
284 )),
285 optimal_config: serde_json::json!(vec![false, false, true, true, false, false]),
287 optimal_value: serde_json::json!(true),
288 }]
289}
290
291#[cfg(test)]
292#[path = "../../unit_tests/models/set/prime_attribute_name.rs"]
293mod tests;