1use core::ffi::{c_int, c_uint, CStr};
6use std::ffi::CString;
7
8use cpu_time::ProcessTime;
9use rustsat::{
10 instances::Cnf,
11 types::{Assignment, Clause, Lit, Var, WClsIter},
12};
13
14use crate::Error;
15
16use super::{ffi, Options, PreproClauses, Stats};
17
18pub struct MaxPre {
20 handle: *mut ffi::CMaxPre,
22 offsets: Vec<isize>,
24 stats: Stats,
26}
27
28impl PreproClauses for MaxPre {
29 fn signature() -> &'static str {
30 let c_chars = unsafe { ffi::cmaxpre_signature() };
31 let c_str = unsafe { CStr::from_ptr(c_chars) };
32 c_str
33 .to_str()
34 .expect("MaxPre signature returned invalid UTF-8")
35 }
36
37 fn new<CI: WClsIter>(hards: Cnf, softs: Vec<(CI, isize)>, inprocessing: bool) -> Self {
38 let mut top = 1;
39 let softs: Vec<(Vec<(Clause, usize)>, isize)> = softs
40 .into_iter()
41 .map(|(cls, ofs)| (cls.into_iter().collect(), ofs))
42 .collect();
43 top = softs.iter().fold(top, |top, softs| {
44 softs.0.iter().fold(top, |top, (_, w)| top + w)
45 });
46 let mut stats = Stats {
47 n_objs: softs.len(),
48 n_orig_hard_clauses: hards.len(),
49 ..Default::default()
50 };
51 let handle = unsafe { ffi::cmaxpre_init_start(top as u64, ffi::map_bool(inprocessing)) };
52 hards.into_iter().for_each(|cl| {
53 cl.into_iter().for_each(|l| {
54 stats.max_orig_var = Self::track_max_var(stats.max_orig_var, l.var());
55 unsafe { ffi::cmaxpre_init_add_lit(handle, l.to_ipasir()) }
56 });
57 unsafe { ffi::cmaxpre_init_add_lit(handle, 0) };
58 });
59 let mut offsets = Vec::new();
60 softs.into_iter().enumerate().for_each(|(idx, softs)| {
61 offsets.push(softs.1);
62 stats.n_orig_soft_clauses.push(softs.0.len());
63 softs.0.into_iter().for_each(|(cl, w)| {
64 (0..idx).for_each(|_| unsafe { ffi::cmaxpre_init_add_weight(handle, 0) });
66 unsafe { ffi::cmaxpre_init_add_weight(handle, w as u64) };
68 cl.into_iter().for_each(|l| {
70 stats.max_orig_var = Self::track_max_var(stats.max_orig_var, l.var());
71 unsafe { ffi::cmaxpre_init_add_lit(handle, l.to_ipasir()) }
72 });
73 unsafe { ffi::cmaxpre_init_add_lit(handle, 0) };
74 })
75 });
76 unsafe { ffi::cmaxpre_init_finalize(handle) };
77 Self {
78 handle,
79 offsets,
80 stats,
81 }
82 }
83
84 fn preprocess(&mut self, techniques: &str, log_level: c_int, time_limit: f64) {
85 let start = ProcessTime::now();
86 let techniques = CString::new(techniques).unwrap();
87 unsafe {
88 ffi::cmaxpre_preprocess(
89 self.handle,
90 techniques.as_ptr(),
91 log_level,
92 time_limit,
93 ffi::FALSE,
94 ffi::FALSE,
95 )
96 };
97 self.stats.prepro_time += start.elapsed();
98 }
99
100 fn top_weight(&self) -> u64 {
101 unsafe { ffi::cmaxpre_get_top_weight(self.handle) }
102 }
103
104 fn n_prepro_clauses(&self) -> c_uint {
105 unsafe { ffi::cmaxpre_get_n_prepro_clauses(self.handle) }
106 }
107
108 fn n_prepro_labels(&self) -> c_uint {
109 unsafe { ffi::cmaxpre_get_n_prepro_labels(self.handle) }
110 }
111
112 fn n_prepro_fixed_lits(&self) -> c_uint {
113 unsafe { ffi::cmaxpre_get_n_prepro_fixed(self.handle) }
114 }
115
116 fn prepro_instance(&mut self) -> (Cnf, Vec<(Vec<(Clause, usize)>, isize)>) {
117 let n_cls = self.n_prepro_clauses();
118 let top = self.top_weight();
119 let mut hards = Cnf::new();
120 let mut softs: Vec<Vec<(Clause, usize)>> = vec![Default::default(); self.stats.n_objs];
121 for cl_idx in 0..n_cls {
122 let mut clause = Clause::new();
124 let mut lit_idx = 0;
125 loop {
126 let lit = unsafe { ffi::cmaxpre_get_prepro_lit(self.handle, cl_idx, lit_idx) };
127 if lit == 0 {
128 break;
129 }
130 let lit = Lit::from_ipasir(lit).unwrap();
131 self.stats.max_prepro_var =
132 Self::track_max_var(self.stats.max_prepro_var, lit.var());
133 clause.add(lit);
134 lit_idx += 1;
135 }
136 let mut is_hard = true;
138 for obj_idx in 0..self.stats.n_objs {
139 let w = unsafe {
140 ffi::cmaxpre_get_prepro_weight(self.handle, cl_idx, obj_idx as c_uint)
141 };
142 if w == 0 {
143 continue;
144 }
145 if w != top {
146 if softs.len() < obj_idx + 1 {
148 softs.resize(obj_idx + 1, Default::default());
149 }
150 softs[obj_idx].push((clause.clone(), w as usize));
151 is_hard = false;
152 }
153 }
154 if is_hard {
155 hards.add_clause(clause);
157 }
158 }
159 self.stats.n_prepro_hard_clauses = hards.len();
160 self.stats.n_prepro_soft_clauses = softs.iter().map(|s| s.len()).collect();
161 self.stats.removed_weight.clear();
162 let softs = softs
163 .into_iter()
164 .enumerate()
165 .map(|(idx, s)| {
166 let rem_weight =
167 unsafe { ffi::cmaxpre_get_removed_weight(self.handle, idx as c_uint) } as usize;
168 self.stats.removed_weight.push(rem_weight);
169 let offset = rem_weight as isize + self.offsets[idx];
170 (s, offset)
171 })
172 .collect();
173 (hards, softs)
174 }
175
176 fn prepro_labels(&self) -> Vec<Lit> {
177 let n_lbls = self.n_prepro_labels();
178 let mut lbls = Vec::new();
179 for lbl_idx in 0..n_lbls {
180 lbls.push(
181 Lit::from_ipasir(unsafe { ffi::cmaxpre_get_prepro_label(self.handle, lbl_idx) })
182 .unwrap(),
183 );
184 }
185 lbls
186 }
187
188 fn prepro_fixed_lits(&self) -> Vec<Lit> {
189 let n_fixed = self.n_prepro_fixed_lits();
190 let mut fixed = Vec::new();
191 for fixed_idx in 0..n_fixed {
192 fixed.push(
193 Lit::from_ipasir(unsafe {
194 ffi::cmaxpre_get_prepro_fixed_lit(self.handle, fixed_idx)
195 })
196 .unwrap(),
197 );
198 }
199 fixed
200 }
201
202 fn max_orig_var(&self) -> Var {
203 Lit::from_ipasir(unsafe { ffi::cmaxpre_get_original_variables(self.handle) })
204 .unwrap()
205 .var()
206 }
207
208 fn upper_bound(&self) -> u64 {
209 unsafe { ffi::cmaxpre_get_upper_bound(self.handle) }
210 }
211
212 fn reconstruct(&mut self, sol: Assignment) -> Assignment {
213 let start = ProcessTime::now();
214 sol.into_iter()
215 .for_each(|l| unsafe { ffi::cmaxpre_assignment_add(self.handle, l.to_ipasir()) });
216 unsafe { ffi::cmaxpre_reconstruct(self.handle) };
217 let max_var = self.max_orig_var();
218 let rec = (1..max_var.pos_lit().to_ipasir() + 1)
219 .map(|l| {
220 if unsafe { ffi::cmaxpre_reconstructed_val(self.handle, l) } > 0 {
221 Lit::from_ipasir(l).unwrap()
222 } else {
223 Lit::from_ipasir(-l).unwrap()
224 }
225 })
226 .collect();
227 self.stats.reconst_time += start.elapsed();
228 rec
229 }
230
231 fn add_var(&mut self) -> Result<Var, Error> {
232 let v = unsafe { ffi::cmaxpre_add_var(self.handle, 0) };
233 if v == 0 {
234 return Err(Error::Generic);
235 }
236 Ok(Lit::from_ipasir(v).unwrap().var())
237 }
238
239 fn add_clause(&mut self, clause: Clause) -> Result<(), Error> {
240 clause.into_iter().for_each(|l| unsafe {
241 ffi::cmaxpre_add_lit(self.handle, l.to_ipasir());
242 });
243 if unsafe { ffi::cmaxpre_add_lit(self.handle, 0) } == ffi::FALSE {
244 return Err(Error::Generic);
245 }
246 Ok(())
247 }
248
249 fn add_label(&mut self, label: Lit, weight: usize) -> Result<Lit, Error> {
250 let l = unsafe { ffi::cmaxpre_add_label(self.handle, label.to_ipasir(), weight as u64) };
251 if l == 0 {
252 return Err(Error::Generic);
253 }
254 Ok(Lit::from_ipasir(l).unwrap())
255 }
256
257 fn alter_weight(&mut self, label: Lit, weight: usize) -> Result<(), Error> {
258 if unsafe { ffi::cmaxpre_alter_weight(self.handle, label.to_ipasir(), weight as u64) }
259 == ffi::FALSE
260 {
261 return Err(Error::Generic);
262 }
263 Ok(())
264 }
265
266 fn label_to_var(&mut self, label: Lit) -> Result<(), Error> {
267 if unsafe { ffi::cmaxpre_label_to_var(self.handle, label.to_ipasir()) } == ffi::FALSE {
268 return Err(Error::Generic);
269 }
270 Ok(())
271 }
272
273 fn reset_removed_weight(&mut self) -> Result<(), Error> {
274 if unsafe { ffi::cmaxpre_reset_removed_weight(self.handle) } == ffi::FALSE {
275 return Err(Error::Generic);
276 }
277 Ok(())
278 }
279
280 fn removed_weight(&mut self) -> Vec<usize> {
281 self.stats.removed_weight = (0..self.stats.n_objs)
282 .map(|obj_idx| unsafe {
283 ffi::cmaxpre_get_removed_weight(self.handle, obj_idx as c_uint)
284 } as usize)
285 .collect();
286 self.stats.removed_weight.clone()
287 }
288
289 fn set_options(&mut self, opts: Options) {
290 if let Some(val) = opts.bve_sort_max_first {
291 unsafe { ffi::cmaxpre_set_bve_gate_extraction(self.handle, ffi::map_bool(val)) };
292 }
293 if let Some(val) = opts.label_matching {
294 unsafe { ffi::cmaxpre_set_label_matching(self.handle, ffi::map_bool(val)) };
295 }
296 if let Some(val) = opts.skip_technique {
297 unsafe { ffi::cmaxpre_set_skip_technique(self.handle, val) };
298 }
299 if let Some(val) = opts.bve_sort_max_first {
300 unsafe { ffi::cmaxpre_set_bve_sort_max_first(self.handle, ffi::map_bool(val)) };
301 }
302 if let Some(val) = opts.bve_local_grow_limit {
303 unsafe { ffi::cmaxpre_set_bve_local_grow_limit(self.handle, val) };
304 }
305 if let Some(val) = opts.bve_global_grow_limit {
306 unsafe { ffi::cmaxpre_set_bve_global_grow_limit(self.handle, val) };
307 }
308 if let Some(val) = opts.max_bbtms_vars {
309 unsafe { ffi::cmaxpre_set_max_bbtms_vars(self.handle, val) };
310 }
311 if let Some(val) = opts.harden_in_model_search {
312 unsafe { ffi::cmaxpre_set_harden_in_model_search(self.handle, ffi::map_bool(val)) };
313 }
314 if let Some(val) = opts.model_search_iter_limits {
315 unsafe { ffi::cmaxpre_set_model_search_iter_limit(self.handle, val) };
316 }
317 }
318
319 fn print_instance(&self) {
320 unsafe { ffi::cmaxpre_print_instance_stdout(self.handle) }
321 }
322
323 fn print_solution(&self, sol: Assignment, weight: usize) {
324 sol.into_iter()
325 .for_each(|l| unsafe { ffi::cmaxpre_assignment_add(self.handle, l.to_ipasir()) });
326 unsafe { ffi::cmaxpre_print_solution_stdout(self.handle, weight as u64) }
327 }
328
329 fn print_map(&self) {
330 unsafe { ffi::cmaxpre_print_map_stdout(self.handle) }
331 }
332
333 fn print_technique_log(&self) {
334 unsafe { ffi::cmaxpre_print_technique_log_stdout(self.handle) }
335 }
336
337 fn print_info_log(&self) {
338 unsafe { ffi::cmaxpre_print_info_log_stdout(self.handle) }
339 }
340
341 fn print_stats(&self) {
342 unsafe { ffi::cmaxpre_print_preprocessor_stats_stdout(self.handle) }
343 }
344
345 fn stats(&self) -> Stats {
346 self.stats.clone()
347 }
348}
349
350impl MaxPre {
351 fn track_max_var(max_var: Option<Var>, new_var: Var) -> Option<Var> {
353 match max_var {
354 Some(max_var) => {
355 if new_var > max_var {
356 Some(new_var)
357 } else {
358 Some(max_var)
359 }
360 }
361 None => Some(new_var),
362 }
363 }
364}
365
366impl Drop for MaxPre {
367 fn drop(&mut self) {
368 unsafe { ffi::cmaxpre_release(self.handle) }
369 }
370}
371
372#[cfg(test)]
373mod tests {
374 use rustsat::{instances::Cnf, lit, types::Clause};
375
376 use crate::PreproClauses;
377
378 use super::MaxPre;
379
380 #[test]
381 fn construct() {
382 let mut cnf = Cnf::new();
383 cnf.add_binary(lit![0], lit![2]);
384 MaxPre::new::<Vec<(Clause, usize)>>(cnf, vec![], true);
385 }
386}