1+ #pragma once
2+
3+ #include < mt-kahypar/dynamic/dynamic_strategy.h>
4+
5+ namespace mt_kahypar ::dyn {
6+
7+ class Leopard : public DynamicStrategy {
8+
9+ private:
10+
11+ vec<size_t > skippedComps; // number of consecutive times a node was not examined for reassignment
12+ double skipping_threshold = 0.1 ; // threshold for examining a node for reassignment as experimentally determined in the leopard paper
13+
14+ PartitionID assign_node_first_free_partition (const HypernodeID& hn) {
15+ for (PartitionID p = 0 ; p < context.partition .k ; ++p) {
16+ if (partitioned_hypergraph_m.partWeight (p) + hypergraph_m.nodeWeight (hn) <=
17+ context.partition .max_part_weights [p]) {
18+ partitioned_hypergraph_m.addNode (hn, p);
19+ return p;
20+ }
21+ }
22+ // if no partition could accommodate the node put in the first
23+ partitioned_hypergraph_m.addNode (hn, 0 );
24+ return 0 ;
25+ }
26+
27+
28+ PartitionID getTargetPartFennel (const HypernodeID& hn) {
29+ double fennel_alpha = sqrt (sqrt (context.partition .k ) * hypergraph_m.initialNumEdges () / pow (hypergraph_m.initialNumNodes (),1.5 ));
30+ const PartitionID current_part = partitioned_hypergraph_m.partID (hn);
31+ ASSERT (current_part != kInvalidPartition );
32+ std::vector block_connectivities (context.partition .k , std::make_tuple (0 ,0 ));
33+ for ( PartitionID p = 0 ; p < context.partition .k ; ++p ) {
34+ block_connectivities[p] = std::make_tuple (0 , p);
35+ }
36+ // compute neighbor count in each block
37+ for ( const HyperedgeID& he : hypergraph_m.incidentEdges (hn) ) {
38+ for ( const HypernodeID& hn2 : hypergraph_m.pins (he) ) {
39+ block_connectivities[partitioned_hypergraph_m.partID (hn2)] = std::make_tuple (std::get<0 >(block_connectivities[partitioned_hypergraph_m.partID (hn2)]) + 1 , partitioned_hypergraph_m.partID (hn2));
40+ }
41+ }
42+ std::vector<std::tuple<double , PartitionID>> block_scores (context.partition .k , std::make_tuple (0 ,0 ));
43+ for ( PartitionID p = 0 ; p < context.partition .k ; ++p )
44+ {
45+ constexpr double fennel_gamma = 1.5 ;
46+ double score = std::get<0 >(block_connectivities[p]) - fennel_alpha * (fennel_gamma / 2 ) * pow (partitioned_hypergraph_m.partWeight (p), fennel_gamma - 1 );
47+ block_scores[p] = std::make_tuple (score, p);
48+ }
49+
50+ // sort block_scores in descending order
51+ std::sort (block_scores.begin (), block_scores.end (), [](const auto & a, const auto & b) {
52+ return std::get<0 >(a) > std::get<0 >(b);
53+ });
54+
55+ // return the block with the highest score that doesn't violate max_part_weights (imbalance)
56+ for (const auto & block_score : block_scores)
57+ {
58+ if (std::get<1 >(block_score) != current_part)
59+ {
60+ if (partitioned_hypergraph_m.partWeight (std::get<1 >(block_score)) + hypergraph_m.nodeWeight (hn) <=
61+ context.partition .max_part_weights [std::get<1 >(block_score)])
62+ {
63+ return std::get<1 >(block_score);
64+ }
65+ }
66+ }
67+ return current_part;
68+ }
69+
70+ bool toExamineOrNot (const HypernodeID& hn, size_t threshold) {
71+ size_t neighbors = 0 ;
72+ for (const HyperedgeID& he : hypergraph_m.incidentEdges (hn)) {
73+ for (const HypernodeID& hn2 : hypergraph_m.pins (he)) {
74+ if (hn2 != hn) {
75+ neighbors++;
76+ }
77+ }
78+ }
79+ if (neighbors == 0 ) {
80+ return false ;
81+ }
82+ if ((skippedComps[hn] + 1 ) / neighbors >= threshold) {
83+ skippedComps[hn] = 0 ;
84+ return true ;
85+ } else {
86+ skippedComps[hn]++;
87+ return false ;
88+ }
89+ }
90+
91+ public:
92+
93+ Leopard (ds::MutableHypergraph& hypergraph_m, Context& context)
94+ : DynamicStrategy(hypergraph_m, context) {}
95+
96+ MutablePartitionedHypergraph& init () override {
97+ partitioned_hypergraph_m = partition_hypergraph_km1 (hypergraph_m, context);
98+ ASSERT (partitioned_hypergraph_m.checkAllConnectivitySets ());
99+ for (HypernodeID hn = 0 ; hn < hypergraph_m.initialNumNodes (); ++hn) {
100+ skippedComps.push_back (0 );
101+ }
102+ return partitioned_hypergraph_m;
103+ }
104+
105+ void partition (Change& change, size_t changes_size) override {
106+ (void ) changes_size;
107+ ASSERT (metrics::isBalanced (partitioned_hypergraph_m, context));
108+ // ASSERT(partitioned_hypergraph_m.checkAllConnectivitySets());
109+
110+ // Removals are not supported in streaming mode
111+
112+ ASSERT (change.removed_nodes .empty ());
113+ ASSERT (change.removed_edges .empty ());
114+ ASSERT (change.removed_pins .empty ());
115+
116+ for (const HypernodeID& hn : change.added_nodes ) {
117+ const HypernodeID new_hn = hypergraph_m.addHypernode ({}, 1 );
118+ (void ) new_hn;
119+ ASSERT (hn == new_hn);
120+ updateMaxPartWeight (context, hypergraph_m);
121+ const PartitionID assigned_part = assign_node_first_free_partition (hn);
122+ (void ) assigned_part;
123+ ASSERT (assigned_part != kInvalidPartition );
124+ skippedComps.push_back (0 );
125+ }
126+
127+ for (const HyperedgeID& he : change.added_edges ) {
128+ hypergraph_m.addHyperedge ({}, 1 );
129+ partitioned_hypergraph_m.addEdge (he);
130+ }
131+
132+ for (const auto & [node, edge] : change.added_pins )
133+ {
134+ hypergraph_m.addPin (edge, node);
135+ partitioned_hypergraph_m.incrementPinCountOfBlockWrapper (edge, partitioned_hypergraph_m.partID (node));
136+ }
137+
138+ std::vector<HypernodeID> nodes_to_examine;
139+ for (const HyperedgeID& he : change.added_edges )
140+ {
141+ for (const HypernodeID& hn : hypergraph_m.pins (he))
142+ {
143+ nodes_to_examine.push_back (hn);
144+ }
145+ }
146+
147+ size_t examined_nodes = 1 ;
148+
149+ while (!nodes_to_examine.empty () && examined_nodes < 10 )
150+ {
151+ const HypernodeID hn = nodes_to_examine.back ();
152+ nodes_to_examine.pop_back ();
153+ examined_nodes++;
154+ for (size_t stage = 1 ; stage <= 2 ; ++stage)
155+ {
156+ const PartitionID target_part = getTargetPartFennel (hn);
157+ if (target_part != partitioned_hypergraph_m.partID (hn)) {
158+ partitioned_hypergraph_m.changeNodePart (hn, partitioned_hypergraph_m.partID (hn), target_part);
159+ for (const HyperedgeID& he : hypergraph_m.incidentEdges (hn))
160+ {
161+ for (const HypernodeID& hn2 : hypergraph_m.pins (he))
162+ {
163+ if (hn2 != hn && toExamineOrNot (hn2, skipping_threshold)) {
164+ nodes_to_examine.push_back (hn2);
165+ }
166+ }
167+ }
168+ }
169+ }
170+ }
171+ }
172+ };
173+ }
0 commit comments