10 #include <boost/bimap.hpp>
11 #include <boost/bimap/unordered_set_of.hpp>
13 #include <ilcplex/ilocplex.h>
15 #include "utils/utils.hpp"
16 #include "utils/graph_typedefs.hpp"
19 #define is_true(x) (lround(x))
23 #define LAZY_ADD_ALL_CYCLES
27 struct callback_statistics {
30 unsigned times_called;
32 callback_statistics(): time_spent(0.0), cuts_added(0), times_called(0) {}
41 using vertex_var_map = unordered_map<Vertex<Graph>, IloNumVar>;
44 struct var_collection{
47 EdgeVarMap<Graph> y[2];
48 EdgeVarMap<Graph> z[3];
49 vertex_var_map<Graph> ys, yt, zs, zt;
51 var_collection(
const Graph& _g): g(_g) {}
55 typedef adjacency_list<hash_setS,
63 template<
class GraphA,
class GraphB>
64 using TranslateMap = boost::bimap< boost::bimaps::unordered_set_of<Vertex<GraphA> >, boost::bimaps::unordered_set_of<Vertex<GraphB> > >;
69 class LazyCyclesBlockCallback:
public IloCplex::LazyConstraintCallbackI{
70 const var_collection<Graph>& vars;
71 callback_statistics& stats;
74 IloCplex::CallbackI* duplicateCallback()
const{
75 return (
new (getEnv()) LazyCyclesBlockCallback(getEnv(), vars, stats));
78 LazyCyclesBlockCallback(IloEnv env,
const var_collection<Graph>& _vars, callback_statistics& _stats):
79 IloCplex::LazyConstraintCallbackI(env),
87 void add_arc_from_variable(
89 const VertexPair<Graph>& uv,
90 const TranslateMap<Graph,AuxGraph>& translate,
93 if(is_true(getValue(var))){
94 const Vertex<Graph> u = uv.first;
95 const Vertex<Graph> v = uv.second;
96 const Vertex<AuxGraph> new_u = translate.left.at(u);
97 const Vertex<AuxGraph> new_v = translate.left.at(v);
98 add_edge(new_u, new_v, aux);
99 DEBUG5(std::cout <<
"adding "<<VertexPairAndGraph<Graph>(uv, vars.g)<<std::endl);
100 }
else DEBUG5(std::cout <<
"not adding "<<VertexPairAndGraph<Graph>(uv, vars.g)<<
" since "<<getValue(var)<<
" translates to "<<is_true(getValue(var))<<std::endl);
104 void add_corresponding_variable(
const EdgeVarMap<Graph> varmap[],
const VertexPair<Graph>& uv, IloExpr& exp)
const{
105 if(contains(varmap[0], uv))
106 exp += varmap[0].at(uv);
108 exp += varmap[1].at(uv);
115 template<
class Graph>
116 IloCplex::Callback GetLazyCyclesBlock(IloEnv env,
const var_collection<Graph>& vars, callback_statistics& stats) {
117 return (
new (env) LazyCyclesBlockCallback<Graph>(env, vars, stats));
122 template<
class Graph>
123 void LazyCyclesBlockCallback<Graph>::main(){
124 time_t start_time = clock();
128 TranslateMap<Graph, AuxGraph> translate;
129 typedef typename TranslateMap<Graph, AuxGraph>::value_type TranslatePair;
134 std::cout <<
"solution edges: "<<std::endl;
135 for(EdgeIterRange<Graph> e = edges(vars.g); e.first != e.second; ++e.first){
136 const Vertex<Graph>& u = source(*e.first, vars.g);
137 const Vertex<Graph>& v = target(*e.first, vars.g);
139 const VertexPair<Graph> uv(u, v);
140 const VertexPair<Graph> vu(v, u);
141 std::cout<<VertexPairAndGraph<Graph>(uv, vars.g)<<
" by ";for(unsigned l = 0; l < 3; ++l){
143 if(contains(vars.y[l], uv) && is_true(getValue(vars.y[l].at(uv)))) std::cout <<
"y["<<l<<
"]_"<<VertexPairAndGraph<Graph>(uv,vars.g)<<
", ";
144 if(contains(vars.y[l], vu) && is_true(getValue(vars.y[l].at(vu)))) std::cout <<
"y["<<l<<
"]_"<<VertexPairAndGraph<Graph>(vu,vars.g)<<
", ";
146 if(contains(vars.z[l], uv) && is_true(getValue(vars.z[l].at(uv)))) std::cout <<
"z["<<l<<
"]_"<<VertexPairAndGraph<Graph>(uv,vars.g)<<
", ";
147 if(contains(vars.z[l], vu) && is_true(getValue(vars.z[l].at(vu)))) std::cout <<
"z["<<l<<
"]_"<<VertexPairAndGraph<Graph>(vu,vars.g)<<
", ";
148 std::cout << std::endl;
157 for( VertexIterRange<Graph> i = vertices(vars.g); i.first != i.second; ++i.first ){
158 const Vertex<Graph> orig_u = *(i.first);
159 const Vertex<AuxGraph> new_u = add_vertex(aux);
160 translate.insert(TranslatePair(orig_u, new_u));
161 DEBUG5(std::cout <<
"adding "<<VertexAndGraph<Graph>(orig_u, vars.g)<<std::endl);
164 for(
unsigned char j = 0; j < 2; ++j) {
165 DEBUG5(std::cout <<
"adding y-layer-"<<(
int)j<<
" --> y-layer-"<<(
int)(j+1)%2<<
" arcs"<<std::endl);
166 for(
const auto& i : vars.y[j] ) add_arc_from_variable(i.second, i.first, translate, aux);
168 for(
unsigned char j = 0; j < 3; ++j) {
169 DEBUG5(
if(j<2) std::cout <<
"adding z-layer-"<<(
int)j<<
" --> z-layer-"<<(
int)(j+1)%2<<
" arcs"<<std::endl);
170 DEBUG5(
if(j==2) std::cout <<
"adding z-layer-1 --> z-layer-2 arcs"<<std::endl);
171 for(
const auto& i : vars.z[j] ) add_arc_from_variable(i.second, i.first, translate, aux);
175 std::stack<Vertex<AuxGraph> > pending;
176 DEBUG5(std::cout<<
"coming from y_s: ");
177 for(
const auto& i : vars.ys )
178 if(is_true(getValue(i.second))) {
179 DEBUG5(std::cout << VertexAndGraph<Graph>(i.first, vars.g)<<
" ");
180 pending.push(translate.left.at(i.first));
182 DEBUG5(std::cout<< std::endl<<
"coming from z_s: ");
183 for(
const auto& i : vars.zs )
184 if(is_true(getValue(i.second))) {
185 DEBUG5(std::cout << VertexAndGraph<Graph>(i.first, vars.g)<<
" ");
186 pending.push(translate.left.at(i.first));
188 DEBUG5(std::cout<<std::endl);
189 DEBUG5(std::cout<<
"going to y_t: ");
190 for(
const auto& i : vars.yt )
191 if(is_true(getValue(i.second))) {
192 DEBUG5(std::cout << VertexAndGraph<Graph>(i.first, vars.g)<<
" ");
194 DEBUG5(std::cout<< std::endl<<
"going to z_t: ");
195 for(
const auto& i : vars.zt )
196 if(is_true(getValue(i.second))) {
197 DEBUG5(std::cout << VertexAndGraph<Graph>(i.first, vars.g)<<
" ");
199 DEBUG5(std::cout<<std::endl);
201 unsigned num_p = pending.size();
203 while(!pending.empty()){
204 const Vertex<AuxGraph> u = pending.top(); pending.pop();
206 const unsigned char deg = out_degree(u, aux);
210 const Edge<AuxGraph> e = *(out_edges(u, aux).first);
211 const Vertex<AuxGraph> v = target(e, aux);
212 DEBUG5(std::cout << VertexAndGraph<Graph>(translate.right.at(v), vars.g) <<
" is on a path by edge from "<<VertexAndGraph<Graph>(translate.right.at(source(e, aux)), vars.g)<<std::endl);
219 DEBUG5(std::cout<<
"remaining arcs in auxilliary graph: "<<num_edges(aux)<<std::endl;);
220 unsigned num_new_cuts = 0;
223 for(VertexIterRange<AuxGraph> i = vertices(aux); i.first != i.second; ++i.first)
224 if(out_degree(*(i.first), aux) != in_degree(*(i.first), aux)){
225 const Vertex<Graph> u = translate.right.at(*(i.first));
227 std::cout <<
"found vertex "<<VertexAndGraph<Graph>(u, vars.g)<<
" with outdeg = "<< out_degree(*(i.first), aux)<<
" & indeg = "<< in_degree(*(i.first), aux) << std::endl;
228 std::cout <<
"we have: ";
229 if( is_true(getValue(vars.ys.at(u)))) std::cout<<
"ys ";
230 if( is_true(getValue(vars.yt.at(u)))) std::cout<<
"yt ";
231 if( is_true(getValue(vars.zs.at(u)))) std::cout<<
"zs ";
232 if( is_true(getValue(vars.zt.at(u)))) std::cout<<
"zt ";
233 for(VertexIterRange<AuxGraph> j = vertices(aux); j.first != j.second; ++j.first){
234 const Vertex<Graph> v = translate.right.at(*(j.first));
235 VertexPair<Graph> uv(u, v);
236 VertexPair<Graph> vu(v, u);
238 for(unsigned l = 0; l < 3; ++l){
240 if(contains(vars.y[l], uv) && is_true(getValue(vars.y[l].at(uv)))) std::cout <<
"y["<<l<<
"]_"<<VertexPairAndGraph<Graph>(uv,vars.g)<<
", ";
241 if(contains(vars.y[l], vu) && is_true(getValue(vars.y[l].at(vu)))) std::cout <<
"y["<<l<<
"]_"<<VertexPairAndGraph<Graph>(vu,vars.g)<<
", ";
243 if(contains(vars.z[l], uv) && is_true(getValue(vars.z[l].at(uv)))) std::cout <<
"z["<<l<<
"]_"<<VertexPairAndGraph<Graph>(uv,vars.g)<<
", ";
244 if(contains(vars.z[l], vu) && is_true(getValue(vars.z[l].at(vu)))) std::cout <<
"z["<<l<<
"]_"<<VertexPairAndGraph<Graph>(vu,vars.g)<<
", ";
247 std::cout << std::endl;
249 Vertex<AuxGraph> ux = *(i.first);
250 while(in_degree(ux, aux) > 0){
251 const auto in_edge = in_edges(ux, aux).first;
252 ux = source(*in_edge, aux);
253 std::cout << VertexAndGraph<Graph>(translate.right.at(ux), vars.g)<<
" has indeg "<<in_degree(ux, aux)<<std::endl;
255 while(out_degree(ux, aux) > 0){
256 const auto out_edge = out_edges(ux, aux).first;
257 ux = target(*out_edge, aux);
258 std::cout << VertexAndGraph<Graph>(translate.right.at(ux), vars.g)<<
" has outdeg "<<out_degree(ux, aux)<<std::endl;
265 #ifdef LAZY_ADD_ALL_CYCLES
266 while(num_edges(aux)){
268 std::stack<Edge<AuxGraph> > e_pending;
269 e_pending.push(*(edges(aux).first));
270 IloExpr y_expr(getEnv()), z_expr(getEnv()), y_rev_expr(getEnv()), z_rev_expr(getEnv());
272 DEBUG3(std::cout <<
"forbidding cycle: ");
273 while(!e_pending.empty()){
274 const Edge<AuxGraph> e = e_pending.top(); e_pending.pop();
275 const Vertex<AuxGraph> u = source(e, aux);
276 const Vertex<AuxGraph> v = target(e, aux);
277 const Vertex<AuxGraph> old_u = translate.right.at(u);
278 const Vertex<AuxGraph> old_v = translate.right.at(v);
279 const VertexPair<Graph> old_uv = VertexPair<Graph>(old_u, old_v);
280 const VertexPair<Graph> old_vu = VertexPair<Graph>(old_v, old_u);
281 DEBUG3(std::cout << VertexPairAndGraph<Graph>(old_uv, vars.g)<<
" ");
283 const unsigned char deg = out_degree(v, aux);
286 if(deg == 1) e_pending.push(*(out_edges(v, aux).first));
288 add_corresponding_variable(vars.y, old_uv, y_expr);
289 add_corresponding_variable(vars.y, old_vu, y_rev_expr);
290 add_corresponding_variable(vars.z, old_uv, z_expr);
291 add_corresponding_variable(vars.z, old_vu, z_rev_expr);
294 DEBUG5(std::cout <<
" by allowing only <"<<count<<
" of its edges");
295 DEBUG3(std::cout << std::endl);
298 add(y_rev_expr < count);
299 add(z_rev_expr < count);
301 #ifdef LAZY_ADD_ALL_CYCLES
306 DEBUG2(std::cout <<
"======= callback called, found "<<num_p<<
" paths & added "<<num_new_cuts<<
" new cuts ======"<<std::endl);
307 stats.times_called++;
308 stats.cuts_added += num_new_cuts;
309 stats.time_spent += ( (double)(clock() - start_time))/CLOCKS_PER_SEC;
315 template<
class Graph>
316 void populate_variables(IloEnv& env, var_collection<Graph>& vars,
const Instance<Graph>& I){
318 DEBUG5(std::cout <<
"creating x_ij/y_ij/z_ij"<<std::endl);
320 for(EdgeIterRange<Graph> e = edges(g); e.first != e.second; ++e.first){
321 const Vertex<Graph>& u = source(*e.first, g);
322 const Vertex<Graph>& v = target(*e.first, g);
323 const std::string uname = std::to_string(
get(vertex_name, g, u));
324 const std::string vname = std::to_string(
get(vertex_name, g, v));
325 const std::string name(
"x_" + uname +
"," + vname);
326 DEBUG5(std::cout <<
"creating "<<name<<std::endl);
327 vars.x.emplace(std::piecewise_construct, std::make_tuple(u, v), std::make_tuple(env, 0, 1, IloNumVar::Bool, name.c_str()));
329 for(
unsigned k = 0; k < 3; ++k)
if( I.is_matching_edge(*e.first) == (k == 0)){
330 const std::string yname_uv(
"y^" + std::to_string(k) +
"_" + uname +
"," + vname);
331 const std::string yname_vu(
"y^" + std::to_string(k) +
"_" + vname +
"," + uname);
332 const std::string zname_uv(
"z^" + std::to_string(k) +
"_" + uname +
"," + vname);
333 const std::string zname_vu(
"z^" + std::to_string(k) +
"_" + vname +
"," + uname);
336 EdgeVarMap<Graph>& yk_map = vars.y[k];
338 yk_map.emplace(std::piecewise_construct, std::make_tuple(u,v), std::make_tuple(env, 0, 1, IloNumVar::Bool, yname_uv.c_str()));
339 yk_map.emplace(std::piecewise_construct, std::make_tuple(v,u), std::make_tuple(env, 0, 1, IloNumVar::Bool, yname_vu.c_str()));
341 EdgeVarMap<Graph>& zk_map = vars.z[k];
343 zk_map.emplace(std::piecewise_construct, std::make_tuple(u,v), std::make_tuple(env, 0, 1, IloNumVar::Bool, zname_uv.c_str()));
344 zk_map.emplace(std::piecewise_construct, std::make_tuple(v,u), std::make_tuple(env, 0, 1, IloNumVar::Bool, zname_vu.c_str()));
347 DEBUG5(std::cout <<
"creating sp/tp/sc/tc interaction variables"<<std::endl);
349 for(VertexIterRange<Graph> v = vertices(g); v.first != v.second; ++v.first){
350 const std::string name(std::to_string(
get(vertex_name, g, *v.first)));
352 vars.ys[*v.first] = IloNumVar(env, 0, 1, IloNumVar::Bool, (
"y_sp," + name).c_str());
354 vars.yt[*v.first] = IloNumVar(env, 0, 1, IloNumVar::Bool, (
"y_" + name +
",tp").c_str());
356 vars.zs[*v.first] = IloNumVar(env, 0, 1, IloNumVar::Bool, (
"z_sc," + name).c_str());
358 vars.zt[*v.first] = IloNumVar(env, 0, 1, IloNumVar::Bool, (
"z_" + name +
",tc").c_str());
363 template<
class Graph>
364 void populate_constraints(IloEnv& env, IloRangeArray& c,
const var_collection<Graph>& vars,
const Instance<Graph>& I){
365 const Graph& g(*I.g);
368 DEBUG5(std::cout <<
"adding (27): of all the representations of ij, exactly x_ij should be chosen"<<std::endl);
369 for(
auto x : vars.x){
370 const VertexPair<Graph> uv = x.first;
371 const VertexPair<Graph> vu(uv.second, uv.first);
374 for(
size_t l = 0; l < 3; ++l)
if( I.is_matching_edge(uv) == (l == 0)){
376 expr += vars.y[l].at(uv);
377 expr += vars.y[l].at(vu);
379 expr += vars.z[l].at(uv);
380 expr += vars.z[l].at(vu);
387 DEBUG5(std::cout <<
"adding (28): all matching-edges should be chosen"<<std::endl);
389 if(I.is_matching_edge(x.first)) c.add(x.second == 1);
392 DEBUG5(std::cout <<
"adding (35)&(36): there should be at most sigma_p paths & sigma_c cycles"<<std::endl);
394 IloExpr y_expr(env), z_expr(env);
395 for(
auto y : vars.ys) y_expr += y.second;
396 for(
auto z : vars.zs) z_expr += z.second;
397 c.add(y_expr <= (
double)I.num_paths);
398 c.add(z_expr <= (
double)I.num_cycles);
402 DEBUG5(std::cout <<
"adding (29-33): flow conservation"<<std::endl);
403 for(
size_t l = 0; l < 3; ++l){
404 const unsigned prev_l = (l+1) % 2;
405 for(VertexIterRange<Graph> v = vertices(g); v.first != v.second; ++v.first){
406 if(l < 2) DEBUG5(std::cout <<
"for "<<VertexAndGraph<Graph>(*v.first, g)<<
" in layer "<<l<<
" adding\t\t 0 =");
407 IloExpr y_expr(env), z_expr(env);
408 for(AdjIterRange<Graph> nh = adjacent_vertices(*v.first, g); nh.first != nh.second; ++nh.first){
409 const VertexPair<Graph> vu = std::make_pair(*v.first, *nh.first);
410 const VertexPair<Graph> uv = std::make_pair(*nh.first, *v.first);
412 if( ((I.matched_with(*v.first) == *nh.first) == (l == 0)) && (l < 2)){
414 y_expr += vars.y[l].at(vu);
415 DEBUG5(std::cout <<
" + y["<<l<<
"]_"<<VertexPairAndGraph<Graph>(vu, vars.g));
417 z_expr += vars.z[l].at(vu);
418 if(l == 1) z_expr += vars.z[2].at(vu);
420 if( (I.matched_with(*v.first) == *nh.first) == (prev_l == 0)){
423 y_expr -= vars.y[prev_l].at(uv);
424 DEBUG5(std::cout <<
" - y["<<prev_l<<
"]_"<<VertexPairAndGraph<Graph>(uv, vars.g));
425 z_expr -= vars.z[prev_l].at(uv);
428 z_expr -= vars.z[2].at(uv);
433 if(l == 0) y_expr -= vars.ys.at(*v.first);
434 if(l == 0) DEBUG5(std::cout <<
" - ys_"<<VertexAndGraph<Graph>(*v.first, vars.g));
435 if(l == 1) y_expr += vars.yt.at(*v.first);
436 if(l == 1) DEBUG5(std::cout <<
"+ yt_"<<VertexAndGraph<Graph>(*v.first, vars.g));
438 if(l == 0) z_expr -= vars.zs.at(*v.first);
439 if(l == 2) z_expr += vars.zt.at(*v.first);
440 if(l < 2) DEBUG5(std::cout << std::endl);
441 if(l < 2) c.add(y_expr == 0);
447 DEBUG5(std::cout <<
"adding (34): cycle conservation"<<std::endl);
448 for(VertexIterRange<Graph> v = vertices(g); v.first != v.second; ++v.first){
450 z_expr += vars.zs.at(*v.first);
451 z_expr -= vars.zt.at(*v.first);
462 template<
class Graph>
463 void populate_model(IloModel& model, IloEnv& env, var_collection<Graph>& vars,
const Instance<Graph>& I){
464 const Graph& g(*I.g);
465 IloRangeArray c(env);
467 DEBUG3(std::cout <<
"creating variables"<<std::endl);
468 populate_variables(env, vars, I);
470 DEBUG3(std::cout <<
"creating optimization expression"<<std::endl);
471 IloExpr opt_exp(env);
472 for(EdgeIterRange<Graph> e = edges(g); e.first != e.second; ++e.first){
473 const Vertex<Graph>& u = source(*e.first, g);
474 const Vertex<Graph>& v = target(*e.first, g);
476 auto uv = vars.x.find(std::make_pair(u,v));
477 if(uv == vars.x.end()) uv = vars.x.find(std::make_pair(v,u));
478 assert(uv != vars.x.end());
480 opt_exp += (double)
get(edge_weight, g, *e.first) * uv->second;
482 model.add(IloMaximize(env, opt_exp));
484 DEBUG3(std::cout <<
"creating constraints"<<std::endl);
485 populate_constraints(env, c, vars, I);
Definition: read_adj_list.hpp:22