26#include "absl/container/flat_hash_map.h"
27#include "absl/flags/flag.h"
28#include "absl/log/check.h"
29#include "absl/meta/type_traits.h"
30#include "absl/status/status.h"
31#include "absl/strings/str_format.h"
32#include "absl/strings/string_view.h"
35#if !defined(__PORTABLE_PLATFORM__)
43#include "ortools/sat/boolean_problem.pb.h"
44#include "ortools/sat/cp_model.pb.h"
47#include "ortools/sat/sat_parameters.pb.h"
52ABSL_FLAG(std::string, debug_dump_symmetry_graph_to_file,
"",
53 "If this flag is non-empty, an undirected graph whose"
54 " automorphism group is in one-to-one correspondence with the"
55 " symmetries of the SAT problem will be dumped to a file every"
56 " time FindLinearBooleanProblemSymmetries() is called.");
64 const SatSolver& solver, std::vector<bool>* assignment) {
66 for (
int i = 0;
i < problem.num_variables(); ++
i) {
67 assignment->push_back(
78template <
typename LinearTerms>
79std::string ValidateLinearTerms(
const LinearTerms& terms,
80 std::vector<bool>* variable_seen) {
84 const int max_num_errs = 100;
85 for (
int i = 0;
i < terms.literals_size(); ++
i) {
86 if (terms.literals(
i) == 0) {
87 if (++num_errs <= max_num_errs) {
88 err_str += absl::StrFormat(
"Zero literal at position %d\n",
i);
91 if (terms.coefficients(
i) == 0) {
92 if (++num_errs <= max_num_errs) {
93 err_str += absl::StrFormat(
"Literal %d has a zero coefficient\n",
97 const int var = Literal(terms.literals(
i)).Variable().value();
98 if (
var >= variable_seen->size()) {
99 if (++num_errs <= max_num_errs) {
100 err_str += absl::StrFormat(
"Out of bound variable %d\n",
var);
103 if ((*variable_seen)[
var]) {
104 if (++num_errs <= max_num_errs) {
105 err_str += absl::StrFormat(
"Duplicated variable %d\n",
var);
108 (*variable_seen)[
var] =
true;
111 for (
int i = 0;
i < terms.literals_size(); ++
i) {
112 const int var = Literal(terms.literals(
i)).Variable().value();
113 (*variable_seen)[
var] =
false;
116 if (num_errs <= max_num_errs) {
117 err_str = absl::StrFormat(
"%d validation errors:\n", num_errs) + err_str;
120 absl::StrFormat(
"%d validation errors; here are the first %d:\n",
121 num_errs, max_num_errs) +
130template <
typename ProtoFormat>
131std::vector<LiteralWithCoeff> ConvertLinearExpression(
132 const ProtoFormat&
input) {
133 std::vector<LiteralWithCoeff> cst;
134 cst.reserve(
input.literals_size());
135 for (
int i = 0;
i <
input.literals_size(); ++
i) {
137 cst.push_back(LiteralWithCoeff(
literal,
input.coefficients(
i)));
145 std::vector<bool> variable_seen(problem.num_variables(),
false);
146 for (
int i = 0;
i < problem.constraints_size(); ++
i) {
147 const LinearBooleanConstraint& constraint = problem.constraints(
i);
148 const std::string error = ValidateLinearTerms(constraint, &variable_seen);
149 if (!error.empty()) {
151 absl::StatusCode::kInvalidArgument,
152 absl::StrFormat(
"Invalid constraint %i: ",
i) + error);
155 const std::string error =
156 ValidateLinearTerms(problem.objective(), &variable_seen);
157 if (!error.empty()) {
158 return absl::Status(absl::StatusCode::kInvalidArgument,
159 absl::StrFormat(
"Invalid objective: ") + error);
161 return ::absl::OkStatus();
166 for (
int i = 0;
i < problem.num_variables(); ++
i) {
167 IntegerVariableProto*
var = result.add_variables();
168 if (problem.var_names_size() >
i) {
169 var->set_name(problem.var_names(
i));
174 for (
const LinearBooleanConstraint& constraint : problem.constraints()) {
175 ConstraintProto*
ct = result.add_constraints();
176 ct->set_name(constraint.name());
177 LinearConstraintProto* linear =
ct->mutable_linear();
179 for (
int i = 0;
i < constraint.literals_size(); ++
i) {
181 const int lit = constraint.literals(
i);
182 const int64_t coeff = constraint.coefficients(
i);
184 linear->add_vars(
lit - 1);
185 linear->add_coeffs(coeff);
188 linear->add_vars(-
lit - 1);
189 linear->add_coeffs(-coeff);
193 linear->add_domain(constraint.has_lower_bound()
194 ? constraint.lower_bound() + offset
195 : std::numeric_limits<int32_t>::min() + offset);
196 linear->add_domain(constraint.has_upper_bound()
197 ? constraint.upper_bound() + offset
198 : std::numeric_limits<int32_t>::max() + offset);
200 if (problem.has_objective()) {
201 CpObjectiveProto* objective = result.mutable_objective();
203 for (
int i = 0;
i < problem.objective().literals_size(); ++
i) {
204 const int lit = problem.objective().literals(
i);
205 const int64_t coeff = problem.objective().coefficients(
i);
207 objective->add_vars(
lit - 1);
208 objective->add_coeffs(coeff);
210 objective->add_vars(-
lit - 1);
211 objective->add_coeffs(-coeff);
215 objective->set_offset(offset + problem.objective().offset());
216 objective->set_scaling_factor(problem.objective().scaling_factor());
222 LinearObjective* objective = problem->mutable_objective();
223 objective->set_scaling_factor(-objective->scaling_factor());
224 objective->set_offset(-objective->offset());
227 for (
auto& coefficients_ref : *objective->mutable_coefficients()) {
228 coefficients_ref = -coefficients_ref;
240 LOG(WARNING) <<
"The given problem is invalid!";
243 if (solver->
parameters().log_search_progress()) {
244 LOG(INFO) <<
"Loading problem '" << problem.name() <<
"', "
245 << problem.num_variables() <<
" variables, "
246 << problem.constraints_size() <<
" constraints.";
249 std::vector<LiteralWithCoeff> cst;
250 int64_t num_terms = 0;
251 int num_constraints = 0;
252 for (
const LinearBooleanConstraint& constraint : problem.constraints()) {
253 num_terms += constraint.literals_size();
254 cst = ConvertLinearExpression(constraint);
256 constraint.has_lower_bound(), Coefficient(constraint.lower_bound()),
257 constraint.has_upper_bound(), Coefficient(constraint.upper_bound()),
259 LOG(INFO) <<
"Problem detected to be UNSAT when "
260 <<
"adding the constraint #" << num_constraints
261 <<
" with name '" << constraint.name() <<
"'";
266 if (solver->
parameters().log_search_progress()) {
267 LOG(INFO) <<
"The problem contains " << num_terms <<
" terms.";
276 LOG(WARNING) <<
"The given problem is invalid! " <<
status.message();
278 if (solver->
parameters().log_search_progress()) {
279#if !defined(__PORTABLE_PLATFORM__)
280 LOG(INFO) <<
"LinearBooleanProblem memory: " << problem->SpaceUsedLong();
282 LOG(INFO) <<
"Loading problem '" << problem->name() <<
"', "
283 << problem->num_variables() <<
" variables, "
284 << problem->constraints_size() <<
" constraints.";
287 std::vector<LiteralWithCoeff> cst;
288 int64_t num_terms = 0;
289 int num_constraints = 0;
294 std::reverse(problem->mutable_constraints()->begin(),
295 problem->mutable_constraints()->end());
296 for (
int i = problem->constraints_size() - 1;
i >= 0; --
i) {
297 const LinearBooleanConstraint& constraint = problem->constraints(
i);
298 num_terms += constraint.literals_size();
299 cst = ConvertLinearExpression(constraint);
301 constraint.has_lower_bound(), Coefficient(constraint.lower_bound()),
302 constraint.has_upper_bound(), Coefficient(constraint.upper_bound()),
304 LOG(INFO) <<
"Problem detected to be UNSAT when "
305 <<
"adding the constraint #" << num_constraints
306 <<
" with name '" << constraint.name() <<
"'";
309 delete problem->mutable_constraints()->ReleaseLast();
312 LinearBooleanProblem empty_problem;
313 problem->mutable_constraints()->Swap(empty_problem.mutable_constraints());
314 if (solver->
parameters().log_search_progress()) {
315 LOG(INFO) <<
"The problem contains " << num_terms <<
" terms.";
322 const LinearObjective& objective = problem.objective();
323 CHECK_EQ(objective.literals_size(), objective.coefficients_size());
324 int64_t max_abs_weight = 0;
325 for (
const int64_t
coefficient : objective.coefficients()) {
326 max_abs_weight = std::max(max_abs_weight, std::abs(
coefficient));
328 const double max_abs_weight_double = max_abs_weight;
329 for (
int i = 0;
i < objective.literals_size(); ++
i) {
332 const double abs_weight = std::abs(
coefficient) / max_abs_weight_double;
343 std::vector<LiteralWithCoeff> cst =
344 ConvertLinearExpression(problem.objective());
353 std::vector<LiteralWithCoeff> cst =
354 ConvertLinearExpression(problem.objective());
360 const std::vector<bool>& assignment) {
361 CHECK_EQ(assignment.size(), problem.num_variables());
363 const LinearObjective& objective = problem.objective();
364 for (
int i = 0;
i < objective.literals_size(); ++
i) {
366 if (assignment[
literal.Variable().value()] ==
literal.IsPositive()) {
367 sum += objective.coefficients(
i);
374 const std::vector<bool>& assignment) {
375 CHECK_EQ(assignment.size(), problem.num_variables());
378 for (
const LinearBooleanConstraint& constraint : problem.constraints()) {
380 for (
int i = 0;
i < constraint.literals_size(); ++
i) {
382 if (assignment[
literal.Variable().value()] ==
literal.IsPositive()) {
383 sum += constraint.coefficients(
i);
386 if (constraint.has_lower_bound() && sum < constraint.lower_bound()) {
387 LOG(WARNING) <<
"Unsatisfied constraint! sum: " << sum <<
"\n"
391 if (constraint.has_upper_bound() && sum > constraint.upper_bound()) {
392 LOG(WARNING) <<
"Unsatisfied constraint! sum: " << sum <<
"\n"
404 const LinearBooleanProblem& problem) {
406 const bool is_wcnf = (problem.objective().coefficients_size() > 0);
407 const LinearObjective& objective = problem.objective();
413 const int first_slack_variable = problem.original_num_variables();
416 absl::flat_hash_map<int, int64_t> literal_to_weight;
417 std::vector<std::pair<int, int64_t>> non_slack_objective;
422 int64_t hard_weight = 1;
425 for (int64_t
weight : objective.coefficients()) {
427 int signed_literal = objective.literals(
i);
436 signed_literal = -signed_literal;
439 literal_to_weight[objective.literals(
i)] =
weight;
440 if (
Literal(signed_literal).Variable() < first_slack_variable) {
441 non_slack_objective.push_back(std::make_pair(signed_literal,
weight));
446 output += absl::StrFormat(
"p wcnf %d %d %d\n", first_slack_variable,
447 static_cast<int>(problem.constraints_size() +
448 non_slack_objective.size()),
451 output += absl::StrFormat(
"p cnf %d %d\n", problem.num_variables(),
452 problem.constraints_size());
455 std::string constraint_output;
456 for (
const LinearBooleanConstraint& constraint : problem.constraints()) {
457 if (constraint.literals_size() == 0)
return "";
458 constraint_output.clear();
459 int64_t
weight = hard_weight;
460 for (
int i = 0;
i < constraint.literals_size(); ++
i) {
461 if (constraint.coefficients(
i) != 1)
return "";
462 if (is_wcnf && abs(constraint.literals(
i)) - 1 >= first_slack_variable) {
463 weight = literal_to_weight[constraint.literals(
i)];
465 if (
i > 0) constraint_output +=
" ";
470 output += absl::StrFormat(
"%d ",
weight);
472 output += constraint_output +
" 0\n";
477 for (std::pair<int, int64_t> p : non_slack_objective) {
481 output += absl::StrFormat(
"%d %s 0\n", p.second,
literal.DebugString());
489 BooleanAssignment* output) {
490 output->clear_literals();
493 output->add_literals(
500 const std::vector<int>& constraint_indices,
501 LinearBooleanProblem* subproblem) {
502 *subproblem = problem;
503 subproblem->set_name(
"Subproblem of " + problem.name());
504 subproblem->clear_constraints();
505 for (
int index : constraint_indices) {
506 CHECK_LT(
index, problem.constraints_size());
507 subproblem->add_constraints()->MergeFrom(problem.constraints(
index));
516 IdGenerator() =
default;
521 const std::pair<int, int64_t> key(type,
coefficient.value());
522 return id_map_.emplace(key, id_map_.size()).first->second;
526 absl::flat_hash_map<std::pair<int, int64_t>,
int> id_map_;
544template <
typename Graph>
546 const LinearBooleanProblem& problem,
547 std::vector<int>* initial_equivalence_classes) {
549 const int num_variables = problem.num_variables();
551 std::vector<LiteralWithCoeff> cst;
552 for (
const LinearBooleanConstraint& constraint : problem.constraints()) {
553 cst = ConvertLinearExpression(constraint);
555 constraint.has_lower_bound(), Coefficient(constraint.lower_bound()),
556 constraint.has_upper_bound(), Coefficient(constraint.upper_bound()),
563 initial_equivalence_classes->clear();
567 enum NodeType { LITERAL_NODE, CONSTRAINT_NODE, CONSTRAINT_COEFFICIENT_NODE };
568 IdGenerator id_generator;
572 for (
int i = 0;
i < num_variables; ++
i) {
583 initial_equivalence_classes->assign(
585 id_generator.GetId(NodeType::LITERAL_NODE, Coefficient(0)));
594 Coefficient max_value;
595 std::vector<LiteralWithCoeff> expr =
596 ConvertLinearExpression(problem.objective());
599 (*initial_equivalence_classes)[term.literal.Index().value()] =
600 id_generator.GetId(NodeType::LITERAL_NODE, term.coefficient);
610 const int constraint_node_index = initial_equivalence_classes->size();
611 initial_equivalence_classes->push_back(id_generator.GetId(
612 NodeType::CONSTRAINT_NODE, canonical_problem.
Rhs(
i)));
620 int current_node_index = constraint_node_index;
621 Coefficient previous_coefficient(1);
623 if (term.coefficient != previous_coefficient) {
624 current_node_index = initial_equivalence_classes->size();
625 initial_equivalence_classes->push_back(id_generator.GetId(
626 NodeType::CONSTRAINT_COEFFICIENT_NODE, term.coefficient));
627 previous_coefficient = term.coefficient;
632 graph->AddArc(constraint_node_index, current_node_index);
633 graph->AddArc(current_node_index, constraint_node_index);
639 graph->AddArc(current_node_index, term.literal.Index().value());
640 graph->AddArc(term.literal.Index().value(), current_node_index);
644 DCHECK_EQ(
graph->num_nodes(), initial_equivalence_classes->size());
650 LinearObjective* mutable_objective = problem->mutable_objective();
651 int64_t objective_offset = 0;
652 for (
int i = 0;
i < mutable_objective->literals_size(); ++
i) {
653 const int signed_literal = mutable_objective->literals(
i);
654 if (signed_literal < 0) {
655 const int64_t
coefficient = mutable_objective->coefficients(
i);
656 mutable_objective->set_literals(
i, -signed_literal);
661 mutable_objective->set_offset(mutable_objective->offset() + objective_offset);
664 for (LinearBooleanConstraint& constraint :
665 *(problem->mutable_constraints())) {
667 for (
int i = 0;
i < constraint.literals_size(); ++
i) {
668 if (constraint.literals(
i) < 0) {
669 sum += constraint.coefficients(
i);
670 constraint.set_literals(
i, -constraint.literals(
i));
671 constraint.set_coefficients(
i, -constraint.coefficients(
i));
674 if (constraint.has_lower_bound()) {
675 constraint.set_lower_bound(constraint.lower_bound() - sum);
677 if (constraint.has_upper_bound()) {
678 constraint.set_upper_bound(constraint.upper_bound() - sum);
684 const LinearBooleanProblem& problem,
685 std::vector<std::unique_ptr<SparsePermutation>>* generators) {
687 std::vector<int> equivalence_classes;
688 std::unique_ptr<Graph>
graph(
690 LOG(INFO) <<
"Graph has " <<
graph->num_nodes() <<
" nodes and "
691 <<
graph->num_arcs() / 2 <<
" edges.";
692#if !defined(__PORTABLE_PLATFORM__)
693 if (!absl::GetFlag(FLAGS_debug_dump_symmetry_graph_to_file).empty()) {
695 std::vector<int> new_node_index(
graph->num_nodes(), -1);
696 const int num_classes = 1 + *std::max_element(equivalence_classes.begin(),
697 equivalence_classes.end());
698 std::vector<int> class_size(num_classes, 0);
699 for (
const int c : equivalence_classes) ++class_size[c];
700 std::vector<int> next_index_by_class(num_classes, 0);
701 std::partial_sum(class_size.begin(), class_size.end() - 1,
702 next_index_by_class.begin() + 1);
703 for (
int node = 0; node <
graph->num_nodes(); ++node) {
704 new_node_index[node] = next_index_by_class[equivalence_classes[node]]++;
706 std::unique_ptr<Graph> remapped_graph = RemapGraph(*
graph, new_node_index);
708 *remapped_graph, absl::GetFlag(FLAGS_debug_dump_symmetry_graph_to_file),
711 LOG(DFATAL) <<
"Error when writing the symmetry graph to file: "
718 std::vector<int> factorized_automorphism_group_size;
720 CHECK_OK(symmetry_finder.
FindSymmetries(&equivalence_classes, generators,
721 &factorized_automorphism_group_size));
726 double average_support_size = 0.0;
727 int num_generators = 0;
728 for (
int i = 0;
i < generators->size(); ++
i) {
730 std::vector<int> to_delete;
731 for (
int j = 0; j < permutation->
NumCycles(); ++j) {
732 if (*(permutation->
Cycle(j).
begin()) >= 2 * problem.num_variables()) {
733 to_delete.push_back(j);
736 for (
const int node : permutation->
Cycle(j)) {
737 DCHECK_GE(node, 2 * problem.num_variables());
743 if (!permutation->
Support().empty()) {
744 average_support_size += permutation->
Support().size();
745 swap((*generators)[num_generators], (*generators)[
i]);
749 generators->resize(num_generators);
750 average_support_size /= num_generators;
751 LOG(INFO) <<
"# of generators: " << num_generators;
752 LOG(INFO) <<
"Average support size: " << average_support_size;
757 LinearBooleanProblem* problem) {
758 Coefficient bound_shift;
759 Coefficient max_value;
760 std::vector<LiteralWithCoeff> cst;
763 cst = ConvertLinearExpression(problem->objective());
765 LinearObjective* mutable_objective = problem->mutable_objective();
766 mutable_objective->clear_literals();
767 mutable_objective->clear_coefficients();
768 mutable_objective->set_offset(mutable_objective->offset() -
769 bound_shift.value());
771 mutable_objective->add_literals(entry.literal.SignedValue());
772 mutable_objective->add_coefficients(entry.coefficient.value());
776 for (LinearBooleanConstraint& constraint : *problem->mutable_constraints()) {
777 cst = ConvertLinearExpression(constraint);
778 constraint.clear_literals();
779 constraint.clear_coefficients();
783 if (constraint.has_upper_bound()) {
784 constraint.set_upper_bound(constraint.upper_bound() +
785 bound_shift.value());
786 if (max_value <= constraint.upper_bound()) {
787 constraint.clear_upper_bound();
790 if (constraint.has_lower_bound()) {
791 constraint.set_lower_bound(constraint.lower_bound() +
792 bound_shift.value());
794 if (constraint.lower_bound() <= 0) {
795 constraint.clear_lower_bound();
800 if (constraint.has_lower_bound() || constraint.has_upper_bound()) {
802 constraint.add_literals(entry.literal.SignedValue());
803 constraint.add_coefficients(entry.coefficient.value());
810 const int num_constraints = problem->constraints_size();
811 for (
int i = 0;
i < num_constraints; ++
i) {
812 if (!(problem->constraints(
i).literals_size() == 0)) {
813 problem->mutable_constraints()->SwapElements(
i, new_index);
817 problem->mutable_constraints()->DeleteSubrange(new_index,
818 num_constraints - new_index);
822 for (LiteralIndex
index : mapping) {
827 problem->set_num_variables(num_vars);
831 problem->mutable_var_names()->DeleteSubrange(
832 num_vars, problem->var_names_size() - num_vars);
838 LinearBooleanProblem* problem) {
840 for (
int iter = 0; iter < 6; ++iter) {
843 LOG(INFO) <<
"UNSAT when loading the problem.";
853 if (equiv_map.empty()) {
871 BooleanVariable new_var(0);
885 if (equiv_map[
index] >= 0) {
887 const BooleanVariable image = var_map[l.
Variable()];
888 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.")
absl::Status FindSymmetries(std::vector< int > *node_equivalence_classes_io, std::vector< std::unique_ptr< SparsePermutation > > *generators, std::vector< int > *factorized_automorphism_group_size, TimeLimit *time_limit=nullptr)
void RemoveCycles(absl::Span< const int > cycle_indices)
const std::vector< int > & Support() const
Iterator Cycle(int i) const
int NumConstraints() const
Getters. All the constraints are guaranteed to be in canonical form.
Coefficient Rhs(int i) const
const std::vector< LiteralWithCoeff > & Constraint(int i) const
bool AddLinearConstraint(bool use_lower_bound, Coefficient lower_bound, bool use_upper_bound, Coefficient upper_bound, std::vector< LiteralWithCoeff > *cst)
std::string DebugString() const
LiteralIndex NegatedIndex() const
LiteralIndex Index() const
BooleanVariable Variable() const
void ApplyMapping(const util_intops::StrongVector< BooleanVariable, BooleanVariable > &mapping)
void FixVariable(Literal x)
bool AddLinearConstraint(bool use_lower_bound, Coefficient lower_bound, bool use_upper_bound, Coefficient upper_bound, std::vector< LiteralWithCoeff > *cst)
void SetAssignmentPreference(Literal literal, float weight)
void Backtrack(int target_level)
const SatParameters & parameters() const
const VariablesAssignment & Assignment() const
const Trail & LiteralTrail() const
void SetNumVariables(int num_variables)
bool VariableIsAssigned(BooleanVariable var) const
Returns true iff the given variable is assigned.
Literal GetTrueLiteralForAssignedVariable(BooleanVariable var) const
int NumberOfVariables() const
bool LiteralIsTrue(Literal literal) const
void push_back(const value_type &val)
bool AddObjectiveConstraint(const LinearBooleanProblem &problem, bool use_lower_bound, Coefficient lower_bound, bool use_upper_bound, Coefficient upper_bound, SatSolver *solver)
bool LoadBooleanProblem(const LinearBooleanProblem &problem, SatSolver *solver)
Loads a BooleanProblem into a given SatSolver instance.
void ApplyLiteralMappingToBooleanProblem(const util_intops::StrongVector< LiteralIndex, LiteralIndex > &mapping, LinearBooleanProblem *problem)
std::string LinearBooleanProblemToCnfString(const LinearBooleanProblem &problem)
void StoreAssignment(const VariablesAssignment &assignment, BooleanAssignment *output)
bool LoadAndConsumeBooleanProblem(LinearBooleanProblem *problem, SatSolver *solver)
bool ApplyLiteralMapping(const util_intops::StrongVector< LiteralIndex, LiteralIndex > &mapping, std::vector< LiteralWithCoeff > *cst, Coefficient *bound_shift, Coefficient *max_value)
void ExtractAssignment(const LinearBooleanProblem &problem, const SatSolver &solver, std::vector< bool > *assignment)
void MakeAllLiteralsPositive(LinearBooleanProblem *problem)
void ExtractSubproblem(const LinearBooleanProblem &problem, const std::vector< int > &constraint_indices, LinearBooleanProblem *subproblem)
Constructs a sub-problem formed by the constraints with given indices.
bool AddObjectiveUpperBound(const LinearBooleanProblem &problem, Coefficient upper_bound, SatSolver *solver)
Adds the constraint that the objective is smaller than the given upper bound.
void ProbeAndSimplifyProblem(SatPostsolver *postsolver, LinearBooleanProblem *problem)
void ChangeOptimizationDirection(LinearBooleanProblem *problem)
void FindLinearBooleanProblemSymmetries(const LinearBooleanProblem &problem, std::vector< std::unique_ptr< SparsePermutation > > *generators)
void ProbeAndFindEquivalentLiteral(SatSolver *solver, SatPostsolver *postsolver, DratProofHandler *drat_proof_handler, util_intops::StrongVector< LiteralIndex, LiteralIndex > *mapping, SolverLogger *logger)
CpModelProto BooleanProblemToCpModelproto(const LinearBooleanProblem &problem)
const LiteralIndex kTrueLiteralIndex(-2)
const LiteralIndex kFalseLiteralIndex(-3)
absl::Status ValidateBooleanProblem(const LinearBooleanProblem &problem)
bool IsAssignmentValid(const LinearBooleanProblem &problem, const std::vector< bool > &assignment)
Checks that an assignment is valid for the given BooleanProblem.
Coefficient ComputeObjectiveValue(const LinearBooleanProblem &problem, const std::vector< bool > &assignment)
Returns the objective value under the current assignment.
void UseObjectiveForSatAssignmentPreference(const LinearBooleanProblem &problem, SatSolver *solver)
Graph * GenerateGraphForSymmetryDetection(const LinearBooleanProblem &problem, std::vector< int > *initial_equivalence_classes)
bool ComputeBooleanLinearExpressionCanonicalForm(std::vector< LiteralWithCoeff > *cst, Coefficient *bound_shift, Coefficient *max_value)
In SWIG mode, we don't want anything besides these top-level includes.
std::string ProtobufDebugString(const P &message)
util::ReverseArcStaticGraph Graph
Type of graph to use.
absl::Status WriteGraphToFile(const Graph &graph, const std::string &filename, bool directed, absl::Span< const int > num_nodes_with_color)
std::unique_ptr< Graph > RemapGraph(const Graph &graph, absl::Span< const int > new_node_index)
static int input(yyscan_t yyscanner)
std::vector< int >::const_iterator begin() const
Represents a term in a pseudo-Boolean formula.