Limbo
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
MISColoring.h
Go to the documentation of this file.
1 
6 #ifndef LIMBO_ALGORITHMS_COLORING_MISCOLORING
7 #define LIMBO_ALGORITHMS_COLORING_MISCOLORING
8 
9 #include <iostream>
10 #include <vector>
11 #include <queue>
12 #include <set>
13 #include <limits>
14 #include <cassert>
15 #include <cmath>
16 #include <stdlib.h>
17 #include <cstdio>
18 #include <sstream>
19 #include <algorithm>
20 #include <boost/cstdint.hpp>
21 #include <boost/unordered_map.hpp>
22 #include <boost/graph/graph_concepts.hpp>
23 #include <boost/graph/subgraph.hpp>
24 #include <boost/property_map/property_map.hpp>
25 //#include <boost/graph/bipartite.hpp>
26 //#include <boost/graph/kruskal_min_spanning_tree.hpp>
27 //#include <boost/graph/prim_minimum_spanning_tree.hpp>
28 //#include <boost/graph/dijkstra_shortest_paths.hpp>
29 #include <limbo/string/String.h>
31 #if GUROBI == 1
32 #include <limbo/solvers/Solvers.h>
34 #endif
35 
37 namespace limbo
38 {
40 namespace algorithms
41 {
43 namespace coloring
44 {
45 
53 template <typename GraphType>
54 class MISColoring : public Coloring<GraphType>
55 {
56  public:
59  using typename base_type::graph_type;
60  using typename base_type::graph_vertex_type;
61  using typename base_type::graph_edge_type;
62  using typename base_type::vertex_iterator_type;
63  using typename base_type::edge_iterator_type;
64  using typename base_type::edge_weight_type;
65  using typename base_type::ColorNumType;
66  typedef typename base_type::EdgeHashType edge_hash_type;
67 #if GUROBI == 1
70 #endif
71 
75  MISColoring(graph_type const& g)
76  : base_type(g)
77  {}
79  virtual ~MISColoring() {}
80 
84  virtual void precolor(graph_vertex_type /*v*/, int8_t /*c*/) {limboAssert("precoloring is not supported");}
85 
90  void write_graph_sol(string const& filename, std::vector<int8_t> const& vVertexExcludeMark, std::vector<graph_vertex_type> const& vMISVertex) const
91  {
92  std::ofstream out((filename+".gv").c_str());
93  out << "graph D { \n"
94  << " randir = LR\n"
95  << " size=\"4, 3\"\n"
96  << " ratio=\"fill\"\n"
97  << " edge[style=\"bold\",fontsize=200]\n"
98  << " node[shape=\"circle\",fontsize=200]\n";
99 
100  //output nodes
101  uint32_t vertex_num = boost::num_vertices(this->m_graph);
102  for(uint32_t k = 0; k < vertex_num; ++k)
103  {
104  out << " " << k << "[shape=\"circle\"";
105  //output coloring label
106  if (vVertexExcludeMark[k]) // excluded
107  out << ",label=\"" << k << "\",style=filled,fillcolor=\"grey\"";
108  else if (std::find(vMISVertex.begin(), vMISVertex.end(), k) != vMISVertex.end())
109  out << ",label=\"" << k << "\",style=filled,fillcolor=\"red\"";
110  else
111  out << ",label=\"" << k << "\"";
112  out << "]\n";
113  }//end for
114 
115  //output edges
116  edge_iterator_type ei, eie;
117  for (boost::tie(ei, eie) = boost::edges(this->m_graph); ei != eie; ++ei)
118  {
119  edge_weight_type w = boost::get(boost::edge_weight, this->m_graph, *ei);
120  graph_vertex_type s = boost::source(*ei, this->m_graph);
121  graph_vertex_type t = boost::target(*ei, this->m_graph);
122  if (w >= 0) // conflict edge
123  {
124  if (vVertexExcludeMark[s] || vVertexExcludeMark[t]) // skipped
125  out << " " << s << "--" << t << "[color=\"grey\",style=\"solid\",penwidth=3]\n";
126  else
127  out << " " << s << "--" << t << "[color=\"black\",style=\"solid\",penwidth=3]\n";
128  }
129  else // stitch edge
130  out << " " << s << "--" << t << "[color=\"blue\",style=\"dotted\",penwidth=3]\n";
131  }
132  out << "}";
133  out.close();
134  la::graphviz2pdf(filename);
135  }
136 
137  protected:
140  virtual double coloring()
141  {
142  std::vector<int8_t> vVertexExcludeMark (boost::num_vertices(this->m_graph), 0);
143  std::vector<graph_vertex_type> vMISVertex;
144 
145  for (int8_t c = 0; c < this->color_num(); ++c)
146  {
147  vMISVertex.clear();
148  // compute maximum independent set to the residual graph
149 #if GUROBI == 1
150  // find maximum independent set with maximum number of vertices
151  computeMWISILP(vVertexExcludeMark, vMISVertex);
152 #else
153  // only find maximum independent set
154  computeMIS(vVertexExcludeMark, vMISVertex);
155 #endif
156  // apply color
157  for (typename std::vector<graph_vertex_type>::const_iterator vi = vMISVertex.begin(); vi != vMISVertex.end(); ++vi)
158  {
159  this->m_vColor[*vi] = c;
160  vVertexExcludeMark[*vi] = true;
161  }
162  }
163 
164  // greedy coloring for the rest
165  vertex_iterator_type vi, vie;
166  for (boost::tie(vi, vie) = boost::vertices(this->m_graph); vi != vie; ++vi)
167  {
168  graph_vertex_type v = *vi;
169  if (!vVertexExcludeMark[v]) // skip excluded vertices
170  {
171  int8_t bestColor = 0;
172  edge_weight_type bestCost = std::numeric_limits<edge_weight_type>::max();
173  for (int8_t c = 0; c < this->color_num(); ++c)
174  {
175  edge_weight_type curCost = 0;
176  typename boost::graph_traits<graph_type>::adjacency_iterator ui, uie;
177  for (boost::tie(ui, uie) = boost::adjacent_vertices(v, this->m_graph); ui != uie; ++ui)
178  {
179  graph_vertex_type u = *ui;
180  if (this->m_vColor[u] >= 0 && this->m_vColor[u] < this->color_num())
181  {
182  if (c == this->m_vColor[u])
183  {
184  std::pair<graph_edge_type, bool> eU2v = boost::edge(v, u, this->m_graph);
185 #ifdef DEBUG_MISCOLORING
186  assert(eU2v.second);
187 #endif
188  // edge weight is important since we may deal with merged graphs
189  // this is just for generality purpose
190  curCost += std::max((edge_weight_type)1, boost::get(boost::edge_weight, this->m_graph, eU2v.first));
191  }
192  }
193  }
194  if (curCost < bestCost)
195  {
196  bestCost = curCost;
197  bestColor = c;
198  }
199  }
200  this->m_vColor[v] = bestColor;
201  }
202  }
203 
204 #ifdef DEBUG_MISCOLORING
205  this->write_graph("final_output");
206 #endif
207  return this->calc_cost(this->m_vColor);
208  }
209 
210 #if GUROBI == 1
211  double computeMWISILP(std::vector<int8_t> const& vVertexExcludeMark, std::vector<graph_vertex_type>& vMISVertex)
218  {
219  uint32_t vertex_num = boost::num_vertices(this->m_graph);
220  uint32_t edge_num = boost::num_edges(this->m_graph);
221  uint32_t exclude_vertex_num = std::count(vVertexExcludeMark.begin(), vVertexExcludeMark.end(), 1);
222 
224  model_type opt_model;
225  limbo::solvers::GurobiParameters gurobiParams;
226  gurobiParams.setOutputFlag(0);
227  gurobiParams.setNumThreads(this->m_threads);
228  //set up the ILP variables
229  // this is a map of length vertex_num
230  std::vector<model_type::variable_type> vVertex2Var (vertex_num);
231  boost::unordered_map<graph_edge_type, model_type::variable_type, edge_hash_type> hEdge2Var; // edge index
232 
233  // vertex variables
234  opt_model.reserveVariables(vertex_num-exclude_vertex_num+edge_num);
235  vertex_iterator_type vi, vie;
236  for (boost::tie(vi, vie) = boost::vertices(this->m_graph); vi != vie; ++vi)
237  {
238  graph_vertex_type v = *vi;
239  if (!vVertexExcludeMark[v]) // skip excluded vertices
240  {
241  std::ostringstream oss;
242  oss << "v" << v;
243  vVertex2Var[v] = opt_model.addVariable(0, 1, limbo::solvers::INTEGER, oss.str());
244  }
245  }
246  // edge variables
247  edge_iterator_type ei, eie;
248  for (boost::tie(ei, eie) = boost::edges(this->m_graph); ei != eie; ++ei)
249  {
250  graph_vertex_type s = boost::source(*ei, this->m_graph);
251  graph_vertex_type t = boost::target(*ei, this->m_graph);
252 
253  if (!vVertexExcludeMark[s] && !vVertexExcludeMark[t]) // skip any edge connected to excluded vertices
254  {
255  std::ostringstream oss;
256  oss << "e" << s << "_" << t;
257  hEdge2Var.insert(std::make_pair(*ei, opt_model.addVariable(0, 1, limbo::solvers::INTEGER, oss.str())));
258  }
259  }
260 
261  // set up the objective
262  // maximize the edge weight selected, which is equivalent to minimize the edge weight in the residual graph
263  // max. sum e
264  model_type::expression_type obj;
265  for (typename boost::unordered_map<graph_edge_type, model_type::variable_type, edge_hash_type>::const_iterator it = hEdge2Var.begin(); it != hEdge2Var.end(); ++it)
266  obj += it->second;
267  opt_model.setObjective(obj);
268  opt_model.setOptimizeType(limbo::solvers::MAX);
269 
270  // conflict constraints
271  char buf[100];
272  uint32_t constr_num = 0;
273  for (boost::tie(ei, eie) = boost::edges(this->m_graph); ei != eie; ++ei)
274  {
275  graph_vertex_type s = boost::source(*ei, this->m_graph);
276  graph_vertex_type t = boost::target(*ei, this->m_graph);
277 
278  if (!vVertexExcludeMark[s] && !vVertexExcludeMark[t]) // skip any edge connected to excluded vertices
279  {
280  sprintf(buf, "R%u", constr_num++);
281  opt_model.addConstraint(
282  vVertex2Var[s] + vVertex2Var[t] <= 1
283  , buf);
284  }
285  }
286  // edge selected constraints
287  // the edge variable will be 1 if any of its vertices is selected
288  // e <= vs + vt
289  // if vs = vt = 0, e = 0
290  // else e can be any value between 0 and 1
291  // consider that we are maximizing e in the objective
292  for (typename boost::unordered_map<graph_edge_type, model_type::variable_type, edge_hash_type>::const_iterator it = hEdge2Var.begin(); it != hEdge2Var.end(); ++it)
293  {
294  graph_edge_type const& e = it->first;
295  graph_vertex_type s = boost::source(e, this->m_graph);
296  graph_vertex_type t = boost::target(e, this->m_graph);
297 
298  sprintf(buf, "E%u", constr_num++);
299  opt_model.addConstraint(
300  it->second - vVertex2Var[s] - vVertex2Var[t] <= 0
301  , buf);
302  }
303 
304  //optimize model
305  solver_type solver (&opt_model);
306  int32_t opt_status = solver(&gurobiParams);
307 #ifdef DEBUG_MISCOLORING
308  opt_model.print("graph.lp");
309  opt_model.printSolution("graph.sol");
310 #endif
311  if(opt_status == limbo::solvers::INFEASIBLE)
312  {
313  cout << "ERROR: The model is infeasible... EXIT" << endl;
314  exit(1);
315  }
316 
317  // collect maximum independent set vertices
318  for (boost::tie(vi, vie) = boost::vertices(this->m_graph); vi != vie; ++vi)
319  {
320  graph_vertex_type v = *vi;
321  if (!vVertexExcludeMark[v]) // skip excluded vertices
322  {
323  int8_t value = opt_model.variableSolution(vVertex2Var[v]);
324  if (value)
325  vMISVertex.push_back(v);
326  }
327  }
328 
329 #ifdef DEBUG_MISCOLORING
330  this->write_graph_sol("graph_sol", vVertexExcludeMark, vMISVertex); // dump solution figure
331 #endif
332 
333  // return objective value
334  return opt_model.evaluateObjective();
335  }
336 #endif
337 
342  double computeMIS(std::vector<int8_t> const& vVertexExcludeMark, std::vector<graph_vertex_type>& vMISVertex)
343  {
344  uint32_t vertex_num = boost::num_vertices(this->m_graph);
345 
346  // this is a map of length vertex_num
347  std::set<graph_vertex_type> sVertex;
348 
349  for (uint32_t i = 0; i < vertex_num; ++i)
350  {
351  if (!vVertexExcludeMark[i]) // skip excluded vertices
352  sVertex.insert(i);
353  }
354 
355  // while sVertex is not empty
356  // choose v \in V
357  // add v to vMISVertex
358  // remove neighbors of v from V
359  while (!sVertex.empty())
360  {
361  graph_vertex_type v = *sVertex.begin();
362  vMISVertex.push_back(v);
363 
364  sVertex.erase(v);
365  typename boost::graph_traits<graph_type>::adjacency_iterator ui, uie;
366  for (boost::tie(ui, uie) = boost::adjacent_vertices(v, this->m_graph); ui != uie; ++ui)
367  {
368  graph_vertex_type u = *ui;
369  sVertex.erase(u);
370  }
371  }
372 
373 #ifdef DEBUG_MISCOLORING
374  this->write_graph_sol("graph_sol", vVertexExcludeMark, vMISVertex); // dump solution figure
375 #endif
376 
377  // return size of independent set
378  return vMISVertex.size();
379  }
380 };
381 
382 } // namespace coloring
383 } // namespace algorithms
384 } // namespace limbo
385 
386 #endif
hasher class for graph_edge_type
Definition: Coloring.h:138
Gurobi API with limbo::solvers::LinearModel.
Definition: GurobiApi.h:76
void setNumThreads(int v)
set number of threads
Definition: GurobiApi.h:65
Base class for custom Gurobi parameters.
Definition: GurobiApi.h:33
maximize objective
Definition: Solvers.h:32
virtual edge_weight_type calc_cost(std::vector< int8_t > const &vColor) const
Definition: Coloring.h:279
int32_t m_threads
control number of threads for ILP solver
Definition: Coloring.h:234
Basic utilities such as variables and linear expressions in solvers.
Gurobi API wrapper using its C API.
void setOutputFlag(int v)
set output flag
Definition: GurobiApi.h:62
virtual void precolor(graph_vertex_type, int8_t)
Definition: MISColoring.h:84
the model is infeasible
Definition: Solvers.h:37
double computeMIS(std::vector< int8_t > const &vVertexExcludeMark, std::vector< graph_vertex_type > &vMISVertex)
compute maximum independent set, weight is not supported yet
Definition: MISColoring.h:342
std::vector< int8_t > m_vColor
coloring solutions
Definition: Coloring.h:230
namespace for Limbo
Definition: GraphUtility.h:22
std::iterator_traits< Iterator >::value_type max(Iterator first, Iterator last)
get max of an array
Definition: Math.h:61
graph_type const & m_graph
initial graph
Definition: Coloring.h:229
virtual void write_graph(std::string const &filename) const
base class for all graph coloring algorithms
virtual ColorNumType color_num() const
Definition: Coloring.h:170
#define limboAssert(condition)
custom assertion without message
Definition: AssertMsg.h:64
Check string is integer, floating point, number... Convert string to upper/lower cases.
integer number
Definition: Solvers.h:34
model to describe an optimization problem
Definition: Solvers.h:80
void write_graph_sol(string const &filename, std::vector< int8_t > const &vVertexExcludeMark, std::vector< graph_vertex_type > const &vMISVertex) const
Definition: MISColoring.h:90
void graphviz2pdf(std::string const &filename, const char *suffix=".gv")
convert graphviz format to pdf. The input filename should be filename+suffix
Definition: GraphUtility.h:150