conjure_core/solver/adaptors/rustsat/
adaptor.rs

1use std::any::type_name;
2use std::fmt::format;
3use std::hash::Hash;
4use std::iter::Inspect;
5use std::ops::Deref;
6use std::ptr::null;
7use std::vec;
8
9use clap::error;
10use minion_rs::ast::{Model, Tuple};
11use rustsat::encodings::am1::Def;
12use rustsat::solvers::{Solve, SolverResult};
13use rustsat::types::{Assignment, Clause, Lit, TernaryVal, Var as satVar};
14use std::collections::{BTreeMap, HashMap};
15use std::result::Result::Ok;
16use tracing_subscriber::filter::DynFilterFn;
17
18use crate::ast::Domain::BoolDomain;
19
20use rustsat_minisat::core::Minisat;
21
22use crate::ast::{Atom, Expression, Literal, Name};
23use crate::metadata::Metadata;
24use crate::solver::adaptors::rustsat::convs::handle_cnf;
25use crate::solver::SearchComplete::NoSolutions;
26use crate::solver::{
27    self, private, SearchStatus, SolveSuccess, SolverAdaptor, SolverCallback, SolverError,
28    SolverFamily, SolverMutCallback,
29};
30use crate::stats::SolverStats;
31use crate::{ast as conjure_ast, bug, Model as ConjureModel};
32
33use rustsat::instances::{BasicVarManager, Cnf, SatInstance};
34
35use thiserror::Error;
36
37/// A [SolverAdaptor] for interacting with the SatSolver generic and the types thereof.
38pub struct SAT {
39    __non_constructable: private::Internal,
40    model_inst: Option<SatInstance>,
41    var_map: Option<HashMap<String, Lit>>,
42    solver_inst: Minisat,
43    decision_refs: Option<Vec<String>>,
44}
45
46impl private::Sealed for SAT {}
47
48impl Default for SAT {
49    fn default() -> Self {
50        SAT {
51            __non_constructable: private::Internal,
52            solver_inst: Minisat::default(),
53            var_map: None,
54            model_inst: None,
55            decision_refs: None,
56        }
57    }
58}
59
60fn get_ref_sols(
61    find_refs: Vec<String>,
62    sol: Assignment,
63    var_map: HashMap<String, Lit>,
64) -> HashMap<Name, Literal> {
65    let mut solution: HashMap<Name, Literal> = HashMap::new();
66
67    for reference in find_refs {
68        // lit is 'Nothing' for unconstrained - if this is actually happenning, panicking is fine
69        // we are not supposed to do anything to resolve that here.
70        let lit: Lit = match var_map.get(&reference) {
71            Some(a) => *a,
72            None => panic!(
73                "There should never be a non-just literal occurring here. Something is broken upstream."
74            ),
75        };
76        solution.insert(
77            Name::UserName(reference),
78            match sol[lit.var()] {
79                TernaryVal::True => Literal::Int(1),
80                TernaryVal::False => Literal::Int(0),
81                TernaryVal::DontCare => Literal::Int(2),
82            },
83        );
84    }
85
86    solution
87}
88
89impl SolverAdaptor for SAT {
90    fn solve(
91        &mut self,
92        callback: SolverCallback,
93        _: private::Internal,
94    ) -> Result<SolveSuccess, SolverError> {
95        let mut solver = &mut self.solver_inst;
96
97        let cnf: (Cnf, BasicVarManager) = self.model_inst.clone().unwrap().into_cnf();
98
99        (*(solver)).add_cnf(cnf.0);
100
101        let mut has_sol = false;
102        loop {
103            let res = solver.solve().unwrap();
104
105            match res {
106                SolverResult::Sat => {}
107                SolverResult::Unsat => {
108                    return Ok(SolveSuccess {
109                        stats: SolverStats {
110                            conjure_solver_wall_time_s: -1.0,
111                            solver_family: Some(self.get_family()),
112                            solver_adaptor: Some("SAT".to_string()),
113                            nodes: None,
114                            satisfiable: None,
115                            sat_vars: None,
116                            sat_clauses: None,
117                        },
118                        status: if has_sol {
119                            SearchStatus::Complete(solver::SearchComplete::HasSolutions)
120                        } else {
121                            SearchStatus::Complete(NoSolutions)
122                        },
123                    });
124                }
125                SolverResult::Interrupted => {
126                    return Err(SolverError::Runtime("!!Interrupted Solution!!".to_string()))
127                }
128            };
129
130            let sol = solver.full_solution().unwrap();
131            has_sol = true;
132            let solution = get_ref_sols(
133                self.decision_refs.clone().unwrap(),
134                sol.clone(),
135                self.var_map.clone().unwrap(),
136            );
137
138            if !callback(solution) {
139                // println!("callback false");
140                return Ok(SolveSuccess {
141                    stats: SolverStats {
142                        conjure_solver_wall_time_s: -1.0,
143                        solver_family: Some(self.get_family()),
144                        solver_adaptor: Some("SAT".to_string()),
145                        nodes: None,
146                        satisfiable: None,
147                        sat_vars: None,
148                        sat_clauses: None,
149                    },
150                    status: SearchStatus::Incomplete(solver::SearchIncomplete::UserTerminated),
151                });
152            }
153
154            let blocking_vec: Vec<_> = sol.clone().iter().map(|lit| !lit).collect();
155            let mut blocking_cl = Clause::new();
156            for lit_i in blocking_vec {
157                blocking_cl.add(lit_i);
158            }
159
160            solver.add_clause(blocking_cl).unwrap();
161        }
162    }
163
164    fn solve_mut(
165        &mut self,
166        callback: SolverMutCallback,
167        _: private::Internal,
168    ) -> Result<SolveSuccess, SolverError> {
169        Err(SolverError::OpNotSupported("solve_mut".to_owned()))
170    }
171
172    fn load_model(&mut self, model: ConjureModel, _: private::Internal) -> Result<(), SolverError> {
173        let sym_tab = model.as_submodel().symbols().deref().clone();
174        let decisions = sym_tab.into_iter();
175
176        let mut finds: Vec<String> = Vec::new();
177
178        for find_ref in decisions {
179            if (*find_ref.1.domain().unwrap() != BoolDomain) {
180                Err(SolverError::ModelInvalid(
181                    "Only Boolean Decision Variables supported".to_string(),
182                ))?;
183            }
184
185            let name = find_ref.0;
186            finds.push(name.to_string());
187        }
188
189        self.decision_refs = Some(finds);
190
191        let m_clone = model.clone();
192        let vec_constr = m_clone.as_submodel().constraints();
193
194        let vec_cnf = vec_constr.clone();
195        // let constr = &vec_constr[0];
196        // let vec_cnf: Vec<Expression> = match constr {
197        //     Expression::And(_, vec) => vec.clone().unwrap_list().unwrap(),
198
199        //     _ => Err(SolverError::ModelInvalid(
200        //         "Only And Constraints supported".to_string(),
201        //     ))?,
202        // };
203
204        let mut var_map: HashMap<String, Lit> = HashMap::new();
205
206        let inst: SatInstance = handle_cnf(&vec_cnf, &mut var_map);
207
208        self.var_map = Some(var_map);
209        let cnf: (Cnf, BasicVarManager) = inst.clone().into_cnf();
210        tracing::info!("CNF: {:?}", cnf.0);
211        self.model_inst = Some(inst);
212
213        Ok(())
214    }
215
216    fn get_family(&self) -> SolverFamily {
217        SolverFamily::SAT
218    }
219
220    fn init_solver(&mut self, _: private::Internal) {}
221
222    fn get_name(&self) -> Option<String> {
223        Some("SAT".to_string())
224    }
225
226    fn add_adaptor_info_to_stats(&self, stats: SolverStats) -> SolverStats {
227        SolverStats {
228            solver_adaptor: self.get_name(),
229            solver_family: Some(self.get_family()),
230            ..stats
231        }
232    }
233}