1646 lines
48 KiB
Tcl
1646 lines
48 KiB
Tcl
#
|
|
# Copyright 2007 Petra Schilhard.
|
|
#
|
|
# 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.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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 link_list finalpartition
|
|
|
|
global nparts tpwgts 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 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;
|
|
}
|
|
}
|
|
|
|
|
|
#****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;
|
|
}
|
|
}
|
|
}
|
|
|
|
#****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;
|