OR-Tools  9.1
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 "absl/flags/flag.h"
18#include "ortools/base/file.h"
19#include "ortools/base/timer.h"
21
22ABSL_FLAG(std::string, arc_flow_dump_model, "",
23 "File to store the solver specific optimization proto.");
24
25namespace operations_research {
26namespace packing {
27
28namespace {
29double ConvertVectorBinPackingProblem(const vbp::VectorBinPackingProblem& input,
30 ArcFlowGraph* graph) {
31 WallTimer timer;
32 timer.Start();
33 const int num_items = input.item_size();
34 const int num_dims = input.resource_capacity_size();
35
36 // Collect problem data.
37 std::vector<std::vector<int>> shapes(num_items);
38 std::vector<int> demands(num_items);
39 std::vector<int> capacities(num_dims);
40 for (int i = 0; i < num_items; ++i) {
41 shapes[i].assign(input.item(i).resource_usage().begin(),
42 input.item(i).resource_usage().end());
43 demands[i] = input.item(i).num_copies();
44 }
45 for (int i = 0; i < num_dims; ++i) {
46 capacities[i] = input.resource_capacity(i);
47 }
48
49 // Add extra dimensions to encode max_number_of_copies_per_bin.
50 for (int i = 0; i < num_items; ++i) {
51 const int max_copies = input.item(i).max_number_of_copies_per_bin();
52 if (max_copies == 0 || max_copies >= demands[i]) continue;
53 capacities.push_back(max_copies);
54 for (int j = 0; j < num_items; ++j) {
55 shapes[j].push_back(i == j);
56 }
57 }
58
59 *graph = BuildArcFlowGraph(capacities, shapes, demands);
60 const double arc_flow_time = timer.Get();
61
62 VLOG(1) << "The arc-flow grah has " << graph->nodes.size() << " nodes, and "
63 << graph->arcs.size() << " arcs. It was created by exploring "
64 << graph->num_dp_states
65 << " states in the dynamic programming phase in " << arc_flow_time
66 << " s";
67 return arc_flow_time;
68}
69} // namespace
70
72 const vbp::VectorBinPackingProblem& problem,
74 const std::string& mip_params, double time_limit, int num_threads,
75 bool log_statistics) {
76 ArcFlowGraph graph;
77 const double arc_flow_time = ConvertVectorBinPackingProblem(problem, &graph);
78
79 int max_num_bins = 0;
80 for (const auto& item : problem.item()) {
81 max_num_bins += item.num_copies();
82 }
83 const int num_types = problem.item_size();
84 std::vector<std::vector<MPVariable*>> incoming_vars(graph.nodes.size());
85 std::vector<std::vector<MPVariable*>> outgoing_vars(graph.nodes.size());
86 std::vector<MPVariable*> arc_to_var(graph.arcs.size());
87 std::vector<std::vector<MPVariable*>> item_to_vars(num_types);
88
89 MPSolver solver("VectorBinPacking", solver_type);
90 CHECK_OK(solver.SetNumThreads(num_threads));
91
92 for (int v = 0; v < graph.arcs.size(); ++v) {
93 const ArcFlowGraph::Arc& arc = graph.arcs[v];
94 MPVariable* const var =
95 solver.MakeIntVar(0, max_num_bins, absl::StrCat("a", v));
96 incoming_vars[arc.destination].push_back(var);
97 outgoing_vars[arc.source].push_back(var);
98 if (arc.item_index != -1) {
99 item_to_vars[arc.item_index].push_back(var);
100 }
101 }
102
103 // Per item demand constraint.
104 for (int i = 0; i < num_types; ++i) {
105 MPConstraint* const ct = solver.MakeRowConstraint(
106 problem.item(i).num_copies(), problem.item(i).num_copies());
107 for (MPVariable* const var : item_to_vars[i]) {
108 ct->SetCoefficient(var, 1.0);
109 }
110 }
111
112 // Flow conservation.
113 for (int n = 1; n < graph.nodes.size() - 1; ++n) { // Ignore source and sink.
114 MPConstraint* const ct = solver.MakeRowConstraint(0.0, 0.0);
115 for (MPVariable* const var : incoming_vars[n]) {
116 ct->SetCoefficient(var, 1.0);
117 }
118 for (MPVariable* const var : outgoing_vars[n]) {
119 ct->SetCoefficient(var, -1.0);
120 }
121 }
122
123 MPVariable* const obj_var = solver.MakeIntVar(0, max_num_bins, "obj_var");
124 { // Source.
125 MPConstraint* const ct = solver.MakeRowConstraint(0.0, 0.0);
126 for (MPVariable* const var : outgoing_vars[/*source*/ 0]) {
127 ct->SetCoefficient(var, 1.0);
128 }
129 ct->SetCoefficient(obj_var, -1.0);
130 }
131
132 { // Sink.
133 MPConstraint* const ct = solver.MakeRowConstraint(0.0, 0.0);
134 const int sink_node = graph.nodes.size() - 1;
135 for (MPVariable* const var : incoming_vars[sink_node]) {
136 ct->SetCoefficient(var, 1.0);
137 }
138 ct->SetCoefficient(obj_var, -1.0);
139 }
140
141 MPObjective* const objective = solver.MutableObjective();
142 objective->SetCoefficient(obj_var, 1.0);
143
144 if (!absl::GetFlag(FLAGS_arc_flow_dump_model).empty()) {
145 MPModelProto output_model;
146 solver.ExportModelToProto(&output_model);
147 CHECK_OK(file::SetTextProto(absl::GetFlag(FLAGS_arc_flow_dump_model),
148 output_model, file::Defaults()));
149 }
150
151 solver.EnableOutput();
152 solver.SetSolverSpecificParametersAsString(mip_params);
153 solver.SetTimeLimit(absl::Seconds(time_limit));
154 const MPSolver::ResultStatus result_status = solver.Solve();
155
157 solution.set_solve_time_in_seconds(solver.wall_time() / 1000.0);
158 solution.set_arc_flow_time_in_seconds(arc_flow_time);
159 // Check that the problem has an optimal solution.
160 if (result_status == MPSolver::OPTIMAL) {
161 solution.set_status(vbp::OPTIMAL);
162 solution.set_objective_value(objective->Value());
163 } else if (result_status == MPSolver::FEASIBLE) {
164 solution.set_status(vbp::FEASIBLE);
165 solution.set_objective_value(objective->Value());
166 } else if (result_status == MPSolver::INFEASIBLE) {
167 solution.set_status(vbp::INFEASIBLE);
168 }
169
170 // TODO(user): Fill bins in the solution proto.
171
172 if (log_statistics) {
173 const bool has_solution = result_status == MPSolver::OPTIMAL ||
174 result_status == MPSolver::FEASIBLE;
175 absl::PrintF("%-12s: %s\n", "Status",
177 static_cast<MPSolverResponseStatus>(result_status))
178 .c_str());
179 absl::PrintF("%-12s: %15.15e\n", "Objective",
180 has_solution ? solver.Objective().Value() : 0.0);
181 absl::PrintF("%-12s: %15.15e\n", "BestBound",
182 has_solution ? solver.Objective().BestBound() : 0.0);
183 absl::PrintF("%-12s: %d\n", "Iterations", solver.iterations());
184 absl::PrintF("%-12s: %d\n", "Nodes", solver.nodes());
185 absl::PrintF("%-12s: %-6.4g\n", "Time", solution.solve_time_in_seconds());
186 }
187
188 return solution;
189}
190
191} // namespace packing
192} // namespace operations_research
ABSL_FLAG(std::string, arc_flow_dump_model, "", "File to store the solver specific optimization proto.")
#define CHECK_OK(x)
Definition: base/logging.h:42
#define VLOG(verboselevel)
Definition: base/logging.h:979
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.
double BestBound() const
Returns the best objective bound.
This mathematical programming (MP) solver class is the main class though which users build and solve ...
int64_t iterations() const
Returns the number of simplex iterations.
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.
int64_t nodes() const
Returns the number of branch-and-bound nodes evaluated during the solve.
MPVariable * MakeIntVar(double lb, double ub, const std::string &name)
Creates an integer variable.
const MPObjective & Objective() const
Returns the objective object.
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.
::PROTOBUF_NAMESPACE_ID::int32 num_copies() const
const ::operations_research::packing::vbp::Item & item(int index) const
void set_status(::operations_research::packing::vbp::VectorBinPackingSolveStatus value)
ModelSharedTimeLimit * time_limit
const Constraint * ct
IntVar * var
Definition: expr_array.cc:1874
int Defaults()
Definition: base/file.h:119
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, bool log_statistics)
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.
const std::string & MPSolverResponseStatus_Name(T enum_t_value)
static int input(yyscan_t yyscanner)
std::vector< std::vector< int > > nodes