@@ -1743,23 +1743,10 @@ void ClusteringEngine::mergeChildrenBelowThresholds(
17431743 }
17441744 // Firstly we perform Type 1 merge
17451745 for (int i = 0 ; i < num_small_children; i++) {
1746- const int cluster_id = small_children[i]->getCloseCluster (
1747- small_children_ids, tree_->min_net_count_for_connection );
1748- debugPrint (
1749- logger_,
1750- MPL,
1751- " multilevel_autoclustering" ,
1752- 1 ,
1753- " Candidate cluster: {} - {}" ,
1754- small_children[i]->getName (),
1755- (cluster_id != -1 ? tree_->maps .id_to_cluster [cluster_id]->getName ()
1756- : " " ));
1757- if (cluster_id != -1
1758- && !tree_->maps .id_to_cluster [cluster_id]->isIOCluster ()) {
1759- Cluster* close_cluster = tree_->maps .id_to_cluster [cluster_id];
1760- if (attemptMerge (close_cluster, small_children[i])) {
1761- cluster_class[i] = close_cluster->getId ();
1762- }
1746+ Cluster* close_cluster = findSingleWellFormedConnectedCluster (
1747+ small_children[i], small_children_ids);
1748+ if (close_cluster && attemptMerge (close_cluster, small_children[i])) {
1749+ cluster_class[i] = close_cluster->getId ();
17631750 }
17641751 }
17651752
@@ -1772,8 +1759,7 @@ void ClusteringEngine::mergeChildrenBelowThresholds(
17721759 continue ;
17731760 }
17741761
1775- if (small_children[i]->isSameConnSignature (
1776- *small_children[j], tree_->min_net_count_for_connection )) {
1762+ if (sameConnectionSignature (small_children[i], small_children[j])) {
17771763 if (attemptMerge (small_children[i], small_children[j])) {
17781764 cluster_class[j] = i;
17791765 } else {
@@ -1860,6 +1846,128 @@ void ClusteringEngine::mergeChildrenBelowThresholds(
18601846 " Finished merging clusters" );
18611847}
18621848
1849+ bool ClusteringEngine::sameConnectionSignature (Cluster* a, Cluster* b) const
1850+ {
1851+ std::vector<int > a_neighbors = findNeighbors (a, /* ignore */ b);
1852+ if (a_neighbors.empty ()) {
1853+ return false ;
1854+ }
1855+
1856+ std::vector<int > b_neighbors = findNeighbors (b, /* ignore */ a);
1857+ if (b_neighbors.size () != a_neighbors.size ()) {
1858+ return false ;
1859+ }
1860+
1861+ std::ranges::sort (a_neighbors);
1862+ std::ranges::sort (b_neighbors);
1863+
1864+ for (int i = 0 ; i < a_neighbors.size (); i++) {
1865+ if (a_neighbors[i] != b_neighbors[i]) {
1866+ return false ;
1867+ }
1868+ }
1869+
1870+ return true ;
1871+ }
1872+
1873+ std::vector<int > ClusteringEngine::findNeighbors (Cluster* target_cluster,
1874+ Cluster* ignored_cluster) const
1875+ {
1876+ std::vector<int > neighbors;
1877+ const ConnectionsMap& target_connections
1878+ = target_cluster->getConnectionsMap ();
1879+
1880+ for (const auto & [cluster_id, connection_weight] : target_connections) {
1881+ if (cluster_id == target_cluster->getId ()) {
1882+ logger_->error (MPL,
1883+ 53 ,
1884+ " Cluster {} is connected to itself." ,
1885+ target_cluster->getName ());
1886+ }
1887+
1888+ if (cluster_id == ignored_cluster->getId ()) {
1889+ continue ;
1890+ }
1891+
1892+ const float connection_ratio
1893+ = connection_weight / target_cluster->allConnectionsWeight ();
1894+
1895+ if (connection_ratio >= minimum_connection_ratio_) {
1896+ neighbors.push_back (cluster_id);
1897+ }
1898+ }
1899+
1900+ return neighbors;
1901+ }
1902+
1903+ bool ClusteringEngine::strongConnection (Cluster* a,
1904+ Cluster* b,
1905+ const float * connection_weight) const
1906+ {
1907+ if (a == b) {
1908+ logger_->error (
1909+ MPL,
1910+ 61 ,
1911+ " Attempt to evaluate if cluster {} has strong connection with itself." ,
1912+ a->getName ());
1913+ }
1914+
1915+ // Attention that we need to subtract the weight of the connection that
1916+ // we're evaluating otherwise we'll be taking it into account twice.
1917+ float total_weight = a->allConnectionsWeight () + b->allConnectionsWeight ();
1918+ float connection_ratio = 0.0 ;
1919+ if (connection_weight) {
1920+ total_weight -= *connection_weight;
1921+ connection_ratio = *connection_weight / total_weight;
1922+ } else {
1923+ const ConnectionsMap& a_connections = a->getConnectionsMap ();
1924+ auto itr = a_connections.find (b->getId ());
1925+
1926+ if (itr != a_connections.end ()) {
1927+ const float conn_weight = itr->second ;
1928+ total_weight -= conn_weight;
1929+ connection_ratio = conn_weight / total_weight;
1930+ }
1931+ }
1932+
1933+ return connection_ratio >= minimum_connection_ratio_;
1934+ }
1935+
1936+ Cluster* ClusteringEngine::findSingleWellFormedConnectedCluster (
1937+ Cluster* target_cluster,
1938+ const std::vector<int >& small_clusters_id_list) const
1939+ {
1940+ int number_of_close_clusters = 0 ;
1941+ Cluster* close_cluster = nullptr ;
1942+ const ConnectionsMap& target_connections
1943+ = target_cluster->getConnectionsMap ();
1944+
1945+ for (auto & [cluster_id, connection_weight] : target_connections) {
1946+ Cluster* candidate = tree_->maps .id_to_cluster .at (cluster_id);
1947+
1948+ if (candidate->isIOCluster ()) {
1949+ continue ;
1950+ }
1951+
1952+ if (strongConnection (target_cluster, candidate, &connection_weight)) {
1953+ auto small_child_found
1954+ = std::ranges::find (small_clusters_id_list, cluster_id);
1955+
1956+ // A small child is not well-formed, so we avoid them.
1957+ if (small_child_found == small_clusters_id_list.end ()) {
1958+ number_of_close_clusters++;
1959+ close_cluster = candidate;
1960+ }
1961+ }
1962+ }
1963+
1964+ if (number_of_close_clusters == 1 ) {
1965+ return close_cluster;
1966+ }
1967+
1968+ return nullptr ;
1969+ }
1970+
18631971bool ClusteringEngine::attemptMerge (Cluster* receiver, Cluster* incomer)
18641972{
18651973 // Cache incomer data in case it is deleted.
@@ -1910,6 +2018,8 @@ void ClusteringEngine::clearConnections()
19102018
19112019void ClusteringEngine::buildNetListConnections ()
19122020{
2021+ const float connection_weight = 1.0 ;
2022+
19132023 for (odb::dbNet* net : block_->getNets ()) {
19142024 if (!isValidNet (net)) {
19152025 continue ;
@@ -1929,11 +2039,9 @@ void ClusteringEngine::buildNetListConnections()
19292039 }
19302040 }
19312041
1932- bool net_has_io_pin = false ;
19332042 if (tree_->io_pads .empty ()) {
19342043 for (odb::dbBTerm* bterm : net->getBTerms ()) {
19352044 const int cluster_id = tree_->maps .bterm_to_cluster_id .at (bterm);
1936- net_has_io_pin = true ;
19372045
19382046 if (bterm->getIoType () == odb::dbIoType::INPUT) {
19392047 driver_cluster_id = cluster_id;
@@ -1945,13 +2053,12 @@ void ClusteringEngine::buildNetListConnections()
19452053
19462054 if (driver_cluster_id != -1 && !load_clusters_ids.empty ()
19472055 && load_clusters_ids.size () < tree_->large_net_threshold ) {
1948- const float weight = net_has_io_pin ? tree_->virtual_weight : 1.0 ;
19492056 Cluster* driver_cluster = tree_->maps .id_to_cluster .at (driver_cluster_id);
19502057
19512058 for (const int load_cluster_id : load_clusters_ids) {
19522059 if (load_cluster_id != driver_cluster_id) {
19532060 Cluster* load_cluster = tree_->maps .id_to_cluster .at (load_cluster_id);
1954- connect (driver_cluster, load_cluster, weight );
2061+ connect (driver_cluster, load_cluster, connection_weight );
19552062 }
19562063 }
19572064 }
@@ -2210,8 +2317,7 @@ void ClusteringEngine::classifyMacrosByConnSignature(
22102317 continue ;
22112318 }
22122319
2213- if (macro_clusters[i]->isSameConnSignature (
2214- *macro_clusters[j], tree_->min_net_count_for_connection )) {
2320+ if (sameConnectionSignature (macro_clusters[i], macro_clusters[j])) {
22152321 signature_class[j] = i;
22162322 }
22172323 }
@@ -2239,8 +2345,11 @@ void ClusteringEngine::classifyMacrosByInterconn(
22392345 if (interconn_class[i] == -1 ) {
22402346 interconn_class[i] = i;
22412347 for (int j = 0 ; j < macro_clusters.size (); j++) {
2242- if (macro_clusters[i]->hasMacroConnectionWith (
2243- *macro_clusters[j], tree_->min_net_count_for_connection )) {
2348+ if (macro_clusters[i] == macro_clusters[j]) {
2349+ continue ;
2350+ }
2351+
2352+ if (strongConnection (macro_clusters[i], macro_clusters[j])) {
22442353 if (interconn_class[j] != -1 ) {
22452354 interconn_class[i] = interconn_class[j];
22462355 break ;
0 commit comments