Program Listing for File GraphTools.hpp

Return to documentation for file (include/networkit/graph/GraphTools.hpp)

#ifndef NETWORKIT_GRAPH_GRAPH_TOOLS_HPP_
#define NETWORKIT_GRAPH_GRAPH_TOOLS_HPP_

#include <unordered_map>
#include <unordered_set>
#include <utility>
#include "networkit/graph/TopologicalSort.hpp"

#include <tlx/unused.hpp>
#include <networkit/GlobalState.hpp>
#include <networkit/graph/Graph.hpp>

namespace NetworKit {
namespace GraphTools {

count maxDegree(const Graph &G);

count maxInDegree(const Graph &G);

edgeweight maxWeightedDegree(const Graph &G);

edgeweight maxWeightedInDegree(const Graph &G);

node randomNode(const Graph &G);

std::vector<node> randomNodes(const Graph &G, count n);

node randomNeighbor(const Graph &G, node u);

std::pair<node, node> randomEdge(const Graph &G, bool uniformDistribution = false);

std::vector<std::pair<node, node>> randomEdges(const Graph &G, count nr);

template <class InputIt>
void removeEdgesFromIsolatedSet(Graph &G, InputIt first, InputIt last) {
    count removedEdges = 0;
    while (first != last) {
        const auto u = *first++;
        removedEdges += G.degree(u);
        G.removePartialOutEdges(unsafe, u);
        if (G.isDirected()) {
            G.removePartialInEdges(unsafe, u);
        }
    }

    G.setEdgeCount(unsafe, G.numberOfEdges() - (G.isDirected() ? removedEdges : removedEdges / 2));
}

std::pair<node, node> size(const Graph &G) noexcept;

double density(const Graph &G) noexcept;

double volume(const Graph &G);

template <class InputIt>
double volume(const Graph &G, InputIt first, InputIt last) {
    double outVolume = 0.0;

    // Compute volume of subgraph
    for (; first != last; ++first)
        outVolume += G.weightedDegree(*first, !G.isDirected());

    return outVolume;
}

template <class InputIt>
double inVolume(const Graph &G, InputIt first, InputIt last) {
    double inVolume = 0.0;

    // Compute volume of subgraph
    for (; first != last; ++first)
        inVolume += G.weightedDegreeIn(*first, !G.isDirected());

    return inVolume;
}

Graph copyNodes(const Graph &G);

Graph subgraphFromNodes(const Graph &G, const std::unordered_set<node> &nodes);

template <class InputIt>
Graph subgraphFromNodes(const Graph &G, InputIt first, InputIt last, bool compact = false) {
    count subgraphIdBound = 0;
    std::unordered_map<node, node> reverseMapping;

    if (compact) {
        for (InputIt it = first; it != last; ++it) {
            reverseMapping[*it] = subgraphIdBound;
            ++subgraphIdBound;
        }
    } else {
        subgraphIdBound = G.upperNodeIdBound();
    }

    Graph S(subgraphIdBound, G.isWeighted(), G.isDirected());

    if (compact) {
        for (auto nodeIt : reverseMapping) {
            node u = nodeIt.first;
            node localU = nodeIt.second;
            G.forNeighborsOf(u, [&](node v, edgeweight weight) {
                if (!G.isDirected() && u > v)
                    return;

                auto vMapping = reverseMapping.find(v);
                if (vMapping != reverseMapping.end())
                    S.addEdge(localU, vMapping->second, weight);
            });
        }
    } else {
        // First, delete all nodes
        for (node u = 0; u < G.upperNodeIdBound(); ++u) {
            S.removeNode(u);
        }

        // Restore all given nodes
        for (InputIt it = first; it != last; ++it) {
            S.restoreNode(*it);
        }

        G.forEdges([&](node u, node v, edgeweight w) {
            // only include edges if at least one endpoint is in nodes
            if (S.hasNode(u) && S.hasNode(v)) {
                S.addEdge(u, v, w);
            }
        });
    }

    return S;
}

Graph subgraphAndNeighborsFromNodes(const Graph &G, const std::unordered_set<node> &nodes,
                                    bool includeOutNeighbors = false,
                                    bool includeInNeighbors = false);

Graph toUndirected(const Graph &G);

Graph toUnweighted(const Graph &G);

Graph toWeighted(const Graph &G);

Graph transpose(const Graph &G);

void append(Graph &G, const Graph &G1);

void merge(Graph &G, const Graph &G1);

Graph getCompactedGraph(const Graph &graph, const std::unordered_map<node, node> &nodeIdMap);

std::unordered_map<node, node> getContinuousNodeIds(const Graph &graph);

std::unordered_map<node, node> getRandomContinuousNodeIds(const Graph &graph);

std::vector<node> invertContinuousNodeIds(const std::unordered_map<node, node> &nodeIdMap,
                                          const Graph &G);

Graph restoreGraph(const std::vector<node> &invertedIdMap, const Graph &G);

node augmentGraph(Graph &G);

std::pair<Graph, node> createAugmentedGraph(const Graph &G);

void sortEdgesByWeight(Graph &G, bool decreasing = false);

std::vector<node> topologicalSort(const Graph &G,
                                  const std::unordered_map<node, node> &nodeIdMapping = {},
                                  bool checkMapping = false);

template <class Distribution = std::uniform_real_distribution<edgeweight>>
void randomizeWeights(Graph &G, Distribution distr = std::uniform_real_distribution<edgeweight>{
                                    0, std::nexttoward(1.0, 2.0)}) {
    if (!G.isWeighted())
        G = toWeighted(G);
#pragma omp parallel
    {
        std::mt19937 gen;
        // each thread is given is own seed
        const auto baseSeed =
            (GlobalState::getGlobalSeed() != 0 && !GlobalState::getSeedUseThreadId())
                ? Aux::Random::integer()
                : Aux::Random::getSeed();

// static combined with giving each thread its own seed ensures that this process is determinisc
#pragma omp for schedule(static)
        for (omp_index u = 0; u < G.upperNodeIdBound(); ++u) {
            // we use the hash of the origin node so that the weights are independent
            // of  processing order
            gen.seed(std::hash<node>()(u) + 0x9e3779b9 + (baseSeed << 6) + (baseSeed >> 2));
            index j = 0;
            G.forEdgesOf(u, [&](node v) {
                if (u > v && !G.isDirected()) { // avoid visiting edges twice in undirected graphs
                    ++j;
                    return;
                }
                edgeweight ew = distr(gen);
                G.setWeightAtIthNeighbor(Unsafe{}, u, j, ew);
                ++j;
                index k = 0;
                // we need to set the other direction of the edge to the same weight
                if (G.isDirected()) {
                    G.forInEdgesOf(v, [&](node w) {
                        if (w == u) {
                            G.setWeightAtIthInNeighbor(Unsafe{}, v, k, ew);
                        }
                        ++k;
                    });
                } else if (u != v) {
                    G.forEdgesOf(v, [&](node w) {
                        if (w == u) {
                            G.setWeightAtIthNeighbor(Unsafe{}, v, k, ew);
                        }
                        ++k;
                    });
                }
            });
        }
    }
}

template <typename UnaryIdMapper, typename SkipEdgePredicate>
Graph getRemappedGraph(const Graph &graph, count numNodes, UnaryIdMapper &&oldIdToNew,
                       SkipEdgePredicate &&skipNode, bool preallocate = true) {
    tlx::unused(preallocate); // TODO: Add perallocate as soon as Graph supports it

#ifndef NDEBUG
    graph.forNodes([&](node u) { assert(skipNode(u) || oldIdToNew(u) < numNodes); });
#endif // NDEBUG

    const auto directed = graph.isDirected();
    Graph Gnew(numNodes, graph.isWeighted(), directed);

    graph.forNodes([&](const node u) { // TODO: Make parallel when graph support addHalfEdge
        if (skipNode(u))
            return;

        const node mapped_u = oldIdToNew(u);
        graph.forNeighborsOf(u, [&](node, node v, edgeweight ew) {
            if (!directed && v < u)
                return;
            if (skipNode(v))
                return;

            const node mapped_v = oldIdToNew(v);
            Gnew.addEdge(mapped_u, mapped_v, ew);
        });
    });

    return Gnew;
}

template <typename UnaryIdMapper>
Graph getRemappedGraph(const Graph &graph, count numNodes, UnaryIdMapper &&oldIdToNew,
                       bool preallocate = true) {
    return getRemappedGraph(
        graph, numNodes, std::forward<UnaryIdMapper>(oldIdToNew), [](node) { return false; },
        preallocate);
}

} // namespace GraphTools

} // namespace NetworKit

#endif // NETWORKIT_GRAPH_GRAPH_TOOLS_HPP_