Skip to content

Commit

Permalink
Merge branch 'sparsification-random' into sparsification
Browse files Browse the repository at this point in the history
  • Loading branch information
Dominik Rosch committed Jul 9, 2024
2 parents 77f2146 + aec5832 commit 01032d3
Show file tree
Hide file tree
Showing 22 changed files with 477 additions and 50 deletions.
6 changes: 4 additions & 2 deletions apps/KaMinPar.cc
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,8 @@ int main(int argc, char *argv[]) {
std::exit(0);
}

if (ctx.coarsening.sparsification_algorithm == SparsificationAlgorithm::EFFECTIVE_RESISTANCE) {
if (ctx.sparsification.algorithm == SparsificationAlgorithm::EFFECTIVE_RESISTANCE ||
ctx.sparsification.score_function == ScoreFunctionSection::EFFECTIVE_RESISTANCE) {
sparsification::EffectiveResistanceScore::init_julia();
}

Expand Down Expand Up @@ -256,7 +257,8 @@ int main(int argc, char *argv[]) {

DISABLE_HEAP_PROFILER();

if (ctx.coarsening.sparsification_algorithm == SparsificationAlgorithm::EFFECTIVE_RESISTANCE) {
if (ctx.sparsification.algorithm == SparsificationAlgorithm::EFFECTIVE_RESISTANCE ||
ctx.sparsification.score_function == ScoreFunctionSection::EFFECTIVE_RESISTANCE) {
sparsification::EffectiveResistanceScore::finalize_julia();
}

Expand Down
19 changes: 15 additions & 4 deletions kaminpar-cli/kaminpar_arguments.cc
Original file line number Diff line number Diff line change
Expand Up @@ -123,18 +123,29 @@ CLI::Option_group *create_coarsening_options(CLI::App *app, Context &ctx) {
- sparsifying-clustering: like clustering with additionale edge sparsification)")
->capture_default_str();

coarsening->add_option("--c-sparsification", ctx.coarsening.sparsification_algorithm)
coarsening->add_option("--c-sparsification", ctx.sparsification.algorithm)
->transform(CLI::CheckedTransformer(get_sparsification_algorithms()))
->description(R"(One of the following options:
- random, rn: uniform random sampling
- forest-fire, ff: sampling by forest fire scores
- k-neighbour, kn: k-Neighbour sampling
- k-neighbour-spanning-tree, kn-st: k-Neighbour sampling with spanning tree
- weight-threshold, wt: sample edges with weights above threshold
- effective-resistance, er: sample edges with relative effective-resistance above threshold)")
- effective-resistance, er: sample edges with relative effective-resistance above threshold
- independent-random, ir: sample edges indepently with probabilites proportional to scores
- random-with-replacement, rw/r: draw random edges WITH replacment and probailites proportinal to scores
- random-without-replacement, rw/or: draw random edges WITHOUT replacment and probailites proportinal to scores)")
->capture_default_str();

coarsening->add_option("--s-target", ctx.coarsening.sparsification_target)
coarsening->add_option("--s-score", ctx.sparsification.score_function)
->transform(CLI::CheckedTransformer(get_score_function()))
->description(R"(How the scores for sampling are calculated:
- weight, w: use edge weights as scores
- effective-restistance, er: effective resistance relativ to the resistance of an edge
- forest-fire, ff)")
->capture_default_str();

coarsening->add_option("--s-target", ctx.sparsification.target)
->transform(CLI::CheckedTransformer(get_sparsification_target_selection(), CLI::ignore_case)
.description(""))
->description(
Expand All @@ -145,7 +156,7 @@ CLI::Option_group *create_coarsening_options(CLI::App *app, Context &ctx) {
)
->capture_default_str();

coarsening->add_option("--s-factor", ctx.coarsening.sparsification_factor)
coarsening->add_option("--s-factor", ctx.sparsification.target_factor)
->check(CLI::PositiveNumber)
->description(R"(The factor c for the sparsification target, supplied with --s-target.)")
->default_val(1);
Expand Down
3 changes: 3 additions & 0 deletions kaminpar-common/random.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ class Random {
bool random_bool(const double prob) {
return _real_dist(_generator) <= prob;
}
double random_double() {
return _real_dist(_generator);
}

template <typename Container> void shuffle(Container &&vec) {
std::shuffle(vec.begin(), vec.end(), _generator);
Expand Down
11 changes: 8 additions & 3 deletions kaminpar-shm/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
file(GLOB_RECURSE KAMINPAR_SHM_SOURCE_FILES CONFIGURE_DEPENDS
*.cc *.h)
*.cc *.h)

add_library(kaminpar_shm ${KAMINPAR_SHM_SOURCE_FILES}
coarsening/sparsification/Sampler.h
Expand All @@ -21,8 +21,13 @@ add_library(kaminpar_shm ${KAMINPAR_SHM_SOURCE_FILES}
coarsening/sparsification/ScoreBacedSampler.h
coarsening/sparsification/NetworKitScoreAdapter.cpp
coarsening/sparsification/NetworKitScoreAdapter.h
coarsening/sparsification/EffectiveResistanceScore.cpp)
target_include_directories(kaminpar_shm PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../" ${JL_SHARE}/../../include/julia/)
coarsening/sparsification/EffectiveResistanceScore.cpp
coarsening/sparsification/RandomWithReplacementSampler.h
coarsening/sparsification/IndexDistributionWithoutReplacement.h
coarsening/sparsification/IndexDistributionWithReplacement.h
coarsening/sparsification/RandomWithoutReplacementSampler.h
coarsening/sparsification/IndependentRandomSampler.h)
target_include_directories(kaminpar_shm PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../" ${JL_SHARE}/../../include/julia/)
target_link_libraries(kaminpar_shm PUBLIC kaminpar_common networkit ${JL_SHARE}/../../lib/libjulia.so)

# If we can find Mt-KaHyPar, make it available as an option for refinement
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ JULIA_DEFINE_FAST_TLS // only define this once, in an executable (not in a share
// Back to 0-based indexing
sparsifyer.i[k] - 1,
sparsifyer.j[k] - 1,
static_cast<EdgeWeight>(sparsifyer.v[k])
sparsifyer.v[k]
);
}
std::sort(
Expand Down
52 changes: 52 additions & 0 deletions kaminpar-shm/coarsening/sparsification/IndependentRandomSampler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#pragma once
#include "ScoreBacedSampler.h"
#include "sparsification_utils.h"

#include "kaminpar-common/random.h"

namespace kaminpar::shm::sparsification {
template <typename Score> class IndependentRandomSampler : public ScoreBacedSampler<Score> {
public:
IndependentRandomSampler(
std::unique_ptr<ScoreFunction<Score>> scoreFunction,
std::unique_ptr<ReweighingFunction<Score>> reweighingFunction
)
: ScoreBacedSampler<Score>(std::move(scoreFunction), std::move(reweighingFunction)) {}

StaticArray<EdgeWeight> sample(const CSRGraph &g, EdgeID target_edge_amount) override {
auto scores = this->_score_function->scores(g);
double factor = normalizationFactor(g, scores, target_edge_amount);

StaticArray<EdgeWeight> sample(g.m(), 0);
utils::for_upward_edges(g, [&](EdgeID e) {
sample[e] = Random::instance().random_bool(factor * scores[e])
? this->_reweighing_function->new_weight(g.edge_weight(e), scores[e])
: 0;
});
return sample;
}

private:
double normalizationFactor(const CSRGraph &g, const StaticArray<Score> &scores, EdgeID target) {
StaticArray<Score> sorted_scores(g.m() / 2);
StaticArray<Score> prefix_sum(g.m() / 2);
EdgeID i = 0;
utils::for_upward_edges(g, [&](EdgeID e) { sorted_scores[i++] = scores[e]; });
std::sort(sorted_scores.begin(), sorted_scores.end());
parallel::prefix_sum(sorted_scores.begin(), sorted_scores.end(), prefix_sum.begin());

EdgeID upper = 0;
EdgeID lower = sorted_scores.size();
while (lower + 1 < upper) {
EdgeID mid = lower + (upper - lower) / 2;
if (target < (sorted_scores.size() - mid + prefix_sum[mid] / sorted_scores[mid + 1]))
upper = mid;
else
lower = mid;
}
EdgeID index = lower;

return static_cast<double>((target - (sorted_scores.size() - index))) / prefix_sum[index];
}
};
}; // namespace kaminpar::shm::sparsification
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// A random distribution over {0, ..., n-1} with probailies propotional to given values
// Implemented using the alias Method


#include "kaminpar-common/random.h"
namespace kaminpar::shm::sparsification {
template <typename T> class IndexDistributionWithReplacement {
public:
IndexDistributionWithReplacement(std::initializer_list<T> values) {
UniformRandomDistribution(values.begin(), values.end());
}

template <class Iterator> IndexDistributionWithReplacement(Iterator begin, Iterator end) {
size_t size = end - begin;
_probabilities.resize(size);
_aliases.resize(size);
double sum = 0;
for (Iterator current = begin; current != end; current++) {
sum += *current;
_probabilities[current - begin] = *current;
}

std::vector<size_t> too_small, too_large;
for (size_t i = 0; i != _probabilities.size(); i++) {
_probabilities[i] *= size / sum;
if (_probabilities[i] < 1)
too_small.push_back(i);
else if (_probabilities[i] > 1)
too_large.push_back(i);
_aliases[i] = i; // default alias to not get issues from rounding errors
}

while (!too_large.empty() && !too_small.empty()) {
auto large = too_large.back();
too_large.pop_back();
auto small = too_small.back();
too_small.pop_back();

_aliases[small] = large;
_probabilities[large] -= 1 - _probabilities[small];

if (_probabilities[large] < 1)
too_small.push_back(large);
if (_probabilities[large] > 1)
too_large.push_back(large);

}
}

size_t operator()() {
size_t i = Random::instance().random_index(0, _probabilities.size());
return Random::instance().random_bool(_probabilities[i]) ? i : _aliases[i];
}

private:
std::vector<double> _probabilities;
std::vector<size_t> _aliases;
};
} // namespace kaminpar::shm::sparsification
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#pragma once

#include "kaminpar-common/random.h"

namespace kaminpar::shm::sparsification {
class IndexDistributionWithoutReplacement {
public:
IndexDistributionWithoutReplacement(std::vector<double> values) {
IndexDistributionWithoutReplacement(values.begin(), values.end());
}
IndexDistributionWithoutReplacement(StaticArray<double> values) {
IndexDistributionWithoutReplacement(values.begin(), values.end());
}
template <typename Iterator>
IndexDistributionWithoutReplacement(Iterator values_begin, Iterator values_end)
: remaining_objects(values_end - values_begin) {
if (remaining_objects == 0)
return;

// size of a complete binary tree, where all values can be in the leaves
size_t size = 1;
while (size <= 2 * remaining_objects) {
size *= 2;
}
size -= 1;
segment_tree.resize(size, 0);

// initalize leafs
const size_t first_leaf = firstLeaf();
for (size_t leaf = first_leaf; leaf < first_leaf + remaining_objects; leaf++) {
segment_tree[leaf] = *(values_begin + (leaf - first_leaf));
}

// calculate sum of subtrees
for (size_t node = segment_tree.size() - 1; node != 0; node--) {
segment_tree[parent(node)] += segment_tree[node];
}
}

size_t operator()() {
double r = Random::instance().random_double() * segment_tree[0];

size_t current_subtree = 0;
while (not isLeaf(current_subtree)) {
if (r <= segment_tree[leftChild(current_subtree)]) {
current_subtree = leftChild(current_subtree);
} else {
r -= segment_tree[leftChild(current_subtree)];
current_subtree = rightChild(current_subtree);
}
}

size_t index = to_index(current_subtree);
double value = segment_tree[current_subtree];

// delete
while (current_subtree != 0) {
segment_tree[current_subtree] -= value;
current_subtree = parent(current_subtree);
}
segment_tree[0] -= value;

remaining_objects--;
return index;
}

size_t size() {
return remaining_objects;
}
bool empty() {
return remaining_objects == 0;
}

private:
bool isLeaf(size_t i) {
return i >= firstLeaf();
}
size_t parent(size_t i) {
return (i - 1) / 2;
}
size_t leftChild(size_t i) {
return 2 * i + 1;
}
size_t rightChild(size_t i) {
return 2 * i + 2;
}
size_t firstLeaf() {
return segment_tree.size() / 2;
}
size_t to_index(size_t leaf) {
return leaf - firstLeaf();
}
std::vector<double> segment_tree;
size_t remaining_objects;
};
} // namespace kaminpar::shm::sparsification
21 changes: 12 additions & 9 deletions kaminpar-shm/coarsening/sparsification/NetworKitScoreAdapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@
#include "NetworKitScoreAdapter.h"

#include <networkit/graph/Graph.hpp>
#include <networkit/sparsification/ForestFireScore.hpp>

#include "networkit_utils.h"
#include "sparsification_utils.h"

namespace kaminpar::shm::sparsification {
template <typename Score>
StaticArray<Score> NetworKitScoreAdapter<Score>::scores(const CSRGraph &g) {
NetworKit::Graph nk_graph = networkit_utils::toNetworKitGraph(g);
nk_graph.indexEdges();
nk_graph.sortEdges();
template <typename EdgeScore, typename Score>
StaticArray<Score> NetworKitScoreAdapter<EdgeScore, Score>::scores(const CSRGraph &g) {
NetworKit::Graph *nk_graph = networkit_utils::toNetworKitGraph(g);
nk_graph->indexEdges();
nk_graph->sortEdges();

auto scorer = _curried_constructor(nk_graph);
auto scorer = _curried_constructor(*nk_graph);
scorer.run();
auto nk_scores = scorer.scores();

Expand All @@ -25,14 +26,16 @@ StaticArray<Score> NetworKitScoreAdapter<Score>::scores(const CSRGraph &g) {
for (NodeID u : g.nodes()) {
for (EdgeID i = 0; i < g.degree(u); i++) {
EdgeID e = sorted_by_target[g.raw_nodes()[u] + i];
auto [v, nk_e] = nk_graph.getIthNeighborWithId(u, i);
auto [v, nk_e] = nk_graph->getIthNeighborWithId(u, i);
KASSERT(g.edge_target(e) == v, "edge target does not match");
scores[e] = scores[nk_e];
scores[e] = nk_scores[nk_e];
}
}

delete (nk_graph);

return scores;
}
template class NetworKitScoreAdapter<double>;
template class NetworKitScoreAdapter<NetworKit::ForestFireScore, double>;

} // namespace kaminpar::shm::sparsification
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
#include "ScoreBacedSampler.h"

namespace kaminpar::shm::sparsification {
template <typename Score> class NetworKitScoreAdapter : public ScoreFunction<Score> {
template <typename EdgeScore, typename Score> class NetworKitScoreAdapter : public ScoreFunction<Score> {
public:
NetworKitScoreAdapter(std::function<NetworKit::EdgeScore<Score>(NetworKit::Graph)> constructor)
NetworKitScoreAdapter(std::function<EdgeScore(NetworKit::Graph&)> constructor)
: _curried_constructor(constructor) {}
StaticArray<Score> scores(const CSRGraph &g) override;

private:
std::function<NetworKit::EdgeScore<Score>(NetworKit::Graph)> _curried_constructor;
std::function<EdgeScore(NetworKit::Graph&)> _curried_constructor;
};
} // namespace kaminpar::shm::sparsification
Loading

0 comments on commit 01032d3

Please sign in to comment.