Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
lrat_proof_handler.h
Go to the documentation of this file.
1// Copyright 2010-2025 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14#ifndef ORTOOLS_SAT_LRAT_PROOF_HANDLER_H_
15#define ORTOOLS_SAT_LRAT_PROOF_HANDLER_H_
16
17#include <cstdint>
18#include <fstream>
19#include <limits>
20#include <memory>
21#include <string>
22#include <string_view>
23#include <vector>
24
25#include "absl/container/flat_hash_map.h"
26#include "absl/container/flat_hash_set.h"
27#include "absl/log/check.h"
28#include "absl/strings/string_view.h"
29#include "absl/types/span.h"
33#include "ortools/sat/lrat.pb.h"
35#include "ortools/sat/model.h"
39#include "ortools/sat/util.h"
40
41namespace operations_research {
42namespace sat {
43
44// Writes an LRAT proof to a file in "record io" format.
46 public:
47 explicit LratWriter(std::string_view filename);
49
50 std::string_view filename() const { return filename_; }
51
52 void AddImportedClause(ClauseId id, absl::Span<const Literal> clause);
53
54 void AddInferredClause(ClauseId id, absl::Span<const Literal> clause,
55 absl::Span<const ClauseId> unit_ids,
56 absl::Span<const LratChecker::RatIds> rat,
57 bool exported = false);
58
59 void ExportClause(ClauseId id, absl::Span<const Literal> clause);
60
61 void DeleteClause(ClauseId id);
62
63 private:
64 void WriteDeletedClauseIds();
65
66 std::string filename_;
67 std::ofstream ofstream_;
68 RecordWriter writer_;
69 std::vector<ClauseId> deleted_clause_ids_;
70};
71
72// Merges separate LRAT proofs into a single LRAT file in ASCII format.
74 public:
75 explicit LratMerger(Model* model);
77
78 // Merges the given LRAT proofs in a single one, and writes it to a file in
79 // ASCII format. The first proof must be the presolve proof. Its imported
80 // clauses must be the input problem clauses, and their ID must be the 1-based
81 // clause index in the input CNF file. Returns true on success, false
82 // otherwise.
83 bool Merge(absl::Span<const std::string> proof_filenames);
84
85 private:
86 // Clause IDs used in the merged proof. Local clause IDs in individual proof
87 // files are remapped to global clause IDs (except for the presolve proof: its
88 // IDs are kept unchanged). This mapping is stored in `local_to_global_ids_`
89 // (one map per proof file, except for the presolve proof).
90 DEFINE_STRONG_INT_TYPE(GlobalId, int64_t);
91
92 // Reads the proof of the presolved model and adds its clauses to
93 // `shared_clause_ids_`. Also checks this proof if lrat_checker_ is not null.
94 // Returns true on success, false otherwise.
95 bool ReadPresolveProof(const std::string& filename);
96
97 // Canonicalizes (i.e., sorts) and registers a clause so that it can be
98 // imported from an individual proof file.
99 // TODO(user): is the canonicalization really needed?
100 void SortAndAddSharedClause(GlobalId id, std::vector<Literal>& literals);
101
102 // Remaps the local clause IDs in the given inferred clause to global IDs, in
103 // place. Returns true on success, false otherwise.
104 bool RemapInferredClause(int worker_index, const std::string& filename,
105 LratInferredClause& inferred_clause);
106 bool RemapClauseIds(int worker_index, const std::string& filename,
107 google::protobuf::RepeatedField<int64_t>* clause_ids);
108
109 // Writes the given clause to the merged proof file, in LRAT ASCII file
110 // format. Also checks it if lrat_checker_ is not null. Returns true on
111 // success, false otherwise.
112 bool WriteInferredClause(const LratInferredClause& inferred_clause);
113
114 GlobalId NextGlobalId() { return GlobalId(next_global_id_++); }
115
116 bool Error(std::string_view message) const;
117 bool LratError() const;
118
119 const int id_;
120 SharedLratProofStatus* proof_status_;
121 std::unique_ptr<LratChecker> lrat_checker_;
122 bool debug_crash_on_error_;
123
124 std::string merged_proof_filename_;
125 std::ofstream merged_proof_file_;
126 GlobalId next_global_id_;
127
128 absl::flat_hash_map<std::vector<Literal>, GlobalId> shared_clause_ids_;
129 std::vector<absl::flat_hash_map<ClauseId, GlobalId>> local_to_global_ids_;
130 std::vector<absl::flat_hash_set<ClauseId>> exported_local_ids_;
131 std::vector<LratProofStep> last_read_steps_;
132
133 std::vector<Literal> tmp_clause_;
134 std::vector<ClauseId> tmp_unit_ids_;
135 std::vector<LratChecker::RatIds> tmp_rat_ids_;
136};
137
138// Handles the LRAT proof of a SAT problem by either checking it incrementally
139// and/or by saving it to a file.
140class LratProofHandler {
141 public:
142 static std::unique_ptr<LratProofHandler> MaybeCreate(Model* model);
143 static std::unique_ptr<LratProofHandler> MaybeCreate(
144 const SatParameters& params, ClauseIdGenerator* id_generator,
145 SharedLratProofStatus* proof_status, SharedStatistics* stats);
146
147 bool lrat_check_enabled() const { return lrat_checker_ != nullptr; }
148 bool lrat_output_enabled() const { return lrat_writer_ != nullptr; }
149 bool drat_check_enabled() const { return drat_checker_ != nullptr; }
150 bool drat_output_enabled() const { return drat_writer_ != nullptr; }
151
152 int64_t num_assumed_clauses() const { return num_assumed_clauses_; }
153
154 // Adds a clause of the problem. See LratChecker for more details.
155 bool AddProblemClause(ClauseId id, absl::Span<const Literal> clause);
156
157 // No more problem clauses must be added after this call.
158 void EndProblemClauses();
159
160 // Adds a clause which is inferred from the problem clauses and/or the
161 // previously inferred clauses. See LratChecker for more details.
162 bool AddInferredClause(ClauseId id, absl::Span<const Literal> clause,
163 absl::Span<const ClauseId> unit_ids,
164 bool exported = false) {
165 return AddInferredClause(id, clause, unit_ids, {}, exported);
166 }
167 bool AddInferredClause(ClauseId id, absl::Span<const Literal> clause,
168 absl::Span<const ClauseId> unit_ids,
169 absl::Span<const LratChecker::RatIds> rat,
170 bool exported = false);
171
172 // This assumes that the 'new_clause' to prove and all the ones needed for the
173 // proof only touch a small number of variables (<= 6). It will then prove the
174 // new clause by enumerating all possibilities and producing the relevant
175 // intermediate LRAT RUP steps.
176 //
177 // The last two arguments must have the same size and are in one to one
178 // correspondence. Note that we might not need all the given clauses in the
179 // proof.
180 //
181 // Return the new clause id. Note that in some corner cases, this could be
182 // one of the id passed in ids_for_proof. Return kNoClauseId if the proof
183 // is wrong.
185 absl::Span<const Literal> new_clause,
186 absl::Span<const ClauseId> ids_for_proof,
187 const CompactVectorVector<int, Literal>& clauses_for_proof);
188
189 // Adds a clause which was inferred and exported by another worker. Returns
190 // true if successful (the operation can fail if LRAT checks are enabled, and
191 // the ID is already used by another clause).
192 bool AddImportedClause(ClauseId id, absl::Span<const Literal> clause);
193
194 // Adds a clause which is assumed to be true, without proof. Returns true if
195 // successful (the operation fails if DRAT checks are enabled, or if LRAT
196 // checks are enabled and the ID is already used by another clause).
197 bool AddAssumedClause(ClauseId id, absl::Span<const Literal> clause);
198
199 // Exports a clause so that it can be imported by other workers. If you know
200 // whether a clause must be exported when it is inferred, it is more efficient
201 // to use the `exported` parameter of AddInferredClause(). `id` and `clause`
202 // must be the ID and the literals of a previously added clause. This is not
203 // needed for unary and binary clauses, which are always exported.
204 bool ExportClause(ClauseId id, absl::Span<const Literal> clause);
205
206 // Prevents the given clause from being deleted, until UnpinClause() is called
207 // with the same ID. At most one clause can be pinned at any time.
208 void PinClause(ClauseId id, absl::Span<const Literal> clause);
209
210 // Unpins the clause with the given ID, and deletes it if a call to
211 // DeleteClause() for this clause was made since it was pinned.
212 void UnpinClause(ClauseId id);
213
214 // Deletes a problem or inferred clause. The clause literals are only needed
215 // when checking DRAT.
216 void DeleteClause(ClauseId id, absl::Span<const Literal> clause);
217
218 // Returns VALID if all the inferred clauses were successfully checked with
219 // LRAT. Returns INVALID if at least one of them was not. Returns UNKNOWN if
220 // LRAT checks are not enabled.
222
223 // Returns VALID if the unsatisfiability proof is valid and complete, i.e.
224 // whether the empty clause has been successfully inferred. Returns INVALID if
225 // it is not. Returns UNKNOWN if the check timed out (this can only occur
226 // with DRAT checks), or if neither LRAT nor DRAT checks were enabled.
228
229 void Close(bool model_is_unsat);
230
231 // This can be helpful to debug wrong proof, but shouldn't be used otherwise.
232 absl::Span<const Literal> GetLratClauseForDebug(ClauseId id) const {
233 CHECK(lrat_checker_ != nullptr);
234 return lrat_checker_->GetClauseForDebug(id);
235 }
236
237 private:
238 LratProofHandler(const SatParameters& params, ClauseIdGenerator* id_generator,
239 SharedLratProofStatus* proof_status,
240 SharedStatistics* stats);
241
242 bool LratError(absl::string_view message) const;
243
244 const int id_;
245 ClauseIdGenerator* id_generator_;
246 SharedLratProofStatus* proof_status_;
247 std::unique_ptr<LratChecker> lrat_checker_;
248 std::unique_ptr<LratWriter> lrat_writer_;
249 std::unique_ptr<DratChecker> drat_checker_;
250 std::unique_ptr<DratWriter> drat_writer_;
251 double max_drat_time_in_seconds_ = std::numeric_limits<double>::infinity();
252 bool debug_crash_on_error_ = false;
253
254 bool all_problem_clauses_loaded_ = false;
255 int64_t num_assumed_clauses_ = 0;
256
257 ClauseId pinned_clause_id_ = kNoClauseId;
258 std::vector<Literal> pinned_clause_;
259 bool delete_pinned_clause_ = false;
260
261 // Only used when checking DRAT, because the DRAT checker does not support
262 // interleaving problem and inferred clauses.
263 std::vector<std::vector<Literal>> clauses_inferred_during_problem_loading_;
264};
265
266} // namespace sat
267} // namespace operations_research
268
269#endif // ORTOOLS_SAT_LRAT_PROOF_HANDLER_H_
bool Merge(absl::Span< const std::string > proof_filenames)
void DeleteClause(ClauseId id, absl::Span< const Literal > clause)
bool ExportClause(ClauseId id, absl::Span< const Literal > clause)
bool AddProblemClause(ClauseId id, absl::Span< const Literal > clause)
absl::Span< const Literal > GetLratClauseForDebug(ClauseId id) const
ClauseId AddAndProveInferredClauseByEnumeration(absl::Span< const Literal > new_clause, absl::Span< const ClauseId > ids_for_proof, const CompactVectorVector< int, Literal > &clauses_for_proof)
static std::unique_ptr< LratProofHandler > MaybeCreate(Model *model)
bool AddInferredClause(ClauseId id, absl::Span< const Literal > clause, absl::Span< const ClauseId > unit_ids, bool exported=false)
bool AddImportedClause(ClauseId id, absl::Span< const Literal > clause)
void PinClause(ClauseId id, absl::Span< const Literal > clause)
bool AddAssumedClause(ClauseId id, absl::Span< const Literal > clause)
LratWriter(std::string_view filename)
void AddInferredClause(ClauseId id, absl::Span< const Literal > clause, absl::Span< const ClauseId > unit_ids, absl::Span< const LratChecker::RatIds > rat, bool exported=false)
void ExportClause(ClauseId id, absl::Span< const Literal > clause)
void AddImportedClause(ClauseId id, absl::Span< const Literal > clause)
constexpr ClauseId kNoClauseId(0)
OR-Tools root namespace.
#define DEFINE_STRONG_INT_TYPE(type_name, value_type)