problemreductions/models/misc/
minimum_axiom_set.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
9use crate::traits::Problem;
10use crate::types::Min;
11use serde::{Deserialize, Serialize};
12
13inventory::submit! {
14 ProblemSchemaEntry {
15 name: "MinimumAxiomSet",
16 display_name: "Minimum Axiom Set",
17 aliases: &[],
18 dimensions: &[],
19 category: crate::registry::ProblemCategory::Misc,
20 module_path: module_path!(),
21 description: "Find smallest axiom subset whose deductive closure equals the true sentences",
22 fields: &[
23 FieldInfo { name: "num_sentences", type_name: "usize", description: "Total number of sentences |S|" },
24 FieldInfo { name: "true_sentences", type_name: "Vec<usize>", description: "Indices of true sentences T ⊆ S" },
25 FieldInfo { name: "implications", type_name: "Vec<(Vec<usize>, usize)>", description: "Implication rules (antecedents, consequent)" },
26 ],
27 }
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
66#[serde(try_from = "MinimumAxiomSetData")]
67pub struct MinimumAxiomSet {
68 num_sentences: usize,
70 true_sentences: Vec<usize>,
72 implications: Vec<(Vec<usize>, usize)>,
74}
75
76#[derive(Deserialize)]
77struct MinimumAxiomSetData {
78 num_sentences: usize,
79 true_sentences: Vec<usize>,
80 implications: Vec<(Vec<usize>, usize)>,
81}
82
83impl TryFrom<MinimumAxiomSetData> for MinimumAxiomSet {
84 type Error = crate::registry::ConstructionError;
85 fn try_from(data: MinimumAxiomSetData) -> Result<Self, Self::Error> {
86 Self::try_new(data.num_sentences, data.true_sentences, data.implications)
87 }
88}
89
90impl MinimumAxiomSet {
91 pub fn new(
99 num_sentences: usize,
100 true_sentences: Vec<usize>,
101 implications: Vec<(Vec<usize>, usize)>,
102 ) -> Self {
103 Self::try_new(num_sentences, true_sentences, implications)
104 .unwrap_or_else(|error| panic!("{error}"))
105 }
106
107 fn try_new(
108 num_sentences: usize,
109 true_sentences: Vec<usize>,
110 implications: Vec<(Vec<usize>, usize)>,
111 ) -> Result<Self, crate::registry::ConstructionError> {
112 for &s in &true_sentences {
114 if s >= num_sentences {
115 return Err(
116 format!("True sentence index {s} out of range [0, {num_sentences})").into(),
117 );
118 }
119 }
120 let mut seen = vec![false; num_sentences];
122 for &s in &true_sentences {
123 if seen[s] {
124 return Err(format!("Duplicate true sentence index {s}").into());
125 }
126 seen[s] = true;
127 }
128 for (antecedents, consequent) in &implications {
130 for &a in antecedents {
131 if a >= num_sentences {
132 return Err(format!(
133 "Implication antecedent {a} out of range [0, {num_sentences})"
134 )
135 .into());
136 }
137 }
138 if *consequent >= num_sentences {
139 return Err(format!(
140 "Implication consequent {consequent} out of range [0, {num_sentences})"
141 )
142 .into());
143 }
144 }
145 Ok(Self {
146 num_sentences,
147 true_sentences,
148 implications,
149 })
150 }
151
152 pub fn num_sentences(&self) -> usize {
154 self.num_sentences
155 }
156
157 pub fn num_true_sentences(&self) -> usize {
159 self.true_sentences.len()
160 }
161
162 pub fn num_implications(&self) -> usize {
164 self.implications.len()
165 }
166
167 pub fn true_sentences(&self) -> &[usize] {
169 &self.true_sentences
170 }
171
172 pub fn implications(&self) -> &[(Vec<usize>, usize)] {
174 &self.implications
175 }
176}
177
178fn deductive_closure(current: &mut [bool], implications: &[(Vec<usize>, usize)]) {
182 loop {
183 let mut changed = false;
184 for (antecedents, consequent) in implications {
185 if !current[*consequent] && antecedents.iter().all(|&a| current[a]) {
186 current[*consequent] = true;
187 changed = true;
188 }
189 }
190 if !changed {
191 break;
192 }
193 }
194}
195
196impl Problem for MinimumAxiomSet {
197 const NAME: &'static str = "MinimumAxiomSet";
198 type Solution = Vec<bool>;
199 type Value = Min<i64>;
200
201 crate::problem_parameters![
202 ("num_implications", num_implications),
203 ("num_sentences", num_sentences),
204 ("num_true_sentences", num_true_sentences),
205 ];
206
207 fn variant() -> Vec<(&'static str, &'static str)> {
208 crate::variant_params![]
209 }
210
211 fn evaluate(
212 &self,
213 config: &Self::Solution,
214 ) -> Result<Min<i64>, crate::traits::EvaluationError> {
215 Ok({
216 if config.len() != self.num_true_sentences() {
217 return Err(crate::traits::EvaluationError::InvalidConfiguration(
218 "axiom-selection length does not match the true sentences".into(),
219 ));
220 }
221 let mut current = vec![false; self.num_sentences];
223 let mut count = 0usize;
224 for (i, &v) in config.iter().enumerate() {
225 if v {
226 current[self.true_sentences[i]] = true;
227 count += 1;
228 }
229 }
230
231 deductive_closure(&mut current, &self.implications);
233
234 let closure_equals_t = self.true_sentences.iter().all(|&s| current[s]);
236
237 if closure_equals_t {
238 Min(Some(i64::try_from(count).map_err(|_| {
239 crate::traits::EvaluationError::IntegerOverflow(
240 "converting axiom-set size to i64".into(),
241 )
242 })?))
243 } else {
244 Min(None)
245 }
246 })
247 }
248}
249
250impl crate::solvers::BruteForceProblem for MinimumAxiomSet {
251 fn dimensions(&self) -> Vec<usize> {
252 vec![2; self.num_true_sentences()]
253 }
254}
255
256crate::declare_variants! {
257 default MinimumAxiomSet => "2^num_true_sentences",
258}
259
260crate::register_brute_force! {
261 MinimumAxiomSet decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
262}
263
264#[cfg(feature = "example-db")]
265pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
266 vec![crate::example_db::specs::ModelExampleSpec {
269 id: "minimum_axiom_set",
270 instance: Box::new(MinimumAxiomSet::new(
271 8,
272 vec![0, 1, 2, 3, 4, 5, 6, 7],
273 vec![
274 (vec![0], 2),
275 (vec![0], 3),
276 (vec![1], 4),
277 (vec![1], 5),
278 (vec![2, 4], 6),
279 (vec![3, 5], 7),
280 (vec![6, 7], 0),
281 (vec![6, 7], 1),
282 ],
283 )),
284 optimal_config: serde_json::json!(vec![
285 true, true, false, false, false, false, false, false
286 ]),
287 optimal_value: serde_json::json!(2),
288 }]
289}
290
291#[cfg(test)]
292#[path = "../../unit_tests/models/misc/minimum_axiom_set.rs"]
293mod tests;