OR-Tools  9.6
facility_lp_benders_mo.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 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"
67 #include "ortools/base/logging.h"
71 
72 ABSL_FLAG(int, num_facilities, 3000, "Number of facilities.");
73 ABSL_FLAG(int, num_locations, 50, "Number of locations.");
74 ABSL_FLAG(double, edge_probability, 0.99, "Edge probability.");
75 ABSL_FLAG(double, benders_precission, 1e-9, "Benders target precission.");
76 ABSL_FLAG(double, location_demand, 1, "Client demands.");
77 ABSL_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 
85 namespace {
86 
88 constexpr double kInf = std::numeric_limits<double>::infinity();
89 constexpr 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.
96 using Edge = std::pair<int, int>;
97 
98 // A simple randomly-generated facility-location network.
99 class 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 
130 Network::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 
180 struct 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.
192 absl::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,...
271 struct FirstStageProblem {
273  std::vector<math_opt::Variable> z;
275 
276  FirstStageProblem(const Network& network, const double facility_cost);
277 };
278 
279 FirstStageProblem::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.
299 struct 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.
316 class 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 
345 absl::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 
368 absl::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 
384 SecondStageSolver::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 
431 absl::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)
502 absl::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)
553 absl::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 
584 absl::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 }
650 absl::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 
671 int 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 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={})
SatParameters parameters
int main(int argc, char **argv)
ABSL_FLAG(int, num_facilities, 3000, "Number of facilities.")
absl::Status status
Definition: g_gurobi.cc:41
GRBmodel * model
void InitGoogle(const char *usage, int *argc, char ***argv, bool deprecated)
Definition: init_google.h:34
constexpr double kZeroTol
LinearExpression Sum(const Iterable &items)
absl::StatusOr< SolveResult > Solve(const Model &model, const SolverType solver_type, const SolveArguments &solve_args, const SolverInitArguments &init_args)
StatusBuilder InternalErrorBuilder()
IntVar * upper_bound
Definition: routing.cc:1087
IntVar * lower_bound
Definition: routing.cc:1086
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)