@@ -117,7 +117,7 @@ private SpanningTree growApproach(SpanningTree spanningTree) {
117117 // is actually better
118118 if (spanningTree .effectiveNodeCount () < k )
119119 return spanningTree ;
120-
120+
121121 HugeLongArray outDegree = HugeLongArray .newArray (graph .nodeCount ());
122122
123123 HugeLongArray parent = HugeLongArray .newArray (graph .nodeCount ());
@@ -152,10 +152,7 @@ private SpanningTree growApproach(SpanningTree spanningTree) {
152152 nodesInTree ++;
153153 nodeAdded = true ;
154154 } else {
155- while (!exterior .get (toTrim .top ())) { //not valid frontier nodes anymore, just ignore
156- toTrim .pop (); //as we said, pq does not have a direct remove method
157- }
158- var nodeToTrim = toTrim .top (); //a leaf node with worst cost
155+ var nodeToTrim = findNextValidLeaf (toTrim , exterior ); //a leaf node with currently theworst cost
159156 if (parent .get (node ) == nodeToTrim ) {
160157 //we cannot add it, if we're supposed to remove its parent
161158 //TODO: should be totally feasible to consider the 2nd worst then.
@@ -197,13 +194,7 @@ private SpanningTree growApproach(SpanningTree spanningTree) {
197194 }
198195 }
199196 if (affectedNode != -1 ) { //if a node has been converted to a leaf
200- if (!toTrim .containsElement (affectedNode )) {
201- toTrim .add (affectedNode , affectedCost ); //add it to pq
202- } else {
203- //it is still in the queue, but it is not a leaf anymore, so it's value is obsolete
204- toTrim .set (affectedNode , affectedCost );
205- }
206- exterior .set (affectedNode ); //and mark it in the exterior
197+ updateExterior (affectedNode , affectedCost , toTrim , exterior );
207198 }
208199 } else {
209200 //the root is removed, long live the new root!
@@ -212,14 +203,9 @@ private SpanningTree growApproach(SpanningTree spanningTree) {
212203 var newRoot = rootNodeAdjacent .nextSetBit (0 );
213204 rootNodeAdjacent .clear (); //empty everything
214205 //find the children of the new root (this can happen once per node)
215- graph .forEachRelationship (newRoot , (s , t ) -> {
216- //relevant are only those nodes which are currently
217- //in the k-tree
218- if (parent .get (t ) == s && included .get (t )) {
219- rootNodeAdjacent .set (t );
220- }
221- return true ;
222- });
206+
207+ fillChildren (newRoot , rootNodeAdjacent , parent , included );
208+
223209 root = newRoot ;
224210 //set it as root
225211 clearNode (root , parent , costToParent );
@@ -247,32 +233,26 @@ private SpanningTree growApproach(SpanningTree spanningTree) {
247233 //then the node (being a leaf) is added to the trimming priority queu
248234 toTrim .add (node , associatedCost );
249235 exterior .set (node ); //and the exterior
236+ relaxNode (node , priorityQueue , parent , spanningTree );
250237
251- graph .forEachRelationship (node , (s , t ) -> {
252- if (parent .get (t ) == s ) {
253- //TODO: work's only on mst edges for now (should be doable to re-find an k-MST from whole graph)
254- if (!priorityQueue .containsElement (t )) {
255- priorityQueue .add (t , spanningTree .costToParent (t ));
256- }
257-
258- }
259- return true ;
260- });
261238 } else {
262239 clearNode (node , parent , costToParent );
263-
264240 }
265241 }
266242 //post-processing step: anything not touched is reset to -1
243+ pruneUntouchedNodes (parent , costToParent , included );
244+ progressTracker .endSubTask ();
245+ return new SpanningTree (root , graph .nodeCount (), k , parent , costToParent , totalCost );
246+
247+ }
248+
249+ private void pruneUntouchedNodes (HugeLongArray parent , HugeDoubleArray costToParent , BitSet included ) {
267250 graph .forEachNode (nodeId -> {
268251 if (!included .get (nodeId )) {
269252 clearNode (nodeId , parent , costToParent );
270253 }
271254 return true ;
272255 });
273- progressTracker .endSubTask ();
274- return new SpanningTree (root , graph .nodeCount (), k , parent , costToParent , totalCost );
275-
276256 }
277257
278258 private void clearNode (long node , HugeLongArray parent , HugeDoubleArray costToParent ) {
@@ -288,6 +268,52 @@ private boolean moveMakesSense(double cost1, double cost2, DoubleUnaryOperator m
288268 }
289269 }
290270
271+ private void updateExterior (long affectedNode , double affectedCost , HugeLongPriorityQueue toTrim , BitSet exterior ) {
272+ if (!toTrim .containsElement (affectedNode )) {
273+ toTrim .add (affectedNode , affectedCost ); //add it to pq
274+ } else {
275+ //it is still in the queue, but it is not a leaf anymore, so it's value is obsolete
276+ toTrim .set (affectedNode , affectedCost );
277+ }
278+ exterior .set (affectedNode ); //and mark it in the exterior
279+ }
280+
281+ private long findNextValidLeaf (HugeLongPriorityQueue toTrim , BitSet exterior ) {
282+ while (!exterior .get (toTrim .top ())) { //not valid frontier nodes anymore, just ignore
283+ toTrim .pop (); //as we said, pq does not have a direct remove method
284+ }
285+ return toTrim .top ();
286+ }
287+
288+ private void fillChildren (long newRoot , BitSet rootNodeAdjacent , HugeLongArray parent , BitSet included ) {
289+ graph .forEachRelationship (newRoot , (s , t ) -> {
290+ //relevant are only those nodes which are currently
291+ //in the k-tree
292+ if (parent .get (t ) == s && included .get (t )) {
293+ rootNodeAdjacent .set (t );
294+ }
295+ return true ;
296+ });
297+ }
298+
299+ private void relaxNode (
300+ long node ,
301+ HugeLongPriorityQueue priorityQueue ,
302+ HugeLongArray parent ,
303+ SpanningTree spanningTree
304+ ) {
305+ graph .forEachRelationship (node , (s , t ) -> {
306+ if (parent .get (t ) == s ) {
307+ //TODO: work's only on mst edges for now (should be doable to re-find an k-MST from whole graph)
308+ if (!priorityQueue .containsElement (t )) {
309+ priorityQueue .add (t , spanningTree .costToParent (t ));
310+ }
311+
312+ }
313+ return true ;
314+ });
315+ }
316+
291317}
292318
293319
0 commit comments