WIP: some refactoring, joining of clusters by child clusters. Needs testing.

This commit is contained in:
Matthias Koefferlein 2018-12-03 22:05:54 +01:00
parent e8c86834cb
commit 5a48ac3974
3 changed files with 273 additions and 115 deletions

View File

@ -31,6 +31,8 @@
#include <vector>
#include <map>
#include <list>
#include <set>
namespace db
{
@ -304,7 +306,7 @@ template class DB_PUBLIC local_cluster<db::PolygonRef>;
template <class T>
local_clusters<T>::local_clusters ()
: m_needs_update (false)
: m_needs_update (false), m_next_dummy_id (0)
{
// .. nothing yet ..
}
@ -315,22 +317,38 @@ void local_clusters<T>::clear ()
m_needs_update = false;
m_clusters.clear ();
m_bbox = box_type ();
m_next_dummy_id = 0;
}
template <class T>
const local_cluster<T> &
local_clusters<T>::cluster_by_id (typename local_cluster<T>::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<T> 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 <class T>
void
local_clusters<T>::remove_cluster (typename local_cluster<T>::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<T> *to_delete = const_cast<local_cluster<T> *> (& m_clusters.objects ().item (id - 1));
@ -338,6 +356,25 @@ local_clusters<T>::remove_cluster (typename local_cluster<T>::id_type id)
m_needs_update = true;
}
template <class T>
void
local_clusters<T>::join_cluster_with (typename local_cluster<T>::id_type id, typename local_cluster<T>::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<T> *with = const_cast<local_cluster<T> *> (& m_clusters.objects ().item (with_id - 1));
local_cluster<T> *first = const_cast<local_cluster<T> *> (& m_clusters.objects ().item (id - 1));
first->join_with (*with);
m_clusters.erase (m_clusters.iterator_from_pointer (with));
m_needs_update = true;
}
template <class T>
local_cluster<T> *
local_clusters<T>::insert ()
@ -488,48 +525,43 @@ void
connected_clusters<T>::add_connection (typename local_cluster<T>::id_type id, const ClusterInstance &inst)
{
m_connections [id].push_back (inst);
m_rev_connections [inst] = id;
}
template <class T>
void
connected_clusters<T>::join_connected_clusters (typename local_cluster<T>::id_type id, typename local_cluster<T>::id_type with_id)
connected_clusters<T>::join_cluster_with (typename local_cluster<T>::id_type id, typename local_cluster<T>::id_type with_id)
{
if (id == with_id) {
return;
}
// join the shape clusters
local_clusters<T>::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 <class T>
typename local_cluster<T>::id_type
connected_clusters<T>::find_cluster_with_connection (const ClusterInstance &ci, size_t strip) const
connected_clusters<T>::find_cluster_with_connection (const ClusterInstance &inst) const
{
for (typename std::map<typename local_cluster<T>::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<ClusterInstance, typename local_cluster<T>::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<T>::box_type box_type;
typedef typename local_cluster<T>::id_type id_type;
typedef std::map<ClusterInstance, local_cluster<T> *> connector_map;
typedef std::map<ClusterInstance, id_type> 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<std::set<id_type> >::const_iterator sc = m_cm2join_sets.begin (); sc != m_cm2join_sets.end (); ++sc) {
if (sc->empty ()) {
// dropped ones are empty
continue;
}
typename std::set<id_type>::const_iterator c = sc->begin ();
++c;
for (typename std::set<id_type>::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<T> *mp_cell_clusters;
@ -666,7 +722,8 @@ private:
const cell_clusters_box_converter<T> *mp_cbc;
const db::Connectivity *mp_conn;
connector_map m_connectors;
mutable std::map<ClusterInstance, ClusterInstance> m_reduction_cache;
std::map<id_type, std::set<id_type> *> m_cm2join_map;
std::list<std::set<id_type> > 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<db::InstElement> 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<db::InstElement> 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<T> *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<db::InstElement> 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<ClusterInstance, ClusterInstance>::const_iterator kr = m_reduction_cache.find (k);
if (kr != m_reduction_cache.end ()) {
k = kr->second;
return;
typename std::map<id_type, std::set<id_type> *>::const_iterator x = m_cm2join_map.find (a);
typename std::map<id_type, std::set<id_type> *>::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<id_type> ());
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<id_type>::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<db::InstElement> &path) const
{
std::vector<db::InstElement>::const_iterator p = path.end ();
tl_assert (p != path.begin ());
const db::connected_clusters<T> &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<T> &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<T>::do_build (cell_clusters_box_converter<T> &cbc, const db::Layou
bs2.process (rec, 1 /*touching*/, local_cluster_box_convert<T> (), cibc);
}
// finally join local clusters which got connected by child clusters
rec.join_superclusters ();
}
template <class T>
@ -1002,6 +1118,15 @@ hier_clusters<T>::clusters_per_cell (db::cell_index_type cell_index) const
}
}
template <class T>
connected_clusters<T> &
hier_clusters<T>::clusters_per_cell (db::cell_index_type cell_index)
{
typename std::map<db::cell_index_type, connected_clusters<T> >::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<db::PolygonRef>;

View File

@ -254,6 +254,13 @@ public:
*/
void remove_cluster (typename local_cluster<T>::id_type id);
/**
* @brief Joins a cluster with another one
*
* This will also remove the other cluster.
*/
void join_cluster_with (typename local_cluster<T>::id_type id, typename local_cluster<T>::id_type with_id);
/**
* @brief Gets the bounding box of the clusters
*/
@ -304,12 +311,23 @@ public:
*/
local_cluster<T> *insert ();
/**
* @brief Allocates a new ID for dummy clusters
*
* Dummy cluster ID's will deliver empty clusters. Used for connectors.
*/
typename local_cluster<T>::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<db::InstElement> 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 T> class hier_clusters;
@ -393,27 +409,29 @@ public:
*/
const connections_type &connections_for_cluster (typename local_cluster<T>::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<T>::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<T>::id_type, const ClusterInstance &inst);
/**
* @brief Joins all connections of with_id to id
*/
void join_connected_clusters (typename local_cluster<T>::id_type id, typename local_cluster<T>::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<T>::id_type find_cluster_with_connection (const ClusterInstance &inst, size_t strip) const;
void join_cluster_with (typename local_cluster<T>::id_type id, typename local_cluster<T>::id_type with_id);
private:
std::map<typename local_cluster<T>::id_type, connections_type> m_connections;
std::map<ClusterInstance, typename local_cluster<T>::id_type> m_rev_connections;
};
template <typename> class cell_clusters_box_converter;
@ -444,6 +462,11 @@ public:
*/
const connected_clusters<T> &clusters_per_cell (db::cell_index_type cell_index) const;
/**
* @brief Gets the connected clusters for a given cell (non-const version)
*/
connected_clusters<T> &clusters_per_cell (db::cell_index_type cell_index);
/**
* @brief Clears this collection
*/

View File

@ -337,16 +337,6 @@ TEST(30_LocalConnectedClusters)
db::connected_clusters<db::PolygonRef> 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<db::PolygonRef>::connections_type x;
db::connected_clusters<db::PolygonRef>::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);
}