From: ana Date: Tue, 12 Jun 2007 10:28:43 +0000 (+0000) Subject: Added graph_partitioning.tcl, node_weights X-Git-Url: https://git.entuzijast.net/?a=commitdiff_plain;h=0ce7f66a95cfcd2af4dc0c5a3f7c4092c26f1c26;p=imunes.git Added graph_partitioning.tcl, node_weights Bug found by: Submitted by: Reviewed by: Approved by: Obtained from: --- diff --git a/graph_partitioning.tcl b/graph_partitioning.tcl new file mode 100644 index 0000000..140fbae --- /dev/null +++ b/graph_partitioning.tcl @@ -0,0 +1,1662 @@ +# $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; diff --git a/node_weights b/node_weights new file mode 100644 index 0000000..c9b1f25 --- /dev/null +++ b/node_weights @@ -0,0 +1,7 @@ +# $Id +6 +5 +4 +3 +2 +1