mirror of https://github.com/KLayout/klayout.git
524 lines
14 KiB
C++
524 lines
14 KiB
C++
|
|
/*
|
|
|
|
KLayout Layout Viewer
|
|
Copyright (C) 2006-2017 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 "dbLayout.h"
|
|
#include "dbCellGraphUtils.h"
|
|
#include "dbFuzzyCellMapping.h"
|
|
#include "tlLog.h"
|
|
#include "tlTimer.h"
|
|
#include "tlProgress.h"
|
|
#include "tlFixedVector.h"
|
|
|
|
#include <memory>
|
|
#include <map>
|
|
#include <set>
|
|
#include <vector>
|
|
|
|
namespace db
|
|
{
|
|
|
|
class TransformationMatrixSum
|
|
{
|
|
public:
|
|
TransformationMatrixSum (const db::Layout &, const db::Cell &)
|
|
{
|
|
}
|
|
|
|
TransformationMatrixSum (const db::Matrix2d &m)
|
|
{
|
|
m_m = m;
|
|
}
|
|
|
|
TransformationMatrixSum transformed (const db::CellInstArray &inst) const
|
|
{
|
|
TransformationMatrixSum result (*this);
|
|
result.transform (inst);
|
|
return result;
|
|
}
|
|
|
|
void transform (const db::CellInstArray &inst)
|
|
{
|
|
m_m = db::Matrix2d (inst.complex_trans ().inverted ());
|
|
}
|
|
|
|
void add (const TransformationMatrixSum &other)
|
|
{
|
|
m_m += other.m_m;
|
|
}
|
|
|
|
const db::Matrix2d &m () const { return m_m; }
|
|
|
|
private:
|
|
db::Matrix2d m_m;
|
|
};
|
|
|
|
class InstanceReferenceSum
|
|
{
|
|
public:
|
|
InstanceReferenceSum (const db::Layout &, const db::Cell &)
|
|
: m_count (0)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
InstanceReferenceSum (size_t count, const db::Matrix2d &m, const db::DPoint &p)
|
|
: m_count (count), m_m (m), m_p (p)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
InstanceReferenceSum transformed (const db::CellInstArray &inst) const
|
|
{
|
|
db::Matrix2d m_res;
|
|
db::DVector p_res;
|
|
|
|
m_res += db::Matrix2d (inst.complex_trans ()) * double (inst.size ());
|
|
|
|
for (db::CellInstArray::iterator a = inst.begin (); ! a.at_end (); ++a) {
|
|
p_res += db::DVector ((*a).disp ());
|
|
}
|
|
|
|
if (m_count == 0) {
|
|
return InstanceReferenceSum (inst.size (), m_res, m_p + p_res);
|
|
} else {
|
|
return InstanceReferenceSum (m_count * inst.size (), m_m * m_res, m_p + m_m * p_res);
|
|
}
|
|
}
|
|
|
|
void add (const InstanceReferenceSum &other)
|
|
{
|
|
m_count += other.m_count;
|
|
m_p += db::DVector (other.m_p);
|
|
m_m += other.m_m;
|
|
}
|
|
|
|
size_t n () const { return m_count; }
|
|
const db::DPoint &p () const { return m_p; }
|
|
const db::Matrix2d &m () const { return m_m; }
|
|
|
|
private:
|
|
size_t m_count;
|
|
db::Matrix2d m_m;
|
|
db::DPoint m_p;
|
|
};
|
|
|
|
// -------------------------------------------------------------------------------------
|
|
// FuzzyCellMapping implementation
|
|
|
|
FuzzyCellMapping::FuzzyCellMapping ()
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
void FuzzyCellMapping::clear ()
|
|
{
|
|
m_b2a_mapping.clear ();
|
|
}
|
|
|
|
inline double distance_func (double a, double b)
|
|
{
|
|
if (fabs (a) + fabs (b) < 1e-6) {
|
|
return 0.0;
|
|
} else {
|
|
return 2.0 * fabs (a - b) / (fabs (a) + fabs (b));
|
|
}
|
|
}
|
|
|
|
struct CellSignature
|
|
{
|
|
size_t weight;
|
|
db::Box bbox;
|
|
size_t instances;
|
|
std::vector<size_t> shapes;
|
|
db::Matrix2d tm_avg;
|
|
db::DPoint p_avg;
|
|
|
|
bool distance_less_or_equal (const CellSignature &other, double dmin, double &dmin_out) const
|
|
{
|
|
double d;
|
|
d = distance_func (weight, other.weight);
|
|
if (dmin >= 0.0 && d > dmin + 1e-6) {
|
|
return false;
|
|
}
|
|
|
|
d += distance_func (bbox.left (), other.bbox.left ());
|
|
if (dmin >= 0.0 && d > dmin + 1e-6) {
|
|
return false;
|
|
}
|
|
|
|
d += distance_func (bbox.top (), other.bbox.top ());
|
|
if (dmin >= 0.0 && d > dmin + 1e-6) {
|
|
return false;
|
|
}
|
|
|
|
d += distance_func (bbox.right (), other.bbox.right ());
|
|
if (dmin >= 0.0 && d > dmin + 1e-6) {
|
|
return false;
|
|
}
|
|
|
|
d += distance_func (bbox.bottom (), other.bbox.bottom ());
|
|
if (dmin >= 0.0 && d > dmin + 1e-6) {
|
|
return false;
|
|
}
|
|
|
|
d += distance_func (instances, other.instances);
|
|
if (dmin >= 0.0 && d > dmin + 1e-6) {
|
|
return false;
|
|
}
|
|
|
|
std::vector<size_t>::const_iterator s1, s2;
|
|
for (s1 = shapes.begin (), s2 = other.shapes.begin (); s1 != shapes.end (); ++s1, ++s2) {
|
|
d += distance_func (*s1, *s2);
|
|
if (dmin >= 0.0 && d > dmin + 1e-6) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
d += distance_func (tm_avg.m11 (), other.tm_avg.m11 ());
|
|
if (dmin >= 0.0 && d > dmin + 1e-6) {
|
|
return false;
|
|
}
|
|
|
|
d += distance_func (tm_avg.m12 (), other.tm_avg.m12 ());
|
|
if (dmin >= 0.0 && d > dmin + 1e-6) {
|
|
return false;
|
|
}
|
|
|
|
d += distance_func (tm_avg.m21 (), other.tm_avg.m21 ());
|
|
if (dmin >= 0.0 && d > dmin + 1e-6) {
|
|
return false;
|
|
}
|
|
|
|
d += distance_func (tm_avg.m22 (), other.tm_avg.m22 ());
|
|
if (dmin >= 0.0 && d > dmin + 1e-6) {
|
|
return false;
|
|
}
|
|
|
|
d += distance_func (p_avg.x (), other.p_avg.x ());
|
|
if (dmin >= 0.0 && d > dmin + 1e-6) {
|
|
return false;
|
|
}
|
|
|
|
d += distance_func (p_avg.y (), other.p_avg.y ());
|
|
if (dmin >= 0.0 && d > dmin + 1e-6) {
|
|
return false;
|
|
}
|
|
|
|
dmin_out = d;
|
|
|
|
return true;
|
|
}
|
|
|
|
std::string to_string () const
|
|
{
|
|
std::string st;
|
|
for (std::vector <size_t>::const_iterator s = shapes.begin (); s != shapes.end (); ++s) {
|
|
if (! st.empty ()) {
|
|
st += ",";
|
|
}
|
|
st += tl::to_string (*s);
|
|
}
|
|
|
|
return "weight=" + tl::to_string (weight) +
|
|
" bbox=" + bbox.to_string () +
|
|
" instances=" + tl::to_string (instances) +
|
|
" shapes=" + st +
|
|
" tm_avg=" + tm_avg.to_string () +
|
|
" p_avg=" + p_avg.to_string ();
|
|
}
|
|
};
|
|
|
|
static void collect_cell_signatures (const db::Layout &layout, const std::vector <unsigned int> &layers, db::cell_index_type cell_index, std::map <db::cell_index_type, CellSignature> &metrics, const std::string &progress_report)
|
|
{
|
|
db::InstanceStatistics<InstanceReferenceSum> rs (&layout, cell_index);
|
|
|
|
tl::RelativeProgress progress (progress_report, rs.selection ().size ());
|
|
|
|
for (db::CellCounter::selection_iterator c = rs.begin (); c != rs.end (); ++c) {
|
|
|
|
++progress;
|
|
|
|
const db::Cell &cell = layout.cell (*c);
|
|
const InstanceReferenceSum &rsv = rs.value (*c);
|
|
|
|
CellSignature &m = metrics.insert (std::make_pair (*c, CellSignature ())).first->second;
|
|
|
|
m.shapes.reserve (layers.size ());
|
|
for (std::vector <unsigned int>::const_iterator l = layers.begin (); l != layers.end (); ++l) {
|
|
const db::Shapes &shapes = cell.shapes (*l);
|
|
size_t n = 0;
|
|
// TODO: right now, the only way to get the "true" shape count is to iterate ...
|
|
for (db::ShapeIterator s = shapes.begin (db::ShapeIterator::All); ! s.at_end (); ++s) {
|
|
++n;
|
|
}
|
|
m.shapes.push_back (n);
|
|
}
|
|
|
|
m.weight = rsv.n ();
|
|
|
|
m.bbox = cell.bbox ();
|
|
size_t ni = 0;
|
|
for (db::Instances::const_iterator i = cell.begin (); ! i.at_end (); ++i) {
|
|
ni += i->size ();
|
|
}
|
|
m.instances = ni;
|
|
|
|
double n = std::max (1.0, double (m.weight));
|
|
m.tm_avg = rsv.m () * (1.0 / n);
|
|
m.p_avg = rsv.p () * (1.0 / n);
|
|
|
|
if (tl::verbosity () >= 40) {
|
|
tl::info << " " << layout.cell_name (*c) << " " << m.to_string ();
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
FuzzyCellMapping::create (const db::Layout &layout_a, db::cell_index_type cell_index_a, const db::Layout &layout_b, db::cell_index_type cell_index_b)
|
|
{
|
|
tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (QObject::tr ("Cell mapping")));
|
|
|
|
if (tl::verbosity () >= 20) {
|
|
tl::info << "Cell mapping";
|
|
}
|
|
|
|
std::vector <unsigned int> la, lb;
|
|
|
|
for (db::Layout::layer_iterator l = layout_a.begin_layers (); l != layout_a.end_layers (); ++l) {
|
|
for (db::Layout::layer_iterator ll = layout_b.begin_layers (); ll != layout_b.end_layers (); ++ll) {
|
|
if ((*l).second->log_equal (*(*ll).second)) {
|
|
la.push_back ((*l).first);
|
|
lb.push_back ((*ll).first);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (tl::verbosity () >= 40) {
|
|
tl::info << "Signatures (a):";
|
|
}
|
|
|
|
std::map <db::cell_index_type, CellSignature> ma;
|
|
collect_cell_signatures (layout_a, la, cell_index_a, ma, tl::to_string (QObject::tr ("Collecting cell signatures (A)")));
|
|
|
|
if (tl::verbosity () >= 40) {
|
|
tl::info << "Signatures (b):";
|
|
}
|
|
|
|
std::map <db::cell_index_type, CellSignature> mb;
|
|
collect_cell_signatures (layout_b, lb, cell_index_b, mb, tl::to_string (QObject::tr ("Collecting cell signatures (B)")));
|
|
|
|
tl::RelativeProgress progress (tl::to_string (QObject::tr ("Finding matching cells")), ma.size () * ma.size ());
|
|
|
|
for (std::map <db::cell_index_type, CellSignature>::const_iterator m = ma.begin (); m != ma.end (); ++m) {
|
|
|
|
if (tl::verbosity () >= 30) {
|
|
tl::info << "Treating cell (a) " << layout_a.cell_name (m->first);
|
|
}
|
|
|
|
++progress;
|
|
|
|
// look up the nearest match in the "b" cells.
|
|
double dmin = -1.0;
|
|
std::vector <db::cell_index_type> cmin;
|
|
for (std::map <db::cell_index_type, CellSignature>::const_iterator n = mb.begin (); n != mb.end (); ++n) {
|
|
|
|
++progress;
|
|
|
|
double d = -1.0;
|
|
if (n->second.distance_less_or_equal (m->second, dmin, d)) {
|
|
|
|
if (dmin >= 0.0 && distance_func (d, dmin) > 1e-6) {
|
|
cmin.clear ();
|
|
}
|
|
|
|
dmin = d;
|
|
cmin.push_back (n->first);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (tl::verbosity () >= 40) {
|
|
tl::info << "First-level candidates (b):" << tl::noendl;
|
|
for (std::vector<db::cell_index_type>::const_iterator c = cmin.begin (); c != cmin.end (); ++c) {
|
|
tl::info << " " << layout_b.cell_name (*c) << tl::noendl;
|
|
}
|
|
tl::info << "";
|
|
}
|
|
|
|
std::vector <db::cell_index_type> cmin_confirmed;
|
|
|
|
// if a unique match was found, confirm the match by looking if there is a "a" cell matching better.
|
|
for (std::vector <db::cell_index_type>::const_iterator c = cmin.begin (); c != cmin.end (); ++c) {
|
|
|
|
bool confirmed = true;
|
|
|
|
const CellSignature &mmin = mb [*c];
|
|
std::map <db::cell_index_type, CellSignature>::const_iterator mm = m;
|
|
++mm;
|
|
for ( ; mm != ma.end () && confirmed; ++mm) {
|
|
|
|
++progress;
|
|
double d = -1.0;
|
|
if (mmin.distance_less_or_equal (mm->second, dmin, d) && distance_func (d, dmin) > 1e-6) {
|
|
confirmed = false;
|
|
}
|
|
|
|
}
|
|
|
|
if (confirmed) {
|
|
cmin_confirmed.push_back (*c);
|
|
}
|
|
|
|
}
|
|
|
|
std::swap (cmin, cmin_confirmed);
|
|
|
|
if (tl::verbosity () >= 40) {
|
|
tl::info << "Confirmed candidates (b):" << tl::noendl;
|
|
for (std::vector<db::cell_index_type>::const_iterator c = cmin.begin (); c != cmin.end (); ++c) {
|
|
tl::info << " " << layout_b.cell_name (*c) << tl::noendl;
|
|
}
|
|
tl::info << "";
|
|
}
|
|
|
|
// If there is no unique mapping, use the name similarity (measured by the edit distance)
|
|
// as a distance measure
|
|
if (cmin.size () > 1) {
|
|
|
|
int min_ed = std::numeric_limits<int>::max ();
|
|
db::cell_index_type min_ed_ci;
|
|
|
|
for (std::vector<db::cell_index_type>::const_iterator c = cmin.begin (); c != cmin.end (); ++c) {
|
|
|
|
int ed = tl::edit_distance (layout_a.cell_name (m->first), layout_b.cell_name (*c));
|
|
if (ed < min_ed) {
|
|
min_ed = ed;
|
|
min_ed_ci = *c;
|
|
}
|
|
|
|
}
|
|
|
|
cmin.clear ();
|
|
cmin.push_back (min_ed_ci);
|
|
|
|
}
|
|
|
|
if (tl::verbosity () >= 40) {
|
|
tl::info << "Refined candidates (b):" << tl::noendl;
|
|
for (std::vector<db::cell_index_type>::const_iterator c = cmin.begin (); c != cmin.end (); ++c) {
|
|
tl::info << " " << layout_b.cell_name (*c) << tl::noendl;
|
|
}
|
|
tl::info << "";
|
|
}
|
|
|
|
if (cmin.size () > 0) {
|
|
|
|
#if 0 // debugging
|
|
if (std::string (layout_a.cell_name (m->first)) != std::string (layout_b.cell_name (cmin [0]))) {
|
|
tl::info << "Name mismatch " << layout_a.cell_name (m->first) << " and " << layout_b.cell_name (cmin [0]);
|
|
tl::info << " (A) signature " << m->second.to_string ();
|
|
tl::info << " (B) signature " << mb [cmin [0]].to_string ();
|
|
}
|
|
#endif
|
|
|
|
if (tl::verbosity () >= 30) {
|
|
tl::info << "Cell mapping - found a matching pair " << layout_a.cell_name (m->first) << " and " << layout_b.cell_name (cmin [0]);
|
|
}
|
|
|
|
mb.erase (cmin [0]);
|
|
m_b2a_mapping.insert (std::make_pair (cmin [0], m->first));
|
|
|
|
} else {
|
|
|
|
if (tl::verbosity () >= 30) {
|
|
if (cmin.size () == 0) {
|
|
tl::info << "Cell mapping - no match found for " << layout_a.cell_name (m->first);
|
|
} else {
|
|
tl::info << "Cell mapping - multiple matches found for " << layout_a.cell_name (m->first) << ": " << tl::noendl;
|
|
for (size_t i = 0; i < cmin.size (); ++i) {
|
|
if (i > 0) {
|
|
tl::info << ", " << tl::noendl;
|
|
}
|
|
tl::info << layout_b.cell_name (cmin [i]) << tl::noendl;
|
|
}
|
|
tl::info << ""; // implies eol
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void
|
|
FuzzyCellMapping::dump_mapping (const std::map <db::cell_index_type, std::vector<db::cell_index_type> > &candidates,
|
|
const db::Layout &layout_a, const db::Layout &layout_b)
|
|
{
|
|
for (std::map <db::cell_index_type, std::vector<db::cell_index_type> >::const_iterator cand = candidates.begin (); cand != candidates.end (); ++cand) {
|
|
tl::info << " " << layout_a.cell_name (cand->first) << " ->" << tl::noendl;
|
|
int n = 5;
|
|
for (std::vector<db::cell_index_type>::const_iterator c = cand->second.begin (); c != cand->second.end () && --n > 0; ++c) {
|
|
tl::info << " " << layout_b.cell_name (*c) << tl::noendl;
|
|
}
|
|
if (n == 0) {
|
|
tl::info << " ..";
|
|
} else {
|
|
tl::info << "";
|
|
}
|
|
}
|
|
}
|
|
|
|
std::pair<bool, db::cell_index_type>
|
|
FuzzyCellMapping::cell_mapping_pair (db::cell_index_type cell_index_b) const
|
|
{
|
|
std::map <db::cell_index_type, db::cell_index_type>::const_iterator m = m_b2a_mapping.find (cell_index_b);
|
|
if (m == m_b2a_mapping.end ()) {
|
|
return std::make_pair (false, 0);
|
|
} else {
|
|
return std::make_pair (true, m->second);
|
|
}
|
|
}
|
|
|
|
bool
|
|
FuzzyCellMapping::has_mapping (db::cell_index_type cell_index_b) const
|
|
{
|
|
std::map <db::cell_index_type, db::cell_index_type>::const_iterator m = m_b2a_mapping.find (cell_index_b);
|
|
return (m != m_b2a_mapping.end ());
|
|
}
|
|
|
|
db::cell_index_type
|
|
FuzzyCellMapping::cell_mapping (db::cell_index_type cell_index_b) const
|
|
{
|
|
std::map <db::cell_index_type, db::cell_index_type>::const_iterator m = m_b2a_mapping.find (cell_index_b);
|
|
tl_assert (m != m_b2a_mapping.end ());
|
|
return m->second;
|
|
}
|
|
|
|
}
|
|
|