tweak c++ examples
This commit is contained in:
@@ -46,7 +46,7 @@ ABSL_FLAG(int, max_bins, 0,
|
||||
"use some heuristics to compute this number.");
|
||||
ABSL_FLAG(bool, symmetry_breaking, true, "Use symmetry breaking constraints");
|
||||
ABSL_FLAG(bool, use_global_cumulative, true,
|
||||
"Use a globalcumulative relaxation");
|
||||
"Use a global cumulative relaxation");
|
||||
|
||||
namespace operations_research {
|
||||
namespace sat {
|
||||
@@ -86,7 +86,8 @@ absl::btree_set<int> FindFixedItems(
|
||||
absl::btree_set<int> fixed_items;
|
||||
|
||||
// We start by fixing big pairwise incompatible items. Each to its own bin.
|
||||
// See https://arxiv.org/pdf/1909.06835.pdf.
|
||||
// See Côté; Haouari; Iori. (2019). A Primal Decomposition Algorithm for the
|
||||
// Two-dimensional Bin Packing Problem (https://arxiv.org/pdf/1909.06835.pdf).
|
||||
const int num_items = problem.items_size();
|
||||
const auto box_dimensions = problem.box_shape().dimensions();
|
||||
|
||||
@@ -144,13 +145,15 @@ absl::btree_set<int> FindFixedItems(
|
||||
if (!found_incompatible_pair && !incompatible_pair_candidates.empty()) {
|
||||
// We could not add a pair of mutually incompatible items to our list. But
|
||||
// we know a set of elements that are incompatible with all the big ones.
|
||||
// Let's add the one with the largest area.
|
||||
// Let's add the one with the largest area. Note that if there are no big
|
||||
// items, incompatible_pair_candidates contains all items and we will just
|
||||
// fix the largest element.
|
||||
fixed_items.insert(*std::min_element(incompatible_pair_candidates.begin(),
|
||||
incompatible_pair_candidates.end(),
|
||||
GreaterByArea(problem)));
|
||||
}
|
||||
|
||||
if (!fixed_items.empty()) {
|
||||
if (fixed_items.size() > 1) {
|
||||
std::string_view message_end = ".";
|
||||
if (found_incompatible_pair) {
|
||||
message_end =
|
||||
@@ -164,16 +167,6 @@ absl::btree_set<int> FindFixedItems(
|
||||
<< message_end;
|
||||
}
|
||||
|
||||
if (fixed_items.empty()) {
|
||||
// We couldn't fix any items, just fix the one with the biggest area.
|
||||
std::vector<int> all_items;
|
||||
for (int i = 0; i < num_items; ++i) {
|
||||
all_items.push_back(i);
|
||||
}
|
||||
fixed_items.insert(*std::min_element(all_items.begin(), all_items.end(),
|
||||
GreaterByArea(problem)));
|
||||
}
|
||||
|
||||
return fixed_items;
|
||||
}
|
||||
|
||||
@@ -276,10 +269,22 @@ void LoadAndSolve(const std::string& file_name, int instance) {
|
||||
LOG(INFO) << num_incompatible_pairs << " incompatible pairs of items";
|
||||
}
|
||||
|
||||
// Compute the min size in each dimension.
|
||||
std::vector<int64_t> min_sizes_per_dimension = {box_dimensions.begin(),
|
||||
box_dimensions.end()};
|
||||
for (int item = 0; item < num_items; ++item) {
|
||||
for (int dim = 0; dim < num_dimensions; ++dim) {
|
||||
min_sizes_per_dimension[dim] =
|
||||
std::min(min_sizes_per_dimension[dim],
|
||||
problem.items(item).shapes(0).dimensions(dim));
|
||||
}
|
||||
}
|
||||
|
||||
// Manages positions and sizes for each item.
|
||||
std::vector<std::vector<std::vector<IntervalVar>>>
|
||||
interval_by_item_bin_dimension(num_items);
|
||||
std::vector<std::vector<IntVar>> starts_by_dimension(num_items);
|
||||
absl::btree_set<int> items_exclusive_in_at_least_one_dimension;
|
||||
for (int item = 0; item < num_items; ++item) {
|
||||
interval_by_item_bin_dimension[item].resize(max_bins);
|
||||
starts_by_dimension[item].resize(num_dimensions);
|
||||
@@ -290,8 +295,17 @@ void LoadAndSolve(const std::string& file_name, int instance) {
|
||||
const int64_t size = problem.items(item).shapes(0).dimensions(dim);
|
||||
IntVar start;
|
||||
if (b == 0) {
|
||||
start = cp_model.NewIntVar({0, dimension - size});
|
||||
// For item fixed to a given bin, by symmetry of rotation we can also
|
||||
// assume it is in the lower left corner.
|
||||
const int64_t start_max = fixed_items.contains(item)
|
||||
? (dimension - size + 1) / 2
|
||||
: dimension - size;
|
||||
start = cp_model.NewIntVar({0, start_max});
|
||||
starts_by_dimension[item][dim] = start;
|
||||
|
||||
if (size + min_sizes_per_dimension[dim] > dimension) {
|
||||
items_exclusive_in_at_least_one_dimension.insert(item);
|
||||
}
|
||||
} else {
|
||||
start = starts_by_dimension[item][dim];
|
||||
}
|
||||
@@ -302,6 +316,28 @@ void LoadAndSolve(const std::string& file_name, int instance) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!items_exclusive_in_at_least_one_dimension.empty()) {
|
||||
int num_boxes_fixed_in_corner = 0;
|
||||
int num_boxes_fixed_on_one_border = 0;
|
||||
for (const int item : items_exclusive_in_at_least_one_dimension) {
|
||||
for (int dim = 0; dim < num_dimensions; ++dim) {
|
||||
if (fixed_items.contains(item)) { // Fix to down left corner (0, 0).
|
||||
cp_model.AddEquality(starts_by_dimension[item][dim], 0);
|
||||
if (dim == 0) ++num_boxes_fixed_in_corner;
|
||||
} else {
|
||||
const int64_t dimension = box_dimensions[dim];
|
||||
const int64_t size = problem.items(item).shapes(0).dimensions(dim);
|
||||
if (size + min_sizes_per_dimension[dim] > dimension) {
|
||||
cp_model.AddEquality(starts_by_dimension[item][dim], 0);
|
||||
++num_boxes_fixed_on_one_border;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG(INFO) << num_boxes_fixed_in_corner << " boxes fixed in one corner";
|
||||
LOG(INFO) << num_boxes_fixed_on_one_border << " boxes fixed on one border";
|
||||
}
|
||||
|
||||
// Non overlapping.
|
||||
LOG(INFO) << "Box size: " << box_dimensions[0] << "*" << box_dimensions[1];
|
||||
for (int b = 0; b < max_bins; ++b) {
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/types/span.h"
|
||||
#include "ortools/base/commandlineflags.h"
|
||||
#include "ortools/base/init_google.h"
|
||||
#include "ortools/base/logging.h"
|
||||
@@ -301,7 +302,7 @@ class CoveringProblem {
|
||||
// Grid is a row-major string of length width*height with '@' for an
|
||||
// occupied cell (strawberry) and '.' for an empty cell. Solver is
|
||||
// not owned.
|
||||
CoveringProblem(MPSolver* solver, const Instance& instance)
|
||||
CoveringProblem(MPSolver* const solver, const Instance& instance)
|
||||
: solver_(solver),
|
||||
max_boxes_(instance.max_boxes),
|
||||
width_(instance.width),
|
||||
@@ -512,7 +513,7 @@ class CoveringProblem {
|
||||
}
|
||||
|
||||
// Gets 2d array element, returning 0 if out-of-bounds.
|
||||
double zero_access(const std::vector<double>& array, int x, int y) const {
|
||||
double zero_access(absl::Span<const double> array, int x, int y) const {
|
||||
if (x < 0 || y < 0) {
|
||||
return 0;
|
||||
}
|
||||
@@ -547,18 +548,15 @@ class CoveringProblem {
|
||||
|
||||
// Solves iteratively using delayed column generation, up to maximum
|
||||
// number of steps.
|
||||
void SolveInstance(const Instance& instance, std::string solver_name) {
|
||||
// Prepares the solver->
|
||||
std::unique_ptr<MPSolver> solver(MPSolver::CreateSolver(solver_name));
|
||||
if (!solver) {
|
||||
LOG(INFO) << "Solver type '" << solver_name << "' not supported, or not linked in";
|
||||
return;
|
||||
}
|
||||
solver->SuppressOutput();
|
||||
solver->MutableObjective()->SetMinimization();
|
||||
void SolveInstance(const Instance& instance,
|
||||
MPSolver::OptimizationProblemType solver_type) {
|
||||
// Prepares the solver.
|
||||
MPSolver solver("ColumnGeneration", solver_type);
|
||||
solver.SuppressOutput();
|
||||
solver.MutableObjective()->SetMinimization();
|
||||
|
||||
// Construct problem.
|
||||
CoveringProblem problem(solver.get(), instance);
|
||||
CoveringProblem problem(&solver, instance);
|
||||
CHECK(problem.Init());
|
||||
LOG(INFO) << "Initial problem:\n" << problem.PrintGrid();
|
||||
|
||||
@@ -569,7 +567,7 @@ void SolveInstance(const Instance& instance, std::string solver_name) {
|
||||
}
|
||||
|
||||
// Solve with existing columns.
|
||||
CHECK_EQ(MPSolver::OPTIMAL, solver->Solve());
|
||||
CHECK_EQ(MPSolver::OPTIMAL, solver.Solve());
|
||||
if (absl::GetFlag(FLAGS_colgen_verbose)) {
|
||||
LOG(INFO) << problem.PrintCovering();
|
||||
}
|
||||
@@ -593,7 +591,7 @@ void SolveInstance(const Instance& instance, std::string solver_name) {
|
||||
|
||||
if (step_number >= absl::GetFlag(FLAGS_colgen_max_iterations)) {
|
||||
// Solve one last time with all generated columns.
|
||||
CHECK_EQ(MPSolver::OPTIMAL, solver->Solve());
|
||||
CHECK_EQ(MPSolver::OPTIMAL, solver.Solve());
|
||||
}
|
||||
|
||||
LOG(INFO) << step_number << " columns added";
|
||||
@@ -609,6 +607,39 @@ int main(int argc, char** argv) {
|
||||
|
||||
absl::SetFlag(&FLAGS_stderrthreshold, 0);
|
||||
InitGoogle(usage.c_str(), &argc, &argv, true);
|
||||
|
||||
operations_research::MPSolver::OptimizationProblemType solver_type;
|
||||
bool found = false;
|
||||
#if defined(USE_CLP)
|
||||
if (absl::GetFlag(FLAGS_colgen_solver) == "clp") {
|
||||
solver_type = operations_research::MPSolver::CLP_LINEAR_PROGRAMMING;
|
||||
found = true;
|
||||
}
|
||||
#endif // USE_CLP
|
||||
//#if defined(USE_GLOP)
|
||||
if (absl::GetFlag(FLAGS_colgen_solver) == "glop") {
|
||||
solver_type = operations_research::MPSolver::GLOP_LINEAR_PROGRAMMING;
|
||||
found = true;
|
||||
}
|
||||
//#endif // USE_GLOP
|
||||
#if defined(USE_XPRESS)
|
||||
if (absl::GetFlag(FLAGS_colgen_solver) == "xpress") {
|
||||
solver_type = operations_research::MPSolver::XPRESS_LINEAR_PROGRAMMING;
|
||||
// solver_type = operations_research::MPSolver::CPLEX_LINEAR_PROGRAMMING;
|
||||
found = true;
|
||||
}
|
||||
#endif
|
||||
#if defined(USE_CPLEX)
|
||||
if (absl::GetFlag(FLAGS_colgen_solver) == "cplex") {
|
||||
solver_type = operations_research::MPSolver::CPLEX_LINEAR_PROGRAMMING;
|
||||
found = true;
|
||||
}
|
||||
#endif
|
||||
if (!found) {
|
||||
LOG(ERROR) << "Unknown solver " << absl::GetFlag(FLAGS_colgen_solver);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LOG(INFO) << "Chosen solver: " << absl::GetFlag(FLAGS_colgen_solver)
|
||||
<< std::endl;
|
||||
|
||||
@@ -616,7 +647,7 @@ int main(int argc, char** argv) {
|
||||
for (int i = 0; i < operations_research::kInstanceCount; ++i) {
|
||||
const operations_research::Instance& instance =
|
||||
operations_research::kInstances[i];
|
||||
operations_research::SolveInstance(instance, absl::GetFlag(FLAGS_colgen_solver));
|
||||
operations_research::SolveInstance(instance, solver_type);
|
||||
}
|
||||
} else {
|
||||
CHECK_GE(absl::GetFlag(FLAGS_colgen_instance), 0);
|
||||
@@ -624,7 +655,7 @@ int main(int argc, char** argv) {
|
||||
operations_research::kInstanceCount);
|
||||
const operations_research::Instance& instance =
|
||||
operations_research::kInstances[absl::GetFlag(FLAGS_colgen_instance)];
|
||||
operations_research::SolveInstance(instance, absl::GetFlag(FLAGS_colgen_solver));
|
||||
operations_research::SolveInstance(instance, solver_type);
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user