1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
//! Determines the satisfiability of the formula in a context.
//!
//! # Overview
//!
//! [solve](crate::procedures::solve) casts the conflict-driven clause-learning algorithm through a valuation relative consequence operator over formulas.
//!
//! On this operator a formula entails either itself, or a tautological consequence of itself with some additional clause.
//! And, if the operator cannot be applied, the formula is unsatisfiable.[^op-note]
//! [^op-note]: Alternatively, the operator may return some designated formula such as falsum.
//!
//! - If the formula entails itself, then inspection of the valuation is required:
//! + If the valuation is partial (and not complete) the valuation *may be* satisfiable, though the rules of inference associated with the operator do not support the derivation of a complete valuation (and so some decision must be made).
//! + If the valuation is complete, the formula is satisfiable (on the given valuation).
//! - If the formula entails some formula with an additional clause, then:
//! + The formula is unsatisfiable on the given valuation, but *may be* satisfiable on some other valuation.\
//! Specifically, there is some sub-valuation of the current valuation on which the added clause asserts some literal, and a '[backjump](crate::procedures::backjump)' may be made to that valuation.
//!
//! [solve](crate::procedures::solve), then, manages the detailed operator, whose implementation is given in [apply_consequences].
//! This amounts to applying an instance of the operator which:
//!
//! - Returns *unsatisfiable*, if it is not possible to apply the consequence relation.
//! - Returns *satisfiable*, if the formula entails itself and the valuation is complete.
//! - Makes a decision, if the formula entails itself and the valuation is partial.
//! - Backjumps to a different valuation, if the formula entails some formula with an additional clause.
//!
//! Though, at points this process may be interrupted for some other action.
//! In particular, [solve](crate::procedures::solve) may revise the valuation to some other valuation (e.g. by forgetting any decisions made) regardless of whether the formula entails some formula with an additional clause.
//!
//! Roughly, the loop is as diagrammed:
//!
//! ```none
//! +---------------+
//! +-------| make_decision |
//! | +---------------+
//! | ⌃
//! | |
//! | | if there is no update to the formula, and the valuation is partial
//! | |
//! | | +-----> satisfiable, if the valuation is complete
//! ⌄ +--------------------+ |
//! --+-->| apply_consequences |-----+
//! ⌃ +--------------------+ |
//! | | +-----> unsatisfiable, if apply_consequences fails
//! | |
//! | | if a clause is added to the formula
//! | |
//! | ⌄
//! | +----------+
//! +-----------| backjump |
//! +----------+
//! ```
//!
//! And, abstracting from various other bookkeeping tasks and optional actions after a context, solve is:
//!
//! ```rust,ignore
//! loop {
//!
//! match self.apply_consequences()? {
//! apply_consequences::Ok::FundamentalConflict => break,
//!
//! apply_consequences::Ok::Exhausted => {
//! //
//! match self.make_decision()? {
//! decision::Ok::Made => continue,
//! decision::Ok::Exhausted => break,
//! }
//! }
//!
//! apply_consequences::Ok::UnitClause(literal) => {
//! self.backjump(0);
//! self.q_literal(literal)?;
//! }
//!
//! apply_consequences::Ok::AssertingClause(key, literal) => {
//! let the_clause = self.clause_db.get(&key)?;
//! self.backjump(self.non_chronological_backjump_level(the_clause)?);
//! self.q_literal(literal)?;
//! }
//! }
//! // Additional actions after a conflict, before the next loop.
//! ...
//! }
//! ```
//!
//! The distinction between a unit clause and clause being returned from [apply_consequence](crate::procedures::apply_consequences) is made only to avoid the overhead of accessing a clause and determing the relevant backjump level in the case of a unit clause.
//!
//! # Example
//!
//! ```rust
//! # fn value_of(variable: &str, context: &Context) -> Option<bool> {
//! # let mut the_value = None;
//! # if context.atom_db.valuation_string().contains(variable) {
//! # the_value = Some(true)
//! # }
//! # if context
//! # .atom_db
//! # .valuation_string()
//! # .contains(format!("-{variable}").as_str())
//! # {
//! # the_value = Some(false)
//! # }
//! # the_value
//! # }
//! # use otter_sat::config::Config;
//! # use otter_sat::context::Context;
//! # use otter_sat::dispatch::library::report::{self};
//! let config = Config::default();
//!
//! let mut the_context: Context = Context::from_config(config, None);
//!
//! let not_p_or_q = the_context.clause_from_string("-p q").unwrap();
//! let p_or_not_q = the_context.clause_from_string("p -q").unwrap();
//! let _ = the_context.add_clause(not_p_or_q);
//! let _ = the_context.add_clause(p_or_not_q);
//!
//! assert!(the_context.solve().is_ok());
//! let status = the_context.report();
//! let valuation = the_context.atom_db.valuation_string();
//!
//! assert_eq!(value_of("p", &the_context), Some(false));
//! assert_eq!(value_of("q", &the_context), Some(false));
//!
//! let p_clause = the_context.clause_from_string("p").unwrap();
//! let error = the_context.add_clause(p_clause);
//!
//! the_context.clear_decisions();
//!
//! let p_clause = the_context.clause_from_string("p").unwrap();
//! let _p_ok = the_context.add_clause(p_clause);
//!
//! assert_eq!(value_of("p", &the_context), Some(true));
//!
//! assert!(the_context.solve().is_ok());
//!
//! assert_eq!(the_context.report(), report::Solve::Satisfiable);
//! ```
//!
//! # Literature
//!
//! The core solve procedure was developed by reading [Decision Procedures](https://doi.org/10.1007/978-3-662-50497-0)[^a]
//! and the [Handbook of satisfiability](https://www.iospress.com/catalog/books/handbook-of-satisfiability-2).[^b]
//! Though, the presentation given is original.
//!
//! [^a]: Specifically, Chapter 2 on decision procedures for propositional logic.
//! [^b]: Specifcally, chapters 3 and 4 on complete algorithms and CDCL techniques.
//!
use crate::;