14 #ifndef OR_TOOLS_SAT_SAT_CNF_READER_H_
15 #define OR_TOOLS_SAT_SAT_CNF_READER_H_
23 #include "absl/container/btree_map.h"
24 #include "absl/flags/flag.h"
25 #include "absl/strings/numbers.h"
26 #include "absl/strings/str_split.h"
27 #include "absl/strings/string_view.h"
28 #include "absl/types/span.h"
33 #include "ortools/sat/boolean_problem.pb.h"
34 #include "ortools/sat/cp_model.pb.h"
38 "If true, when we add a slack variable to reify a soft clause, we "
39 "enforce the fact that when it is true, the clause must be false.");
49 problem->set_original_num_variables(num);
53 LinearBooleanConstraint* constraint =
problem->add_constraints();
54 constraint->mutable_literals()->Reserve(clause.size());
55 constraint->mutable_coefficients()->Reserve(clause.size());
56 constraint->set_lower_bound(1);
57 for (
const int literal : clause) {
58 constraint->add_literals(
literal);
59 constraint->add_coefficients(1);
64 CHECK_GE(
literal, 0) <<
"Negative literal not supported.";
70 problem->mutable_objective()->set_offset(offset);
80 for (
int i = 0; i < num; ++i) {
81 IntegerVariableProto* variable =
problem->add_variables();
82 variable->add_domain(0);
83 variable->add_domain(1);
94 return signed_value > 0 ? signed_value - 1 : signed_value;
98 auto* constraint =
problem->add_constraints()->mutable_bool_or();
99 constraint->mutable_literals()->Reserve(clause.size());
100 for (
const int literal : clause) {
106 CHECK_GE(
literal, 0) <<
"Negative literal not supported.";
112 problem->mutable_objective()->set_offset(offset);
132 bool Load(
const std::string& filename, LinearBooleanProblem* problem) {
134 problem->set_name(ExtractProblemName(filename));
136 return LoadInternal(filename, &wrapper);
138 bool Load(
const std::string& filename, CpModelProto* problem) {
140 problem->set_name(ExtractProblemName(filename));
142 return LoadInternal(filename, &wrapper);
146 template <
class Problem>
147 bool LoadInternal(
const std::string& filename, Problem* problem) {
148 positive_literal_to_weight_.clear();
149 objective_offset_ = 0;
151 end_marker_seen_ =
false;
153 num_skipped_soft_clauses_ = 0;
154 num_singleton_soft_clauses_ = 0;
155 num_added_clauses_ = 0;
156 num_slack_variables_ = 0;
161 ProcessNewLine(
line, problem);
163 if (num_lines == 0) {
164 LOG(FATAL) <<
"File '" << filename <<
"' is empty or can't be read.";
166 problem->SetOriginalNumVariables(num_variables_);
167 problem->SetNumVariables(num_variables_ + num_slack_variables_);
170 if (!positive_literal_to_weight_.empty()) {
171 for (
const std::pair<int, int64_t> p : positive_literal_to_weight_) {
173 problem->AddObjectiveTerm(p.first, p.second);
176 problem->SetObjectiveOffset(objective_offset_);
179 if (num_clauses_ != num_added_clauses_ + num_singleton_soft_clauses_ +
180 num_skipped_soft_clauses_) {
181 LOG(ERROR) <<
"Wrong number of clauses. " << num_clauses_ <<
" "
182 << num_added_clauses_;
190 static std::string ExtractProblemName(
const std::string& filename) {
191 const int found = filename.find_last_of(
'/');
192 const std::string problem_name =
193 found != std::string::npos ? filename.substr(found + 1) : filename;
197 int64_t StringPieceAtoi(absl::string_view
input) {
206 void ProcessHeader(
const std::string&
line) {
207 static const char kWordDelimiters[] =
" ";
208 words_ = absl::StrSplit(
line, kWordDelimiters, absl::SkipEmpty());
210 CHECK_EQ(words_[0],
"p");
211 if (words_[1] ==
"cnf" || words_[1] ==
"wcnf") {
212 num_variables_ = StringPieceAtoi(words_[2]);
213 num_clauses_ = StringPieceAtoi(words_[3]);
214 if (words_[1] ==
"wcnf") {
216 hard_weight_ = (words_.size() > 4) ? StringPieceAtoi(words_[4]) : 0;
220 LOG(FATAL) <<
"Unknown file type: " << words_[1];
224 template <
class Problem>
225 void ProcessNewLine(
const std::string&
line, Problem* problem) {
226 if (
line.empty() || end_marker_seen_)
return;
227 if (
line[0] ==
'c')
return;
228 if (
line[0] ==
'%') {
229 end_marker_seen_ =
true;
232 if (
line[0] ==
'p') {
237 static const char kWordDelimiters[] =
" ";
238 auto splitter = absl::StrSplit(
line, kWordDelimiters, absl::SkipEmpty());
242 (!is_wcnf_ && interpret_cnf_as_max_sat_) ? 1 : hard_weight_;
244 bool end_marker_seen =
false;
245 for (
const absl::string_view word : splitter) {
246 const int64_t signed_value = StringPieceAtoi(word);
247 if (first && is_wcnf_) {
249 if (signed_value == 0) {
250 ++num_skipped_soft_clauses_;
255 if (signed_value == 0) {
256 end_marker_seen =
true;
259 tmp_clause_.push_back(signed_value);
263 if (!end_marker_seen)
return;
265 if (
weight == hard_weight_) {
266 ++num_added_clauses_;
267 problem->AddConstraint(tmp_clause_);
269 if (tmp_clause_.size() == 1) {
274 ++num_singleton_soft_clauses_;
275 const int literal = -tmp_clause_[0];
280 objective_offset_ +=
weight;
285 const int slack_literal = num_variables_ + num_slack_variables_ + 1;
286 ++num_slack_variables_;
288 tmp_clause_.push_back(slack_literal);
290 ++num_added_clauses_;
291 problem->AddConstraint(tmp_clause_);
293 if (slack_literal > 0) {
294 positive_literal_to_weight_[slack_literal] +=
weight;
296 positive_literal_to_weight_[-slack_literal] -=
weight;
297 objective_offset_ +=
weight;
300 if (absl::GetFlag(FLAGS_wcnf_use_strong_slack)) {
303 for (
int i = 0; i + 1 < tmp_clause_.size(); ++i) {
304 problem->AddConstraint({-slack_literal, -tmp_clause_[i]});
311 bool interpret_cnf_as_max_sat_;
317 std::vector<absl::string_view> words_;
321 absl::btree_map<int, int64_t> positive_literal_to_weight_;
322 int64_t objective_offset_;
327 bool end_marker_seen_;
328 int64_t hard_weight_;
330 int num_slack_variables_;
331 int num_skipped_soft_clauses_;
332 int num_singleton_soft_clauses_;
333 int num_added_clauses_;
335 std::vector<int> tmp_clause_;
bool Load(const std::string &filename, CpModelProto *problem)
bool Load(const std::string &filename, LinearBooleanProblem *problem)
void InterpretCnfAsMaxSat(bool v)
Collection of objects used to extend the Constraint Solver library.
static int input(yyscan_t yyscanner)
ABSL_FLAG(bool, wcnf_use_strong_slack, true, "If true, when we add a slack variable to reify a soft clause, we " "enforce the fact that when it is true, the clause must be false.")
int LiteralToRef(int signed_value)
CpModelProtoWrapper(CpModelProto *p)
void SetNumVariables(int num)
void AddObjectiveTerm(int literal, int64_t value)
void AddConstraint(absl::Span< const int > clause)
void SetObjectiveOffset(int64_t offset)
void SetOriginalNumVariables(int num)
void SetNumVariables(int num)
void AddObjectiveTerm(int literal, int64_t value)
void AddConstraint(absl::Span< const int > clause)
void SetObjectiveOffset(int64_t offset)
LinearBooleanProblemWrapper(LinearBooleanProblem *p)
void SetOriginalNumVariables(int num)
LinearBooleanProblem * problem