Limbo
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
ILPColoring.h
Go to the documentation of this file.
1 
8 #ifndef LIMBO_ALGORITHMS_COLORING_ILPCOLORING
9 #define LIMBO_ALGORITHMS_COLORING_ILPCOLORING
10 
11 #include <iostream>
12 #include <vector>
13 #include <queue>
14 #include <set>
15 #include <limits>
16 #include <cassert>
17 #include <cmath>
18 #include <stdlib.h>
19 #include <cstdio>
20 #include <sstream>
21 #include <algorithm>
22 #include <boost/cstdint.hpp>
23 #include <boost/unordered_map.hpp>
24 #include <boost/graph/graph_concepts.hpp>
25 #include <boost/graph/subgraph.hpp>
26 #include <boost/property_map/property_map.hpp>
27 //#include <boost/graph/bipartite.hpp>
28 //#include <boost/graph/kruskal_min_spanning_tree.hpp>
29 //#include <boost/graph/prim_minimum_spanning_tree.hpp>
30 //#include <boost/graph/dijkstra_shortest_paths.hpp>
31 #include <limbo/string/String.h>
33 #include <limbo/solvers/Solvers.h>
35 
37 namespace limbo
38 {
40 namespace algorithms
41 {
43 namespace coloring
44 {
45 
53 template <typename GraphType>
54 class ILPColoring : 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;
70 
73  ILPColoring(graph_type const& g)
74  : base_type(g)
75  {}
77  virtual ~ILPColoring() {}
78 
83  void write_graph_sol(string const& filename, model_type const& opt_model, std::vector<model_type::variable_type> const& vVertexBit) const;
84 
85  protected:
88  virtual double coloring();
89 };
90 
91 template <typename GraphType>
93 {
94  uint32_t vertex_num = boost::num_vertices(this->m_graph);
95  uint32_t edge_num = boost::num_edges(this->m_graph);
96  uint32_t vertex_variable_num = vertex_num<<1;
97 
98  boost::unordered_map<graph_edge_type, uint32_t, edge_hash_type> hEdgeIdx; // edge index
99 
100  uint32_t cnt = 0;
101  edge_iterator_type ei, eie;
102  for (boost::tie(ei, eie) = boost::edges(this->m_graph); ei != eie; ++ei, ++cnt)
103  hEdgeIdx[*ei] = cnt;
104 
106  model_type opt_model;
107  limbo::solvers::GurobiParameters gurobiParams;
108  gurobiParams.setOutputFlag(0);
109  gurobiParams.setNumThreads(this->m_threads);
110  //set up the ILP variables
111  std::vector<model_type::variable_type> vVertexBit;
112  std::vector<model_type::variable_type> vEdgeBit;
113 
114  opt_model.reserveVariables(vertex_variable_num+edge_num);
115  if (this->m_color_num == base_type::THREE)
116  opt_model.reserveConstraints(edge_num*4+vertex_num);
117  else
118  opt_model.reserveConstraints(edge_num*4);
119 
120  // vertex variables
121  vVertexBit.reserve(vertex_variable_num);
122  for (uint32_t i = 0; i != vertex_variable_num; ++i)
123  {
124  uint32_t vertex_idx = (i>>1);
125  std::ostringstream oss;
126  oss << "v" << i;
127  if (this->m_vColor[vertex_idx] >= 0 && this->m_vColor[vertex_idx] < this->m_color_num) // precolored
128  {
129  int8_t color_bit;
130  if ((i&1) == 0) color_bit = (this->m_vColor[vertex_idx]>>1)&1;
131  else color_bit = this->m_vColor[vertex_idx]&1;
132  vVertexBit.push_back(opt_model.addVariable(color_bit, color_bit, limbo::solvers::INTEGER, oss.str()));
133  }
134  else // uncolored
135  vVertexBit.push_back(opt_model.addVariable(0, 1, limbo::solvers::INTEGER, oss.str()));
136  }
137 
138  // edge variables
139  vEdgeBit.reserve(edge_num);
140  for (uint32_t i = 0; i != edge_num; ++i)
141  {
142  std::ostringstream oss;
143  oss << "e" << i;
144  vEdgeBit.push_back(opt_model.addVariable(0, 1, limbo::solvers::INTEGER, oss.str()));
145  }
146 
147  // set up the objective
149  for (boost::tie(ei, eie) = edges(this->m_graph); ei != eie; ++ei)
150  {
151  edge_weight_type w = boost::get(boost::edge_weight, this->m_graph, *ei);
152  if (w > 0) // weighted conflict
153  obj += w*vEdgeBit[hEdgeIdx[*ei]];
154  else if (w < 0) // weighted stitch
155  obj += this->m_stitch_weight*(-w)*vEdgeBit[hEdgeIdx[*ei]];
156  }
157  opt_model.setObjective(obj);
159 
160  // set up the constraints
161  uint32_t constr_num = 0;
162  for (boost::tie(ei, eie) = boost::edges(this->m_graph); ei != eie; ++ei)
163  {
164  graph_vertex_type s = boost::source(*ei, this->m_graph);
165  graph_vertex_type t = boost::target(*ei, this->m_graph);
166 
167  uint32_t vertex_idx1 = s<<1;
168  uint32_t vertex_idx2 = t<<1;
169 
170  edge_weight_type w = boost::get(boost::edge_weight, this->m_graph, *ei);
171  uint32_t edge_idx = hEdgeIdx[*ei];
172 
173  char buf[100];
174  string tmpConstr_name;
175  if (w >= 0) // constraints for conflict edges
176  {
177  sprintf(buf, "R%u", constr_num++);
178  opt_model.addConstraint(
179  vVertexBit[vertex_idx1] + vVertexBit[vertex_idx1+1]
180  + vVertexBit[vertex_idx2] + vVertexBit[vertex_idx2+1]
181  + vEdgeBit[edge_idx] >= 1
182  , buf);
183 
184  sprintf(buf, "R%u", constr_num++);
185  opt_model.addConstraint(
186  - vVertexBit[vertex_idx1] + vVertexBit[vertex_idx1+1]
187  - vVertexBit[vertex_idx2] + vVertexBit[vertex_idx2+1]
188  + vEdgeBit[edge_idx] >= -1
189  , buf);
190 
191  sprintf(buf, "R%u", constr_num++);
192  opt_model.addConstraint(
193  vVertexBit[vertex_idx1] - vVertexBit[vertex_idx1+1]
194  + vVertexBit[vertex_idx2] - vVertexBit[vertex_idx2+1]
195  + vEdgeBit[edge_idx] >= -1
196  , buf);
197 
198  sprintf(buf, "R%u", constr_num++);
199  opt_model.addConstraint(
200  - vVertexBit[vertex_idx1] - vVertexBit[vertex_idx1+1]
201  - vVertexBit[vertex_idx2] - vVertexBit[vertex_idx2+1]
202  + vEdgeBit[edge_idx] >= -3
203  , buf);
204 
205  }
206  else // constraints for stitch edges
207  {
208  sprintf(buf, "R%u", constr_num++);
209  opt_model.addConstraint(
210  vVertexBit[vertex_idx1] - vVertexBit[vertex_idx2] - vEdgeBit[edge_idx] <= 0
211  , buf);
212 
213  sprintf(buf, "R%u", constr_num++);
214  opt_model.addConstraint(
215  vVertexBit[vertex_idx2] - vVertexBit[vertex_idx1] - vEdgeBit[edge_idx] <= 0
216  , buf);
217 
218  sprintf(buf, "R%u", constr_num++);
219  opt_model.addConstraint(
220  vVertexBit[vertex_idx1+1] - vVertexBit[vertex_idx2+1] - vEdgeBit[edge_idx] <= 0
221  , buf);
222 
223  sprintf(buf, "R%u", constr_num++);
224  opt_model.addConstraint(
225  vVertexBit[vertex_idx2+1] - vVertexBit[vertex_idx1+1] - vEdgeBit[edge_idx] <= 0
226  , buf);
227  }
228  }
229 
230  // additional constraints for 3-coloring
231  if (this->m_color_num == base_type::THREE)
232  {
233  char buf[100];
234  for(uint32_t k = 0; k != vertex_variable_num; k += 2)
235  {
236  sprintf(buf, "R%u", constr_num++);
237  opt_model.addConstraint(vVertexBit[k] + vVertexBit[k+1] <= 1, buf);
238  }
239  }
240 
241  //optimize model
242  solver_type solver (&opt_model);
243  int32_t opt_status = solver(&gurobiParams);
244 #ifdef DEBUG_ILPCOLORING
245  opt_model.print("graph.lp");
246  opt_model.printSolution("graph.sol");
247 #endif
248  if(opt_status == limbo::solvers::INFEASIBLE)
249  {
250  cout << "ERROR: The model is infeasible... EXIT" << endl;
251  exit(1);
252  }
253 
254 #ifdef DEBUG_ILPCOLORING
255  this->write_graph_sol("graph_sol", opt_model, vVertexBit); // dump solution figure
256 #endif
257 
258  // collect coloring solution
259  for (uint32_t k = 0; k != vertex_variable_num; k += 2)
260  {
261  int8_t color = (opt_model.variableSolution(vVertexBit[k])*2)+opt_model.variableSolution(vVertexBit[k+1]);
262  uint32_t vertex_idx = (k>>1);
263 
264  assert(color >= 0 && color < this->m_color_num);
265  if (this->m_vColor[vertex_idx] >= 0 && this->m_vColor[vertex_idx] < this->m_color_num) // check precolored vertex
266  assert(this->m_vColor[vertex_idx] == color);
267  else // assign color to uncolored vertex
268  this->m_vColor[vertex_idx] = color;
269  }
270 
271  // return objective value
272  return opt_model.evaluateObjective();
273 }
274 
275 template <typename GraphType>
276 void ILPColoring<GraphType>::write_graph_sol(string const& filename, typename ILPColoring<GraphType>::model_type const& opt_model,
277  std::vector<typename ILPColoring<GraphType>::model_type::variable_type> const& vVertexBit) const
278 {
279  std::ofstream out((filename+".gv").c_str());
280  out << "graph D { \n"
281  << " randir = LR\n"
282  << " size=\"4, 3\"\n"
283  << " ratio=\"fill\"\n"
284  << " edge[style=\"bold\",fontsize=200]\n"
285  << " node[shape=\"circle\",fontsize=200]\n";
286 
287  //output nodes
288  uint32_t vertex_num = boost::num_vertices(this->m_graph);
289  for(uint32_t k = 0; k < vertex_num; ++k)
290  {
291  out << " " << k << "[shape=\"circle\"";
292  //output coloring label
293  out << ",label=\"" << k << ":(" << opt_model.variableSolution(vVertexBit[(k<<1)]) << "," << opt_model.variableSolution(vVertexBit[(k<<1)+1]) << ")\"";
294  out << "]\n";
295  }//end for
296 
297  //output edges
298  edge_iterator_type ei, eie;
299  for (boost::tie(ei, eie) = boost::edges(this->m_graph); ei != eie; ++ei)
300  {
301  edge_weight_type w = boost::get(boost::edge_weight, this->m_graph, *ei);
302  graph_vertex_type s = boost::source(*ei, this->m_graph);
303  graph_vertex_type t = boost::target(*ei, this->m_graph);
304  if (w >= 0) // conflict edge
305  {
306  bool conflict_flag = (opt_model.variableSolution(vVertexBit[(s<<1)]) == opt_model.variableSolution(vVertexBit[(t<<1)])
307  && opt_model.variableSolution(vVertexBit[(s<<1)+1]) == opt_model.variableSolution(vVertexBit[(t<<1)+1]));
308 
309  if(conflict_flag)
310  out << " " << s << "--" << t << "[color=\"red\",style=\"solid\",penwidth=3]\n";
311  else
312  out << " " << s << "--" << t << "[color=\"black\",style=\"solid\",penwidth=3]\n";
313  }
314  else // stitch edge
315  out << " " << s << "--" << t << "[color=\"blue\",style=\"dotted\",penwidth=3]\n";
316  }
317  out << "}";
318  out.close();
319  la::graphviz2pdf(filename);
320 }
321 
322 } // namespace coloring
323 } // namespace algorithms
324 } // namespace limbo
325 
326 #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
variable_value_type variableSolution(variable_type const &var) const
Definition: Solvers.h:1376
Base class for custom Gurobi parameters.
Definition: GurobiApi.h:33
bool printSolution(std::string const &filename) const
print solutions to file
Definition: Solvers.h:1665
bool addConstraint(constraint_type const &constr, std::string name="")
add a constraint
Definition: Solvers.h:1217
Basic utilities such as variables and linear expressions in solvers.
void reserveVariables(unsigned int n)
reserve space for variables
Definition: Solvers.h:1382
void reserveConstraints(unsigned int n)
reserve space for constraints
Definition: Solvers.h:1389
void setOptimizeType(SolverProperty optType)
Definition: Solvers.h:1310
Gurobi API wrapper using its C API.
void setObjective(expression_type const &expr)
set objective
Definition: Solvers.h:1295
void setOutputFlag(int v)
set output flag
Definition: GurobiApi.h:62
void write_graph_sol(string const &filename, model_type const &opt_model, std::vector< model_type::variable_type > const &vVertexBit) const
Definition: ILPColoring.h:276
the model is infeasible
Definition: Solvers.h:37
namespace for Limbo
Definition: GraphUtility.h:22
base class for all graph coloring algorithms
Check string is integer, floating point, number... Convert string to upper/lower cases.
coefficient_value_type evaluateObjective(std::vector< variable_value_type > const &vVariableSol) const
evaluate objective
Definition: Solvers.h:1425
integer number
Definition: Solvers.h:34
model to describe an optimization problem
Definition: Solvers.h:80
minimize objective
Definition: Solvers.h:31
variable_type addVariable(variable_value_type lb, variable_value_type ub, SolverProperty nt, std::string name="")
add one variable
Definition: Solvers.h:1365
bool print(std::string const &filename) const
print problem in lp format to file
Definition: Solvers.h:1552
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