Skip to main content

rustsat/instances/
sat.rs

1//! # Satisfiability Instance Representations
2
3use std::{
4    cmp,
5    collections::{BTreeSet, TryReserveError},
6    io,
7    ops::Index,
8    path::Path,
9};
10
11use crate::{
12    clause,
13    encodings::{atomics, card, pb, CollectClauses},
14    lit,
15    types::{
16        constraints::{CardConstraint, ConstraintRef, PbConstraint},
17        Assignment, Clause, Lit, TernaryVal, Var,
18    },
19    utils::{unreachable_err, LimitedIter},
20};
21
22use super::{
23    fio::{self, dimacs::CnfLine},
24    BasicVarManager, ManageVars, ReindexVars, WriteDimacsError,
25};
26
27/// Simple type representing a CNF formula. Other than [`Instance<VM>`], this
28/// type only supports clauses and does have an internal variable manager.
29///
30/// ```
31/// use rustsat::types::Var;
32/// let cnf = (Var::new(0) | Var::new(1)) & (!Var::new(0) | !Var::new(1));
33/// assert_eq!(cnf.len(), 2);
34/// ```
35#[derive(Clone, PartialEq, Eq, Default)]
36#[cfg_attr(
37    any(feature = "serde", test),
38    derive(serde::Serialize, serde::Deserialize)
39)]
40pub struct Cnf {
41    pub(super) clauses: Vec<Clause>,
42}
43
44impl std::fmt::Debug for Cnf {
45    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46        f.debug_struct("Cnf")
47            .field("clauses", &self.clauses)
48            .finish()
49    }
50}
51
52impl Cnf {
53    /// Creates a new [`Cnf`]
54    #[must_use]
55    pub fn new() -> Cnf {
56        Cnf::default()
57    }
58
59    /// Creates a new [`Cnf`] with a given capacity of clauses
60    #[must_use]
61    pub fn with_capacity(capacity: usize) -> Cnf {
62        Cnf {
63            clauses: Vec::with_capacity(capacity),
64        }
65    }
66
67    /// Tries to reserve memory for at least `additional` new clauses
68    ///
69    /// # Errors
70    ///
71    /// If the capacity overflows, or the allocator reports a failure, then an error is returned.
72    #[inline]
73    pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
74        self.clauses.try_reserve(additional)
75    }
76
77    /// Shrinks the allocated memory of the [`Cnf`] to fit the number of clauses
78    #[inline]
79    pub fn shrink_to_fit(&mut self) {
80        self.clauses.shrink_to_fit();
81    }
82
83    /// Gets the capacity of the [`Cnf`]
84    #[inline]
85    #[must_use]
86    pub fn capacity(&self) -> usize {
87        self.clauses.capacity()
88    }
89
90    /// Checks if the CNF is empty
91    #[inline]
92    #[must_use]
93    pub fn is_empty(&self) -> bool {
94        self.clauses.is_empty()
95    }
96
97    /// Returns the number of clauses in the instance
98    #[inline]
99    #[must_use]
100    pub fn len(&self) -> usize {
101        self.clauses.len()
102    }
103
104    /// Clears the CNF formula
105    #[inline]
106    pub fn clear(&mut self) {
107        self.clauses.clear();
108    }
109
110    /// Adds a clause from a slice of literals
111    ///
112    /// You can also use `self &= Clause::from(lits)`
113    pub fn add_nary(&mut self, lits: &[Lit]) {
114        *self &= Clause::from(lits);
115    }
116
117    /// See [`atomics::lit_impl_lit`]
118    pub fn add_lit_impl_lit(&mut self, a: Lit, b: Lit) {
119        *self &= atomics::lit_impl_lit(a, b);
120    }
121
122    /// See [`atomics::lit_impl_clause`]
123    pub fn add_lit_impl_clause(&mut self, a: Lit, b: &[Lit]) {
124        *self &= atomics::lit_impl_clause(a, b);
125    }
126
127    /// See [`atomics::lit_impl_cube`]
128    pub fn add_lit_impl_cube(&mut self, a: Lit, b: &[Lit]) {
129        self.extend(atomics::lit_impl_cube(a, b));
130    }
131
132    /// See [`atomics::cube_impl_lit`]
133    pub fn add_cube_impl_lit(&mut self, a: &[Lit], b: Lit) {
134        *self &= atomics::cube_impl_lit(a, b);
135    }
136
137    /// See [`atomics::clause_impl_lit`]
138    pub fn add_clause_impl_lit(&mut self, a: &[Lit], b: Lit) {
139        self.extend(atomics::clause_impl_lit(a, b));
140    }
141
142    /// See [`atomics::cube_impl_clause`]
143    pub fn add_cube_impl_clause(&mut self, a: &[Lit], b: &[Lit]) {
144        *self &= atomics::cube_impl_clause(a, b);
145    }
146
147    /// See [`atomics::clause_impl_clause`]
148    pub fn add_clause_impl_clause(&mut self, a: &[Lit], b: &[Lit]) {
149        self.extend(atomics::clause_impl_clause(a, b));
150    }
151
152    /// See [`atomics::clause_impl_cube`]
153    pub fn add_clause_impl_cube(&mut self, a: &[Lit], b: &[Lit]) {
154        self.extend(atomics::clause_impl_cube(a, b));
155    }
156
157    /// See [`atomics::cube_impl_cube`]
158    pub fn add_cube_impl_cube(&mut self, a: &[Lit], b: &[Lit]) {
159        self.extend(atomics::cube_impl_cube(a, b));
160    }
161
162    /// Joins the current CNF with another one. Like [`Cnf::extend`] but
163    /// consumes the object and returns a new object.
164    #[must_use]
165    pub fn join(mut self, other: Cnf) -> Cnf {
166        self.extend(other);
167        self
168    }
169
170    /// Returns an iterator over references to the clauses
171    pub fn iter(&self) -> std::slice::Iter<'_, Clause> {
172        self.clauses.iter()
173    }
174
175    /// Returns an iterator over mutable references to the clauses
176    pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, Clause> {
177        self.clauses.iter_mut()
178    }
179
180    /// Normalizes the CNF. This includes normalizing and sorting the clauses,
181    /// removing duplicates and tautologies. Comparing two normalized CNFs
182    /// is equal to comparing sets of sets of literals.
183    #[must_use]
184    pub fn normalize(self) -> Self {
185        let mut norm_clauses: Vec<Clause> =
186            self.into_iter().filter_map(Clause::normalize).collect();
187        // Sort and filter duplicates
188        norm_clauses.sort_unstable();
189        norm_clauses.dedup();
190        Self {
191            clauses: norm_clauses,
192        }
193    }
194
195    /// Sanitizes the CNF by removing tautologies, removing redundant literals,
196    /// etc.
197    #[must_use]
198    pub fn sanitize(self) -> Self {
199        Self {
200            clauses: self.into_iter().filter_map(Clause::sanitize).collect(),
201        }
202    }
203
204    #[cfg(feature = "rand")]
205    /// Randomly shuffles the order of clauses in the CNF
206    #[must_use]
207    pub fn shuffle(mut self) -> Self {
208        use rand::seq::SliceRandom;
209        let mut rng = rand::rng();
210        self.clauses[..].shuffle(&mut rng);
211        self
212    }
213
214    /// Adds a clause to the CNF
215    ///
216    /// You can also use `self &= clause`
217    #[inline]
218    pub fn add_clause(&mut self, clause: Clause) {
219        self.clauses.push(clause);
220    }
221
222    /// Adds a unit clause to the CNF
223    ///
224    /// You can also use `self &= unit`
225    pub fn add_unit(&mut self, unit: Lit) {
226        *self &= unit;
227    }
228
229    /// Adds a binary clause to the CNF
230    ///
231    /// You can also use `self &= lit1 | lit2`
232    pub fn add_binary(&mut self, lit1: Lit, lit2: Lit) {
233        *self &= lit1 | lit2;
234    }
235
236    /// Adds a ternary clause to the CNF
237    ///
238    /// You can also use `self &= lit1 | lit2 | lit3`
239    pub fn add_ternary(&mut self, lit1: Lit, lit2: Lit, lit3: Lit) {
240        *self &= lit1 | lit2 | lit3;
241    }
242
243    /// Writes the CNF to a DIMACS CNF file at a path
244    ///
245    /// # Errors
246    ///
247    /// If the file could not be written to, returns [`io::Error`].
248    pub fn write_dimacs_path<P: AsRef<Path>>(&self, path: P, n_vars: u32) -> Result<(), io::Error> {
249        let mut writer = fio::open_compressed_uncompressed_write(path)?;
250        self.write_dimacs(&mut writer, n_vars)
251    }
252
253    /// Writes the CNF to DIMACS CNF
254    ///
255    /// # Performance
256    ///
257    /// For performance, consider using a [`std::io::BufWriter`] instance.
258    ///
259    /// # Errors
260    ///
261    /// If writing fails, returns [`io::Error`].
262    pub fn write_dimacs<W: io::Write>(&self, writer: W, n_vars: u32) -> Result<(), io::Error> {
263        fio::dimacs::write_cnf_annotated(writer, self, n_vars)
264    }
265
266    /// Checks the value of the CNF under a given assignment
267    #[must_use]
268    pub fn evaluate(&self, assign: &Assignment) -> TernaryVal {
269        let mut val = TernaryVal::True;
270        for clause in &self.clauses {
271            match clause.evaluate(assign) {
272                TernaryVal::True => (),
273                TernaryVal::False => return TernaryVal::False,
274                TernaryVal::DontCare => val = TernaryVal::DontCare,
275            }
276        }
277        val
278    }
279}
280
281impl<'slf> IntoIterator for &'slf Cnf {
282    type Item = &'slf Clause;
283
284    type IntoIter = std::slice::Iter<'slf, Clause>;
285
286    fn into_iter(self) -> Self::IntoIter {
287        self.iter()
288    }
289}
290
291impl<'slf> IntoIterator for &'slf mut Cnf {
292    type Item = &'slf mut Clause;
293
294    type IntoIter = std::slice::IterMut<'slf, Clause>;
295
296    fn into_iter(self) -> Self::IntoIter {
297        self.iter_mut()
298    }
299}
300
301impl CollectClauses for Cnf {
302    fn n_clauses(&self) -> usize {
303        self.clauses.len()
304    }
305
306    fn extend_clauses<T>(&mut self, cl_iter: T) -> Result<(), crate::OutOfMemory>
307    where
308        T: IntoIterator<Item = Clause>,
309    {
310        let cl_iter = cl_iter.into_iter();
311        if let Some(ub) = cl_iter.size_hint().1 {
312            self.try_reserve(ub)?;
313            self.extend(cl_iter);
314        } else {
315            // Extend by reserving in exponential chunks
316            let mut cl_iter = cl_iter.peekable();
317            while cl_iter.peek().is_some() {
318                let additional = (self.len() + cmp::max(cl_iter.size_hint().0, 1))
319                    .next_power_of_two()
320                    - self.len();
321                self.try_reserve(additional)?;
322                self.extend(LimitedIter::new(&mut cl_iter, additional));
323            }
324        }
325        Ok(())
326    }
327}
328
329#[cfg(feature = "proof-logging")]
330impl crate::encodings::cert::CollectClauses for Cnf {}
331
332impl IntoIterator for Cnf {
333    type Item = Clause;
334
335    type IntoIter = std::vec::IntoIter<Clause>;
336
337    fn into_iter(self) -> Self::IntoIter {
338        self.clauses.into_iter()
339    }
340}
341
342impl FromIterator<Clause> for Cnf {
343    fn from_iter<T: IntoIterator<Item = Clause>>(iter: T) -> Self {
344        Self {
345            clauses: iter.into_iter().collect(),
346        }
347    }
348}
349
350impl FromIterator<CnfLine> for Cnf {
351    fn from_iter<T: IntoIterator<Item = CnfLine>>(iter: T) -> Self {
352        iter.into_iter().filter_map(CnfLine::clause).collect()
353    }
354}
355
356impl Extend<Clause> for Cnf {
357    fn extend<Iter: IntoIterator<Item = Clause>>(&mut self, iter: Iter) {
358        self.clauses.extend(iter);
359    }
360}
361
362impl Index<usize> for Cnf {
363    type Output = Clause;
364
365    fn index(&self, index: usize) -> &Self::Output {
366        &self.clauses[index]
367    }
368}
369
370/// Type representing a satisfiability instance. Supported constraints are
371/// clauses, cardinality constraints and pseudo-boolean constraints.
372#[derive(Clone, Debug, PartialEq, Eq)]
373#[cfg_attr(
374    any(feature = "serde", test),
375    derive(serde::Serialize, serde::Deserialize)
376)]
377pub struct Instance<VM: ManageVars = BasicVarManager> {
378    pub(super) cnf: Cnf,
379    pub(super) cards: Vec<CardConstraint>,
380    pub(super) pbs: Vec<PbConstraint>,
381    pub(super) var_manager: VM,
382}
383
384impl<VM: ManageVars> Instance<VM> {
385    /// Creates a new satisfiability instance with a specific var manager
386    pub fn new_with_manager(var_manager: VM) -> Self {
387        Instance {
388            cnf: Cnf::new(),
389            cards: vec![],
390            pbs: vec![],
391            var_manager,
392        }
393    }
394
395    /// Returns the number of clauses in the instance
396    pub fn n_clauses(&self) -> usize {
397        self.cnf.n_clauses()
398    }
399
400    /// Returns the number of cardinality constraints in the instance
401    pub fn n_cards(&self) -> usize {
402        self.cards.len()
403    }
404
405    /// Returns the number of PB constraints in the instance
406    pub fn n_pbs(&self) -> usize {
407        self.pbs.len()
408    }
409
410    /// Returns the total number of constraints
411    ///
412    /// This will always be equal to the sum of clauses, cardinality constraints, and PB
413    /// constraints.
414    /// ```
415    /// let inst: rustsat::instances::SatInstance = Default::default();
416    /// assert_eq!(inst.n_constraints(), inst.n_clauses() + inst.n_cards() + inst.n_pbs());
417    /// ```
418    pub fn n_constraints(&self) -> usize {
419        self.n_clauses() + self.n_cards() + self.n_pbs()
420    }
421
422    /// Adds a clause to the instance
423    ///
424    /// You can also use `self &= cl`
425    pub fn add_clause(&mut self, cl: Clause) {
426        cl.iter().for_each(|l| {
427            self.var_manager.mark_used(l.var());
428        });
429        self.cnf.add_clause(cl);
430    }
431
432    /// Adds a clause from a slice of literals
433    ///
434    /// You can also use `self &= Clause::from(lits)`
435    pub fn add_nary(&mut self, lits: &[Lit]) {
436        *self &= Clause::from(lits);
437    }
438
439    /// Adds a unit clause to the instance
440    ///
441    /// You can also use `self &= unit`
442    pub fn add_unit(&mut self, unit: Lit) {
443        *self &= unit;
444    }
445
446    /// Adds a binary clause to the instance
447    pub fn add_binary(&mut self, lit1: Lit, lit2: Lit) {
448        *self &= lit1 | lit2;
449    }
450
451    /// Adds a ternary clause to the instance
452    pub fn add_ternary(&mut self, lit1: Lit, lit2: Lit, lit3: Lit) {
453        *self &= lit1 | lit2 | lit3;
454    }
455
456    /// Adds an implication of form `(a -> b)` to the instance
457    pub fn add_lit_impl_lit(&mut self, a: Lit, b: Lit) {
458        self.var_manager.mark_used(a.var());
459        self.var_manager.mark_used(b.var());
460        self.cnf.add_lit_impl_lit(a, b);
461    }
462
463    /// Adds an implication of form `a -> (b1 | b2 | ... | bm)`
464    pub fn add_lit_impl_clause(&mut self, a: Lit, b: &[Lit]) {
465        self.var_manager.mark_used(a.var());
466        for l in b {
467            self.var_manager.mark_used(l.var());
468        }
469        self.cnf.add_lit_impl_clause(a, b);
470    }
471
472    /// Adds an implication of form `a -> (b1 & b2 & ... & bm)`
473    pub fn add_lit_impl_cube(&mut self, a: Lit, b: &[Lit]) {
474        self.var_manager.mark_used(a.var());
475        for l in b {
476            self.var_manager.mark_used(l.var());
477        }
478        self.cnf.add_lit_impl_cube(a, b);
479    }
480
481    /// Adds an implication of form `(a1 & a2 & ... & an) -> b`
482    pub fn add_cube_impl_lit(&mut self, a: &[Lit], b: Lit) {
483        for l in a {
484            self.var_manager.mark_used(l.var());
485        }
486        self.var_manager.mark_used(b.var());
487        self.cnf.add_cube_impl_lit(a, b);
488    }
489
490    /// Adds an implication of form `(a1 | a2 | ... | an) -> b`
491    pub fn add_clause_impl_lit(&mut self, a: &[Lit], b: Lit) {
492        for l in a {
493            self.var_manager.mark_used(l.var());
494        }
495        self.var_manager.mark_used(b.var());
496        self.cnf.add_clause_impl_lit(a, b);
497    }
498
499    /// Adds an implication of form `(a1 & a2 & ... & an) -> (b1 | b2 | ... | bm)`
500    pub fn add_cube_impl_clause(&mut self, a: &[Lit], b: &[Lit]) {
501        for l in a {
502            self.var_manager.mark_used(l.var());
503        }
504        for l in b {
505            self.var_manager.mark_used(l.var());
506        }
507        self.cnf.add_cube_impl_clause(a, b);
508    }
509
510    /// Adds an implication of form `(a1 | a2 | ... | an) -> (b1 | b2 | ... | bm)`
511    pub fn add_clause_impl_clause(&mut self, a: &[Lit], b: &[Lit]) {
512        for l in a {
513            self.var_manager.mark_used(l.var());
514        }
515        for l in b {
516            self.var_manager.mark_used(l.var());
517        }
518        self.cnf.add_clause_impl_clause(a, b);
519    }
520
521    /// Adds an implication of form `(a1 | a2 | ... | an) -> (b1 & b2 & ... & bm)`
522    pub fn add_clause_impl_cube(&mut self, a: &[Lit], b: &[Lit]) {
523        for l in a {
524            self.var_manager.mark_used(l.var());
525        }
526        for l in b {
527            self.var_manager.mark_used(l.var());
528        }
529        self.cnf.add_clause_impl_cube(a, b);
530    }
531
532    /// Adds an implication of form `(a1 & a2 & ... & an) -> (b1 & b2 & ... & bm)`
533    pub fn add_cube_impl_cube(&mut self, a: &[Lit], b: &[Lit]) {
534        for l in a {
535            self.var_manager.mark_used(l.var());
536        }
537        for l in b {
538            self.var_manager.mark_used(l.var());
539        }
540        self.cnf.add_cube_impl_cube(a, b);
541    }
542
543    /// Adds a cardinality constraint
544    pub fn add_card_constr(&mut self, card: CardConstraint) {
545        for l in &card {
546            self.var_manager.mark_used(l.var());
547        }
548        self.cards.push(card);
549    }
550
551    /// Adds a cardinality constraint
552    pub fn add_pb_constr(&mut self, pb: PbConstraint) {
553        for (l, _) in &pb {
554            self.var_manager.mark_used(l.var());
555        }
556        self.pbs.push(pb);
557    }
558
559    /// Gets a reference to the internal CNF
560    pub fn cnf(&self) -> &Cnf {
561        &self.cnf
562    }
563
564    /// Gets a mutable reference to the variable manager
565    pub fn var_manager_mut(&mut self) -> &mut VM {
566        &mut self.var_manager
567    }
568
569    /// Gets a reference to the variable manager
570    pub fn var_manager_ref(&self) -> &VM {
571        &self.var_manager
572    }
573
574    /// Reserves a new variable in the internal variable manager. This is a
575    /// shortcut for `inst.var_manager().new_var()`.
576    pub fn new_var(&mut self) -> Var {
577        self.var_manager_mut().new_var()
578    }
579
580    /// Reserves a new variable in the internal variable manager. This is a
581    /// shortcut for `inst.var_manager().new_lit()`.
582    pub fn new_lit(&mut self) -> Lit {
583        self.var_manager_mut().new_lit()
584    }
585
586    /// Gets the used variable with the highest index. This is a shortcut
587    /// for `inst.var_manager().max_var()`.
588    pub fn max_var(&self) -> Option<Var> {
589        self.var_manager_ref().max_var()
590    }
591
592    /// Returns the number of variables in the variable manager of the instance
593    pub fn n_vars(&self) -> u32 {
594        self.var_manager_ref().n_used()
595    }
596
597    /// Converts the included variable manager to a different type
598    pub fn change_var_manager<VM2, VMC>(self, vm_converter: VMC) -> (Instance<VM2>, VM)
599    where
600        VM2: ManageVars,
601        VMC: Fn(&VM) -> VM2,
602    {
603        (
604            Instance {
605                cnf: self.cnf,
606                cards: self.cards,
607                pbs: self.pbs,
608                var_manager: vm_converter(&self.var_manager),
609            },
610            self.var_manager,
611        )
612    }
613
614    /// Converts the instance to a set of clauses.
615    /// Uses the default encoders from the `encodings` module.
616    ///
617    /// See [`Self::convert_to_cnf`] for converting in place
618    ///
619    /// # Panics
620    ///
621    /// This might panic if the conversion to [`Cnf`] runs out of memory.
622    pub fn into_cnf(self) -> (Cnf, VM) {
623        self.into_cnf_with_encoders(
624            |constr, cnf, vm| {
625                card::default_encode_cardinality_constraint(constr, cnf, vm)
626                    .expect("cardinality encoding ran out of memory");
627            },
628            |constr, cnf, vm| {
629                pb::default_encode_pb_constraint(constr, cnf, vm)
630                    .expect("pb encoding ran out of memory");
631            },
632        )
633    }
634
635    /// Converts the instance to a set of clauses in-place.
636    /// Uses the default encoders from the `encodings` module.
637    ///
638    /// See [`Self::into_cnf`] if you don't need to convert in place
639    ///
640    /// # Panics
641    ///
642    /// This might panic if the conversion to [`Cnf`] runs out of memory.
643    pub fn convert_to_cnf(&mut self) {
644        self.convert_to_cnf_with_encoders(
645            |constr, cnf, vm| {
646                card::default_encode_cardinality_constraint(constr, cnf, vm)
647                    .expect("cardinality encoding ran out of memory");
648            },
649            |constr, cnf, vm| {
650                pb::default_encode_pb_constraint(constr, cnf, vm)
651                    .expect("pb encoding ran out of memory");
652            },
653        );
654    }
655
656    /// Converts the instance to a set of clauses with explicitly specified
657    /// converters for non-clausal constraints.
658    ///
659    /// See [`Self::into_cnf_with_encoders`] to convert in place
660    ///
661    /// # Panic
662    ///
663    /// The encoder functions might panic if the conversion runs out of memory.
664    pub fn into_cnf_with_encoders<CardEnc, PBEnc>(
665        mut self,
666        card_encoder: CardEnc,
667        pb_encoder: PBEnc,
668    ) -> (Cnf, VM)
669    where
670        CardEnc: FnMut(CardConstraint, &mut Cnf, &mut dyn ManageVars),
671        PBEnc: FnMut(PbConstraint, &mut Cnf, &mut dyn ManageVars),
672    {
673        self.convert_to_cnf_with_encoders(card_encoder, pb_encoder);
674        (self.cnf, self.var_manager)
675    }
676
677    /// Converts the instance in-place to a set of clauses with explicitly specified
678    /// converters for non-clausal constraints.
679    ///
680    /// See [`Self::into_cnf_with_encoders`] if you don't need to convert in place
681    ///
682    /// # Panic
683    ///
684    /// The encoder functions might panic if the conversion runs out of memory.
685    pub fn convert_to_cnf_with_encoders<CardEnc, PBEnc>(
686        &mut self,
687        mut card_encoder: CardEnc,
688        mut pb_encoder: PBEnc,
689    ) where
690        CardEnc: FnMut(CardConstraint, &mut Cnf, &mut dyn ManageVars),
691        PBEnc: FnMut(PbConstraint, &mut Cnf, &mut dyn ManageVars),
692    {
693        self.cards
694            .drain(..)
695            .for_each(|constr| card_encoder(constr, &mut self.cnf, &mut self.var_manager));
696        self.pbs
697            .drain(..)
698            .for_each(|constr| pb_encoder(constr, &mut self.cnf, &mut self.var_manager));
699    }
700
701    /// Converts the instance to a set of [`PbConstraint`]s
702    pub fn into_pbs(mut self) -> (Vec<PbConstraint>, VM) {
703        self.pbs
704            .extend(self.cards.into_iter().map(PbConstraint::from));
705        self.pbs
706            .extend(self.cnf.into_iter().map(PbConstraint::from));
707        (self.pbs, self.var_manager)
708    }
709
710    /// Extends the instance by another instance
711    pub fn extend(&mut self, other: Instance<VM>) {
712        self.cnf.extend(other.cnf);
713        self.var_manager.combine(other.var_manager);
714    }
715
716    /// Re-indexes all variables in the instance with a re-indexing variable manager
717    #[must_use]
718    pub fn reindex<R: ReindexVars>(mut self, mut reindexer: R) -> Instance<R> {
719        self.cnf
720            .iter_mut()
721            .for_each(|cl| cl.iter_mut().for_each(|l| *l = reindexer.reindex_lit(*l)));
722        self.cards
723            .iter_mut()
724            .for_each(|card| card.iter_mut().for_each(|l| *l = reindexer.reindex_lit(*l)));
725        self.pbs.iter_mut().for_each(|pb| {
726            pb.iter_mut()
727                .for_each(|(l, _)| *l = reindexer.reindex_lit(*l));
728        });
729        Instance {
730            cnf: self.cnf,
731            cards: self.cards,
732            pbs: self.pbs,
733            var_manager: reindexer,
734        }
735    }
736
737    pub(crate) fn extend_var_set(&self, varset: &mut BTreeSet<Var>) {
738        varset.extend(self.cnf.iter().flat_map(|cl| cl.iter().map(|l| l.var())));
739        varset.extend(
740            self.cards
741                .iter()
742                .flat_map(|card| card.iter().map(|l| l.var())),
743        );
744        varset.extend(
745            self.pbs
746                .iter()
747                .flat_map(|pbs| pbs.iter().map(|(l, _)| l.var())),
748        );
749    }
750
751    /// Gets the set of variables in the instance
752    pub fn var_set(&self) -> BTreeSet<Var> {
753        let mut varset = BTreeSet::new();
754        self.extend_var_set(&mut varset);
755        varset
756    }
757
758    /// Re-index all variables in the instance in order
759    ///
760    /// If the re-indexing variable manager produces new free variables in order, this results in
761    /// the variable _order_ being preserved with gaps in the variable space being closed
762    #[must_use]
763    pub fn reindex_ordered<R: ReindexVars>(self, mut reindexer: R) -> Instance<R> {
764        let mut varset = BTreeSet::new();
765        self.extend_var_set(&mut varset);
766        // reindex variables in order to ensure ordered reindexing
767        for var in varset {
768            reindexer.reindex(var);
769        }
770        self.reindex(reindexer)
771    }
772
773    #[cfg(feature = "rand")]
774    /// Randomly shuffles the order of constraints.
775    #[must_use]
776    pub fn shuffle(mut self) -> Self {
777        use rand::seq::SliceRandom;
778        self.cnf = self.cnf.shuffle();
779        let mut rng = rand::rng();
780        self.cards[..].shuffle(&mut rng);
781        self.pbs[..].shuffle(&mut rng);
782        self
783    }
784
785    /// Writes the instance to a DIMACS CNF file at a path
786    ///
787    /// This requires that the instance is clausal, i.e., does not contain any non-converted
788    /// cardinality of pseudo-boolean constraints. If necessary, the instance can be converted by
789    /// [`Self::convert_to_cnf`] or [`Self::convert_to_cnf_with_encoders`] first.
790    ///
791    /// # Errors
792    ///
793    /// If the instance is not clausal or writing fails
794    pub fn write_dimacs_path<P: AsRef<Path>>(&self, path: P) -> Result<(), WriteDimacsError> {
795        let mut writer = fio::open_compressed_uncompressed_write(path)?;
796        self.write_dimacs(&mut writer)
797    }
798
799    /// Writes the instance to DIMACS CNF
800    ///
801    /// This requires that the instance is clausal, i.e., does not contain any non-converted
802    /// cardinality of pseudo-boolean constraints. If necessary, the instance can be converted by
803    /// [`Self::convert_to_cnf`] or [`Self::convert_to_cnf_with_encoders`] first.
804    ///
805    /// # Performance
806    ///
807    /// For performance, consider using a [`std::io::BufWriter`] instance.
808    ///
809    /// # Errors
810    ///
811    /// If the instance is not clausal or writing fails
812    pub fn write_dimacs<W: io::Write>(&self, writer: W) -> Result<(), WriteDimacsError> {
813        if self.n_cards() > 0 || self.n_pbs() > 0 {
814            return Err(WriteDimacsError::RequiresClausal);
815        }
816        let n_vars = self.n_vars();
817        Ok(fio::dimacs::write_cnf_annotated(writer, &self.cnf, n_vars)?)
818    }
819
820    /// Writes the instance to an OPB file at a path
821    ///
822    /// # Errors
823    ///
824    /// If writing to file fails, returns [`io::Error`].
825    pub fn write_opb_path<P: AsRef<Path>>(
826        &self,
827        path: P,
828        opts: fio::opb::Options,
829    ) -> Result<(), io::Error> {
830        let mut writer = fio::open_compressed_uncompressed_write(path)?;
831        self.write_opb(&mut writer, opts)
832    }
833
834    /// Writes the instance to an OPB file
835    ///
836    /// # Performance
837    ///
838    /// For performance, consider using a [`std::io::BufWriter`] instance.
839    ///
840    /// # Errors
841    ///
842    /// If writing fails, returns [`io::Error`].
843    pub fn write_opb<W: io::Write>(
844        &self,
845        writer: W,
846        opts: fio::opb::Options,
847    ) -> Result<(), io::Error> {
848        fio::opb::write_sat(writer, self, opts)
849    }
850
851    /// Sanitizes the constraints, i.e., for example a cardinality
852    /// constraint of form `x + y >= 1` will be converted to a clause and
853    /// tautologies will be removed.
854    #[must_use]
855    pub fn sanitize(self) -> Self {
856        let mut unsat = false;
857        let mut cnf = self.cnf;
858        let mut cards = self.cards;
859        let pbs = self
860            .pbs
861            .into_iter()
862            .filter_map(|pb| {
863                if pb.is_tautology() {
864                    return None;
865                }
866                if pb.is_unsat() {
867                    unsat = true;
868                    return None;
869                }
870                if pb.is_positive_assignment() {
871                    // Add unit clauses
872                    pb.into_lits()
873                        .into_iter()
874                        .for_each(|(l, _)| cnf.add_unit(l));
875                    return None;
876                }
877                if pb.is_negative_assignment() {
878                    // Add unit clauses
879                    pb.into_lits()
880                        .into_iter()
881                        .for_each(|(l, _)| cnf.add_unit(!l));
882                    return None;
883                }
884                if pb.is_clause() {
885                    cnf.add_clause(unreachable_err!(pb.into_clause()));
886                    return None;
887                }
888                if pb.is_card() {
889                    cards.push(unreachable_err!(pb.into_card_constr()));
890                    return None;
891                }
892                Some(pb)
893            })
894            .collect();
895        let cards = cards
896            .into_iter()
897            .filter_map(|card| {
898                if card.is_tautology() {
899                    return None;
900                }
901                if card.is_unsat() {
902                    unsat = true;
903                    return None;
904                }
905                if card.is_positive_assignment() {
906                    // Add unit clauses
907                    card.into_lits().into_iter().for_each(|l| cnf.add_unit(l));
908                    return None;
909                }
910                if card.is_negative_assignment() {
911                    // Add unit clauses
912                    card.into_lits().into_iter().for_each(|l| cnf.add_unit(!l));
913                    return None;
914                }
915                if card.is_clause() {
916                    cnf.add_clause(unreachable_err!(card.into_clause()));
917                    return None;
918                }
919                Some(card)
920            })
921            .collect();
922        if unsat {
923            return Self {
924                cnf: Cnf::from_iter(vec![clause![lit![0]], clause![!lit![0]]]),
925                cards: vec![],
926                pbs: vec![],
927                var_manager: self.var_manager,
928            };
929        }
930        Self {
931            cnf: cnf.into_iter().filter_map(Clause::sanitize).collect(),
932            cards,
933            pbs,
934            var_manager: self.var_manager,
935        }
936    }
937
938    /// Computes the value of the instance under a given assignment
939    #[must_use]
940    pub fn evaluate(&self, assign: &Assignment) -> TernaryVal {
941        let mut val = TernaryVal::True;
942        match self.cnf.evaluate(assign) {
943            TernaryVal::True => (),
944            TernaryVal::False => return TernaryVal::False,
945            TernaryVal::DontCare => val = TernaryVal::DontCare,
946        }
947        for card in &self.cards {
948            match card.evaluate(assign) {
949                TernaryVal::True => (),
950                TernaryVal::False => return TernaryVal::False,
951                TernaryVal::DontCare => val = TernaryVal::DontCare,
952            }
953        }
954        for pb in &self.pbs {
955            match pb.evaluate(assign) {
956                TernaryVal::True => (),
957                TernaryVal::False => return TernaryVal::False,
958                TernaryVal::DontCare => val = TernaryVal::DontCare,
959            }
960        }
961        val
962    }
963
964    /// Returns an unsatisfied constraint, if one exists
965    pub fn unsat_constraint(&self, assign: &Assignment) -> Option<ConstraintRef<'_>> {
966        for clause in &self.cnf {
967            if clause.evaluate(assign) != TernaryVal::True {
968                return Some(ConstraintRef::Clause(clause));
969            }
970        }
971        for card in &self.cards {
972            if card.evaluate(assign) != TernaryVal::True {
973                return Some(ConstraintRef::Card(card));
974            }
975        }
976        for pb in &self.pbs {
977            if pb.evaluate(assign) != TernaryVal::True {
978                return Some(ConstraintRef::Pb(pb));
979            }
980        }
981        None
982    }
983}
984
985impl<VM: ManageVars + Default> Instance<VM> {
986    /// Creates a new satisfiability instance
987    #[must_use]
988    pub fn new() -> Instance<VM> {
989        Instance::default()
990    }
991
992    /// Parses a DIMACS instance from a reader object.
993    ///
994    /// # File Format
995    ///
996    /// The file format expected by this parser is the DIMACS CNF format used in
997    /// the SAT competition since 2011. For details on the file format see
998    /// [here](http://www.satcompetition.org/2011/format-benchmarks2011.html).
999    ///
1000    /// If a DIMACS WCNF or MCNF file is parsed with this method, the objectives
1001    /// are ignored and only the constraints returned.
1002    ///
1003    /// # Errors
1004    ///
1005    /// [`io::Error`] or if parsing fails.
1006    pub fn from_dimacs<R: io::BufRead>(reader: R) -> Result<Self, fio::Error> {
1007        let header_data =
1008            fio::dimacs::Parser::<fio::dimacs::CnfHeader, _>::new(reader).forward_to_body()?;
1009        let mut inst = Self::default();
1010        inst.var_manager_mut()
1011            .increase_next_free(Var::new(header_data.n_vars));
1012        for data in header_data.body_parser {
1013            match data? {
1014                fio::dimacs::CnfData::Clause(clause) => inst.add_clause(clause),
1015                fio::dimacs::CnfData::Comment(_) => (),
1016            }
1017        }
1018        Ok(inst)
1019    }
1020
1021    /// Parses a DIMACS instance from a file path. For more details see
1022    /// [`Instance::from_dimacs`]. With feature `compression` supports
1023    /// bzip2 and gzip compression, detected by the file extension.
1024    ///
1025    /// # Errors
1026    ///
1027    /// [`io::Error`] or if parsing fails.
1028    pub fn from_dimacs_path<P: AsRef<Path>>(path: P) -> Result<Self, fio::Error> {
1029        let mut reader = fio::open_compressed_uncompressed_read(path)?;
1030        Instance::from_dimacs(&mut reader)
1031    }
1032
1033    /// Parses an OPB instance from a reader object.
1034    ///
1035    /// # File Format
1036    ///
1037    /// The file format expected by this parser is the OPB format for
1038    /// pseudo-boolean satisfaction instances. For details on the file format
1039    /// see [here](https://www.cril.univ-artois.fr/PB12/format.pdf).
1040    ///
1041    /// # Errors
1042    ///
1043    /// [`io::Error`] or if parsing fails.
1044    pub fn from_opb<R: io::BufRead>(
1045        reader: R,
1046        opts: fio::opb::Options,
1047    ) -> Result<Self, fio::Error> {
1048        let parser = fio::opb::Parser::new(reader, opts);
1049        let mut inst = Self::new();
1050        for data in parser {
1051            match data? {
1052                fio::opb::Data::Cmt(_) => {}
1053                fio::opb::Data::Constr(constr) => inst.add_pb_constr(constr),
1054                #[cfg(feature = "optimization")]
1055                fio::opb::Data::Obj(_) => {
1056                    return Err(fio::Error::ObjInSat);
1057                }
1058            }
1059        }
1060        Ok(inst)
1061    }
1062
1063    /// Parses an OPB instance from a file path. For more details see
1064    /// [`Instance::from_opb`]. With feature `compression` supports
1065    /// bzip2 and gzip compression, detected by the file extension.
1066    ///
1067    /// # Errors
1068    ///
1069    /// [`io::Error`] or if parsing fails.
1070    pub fn from_opb_path<P: AsRef<Path>>(
1071        path: P,
1072        opts: fio::opb::Options,
1073    ) -> Result<Self, fio::Error> {
1074        let mut reader = fio::open_compressed_uncompressed_read(path)?;
1075        Instance::from_opb(&mut reader, opts)
1076    }
1077}
1078
1079impl<VM: ManageVars + Default> Default for Instance<VM> {
1080    fn default() -> Self {
1081        Self {
1082            cnf: Cnf::default(),
1083            cards: Vec::default(),
1084            pbs: Vec::default(),
1085            var_manager: VM::default(),
1086        }
1087    }
1088}
1089
1090impl<VM: ManageVars + Default> FromIterator<Clause> for Instance<VM> {
1091    fn from_iter<T: IntoIterator<Item = Clause>>(iter: T) -> Self {
1092        let mut inst = Self::default();
1093        iter.into_iter().for_each(|cl| inst.add_clause(cl));
1094        inst
1095    }
1096}
1097
1098impl<VM: ManageVars + Default> FromIterator<CnfLine> for Instance<VM> {
1099    fn from_iter<T: IntoIterator<Item = CnfLine>>(iter: T) -> Self {
1100        iter.into_iter().filter_map(CnfLine::clause).collect()
1101    }
1102}
1103
1104impl<VM: ManageVars + Default> FromIterator<PbConstraint> for Instance<VM> {
1105    fn from_iter<T: IntoIterator<Item = PbConstraint>>(iter: T) -> Self {
1106        let mut inst = Self::default();
1107        iter.into_iter()
1108            .for_each(|constr| inst.add_pb_constr(constr));
1109        inst
1110    }
1111}
1112
1113impl<VM: ManageVars + Default> FromIterator<CardConstraint> for Instance<VM> {
1114    fn from_iter<T: IntoIterator<Item = CardConstraint>>(iter: T) -> Self {
1115        let mut inst = Self::default();
1116        iter.into_iter()
1117            .for_each(|constr| inst.add_card_constr(constr));
1118        inst
1119    }
1120}
1121
1122impl<VM: ManageVars + Default> FromIterator<fio::opb::Data> for Instance<VM> {
1123    fn from_iter<T: IntoIterator<Item = fio::opb::Data>>(iter: T) -> Self {
1124        iter.into_iter()
1125            .filter_map(fio::opb::Data::constraint)
1126            .collect()
1127    }
1128}
1129
1130impl<VM: ManageVars + Default> From<Cnf> for Instance<VM> {
1131    fn from(value: Cnf) -> Self {
1132        let mut inst = Self {
1133            cnf: value,
1134            ..Default::default()
1135        };
1136        for cl in &inst.cnf {
1137            for l in cl {
1138                inst.var_manager.increase_next_free(l.var() + 1);
1139            }
1140        }
1141        inst
1142    }
1143}
1144
1145impl<VM: ManageVars> Extend<Clause> for Instance<VM> {
1146    fn extend<T: IntoIterator<Item = Clause>>(&mut self, iter: T) {
1147        self.cnf.extend(iter);
1148    }
1149}
1150
1151impl CollectClauses for Instance {
1152    fn n_clauses(&self) -> usize {
1153        self.n_clauses()
1154    }
1155
1156    fn extend_clauses<T>(&mut self, cl_iter: T) -> Result<(), crate::OutOfMemory>
1157    where
1158        T: IntoIterator<Item = Clause>,
1159    {
1160        self.cnf.extend_clauses(cl_iter)
1161    }
1162}
1163
1164#[cfg(test)]
1165mod tests {
1166    use crate::{instances::Cnf, lit};
1167
1168    #[test]
1169    fn reindex_ordered() {
1170        let mut inst: super::Instance = super::Instance::default();
1171        inst.add_nary(&[lit![4], lit![1]]);
1172        inst.add_nary(&[lit![0], lit![2]]);
1173        let inst = inst.reindex_ordered(super::super::ReindexingVarManager::default());
1174        let (cnf, _) = inst.into_cnf();
1175        assert_eq!(cnf, Cnf::from_iter([lit![3] | lit![1], lit![0] | lit![2]]));
1176    }
1177}