--- /dev/null
+# $Id
+# Copyright 2005 University of Zagreb, Croatia. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of Zagreb,
+# Croatia and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+#****f* graph_partitioning.tcl/writePartitions
+# NAME
+# writePartitions -- write partitions
+# SYNOPSIS
+# writePartitions node_weight
+# FUNCTION
+# Procedure that writes for each node its partition.
+# INPUTS
+# * node_weight -- array of node weights
+#****
+proc writePartitions {node_weight} {
+ global nparts;
+ global node_list;
+ global link_list;
+ global split_list;
+ global finalpartition;
+
+ upvar $node_weight nweight;
+
+ #counts how many nodes are in each partition
+ array set nr_nodes_partition {};
+ #sum up the weight of each partition
+ array set weight_partition {};
+
+ for {set i 0} {$i<$nparts} {incr i} {
+ set nr_nodes_partition($i) 0;
+ set weight_partition($i) 0;
+ }
+
+ set i 0;
+ foreach node $node_list {
+ #write to node its partition
+ setPartition $node $finalpartition($i);
+ incr nr_nodes_partition($finalpartition($i)) 1;
+ incr weight_partition($finalpartition($i)) $nweight($i);
+ incr i;
+ }
+
+ #disconnect for algorithm connected nodes
+ foreach split $split_list {
+ set node1 [lindex $split 0];
+ set node2 [lindex $split 1];
+ set linkToSplit [linkByPeers $node1 $node2];
+ splitGUILink $linkToSplit ;
+ }
+
+ set outstr "";
+ for {set i 0} {$i < $nparts} {incr i} {
+ set outstr "p$i: $nr_nodes_partition($i) vertices with weight $weight_partition($i)";
+ #puts [format %s $outstr];
+ }
+
+ redrawAll;
+ updateUndoLog;
+ tk_dialog .dialog1 "Graph partitioning output" "Done.\n" info 0 Dismiss;
+}
+
+#****f* graph_partitioning.tcl/setPartition
+# NAME
+# setPartition -- set partition
+# SYNOPSIS
+# setPartition $node $partition
+# FUNCTION
+# Procedure searches the node for the information about
+# its partition, if found it replace the info, if not found
+# it adds the information to the node.
+# INPUTS
+# * node -- node id
+# * partition -- partition of the node
+#****
+proc setPartition { node partition } {
+ global $node;
+
+ set p [lsearch [set $node] "partition *"];
+ if { $p >= 0 } {
+ set $node [lreplace [set $node] $p $p "partition p$partition"];
+ } else {
+ set $node [linsert [set $node] end "partition p$partition"];
+ }
+}
+
+#****f* graph_partitioning.tcl/getNodePartition
+# NAME
+# getNodePartition -- get node partition
+# SYNOPSIS
+# getNodePartition $node
+# FUNCTION
+# Function searches for node's partition, and returns it
+# (or empty string if not found)
+# INPUTS
+# * node -- node id
+# RESULT
+# * part -- the node's partition
+#****
+proc getNodePartition { node } {
+ global $node;
+ set part [lindex [lsearch -inline [set $node] "partition *"] 1];
+ return $part;
+}
+
+#****f* graph_partitioning.tcl/debug
+# NAME
+# debug
+# SYNOPSIS
+# debug $message
+# FUNCTION
+# Prints the message to the stderr if enabled.
+# INPUTS
+# * message -- a messege to be printed
+#****
+proc debug { message } {
+ global debug;
+ if ![info exists debug(enabled)] {
+ #do nothing
+ return;
+ }
+ puts stderr $message;
+}
+
+#****f* graph_partitioning.tcl/graphPartition
+# NAME
+# graphPartition -- graph partition
+# SYNOPSIS
+# graphPartition $partNum
+# FUNCTION
+# Procedure which prepares arrays for partitioning
+# algorithm, and starts the algorithm.
+# INPUTS
+# * partNum -- number of partitions
+#****
+proc graphPartition {partNum} {
+ global node_list;
+ global link_list;
+ global finalpartition;
+
+ global nparts;
+ global tpwgts;
+ global max_nweight;
+
+ array set node_weight {};
+ array set edge_weight {};
+ array set node_neighbour {};
+ array set edge_array {};
+ array set tpwgts {};
+ array set node_map {};
+
+ puts "";
+ #puts " Starting graph partitioning...";
+ #puts "+------------------------------------------------+";
+ #puts "";
+
+ set start [clock clicks -milliseconds];
+
+ #initialise the arrays for the algorithm
+ set nparts $partNum;
+ initNodes node_weight;
+ set nvertices [array size node_weight];
+ initNeighbours node_neighbour edge_array edge_weight;
+
+ if {$nparts > $nvertices} then {
+ debug "Number of vertices should be greater then number of partitions.";
+ displayErrorMessage "Number of vertices should be greater then number of partitions.";
+ return;
+ } elseif {$nparts < 2} {
+ debug "Number of partition should be greater then 1.";
+ displayErrorMessage "Number of partition should be greater then 1.";
+ return;
+ }
+
+ #calculate tpwgts array
+ for {set i 0} {$i < $nparts} {incr i} {
+ set tpwgts($i) [expr {1.0 / (1.0 * $nparts)}];
+ }
+
+ set t [time {
+ recursiveBisection $nvertices node_weight node_neighbour edge_array edge_weight tpwgts $nparts 0 node_map;
+ } 1];
+ set microsec [lindex $t 0];
+ puts "total time: [expr {$microsec * 0.000001}] sec";
+
+
+ #compute cut
+ set cut 0;
+ for {set i 0} {$i < $nvertices} {incr i} {
+ set id($i) 0;
+ set ed($i) 0;
+
+ set curr_partition $finalpartition($i);
+
+ # calculates the sum of the edge weights of the adjacent vertices of i
+ if {[info exists node_neighbour($i)]} then {
+ foreach ngb $node_neighbour($i) {
+ if {$curr_partition == $finalpartition($ngb)} then {
+ # vertice in the same partition
+ incr id($i) 1;#$edge_weight([getEdgeBetween $i $ngb edge_array]);
+ } else {
+ # vertice in a different partition
+ incr ed($i) 1;#$edge_weight([getEdgeBetween $i $ngb edge_array]);
+ };#if-else
+ };#foreach
+
+ if {$ed($i) > 0 || [llength $node_neighbour($i)] == 0} then { ;#vanjski node, ili nema susjeda
+ incr cut $ed($i);
+ }
+ }
+ }
+ set cut [expr {$cut / 2}];
+ puts "end cut: $cut";
+
+ #save the partitions
+ writePartitions node_weight;
+
+ set end [clock clicks -milliseconds];
+ puts [format "total elapsed time: %.6f s" [expr {($end - $start) * 0.001}]];
+
+ puts "";
+ puts " Done graph partitioning.";
+ puts "+------------------------------------------------+";
+ puts "";
+}
+
+#****f* graph_partitioning.tcl/initNodes
+# NAME
+# initNodes
+# SYNOPSIS
+# initNodes node_weight
+# FUNCTION
+# Initialise the node_weight array.
+# INPUTS
+# * node_weight -- empty array of node weights
+#****
+proc initNodes {node_weight} {
+ global node_list;
+ upvar $node_weight nweight;
+
+ set i 0;
+ foreach node $node_list {
+ #if the node is pseudo, remove it
+ if {[nodeType $node] == "pseudo"} then {
+ mergePseudoLink $node
+ } else {
+ #seve node's weight into array
+ set nweight($i) [getNodeWeight $node];
+ incr i;
+ }
+ }
+}
+
+#****f* graph_partitioning.tcl/mergePseudoLink
+# NAME
+# mergePseudoLink -- merge pseudo link
+# SYNOPSIS
+# mergePseudoLink $pnode
+# FUNCTION
+# Removes pseudo connections.
+# INPUTS
+# * pnode -- pseudo node id
+#****
+proc mergePseudoLink { pnode } {
+ global node_list;
+ global split_list;
+
+ foreach n $node_list {
+ #get the links connecting the both pseudo node's
+ set l1 [linkByPeers $pnode $n];
+ if {$l1 != ""} then {
+ set l2 [getLinkMirror $l1];
+ #set peers1 [linkPeers $l1];
+ set peers2 [linkPeers $l2];
+
+ #get it's not-pseudo peers
+ #set n1 [lindex $peers1 0];
+ set n2 [lindex $peers2 0];
+
+ mergeLink $l1;
+ if {[lsearch $split_list "$n $n2"] < 0 && [lsearch $split_list "$n2 $n"] < 0} then {
+ lappend split_list "$n $n2";
+ break;
+ }
+ }
+ }
+}
+
+#****f* graph_partitioning.tcl/initNeighbours
+# NAME
+# initNeighbours
+# SYNOPSIS
+# initNeighbours node_neighbour edge_array edge_weight
+# FUNCTION
+# Initialise node_neighbour, edge_array and edge_weight array.
+# INPUTS
+# * node_neighbour -- empty array
+# * edge_array -- empty array
+# * edge_weight -- empty array
+#****
+proc initNeighbours {node_neighbour edge_array edge_weight} {
+ global node_list;
+ global link_list;
+
+ upvar $edge_array earray;
+ upvar $node_neighbour nneighbour;
+ upvar $edge_weight eweight;
+
+ for {set i 0} {$i < [llength $link_list]} {incr i} {
+ #take the edge one after the other
+ set edge [lindex $link_list $i];
+ #read nodes incident to edge
+ set peers [linkPeers $edge];
+ set node1 [lindex $peers 0];
+ set node2 [lindex $peers 1];
+ #read node's index in the node_list
+ set idx_n1 [lsearch $node_list $node1];
+ set idx_n2 [lsearch $node_list $node2];
+ #node1 is adjacent to node2
+ lappend nneighbour($idx_n1) $idx_n2;
+ lappend nneighbour($idx_n2) $idx_n1;
+ set earray($i) "$idx_n1 $idx_n2";
+ #calculate the link weight
+ set eweight($i) [expr {[getLinkWeight $edge] / 100000}]; #!!!!!!s
+ }
+}
+
+#****f* graph_partitioning.tcl/recursiveBisection
+# NAME
+# recursiveBisection
+# SYNOPSIS
+# recursiveBisection $nvertices node_weight node_neighbour
+# edge_array edge_weight tpart_wgts $new_parts $part_nr up_map
+# FUNCTION
+# Recursive starts coarsening, initial partitioning and uncoarsening.
+# In each recursion it bisect the graph and recalculates arrays for each
+# part.
+# INPUTS
+# * nvertices -- number of vertices
+# * node_weight -- array of node weights
+# * node_neighbour -- array of node neighbours
+# * edge_array -- array of edges
+# * edge_weight -- array of edge weights
+# * tpart_wgts -- array of each partition ratio of the graph
+# * new_parts -- number to divide the graph
+# * part_nr -- counts how deep the recursion is
+# * up_map --
+#****
+proc recursiveBisection {nvertices node_weight node_neighbour edge_array edge_weight tpart_wgts new_parts part_nr up_map} {
+ global part_mincut;
+ global finalpartition;
+ global part_partition;
+
+ upvar $node_weight nweight;
+ upvar $node_neighbour nneighbour;
+ upvar $edge_array earray;
+ upvar $edge_weight eweight;
+ upvar $tpart_wgts tpwgts;
+ upvar $up_map upmap;
+
+ array set tpwgts2 {};
+
+ set nparts $new_parts;
+ set cut 0;
+ debug "recursiveBisection!!!!";
+ debug "RB: nparts=$nparts";
+
+ #calculate for each partition its wished weight
+ set tvwgt [sum_array $nvertices nweight];
+ set sum_tpwgt [sum_array [expr {$nparts / 2}] tpwgts];
+ set tpwgts2(0) [expr {int ( ceil($tvwgt * $sum_tpwgt))}];
+ set tp2 [expr {ceil($sum_tpwgt * $tvwgt)}];
+ set tpwgts2(1) [expr {int($tvwgt - $tpwgts2(0))}];
+ debug "RB: tvwgt=$tvwgt, tpwgts2(0)=$tpwgts2(0), tpwgts2(1)=$tpwgts2(1), sum_tpwgt=$sum_tpwgt";
+
+ #start partitioning
+ coarseGraph $nvertices nweight nneighbour earray eweight tpwgts2;
+ #minimal cut
+ set cut $part_mincut;
+
+ #calculate for each vertex its right partition by adding to "0" and "1" partition a number
+ for {set i 0} {$i < $nvertices} {incr i} {
+ if {[array size upmap] > 0} {
+ set finalpartition($upmap($i)) [expr {$part_partition($i) + $part_nr}];
+ } else {
+ set finalpartition($i) [expr {$part_partition($i) + $part_nr}];
+ }
+ }
+
+ #when partition in more then 2 parts, divide the graph in 2 halfs (subgraph "0" and subgraph "1")
+ if {$nparts > 2} then {
+ array set snode_neighbour {};
+ array set snode_weight {};
+ array set sedge_array {};
+ array set sedge_weight {};
+ array set snode_map {};
+ array set snode_map_help {}; #auxiliary variable
+
+ array set sn_vtxs {};
+ set sn_vtxs(0) 0;
+ set sn_vtxs(1) 0;
+
+ array set sn_edges {};
+ set sn_edges(0) 0;
+ set sn_edges(1) 0;
+
+ splitGraph $nvertices nneighbour nweight earray eweight snode_neighbour snode_weight sedge_array sedge_weight snode_map sn_vtxs sn_edges snode_map_help;
+
+ array set snode_neighbour0 {};
+ array set snode_weight0 {};
+ array set sedge_array0 {};
+ array set sedge_weight0 {};
+ array set snode_map0 {};
+
+ array set snode_neighbour1 {};
+ array set snode_weight1 {};
+ array set sedge_array1 {};
+ array set sedge_weight1 {};
+ array set snode_map1 {};
+
+ #save the node characteristics from both subgraphs in two different arrays
+
+ for {set i 0} {$i < $sn_vtxs(0)} {incr i} {
+ if {[info exists snode_neighbour(0,$i)]} then {
+ set snode_neighbour0($i) $snode_neighbour(0,$i);
+ }
+ if {[array size upmap] > 0} then {
+ set snode_map0($i) $upmap($snode_map_help(0,$i));
+ } else {
+ set snode_map0($i) $snode_map_help(0,$i);
+ }
+ debug "snode_map0($i)=$snode_map0($i)";
+ set snode_weight0($i) $snode_weight(0,$i);
+ }
+
+ for {set i 0} {$i < $sn_vtxs(1)} {incr i} {
+ if {[info exists snode_neighbour(1,$i)]} then {
+ set snode_neighbour1($i) $snode_neighbour(1,$i);
+ }
+ if {[array size upmap] > 0} then {
+ set snode_map1($i) $upmap($snode_map_help(1,$i));
+ } else {
+ set snode_map1($i) $snode_map_help(1,$i);
+ }
+ debug "snode_map1($i)=$snode_map1($i)";
+ set snode_weight1($i) $snode_weight(1,$i);
+ }
+
+ #save the link characteristics from both subgraphs in two different arrays
+
+ for {set i 0} {$i < $sn_edges(0)} {incr i} {
+ set sedge_array0($i) $sedge_array(0,$i);
+ set sedge_weight0($i) $sedge_weight(0,$i);
+ }
+
+ for {set i 0} {$i < $sn_edges(1)} {incr i} {
+ set sedge_array1($i) $sedge_array(1,$i);
+ set sedge_weight1($i) $sedge_weight(1,$i);
+ }
+ }
+
+ #update the tpwgts (partition's ratio of the graph)
+ mult_array 0 [expr {int($nparts / 2)}] tpwgts [expr {1 / $sum_tpwgt}];
+ mult_array [expr {int($nparts / 2)}] $nparts tpwgts [expr {1.0 / (1.0 - $sum_tpwgt)}];
+ set max [expr {int($nparts - $nparts / 2)}];
+
+ for {set i 0} {$i < $max} {incr i} {
+ set new_tpwgts($i) $tpwgts([expr {$i + int($nparts / 2)}]);
+ debug " new_tpwgts($i)=$new_tpwgts($i)";
+ }
+
+ #call recursive itself
+ if {$nparts > 3} then {
+ #partition the first subgraph
+ recursiveBisection $sn_vtxs(0) snode_weight0 snode_neighbour0 sedge_array0 sedge_weight0 tpwgts [expr {int($nparts / 2)}] $part_nr snode_map0;
+ #partition the second subgraph
+ recursiveBisection $sn_vtxs(1) snode_weight1 snode_neighbour1 sedge_array1 sedge_weight1 new_tpwgts [expr {int($nparts - $nparts / 2)}] [expr {int($part_nr + $nparts / 2)} ] snode_map1;
+ } elseif {$nparts == 3} then {
+ #partition the second subgraph
+ recursiveBisection $sn_vtxs(1) snode_weight1 snode_neighbour1 sedge_array1 sedge_weight1 new_tpwgts [expr {int($nparts - $nparts / 2)}] [expr {int($part_nr + $nparts / 2)}] snode_map1;
+ }
+}
+
+#****f* graph_partitioning.tcl/coarseGraph
+# NAME
+# coarseGraph -- coarsening and uncoarsening
+# SYNOPSIS
+# coarseGraph $nvertices node_weight node_neighbour edge_array edge_weight tpwgts2
+# FUNCTION
+# Coarsening and uncoarsening phase. Procedure first recursivly coarse the graph.
+# The coarsest graph is partitionend. In "backrolling" of recurson, the coarse
+# graph is uncoarsen and refined.
+# INPUTS
+# * nvertices -- number of vertices
+# * node_weight -- array of node weights
+# * node_neighbour -- array of node neighbours
+# * edge_array -- array of edges
+# * edge_weight -- array of edge weights
+# * tpwgts2 -- array of each partition size of the graph
+#****
+proc coarseGraph {nvertices node_weight node_neighbour edge_array edge_weight tpwgts2} {
+ global nparts;
+ global max_nweight;
+ global COARSEN_TO;
+
+ upvar $node_weight nweight;
+ upvar $node_neighbour nneighbour;
+ upvar $edge_array earray;
+ upvar $edge_weight eweight;
+ upvar $tpwgts2 tpwgts;
+
+ debug "MatchRm... $nvertices";
+
+ array set cnweight {};
+ array set nmap {};
+ array set nmatch {};
+
+ set matched "";
+ set cnvertices 0;
+
+ #permute the nodes
+ set permList [makePermList $nvertices ];
+ #array with random permuted nodes
+ array set permArray $permList;
+ set sum_nweight [sum_array $nvertices nweight];
+ set max_nweight [expr {1.5 * $sum_nweight / 20}];
+
+ #match the vertices
+ for {set i 0} {[llength $matched] < $nvertices} {incr i} {
+ set unmatched_node $permArray($i);
+
+ if {[lsearch $matched $unmatched_node] == -1} then {
+ lappend matched $unmatched_node;
+ set matched_ngb 0;
+ set max_eweight 0;
+ debug "matched=$matched";
+
+ #node has an unmatched, passend neighbor, and is matched with it
+if {$nvertices > $COARSEN_TO && [info exists nneighbour($unmatched_node)]} then {
+ foreach ngb $nneighbour($unmatched_node) {
+ if {[lsearch $matched $ngb] == -1 && [expr {$nweight($i) + $nweight($ngb)}] < $max_nweight && $max_eweight < $eweight([getEdgeBetween $unmatched_node $ngb earray])} then {
+ set matched_ngb 1;
+ lappend matched $ngb;
+ set max_eweight $eweight([getEdgeBetween $unmatched_node $ngb earray]);
+ set nmatch($unmatched_node) $ngb;
+ set nmatch($ngb) $unmatched_node;
+ set nmap($unmatched_node) $cnvertices; #potrebno za uncoarse
+ set nmap($ngb) $cnvertices; #potrebno za uncoarse
+ set cnweight($cnvertices) [expr {$nweight($unmatched_node) + $nweight($ngb)}];
+
+ }
+ }
+}
+ #node is matched with itself
+ if {$matched_ngb == 0} then {
+ set nmatch($unmatched_node) $unmatched_node;
+ set nmap($unmatched_node) $cnvertices;
+ set cnweight($cnvertices) $nweight($unmatched_node);
+ }
+ debug "nmap($unmatched_node)=$nmap($unmatched_node),$unmatched_node,$nmatch($unmatched_node) ";
+ incr cnvertices;
+ }
+ }
+
+ array set cnneighbour {};
+ set used_nodes "";
+ set cngb 0;
+
+ #coarse graph
+ for {set i 0} {[llength $used_nodes] < $cnvertices} {incr i} {
+ set parent1 $i;
+ set parent2 $nmatch($i);
+ set cnode $nmap($parent1);
+
+ if {[lsearch $used_nodes $cnode] > -1} {
+ continue;
+ }
+ lappend used_nodes $cnode;
+
+ #save all neighbours from the 2 parent nodes to their coarse node
+ set temp_ngb_list "";
+if {[info exists nneighbour($parent1)] && [info exists nneighbour($parent2)]} then {
+ # take all neighbours from "parent"-nodes
+ set all_neighbours [concat $nneighbour($parent1) $nneighbour($parent2)];
+ foreach ngb $all_neighbours {
+ set ngb_map $nmap($ngb);
+ #don't save duplicates
+ if {$ngb_map == $cnode} then {
+ continue;
+ }
+
+ if {[lsearch $temp_ngb_list $ngb_map] == -1} then {
+ lappend temp_ngb_list $ngb_map;
+ }
+ }
+
+ set cnneighbour($cnode) $temp_ngb_list;
+ }
+ }
+
+############## EDGES ###############
+
+ array set cearray {};
+ set cnum_edges 0;
+
+ #coarse edges
+ for {set i 0} {$i < [array size earray]} {incr i} {
+ set twin 0;
+ set node1 [lindex $earray($i) 0];
+ set node2 [lindex $earray($i) 1];
+
+ set cnode1 $nmap($node1);
+ set cnode2 $nmap($node2);
+
+ if {$cnode1 == $cnode2} then { ;#edge between two coarsed nodes disappears
+ } else {
+ #check if the link already exists in coarsed graph
+ for {set j 0} {$j < [array size cearray]} {incr j} {
+ if {$cearray($j) == "$cnode1 $cnode2" || $cearray($j) == "$cnode2 $cnode1"} then {
+ set twin 1;
+ #add the edge weight to the weight of coarsed edge
+ incr ceweight($j) $eweight($i);
+ break;
+ }
+ }
+ #if its no double edge, make a new edge in coarsed graph
+ if {$twin == 0} then {
+ set cearray($cnum_edges) "$cnode1 $cnode2";
+ set ceweight($cnum_edges) $eweight($i);
+ incr cnum_edges;
+ }
+ }
+ }
+
+ #repeat coarsening
+ if {$cnvertices > $COARSEN_TO && $nvertices > $cnvertices} {
+ coarseGraph $cnvertices cnweight cnneighbour cearray ceweight tpwgts;
+ } else {
+ #enough coarsed, partition the coarsest graph
+ makePartitions $cnvertices cnweight cnneighbour cearray ceweight tpwgts;
+ }
+ debug "match Over !!!";
+
+ #balance, refine and uncoarse the graph
+ balance $cnvertices cnneighbour cnweight cearray ceweight tpwgts 4;
+ FMRefinement $cnvertices cnneighbour cnweight cearray ceweight tpwgts 4;
+ project2waypartition $nvertices earray eweight nneighbour nmap nweight $cnvertices cnweight;
+}
+
+#****f* graph_partitioning.tcl/makePartitions
+# NAME
+# makePartitions -- initial partitioning
+# SYNOPSIS
+# makePartitions $nvertices node_weight node_neighbour edge_array edge_weight $tpwgts2
+# FUNCTION
+# Initial partitioning of the coarsest graph.
+# INPUTS
+# * nvertices -- number of vertices
+# * node_weight -- array of node weights
+# * node_neighbour -- array of node neighbours
+# * edge_array -- array of edges
+# * edge_weight -- array of edge weights
+# * tpwgts2 -- array of each partition size of the graph
+#****
+proc makePartitions {nvertices node_weight node_neighbour edge_array edge_weight tpwgts2} {
+ global COARSEN_TO;
+ global part_pwgts;
+ global part_partition;
+ global part_boundary;
+ global part_id;
+ global part_ed;
+ global part_mincut;
+
+ upvar $node_weight nweight;
+ upvar $node_neighbour nneighbour;
+ upvar $edge_array earray;
+ upvar $edge_weight eweight;
+ upvar $tpwgts2 tpwgts;
+
+ #the sum of weight of all neighbours
+ array set wsum_ngbs {};
+ array set bestpartition {};
+ array set part_partition {};
+ array set visited {};
+ array set part_ed {};
+ array set part_id {};
+
+ set part_mincut 0;
+
+ #calculate the sum of all edge-weights in graph
+ set wsum 1;
+ for {set i 0} {$i < $nvertices} {incr i} {
+ if {[info exists nneighbour($i)]} then {
+ foreach ngb $nneighbour($i) {
+ set e [getEdgeBetween $i $ngb earray];
+ incr wsum $eweight($e);
+ }
+ }
+ set bestpartitions($i) -1;
+ }
+ set bestcut $wsum;
+
+ if {$nvertices <= $COARSEN_TO} then {
+ set nbfs 4;
+ } else {
+ set nbfs 9;
+ }
+
+ while {$nbfs > 1} {
+ incr nbfs -1;
+ set part_boundary "";
+ # set all vertices to partition 1, and for all vertices to not visited
+ for {set i 0} {$i < $nvertices} {incr i} {
+ set part_partition($i) 1;
+ set visited($i) 0;
+ }
+
+ set part_pwgts(0) 0;
+ set part_pwgts(1) [expr {$tpwgts(0) + $tpwgts(1)}];
+
+ # Breadth - first algorithm
+ set queue {};
+ set start_node [expr {int(rand() * $nvertices)}];
+ set queue $start_node;
+ set visited($start_node) 1;
+
+ while {1} {
+ #graph is disconnected
+ if {[llength $queue] == 0} {
+ set more_left 0;
+ for {set n 0} {$n < $nvertices} {incr n} {
+ if {$visited($n) == 0} then {
+ set queue $n;
+ set visited($n) 1;
+ set more_left 1;
+ break;
+ }
+ }
+ if {$more_left == 0} then {
+ debug "no more left!";
+ break;
+ }
+ }
+ # take the first node from queue
+ set i [lindex $queue 0];
+ set queue [lreplace $queue 0 0];
+
+ if {$part_pwgts(0) > 0 && [expr {$part_pwgts(1) - $nweight($i)}] < $tpwgts(1)} then {
+ debug "preveliko, dalje...";
+ continue;
+ }
+ #change partition of i from 1 to 0
+ set part_partition($i) 0;
+
+ #update the partitions weight
+ set part_pwgts(0) [expr {$part_pwgts(0) + $nweight($i)}];
+ set part_pwgts(1) [expr {$part_pwgts(1) - $nweight($i)}];
+
+ #partition is bigger than it should be
+ if {$part_pwgts(1) <= $tpwgts(1)} then {
+ debug "tpwgts(1)=$tpwgts(1)";
+ break;
+ }
+
+ #search for the not visited neighbors, and attach them to the queue
+ if {[info exists nneighbour($i)]} then {
+ foreach ngb $nneighbour($i) {
+ if {$visited($ngb) == 0} {
+ set visited($ngb) 1;
+ lappend queue $ngb;
+ };#if
+ };#foreach
+ }
+
+ };#while
+
+
+ array set pwgts2 {};
+ set pwgts2(0) 0;
+ set pwgts2(1) 0;
+
+ #calculate ID and ED for each vertex
+ for {set i 0} {$i < $nvertices} {incr i} {
+ set part_id($i) 0;
+ set part_ed($i) 0;
+
+ set curr_partition $part_partition($i);
+ incr pwgts2($curr_partition) $nweight($i);
+
+ # calculates the sum of the edge weights of the adjacent vertices of i
+ if {[info exists nneighbour($i)]} then {
+ foreach ngb $nneighbour($i) {
+ if {$curr_partition == $part_partition($ngb)} then {
+ # vertice in the same partition
+ incr part_id($i) $eweight([getEdgeBetween $i $ngb earray]);
+ } else {
+ # vertice in a different partition
+ incr part_ed($i) $eweight([getEdgeBetween $i $ngb earray]);
+ };#if-else
+ };#foreach
+
+ if {$part_ed($i) > 0 || [llength $nneighbour($i)] == 0} then { ;#vanjski node, ili nema susjeda
+ incr part_mincut $part_ed($i);
+ lappend part_boundary $i;
+ }
+ }
+ }
+
+ set part_mincut [expr {$part_mincut / 2}];
+ set sum 0;
+ debug "init part: part_mincut=$part_mincut";
+ for {set k 0} {$k < $nvertices} {incr k} {
+ incr sum $nweight($k);
+ }
+
+ if {$pwgts2(0) + $pwgts2(1) != $sum} {
+ error "refine: partition weigth wrong!";
+ }
+
+ #balance the graph
+ balance $nvertices nneighbour nweight earray eweight tpwgts 4;
+ # edge - based FM refinement
+ FMRefinement $nvertices nneighbour nweight earray eweight tpwgts 4;
+
+ #save the partitions if better then current saved
+ if {$bestcut > $part_mincut} then {
+ set bestcut $part_mincut;
+ set bestboundary $part_boundary;
+ set bestpwgts(0) $part_pwgts(0);
+ set bestpwgts(1) $part_pwgts(1);
+ for {set i 0} {$i < $nvertices} {incr i} {
+ set bestpartitions($i) $part_partition($i); #save the best partitions
+ set bestid($i) $part_id($i);
+ set bested($i) $part_ed($i);
+
+ }
+ if {$part_mincut == 0} then {
+ break;
+ }
+
+ }
+ }
+
+ #save to globals the best found partitions
+ set part_mincut $bestcut;
+ set part_boundary $bestboundary;
+ set part_pwgts(0) $bestpwgts(0);
+ set part_pwgts(1) $bestpwgts(1);
+ for {set i 0} {$i < $nvertices} {incr i} {
+ set part_partition($i) $bestpartitions($i);
+ set part_id($i) $bestid($i);
+ set part_ed($i) $bested($i);
+ }
+}
+
+#****f* graph_partitioning.tcl/balance
+# NAME
+# balance
+# SYNOPSIS
+# balance $nvertices node_neighbour node_weight edge_array edge_weight tpart_wgts $npasses
+# FUNCTION
+# Procedure swaps vertices between two partitions, to make the partitions balanced.
+# The vertices from the bigger partition
+# are swapped to the smaller partition. After swapping, the ed and id arrays are
+# for all neighbor vertices updated.
+# INPUTS
+# * nvertices -- number of vertices
+# * node_weight -- array of node weights
+# * node_neighbour -- array of node neighbours
+# * edge_array -- array of edges
+# * edge_weight -- array of edge weights
+# * tpart_wgts -- array of each partition size of the graph
+# * npasses -- number of swap tries
+#****
+proc balance {nvertices node_neighbour node_weight edge_array edge_weight tpart_wgts npasses} {
+ global part_pwgts;
+ global part_partition;
+ global part_boundary;
+ global part_id;
+ global part_ed;
+ global part_mincut;
+
+ upvar $node_neighbour nneighbour;
+ upvar $node_weight nweight;
+ upvar $edge_array earray;
+ upvar $edge_weight eweight;
+ upvar $tpart_wgts tpwgts;
+
+
+ set move_from -1;
+ set move_to -1;
+
+ #there is no boundary nodes
+ if {[llength $part_boundary] == 0} then {
+ return;
+ }
+
+ # chose the the bigger partition to move from
+ if {($tpwgts(0) - $part_pwgts(0)) < ($tpwgts(1) - $part_pwgts(1))} then {
+ set move_from 0;
+ set move_to 1;
+ } else {
+ set move_from 1;
+ set move_to 0;
+ }
+
+ #prority queue
+ array set queue {};
+
+ #put all boundary nodes from move_from partition into queue
+ for {set i 0} {$i < [llength $part_boundary]} {incr i} {
+ set b [lindex $part_boundary $i];
+ if {$part_partition($b) == $move_from} then {
+ set b_gain [expr {$part_ed($b) - $part_id($b)}];
+ push queue($move_from) "$b $b_gain";
+ }
+ }
+
+ # set all vertices free to move
+ for {set i 0} {$i < $nvertices} {incr i} {
+ set moved($i) -1;
+ }
+
+
+ for {set pass 0} {$pass < $npasses} {incr pass} {
+ # doesn't exists, if nodes are not connected
+ if {![info exists queue($move_from)]} then {
+ break;
+ }
+
+ # chose the node with the highest gain
+ set hi_gain [pop queue($move_from)];
+ if {$hi_gain == ""} {
+ debug "queue($move_from) empty.";
+ break;
+ }
+
+ #if the size of the partition, in which the node should be moved is to small
+ #dont move it
+ if {$part_pwgts($move_to) + $nweight($hi_gain) > $tpwgts($move_to)} then {
+ break;
+ }
+
+ #update partitions weight
+ incr part_pwgts($move_from) [expr {-$nweight($hi_gain)}];
+ incr part_pwgts($move_to) $nweight($hi_gain);
+
+ set part_partition($hi_gain) $move_to;
+
+ #all the "extern" links are now "intern", and umgekehrt
+ set tmp $part_ed($hi_gain);
+ set part_ed($hi_gain) $part_id($hi_gain);
+ set part_id($hi_gain) $tmp;
+
+ #if it's no more boundary node
+ if {$part_ed($hi_gain) == 0} then {
+ #remove it from the bndy list
+ set bndy [lreplace $part_boundary [lsearch $part_boundary $hi_gain] [lsearch $part_boundary $hi_gain]];
+ }
+
+ #update part_id, part_ed values
+ # go throught all neighbours of node "hi_gain"
+ if {[info exists nneighbour($hi_gain)]} then {
+ foreach ngb $nneighbour($hi_gain) {
+ set is_bnd_node $part_ed($ngb); #if the value is > 0, it is a boundary node
+ set edgeBetween [getEdgeBetween $hi_gain $ngb earray];
+ if {$part_partition($ngb) == $move_to} then {
+ incr part_id($ngb) $eweight($edgeBetween);
+ incr part_ed($ngb) -$eweight($edgeBetween);
+ } else {
+ incr part_ed($ngb) $eweight($edgeBetween);
+ incr part_id($ngb) -$eweight($edgeBetween);
+ }
+
+ if {$is_bnd_node > 0} then {
+ #node "ngb" is no longer an boundary node
+ if {$part_ed($ngb) == 0} then {
+ #remove it from the boundary list
+ set part_boundary [lreplace $part_boundary [lsearch $part_boundary $ngb] [lsearch $part_boundary $ngb]];
+ if {$moved($ngb) == -1 && ($part_partition($ngb)==$move_from)} then {
+ #if not moved -> remove it from the queue
+ removeFromQueue queue($part_partition($ngb)) $ngb;
+ }
+ } else {
+ #if it wasn't been moved, update it in queue
+ if {$moved($ngb) == -1 && ($part_partition($ngb) == $move_from)} then {
+ removeFromQueue queue($part_partition($ngb)) $ngb;
+ set new_gain [expr {$part_ed($ngb) - $part_id($ngb)}];
+ push queue($part_partition($ngb)) "$ngb $new_gain";
+ }
+ }
+ } else { ;#puts "not boundary node: $ngb";
+ if {$part_ed($ngb) > 0} then { ;#new boundary node
+ lappend part_boundary $ngb;
+ #add it to the queue
+ if {$moved($ngb) == -1} then {
+ push queue($part_partition($ngb)) "$ngb [expr {$part_ed($ngb) - $part_id($ngb)}]";
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+#****f* graph_partitioning.tcl/FMRefinement
+# NAME
+# FMRefinement
+# SYNOPSIS
+# FMRefinement $nvertices node_neighbour node_weight edge_array edge_weight tpart_wgt $npasses
+# FUNCTION
+# Procedure swaps the vertices between two partition, to reduce the edge-cut.
+# INPUTS
+# * nvertices -- number of vertices
+# * node_weight -- array of node weights
+# * node_neighbour -- array of node neighbours
+# * edge_array -- array of edges
+# * edge_weight -- array of edge weights
+# * tpart_wgts -- array of each partition size of the graph
+# * npasses -- number of swap tries
+#****
+proc FMRefinement {nvertices node_neighbour node_weight edge_array edge_weight tpart_wgt npasses} {
+ global part_pwgts;
+ global part_partition;
+ global part_boundary;
+ global part_id;
+ global part_ed;
+ global part_mincut;
+
+ upvar $node_weight nweight;
+ upvar $node_neighbour nneighbour;
+ upvar $edge_array earray;
+ upvar $edge_weight eweight;
+ upvar $tpart_wgt tpwgts;
+
+
+ array set queue {};
+ array set bak_id {};
+ array set bak_ed {};
+ array set bak_part {};
+ array set bak_pwgts {};
+
+ set bak_bndy -1;
+ set orig_diff [expr {abs ($tpwgts(0) - $part_pwgts(0))}];
+ set avg1 [expr {($part_pwgts(0) + $part_pwgts(1)) / 20}];
+ set avg2 [expr {2 * ($part_pwgts(0) + $part_pwgts(1)) / $nvertices}];
+ if {$avg1 < $avg2} then {
+ set avg_pwgt $avg1;
+ } else {
+ set avg_pwgt $avg2;
+ }
+
+ set swap_limit [expr {int(0.01 * $nvertices)}];
+ if {$swap_limit < 15} then {
+ set swap_limit 15;
+ }
+
+ #pamti najbolju kombinaciju
+ set bak_bndy $part_boundary;
+ for {set i 0} {$i < $nvertices} {incr i} {
+ set bak_part($i) $part_partition($i);
+ set bak_id($i) $part_id($i);
+ set bak_ed($i) $part_ed($i);
+ }
+ set bak_pwgts(0) $part_pwgts(0);
+ set bak_pwgts(1) $part_pwgts(1);
+
+
+ # set all vertices free to move
+ for {set i 0} {$i < $nvertices} {incr i} {
+ set moved($i) -1;
+ }
+
+ for {set pass 0} {$pass < $npasses} {incr pass} {
+ #set all variables to their's initial values
+ set bndy $part_boundary;
+ set newcut $part_mincut;
+ set mincut $part_mincut;
+ set min_diff [expr {abs ($tpwgts(0) - $part_pwgts(0))}];
+
+ for {set i 0} {$i < 2} {incr i} {
+ set pwgts($i) $part_pwgts($i);
+ set queue($i) "";
+ }
+
+ for {set i 0} {$i < $nvertices} {incr i} {
+ set part($i) $part_partition($i);
+ set id($i) $part_id($i);
+ set ed($i) $part_ed($i);
+ }
+
+ # insert boundary nodes in the priority queue
+ set permList [makePermArray bndy];
+ array set permArray $permList;
+ set mincutorder -1;
+
+ for {set i 0} {$i < [array size permArray]} {incr i} {
+ set node $permArray($i);
+ #calculate the node's gain
+ set node_gain [expr {$ed($node) - $id($node)}];
+ # push in the queue 0 or 1 (depends in which partition node is) the node and its gain
+ push queue($part($node)) "$node $node_gain";
+ };#foreach
+
+ # chose the best-gain move
+ for {set nswaps 0} {$nswaps < $nvertices} {incr nswaps} {
+ debug "nswaps=$nswaps";
+ # chose the node from the bigger partition to move to the smaller
+ if {($tpwgts(0) - $pwgts(0)) < ($tpwgts(1) - $pwgts(1))} then {
+ set move_from 0;
+ set move_to 1;
+ } else {
+ set move_from 1;
+ set move_to 0;
+ }
+
+ # chose the node with the highest gain
+ set hi_gain [pop queue($move_from)];
+ if {$hi_gain == ""} {
+ break;
+ }
+
+ # update the cut and partitions weight
+ set newcut [expr {$newcut - $ed($hi_gain) + $id($hi_gain)}];
+ incr pwgts($move_from) [expr {-$nweight($hi_gain)}];
+ incr pwgts($move_to) $nweight($hi_gain);
+
+ #check if the new cut better is than the old one
+ set new_diff [expr {abs ($tpwgts(0) - $pwgts(0))}];
+ if {($newcut < $mincut) && ($new_diff <= $orig_diff + $avg_pwgt) ||
+ ($newcut == $mincut) && ($new_diff < $min_diff)} then {
+ set mincutorder $nswaps;
+ set mincut $newcut;
+ set min_diff $new_diff;
+ } elseif {$nswaps - $mincutorder > $swap_limit} {
+ incr newcut [expr {$ed($hi_gain) - $id($hi_gain)}];
+ incr pwgts($move_to) [expr {-$nweight($hi_gain)}];
+ incr pwgts($move_from) $nweight($hi_gain);
+ break;
+ }
+
+ #move node to the other partion
+ set part($hi_gain) $move_to;
+ set moved($hi_gain) $nswaps;
+ set swaps($nswaps) $hi_gain;
+
+ #all the "extern" links are now "intern", and reverse
+ set tmp $ed($hi_gain);
+ set ed($hi_gain) $id($hi_gain);
+ set id($hi_gain) $tmp;
+
+ #if it's no more boundary node
+ if {$ed($hi_gain) == 0} then {
+ #remove it from the bndy list
+ set bndy [lreplace $bndy [lsearch $bndy $hi_gain] [lsearch $bndy $hi_gain]];
+ }
+
+ #update ID, ED values
+ # go throught all neighbours of node "hi_gain"
+ if {[info exists nneighbour($hi_gain)]} then {
+ foreach ngb $nneighbour($hi_gain) {
+ set is_bnd_node $ed($ngb); #if the value is > 0, it is a boundary node
+ set edgeBetween [getEdgeBetween $hi_gain $ngb earray];
+ if {$part($ngb) == $move_to} then {
+ incr id($ngb) $eweight($edgeBetween);
+ incr ed($ngb) -$eweight($edgeBetween);
+ } else {
+ incr ed($ngb) $eweight($edgeBetween);
+ incr id($ngb) -$eweight($edgeBetween);
+ }
+ if {$is_bnd_node > 0} then {
+ #node "ngb" is no longer an boundary node
+ if {$ed($ngb) == 0} then {
+ #remove it from the boundary list
+ set bndy [lreplace $bndy [lsearch $bndy $ngb] [lsearch $bndy $ngb]];
+ if {$moved($ngb) == -1} then {
+ #if not moved -> remove it from the queue
+ removeFromQueue queue($part($ngb)) $ngb;
+ }
+ } else {
+ #if it wasn't been moved, update it in queue
+ if {$moved($ngb) == -1} then {
+ removeFromQueue queue($part($ngb)) $ngb;
+ set new_gain [expr {$ed($ngb) - $id($ngb)}];
+ push queue($part($ngb)) "$ngb $new_gain";
+ }
+ }
+ } else { ;#puts "not boundary node: $ngb";
+ if {$ed($ngb) > 0} then { ;#new boundary node
+ lappend bndy $ngb;
+ #add it to the queue
+ if {$moved($ngb) == -1} then {
+ push queue($part($ngb)) "$ngb [expr {$ed($ngb) - $id($ngb)}]";
+ }
+ }
+ }
+ }
+ }
+ if {$mincutorder > -1} then {
+ set mincutorder -1;
+ set mincut $newcut;
+ set bak_bndy $bndy;
+ for {set j 0} {$j < $nvertices} {incr j} {
+ set bak_id($j) $id($j);
+ set bak_ed($j) $ed($j);
+ set bak_part($j) $part($j);
+ }
+ set bak_pwgts(0) $pwgts(0);
+ set bak_pwgts(1) $pwgts(1);
+ }
+ };#inner loop
+ };#outer loop
+
+ #save the best partitions
+ set part_mincut $mincut;
+ set part_boundary $bak_bndy;
+ for {set i 0} {$i < $nvertices} {incr i} {
+ set part_partition($i) $bak_part($i);
+ set part_id($i) $bak_id($i);
+ set part_ed($i) $bak_ed($i);
+ }
+ set part_pwgts(0) $bak_pwgts(0);
+ set part_pwgts(1) $bak_pwgts(1);
+}
+
+#****f* graph_partitioning.tcl/project2waypartition
+# NAME
+# project2waypartition
+# SYNOPSIS
+# project2waypartition $nvertices edge_array edge_weight node_neighbour node_map node_weight cnv $cnvw
+# FUNCTION
+# The partitions from the coarserer graph propagates one level up.
+# INPUTS
+# * nvertices -- number of vertices
+# * node_weight -- array of node weights
+# * node_neighbour -- array of node neighbours
+# * edge_array -- array of edges
+# * edge_weight -- array of edge weights
+# * node_map -- array with mappings of nodes from parent to child (coarse) graph
+# * cnv -- number of vertices in coarse graph
+# * cnvw -- array of node weights in coarse graph
+#****
+proc project2waypartition {nvertices edge_array edge_weight node_neighbour node_map node_weight cnv cnvw} {
+ global part_pwgts;
+ global part_mincut;
+ global part_partition;
+ global part_id;
+ global part_ed;
+ global part_boundary;
+
+ upvar $cnvw cnw;
+ upvar $node_weight nweight;
+ upvar $edge_array earray;
+ upvar $edge_weight eweight;
+ upvar $node_neighbour nneighbour;
+ upvar $node_map nmap;
+
+ array set part_ed {};
+ array set part_id {};
+ set part_boundary "";
+ array set pwgts2 {};
+
+ #sum the weight of nodes in finer graph
+ set n 0; set p 0;
+ for {set i 0} {$i < $nvertices} {incr i} {
+ incr n $nweight($i);
+ }
+ #sum the weight of nodes in coarsed graph
+ for {set i 0} {$i < $cnv} {incr i} {
+ incr p $cnw($i);
+ }
+
+ #get partition for each vertex in finer graph
+ for {set i 0} {$i < $nvertices} {incr i} {
+ set cnode $nmap($i); #get the node in coarsed graph which corresponse to the node in finer graph
+ set partition($i) $part_partition($cnode); #get it's partition too
+ set part_ed($i) 0;
+ set part_id($i) 0;
+ set pwgts2(0) 0;
+ set pwgts2(1) 0;
+}
+
+ #calculate ID and ED
+ for {set i 0} {$i < $nvertices} {incr i} {
+ if {![info exists nneighbour($i)] || [llength $nneighbour($i)] == 0} then {
+ lappend part_boundary $i;
+ } else {
+ foreach ngb $nneighbour($i) {
+ if {$partition($ngb) == $partition($i)} then {
+ incr part_id($i) $eweight([getEdgeBetween $i $ngb earray]); #mogu uzeti stare tezine - posto iste
+ #incr id($ngb) $eweight([...]);
+ } else {
+ incr part_ed($i) $eweight([getEdgeBetween $i $ngb earray]);
+ #incr ed($ngb) $eweight([...]);
+ }
+ };#foreach
+
+ if {[expr $part_ed($i) > 0 || [llength $nneighbour($i)] == 0]} then {
+ lappend part_boundary $i;
+ }
+ }
+ set part_partition($i) $partition($i);
+ incr pwgts2($partition($i)) $nweight($i);
+ };#for
+}
+
+#****f* graph_partitioning.tcl/splitGraph
+# NAME
+# splitGraph
+# SYNOPSIS
+# splitGraph $nvertices node_neighbour node_weight edge_array edge_weight snode_neighbour snode_weight sedge_array sedge_weight snode_map sn_vtxs sn_edges snode_map_help
+# FUNCTION
+# Divides the graph into two parts, one with nodes in partition 0 and the other with
+# the nodes in the partition 1.
+# INPUTS
+# * nvertices -- number of vertices
+# * node_weight -- array of node weights
+# * node_neighbour -- array of node neighbours
+# * edge_array -- array of edges
+# * edge_weight -- array of edge weights
+# * snode_weight -- array of node weights of the split graph
+# * snode_neighbour -- array of node neighbours of the split graph
+# * sedge_array -- array of edges of the split graph
+# * sedge_weight -- array of edge weights of the split graph
+# * snode_map -- array with mappings of nodes from parent to child (coarse) graph
+# * sn_vtxs -- number of vertices of the split graph
+# * sn_edges -- number of edges of the split graph
+# * snode_map_help -- help variable, needed for later connecting, in this procedure disconnected graphs
+#****
+proc splitGraph {nvertices node_neighbour node_weight edge_array edge_weight snode_neighbour snode_weight sedge_array sedge_weight snode_map sn_vtxs sn_edges snode_map_help} {
+ global part_partition;
+
+ upvar $node_neighbour nneighbour;
+ upvar $node_weight nweight;
+ upvar $edge_array earray;
+ upvar $edge_weight eweight;
+ upvar $snode_neighbour snneighbour;
+ upvar $snode_weight snweight;
+ upvar $sedge_array searray;
+ upvar $sedge_weight seweight;
+ upvar $snode_map snmap;
+ upvar $snode_map_help snmap_h;
+ upvar $sn_vtxs snvtxs;
+ upvar $sn_edges snedges;
+
+ array set sum_np {};
+ set sum_np(0) 0;
+ set sum_np(1) 0;
+
+ array set auxn {};
+ array set auxw {};
+
+ #sets variables needed later for connecting the splited graph
+ for {set i 0} {$i < $nvertices} {incr i} {
+ set p $part_partition($i);
+ set snmap($p,$i) $snvtxs($p);
+ set snmap_h($p,$snvtxs($p)) $i;
+ incr snvtxs($p);
+ }
+
+ #split the graph
+ for {set i 0} {$i < $nvertices} {incr i} {
+ set p_i $part_partition($i);
+ set s_i $snmap($p_i,$i);
+ set sum 0;
+
+ if {[info exists nneighbour($i)]} then {
+ foreach ngb $nneighbour($i) {
+ set p $part_partition($ngb);
+ if {$p == $p_i} then {
+ set twin 0;
+ set sngb $snmap($p_i,$ngb);
+ lappend snneighbour($p_i,$s_i) $sngb;
+ for {set a 0} {$a < $snedges($p_i)} {incr a} {
+ if {$searray($p_i,$a) == "$s_i $sngb" || $searray($p_i,$a) == "$sngb $s_i"} then {
+ set twin 1;
+ break;
+ }
+ }
+ if {$twin == 0} then {
+ set searray($p_i,$snedges($p_i)) "$s_i $sngb";
+ set seweight($p_i,$snedges($p_i)) $eweight([getEdgeBetween $i $ngb earray]);
+ incr snedges($p_i);
+ }
+ incr sum $nweight($ngb);
+ } else {
+ incr sum [expr {-$nweight($ngb)}]
+ }
+
+ };#foreach
+}
+ set snweight($p_i,$s_i) $nweight($i);
+ set sadjwgtsum($p_i,$s_i)) $sum;
+ }
+
+}
+
+
+
+
+#####################PROCEDURES ##########################
+
+#****f* graph_partitioning.tcl/sum_array
+# NAME
+# sum_array
+# SYNOPSIS
+# sum_array $end arr
+# FUNCTION
+# Function sum the elements from the array.
+# INPUTS
+# * end -- until what index to sum
+# * arr -- array of numbers
+# RESULT
+# * sum -- the sum of first "end" numbers
+#****
+proc sum_array {end arr} {
+ upvar 1 $arr a;
+ set sum 0.0;
+
+ for {set i 0} {$i < $end} {incr i} {
+ set sum [expr {$sum + $a($i)}];
+ }
+
+ return $sum;
+}
+
+#****f* graph_partitioning.tcl/mult_array
+# NAME
+# mult_array
+# SYNOPSIS
+# mult_array $start $end arr $prod
+# FUNCTION
+# Procedure multiplyes the elements between start and end position in the array
+# with the number prod
+# INPUTS
+# * start -- the position in array from where to start multipling
+# * end -- the position in array until which to multiply
+# * arr -- array
+# * prod --
+# RESULT
+# *
+#****
+proc mult_array {start end arr prod} {
+ upvar 1 $arr a;
+ for {set i $start} {$i < $end} {incr i} {
+ set a($i) [expr {$a($i) * $prod }];
+ }
+}
+
+#****f* graph_partitioning.tcl/makePermList
+# NAME
+# makePermList -- make permuted list
+# SYNOPSIS
+# makePermList $num
+# FUNCTION
+# Function makes a new list with num elements, and randomizes the list.
+# INPUTS
+# * num -- number of elements in list
+# RESULT
+# * list -- permuted list
+#****
+proc makePermList {num} {
+ array set permList "";
+
+ for {set i 0} {$i < $num} {incr i} {
+ set permList($i) $i;
+ }
+
+ if {$num > 4} {
+ for {set i 0} {$i < $num} {incr i 16} {
+ set rand1 [expr {int(rand() * ($num - 4))}]
+ set rand2 [expr {int(rand() * ($num - 4))}]
+
+ swap permList $rand1 $rand2;
+ swap permList [expr $rand1+1] [expr $rand2+1];
+ swap permList [expr $rand1+2] [expr $rand2+2];
+ swap permList [expr $rand1+3] [expr $rand2+3];
+ }
+ }
+
+ return [array get permList];
+}
+
+#****f* graph_partitioning.tcl/makePermArray
+# NAME
+# makePermArray -- make permuted array
+# SYNOPSIS
+# makePermArray arr
+# FUNCTION
+# Function randomizes the elements in the array.
+# INPUTS
+# * arr -- array to randomize
+# RESULT
+# * list -- permuted list
+#****
+proc makePermArray {arr} {
+ upvar $arr a;
+ array set permList "";
+ set a_size [llength $a];
+ for {set i 0} {$i < $a_size} {incr i} {
+ set permList($i) [lindex $a $i];
+ }
+
+ if {$a_size > 4} {
+ for {set i 0} {$i < $a_size} {incr i 16} {
+ set rand1 [expr {int(rand() * ($a_size - 4))}]
+ set rand2 [expr {int(rand() * ($a_size - 4))}]
+
+ swap permList $rand1 $rand2;
+ swap permList [expr $rand1+1] [expr $rand2+1];
+ swap permList [expr $rand1+2] [expr $rand2+2];
+ swap permList [expr $rand1+3] [expr $rand2+3];
+ }
+ }
+
+ return [array get permList];
+}
+
+#****f* graph_partitioning.tcl/swap
+# NAME
+# swap
+# SYNOPSIS
+# swap permArray $idx1 $idx2
+# FUNCTION
+# Procedure swapps two elements in the array.
+# INPUTS
+# * permList -- array
+# * idx1 -- index of the first element
+# * idx2 -- index of the second element
+#****
+proc swap {permArray idx1 idx2} {
+ upvar $permArray rarray;
+
+ set temp $rarray($idx1);
+ set rarray($idx1) $rarray($idx2);
+ set rarray($idx2) $temp;
+}
+
+#****f* graph_partitioning.tcl/getEdgeBetween
+# NAME
+# getEdgeBetween -- get edge between
+# SYNOPSIS
+# getEdgeBetween $node1 $node2 edge_array
+# FUNCTION
+# Function searches for an edge connecting the two nodes.
+# INPUTS
+# * node1 -- first node id
+# * node2 -- second node id
+# * edge_array -- array of edges
+# RESULT
+# * i -- index of the edge connecting the nodes, or null
+#****
+proc getEdgeBetween {node1 node2 edge_array} {
+ upvar $edge_array earray;
+ for {set i 0} {$i < [array size earray]} {incr i} {
+ if {$earray($i) == "$node1 $node2" || $earray($i) == "$node2 $node1"} then {
+ return $i;
+ };#if
+ };#foreach
+}
+
+#****f* graph_partitioning.tcl/push
+# NAME
+# push
+# SYNOPSIS
+# push
+# FUNCTION
+# Alias for the command lappend.
+#****
+interp alias {} push {} lappend
+
+#****f* graph_partitioning.tcl/pop
+# NAME
+# pop
+# SYNOPSIS
+# pop queue
+# FUNCTION
+# Returns the element from the queue with the highest priority.
+# INPUTS
+# * queue_name -- array
+# RESULT
+# * hi_elem -- element from the array
+#****
+proc pop {queue_name} {
+ upvar 1 $queue_name queue;
+
+ #sort items after priority
+ set queue [lsort -integer -decreasing -index 1 $queue];
+ set hi_ [lindex $queue 0];
+ set hi_elem [lindex $hi_ 0];
+ set queue [lrange $queue 1 end];
+ return $hi_elem;
+}
+
+#****f* graph_partitioning.tcl/removeFromQueue
+# NAME
+# removeFromQueue -- remove from the queue
+# SYNOPSIS
+# removeFromQueue queue_name $node
+# FUNCTION
+# Removes the node from the queue.
+# INPUTS
+# * queue_name -- array
+# * node -- node id
+#****
+proc removeFromQueue {queue_name node } {
+ upvar 1 $queue_name queue;
+
+ foreach q $queue {
+ set n [lindex $q 0];
+ if {$n == $node} then {
+ set node_idx [lsearch $queue $q];
+ set queue [lreplace $queue $node_idx $node_idx];
+ }
+ }
+}
+
+
+############ GLOBALS
+
+set debug 0;
+array set tpwgts {};
+set nparts 0;
+set finalcut 0;
+set COARSEN_TO 20;
+array set pwgts {};
+set minPartWeight 0;
+set split_list "";
+array set finalpartition {};
+set part_boundary "";
+array set part_partition {};
+array set part_id {};
+array set part_ed {};
+array set part_pwgts {};
+set part_mincut 0;