OR-Tools  9.6
bop_base.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 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 #include "ortools/bop/bop_base.h"
15 
16 #include <algorithm>
17 #include <cstdint>
18 #include <limits>
19 #include <string>
20 #include <vector>
21 
22 #include "absl/strings/string_view.h"
23 #include "absl/synchronization/mutex.h"
25 
26 namespace operations_research {
27 namespace bop {
28 
29 using ::operations_research::sat::LinearBooleanProblem;
30 using ::operations_research::sat::LinearObjective;
31 
33  : name_(name), stats_(name) {
35 }
36 
39 }
40 
42  switch (status) {
44  return "OPTIMAL_SOLUTION_FOUND";
45  case SOLUTION_FOUND:
46  return "SOLUTION_FOUND";
47  case INFEASIBLE:
48  return "INFEASIBLE";
49  case LIMIT_REACHED:
50  return "LIMIT_REACHED";
51  case INFORMATION_FOUND:
52  return "INFORMATION_FOUND";
53  case CONTINUE:
54  return "CONTINUE";
55  case ABORT:
56  return "ABORT";
57  }
58  // Fallback. We don't use "default:" so the compiler will return an error
59  // if we forgot one enum case above.
60  LOG(DFATAL) << "Invalid Status " << static_cast<int>(status);
61  return "UNKNOWN Status";
62 }
63 
64 //------------------------------------------------------------------------------
65 // ProblemState
66 //------------------------------------------------------------------------------
67 const int64_t ProblemState::kInitialStampValue(0);
68 
69 ProblemState::ProblemState(const LinearBooleanProblem& problem)
70  : original_problem_(problem),
71  parameters_(),
72  update_stamp_(kInitialStampValue + 1),
73  is_fixed_(problem.num_variables(), false),
74  fixed_values_(problem.num_variables(), false),
75  lp_values_(),
76  solution_(problem, "AllZero"),
77  assignment_preference_(),
78  lower_bound_(std::numeric_limits<int64_t>::min()),
79  upper_bound_(std::numeric_limits<int64_t>::max()) {
80  // TODO(user): Extract to a function used by all solvers.
81  // Compute trivial unscaled lower bound.
82  const LinearObjective& objective = problem.objective();
83  lower_bound_ = 0;
84  for (int i = 0; i < objective.coefficients_size(); ++i) {
85  // Fix template version for or-tools.
86  lower_bound_ += std::min<int64_t>(int64_t{0}, objective.coefficients(i));
87  }
88  upper_bound_ = solution_.IsFeasible() ? solution_.GetCost()
90 }
91 
92 // TODO(user): refactor this to not rely on the optimization status.
93 // All the information can be encoded in the learned_info bounds.
95  const LearnedInfo& learned_info,
96  BopOptimizerBase::Status optimization_status) {
97  const std::string kIndent(25, ' ');
98 
99  bool new_lp_values = false;
100  if (!learned_info.lp_values.empty()) {
101  if (lp_values_ != learned_info.lp_values) {
102  lp_values_ = learned_info.lp_values;
103  new_lp_values = true;
104  VLOG(1) << kIndent + "New LP values.";
105  }
106  }
107 
108  bool new_binary_clauses = false;
109  if (!learned_info.binary_clauses.empty()) {
110  const int old_num = binary_clause_manager_.NumClauses();
111  for (sat::BinaryClause c : learned_info.binary_clauses) {
112  const int num_vars = original_problem_.num_variables();
113  if (c.a.Variable() < num_vars && c.b.Variable() < num_vars) {
114  binary_clause_manager_.Add(c);
115  }
116  }
117  if (binary_clause_manager_.NumClauses() > old_num) {
118  new_binary_clauses = true;
119  VLOG(1) << kIndent + "Num binary clauses: "
120  << binary_clause_manager_.NumClauses();
121  }
122  }
123 
124  bool new_solution = false;
125  if (learned_info.solution.IsFeasible() &&
126  (!solution_.IsFeasible() ||
127  learned_info.solution.GetCost() < solution_.GetCost())) {
128  solution_ = learned_info.solution;
129  new_solution = true;
130  VLOG(1) << kIndent + "New solution.";
131  }
132 
133  bool new_lower_bound = false;
134  if (learned_info.lower_bound > lower_bound()) {
135  lower_bound_ = learned_info.lower_bound;
136  new_lower_bound = true;
137  VLOG(1) << kIndent + "New lower bound.";
138  }
139 
140  if (solution_.IsFeasible()) {
141  upper_bound_ = std::min(upper_bound(), solution_.GetCost());
142  if (upper_bound() <= lower_bound() ||
143  (upper_bound() - lower_bound() <=
144  parameters_.relative_gap_limit() *
145  std::max(std::abs(upper_bound()), std::abs(lower_bound())))) {
146  // The lower bound might be greater that the cost of a feasible solution
147  // due to rounding errors in the problem scaling and Glop.
148  // As a feasible solution was found, the solution is proved optimal.
149  MarkAsOptimal();
150  }
151  }
152 
153  // Merge fixed variables. Note that variables added during search, i.e. not
154  // in the original problem, are ignored.
155  int num_newly_fixed_variables = 0;
156  for (const sat::Literal literal : learned_info.fixed_literals) {
157  const VariableIndex var(literal.Variable().value());
158  if (var >= original_problem_.num_variables()) {
159  continue;
160  }
161  const bool value = literal.IsPositive();
162  if (is_fixed_[var]) {
163  if (fixed_values_[var] != value) {
165  return true;
166  }
167  } else {
168  is_fixed_[var] = true;
169  fixed_values_[var] = value;
170  ++num_newly_fixed_variables;
171  }
172  }
173  if (num_newly_fixed_variables > 0) {
174  int num_fixed_variables = 0;
175  for (const bool is_fixed : is_fixed_) {
176  if (is_fixed) {
177  ++num_fixed_variables;
178  }
179  }
180  VLOG(1) << kIndent << num_newly_fixed_variables
181  << " newly fixed variables (" << num_fixed_variables << " / "
182  << is_fixed_.size() << ").";
183  if (num_fixed_variables == is_fixed_.size()) {
184  // Set the solution to the fixed variables.
185  BopSolution fixed_solution = solution_;
186  for (VariableIndex var(0); var < is_fixed_.size(); ++var) {
187  fixed_solution.SetValue(var, fixed_values_[var]);
188  }
189  if (fixed_solution.IsFeasible()) {
190  solution_ = fixed_solution;
191  }
192  if (solution_.IsFeasible()) {
193  MarkAsOptimal();
194  VLOG(1) << kIndent << "Optimal";
195  } else {
197  }
198  }
199  }
200 
201  bool known_status = false;
202  if (optimization_status == BopOptimizerBase::OPTIMAL_SOLUTION_FOUND) {
203  MarkAsOptimal();
204  known_status = true;
205  } else if (optimization_status == BopOptimizerBase::INFEASIBLE) {
207  known_status = true;
208  }
209 
210  const bool updated = new_lp_values || new_binary_clauses || new_solution ||
211  new_lower_bound || num_newly_fixed_variables > 0 ||
212  known_status;
213  if (updated) ++update_stamp_;
214  return updated;
215 }
216 
218  LearnedInfo learned_info(original_problem_);
219  for (VariableIndex var(0); var < is_fixed_.size(); ++var) {
220  if (is_fixed_[var]) {
221  learned_info.fixed_literals.push_back(
222  sat::Literal(sat::BooleanVariable(var.value()), fixed_values_[var]));
223  }
224  }
225  learned_info.solution = solution_;
226  learned_info.lower_bound = lower_bound();
227  learned_info.lp_values = lp_values_;
228  learned_info.binary_clauses = NewlyAddedBinaryClauses();
229 
230  return learned_info;
231 }
232 
234  CHECK(solution_.IsFeasible());
235  lower_bound_ = upper_bound();
236  ++update_stamp_;
237 }
238 
240  // Mark as infeasible, i.e. set a lower_bound greater than the upper_bound.
241  CHECK(!solution_.IsFeasible());
243  lower_bound_ = std::numeric_limits<int64_t>::max();
244  upper_bound_ = std::numeric_limits<int64_t>::max() - 1;
245  } else {
246  lower_bound_ = upper_bound_ - 1;
247  }
248  ++update_stamp_;
249 }
250 
251 const std::vector<sat::BinaryClause>& ProblemState::NewlyAddedBinaryClauses()
252  const {
253  return binary_clause_manager_.newly_added();
254 }
255 
257  binary_clause_manager_.ClearNewlyAdded();
258 }
259 } // namespace bop
260 } // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
size_type size() const
bool empty() const
std::string StatString() const
Definition: stats.cc:77
static std::string GetStatusString(Status status)
Definition: bop_base.cc:41
BopOptimizerBase(absl::string_view name)
Definition: bop_base.cc:32
void SetValue(VariableIndex var, bool value)
Definition: bop_solution.h:40
bool MergeLearnedInfo(const LearnedInfo &learned_info, BopOptimizerBase::Status optimization_status)
Definition: bop_base.cc:94
const std::vector< sat::BinaryClause > & NewlyAddedBinaryClauses() const
Definition: bop_base.cc:251
LearnedInfo GetLearnedInfo() const
Definition: bop_base.cc:217
static const int64_t kInitialStampValue
Definition: bop_base.h:158
ProblemState(const sat::LinearBooleanProblem &problem)
Definition: bop_base.cc:69
const absl::StrongVector< VariableIndex, bool > & is_fixed() const
Definition: bop_base.h:179
const std::vector< BinaryClause > & newly_added() const
Definition: clause.h:404
BooleanVariable Variable() const
Definition: sat_base.h:86
const std::string name
int64_t value
IntVar * var
Definition: expr_array.cc:1874
absl::Status status
Definition: g_gurobi.cc:41
Collection of objects used to extend the Constraint Solver library.
Literal literal
Definition: optimization.cc:88
#define IF_STATS_ENABLED(instructions)
Definition: stats.h:438
#define SCOPED_TIME_STAT(stats)
Definition: stats.h:439
std::vector< sat::Literal > fixed_literals
Definition: bop_base.h:269
std::vector< sat::BinaryClause > binary_clauses
Definition: bop_base.h:284
#define VLOG(verboselevel)
Definition: vlog.h:39