OpenGM  2.3.x
Discrete Graphical Model Library
planar_graph.hxx
Go to the documentation of this file.
1 #pragma once
2 #ifndef OPENGM_PLANAR_GRAPH_HXX
3 #define OPENGM_PLANAR_GRAPH_HXX
4 
5 #include <iostream>
6 #include <vector>
7 #include <stack>
8 #include <list>
9 #include "opengm/opengm.hxx"
10 
11 //TODO: Fix include path
12 #include <planarity.src-patched/graph.h>
13 #include <planarity.src-patched/listcoll.h>
14 #include <planarity.src-patched/stack.h>
15 #include <planarity.src-patched/appconst.h>
16 
17 #include <blossom5.src-patched/PerfectMatching.h>
18 #include <blossom5.src-patched/PMimplementation.h>
19 #include <blossom5.src-patched/MinCost/MinCost.h>
20 
21 namespace opengm {
22  namespace external {
23  namespace planargraph {
24 
25  typedef double DataType;
26 
28 // PlanarGraph components
30 
31  struct Node
32  {
33  Node() : weight(0.0), adj(0) {};
34 
35  // Node weight
36  // Unused (0.0) for dual edges
37  // do zrobienia: make node class a template class and only use weight in reweighted_planar_max_cut
38  DataType weight;
39 
40  // List of indices of the dual edges
41  std::list<size_t> adj;
42  };
43 
44 
45  struct Edge
46  {
47  Edge()
48  : tail(0), head(0), weight(0.0), left_face(-1), right_face(-1) {};
49 
50  // Indices of tail and head node
51  size_t tail;
52  size_t head;
53 
54  // Edge weight
55  DataType weight;
56 
57  // Indices of left and right face (as seen from head to tail).
58  // -1 if unassigned
59  int left_face;
61  };
62 
63 
64  struct Face
65  {
66  Face() : edges(0), dual_nodes(0) {};
67 
68  // List of edges surrounding the face. Is in sorted order (s.t. the
69  // edges form an orbit) after calling planarize()
70  std::list<size_t> edges;
71 
72  // List of the dual nodes_ (forming a clique) belonging to this face
73  std::list<size_t> dual_nodes;
74  };
75 
76 
78 // PlanarGraph class definition
80 
82  {
83  public:
84  PlanarGraph();
85  PlanarGraph(size_t n, bool debug);
86  ~PlanarGraph();
87 
88  size_t num_nodes() const { return nodes_.size(); };
89  size_t num_edges() const { return edges.size(); };
90  size_t num_faces() const { return faces.size(); };
91 
92 
93  size_t add_node();
94  long int find_edge(size_t u, size_t v) const;
95  size_t add_edge(size_t u, size_t v, DataType w);
96  void add_edge_weight(size_t e, DataType w);
97 
98  void print();
99 
100  void planarize();
101  void construct_dual();
102 
103  void get_labeling(std::vector<int> & x) const;
104 
105  void calculate_maxcut();
106  std::vector<bool> get_cut() const;
107  std::vector<int> get_labeling_from_cut(const std::vector<bool>& cut) const;
108  double cost_of_cut(const std::vector<int>& x) const;
109  double cost_of_cut() const;
110 
111  protected:
112  long int get_dest(size_t v, size_t e) const;
113  long int get_following_edge(size_t v, size_t e) const;
114 
115  void clear_faces();
116 
117  size_t compute_dual_num_edges() const;
118 
119  std::vector<Node> nodes_;
120  std::vector<Edge> edges;
121  std::vector<Face> faces;
122 
123  //std::unique_ptr<PerfectMatching> Dual_;
124  PerfectMatching* Dual_;
125 
126  bool debug_;
127 
128  };
129 
130 
132 // Basic functionality
134 
135 
136  //
137 
139  : nodes_(0), edges (0), faces(0), Dual_(NULL), debug_(false)
140  // : nodes_(0), edges (0), faces(0), Dual_(nullptr), debug_(debug)
141  {
142  nodes_.reserve(0);
143  edges.reserve(0);
144  }
145 
146  PlanarGraph::PlanarGraph(size_t n, bool debug = false)
147  : nodes_(0), edges (0), faces(0), Dual_(NULL), debug_(debug)
148  // : nodes_(0), edges (0), faces(0), Dual_(nullptr), debug_(debug)
149  {
150  nodes_.reserve(n);
151  edges.reserve(3*n);
152  }
153 
155  if(Dual_ != NULL)
156  delete Dual_;
157  }
158 
159  // Adds a new node with weight w to the graph
161  {
162  // Create new node object (in place) and set its weight
163  size_t v = nodes_.size();
164  nodes_.resize(v+1);
165 
166  // Return index of the new node
167  return v;
168  }
169 
170 
171  // Adds new edge with weight w between nodes_ u and v
172  size_t PlanarGraph::add_edge(size_t u, size_t v, DataType w)
173  {
174  // Check that u and v are valid node indices
175  OPENGM_ASSERT(u >= 0 && u<nodes_.size());
176  OPENGM_ASSERT(v >= 0 && v<nodes_.size());
177 
178  // Create new edge object (in place) and set properties
179  size_t e = edges.size();
180  edges.resize(e+1);
181  edges[e].tail = u;
182  edges[e].head = v;
183  edges[e].weight = w;
184 
185  // Add edge index to both nodes_' adjacency lists
186  nodes_[u].adj.push_back(e);
187  nodes_[v].adj.push_back(e);
188 
189  // Return index of the new edge
190  return e;
191  }
192 
193 
194  // Adds weight w to the existing edge e
195  void PlanarGraph::add_edge_weight(size_t e, DataType w)
196  {
197  // Check the e is a valid edge index
198  OPENGM_ASSERT(e >= 0 && e < edges.size());
199 
200  edges[e].weight = w;
201  }
202 
203 
204  // Returns index of the edge connecting nodes_ u and v
205  // -1 if such an edge does not exist
206  long int PlanarGraph::find_edge(size_t u, size_t v) const
207  {
208  // Search u's adjacency list for an edge connecting it to v.
209  for(size_t e=0; e<nodes_[u].adj.size(); ++e)
210  {
211  if( edges[e].tail==v || edges[e].head==v )
212  {
213  return e;
214  }
215  }
216 
217  // Return -1 if the loop did not find one.
218  return -1;
219  }
220 
221 
222  // Returns index of the destination node of Edge e as seen from node v
223  // -1 if e is not incident on v
224  long int PlanarGraph::get_dest(size_t v, size_t e) const
225  {
226  if(v == edges[e].tail)
227  return edges[e].head;
228  else if (v == edges[e].head)
229  return edges[e].tail;
230  else
231  return -1;
232  }
233 
234 
235  // Simple command line output of the graph for debugging
237  {
238  if(debug_)
239  std::cout << "PlanarGraph with " << num_nodes() << " nodes_, "
240  << num_edges() << " edges and " << num_faces() << " faces.\n";
241 
242  for(size_t u = 0; u<nodes_.size(); ++u)
243  {
244  // Print current node's id and weight
245  if(debug_)
246  std::cout << u << "\t[" << nodes_[u].weight << "]:\t";
247 
248  // For all edges in current node's adjacency list
249  for(std::list<size_t>::iterator it = nodes_[u].adj.begin();
250  it != nodes_[u].adj.end(); ++it)
251  {
252  // Get the destination of the current edge
253  // Print destination id and weight of the edge
254  size_t v = get_dest(u, *it);
255  if(debug_)
256  std::cout << v << " (" << edges[*it].weight << "), ";
257  }
258  if(debug_)
259  std::cout << "\n";
260  }
261  }
262 
263 
265 // Construction of planar embedding
267 
268  // Returns the index of the edge that succeeds e in v's adjacency list
269  // -1 if e is not incident on v
270  long int PlanarGraph::get_following_edge(size_t v, size_t e) const
271  {
272  // Iterate to e in v's adjacency list
273  std::list<size_t>::const_iterator it = nodes_[v].adj.begin();
274  while((*it != e) && (it != nodes_[v].adj.end()))
275  ++it;
276 
277  if(it==nodes_[v].adj.end()) // e is not in v's adj list
278  {
279  return -1;
280  }
281  else // e is in v's adj list
282  {
283  ++it; // Make one more step
284  if(it==nodes_[v].adj.end()) // e is the last element in v's adj list
285  return nodes_[v].adj.front();
286  else
287  return *it;
288  }
289  }
290 
291 
293  {
294  // Pop all elements from the graph's list of faces
295  while(!faces.empty())
296  {
297  faces.pop_back();
298  }
299 
300  // Set the face indices of all edges to -1
301  for(std::vector<Edge>::iterator it = edges.begin(); it != edges.end(); ++it)
302  {
303  it->left_face = -1;
304  it->right_face = -1;
305  }
306  }
307 
309  {
310  // number of dual edges if the number of original edges ( = cross edges ) + clique edges in each face
311  size_t dual_num_edges = num_edges();
312  for(size_t f=0; f<faces.size(); ++f) {
313  const size_t face_size = faces[f].edges.size();
314  dual_num_edges += (face_size * (face_size - 1))/2;
315  }
316  return dual_num_edges;
317  }
318 
319 
320  // Planarizes the graph, i.e.
321  // - Sorts the adjacency lists of all nodes_ to form a rotation system
322  // - Constructs faces and sets face/edge relations (see definitions of
323  // struct Edge and struct Face)
325  {
326  // ToDo
327  // Assert that the graph is biconnected
328  // Reserve space for faces (how much?)
329 
331  graphP g = gp_New();
332  gp_InitGraph(g, num_nodes());
333  for(std::vector<Edge>::iterator it=edges.begin(); it!=edges.end(); ++it)
334  {
335  gp_AddEdge(g, it->tail, 0, it->head, 0);
336  }
337 
338  // Invoke code that sorts the adjacency lists
339  if (gp_Embed(g, EMBEDFLAGS_PLANAR) == OK) {
340  gp_SortVertices(g);
341  } else {
342  throw("PlanarGraph not planar\n");
343  }
344 
346  for (size_t i = 0; i < g->N; ++i)
347  {
348  size_t u = i;
349 
350  size_t j = g->G[i].link[1];
351  while (j >= g->N)
352  {
353  OPENGM_ASSERT(i != g->G[j].v); // What does this OPENGM_ASSERT do?
354  OPENGM_ASSERT(g->G[j].v < g->N);
355 
356  size_t v = g->G[j].v;
357 
358  // Find the edge connecting u and v
359  std::list<size_t>::iterator it = nodes_[u].adj.begin();
360  while(edges[*it].tail != v && edges[*it].head != v && it != nodes_[u].adj.end())
361  ++it;
362  size_t e = *it;
363  OPENGM_ASSERT(it != nodes_[i].adj.end());
364 
365  // Remove the edge from its current position, and insert at the back
366  nodes_[u].adj.erase(it);
367  nodes_[u].adj.push_back(e);
368 
369  j = g->G[j].link[1];
370  }
371  }
372 
374  clear_faces();
375 
376 
378  // do zrobienia: code for following the orbit starting from left and right face is mostly duplication: clean up!
379  for(size_t e = 0; e < edges.size(); ++e) // Loop over all edges
380  {
381  // Check if the right face of e has already been dealt with.
382  // If not, construct it!
383 
384  //enum class face_type {left,right};
385  typedef int face_type;
386  const face_type face_type_left = 1;
387  const face_type face_type_right = 2;
388 
389 
390  if(edges[e].right_face == -1)
391  {
392  // Create new face object
393  const size_t f = faces.size();
394  faces.resize(f+1);
395 
396  // Assign e <-> f
397  faces[f].edges.push_back(e);
398  edges[e].right_face = f;
399 
400  // Follow the orbit in FORWARD direction (i.e. starting with e's head)
401  size_t v = edges[e].head; // Next node
402  size_t ee = e;
403  //size_t ee = get_following_edge(v, e); // Next edge
404  face_type ee_face;
405  do {
406  // Get next node and edge
407  ee = get_following_edge(v, ee);
408  v = get_dest(v, ee);
409 
410  // Set f as face of ee, left or right depends on the formal direction of ee
411  if(v==edges[ee].tail) {
412  edges[ee].left_face = f;
413  ee_face = face_type_left;
414  }
415  if(v==edges[ee].head) {
416  edges[ee].right_face = f;
417  ee_face = face_type_right;
418  }
419  faces[f].edges.push_back(ee); // a face can have the same edge in the left and right. do zeobienia: this must be reflected in the faces data structure as well.
420 
421 
422  } while(! (ee == e && ee_face == face_type_right) ); // If ee==e and we are on the same side again, we went the full circle
423  }
424 
425  // Check if the left face of e has already been dealt with.
426  // If not, construct it!
427  // to do: remove duplicate code by switching left for right and vice versa
428  if(edges[e].left_face == -1)
429  {
430  // Create new face object
431  const size_t f = faces.size();
432  faces.resize(f+1);
433 
434  // Assign e <-> f
435  faces[f].edges.push_back(e);
436  edges[e].left_face = f;
437 
438  // Follow the orbit in BACKWARD direction (i.e. starting with e's tail)
439  size_t v = edges[e].tail; // Next node
440  size_t ee = e;
441  //size_t ee = get_following_edge(v, e); // Next edge
442  face_type ee_face;
443  do {
444  // Get next node and edge
445  ee = get_following_edge(v, ee);
446  v = get_dest(v, ee);
447 
448  // Set f as face of ee, left or right depends on the formal direction of ee
449  if(v==edges[ee].tail) {
450  edges[ee].left_face = f;
451  ee_face = face_type_left;
452  }
453  if(v==edges[ee].head) {
454  edges[ee].right_face = f;
455  ee_face = face_type_right;
456  }
457  faces[f].edges.push_back(ee); // a face can have the same edge in the left and right. do zeobienia: this must be reflected in the faces data structure as well.
458 
459 
460  } while(! (ee == e && ee_face == face_type_left) ); // If ee==e and we are on the same side again, we went the full circle
461  }
462  }
463 
465  // Check: Do all edges have a left and a right face?
466  for(std::vector<Edge>::iterator it=edges.begin(); it!=edges.end(); ++it)
467  {
468  OPENGM_ASSERT((*it).left_face != -1);
469  OPENGM_ASSERT((*it).right_face != -1);
470  }
471 
472  // Check if genus = 0, i.e graph is planar
474 
475  // Delete planarity code graph
476  gp_Free(&g);
477  }
478 
479 
481 // Construction of dual graph
483 
484  // Constructs the expanded dual of the graph. The primal graph needs to
485  // be planarized before.
487  {
488  // Allocate dual graph in PerfectMatching data structure
489  // Todo: Reasonable number of max dual edges
490  Dual_ = new PerfectMatching(2*num_edges(), compute_dual_num_edges());
491  // Dual_ = std::unique_ptr<PerfectMatching>(new PerfectMatching(2*num_edges(), compute_dual_num_edges()));
492  PerfectMatching::Options Dual_options;
493  Dual_options.verbose = false;
494  Dual_->options = Dual_options;
495 
496  // insert all cross edges corresponding to the original edges
497  // note: cross edges directly correspond to the original edges
498  size_t counter = 0;
499  for(size_t e = 0; e < edges.size(); ++e)
500  {
501  // For the current edge of G, add dual nodes_ for its two faces
502  const size_t u = counter;
503  const size_t v = counter + 1;
504  counter += 2;
505 
506  // Add the dual cross edge of e, connecting u and v
507  // Weight is the negative of e's weight
508  Dual_->AddEdge(u, v, edges[e].weight);
509  }
510 
511  // insert clique edges connecting all dual nodes inside a face
512  counter = 0;
513  for(size_t e = 0; e < edges.size(); ++e)
514  {
515  size_t u = counter;
516  size_t v = counter + 1;
517  counter += 2;
518 
519  // "Integrate" u into the left face of e
520  size_t f = edges[e].left_face;
521  for(std::list<size_t>::iterator it = faces[f].dual_nodes.begin(); it != faces[f].dual_nodes.end(); ++it)
522  {
523  Dual_->AddEdge(u, *it, 0.0);
524  }
525  faces[f].dual_nodes.push_back(u);
526 
527  // "Integrate" v into the right face
528  f = edges[e].right_face;
529  for(std::list<size_t>::iterator it = faces[f].dual_nodes.begin(); it != faces[f].dual_nodes.end(); ++it)
530  {
531  Dual_->AddEdge(v, *it, 0.0);
532  }
533  faces[f].dual_nodes.push_back(v);
534  }
535  }
536 
538  {
539  double cost = 0.0;
540  for(size_t e = 0; e < num_edges(); ++e)
541  {
542  if(Dual_->GetSolution(e) == 0)
543  cost += edges[e].weight;
544  }
545 
546  return cost;
547  }
548 
549  double PlanarGraph::cost_of_cut(const std::vector<int>& x) const
550  {
551  // do zrobienia: check if x is a cut
552  double cost = 0.0;
553  for(size_t e = 0; e < num_edges(); ++e)
554  {
555  if(x[e] == 1)
556  cost += edges[e].weight;
557  }
558 
559  return cost;
560  }
561 
563  {
564  //OPENGM_ASSERT(Dual_ != nullptr);
565  OPENGM_ASSERT(Dual_ != NULL);
566  Dual_->Solve();
567  }
568 
569  std::vector<bool> PlanarGraph::get_cut() const
570  {
571  std::vector<bool> cut(num_edges(),false);
572  for(size_t e = 0; e < num_edges(); ++e)
573  {
574  if(Dual_->GetSolution(e) == 0) {
575  cut[e] = true;
576  } else if(Dual_->GetSolution(e) == 1) {
577  cut[e] = false;
578  } else {
579  throw std::logic_error("Perfect matching solver did not succeed");
580  }
581  }
582  return cut;
583  }
584 
585  // Reads the labeling defined by a given cut into the vector x. Does
586  // not output the labels for the unary nodes_. A cut has to be given
587  std::vector<int> PlanarGraph::get_labeling_from_cut(const std::vector<bool>& cut) const
588  {
589  OPENGM_ASSERT(cut.size() == edges.size());
590  // Make labeling size num_nodes(), i.e. including unary nodes_. Set all
591  // labels to -1 (meaning unassigned)
592  std::vector<int> labeling(num_nodes());
593  for(size_t v = 0; v < num_nodes(); ++v)
594  labeling[v] = -1;
595 
596  std::stack<size_t> s;
597  size_t visited_nodes=0;
598  for(size_t startnode=0; startnode< num_nodes(); ++startnode){
599  if(visited_nodes==num_nodes())
600  break;
601  if( labeling[startnode]!= -1)
602  continue;
603  labeling[startnode] = 0;
604  s.push(startnode);
605 
606  while(!s.empty()) // As long as stack is not empty
607  {
608  // Take top element from stack
609  size_t u = s.top();
610  s.pop();
611  ++visited_nodes;
612 
613  // Go through all incident edges
614  for(std::list<size_t>::const_iterator it = nodes_[u].adj.begin(); it != nodes_[u].adj.end(); ++it)
615  {
616  size_t e = *it; // Edge and...
617  size_t v = get_dest(u, e); // its destination (i.e. the neighbor)
618 
619  // If the neighbor has not yet been seen, put it on the
620  // stack and assign the respective label
621  if(labeling[v] == -1)
622  {
623  s.push(v);
624 
625  if(cut[e])
626  labeling[v] = (labeling[u] + 1) % 2; // mapping 0->1 and 1->0
627  else
628  labeling[v] = labeling[u];
629  }
630 
631  // Check for inconsistent cut when encountering a node again
632  if(labeling[v] != -1) // node already seen
633  {
634  if(cut[e]) {
635  OPENGM_ASSERT(labeling[v] + labeling[u] == 1);
636  } else {
637  OPENGM_ASSERT(labeling[v] == labeling[u]);
638  }
639  }
640  }
641  }
642  }
643 
644  for(size_t v=0; v<labeling.size(); ++v) {
645  OPENGM_ASSERT(labeling[v] == 0 || labeling[v] == 1);
646  }
647 
648  return labeling;
649  }
650 
651 
652  void PlanarGraph::get_labeling(std::vector<int> & x) const
653  {
654  std::vector<bool> cut = get_cut();
655  x = get_labeling_from_cut(cut);
656  }
657 
658  } //namespace planargraph
659  } // namespace external
660 } // namespace opengm
661 
662 #endif // OPENGM_PLANAR_GRAPH_HXX
The OpenGM namespace.
Definition: config.hxx:43
size_t add_edge(size_t u, size_t v, DataType w)
std::vector< int > get_labeling_from_cut(const std::vector< bool > &cut) const
#define OPENGM_ASSERT(expression)
Definition: opengm.hxx:77
void get_labeling(std::vector< int > &x) const
void add_edge_weight(size_t e, DataType w)
long int find_edge(size_t u, size_t v) const
std::vector< bool > get_cut() const
long int get_following_edge(size_t v, size_t e) const
long int get_dest(size_t v, size_t e) const