|
| 1 | +# PageRank Feedback Arc Set (PageRankFAS) Algorithm |
| 2 | + |
| 3 | +Based on the paper *"Computing a Feedback Arc Set Using PageRank"* by Geladaris, Lionakis, and Tollis ([arXiv:2208.09234](https://arxiv.org/abs/2208.09234)). |
| 4 | + |
| 5 | +## High-Level Algorithm Flow |
| 6 | + |
| 7 | +```mermaid |
| 8 | +flowchart TD |
| 9 | + A["**Input:** Directed Graph G(V, E)"] --> B["Copy graph into<br/>working graph G'"] |
| 10 | + B --> C{"Does G'<br/>have cycles?"} |
| 11 | + C -- No --> D["**Output:** Feedback Arc Set<br/>(set of removed edges)"] |
| 12 | + C -- Yes --> E["Find Strongly Connected<br/>Components (SCCs)<br/>using Kosaraju's algorithm"] |
| 13 | + E --> F["Filter to non-trivial SCCs<br/>(size > 1)"] |
| 14 | + F --> G["Process each SCC"] |
| 15 | + G --> H["Extract subgraph<br/>for this SCC"] |
| 16 | + H --> I["Build Line Digraph L(G)<br/>from SCC subgraph"] |
| 17 | + I --> J["Run PageRank<br/>on Line Digraph"] |
| 18 | + J --> K["Select edge with<br/>highest PageRank score"] |
| 19 | + K --> L["Remove edge from G'<br/>and add to FAS"] |
| 20 | + L --> C |
| 21 | +``` |
| 22 | + |
| 23 | +## Line Digraph Construction |
| 24 | + |
| 25 | +Each edge in the original graph becomes a **vertex** in the line digraph. Edges in the line digraph represent adjacency (consecutive traversal) in the original graph. |
| 26 | + |
| 27 | +```mermaid |
| 28 | +flowchart TD |
| 29 | + subgraph Original["**Original SCC Subgraph**"] |
| 30 | + direction LR |
| 31 | + oA((A)) -->|e1| oB((B)) |
| 32 | + oB -->|e2| oC((C)) |
| 33 | + oC -->|e3| oA |
| 34 | + oA -->|e4| oC |
| 35 | + end |
| 36 | +
|
| 37 | + Original --> Transform["Transform: each original edge<br/>becomes a line digraph vertex"] |
| 38 | +
|
| 39 | + subgraph Line["**Line Digraph L(G)**"] |
| 40 | + direction LR |
| 41 | + le1["e1 (A→B)"] -->|"B is source of e2"| le2["e2 (B→C)"] |
| 42 | + le2 -->|"C is source of e3"| le3["e3 (C→A)"] |
| 43 | + le2 -->|"C is source of... none extra"| le2x[ ] |
| 44 | + le3 -->|"A is source of e1"| le1 |
| 45 | + le3 -->|"A is source of e4"| le4["e4 (A→C)"] |
| 46 | + le4 -->|"C is source of e3"| le3 |
| 47 | + le1 -.-> le4x[ ] |
| 48 | + end |
| 49 | +
|
| 50 | + style le2x display:none |
| 51 | + style le4x display:none |
| 52 | +``` |
| 53 | + |
| 54 | +## Line Digraph Edge Creation (DFS-Based — Algorithm 3) |
| 55 | + |
| 56 | +```mermaid |
| 57 | +flowchart TD |
| 58 | + S["Start DFS from<br/>arbitrary vertex v₀"] --> V["Visit vertex v,<br/>mark as visited"] |
| 59 | + V --> OE["For each outgoing<br/>edge e of v"] |
| 60 | + OE --> LV["Get LineVertex<br/>for edge e"] |
| 61 | + LV --> PREV{"Previous<br/>LineVertex<br/>exists?"} |
| 62 | + PREV -- Yes --> ADD_EDGE["Add edge:<br/>prevLineVertex → currentLineVertex<br/>in Line Digraph"] |
| 63 | + PREV -- No --> CHECK |
| 64 | + ADD_EDGE --> CHECK{"Target of e<br/>already visited?"} |
| 65 | + CHECK -- No --> REC["Recurse DFS on target<br/>with currentLineVertex as prev"] |
| 66 | + CHECK -- Yes --> BACK["Add edges from currentLineVertex<br/>to all LineVertices of<br/>target's outgoing edges<br/>(back-edge handling)"] |
| 67 | + REC --> OE |
| 68 | + BACK --> OE |
| 69 | +``` |
| 70 | + |
| 71 | +## PageRank Computation (Algorithm 4) |
| 72 | + |
| 73 | +```mermaid |
| 74 | +flowchart TD |
| 75 | + INIT["Initialize all LineVertex scores<br/>score(v) = 1 / N"] --> ITER{"Iteration<br/>i < maxIterations?"} |
| 76 | + ITER -- No --> RESULT["Return PageRank scores<br/>for all LineVertices"] |
| 77 | + ITER -- Yes --> NEWMAP["Create new score map<br/>(all zeros)"] |
| 78 | + NEWMAP --> EACH["For each LineVertex v<br/>(in parallel)"] |
| 79 | + EACH --> SINK{"v has outgoing<br/>neighbors?"} |
| 80 | + SINK -- "No (sink)" --> SELF["newScore(v) += score(v)<br/>(keep score on itself)"] |
| 81 | + SINK -- Yes --> DIST["Distribute score(v) equally<br/>among outgoing neighbors:<br/>each gets score(v) / outDegree(v)"] |
| 82 | + DIST --> MERGE["newScore(target) += share<br/>using atomic merge"] |
| 83 | + SELF --> SWAP |
| 84 | + MERGE --> SWAP["Swap: currentScores = newScores"] |
| 85 | + SWAP --> ITER |
| 86 | +``` |
| 87 | + |
| 88 | +## Selecting the Feedback Edge |
| 89 | + |
| 90 | +```mermaid |
| 91 | +flowchart LR |
| 92 | + PR["PageRank scores<br/>on Line Digraph"] --> MAX["Find LineVertex with<br/>**maximum** PageRank score"] |
| 93 | + MAX --> ORIG["Map back to<br/>original edge via<br/>LineVertex.getOriginalEdge()"] |
| 94 | + ORIG --> REMOVE["Remove edge from<br/>working graph &<br/>add to FAS"] |
| 95 | +``` |
| 96 | + |
| 97 | +## Class Relationships |
| 98 | + |
| 99 | +```mermaid |
| 100 | +classDiagram |
| 101 | + class PageRankFAS~V, E~ { |
| 102 | + -Graph originalGraph |
| 103 | + -int pageRankIterations |
| 104 | + -Class edgeClass |
| 105 | + +computeFeedbackArcSet() Set~E~ |
| 106 | + -processStronglyConnectedComponent(graph, scc) E |
| 107 | + -createLineDigraph(graph) LineDigraph |
| 108 | + -createLineDigraphEdges(graph, lineDigraph, map) |
| 109 | + -createLineDigraphEdgesDFS(graph, lineDigraph, map, vertex, prev, visited) |
| 110 | + -computePageRank(lineDigraph) Map |
| 111 | + -applyOneIteration(vertices, lineDigraph, current, new) |
| 112 | + -findStronglyConnectedComponents(graph) List |
| 113 | + -hasCycles(graph) boolean |
| 114 | + -createGraphCopy(original) Graph |
| 115 | + -createSubgraph(graph, vertices) Graph |
| 116 | + } |
| 117 | +
|
| 118 | + class LineDigraph~V, E~ { |
| 119 | + -Set vertices |
| 120 | + -Map adjacencyMap |
| 121 | + -Map incomingMap |
| 122 | + +addVertex(vertex) boolean |
| 123 | + +addEdge(source, target) boolean |
| 124 | + +vertexSet() Set |
| 125 | + +getOutgoingNeighbors(vertex) Set |
| 126 | + +getIncomingNeighbors(vertex) Set |
| 127 | + } |
| 128 | +
|
| 129 | + class LineVertex~V, E~ { |
| 130 | + -V source |
| 131 | + -V target |
| 132 | + -E originalEdge |
| 133 | + +getSource() V |
| 134 | + +getTarget() V |
| 135 | + +getOriginalEdge() E |
| 136 | + } |
| 137 | +
|
| 138 | + PageRankFAS --> LineDigraph : creates |
| 139 | + PageRankFAS --> LineVertex : creates |
| 140 | + LineDigraph o-- LineVertex : contains |
| 141 | +``` |
0 commit comments