Scaffolding  0.1
This program can assemble genome scaffolds using the pairing information in paired-end reads.
ilp_model_onegraph.hpp
1 
2 
3 #ifndef ILP_MODEL_HPP
4 #define ILP_MODEL_HPP
5 
6 #include <string>
7 #include <vector>
8 
9 #include "ilp_common.hpp"
10 
11 #include "utils/profiling.hpp"
12 
13 
14 
15 namespace scaffold{
16 
17  template<class Graph>
18  struct var_collection : public var_collection_virt<Graph> {
19  EdgeVarMap<Graph> y;
20  EdgeVarMap<Graph> w;
21 
22  var_collection(const Instance<Graph>& _I):
23  var_collection_virt<Graph>::var_collection_virt(_I) {}
24  };
25 
26  // block isolated cycles as lazy constraint callback
27  template<class Graph>
28  class LazyCyclesBlockCallback: public IloCplex::LazyConstraintCallbackI{
29  const var_collection<Graph>& vars;
30  callback_statistics& stats;
31  public:
32 
33  IloCplex::CallbackI* duplicateCallback() const{
34  return (new (getEnv()) LazyCyclesBlockCallback(getEnv(), vars, stats));
35  }
36 
37  LazyCyclesBlockCallback(IloEnv env, const var_collection<Graph>& _vars, callback_statistics& _stats):
38  IloCplex::LazyConstraintCallbackI(env),
39  vars(_vars),
40  stats(_stats)
41  {}
42 
43  void output_current_aux_graph(const AuxGraph& aux, const TranslateBiMap<Graph,AuxGraph>& translate) const;
44  void main();
45 
46  // adds an arc to the auxiliary graph, given an IloNumVar and a VertexPair
47  void add_arc_from_variable(
48  const IloNumVar& var,
49  const VertexPair<Graph>& uv,
50  const TranslateBiMap<Graph,AuxGraph>& translate,
51  AuxGraph& aux) const
52  {
53  if(is_true(getValue(var))){
54  const Vertex<Graph> u = uv.first;
55  const Vertex<Graph> v = uv.second;
56  const Vertex<AuxGraph> new_u = translate.left.at(u);
57  const Vertex<AuxGraph> new_v = translate.left.at(v);
58  add_edge(new_u, new_v, aux);
59  DEBUG5(std::cout << "adding "<<VertexPairAndGraph<Graph>(uv, *vars.I.g)<<std::endl);
60  } else DEBUG5(std::cout << "not adding "<<VertexPairAndGraph<Graph>(uv, *vars.I.g)<< " since "<<getValue(var)<<" translates to "<<is_true(getValue(var))<<std::endl);
61  }
62 
63  }; // class
64 
65  // callback function
66  template<class Graph>
67  IloCplex::Callback GetLazyCyclesBlock(IloEnv env, const var_collection<Graph>& vars, callback_statistics& stats) {
68  return (new (env) LazyCyclesBlockCallback<Graph>(env, vars, stats));
69  }
70 
71 
72  template<class Graph>
73  void LazyCyclesBlockCallback<Graph>::output_current_aux_graph(const AuxGraph& aux, const TranslateBiMap<Graph,AuxGraph>& translate) const{
74  const Graph& g = *vars.I.g;
75  for(VertexIterRange<AuxGraph> i = vertices(aux); i.first != i.second; ++i.first)
76  if(out_degree(*(i.first), aux) != in_degree(*(i.first), aux)){
77  const Vertex<Graph> u = translate.right.at(*(i.first));
78 
79  std::cout << "found vertex "<<VertexAndGraph<Graph>(u, g)<<" with outdeg = "<< out_degree(*(i.first), aux)<<" & indeg = "<< in_degree(*(i.first), aux) << std::endl;
80  std::cout << "we have: ";
81  if( is_true(getValue(vars.s.at(u)))) std::cout<<"s ";
82  if( is_true(getValue(vars.tp.at(u)))) std::cout<<"tp ";
83  if( is_true(getValue(vars.tc.at(u)))) std::cout<<"tc ";
84  for(VertexIterRange<AuxGraph> j = vertices(aux); j.first != j.second; ++j.first){
85  const Vertex<Graph> v = translate.right.at(*(j.first));
86  VertexPair<Graph> uv(u, v);
87  VertexPair<Graph> vu(v, u);
88 
89  if(contains(vars.y, uv) && is_true(getValue(vars.y.at(uv)))) std::cout << "y_"<<VertexPairAndGraph<Graph>(uv,g)<<", ";
90  if(contains(vars.y, vu) && is_true(getValue(vars.y.at(vu)))) std::cout << "y_"<<VertexPairAndGraph<Graph>(vu,g)<<", ";
91  }// for
92  std::cout << std::endl;
93 
94  Vertex<AuxGraph> ux = *(i.first);
95  while(in_degree(ux, aux) > 0){
96  const auto in_edge = in_edges(ux, aux).first;
97  ux = source(*in_edge, aux);
98  std::cout << VertexAndGraph<Graph>(translate.right.at(ux), g)<< " has indeg "<<in_degree(ux, aux)<<std::endl;
99  }
100  while(out_degree(ux, aux) > 0){
101  const auto out_edge = out_edges(ux, aux).first;
102  ux = target(*out_edge, aux);
103  std::cout << VertexAndGraph<Graph>(translate.right.at(ux), g)<< " has outdeg "<<out_degree(ux, aux)<<std::endl;
104  }
105 
106  exit(1);
107  }// if & for
108  }
109 
110  template<class Graph>
111  void LazyCyclesBlockCallback<Graph>::main(){
112  stats.time_spent.resume();
113  AuxGraph aux;
114 
115  const Instance<Graph>& I = vars.I;
116  const Graph& g = *I.g;
117 
118  // Step 1: construct auxiliary graph
119  TranslateBiMap<Graph, AuxGraph> translate;
120  typedef typename TranslateBiMap<Graph, AuxGraph>::value_type TranslatePair;
121 
122  DEBUG5(
123  std::cout << "solution edges: "<<std::endl;
124  for(EdgeIterRange<Graph> e = edges(g); e.first != e.second; ++e.first){
125  const Vertex<Graph>& u = source(*e.first, g);
126  const Vertex<Graph>& v = target(*e.first, g);
127  // get the appropriate x_ij variable
128  const VertexPair<Graph> uv(u, v);
129  const VertexPair<Graph> vu(v, u);
130  std::cout<<VertexPairAndGraph<Graph>(uv, g)<<" by ";
131  if(contains(vars.y, uv) && is_true(getValue(vars.y.at(uv)))) std::cout << "y_"<<VertexPairAndGraph<Graph>(uv,g)<<", ";
132  if(contains(vars.y, vu) && is_true(getValue(vars.y.at(vu)))) std::cout << "y_"<<VertexPairAndGraph<Graph>(vu,g)<<", ";
133  std::cout << std::endl;
134  }
135  );
136 
137  // create vertices
138  for( VertexIterRange<Graph> i = vertices(g); i.first != i.second; ++i.first ){
139  const Vertex<Graph> orig_u = *(i.first);
140  const Vertex<AuxGraph> new_u = add_vertex(aux);
141  translate.insert(TranslatePair(orig_u, new_u));
142  DEBUG5(std::cout << "adding "<<VertexAndGraph<Graph>(orig_u, g)<<std::endl);
143  }
144  // create arcs, merging y-arcs and z-arcs into one auxilliary digraph
145  DEBUG5(std::cout << "adding y arcs"<<std::endl);
146  for( const auto& i : vars.y ) add_arc_from_variable(i.second, i.first, translate, aux);
147 
148  // Step 2: remove all arcs reachable from s
149  std::stack<Vertex<AuxGraph> > pending;
150  for( const auto& i : vars.s )
151  if(is_true(getValue(i.second))) pending.push(translate.left.at(i.first));
152 
153  DEBUG5(
154  std::cout<<"coming from y_s: ";
155  for( const auto& i : vars.s )
156  if(is_true(getValue(i.second))) std::cout << VertexAndGraph<Graph>(i.first, g)<<" ";
157  std::cout<<std::endl;
158  std::cout<<"going to tp: ";
159  for( const auto& i : vars.tp )
160  if(is_true(getValue(i.second))) std::cout << VertexAndGraph<Graph>(i.first, g)<<" ";
161  std::cout<< std::endl<<"going to tc: ";
162  for( const auto& i : vars.tc )
163  if(is_true(getValue(i.second))) std::cout << VertexAndGraph<Graph>(i.first, g)<<" ";
164  std::cout<<std::endl;
165  );
166 
167  DEBUG2(unsigned num_p = pending.size();)
168 
169  while(!pending.empty()){
170  const Vertex<AuxGraph> u = pending.top(); pending.pop();
171  // note that u has an incoming arc y_s -> u, so, by flow conservation, it has only 1 outgoing arc
172  const unsigned char deg = out_degree(u, aux);
173  assert(deg <= 1);
174  // if this outgoing arc is not to y_t, then add the next vertex to pending & remove the arc
175  if(deg == 1) {
176  const Edge<AuxGraph> e = *(out_edges(u, aux).first);
177  const Vertex<AuxGraph> v = target(e, aux);
178  DEBUG5(std::cout << VertexAndGraph<Graph>(translate.right.at(v), g) << " is on a path by edge from "<<VertexAndGraph<Graph>(translate.right.at(source(e, aux)), g)<<std::endl);
179  pending.push(v);
180  remove_edge(e, aux);
181  }
182  }
183 
184  // Step 3: find a cycle in the remaining part of the graph
185  DEBUG5(std::cout<< "remaining arcs in auxilliary graph: "<<num_edges(aux)<<std::endl;);
186  unsigned num_new_cuts = 0;
187  if(num_edges(aux)){
188  DEBUG5(output_current_aux_graph(aux, translate));
189 
190 #ifdef LAZY_ADD_ALL_CYCLES
191  while(num_edges(aux)){
192 #endif
193  Edge<AuxGraph> uv = *(edges(aux).first);
194  IloExpr expr(getEnv()), rev_expr(getEnv());
195  unsigned count = 0;
196  unsigned v_deg = 0;
197  DEBUG3(std::cout << "forbidding cycle: ");
198  do{
199  const Vertex<AuxGraph> u = source(uv, aux);
200  const Vertex<AuxGraph> v = target(uv, aux);
201  const Vertex<Graph> old_u = translate.right.at(u);
202  const Vertex<Graph> old_v = translate.right.at(v);
203  const VertexPair<Graph> old_uv = VertexPair<Graph>(old_u, old_v);
204  const VertexPair<Graph> old_vu = VertexPair<Graph>(old_v, old_u);
205 
206  DEBUG3(std::cout << VertexPairAndGraph<Graph>(old_uv, g)<<" ");
207  remove_edge(uv, aux);
208  v_deg = boost::out_degree(v, aux);
209  assert(v_deg <= 1);
210  // if this outgoing arc is not to our initial arc, then add the next vertex to pending & remove the arc
211  if(v_deg == 1) uv = *(out_edges(v, aux).first);
212 
213  // forbid the cycle and its reverse in y and z
214  expr += vars.y.at(old_uv);
215  expr -= vars.tc.at(old_v);
216 // rev_expr += vars.y.at(old_vu);
217 // rev_expr -= vars.tc.at(old_u);
218  expr += vars.y.at(old_vu);
219 
220  ++count;
221  } while(v_deg != 0);
222  DEBUG5(std::cout << " by allowing only <"<<count<<" of its edges (unless it's connected to t_c)");
223  DEBUG3(std::cout << std::endl);
224  add(expr < count);
225 // add(rev_expr < count);
226 // num_new_cuts += 2;
227  num_new_cuts++;
228 #ifdef LAZY_ADD_ALL_CYCLES
229  }// while(num_edges(aux))
230 #endif
231  }// if(num_edges(aux))
232  // Step 4: collect statistical information
233  DEBUG2(std::cout << "======= callback called, found "<<num_p<<" paths & added "<<num_new_cuts<<" new cuts ======"<<std::endl);
234  stats.times_called++;
235  stats.cuts_added += num_new_cuts;
236  stats.time_spent.pause();
237  } // main()
238 
239 
240 
241  // initialize the variables for the ILP formulation
242  template<class Graph>
243  void populate_variables(IloEnv& env, var_collection<Graph>& vars, const ilp_options& opts){
244  const Instance<Graph>& I = vars.I;
245  const Graph& g = *I.g;
246  DEBUG5(std::cout << "creating x_{u,v}/y_uv"<<std::endl);
247  for(EdgeIterRange<Graph> e = edges(g); e.first != e.second; ++e.first){
248  const Vertex<Graph>& u = source(*e.first, g);
249  const Vertex<Graph>& v = target(*e.first, g);
250  const std::string uname = std::to_string(boost::get(boost::vertex_name, g, u));
251  const std::string vname = std::to_string(boost::get(boost::vertex_name, g, v));
252 
253  // Step 1: add variables x_e = 1 <=> e belogs to a solution path/cycle
254  const std::string xname("x_" + uname + "," + vname);
255  DEBUG5(std::cout << "creating "<<xname<<std::endl);
256  vars.x.emplace(std::piecewise_construct, std::make_tuple(u,v), std::make_tuple(env, 0, 1, IloNumVar::Bool, xname.c_str()));
257  // Step 2: add variables y^k_ij
258  const std::string yname1("y_" + uname + "," + vname);
259  const std::string yname2("y_" + vname + "," + uname);
260  DEBUG5(std::cout << "creating "<<yname1 << " & "<< yname2<<std::endl);
261  vars.y.emplace(std::piecewise_construct, std::make_tuple(u,v), std::make_tuple(env, 0, 1, IloNumVar::Bool, yname1.c_str()));
262  vars.y.emplace(std::piecewise_construct, std::make_tuple(v,u), std::make_tuple(env, 0, 1, IloNumVar::Bool, yname2.c_str()));
263 
264  if(opts.account_for_contig_jumps){
265  const std::string wname1("w_" + uname + "," + vname);
266  const std::string wname2("w_" + vname + "," + uname);
267  DEBUG5(std::cout << "creating "<<wname1 << " & "<< wname2<<std::endl);
268  vars.w.emplace(std::piecewise_construct, std::make_tuple(u,v), std::make_tuple(env, 0, 1, IloNumVar::Bool, wname1.c_str()));
269  vars.w.emplace(std::piecewise_construct, std::make_tuple(v,u), std::make_tuple(env, 0, 1, IloNumVar::Bool, wname2.c_str()));
270  }
271  }
272  DEBUG5(std::cout << "creating s/tp/tc interaction variables"<<std::endl);
273  // Step 3: add variables y_s,i and y_i,tp and y_i,tc
274  for(VertexIterRange<Graph> v = vertices(g); v.first != v.second; ++v.first){
275  const std::string name(std::to_string(get(vertex_name, g, *v.first)));
276  // on layer 0, come up from s
277  vars.s[*v.first] = IloNumVar(env, 0, 1, IloNumVar::Bool, ("y_s," + name).c_str());
278  // on layer 1, go up to tp
279  vars.tp[*v.first] = IloNumVar(env, 0, 1, IloNumVar::Bool, ("y_" + name + ",tp").c_str());
280  // on layer 0, go up to tc
281  vars.tc[*v.first] = IloNumVar(env, 0, 1, IloNumVar::Bool, ("y_" + name + ",tc").c_str());
282  }
283  }
284 
285  // initialize the constraints for the ILP formulation
286  template<class Graph>
287  void populate_constraints(IloEnv& env, IloRangeArray& c, const var_collection<Graph>& vars, const ilp_options& opts){
288  const Instance<Graph>& I = vars.I;
289  const Graph& g(*I.g);
290 
291  // constraint (1): of all the representations of ij, exactly x_ij should be chosen!
292  DEBUG5(std::cout << "adding (1): of all the representations of e, exactly x_e should be chosen"<<std::endl);
293  for(auto x : vars.x){
294  const VertexPair<Graph> uv = x.first;
295  const VertexPair<Graph> vu(uv.second, uv.first);
296 
297  IloExpr expr(env);
298  expr += vars.y.at(uv);
299  expr += vars.y.at(vu);
300 
301  // use the w variables (indicating that there is a contig jump over uv or vu) to
302  // allow x = 1 even if neither uv nor vu have been chosen by y
303  if(opts.account_for_contig_jumps){
304  expr += vars.w.at(uv);
305  expr += vars.w.at(vu);
306  }
307 
308  expr -= x.second;
309  c.add(expr == 0);
310  }
311 
312  // constraint (2): all matching-edges should be chosen!
313  DEBUG5(std::cout << "adding (2): all matching-edges should be chosen"<<std::endl);
314  for(auto x : vars.x)
315  if(I.is_matching_edge(x.first)) c.add(x.second == 1);
316 
317  if(opts.limit_paths_and_cycles_individually){
318  // constraint (4),(6): there should be at most sigma_p paths & sigma_c cycles
319  DEBUG5(std::cout << "adding (4)&(6): there should be at most sigma_p paths & sigma_c cycles"<<std::endl);
320  IloExpr p_expr(env), c_expr(env);
321  for(auto y : vars.tp) p_expr += y.second;
322  for(auto y : vars.tc) c_expr += y.second;
323  c.add(p_expr <= (double)I.num_paths);
324  c.add(c_expr <= (double)I.num_cycles);
325  } else {
326  // constraint (4),(6): there should be at most sigma_p objects (paths or cycles)
327  DEBUG5(std::cout << "adding (4)&(6): there should be at most sigma_p (possibly closed) paths"<<std::endl);
328  IloExpr expr(env);
329  for(auto y : vars.s) expr += y.second;
330  c.add(expr <= (double)I.num_paths);
331  }
332 
333  // constraint (3): everything that comes into layer l should leave layer l
334  DEBUG5(std::cout << "adding (3): flow conservation"<<std::endl);
335  for(unsigned layer = 0; layer < 2; ++layer){
336  for(VertexIterRange<Graph> u = boost::vertices(g); u.first != u.second; ++u.first){
337  IloExpr expr(env);
338  for(AdjIterRange<Graph> nh = boost::adjacent_vertices(*u.first, g); nh.first != nh.second; ++nh.first){
339  const VertexPair<Graph> uv = std::make_pair(*u.first, *nh.first);
340  // remember: layer 0 -> 1 contains only matching edges
341  if(I.is_matching_edge(uv) == (layer == 0)){
342  // flow goes to next layer along uv
343  expr += vars.y.at(uv);
344  DEBUG5(std::cout << " + y_" << VertexPairAndGraph<Graph>(uv, g));
345  } else {
346  const VertexPair<Graph> vu = std::make_pair(*nh.first, *u.first);
347  // flow comes from the other layer alog vu
348  expr -= vars.y.at(vu);
349  DEBUG5(std::cout << " - y_"<<VertexPairAndGraph<Graph>(vu, g));
350  }// if
351  }// for
352  if(layer == 0){
353  // on layer 0, p-flow can come up from s
354  DEBUG5(std::cout << " - y_s,"<<VertexAndGraph<Graph>(*u.first, g));
355  expr -= vars.s.at(*u.first);
356  // on layer 0, p-flow can go up to tc
357  DEBUG5(std::cout << " + y_"<<VertexAndGraph<Graph>(*u.first, g)<<",tc");
358  expr += vars.tc.at(*u.first);
359  } else {
360  // on layer 1, p-flow can go up to tp
361  DEBUG5(std::cout << " + y_"<<VertexAndGraph<Graph>(*u.first, g)<<",tp");
362  expr += vars.tp.at(*u.first);
363  }// if
364  c.add(expr == 0);
365  }// for
366  }// for
367 
368  // constraint (7): everything that goes to tc has to come in from s
369  DEBUG5(std::cout << "adding (7): cycle conservation"<<std::endl);
370  for(VertexIterRange<Graph> u = boost::vertices(g); u.first != u.second; ++u.first){
371  IloExpr expr(env);
372  expr += vars.s.at(*u.first);
373  expr -= vars.tc.at(*u.first);
374  c.add(expr >= 0);
375  }
376 
377  // constraint (5): each subset S of vertices should contain at most |S|-1 endpoints of arcs
378  //DEBUG5(std::cout << "adding (5): each subset S of vertices should contain at most |S|-1 endpoints of arcs"<<std::endl);
379  // done via callback!
380 
381  if(opts.account_for_contig_jumps){
382  // constraint: contig jumps
383  DEBUG5(std::cout << "adding contig jump constraints"<<std::endl);
384  EdgeExpMap<Graph> expr;
385 
386 #warning TODO: proper handling of contig jumps!
387 
388  for(VertexIterRange<Graph> ur = boost::vertices(g); ur.first != ur.second; ++ur.first){
389  const Vertex<Graph> u(*ur.first);
390  for(AdjIterRange<Graph> vr = boost::adjacent_vertices(u, g); vr.first != vr.second; ++vr.first) {
391  const Vertex<Graph> v(*vr.first);
392  const VertexPair<Graph> uv(u,v);
393  IloExpr uv_expr(env);
394 
395  if(!I.is_matching_edge(uv)){
396  const std::string uname = std::to_string(boost::get(boost::vertex_name, g, u));
397  const std::string vname = std::to_string(boost::get(boost::vertex_name, g, v));
398 
399 
400  // check whether we have a 3-path over uv
401  for(AdjIterRange<Graph> xr = boost::adjacent_vertices(u, g); xr.first != xr.second; ++xr.first) if(xr.first != vr.first){
402  const Vertex<Graph> x(*xr.first);
403  const Vertex<Graph> y(I.matched_with(x));
404 
405  if(y != u){
406  // get the length of the contig xy
407  const Edge<Graph> e_xy(boost::edge(x, y, g).first);
408  const size_t xy_contig_length = boost::get(boost::edge_length, g, e_xy);
409 
410  // check if the contig is small and that g contains uy
411  if( (xy_contig_length < I.insert_size) && (boost::edge(v, y, g).second) ){
412  // add the constraint: if the 3 arcs of the path u-x-y-v are taken, allow to take uv as well (via variable w)
413  // to this end, add a bool variable indicating that the path was taken (in this direction)
414 
415  const std::string xname = std::to_string(boost::get(boost::vertex_name, g, x));
416  const std::string yname = std::to_string(boost::get(boost::vertex_name, g, y));
417  const std::string pname("p_" + uname + "," + xname + "," + yname + "," + vname);
418  DEBUG5(std::cout << "creating "<<pname << std::endl);
419 
420  IloNumVar p(env, 0, 1, IloNumVar::Bool, pname.c_str());
421 
422  // if we have a 3-path over uv, force a solution to take both ux and yv in order to take w[uv]
423  IloExpr p_expr(env);
424  p_expr += vars.y.at(VertexPair<Graph>(u, x));
425  //p_expr += vars.y.at(VertexPair<Graph>(x, y)); // we know this is 1 in every solution
426  p_expr += vars.y.at(VertexPair<Graph>(y, v));
427  p_expr -= (p * 2);
428  c.add(p_expr >= 0);
429 
430  // for each path that is completely taken in a solution, allow taking w[uv]
431  uv_expr += p;
432  }// if uy adjacent and contig small
433  }// if u != y
434  }// for x
435  } else {
436  // if uv IS a matching edge, see if its degrees are (1,2) and, if so, allow taking uv & vx via w
437  if( (boost::degree(u, g) == 1) && (boost::degree(v, g) == 2)){
438  const Edge<Graph> e_uv(boost::edge(u, v, g).first);
439  const size_t uv_contig_length = boost::get(boost::edge_length, g, e_uv);
440  if(uv_contig_length < I.insert_size){
441  }
442  }
443  } // if uv not matching
444  uv_expr -= vars.w.at(uv);
445  c.add(uv_expr >= 0);
446  }// for v
447  }// for u
448  }// if
449  }// function
450 
451 
452  // compute the ILP formulation for a graph g
453  template<class Graph>
454  void populate_model(IloModel& model, IloEnv& env, var_collection<Graph>& vars, const ilp_options& opts){
455  const Instance<Graph>& I = vars.I;
456  const Graph& g(*I.g);
457  IloRangeArray c(env);
458  // construct variables, keeping note of the x_ij, y^l_ij and z^l_ij variables
459  DEBUG3(std::cout << "creating variables"<<std::endl);
460  populate_variables(env, vars, opts);
461  // add optimization function
462  DEBUG3(std::cout << "creating optimization expression"<<std::endl);
463  IloExpr opt_exp(env);
464  for(EdgeIterRange<Graph> e = edges(g); e.first != e.second; ++e.first){
465  const Vertex<Graph>& u = source(*e.first, g);
466  const Vertex<Graph>& v = target(*e.first, g);
467  // get the appropriate x_ij variable
468  auto uv = vars.x.find(VertexPair<Graph>(u,v));
469  if(uv == vars.x.end()) uv = vars.x.find(VertexPair<Graph>(v,u));
470  assert(uv != vars.x.end());
471  // add the term weight(e)*x_e
472  opt_exp += (double)boost::get(boost::edge_weight, g, *e.first) * uv->second;
473  }
474  model.add(IloMaximize(env, opt_exp));
475  // construct constraints
476  DEBUG3(std::cout << "creating constraints"<<std::endl);
477  populate_constraints(env, c, vars, opts);
478  model.add(c);
479  }
480 
481 }
482 
483 #endif
484 
Definition: read_adj_list.hpp:22