88#include "absl/flags/flag.h"
89#include "absl/memory/memory.h"
90#include "absl/status/status.h"
91#include "absl/status/statusor.h"
92#include "absl/strings/str_format.h"
93#include "absl/strings/string_view.h"
103 "Stepsize for gradient ascent, determined as step_size^t.");
105 "Max number of iterations for gradient ascent.");
107 "If true, the side constraint associated to resource 1 is dualized.");
109 "If true, the side constraint associated to resource 2 is dualized.");
112 "If true, shows the iteration log of the subgradient ascent "
113 "procedure use to solve the Lagrangian problem");
118using ::operations_research::MathUtil;
120namespace math_opt = ::operations_research::math_opt;
132 std::vector<Arc> arcs;
138 FlowModel() :
model(std::make_unique<math_opt::Model>(
"LagrangianProblem")) {}
139 std::unique_ptr<math_opt::Model>
model;
143 std::vector<math_opt::Variable> flow_vars;
147FlowModel CreateShortestPathModel(
const Graph graph) {
148 FlowModel flow_model;
150 for (
const Arc&
arc : graph.arcs) {
153 absl::StrFormat(
"x_%d_%d",
arc.i,
arc.j));
154 flow_model.cost +=
arc.cost *
var;
155 flow_model.resource_1 +=
arc.resource_1 *
var;
156 flow_model.resource_2 +=
arc.resource_2 *
var;
157 flow_model.flow_vars.push_back(
var);
161 std::vector<math_opt::LinearExpression> out_flow(graph.num_nodes);
162 std::vector<math_opt::LinearExpression> in_flow(graph.num_nodes);
163 for (
int arc_index = 0; arc_index < graph.arcs.size(); ++arc_index) {
164 out_flow[graph.arcs[arc_index].i] += flow_model.flow_vars[arc_index];
165 in_flow[graph.arcs[arc_index].j] += flow_model.flow_vars[arc_index];
167 for (
int node_index = 0; node_index < graph.num_nodes; ++node_index) {
168 int rhs = node_index == graph.source ? 1
169 : node_index == graph.sink ? -1
171 model.AddLinearConstraint(out_flow[node_index] - in_flow[node_index] ==
178Graph CreateSampleNetwork() {
179 std::vector<Arc> arcs;
181 {.i = 0, .j = 1, .cost = 12, .resource_1 = 1, .resource_2 = 1});
183 {.i = 0, .j = 2, .cost = 3, .resource_1 = 2.5, .resource_2 = 1});
185 {.i = 1, .j = 3, .cost = 5, .resource_1 = 1, .resource_2 = 1.5});
187 {.i = 1, .j = 4, .cost = 5, .resource_1 = 2.5, .resource_2 = 1});
189 {.i = 2, .j = 1, .cost = 7, .resource_1 = 2.5, .resource_2 = 1});
191 {.i = 2, .j = 3, .cost = 5, .resource_1 = 7, .resource_2 = 2.5});
193 {.i = 2, .j = 4, .cost = 1, .resource_1 = 6.5, .resource_2 = 1});
195 {.i = 3, .j = 5, .cost = 6, .resource_1 = 1, .resource_2 = 2.0});
197 {.i = 4, .j = 3, .cost = 3, .resource_1 = 1, .resource_2 = 0.5});
199 {.i = 4, .j = 5, .cost = 5, .resource_1 = 2.5, .resource_2 = 1});
200 const Graph graph = {.num_nodes = 6, .arcs = arcs, .source = 0, .sink = 5};
206absl::StatusOr<FlowModel> SolveMip(
const Graph graph,
207 const double max_resource_1,
208 const double max_resource_2) {
209 FlowModel flow_model;
211 for (
const Arc&
arc : graph.arcs) {
213 absl::StrFormat(
"x_%d_%d",
arc.i,
arc.j));
214 flow_model.cost +=
arc.cost *
var;
215 flow_model.resource_1 += +
arc.resource_1 *
var;
216 flow_model.resource_2 +=
arc.resource_2 *
var;
217 flow_model.flow_vars.push_back(
var);
221 std::vector<math_opt::LinearExpression> out_flow(graph.num_nodes);
222 std::vector<math_opt::LinearExpression> in_flow(graph.num_nodes);
223 for (
int arc_index = 0; arc_index < graph.arcs.size(); ++arc_index) {
224 out_flow[graph.arcs[arc_index].i] += flow_model.flow_vars[arc_index];
225 in_flow[graph.arcs[arc_index].j] += flow_model.flow_vars[arc_index];
227 for (
int node_index = 0; node_index < graph.num_nodes; ++node_index) {
228 int rhs = node_index == graph.source ? 1
229 : node_index == graph.sink ? -1
231 model.AddLinearConstraint(out_flow[node_index] - in_flow[node_index] ==
235 model.AddLinearConstraint(flow_model.resource_1 <= max_resource_1,
237 model.AddLinearConstraint(flow_model.resource_2 <= max_resource_2,
239 model.Minimize(flow_model.cost);
243 case math_opt::TerminationReason::kOptimal:
244 case math_opt::TerminationReason::kFeasible:
245 std::cout <<
"MIP Solution with 2 side constraints" << std::endl;
246 std::cout << absl::StrFormat(
"MIP objective value: %6.3f",
249 std::cout <<
"Resource 1: "
252 std::cout <<
"Resource 2: "
255 std::cout <<
"========================================" << std::endl;
259 <<
"model failed to solve: " << result.
termination;
265absl::Status SolveLinearRelaxation(FlowModel& flow_model,
const Graph& graph,
266 const double max_resource_1,
267 const double max_resource_2) {
272 case math_opt::TerminationReason::kOptimal:
273 case math_opt::TerminationReason::kFeasible:
274 std::cout <<
"LP relaxation with 2 side constraints" << std::endl;
275 std::cout << absl::StrFormat(
"LP objective value: %6.3f",
278 std::cout <<
"Resource 1: "
281 std::cout <<
"Resource 2: "
284 std::cout <<
"========================================" << std::endl;
285 return absl::OkStatus();
288 <<
"model failed to solve: " << result.
termination;
292absl::Status SolveLagrangianRelaxation(
const Graph graph,
293 const double max_resource_1,
294 const double max_resource_2) {
296 FlowModel flow_model = CreateShortestPathModel(graph);
303 std::vector<double> mu;
304 std::vector<math_opt::LinearExpression> grad_mu;
305 const bool dualized_resource_1 = absl::GetFlag(FLAGS_dualize_resource_1);
306 const bool dualized_resource_2 = absl::GetFlag(FLAGS_dualize_resource_2);
307 const bool lagrangian_output = absl::GetFlag(FLAGS_lagrangian_output);
308 CHECK(dualized_resource_1 || dualized_resource_2)
309 <<
"At least one of the side constraints should be dualized.";
314 const double initial_dual_value = -10;
315 if (dualized_resource_1 && !dualized_resource_2) {
316 mu.push_back(initial_dual_value);
317 grad_mu.push_back(max_resource_1 - resource_1);
318 model.AddLinearConstraint(resource_2 <= max_resource_2);
322 }
else if (!dualized_resource_1 && dualized_resource_2) {
323 mu.push_back(initial_dual_value);
324 grad_mu.push_back(max_resource_2 - resource_2);
325 model.AddLinearConstraint(resource_1 <= max_resource_1);
330 mu.push_back(initial_dual_value);
331 mu.push_back(initial_dual_value);
332 grad_mu.push_back(max_resource_1 - resource_1);
333 grad_mu.push_back(max_resource_2 - resource_2);
337 bool termination =
false;
339 const double step_size = absl::GetFlag(FLAGS_step_size);
340 CHECK_GT(step_size, 0) <<
"step_size must be strictly positive";
341 CHECK_LT(step_size, 1) <<
"step_size must be strictly less than 1";
342 const int max_iterations = absl::GetFlag(FLAGS_max_iterations);
344 <<
"Number of iterations must be strictly positive.";
347 double upper_bound = std::numeric_limits<double>::infinity();
348 double lower_bound = -std::numeric_limits<double>::infinity();
349 double best_solution_resource_1 = 0;
350 double best_solution_resource_2 = 0;
352 if (lagrangian_output) {
353 std::cout <<
"Starting gradient ascent..." << std::endl;
354 std::cout << absl::StrFormat(
"%4s %6s %6s %9s %10s %10s",
"Iter",
"LB",
355 "UB",
"Step size",
"mu_t",
"grad_mu_t")
359 while (!termination) {
361 lagrangian_function +=
cost;
362 for (
int k = 0; k < mu.size(); ++k) {
363 lagrangian_function += mu[k] * grad_mu[k];
365 model.Minimize(lagrangian_function);
369 case math_opt::TerminationReason::kOptimal:
370 case math_opt::TerminationReason::kFeasible:
374 <<
"failed to minimize lagrangian function: "
379 bool feasible =
true;
390 std::vector<double> grad_mu_vals;
391 const double step_size_t = MathUtil::IPow(step_size, iterations);
392 for (
int k = 0; k < mu.size(); ++k) {
394 const double grad_mu_val = grad_mu[k].Evaluate(vars_val);
395 grad_mu_vals.push_back(grad_mu_val);
396 mu[k] =
std::min(0.0, mu[k] + step_size_t * grad_mu_val);
397 if (grad_mu_val < 0) {
402 const double path_cost =
cost.Evaluate(vars_val);
404 best_solution_resource_1 = resource_1.
Evaluate(vars_val);
405 best_solution_resource_2 = resource_2.
Evaluate(vars_val);
406 if (lagrangian_output) {
407 std::cout <<
"Feasible solution with"
409 "cost=%4.2f, resource_1=%4.2f, and resource_2=%4.2f. ",
410 path_cost, best_solution_resource_1,
411 best_solution_resource_2)
420 if (lagrangian_output) {
421 std::cout << absl::StrFormat(
"%4d %6.3f %6.3f %9.3f", iterations,
429 for (
double value : grad_mu_vals) {
440 std::cout <<
"Lagrangian relaxation with 2 side constraints" << std::endl;
441 std::cout <<
"Constraint for resource 1 dualized: "
442 << (dualized_resource_1 ?
"true" :
"false") << std::endl;
443 std::cout <<
"Constraint for resource 2 dualized: "
444 << (dualized_resource_2 ?
"true" :
"false") << std::endl;
445 std::cout << absl::StrFormat(
"Lower bound: %6.3f",
lower_bound) << std::endl;
446 std::cout << absl::StrFormat(
"Upper bound: %6.3f (Integer solution)",
449 std::cout <<
"========================================" << std::endl;
450 return absl::OkStatus();
453void RelaxModel(FlowModel& flow_model) {
455 flow_model.model->set_continuous(
var);
456 flow_model.model->set_lower_bound(
var, 0.0);
457 flow_model.model->set_upper_bound(
var, 1.0);
461absl::Status SolveFullModel(
const Graph& graph,
double max_resource_1,
462 double max_resource_2) {
464 SolveMip(graph, max_resource_1, max_resource_2));
465 RelaxModel(flow_model);
466 return SolveLinearRelaxation(flow_model, graph, max_resource_1,
472 const Graph graph = CreateSampleNetwork();
473 const double max_resource_1 = 10;
474 const double max_resource_2 = 4;
477 <<
"full solve failed";
479 SolveLagrangianRelaxation(graph, max_resource_1, max_resource_2))
480 <<
"lagrangian solve failed";
481 return absl::OkStatus();
485int main(
int argc,
char** argv) {
487 const absl::Status
status = Main();
#define CHECK_LT(val1, val2)
#define CHECK_GT(val1, val2)
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)
double Evaluate(const VariableMap< double > &variable_values) const
void InitGoogle(const char *usage, int *argc, char ***argv, bool deprecated)
int main(int argc, char **argv)
ABSL_FLAG(double, step_size, 0.95, "Stepsize for gradient ascent, determined as step_size^t.")
constexpr double kZeroTol
auto LogContainer(const ContainerT &container, const PolicyT &policy) -> decltype(gtl::LogRange(container.begin(), container.end(), policy))
absl::StatusOr< SolveResult > Solve(const Model &model, const SolverType solver_type, const SolveArguments &solve_args, const SolverInitArguments &init_args)
std::pair< int64_t, int64_t > Arc
StatusBuilder InternalErrorBuilder()
double objective_value() const
const VariableMap< double > & variable_values() const