27 #include "absl/container/flat_hash_map.h"
28 #include "absl/flags/flag.h"
29 #include "absl/status/status.h"
30 #include "absl/strings/str_format.h"
31 #include "absl/strings/string_view.h"
34 #if !defined(__PORTABLE_PLATFORM__)
42 #include "ortools/sat/boolean_problem.pb.h"
43 #include "ortools/sat/cp_model.pb.h"
46 #include "ortools/sat/sat_parameters.pb.h"
51 ABSL_FLAG(std::string, debug_dump_symmetry_graph_to_file,
"",
52 "If this flag is non-empty, an undirected graph whose"
53 " automorphism group is in one-to-one correspondence with the"
54 " symmetries of the SAT problem will be dumped to a file every"
55 " time FindLinearBooleanProblemSymmetries() is called.");
63 const SatSolver& solver, std::vector<bool>* assignment) {
65 for (
int i = 0; i < problem.num_variables(); ++i) {
66 assignment->push_back(
77 template <
typename LinearTerms>
78 std::string ValidateLinearTerms(
const LinearTerms& terms,
79 std::vector<bool>* variable_seen) {
83 const int max_num_errs = 100;
84 for (
int i = 0; i < terms.literals_size(); ++i) {
85 if (terms.literals(i) == 0) {
86 if (++num_errs <= max_num_errs) {
87 err_str += absl::StrFormat(
"Zero literal at position %d\n", i);
90 if (terms.coefficients(i) == 0) {
91 if (++num_errs <= max_num_errs) {
92 err_str += absl::StrFormat(
"Literal %d has a zero coefficient\n",
96 const int var = Literal(terms.literals(i)).Variable().value();
97 if (
var >= variable_seen->size()) {
98 if (++num_errs <= max_num_errs) {
99 err_str += absl::StrFormat(
"Out of bound variable %d\n",
var);
102 if ((*variable_seen)[
var]) {
103 if (++num_errs <= max_num_errs) {
104 err_str += absl::StrFormat(
"Duplicated variable %d\n",
var);
107 (*variable_seen)[
var] =
true;
110 for (
int i = 0; i < terms.literals_size(); ++i) {
111 const int var = Literal(terms.literals(i)).Variable().value();
112 (*variable_seen)[
var] =
false;
115 if (num_errs <= max_num_errs) {
116 err_str = absl::StrFormat(
"%d validation errors:\n", num_errs) + err_str;
119 absl::StrFormat(
"%d validation errors; here are the first %d:\n",
120 num_errs, max_num_errs) +
129 template <
typename ProtoFormat>
130 std::vector<LiteralWithCoeff> ConvertLinearExpression(
131 const ProtoFormat&
input) {
132 std::vector<LiteralWithCoeff> cst;
133 cst.reserve(
input.literals_size());
134 for (
int i = 0; i <
input.literals_size(); ++i) {
136 cst.push_back(LiteralWithCoeff(
literal,
input.coefficients(i)));
144 std::vector<bool> variable_seen(problem.num_variables(),
false);
145 for (
int i = 0; i < problem.constraints_size(); ++i) {
146 const LinearBooleanConstraint& constraint = problem.constraints(i);
147 const std::string error = ValidateLinearTerms(constraint, &variable_seen);
148 if (!error.empty()) {
150 absl::StatusCode::kInvalidArgument,
151 absl::StrFormat(
"Invalid constraint %i: ", i) + error);
154 const std::string error =
155 ValidateLinearTerms(problem.objective(), &variable_seen);
156 if (!error.empty()) {
157 return absl::Status(absl::StatusCode::kInvalidArgument,
158 absl::StrFormat(
"Invalid objective: ") + error);
160 return ::absl::OkStatus();
165 for (
int i = 0; i < problem.num_variables(); ++i) {
166 IntegerVariableProto*
var = result.add_variables();
167 if (problem.var_names_size() > i) {
168 var->set_name(problem.var_names(i));
173 for (
const LinearBooleanConstraint& constraint : problem.constraints()) {
174 ConstraintProto*
ct = result.add_constraints();
175 ct->set_name(constraint.name());
176 LinearConstraintProto* linear =
ct->mutable_linear();
178 for (
int i = 0; i < constraint.literals_size(); ++i) {
180 const int lit = constraint.literals(i);
181 const int64_t coeff = constraint.coefficients(i);
183 linear->add_vars(lit - 1);
184 linear->add_coeffs(coeff);
187 linear->add_vars(-lit - 1);
188 linear->add_coeffs(-coeff);
192 linear->add_domain(constraint.has_lower_bound()
193 ? constraint.lower_bound() + offset
195 linear->add_domain(constraint.has_upper_bound()
196 ? constraint.upper_bound() + offset
199 if (problem.has_objective()) {
200 CpObjectiveProto* objective = result.mutable_objective();
202 for (
int i = 0; i < problem.objective().literals_size(); ++i) {
203 const int lit = problem.objective().literals(i);
204 const int64_t coeff = problem.objective().coefficients(i);
206 objective->add_vars(lit - 1);
207 objective->add_coeffs(coeff);
209 objective->add_vars(-lit - 1);
210 objective->add_coeffs(-coeff);
214 objective->set_offset(offset + problem.objective().offset());
215 objective->set_scaling_factor(problem.objective().scaling_factor());
221 LinearObjective* objective = problem->mutable_objective();
222 objective->set_scaling_factor(-objective->scaling_factor());
223 objective->set_offset(-objective->offset());
226 for (
auto& coefficients_ref : *objective->mutable_coefficients()) {
227 coefficients_ref = -coefficients_ref;
239 LOG(WARNING) <<
"The given problem is invalid!";
242 if (solver->
parameters().log_search_progress()) {
243 LOG(INFO) <<
"Loading problem '" << problem.name() <<
"', "
244 << problem.num_variables() <<
" variables, "
245 << problem.constraints_size() <<
" constraints.";
248 std::vector<LiteralWithCoeff> cst;
249 int64_t num_terms = 0;
250 int num_constraints = 0;
251 for (
const LinearBooleanConstraint& constraint : problem.constraints()) {
252 num_terms += constraint.literals_size();
253 cst = ConvertLinearExpression(constraint);
255 constraint.has_lower_bound(),
Coefficient(constraint.lower_bound()),
256 constraint.has_upper_bound(),
Coefficient(constraint.upper_bound()),
258 LOG(INFO) <<
"Problem detected to be UNSAT when "
259 <<
"adding the constraint #" << num_constraints
260 <<
" with name '" << constraint.name() <<
"'";
265 if (solver->
parameters().log_search_progress()) {
266 LOG(INFO) <<
"The problem contains " << num_terms <<
" terms.";
275 LOG(WARNING) <<
"The given problem is invalid! " <<
status.message();
277 if (solver->
parameters().log_search_progress()) {
278 #if !defined(__PORTABLE_PLATFORM__)
279 LOG(INFO) <<
"LinearBooleanProblem memory: " << problem->SpaceUsedLong();
281 LOG(INFO) <<
"Loading problem '" << problem->name() <<
"', "
282 << problem->num_variables() <<
" variables, "
283 << problem->constraints_size() <<
" constraints.";
286 std::vector<LiteralWithCoeff> cst;
287 int64_t num_terms = 0;
288 int num_constraints = 0;
293 std::reverse(problem->mutable_constraints()->begin(),
294 problem->mutable_constraints()->end());
295 for (
int i = problem->constraints_size() - 1; i >= 0; --i) {
296 const LinearBooleanConstraint& constraint = problem->constraints(i);
297 num_terms += constraint.literals_size();
298 cst = ConvertLinearExpression(constraint);
300 constraint.has_lower_bound(),
Coefficient(constraint.lower_bound()),
301 constraint.has_upper_bound(),
Coefficient(constraint.upper_bound()),
303 LOG(INFO) <<
"Problem detected to be UNSAT when "
304 <<
"adding the constraint #" << num_constraints
305 <<
" with name '" << constraint.name() <<
"'";
308 delete problem->mutable_constraints()->ReleaseLast();
311 LinearBooleanProblem empty_problem;
312 problem->mutable_constraints()->Swap(empty_problem.mutable_constraints());
313 if (solver->
parameters().log_search_progress()) {
314 LOG(INFO) <<
"The problem contains " << num_terms <<
" terms.";
321 const LinearObjective& objective = problem.objective();
322 CHECK_EQ(objective.literals_size(), objective.coefficients_size());
323 int64_t max_abs_weight = 0;
324 for (
const int64_t
coefficient : objective.coefficients()) {
327 const double max_abs_weight_double = max_abs_weight;
328 for (
int i = 0; i < objective.literals_size(); ++i) {
330 const int64_t
coefficient = objective.coefficients(i);
331 const double abs_weight = std::abs(
coefficient) / max_abs_weight_double;
342 std::vector<LiteralWithCoeff> cst =
343 ConvertLinearExpression(problem.objective());
352 std::vector<LiteralWithCoeff> cst =
353 ConvertLinearExpression(problem.objective());
359 const std::vector<bool>& assignment) {
360 CHECK_EQ(assignment.size(), problem.num_variables());
362 const LinearObjective& objective = problem.objective();
363 for (
int i = 0; i < objective.literals_size(); ++i) {
366 sum += objective.coefficients(i);
373 const std::vector<bool>& assignment) {
374 CHECK_EQ(assignment.size(), problem.num_variables());
377 for (
const LinearBooleanConstraint& constraint : problem.constraints()) {
379 for (
int i = 0; i < constraint.literals_size(); ++i) {
382 sum += constraint.coefficients(i);
385 if (constraint.has_lower_bound() && sum < constraint.lower_bound()) {
386 LOG(WARNING) <<
"Unsatisfied constraint! sum: " << sum <<
"\n"
390 if (constraint.has_upper_bound() && sum > constraint.upper_bound()) {
391 LOG(WARNING) <<
"Unsatisfied constraint! sum: " << sum <<
"\n"
403 const LinearBooleanProblem& problem) {
405 const bool is_wcnf = (problem.objective().coefficients_size() > 0);
406 const LinearObjective& objective = problem.objective();
412 const int first_slack_variable = problem.original_num_variables();
415 absl::flat_hash_map<int, int64_t> literal_to_weight;
416 std::vector<std::pair<int, int64_t>> non_slack_objective;
421 int64_t hard_weight = 1;
424 for (int64_t
weight : objective.coefficients()) {
426 int signed_literal = objective.literals(i);
435 signed_literal = -signed_literal;
438 literal_to_weight[objective.literals(i)] =
weight;
439 if (
Literal(signed_literal).Variable() < first_slack_variable) {
440 non_slack_objective.push_back(std::make_pair(signed_literal,
weight));
445 output += absl::StrFormat(
"p wcnf %d %d %d\n", first_slack_variable,
446 static_cast<int>(problem.constraints_size() +
447 non_slack_objective.size()),
450 output += absl::StrFormat(
"p cnf %d %d\n", problem.num_variables(),
451 problem.constraints_size());
454 std::string constraint_output;
455 for (
const LinearBooleanConstraint& constraint : problem.constraints()) {
456 if (constraint.literals_size() == 0)
return "";
457 constraint_output.clear();
458 int64_t
weight = hard_weight;
459 for (
int i = 0; i < constraint.literals_size(); ++i) {
460 if (constraint.coefficients(i) != 1)
return "";
461 if (is_wcnf && abs(constraint.literals(i)) - 1 >= first_slack_variable) {
462 weight = literal_to_weight[constraint.literals(i)];
464 if (i > 0) constraint_output +=
" ";
469 output += absl::StrFormat(
"%d ",
weight);
471 output += constraint_output +
" 0\n";
476 for (std::pair<int, int64_t> p : non_slack_objective) {
488 BooleanAssignment* output) {
489 output->clear_literals();
492 output->add_literals(
499 const std::vector<int>& constraint_indices,
500 LinearBooleanProblem* subproblem) {
501 *subproblem = problem;
502 subproblem->set_name(
"Subproblem of " + problem.name());
503 subproblem->clear_constraints();
504 for (
int index : constraint_indices) {
505 CHECK_LT(
index, problem.constraints_size());
506 subproblem->add_constraints()->MergeFrom(problem.constraints(
index));
520 const std::pair<int, int64_t> key(type,
coefficient.value());
521 return id_map_.emplace(key, id_map_.size()).first->second;
525 absl::flat_hash_map<std::pair<int, int64_t>,
int> id_map_;
543 template <
typename Graph>
545 const LinearBooleanProblem& problem,
546 std::vector<int>* initial_equivalence_classes) {
548 const int num_variables = problem.num_variables();
550 std::vector<LiteralWithCoeff> cst;
551 for (
const LinearBooleanConstraint& constraint : problem.constraints()) {
552 cst = ConvertLinearExpression(constraint);
554 constraint.has_lower_bound(),
Coefficient(constraint.lower_bound()),
555 constraint.has_upper_bound(),
Coefficient(constraint.upper_bound()),
562 initial_equivalence_classes->clear();
566 enum NodeType { LITERAL_NODE, CONSTRAINT_NODE, CONSTRAINT_COEFFICIENT_NODE };
567 IdGenerator id_generator;
571 for (
int i = 0; i < num_variables; ++i) {
582 initial_equivalence_classes->assign(
584 id_generator.GetId(NodeType::LITERAL_NODE,
Coefficient(0)));
594 std::vector<LiteralWithCoeff> expr =
595 ConvertLinearExpression(problem.objective());
598 (*initial_equivalence_classes)[term.literal.Index().value()] =
599 id_generator.GetId(NodeType::LITERAL_NODE, term.coefficient);
609 const int constraint_node_index = initial_equivalence_classes->size();
610 initial_equivalence_classes->push_back(id_generator.GetId(
611 NodeType::CONSTRAINT_NODE, canonical_problem.
Rhs(i)));
619 int current_node_index = constraint_node_index;
622 if (term.coefficient != previous_coefficient) {
623 current_node_index = initial_equivalence_classes->size();
624 initial_equivalence_classes->push_back(id_generator.GetId(
625 NodeType::CONSTRAINT_COEFFICIENT_NODE, term.coefficient));
626 previous_coefficient = term.coefficient;
631 graph->AddArc(constraint_node_index, current_node_index);
632 graph->AddArc(current_node_index, constraint_node_index);
638 graph->AddArc(current_node_index, term.literal.Index().value());
639 graph->AddArc(term.literal.Index().value(), current_node_index);
643 DCHECK_EQ(graph->num_nodes(), initial_equivalence_classes->size());
649 LinearObjective* mutable_objective = problem->mutable_objective();
650 int64_t objective_offset = 0;
651 for (
int i = 0; i < mutable_objective->literals_size(); ++i) {
652 const int signed_literal = mutable_objective->literals(i);
653 if (signed_literal < 0) {
654 const int64_t
coefficient = mutable_objective->coefficients(i);
655 mutable_objective->set_literals(i, -signed_literal);
656 mutable_objective->set_coefficients(i, -
coefficient);
660 mutable_objective->set_offset(mutable_objective->offset() + objective_offset);
663 for (LinearBooleanConstraint& constraint :
664 *(problem->mutable_constraints())) {
666 for (
int i = 0; i < constraint.literals_size(); ++i) {
667 if (constraint.literals(i) < 0) {
668 sum += constraint.coefficients(i);
669 constraint.set_literals(i, -constraint.literals(i));
670 constraint.set_coefficients(i, -constraint.coefficients(i));
673 if (constraint.has_lower_bound()) {
674 constraint.set_lower_bound(constraint.lower_bound() - sum);
676 if (constraint.has_upper_bound()) {
677 constraint.set_upper_bound(constraint.upper_bound() - sum);
683 const LinearBooleanProblem& problem,
684 std::vector<std::unique_ptr<SparsePermutation>>* generators) {
686 std::vector<int> equivalence_classes;
687 std::unique_ptr<Graph> graph(
688 GenerateGraphForSymmetryDetection<Graph>(problem, &equivalence_classes));
689 LOG(INFO) <<
"Graph has " << graph->num_nodes() <<
" nodes and "
690 << graph->num_arcs() / 2 <<
" edges.";
691 #if !defined(__PORTABLE_PLATFORM__)
692 if (!absl::GetFlag(FLAGS_debug_dump_symmetry_graph_to_file).empty()) {
694 std::vector<int> new_node_index(graph->num_nodes(), -1);
695 const int num_classes = 1 + *std::max_element(equivalence_classes.begin(),
696 equivalence_classes.end());
697 std::vector<int> class_size(num_classes, 0);
698 for (
const int c : equivalence_classes) ++class_size[c];
699 std::vector<int> next_index_by_class(num_classes, 0);
700 std::partial_sum(class_size.begin(), class_size.end() - 1,
701 next_index_by_class.begin() + 1);
702 for (
int node = 0; node < graph->num_nodes(); ++node) {
703 new_node_index[node] = next_index_by_class[equivalence_classes[node]]++;
705 std::unique_ptr<Graph> remapped_graph =
RemapGraph(*graph, new_node_index);
707 *remapped_graph, absl::GetFlag(FLAGS_debug_dump_symmetry_graph_to_file),
710 LOG(DFATAL) <<
"Error when writing the symmetry graph to file: "
717 std::vector<int> factorized_automorphism_group_size;
719 CHECK(symmetry_finder
720 .FindSymmetries(&equivalence_classes, generators,
721 &factorized_automorphism_group_size)
727 double average_support_size = 0.0;
728 int num_generators = 0;
729 for (
int i = 0; i < generators->size(); ++i) {
731 std::vector<int> to_delete;
732 for (
int j = 0; j < permutation->
NumCycles(); ++j) {
733 if (*(permutation->
Cycle(j).
begin()) >= 2 * problem.num_variables()) {
734 to_delete.push_back(j);
737 for (
const int node : permutation->
Cycle(j)) {
738 DCHECK_GE(node, 2 * problem.num_variables());
744 if (!permutation->
Support().empty()) {
745 average_support_size += permutation->
Support().size();
746 swap((*generators)[num_generators], (*generators)[i]);
750 generators->resize(num_generators);
751 average_support_size /= num_generators;
752 LOG(INFO) <<
"# of generators: " << num_generators;
753 LOG(INFO) <<
"Average support size: " << average_support_size;
758 LinearBooleanProblem* problem) {
761 std::vector<LiteralWithCoeff> cst;
764 cst = ConvertLinearExpression(problem->objective());
766 LinearObjective* mutable_objective = problem->mutable_objective();
767 mutable_objective->clear_literals();
768 mutable_objective->clear_coefficients();
769 mutable_objective->set_offset(mutable_objective->offset() -
770 bound_shift.value());
772 mutable_objective->add_literals(entry.literal.SignedValue());
773 mutable_objective->add_coefficients(entry.coefficient.value());
777 for (LinearBooleanConstraint& constraint : *problem->mutable_constraints()) {
778 cst = ConvertLinearExpression(constraint);
779 constraint.clear_literals();
780 constraint.clear_coefficients();
784 if (constraint.has_upper_bound()) {
785 constraint.set_upper_bound(constraint.upper_bound() +
786 bound_shift.value());
787 if (max_value <= constraint.upper_bound()) {
788 constraint.clear_upper_bound();
791 if (constraint.has_lower_bound()) {
792 constraint.set_lower_bound(constraint.lower_bound() +
793 bound_shift.value());
795 if (constraint.lower_bound() <= 0) {
796 constraint.clear_lower_bound();
801 if (constraint.has_lower_bound() || constraint.has_upper_bound()) {
803 constraint.add_literals(entry.literal.SignedValue());
804 constraint.add_coefficients(entry.coefficient.value());
811 const int num_constraints = problem->constraints_size();
812 for (
int i = 0; i < num_constraints; ++i) {
813 if (!(problem->constraints(i).literals_size() == 0)) {
814 problem->mutable_constraints()->SwapElements(i, new_index);
818 problem->mutable_constraints()->DeleteSubrange(new_index,
819 num_constraints - new_index);
823 for (LiteralIndex
index : mapping) {
828 problem->set_num_variables(num_vars);
832 problem->mutable_var_names()->DeleteSubrange(
833 num_vars, problem->var_names_size() - num_vars);
839 LinearBooleanProblem* problem) {
841 for (
int iter = 0; iter < 6; ++iter) {
844 LOG(INFO) <<
"UNSAT when loading the problem.";
854 if (equiv_map.
empty()) {
872 BooleanVariable new_var(0);
886 if (equiv_map[
index] >= 0) {
888 const BooleanVariable image = var_map[l.
Variable()];
889 CHECK_NE(image, BooleanVariable(-1));
ABSL_FLAG(std::string, debug_dump_symmetry_graph_to_file, "", "If this flag is non-empty, an undirected graph whose" " automorphism group is in one-to-one correspondence with the" " symmetries of the SAT problem will be dumped to a file every" " time FindLinearBooleanProblemSymmetries() is called.")
void push_back(const value_type &x)
void RemoveCycles(const std::vector< int > &cycle_indices)
const std::vector< int > & Support() const
Iterator Cycle(int i) const
int NumConstraints() const
bool AddLinearConstraint(bool use_lower_bound, Coefficient lower_bound, bool use_upper_bound, Coefficient upper_bound, std::vector< LiteralWithCoeff > *cst)
const Coefficient Rhs(int i) const
const std::vector< LiteralWithCoeff > & Constraint(int i) const
LiteralIndex NegatedIndex() const
LiteralIndex Index() const
BooleanVariable Variable() const
std::string DebugString() const
void FixVariable(Literal x)
void ApplyMapping(const absl::StrongVector< BooleanVariable, BooleanVariable > &mapping)
const Trail & LiteralTrail() const
bool AddLinearConstraint(bool use_lower_bound, Coefficient lower_bound, bool use_upper_bound, Coefficient upper_bound, std::vector< LiteralWithCoeff > *cst)
void SetNumVariables(int num_variables)
const SatParameters & parameters() const
void SetAssignmentPreference(Literal literal, double weight)
const VariablesAssignment & Assignment() const
void Backtrack(int target_level)
bool VariableIsAssigned(BooleanVariable var) const
bool LiteralIsTrue(Literal literal) const
Literal GetTrueLiteralForAssignedVariable(BooleanVariable var) const
int NumberOfVariables() const
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
std::tuple< int64_t, int64_t, const double > Coefficient
bool AddObjectiveConstraint(const LinearBooleanProblem &problem, bool use_lower_bound, Coefficient lower_bound, bool use_upper_bound, Coefficient upper_bound, SatSolver *solver)
void StoreAssignment(const VariablesAssignment &assignment, BooleanAssignment *output)
Graph * GenerateGraphForSymmetryDetection(const LinearBooleanProblem &problem, std::vector< int > *initial_equivalence_classes)
void UseObjectiveForSatAssignmentPreference(const LinearBooleanProblem &problem, SatSolver *solver)
bool ApplyLiteralMapping(const absl::StrongVector< LiteralIndex, LiteralIndex > &mapping, std::vector< LiteralWithCoeff > *cst, Coefficient *bound_shift, Coefficient *max_value)
void ExtractSubproblem(const LinearBooleanProblem &problem, const std::vector< int > &constraint_indices, LinearBooleanProblem *subproblem)
absl::Status ValidateBooleanProblem(const LinearBooleanProblem &problem)
bool AddObjectiveUpperBound(const LinearBooleanProblem &problem, Coefficient upper_bound, SatSolver *solver)
void FindLinearBooleanProblemSymmetries(const LinearBooleanProblem &problem, std::vector< std::unique_ptr< SparsePermutation >> *generators)
const LiteralIndex kTrueLiteralIndex(-2)
bool ComputeBooleanLinearExpressionCanonicalForm(std::vector< LiteralWithCoeff > *cst, Coefficient *bound_shift, Coefficient *max_value)
const LiteralIndex kFalseLiteralIndex(-3)
bool LoadAndConsumeBooleanProblem(LinearBooleanProblem *problem, SatSolver *solver)
void ApplyLiteralMappingToBooleanProblem(const absl::StrongVector< LiteralIndex, LiteralIndex > &mapping, LinearBooleanProblem *problem)
bool IsAssignmentValid(const LinearBooleanProblem &problem, const std::vector< bool > &assignment)
void ChangeOptimizationDirection(LinearBooleanProblem *problem)
void ProbeAndSimplifyProblem(SatPostsolver *postsolver, LinearBooleanProblem *problem)
Coefficient ComputeObjectiveValue(const LinearBooleanProblem &problem, const std::vector< bool > &assignment)
void ProbeAndFindEquivalentLiteral(SatSolver *solver, SatPostsolver *postsolver, DratProofHandler *drat_proof_handler, absl::StrongVector< LiteralIndex, LiteralIndex > *mapping)
CpModelProto BooleanProblemToCpModelproto(const LinearBooleanProblem &problem)
void MakeAllLiteralsPositive(LinearBooleanProblem *problem)
bool LoadBooleanProblem(const LinearBooleanProblem &problem, SatSolver *solver)
std::string LinearBooleanProblemToCnfString(const LinearBooleanProblem &problem)
void ExtractAssignment(const LinearBooleanProblem &problem, const SatSolver &solver, std::vector< bool > *assignment)
Collection of objects used to extend the Constraint Solver library.
std::string ProtobufDebugString(const P &message)
absl::Status WriteGraphToFile(const Graph &graph, const std::string &filename, bool directed, const std::vector< int > &num_nodes_with_color)
std::unique_ptr< Graph > RemapGraph(const Graph &graph, const std::vector< int > &new_node_index)
static int input(yyscan_t yyscanner)
std::vector< int >::const_iterator begin() const