Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

PlanarSubgraphTriangles.h
Go to the documentation of this file.
1 
32 #pragma once
33 
35 #include <ogdf/basic/Graph.h>
36 #include <ogdf/basic/GraphCopy.h>
37 #include <ogdf/basic/GraphList.h>
38 #include <ogdf/basic/List.h>
39 #include <ogdf/basic/Module.h>
40 #include <ogdf/basic/basic.h>
41 #include <ogdf/basic/comparer.h>
44 
45 #include <functional>
46 
47 namespace ogdf {
48 
50 
62 template<typename TCost>
64 public:
66 
70  PlanarSubgraphTriangles(bool onlyTriangles = false) : m_onlyTriangles(onlyTriangles) { }
71 
73  virtual PlanarSubgraphTriangles* clone() const override {
75  }
76 
77 protected:
78  virtual Module::ReturnType doCall(const Graph& graph, const List<edge>&, List<edge>& delEdges,
79  const EdgeArray<TCost>* pCost, bool preferredImplyPlanar = false) override {
80  OGDF_ASSERT(isConnected(graph));
82 
83  delEdges.clear();
84  GraphCopy copy(graph);
85  List<edge> edges;
86  copy.allEdges(edges);
87  EdgeArray<bool> includeEdges(copy, false);
88 
89  // sort weighted edges
90  if (pCost != nullptr) {
92  [&](edge e) { return (*pCost)[copy.original(e)]; });
94  [&](adjEntry a) { return (*pCost)[copy.original(a->theEdge())]; });
95  edges.quicksort(edgeCmp);
96 
97  for (node v : copy.nodes) {
98  List<adjEntry> newOrder;
99  v->allAdjEntries(newOrder);
100  newOrder.quicksort(adjCmp);
101  copy.sort(v, newOrder);
102  }
103  }
104 
105  DisjointSets<> components(copy.numberOfNodes());
106  NodeArray<int> set(copy);
107  for (node v : copy.nodes) {
108  set[v] = components.makeSet();
109  }
110 
111  if (!m_onlyTriangles) {
112  // First step: Find as many diamonds as we can.
113  for (edge currentEdge : edges) {
114  // Skip if we already include this edge
115  if (includeEdges[currentEdge]) {
116  continue;
117  }
118 
119  // We assume this edge to be the cord of our diamond. This means we have to find two
120  // distinct triangles to make a diamond, and we will try to prefer ones with higher
121  // weights given by our pCost parameter
122 
123  node source = currentEdge->source();
124  node target = currentEdge->target();
125 
126  edge triangleEdge1 = nullptr, triangleEdge2 = nullptr;
127  node triangleNode = nullptr;
128  int triangleSet = -1;
129 
130  findTriangle(copy, currentEdge, pCost, components, set, [&](node v, edge e1, edge e2) {
131  // Only use the found triangle if none of the nodes are in the same component.
132  // We know that each individually found triangle does not have two nodes in the
133  // same component, so we only have to check the opposite ones.
134  int potentialSet = components.find(set[v]);
135  if (potentialSet == triangleSet) {
136  return false; // This is not a triangle we can use, keep looking!
137  }
138 
139  if (triangleNode == nullptr) {
140  // We don't have a triangle yet, mark this one down as the best we can find from this edge.
141  triangleNode = v;
142  triangleEdge1 = e1;
143  triangleEdge2 = e2;
144  triangleSet = potentialSet;
145  return false; // continue searching for a second triangle to make our diamond
146  } else {
147  // We already found a triangle before, so this is the second-best triangle we can find,
148  // making a diamond.
149  includeEdges[currentEdge] = includeEdges[triangleEdge1] =
150  includeEdges[triangleEdge2] = includeEdges[e1] = includeEdges[e2] =
151  true;
152 
153  // Link up diamond nodes' components. These cannot be on the same connected subgraph yet.
154  int sourceSet = components.find(set[source]);
155  int targetSet = components.find(set[target]);
156  components.link(
157  components.link(components.link(sourceSet, targetSet), triangleSet),
158  potentialSet);
159  return true; // stop looking
160  }
161  });
162  }
163  }
164 
165  // Second step: Find as many triangles as we can.
166  for (edge currentEdge : edges) {
167  if (includeEdges[currentEdge]) {
168  continue;
169  }
170 
171  node source = currentEdge->source();
172  node target = currentEdge->target();
173 
174  findTriangle(copy, currentEdge, pCost, components, set, [&](node v, edge e1, edge e2) {
175  includeEdges[currentEdge] = includeEdges[e1] = includeEdges[e2] = true;
176  int potentialSet = components.find(set[v]);
177  int sourceSet = components.find(set[source]);
178  int targetSet = components.find(set[target]);
179  components.link(components.link(sourceSet, targetSet), potentialSet);
180  return true;
181  });
182  }
183 
184  // Third step: Link unconnected sub graphs
185  for (edge currentEdge : edges) {
186  node source = currentEdge->source();
187  node target = currentEdge->target();
188  int sourceSet = components.find(set[source]);
189  int targetSet = components.find(set[target]);
190  if (sourceSet != targetSet) {
191  includeEdges[currentEdge] = true;
192  components.link(sourceSet, targetSet);
193  }
194 
195  if (!includeEdges[currentEdge]) {
196  delEdges.pushBack(copy.original(currentEdge));
197  }
198  }
199 
201  }
202 
203 
204 private:
206 
208 
216  edge searchEdge(node target, node source, internal::GraphIterator<adjEntry> connectionIterator) {
217  while (connectionIterator != source->adjEntries.end()) {
218  if ((*connectionIterator)->twinNode() == target) {
219  return (*connectionIterator)->theEdge();
220  }
221  connectionIterator++;
222  }
223  return nullptr;
224  }
225 
227 
243  void findTriangle(GraphCopy& copy, edge currentEdge, const EdgeArray<TCost>* pCost,
244  DisjointSets<>& components, NodeArray<int>& set,
245  std::function<bool(node, edge, edge)> callback) {
246  node source = currentEdge->source();
247  node target = currentEdge->target();
248  auto sourceIt = source->adjEntries.begin();
249  auto targetIt = target->adjEntries.begin();
250  int sourceSet = components.find(set[source]);
251  int targetSet = components.find(set[target]);
252  // Our nodes cannot be in the same set.
253  if (sourceSet == targetSet) {
254  return;
255  }
256 
257  while (sourceIt != source->adjEntries.end() && targetIt != target->adjEntries.end()) {
258  if ((*sourceIt)->theEdge() == currentEdge) {
259  sourceIt++;
260  continue; // re-check loop condition
261  }
262  if ((*targetIt)->theEdge() == currentEdge) {
263  targetIt++;
264  continue; // re-check loop condition
265  }
266 
267  // Look for a triangle, starting with the edge with next highest weight
268  // If no weights are given, it does not matter which edge we start with
269  adjEntry potentialConnector;
270  node potentialConnection;
271  internal::GraphIterator<adjEntry> potentialConnectionIterator;
272  if (pCost == nullptr
273  || (*pCost)[copy.original((*sourceIt)->theEdge())]
274  > (*pCost)[copy.original((*targetIt)->theEdge())]) {
275  potentialConnector = *sourceIt;
276  potentialConnection = target;
277  potentialConnectionIterator = targetIt;
278  sourceIt++;
279  } else {
280  potentialConnector = *targetIt;
281  potentialConnection = source;
282  potentialConnectionIterator = sourceIt;
283  targetIt++;
284  }
285  // Note: sourceIt and targetIt may be invalid until next loop
286 
287  node potentialNode = potentialConnector->twinNode();
288  int potentialSet = components.find(set[potentialNode]);
289 
290  // Only use this edge if it doesn't connect back to one of the components
291  if (potentialSet != sourceSet && potentialSet != targetSet) {
292  edge potentialEdge =
293  searchEdge(potentialNode, potentialConnection, potentialConnectionIterator);
294  if (potentialEdge != nullptr) {
295  // We found a triangle.
296  // If our callback returns true, it's signalling that we're done and don't
297  // want to look for another triangle.
298  // If it returns false, we continue looking.
299  if (callback(potentialNode, potentialEdge, potentialConnector->theEdge())) {
300  return;
301  }
302  }
303  }
304  }
305  }
306 };
307 
308 }
ogdf
The namespace for all OGDF objects.
Definition: multilevelmixer.cpp:39
ogdf::PlanarSubgraphModule
Interface for planar subgraph algorithms.
Definition: PlanarSubgraphModule.h:53
Graph.h
Includes declaration of graph class.
ogdf::Module::ReturnType::Feasible
@ Feasible
The solution is feasible.
OGDF_ASSERT
#define OGDF_ASSERT(expr)
Assert condition expr. See doc/build.md for more information.
Definition: basic.h:66
ogdf::internal::GraphIteratorBase
Definition: graph_iterators.h:45
ogdf::DisjointSets::find
int find(disjoint_sets::CompressionOption< CompressionOptions::PathCompression >, int set)
Definition: DisjointSets.h:332
ogdf::GraphCopy
Copies of graphs supporting edge splitting.
Definition: GraphCopy.h:391
ogdf::DisjointSets::makeSet
int makeSet()
Initializes a singleton set.
Definition: DisjointSets.h:235
ogdf::isConnected
bool isConnected(const Graph &G)
Returns true iff G is connected.
ogdf::AdjElement
Class for adjacency list elements.
Definition: Graph_d.h:142
ogdf::AdjElement::theEdge
edge theEdge() const
Returns the edge associated with this adjacency entry.
Definition: Graph_d.h:160
ogdf::DisjointSets::link
int link(disjoint_sets::LinkOption< LinkOptions::Naive >, int set1, int set2)
Definition: DisjointSets.h:624
ogdf::PlanarSubgraphTriangles
Maximum planar subgraph approximation algorithms by Chalermsook/Schmid and Calinescu et al.
Definition: PlanarSubgraphTriangles.h:63
Minisat::Internal::copy
static void copy(const T &from, T &to)
Definition: Alg.h:61
PlanarSubgraphModule.h
Declaration of interface for planar subgraph algorithms.
ogdf::DisjointSets
A Union/Find data structure for maintaining disjoint sets.
Definition: DisjointSets.h:90
ogdf::PlanarSubgraphTriangles::doCall
virtual Module::ReturnType doCall(const Graph &graph, const List< edge > &, List< edge > &delEdges, const EdgeArray< TCost > *pCost, bool preferredImplyPlanar=false) override
Computes the set of edges delEdges which have to be deleted to obtain the planar subgraph.
Definition: PlanarSubgraphTriangles.h:78
GraphList.h
Decralation of GraphElement and GraphList classes.
ogdf::PlanarSubgraphTriangles::searchEdge
edge searchEdge(node target, node source, internal::GraphIterator< adjEntry > connectionIterator)
Finds an edge, starting at a given iterator.
Definition: PlanarSubgraphTriangles.h:216
ogdf::NodeElement::adjEntries
internal::GraphObjectContainer< AdjElement > adjEntries
The container containing all entries in the adjacency list of this node.
Definition: Graph_d.h:271
ogdf::PlanarSubgraphTriangles::clone
virtual PlanarSubgraphTriangles * clone() const override
Returns a new instance of the planarization module with the same settings.
Definition: PlanarSubgraphTriangles.h:73
ogdf::EdgeElement::source
node source() const
Returns the source node of the edge.
Definition: Graph_d.h:398
GraphCopy.h
Declaration of graph copy classes.
ogdf::List< edge >
ogdf::internal::GraphRegisteredArray
RegisteredArray for nodes, edges and adjEntries of a graph.
Definition: Graph_d.h:658
ogdf::Graph
Data type for general directed graphs (adjacency list representation).
Definition: Graph_d.h:869
ogdf::isSimpleUndirected
bool isSimpleUndirected(const Graph &G)
Returns true iff G contains neither self-loops nor undirected parallel edges.
Definition: simple_graph_alg.h:423
ogdf::PlanarSubgraphTriangles::findTriangle
void findTriangle(GraphCopy &copy, edge currentEdge, const EdgeArray< TCost > *pCost, DisjointSets<> &components, NodeArray< int > &set, std::function< bool(node, edge, edge)> callback)
Finds all triangles from a given edge and calls a callback function on them.
Definition: PlanarSubgraphTriangles.h:243
ogdf::PlanarSubgraphTriangles::m_onlyTriangles
bool m_onlyTriangles
Whether we want to only check for triangles.
Definition: PlanarSubgraphTriangles.h:205
ogdf::AdjElement::twinNode
node twinNode() const
Returns the associated node of the corresponding adjacency entry (shorthand for twin()->theNode()).
Definition: Graph_d.h:175
ogdf::PlanarSubgraphTriangles::PlanarSubgraphTriangles
PlanarSubgraphTriangles(bool onlyTriangles=false)
Creates a planarization module based on triangle or diamond matching.
Definition: PlanarSubgraphTriangles.h:70
ogdf::NodeElement::allAdjEntries
void allAdjEntries(ADJLIST &adjList) const
Returns a list with all adjacency entries of this node.
Definition: Graph_d.h:303
basic.h
Basic declarations, included by all source files.
DisjointSets.h
Implementation of disjoint sets data structures (union-find functionality).
ogdf::EdgeElement
Class for the representation of edges.
Definition: Graph_d.h:363
List.h
Declaration of doubly linked lists and iterators.
Module.h
Declares base class for all module types.
ogdf::EdgeElement::target
node target() const
Returns the target node of the edge.
Definition: Graph_d.h:401
ogdf::Module::ReturnType
ReturnType
The return type of a module.
Definition: Module.h:52
comparer.h
Declarations for Comparer objects.
ogdf::NodeElement
Class for the representation of nodes.
Definition: Graph_d.h:240
simple_graph_alg.h
Declaration of simple graph algorithms.
ogdf::GenericComparer
Compare elements based on a single comparable attribute.
Definition: comparer.h:42
ogdf::List::pushBack
iterator pushBack(const E &x)
Adds element x at the end of the list.
Definition: List.h:1547
ogdf::internal::EdgeArrayBase2
RegisteredArray for edges of a graph, specialized for EdgeArray<edge>.
Definition: Graph_d.h:716
ogdf::List::clear
void clear()
Removes all elements from the list.
Definition: List.h:1626