From 5a48ac3974bbb11c4a7cb66d17f10d6cbb9c8682 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 3 Dec 2018 22:05:54 +0100 Subject: [PATCH] WIP: some refactoring, joining of clusters by child clusters. Needs testing. --- src/db/db/dbHierNetworkProcessor.cc | 259 +++++++++++++----- src/db/db/dbHierNetworkProcessor.h | 61 +++-- .../unit_tests/dbHierNetworkProcessorTests.cc | 68 +++-- 3 files changed, 273 insertions(+), 115 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 53a1870e8..3ee81d50c 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -31,6 +31,8 @@ #include #include +#include +#include namespace db { @@ -304,7 +306,7 @@ template class DB_PUBLIC local_cluster; template local_clusters::local_clusters () - : m_needs_update (false) + : m_needs_update (false), m_next_dummy_id (0) { // .. nothing yet .. } @@ -315,22 +317,38 @@ void local_clusters::clear () m_needs_update = false; m_clusters.clear (); m_bbox = box_type (); + m_next_dummy_id = 0; } template const local_cluster & local_clusters::cluster_by_id (typename local_cluster::id_type id) const { - // by convention the ID is the index + 1 so 0 can be used as "nil" - tl_assert (id > 0 && id <= m_clusters.size ()); - return m_clusters.objects ().item (id - 1); + tl_assert (id > 0); + + if (id > m_clusters.size ()) { + + // dummy connectors are not real ones - they just carry an arbitrary + // ID. Still they need to be treated as empty ones. + static local_cluster empty_cluster; + return empty_cluster; + + } else { + + // by convention the ID is the index + 1 so 0 can be used as "nil" + return m_clusters.objects ().item (id - 1); + + } } template void local_clusters::remove_cluster (typename local_cluster::id_type id) { - tl_assert (id > 0 && id <= m_clusters.size ()); + if (id == 0 || id > m_clusters.size ()) { + return; + } + // TODO: get rid of this const_cast by providing "delete by index" // m_clusters.erase (id - 1) local_cluster *to_delete = const_cast *> (& m_clusters.objects ().item (id - 1)); @@ -338,6 +356,25 @@ local_clusters::remove_cluster (typename local_cluster::id_type id) m_needs_update = true; } +template +void +local_clusters::join_cluster_with (typename local_cluster::id_type id, typename local_cluster::id_type with_id) +{ + tl_assert (id > 0); + if (with_id == 0 || with_id > m_clusters.size () || id > m_clusters.size ()) { + return; + } + + // TODO: this const_cast is required. But we know what we're doing ... + local_cluster *with = const_cast *> (& m_clusters.objects ().item (with_id - 1)); + local_cluster *first = const_cast *> (& m_clusters.objects ().item (id - 1)); + first->join_with (*with); + + m_clusters.erase (m_clusters.iterator_from_pointer (with)); + + m_needs_update = true; +} + template local_cluster * local_clusters::insert () @@ -488,48 +525,43 @@ void connected_clusters::add_connection (typename local_cluster::id_type id, const ClusterInstance &inst) { m_connections [id].push_back (inst); + m_rev_connections [inst] = id; } template void -connected_clusters::join_connected_clusters (typename local_cluster::id_type id, typename local_cluster::id_type with_id) +connected_clusters::join_cluster_with (typename local_cluster::id_type id, typename local_cluster::id_type with_id) { if (id == with_id) { return; } + // join the shape clusters + local_clusters::join_cluster_with (id, with_id); + + // handle the connections by translating + const connections_type &to_join = connections_for_cluster (with_id); connections_type &target = m_connections [id]; target.insert (target.end (), to_join.begin (), to_join.end ()); + + for (connections_type::const_iterator c = to_join.begin (); c != to_join.end (); ++c) { + m_rev_connections [*c] = id; + } + m_connections.erase (with_id); } template typename local_cluster::id_type -connected_clusters::find_cluster_with_connection (const ClusterInstance &ci, size_t strip) const +connected_clusters::find_cluster_with_connection (const ClusterInstance &inst) const { - for (typename std::map::id_type, connections_type>::const_iterator i = m_connections.begin (); i != m_connections.end (); ++i) { - - for (connections_type::const_iterator j = i->second.begin (); j != i->second.end (); ++j) { - - const ClusterInstance::inst_path_type &path = j->inst (); - if (j->id () == ci.id () && path.size () == ci.inst ().size () - strip) { - - bool match = true; - for (size_t k = 0; k < path.size () && match; ++k) { - match = (path [k] == ci.inst () [k + strip]); - } - if (match) { - return i->first; - } - - } - - } - + typename std::map::id_type>::const_iterator rc = m_rev_connections.find (inst); + if (rc != m_rev_connections.end ()) { + return rc->second; + } else { + return 0; } - - return 0; } // explicit instantiations @@ -623,7 +655,7 @@ struct hc_receiver public: typedef typename hier_clusters::box_type box_type; typedef typename local_cluster::id_type id_type; - typedef std::map *> connector_map; + typedef std::map connector_map; /** * @brief Constructor @@ -659,6 +691,30 @@ public: return false; } + /** + * @brief Finally join the clusters in the join set + * + * This step is postponed because doing this while the iteration happens would + * invalidate the box trees. + */ + void join_superclusters () + { + for (typename std::list >::const_iterator sc = m_cm2join_sets.begin (); sc != m_cm2join_sets.end (); ++sc) { + + if (sc->empty ()) { + // dropped ones are empty + continue; + } + + typename std::set::const_iterator c = sc->begin (); + ++c; + for (typename std::set::const_iterator cc = c; cc != sc->end (); ++cc) { + mp_cell_clusters->join_cluster_with (*c, *cc); + } + + } + } + private: const db::Layout *mp_layout; db::connected_clusters *mp_cell_clusters; @@ -666,7 +722,8 @@ private: const cell_clusters_box_converter *mp_cbc; const db::Connectivity *mp_conn; connector_map m_connectors; - mutable std::map m_reduction_cache; + std::map *> m_cm2join_map; + std::list > m_cm2join_sets; /** * @brief Handles the cluster interactions between two instances or instance arrays @@ -701,7 +758,7 @@ private: box_type ib1 = bb1.transformed (tt1); box_type ib12 = ib1.transformed (t12); - ClusterInstance::inst_path_type pp1; + std::vector pp1; pp1.reserve (p1.size () + 1); pp1.insert (pp1.end (), p1.begin (), p1.end ()); pp1.push_back (db::InstElement (i1, ii1)); @@ -713,7 +770,7 @@ private: if (ib1.touches (ib2)) { - ClusterInstance::inst_path_type pp2; + std::vector pp2; pp2.reserve (p2.size () + 1); pp2.insert (pp2.end (), p2.begin (), p2.end ()); pp2.push_back (db::InstElement (i2, ii2)); @@ -767,11 +824,8 @@ private: if (i->interacts (*j, t21, *mp_conn)) { - ClusterInstance k1 (i->id (), p1); - reduce (k1); - - ClusterInstance k2 (j->id (), p2); - reduce (k2); + ClusterInstance k1 = make_path (i->id (), p1); + ClusterInstance k2 = make_path (j->id (), p2); typename connector_map::iterator x1 = m_connectors.find (k1); typename connector_map::iterator x2 = m_connectors.find (k2); @@ -780,23 +834,23 @@ private: if (x2 == m_connectors.end ()) { - db::local_cluster *connector = mp_cell_clusters->insert (); + id_type connector = mp_cell_clusters->insert_dummy (); m_connectors [k1] = connector; - mp_cell_clusters->add_connection (connector->id (), k1); - mp_cell_clusters->add_connection (connector->id (), k2); + mp_cell_clusters->add_connection (connector, k1); + mp_cell_clusters->add_connection (connector, k2); } else { - mp_cell_clusters->add_connection (x2->second->id (), k1); + mp_cell_clusters->add_connection (x2->second, k1); } } else if (x2 == m_connectors.end ()) { - mp_cell_clusters->add_connection (x1->second->id (), k2); + mp_cell_clusters->add_connection (x1->second, k2); } else if (x1->second != x2->second) { - mp_cell_clusters->join_connected_clusters (x1->second->id (), x2->second->id ()); - mp_cell_clusters->remove_cluster (x2->second->id ()); + mp_cell_clusters->join_cluster_with (x1->second, x2->second); + mp_cell_clusters->remove_cluster (x2->second); x2->second = x1->second; } @@ -836,7 +890,7 @@ private: if (b1.touches (ib2)) { - ClusterInstance::inst_path_type pp2; + std::vector pp2; pp2.reserve (p2.size () + 1); pp2.insert (pp2.end (), p2.begin (), p2.end ()); pp2.push_back (db::InstElement (i2, ii2)); @@ -870,10 +924,19 @@ private: if (c1.interacts (*j, t2, *mp_conn)) { - ClusterInstance k2 (j->id (), p2); - reduce (k2); + ClusterInstance k2 = make_path (j->id (), p2); - mp_cell_clusters->add_connection (c1.id (), k2); + id_type other = mp_cell_clusters->find_cluster_with_connection (k2); + if (other > 0) { + + // we found a child cluster that connects two clusters on our own level: + // we must join them into one, but not now. We're still iterating and + // would invalidate the box trees. So keep this now and combine the clusters later. + mark_to_join (other, c1.id ()); + + } else { + mp_cell_clusters->add_connection (c1.id (), k2); + } } @@ -881,35 +944,85 @@ private: } /** - * @brief Reduce the connection path to the connector highest in the hierarchy - * - * This feature shall prevent a situation where a connection is made to a sub-cluster of - * another hierarchical cluster. We always attach to the highest possible connector. + * @brief Inserts a pair of clusters to join */ - void reduce (ClusterInstance &k) const + void mark_to_join (id_type a, id_type b) { - std::map::const_iterator kr = m_reduction_cache.find (k); - if (kr != m_reduction_cache.end ()) { - k = kr->second; - return; + typename std::map *>::const_iterator x = m_cm2join_map.find (a); + typename std::map *>::const_iterator y = m_cm2join_map.find (b); + + if (x == m_cm2join_map.end ()) { + + if (y == m_cm2join_map.end ()) { + + m_cm2join_sets.push_back (std::set ()); + m_cm2join_sets.back ().insert (a); + m_cm2join_sets.back ().insert (b); + + m_cm2join_map [a] = &m_cm2join_sets.back (); + m_cm2join_map [b] = &m_cm2join_sets.back (); + + } else { + + y->second->insert (a); + m_cm2join_map [a] = y->second; + + } + + } else if (y == m_cm2join_map.end ()) { + + x->second->insert (b); + m_cm2join_map [b] = x->second; + + } else if (x->second != y->second) { + + // join two superclusters + x->second->insert (y->second->begin (), y->second->end ()); + for (typename std::set::const_iterator i = y->second->begin (); i != y->second->end (); ++i) { + m_cm2join_map [*i] = x->second; + } + y->second->clear (); + } + } - for (size_t rn = 1; rn + 1 < k.inst ().size (); ++rn) { + /** + * @brief Makes a valid path to a child cluster + * + * Cluster connections can only cross one level of hierarchy. This method + * creates necessary dummy entries for the given path. + */ + ClusterInstance make_path (id_type id, const std::vector &path) const + { + std::vector::const_iterator p = path.end (); + tl_assert (p != path.begin ()); - const db::connected_clusters &clusters = mp_tree->clusters_per_cell (k.inst () [rn - 1].inst_ptr.cell_index ()); - id_type red_id = clusters.find_cluster_with_connection (k, rn); - if (red_id > 0) { + while (true) { - // reduction possible - ClusterInstance::inst_path_type new_path; - new_path.insert (new_path.end (), k.inst ().begin (), k.inst ().begin () + rn); - k = ClusterInstance (red_id, new_path); - return; + --p; + + ClusterInstance ci (id, *p); + if (p == path.begin ()) { + return ci; + } + + connected_clusters &target_cc = mp_tree->clusters_per_cell (p [-1].inst_ptr.cell_index ()); + id_type parent_cluster = target_cc.find_cluster_with_connection (ci); + + if (parent_cluster > 0) { + + // taken parent + id = parent_cluster; + + } else { + + // no parent -> create vertical connector + id = target_cc.insert_dummy (); + target_cc.add_connection (id, ci); } } - } }; @@ -987,6 +1100,9 @@ hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layou bs2.process (rec, 1 /*touching*/, local_cluster_box_convert (), cibc); } + + // finally join local clusters which got connected by child clusters + rec.join_superclusters (); } template @@ -1002,6 +1118,15 @@ hier_clusters::clusters_per_cell (db::cell_index_type cell_index) const } } +template +connected_clusters & +hier_clusters::clusters_per_cell (db::cell_index_type cell_index) +{ + typename std::map >::iterator c = m_per_cell_clusters.find (cell_index); + tl_assert (c != m_per_cell_clusters.end ()); + return c->second; +} + // explicit instantiations template class DB_PUBLIC hier_clusters; diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index 3964cc76b..f15013901 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -254,6 +254,13 @@ public: */ void remove_cluster (typename local_cluster::id_type id); + /** + * @brief Joins a cluster with another one + * + * This will also remove the other cluster. + */ + void join_cluster_with (typename local_cluster::id_type id, typename local_cluster::id_type with_id); + /** * @brief Gets the bounding box of the clusters */ @@ -304,12 +311,23 @@ public: */ local_cluster *insert (); + /** + * @brief Allocates a new ID for dummy clusters + * + * Dummy cluster ID's will deliver empty clusters. Used for connectors. + */ + typename local_cluster::id_type insert_dummy () + { + return --m_next_dummy_id; + } + private: void ensure_sorted (); bool m_needs_update; box_type m_bbox; tree_type m_clusters; + size_t m_next_dummy_id; }; /** @@ -318,10 +336,8 @@ private: class DB_PUBLIC ClusterInstance { public: - typedef std::vector inst_path_type; - - ClusterInstance (size_t id, const inst_path_type &inst_path) - : m_id (id), m_inst_path (inst_path) + ClusterInstance (size_t id, const db::InstElement &inst) + : m_id (id), m_inst (inst) { // .. nothing yet .. } @@ -337,9 +353,9 @@ public: /** * @brief Gets the instance path */ - const inst_path_type &inst () const + const db::InstElement &inst () const { - return m_inst_path; + return m_inst; } /** @@ -347,7 +363,7 @@ public: */ bool operator== (const ClusterInstance &other) const { - return m_id == other.m_id && m_inst_path == other.m_inst_path; + return m_id == other.m_id && m_inst == other.m_inst; } /** @@ -358,12 +374,12 @@ public: if (m_id != other.m_id) { return m_id < other.m_id; } - return m_inst_path < other.m_inst_path; + return m_inst < other.m_inst; } private: size_t m_id; - inst_path_type m_inst_path; + db::InstElement m_inst; }; template class hier_clusters; @@ -393,27 +409,29 @@ public: */ const connections_type &connections_for_cluster (typename local_cluster::id_type id) const; + /** + * @brief Reverse "connections_for_cluster" + * Finds the cluster which has a connection given by inst. + * Returns 0 if the given connection does not exist. + */ + typename local_cluster::id_type find_cluster_with_connection (const ClusterInstance &inst) const; + /** * @brief Adds a connection between a local cluster and one from a child instance */ void add_connection (typename local_cluster::id_type, const ClusterInstance &inst); /** - * @brief Joins all connections of with_id to id - */ - void join_connected_clusters (typename local_cluster::id_type id, typename local_cluster::id_type with_id); - - /** - * @brief Reverse "connections_for_cluster" + * @brief Joins the cluster id with the cluster with_id * - * Finds the cluster which has a connection given by inst. "strip" elements - * are stipped from the front of the instantiation path. - * This method returns 0 if no cluster can be found. + * The "with_id" cluster is removed. All connections of "with_id" are transferred to the + * first one. All shapes of "with_id" are transferred to "id". */ - typename local_cluster::id_type find_cluster_with_connection (const ClusterInstance &inst, size_t strip) const; + void join_cluster_with (typename local_cluster::id_type id, typename local_cluster::id_type with_id); private: std::map::id_type, connections_type> m_connections; + std::map::id_type> m_rev_connections; }; template class cell_clusters_box_converter; @@ -444,6 +462,11 @@ public: */ const connected_clusters &clusters_per_cell (db::cell_index_type cell_index) const; + /** + * @brief Gets the connected clusters for a given cell (non-const version) + */ + connected_clusters &clusters_per_cell (db::cell_index_type cell_index); + /** * @brief Clears this collection */ diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index f3f45eae5..b28c7fee5 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -337,16 +337,6 @@ TEST(30_LocalConnectedClusters) db::connected_clusters cc; - db::ClusterInstance::inst_path_type p1; - p1.push_back (db::InstElement (i1)); - - db::ClusterInstance::inst_path_type p2; - p2.push_back (db::InstElement (i1)); - p2.push_back (db::InstElement (i2)); - - db::ClusterInstance::inst_path_type p3; - p3.push_back (db::InstElement (i2)); - db::connected_clusters::connections_type x; db::connected_clusters::connections_type::const_iterator ix; @@ -355,44 +345,67 @@ TEST(30_LocalConnectedClusters) x = cc.connections_for_cluster (2); EXPECT_EQ (x.size (), size_t (0)); - cc.add_connection (1, db::ClusterInstance (1, p1)); - cc.add_connection (1, db::ClusterInstance (2, p2)); + // after this: + // [#1] -> i1:#1 + // -> i2:#2 + cc.add_connection (1, db::ClusterInstance (1, db::InstElement (i1))); + cc.add_connection (1, db::ClusterInstance (2, db::InstElement (i2))); x = cc.connections_for_cluster (1); EXPECT_EQ (x.size (), size_t (2)); x = cc.connections_for_cluster (2); EXPECT_EQ (x.size (), size_t (0)); - cc.add_connection (2, db::ClusterInstance (1, p2)); + // after this: + // [#1] -> i1:#1 + // -> i2:#2 + // [#2] -> i2:#1 + cc.add_connection (2, db::ClusterInstance (1, db::InstElement (i2))); x = cc.connections_for_cluster (2); EXPECT_EQ (x.size (), size_t (1)); - cc.join_connected_clusters (1, 2); + cc.join_cluster_with (1, 2); x = cc.connections_for_cluster (1); EXPECT_EQ (x.size (), size_t (3)); ix = x.begin (); EXPECT_EQ (ix->id (), size_t (1)); - EXPECT_EQ (ix->inst () == p1, true); + EXPECT_EQ (ix->inst () == db::InstElement (i1), true); ++ix; EXPECT_EQ (ix->id (), size_t (2)); - EXPECT_EQ (ix->inst () == p2, true); + EXPECT_EQ (ix->inst () == db::InstElement (i2), true); ++ix; EXPECT_EQ (ix->id (), size_t (1)); - EXPECT_EQ (ix->inst () == p2, true); + EXPECT_EQ (ix->inst () == db::InstElement (i2), true); x = cc.connections_for_cluster (2); EXPECT_EQ (x.size (), size_t (0)); - cc.add_connection (2, db::ClusterInstance (3, p1)); + // after this: + // [#1] -> i1:#1 + // -> i2:#2 + // [#2] -> i2:#1 + // -> i1:#3 + cc.add_connection (2, db::ClusterInstance (3, db::InstElement (i1))); - EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (3, p1), 0), size_t (2)); - EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (2, p1), 0), size_t (0)); - EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (2, p2), 0), size_t (1)); + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (3, db::InstElement (i1))), size_t (2)); + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (2, db::InstElement (i1))), size_t (0)); + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (2, db::InstElement (i2))), size_t (1)); - cc.add_connection (17, db::ClusterInstance (2, p3)); + // after this: + // [#1] -> i1:#1 + // -> i2:#2 + // -> i2:#1 + // -> i1:#3 + cc.join_cluster_with (1, 2); + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (3, db::InstElement (i1))), size_t (1)); + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (1, db::InstElement (i1))), size_t (1)); + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (2, db::InstElement (i1))), size_t (0)); + EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (2, db::InstElement (i2))), size_t (1)); - // p3 == p2 minus 1 at the beginning - EXPECT_EQ (cc.find_cluster_with_connection (db::ClusterInstance (2, p2), 1 /*one stripped*/), size_t (17)); + x = cc.connections_for_cluster (1); + EXPECT_EQ (x.size (), size_t (4)); + x = cc.connections_for_cluster (2); + EXPECT_EQ (x.size (), size_t (0)); } static void normalize_layer (db::Layout &layout, unsigned int layer) @@ -433,12 +446,9 @@ static void copy_cluster_shapes (db::Shapes &out, db::cell_index_type ci, const const connections_type &connections = hc.clusters_per_cell (ci).connections_for_cluster (cluster.id ()); for (connections_type::const_iterator i = connections.begin (); i != connections.end (); ++i) { - db::ICplxTrans t = trans; - for (db::ClusterInstance::inst_path_type::const_iterator p = i->inst ().begin (); p != i->inst ().end (); ++p) { - t = t * p->complex_trans (); - } + db::ICplxTrans t = trans * i->inst ().complex_trans (); - db::cell_index_type cci = i->inst ().back ().inst_ptr.cell_index (); + db::cell_index_type cci = i->inst ().inst_ptr.cell_index (); copy_cluster_shapes (out, cci, hc, hc.clusters_per_cell (cci).cluster_by_id (i->id ()), t, conn); }