Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

PlanarSubgraphTriangles.h
Go to the documentation of this file.
1 
32 #pragma once
33 
37 
38 namespace ogdf {
39 
41 
53 template<typename TCost>
55 public:
57 
61  PlanarSubgraphTriangles(bool onlyTriangles = false) : m_onlyTriangles(onlyTriangles) { }
62 
64  virtual PlanarSubgraphTriangles* clone() const override {
66  }
67 
68 protected:
69  virtual Module::ReturnType doCall(const Graph& graph, const List<edge>&, List<edge>& delEdges,
70  const EdgeArray<TCost>* pCost, bool preferredImplyPlanar = false) override {
71  OGDF_ASSERT(isConnected(graph));
73 
74  delEdges.clear();
75  GraphCopy copy(graph);
76  List<edge> edges;
77  copy.allEdges(edges);
78  EdgeArray<bool> includeEdges(copy, false);
79 
80  // sort weighted edges
81  if (pCost != nullptr) {
83  [&](edge e) { return (*pCost)[copy.original(e)]; });
85  [&](adjEntry a) { return (*pCost)[copy.original(a->theEdge())]; });
86  edges.quicksort(edgeCmp);
87 
88  for (node v : copy.nodes) {
89  List<adjEntry> newOrder;
90  v->allAdjEntries(newOrder);
91  newOrder.quicksort(adjCmp);
92  copy.sort(v, newOrder);
93  }
94  }
95 
96  DisjointSets<> components(copy.numberOfNodes());
97  NodeArray<int> set(copy);
98  for (node v : copy.nodes) {
99  set[v] = components.makeSet();
100  }
101 
102  if (!m_onlyTriangles) {
103  // First step: Find as many diamonds as we can.
104  for (edge currentEdge : edges) {
105  // Skip if we already include this edge
106  if (includeEdges[currentEdge]) {
107  continue;
108  }
109 
110  // We assume this edge to be the cord of our diamond. This means we have to find two
111  // distinct triangles to make a diamond, and we will try to prefer ones with higher
112  // weights given by our pCost parameter
113 
114  node source = currentEdge->source();
115  node target = currentEdge->target();
116 
117  edge triangleEdge1 = nullptr, triangleEdge2 = nullptr;
118  node triangleNode = nullptr;
119  int triangleSet = -1;
120 
121  findTriangle(copy, currentEdge, pCost, components, set, [&](node v, edge e1, edge e2) {
122  // Only use the found triangle if none of the nodes are in the same component.
123  // We know that each individually found triangle does not have two nodes in the
124  // same component, so we only have to check the opposite ones.
125  int potentialSet = components.find(set[v]);
126  if (potentialSet == triangleSet) {
127  return false; // This is not a triangle we can use, keep looking!
128  }
129 
130  if (triangleNode == nullptr) {
131  // We don't have a triangle yet, mark this one down as the best we can find from this edge.
132  triangleNode = v;
133  triangleEdge1 = e1;
134  triangleEdge2 = e2;
135  triangleSet = potentialSet;
136  return false; // continue searching for a second triangle to make our diamond
137  } else {
138  // We already found a triangle before, so this is the second-best triangle we can find,
139  // making a diamond.
140  includeEdges[currentEdge] = includeEdges[triangleEdge1] =
141  includeEdges[triangleEdge2] = includeEdges[e1] = includeEdges[e2] =
142  true;
143 
144  // Link up diamond nodes' components. These cannot be on the same connected subgraph yet.
145  int sourceSet = components.find(set[source]);
146  int targetSet = components.find(set[target]);
147  components.link(
148  components.link(components.link(sourceSet, targetSet), triangleSet),
149  potentialSet);
150  return true; // stop looking
151  }
152  });
153  }
154  }
155 
156  // Second step: Find as many triangles as we can.
157  for (edge currentEdge : edges) {
158  if (includeEdges[currentEdge]) {
159  continue;
160  }
161 
162  node source = currentEdge->source();
163  node target = currentEdge->target();
164 
165  findTriangle(copy, currentEdge, pCost, components, set, [&](node v, edge e1, edge e2) {
166  includeEdges[currentEdge] = includeEdges[e1] = includeEdges[e2] = true;
167  int potentialSet = components.find(set[v]);
168  int sourceSet = components.find(set[source]);
169  int targetSet = components.find(set[target]);
170  components.link(components.link(sourceSet, targetSet), potentialSet);
171  return true;
172  });
173  }
174 
175  // Third step: Link unconnected sub graphs
176  for (edge currentEdge : edges) {
177  node source = currentEdge->source();
178  node target = currentEdge->target();
179  int sourceSet = components.find(set[source]);
180  int targetSet = components.find(set[target]);
181  if (sourceSet != targetSet) {
182  includeEdges[currentEdge] = true;
183  components.link(sourceSet, targetSet);
184  }
185 
186  if (!includeEdges[currentEdge]) {
187  delEdges.pushBack(copy.original(currentEdge));
188  }
189  }
190 
192  }
193 
194 
195 private:
197 
199 
207  edge searchEdge(node target, node source, internal::GraphIterator<adjEntry> connectionIterator) {
208  while (connectionIterator != source->adjEntries.end()) {
209  if ((*connectionIterator)->twinNode() == target) {
210  return (*connectionIterator)->theEdge();
211  }
212  connectionIterator++;
213  }
214  return nullptr;
215  }
216 
218 
234  void findTriangle(GraphCopy& copy, edge currentEdge, const EdgeArray<TCost>* pCost,
235  DisjointSets<>& components, NodeArray<int>& set,
236  std::function<bool(node, edge, edge)> callback) {
237  node source = currentEdge->source();
238  node target = currentEdge->target();
239  auto sourceIt = source->adjEntries.begin();
240  auto targetIt = target->adjEntries.begin();
241  int sourceSet = components.find(set[source]);
242  int targetSet = components.find(set[target]);
243  // Our nodes cannot be in the same set.
244  if (sourceSet == targetSet) {
245  return;
246  }
247 
248  while (sourceIt != source->adjEntries.end() && targetIt != target->adjEntries.end()) {
249  if ((*sourceIt)->theEdge() == currentEdge) {
250  sourceIt++;
251  continue; // re-check loop condition
252  }
253  if ((*targetIt)->theEdge() == currentEdge) {
254  targetIt++;
255  continue; // re-check loop condition
256  }
257 
258  // Look for a triangle, starting with the edge with next highest weight
259  // If no weights are given, it does not matter which edge we start with
260  adjEntry potentialConnector;
261  node potentialConnection;
262  internal::GraphIterator<adjEntry> potentialConnectionIterator;
263  if (pCost == nullptr
264  || (*pCost)[copy.original((*sourceIt)->theEdge())]
265  > (*pCost)[copy.original((*targetIt)->theEdge())]) {
266  potentialConnector = *sourceIt;
267  potentialConnection = target;
268  potentialConnectionIterator = targetIt;
269  sourceIt++;
270  } else {
271  potentialConnector = *targetIt;
272  potentialConnection = source;
273  potentialConnectionIterator = sourceIt;
274  targetIt++;
275  }
276  // Note: sourceIt and targetIt may be invalid until next loop
277 
278  node potentialNode = potentialConnector->twinNode();
279  int potentialSet = components.find(set[potentialNode]);
280 
281  // Only use this edge if it doesn't connect back to one of the components
282  if (potentialSet != sourceSet && potentialSet != targetSet) {
283  edge potentialEdge =
284  searchEdge(potentialNode, potentialConnection, potentialConnectionIterator);
285  if (potentialEdge != nullptr) {
286  // We found a triangle.
287  // If our callback returns true, it's signalling that we're done and don't
288  // want to look for another triangle.
289  // If it returns false, we continue looking.
290  if (callback(potentialNode, potentialEdge, potentialConnector->theEdge())) {
291  return;
292  }
293  }
294  }
295  }
296  }
297 };
298 
299 }
ogdf
The namespace for all OGDF objects.
Definition: AugmentationModule.h:36
ogdf::PlanarSubgraphModule
Interface for planar subgraph algorithms.
Definition: PlanarSubgraphModule.h:48
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:54
ogdf::internal::GraphIteratorBase
Definition: graph_iterators.h:44
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:384
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:135
ogdf::AdjElement::theEdge
edge theEdge() const
Returns the edge associated with this adjacency entry.
Definition: Graph_d.h:153
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:54
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:69
ogdf::PlanarSubgraphTriangles::searchEdge
edge searchEdge(node target, node source, internal::GraphIterator< adjEntry > connectionIterator)
Finds an edge, starting at a given iterator.
Definition: PlanarSubgraphTriangles.h:207
ogdf::NodeElement::adjEntries
internal::GraphObjectContainer< AdjElement > adjEntries
The container containing all entries in the adjacency list of this node.
Definition: Graph_d.h:264
ogdf::PlanarSubgraphTriangles::clone
virtual PlanarSubgraphTriangles * clone() const override
Returns a new instance of the planarization module with the same settings.
Definition: PlanarSubgraphTriangles.h:64
ogdf::EdgeElement::source
node source() const
Returns the source node of the edge.
Definition: Graph_d.h:391
ogdf::List< edge >
ogdf::internal::GraphRegisteredArray
RegisteredArray for nodes, edges and adjEntries of a graph.
Definition: Graph_d.h:651
ogdf::Graph
Data type for general directed graphs (adjacency list representation).
Definition: Graph_d.h:862
ogdf::isSimpleUndirected
bool isSimpleUndirected(const Graph &G)
Returns true iff G contains neither self-loops nor undirected parallel edges.
Definition: simple_graph_alg.h:415
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:234
ogdf::PlanarSubgraphTriangles::m_onlyTriangles
bool m_onlyTriangles
Whether we want to only check for triangles.
Definition: PlanarSubgraphTriangles.h:196
ogdf::AdjElement::twinNode
node twinNode() const
Returns the associated node of the corresponding adjacency entry (shorthand for twin()->theNode()).
Definition: Graph_d.h:168
ogdf::PlanarSubgraphTriangles::PlanarSubgraphTriangles
PlanarSubgraphTriangles(bool onlyTriangles=false)
Creates a planarization module based on triangle or diamond matching.
Definition: PlanarSubgraphTriangles.h:61
ogdf::NodeElement::allAdjEntries
void allAdjEntries(ADJLIST &adjList) const
Returns a list with all adjacency entries of this node.
Definition: Graph_d.h:296
DisjointSets.h
Implementation of disjoint sets data structures (union-find functionality).
ogdf::EdgeElement
Class for the representation of edges.
Definition: Graph_d.h:356
ogdf::EdgeElement::target
node target() const
Returns the target node of the edge.
Definition: Graph_d.h:394
ogdf::Module::ReturnType
ReturnType
The return type of a module.
Definition: Module.h:50
ogdf::NodeElement
Class for the representation of nodes.
Definition: Graph_d.h:233
simple_graph_alg.h
Declaration of simple graph algorithms.
ogdf::GenericComparer
Compare elements based on a single comparable attribute.
Definition: comparer.h:398
ogdf::List::pushBack
iterator pushBack(const E &x)
Adds element x at the end of the list.
Definition: List.h:1537
ogdf::internal::EdgeArrayBase2
RegisteredArray for edges of a graph, specialized for EdgeArray<edge>.
Definition: Graph_d.h:709
ogdf::List::clear
void clear()
Removes all elements from the list.
Definition: List.h:1616