88 #include "absl/flags/parse.h"
89 #include "absl/flags/usage.h"
91 #include "absl/flags/flag.h"
92 #include "absl/memory/memory.h"
93 #include "absl/status/statusor.h"
94 #include "absl/strings/str_format.h"
95 #include "absl/strings/string_view.h"
101 "Stepsize for gradient ascent, determined as step_size^t.");
103 "Max number of iterations for gradient ascent.");
105 "If true, the side constraint associated to resource 1 is dualized.");
107 "If true, the side constraint associated to resource 2 is dualized.");
110 "If true, shows the iteration log of the subgradient ascent "
111 "procedure use to solve the Lagrangian problem");
116 using ::operations_research::MathUtil;
117 using ::operations_research::math_opt::LinearExpression;
118 using ::operations_research::math_opt::MathOpt;
119 using ::operations_research::math_opt::Result;
120 using ::operations_research::math_opt::SolveParametersProto;
121 using ::operations_research::math_opt::SolverType;
122 using ::operations_research::math_opt::Variable;
135 std::vector<Arc> arcs;
141 explicit FlowModel(SolverType solver_type) {
142 model = std::make_unique<MathOpt>(solver_type,
"LagrangianProblem");
144 std::unique_ptr<MathOpt>
model;
145 LinearExpression
cost;
146 LinearExpression resource_1;
147 LinearExpression resource_2;
148 std::vector<Variable> flow_vars;
152 FlowModel CreateShortestPathModel(
const Graph graph) {
153 FlowModel flow_model(operations_research::math_opt::SOLVER_TYPE_GSCIP);
154 MathOpt&
model = *flow_model.model;
155 for (
const Arc& arc : graph.arcs) {
156 Variable
var =
model.AddContinuousVariable(
158 absl::StrFormat(
"x_%d_%d", arc.i, arc.j));
159 flow_model.cost += arc.cost *
var;
160 flow_model.resource_1 += arc.resource_1 *
var;
161 flow_model.resource_2 += arc.resource_2 *
var;
162 flow_model.flow_vars.push_back(
var);
166 std::vector<LinearExpression> out_flow(graph.num_nodes);
167 std::vector<LinearExpression> in_flow(graph.num_nodes);
168 for (
int arc_index = 0; arc_index < graph.arcs.size(); ++arc_index) {
169 out_flow[graph.arcs[arc_index].i] += flow_model.flow_vars[arc_index];
170 in_flow[graph.arcs[arc_index].j] += flow_model.flow_vars[arc_index];
172 for (
int node_index = 0; node_index < graph.num_nodes; ++node_index) {
173 int rhs = node_index == graph.source ? 1
174 : node_index == graph.sink ? -1
176 model.AddLinearConstraint(out_flow[node_index] - in_flow[node_index] ==
183 Graph CreateSampleNetwork() {
184 std::vector<Arc> arcs;
186 {.i = 0, .j = 1, .cost = 12, .resource_1 = 1, .resource_2 = 1});
188 {.i = 0, .j = 2, .cost = 3, .resource_1 = 2.5, .resource_2 = 1});
190 {.i = 1, .j = 3, .cost = 5, .resource_1 = 1, .resource_2 = 1.5});
192 {.i = 1, .j = 4, .cost = 5, .resource_1 = 2.5, .resource_2 = 1});
194 {.i = 2, .j = 1, .cost = 7, .resource_1 = 2.5, .resource_2 = 1});
196 {.i = 2, .j = 3, .cost = 5, .resource_1 = 7, .resource_2 = 2.5});
198 {.i = 2, .j = 4, .cost = 1, .resource_1 = 6.5, .resource_2 = 1});
200 {.i = 3, .j = 5, .cost = 6, .resource_1 = 1, .resource_2 = 2.0});
202 {.i = 4, .j = 3, .cost = 3, .resource_1 = 1, .resource_2 = 0.5});
204 {.i = 4, .j = 5, .cost = 5, .resource_1 = 2.5, .resource_2 = 1});
205 const Graph graph = {.num_nodes = 6, .arcs = arcs, .source = 0, .sink = 5};
211 FlowModel SolveMip(
const Graph graph,
const double max_resource_1,
212 const double max_resource_2) {
213 FlowModel flow_model(operations_research::math_opt::SOLVER_TYPE_GSCIP);
214 MathOpt&
model = *flow_model.model;
215 for (
const Arc& arc : graph.arcs) {
216 Variable
var =
model.AddBinaryVariable(
217 absl::StrFormat(
"x_%d_%d", arc.i, arc.j));
218 flow_model.cost += arc.cost *
var;
219 flow_model.resource_1 += +arc.resource_1 *
var;
220 flow_model.resource_2 += arc.resource_2 *
var;
221 flow_model.flow_vars.push_back(
var);
225 std::vector<LinearExpression> out_flow(graph.num_nodes);
226 std::vector<LinearExpression> in_flow(graph.num_nodes);
227 for (
int arc_index = 0; arc_index < graph.arcs.size(); ++arc_index) {
228 out_flow[graph.arcs[arc_index].i] += flow_model.flow_vars[arc_index];
229 in_flow[graph.arcs[arc_index].j] += flow_model.flow_vars[arc_index];
231 for (
int node_index = 0; node_index < graph.num_nodes; ++node_index) {
232 int rhs = node_index == graph.source ? 1
233 : node_index == graph.sink ? -1
235 model.AddLinearConstraint(out_flow[node_index] - in_flow[node_index] ==
239 model.AddLinearConstraint(flow_model.resource_1 <= max_resource_1,
241 model.AddLinearConstraint(flow_model.resource_2 <= max_resource_2,
243 model.objective().Minimize(flow_model.cost);
244 SolveParametersProto params;
245 params.mutable_common_parameters()->set_enable_output(
false);
246 const Result result =
model.Solve(params).value();
247 const VariableMap<double>& variable_values = result.variable_values();
248 std::cout <<
"MIP Solution with 2 side constraints" << std::endl;
249 std::cout << absl::StrFormat(
"MIP objective value: %6.3f",
250 result.objective_value())
252 std::cout <<
"Resource 1: " << flow_model.resource_1.Evaluate(variable_values)
254 std::cout <<
"Resource 2: " << flow_model.resource_2.Evaluate(variable_values)
256 std::cout <<
"========================================" << std::endl;
262 void SolveLinearRelaxation(FlowModel& flow_model,
const Graph& graph,
263 const double max_resource_1,
264 const double max_resource_2) {
265 MathOpt&
model = *flow_model.model;
266 SolveParametersProto params;
267 params.mutable_common_parameters()->set_enable_output(
false);
268 const Result result =
model.Solve(params).value();
269 const VariableMap<double>& variable_values = result.variable_values();
270 std::cout <<
"LP relaxation with 2 side constraints" << std::endl;
271 std::cout << absl::StrFormat(
"LP objective value: %6.3f",
272 result.objective_value())
274 std::cout <<
"Resource 1: " << flow_model.resource_1.Evaluate(variable_values)
276 std::cout <<
"Resource 2: " << flow_model.resource_2.Evaluate(variable_values)
278 std::cout <<
"========================================" << std::endl;
281 void SolveLagrangianRelaxation(
const Graph graph,
const double max_resource_1,
282 const double max_resource_2) {
284 FlowModel flow_model = CreateShortestPathModel(graph);
285 MathOpt&
model = *flow_model.model;
286 LinearExpression&
cost = flow_model.cost;
287 LinearExpression& resource_1 = flow_model.resource_1;
288 LinearExpression& resource_2 = flow_model.resource_2;
289 SolveParametersProto params;
290 params.mutable_common_parameters()->set_enable_output(
false);
293 std::vector<double> mu;
294 std::vector<LinearExpression> grad_mu;
295 const bool dualized_resource_1 = absl::GetFlag(FLAGS_dualize_resource_1);
296 const bool dualized_resource_2 = absl::GetFlag(FLAGS_dualize_resource_2);
297 const bool lagrangian_output = absl::GetFlag(FLAGS_lagrangian_output);
298 CHECK(dualized_resource_1 || dualized_resource_2)
299 <<
"At least one of the side constraints should be dualized.";
304 const double initial_dual_value = -10;
305 if (dualized_resource_1 && !dualized_resource_2) {
306 mu.push_back(initial_dual_value);
307 grad_mu.push_back(max_resource_1 - resource_1);
308 model.AddLinearConstraint(resource_2 <= max_resource_2);
309 for (Variable&
var : flow_model.flow_vars) {
312 }
else if (!dualized_resource_1 && dualized_resource_2) {
313 mu.push_back(initial_dual_value);
314 grad_mu.push_back(max_resource_2 - resource_2);
315 model.AddLinearConstraint(resource_1 <= max_resource_1);
316 for (Variable&
var : flow_model.flow_vars) {
320 mu.push_back(initial_dual_value);
321 mu.push_back(initial_dual_value);
322 grad_mu.push_back(max_resource_1 - resource_1);
323 grad_mu.push_back(max_resource_2 - resource_2);
327 bool termination =
false;
329 const double step_size = absl::GetFlag(FLAGS_step_size);
330 CHECK_GT(step_size, 0) <<
"step_size must be strictly positive";
331 CHECK_LT(step_size, 1) <<
"step_size must be strictly less than 1";
332 const int max_iterations = absl::GetFlag(FLAGS_max_iterations);
334 <<
"Number of iterations must be strictly positive.";
337 double upper_bound = std::numeric_limits<double>().infinity();
338 double lower_bound = -std::numeric_limits<double>().infinity();
339 double best_solution_resource_1 = 0;
340 double best_solution_resource_2 = 0;
342 if (lagrangian_output) {
343 std::cout <<
"Starting gradient ascent..." << std::endl;
344 std::cout << absl::StrFormat(
"%4s %6s %6s %9s %10s %10s",
"Iter",
"LB",
345 "UB",
"Step size",
"mu_t",
"grad_mu_t")
349 while (!termination) {
350 LinearExpression lagrangian_function;
351 lagrangian_function +=
cost;
352 for (
int k = 0; k < mu.size(); ++k) {
353 lagrangian_function += mu[k] * grad_mu[k];
355 model.objective().Minimize(lagrangian_function);
356 Result result =
model.Solve(params).value();
357 const VariableMap<double>& vars_val = result.variable_values();
358 bool feasible =
true;
369 std::vector<double> grad_mu_vals;
370 const double step_size_t = MathUtil::IPow(step_size, iterations);
371 for (
int k = 0; k < mu.size(); ++k) {
373 const double grad_mu_val = grad_mu[k].Evaluate(vars_val);
374 grad_mu_vals.push_back(grad_mu_val);
375 mu[k] =
std::min(0.0, mu[k] + step_size_t * grad_mu_val);
376 if (grad_mu_val < 0) {
381 const double path_cost =
cost.Evaluate(vars_val);
383 best_solution_resource_1 = resource_1.Evaluate(vars_val);
384 best_solution_resource_2 = resource_2.Evaluate(vars_val);
385 if (lagrangian_output) {
386 std::cout <<
"Feasible solution with"
388 "cost=%4.2f, resource_1=%4.2f, and resource_2=%4.2f. ",
389 path_cost, best_solution_resource_1,
390 best_solution_resource_2)
399 if (lagrangian_output) {
400 std::cout << absl::StrFormat(
"%4d %6.3f %6.3f %9.3f", iterations,
408 for (
double value : grad_mu_vals) {
419 std::cout <<
"Lagrangian relaxation with 2 side constraints" << std::endl;
420 std::cout <<
"Constraint for resource 1 dualized: "
421 << (dualized_resource_1 ?
"true" :
"false") << std::endl;
422 std::cout <<
"Constraint for resource 2 dualized: "
423 << (dualized_resource_2 ?
"true" :
"false") << std::endl;
424 std::cout << absl::StrFormat(
"Lower bound: %6.3f",
lower_bound) << std::endl;
425 std::cout << absl::StrFormat(
"Upper bound: %6.3f (Integer solution)",
428 std::cout <<
"========================================" << std::endl;
431 void RelaxModel(FlowModel& flow_model) {
432 for (Variable&
var : flow_model.flow_vars) {
433 var.set_continuous();
434 var.set_lower_bound(0.0);
435 var.set_upper_bound(1.0);
439 void SolveFullModel(
const Graph& graph,
double max_resource_1,
440 double max_resource_2) {
441 FlowModel flow_model = SolveMip(graph, max_resource_1, max_resource_2);
442 RelaxModel(flow_model);
443 SolveLinearRelaxation(flow_model, graph, max_resource_1, max_resource_2);
448 int main(
int argc,
char** argv) {
450 absl::ParseCommandLine(argc, argv);
453 const Graph graph = CreateSampleNetwork();
454 const double max_resource_1 = 10;
455 const double max_resource_2 = 4;
457 SolveFullModel(graph, max_resource_1, max_resource_2);
459 SolveLagrangianRelaxation(graph, max_resource_1, max_resource_2);
#define CHECK_LT(val1, val2)
#define CHECK_GT(val1, val2)
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
void InitGoogleLogging(const char *argv0)
auto LogContainer(const ContainerT &container, const PolicyT &policy) -> decltype(gtl::LogRange(container.begin(), container.end(), policy))
IdMap< Variable, V > VariableMap
std::pair< int64_t, int64_t > Arc