Skip to content

Commit

Permalink
tweak c++ examples
Browse files Browse the repository at this point in the history
  • Loading branch information
lperron committed Oct 27, 2023
1 parent 6387ab3 commit 67d4792
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 31 deletions.
66 changes: 51 additions & 15 deletions examples/cpp/binpacking_2d_sat.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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 =
Expand All @@ -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;
}

Expand Down Expand Up @@ -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);
Expand All @@ -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];
}
Expand All @@ -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) {
Expand Down
63 changes: 47 additions & 16 deletions examples/cpp/strawberry_fields_with_column_generation.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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();

Expand All @@ -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();
}
Expand All @@ -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";
Expand All @@ -609,22 +607,55 @@ 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;

if (absl::GetFlag(FLAGS_colgen_instance) == -1) {
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);
CHECK_LT(absl::GetFlag(FLAGS_colgen_instance),
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;
}

0 comments on commit 67d4792

Please sign in to comment.