diff --git a/kaminpar-cli/dkaminpar_arguments.cc b/kaminpar-cli/dkaminpar_arguments.cc index 810f4125..9ece7d6c 100644 --- a/kaminpar-cli/dkaminpar_arguments.cc +++ b/kaminpar-cli/dkaminpar_arguments.cc @@ -142,7 +142,6 @@ CLI::Option_group *create_refinement_options(CLI::App *app, Context &ctx) { ) ->capture_default_str(); - create_fm_refinement_options(app, ctx); create_lp_refinement_options(app, ctx); create_colored_lp_refinement_options(app, ctx); create_jet_refinement_options(app, ctx); @@ -153,62 +152,6 @@ CLI::Option_group *create_refinement_options(CLI::App *app, Context &ctx) { return refinement; } -CLI::Option_group *create_fm_refinement_options(CLI::App *app, Context &ctx) { - auto *fm = app->add_option_group("Refinement -> FM"); - - fm->add_option( - "--r-fm-alpha", ctx.refinement.fm.alpha, "Alpha parameter for the adaptive stopping rule." - ) - ->capture_default_str(); - - fm->add_flag("--r-fm-independent-seeds", ctx.refinement.fm.use_independent_seeds) - ->capture_default_str(); - fm->add_flag("--r-fm-bfs-seeds-as-fm-seeds", ctx.refinement.fm.use_bfs_seeds_as_fm_seeds) - ->capture_default_str(); - - fm->add_option("--r-fm-max-hops", ctx.refinement.fm.max_hops)->capture_default_str(); - fm->add_option("--r-fm-max-radius", ctx.refinement.fm.max_radius)->capture_default_str(); - fm->add_option("--r-fm-num-global-iterations", ctx.refinement.fm.num_global_iterations) - ->capture_default_str(); - fm->add_option("--r-fm-num-local-iterations", ctx.refinement.fm.num_local_iterations) - ->capture_default_str(); - fm->add_flag( - "--r-fm-revert-local-moves-after-batch", ctx.refinement.fm.revert_local_moves_after_batch - ) - ->capture_default_str(); - fm->add_flag( - "--r-fm-rebalance-after-each-global-iteration", - ctx.refinement.fm.rebalance_after_each_global_iteration - ) - ->capture_default_str(); - fm->add_flag("--r-fm-rebalance-after-refinement", ctx.refinement.fm.rebalance_after_refinement) - ->capture_default_str(); - fm->add_option("--r-fm-balancing-algorithm", ctx.refinement.fm.balancing_algorithm) - ->transform(CLI::CheckedTransformer(get_balancing_algorithms()).description("")) - ->description( - std::string("Balancing algorithm(s). Possible options are:\n") + - get_balancing_algorithms_description() - ) - ->capture_default_str(); - fm->add_flag("--r-fm-rollback", ctx.refinement.fm.rollback_deterioration)->capture_default_str(); - - fm->add_flag("--r-fm-use-abortion-threshold", ctx.refinement.fm.use_abortion_threshold) - ->capture_default_str(); - fm->add_option("--r-fm-abortion-threshold", ctx.refinement.fm.abortion_threshold) - ->capture_default_str(); - - fm->add_flag( - "--r-fm-chunk-local-rounds", - ctx.refinement.fm.chunk_local_rounds, - "If enabled, divide local rounds into chunks and synchronize the partition state after " - "each chunk." - ) - ->capture_default_str(); - create_chunks_options(fm, "--r-fm", ctx.refinement.fm.chunks); - - return fm; -} - CLI::Option_group *create_lp_refinement_options(CLI::App *app, Context &ctx) { auto *lp = app->add_option_group("Refinement -> Chunked Label Propagation"); diff --git a/kaminpar-cli/dkaminpar_arguments.h b/kaminpar-cli/dkaminpar_arguments.h index e8db98fe..e43f19f8 100644 --- a/kaminpar-cli/dkaminpar_arguments.h +++ b/kaminpar-cli/dkaminpar_arguments.h @@ -24,8 +24,6 @@ CLI::Option_group *create_initial_partitioning_options(CLI::App *app, Context &c CLI::Option_group *create_refinement_options(CLI::App *app, Context &ctx); -CLI::Option_group *create_fm_refinement_options(CLI::App *app, Context &ctx); - CLI::Option_group *create_lp_refinement_options(CLI::App *app, Context &ctx); CLI::Option_group *create_colored_lp_refinement_options(CLI::App *app, Context &ctx); diff --git a/kaminpar-dist/context_io.cc b/kaminpar-dist/context_io.cc index 613624b7..2344da45 100644 --- a/kaminpar-dist/context_io.cc +++ b/kaminpar-dist/context_io.cc @@ -121,11 +121,9 @@ std::unordered_map get_kway_refinement_algorit {"noop", RefinementAlgorithm::NOOP}, {"lp/batches", RefinementAlgorithm::BATCHED_LP}, {"lp/colors", RefinementAlgorithm::COLORED_LP}, - {"fm/global", RefinementAlgorithm::GLOBAL_FM}, {"greedy-balancer/nodes", RefinementAlgorithm::GREEDY_NODE_BALANCER}, {"greedy-balancer/clusters", RefinementAlgorithm::GREEDY_CLUSTER_BALANCER}, - {"jet/refiner", RefinementAlgorithm::JET_REFINER}, - {"jet/balancer", RefinementAlgorithm::JET_BALANCER}, + {"jet", RefinementAlgorithm::JET_REFINER}, {"mtkahypar", RefinementAlgorithm::MTKAHYPAR}, }; } @@ -135,7 +133,6 @@ std::unordered_map get_balancing_algorithms() {"noop", RefinementAlgorithm::NOOP}, {"greedy-balancer/nodes", RefinementAlgorithm::GREEDY_NODE_BALANCER}, {"greedy-balancer/clusters", RefinementAlgorithm::GREEDY_CLUSTER_BALANCER}, - {"jet/balancer", RefinementAlgorithm::JET_BALANCER}, {"mtkahypar", RefinementAlgorithm::MTKAHYPAR}, }; }; @@ -148,16 +145,12 @@ std::ostream &operator<<(std::ostream &out, const RefinementAlgorithm algorithm) return out << "lp/batches"; case RefinementAlgorithm::COLORED_LP: return out << "lp/colors"; - case RefinementAlgorithm::GLOBAL_FM: - return out << "fm/global"; case RefinementAlgorithm::GREEDY_NODE_BALANCER: return out << "greedy-balancer/nodes"; case RefinementAlgorithm::GREEDY_CLUSTER_BALANCER: return out << "greedy-balancer/clusters"; case RefinementAlgorithm::JET_REFINER: return out << "jet/refiner"; - case RefinementAlgorithm::JET_BALANCER: - return out << "jet/balancer"; case RefinementAlgorithm::MTKAHYPAR: return out << "mtkahypar"; } @@ -170,8 +163,6 @@ std::string get_refinement_algorithms_description() { - noop: do nothing - lp/batches: LP where batches are nodes with subsequent IDs - lp/colors: LP where batches are color classes -- fm/local: local FM -- fm/global: global FM - jet/refiner: reimplementation of JET's refinement algorithm)") .substr(1) + "\n" + get_balancing_algorithms_description(); @@ -179,9 +170,8 @@ std::string get_refinement_algorithms_description() { std::string get_balancing_algorithms_description() { return std::string(R"( -- jet/balancer: reimplementation of JET's balancing algorithm -- greedy-balancer/singletons: greedy, move individual nodes -- greedy-balancer/movesets: greedy, move sets of nodes)") +- greedy-balancer/nodes: greedy, move individual nodes +- greedy-balancer/clusters: greedy, move sets of nodes)") .substr(1); } @@ -316,7 +306,7 @@ void print(const PartitionContext &ctx, const bool root, std::ostream &out, MPI_ }); const auto width = std::ceil(std::log10(size)) + 1; - const GlobalNodeID num_global_total_nodes = + const auto num_global_total_nodes = mpi::allreduce(ctx.graph->total_n, MPI_SUM, comm); if (root) { @@ -478,30 +468,9 @@ void print(const RefinementContext &ctx, const ParallelContext ¶llel, std::o << ", final " << ctx.jet.final_negative_gain_factor << "\n"; out << " Balancing algorithm: " << ctx.jet.balancing_algorithm << "\n"; } - if (ctx.includes_algorithm(RefinementAlgorithm::GLOBAL_FM)) { - out << "Global FM refinement: " << RefinementAlgorithm::GLOBAL_FM << "\n"; - out << " Number of iterations: " << ctx.fm.num_global_iterations << " x " - << ctx.fm.num_local_iterations << "\n"; - if (ctx.fm.chunk_local_rounds) { - print(ctx.fm.chunks, parallel, out); - } - out << " Search radius: " << ctx.fm.max_radius << " via " << ctx.fm.max_hops - << " hop(s)\n"; - out << " Revert batch-local moves: " - << (ctx.fm.revert_local_moves_after_batch ? "yes" : "no") << "\n"; - out << " Rollback to best partition: " << (ctx.fm.rollback_deterioration ? "yes" : "no") - << "\n"; - out << " Rebalance algorithm: " << ctx.fm.balancing_algorithm << "\n"; - out << " Rebalance after iter.: " - << (ctx.fm.rebalance_after_each_global_iteration ? "yes" : "no") << "\n"; - out << " Rebalance after ref.: " << (ctx.fm.rebalance_after_refinement ? "yes" : "no") - << "\n"; - } if (ctx.includes_algorithm(RefinementAlgorithm::GREEDY_NODE_BALANCER) || (ctx.includes_algorithm(RefinementAlgorithm::JET_REFINER) && - ctx.jet.balancing_algorithm == RefinementAlgorithm::GREEDY_NODE_BALANCER) || - (ctx.includes_algorithm(RefinementAlgorithm::GLOBAL_FM) && - ctx.fm.balancing_algorithm == RefinementAlgorithm::GREEDY_NODE_BALANCER)) { + ctx.jet.balancing_algorithm == RefinementAlgorithm::GREEDY_NODE_BALANCER)) { out << "Node balancer: " << RefinementAlgorithm::GREEDY_NODE_BALANCER << "\n"; out << " Number of rounds: " << ctx.node_balancer.max_num_rounds << "\n"; out << " Sequential balancing: " @@ -526,9 +495,7 @@ void print(const RefinementContext &ctx, const ParallelContext ¶llel, std::o } if (ctx.includes_algorithm(RefinementAlgorithm::GREEDY_CLUSTER_BALANCER) || (ctx.includes_algorithm(RefinementAlgorithm::JET_REFINER) && - ctx.jet.balancing_algorithm == RefinementAlgorithm::GREEDY_CLUSTER_BALANCER) || - (ctx.includes_algorithm(RefinementAlgorithm::GLOBAL_FM) && - ctx.fm.balancing_algorithm == RefinementAlgorithm::GREEDY_CLUSTER_BALANCER)) { + ctx.jet.balancing_algorithm == RefinementAlgorithm::GREEDY_CLUSTER_BALANCER)) { out << "Cluster balancer: " << RefinementAlgorithm::GREEDY_CLUSTER_BALANCER << "\n"; out << " Clusters: " << ctx.cluster_balancer.cluster_strategy << "\n"; out << " Max weight: " << ctx.cluster_balancer.cluster_size_strategy << " x " @@ -557,14 +524,5 @@ void print(const RefinementContext &ctx, const ParallelContext ¶llel, std::o << 100.0 * ctx.cluster_balancer.par_initial_rebalance_fraction << "%, increase by " << 100.0 * ctx.cluster_balancer.par_rebalance_fraction_increase << "% each round\n"; } - if (ctx.includes_algorithm(RefinementAlgorithm::JET_BALANCER) || - (ctx.includes_algorithm(RefinementAlgorithm::JET_REFINER) && - ctx.jet.balancing_algorithm == RefinementAlgorithm::JET_BALANCER) || - (ctx.includes_algorithm(RefinementAlgorithm::GLOBAL_FM) && - ctx.fm.balancing_algorithm == RefinementAlgorithm::JET_BALANCER)) { - out << "Jet balancer: " << RefinementAlgorithm::JET_BALANCER << "\n"; - out << " Number of iterations: " << ctx.jet_balancer.num_weak_iterations << " weak + " - << ctx.jet_balancer.num_strong_iterations << " strong\n"; - } } } // namespace kaminpar::dist diff --git a/kaminpar-dist/dkaminpar.h b/kaminpar-dist/dkaminpar.h index 76407ddd..f75214a5 100644 --- a/kaminpar-dist/dkaminpar.h +++ b/kaminpar-dist/dkaminpar.h @@ -82,9 +82,7 @@ enum class RefinementAlgorithm { NOOP, BATCHED_LP, COLORED_LP, - GLOBAL_FM, JET_REFINER, - JET_BALANCER, GREEDY_NODE_BALANCER, GREEDY_CLUSTER_BALANCER, MTKAHYPAR, @@ -188,33 +186,6 @@ struct LabelPropagationRefinementContext { bool ignore_probabilities; }; -struct FMRefinementContext { - double alpha; - - bool use_independent_seeds; - bool use_bfs_seeds_as_fm_seeds; - - bool chunk_local_rounds; - ChunksContext chunks; - - int max_hops; - int max_radius; - - int num_global_iterations; - int num_local_iterations; - - bool revert_local_moves_after_batch; - - bool rebalance_after_each_global_iteration; - bool rebalance_after_refinement; - RefinementAlgorithm balancing_algorithm; - - bool rollback_deterioration; - - bool use_abortion_threshold; - double abortion_threshold; -}; - struct MtKaHyParRefinementContext { std::string config_filename; std::string fine_config_filename; @@ -300,11 +271,6 @@ struct ClusterBalancerContext { bool switch_to_singleton_after_stallmate; }; -struct JetBalancerContext { - int num_weak_iterations; - int num_strong_iterations; -}; - struct JetRefinementContext { int num_coarse_rounds; int num_fine_rounds; @@ -330,12 +296,10 @@ struct RefinementContext { LabelPropagationRefinementContext lp; ColoredLabelPropagationRefinementContext colored_lp; - FMRefinementContext fm; NodeBalancerContext node_balancer; ClusterBalancerContext cluster_balancer; JetRefinementContext jet; - JetBalancerContext jet_balancer; MtKaHyParRefinementContext mtkahypar; diff --git a/kaminpar-dist/factories.cc b/kaminpar-dist/factories.cc index 9f2d99d5..5a24166c 100644 --- a/kaminpar-dist/factories.cc +++ b/kaminpar-dist/factories.cc @@ -27,8 +27,6 @@ #include "kaminpar-dist/refinement/adapters/mtkahypar_refiner.h" #include "kaminpar-dist/refinement/balancer/cluster_balancer.h" #include "kaminpar-dist/refinement/balancer/node_balancer.h" -#include "kaminpar-dist/refinement/fm/fm_refiner.h" -#include "kaminpar-dist/refinement/jet/jet_balancer.h" #include "kaminpar-dist/refinement/jet/jet_refiner.h" #include "kaminpar-dist/refinement/lp/clp_refiner.h" #include "kaminpar-dist/refinement/lp/lp_refiner.h" @@ -92,15 +90,9 @@ create_refiner(const Context &ctx, const RefinementAlgorithm algorithm) { case RefinementAlgorithm::COLORED_LP: return std::make_unique(ctx); - case RefinementAlgorithm::GLOBAL_FM: - return std::make_unique(ctx); - case RefinementAlgorithm::JET_REFINER: return std::make_unique(ctx); - case RefinementAlgorithm::JET_BALANCER: - return std::make_unique(ctx); - case RefinementAlgorithm::GREEDY_NODE_BALANCER: return std::make_unique(ctx); diff --git a/kaminpar-dist/presets.cc b/kaminpar-dist/presets.cc index 2a377a58..f758c039 100644 --- a/kaminpar-dist/presets.cc +++ b/kaminpar-dist/presets.cc @@ -17,16 +17,14 @@ namespace kaminpar::dist { Context create_context_by_preset_name(const std::string &name) { if (name == "default" || name == "fast") { return create_default_context(); - } else if (name == "strong") { + } else if (name == "strong" || name == "4xjet") { return create_strong_context(); + } else if (name == "jet") { + return create_jet_context(); } else if (name == "europar23-fast") { return create_europar23_fast_context(); } else if (name == "europar23-strong") { return create_europar23_strong_context(); - } else if (name == "jet") { - return create_jet_context(); - } else if (name == "fm") { - return create_fm_context(); } throw std::runtime_error("invalid preset name"); @@ -38,8 +36,6 @@ std::unordered_set get_preset_names() { "strong", "europar23-fast", "europar23-strong", - "jet", - "fm", }; } @@ -162,38 +158,6 @@ Context create_default_context() { .use_active_set = false, .move_execution_strategy = LabelPropagationMoveExecutionStrategy::BEST_MOVES, }, - .fm = - { - .alpha = 1.0, - - .use_independent_seeds = false, - .use_bfs_seeds_as_fm_seeds = true, - - .chunk_local_rounds = false, - .chunks = - { - .total_num_chunks = 128, - .fixed_num_chunks = 0, - .min_num_chunks = 8, - .scale_chunks_with_threads = false, - }, - - .max_hops = 1, - .max_radius = 1, - - .num_global_iterations = 10, - .num_local_iterations = 1, - - .revert_local_moves_after_batch = true, - .rebalance_after_each_global_iteration = true, - .rebalance_after_refinement = false, - .balancing_algorithm = RefinementAlgorithm::GREEDY_NODE_BALANCER, - - .rollback_deterioration = true, - - .use_abortion_threshold = true, - .abortion_threshold = 0.999, - }, .node_balancer = { .max_num_rounds = std::numeric_limits::max(), @@ -246,11 +210,6 @@ Context create_default_context() { .final_negative_gain_factor = 0.25, .balancing_algorithm = RefinementAlgorithm::GREEDY_NODE_BALANCER, }, - .jet_balancer = - { - .num_weak_iterations = 2, - .num_strong_iterations = 1, - }, .mtkahypar = { .config_filename = "", @@ -267,12 +226,21 @@ Context create_default_context() { Context create_strong_context() { Context ctx = create_default_context(); - ctx.initial_partitioning.kaminpar = shm::create_strong_context(); - ctx.coarsening.global_lp.num_iterations = 5; + ctx.refinement.jet.dynamic_negative_gain_factor = true; + ctx.refinement.jet.num_coarse_rounds = 4; + ctx.refinement.jet.num_fine_rounds = 4; ctx.refinement.algorithms = { - RefinementAlgorithm::GREEDY_NODE_BALANCER, - RefinementAlgorithm::BATCHED_LP, - RefinementAlgorithm::JET_REFINER}; + RefinementAlgorithm::GREEDY_NODE_BALANCER, RefinementAlgorithm::JET_REFINER}; + return ctx; +} + +Context create_jet_context() { + Context ctx = create_default_context(); + ctx.refinement.jet.dynamic_negative_gain_factor = false; + ctx.refinement.jet.num_coarse_rounds = 1; + ctx.refinement.jet.num_fine_rounds = 1; + ctx.refinement.algorithms = { + RefinementAlgorithm::GREEDY_NODE_BALANCER, RefinementAlgorithm::JET_REFINER}; return ctx; } @@ -288,21 +256,4 @@ Context create_europar23_strong_context() { ctx.coarsening.global_lp.num_iterations = 5; return ctx; } - -Context create_jet_context() { - Context ctx = create_default_context(); - ctx.refinement.algorithms = { - RefinementAlgorithm::GREEDY_NODE_BALANCER, RefinementAlgorithm::JET_REFINER}; - return ctx; -} - -Context create_fm_context() { - Context ctx = create_default_context(); - ctx.refinement.algorithms = { - RefinementAlgorithm::GREEDY_NODE_BALANCER, - RefinementAlgorithm::BATCHED_LP, - RefinementAlgorithm::GLOBAL_FM, - RefinementAlgorithm::GREEDY_NODE_BALANCER}; - return ctx; -} } // namespace kaminpar::dist diff --git a/kaminpar-dist/presets.h b/kaminpar-dist/presets.h index feac4d2e..4e8134ea 100644 --- a/kaminpar-dist/presets.h +++ b/kaminpar-dist/presets.h @@ -14,5 +14,4 @@ namespace kaminpar::dist { Context create_europar23_fast_context(); Context create_europar23_strong_context(); Context create_jet_context(); -Context create_fm_context(); } // namespace kaminpar::dist diff --git a/kaminpar-dist/refinement/fm/fm_refiner.cc b/kaminpar-dist/refinement/fm/fm_refiner.cc deleted file mode 100644 index 5745501a..00000000 --- a/kaminpar-dist/refinement/fm/fm_refiner.cc +++ /dev/null @@ -1,478 +0,0 @@ -/******************************************************************************* - * Distributed FM refiner. - * - * @file: fm_refiner.cc - * @author: Daniel Seemaier - * @date: 11.09.2022 - ******************************************************************************/ -#include "kaminpar-dist/refinement/fm/fm_refiner.h" - -#include -#include -#include -#include - -#include "kaminpar-dist/algorithms/border_nodes.h" -#include "kaminpar-dist/algorithms/independent_set.h" -#include "kaminpar-dist/context.h" -#include "kaminpar-dist/datastructures/distributed_graph.h" -#include "kaminpar-dist/datastructures/distributed_partitioned_graph.h" -#include "kaminpar-dist/datastructures/growt.h" -#include "kaminpar-dist/factories.h" -#include "kaminpar-dist/graphutils/bfs_extractor.h" -#include "kaminpar-dist/metrics.h" -#include "kaminpar-dist/refinement/fm/move_conflict_resolver.h" -#include "kaminpar-dist/refinement/snapshooter.h" -#include "kaminpar-dist/timer.h" - -#include "kaminpar-shm/datastructures/graph.h" -#include "kaminpar-shm/datastructures/partitioned_graph.h" -#include "kaminpar-shm/refinement/fm/fm_refiner.h" -#include "kaminpar-shm/refinement/fm/stopping_policies.h" - -#include "kaminpar-common/datastructures/binary_heap.h" -#include "kaminpar-common/datastructures/rating_map.h" -#include "kaminpar-common/random.h" - -#define HEAVY assert::heavy - -namespace kaminpar::dist { -namespace { -SET_STATISTICS(true); -SET_DEBUG(true); -} // namespace - -namespace fm { -NodeMapper::NodeMapper(NoinitVector batch_to_graph) - : _batch_to_graph(std::move(batch_to_graph)) { - construct(); -} - -inline GlobalNodeID NodeMapper::to_graph(NodeID bnode) const { - KASSERT(bnode < _batch_to_graph.size()); - return _batch_to_graph[bnode]; -} - -inline NodeID NodeMapper::to_batch(GlobalNodeID gnode) const { - auto it = _graph_to_batch.find(gnode + 1); - return it != _graph_to_batch.end() ? (*it).second : kInvalidNodeID; -} - -void NodeMapper::construct() { - tbb::parallel_for(0, _batch_to_graph.size(), [&](const std::size_t i) { - KASSERT( - _graph_to_batch.find(_batch_to_graph[i] + 1) == _graph_to_batch.end(), - "both nodes " << i << " and " << (*_graph_to_batch.find(_batch_to_graph[i] + 1)).second - << " from the batch graph are mapped to the global node " - << _batch_to_graph[i] << " == " - << _batch_to_graph[(*_graph_to_batch.find(_batch_to_graph[i] + 1)).second] - ); - _graph_to_batch.insert(_batch_to_graph[i] + 1, i); - }); -} -} // namespace fm - -FMRefinerFactory::FMRefinerFactory(const Context &ctx) : _ctx(ctx) {} - -std::unique_ptr -FMRefinerFactory::create(DistributedPartitionedGraph &p_graph, const PartitionContext &p_ctx) { - return std::make_unique(_ctx, p_graph, p_ctx); -} - -FMRefiner::FMRefiner( - const Context &ctx, DistributedPartitionedGraph &p_graph, const PartitionContext &p_ctx -) - : _ctx(ctx), - _fm_ctx(ctx.refinement.fm), - _p_graph(p_graph), - _p_ctx(p_ctx), - _balancer_factory(factory::create_refiner(_ctx, _fm_ctx.balancing_algorithm)) {} - -void FMRefiner::initialize() {} - -bool FMRefiner::refine() { - TIMER_BARRIER(_p_graph.communicator()); - SCOPED_TIMER("FM"); - - auto balancer = _balancer_factory->create(_p_graph, _p_ctx); - - // Track the best partition that we see during FM refinement - std::unique_ptr snapshooter = - [&]() -> std::unique_ptr { - if (_fm_ctx.rollback_deterioration) { - return std::make_unique(_p_graph.total_n(), _p_ctx.k); - } else { - return std::make_unique(); - } - }(); - snapshooter->init(_p_graph, _p_ctx); - - for (std::size_t global_round = 0; global_round < _fm_ctx.num_global_iterations; ++global_round) { - const EdgeWeight initial_cut = - _ctx.refinement.fm.use_abortion_threshold ? metrics::edge_cut(_p_graph) : -1; - - START_TIMER("Find seed nodes"); - const auto seed_nodes = _fm_ctx.use_independent_seeds - ? graph::find_independent_border_set(_p_graph, global_round) - : graph::find_border_nodes(_p_graph); - STOP_TIMER(); - TIMER_BARRIER(_p_graph.communicator()); - - // Run distributed BFS to extract batches around the seed nodes - START_TIMER("Run BFS"); - graph::BfsExtractor bfs_extractor(_p_graph.graph()); - bfs_extractor.initialize(_p_graph); - bfs_extractor.set_max_hops(_fm_ctx.max_hops); - bfs_extractor.set_max_radius(_fm_ctx.max_radius); - auto extraction_result = bfs_extractor.extract(seed_nodes); - STOP_TIMER(); - TIMER_BARRIER(_p_graph.communicator()); - - // This is the batch graph containing the subgraphs around all seed nodes - // The last `k` nodes are pseudo-nodes representing the blocks of the graph partition - shm::Graph *b_graph = extraction_result.graph.get(); - shm::PartitionedGraph *bp_graph = extraction_result.p_graph.get(); - - IF_STATS { - global_round_stats(global_round, *b_graph, seed_nodes); - } - - KASSERT( - shm::debug::validate_graph(*b_graph, true, _p_graph.k()), - "BFS extractor returned invalid graph data structure", - HEAVY - ); - - // Overwrite the block weights in the batch graph with block weights of the global graph - for (const BlockID block : _p_graph.blocks()) { - bp_graph->set_block_weight(block, _p_graph.block_weight(block)); - } - - // @todo obvious optimization: avoid re-allocation - START_TIMER("Prepare thread-local workers"); - const shm::PartitionContext shm_p_ctx = setup_shm_p_ctx(*b_graph); - const fm::NodeMapper node_mapper(extraction_result.node_mapping); - const shm::KwayFMRefinementContext shm_fm_ctx = setup_fm_ctx(); - const shm::Context shm_ctx = shm::create_default_context(); - shm::fm::SharedData<> shared = setup_fm_data(shm_ctx, *bp_graph, seed_nodes, node_mapper); - - // Create thread-local workers numbered 1..P - std::atomic next_id = 0; - tbb::enumerable_thread_specific> worker_ets([&] { - // It is important that worker IDs start at 1, otherwise the node - // tracker won't work since 0 is reserved for "unmarked / untracked" - shm::LocalizedFMRefiner<> worker(++next_id, shm_p_ctx, shm_fm_ctx, *bp_graph, shared); - - // This allows access to the moves that were applied to the shared bp_graph partition - worker.enable_move_recording(); - - return worker; - }); - STOP_TIMER(); - TIMER_BARRIER(_p_graph.communicator()); - - std::vector move_sets; - - for (std::size_t local_round = 0; local_round < _fm_ctx.num_local_iterations; ++local_round) { - // Unlock previously locked nodes, etc ... - // @todo re-init border nodes if not using the independent set as FM seeds - START_TIMER("Prepared shared data"); - prepare_shared_data_for_local_round(*bp_graph, shared); - STOP_TIMER(); - TIMER_BARRIER(_p_graph.communicator()); - - // Compute the number of seeds per chunk / batch - const NodeID total_num_seeds = shared.border_nodes.size(); - const NodeID total_num_chunks = - _fm_ctx.chunk_local_rounds - ? std::min(_fm_ctx.chunks.compute(_ctx.parallel), total_num_seeds) - : 1; - const NodeID num_seeds_per_chunk = std::ceil(1.0 * total_num_seeds / total_num_chunks); - bool have_more_seeds = true; - - DBG0 << "Number of seeds on PE 0: " << total_num_seeds; - DBG0 << "Number of chunks on PE 0: " << total_num_chunks; - DBG0 << "Seeds per chunk on PE 0: " << num_seeds_per_chunk; - - // Perform local search on the current chunk / batch, synchronize and apply moves to the - // distributed graph ... - do { - move_sets.clear(); - - START_TIMER("Thread-local FM"); - for (NodeID progress = 0; shared.border_nodes.has_more() && progress < num_seeds_per_chunk; - ++progress) { - // Perform the FM search starting at the next seed - auto &worker = worker_ets.local(); - const EdgeWeight gain = worker.run_batch(); - const NodeID seed = worker.last_batch_seed_nodes().front(); - const auto &moves = worker.last_batch_moves(); - - // Zero seeds are possible if we do not unlock moved nodes after a batch - KASSERT(worker.last_batch_seed_nodes().size() <= 1, "expected exactly one seed node"); - KASSERT( - moves.empty() || !worker.last_batch_seed_nodes().empty(), - "search did not get any seeds nodes, yet performed some moves" - ); - - if (!moves.empty()) { - // Unique identifier for this set of moves == global node ID of the FM seed - // const GlobalNodeID group = node_mapper.to_graph(seed); - // - // Since the global conflict resolver uses the upper 32 bits to store the owning PE, we - // can use the local seed ID and still have groups that are unique globally - const GlobalNodeID group = seed; - - for (const auto &[node, from, improvement] : moves) { - move_sets.push_back(GlobalMove{ - .node = node_mapper.to_graph(node), - .group = group, - .weight = static_cast(bp_graph->node_weight(node)), - .gain = gain, - .from = from, - .to = bp_graph->block(node), - }); - } - - // Revert changes to the batch graph to make local FM searches independent of each other - if (_fm_ctx.revert_local_moves_after_batch) { - for (const auto &[node, from, improvement] : moves) { - const BlockID to = bp_graph->block(node); - bp_graph->set_block(node, from); - shared.gain_cache.move(*bp_graph, node, to, from); - - if (node != seed) { - shared.node_tracker.set(node, shm::fm::NodeTracker::UNLOCKED); - } - } - } - } - } - STOP_TIMER(); - TIMER_BARRIER(_p_graph.communicator()); - - // Resolve global move conflicts: after this operation, global_move_buffer accepted moves - // with valid node IDs (is_valid_id(node)) and rejected moves with invalid node IDs - // (is_invalid_id(node)) - START_TIMER("Resolve move conflicts"); - auto global_move_buffer = - broadcast_and_resolve_global_moves(move_sets, _p_graph.communicator()); - STOP_TIMER(); - TIMER_BARRIER(_p_graph.communicator()); - - // Apply moves to global partition and extract graph - START_TIMER("Apply moves"); - for (const auto &[node, group, weight, gain, from, to] : global_move_buffer) { - // Apply move to distributed graph - if (is_valid_id(node)) { - if (_p_graph.contains_global_node(node)) { - const NodeID lnode = _p_graph.global_to_local_node(node); - KASSERT(_p_graph.block(lnode) == from, V(lnode) << V(from)); - _p_graph.set_block(lnode, to); - } else { - _p_graph.set_block_weight(from, _p_graph.block_weight(from) - weight); - _p_graph.set_block_weight(to, _p_graph.block_weight(to) + weight); - } - } - - // Apply move to local graph (if contained in local graph) - if (_fm_ctx.revert_local_moves_after_batch && is_valid_id(node)) { - if (const NodeID bnode = node_mapper.to_batch(node); bnode != kInvalidNodeID) { - bp_graph->set_block(bnode, to); - shared.gain_cache.move(*bp_graph, bnode, from, to); - } - } else if (!_fm_ctx.revert_local_moves_after_batch && is_invalid_id(node)) { - if (const NodeID bnode = node_mapper.to_batch(extract_id(node)); - bnode != kInvalidNodeID) { - bp_graph->set_block(bnode, from); - shared.gain_cache.move(*bp_graph, bnode, to, from); - } - } - } - - // Block weights in the batch graph are no longer valid (@todo why?), so we must copy them - // from the global graph again - for (const BlockID block : _p_graph.blocks()) { - bp_graph->set_block_weight(block, _p_graph.block_weight(block)); - } - STOP_TIMER(); - TIMER_BARRIER(_p_graph.communicator()); - - KASSERT( - debug::validate_partition(_p_graph), - "global partition in inconsistent state after round " << global_round << "/" - << local_round, - HEAVY - ); - - // Continue for as long as some PE has more seeds left - have_more_seeds = shared.border_nodes.has_more(); - MPI_Allreduce( - MPI_IN_PLACE, &have_more_seeds, 1, MPI_C_BOOL, MPI_LOR, _p_graph.communicator() - ); - } while (have_more_seeds); - } - - if (_fm_ctx.rebalance_after_each_global_iteration) { - // Since we have changed the partition, re-initialize the balancer - balancer->initialize(); - balancer->refine(); - TIMER_BARRIER(_p_graph.communicator()); - } - - START_TIMER("Update snapshot"); - snapshooter->update(_p_graph, _p_ctx); - STOP_TIMER(); - TIMER_BARRIER(_p_graph.communicator()); - - if (_ctx.refinement.fm.use_abortion_threshold) { - const EdgeWeight final_cut = metrics::edge_cut(_p_graph); - const double improvement = 1.0 * (initial_cut - final_cut) / initial_cut; - if (1.0 - improvement > _ctx.refinement.fm.abortion_threshold) { - break; - } - } - } - - if (!_fm_ctx.rebalance_after_each_global_iteration && _fm_ctx.rebalance_after_refinement) { - // Since we have changed the partition, re-initialize the balancer - balancer->initialize(); - balancer->refine(); - - START_TIMER("Update snapshot"); - snapshooter->update(_p_graph, _p_ctx); - STOP_TIMER(); - TIMER_BARRIER(_p_graph.communicator()); - } - - START_TIMER("Rollback to snapshot"); - snapshooter->rollback(_p_graph); - STOP_TIMER(); - TIMER_BARRIER(_p_graph.communicator()); - - return false; -} - -shm::PartitionContext FMRefiner::setup_shm_p_ctx(const shm::Graph &b_graph) const { - shm::PartitionContext shm_p_ctx; - shm_p_ctx.epsilon = _p_ctx.epsilon; - shm_p_ctx.k = _p_ctx.k; - shm_p_ctx.n = asserting_cast(b_graph.n()); - shm_p_ctx.m = asserting_cast(b_graph.m()); - shm_p_ctx.total_node_weight = - asserting_cast(_p_ctx.graph->global_total_node_weight); - shm_p_ctx.total_edge_weight = - asserting_cast(_p_ctx.graph->global_total_edge_weight); - shm_p_ctx.max_node_weight = asserting_cast(_p_ctx.graph->global_max_node_weight); - shm_p_ctx.setup_block_weights(); - return shm_p_ctx; -} - -shm::fm::SharedData<> FMRefiner::setup_fm_data( - const shm::Context &ctx, - const shm::PartitionedGraph &bp_graph, - const std::vector &lseeds, - const fm::NodeMapper &mapper -) const { - shm::fm::SharedData<> shared(ctx, bp_graph.n(), bp_graph.k()); - - shared.gain_cache.initialize(bp_graph); - if (_fm_ctx.use_bfs_seeds_as_fm_seeds) { - tbb::concurrent_vector bseeds; - tbb::parallel_for(0, lseeds.size(), [&](const std::size_t i) { - const NodeID lseed = lseeds[i]; - const GlobalNodeID gseed = _p_graph.local_to_global_node(lseed); - bseeds.push_back(mapper.to_batch(gseed)); - }); - KASSERT(std::find(bseeds.begin(), bseeds.end(), kInvalidNodeID) == bseeds.end()); - shared.border_nodes.init_precomputed(bp_graph, bseeds); - } else { - shared.border_nodes.init(bp_graph); - } - shared.border_nodes.shuffle(); - - return shared; -} - -void FMRefiner::prepare_shared_data_for_local_round( - shm::PartitionedGraph &bp_graph, shm::fm::SharedData<> &shared -) { - shared.node_tracker.reset(); - for (const BlockID block : _p_graph.blocks()) { - shared.node_tracker.set(bp_graph.n() - block - 1, shm::fm::NodeTracker::MOVED_GLOBALLY); - } -} - -shm::KwayFMRefinementContext FMRefiner::setup_fm_ctx() const { - return shm::KwayFMRefinementContext{ - .num_seed_nodes = 1, - .alpha = _fm_ctx.alpha, - .num_iterations = 1, - .unlock_seed_nodes = false, - .use_exact_abortion_threshold = false, - .abortion_threshold = 0.999, - }; -} - -void FMRefiner::global_round_stats( - const int round, const shm::Graph &b_graph, const std::vector &seed_nodes -) const { - SCOPED_TIMER("Statistics"); - - const auto [n_min, n_avg, n_max, n_sum] = - mpi::gather_statistics(b_graph.n(), _p_graph.communicator()); - const auto [m_min, m_avg, m_max, m_sum] = - mpi::gather_statistics(b_graph.m(), _p_graph.communicator()); - const auto [seeds_min, seeds_avg, seeds_max, seeds_sum] = - mpi::gather_statistics(seed_nodes.size(), _p_graph.communicator()); - - STATS << "FM batch graph -> Iteration " << round << ": Hops=" << _fm_ctx.max_hops - << ", Radius=" << _fm_ctx.max_radius; - STATS << " [FM:BATCH:" << round << "] Number of nodes: [Min=" << n_min << ", Mean=" << n_avg - << ", Max=" << n_max << ", Sum=" << n_sum << "]"; - STATS << " [FM:BATCH:" << round << "] Number of edges: [Min=" << m_min << ", Mean=" << m_avg - << ", Max=" << m_max << ", Sum=" << m_sum << "]"; - STATS << " [FM:BATCH:" << round << "] Number of seeds: [Min=" << seeds_min - << ", Mean=" << seeds_avg << ", Max=" << seeds_max << ", Sum=" << seeds_sum << "]"; - - const EdgeID seeds_deg_sum = std::accumulate( - seed_nodes.begin(), - seed_nodes.end(), - 0, - [&](const NodeID sum, const NodeID node) { return sum + _p_graph.degree(node); } - ); - - const EdgeID seeds_nonlocal_deg_sum = std::accumulate( - seed_nodes.begin(), - seed_nodes.end(), - 0, - [&](const NodeID sum, const NodeID node) { - return sum + std::accumulate( - _p_graph.adjacent_nodes(node).begin(), - _p_graph.adjacent_nodes(node).end(), - 0, - [&](const EdgeID deg, const NodeID neighbor) { - return deg + (_p_graph.block(node) != _p_graph.block(neighbor)); - } - ); - } - ); - - const auto [seeds_deg_min, seeds_deg_avg, seeds_deg_max, seeds_deg_sum_all] = - mpi::gather_statistics(seeds_deg_sum, _p_graph.communicator()); - const auto - [seeds_nonlocal_deg_min, - seeds_nonlocal_deg_avg, - seeds_nonlocal_deg_max, - seeds_nonlocal_deg_sum_all] = - mpi::gather_statistics(seeds_nonlocal_deg_sum, _p_graph.communicator()); - - STATS << " [FM:BATCH:" << round << "] Seed node degrees: [Min=" << seeds_deg_min - << ", Mean=" << seeds_deg_avg << ", Max=" << seeds_deg_max << ", Sum=" << seeds_deg_sum_all - << "]"; - STATS << " [FM:BATCH:" << round - << "] Seed node non-local degrees: [Min=" << seeds_nonlocal_deg_min - << ", Mean=" << seeds_nonlocal_deg_avg << ", Max=" << seeds_nonlocal_deg_max - << ", Sum=" << seeds_nonlocal_deg_sum_all << "]"; -} -} // namespace kaminpar::dist diff --git a/kaminpar-dist/refinement/fm/fm_refiner.h b/kaminpar-dist/refinement/fm/fm_refiner.h deleted file mode 100644 index b40f235b..00000000 --- a/kaminpar-dist/refinement/fm/fm_refiner.h +++ /dev/null @@ -1,104 +0,0 @@ -/******************************************************************************* - * Distributed FM refiner. - * - * @file: fm_refiner.h - * @author: Daniel Seemaier - * @date: 11.09.2022 - ******************************************************************************/ -#pragma once - -#include - -#include "kaminpar-dist/context.h" -#include "kaminpar-dist/datastructures/distributed_graph.h" -#include "kaminpar-dist/datastructures/distributed_partitioned_graph.h" -#include "kaminpar-dist/datastructures/growt.h" -#include "kaminpar-dist/refinement/refiner.h" - -#include "kaminpar-shm/refinement/fm/fm_refiner.h" - -#include "kaminpar-common/logger.h" -#include "kaminpar-common/parallel/atomic.h" - -namespace kaminpar::dist { -namespace fm { -class NodeMapper { -public: - NodeMapper(NoinitVector batch_to_graph); - - NodeMapper(const NodeMapper &) = delete; - NodeMapper &operator=(const NodeMapper &) = delete; - - NodeMapper(NodeMapper &&) noexcept = default; - NodeMapper &operator=(NodeMapper &&) = delete; - - GlobalNodeID to_graph(NodeID bnode) const; - NodeID to_batch(GlobalNodeID gnode) const; - -private: - void construct(); - - NoinitVector _batch_to_graph; - growt::StaticGhostNodeMapping _graph_to_batch; -}; - -} // namespace fm - -class FMRefinerFactory : public GlobalRefinerFactory { -public: - FMRefinerFactory(const Context &ctx); - - FMRefinerFactory(const FMRefinerFactory &) = delete; - FMRefinerFactory &operator=(const FMRefinerFactory &) = delete; - - FMRefinerFactory(FMRefinerFactory &&) noexcept = default; - FMRefinerFactory &operator=(FMRefinerFactory &&) = delete; - - std::unique_ptr - create(DistributedPartitionedGraph &p_graph, const PartitionContext &p_ctx) final; - -private: - const Context &_ctx; -}; - -class FMRefiner : public GlobalRefiner { -public: - FMRefiner( - const Context &ctx, DistributedPartitionedGraph &p_graph, const PartitionContext &p_ctx - ); - - FMRefiner(const FMRefiner &) = delete; - FMRefiner &operator=(const FMRefiner &) = delete; - FMRefiner(FMRefiner &&) noexcept = default; - FMRefiner &operator=(FMRefiner &&) = delete; - - void initialize() final; - bool refine() final; - -private: - shm::PartitionContext setup_shm_p_ctx(const shm::Graph &b_graph) const; - shm::fm::SharedData<> setup_fm_data( - const shm::Context &shm_ctx, - const shm::PartitionedGraph &bp_graph, - const std::vector &seeds, - const fm::NodeMapper &mapper - ) const; - shm::KwayFMRefinementContext setup_fm_ctx() const; - - void prepare_shared_data_for_local_round( - shm::PartitionedGraph &bp_graph, shm::fm::SharedData<> &shared - ); - - void global_round_stats( - int round, const shm::Graph &b_graph, const std::vector &seed_nodes - ) const; - - const Context &_ctx; - const FMRefinementContext &_fm_ctx; - - DistributedPartitionedGraph &_p_graph; - const PartitionContext &_p_ctx; - - std::unique_ptr _balancer_factory; -}; -} // namespace kaminpar::dist diff --git a/kaminpar-dist/refinement/fm/move_conflict_resolver.cc b/kaminpar-dist/refinement/fm/move_conflict_resolver.cc deleted file mode 100644 index daf3bae6..00000000 --- a/kaminpar-dist/refinement/fm/move_conflict_resolver.cc +++ /dev/null @@ -1,178 +0,0 @@ -/******************************************************************************* - * Resolve conflicts between move sequences by accepting moves from the best - * group. - * - * @file: move_conflict_resolver.cc - * @author: Daniel Seemaier - * @date: 11.09.2022 - ******************************************************************************/ -#include "kaminpar-dist/refinement/fm/move_conflict_resolver.h" - -#include - -#include "kaminpar-mpi/wrapper.h" - -#include "kaminpar-dist/dkaminpar.h" - -#include "kaminpar-common/asserting_cast.h" -#include "kaminpar-common/logger.h" -#include "kaminpar-common/parallel/algorithm.h" -#include "kaminpar-common/timer.h" - -namespace kaminpar::dist { -namespace { -SET_DEBUG(false); -} - -std::vector -allgather_global_moves(std::vector &my_global_moves, MPI_Comm comm) { - SCOPED_TIMER("Allgather global moves"); - - DBG << "Contributing " << my_global_moves.size() << " moves"; - - // Make groups unique by PE - START_TIMER("Make groups unique"); - const PEID rank = mpi::get_comm_rank(comm); - for (auto &move : my_global_moves) { - move.group = move.group << 32 | rank; - } - STOP_TIMER(); - - // Allgather number of global moves on each PE - START_TIMER("Allgather move counts"); - const int my_moves_count = asserting_cast(my_global_moves.size()); - const auto recv_counts = mpi::allgather(my_moves_count, comm); - STOP_TIMER(); - - START_TIMER("Compute move displs"); - std::vector recv_displs(recv_counts.size() + 1); - parallel::prefix_sum(recv_counts.begin(), recv_counts.end(), recv_displs.begin() + 1); - STOP_TIMER(); - - // Allgather global moves - START_TIMER("Allgathering moves"); - std::vector result(recv_displs.back()); - mpi::allgatherv( - my_global_moves.data(), - my_moves_count, - result.data(), - recv_counts.data(), - recv_displs.data(), - comm - ); - STOP_TIMER(); - - return result; -} - -void sort_move_groups(std::vector &global_moves) { - SCOPED_TIMER("Sort move groups"); - std::sort(global_moves.begin(), global_moves.end(), [](const auto &lhs, const auto &rhs) { - return lhs.gain > rhs.gain || (lhs.gain == rhs.gain && lhs.group < rhs.group); - }); -} - -void sort_and_compress_move_groups(std::vector &global_moves) { - SCOPED_TIMER("Sort and compress move groups"); - - // Sort by move group - std::sort(global_moves.begin(), global_moves.end(), [](const auto &lhs, const auto &rhs) { - return lhs.group < rhs.group; - }); - - // Compute group gains - std::vector group_gains; - for (std::size_t i = 0; i < global_moves.size();) { - std::int64_t current_group = global_moves[i].group; - EdgeWeight group_gain = 0; - - while (i < global_moves.size() && global_moves[i].group == current_group) { - group_gain += global_moves[i].gain; - global_moves[i].group = group_gains.size(); - ++i; - } - - group_gains.emplace_back(group_gain); - } - - // Sort by group gains - std::sort( - global_moves.begin(), - global_moves.end(), - [&group_gains](const auto &lhs, const auto &rhs) { - return group_gains[lhs.group] > group_gains[rhs.group]; - } - ); -} - -void resolve_move_conflicts_greedy(std::vector &global_moves) { - SCOPED_TIMER("Resolve move conflicts"); - - std::unordered_set moved_nodes; - - for (std::size_t i = 0; i < global_moves.size();) { - std::int64_t current_group = global_moves[i].group; - bool found_conflict = false; - - // First pass: check that no node has been moved before - for (std::size_t j = i; - j < global_moves.size() && global_moves[j].group == current_group && !found_conflict; - ++j) { - const GlobalNodeID current_node = global_moves[j].node; - found_conflict = moved_nodes.find(current_node) != moved_nodes.end(); - } - - // Second pass: mark all moves in this group as invalid or add them to the - // moved nodes - for (std::size_t j = i; j < global_moves.size() && global_moves[j].group == current_group; - ++j) { - if (found_conflict) { - global_moves[j].node = invalidate_id(global_moves[j].node); - } else { - const GlobalNodeID current_node = global_moves[j].node; - moved_nodes.insert(current_node); - } - - ++i; - } - } -} - -std::vector -broadcast_and_resolve_global_moves(std::vector &my_global_moves, MPI_Comm comm) { - DBG << "Got " << my_global_moves.size() << " global moves on this PE"; - - // Resolve conflicts locally - START_TIMER("Local conflict resolution"); - sort_move_groups(my_global_moves); - resolve_move_conflicts_greedy(my_global_moves); - STOP_TIMER(); - - // Filter rejected moves from this PE to reduce communication volume - std::vector my_filtered_global_moves; - for (const auto &move : my_global_moves) { - if (is_valid_id(move.node)) { - my_filtered_global_moves.push_back(move); - } - } - - DBG << "After resolving local conflicts: " << my_filtered_global_moves.size() - << " global moves on this PE"; - auto global_moves = allgather_global_moves(my_filtered_global_moves, comm); - DBG << "After allgathering: " << global_moves.size() << " global moves"; - - START_TIMER("Global conflict resolution"); - sort_move_groups(global_moves); - resolve_move_conflicts_greedy(global_moves); - STOP_TIMER(); - - // Also add rejected moves from this PE to build a consistent interface - for (const auto &move : my_global_moves) { - if (is_invalid_id(move.node)) { - global_moves.push_back(move); - } - } - - return global_moves; -} -} // namespace kaminpar::dist diff --git a/kaminpar-dist/refinement/fm/move_conflict_resolver.h b/kaminpar-dist/refinement/fm/move_conflict_resolver.h deleted file mode 100644 index 265549e2..00000000 --- a/kaminpar-dist/refinement/fm/move_conflict_resolver.h +++ /dev/null @@ -1,53 +0,0 @@ -/******************************************************************************* - * Resolve conflicts between move sequences by accepting moves from the best - * group. - * - * @file: move_conflict_resolver.h - * @author: Daniel Seemaier - * @date: 11.09.2022 - ******************************************************************************/ -#pragma once - -#include -#include - -#include - -#include "kaminpar-dist/dkaminpar.h" - -namespace kaminpar::dist { -struct GlobalMove { - GlobalNodeID node; - GlobalNodeID group; - NodeWeight weight; - EdgeWeight gain; - BlockID from; - BlockID to; -}; - -inline GlobalNodeID invalidate_id(const GlobalNodeID node) { - return node | (1ul << 63); -} - -inline bool is_invalid_id(const GlobalNodeID node) { - return node & (1ul << 63); -} - -inline bool is_valid_id(const GlobalNodeID node) { - return !is_invalid_id(node); -} - -inline GlobalNodeID extract_id(const GlobalNodeID node) { - return is_invalid_id(node) ? node & ~(1ul << 63) : node; -} - -std::vector -allgather_global_moves(std::vector &my_global_moves, MPI_Comm comm); - -void sort_and_compress_move_groups(std::vector &global_moves); - -void resolve_move_conflicts_greedy(std::vector &global_moves); - -std::vector -broadcast_and_resolve_global_moves(std::vector &my_global_moves, MPI_Comm comm); -} // namespace kaminpar::dist diff --git a/kaminpar-dist/refinement/jet/jet_balancer.cc b/kaminpar-dist/refinement/jet/jet_balancer.cc deleted file mode 100644 index 28c56242..00000000 --- a/kaminpar-dist/refinement/jet/jet_balancer.cc +++ /dev/null @@ -1,167 +0,0 @@ -/******************************************************************************* - * Distributed JET balancer due to: "Jet: Multilevel Graph Partitioning on GPUs" - * by Gilbert et al. - * - * @file: jet_balancer.cc - * @author: Daniel Seemaier - * @date: 29.06.2023 - ******************************************************************************/ -#include "kaminpar-dist/refinement/jet/jet_balancer.h" - -#include "kaminpar-dist/datastructures/distributed_partitioned_graph.h" -#include "kaminpar-dist/dkaminpar.h" -#include "kaminpar-dist/metrics.h" - -#include "kaminpar-common/timer.h" - -namespace kaminpar::dist { -JetBalancerFactory::JetBalancerFactory(const Context &ctx) : _ctx(ctx) {} - -std::unique_ptr -JetBalancerFactory::create(DistributedPartitionedGraph &p_graph, const PartitionContext &p_ctx) { - return std::make_unique(_ctx, p_graph, p_ctx); -} - -JetBalancer::JetBalancer( - const Context &ctx, DistributedPartitionedGraph &p_graph, const PartitionContext &p_ctx -) - : _ctx(ctx), - _p_graph(p_graph), - _p_ctx(p_ctx), - _gain_calculator(_p_ctx.k), - _local_buckets(_p_graph, _p_ctx) {} - -void JetBalancer::initialize() {} - -bool JetBalancer::refine() { - using namespace jet; - - _local_buckets.init(_gain_calculator); - - for (int it = 0; it < num_weak_iterations(); ++it) { - weak_iteration(); - } - for (int it = 0; it < num_strong_iterations(); ++it) { - strong_iteration(); - } - - return false; -} - -void JetBalancer::weak_iteration() {} - -void JetBalancer::strong_iteration() {} - -StaticArray JetBalancer::compute_compacitifed_global_bucket_sizes( - StaticArray local_bucket_sizes -) { - MPI_Allreduce( - MPI_IN_PLACE, - local_bucket_sizes.data(), - local_bucket_sizes.size(), - mpi::type::get(), - MPI_SUM, - MPI_COMM_WORLD - ); - return local_bucket_sizes; -} - -bool JetBalancer::is_overloaded_block(const BlockID block) const { - return block_overload(block) > 0; -} - -BlockWeight JetBalancer::block_overload(const BlockID block) const { - return std::max( - 0, _p_ctx.graph->max_block_weight(block) - _p_graph.block_weight(block) - ); -} - -int JetBalancer::num_weak_iterations() const { - return _ctx.refinement.jet_balancer.num_weak_iterations; -} - -int JetBalancer::num_strong_iterations() const { - return _ctx.refinement.jet_balancer.num_strong_iterations; -} - -namespace jet { -Buckets::Buckets(const DistributedPartitionedGraph &p_graph, const PartitionContext &p_ctx) - : _p_graph(p_graph), - _p_ctx(p_ctx), - _bucket_sizes(p_graph.k() * kNumBuckets) { - clear(); -} - -Buckets::Buckets( - const DistributedPartitionedGraph &p_graph, - const PartitionContext &p_ctx, - StaticArray compactified_sizes -) - : Buckets(p_graph, p_ctx) { - BlockID compact_block = 0; - for (const BlockID block : p_graph.blocks()) { - if (p_graph.block_weight(block) > p_ctx.graph->max_block_weight(block)) { - std::copy( - compactified_sizes.begin() + compact_block * kNumBuckets, - compactified_sizes.begin() + (compact_block + 1) * kNumBuckets, - _bucket_sizes.begin() + block * kNumBuckets - ); - ++compact_block; - } - } -} - -void Buckets::init(const RandomizedGainCalculator &gain_calculator) { - for (const NodeID node : _p_graph.nodes()) { - const BlockID block = _p_graph.block(node); - if (_p_graph.block_weight(block) > _p_ctx.graph->max_block_weight(block)) { - const auto max_gainer = gain_calculator.compute_max_gainer(node, _p_ctx); - const EdgeWeight gain = max_gainer.absolute_gain(); - const std::size_t bucket = compute_bucket(gain); - size(block, bucket) += _p_graph.node_weight(node); - } - } -} - -void Buckets::clear() { - std::fill(_bucket_sizes.begin(), _bucket_sizes.end(), 0); -} - -GlobalNodeWeight &Buckets::size(const BlockID block, const std::size_t bucket) { - return _bucket_sizes[block * kNumBuckets + bucket]; -} - -GlobalNodeWeight Buckets::size(const BlockID block, const std::size_t bucket) const { - return _bucket_sizes[block * kNumBuckets + bucket]; -} - -StaticArray Buckets::compactify() const { - const BlockID num_overloaded_blocks = metrics::num_imbalanced_blocks(_p_graph, _p_ctx); - StaticArray compactified_sizes(num_overloaded_blocks * kNumBuckets); - BlockID compact_block = 0; - - for (const BlockID block : _p_graph.blocks()) { - if (_p_graph.block_weight(block) > _p_ctx.graph->max_block_weight(block)) { - std::copy( - _bucket_sizes.begin() + block * kNumBuckets, - _bucket_sizes.begin() + (block + 1) * kNumBuckets, - compactified_sizes.begin() + compact_block * kNumBuckets - ); - ++compact_block; - } - } - - return compactified_sizes; -} - -std::size_t Buckets::compute_bucket(const EdgeWeight gain) { - if (gain > 0) { - return 0; - } else if (gain == 0) { - return std::min(kNumBuckets - 1, 1); - } else { // gain < 0 - return std::min(kNumBuckets - 1, 2 + std::floor(std::log2(-gain))); - } -} -} // namespace jet -} // namespace kaminpar::dist diff --git a/kaminpar-dist/refinement/jet/jet_balancer.h b/kaminpar-dist/refinement/jet/jet_balancer.h deleted file mode 100644 index dcdc2550..00000000 --- a/kaminpar-dist/refinement/jet/jet_balancer.h +++ /dev/null @@ -1,103 +0,0 @@ -/******************************************************************************* - * Distributed JET balancer due to: "Jet: Multilevel Graph Partitioning on GPUs" - * by Gilbert et al. - * - * @file: jet_balancer.h - * @author: Daniel Seemaier - * @date: 29.06.2023 - ******************************************************************************/ -#pragma once - -#include "kaminpar-dist/context.h" -#include "kaminpar-dist/refinement/gain_calculator.h" -#include "kaminpar-dist/refinement/refiner.h" - -namespace kaminpar::dist { -class JetBalancerFactory : public GlobalRefinerFactory { -public: - JetBalancerFactory(const Context &ctx); - - JetBalancerFactory(const JetBalancerFactory &) = delete; - JetBalancerFactory &operator=(const JetBalancerFactory &) = delete; - - JetBalancerFactory(JetBalancerFactory &&) noexcept = default; - JetBalancerFactory &operator=(JetBalancerFactory &&) = delete; - - std::unique_ptr - create(DistributedPartitionedGraph &p_graph, const PartitionContext &p_ctx) final; - -private: - const Context &_ctx; -}; - -namespace jet { -class Buckets { -public: - constexpr static std::size_t kNumBuckets = 16; - - Buckets(const DistributedPartitionedGraph &p_graph, const PartitionContext &p_ctx); - - Buckets( - const DistributedPartitionedGraph &p_graph, - const PartitionContext &p_ctx, - StaticArray compactified_sizes - ); - - void init(const RandomizedGainCalculator &gain_calculator); - void clear(); - - GlobalNodeWeight &size(BlockID block, std::size_t bucket); - GlobalNodeWeight size(BlockID block, std::size_t bucket) const; - - StaticArray compactify() const; - - static std::size_t compute_bucket(EdgeWeight gain); - -private: - const DistributedPartitionedGraph &_p_graph; - const PartitionContext &_p_ctx; - - StaticArray _bucket_sizes; -}; -} // namespace jet - -class JetBalancer : public GlobalRefiner { - SET_STATISTICS_FROM_GLOBAL(); - SET_DEBUG(true); - -public: - JetBalancer( - const Context &ctx, DistributedPartitionedGraph &p_graph, const PartitionContext &p_ctx - ); - - JetBalancer(const JetBalancer &) = delete; - JetBalancer &operator=(const JetBalancer &) = delete; - - JetBalancer(JetBalancer &&) noexcept = default; - JetBalancer &operator=(JetBalancer &&) = delete; - - void initialize() final; - bool refine() final; - -private: - void weak_iteration(); - void strong_iteration(); - - StaticArray - compute_compacitifed_global_bucket_sizes(StaticArray local_bucket_sizes); - - bool is_overloaded_block(BlockID block) const; - BlockWeight block_overload(BlockID block) const; - - int num_weak_iterations() const; - int num_strong_iterations() const; - - const Context &_ctx; - DistributedPartitionedGraph &_p_graph; - const PartitionContext &_p_ctx; - - RandomizedGainCalculator _gain_calculator; - - jet::Buckets _local_buckets; -}; -} // namespace kaminpar::dist