88#include "absl/flags/flag.h"
89#include "absl/flags/parse.h"
90#include "absl/flags/usage.h"
91#include "absl/memory/memory.h"
92#include "absl/status/statusor.h"
93#include "absl/strings/str_format.h"
94#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");
116using ::operations_research::MathUtil;
117using ::operations_research::math_opt::LinearExpression;
118using ::operations_research::math_opt::Model;
119using ::operations_research::math_opt::SolveArguments;
120using ::operations_research::math_opt::SolveResult;
123using ::operations_research::math_opt::Variable;
136 std::vector<Arc> arcs;
142 FlowModel() :
model(std::make_unique<Model>(
"LagrangianProblem")) {}
143 std::unique_ptr<Model>
model;
144 LinearExpression
cost;
145 LinearExpression resource_1;
146 LinearExpression resource_2;
147 std::vector<Variable> flow_vars;
151FlowModel CreateShortestPathModel(
const Graph graph) {
152 FlowModel flow_model;
153 Model&
model = *flow_model.model;
154 for (
const Arc& arc : graph.arcs) {
155 Variable
var =
model.AddContinuousVariable(
157 absl::StrFormat(
"x_%d_%d", arc.i, arc.j));
158 flow_model.cost += arc.cost *
var;
159 flow_model.resource_1 += arc.resource_1 *
var;
160 flow_model.resource_2 += arc.resource_2 *
var;
161 flow_model.flow_vars.push_back(
var);
165 std::vector<LinearExpression> out_flow(graph.num_nodes);
166 std::vector<LinearExpression> in_flow(graph.num_nodes);
167 for (
int arc_index = 0; arc_index < graph.arcs.size(); ++arc_index) {
168 out_flow[graph.arcs[arc_index].i] += flow_model.flow_vars[arc_index];
169 in_flow[graph.arcs[arc_index].j] += flow_model.flow_vars[arc_index];
171 for (
int node_index = 0; node_index < graph.num_nodes; ++node_index) {
172 int rhs = node_index == graph.source ? 1
173 : node_index == graph.sink ? -1
175 model.AddLinearConstraint(out_flow[node_index] - in_flow[node_index] ==
182Graph CreateSampleNetwork() {
183 std::vector<Arc> arcs;
185 {.i = 0, .j = 1, .cost = 12, .resource_1 = 1, .resource_2 = 1});
187 {.i = 0, .j = 2, .cost = 3, .resource_1 = 2.5, .resource_2 = 1});
189 {.i = 1, .j = 3, .cost = 5, .resource_1 = 1, .resource_2 = 1.5});
191 {.i = 1, .j = 4, .cost = 5, .resource_1 = 2.5, .resource_2 = 1});
193 {.i = 2, .j = 1, .cost = 7, .resource_1 = 2.5, .resource_2 = 1});
195 {.i = 2, .j = 3, .cost = 5, .resource_1 = 7, .resource_2 = 2.5});
197 {.i = 2, .j = 4, .cost = 1, .resource_1 = 6.5, .resource_2 = 1});
199 {.i = 3, .j = 5, .cost = 6, .resource_1 = 1, .resource_2 = 2.0});
201 {.i = 4, .j = 3, .cost = 3, .resource_1 = 1, .resource_2 = 0.5});
203 {.i = 4, .j = 5, .cost = 5, .resource_1 = 2.5, .resource_2 = 1});
204 const Graph graph = {.num_nodes = 6, .arcs = arcs, .source = 0, .sink = 5};
210FlowModel SolveMip(
const Graph graph,
const double max_resource_1,
211 const double max_resource_2) {
212 FlowModel flow_model;
213 Model&
model = *flow_model.model;
214 for (
const Arc& arc : graph.arcs) {
215 Variable
var =
model.AddBinaryVariable(
216 absl::StrFormat(
"x_%d_%d", arc.i, arc.j));
217 flow_model.cost += arc.cost *
var;
218 flow_model.resource_1 += +arc.resource_1 *
var;
219 flow_model.resource_2 += arc.resource_2 *
var;
220 flow_model.flow_vars.push_back(
var);
224 std::vector<LinearExpression> out_flow(graph.num_nodes);
225 std::vector<LinearExpression> in_flow(graph.num_nodes);
226 for (
int arc_index = 0; arc_index < graph.arcs.size(); ++arc_index) {
227 out_flow[graph.arcs[arc_index].i] += flow_model.flow_vars[arc_index];
228 in_flow[graph.arcs[arc_index].j] += flow_model.flow_vars[arc_index];
230 for (
int node_index = 0; node_index < graph.num_nodes; ++node_index) {
231 int rhs = node_index == graph.source ? 1
232 : node_index == graph.sink ? -1
234 model.AddLinearConstraint(out_flow[node_index] - in_flow[node_index] ==
238 model.AddLinearConstraint(flow_model.resource_1 <= max_resource_1,
240 model.AddLinearConstraint(flow_model.resource_2 <= max_resource_2,
242 model.Minimize(flow_model.cost);
243 const SolveResult result =
Solve(
model, SolverType::kGscip).value();
244 const VariableMap<double>& variable_values = result.variable_values();
245 std::cout <<
"MIP Solution with 2 side constraints" << std::endl;
246 std::cout << absl::StrFormat(
"MIP objective value: %6.3f",
247 result.objective_value())
249 std::cout <<
"Resource 1: " << flow_model.resource_1.Evaluate(variable_values)
251 std::cout <<
"Resource 2: " << flow_model.resource_2.Evaluate(variable_values)
253 std::cout <<
"========================================" << std::endl;
259void SolveLinearRelaxation(FlowModel& flow_model,
const Graph& graph,
260 const double max_resource_1,
261 const double max_resource_2) {
262 Model&
model = *flow_model.model;
263 const SolveResult result =
Solve(
model, SolverType::kGscip).value();
264 const VariableMap<double>& variable_values = result.variable_values();
265 std::cout <<
"LP relaxation with 2 side constraints" << std::endl;
266 std::cout << absl::StrFormat(
"LP objective value: %6.3f",
267 result.objective_value())
269 std::cout <<
"Resource 1: " << flow_model.resource_1.Evaluate(variable_values)
271 std::cout <<
"Resource 2: " << flow_model.resource_2.Evaluate(variable_values)
273 std::cout <<
"========================================" << std::endl;
276void SolveLagrangianRelaxation(
const Graph graph,
const double max_resource_1,
277 const double max_resource_2) {
279 FlowModel flow_model = CreateShortestPathModel(graph);
280 Model&
model = *flow_model.model;
281 LinearExpression&
cost = flow_model.cost;
282 LinearExpression& resource_1 = flow_model.resource_1;
283 LinearExpression& resource_2 = flow_model.resource_2;
286 std::vector<double> mu;
287 std::vector<LinearExpression> grad_mu;
288 const bool dualized_resource_1 = absl::GetFlag(FLAGS_dualize_resource_1);
289 const bool dualized_resource_2 = absl::GetFlag(FLAGS_dualize_resource_2);
290 const bool lagrangian_output = absl::GetFlag(FLAGS_lagrangian_output);
291 CHECK(dualized_resource_1 || dualized_resource_2)
292 <<
"At least one of the side constraints should be dualized.";
297 const double initial_dual_value = -10;
298 if (dualized_resource_1 && !dualized_resource_2) {
299 mu.push_back(initial_dual_value);
300 grad_mu.push_back(max_resource_1 - resource_1);
301 model.AddLinearConstraint(resource_2 <= max_resource_2);
302 for (Variable&
var : flow_model.flow_vars) {
305 }
else if (!dualized_resource_1 && dualized_resource_2) {
306 mu.push_back(initial_dual_value);
307 grad_mu.push_back(max_resource_2 - resource_2);
308 model.AddLinearConstraint(resource_1 <= max_resource_1);
309 for (Variable&
var : flow_model.flow_vars) {
313 mu.push_back(initial_dual_value);
314 mu.push_back(initial_dual_value);
315 grad_mu.push_back(max_resource_1 - resource_1);
316 grad_mu.push_back(max_resource_2 - resource_2);
320 bool termination =
false;
322 const double step_size = absl::GetFlag(FLAGS_step_size);
323 CHECK_GT(step_size, 0) <<
"step_size must be strictly positive";
324 CHECK_LT(step_size, 1) <<
"step_size must be strictly less than 1";
325 const int max_iterations = absl::GetFlag(FLAGS_max_iterations);
327 <<
"Number of iterations must be strictly positive.";
330 double upper_bound = std::numeric_limits<double>::infinity();
331 double lower_bound = -std::numeric_limits<double>::infinity();
332 double best_solution_resource_1 = 0;
333 double best_solution_resource_2 = 0;
335 if (lagrangian_output) {
336 std::cout <<
"Starting gradient ascent..." << std::endl;
337 std::cout << absl::StrFormat(
"%4s %6s %6s %9s %10s %10s",
"Iter",
"LB",
338 "UB",
"Step size",
"mu_t",
"grad_mu_t")
342 while (!termination) {
343 LinearExpression lagrangian_function;
344 lagrangian_function +=
cost;
345 for (
int k = 0; k < mu.size(); ++k) {
346 lagrangian_function += mu[k] * grad_mu[k];
348 model.Minimize(lagrangian_function);
349 SolveResult result =
Solve(
model, SolverType::kGscip).value();
350 CHECK_EQ(result.termination.reason, TerminationReason::kOptimal)
351 << result.termination;
352 const VariableMap<double>& vars_val = result.variable_values();
353 bool feasible =
true;
364 std::vector<double> grad_mu_vals;
365 const double step_size_t = MathUtil::IPow(step_size, iterations);
366 for (
int k = 0; k < mu.size(); ++k) {
368 const double grad_mu_val = grad_mu[k].Evaluate(vars_val);
369 grad_mu_vals.push_back(grad_mu_val);
370 mu[k] =
std::min(0.0, mu[k] + step_size_t * grad_mu_val);
371 if (grad_mu_val < 0) {
376 const double path_cost =
cost.Evaluate(vars_val);
378 best_solution_resource_1 = resource_1.Evaluate(vars_val);
379 best_solution_resource_2 = resource_2.Evaluate(vars_val);
380 if (lagrangian_output) {
381 std::cout <<
"Feasible solution with"
383 "cost=%4.2f, resource_1=%4.2f, and resource_2=%4.2f. ",
384 path_cost, best_solution_resource_1,
385 best_solution_resource_2)
394 if (lagrangian_output) {
395 std::cout << absl::StrFormat(
"%4d %6.3f %6.3f %9.3f", iterations,
403 for (
double value : grad_mu_vals) {
414 std::cout <<
"Lagrangian relaxation with 2 side constraints" << std::endl;
415 std::cout <<
"Constraint for resource 1 dualized: "
416 << (dualized_resource_1 ?
"true" :
"false") << std::endl;
417 std::cout <<
"Constraint for resource 2 dualized: "
418 << (dualized_resource_2 ?
"true" :
"false") << std::endl;
419 std::cout << absl::StrFormat(
"Lower bound: %6.3f",
lower_bound) << std::endl;
420 std::cout << absl::StrFormat(
"Upper bound: %6.3f (Integer solution)",
423 std::cout <<
"========================================" << std::endl;
426void RelaxModel(FlowModel& flow_model) {
427 for (Variable&
var : flow_model.flow_vars) {
428 flow_model.model->set_continuous(
var);
429 flow_model.model->set_lower_bound(
var, 0.0);
430 flow_model.model->set_upper_bound(
var, 1.0);
434void SolveFullModel(
const Graph& graph,
double max_resource_1,
435 double max_resource_2) {
436 FlowModel flow_model = SolveMip(graph, max_resource_1, max_resource_2);
437 RelaxModel(flow_model);
438 SolveLinearRelaxation(flow_model, graph, max_resource_1, max_resource_2);
443int main(
int argc,
char** argv) {
445 absl::ParseCommandLine(argc, argv);
448 const Graph graph = CreateSampleNetwork();
449 const double max_resource_1 = 10;
450 const double max_resource_2 = 4;
452 SolveFullModel(graph, max_resource_1, max_resource_2);
454 SolveLagrangianRelaxation(graph, max_resource_1, max_resource_2);
#define CHECK_LT(val1, val2)
#define CHECK_EQ(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
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