Skip to content

Commit

Permalink
add uniform random sampling
Browse files Browse the repository at this point in the history
  • Loading branch information
Dominik Rosch committed May 8, 2024
1 parent c31a63f commit 88982c8
Show file tree
Hide file tree
Showing 11 changed files with 346 additions and 2 deletions.
3 changes: 2 additions & 1 deletion kaminpar-cli/kaminpar_arguments.cc
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ CLI::Option_group *create_coarsening_options(CLI::App *app, Context &ctx) {
->transform(CLI::CheckedTransformer(get_coarsening_algorithms()).description(""))
->description(R"(One of the following options:
- noop: disable coarsening
- clustering: coarsening by clustering and contracting)")
- clustering: coarsening by clustering and contracting
- sparsifying-clustering: like clustering with additionale edge sparsification)")
->capture_default_str();

coarsening
Expand Down
6 changes: 5 additions & 1 deletion kaminpar-shm/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
file(GLOB_RECURSE KAMINPAR_SHM_SOURCE_FILES CONFIGURE_DEPENDS
*.cc *.h)

add_library(kaminpar_shm ${KAMINPAR_SHM_SOURCE_FILES})
add_library(kaminpar_shm ${KAMINPAR_SHM_SOURCE_FILES}
coarsening/sparsification/Sampler.h
coarsening/sparsification/UniformRandomSampler.cpp
coarsening/sparsification/UniformRandomSampler.h
coarsening/sparsifing_cluster_coarsener.h)
target_include_directories(kaminpar_shm PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../")
target_link_libraries(kaminpar_shm PUBLIC kaminpar_common)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ template <template <typename> typename Mapping> class CoarseGraphImpl : public C
return _graph;
}

Mapping<NodeID> &get_mapping() {
return _mapping;
}

void project(const StaticArray<BlockID> &array, StaticArray<BlockID> &onto) final {
tbb::parallel_for<std::size_t>(0, onto.size(), [&](const std::size_t i) {
onto[i] = array[_mapping[i]];
Expand Down
21 changes: 21 additions & 0 deletions kaminpar-shm/coarsening/sparsification/Sampler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// Created by badger on 5/6/24.
//

#ifndef SAMPLER_H
#define SAMPLER_H
#include "kaminpar-shm/datastructures/csr_graph.h"
#include "kaminpar-shm/kaminpar.h"

#include "kaminpar-common/datastructures/static_array.h"

namespace kaminpar::shm::sparsification {

class Sampler {
public:
virtual StaticArray<EdgeWeight> sample(const CSRGraph &g) = 0;
};

} // kaminpar::shm

#endif //SAMPLER_H
30 changes: 30 additions & 0 deletions kaminpar-shm/coarsening/sparsification/UniformRandomSampler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// Created by badger on 5/6/24.
//

#include "UniformRandomSampler.h"

#include "kaminpar-common/random.h"

namespace kaminpar::shm::sparsification {

StaticArray<EdgeWeight> UniformRandomSampler::sample(const CSRGraph &g) {
unsigned int backedges_deleted = 0;
unsigned int backedges = 0;
unsigned int frontedges = 0;
StaticArray<EdgeWeight> sample = StaticArray<EdgeWeight>(g.m());
unsigned int edges_deleted = 0;
for (int v = 0; v < g.n(); ++v) {
for (EdgeID e : g.incident_edges(v)) {
KASSERT(v != g.edge_target(e), "no loops allowed", assert::always);
if (v < g.edge_target(e)) {
frontedges++;
sample[e] = Random::instance().random_bool(_probability) ? g.edge_weight(e) : 0;
if (!sample[e])
edges_deleted++;
}
}
}
return sample;
}
} // namespace kaminpar::shm::sparsification
20 changes: 20 additions & 0 deletions kaminpar-shm/coarsening/sparsification/UniformRandomSampler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// Created by badger on 5/6/24.
//

#ifndef UNIFORMRANDOMSAMPLER_H
#define UNIFORMRANDOMSAMPLER_H
#include "Sampler.h"

namespace kaminpar::shm::sparsification {
class UniformRandomSampler : public Sampler {
private:
float _probability;

public:
UniformRandomSampler(float probability) : _probability(probability) {}
StaticArray<EdgeWeight> sample(const CSRGraph &g) override;
};
} // namespace kaminpar::shm::sparsification

#endif // UNIFORMRANDOMSAMPLER_H
190 changes: 190 additions & 0 deletions kaminpar-shm/coarsening/sparsifing_cluster_coarsener.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
/*******************************************************************************
* cluster corsener with included sparsification
*
* @file: cluster_coarsener.cc
* @author: Dominik Rosch
* @date: 29.09.2021
******************************************************************************/
#include "kaminpar-shm/coarsening/sparsifing_cluster_coarsener.h"

#include "contraction/cluster_contraction_preprocessing.h"
#include "sparsification/UniformRandomSampler.h"

#include "kaminpar-shm/coarsening/contraction/cluster_contraction.h"
#include "kaminpar-shm/coarsening/max_cluster_weights.h"
#include "kaminpar-shm/context_io.h"
#include "kaminpar-shm/factories.h"
#include "kaminpar-shm/kaminpar.h"

#include "kaminpar-common/assert.h"
#include "kaminpar-common/heap_profiler.h"
#include "kaminpar-common/random.h"
#include "kaminpar-common/timer.h"

namespace kaminpar::shm {
SparsifingClusteringCoarsener::SparsifingClusteringCoarsener(
const Context &ctx, const PartitionContext &p_ctx
)
: _clustering_algorithm(factory::create_clusterer(ctx)),
_sampling_algorithm(std::make_unique<sparsification::UniformRandomSampler>(
sparsification::UniformRandomSampler(0.3)
)),
_c_ctx(ctx.coarsening),
_p_ctx(p_ctx) {}

void SparsifingClusteringCoarsener::initialize(const Graph *graph) {
_hierarchy.clear();
_input_graph = graph;
}

/**
* Deletes all edges with weight 0 in the sample and reweights the rest
* Only the sample entries for an edge (v, u) with v < u are considered
* @param csr Graph in csr format
* @param sample for every edge 0, if it should be removed, its (new) weight otherwise
* @param edges_kept how many edges are samples, i.e., how many entries in sample are not 0
*/
CSRGraph
SparsifingClusteringCoarsener::sparsify(const CSRGraph *g, StaticArray<EdgeWeight> sample) {
auto nodes = StaticArray<EdgeID>(g->n() + 1);
for (NodeID v : g->nodes()) {
for (EdgeID e : g->incident_edges(v)) {
NodeID u = g->edge_target(e);
if (v < u && sample[e]) {
nodes[v + 1]++;
nodes[u + 1]++;
}
}
}
parallel::prefix_sum(nodes.begin(), nodes.end(), nodes.begin());

auto edges_added = StaticArray<EdgeID>(g->n(), 0);
auto edges = StaticArray<NodeID>(nodes[g->n()]);
auto edge_weights = StaticArray<EdgeWeight>(nodes[g->n()]);

for (NodeID v : g->nodes()) {
for (EdgeID e : g->incident_edges(v)) {
NodeID u = g->edge_target(e);
if (v < u && sample[e]) {
edges[nodes[v] + edges_added[v]] = u;
edges[nodes[u] + edges_added[u]] = v;
edge_weights[nodes[v] + edges_added[v]] = sample[e];
edge_weights[nodes[u] + edges_added[u]] = sample[e];
edges_added[v]++;
edges_added[u]++;
}
}
}

return CSRGraph(
std::move(nodes),
std::move(edges),
std::move(StaticArray<NodeWeight>(g->raw_node_weights().begin(), g->raw_node_weights().end())
),
std::move(edge_weights)
);
}

bool SparsifingClusteringCoarsener::coarsen() {
SCOPED_HEAP_PROFILER("Level", std::to_string(_hierarchy.size()));
SCOPED_TIMER("Level", std::to_string(_hierarchy.size()));

START_HEAP_PROFILER("Allocation");
RECORD("clustering") StaticArray<NodeID> clustering(current().n(), static_array::noinit);
STOP_HEAP_PROFILER();

const bool free_allocated_memory = !keep_allocated_memory();
const NodeWeight total_node_weight = current().total_node_weight();
const NodeID prev_n = current().n();

START_HEAP_PROFILER("Label Propagation");
START_TIMER("Label Propagation");
_clustering_algorithm->set_max_cluster_weight(
compute_max_cluster_weight<NodeWeight>(_c_ctx, _p_ctx, prev_n, total_node_weight)
);
_clustering_algorithm->set_desired_cluster_count(0);
_clustering_algorithm->compute_clustering(clustering, current(), free_allocated_memory);
STOP_TIMER();
STOP_HEAP_PROFILER();

START_HEAP_PROFILER("Contract graph");
auto coarsened = TIMED_SCOPE("Contract graph") {
return contract_clustering(
current(), std::move(clustering), _c_ctx.contraction, _contraction_m_ctx
);
};

KASSERT(coarsened->get().m() % 2 == 0, "graph should be undirected", assert::always);
const CSRGraph *csr = dynamic_cast<const CSRGraph *>(coarsened->get().underlying_graph());
KASSERT(csr != nullptr, "can only be used with a CSRGraph", assert::always);

auto sample = _sampling_algorithm->sample(*csr);
CSRGraph sparsified = sparsify(csr, std::move(sample));

_hierarchy.push_back(std::make_unique<contraction::CoarseGraphImpl<StaticArray>>(
Graph(std::make_unique<CSRGraph>(std::move(sparsified))),
std::move(
dynamic_cast<contraction::CoarseGraphImpl<StaticArray> *>(coarsened.get())->get_mapping()
)
));
STOP_HEAP_PROFILER();

const NodeID next_n = current().n();
const bool converged = (1.0 - 1.0 * next_n / prev_n) <= _c_ctx.convergence_threshold;

if (free_allocated_memory) {
_contraction_m_ctx.buckets.free();
_contraction_m_ctx.buckets_index.free();
_contraction_m_ctx.all_buffered_nodes.free();
}

return !converged;
}

PartitionedGraph SparsifingClusteringCoarsener::uncoarsen(PartitionedGraph &&p_graph) {
SCOPED_HEAP_PROFILER("Level", std::to_string(_hierarchy.size()));
SCOPED_TIMER("Level", std::to_string(_hierarchy.size()));

const BlockID p_graph_k = p_graph.k();
const auto p_graph_partition = p_graph.take_raw_partition();

auto coarsened = pop_hierarchy(std::move(p_graph));
const NodeID next_n = current().n();

START_HEAP_PROFILER("Allocation");
START_TIMER("Allocation");
RECORD("partition") StaticArray<BlockID> partition(next_n);
STOP_TIMER();
STOP_HEAP_PROFILER();

START_TIMER("Project partition");
coarsened->project(p_graph_partition, partition);
STOP_TIMER();

SCOPED_HEAP_PROFILER("Create graph");
SCOPED_TIMER("Create graph");
return {current(), p_graph_k, std::move(partition)};
}

std::unique_ptr<CoarseGraph> SparsifingClusteringCoarsener::pop_hierarchy(PartitionedGraph &&p_graph
) {
KASSERT(!empty(), "cannot pop from an empty graph hierarchy", assert::light);

auto coarsened = std::move(_hierarchy.back());
_hierarchy.pop_back();

KASSERT(
&coarsened->get() == &p_graph.graph(),
"p_graph wraps a different graph (ptr="
<< &p_graph.graph() << ") than the one that was coarsened (ptr=" << &coarsened->get()
<< ")",
assert::light
);

return coarsened;
}

bool SparsifingClusteringCoarsener::keep_allocated_memory() const {
return level() >= _c_ctx.clustering.max_mem_free_coarsening_level;
}
} // namespace kaminpar::shm
61 changes: 61 additions & 0 deletions kaminpar-shm/coarsening/sparsifing_cluster_coarsener.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*******************************************************************************
* Coarsener that is optimized to contract clusterings.
*
* @file: cluster_coarsener.h
* @author: Daniel Seemaier
* @date: 29.09.2021
******************************************************************************/
#pragma once

#include "sparsification/Sampler.h"

#include "kaminpar-shm/coarsening/clusterer.h"
#include "kaminpar-shm/coarsening/coarsener.h"
#include "kaminpar-shm/coarsening/contraction/cluster_contraction.h"
#include "kaminpar-shm/datastructures/graph.h"
#include "kaminpar-shm/datastructures/partitioned_graph.h"
#include "kaminpar-shm/kaminpar.h"

namespace kaminpar::shm {
class SparsifingClusteringCoarsener : public Coarsener {
public:
SparsifingClusteringCoarsener(const Context &ctx, const PartitionContext &p_ctx);

SparsifingClusteringCoarsener(const SparsifingClusteringCoarsener &) = delete;
SparsifingClusteringCoarsener &operator=(const SparsifingClusteringCoarsener) = delete;

SparsifingClusteringCoarsener(SparsifingClusteringCoarsener &&) = delete;
SparsifingClusteringCoarsener &operator=(SparsifingClusteringCoarsener &&) = delete;

void initialize(const Graph *graph) final;

CSRGraph sparsify(const CSRGraph *csr, StaticArray<EdgeWeight> sample);

bool coarsen() final;
PartitionedGraph uncoarsen(PartitionedGraph &&p_graph) final;

[[nodiscard]] const Graph &current() const final {
return _hierarchy.empty() ? *_input_graph : _hierarchy.back()->get();
}

[[nodiscard]] std::size_t level() const final {
return _hierarchy.size();
}

private:
std::unique_ptr<CoarseGraph> pop_hierarchy(PartitionedGraph &&p_graph);

[[nodiscard]] bool keep_allocated_memory() const;

const CoarseningContext &_c_ctx;
const PartitionContext &_p_ctx;

const Graph *_input_graph;
std::vector<std::unique_ptr<CoarseGraph>> _hierarchy;

std::unique_ptr<Clusterer> _clustering_algorithm;
std::unique_ptr<sparsification::Sampler> _sampling_algorithm;

contraction::MemoryContext _contraction_m_ctx{};
};
} // namespace kaminpar::shm
1 change: 1 addition & 0 deletions kaminpar-shm/context_io.cc
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ std::unordered_map<std::string, CoarseningAlgorithm> get_coarsening_algorithms()
return {
{"noop", CoarseningAlgorithm::NOOP},
{"clustering", CoarseningAlgorithm::CLUSTERING},
{"sparsifying-clustering", CoarseningAlgorithm::SPARSIFYING_COARSENER}
};
}

Expand Down
5 changes: 5 additions & 0 deletions kaminpar-shm/factories.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
#include "kaminpar-shm/coarsening/noop_coarsener.h"

// Refinement
#include "coarsening/sparsifing_cluster_coarsener.h"

#include "kaminpar-shm/refinement/adapters/mtkahypar_refiner.h"
#include "kaminpar-shm/refinement/balancer/greedy_balancer.h"
#include "kaminpar-shm/refinement/fm/fm_refiner.h"
Expand Down Expand Up @@ -77,6 +79,9 @@ std::unique_ptr<Coarsener> create_coarsener(const Context &ctx, const PartitionC

case CoarseningAlgorithm::CLUSTERING:
return std::make_unique<ClusteringCoarsener>(ctx, p_ctx);

case CoarseningAlgorithm::SPARSIFYING_COARSENER:
return std::make_unique<SparsifingClusteringCoarsener>(ctx, p_ctx);
}

__builtin_unreachable();
Expand Down
Loading

0 comments on commit 88982c8

Please sign in to comment.