Skip to content

Commit

Permalink
feat: arbitrary block weights (#36)
Browse files Browse the repository at this point in the history
* wip: refactor for arbitrary block weights

* wip: refactor for arbitrary block weights

* wip: compiles again

* fix: bad epsilon init

* dbg: add output

* fix: missing _p_ctx update after extending partitions

* fix: block ID mixups etc

* fix: fix even more block ID mixups etc

* fix: use same max cluster weight as before

* fix: add adaptive epsilon

* dbg: add more debug output

* fix: adaptive epsilon

* fix: async ip

* refactor: cleanup adaptive epsilon

* fix: sync ip strategy

* dbg: disable debug output

* refactor: remove obsolete context.*

* fix: bad perfectly balanced weight

* wip

* wip: more quickfixes

* fix: more fixes

* wip

* refactor: clean up extend_partition signatures

* fix: part of running time regression

* fix: more running time regression

* fix: unit tests and distributed integration

* feat: add CLI option to specify arbitrary block weights

* feat: show block weights of the output partition

* refactor: improve output

* feat: allow both absolute (-B) and relative (-b) block weight specification

* doc: update code example in README for arbitrary block weights

* disable adaptive epsilon for arbitrary block weights for now

* fix: endless loop in initial ggg bipartitioner

The initial greedy graph growing bipartitioner could get stuck in an
endless loop if the perfectly balanced block weight of one block is set
to 0. To fix this, change < to <=.

* feat: speedup max block weight computation when using eps

* misc

* fix: revert broken ggg fix, check whether there are any more nodes to
determine termination instead

* fix: bad max cluster weight for graphs with isolated nodes

* update output

* test

* disable output

* fix max block weight due to rounding errors when k is kinda large

* eliminate more differences in rounding vs main branch

* ensure right max block weights on toplevel

* fix the fix: toplevel check was wrong

* remove extra nl

* fix max cluster weight in ip ml cycle

* disable debug output

* fix: compilation error

* fix: do not use thread-local block weight buffers for larger k values (>= 65536 for now), since this might cause rare slowdowns on some machines
  • Loading branch information
DanielSeemaier authored Dec 1, 2024
1 parent d983386 commit 0434537
Show file tree
Hide file tree
Showing 82 changed files with 1,231 additions and 961 deletions.
4 changes: 3 additions & 1 deletion README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ KaMinPar shm(int num_threads, shm::create_default_context());
// KaMinPar::reseed(int seed);
shm.borrow_and_mutate_graph(NodeID n, EdgeID *xadj, NodeID *adjncy, NodeWeight *vwgt = nullptr, EdgeWeight *adjwgt = nullptr);
// alternatively: shm.copy_graph(n, xadj, adjncy, vwgt, adjwgt); will work on a copy of the graph
shm.compute_partition(BlockID number_of_blocks, BlockID *out_partition);
shm.compute_partition(BlockID number_of_blocks, double epsilon, BlockID *out_partition);
// alternatively: shm.compute_partition(std::vector<BlockWeight> max_block_weights, BlockID *out_partition);
// Note: you must ensure that the total max block weight is larger than the total node weight of the graph

// Call the distributed partitioner:
dKaMinPar dist(MPI_Comm comm, int num_threads, dist::create_default_context());
Expand Down
66 changes: 61 additions & 5 deletions apps/KaMinPar.cc
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ struct ApplicationContext {
float heap_profiler_min_struct_size = 10;

BlockID k = 0;
double epsilon = 0.03;
std::vector<BlockWeight> max_block_weights = {};
std::vector<double> max_block_weight_factors = {};

bool quiet = false;
bool experiment = false;
Expand Down Expand Up @@ -103,15 +106,45 @@ The output should be stored in a file and can be used by the -C,--config option.

// Mandatory -> ... or partition a graph
auto *gp_group = mandatory->add_option_group("Partitioning")->silent();
gp_group->add_option("-k,--k", app.k, "Number of blocks in the partition.")
->configurable(false)
->check(CLI::Range(static_cast<BlockID>(2), std::numeric_limits<BlockID>::max()))
->required();
gp_group->add_option("-G,--graph", app.graph_filename, "Input graph in METIS format.")
->check(CLI::ExistingFile)
->configurable(false);

auto *partition_group = gp_group->add_option_group("Partition settings")->require_option(1);
partition_group
->add_option(
"-k,--k",
app.k,
"Number of blocks in the partition. This option will be ignored if explicit block "
"weights are specified via --block-weights or --block-weight-factors."
)
->check(CLI::Range(static_cast<BlockID>(2), std::numeric_limits<BlockID>::max()));
partition_group
->add_option(
"-B,--block-weights",
app.max_block_weights,
"Absolute max block weights, one weight for each block of the partition. If this "
"option is set, --epsilon will be ignored."
)
->check(CLI::NonNegativeNumber)
->capture_default_str();
partition_group->add_option(
"-b,--block-weight-factors",
app.max_block_weight_factors,
"Max block weights relative to the total node weight of the input graph, one factor for each "
"block of the partition. If this option is set, --epsilon will be ignored."
);

// Application options
cli.add_option(
"-e,--epsilon",
app.epsilon,
"Maximum allowed imbalance, e.g. 0.03 for 3%. Must be greater than 0%. If maximum block "
"weights are specified explicitly via the --block-weights, this option will be ignored."
)
->check(CLI::NonNegativeNumber)
->capture_default_str();

cli.add_option("-s,--seed", app.seed, "Seed for random number generation.")
->default_val(app.seed);
cli.add_flag("-q,--quiet", app.quiet, "Suppress all console output.");
Expand Down Expand Up @@ -414,7 +447,30 @@ int main(int argc, char *argv[]) {

// Compute partition
partitioner.set_graph(std::move(graph));
partitioner.compute_partition(app.k, partition.data());
if (!app.max_block_weight_factors.empty()) {
const NodeWeight total_node_weight = partitioner.graph()->total_node_weight();
app.max_block_weights.reserve(app.max_block_weight_factors.size());
for (const double &factor : app.max_block_weight_factors) {
app.max_block_weights.push_back(std::ceil(factor * total_node_weight));
}
}

if (!app.max_block_weights.empty()) {
const BlockWeight total_block_weight = std::accumulate(
app.max_block_weights.begin(), app.max_block_weights.end(), static_cast<BlockWeight>(0)
);
const NodeWeight total_node_weight = partitioner.graph()->total_node_weight();
if (total_node_weight > total_block_weight) {
LOG_ERROR << "Total max block weights (" << total_block_weight
<< ") is smaller than the total node weight (" << total_node_weight
<< ") of the graph. This does not work. Please increase your max block weights.";
std::exit(1);
}

partitioner.compute_partition(std::move(app.max_block_weights), partition.data());
} else {
partitioner.compute_partition(app.k, app.epsilon, partition.data());
}

// Save graph partition
if (!app.partition_filename.empty()) {
Expand Down
2 changes: 1 addition & 1 deletion apps/benchmarks/shm_label_propagation_benchmark.cc
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ int main(int argc, char *argv[]) {
STOP_TIMER();

if (graph.sorted()) {
graph::integrate_isolated_nodes(graph, original_epsilon, ctx);
graph.integrate_isolated_nodes();
}

cio::print_delimiter("Input Summary", '#');
Expand Down
2 changes: 1 addition & 1 deletion apps/tools/shm_graph_rearrangement_tool.cc
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ int main(int argc, char *argv[]) {
LOG << "Rearranging graph...";
if (ctx.node_ordering == NodeOrdering::DEGREE_BUCKETS) {
graph = graph::rearrange_by_degree_buckets(graph.csr_graph());
graph::integrate_isolated_nodes(graph, ctx.partition.epsilon, ctx);
graph.integrate_isolated_nodes();
}

if (ctx.edge_ordering == EdgeOrdering::COMPRESSION) {
Expand Down
16 changes: 6 additions & 10 deletions kaminpar-cli/kaminpar_arguments.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,6 @@ CLI::Option_group *create_graph_compression_options(CLI::App *app, Context &ctx)
CLI::Option_group *create_partitioning_options(CLI::App *app, Context &ctx) {
auto *partitioning = app->add_option_group("Partitioning");

partitioning
->add_option(
"-e,--epsilon",
ctx.partition.epsilon,
"Maximum allowed imbalance, e.g. 0.03 for 3%. Must be strictly "
"positive."
)
->check(CLI::NonNegativeNumber)
->capture_default_str();

// Partitioning options
partitioning->add_option("-m,--p-mode", ctx.partitioning.mode)
->transform(CLI::CheckedTransformer(get_partitioning_modes()).description(""))
Expand Down Expand Up @@ -341,6 +331,12 @@ CLI::Option_group *create_initial_partitioning_options(CLI::App *app, Context &c
"--i-r-disable", ctx.initial_partitioning.refinement.disabled, "Disable initial refinement."
)
->capture_default_str();
ip->add_flag(
"--i-adaptive-epsilon",
ctx.initial_partitioning.use_adaptive_epsilon,
"Use adaptive epsilon."
)
->capture_default_str();

return ip;
}
Expand Down
2 changes: 1 addition & 1 deletion kaminpar-dist/dkaminpar.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
#include "kaminpar-dist/metrics.h"
#include "kaminpar-dist/timer.h"

#include "kaminpar-shm/context.h"
#include "kaminpar-shm/kaminpar.h"

#include "kaminpar-common/console_io.h"
#include "kaminpar-common/environment.h"
Expand Down
4 changes: 4 additions & 0 deletions kaminpar-dist/dkaminpar.h
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,10 @@ struct PartitionContext {

~PartitionContext();

[[nodiscard]] double inferred_epsilon() const {
return epsilon;
}

BlockID k = kInvalidBlockID;
BlockID K = kInvalidBlockID;
double epsilon;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "kaminpar-common/timer.h"

namespace kaminpar::dist {

shm::PartitionedGraph KaMinParInitialPartitioner::initial_partition(
const shm::Graph &graph, const PartitionContext &p_ctx
) {
Expand All @@ -25,9 +26,8 @@ shm::PartitionedGraph KaMinParInitialPartitioner::initial_partition(

auto shm_ctx = _ctx.initial_partitioning.kaminpar;
shm_ctx.refinement.lp.num_iterations = 1;
shm_ctx.partition.k = p_ctx.k;
shm_ctx.partition.epsilon = p_ctx.epsilon;
shm_ctx.setup(graph);
shm_ctx.partition.setup(graph, p_ctx.k, p_ctx.epsilon);
shm_ctx.compression.setup(graph);

DISABLE_TIMERS();
START_HEAP_PROFILER("KaMinPar");
Expand All @@ -40,4 +40,5 @@ shm::PartitionedGraph KaMinParInitialPartitioner::initial_partition(

return p_graph;
}

} // namespace kaminpar::dist
2 changes: 2 additions & 0 deletions kaminpar-shm/coarsening/cluster_coarsener.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
namespace kaminpar::shm {

namespace {

SET_DEBUG(false);

}

ClusteringCoarsener::ClusteringCoarsener(const Context &ctx, const PartitionContext &p_ctx)
Expand Down
14 changes: 10 additions & 4 deletions kaminpar-shm/coarsening/max_cluster_weights.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "kaminpar-shm/kaminpar.h"

namespace kaminpar::shm {

template <typename NodeWeight, typename PartitionContext>
NodeWeight compute_max_cluster_weight(
const CoarseningContext &c_ctx,
Expand All @@ -24,12 +25,12 @@ NodeWeight compute_max_cluster_weight(

switch (c_ctx.clustering.cluster_weight_limit) {
case ClusterWeightLimit::EPSILON_BLOCK_WEIGHT:
max_cluster_weight = (p_ctx.epsilon * total_node_weight) /
max_cluster_weight = (p_ctx.infer_epsilon(total_node_weight) * total_node_weight) /
std::clamp<BlockID>(n / c_ctx.contraction_limit, 2, p_ctx.k);
break;

case ClusterWeightLimit::BLOCK_WEIGHT:
max_cluster_weight = (1.0 + p_ctx.epsilon) * total_node_weight / p_ctx.k;
max_cluster_weight = (1.0 + p_ctx.epsilon()) * total_node_weight / p_ctx.k;
break;

case ClusterWeightLimit::ONE:
Expand All @@ -55,12 +56,16 @@ NodeWeight compute_max_cluster_weight(

switch (c_ctx.cluster_weight_limit) {
case ClusterWeightLimit::EPSILON_BLOCK_WEIGHT:
max_cluster_weight = (p_ctx.epsilon * total_node_weight) /
max_cluster_weight = (p_ctx.inferred_epsilon() * total_node_weight) /
std::clamp<BlockID>(n / c_ctx.contraction_limit, 2, p_ctx.k);
break;

case ClusterWeightLimit::BLOCK_WEIGHT:
max_cluster_weight = (1.0 + p_ctx.epsilon) * total_node_weight / p_ctx.k;
if constexpr (requires { p_ctx.epsilon(); }) {
max_cluster_weight = (1.0 + p_ctx.epsilon()) * total_node_weight / p_ctx.k;
} else {
max_cluster_weight = (1.0 + p_ctx.epsilon) * total_node_weight / p_ctx.k;
}
break;

case ClusterWeightLimit::ONE:
Expand All @@ -74,4 +79,5 @@ NodeWeight compute_max_cluster_weight(

return static_cast<NodeWeight>(max_cluster_weight * c_ctx.cluster_weight_multiplier);
}

} // namespace kaminpar::shm
Loading

0 comments on commit 0434537

Please sign in to comment.