22 #include "absl/flags/parse.h"
23 #include "absl/flags/usage.h"
25 #include "absl/container/flat_hash_map.h"
26 #include "absl/flags/flag.h"
27 #include "absl/random/random.h"
28 #include "absl/random/uniform_int_distribution.h"
29 #include "absl/status/statusor.h"
30 #include "absl/strings/str_format.h"
31 #include "absl/strings/string_view.h"
32 #include "absl/time/clock.h"
33 #include "absl/time/time.h"
36 ABSL_FLAG(
int, num_facilities, 3000,
"Number of facilities.");
37 ABSL_FLAG(
int, num_locations, 50,
"Number of locations.");
38 ABSL_FLAG(
double, edge_probability, 0.99,
"Edge probability.");
39 ABSL_FLAG(
double, benders_precission, 1e-9,
"Benders target precission.");
40 ABSL_FLAG(
double, location_demand, 1,
"Client demands.");
41 ABSL_FLAG(
double, facility_cost, 100,
"Facility capacity cost.");
43 double, location_fraction, 0.001,
44 "Fraction of a facility's capacity that can be used by each location.");
47 using ::operations_research::math_opt::GurobiParametersProto;
48 using ::operations_research::math_opt::LinearConstraint;
49 using ::operations_research::math_opt::LinearExpression;
50 using ::operations_research::math_opt::MathOpt;
51 using ::operations_research::math_opt::Objective;
52 using ::operations_research::math_opt::Result;
53 using ::operations_research::math_opt::SolveParametersProto;
54 using ::operations_research::math_opt::SolveResultProto;
56 using ::operations_research::math_opt::Variable;
59 using Edge = std::pair<int, int>;
64 Network(
int num_facilities,
int num_locations,
double edge_probability);
66 int num_facilities()
const {
return num_facilities_; }
67 int num_locations()
const {
return num_locations_; }
69 const std::vector<Edge>& edges()
const {
return edges_; }
71 const std::vector<Edge>& edges_incident_to_facility(
72 const int facility)
const {
73 return facility_edge_incidence_[facility];
76 const std::vector<Edge>& edges_incident_to_location(
77 const int location)
const {
78 return location_edge_incidence_[location];
81 double edge_cost(
const Edge& edge)
const {
return edge_costs_.at(edge); }
87 std::vector<Edge> edges_;
88 absl::flat_hash_map<Edge, double> edge_costs_;
89 std::vector<std::vector<Edge>> facility_edge_incidence_;
90 std::vector<std::vector<Edge>> location_edge_incidence_;
93 Network::Network(
const int num_facilities,
const int num_locations,
94 const double edge_probability) {
97 num_facilities_ = num_facilities;
98 num_locations_ = num_locations;
99 facility_edge_incidence_ =
100 std::vector<std::vector<std::pair<int, int>>>(num_facilities);
101 location_edge_incidence_ = std::vector<std::vector<std::pair<int, int>>>(
102 num_locations, std::vector<std::pair<int, int>>());
103 for (
int facility = 0; facility < num_facilities_; ++facility) {
104 for (
int location = 0; location < num_locations_; ++location) {
105 if (absl::Bernoulli(bitgen, edge_probability)) {
106 const std::pair<int, int> edge({facility, location});
107 facility_edge_incidence_[facility].push_back(edge);
108 location_edge_incidence_[location].push_back(edge);
109 edges_.push_back(edge);
110 edge_costs_.insert({edge, absl::Uniform(bitgen, 0, 1.0)});
116 for (
int facility = 0; facility < num_facilities; ++facility) {
117 auto& locations = facility_edge_incidence_[facility];
118 if (locations.empty()) {
120 absl::uniform_int_distribution<int>(0, num_locations - 1)(bitgen);
121 const std::pair<int, int> edge({facility, location});
122 locations.push_back(edge);
123 location_edge_incidence_[location].push_back(edge);
124 edges_.push_back(edge);
125 edge_costs_.insert({edge, absl::Uniform(bitgen, 0, 1.0)});
128 for (
int location = 0; location < num_locations; ++location) {
129 auto& facilities = location_edge_incidence_[location];
130 if (facilities.empty()) {
132 absl::uniform_int_distribution<int>(0, num_facilities - 1)(bitgen);
133 const std::pair<int, int> edge({facility, location});
134 facilities.push_back(edge);
135 facility_edge_incidence_[facility].push_back(edge);
136 edges_.push_back(edge);
137 edge_costs_.insert({edge, absl::Uniform(bitgen, 0, 1.0)});
142 constexpr
double kInf = std::numeric_limits<double>::infinity();
173 void FullProblem(
const Network& network,
const double location_demand,
174 const double facility_cost,
const double location_fraction) {
175 const int num_facilities = network.num_facilities();
176 const int num_locations = network.num_locations();
178 MathOpt
model(operations_research::math_opt::SOLVER_TYPE_GUROBI,
179 "Full network design problem");
180 const Objective objective =
model.objective();
181 objective.set_minimize();
184 std::vector<Variable> z;
185 for (
int j = 0; j < num_facilities; j++) {
186 const Variable z_j =
model.AddContinuousVariable(0.0,
kInf);
188 objective.set_linear_coefficient(z_j, facility_cost);
192 absl::flat_hash_map<Edge, Variable> x;
193 for (
const auto& edge : network.edges()) {
194 const Variable x_edge =
model.AddContinuousVariable(0.0,
kInf);
195 x.insert({edge, x_edge});
196 objective.set_linear_coefficient(x_edge, network.edge_cost(edge));
200 for (
int location = 0; location < num_locations; ++location) {
201 LinearExpression linear_expression;
202 for (
const auto& edge : network.edges_incident_to_location(location)) {
203 linear_expression += x.at(edge);
205 model.AddLinearConstraint(linear_expression >= location_demand);
209 for (
int facility = 0; facility < num_facilities; ++facility) {
210 LinearExpression linear_expression;
211 for (
const auto& edge : network.edges_incident_to_facility(facility)) {
212 linear_expression += x.at(edge);
214 model.AddLinearConstraint(linear_expression <= z[facility]);
218 for (
int facility = 0; facility < num_facilities; ++facility) {
219 for (
const auto& edge : network.edges_incident_to_facility(facility)) {
220 model.AddLinearConstraint(x.at(edge) <= location_fraction * z[facility]);
223 const Result result =
model.Solve(SolveParametersProto()).value();
224 for (
const auto& warning : result.warnings) {
225 LOG(
WARNING) <<
"Solver warning: " << warning << std::endl;
228 <<
"Failed to find an optimal solution: " << result.termination_detail;
229 std::cout <<
"Full problem optimal objective: "
230 << absl::StrFormat(
"%.9f", result.objective_value()) << std::endl;
233 void Benders(
const Network network,
const double location_demand,
234 const double facility_cost,
const double location_fraction,
235 const double target_precission,
236 const int maximum_iterations = 30000) {
237 const int num_facilities = network.num_facilities();
238 const int num_locations = network.num_locations();
247 MathOpt first_stage_model(operations_research::math_opt::SOLVER_TYPE_GUROBI,
248 "First stage problem");
249 std::vector<Variable> z;
250 for (
int j = 0; j < num_facilities; j++) {
251 z.push_back(first_stage_model.AddContinuousVariable(0.0,
kInf));
254 const Variable w = first_stage_model.AddContinuousVariable(0.0,
kInf);
256 first_stage_model.objective().Minimize(facility_cost *
Sum(z) + w);
258 SolveParametersProto first_stage_params;
259 first_stage_params.mutable_common_parameters()->set_enable_output(
false);
270 MathOpt second_stage_model(operations_research::math_opt::SOLVER_TYPE_GUROBI,
271 "Second stage model");
272 const Objective second_stage_objective = second_stage_model.objective();
273 second_stage_objective.set_minimize();
274 absl::flat_hash_map<Edge, Variable> x;
275 for (
const auto& edge : network.edges()) {
276 const Variable x_edge = second_stage_model.AddContinuousVariable(0.0,
kInf);
277 x.insert({edge, x_edge});
278 second_stage_objective.set_linear_coefficient(x_edge,
279 network.edge_cost(edge));
281 std::vector<LinearConstraint> demand_constraints;
283 for (
int location = 0; location < num_locations; ++location) {
284 LinearExpression linear_expression;
285 for (
const auto& edge : network.edges_incident_to_location(location)) {
286 linear_expression += x.at(edge);
288 demand_constraints.push_back(second_stage_model.AddLinearConstraint(
289 linear_expression >= location_demand));
291 std::vector<LinearConstraint> supply_constraints;
292 for (
int facility = 0; facility < num_facilities; ++facility) {
293 LinearExpression linear_expression;
294 for (
const auto& edge : network.edges_incident_to_facility(facility)) {
295 linear_expression += x.at(edge);
297 supply_constraints.push_back(
298 second_stage_model.AddLinearConstraint(linear_expression <=
kInf));
301 SolveParametersProto second_stage_params;
302 second_stage_params.mutable_common_parameters()->set_enable_output(
false);
303 GurobiParametersProto::Parameter* param1 =
304 second_stage_params.mutable_gurobi_parameters()->add_parameters();
305 param1->set_name(
"InfUnbdInfo");
306 param1->set_value(
"1");
310 double best_upper_bound =
kInf;
312 LOG(
INFO) <<
"Iteration: " << iteration;
314 const Result first_stage_result =
315 first_stage_model.Solve(first_stage_params).value();
316 for (
const auto& warning : first_stage_result.warnings) {
317 LOG(
WARNING) <<
"Solver warning: " << warning << std::endl;
320 const double lower_bound = first_stage_result.objective_value();
324 for (
int facility = 0; facility < num_facilities; ++facility) {
325 const double capacity_value =
326 first_stage_result.variable_values().at(z[facility]);
327 for (
const auto& edge : network.edges_incident_to_facility(facility)) {
328 x.at(edge).set_upper_bound(location_fraction * capacity_value);
330 supply_constraints[facility].set_upper_bound(capacity_value);
334 const Result second_stage_result =
335 second_stage_model.Solve(second_stage_params).value();
336 for (
const auto& warning : second_stage_result.warnings) {
337 LOG(
WARNING) <<
"Solver warning: " << warning << std::endl;
339 if (second_stage_result.termination_reason ==
357 LOG(
INFO) <<
"Adding feasibility cut...";
358 LinearExpression feasibility_cut_expression;
359 for (
int facility = 0; facility < num_facilities; ++facility) {
361 for (
const auto& edge : network.edges_incident_to_facility(facility)) {
362 const double reduced_cost =
363 second_stage_result.ray_reduced_costs().at(x.at(edge));
364 if (reduced_cost < 0) {
368 double dual_value = second_stage_result.ray_dual_values().at(
369 supply_constraints[facility]);
371 feasibility_cut_expression +=
coefficient * z[facility];
373 double constant = 0.0;
374 for (
const auto& constraint : demand_constraints) {
376 second_stage_result.ray_dual_values().at(constraint);
377 if (dual_value > 0) {
378 constant += dual_value;
381 first_stage_model.AddLinearConstraint(
382 feasibility_cut_expression + constant <= 0.0);
400 QCHECK_EQ(second_stage_result.termination_reason,
402 LOG(
INFO) <<
"Adding optimality cut...";
403 LinearExpression optimality_cut_expression;
405 for (
int facility = 0; facility < num_facilities; ++facility) {
407 for (
const auto& edge : network.edges_incident_to_facility(facility)) {
409 second_stage_result.variable_values().at(x.at(edge));
410 const double reduced_cost =
411 second_stage_result.reduced_costs().at(x.at(edge));
412 if (reduced_cost < 0) {
417 second_stage_result.dual_values().at(supply_constraints[facility]);
419 optimality_cut_expression +=
coefficient * z[facility];
421 double constant = 0.0;
422 for (
const auto& constraint : demand_constraints) {
423 double dual_value = second_stage_result.dual_values().at(constraint);
424 if (dual_value > 0) {
425 constant += dual_value;
429 upper_bound += first_stage_result.objective_value() -
430 first_stage_result.variable_values().at(w);
433 first_stage_model.AddLinearConstraint(
434 optimality_cut_expression + constant <= w);
436 LOG(
INFO) <<
"UB = " << best_upper_bound;
438 if (best_upper_bound -
lower_bound < target_precission) {
439 std::cout <<
"Total iterations = " << iteration << std::endl;
440 std::cout <<
"Final LB = " << absl::StrFormat(
"%.9f",
lower_bound)
442 std::cout <<
"Final UB = " << absl::StrFormat(
"%.9f", best_upper_bound)
446 if (iteration > maximum_iterations) {
454 int main(
int argc,
char** argv) {
456 absl::ParseCommandLine(argc, argv);
457 const Network network(absl::GetFlag(FLAGS_num_facilities),
458 absl::GetFlag(FLAGS_num_locations),
459 absl::GetFlag(FLAGS_edge_probability));
460 absl::Time start = absl::Now();
461 FullProblem(network, absl::GetFlag(FLAGS_location_demand),
462 absl::GetFlag(FLAGS_facility_cost),
463 absl::GetFlag(FLAGS_location_fraction));
464 std::cout <<
"Full solve time : " << absl::Now() - start << std::endl;
466 Benders(network, absl::GetFlag(FLAGS_location_demand),
467 absl::GetFlag(FLAGS_facility_cost),
468 absl::GetFlag(FLAGS_location_fraction),
469 absl::GetFlag(FLAGS_benders_precission));
470 std::cout <<
"Benders solve time : " << absl::Now() - start << std::endl;
int main(int argc, char **argv)
ABSL_FLAG(int, num_facilities, 3000, "Number of facilities.")
void InitGoogleLogging(const char *argv0)
LinearExpression Sum(const Iterable &items)