Skip to content

Commit

Permalink
Merge pull request #456 from angriman/refactor/strongly-connected-com…
Browse files Browse the repository at this point in the history
…ponents

Refactor: Strongly Connected Components
  • Loading branch information
angriman committed May 22, 2020
2 parents 257584c + 0bcdb8b commit 7d8424b
Show file tree
Hide file tree
Showing 10 changed files with 390 additions and 199 deletions.
117 changes: 96 additions & 21 deletions include/networkit/components/StronglyConnectedComponents.hpp
Original file line number Diff line number Diff line change
@@ -1,70 +1,145 @@
/*
* StronglyConnectedComponents.h
* StronglyConnectedComponents.hpp
*
* Created on: 01.06.2014
* Author: Klara Reichard (klara.reichard@gmail.com), Marvin Ritter (marvin.ritter@gmail.com)
* Authors: Klara Reichard <klara.reichard@gmail.com>
* Marvin Ritter <marvin.ritter@gmail.com>
* Obada Mahdi <omahdi@gmail.com>
* Eugenio Angriman <angrimae@hu-berlin.de>
*/

// networkit-format

#ifndef NETWORKIT_COMPONENTS_STRONGLY_CONNECTED_COMPONENTS_HPP_
#define NETWORKIT_COMPONENTS_STRONGLY_CONNECTED_COMPONENTS_HPP_

#include <map>
#include <vector>

#include <networkit/base/Algorithm.hpp>
#include <networkit/graph/Graph.hpp>
#include <networkit/structures/Partition.hpp>

#include <tlx/define/deprecated.hpp>

namespace NetworKit {

/**
* @ingroup components
* Determines the strongly connected components of an directed graph.
*
*/
class StronglyConnectedComponents {
class StronglyConnectedComponents final : public Algorithm {

public:
StronglyConnectedComponents(const Graph& G, bool iterativeAlgo=true);
/**
* Determines the strongly connected components of a directed graph using Tarjan's algorithm.
*
* @param G Graph A directed graph.
*/
StronglyConnectedComponents(const Graph &G);

/**
* This method determines the connected components for the graph g
* (by default: iteratively).
* Determines the strongly connected components of a directed graph using Tarjan's algorithm.
*
* @param G Graph A directed graph.
* @param iterativeAlgo bool Whether to use the iterative algorithm or the recursive one.
*
* This constructor has been deprecated because the recursive algorithm has been deprecated.
*/
void run();
TLX_DEPRECATED(StronglyConnectedComponents(const Graph &G, bool iterativeAlgo));

/**
* Runs the algorithm.
*/
void run() override;

/**
* This method determines the connected components for the graph g
* (iterative implementation).
*
* This method is deprecated, run() already uses the iterative implementation.
*/
void runIteratively();
void TLX_DEPRECATED(runIteratively());

/**
* This method determines the connected components for the graph g
* (recursive implementation).
*
* This method is deprecated, use run().
*/
void runRecursively();
void TLX_DEPRECATED(runRecursively());

/**
* This method returns the number of connected components.
* Return the number of connected components.
*
* @return count The number of components of the graph.
*/
count numberOfComponents();
count numberOfComponents() const {
assureFinished();
return componentIndex;
}

/**
* This method returns the the component in which node query is situated.
* Returns the component of the input node.
*
* @param[in] query the node whose component is asked for
* @param[in] u The input node.
* @return count The index of the component of the input node.
*/
count componentOfNode(node u);
index componentOfNode(node u) const {
assureFinished();
assert(G->hasNode(u));
return component[u];
}

/**
* Return a Partition object that represents the components.
*
* @return Partition Partitioning of the strongly connected components.
*/
Partition getPartition() const {
assureFinished();
return Partition(component);
}

/**
* Return a Partition that represents the components
* Return a map with the component indexes as keys, and their size as values.
*
* @return std::map<index, count> Map with components indexes as keys, and their size as values.
*/
Partition getPartition();
std::map<node, count> getComponentSizes() const {
assureFinished();

std::vector<count> componentSizes(componentIndex, 0);
G->forNodes([&](const node u) { ++componentSizes[component[u]]; });

std::map<node, count> result;
for (index i = 0; i < componentIndex; ++i)
result[i] = componentSizes[i];

return result;
}

/**
* Return a list of components.
*
* @return std::vector<std::vector<node>> List of components.
*/
std::vector<std::vector<node>> getComponents() const {
assureFinished();
std::vector<std::vector<node>> result(componentIndex);

G->forNodes([&](const node u) { result[component[u]].push_back(u); });

return result;
}

private:
const Graph* G;
const Graph *G;
bool iterativeAlgo;
Partition component;
std::vector<index> component;
index componentIndex;
};

}

} // namespace NetworKit

#endif // NETWORKIT_COMPONENTS_STRONGLY_CONNECTED_COMPONENTS_HPP_
20 changes: 1 addition & 19 deletions include/networkit/graph/Graph.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1769,25 +1769,7 @@ class Graph final {
}

/**
* Get i-th (outgoing) neighbor of @a u.
* WARNING: This function is deprecated or only temporary.
*
* @param u Node.
* @param i index; should be in [0, degreeOut(u))
* @return @a i -th (outgoing) neighbor of @a u, or @c none if no such
* neighbor exists.
*/
template <bool graphIsDirected>
node getIthNeighbor(node u, index i) const {
node v = outEdges[u][i];
if (useEdgeInIteration<graphIsDirected>(u, v))
return v;
else
return none;
}

/**
* Get i-th (outgoing) neighbor of @a u.
* Return the i-th (outgoing) neighbor of @a u.
*
* @param u Node.
* @param i index; should be in [0, degreeOut(u))
Expand Down
98 changes: 70 additions & 28 deletions networkit/_NetworKit.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -6782,62 +6782,104 @@ cdef class ParallelConnectedComponents(Algorithm):
cdef extern from "<networkit/components/StronglyConnectedComponents.hpp>":

cdef cppclass _StronglyConnectedComponents "NetworKit::StronglyConnectedComponents":
_StronglyConnectedComponents(_Graph G, bool_t iterativeAlgo) except +
_StronglyConnectedComponents(_Graph G, bool_t) except +
void run() nogil except +
void runIteratively() nogil except +
void runRecursively() nogil except +
count numberOfComponents() except +
count componentOfNode(node query) except +
_Partition getPartition() except +
map[node, count] getComponentSizes() except +
vector[vector[node]] getComponents() except +


cdef class StronglyConnectedComponents:
""" Determines the connected components and associated values for
a directed graph.
By default, the iterative implementation is used. If edges on the graph have been removed,
you should switch to the recursive implementation.
Parameters
----------
G : networkit.Graph
The graph.
iterativeAlgo : bool
Specifies which implementation to use, by default True for the iterative implementation.
"""
cdef _StronglyConnectedComponents* _this
cdef Graph _G

def __cinit__(self, Graph G, iterativeAlgo = True):
""" Computes the strongly connected components of a directed graph.
Parameters
----------
G : networkit.Graph
The graph.
iterativeAlgo : bool
Whether to use the iterative algorithm or the recursive one.
"""
if not iterativeAlgo:
from warnings import warn
warn("The recursive implementation of StronglyConnectedComponents has been deprecated.")
self._G = G
self._this = new _StronglyConnectedComponents(G._this, iterativeAlgo)

def __dealloc__(self):
del self._this

def run(self):
"""
Runs the algorithm.
"""
with nogil:
self._this.run()
return self

def runIteratively(self):
with nogil:
self._this.runIteratively()
return self

def runRecursively(self):
with nogil:
self._this.runRecursively()
return self

def getPartition(self):
"""
Returns a Partition object representing the strongly connected components.
Returns
-------
Partition
The strongly connected components.
"""
return Partition().setThis(self._this.getPartition())

def numberOfComponents(self):
"""
Returns the number of strongly connected components of the graph.
Returns
-------
int
The number of strongly connected components.
"""
return self._this.numberOfComponents()

def componentOfNode(self, v):
return self._this.componentOfNode(v)
def componentOfNode(self, u):
"""
Returns the component of node `u`.
Parameters
----------
u : node
A node in the graph.
Returns
int
The component of node `u`.
"""
return self._this.componentOfNode(u)

def getComponentSizes(self):
"""
Returns a map with the component indexes as keys, and their size as values.
Returns
-------
map[index, count]
Map with component indexes as keys, and their size as values.
"""
return self._this.getComponentSizes()

def getComponents(self):
"""
Returns a list of components.
Returns
-------
list[list[node]]
A list of components.
"""
return self._this.getComponents()


cdef extern from "<networkit/components/WeaklyConnectedComponents.hpp>":
Expand Down
18 changes: 9 additions & 9 deletions networkit/cpp/centrality/TopCloseness.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,14 @@ void TopCloseness::computeReachableNodesDir() {
// We compute the vector sccs_vec, where each component contains the list of
// its nodes
for (count v = 0; v < n; v++) {
sccs_vec[sccs.componentOfNode(v) - 1].push_back(v);
sccs_vec[sccs.componentOfNode(v)].push_back(v);
}

// We compute the SCC graph and store it in sccGraph
for (count V = 0; V < N; V++) {
for (count v : sccs_vec[V]) {
G.forNeighborsOf(v, [&](node w) {
count W = sccs.componentOfNode(w) - 1;
count W = sccs.componentOfNode(w);

if (W != V && !found[W]) {
found[W] = true;
Expand Down Expand Up @@ -145,13 +145,13 @@ void TopCloseness::computeReachableNodesDir() {
auto &reachL = *(reachLPtr.get());
auto &reachU = *(reachUPtr.get());
for (count v = 0; v < n; v++) {
reachL[v] = reachL_scc[sccs.componentOfNode(v) - 1];
reachU[v] = reachU_scc[sccs.componentOfNode(v) - 1];
if (false) { // MICHELE: used to check if the bounds are correct
count r = 0;
Traversal::BFSfrom(G, v, [&](node, count) { ++r; });
assert(reachL[v] <= r && reachU[v] >= r);
}
reachL[v] = reachL_scc[sccs.componentOfNode(v)];
reachU[v] = reachU_scc[sccs.componentOfNode(v)];
#ifndef NDEBUG
count r = 0;
Traversal::BFSfrom(G, v, [&](node, count) { r++; });
assert(reachL[v] <= r && reachU[v] >= r);
#endif
}
}

Expand Down
6 changes: 3 additions & 3 deletions networkit/cpp/centrality/TopHarmonicCloseness.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -464,14 +464,14 @@ void TopHarmonicCloseness::computeReachableNodesDirected() {
// its nodes
for (count v = 0; v < n; v++) {
component[v] = sccs.componentOfNode(v);
sccs_vec[sccs.componentOfNode(v) - 1].push_back(v);
sccs_vec[sccs.componentOfNode(v)].push_back(v);
}

// We compute the SCC graph and store it in sccGraph
for (count V = 0; V < N; V++) {
for (node v : sccs_vec[V]) {
G.forNeighborsOf(v, [&](node w) {
count W = sccs.componentOfNode(w) - 1;
count W = sccs.componentOfNode(w);

if (W != V && !found[W]) {
found[W] = true;
Expand Down Expand Up @@ -533,7 +533,7 @@ void TopHarmonicCloseness::computeReachableNodesDirected() {
}

for (count v = 0; v < n; v++) {
r[v] = reachU_scc[sccs.componentOfNode(v) - 1];
r[v] = reachU_scc[sccs.componentOfNode(v)];
}
}
} // namespace NetworKit
1 change: 1 addition & 0 deletions networkit/cpp/centrality/test/CentralityGTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1220,6 +1220,7 @@ TEST_P(CentralityGTest, testTopCloseness) {
TEST_F(CentralityGTest, testTopHarmonicClosenessDirected) {
count size = 400;
count k = 10;
Aux::Random::setSeed(42, false);
Graph G1 = DorogovtsevMendesGenerator(size).generate();
Graph G(G1.upperNodeIdBound(), false, true);
G1.forEdges([&](node u, node v) {
Expand Down
Loading

0 comments on commit 7d8424b

Please sign in to comment.