scuttle_core/
options.rs

1//! # Options
2//!
3//! This module contains all configuration options or the $P$-minimal solver.
4
5use std::fmt;
6
7use crate::Phase;
8
9/// Solver-wide configuration options
10#[derive(Clone, Copy)]
11#[derive(Default)]
12pub struct KernelOptions {
13    /// The Pareto point enumeration mode
14    pub enumeration: EnumOptions,
15    /// Reserve encoding variables in advance
16    pub reserve_enc_vars: bool,
17    /// Heuristic solution improvement options
18    pub heuristic_improvements: HeurImprOptions,
19    /// Solution-guided search (aka phasing solutions)
20    pub solution_guided_search: bool,
21    /// Core trimming (in core-guided algorithms)
22    pub core_trimming: bool,
23    /// Core minimization (in core-guided algorithms)
24    pub core_minimization: bool,
25    /// Core exhaustion (in OLL)
26    pub core_exhaustion: bool,
27    /// Store the original clauses
28    pub store_cnf: bool,
29}
30
31
32impl KernelOptions {
33    pub fn set_enumeration(&mut self, enumeration: EnumOptions) {
34        self.enumeration = enumeration;
35    }
36}
37
38#[derive(Clone, Default)]
39pub struct CoreBoostingOptions {
40    /// Whether to merge or rebase the encoding
41    pub rebase: bool,
42    /// What to do after core boosting
43    pub after: AfterCbOptions,
44}
45
46#[derive(Clone, Default, PartialEq, Eq)]
47pub enum AfterCbOptions {
48    /// Don't do anything special after core boosting
49    #[default]
50    Nothing,
51    /// Reset the SAT oracle after core boosting
52    Reset,
53    /// Perform MaxPre "inprocessing" after core boosting
54    #[cfg(feature = "maxpre")]
55    Inpro(String),
56}
57
58pub type KernelWithCbOptions = (KernelOptions, Option<CoreBoostingOptions>);
59
60/// Enumeration options for the $P$-minimal solver
61#[derive(Default, Clone, Copy, PartialEq, Eq)]
62pub enum EnumOptions {
63    #[default]
64    /// Don't enumerate at each Pareto point
65    NoEnum,
66    /// Enumerate Pareto-optimal solutions (with an optional limit) at each
67    /// Pareto point using the provided blocking clause generator
68    Solutions(Option<usize>),
69    /// Enumerate Pareto-MCSs (with an optional limit) at each Pareto point
70    PMCSs(Option<usize>),
71}
72
73/// Options regarding heuristic solution improvement
74#[derive(Clone, Copy)]
75pub struct HeurImprOptions {
76    /// When to perform solution tightening (flipping objective literals that can
77    /// be flipped without breaking satisfiability)
78    pub solution_tightening: HeurImprWhen,
79}
80
81impl HeurImprOptions {
82    /// No heuristic improvements
83    pub fn none() -> Self {
84        Self {
85            solution_tightening: HeurImprWhen::Never,
86        }
87    }
88
89    /// Always all heuristic improvements
90    pub fn all() -> Self {
91        Self {
92            solution_tightening: HeurImprWhen::Always,
93        }
94    }
95}
96
97impl Default for HeurImprOptions {
98    fn default() -> Self {
99        Self {
100            solution_tightening: HeurImprWhen::OuterLoop,
101        }
102    }
103}
104
105/// Options for when solution improvement can be performed
106#[derive(Clone, Copy, Default, PartialEq, Eq)]
107#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
108pub enum HeurImprWhen {
109    /// Never perform solution improvement
110    #[default]
111    Never,
112    /// Always perform solution improvement
113    Always,
114    /// Only perform solution improvement in outer loop
115    OuterLoop,
116}
117
118impl HeurImprWhen {
119    /// Checks whether the technique is wanted for a given phase
120    pub(crate) fn wanted(&self, phase: Phase) -> bool {
121        if phase == Phase::Enumeration {
122            return false;
123        }
124        match self {
125            HeurImprWhen::Never => false,
126            HeurImprWhen::Always => true,
127            HeurImprWhen::OuterLoop => phase == Phase::OuterLoop,
128        }
129    }
130}
131
132impl fmt::Display for HeurImprWhen {
133    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134        match self {
135            HeurImprWhen::Never => write!(f, "never"),
136            HeurImprWhen::Always => write!(f, "always"),
137            HeurImprWhen::OuterLoop => write!(f, "outer-loop"),
138        }
139    }
140}
141
142/// Limits for a call to [`crate::Solve::solve`]
143#[derive(Clone, Copy, Default)]
144pub struct Limits {
145    /// The maximum number of Pareto points to enumerate
146    pub pps: Option<usize>,
147    /// The maximum number of solutions to enumerate
148    pub sols: Option<usize>,
149    /// The maximum number of candidates to consider
150    pub candidates: Option<usize>,
151    /// The maximum number of SAT oracle calls to make
152    pub oracle_calls: Option<usize>,
153}
154
155impl Limits {
156    /// No limits
157    pub fn none() -> Limits {
158        Limits {
159            pps: None,
160            sols: None,
161            candidates: None,
162            oracle_calls: None,
163        }
164    }
165}
166
167/// Possible recursion anchors for the divide and conquer algorithm
168#[derive(Clone, Copy, Default, PartialEq, Eq, Debug)]
169pub enum DivConAnchor {
170    /// Linear Sat-Unsat for single-objective subproblems
171    LinSu,
172    /// BiOptSat (Sat-Unsat) for bi-objective subproblems
173    #[default]
174    BiOptSat,
175    /// P-Minimal at subproblems of given size
176    PMinimal(SubProblemSize),
177    /// Run lower-bounding algorithm at subproblems of given size
178    LowerBounding(SubProblemSize),
179    /// Run the appropriate anchor (Linear Sat-Unsat / BiOptSat / P-Minimal) at
180    /// subproblems of size `n-x`.
181    NMinus(usize),
182}
183
184impl fmt::Display for DivConAnchor {
185    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186        match self {
187            DivConAnchor::LinSu => write!(f, "lin-su"),
188            DivConAnchor::BiOptSat => write!(f, "bioptsat"),
189            DivConAnchor::PMinimal(size) => write!(f, "p-minimal({})", size),
190            DivConAnchor::LowerBounding(size) => write!(f, "lower-bounding({})", size),
191            DivConAnchor::NMinus(x) => write!(f, "n-minus({})", x),
192        }
193    }
194}
195
196/// Possible options for building objective encodings in divide and conquer
197#[derive(Clone, Copy, Default, PartialEq, Eq, Debug)]
198#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
199pub enum BuildEncodings {
200    /// Only once after the first ideal point
201    #[default]
202    Once,
203    /// Rebuild after each ideal point but don't restart the oracle
204    Rebuild,
205    /// Restart the oracle after each ideal point and rebuild the encodings
206    CleanRebuild,
207}
208
209impl fmt::Display for BuildEncodings {
210    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211        match self {
212            BuildEncodings::Once => write!(f, "once"),
213            BuildEncodings::Rebuild => write!(f, "rebuild"),
214            BuildEncodings::CleanRebuild => write!(f, "clean-rebuild"),
215        }
216    }
217}
218
219/// Specifying the size of a subproblem
220#[derive(Clone, Copy, PartialEq, Eq, Debug)]
221pub enum SubProblemSize {
222    /// An absolute subproblem size
223    Abs(usize),
224    /// A subproblem `x` smaller than the original problem
225    Smaller(usize),
226}
227
228impl SubProblemSize {
229    /// Calculates the absolute problem size given the original instance size
230    pub fn absolute(&self, prob_size: usize) -> usize {
231        match self {
232            SubProblemSize::Abs(abs) => *abs,
233            SubProblemSize::Smaller(smaller) => prob_size - *smaller,
234        }
235    }
236}
237
238impl fmt::Display for SubProblemSize {
239    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
240        match self {
241            SubProblemSize::Abs(size) => write!(f, "+{}", size),
242            SubProblemSize::Smaller(size) => write!(f, "-{}", size),
243        }
244    }
245}