Scaffolding  0.1
This program can assemble genome scaffolds using the pairing information in paired-end reads.
ilp_model.hpp
1 
2 
3 #ifndef ILP_MODEL_HPP
4 #define ILP_MODEL_HPP
5 
6 #include <string>
7 #include <vector>
8 #include <ilcplex/ilocplex.h>
9 
10 
11 #include "utils/utils.hpp"
12 #include "utils/graph_typedefs.hpp"
13 
14 
15 namespace scaffold{
16 
17  // a mapping of edges to variables
18  template<class Graph>
19  using edge_var_map = unordered_map<VertexPair<Graph>, IloNumVar, pair_hash<Vertex<Graph>,Vertex<Graph> > >;
20  // a mapping of vertices to variables
21  template<class Graph>
22  using vertex_var_map = unordered_map<Vertex<Graph>, IloNumVar>;
23 
24  template<class Graph>
26  edge_var_map<Graph> x;
27  unordered_map<size_t, edge_var_map<Graph> > y;
28  unordered_map<size_t, edge_var_map<Graph> > z;
29  unordered_map<size_t, vertex_var_map<Graph> > spy, ytp, scz, ztc;
30  };
31 
32  // initialize the variables for the ILP formulation
33  template<class Graph>
34  void populate_variables(IloEnv& env, var_collection<Graph>& vars, const Instance<Graph>& I){
35  Graph& g = *I.g;
36  const size_t n = num_vertices(g);
37  DEBUG3(std::cout << "creating x_ij/y_ij/z_ij"<<std::endl);
38  // Step 1: add variables x_ij = 1 <=> ij belogs to a path/cycle
39  for(EdgeIterRange<Graph> e = edges(g); e.first != e.second; ++e.first){
40  const Vertex<Graph>& u = source(*e.first, g);
41  const Vertex<Graph>& v = target(*e.first, g);
42  const std::string uname = std::to_string(get(vertex_name, g, u));
43  const std::string vname = std::to_string(get(vertex_name, g, v));
44  const std::string name("x_" + uname + "," + vname);
45  DEBUG4(std::cout << "creating "<<name<<std::endl);
46  vars.x.emplace(std::piecewise_construct, std::make_tuple(u, v), std::make_tuple(env, 0, 1, IloNumVar::Bool, name.c_str()));
47  // on even layers, use only matching edges, on odd layers, don't use matching edges
48  for(size_t l = 0; l < n - 1; ++l) if((l % 2 == 0) == (I.matched_with(u) == v)){
49  edge_var_map<Graph>& yl_map = vars.y[l];
50  edge_var_map<Graph>& zl_map = vars.z[l];
51  // Step 2: add variables y^l_ij
52  const std::string yname_uv( "y^" + std::to_string(l) + "_" + uname + "," + vname);
53  const std::string yname_vu( "y^" + std::to_string(l) + "_" + vname + "," + uname);
54  yl_map.emplace(std::piecewise_construct, std::make_tuple(u,v), std::make_tuple(env, 0, 1, IloNumVar::Bool, yname_uv.c_str()));
55  yl_map.emplace(std::piecewise_construct, std::make_tuple(v,u), std::make_tuple(env, 0, 1, IloNumVar::Bool, yname_vu.c_str()));
56  // Step 3: add variables z^l_ij
57  const std::string zname_uv( "z^" + std::to_string(l) + "_" + uname + "," + vname);
58  const std::string zname_vu( "z^" + std::to_string(l) + "_" + vname + "," + uname);
59  zl_map.emplace(std::piecewise_construct, std::make_tuple(u,v), std::make_tuple(env, 0, 1, IloNumVar::Bool, zname_uv.c_str()));
60  zl_map.emplace(std::piecewise_construct, std::make_tuple(v,u), std::make_tuple(env, 0, 1, IloNumVar::Bool, zname_vu.c_str()));
61  }
62  }
63  DEBUG3(std::cout << "creating sp/tp/sc/tc interaction variables"<<std::endl);
64  // Step 4: add variables y^0_sp,i and y^n_i,tp (and the z-versions)
65  for(VertexIterRange<Graph> v = vertices(g); v.first != v.second; ++v.first){
66  const std::string name(std::to_string(get(vertex_name, g, *v.first)));
67  for(size_t l = 0; l < n; ++l){
68  const std::string lname(std::to_string(l) + "_");
69  if(l % 2 == 1){
70  // on all odd layers, go up to tp
71  vars.ytp[l][*v.first] = IloNumVar(env, 0, 1, IloNumVar::Bool, ("y^" + lname + name + ",tp").c_str());
72  } else{
73  // on all even layers, come up from sp
74  vars.spy[l][*v.first] = IloNumVar(env, 0, 1, IloNumVar::Bool, ("y^" + lname + "sp," + name).c_str());
75  // on all even layers, come up from sc
76  vars.scz[l][*v.first] = IloNumVar(env, 0, 1, IloNumVar::Bool, ("z^" + lname + "sc," + name).c_str());
77  // on all even layers, go up to tc
78  vars.ztc[l][*v.first] = IloNumVar(env, 0, 1, IloNumVar::Bool, ("z^" + lname + name + ",tc").c_str());
79  }
80  }
81  }
82  }
83 
84  // initialize the variables for the ILP formulation
85  template<class Graph>
86  void populate_constraints(IloEnv& env, IloRangeArray& c, const var_collection<Graph>& vars, const Instance<Graph>& I){
87  const Graph& g(*I.g);
88  const size_t n = num_vertices(g);
89 
90  // constraint (2): of all the representations of ij, exactly x_ij should be chosen!
91  DEBUG3(std::cout << "adding (2): of all the representations of ij, exactly x_ij should be chosen"<<std::endl);
92  for(auto x : vars.x){
93  VertexPair<Graph> uv = x.first;
94  VertexPair<Graph> vu = std::make_pair(uv.second, uv.first);
95  IloExpr expr(env);
96  for(size_t l = 0; l < n - 1; ++l) if((l % 2 == 0) == (I.is_matching_edge(uv))){
97  expr += vars.y.at(l).at(uv);
98  expr += vars.y.at(l).at(vu);
99  expr += vars.z.at(l).at(uv);
100  expr += vars.z.at(l).at(vu);
101  }
102  expr -= x.second;
103  c.add(expr == 0);
104  }
105 
106  // constraint (3): all matching-edges should be chosen!
107  DEBUG3(std::cout << "adding (3): all matching-edges should be chosen"<<std::endl);
108  for(auto x : vars.x)
109  if(I.is_matching_edge(x.first)) c.add(x.second == 1);
110 
111  // constraint (4),(8): there should be at most sigma_p paths & sigma_c cycles
112  DEBUG3(std::cout << "adding (4)&(8): there should be at most sigma_p paths & sigma_c cycles"<<std::endl);
113  {
114  IloExpr y_expr(env), z_expr(env);
115  for(size_t l = 0; l < n; ++l) if(l % 2 == 0){
116  for(auto y : vars.spy.at(l)) y_expr += y.second;
117  for(auto z : vars.scz.at(l)) z_expr += z.second;
118  }
119  c.add(y_expr <= (double)I.num_paths);
120  c.add(z_expr <= (double)I.num_cycles);
121  }
122 
123  // constraint (5),(6),(7),(9),(10),(11): everything that comes into layer l should leave layer l
124  DEBUG3(std::cout << "adding (5-7)&(9-11): everything that comes into layer l should leave layer l"<<std::endl);
125  for(size_t l = 0; l < n; ++l){
126  for(VertexIterRange<Graph> v = vertices(g); v.first != v.second; ++v.first){
127  IloExpr y_expr(env), z_expr(env);
128  for(AdjIterRange<Graph> nh = adjacent_vertices(*v.first, g); nh.first != nh.second; ++nh.first){
129  // remember: even layers contain only matching edges
130  if((l % 2 == 0) == (I.matched_with(*v.first) == *nh.first)){
131  // if we're not in the last layer, flow can go to the next layer
132  if(l < n - 1){
133  y_expr += vars.y.at(l).at(std::make_pair(*v.first, *nh.first));
134  z_expr += vars.z.at(l).at(std::make_pair(*v.first, *nh.first));
135  }
136  } else {
137  // if we're not in layer 0, then flow can come from the previous layer
138  if(l > 0){
139  y_expr -= vars.y.at(l-1).at(std::make_pair(*nh.first, *v.first));
140  z_expr -= vars.z.at(l-1).at(std::make_pair(*nh.first, *v.first));
141  }
142  }
143  }
144  // if l is odd, then flow can go up to tp & if l is even, then flow can come up from sp
145  if(l % 2 == 1) y_expr += vars.ytp.at(l).at(*v.first); else y_expr -= vars.spy.at(l).at(*v.first);
146  // if l is even, flow can come up from sc & go up to tc
147  if(l % 2 == 0) {
148  z_expr += vars.ztc.at(l).at(*v.first);
149  z_expr -= vars.scz.at(l).at(*v.first);
150  }
151 
152  c.add(y_expr == 0);
153  c.add(z_expr == 0);
154  }
155  }
156 
157  // constraint (12): if a cycle is started at vertex v, it should also end at v
158  // NOTE: the current formulation is "if a cycle starts at v, then SOME cycle also ends at v"
159  DEBUG3(std::cout << "adding (12): if a cycles starts at v, a cycle ends at v"<<std::endl);
160  for(VertexIterRange<Graph> v = vertices(g); v.first != v.second; ++v.first){
161  IloExpr expr(env);
162  for(size_t l = 0; l < n; ++l) if(l % 2 == 0){
163  expr += vars.scz.at(l).at(*v.first);
164  expr -= vars.ztc.at(l).at(*v.first);
165  }
166  c.add(expr == 0);
167  }
168  }
169 
170 
171  // compute the ILP formulation for a graph g
172  template<class Graph>
173  void populate_model(IloModel& model, IloEnv& env, var_collection<Graph>& vars, const Instance<Graph>& I){
174  const Graph& g(*I.g);
175  IloRangeArray c(env);
176  // construct variables, keeping note of the x_ij, y^l_ij and z^l_ij variables
177  DEBUG2(std::cout << "creating variables"<<std::endl);
178  populate_variables(env, vars, I);
179  // add optimization function
180  DEBUG2(std::cout << "creating optimization expression"<<std::endl);
181  IloExpr opt_exp(env);
182  for(EdgeIterRange<Graph> e = edges(g); e.first != e.second; ++e.first){
183  const Vertex<Graph>& u = source(*e.first, g);
184  const Vertex<Graph>& v = target(*e.first, g);
185  // get the appropriate x_ij variable
186  auto uv = vars.x.find(std::make_pair(u,v));
187  if(uv == vars.x.end()) uv = vars.x.find(std::make_pair(v,u));
188  assert(uv != vars.x.end());
189  // add the term weight(ij)*x_ij
190  opt_exp += (double)get(edge_weight, g, *e.first) * uv->second;
191  }
192  model.add(IloMaximize(env, opt_exp));
193  // construct constraints
194  DEBUG2(std::cout << "creating constraints"<<std::endl);
195  populate_constraints(env, c, vars, I);
196  model.add(c);
197  }
198 
199 }
200 
201 #endif
202 
Definition: read_adj_list.hpp:22
Definition: ilp_model.hpp:25
Definition: utils.hpp:96