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)]
66pub struct MinimumAxiomSet {
67 num_sentences: usize,
69 true_sentences: Vec<usize>,
71 implications: Vec<(Vec<usize>, usize)>,
73}
74
75impl MinimumAxiomSet {
76 pub fn new(
84 num_sentences: usize,
85 true_sentences: Vec<usize>,
86 implications: Vec<(Vec<usize>, usize)>,
87 ) -> Self {
88 for &s in &true_sentences {
90 assert!(
91 s < num_sentences,
92 "True sentence index {s} out of range [0, {num_sentences})"
93 );
94 }
95 let mut seen = vec![false; num_sentences];
97 for &s in &true_sentences {
98 assert!(!seen[s], "Duplicate true sentence index {s}");
99 seen[s] = true;
100 }
101 for (antecedents, consequent) in &implications {
103 for &a in antecedents {
104 assert!(
105 a < num_sentences,
106 "Implication antecedent {a} out of range [0, {num_sentences})"
107 );
108 }
109 assert!(
110 *consequent < num_sentences,
111 "Implication consequent {consequent} out of range [0, {num_sentences})"
112 );
113 }
114 Self {
115 num_sentences,
116 true_sentences,
117 implications,
118 }
119 }
120
121 pub fn num_sentences(&self) -> usize {
123 self.num_sentences
124 }
125
126 pub fn num_true_sentences(&self) -> usize {
128 self.true_sentences.len()
129 }
130
131 pub fn num_implications(&self) -> usize {
133 self.implications.len()
134 }
135
136 pub fn true_sentences(&self) -> &[usize] {
138 &self.true_sentences
139 }
140
141 pub fn implications(&self) -> &[(Vec<usize>, usize)] {
143 &self.implications
144 }
145}
146
147fn deductive_closure(current: &mut [bool], implications: &[(Vec<usize>, usize)]) {
151 loop {
152 let mut changed = false;
153 for (antecedents, consequent) in implications {
154 if !current[*consequent] && antecedents.iter().all(|&a| current[a]) {
155 current[*consequent] = true;
156 changed = true;
157 }
158 }
159 if !changed {
160 break;
161 }
162 }
163}
164
165impl Problem for MinimumAxiomSet {
166 const NAME: &'static str = "MinimumAxiomSet";
167 type Solution = Vec<bool>;
168 type Value = Min<i64>;
169
170 crate::problem_parameters![
171 ("num_implications", num_implications),
172 ("num_sentences", num_sentences),
173 ("num_true_sentences", num_true_sentences),
174 ];
175
176 fn variant() -> Vec<(&'static str, &'static str)> {
177 crate::variant_params![]
178 }
179
180 fn evaluate(
181 &self,
182 config: &Self::Solution,
183 ) -> Result<Min<i64>, crate::traits::EvaluationError> {
184 Ok({
185 if config.len() != self.num_true_sentences() {
186 return Err(crate::traits::EvaluationError::InvalidConfiguration(
187 "axiom-selection length does not match the true sentences".into(),
188 ));
189 }
190 let mut current = vec![false; self.num_sentences];
192 let mut count = 0usize;
193 for (i, &v) in config.iter().enumerate() {
194 if v {
195 current[self.true_sentences[i]] = true;
196 count += 1;
197 }
198 }
199
200 deductive_closure(&mut current, &self.implications);
202
203 let closure_equals_t = self.true_sentences.iter().all(|&s| current[s]);
205
206 if closure_equals_t {
207 Min(Some(i64::try_from(count).map_err(|_| {
208 crate::traits::EvaluationError::IntegerOverflow(
209 "converting axiom-set size to i64".into(),
210 )
211 })?))
212 } else {
213 Min(None)
214 }
215 })
216 }
217}
218
219impl crate::solvers::BruteForceProblem for MinimumAxiomSet {
220 fn dimensions(&self) -> Vec<usize> {
221 vec![2; self.num_true_sentences()]
222 }
223}
224
225crate::declare_variants! {
226 default MinimumAxiomSet => "2^num_true_sentences",
227}
228
229crate::register_brute_force! {
230 MinimumAxiomSet decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
231}
232
233#[cfg(feature = "example-db")]
234pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
235 vec![crate::example_db::specs::ModelExampleSpec {
238 id: "minimum_axiom_set",
239 instance: Box::new(MinimumAxiomSet::new(
240 8,
241 vec![0, 1, 2, 3, 4, 5, 6, 7],
242 vec![
243 (vec![0], 2),
244 (vec![0], 3),
245 (vec![1], 4),
246 (vec![1], 5),
247 (vec![2, 4], 6),
248 (vec![3, 5], 7),
249 (vec![6, 7], 0),
250 (vec![6, 7], 1),
251 ],
252 )),
253 optimal_config: serde_json::json!(vec![
254 true, true, false, false, false, false, false, false
255 ]),
256 optimal_value: serde_json::json!(2),
257 }]
258}
259
260#[cfg(test)]
261#[path = "../../unit_tests/models/misc/minimum_axiom_set.rs"]
262mod tests;