mirror of https://github.com/KLayout/klayout.git
Implemented implicit joining of nets with the same label.
This commit is contained in:
parent
3f1cd226a5
commit
9c0123df20
|
|
@ -817,7 +817,7 @@ private:
|
|||
|
||||
template <class T>
|
||||
void
|
||||
local_clusters<T>::build_clusters (const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn)
|
||||
local_clusters<T>::build_clusters (const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters<unsigned int> *attr_equivalence)
|
||||
{
|
||||
bool report_progress = tl::verbosity () >= 50;
|
||||
static std::string desc = tl::to_string (tr ("Building local clusters"));
|
||||
|
|
@ -836,6 +836,57 @@ local_clusters<T>::build_clusters (const db::Cell &cell, db::ShapeIterator::flag
|
|||
cluster_building_receiver<T, box_type> rec (conn);
|
||||
bs.process (rec, 1 /*==touching*/, bc);
|
||||
rec.generate_clusters (*this);
|
||||
|
||||
if (attr_equivalence && attr_equivalence->size () > 0) {
|
||||
apply_attr_equivalences (*attr_equivalence);
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void
|
||||
local_clusters<T>::apply_attr_equivalences (const tl::equivalence_clusters<unsigned int> &attr_equivalence)
|
||||
{
|
||||
tl::equivalence_clusters<unsigned int> eq;
|
||||
|
||||
// collect all local attributes (the ones which are present in attr_equivalence) into "eq"
|
||||
// and form equivalences for multi-attribute clusters.
|
||||
for (const_iterator c = begin (); c != end (); ++c) {
|
||||
typename local_cluster<T>::attr_iterator a0;
|
||||
for (typename local_cluster<T>::attr_iterator a = c->begin_attr (); a != c->end_attr (); ++a) {
|
||||
if (attr_equivalence.has_attribute (*a)) {
|
||||
if (a0 == typename local_cluster<T>::attr_iterator ()) {
|
||||
a0 = a;
|
||||
}
|
||||
eq.same (*a0, *a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// apply the equivalences implied by attr_equivalence
|
||||
eq.apply_equivalences (attr_equivalence);
|
||||
|
||||
// identify the layout clusters joined into one attribute cluster and join them
|
||||
|
||||
std::map<tl::equivalence_clusters<unsigned int>::cluster_id_type, std::set<size_t> > c2c;
|
||||
|
||||
for (const_iterator c = begin (); c != end (); ++c) {
|
||||
for (typename local_cluster<T>::attr_iterator a = c->begin_attr (); a != c->end_attr (); ++a) {
|
||||
tl::equivalence_clusters<unsigned int>::cluster_id_type cl = attr_equivalence.cluster_id (*a);
|
||||
if (cl > 0) {
|
||||
c2c [cl].insert (c->id ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (std::map<tl::equivalence_clusters<unsigned int>::cluster_id_type, std::set<size_t> >::const_iterator c = c2c.begin (); c != c2c.end (); ++c) {
|
||||
if (c->second.size () > 1) {
|
||||
std::set<size_t>::const_iterator cl0 = c->second.begin ();
|
||||
std::set<size_t>::const_iterator cl = cl0;
|
||||
while (++cl != c->second.end ()) {
|
||||
join_cluster_with (*cl0, *cl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// explicit instantiations
|
||||
|
|
@ -992,11 +1043,11 @@ void hier_clusters<T>::clear ()
|
|||
|
||||
template <class T>
|
||||
void
|
||||
hier_clusters<T>::build (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn)
|
||||
hier_clusters<T>::build (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters<unsigned int> *attr_equivalence)
|
||||
{
|
||||
clear ();
|
||||
cell_clusters_box_converter<T> cbc (layout, *this);
|
||||
do_build (cbc, layout, cell, shape_flags, conn);
|
||||
do_build (cbc, layout, cell, shape_flags, conn, attr_equivalence);
|
||||
}
|
||||
|
||||
namespace
|
||||
|
|
@ -1649,7 +1700,7 @@ hier_clusters<T>::make_path (const db::Layout &layout, const db::Cell &cell, siz
|
|||
|
||||
template <class T>
|
||||
void
|
||||
hier_clusters<T>::do_build (cell_clusters_box_converter<T> &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn)
|
||||
hier_clusters<T>::do_build (cell_clusters_box_converter<T> &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters<unsigned int> *attr_equivalence)
|
||||
{
|
||||
tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Computing shape clusters")));
|
||||
|
||||
|
|
@ -1664,7 +1715,7 @@ hier_clusters<T>::do_build (cell_clusters_box_converter<T> &cbc, const db::Layou
|
|||
tl::RelativeProgress progress (tl::to_string (tr ("Computing local clusters")), called.size (), 1);
|
||||
|
||||
for (std::set<db::cell_index_type>::const_iterator c = called.begin (); c != called.end (); ++c) {
|
||||
build_local_cluster (layout, layout.cell (*c), shape_flags, conn);
|
||||
build_local_cluster (layout, layout.cell (*c), shape_flags, conn, attr_equivalence);
|
||||
++progress;
|
||||
}
|
||||
}
|
||||
|
|
@ -1707,7 +1758,7 @@ hier_clusters<T>::do_build (cell_clusters_box_converter<T> &cbc, const db::Layou
|
|||
|
||||
template <class T>
|
||||
void
|
||||
hier_clusters<T>::build_local_cluster (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn)
|
||||
hier_clusters<T>::build_local_cluster (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters<unsigned int> *attr_equivalence)
|
||||
{
|
||||
std::string msg = tl::to_string (tr ("Computing local clusters for cell: ")) + std::string (layout.cell_name (cell.cell_index ()));
|
||||
if (tl::verbosity () >= 40) {
|
||||
|
|
@ -1716,7 +1767,7 @@ hier_clusters<T>::build_local_cluster (const db::Layout &layout, const db::Cell
|
|||
tl::SelfTimer timer (tl::verbosity () >= 41, msg);
|
||||
|
||||
connected_clusters<T> &local = m_per_cell_clusters [cell.cell_index ()];
|
||||
local.build_clusters (cell, shape_flags, conn);
|
||||
local.build_clusters (cell, shape_flags, conn, attr_equivalence);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include "dbBoxTree.h"
|
||||
#include "dbCell.h"
|
||||
#include "dbInstElement.h"
|
||||
#include "tlEquivalenceClusters.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
|
@ -463,8 +464,13 @@ public:
|
|||
* This method will only build the local clusters. Child cells
|
||||
* are not taken into account. Only the shape types listed in
|
||||
* shape_flags are taken.
|
||||
*
|
||||
* If attr_equivalence is non-null, all clusters with attributes
|
||||
* listed as equivalent in this object are joined. Additional
|
||||
* cluster joining may happen in this case, because multi-attribute
|
||||
* assignment might create connections too.
|
||||
*/
|
||||
void build_clusters (const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn);
|
||||
void build_clusters (const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters<unsigned int> *attr_equivalence = 0);
|
||||
|
||||
/**
|
||||
* @brief Creates and inserts a new clusters
|
||||
|
|
@ -490,6 +496,8 @@ private:
|
|||
box_type m_bbox;
|
||||
tree_type m_clusters;
|
||||
size_t m_next_dummy_id;
|
||||
|
||||
void apply_attr_equivalences (const tl::equivalence_clusters<unsigned int> &attr_equivalence);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -740,7 +748,7 @@ public:
|
|||
/**
|
||||
* @brief Builds a hierarchy of clusters from a cell hierarchy and given connectivity
|
||||
*/
|
||||
void build (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn);
|
||||
void build (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters<unsigned int> *attr_equivalence = 0);
|
||||
|
||||
/**
|
||||
* @brief Gets the connected clusters for a given cell
|
||||
|
|
@ -778,10 +786,10 @@ public:
|
|||
ClusterInstance make_path (const db::Layout &layout, const db::Cell &cell, size_t id, const std::vector<db::InstElement> &path);
|
||||
|
||||
private:
|
||||
void build_local_cluster (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn);
|
||||
void build_local_cluster (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters<unsigned int> *attr_equivalence);
|
||||
void build_hier_connections (cell_clusters_box_converter<T> &cbc, const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn);
|
||||
void build_hier_connections_for_cells (cell_clusters_box_converter<T> &cbc, const db::Layout &layout, const std::vector<db::cell_index_type> &cells, const db::Connectivity &conn, tl::RelativeProgress &progress);
|
||||
void do_build (cell_clusters_box_converter<T> &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn);
|
||||
void do_build (cell_clusters_box_converter<T> &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters<unsigned int> *attr_equivalence = 0);
|
||||
|
||||
std::map<db::cell_index_type, connected_clusters<T> > m_per_cell_clusters;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -226,7 +226,7 @@ size_t LayoutToNetlist::global_net_id (const std::string &name)
|
|||
return m_conn.global_net_id (name);
|
||||
}
|
||||
|
||||
void LayoutToNetlist::extract_netlist ()
|
||||
void LayoutToNetlist::extract_netlist (bool join_nets_by_label)
|
||||
{
|
||||
if (m_netlist_extracted) {
|
||||
throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
|
||||
|
|
@ -236,7 +236,7 @@ void LayoutToNetlist::extract_netlist ()
|
|||
}
|
||||
|
||||
db::NetlistExtractor netex;
|
||||
netex.extract_nets(m_dss, m_conn, *mp_netlist, m_net_clusters);
|
||||
netex.extract_nets(m_dss, m_conn, *mp_netlist, m_net_clusters, join_nets_by_label);
|
||||
|
||||
m_netlist_extracted = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -269,7 +269,7 @@ public:
|
|||
* @brief Runs the netlist extraction
|
||||
* See the class description for more details.
|
||||
*/
|
||||
void extract_netlist ();
|
||||
void extract_netlist (bool join_nets_by_label = true);
|
||||
|
||||
/**
|
||||
* @brief Marks the netlist as extracted
|
||||
|
|
|
|||
|
|
@ -33,8 +33,32 @@ NetlistExtractor::NetlistExtractor ()
|
|||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
static void
|
||||
build_net_name_equivalence (const db::Layout *layout, db::property_names_id_type net_name_id, tl::equivalence_clusters<unsigned int> &eq)
|
||||
{
|
||||
std::map<std::string, std::set<unsigned int> > prop_by_name;
|
||||
|
||||
for (db::PropertiesRepository::iterator i = layout->properties_repository ().begin (); i != layout->properties_repository ().end (); ++i) {
|
||||
for (db::PropertiesRepository::properties_set::const_iterator p = i->second.begin (); p != i->second.end (); ++p) {
|
||||
if (p->first == net_name_id) {
|
||||
std::string nn = p->second.to_string ();
|
||||
prop_by_name [nn].insert (i->first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (std::map<std::string, std::set<unsigned int> >::const_iterator pn = prop_by_name.begin (); pn != prop_by_name.end (); ++pn) {
|
||||
std::set<unsigned int>::const_iterator p = pn->second.begin ();
|
||||
std::set<unsigned int>::const_iterator p0 = p;
|
||||
while (p != pn->second.end ()) {
|
||||
eq.same (*p0, *p);
|
||||
++p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters)
|
||||
NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters, bool join_nets_by_label)
|
||||
{
|
||||
mp_clusters = &clusters;
|
||||
mp_layout = &dss.const_layout ();
|
||||
|
|
@ -52,7 +76,11 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect
|
|||
|
||||
// the big part: actually extract the nets
|
||||
|
||||
mp_clusters->build (*mp_layout, *mp_cell, db::ShapeIterator::Polygons, conn);
|
||||
tl::equivalence_clusters<unsigned int> net_name_equivalence;
|
||||
if (m_text_annot_name_id.first && join_nets_by_label) {
|
||||
build_net_name_equivalence (mp_layout, m_text_annot_name_id.second, net_name_equivalence);
|
||||
}
|
||||
mp_clusters->build (*mp_layout, *mp_cell, db::ShapeIterator::Polygons, conn, &net_name_equivalence);
|
||||
|
||||
// reverse lookup for Circuit vs. cell index
|
||||
std::map<db::cell_index_type, db::Circuit *> circuits;
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ public:
|
|||
* @brief Extract the nets
|
||||
* See the class description for more details.
|
||||
*/
|
||||
void extract_nets (const db::DeepShapeStore &dss, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters);
|
||||
void extract_nets (const db::DeepShapeStore &dss, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters, bool join_nets_by_label = true);
|
||||
|
||||
private:
|
||||
hier_clusters_type *mp_clusters;
|
||||
|
|
|
|||
|
|
@ -213,8 +213,10 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
|
|||
gsi::method ("global_net_name", &db::LayoutToNetlist::global_net_name, gsi::arg ("global_net_id"),
|
||||
"@brief Gets the global net name for the given global net ID."
|
||||
) +
|
||||
gsi::method ("extract_netlist", &db::LayoutToNetlist::extract_netlist,
|
||||
gsi::method ("extract_netlist", &db::LayoutToNetlist::extract_netlist, gsi::arg ("join_nets_by_label", true),
|
||||
"@brief Runs the netlist extraction\n"
|
||||
"If join_nets_by_label is true, nets on the same hierarchy level carrying the same label will be connected "
|
||||
"implicitly even if there is no physical connection.\n"
|
||||
"See the class description for more details.\n"
|
||||
) +
|
||||
gsi::method_ext ("internal_layout", &l2n_internal_layout,
|
||||
|
|
|
|||
|
|
@ -465,7 +465,8 @@ TEST(2_DeviceAndNetExtractionFlat)
|
|||
|
||||
// extract the nets
|
||||
|
||||
net_ex.extract_nets (dss, conn, nl, cl);
|
||||
// don't use "join_nets_by_label" because the flattened texts will spoil everything
|
||||
net_ex.extract_nets (dss, conn, nl, cl, false);
|
||||
|
||||
// debug layers produced for nets
|
||||
// 202/0 -> Active
|
||||
|
|
@ -547,3 +548,244 @@ TEST(2_DeviceAndNetExtractionFlat)
|
|||
|
||||
db::compare_layouts (_this, ly, au);
|
||||
}
|
||||
|
||||
static bool
|
||||
all_net_names_unique (const db::Circuit &c)
|
||||
{
|
||||
std::set<std::string> names;
|
||||
for (db::Circuit::const_net_iterator n = c.begin_nets (); n != c.end_nets (); ++n) {
|
||||
if (! n->name ().empty ()) {
|
||||
if (names.find (n->name ()) != names.end ()) {
|
||||
return false;
|
||||
} else {
|
||||
names.insert (n->name ());
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
all_net_names_unique (const db::Netlist &nl)
|
||||
{
|
||||
for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) {
|
||||
if (! all_net_names_unique (*c)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST(3_DeviceAndNetExtractionWithImplicitConnections)
|
||||
{
|
||||
db::Layout ly;
|
||||
db::LayerMap lmap;
|
||||
|
||||
unsigned int nwell = define_layer (ly, lmap, 1);
|
||||
unsigned int active = define_layer (ly, lmap, 2);
|
||||
unsigned int poly = define_layer (ly, lmap, 3);
|
||||
unsigned int poly_lbl = define_layer (ly, lmap, 3, 1);
|
||||
unsigned int diff_cont = define_layer (ly, lmap, 4);
|
||||
unsigned int poly_cont = define_layer (ly, lmap, 5);
|
||||
unsigned int metal1 = define_layer (ly, lmap, 6);
|
||||
unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1);
|
||||
unsigned int via1 = define_layer (ly, lmap, 7);
|
||||
unsigned int metal2 = define_layer (ly, lmap, 8);
|
||||
unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1);
|
||||
|
||||
{
|
||||
db::LoadLayoutOptions options;
|
||||
options.get_options<db::CommonReaderOptions> ().layer_map = lmap;
|
||||
options.get_options<db::CommonReaderOptions> ().create_other_layers = false;
|
||||
|
||||
std::string fn (tl::testsrc ());
|
||||
fn = tl::combine_path (fn, "testdata");
|
||||
fn = tl::combine_path (fn, "algo");
|
||||
fn = tl::combine_path (fn, "device_extract_l1_implicit_nets.gds");
|
||||
|
||||
tl::InputStream stream (fn);
|
||||
db::Reader reader (stream);
|
||||
reader.read (ly, options);
|
||||
}
|
||||
|
||||
db::Cell &tc = ly.cell (*ly.begin_top_down ());
|
||||
|
||||
db::DeepShapeStore dss;
|
||||
dss.set_text_enlargement (1);
|
||||
dss.set_text_property_name (tl::Variant ("LABEL"));
|
||||
|
||||
// original layers
|
||||
db::Region rnwell (db::RecursiveShapeIterator (ly, tc, nwell), dss);
|
||||
db::Region ractive (db::RecursiveShapeIterator (ly, tc, active), dss);
|
||||
db::Region rpoly (db::RecursiveShapeIterator (ly, tc, poly), dss);
|
||||
db::Region rpoly_lbl (db::RecursiveShapeIterator (ly, tc, poly_lbl), dss);
|
||||
db::Region rdiff_cont (db::RecursiveShapeIterator (ly, tc, diff_cont), dss);
|
||||
db::Region rpoly_cont (db::RecursiveShapeIterator (ly, tc, poly_cont), dss);
|
||||
db::Region rmetal1 (db::RecursiveShapeIterator (ly, tc, metal1), dss);
|
||||
db::Region rmetal1_lbl (db::RecursiveShapeIterator (ly, tc, metal1_lbl), dss);
|
||||
db::Region rvia1 (db::RecursiveShapeIterator (ly, tc, via1), dss);
|
||||
db::Region rmetal2 (db::RecursiveShapeIterator (ly, tc, metal2), dss);
|
||||
db::Region rmetal2_lbl (db::RecursiveShapeIterator (ly, tc, metal2_lbl), dss);
|
||||
|
||||
// derived regions
|
||||
|
||||
db::Region rpactive = ractive & rnwell;
|
||||
db::Region rpgate = rpactive & rpoly;
|
||||
db::Region rpsd = rpactive - rpgate;
|
||||
|
||||
db::Region rnactive = ractive - rnwell;
|
||||
db::Region rngate = rnactive & rpoly;
|
||||
db::Region rnsd = rnactive - rngate;
|
||||
|
||||
// return the computed layers into the original layout and write it for debugging purposes
|
||||
|
||||
unsigned int lgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate
|
||||
unsigned int lsd = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Source/Drain
|
||||
unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion
|
||||
unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion
|
||||
|
||||
rpgate.insert_into (&ly, tc.cell_index (), lgate);
|
||||
rngate.insert_into (&ly, tc.cell_index (), lgate);
|
||||
rpsd.insert_into (&ly, tc.cell_index (), lsd);
|
||||
rnsd.insert_into (&ly, tc.cell_index (), lsd);
|
||||
rpsd.insert_into (&ly, tc.cell_index (), lpdiff);
|
||||
rnsd.insert_into (&ly, tc.cell_index (), lndiff);
|
||||
|
||||
// perform the extraction
|
||||
|
||||
db::Netlist nl;
|
||||
db::hier_clusters<db::PolygonRef> cl;
|
||||
|
||||
db::NetlistDeviceExtractorMOS3Transistor pmos_ex ("PMOS");
|
||||
db::NetlistDeviceExtractorMOS3Transistor nmos_ex ("NMOS");
|
||||
|
||||
db::NetlistDeviceExtractor::input_layers dl;
|
||||
|
||||
dl["SD"] = &rpsd;
|
||||
dl["G"] = &rpgate;
|
||||
dl["P"] = &rpoly; // not needed for extraction but to return terminal shapes
|
||||
pmos_ex.extract (dss, dl, nl, cl);
|
||||
|
||||
dl["SD"] = &rnsd;
|
||||
dl["G"] = &rngate;
|
||||
dl["P"] = &rpoly; // not needed for extraction but to return terminal shapes
|
||||
nmos_ex.extract (dss, dl, nl, cl);
|
||||
|
||||
// perform the net extraction
|
||||
|
||||
db::NetlistExtractor net_ex;
|
||||
|
||||
db::Connectivity conn;
|
||||
// Intra-layer
|
||||
conn.connect (rpsd);
|
||||
conn.connect (rnsd);
|
||||
conn.connect (rpoly);
|
||||
conn.connect (rdiff_cont);
|
||||
conn.connect (rpoly_cont);
|
||||
conn.connect (rmetal1);
|
||||
conn.connect (rvia1);
|
||||
conn.connect (rmetal2);
|
||||
// Inter-layer
|
||||
conn.connect (rpsd, rdiff_cont);
|
||||
conn.connect (rnsd, rdiff_cont);
|
||||
conn.connect (rpoly, rpoly_cont);
|
||||
conn.connect (rpoly_cont, rmetal1);
|
||||
conn.connect (rdiff_cont, rmetal1);
|
||||
conn.connect (rmetal1, rvia1);
|
||||
conn.connect (rvia1, rmetal2);
|
||||
conn.connect (rpoly, rpoly_lbl); // attaches labels
|
||||
conn.connect (rmetal1, rmetal1_lbl); // attaches labels
|
||||
conn.connect (rmetal2, rmetal2_lbl); // attaches labels
|
||||
|
||||
// extract the nets
|
||||
|
||||
net_ex.extract_nets (dss, conn, nl, cl);
|
||||
|
||||
EXPECT_EQ (all_net_names_unique (nl), true);
|
||||
|
||||
// debug layers produced for nets
|
||||
// 202/0 -> Active
|
||||
// 203/0 -> Poly
|
||||
// 204/0 -> Diffusion contacts
|
||||
// 205/0 -> Poly contacts
|
||||
// 206/0 -> Metal1
|
||||
// 207/0 -> Via1
|
||||
// 208/0 -> Metal2
|
||||
// 210/0 -> N source/drain
|
||||
// 211/0 -> P source/drain
|
||||
std::map<unsigned int, unsigned int> dump_map;
|
||||
dump_map [layer_of (rpsd) ] = ly.insert_layer (db::LayerProperties (210, 0));
|
||||
dump_map [layer_of (rnsd) ] = ly.insert_layer (db::LayerProperties (211, 0));
|
||||
dump_map [layer_of (rpoly) ] = ly.insert_layer (db::LayerProperties (203, 0));
|
||||
dump_map [layer_of (rdiff_cont)] = ly.insert_layer (db::LayerProperties (204, 0));
|
||||
dump_map [layer_of (rpoly_cont)] = ly.insert_layer (db::LayerProperties (205, 0));
|
||||
dump_map [layer_of (rmetal1) ] = ly.insert_layer (db::LayerProperties (206, 0));
|
||||
dump_map [layer_of (rvia1) ] = ly.insert_layer (db::LayerProperties (207, 0));
|
||||
dump_map [layer_of (rmetal2) ] = ly.insert_layer (db::LayerProperties (208, 0));
|
||||
|
||||
// write nets to layout
|
||||
db::CellMapping cm = dss.cell_mapping_to_original (0, &ly, tc.cell_index ());
|
||||
dump_nets_to_layout (nl, cl, ly, dump_map, cm);
|
||||
|
||||
// compare netlist as string
|
||||
EXPECT_EQ (nl.to_string (),
|
||||
"Circuit RINGO ():\n"
|
||||
" XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
|
||||
" XINV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
|
||||
" XINV2 $3 (IN=NEXT,$2=$I43,OUT=$I5,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
|
||||
" XINV2 $4 (IN=$I3,$2=$I42,OUT=NEXT,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
|
||||
" XINV2 $5 (IN=$I5,$2=$I44,OUT=$I6,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
|
||||
" XINV2 $6 (IN=$I6,$2=$I45,OUT=$I7,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
|
||||
" XINV2 $7 (IN=$I7,$2=$I46,OUT=$I8,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
|
||||
" XINV2 $8 (IN=$I19,$2=$I39,OUT=$I1,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
|
||||
" XINV2 $9 (IN=$I1,$2=$I40,OUT=$I2,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
|
||||
" XINV2 $10 (IN=$I2,$2=$I41,OUT=$I3,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
|
||||
"Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n"
|
||||
" DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n"
|
||||
" DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n"
|
||||
" DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n"
|
||||
" DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n"
|
||||
" XTRANS $1 ($1=$2,$2=$4,$3=IN)\n"
|
||||
" XTRANS $2 ($1=$2,$2=$5,$3=IN)\n"
|
||||
" XTRANS $3 ($1=$5,$2=OUT,$3=$2)\n"
|
||||
" XTRANS $4 ($1=$4,$2=OUT,$3=$2)\n"
|
||||
"Circuit TRANS ($1=$1,$2=$2,$3=$3):\n"
|
||||
);
|
||||
|
||||
// doesn't do anything here, but we test that this does not destroy anything:
|
||||
nl.combine_devices ();
|
||||
|
||||
// make pins for named nets of top-level circuits - this way they are not purged
|
||||
nl.make_top_level_pins ();
|
||||
nl.purge ();
|
||||
|
||||
// compare netlist as string
|
||||
EXPECT_EQ (nl.to_string (),
|
||||
"Circuit RINGO (FB=FB,OSC=OSC,NEXT=NEXT,'VSSZ,VSS'='VSSZ,VSS','VDDZ,VDD'='VDDZ,VDD'):\n"
|
||||
" XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
|
||||
" XINV2 $2 (IN=FB,$2=(null),OUT=$I19,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
|
||||
" XINV2 $3 (IN=NEXT,$2=(null),OUT=$I5,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
|
||||
" XINV2 $4 (IN=$I3,$2=(null),OUT=NEXT,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
|
||||
" XINV2 $5 (IN=$I5,$2=(null),OUT=$I6,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
|
||||
" XINV2 $6 (IN=$I6,$2=(null),OUT=$I7,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
|
||||
" XINV2 $7 (IN=$I7,$2=(null),OUT=$I8,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
|
||||
" XINV2 $8 (IN=$I19,$2=(null),OUT=$I1,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
|
||||
" XINV2 $9 (IN=$I1,$2=(null),OUT=$I2,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
|
||||
" XINV2 $10 (IN=$I2,$2=(null),OUT=$I3,$4='VSSZ,VSS',$5='VDDZ,VDD')\n"
|
||||
"Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n"
|
||||
" DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n"
|
||||
" DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n"
|
||||
" DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n"
|
||||
" DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n"
|
||||
);
|
||||
|
||||
// compare the collected test data
|
||||
|
||||
std::string au = tl::testsrc ();
|
||||
au = tl::combine_path (au, "testdata");
|
||||
au = tl::combine_path (au, "algo");
|
||||
au = tl::combine_path (au, "device_extract_au1_implicit_nets.gds");
|
||||
|
||||
db::compare_layouts (_this, ly, au);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,8 @@ SOURCES = \
|
|||
tlUri.cc \
|
||||
tlLongInt.cc \
|
||||
tlUniqueId.cc \
|
||||
tlList.cc
|
||||
tlList.cc \
|
||||
tlEquivalenceClusters.cc
|
||||
|
||||
HEADERS = \
|
||||
tlAlgorithm.h \
|
||||
|
|
@ -96,7 +97,8 @@ HEADERS = \
|
|||
tlUri.h \
|
||||
tlLongInt.h \
|
||||
tlUniqueId.h \
|
||||
tlList.h
|
||||
tlList.h \
|
||||
tlEquivalenceClusters.h
|
||||
|
||||
equals(HAVE_CURL, "1") {
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "tlEquivalenceClusters.h"
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
// .. nothing yet ..
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,263 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HDR_tlEquivalenceClusters
|
||||
#define HDR_tlEquivalenceClusters
|
||||
|
||||
#include "tlCommon.h"
|
||||
#include "tlAssert.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A utility class forming clusters based on equivalence of a certain attribute
|
||||
*
|
||||
* To use this class, feed it with equivalences using "is_same", e.g.
|
||||
*
|
||||
* @code
|
||||
* equivalence_clusters<int> eq;
|
||||
* // forms two clusters: 1,2,5 and 3,4
|
||||
* eq.same (1, 2);
|
||||
* eq.same (3, 4);
|
||||
* eq.same (1, 5);
|
||||
* @endcode
|
||||
*
|
||||
* Self-equivalence is a way of introduction an attribute without
|
||||
* an equivalence:
|
||||
*
|
||||
* @code
|
||||
* equivalence_clusters<int> eq;
|
||||
* // after this, 1 is a known attribute
|
||||
* eq.same (1, 1);
|
||||
* eq.has_attribute (1); // ->true
|
||||
* @endcode
|
||||
*
|
||||
* Equivalence clusters can be merged, forming new and bigger clusters.
|
||||
*
|
||||
* Eventually, each cluster is represented by a non-zero integer ID.
|
||||
* The cluster ID can obtained per attribute value using "cluster_id".
|
||||
* In the above example this will be:
|
||||
*
|
||||
* @code
|
||||
* eq.cluster_id (1); // ->1
|
||||
* eq.cluster_id (2); // ->1
|
||||
* eq.cluster_id (3); // ->2
|
||||
* eq.cluster_id (4); // ->2
|
||||
* eq.cluster_id (5); // ->1
|
||||
* eq.cluster_id (6); // ->0 (unknown)
|
||||
* @endcode
|
||||
*
|
||||
* "size" will give the maximum cluster ID.
|
||||
*/
|
||||
template <class T>
|
||||
class equivalence_clusters
|
||||
{
|
||||
public:
|
||||
typedef size_t cluster_id_type;
|
||||
typedef T attribute_type;
|
||||
typedef typename std::vector<typename std::map<T, size_t>::iterator>::const_iterator cluster_iterator;
|
||||
|
||||
/**
|
||||
* @brief Creates an empty equivalence cluster
|
||||
*/
|
||||
equivalence_clusters ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Makes attr1 and attr2 equivalent
|
||||
*/
|
||||
void same (const T &attr1, const T &attr2)
|
||||
{
|
||||
cluster_id_type cl1 = cluster_id (attr1);
|
||||
|
||||
if (attr1 == attr2) {
|
||||
// special case of "self-identity"
|
||||
if (! cl1) {
|
||||
cluster_id_type cl = new_cluster ();
|
||||
insert (attr1, cl);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
cluster_id_type cl2 = cluster_id (attr2);
|
||||
if (! cl1 || ! cl2) {
|
||||
|
||||
if (cl1) {
|
||||
insert (attr2, cl1);
|
||||
} else if (cl2) {
|
||||
insert (attr1, cl2);
|
||||
} else {
|
||||
cluster_id_type cl = new_cluster ();
|
||||
insert (attr1, cl);
|
||||
insert (attr2, cl);
|
||||
}
|
||||
|
||||
} else if (cl1 != cl2) {
|
||||
join (cl1, cl2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true, if attr is part of the equivalence clusters
|
||||
*/
|
||||
bool has_attribute (const T &attr) const
|
||||
{
|
||||
return m_cluster_id_by_attr.find (attr) != m_cluster_id_by_attr.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the cluster ID for the given attribute of 0 if the attribute is not assigned to a cluster
|
||||
*/
|
||||
cluster_id_type cluster_id (const T &attr) const
|
||||
{
|
||||
typename std::map<T, size_t>::const_iterator c = m_cluster_id_by_attr.find (attr);
|
||||
if (c != m_cluster_id_by_attr.end ()) {
|
||||
return c->second;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Applies the equivalences of the other clusters
|
||||
*
|
||||
* This method will join clusters already within this equivalence cluster collection
|
||||
* based on the equivalences listed in the other clusters collection.
|
||||
*
|
||||
* In contrast to merge, his method will not introduce new attributes.
|
||||
*/
|
||||
void apply_equivalences (const equivalence_clusters &other)
|
||||
{
|
||||
std::vector<T> attrs;
|
||||
for (typename std::map<T, size_t>::const_iterator a = m_cluster_id_by_attr.begin (); a != m_cluster_id_by_attr.end (); ++a) {
|
||||
if (other.has_attribute (a->first)) {
|
||||
attrs.push_back (a->first);
|
||||
}
|
||||
}
|
||||
|
||||
for (typename std::vector<T>::const_iterator a = attrs.begin (); a != attrs.end (); ++a) {
|
||||
cluster_id_type cl = other.cluster_id (*a);
|
||||
cluster_iterator b = other.begin_cluster (cl);
|
||||
cluster_iterator e = other.end_cluster (cl);
|
||||
for (cluster_iterator i = b; i != e; ++i) {
|
||||
if ((*i)->first != *a && has_attribute ((*i)->first)) {
|
||||
same ((*i)->first, *a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Merges the equivalences of the other clusters into this
|
||||
*
|
||||
* This method will add all attributes from the other cluster collection and join
|
||||
* clusters based on the equivalences there.
|
||||
*/
|
||||
void merge (const equivalence_clusters &other)
|
||||
{
|
||||
for (cluster_id_type cl = 1; cl <= other.size (); ++cl) {
|
||||
cluster_iterator b = other.begin_cluster (cl);
|
||||
cluster_iterator e = other.end_cluster (cl);
|
||||
for (cluster_iterator i = b; i != e; ++i) {
|
||||
same ((*b)->first, (*i)->first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of clusters kept inside this collection
|
||||
*/
|
||||
size_t size () const
|
||||
{
|
||||
return m_clusters.size ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Begin iterator for the cluster elements for a given cluster ID.
|
||||
* To access, the attribute, use "(*i)->first".
|
||||
*/
|
||||
cluster_iterator begin_cluster (size_t cluster_id) const
|
||||
{
|
||||
tl_assert (cluster_id > 0);
|
||||
return m_clusters [cluster_id - 1].begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator for the cluster elements for a given cluster ID.
|
||||
*/
|
||||
cluster_iterator end_cluster (size_t cluster_id) const
|
||||
{
|
||||
tl_assert (cluster_id > 0);
|
||||
return m_clusters [cluster_id - 1].end ();
|
||||
}
|
||||
|
||||
private:
|
||||
void insert (const T &attr, cluster_id_type into)
|
||||
{
|
||||
typename std::map<T, size_t>::iterator c = m_cluster_id_by_attr.insert (std::make_pair (attr, into)).first;
|
||||
m_clusters [into - 1].push_back (c);
|
||||
}
|
||||
|
||||
void join (cluster_id_type id, cluster_id_type with_id)
|
||||
{
|
||||
tl_assert (id > 0);
|
||||
tl_assert (with_id > 0);
|
||||
std::vector<typename std::map<T, size_t>::iterator> &cnew = m_clusters [id - 1];
|
||||
std::vector<typename std::map<T, size_t>::iterator> &c = m_clusters [with_id - 1];
|
||||
for (typename std::vector<typename std::map<T, size_t>::iterator>::iterator i = c.begin (); i != c.end (); ++i) {
|
||||
(*i)->second = id;
|
||||
cnew.push_back (*i);
|
||||
}
|
||||
|
||||
c.clear ();
|
||||
m_free_slots.push_back (with_id);
|
||||
}
|
||||
|
||||
cluster_id_type new_cluster ()
|
||||
{
|
||||
if (! m_free_slots.empty ()) {
|
||||
cluster_id_type cl = m_free_slots.back ();
|
||||
m_free_slots.pop_back ();
|
||||
return cl;
|
||||
} else {
|
||||
m_clusters.push_back (std::vector<typename std::map<T, size_t>::iterator> ());
|
||||
return m_clusters.size ();
|
||||
}
|
||||
}
|
||||
|
||||
std::map<T, size_t> m_cluster_id_by_attr;
|
||||
std::vector<std::vector<typename std::map<T, size_t>::iterator> > m_clusters;
|
||||
std::vector<size_t> m_free_slots;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,211 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "tlEquivalenceClusters.h"
|
||||
#include "tlUnitTest.h"
|
||||
|
||||
// basics
|
||||
TEST(1_basics)
|
||||
{
|
||||
tl::equivalence_clusters<int> eq;
|
||||
|
||||
eq.same (1, 5);
|
||||
eq.same (2, 3);
|
||||
eq.same (5, 4);
|
||||
|
||||
EXPECT_EQ (eq.cluster_id (1), size_t (1));
|
||||
EXPECT_EQ (eq.cluster_id (2), size_t (2));
|
||||
EXPECT_EQ (eq.cluster_id (3), size_t (2));
|
||||
EXPECT_EQ (eq.cluster_id (4), size_t (1));
|
||||
EXPECT_EQ (eq.cluster_id (5), size_t (1));
|
||||
EXPECT_EQ (eq.cluster_id (6), size_t (0));
|
||||
|
||||
EXPECT_EQ (eq.size (), size_t (2));
|
||||
|
||||
eq.same (2, 6);
|
||||
EXPECT_EQ (eq.cluster_id (6), size_t (2));
|
||||
EXPECT_EQ (eq.cluster_id (7), size_t (0));
|
||||
|
||||
EXPECT_EQ (eq.size (), size_t (2));
|
||||
|
||||
eq.same (7, 8);
|
||||
EXPECT_EQ (eq.size (), size_t (3));
|
||||
EXPECT_EQ (eq.cluster_id (6), size_t (2));
|
||||
EXPECT_EQ (eq.cluster_id (7), size_t (3));
|
||||
EXPECT_EQ (eq.cluster_id (8), size_t (3));
|
||||
}
|
||||
|
||||
// joining of clusters
|
||||
TEST(2_join)
|
||||
{
|
||||
tl::equivalence_clusters<int> eq;
|
||||
|
||||
eq.same (1, 2);
|
||||
eq.same (3, 4);
|
||||
eq.same (5, 6);
|
||||
|
||||
EXPECT_EQ (eq.cluster_id (1), size_t (1));
|
||||
EXPECT_EQ (eq.cluster_id (2), size_t (1));
|
||||
EXPECT_EQ (eq.cluster_id (3), size_t (2));
|
||||
EXPECT_EQ (eq.cluster_id (4), size_t (2));
|
||||
EXPECT_EQ (eq.cluster_id (5), size_t (3));
|
||||
EXPECT_EQ (eq.cluster_id (6), size_t (3));
|
||||
|
||||
eq.same (3, 2);
|
||||
|
||||
EXPECT_EQ (eq.cluster_id (1), size_t (2));
|
||||
EXPECT_EQ (eq.cluster_id (2), size_t (2));
|
||||
EXPECT_EQ (eq.cluster_id (3), size_t (2));
|
||||
EXPECT_EQ (eq.cluster_id (4), size_t (2));
|
||||
EXPECT_EQ (eq.cluster_id (5), size_t (3));
|
||||
EXPECT_EQ (eq.cluster_id (6), size_t (3));
|
||||
|
||||
eq.same (4, 5);
|
||||
|
||||
EXPECT_EQ (eq.cluster_id (1), size_t (2));
|
||||
EXPECT_EQ (eq.cluster_id (2), size_t (2));
|
||||
EXPECT_EQ (eq.cluster_id (3), size_t (2));
|
||||
EXPECT_EQ (eq.cluster_id (4), size_t (2));
|
||||
EXPECT_EQ (eq.cluster_id (5), size_t (2));
|
||||
EXPECT_EQ (eq.cluster_id (6), size_t (2));
|
||||
|
||||
eq.same (10, 11);
|
||||
eq.same (12, 13);
|
||||
|
||||
EXPECT_EQ (eq.cluster_id (10), size_t (3));
|
||||
EXPECT_EQ (eq.cluster_id (11), size_t (3));
|
||||
|
||||
EXPECT_EQ (eq.cluster_id (12), size_t (1));
|
||||
EXPECT_EQ (eq.cluster_id (13), size_t (1));
|
||||
}
|
||||
|
||||
// size
|
||||
TEST(3_size)
|
||||
{
|
||||
tl::equivalence_clusters<int> eq;
|
||||
|
||||
eq.same (1, 2);
|
||||
EXPECT_EQ (eq.size (), size_t (1));
|
||||
eq.same (2, 4);
|
||||
EXPECT_EQ (eq.size (), size_t (1));
|
||||
eq.same (5, 6);
|
||||
EXPECT_EQ (eq.size (), size_t (2));
|
||||
}
|
||||
|
||||
// has_attribute
|
||||
TEST(4_has_attribute)
|
||||
{
|
||||
tl::equivalence_clusters<int> eq;
|
||||
|
||||
eq.same (1, 1);
|
||||
EXPECT_EQ (eq.has_attribute (1), true);
|
||||
EXPECT_EQ (eq.has_attribute (2), false);
|
||||
eq.same (1, 2);
|
||||
EXPECT_EQ (eq.has_attribute (1), true);
|
||||
EXPECT_EQ (eq.has_attribute (2), true);
|
||||
EXPECT_EQ (eq.has_attribute (3), false);
|
||||
}
|
||||
|
||||
std::string eq2string (const tl::equivalence_clusters<int> &eq)
|
||||
{
|
||||
std::string res;
|
||||
for (size_t c = 1; c <= eq.size (); ++c) {
|
||||
for (tl::equivalence_clusters<int>::cluster_iterator i = eq.begin_cluster (c); i != eq.end_cluster (c); ++i) {
|
||||
if (i != eq.begin_cluster (c)) {
|
||||
res += ",";
|
||||
} else if (! res.empty ()) {
|
||||
res += ";";
|
||||
}
|
||||
res += tl::to_string ((*i)->first);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// iterator
|
||||
TEST(5_iterator)
|
||||
{
|
||||
tl::equivalence_clusters<int> eq;
|
||||
|
||||
eq.same (1, 1);
|
||||
EXPECT_EQ (eq2string (eq), "1");
|
||||
|
||||
eq.same (1, 2);
|
||||
EXPECT_EQ (eq2string (eq), "1,2");
|
||||
|
||||
eq.same (3, 4);
|
||||
EXPECT_EQ (eq2string (eq), "1,2;3,4");
|
||||
|
||||
eq.same (10, 11);
|
||||
EXPECT_EQ (eq2string (eq), "1,2;3,4;10,11");
|
||||
|
||||
eq.same (1, 10);
|
||||
EXPECT_EQ (eq2string (eq), "1,2,10,11;3,4");
|
||||
}
|
||||
|
||||
// apply_other_equivalences
|
||||
TEST(6_apply_equivalences)
|
||||
{
|
||||
tl::equivalence_clusters<int> eq;
|
||||
|
||||
eq.same (1, 1);
|
||||
eq.same (2, 2);
|
||||
eq.same (3, 4);
|
||||
eq.same (5, 6);
|
||||
EXPECT_EQ (eq2string (eq), "1;2;3,4;5,6");
|
||||
|
||||
tl::equivalence_clusters<int> eq2;
|
||||
|
||||
eq2.same (2, 2);
|
||||
eq2.same (4, 5);
|
||||
eq2.same (4, 10);
|
||||
eq2.same (11, 11);
|
||||
EXPECT_EQ (eq2string (eq2), "2;4,5,10;11");
|
||||
|
||||
eq.apply_equivalences (eq2);
|
||||
EXPECT_EQ (eq2string (eq), "1;2;5,6,3,4");
|
||||
}
|
||||
|
||||
// merge
|
||||
TEST(7_merge)
|
||||
{
|
||||
tl::equivalence_clusters<int> eq;
|
||||
|
||||
eq.same (1, 1);
|
||||
eq.same (2, 2);
|
||||
eq.same (3, 4);
|
||||
eq.same (5, 6);
|
||||
EXPECT_EQ (eq2string (eq), "1;2;3,4;5,6");
|
||||
|
||||
tl::equivalence_clusters<int> eq2;
|
||||
|
||||
eq2.same (2, 2);
|
||||
eq2.same (4, 5);
|
||||
eq2.same (4, 10);
|
||||
eq2.same (11, 11);
|
||||
EXPECT_EQ (eq2string (eq2), "2;4,5,10;11");
|
||||
|
||||
eq.merge (eq2);
|
||||
EXPECT_EQ (eq2string (eq), "1;2;3,4,5,6,10;11");
|
||||
}
|
||||
|
||||
|
|
@ -36,7 +36,8 @@ SOURCES = \
|
|||
tlInt128Support.cc \
|
||||
tlLongInt.cc \
|
||||
tlUniqueIdTests.cc \
|
||||
tlListTests.cc
|
||||
tlListTests.cc \
|
||||
tlEquivalenceClustersTests.cc
|
||||
|
||||
!equals(HAVE_QT, "0") {
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue