OR-Tools  9.2
arc_flow_solver.cc
Go to the documentation of this file.
1 // Copyright 2010-2021 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 <string>
17 
18 #include "absl/container/btree_map.h"
19 #include "absl/flags/flag.h"
21 #include "ortools/base/file.h"
22 #include "ortools/base/timer.h"
25 
26 ABSL_FLAG(std::string, arc_flow_dump_model, "",
27  "File to store the solver specific optimization proto.");
28 
29 namespace operations_research {
30 namespace packing {
31 
32 namespace {
33 double ConvertVectorBinPackingProblem(const vbp::VectorBinPackingProblem& input,
34  ArcFlowGraph* graph) {
35  WallTimer timer;
36  timer.Start();
37  const int num_items = input.item_size();
38  const int num_dims = input.resource_capacity_size();
39 
40  // Collect problem data.
41  std::vector<std::vector<int>> shapes(num_items);
42  std::vector<int> demands(num_items);
43  std::vector<int> capacities(num_dims);
44  for (int i = 0; i < num_items; ++i) {
45  shapes[i].assign(input.item(i).resource_usage().begin(),
46  input.item(i).resource_usage().end());
47  demands[i] = input.item(i).num_copies();
48  }
49  for (int i = 0; i < num_dims; ++i) {
50  capacities[i] = input.resource_capacity(i);
51  }
52 
53  // Add extra dimensions to encode max_number_of_copies_per_bin.
54  for (int i = 0; i < num_items; ++i) {
55  const int max_copies = input.item(i).max_number_of_copies_per_bin();
56  if (max_copies == 0 || max_copies >= demands[i]) continue;
57  capacities.push_back(max_copies);
58  for (int j = 0; j < num_items; ++j) {
59  shapes[j].push_back(i == j);
60  }
61  }
62 
63  *graph = BuildArcFlowGraph(capacities, shapes, demands);
64  const double arc_flow_time = timer.Get();
65 
66  VLOG(1) << "The arc-flow grah has " << graph->nodes.size() << " nodes, and "
67  << graph->arcs.size() << " arcs. It was created by exploring "
68  << graph->num_dp_states
69  << " states in the dynamic programming phase in " << arc_flow_time
70  << " s";
71  return arc_flow_time;
72 }
73 } // namespace
74 
76  const vbp::VectorBinPackingProblem& problem,
78  const std::string& mip_params, double time_limit, int num_threads,
79  int max_bins) {
80  ArcFlowGraph graph;
81  const double arc_flow_time = ConvertVectorBinPackingProblem(problem, &graph);
82 
83  int max_num_bins = 0;
84  if (max_bins > 0) {
85  max_num_bins = max_bins;
86  } else {
87  for (const auto& item : problem.item()) {
88  max_num_bins += item.num_copies();
89  }
90  }
91  const int num_types = problem.item_size();
92  std::vector<std::vector<MPVariable*>> incoming_vars(graph.nodes.size());
93  std::vector<std::vector<MPVariable*>> outgoing_vars(graph.nodes.size());
94  std::vector<MPVariable*> arc_to_var(graph.arcs.size());
95  std::vector<std::vector<MPVariable*>> item_to_vars(num_types);
96 
97  MPSolver solver("VectorBinPacking", solver_type);
98  CHECK_OK(solver.SetNumThreads(num_threads));
99 
100  for (int v = 0; v < graph.arcs.size(); ++v) {
101  const ArcFlowGraph::Arc& arc = graph.arcs[v];
102  MPVariable* const var =
103  solver.MakeIntVar(0, max_num_bins, absl::StrCat("a", v));
104  incoming_vars[arc.destination].push_back(var);
105  outgoing_vars[arc.source].push_back(var);
106  if (arc.item_index != -1) {
107  item_to_vars[arc.item_index].push_back(var);
108  }
109  arc_to_var[v] = var;
110  }
111 
112  // Per item demand constraint.
113  for (int i = 0; i < num_types; ++i) {
114  MPConstraint* const ct = solver.MakeRowConstraint(
115  problem.item(i).num_copies(), problem.item(i).num_copies());
116  for (MPVariable* const var : item_to_vars[i]) {
117  ct->SetCoefficient(var, 1.0);
118  }
119  }
120 
121  // Flow conservation.
122  for (int n = 1; n < graph.nodes.size() - 1; ++n) { // Ignore source and sink.
123  MPConstraint* const ct = solver.MakeRowConstraint(0.0, 0.0);
124  for (MPVariable* const var : incoming_vars[n]) {
125  ct->SetCoefficient(var, 1.0);
126  }
127  for (MPVariable* const var : outgoing_vars[n]) {
128  ct->SetCoefficient(var, -1.0);
129  }
130  }
131 
132  MPVariable* const obj_var = solver.MakeIntVar(0, max_num_bins, "obj_var");
133  { // Source.
134  MPConstraint* const ct = solver.MakeRowConstraint(0.0, 0.0);
135  for (MPVariable* const var : outgoing_vars[/*source*/ 0]) {
136  ct->SetCoefficient(var, 1.0);
137  }
138  ct->SetCoefficient(obj_var, -1.0);
139  }
140 
141  { // Sink.
142  MPConstraint* const ct = solver.MakeRowConstraint(0.0, 0.0);
143  const int sink_node = graph.nodes.size() - 1;
144  for (MPVariable* const var : incoming_vars[sink_node]) {
145  ct->SetCoefficient(var, 1.0);
146  }
147  ct->SetCoefficient(obj_var, -1.0);
148  }
149 
150  MPObjective* const objective = solver.MutableObjective();
151  objective->SetCoefficient(obj_var, 1.0);
152 
153  if (!absl::GetFlag(FLAGS_arc_flow_dump_model).empty()) {
154  MPModelProto output_model;
155  solver.ExportModelToProto(&output_model);
156  CHECK_OK(file::SetTextProto(absl::GetFlag(FLAGS_arc_flow_dump_model),
157  output_model, file::Defaults()));
158  }
159 
160  solver.EnableOutput();
161  solver.SetSolverSpecificParametersAsString(mip_params);
162  solver.SetTimeLimit(absl::Seconds(time_limit));
163  const MPSolver::ResultStatus result_status = solver.Solve();
164 
166  solution.set_solve_time_in_seconds(solver.wall_time() / 1000.0);
167  solution.set_arc_flow_time_in_seconds(arc_flow_time);
168  // Check that the problem has an optimal solution.
169  if (result_status == MPSolver::OPTIMAL) {
170  solution.set_status(vbp::OPTIMAL);
171  solution.set_objective_value(objective->Value());
172  } else if (result_status == MPSolver::FEASIBLE) {
173  solution.set_status(vbp::FEASIBLE);
174  solution.set_objective_value(objective->Value());
175  } else if (result_status == MPSolver::INFEASIBLE) {
176  solution.set_status(vbp::INFEASIBLE);
177  }
178 
179  if (result_status == MPSolver::OPTIMAL ||
180  result_status == MPSolver::FEASIBLE) {
181  // Create the arc flow graph with the flow quantity in each arc.
182  struct NextCountItem {
183  int next = -1;
184  int count = 0;
185  int item = -1;
186  };
187  std::vector<std::vector<NextCountItem>> node_to_next_count_item(
188  graph.nodes.size());
189  for (int v = 0; v < graph.arcs.size(); ++v) {
190  const int count =
191  static_cast<int>(std::round(arc_to_var[v]->solution_value()));
192  if (count == 0) continue;
193  const ArcFlowGraph::Arc& arc = graph.arcs[v];
194  node_to_next_count_item[arc.source].push_back(
195  {arc.destination, count, arc.item_index});
196  }
197 
198  // Unroll each possible path and rebuild bins.
199  struct NextItem {
200  int next = -1;
201  int item = -1;
202  };
203  const auto pop_next_item = [&node_to_next_count_item](int node) {
204  CHECK(!node_to_next_count_item[node].empty());
205  auto& [next, count, item] = node_to_next_count_item[node].back();
206  CHECK_NE(next, -1);
207  CHECK_GT(count, 0);
208  if (--count == 0) {
209  node_to_next_count_item[node].pop_back();
210  }
211  return NextItem({next, item});
212  };
213 
214  const int start_node = 0;
215  const int end_node = graph.nodes.size() - 1;
216  while (!node_to_next_count_item[start_node].empty()) {
217  absl::btree_map<int, int> item_count;
218  int current = start_node;
219  while (current != end_node) {
220  const auto& [next, item] = pop_next_item(current);
221  if (item != -1) {
222  item_count[item]++;
223  } else {
224  CHECK_EQ(next, end_node);
225  }
226  current = next;
227  }
229  for (const auto& [item, count] : item_count) {
230  bin->add_item_indices(item);
231  bin->add_item_copies(count);
232  }
233  }
234  }
235 
236  return solution;
237 }
238 
239 } // namespace packing
240 } // namespace operations_research
ResultStatus
The status of solving the problem.
#define CHECK(condition)
Definition: base/logging.h:495
MPVariable * MakeIntVar(double lb, double ub, const std::string &name)
Creates an integer variable.
double Get() const
Definition: timer.h:45
std::vector< std::vector< int > > nodes
vbp::VectorBinPackingSolution SolveVectorBinPackingWithArcFlow(const vbp::VectorBinPackingProblem &problem, MPSolver::OptimizationProblemType solver_type, const std::string &mip_params, double time_limit, int num_threads, int max_bins)
double Value() const
Returns the objective value of the best solution found so far.
ModelSharedTimeLimit * time_limit
#define CHECK_OK(x)
Definition: base/logging.h:44
absl::Status SetTextProto(const absl::string_view &filename, const google::protobuf::Message &proto, int flags)
Definition: base/file.cc:285
#define CHECK_GT(val1, val2)
Definition: base/logging.h:707
#define VLOG(verboselevel)
Definition: base/logging.h:983
void SetCoefficient(const MPVariable *const var, double coeff)
Sets the coefficient of the variable in the objective.
OptimizationProblemType
The type of problems (LP or MIP) that will be solved and the underlying solver (GLOP,...
A class to express a linear objective.
void Start()
Definition: timer.h:31
MPObjective * MutableObjective()
Returns the mutable objective object.
void set_status(::operations_research::packing::vbp::VectorBinPackingSolveStatus value)
The class for variables of a Mathematical Programming (MP) model.
bool SetSolverSpecificParametersAsString(const std::string &parameters)
Advanced usage: pass solver specific parameters in text format.
Block * next
ABSL_FLAG(std::string, arc_flow_dump_model, "", "File to store the solver specific optimization proto.")
int Defaults()
Definition: base/file.h:119
absl::Status SetNumThreads(int num_threads)
Sets the number of threads to use by the underlying solver.
static int input(yyscan_t yyscanner)
The class for constraints of a Mathematical Programming (MP) model.
feasible, or stopped by limit.
void EnableOutput()
Enables solver logging.
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:702
::operations_research::packing::vbp::VectorBinPackingOneBinInSolution * add_bins()
void SetTimeLimit(absl::Duration time_limit)
ResultStatus Solve()
Solves the problem using the default parameter values.
This mathematical programming (MP) solver class is the main class though which users build and solve ...
Collection of objects used to extend the Constraint Solver library.
IntVar * var
Definition: expr_array.cc:1874
void ExportModelToProto(MPModelProto *output_model) const
Exports model to protocol buffer.
ArcFlowGraph BuildArcFlowGraph(const std::vector< int > &bin_dimensions, const std::vector< std::vector< int >> &item_dimensions_by_type, const std::vector< int > &demand_by_type)
MPConstraint * MakeRowConstraint(double lb, double ub)
Creates a linear constraint with given bounds.
#define CHECK_NE(val1, val2)
Definition: base/logging.h:703
const Constraint * ct
const ::operations_research::packing::vbp::Item & item(int index) const