8000 Add algorithm for finding/removing non-essential edges · codeface-io/SwiftNodes@61f3d6a · GitHub
[go: up one dir, main page]

Skip to content

Commit 61f3d6a

Browse files
committed
Add algorithm for finding/removing non-essential edges
1 parent f5aa668 commit 61f3d6a

File tree

1 file changed

+74
-0
lines changed

1 file changed

+74
-0
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import SwiftyToolz
2+
3+
public extension Graph
4+
{
5+
/**
6+
Remove edges of the condensation graph that are not in its minimum equivalent graph
7+
8+
Note that this will not remove any edges that are part of cycles (i.e. part of strongly connected components), as it only considers edges of the condensation graph. This is because it's [algorithmically](https://en.wikipedia.org/wiki/Feedback_arc_set#Hardness) as well as conceptually hard to decide which edges in cycles are "non-essential". We recommend dealing with cycles independently of using this function.
9+
*/
10+
mutating func removeNonEssentialEdges()
11+
{
12+
findNonEssentialEdges().forEach { self.removeEdge(with: $0) }
13+
}
14+
15+
/**
16+
Find edges of the condensation graph that are not in its minimum equivalent graph
17+
18+
Note that this will not find any edges that are part of cycles (i.e. part of strongly connected components), as it only considers edges of the condensation graph. This is because it's [algorithmically](https://en.wikipedia.org/wiki/Feedback_arc_set#Hardness) as well as conceptually hard to decide which edges in cycles are "non-essential". We recommend dealing with cycles independently of using this function.
19+
*/
20+
func findNonEssentialEdges() -> Set<Edge.ID>
21+
{
22+
var idsOfNonEssentialEdges = Set<Edge.ID>()
23+
24+
// for each component graph individually ...
25+
for component in findComponents()
26+
{
27+
let componentIDs = Set(component.map({ $0.id }))
28+
let componentGraph = subGraph(nodeIDs: componentIDs)
29+
30+
// make condensation graph
31+
let condensationGraph = componentGraph.makeCondensationGraph()
32+
33+
// remember in which condensation node each original node is contained
34+
var condensationNodeIDByNodeID = [NodeID: StronglyConnectedComponent.ID]()
35+
36+
for condensationNode in condensationGraph.nodes
37+
{
38+
for node in condensationNode.value.nodes
39+
{
40+
condensationNodeIDByNodeID[node.id] = condensationNode.id
41+
}
42+
}
43+
44+
// make minimum equivalent condensation graph
45+
let minimumCondensationGraph = condensationGraph.makeMinimumEquivalentGraph()
46+
47+
// for each original edge in the component graph ...
48+
for componentGraphEdge in componentGraph.edges
49+
{
50+
// skip this edge if it is within the same condensation node (within a strongly connected component)
51+
guard let sourceCondensationNodeID = condensationNodeIDByNodeID[componentGraphEdge.originID],
52+
let targetCondensationNodeID = condensationNodeIDByNodeID[componentGraphEdge.destinationID]
53+
else
54+
{
55+
log(error: "Nodes don't have their condensation node IDs set (but must have at this point)")
56+
continue
57+
}
58+
59+
if sourceCondensationNodeID == targetCondensationNodeID { continue }
60+
61+
// remove the edge if the corresponding edge in the condensation graph is not essential
62+
let essentialEdge = minimumCondensationGraph.edge(from: sourceCondensationNodeID,
63+
to: targetCondensationNodeID)
64+
65+
if essentialEdge == nil
66+
{
67+
idsOfNonEssentialEdges += componentGraphEdge.id
68+
}
69+
}
70+
}
71+
72+
return idsOfNonEssentialEdges
73+
}
74+
}

0 commit comments

Comments
 (0)
0