diff --git a/examples/cpp/BUILD.bazel b/examples/cpp/BUILD.bazel index 6036ac0317..ca882b682f 100644 --- a/examples/cpp/BUILD.bazel +++ b/examples/cpp/BUILD.bazel @@ -1,3 +1,18 @@ +cc_binary( + name = "binpacking_2d_sat", + srcs = [ + "binpacking_2d_sat.cc", + ], + deps = [ + "//ortools/base", + "@com_google_protobuf//:protobuf", + "@com_google_absl//absl/flags:flag", + "//ortools/packing:binpacking_2d_parser", + "//ortools/packing:multiple_dimensions_bin_packing_cc_proto", + "//ortools/sat:cp_model", + ], +) + cc_binary( name = "constraint_programming_cp", srcs = ["constraint_programming_cp.cc"], @@ -237,13 +252,23 @@ cc_binary( "//ortools/scheduling:jobshop_scheduling_parser", "//ortools/sat:cp_model", "//ortools/sat:cp_model_solver", - "//ortools/sat:disjunctive", - "//ortools/sat:integer", - "//ortools/sat:intervals", "//ortools/sat:model", - "//ortools/sat:optimization", - "//ortools/sat:precedences", - "//ortools/sat:sat_solver", + + ], +) + +cc_binary( + name = "knapsack_2d_sat", + srcs = [ + "knapsack_2d_sat.cc", + ], + deps = [ + "//ortools/base", + "@com_google_protobuf//:protobuf", + "@com_google_absl//absl/flags:flag", + "//ortools/packing:binpacking_2d_parser", + "//ortools/packing:multiple_dimensions_bin_packing_cc_proto", + "//ortools/sat:cp_model", ], ) diff --git a/examples/cpp/binpacking_2d_sat.cc b/examples/cpp/binpacking_2d_sat.cc new file mode 100644 index 0000000000..e04828ceb6 --- /dev/null +++ b/examples/cpp/binpacking_2d_sat.cc @@ -0,0 +1,202 @@ +// Copyright 2010-2021 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file solves a 2D Bin Packing problem. +// It loads the size of the main rectangle, all available items (rectangles +// too), and tries to fit all rectangles in the minimum numbers of bins (they +// have the size of the main rectangle.) + +#include +#include +#include +#include +#include + +#include "absl/flags/flag.h" +#include "absl/flags/parse.h" +#include "absl/flags/usage.h" +#include "google/protobuf/text_format.h" +#include "ortools/base/commandlineflags.h" +#include "ortools/base/logging.h" +#include "ortools/packing/binpacking_2d_parser.h" +#include "ortools/packing/multiple_dimensions_bin_packing.pb.h" +#include "ortools/sat/cp_model.h" + +ABSL_FLAG(std::string, input, "", "Input file."); +ABSL_FLAG(int, instance, -1, "Instance number if the file."); +ABSL_FLAG(std::string, params, "", "Sat parameters in text proto format."); +ABSL_FLAG(int, max_bins, 0, + "Maximum number of bins. The 0 default value implies the code will " + "use some heuristics to compute this number."); + +namespace operations_research { +namespace sat { + +// Load a 2D binpacking problem and solve it. +void LoadAndSolve(const std::string& file_name, int instance) { + packing::BinPacking2dParser parser; + if (!parser.Load2BPFile(file_name, instance)) { + LOG(FATAL) << "Cannot read instance " << instance << " from file " + << file_name; + } + packing::MultipleDimensionsBinPackingProblem problem = parser.problem(); + LOG(INFO) << "Successfully loaded instance " << instance << " from file " + << file_name; + LOG(INFO) << "Instance has " << problem.items_size() << " items"; + + const auto box_dimensions = problem.box_shape().dimensions(); + const int num_dimensions = box_dimensions.size(); + const int num_items = problem.items_size(); + + const int area_of_one_bin = box_dimensions[0] * box_dimensions[1]; + int sum_of_items_area = 0; + for (const auto& item : problem.items()) { + CHECK_EQ(1, item.shapes_size()); + const auto& shape = item.shapes(0); + CHECK_EQ(2, shape.dimensions_size()); + sum_of_items_area += shape.dimensions(0) * shape.dimensions(1); + } + const int trivial_lb = + (sum_of_items_area + area_of_one_bin - 1) / area_of_one_bin; + + LOG(INFO) << "Trivial lower bound of the number of items = " << trivial_lb; + if (absl::GetFlag(FLAGS_max_bins) == 0) { + LOG(INFO) << "Setting max_bins to " << trivial_lb * 2; + } + const int max_bins = absl::GetFlag(FLAGS_max_bins) == 0 + ? trivial_lb * 2 + : absl::GetFlag(FLAGS_max_bins); + + CpModelBuilder cp_model; + + // Selects the right shape for each item (plus nil shape if not selected). + // The nil shape is the first choice. + std::vector> selected(num_items); + for (int item = 0; item < num_items; ++item) { + const int num_shapes = problem.items(item).shapes_size(); + CHECK_EQ(1, num_shapes); + selected[item].resize(max_bins); + for (int b = 0; b < max_bins; ++b) { + selected[item][b] = cp_model.NewBoolVar(); + } + } + + // Exactly one bin is selected for each item. + for (int item = 0; item < num_items; ++item) { + cp_model.AddEquality(LinearExpr::Sum(selected[item]), 1); + } + + // Manages positions and sizes for each item. + std::vector>> intervals(num_items); + for (int item = 0; item < num_items; ++item) { + intervals[item].resize(max_bins); + for (int b = 0; b < max_bins; ++b) { + intervals[item][b].resize(2); + for (int dim = 0; dim < num_dimensions; ++dim) { + const int64_t dimension = box_dimensions[dim]; + const int64_t size = problem.items(item).shapes(0).dimensions(dim); + IntVar start = cp_model.NewIntVar({0, dimension - size}); + intervals[item][b][dim] = cp_model.NewOptionalFixedSizeIntervalVar( + start, size, selected[item][b]); + } + } + } + + // Non overlapping. + if (num_dimensions == 1) { + LOG(FATAL) << "One dimension is not supported."; + } else if (num_dimensions == 2) { + LOG(INFO) << "Box size: " << box_dimensions[0] << "*" << box_dimensions[1]; + for (int b = 0; b < max_bins; ++b) { + NoOverlap2DConstraint no_overlap_2d = cp_model.AddNoOverlap2D(); + for (int item = 0; item < num_items; ++item) { + no_overlap_2d.AddRectangle(intervals[item][b][0], + intervals[item][b][1]); + } + } + } else { + LOG(FATAL) << num_dimensions << " dimensions not supported."; + } + + // Redundant constraint. + LinearExpr sum_of_areas; + for (int item = 0; item < num_items; ++item) { + const int item_area = problem.items(item).shapes(0).dimensions(0) * + problem.items(item).shapes(0).dimensions(1); + for (int b = 0; b < max_bins; ++b) { + sum_of_areas += selected[item][b] * item_area; + } + } + cp_model.AddEquality(sum_of_areas, sum_of_items_area); + + // Symmetry breaking: The number of items per bin is decreasing. + std::vector num_items_per_bins(max_bins); + LinearExpr all_items; + for (int b = 0; b < max_bins; ++b) { + for (int item = 0; item < num_items; ++item) { + num_items_per_bins[b] += selected[item][b]; + all_items += selected[item][b]; + } + } + for (int b = 1; b < max_bins; ++b) { + cp_model.AddLessOrEqual(num_items_per_bins[b - 1], num_items_per_bins[b]); + } + cp_model.AddEquality(all_items, num_items); + + // Objective. + std::vector bin_is_selected(max_bins); + for (int b = 0; b < max_bins; ++b) { + bin_is_selected[b] = cp_model.NewBoolVar(); + // Link bin_is_selected[i] with the items in bin i. + std::vector all_items; + for (int item = 0; item < num_items; ++item) { + cp_model.AddImplication(selected[item][b], bin_is_selected[b]); + all_items.push_back(selected[item][b].Not()); + } + all_items.push_back(bin_is_selected[b].Not()); + cp_model.AddBoolOr(all_items); + } + cp_model.Minimize(LinearExpr::Sum(bin_is_selected)); + + // Setup parameters. + SatParameters parameters; + parameters.set_log_search_progress(true); + // Parse the --params flag. + if (!absl::GetFlag(FLAGS_params).empty()) { + CHECK(google::protobuf::TextFormat::MergeFromString( + absl::GetFlag(FLAGS_params), ¶meters)) + << absl::GetFlag(FLAGS_params); + } + + const CpSolverResponse response = + SolveWithParameters(cp_model.Build(), parameters); +} + +} // namespace sat +} // namespace operations_research + +int main(int argc, char** argv) { + absl::SetFlag(&FLAGS_logtostderr, true); + google::InitGoogleLogging(argv[0]); + absl::ParseCommandLine(argc, argv); + if (absl::GetFlag(FLAGS_input).empty()) { + LOG(FATAL) << "Please supply a data file with --input="; + } + if (absl::GetFlag(FLAGS_instance) == -1) { + LOG(FATAL) << "Please supply a valid instance number with --instance="; + } + + operations_research::sat::LoadAndSolve(absl::GetFlag(FLAGS_input), + absl::GetFlag(FLAGS_instance)); + return EXIT_SUCCESS; +} diff --git a/examples/cpp/knapsack_2d_sat.cc b/examples/cpp/knapsack_2d_sat.cc new file mode 100644 index 0000000000..e9f34e87cc --- /dev/null +++ b/examples/cpp/knapsack_2d_sat.cc @@ -0,0 +1,238 @@ +// Copyright 2010-2021 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file solves a 2D Bin Packing problem as a 2D Knapsack problem. +// It loads the size of the mainrectangle, all available items (rectangles too), +// and tries to fit as many rectangles as possible in the main rectangle. + +#include +#include +#include +#include +#include + +#include "absl/flags/flag.h" +#include "absl/flags/parse.h" +#include "absl/flags/usage.h" +#include "google/protobuf/text_format.h" +#include "ortools/base/commandlineflags.h" +#include "ortools/base/logging.h" +#include "ortools/packing/binpacking_2d_parser.h" +#include "ortools/packing/multiple_dimensions_bin_packing.pb.h" +#include "ortools/sat/cp_model.h" +#include "ortools/sat/cp_model_solver.h" + +ABSL_FLAG(std::string, input, "", "Input file."); +ABSL_FLAG(int, instance, -1, "Instance number if the file."); +ABSL_FLAG(std::string, params, "", "Sat parameters in text proto format."); + +namespace operations_research { +namespace sat { + +// Logs the current cost and fills solution_in_ascii_form with a nice ascii +// drawing of the current solution. +void CheckAndPrint2DSolution( + const CpSolverResponse& response, + const packing::MultipleDimensionsBinPackingProblem& problem, + const std::vector>& intervals, + std::string* solution_in_ascii_form) { + const int num_items = problem.items_size(); + + const int64_t objective = response.objective_value(); + + const int size_x = problem.box_shape().dimensions(0); + const int size_y = problem.box_shape().dimensions(1); + const int64_t box_area = size_x * size_y; + std::vector> values(size_y); + for (int i = 0; i < size_y; ++i) { + values[i].resize(size_x, -1); + } + int64_t used_area = 0; + for (int item = 0; item < num_items; ++item) { + if (!SolutionBooleanValue(response, intervals[item][0].PresenceBoolVar())) { + continue; + } + const int64_t x = + SolutionIntegerValue(response, intervals[item][0].StartExpr()); + const int64_t y = + SolutionIntegerValue(response, intervals[item][1].StartExpr()); + const int64_t dx = + SolutionIntegerValue(response, intervals[item][0].SizeExpr()); + const int64_t dy = + SolutionIntegerValue(response, intervals[item][1].SizeExpr()); + used_area += dx * dy; + for (int i = x; i < x + dx; ++i) { + for (int j = y; j < y + dy; ++j) { + if (i >= size_x || j >= size_y) { + LOG(WARNING) << "Out of shape box: item = " << item << ", x = " << x + << ", y = " << y << ", dx = " << dx << ", dy = " << dy; + } else { + if (values[i][j] != -1) { + LOG(WARNING) << "Item " << item << " overlaps with item " + << values[i][j]; + } + values[i][j] = item; + } + } + } + } + + if (num_items - objective <= 1) { + LOG(INFO) << "Cost " << objective << ", " << num_items - objective + << " item selected, area used: " << used_area << "/" << box_area; + } else { + LOG(INFO) << "Cost " << objective << ", " << num_items - objective + << " items selected, area used: " << used_area << "/" << box_area; + } + + solution_in_ascii_form->clear(); + for (int j = 0; j < size_y; ++j) { + for (int i = 0; i < size_x; ++i) { + const int v = values[i][j]; + (*solution_in_ascii_form) += v == -1 ? ' ' : 'A' + v; + } + solution_in_ascii_form->append("\n"); + } +} + +// Load a 2d binpacking problem and solve it as a 2d knapsack problem. +// That is fit the max number of object in one box. +void LoadAndSolve(const std::string& file_name, int instance) { + packing::BinPacking2dParser parser; + if (!parser.Load2BPFile(file_name, instance)) { + LOG(FATAL) << "Cannot read instance " << instance << " from file " + << file_name; + } + packing::MultipleDimensionsBinPackingProblem problem = parser.problem(); + LOG(INFO) << "Successfully loaded instance " << instance << " from file " + << file_name; + LOG(INFO) << "Instance has " << problem.items_size() << " items"; + + const auto box_dimensions = problem.box_shape().dimensions(); + const int num_dimensions = box_dimensions.size(); + const int num_items = problem.items_size(); + + CpModelBuilder cp_model; + + // Selects the right shape for each item (plus nil shape if not selected). + // The nil shape is the first choice. + std::vector> selected(num_items); + for (int item = 0; item < num_items; ++item) { + const int num_shapes = problem.items(item).shapes_size(); + LOG(INFO) << " - item " << item << " has " << num_shapes << " shapes"; + selected[item].resize(num_shapes + 1); + for (int shape = 0; shape <= num_shapes; ++shape) { + selected[item][shape] = cp_model.NewBoolVar(); + } + } + + // Exactly one shape is selected for each item. + for (int item = 0; item < num_items; ++item) { + cp_model.AddEquality(LinearExpr::Sum(selected[item]), 1); + } + + // Manages positions and sizes for each item. + std::vector> intervals(num_items); + for (int item = 0; item < num_items; ++item) { + intervals[item].resize(num_dimensions); + const int num_shapes = problem.items(item).shapes_size(); + for (int dim = 0; dim < num_dimensions; ++dim) { + if (num_shapes == 1) { + const int64_t dimension = box_dimensions[dim]; + const int64_t size = problem.items(item).shapes(0).dimensions(dim); + IntVar start = cp_model.NewIntVar({0, dimension - size}); + intervals[item][dim] = cp_model.NewOptionalFixedSizeIntervalVar( + start, size, selected[item][1]); + } else { + const Domain dimension(0, box_dimensions[dim]); + IntVar start = cp_model.NewIntVar(dimension); + IntVar size = cp_model.NewIntVar(dimension); + IntVar end = cp_model.NewIntVar(dimension); + intervals[item][dim] = cp_model.NewIntervalVar(start, size, end); + + for (int shape = 0; shape <= num_shapes; ++shape) { + const int64_t item_size_in_dim = + shape == 0 + ? 0 + : problem.items(item).shapes(shape - 1).dimensions(dim); + cp_model.AddEquality(size, item_size_in_dim) + .OnlyEnforceIf(selected[item][shape]); + } + } + } + } + + // Non overlapping. + if (num_dimensions == 1) { + LOG(FATAL) << "One dimension is not supported."; + } else if (num_dimensions == 2) { + LOG(INFO) << "Box size: " << box_dimensions[0] << "*" << box_dimensions[1]; + NoOverlap2DConstraint no_overlap_2d = cp_model.AddNoOverlap2D(); + for (int item = 0; item < num_items; ++item) { + no_overlap_2d.AddRectangle(intervals[item][0], intervals[item][1]); + } + } else { + LOG(FATAL) << num_dimensions << " dimensions not supported."; + } + + // Objective. + LinearExpr objective; + for (int item_id = 0; item_id < num_items; ++item_id) { + objective += selected[item_id][0] * problem.items(item_id).value(); + } + cp_model.Minimize(objective); + + Model model; + // Setup parameters. + SatParameters parameters; + parameters.set_log_search_progress(true); + // Parse the --params flag. + if (!absl::GetFlag(FLAGS_params).empty()) { + CHECK(google::protobuf::TextFormat::MergeFromString( + absl::GetFlag(FLAGS_params), ¶meters)) + << absl::GetFlag(FLAGS_params); + } + model.Add(NewSatParameters(parameters)); + std::string solution_in_ascii_form; + model.Add(NewFeasibleSolutionObserver([&](const CpSolverResponse& r) { + if (num_dimensions == 2) { + CheckAndPrint2DSolution(r, problem, intervals, &solution_in_ascii_form); + } + })); + + const CpSolverResponse response = SolveCpModel(cp_model.Build(), &model); + LOG(INFO) << CpSolverResponseStats(response); + + if (!solution_in_ascii_form.empty()) { + LOG(INFO) << solution_in_ascii_form; + } +} + +} // namespace sat +} // namespace operations_research + +int main(int argc, char** argv) { + absl::SetFlag(&FLAGS_logtostderr, true); + google::InitGoogleLogging(argv[0]); + absl::ParseCommandLine(argc, argv); + if (absl::GetFlag(FLAGS_input).empty()) { + LOG(FATAL) << "Please supply a data file with --input="; + } + if (absl::GetFlag(FLAGS_instance) == -1) { + LOG(FATAL) << "Please supply a valid instance number with --instance="; + } + + operations_research::sat::LoadAndSolve(absl::GetFlag(FLAGS_input), + absl::GetFlag(FLAGS_instance)); + return EXIT_SUCCESS; +} diff --git a/ortools/packing/BUILD.bazel b/ortools/packing/BUILD.bazel index f9f7720e3f..c77730c2fa 100644 --- a/ortools/packing/BUILD.bazel +++ b/ortools/packing/BUILD.bazel @@ -60,3 +60,30 @@ cc_library( "@com_google_absl//absl/strings", ], ) + +### Multiple Dimension (non vector) Bin Packing ### + +proto_library( + name = "multiple_dimensions_bin_packing_proto", + srcs = ["multiple_dimensions_bin_packing.proto"], + visibility = ["//visibility:public"], +) + +cc_proto_library( + name = "multiple_dimensions_bin_packing_cc_proto", + visibility = ["//visibility:public"], + deps = [":multiple_dimensions_bin_packing_proto"], +) + +cc_library( + name = "binpacking_2d_parser", + srcs = ["binpacking_2d_parser.cc"], + hdrs = ["binpacking_2d_parser.h"], + visibility = ["//visibility:public"], + deps = [ + ":multiple_dimensions_bin_packing_cc_proto", + "//ortools/base", + "//ortools/base:filelineiter", + "@com_google_absl//absl/strings", + ], +) diff --git a/ortools/packing/binpacking_2d_parser.cc b/ortools/packing/binpacking_2d_parser.cc new file mode 100644 index 0000000000..27e05e7395 --- /dev/null +++ b/ortools/packing/binpacking_2d_parser.cc @@ -0,0 +1,96 @@ +// Copyright 2010-2021 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "ortools/packing/binpacking_2d_parser.h" + +#include "absl/strings/numbers.h" +#include "absl/strings/str_split.h" +#include "ortools/base/filelineiter.h" +#include "ortools/base/logging.h" + +namespace operations_research { +namespace packing { + +BinPacking2dParser::BinPacking2dParser() + : num_dimensions_(-1), + load_status_(NOT_STARTED), + num_items_(0), + instances_seen_(0) {} + +bool BinPacking2dParser::Load2BPFile(const std::string& file_name, + int instance) { + if (load_status_ != NOT_STARTED) { + return false; + } + + num_dimensions_ = 2; + + for (const std::string& line : FileLines(file_name)) { + ProcessNew2BpLine(line, instance); + if (load_status_ == PARSING_FINISHED) { + break; + } + } + return num_items_ == problem_.items_size() && num_items_ > 0; +} + +void BinPacking2dParser::ProcessNew2BpLine(const std::string& line, + int instance) { + const std::vector words = + absl::StrSplit(line, absl::ByAnyChar(" :\t\r"), absl::SkipEmpty()); + if (words.size() == 3 && words[1] == "PROBLEM" && words[2] == "CLASS") { + // New instance starting. + instances_seen_++; + if (load_status_ == NOT_STARTED && instances_seen_ == instance) { + load_status_ = INSTANCE_FOUND; + } else if (instances_seen_ > instance) { + load_status_ = PARSING_FINISHED; + } + } + + if (load_status_ == INSTANCE_FOUND) { + if (words.empty()) { + return; + } else if (words.size() == 2 || words[2] == "H(I),W(I),I=1,...,N") { + // Reading an item. + CHECK_NE(num_items_, 0); + CHECK_LT(problem_.items_size(), num_items_); + MultipleDimensionsBinPackingItem* item = problem_.add_items(); + MultipleDimensionsBinPackingShape* shape = item->add_shapes(); + int64_t dim; + CHECK(absl::SimpleAtoi(words[0], &dim)); + shape->add_dimensions(dim); + CHECK(absl::SimpleAtoi(words[1], &dim)); + shape->add_dimensions(dim); + item->set_value(1); + } else if (words[1] == "N.") { // Reading the number of item. + CHECK(absl::SimpleAtoi(words[0], &num_items_)); + } else if (words[2] == "RELATIVE") { + // Just double checking. + int local_instance; + CHECK(absl::SimpleAtoi(words[0], &local_instance)); + CHECK_EQ(local_instance, (instance - 1) % 10 + 1); + } else if (words[2] == "HBIN,WBIN") { + MultipleDimensionsBinPackingShape* box_shape = + problem_.mutable_box_shape(); + int64_t dim; + CHECK(absl::SimpleAtoi(words[0], &dim)); + box_shape->add_dimensions(dim); + CHECK(absl::SimpleAtoi(words[1], &dim)); + box_shape->add_dimensions(dim); + } + } +} + +} // namespace packing +} // namespace operations_research diff --git a/ortools/packing/binpacking_2d_parser.h b/ortools/packing/binpacking_2d_parser.h new file mode 100644 index 0000000000..6bbcb92e3c --- /dev/null +++ b/ortools/packing/binpacking_2d_parser.h @@ -0,0 +1,73 @@ +// Copyright 2010-2021 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef OR_TOOLS_PACKING_BINPACKING_2D_PARSER_H_ +#define OR_TOOLS_PACKING_BINPACKING_2D_PARSER_H_ + +#include +#include + +#include "ortools/base/integral_types.h" +#include "ortools/packing/multiple_dimensions_bin_packing.pb.h" + +namespace operations_research { +namespace packing { + +// A BinPacking parser. +// It supports the following file format: +// - 2bp: +// see http://or.dei.unibo.it/library/two-dimensional-bin-packing-problem +// - Binpacking with conflicts: +// see http://or.dei.unibo.it/library/bin-packing-problem-conflicts +// +// The generated problems have the following characteristics: +// +// You have one box with n dimensions. The size of the box is stored in the +// field box_shape(). +// You need to fit items into this box. Each item has the same number of +// dimensions and one or more possible shapes (this usually means that +// you can rotate the item). Each item has a value, and a possible list of +// conflicts (items you cannot put alongside this item). +// The objective of the problem is to fit as many items as possible in the box +// while maximizing the sum of values of selected items. For each item, you need +// to select the shape and the position of the item in the box. +// Each item must not overlap (in n dimensions) with any other item. +class BinPacking2dParser { + public: + BinPacking2dParser(); + + // Loads the 'instance'th instance of the bin packing problem if the given + // file. The instance are 1 based (first is 1). + // Only one call to a Load*() function is supported. All the subsequent + // calls will do nothing and return false. + bool Load2BPFile(const std::string& file_name, int instance); + MultipleDimensionsBinPackingProblem problem() const { return problem_; } + + private: + enum LoadStatus { NOT_STARTED = 0, INSTANCE_FOUND = 1, PARSING_FINISHED = 2 }; + + void ProcessNew2BpLine(const std::string& line, int instance); + + MultipleDimensionsBinPackingProblem problem_; + int num_dimensions_; + + // Temporary. + LoadStatus load_status_; + int num_items_; + int instances_seen_; +}; + +} // namespace packing +} // namespace operations_research + +#endif // OR_TOOLS_PACKING_BINPACKING_2D_PARSER_H_ diff --git a/ortools/packing/multiple_dimensions_bin_packing.proto b/ortools/packing/multiple_dimensions_bin_packing.proto new file mode 100644 index 0000000000..8dfee14138 --- /dev/null +++ b/ortools/packing/multiple_dimensions_bin_packing.proto @@ -0,0 +1,58 @@ +// Copyright 2010-2021 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package operations_research.packing; + +option java_multiple_files = true; + + +// A Multiple Dimension BinPacking problem. +// It supports the following file format: +// - 2bp: +// see http://or.dei.unibo.it/library/two-dimensional-bin-packing-problem +// +// The generated problems have the following characteristics: +// +// You have identical boxes with n dimensions. The size of one box is stored in +// the field box_shape(). +// You need to fit all items into the minimum number of boxes. Each item has the +// same number of dimensions and one or more possible shapes (this usually means +// that you can rotate the item). +// Each item must not overlap (in n dimensions) with any other item. + +// The shape of one item. each edge is parallel to one axis of a dimension. +// One shape cannot be rotated, the item itself will contain multiple rotated +// shapes. +message MultipleDimensionsBinPackingShape { + repeated int64 dimensions = 1; +} + +// Items with multiple shapes often means that they can be rotated. +message MultipleDimensionsBinPackingItem { + // All available shapes of the item. + repeated MultipleDimensionsBinPackingShape shapes = 1; + + // The value of the item (useful when solving the problem as a knapsack). + int64 value = 2; +} + +// The full problem: the enclosing box and the list of items. +message MultipleDimensionsBinPackingProblem { + // The enclosing shape. + MultipleDimensionsBinPackingShape box_shape = 1; + + // All available items of the problem. + repeated MultipleDimensionsBinPackingItem items = 2; +}