Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
stat_tables.cc
Go to the documentation of this file.
1// Copyright 2010-2024 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
15
16#include <algorithm>
17#include <cstdint>
18#include <string>
19#include <utility>
20#include <vector>
21
22#include "absl/container/btree_map.h"
23#include "absl/strings/str_cat.h"
24#include "absl/strings/str_format.h"
25#include "absl/strings/string_view.h"
26#include "absl/synchronization/mutex.h"
28#include "ortools/sat/cp_model.pb.h"
31#include "ortools/sat/model.h"
35#include "ortools/sat/util.h"
37
39
41 absl::MutexLock mutex_lock(&mutex_);
42
43 timing_table_.push_back(
44 {"Task timing", "n [ min, max] avg dev time",
45 "n [ min, max] avg dev dtime"});
46
47 search_table_.push_back({"Search stats", "Bools", "Conflicts", "Branches",
48 "Restarts", "BoolPropag", "IntegerPropag"});
49
50 clauses_table_.push_back({"SAT stats", "ClassicMinim", "LitRemoved",
51 "LitLearned", "LitForgotten", "Subsumed",
52 "MClauses", "MDecisions", "MLitTrue", "MSubsumed",
53 "MLitRemoved", "MReused"});
54
55 lp_table_.push_back({"Lp stats", "Component", "Iterations", "AddedCuts",
56 "OPTIMAL", "DUAL_F.", "DUAL_U."});
57
58 lp_dim_table_.push_back(
59 {"Lp dimension", "Final dimension of first component"});
60
61 lp_debug_table_.push_back({"Lp debug", "CutPropag", "CutEqPropag", "Adjust",
62 "Overflow", "Bad", "BadScaling"});
63
64 lp_manager_table_.push_back({"Lp pool", "Constraints", "Updates", "Simplif",
65 "Merged", "Shortened", "Split", "Strenghtened",
66 "Cuts/Call"});
67
68 lns_table_.push_back(
69 {"LNS stats", "Improv/Calls", "Closed", "Difficulty", "TimeLimit"});
70
71 ls_table_.push_back({"LS stats", "Batches", "Restarts/Perturbs", "LinMoves",
72 "GenMoves", "CompoundMoves", "Bactracks",
73 "WeightUpdates", "ScoreComputed"});
74}
75
77 absl::MutexLock mutex_lock(&mutex_);
78 timing_table_.push_back({FormatName(subsolver.name()), subsolver.TimingInfo(),
79 subsolver.DeterministicTimingInfo()});
80}
81
83 absl::MutexLock mutex_lock(&mutex_);
84 CpSolverResponse r;
86 search_table_.push_back({FormatName(name), FormatCounter(r.num_booleans()),
87 FormatCounter(r.num_conflicts()),
88 FormatCounter(r.num_branches()),
89 FormatCounter(r.num_restarts()),
90 FormatCounter(r.num_binary_propagations()),
91 FormatCounter(r.num_integer_propagations())});
92}
93
95 absl::MutexLock mutex_lock(&mutex_);
96 SatSolver::Counters counters = model->GetOrCreate<SatSolver>()->counters();
97 clauses_table_.push_back(
109}
110
111void SharedStatTables::AddLpStat(absl::string_view name, Model* model) {
112 absl::MutexLock mutex_lock(&mutex_);
113
114 // Sum per component for the lp_table.
115 int64_t num_compo = 0;
116 int64_t num_iter = 0;
117 int64_t num_cut = 0;
118 int64_t num_optimal = 0;
119 int64_t num_dual_feasible = 0;
120 int64_t num_dual_unbounded = 0;
121
122 // Last dimension of the first component for the lp_dim_table_.
123 std::string dimension;
124
125 // Sum per component for the lp_manager_table_.
126 int64_t num_constraints = 0;
127 int64_t num_constraint_updates = 0;
128 int64_t num_simplifications = 0;
129 int64_t num_merged_constraints = 0;
130 int64_t num_shortened_constraints = 0;
131 int64_t num_split_constraints = 0;
132 int64_t num_coeff_strenghtening = 0;
133 int64_t num_cuts = 0;
134 int64_t num_add_cut_calls = 0;
135
136 // For the cut table.
137 absl::btree_map<std::string, int> type_to_num_cuts;
138
139 // For the debug table.
140 int64_t total_num_cut_propagations = 0;
141 int64_t total_num_eq_propagations = 0;
142 int64_t num_adjusts = 0;
143 int64_t num_cut_overflows = 0;
144 int64_t num_bad_cuts = 0;
145 int64_t num_scaling_issues = 0;
146
147 auto* lps = model->GetOrCreate<LinearProgrammingConstraintCollection>();
148 for (const auto* lp : *lps) {
149 const auto& manager = lp->constraint_manager();
150 ++num_compo;
151 num_iter += lp->total_num_simplex_iterations();
152 num_cut += manager.num_cuts();
153 const auto& num_solve_by_status = lp->num_solves_by_status();
154
155 const int optimal_as_int = static_cast<int>(glop::ProblemStatus::OPTIMAL);
156 if (optimal_as_int < num_solve_by_status.size()) {
157 num_optimal += num_solve_by_status[optimal_as_int];
158 }
159
160 const int dual_feasible_as_int =
161 static_cast<int>(glop::ProblemStatus::DUAL_FEASIBLE);
162 if (dual_feasible_as_int < num_solve_by_status.size()) {
163 num_dual_feasible += num_solve_by_status[dual_feasible_as_int];
164 }
165
166 const int dual_unbounded_as_int =
167 static_cast<int>(glop::ProblemStatus::DUAL_UNBOUNDED);
168 if (dual_unbounded_as_int < num_solve_by_status.size()) {
169 num_dual_unbounded += num_solve_by_status[dual_unbounded_as_int];
170 }
171
172 // In case of more than one component, we take the first one.
173 if (dimension.empty()) dimension = lp->DimensionString();
174
175 // Sum for the lp debug table.
176 total_num_cut_propagations += lp->total_num_cut_propagations();
177 total_num_eq_propagations += lp->total_num_eq_propagations();
178 num_adjusts += lp->num_adjusts();
179 num_cut_overflows += lp->num_cut_overflows();
180 num_bad_cuts += lp->num_bad_cuts();
181 num_scaling_issues += lp->num_scaling_issues();
182
183 // Sum for the lp manager table.
184 num_constraints += manager.num_constraints();
185 num_constraint_updates += manager.num_constraint_updates();
186 num_simplifications += manager.num_simplifications();
187 num_merged_constraints += manager.num_merged_constraints();
188 num_shortened_constraints += manager.num_shortened_constraints();
189 num_split_constraints += manager.num_split_constraints();
190 num_coeff_strenghtening += manager.num_coeff_strenghtening();
191 num_cuts += manager.num_cuts();
192 num_add_cut_calls += manager.num_add_cut_calls();
193
194 for (const auto& [cut_name, num] : manager.type_to_num_cuts()) {
195 type_to_num_cuts[cut_name] += num;
196 }
197 }
198 if (num_compo == 0) return;
199
200 lp_table_.push_back(
201 {FormatName(name), FormatCounter(num_compo), FormatCounter(num_iter),
202 FormatCounter(num_cut), FormatCounter(num_optimal),
203 FormatCounter(num_dual_feasible), FormatCounter(num_dual_unbounded)});
204
205 if (!dimension.empty()) {
206 lp_dim_table_.push_back({FormatName(name), dimension});
207 }
208
209 if (!type_to_num_cuts.empty()) {
210 lp_cut_table_.push_back({std::string(name), std::move(type_to_num_cuts)});
211 }
212
213 lp_debug_table_.push_back(
214 {FormatName(name), FormatCounter(total_num_cut_propagations),
215 FormatCounter(total_num_eq_propagations), FormatCounter(num_adjusts),
216 FormatCounter(num_cut_overflows), FormatCounter(num_bad_cuts),
217 FormatCounter(num_scaling_issues)});
218
219 lp_manager_table_.push_back({FormatName(name), FormatCounter(num_constraints),
220 FormatCounter(num_constraint_updates),
221 FormatCounter(num_simplifications),
222 FormatCounter(num_merged_constraints),
223 FormatCounter(num_shortened_constraints),
224 FormatCounter(num_split_constraints),
225 FormatCounter(num_coeff_strenghtening),
226 absl::StrCat(FormatCounter(num_cuts), "/",
227 FormatCounter(num_add_cut_calls))});
228}
229
230void SharedStatTables::AddLnsStat(absl::string_view name,
231 const NeighborhoodGenerator& generator) {
232 absl::MutexLock mutex_lock(&mutex_);
233 const double fully_solved_proportion =
234 static_cast<double>(generator.num_fully_solved_calls()) /
235 static_cast<double>(std::max(int64_t{1}, generator.num_calls()));
236 lns_table_.push_back(
238 absl::StrCat(generator.num_improving_calls(), "/",
239 generator.num_calls()),
240 absl::StrFormat("%2.0f%%", 100 * fully_solved_proportion),
241 absl::StrFormat("%0.2f", generator.difficulty()),
242 absl::StrFormat("%0.2f", generator.deterministic_limit())});
243}
244
245void SharedStatTables::AddLsStat(absl::string_view name, int64_t num_batches,
246 int64_t num_restarts, int64_t num_linear_moves,
247 int64_t num_general_moves,
248 int64_t num_compound_moves,
249 int64_t num_backtracks,
250 int64_t num_weight_updates,
251 int64_t num_scores_computed) {
252 absl::MutexLock mutex_lock(&mutex_);
253 ls_table_.push_back(
254 {FormatName(name), FormatCounter(num_batches),
255 FormatCounter(num_restarts), FormatCounter(num_linear_moves),
256 FormatCounter(num_general_moves), FormatCounter(num_compound_moves),
257 FormatCounter(num_backtracks), FormatCounter(num_weight_updates),
258 FormatCounter(num_scores_computed)});
259}
260
262 if (!logger->LoggingIsEnabled()) return;
263
264 absl::MutexLock mutex_lock(&mutex_);
265 if (timing_table_.size() > 1) SOLVER_LOG(logger, FormatTable(timing_table_));
266 if (search_table_.size() > 1) SOLVER_LOG(logger, FormatTable(search_table_));
267 if (clauses_table_.size() > 1) {
268 SOLVER_LOG(logger, FormatTable(clauses_table_));
269 }
270
271 if (lp_table_.size() > 1) SOLVER_LOG(logger, FormatTable(lp_table_));
272 if (lp_dim_table_.size() > 1) SOLVER_LOG(logger, FormatTable(lp_dim_table_));
273 if (lp_debug_table_.size() > 1) {
274 SOLVER_LOG(logger, FormatTable(lp_debug_table_));
275 }
276 if (lp_manager_table_.size() > 1) {
277 SOLVER_LOG(logger, FormatTable(lp_manager_table_));
278 }
279
280 // Construct and generate lp cut table.
281 // Note that this one is transposed compared to the normal one.
282 if (!lp_cut_table_.empty()) {
283 // Collect all line names.
284 absl::btree_map<std::string, int> lines;
285 for (const auto& [_, map] : lp_cut_table_) {
286 for (const auto& [type_name, _] : map) {
287 lines[type_name] = 0;
288 }
289 }
290
291 // Create header and compute index.
292 std::vector<std::vector<std::string>> table;
293 int line_index = 1;
294 const int num_cols = lp_cut_table_.size() + 1;
295 table.push_back({"Lp Cut"});
296 table[0].resize(num_cols, "");
297 for (const auto& [type_name, _] : lines) {
298 lines[type_name] = line_index++;
299 table.push_back({absl::StrCat(type_name, ":")});
300 table.back().resize(num_cols, "-");
301 }
302
303 // Fill lines.
304 int col_index = 1;
305 for (const auto& [name, map] : lp_cut_table_) {
306 table[0][col_index] =
307 num_cols > 10 && name.size() > 6 ? name.substr(0, 6) : name;
308 for (const auto& [type_name, count] : map) {
309 table[lines[type_name]][col_index] = FormatCounter(count);
310 }
311 ++col_index;
312 }
313
314 if (table.size() > 1) SOLVER_LOG(logger, FormatTable(table));
315 }
316
317 if (lns_table_.size() > 1) SOLVER_LOG(logger, FormatTable(lns_table_));
318 if (ls_table_.size() > 1) SOLVER_LOG(logger, FormatTable(ls_table_));
319}
320
321} // namespace operations_research::sat
bool LoggingIsEnabled() const
Returns true iff logging is enabled.
Definition logging.h:49
A class that stores the collection of all LP constraints in a model.
Base class for a CpModelProto neighborhood generator.
double deterministic_limit() const
The current time limit that the sub-solve should use on this generator.
int64_t num_improving_calls() const
Out of num_calls(), how many improved the given solution.
double difficulty() const
The current difficulty of this generator.
int64_t num_calls() const
Number of times this generator was called.
int64_t num_fully_solved_calls() const
Number of time the neighborhood was fully solved (OPTIMAL/INFEASIBLE).
void AddTimingStat(const SubSolver &subsolver)
Add a line to the corresponding table.
void AddSearchStat(absl::string_view name, Model *model)
void Display(SolverLogger *logger)
Display the set of table at the end.
void AddLsStat(absl::string_view name, int64_t num_batches, int64_t num_restarts, int64_t num_linear_moves, int64_t num_general_moves, int64_t num_compound_moves, int64_t num_bactracks, int64_t num_weight_updates, int64_t num_scores_computed)
void AddClausesStat(absl::string_view name, Model *model)
void AddLpStat(absl::string_view name, Model *model)
void AddLnsStat(absl::string_view name, const NeighborhoodGenerator &generator)
std::string DeterministicTimingInfo() const
Definition subsolver.h:123
std::string name() const
Returns the name of this SubSolver. Used in logs.
Definition subsolver.h:96
const std::string name
A name for logging purposes.
GRBmodel * model
void FillSolveStatsInResponse(Model *model, CpSolverResponse *response)
std::string FormatCounter(int64_t num)
Prints a positive number with separators for easier reading (ex: 1'348'065).
Definition util.cc:49
std::string FormatName(absl::string_view name)
This is used to format our table first row entry.
Definition util.h:196
std::string FormatTable(std::vector< std::vector< std::string > > &table, int spacing)
Definition util.cc:77
int64_t minimization_num_clauses
TryToMinimizeClause() stats.
Definition sat_solver.h:422
int64_t num_minimizations
Minimization stats.
Definition sat_solver.h:410
int64_t num_literals_learned
Clause learning /deletion stats.
Definition sat_solver.h:417
#define SOLVER_LOG(logger,...)
Definition logging.h:109