OR-Tools  9.3
facility_lp_benders.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
14// An advanced benders decomposition example
15//
16// We consider a network design problem where each location has a demand that
17// must be met by its neighboring facilities, and each facility can control
18// its total capacity. In this version we also require that locations cannot
19// use more that a specified fraction of a facilities capacity.
20//
21// Problem data:
22// * F: set of facilities.
23// * L: set of locations.
24// * E: subset of {(f,l) : f in F, l in L} that describes the network between
25// facilities and locations.
26// * d: demand at location (all demands are equal for simplicity).
27// * c: cost per unit of capacity at a facility (all facilities are have the
28// same cost for simplicity).
29// * h: cost per unit transported through an edge.
30// * a: fraction of a facility's capacity that can be used by each location.
31//
32// Decision variables:
33// * z_f: capacity at facility f in F.
34// * x_(f,l): flow from facility f to location l for all (f,l) in E.
35//
36// Formulation:
37//
38// min c * sum(z_f : f in F) + sum(h_e * x_e : e in E)
39// s.t.
40// x_(f,l) <= a * z_f for all (f,l) in E
41// sum(x_(f,l) : l such that (f,l) in E) <= z_f for all f in F
42// sum(x_(f,l) : f such that (f,l) in E) >= d for all l in L
43// x_e >= 0 for all e in E
44// z_f >= 0 for all f in F
45//
46// Below we solve this problem directly and using a benders decompostion
47// approach.
48
49#include <algorithm>
50#include <iostream>
51#include <limits>
52#include <utility>
53#include <vector>
54
55#include "absl/container/flat_hash_map.h"
56#include "absl/flags/flag.h"
57#include "absl/random/random.h"
58#include "absl/random/seed_sequences.h"
59#include "absl/random/uniform_int_distribution.h"
60#include "absl/status/status.h"
61#include "absl/status/statusor.h"
62#include "absl/strings/str_format.h"
63#include "absl/strings/string_view.h"
64#include "absl/time/clock.h"
65#include "absl/time/time.h"
71
72ABSL_FLAG(int, num_facilities, 3000, "Number of facilities.");
73ABSL_FLAG(int, num_locations, 50, "Number of locations.");
74ABSL_FLAG(double, edge_probability, 0.99, "Edge probability.");
75ABSL_FLAG(double, benders_precission, 1e-9, "Benders target precission.");
76ABSL_FLAG(double, location_demand, 1, "Client demands.");
77ABSL_FLAG(double, facility_cost, 100, "Facility capacity cost.");
79 double, location_fraction, 0.001,
80 "Fraction of a facility's capacity that can be used by each location.");
83 "the LP solver to use, possible values: glop, gurobi, glpk, pdlp");
84
85namespace {
86
88constexpr double kInf = std::numeric_limits<double>::infinity();
89constexpr double kZeroTol = 1.0e-3;
90
92// Facility location instance representation and generation
94
95// First element is a facility and second is a location.
96using Edge = std::pair<int, int>;
97
98// A simple randomly-generated facility-location network.
99class Network {
100 public:
101 Network(int num_facilities, int num_locations, double edge_probability);
102
103 int num_facilities() const { return num_facilities_; }
104 int num_locations() const { return num_locations_; }
105
106 const std::vector<Edge>& edges() const { return edges_; }
107
108 const std::vector<Edge>& edges_incident_to_facility(
109 const int facility) const {
110 return facility_edge_incidence_[facility];
111 }
112
113 const std::vector<Edge>& edges_incident_to_location(
114 const int location) const {
115 return location_edge_incidence_[location];
116 }
117
118 double edge_cost(const Edge& edge) const { return edge_costs_.at(edge); }
119
120 private:
121 int num_facilities_;
122 int num_locations_;
123 // No order is assumed for the following lists of edges.
124 std::vector<Edge> edges_;
125 absl::flat_hash_map<Edge, double> edge_costs_;
126 std::vector<std::vector<Edge>> facility_edge_incidence_;
127 std::vector<std::vector<Edge>> location_edge_incidence_;
128};
129
130Network::Network(const int num_facilities, const int num_locations,
131 const double edge_probability) {
132 absl::SeedSeq seq({1, 2, 3});
133 absl::BitGen bitgen(seq);
134
135 num_facilities_ = num_facilities;
136 num_locations_ = num_locations;
137 facility_edge_incidence_ =
138 std::vector<std::vector<std::pair<int, int>>>(num_facilities);
139 location_edge_incidence_ = std::vector<std::vector<std::pair<int, int>>>(
140 num_locations, std::vector<std::pair<int, int>>());
141 for (int facility = 0; facility < num_facilities_; ++facility) {
142 for (int location = 0; location < num_locations_; ++location) {
143 if (absl::Bernoulli(bitgen, edge_probability)) {
144 const std::pair<int, int> edge({facility, location});
145 facility_edge_incidence_[facility].push_back(edge);
146 location_edge_incidence_[location].push_back(edge);
147 edges_.push_back(edge);
148 edge_costs_.insert({edge, absl::Uniform(bitgen, 0, 1.0)});
149 }
150 }
151 }
152 // Ensure every facility is connected to at least one location and every
153 // location is connected to at least one facility.
154 for (int facility = 0; facility < num_facilities; ++facility) {
155 auto& locations = facility_edge_incidence_[facility];
156 if (locations.empty()) {
157 const int location =
158 absl::uniform_int_distribution<int>(0, num_locations - 1)(bitgen);
159 const std::pair<int, int> edge({facility, location});
160 locations.push_back(edge);
161 location_edge_incidence_[location].push_back(edge);
162 edges_.push_back(edge);
163 edge_costs_.insert({edge, absl::Uniform(bitgen, 0, 1.0)});
164 }
165 }
166 for (int location = 0; location < num_locations; ++location) {
167 auto& facilities = location_edge_incidence_[location];
168 if (facilities.empty()) {
169 const int facility =
170 absl::uniform_int_distribution<int>(0, num_facilities - 1)(bitgen);
171 const std::pair<int, int> edge({facility, location});
172 facilities.push_back(edge);
173 facility_edge_incidence_[facility].push_back(edge);
174 edges_.push_back(edge);
175 edge_costs_.insert({edge, absl::Uniform(bitgen, 0, 1.0)});
176 }
177 }
178}
179
180struct FacilityLocationInstance {
181 Network network;
182 double location_demand;
183 double facility_cost;
184 double location_fraction;
185};
186
188// Direct solve
190
191// See file level comment for problem description and formulation.
192absl::Status FullProblem(const FacilityLocationInstance& instance,
193 const math_opt::SolverType solver_type) {
194 const int num_facilities = instance.network.num_facilities();
195 const int num_locations = instance.network.num_locations();
196
197 math_opt::Model model("Full network design problem");
198
199 // Capacity variables
200 std::vector<math_opt::Variable> z;
201 for (int j = 0; j < num_facilities; j++) {
202 z.push_back(model.AddContinuousVariable(0.0, kInf));
203 }
204
205 // Flow variables
206 absl::flat_hash_map<Edge, math_opt::Variable> x;
207 for (const auto& edge : instance.network.edges()) {
208 const math_opt::Variable x_edge = model.AddContinuousVariable(0.0, kInf);
209 x.insert({edge, x_edge});
210 }
211
212 // Objective Function
213 math_opt::LinearExpression objective_for_edges;
214 for (const auto& [edge, x_edge] : x) {
215 objective_for_edges += instance.network.edge_cost(edge) * x_edge;
216 }
217 model.Minimize(objective_for_edges +
218 instance.facility_cost * math_opt::Sum(z));
219
220 // Demand constraints
221 for (int location = 0; location < num_locations; ++location) {
222 math_opt::LinearExpression incomming_supply;
223 for (const auto& edge :
224 instance.network.edges_incident_to_location(location)) {
225 incomming_supply += x.at(edge);
226 }
227 model.AddLinearConstraint(incomming_supply >= instance.location_demand);
228 }
229
230 // Supply constraints
231 for (int facility = 0; facility < num_facilities; ++facility) {
232 math_opt::LinearExpression outgoing_supply;
233 for (const auto& edge :
234 instance.network.edges_incident_to_facility(facility)) {
235 outgoing_supply += x.at(edge);
236 }
237 model.AddLinearConstraint(outgoing_supply <= z[facility]);
238 }
239
240 // Arc constraints
241 for (int facility = 0; facility < num_facilities; ++facility) {
242 for (const auto& edge :
243 instance.network.edges_incident_to_facility(facility)) {
244 model.AddLinearConstraint(x.at(edge) <=
245 instance.location_fraction * z[facility]);
246 }
247 }
249 math_opt::Solve(model, solver_type));
250 if (result.termination.reason != math_opt::TerminationReason::kOptimal) {
252 << "failed to find an optimal solution: " << result.termination;
253 }
254
255 std::cout << "Full problem optimal objective: "
256 << absl::StrFormat("%.9f", result.objective_value()) << std::endl;
257 return absl::OkStatus();
258}
259
261// Benders solver
263
264// Setup first stage model:
265//
266// min c * sum(z_f : f in F) + w
267// s.t.
268// z_f >= 0 for all f in F
269// sum(fcut_f^i z_f) + fcut_const^i <= 0 for i = 1,...
270// sum(ocut_f^j z_f) + ocut_const^j <= w for j = 1,...
271struct FirstStageProblem {
273 std::vector<math_opt::Variable> z;
275
276 FirstStageProblem(const Network& network, const double facility_cost);
277};
278
279FirstStageProblem::FirstStageProblem(const Network& network,
280 const double facility_cost)
281 : model("First stage problem"), w(model.AddContinuousVariable(0.0, kInf)) {
282 const int num_facilities = network.num_facilities();
283
284 // Capacity variables
285 for (int j = 0; j < num_facilities; j++) {
286 z.push_back(model.AddContinuousVariable(0.0, kInf));
287 }
288
289 // First stage objective
290 model.Minimize(w + facility_cost * Sum(z));
291}
292
293// Represents a cut if the form:
294//
295// z_coefficients^T z + constant <= w_coefficient * w
296//
297// This will be a feasibility cut if w_coefficient = 0.0 and an optimality
298// cut if w_coefficient = 1.
299struct Cut {
300 std::vector<double> z_coefficients;
301 double constant;
302 double w_coefficient;
303};
304
305// Solves the second stage model:
306//
307// min sum(h_e * x_e : e in E)
308// s.t.
309// x_(f,l) <= a * zz_f for all (f,l) in E
310// sum(x_(f,l) : l such that (f,l) in E) <= zz_f for all f in F
311// sum(x_(f,l) : f such that (f,l) in E) >= d for all l in L
312// x_e >= 0 for all e in E
313//
314// where zz_f are fixed values for z_f from the first stage model, and generates
315// an infeasibility or optimality cut as needed.
316class SecondStageSolver {
317 public:
318 static absl::StatusOr<std::unique_ptr<SecondStageSolver>> New(
319 FacilityLocationInstance instance, math_opt::SolverType solver_type);
320
321 absl::StatusOr<std::pair<double, Cut>> Solve(
322 const std::vector<double>& z_values, double w_value,
323 double fist_stage_objective);
324
325 private:
326 SecondStageSolver(FacilityLocationInstance instance,
327 math_opt::SolveParameters second_stage_params);
328
329 absl::StatusOr<Cut> OptimalityCut(
330 const math_opt::SolveResult& second_stage_result);
331 absl::StatusOr<Cut> FeasibilityCut(
332 const math_opt::SolveResult& second_stage_result);
333
334 math_opt::Model second_stage_model_;
335 const Network network_;
336 const double location_fraction_;
337 math_opt::SolveParameters second_stage_params_;
338
339 absl::flat_hash_map<Edge, math_opt::Variable> x_;
340 std::vector<math_opt::LinearConstraint> supply_constraints_;
341 std::vector<math_opt::LinearConstraint> demand_constraints_;
342 std::unique_ptr<math_opt::IncrementalSolver> solver_;
343};
344
345absl::StatusOr<math_opt::SolveParameters> EnsureDualRaySolveParameters(
346 const math_opt::SolverType solver_type) {
348 switch (solver_type) {
349 case math_opt::SolverType::kGurobi:
350 parameters.gurobi.param_values["InfUnbdInfo"] = "1";
351 break;
352 case math_opt::SolverType::kGlop:
353 parameters.presolve = math_opt::Emphasis::kOff;
354 parameters.scaling = math_opt::Emphasis::kOff;
355 parameters.lp_algorithm = math_opt::LPAlgorithm::kDualSimplex;
356 break;
357 case math_opt::SolverType::kGlpk:
358 parameters.presolve = math_opt::Emphasis::kOff;
359 parameters.lp_algorithm = math_opt::LPAlgorithm::kDualSimplex;
360 break;
361 default:
363 << "unsupported solver: " << solver_type;
364 }
365 return parameters;
366}
367
368absl::StatusOr<std::unique_ptr<SecondStageSolver>> SecondStageSolver::New(
369 FacilityLocationInstance instance, const math_opt::SolverType solver_type) {
370 // Set solver arguments to ensure a dual ray is returned.
372 EnsureDualRaySolveParameters(solver_type));
373
374 std::unique_ptr<SecondStageSolver> second_stage_solver =
375 absl::WrapUnique<SecondStageSolver>(
376 new SecondStageSolver(std::move(instance), parameters));
377 ASSIGN_OR_RETURN(std::unique_ptr<math_opt::IncrementalSolver> solver,
379 &second_stage_solver->second_stage_model_, solver_type));
380 second_stage_solver->solver_ = std::move(solver);
381 return std::move(second_stage_solver);
382}
383
384SecondStageSolver::SecondStageSolver(
385 FacilityLocationInstance instance,
386 math_opt::SolveParameters second_stage_params)
387 : second_stage_model_("Second stage model"),
388 network_(std::move(instance.network)),
389 location_fraction_(instance.location_fraction),
390 second_stage_params_(second_stage_params) {
391 const int num_facilities = network_.num_facilities();
392 const int num_locations = network_.num_locations();
393
394 // Flow variables
395 for (const auto& edge : network_.edges()) {
396 const math_opt::Variable x_edge =
397 second_stage_model_.AddContinuousVariable(0.0, kInf);
398 x_.insert({edge, x_edge});
399 }
400
401 // Objective Function
402 math_opt::LinearExpression objective_for_edges;
403 for (const auto& [edge, x_edge] : x_) {
404 objective_for_edges += network_.edge_cost(edge) * x_edge;
405 }
406 second_stage_model_.Minimize(objective_for_edges);
407
408 // Demand constraints
409 for (int location = 0; location < num_locations; ++location) {
410 math_opt::LinearExpression incomming_supply;
411 for (const auto& edge : network_.edges_incident_to_location(location)) {
412 incomming_supply += x_.at(edge);
413 }
414 demand_constraints_.push_back(second_stage_model_.AddLinearConstraint(
415 incomming_supply >= instance.location_demand));
416 }
417
418 // Supply constraints
419 for (int facility = 0; facility < num_facilities; ++facility) {
420 math_opt::LinearExpression outgoing_supply;
421 for (const auto& edge : network_.edges_incident_to_facility(facility)) {
422 outgoing_supply += x_.at(edge);
423 }
424 // Set supply constraint with trivial upper bound to be updated with first
425 // stage information.
426 supply_constraints_.push_back(
427 second_stage_model_.AddLinearConstraint(outgoing_supply <= kInf));
428 }
429}
430
431absl::StatusOr<std::pair<double, Cut>> SecondStageSolver::Solve(
432 const std::vector<double>& z_values, const double w_value,
433 const double fist_stage_objective) {
434 const int num_facilities = network_.num_facilities();
435
436 // Update second stage with first stage solution.
437 for (int facility = 0; facility < num_facilities; ++facility) {
438 if (z_values[facility] < -kZeroTol) {
440 << "negative z_value in first stage: " << z_values[facility]
441 << " for facility " << facility;
442 }
443 // Make sure variable bounds are valid (lb <= ub).
444 const double capacity_value = std::max(z_values[facility], 0.0);
445 for (const auto& edge : network_.edges_incident_to_facility(facility)) {
446 second_stage_model_.set_upper_bound(x_.at(edge),
447 location_fraction_ * capacity_value);
448 }
449 second_stage_model_.set_upper_bound(supply_constraints_[facility],
450 capacity_value);
451 }
452 // Solve and process second stage.
453 ASSIGN_OR_RETURN(const math_opt::SolveResult second_stage_result,
454 solver_->Solve(math_opt::SolveArguments{
455 .parameters = second_stage_params_}));
456 switch (second_stage_result.termination.reason) {
457 case math_opt::TerminationReason::kInfeasible:
458 // If the second stage problem is infeasible we can construct a
459 // feasibility cut from a returned dual ray.
460 {
461 OR_ASSIGN_OR_RETURN3(const Cut feasibility_cut,
462 FeasibilityCut(second_stage_result),
463 _ << "on infeasible for second stage solver");
464 return std::make_pair(kInf, feasibility_cut);
465 }
466 case math_opt::TerminationReason::kOptimal:
467 // If the second stage problem is optimal we can construct an optimality
468 // cut from a returned dual optimal solution. We can also update the upper
469 // bound.
470 {
471 // Upper bound is obtained by switching predicted second stage objective
472 // value w with the true second stage objective value.
473 const double upper_bound = fist_stage_objective - w_value +
474 second_stage_result.objective_value();
475 OR_ASSIGN_OR_RETURN3(const Cut optimality_cut,
476 OptimalityCut(second_stage_result),
477 _ << "on optimal for second stage solver");
478 return std::make_pair(upper_bound, optimality_cut);
479 }
480 default:
482 << "second stage was not solved to optimality or infeasibility: "
483 << second_stage_result.termination;
484 }
485}
486
487// If the second stage problem is infeasible we get a dual ray (r, y) such that
488//
489// sum(r_(f,l)*a*zz_f : (f,l) in E, r_(f,l) < 0)
490// + sum(y_f*zz_f : f in F, y_f < 0)
491// + sum(y_l*d : l in L, y_l > 0) > 0.
492//
493// Then we get the feasibility cut (go/math_opt-advanced-dual-use#benders)
494//
495// sum(fcut_f*z_f) + fcut_const <= 0,
496//
497// where
498//
499// fcut_f = sum(r_(f,l)*a : (f,l) in E, r_(f,l) < 0)
500// + min{y_f, 0}
501// fcut_const = sum*(y_l*d : l in L, y_l > 0)
502absl::StatusOr<Cut> SecondStageSolver::FeasibilityCut(
503 const math_opt::SolveResult& second_stage_result) {
504 const int num_facilities = network_.num_facilities();
505 Cut result;
506
507 if (!second_stage_result.has_dual_ray()) {
508 // MathOpt does not require solvers to return a dual solution on optimal,
509 // but most LP solvers always will, see go/mathopt-solver-contracts for
510 // details.
512 << "no dual ray available for feasibility cut";
513 }
514
515 for (int facility = 0; facility < num_facilities; ++facility) {
516 double coefficient = 0.0;
517 for (const auto& edge : network_.edges_incident_to_facility(facility)) {
518 const double reduced_cost =
519 second_stage_result.ray_reduced_costs().at(x_.at(edge));
520 coefficient += location_fraction_ * std::min(reduced_cost, 0.0);
521 }
522 const double dual_value =
523 second_stage_result.ray_dual_values().at(supply_constraints_[facility]);
524 coefficient += std::min(dual_value, 0.0);
525 result.z_coefficients.push_back(coefficient);
526 }
527 result.constant = 0.0;
528 for (const auto& constraint : demand_constraints_) {
529 const double dual_value =
530 second_stage_result.ray_dual_values().at(constraint);
531 result.constant += std::max(dual_value, 0.0);
532 }
533 result.w_coefficient = 0.0;
534 return result;
535}
536
537// If the second stage problem is optimal we get a dual solution (r, y) such
538// that the optimal objective value is equal to
539//
540// sum(r_(f,l)*a*zz_f : (f,l) in E, r_(f,l) < 0)
541// + sum(y_f*zz_f : f in F, y_f < 0)
542// + sum*(y_l*d : l in L, y_l > 0) > 0.
543//
544// Then we get the optimality cut (go/math_opt-advanced-dual-use#benders)
545//
546// sum(ocut_f*z_f) + ocut_const <= w,
547//
548// where
549//
550// ocut_f = sum(r_(f,l)*a : (f,l) in E, r_(f,l) < 0)
551// + min{y_f, 0}
552// ocut_const = sum*(y_l*d : l in L, y_l > 0)
553absl::StatusOr<Cut> SecondStageSolver::OptimalityCut(
554 const math_opt::SolveResult& second_stage_result) {
555 const int num_facilities = network_.num_facilities();
556 Cut result;
557
558 if (!second_stage_result.has_dual_feasible_solution()) {
560 << "no dual solution available for optimality cut";
561 }
562
563 for (int facility = 0; facility < num_facilities; ++facility) {
564 double coefficient = 0.0;
565 for (const auto& edge : network_.edges_incident_to_facility(facility)) {
566 const double reduced_cost =
567 second_stage_result.reduced_costs().at(x_.at(edge));
568 coefficient += location_fraction_ * std::min(reduced_cost, 0.0);
569 }
570 double dual_value =
571 second_stage_result.dual_values().at(supply_constraints_[facility]);
572 coefficient += std::min(dual_value, 0.0);
573 result.z_coefficients.push_back(coefficient);
574 }
575 result.constant = 0.0;
576 for (const auto& constraint : demand_constraints_) {
577 double dual_value = second_stage_result.dual_values().at(constraint);
578 result.constant += std::max(dual_value, 0.0);
579 }
580 result.w_coefficient = 1.0;
581 return result;
582}
583
584absl::Status Benders(const FacilityLocationInstance& instance,
585 const double target_precission,
586 const math_opt::SolverType solver_type,
587 const int maximum_iterations = 30000) {
588 const int num_facilities = instance.network.num_facilities();
589
590 // Setup first stage model and solver.
591 FirstStageProblem first_stage(instance.network, instance.facility_cost);
593 const std::unique_ptr<math_opt::IncrementalSolver> first_stage_solver,
594 math_opt::IncrementalSolver::New(&first_stage.model, solver_type));
595 // Setup second stage solver.
596 ASSIGN_OR_RETURN(std::unique_ptr<SecondStageSolver> second_stage_solver,
597 SecondStageSolver::New(instance, solver_type));
598 // Start Benders
599 int iteration = 0;
600 double best_upper_bound = kInf;
601 std::vector<double> z_values;
602 z_values.resize(num_facilities);
603 while (true) {
604 LOG(INFO) << "Iteration: " << iteration;
605
606 // Solve and process first stage.
607 ASSIGN_OR_RETURN(const math_opt::SolveResult first_stage_result,
608 first_stage_solver->Solve());
609 if (first_stage_result.termination.reason !=
610 math_opt::TerminationReason::kOptimal) {
612 << "could not solve first stage problem to optimality:"
613 << first_stage_result.termination;
614 }
615 for (int j = 0; j < num_facilities; j++) {
616 z_values[j] = first_stage_result.variable_values().at(first_stage.z[j]);
617 }
618 const double lower_bound = first_stage_result.objective_value();
619 LOG(INFO) << "LB = " << lower_bound;
620 // Solve and process second stage.
622 (auto [upper_bound, cut]),
623 second_stage_solver->Solve(
624 z_values, first_stage_result.variable_values().at(first_stage.w),
625 first_stage_result.objective_value()));
626 math_opt::LinearExpression cut_expression;
627 for (int j = 0; j < num_facilities; j++) {
628 cut_expression += cut.z_coefficients[j] * first_stage.z[j];
629 }
630 cut_expression += cut.constant;
631 first_stage.model.AddLinearConstraint(cut_expression <=
632 cut.w_coefficient * first_stage.w);
633 best_upper_bound = std::min(upper_bound, best_upper_bound);
634 LOG(INFO) << "UB = " << best_upper_bound;
635 ++iteration;
636 if (best_upper_bound - lower_bound < target_precission) {
637 std::cout << "Total iterations = " << iteration << std::endl;
638 std::cout << "Final LB = " << absl::StrFormat("%.9f", lower_bound)
639 << std::endl;
640 std::cout << "Final UB = " << absl::StrFormat("%.9f", best_upper_bound)
641 << std::endl;
642 break;
643 }
644 if (iteration > maximum_iterations) {
645 break;
646 }
647 }
648 return absl::OkStatus();
649}
650absl::Status Main() {
651 const FacilityLocationInstance instance{
652 .network = Network(absl::GetFlag(FLAGS_num_facilities),
653 absl::GetFlag(FLAGS_num_locations),
654 absl::GetFlag(FLAGS_edge_probability)),
655 .location_demand = absl::GetFlag(FLAGS_location_demand),
656 .facility_cost = absl::GetFlag(FLAGS_facility_cost),
657 .location_fraction = absl::GetFlag(FLAGS_location_fraction)};
658 absl::Time start = absl::Now();
659 RETURN_IF_ERROR(FullProblem(instance, absl::GetFlag(FLAGS_solver_type)))
660 << "full solve failed";
661 std::cout << "Full solve time : " << absl::Now() - start << std::endl;
662 start = absl::Now();
663 RETURN_IF_ERROR(Benders(instance, absl::GetFlag(FLAGS_benders_precission),
664 absl::GetFlag(FLAGS_solver_type)))
665 << "Benders solve failed";
666 std::cout << "Benders solve time : " << absl::Now() - start << std::endl;
667 return absl::OkStatus();
668}
669} // namespace
670
671int main(int argc, char** argv) {
672 InitGoogle(argv[0], &argc, &argv, true);
673 const absl::Status status = Main();
674 if (!status.ok()) {
675 LOG(QFATAL) << status;
676 }
677 return 0;
678}
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define LOG(severity)
Definition: base/logging.h:420
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)
static absl::StatusOr< std::unique_ptr< IncrementalSolver > > New(Model *model, SolverType solver_type, SolverInitArguments arguments={})
Definition: solve.cc:104
SatParameters parameters
int main(int argc, char **argv)
ABSL_FLAG(int, num_facilities, 3000, "Number of facilities.")
absl::Status status
Definition: g_gurobi.cc:35
double upper_bound
double lower_bound
GRBmodel * model
void InitGoogle(const char *usage, int *argc, char ***argv, bool deprecated)
Definition: init_google.h:32
constexpr double kZeroTol
const int INFO
Definition: log_severity.h:31
LinearExpression Sum(const Iterable &items)
absl::StatusOr< SolveResult > Solve(const Model &model, const SolverType solver_type, const SolveArguments &solve_args, const SolverInitArguments &init_args)
Definition: solve.cc:94
STL namespace.
StatusBuilder InternalErrorBuilder()
int64_t coefficient
int64_t start
const LinearConstraintMap< double > & dual_values() const
const LinearConstraintMap< double > & ray_dual_values() const
const VariableMap< double > & ray_reduced_costs() const
const VariableMap< double > & reduced_costs() const
#define OR_ASSIGN_OR_RETURN3(lhs, rexpr, error_expression)
const double constant