OR-Tools  9.3
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"
24#include "ortools/packing/vector_bin_packing.pb.h"
25
26ABSL_FLAG(std::string, arc_flow_dump_model, "",
27 "File to store the solver specific optimization proto.");
28
29namespace operations_research {
30namespace packing {
31
32namespace {
33double 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
75vbp::VectorBinPackingSolution SolveVectorBinPackingWithArcFlow(
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
165 vbp::VectorBinPackingSolution solution;
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 }
228 vbp::VectorBinPackingOneBinInSolution* bin = solution.add_bins();
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
ABSL_FLAG(std::string, arc_flow_dump_model, "", "File to store the solver specific optimization proto.")
#define CHECK(condition)
Definition: base/logging.h:495
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:703
#define CHECK_GT(val1, val2)
Definition: base/logging.h:708
#define CHECK_OK(x)
Definition: base/logging.h:44
#define CHECK_NE(val1, val2)
Definition: base/logging.h:704
#define VLOG(verboselevel)
Definition: base/logging.h:984
void Start()
Definition: timer.h:31
double Get() const
Definition: timer.h:45
The class for constraints of a Mathematical Programming (MP) model.
A class to express a linear objective.
void SetCoefficient(const MPVariable *const var, double coeff)
Sets the coefficient of the variable in the objective.
double Value() const
Returns the objective value of the best solution found so far.
This mathematical programming (MP) solver class is the main class though which users build and solve ...
MPConstraint * MakeRowConstraint(double lb, double ub)
Creates a linear constraint with given bounds.
ResultStatus
The status of solving the problem.
@ FEASIBLE
feasible, or stopped by limit.
@ INFEASIBLE
proven infeasible.
OptimizationProblemType
The type of problems (LP or MIP) that will be solved and the underlying solver (GLOP,...
bool SetSolverSpecificParametersAsString(const std::string &parameters)
Advanced usage: pass solver specific parameters in text format.
absl::Status SetNumThreads(int num_threads)
Sets the number of threads to use by the underlying solver.
void ExportModelToProto(MPModelProto *output_model) const
Exports model to protocol buffer.
MPVariable * MakeIntVar(double lb, double ub, const std::string &name)
Creates an integer variable.
ResultStatus Solve()
Solves the problem using the default parameter values.
void EnableOutput()
Enables solver logging.
MPObjective * MutableObjective()
Returns the mutable objective object.
void SetTimeLimit(absl::Duration time_limit)
The class for variables of a Mathematical Programming (MP) model.
Block * next
ModelSharedTimeLimit * time_limit
const Constraint * ct
IntVar * var
Definition: expr_array.cc:1874
int arc
int Defaults()
Definition: base/file.h:120
absl::Status SetTextProto(const absl::string_view &filename, const google::protobuf::Message &proto, int flags)
Definition: base/file.cc:285
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)
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)
Collection of objects used to extend the Constraint Solver library.
static int input(yyscan_t yyscanner)
std::vector< std::vector< int > > nodes