Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

Loading...
Searching...
No Matches
PlanarSubgraphTriangles.h
Go to the documentation of this file.
1
32#pragma once
33
35#include <ogdf/basic/Graph.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
47namespace ogdf {
48
50
62template<typename TCost>
64public:
66
70 PlanarSubgraphTriangles(bool onlyTriangles = false) : m_onlyTriangles(onlyTriangles) { }
71
73 virtual PlanarSubgraphTriangles* clone() const override {
75 }
76
77protected:
78 virtual Module::ReturnType doCall(const Graph& graph, const List<edge>&, List<edge>& delEdges,
79 const EdgeArray<TCost>* pCost, bool preferredImplyPlanar = false) override {
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
204private:
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}
Implementation of disjoint sets data structures (union-find functionality).
Includes declaration of graph class.
Declaration of graph copy classes.
Decralation of GraphElement and GraphList classes.
Declaration of doubly linked lists and iterators.
Declares base class for all module types.
Declaration of interface for planar subgraph algorithms.
Basic declarations, included by all source files.
Class for adjacency list elements.
Definition Graph_d.h:143
edge theEdge() const
Returns the edge associated with this adjacency entry.
Definition Graph_d.h:161
node twinNode() const
Returns the associated node of the corresponding adjacency entry (shorthand for twin()->theNode()).
Definition Graph_d.h:176
A Union/Find data structure for maintaining disjoint sets.
int makeSet()
Initializes a singleton set.
int find(disjoint_sets::CompressionOption< CompressionOptions::PathCompression >, int set)
int link(disjoint_sets::LinkOption< LinkOptions::Naive >, int set1, int set2)
Class for the representation of edges.
Definition Graph_d.h:364
node target() const
Returns the target node of the edge.
Definition Graph_d.h:402
node source() const
Returns the source node of the edge.
Definition Graph_d.h:399
const Graph & original() const
Returns a reference to the original graph.
Definition GraphCopy.h:104
Copies of graphs supporting edge splitting.
Definition GraphCopy.h:390
Data type for general directed graphs (adjacency list representation).
Definition Graph_d.h:866
int numberOfNodes() const
Returns the number of nodes in the graph.
Definition Graph_d.h:979
void sort(node v, const ADJ_ENTRY_LIST &newOrder)
Sorts the adjacency list of node v according to newOrder.
Definition Graph_d.h:1480
void allEdges(CONTAINER &edgeContainer) const
Returns a container with all edges of the graph.
Definition Graph_d.h:1043
internal::GraphObjectContainer< NodeElement > nodes
The container containing all node objects.
Definition Graph_d.h:929
Doubly linked lists (maintaining the length of the list).
Definition List.h:1451
void clear()
Removes all elements from the list.
Definition List.h:1626
iterator pushBack(const E &x)
Adds element x at the end of the list.
Definition List.h:1547
void quicksort()
Sorts the list using Quicksort.
Definition List.h:1331
ReturnType
The return type of a module.
Definition Module.h:52
@ Feasible
The solution is feasible.
Class for the representation of nodes.
Definition Graph_d.h:241
internal::GraphObjectContainer< AdjElement > adjEntries
The container containing all entries in the adjacency list of this node.
Definition Graph_d.h:272
Interface for planar subgraph algorithms.
Maximum planar subgraph approximation algorithms by Chalermsook/Schmid and Calinescu et al.
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.
edge searchEdge(node target, node source, internal::GraphIterator< adjEntry > connectionIterator)
Finds an edge, starting at a given iterator.
PlanarSubgraphTriangles(bool onlyTriangles=false)
Creates a planarization module based on triangle or diamond matching.
bool m_onlyTriangles
Whether we want to only check for triangles.
virtual PlanarSubgraphTriangles * clone() const override
Returns a new instance of the planarization module with the same settings.
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.
RegisteredArray for edges of a graph, specialized for EdgeArray<edge>.
Definition Graph_d.h:717
RegisteredArray for nodes, edges and adjEntries of a graph.
Definition Graph_d.h:659
Declarations for Comparer objects.
bool isConnected(const Graph &G)
Returns true iff G is connected.
bool isSimpleUndirected(const Graph &G)
Returns true iff G contains neither self-loops nor undirected parallel edges.
#define OGDF_ASSERT(expr)
Assert condition expr. See doc/build.md for more information.
Definition basic.h:52
The namespace for all OGDF objects.
Declaration of simple graph algorithms.
Compare elements based on a single comparable attribute.
Definition comparer.h:402