klayout/src/db/db/dbNetlistCompareCore.cc

1702 lines
56 KiB
C++

/*
KLayout Layout Viewer
Copyright (C) 2006-2022 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 "dbNetlistCompareCore.h"
#include "dbNetlistCompareUtils.h"
#include "dbNetlistCompare.h"
#include "dbDevice.h"
#include "dbDeviceClass.h"
#include "dbNet.h"
#include "dbSubCircuit.h"
#include "dbCircuit.h"
#include "tlAssert.h"
#include "tlLog.h"
#include "tlInternational.h"
namespace db
{
// --------------------------------------------------------------------------------------------------------------------
// Some utility classes for NetGraph implementation
template <class Obj>
class generic_mapper_for_target_node
{
public:
generic_mapper_for_target_node ()
{
// .. nothing yet ..
}
static void derive_mapping (const generic_mapper_for_target_node<Obj> &m1, const generic_mapper_for_target_node<Obj> &m2, size_t n1, size_t n2, std::vector<std::pair<const Obj *, const Obj *> > &mapped)
{
if (m1.empty () || m2.empty ()) {
return;
}
const std::set<std::pair<CatAndIds, const Obj *> > &s1 = m1.for_node (n1);
const std::set<std::pair<CatAndIds, const Obj *> > &s2 = m2.for_node (n2);
typename std::set<std::pair<CatAndIds, const Obj *> >::const_iterator i1 = s1.begin (), i2 = s2.begin ();
while (i1 != s1.end () && i2 != s2.end ()) {
if (i1->first < i2->first) {
++i1;
} else if (i2->first < i1->first) {
++i2;
} else {
typename std::set<std::pair<CatAndIds, const Obj *> >::const_iterator i10 = i1, i20 = i2;
size_t n1 = 0, n2 = 0;
while (i1 != s1.end () && i1->first == i10->first) {
++i1;
++n1;
}
while (i2 != s2.end () && i2->first == i20->first) {
++i2;
++n2;
}
if (n1 == 1 && n2 == 1) {
// unique mapping - one device of one category
mapped.push_back (std::make_pair (i10->second, i20->second));
}
}
}
}
protected:
const std::set<std::pair<CatAndIds, const Obj *> > &for_node (size_t ni) const
{
typename std::map<size_t, std::set<std::pair<CatAndIds, const Obj *> > >::const_iterator d = m_per_target_node.find (ni);
tl_assert (d != m_per_target_node.end ());
return d->second;
}
std::set<std::pair<CatAndIds, const Obj *> > &for_node_nc (size_t ni)
{
return m_per_target_node [ni];
}
bool empty () const
{
return m_per_target_node.empty ();
}
private:
std::map<size_t, std::set<std::pair<CatAndIds, const Obj *> > > m_per_target_node;
};
class DeviceMapperForTargetNode
: public generic_mapper_for_target_node<db::Device>
{
public:
DeviceMapperForTargetNode ()
: generic_mapper_for_target_node<db::Device> ()
{
// .. nothing yet ..
}
void insert (const NetGraphNode::edge_type &e)
{
if (e.first.empty ()) {
// happens initially
return;
}
size_t ni = e.second.first;
std::set<std::pair<CatAndIds, const Device *> > &dev = for_node_nc (ni);
for (std::vector<Transition>::const_iterator j = e.first.begin (); j != e.first.end (); ++j) {
if (! j->is_for_subcircuit ()) {
dev.insert (std::make_pair (j->make_key (), j->device ()));
}
}
}
};
class SubCircuitMapperForTargetNode
: public generic_mapper_for_target_node<db::SubCircuit>
{
public:
SubCircuitMapperForTargetNode ()
: generic_mapper_for_target_node<db::SubCircuit> ()
{
// .. nothing yet ..
}
void insert (const NetGraphNode::edge_type &e)
{
if (e.first.empty ()) {
// happens initially
return;
}
size_t ni = e.second.first;
std::set<std::pair<CatAndIds, const SubCircuit *> > &sc = for_node_nc (ni);
for (std::vector<Transition>::const_iterator j = e.first.begin (); j != e.first.end (); ++j) {
if (j->is_for_subcircuit ()) {
sc.insert (std::make_pair (j->make_key (), j->subcircuit ()));
}
}
}
};
// --------------------------------------------------------------------------------------------------------------------
/**
* @brief An audit object which allows reverting tentative node assignments
*/
class TentativeNodeMapping
{
public:
TentativeNodeMapping ()
{ }
~TentativeNodeMapping ()
{
for (std::vector<std::pair<NetGraph *, size_t> >::const_iterator i = m_to_undo.begin (); i != m_to_undo.end (); ++i) {
i->first->unidentify (i->second);
}
for (std::vector<std::pair<NetGraph *, size_t> >::const_iterator i = m_to_undo_to_unknown.begin (); i != m_to_undo_to_unknown.end (); ++i) {
i->first->identify (i->second, unknown_id);
}
for (std::vector<std::pair<DeviceEquivalenceTracker *, std::pair<const db::Device *, const db::Device *> > >::const_iterator i = m_to_undo_devices.begin (); i != m_to_undo_devices.end (); ++i) {
i->first->unmap (i->second.first, i->second.second);
}
for (std::vector<std::pair<SubCircuitEquivalenceTracker *, std::pair<const db::SubCircuit *, const db::SubCircuit *> > >::const_iterator i = m_to_undo_subcircuits.begin (); i != m_to_undo_subcircuits.end (); ++i) {
i->first->unmap (i->second.first, i->second.second);
}
}
static void map_pair (TentativeNodeMapping *nm, NetGraph *g1, size_t n1, NetGraph *g2, size_t n2,
const DeviceMapperForTargetNode &dm1, const DeviceMapperForTargetNode &dm2, DeviceEquivalenceTracker &device_eq,
const SubCircuitMapperForTargetNode &scm1, const SubCircuitMapperForTargetNode &scm2, SubCircuitEquivalenceTracker &subcircuit_eq,
size_t depth, bool exact_match = true)
{
g1->identify (n1, n2, exact_match);
g2->identify (n2, n1, exact_match);
if (nm) {
nm->keep (g1, n1);
nm->keep (g2, n2);
}
derive_device_equivalence (nm, n1, n2, dm1, dm2, device_eq, depth);
derive_subcircuit_equivalence (nm, n1, n2, scm1, scm2, subcircuit_eq, depth);
}
static void map_pair_from_unknown (TentativeNodeMapping *nm, NetGraph *g1, size_t n1, NetGraph *g2, size_t n2,
const DeviceMapperForTargetNode &dm1, const DeviceMapperForTargetNode &dm2, DeviceEquivalenceTracker &device_eq,
const SubCircuitMapperForTargetNode &scm1, const SubCircuitMapperForTargetNode &scm2, SubCircuitEquivalenceTracker &subcircuit_eq,
size_t depth)
{
g1->identify (n1, n2);
g2->identify (n2, n1);
if (nm) {
nm->keep_for_unknown (g1, n1);
nm->keep_for_unknown (g2, n2);
}
derive_device_equivalence (nm, n1, n2, dm1, dm2, device_eq, depth);
derive_subcircuit_equivalence (nm, n1, n2, scm1, scm2, subcircuit_eq, depth);
}
static void map_to_unknown (TentativeNodeMapping *nm, NetGraph *g1, size_t n1)
{
g1->identify (n1, unknown_id);
if (nm) {
nm->keep (g1, n1);
}
}
static void derive_device_equivalence (TentativeNodeMapping *nm, size_t n1, size_t n2,
const DeviceMapperForTargetNode &dm1, const DeviceMapperForTargetNode &dm2, DeviceEquivalenceTracker &device_eq, size_t depth)
{
std::vector<std::pair<const db::Device *, const db::Device *> > device_map;
DeviceMapperForTargetNode::derive_mapping (dm1, dm2, n1, n2, device_map);
for (std::vector<std::pair<const db::Device *, const db::Device *> >::const_iterator dd = device_map.begin (); dd != device_map.end (); ++dd) {
if (device_eq.map (dd->first, dd->second)) {
if (nm) {
nm->keep (&device_eq, dd->first, dd->second);
} else {
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
tl::info << nl_compare_debug_indent (depth) << "enforcing device equivalence: " << dd->first->expanded_name () << " vs. " << dd->second->expanded_name ();
}
}
}
}
}
static void derive_subcircuit_equivalence (TentativeNodeMapping *nm, size_t n1, size_t n2,
const SubCircuitMapperForTargetNode &scm1, const SubCircuitMapperForTargetNode &scm2, SubCircuitEquivalenceTracker &subcircuit_eq, size_t depth)
{
std::vector<std::pair<const db::SubCircuit *, const db::SubCircuit *> > subcircuit_map;
SubCircuitMapperForTargetNode::derive_mapping (scm1, scm2, n1, n2, subcircuit_map);
for (std::vector<std::pair<const db::SubCircuit *, const db::SubCircuit *> >::const_iterator cc = subcircuit_map.begin (); cc != subcircuit_map.end (); ++cc) {
if (subcircuit_eq.map (cc->first, cc->second)) {
if (nm) {
nm->keep (&subcircuit_eq, cc->first, cc->second);
} else {
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
tl::info << nl_compare_debug_indent(depth) << "enforcing subcircuit equivalence: " << cc->first->expanded_name () << " vs. " << cc->second->expanded_name ();
}
}
}
}
}
void clear ()
{
m_to_undo.clear ();
m_to_undo_to_unknown.clear ();
m_to_undo_devices.clear ();
m_to_undo_subcircuits.clear ();
}
void swap (TentativeNodeMapping &other)
{
m_to_undo.swap (other.m_to_undo);
m_to_undo_to_unknown.swap (other.m_to_undo_to_unknown);
m_to_undo_devices.swap (other.m_to_undo_devices);
m_to_undo_subcircuits.swap (other.m_to_undo_subcircuits);
}
std::vector<std::pair<NetGraph *, size_t> > nodes_tracked ()
{
std::vector<std::pair<NetGraph *, size_t> > res = m_to_undo;
res.insert (res.end (), m_to_undo_to_unknown.begin (), m_to_undo_to_unknown.end ());
return res;
}
private:
std::vector<std::pair<NetGraph *, size_t> > m_to_undo, m_to_undo_to_unknown;
std::vector<std::pair<DeviceEquivalenceTracker *, std::pair<const db::Device *, const db::Device *> > > m_to_undo_devices;
std::vector<std::pair<SubCircuitEquivalenceTracker *, std::pair<const db::SubCircuit *, const db::SubCircuit *> > > m_to_undo_subcircuits;
void keep (NetGraph *g1, size_t n1)
{
m_to_undo.push_back (std::make_pair (g1, n1));
}
void keep_for_unknown (NetGraph *g1, size_t n1)
{
m_to_undo_to_unknown.push_back (std::make_pair (g1, n1));
}
void keep (DeviceEquivalenceTracker *dt, const db::Device *a, const db::Device *b)
{
m_to_undo_devices.push_back (std::make_pair (dt, std::make_pair (a, b)));
}
void keep (SubCircuitEquivalenceTracker *dt, const db::SubCircuit *a, const db::SubCircuit *b)
{
m_to_undo_subcircuits.push_back (std::make_pair (dt, std::make_pair (a, b)));
}
};
// --------------------------------------------------------------------------------------------------------------------
/**
* @brief Returns true if the edges (given by transition iterators) are compatible with already established device or subcircuit equivalences.
*/
static bool edges_are_compatible (const NetGraphNode::edge_type &e, const NetGraphNode::edge_type &e_other, const DeviceEquivalenceTracker &device_eq, const SubCircuitEquivalenceTracker &sc_eq)
{
std::vector<Transition>::const_iterator t1 = e.first.begin (), tt1 = e.first.end ();
std::vector<Transition>::const_iterator t2 = e_other.first.begin (), tt2 = e_other.first.end ();
std::vector<void *> p1, p2;
while (t1 != tt1 && t2 != tt2) {
std::vector<Transition>::const_iterator t10 = t1, t20 = t2;
p1.clear ();
while (t1 != tt1 && *t1 == *t10) {
if (t1->is_for_subcircuit ()) {
p1.push_back ((void *) sc_eq.other (t1->subcircuit ()));
} else {
p1.push_back ((void *) device_eq.other (t1->device ()));
}
++t1;
}
p2.clear ();
while (t2 != tt2 && *t2 == *t20) {
if (t2->is_for_subcircuit ()) {
p2.push_back ((void *) (sc_eq.other (t2->subcircuit ()) ? t2->subcircuit () : 0));
} else {
p2.push_back ((void *) (device_eq.other (t2->device ()) ? t2->device () : 0));
}
++t2;
}
std::sort (p1.begin (), p1.end ());
std::sort (p2.begin (), p2.end ());
if (p1 != p2) {
return false;
}
}
tl_assert (t1 == tt1 && t2 == tt2);
return true;
}
// --------------------------------------------------------------------------------------------------------------------
/**
* @brief Represents an interval of NetGraphNode objects in a node set
*/
struct NodeRange
{
NodeRange (size_t _num1, std::vector<NodeEdgePair>::iterator _n1, std::vector<NodeEdgePair>::iterator _nn1,
size_t _num2, std::vector<NodeEdgePair>::iterator _n2, std::vector<NodeEdgePair>::iterator _nn2)
: num1 (_num1), num2 (_num2), n1 (_n1), nn1 (_nn1), n2 (_n2), nn2 (_nn2)
{
// .. nothing yet ..
}
bool operator< (const NodeRange &other) const
{
if (num1 != other.num1) {
return num1 < other.num1;
}
return num2 < other.num2;
}
size_t num1, num2;
std::vector<NodeEdgePair>::iterator n1, nn1, n2, nn2;
};
// --------------------------------------------------------------------------------------------------------------------
// NetlistCompareCore implementation
NetlistCompareCore::NetlistCompareCore (NetGraph *graph, NetGraph *other_graph)
: max_depth (0),
max_n_branch (0),
depth_first (true),
dont_consider_net_names (false),
with_ambiguous (false),
logger (0),
circuit_pin_mapper (0),
subcircuit_equivalence (0),
device_equivalence (0),
progress (0),
mp_graph (graph),
mp_other_graph (other_graph)
{
// .. nothing yet ..
}
size_t
NetlistCompareCore::derive_node_identities_for_edges (NetGraphNode::edge_iterator e, NetGraphNode::edge_iterator ee, NetGraphNode::edge_iterator e_other, NetGraphNode::edge_iterator ee_other, size_t net_index, size_t other_net_index, size_t depth, size_t n_branch, TentativeNodeMapping *tentative) const
{
// NOTE: we can skip edges to known nodes because we did a pre-analysis making sure those are compatible
std::vector<NodeEdgePair> nodes;
nodes.reserve (ee - e);
std::vector<NodeEdgePair> other_nodes;
other_nodes.reserve (ee_other - e_other);
tl_assert (e->first == e_other->first);
for (NetGraphNode::edge_iterator i = e; i != ee; ++i) {
if (i->second.first != net_index) {
const NetGraphNode *nn = &mp_graph->node (i->second.first);
if (! nn->has_other ()) {
nodes.push_back (NodeEdgePair (nn, i));
}
}
}
if (! nodes.empty ()) { // if non-ambiguous, non-assigned
for (NetGraphNode::edge_iterator i = e_other; i != ee_other; ++i) {
if (i->second.first != other_net_index) {
const NetGraphNode *nn = &mp_other_graph->node (i->second.first);
if (! nn->has_other ()) {
other_nodes.push_back (NodeEdgePair (nn, i));
}
}
}
}
if (nodes.empty () || other_nodes.empty ()) {
return 0;
}
if (tentative) {
if (nodes.size () != other_nodes.size ()) {
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
tl::info << nl_compare_debug_indent(depth) << "=> rejected branch.";
}
return failed_match;
}
}
std::sort (nodes.begin (), nodes.end (), CompareNodeEdgePair ());
std::sort (other_nodes.begin (), other_nodes.end (), CompareNodeEdgePair ());
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
// print transitions if requested
tl::info << nl_compare_debug_indent(depth) << "considering transitions:";
bool first = true;
for (std::vector<NodeEdgePair>::const_iterator i = nodes.begin (); i != nodes.end (); ++i) {
const NetGraphNode *nn = i->node;
if (first) {
tl::info << nl_compare_debug_indent (depth) << " here: " << (mp_graph->node (net_index).net () ? mp_graph->node (net_index).net ()->expanded_name ().c_str () : "(null)") << " ->";
first = false;
}
tl::info << nl_compare_debug_indent (depth) << " " << (nn->net () ? nn->net ()->expanded_name ().c_str() : "(null)") << " via: " << tl::noendl;
for (std::vector<Transition>::const_iterator t = i->edge->first.begin (); t != i->edge->first.end(); ++t) {
tl::info << (t != i->edge->first.begin () ? "; " : "") << t->to_string() << tl::noendl;
}
tl::info << "";
}
first = true;
for (std::vector<NodeEdgePair>::const_iterator i = other_nodes.begin (); i != other_nodes.end (); ++i) {
const NetGraphNode *nn = i->node;
if (first) {
tl::info << nl_compare_debug_indent (depth) << " there: " << (mp_other_graph->node (other_net_index).net () ? mp_other_graph->node (other_net_index).net ()->expanded_name ().c_str () : "(null)") << " ->";
first = false;
}
tl::info << nl_compare_debug_indent(depth) << " " << (nn->net() ? nn->net()->expanded_name().c_str() : "(null)") << " via: " << tl::noendl;
for (std::vector<Transition>::const_iterator t = i->edge->first.begin (); t != i->edge->first.end(); ++t) {
tl::info << (t != i->edge->first.begin () ? "; " : "") << t->to_string() << tl::noendl;
}
tl::info << "";
}
}
// for the purpose of match evaluation we require an exact match of the node structure
if (tentative) {
// 1:1 pairing is less strict
if (nodes.size () > 1) {
for (size_t i = 0; i < nodes.size (); ++i) {
if (! (*nodes[i].node == *other_nodes[i].node)) {
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
tl::info << nl_compare_debug_indent(depth) << "=> rejected branch.";
}
return failed_match;
}
}
}
}
// propagate pairing in picky mode: this means we only accept a match if the node set
// is exactly identical and no ambiguous nodes are present when ambiguous nodes are forbidden
size_t bt_count = derive_node_identities_from_node_set (nodes, other_nodes, depth, n_branch, tentative);
if (bt_count == failed_match) {
if (tentative) {
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
tl::info << nl_compare_debug_indent(depth) << "=> rejected branch.";
}
} else {
bt_count = 0;
}
}
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
if (! bt_count) {
tl::info << nl_compare_debug_indent(depth) << "=> no updates.";
}
}
return bt_count;
}
static bool has_subcircuits (db::NetGraphNode::edge_iterator e, db::NetGraphNode::edge_iterator ee)
{
while (e != ee) {
for (std::vector<Transition>::const_iterator t = e->first.begin (); t != e->first.end (); ++t) {
if (t->is_for_subcircuit ()) {
return true;
}
}
++e;
}
return false;
}
size_t
NetlistCompareCore::derive_node_identities (size_t net_index) const
{
return derive_node_identities (net_index, 0, 1, (TentativeNodeMapping *) 0);
}
size_t
NetlistCompareCore::derive_node_identities (size_t net_index, size_t depth, size_t n_branch, TentativeNodeMapping *tentative) const
{
NetGraphNode *n = & mp_graph->node (net_index);
size_t other_net_index = n->other_net_index ();
NetGraphNode *n_other = & mp_other_graph->node (other_net_index);
NetGraphNode nn, nn_other;
// If there are subcircuits on the graph we temporarily create edges from our node to the other nodes of
// the subcircuit. This way we don't need to keep #pin*(#pin-1) edges
if (has_subcircuits (n->begin (), n->end ())) {
nn = *n;
nn.expand_subcircuit_nodes (mp_graph);
n = &nn;
nn_other = *n_other;
nn_other.expand_subcircuit_nodes (mp_other_graph);
n_other = &nn_other;
}
// do a pre-analysis filtering out all nodes with fully satisfied edges or which provide a contradiction
bool analysis_required = false;
for (NetGraphNode::edge_iterator e = n->begin (); e != n->end (); ) {
NetGraphNode::edge_iterator ee = e;
++ee;
while (ee != n->end () && ee->first == e->first) {
++ee;
}
NetGraphNode::edge_iterator e_other = n_other->find_edge (e->first);
if (e_other != n_other->end ()) {
NetGraphNode::edge_iterator ee_other = e_other;
++ee_other;
while (ee_other != n_other->end () && ee_other->first == e_other->first) {
++ee_other;
}
std::vector<const NetGraphNode *> nodes;
nodes.reserve (ee - e);
std::vector<const NetGraphNode *> other_nodes_translated;
other_nodes_translated.reserve (ee_other - e_other);
tl_assert (e->first == e_other->first);
for (NetGraphNode::edge_iterator i = e; i != ee; ++i) {
if (i->second.first != net_index) {
const NetGraphNode *nn = &mp_graph->node (i->second.first);
if (nn->has_other ()) {
nodes.push_back (nn);
} else {
analysis_required = true;
}
}
}
for (NetGraphNode::edge_iterator i = e_other; i != ee_other; ++i) {
if (i->second.first != other_net_index) {
const NetGraphNode *nn = &mp_other_graph->node (i->second.first);
if (nn->has_other ()) {
other_nodes_translated.push_back (&mp_graph->node (nn->other_net_index ()));
} else {
analysis_required = true;
}
}
}
std::sort (nodes.begin (), nodes.end ());
std::sort (other_nodes_translated.begin (), other_nodes_translated.end ());
// No fit, we can shortcut
if (nodes != other_nodes_translated) {
return tentative ? failed_match : 0;
}
} else if (tentative) {
// in tentative mode an exact match is required: no having the same edges for a node disqualifies the node
// as matching.
return failed_match;
}
e = ee;
}
if (tentative) {
// in tentative mode, again an exact match is required
for (NetGraphNode::edge_iterator e_other = n_other->begin (); e_other != n_other->end (); ) {
NetGraphNode::edge_iterator ee_other = e_other;
++ee_other;
while (ee_other != n_other->end () && ee_other->first == e_other->first) {
++ee_other;
}
NetGraphNode::edge_iterator e = n->find_edge (e_other->first);
if (e == n->end ()) {
return failed_match;
}
e_other = ee_other;
}
}
if (! analysis_required) {
return 0;
}
// do a detailed analysis
size_t new_nodes = 0;
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
if (! tentative) {
tl::info << nl_compare_debug_indent(depth) << "deducing from pair: " << n->net ()->expanded_name () << " vs. " << n_other->net ()->expanded_name ();
} else {
tl::info << nl_compare_debug_indent(depth) << "tentatively deducing from pair: " << n->net ()->expanded_name () << " vs. " << n_other->net ()->expanded_name ();
}
}
// non-ambiguous paths to non-assigned nodes create a node identity on the
// end of this path
for (NetGraphNode::edge_iterator e = n->begin (); e != n->end (); ) {
NetGraphNode::edge_iterator ee = e;
++ee;
while (ee != n->end () && ee->first == e->first) {
++ee;
}
NetGraphNode::edge_iterator e_other = n_other->find_edge (e->first);
if (e_other != n_other->end ()) {
NetGraphNode::edge_iterator ee_other = e_other;
++ee_other;
while (ee_other != n_other->end () && ee_other->first == e_other->first) {
++ee_other;
}
size_t bt_count = derive_node_identities_for_edges (e, ee, e_other, ee_other, net_index, other_net_index, depth, n_branch, tentative);
if (bt_count == failed_match) {
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
tl::info << nl_compare_debug_indent(depth) << "=> rejected pair.";
}
return bt_count;
} else {
new_nodes += bt_count;
}
}
e = ee;
}
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
if (! tentative && new_nodes > 0) {
tl::info << nl_compare_debug_indent(depth) << "=> finished pair deduction: " << n->net ()->expanded_name () << " vs. " << n_other->net ()->expanded_name () << " with " << new_nodes << " new pairs";
}
}
return new_nodes;
}
namespace {
struct SortNodeByNet
{
public:
bool operator() (const NodeEdgePair &a, const NodeEdgePair &b) const
{
tl_assert (a.node->net () && b.node->net ());
return name_compare (a.node->net (), b.node->net ()) < 0;
}
};
}
static void sort_node_range_by_best_match (const NodeRange &nr)
{
std::stable_sort (nr.n1, nr.nn1, SortNodeByNet ());
std::stable_sort (nr.n2, nr.nn2, SortNodeByNet ());
std::vector<NodeEdgePair> nomatch1, nomatch2;
nomatch1.reserve (nr.nn1 - nr.n1);
nomatch2.reserve (nr.nn2 - nr.n2);
std::vector<NodeEdgePair>::const_iterator i = nr.n1, j = nr.n2;
std::vector<NodeEdgePair>::iterator iw = nr.n1, jw = nr.n2;
SortNodeByNet compare;
while (i != nr.nn1 || j != nr.nn2) {
if (j == nr.nn2) {
nomatch1.push_back (*i);
++i;
} else if (i == nr.nn1) {
nomatch2.push_back (*j);
++j;
} else if (compare (*i, *j)) {
nomatch1.push_back (*i);
++i;
} else if (compare (*j, *i)) {
nomatch2.push_back (*j);
++j;
} else {
if (iw != i) {
*iw = *i;
}
++iw, ++i;
if (jw != j) {
*jw = *j;
}
++jw, ++j;
}
}
tl_assert (iw + nomatch1.size () == nr.nn1);
tl_assert (jw + nomatch2.size () == nr.nn2);
for (i = nomatch1.begin (); i != nomatch1.end (); ++i) {
*iw++ = *i;
}
for (j = nomatch2.begin (); j != nomatch2.end (); ++j) {
*jw++ = *j;
}
}
size_t
NetlistCompareCore::derive_node_identities_from_ambiguity_group (const NodeRange &nr, DeviceMapperForTargetNode &dm, DeviceMapperForTargetNode &dm_other, SubCircuitMapperForTargetNode &scm, SubCircuitMapperForTargetNode &scm_other, size_t depth, size_t n_branch, TentativeNodeMapping *tentative) const
{
tl::AbsoluteProgress local_progress (tl::to_string (tr ("Deriving match for ambiguous net group")));
std::string indent_s;
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
indent_s = nl_compare_debug_indent (depth);
indent_s += "*" + tl::to_string (n_branch) + " ";
}
size_t new_nodes = 0;
size_t complexity = std::max (nr.num1, nr.num2);
// sort the ambiguity group such that net names match best
std::vector<std::pair<const NetGraphNode *, const NetGraphNode *> > pairs;
std::list<TentativeNodeMapping> tn_for_pairs;
tl::equivalence_clusters<const NetGraphNode *> equivalent_other_nodes;
sort_node_range_by_best_match (nr);
{
// marks the nodes from the ambiguity group as unknown so we don't revisit them (causing deep recursion)
TentativeNodeMapping tn_temp;
// collect and mark the ambiguity combinations to consider
std::vector<std::vector<NodeEdgePair>::const_iterator> iters1, iters2;
for (std::vector<NodeEdgePair>::const_iterator i1 = nr.n1; i1 != nr.nn1; ++i1) {
if (! i1->node->has_any_other ()) {
iters1.push_back (i1);
size_t ni = mp_graph->node_index_for_net (i1->node->net ());
TentativeNodeMapping::map_to_unknown (&tn_temp, mp_graph, ni);
}
}
for (std::vector<NodeEdgePair>::const_iterator i2 = nr.n2; i2 != nr.nn2; ++i2) {
if (! i2->node->has_any_other ()) {
iters2.push_back (i2);
size_t other_ni = mp_other_graph->node_index_for_net (i2->node->net ());
TentativeNodeMapping::map_to_unknown (&tn_temp, mp_other_graph, other_ni);
}
}
for (std::vector<std::vector<NodeEdgePair>::const_iterator>::const_iterator ii1 = iters1.begin (); ii1 != iters1.end (); ++ii1) {
std::vector<NodeEdgePair>::const_iterator i1 = *ii1;
// use net names to resolve ambiguities or for passive nets
// (Rationale for the latter: passive nets cannot be told apart topologically and are typical for blackbox models.
// So the net name is the only differentiator)
bool use_name = ! dont_consider_net_names || i1->node->net ()->is_passive ();
bool use_topology = dont_consider_net_names || i1->node->net ()->is_passive ();
// in tentative mode, reject this choice if nets are named and all other nets in the ambiguity group differ -> this favors net matching by name
if (use_name && tentative) {
bool any_matching = false;
for (std::vector<std::vector<NodeEdgePair>::const_iterator>::iterator ii2 = iters2.begin (); ii2 != iters2.end () && ! any_matching; ++ii2) {
std::vector<NodeEdgePair>::const_iterator i2 = *ii2;
any_matching = !net_names_are_different (i1->node->net (), i2->node->net ());
}
if (! any_matching) {
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
tl::info << indent_s << "ambiguity group rejected - all ambiguous other net names are mismatching for: " << i1->node->net ()->expanded_name ();
}
// a mismatch - stop here.
return failed_match;
}
}
bool any = false;
bool need_rerun = false;
size_t node_count = 0;
std::vector<std::vector<NodeEdgePair>::const_iterator>::iterator to_remove = iters2.end ();
for (std::vector<std::vector<NodeEdgePair>::const_iterator>::iterator ii2 = iters2.begin (); ii2 != iters2.end (); ++ii2) {
++local_progress;
std::vector<NodeEdgePair>::const_iterator i2 = *ii2;
// try this candidate in tentative mode
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
tl::info << indent_s << "trying in tentative mode: " << i1->node->net ()->expanded_name () << " vs. " << i2->node->net ()->expanded_name ();
}
if (! edges_are_compatible (*i1->edge, *i2->edge, *device_equivalence, *subcircuit_equivalence)) {
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
tl::info << indent_s << "=> rejected because edges are incompatible with already established device or subcircuit equivalences";
}
continue;
}
if (use_name && net_names_are_equal (i1->node->net (), i2->node->net ())) {
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
tl::info << indent_s << "=> accepted for identical names";
}
// utilize net names to propose a match
if (any) {
pairs.pop_back ();
}
pairs.push_back (std::make_pair (i1->node, i2->node));
to_remove = ii2;
node_count = 1;
any = true;
break;
} else if (use_topology) {
size_t ni = mp_graph->node_index_for_net (i1->node->net ());
size_t other_ni = mp_other_graph->node_index_for_net (i2->node->net ());
TentativeNodeMapping tn;
TentativeNodeMapping::map_pair_from_unknown (&tn, mp_graph, ni, mp_other_graph, other_ni, dm, dm_other, *device_equivalence, scm, scm_other, *subcircuit_equivalence, depth);
size_t bt_count = derive_node_identities (ni, depth + 1, complexity * n_branch, &tn);
if (bt_count != failed_match) {
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
tl::info << indent_s << "match found";
}
// we have a match ...
if (any) {
// there is already a known pair, so we can mark *i2 and the previous *i2 as equivalent
// (makes them ambiguous)
equivalent_other_nodes.same (i2->node, pairs.back ().second);
// we know enough now ...
break;
} else {
// identified a new pair
node_count = bt_count + 1;
pairs.push_back (std::make_pair (i1->node, i2->node));
to_remove = ii2;
need_rerun = true;
any = true;
// no ambiguity analysis in tentative mode - we can stop now
if (tentative) {
break;
}
}
}
}
}
if (any) {
new_nodes += node_count;
// Add the new pair to the temporary mapping (even in tentative mode)
// Reasoning: doing the mapping may render other nets incompatible, so to ensure "edges_are_compatible" works properly we
// need to lock the current pairs resources such as devices by listing them in the mapping. This is doing by "derive_*_equivalence" inside
// TentativeNodeMapping::map_pair
std::vector<NodeEdgePair>::const_iterator i2 = *to_remove;
size_t ni = mp_graph->node_index_for_net (i1->node->net ());
size_t other_ni = mp_other_graph->node_index_for_net (i2->node->net ());
TentativeNodeMapping::map_pair (&tn_temp, mp_graph, ni, mp_other_graph, other_ni, dm, dm_other, *device_equivalence, scm, scm_other, *subcircuit_equivalence, depth);
if (need_rerun && ! tentative) {
// Re-run the mapping for the selected pair and stash that - this will lock this mapping when investigating other
// branches of the ambiguity resolution tree
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare || tl::verbosity () >= 40) {
tl::info << indent_s << "finalizing decision (rerun tracking): " << i1->node->net ()->expanded_name () << " vs. " << i2->node->net ()->expanded_name ();
}
tn_for_pairs.push_back (TentativeNodeMapping ());
size_t bt_count = derive_node_identities (ni, depth + 1, complexity * n_branch, &tn_for_pairs.back ());
tl_assert (bt_count != failed_match);
}
// now we can get rid of the node and reduce the "other" list of ambiguous nodes
iters2.erase (to_remove);
}
if (! any && tentative) {
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
tl::info << indent_s << "mismatch.";
}
// a mismatch - stop here.
return failed_match;
}
}
}
if (! tentative) {
// issue the matching pairs
// ambiguous pins
std::vector<size_t> pa, pb;
std::set<const db::Net *> seen;
for (std::vector<std::pair<const NetGraphNode *, const NetGraphNode *> >::const_iterator p = pairs.begin (); p != pairs.end (); ++p) {
size_t ni = mp_graph->node_index_for_net (p->first->net ());
size_t other_ni = mp_other_graph->node_index_for_net (p->second->net ());
TentativeNodeMapping::map_pair (0, mp_graph, ni, mp_other_graph, other_ni, dm, dm_other, *device_equivalence, scm, scm_other, *subcircuit_equivalence, depth);
bool ambiguous = equivalent_other_nodes.has_attribute (p->second);
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare || tl::verbosity () >= 40) {
if (ambiguous) {
tl::info << indent_s << "deduced ambiguous match: " << p->first->net ()->expanded_name () << " vs. " << p->second->net ()->expanded_name ();
} else {
tl::info << indent_s << "deduced match: " << p->first->net ()->expanded_name () << " vs. " << p->second->net ()->expanded_name ();
}
}
tl_assert (seen.find (p->first->net ()) == seen.end ());
seen.insert (p->first->net ());
if (ambiguous) {
if (logger) {
if (logger->wants_log_entries ()) {
logger->log_entry (db::NetlistCompareLogger::Warning,
tl::sprintf (tl::to_string (tr ("Matching nets %s from an ambiguous group of nets")), nets2string (p->first->net (), p->second->net ())));
}
logger->match_ambiguous_nets (p->first->net (), p->second->net ());
}
for (db::Net::const_pin_iterator i = p->first->net ()->begin_pins (); i != p->first->net ()->end_pins (); ++i) {
pa.push_back (i->pin ()->id ());
}
for (db::Net::const_pin_iterator i = p->second->net ()->begin_pins (); i != p->second->net ()->end_pins (); ++i) {
pb.push_back (i->pin ()->id ());
}
} else if (logger) {
logger->match_nets (p->first->net (), p->second->net ());
}
++*progress;
}
// Establish further mappings from the mappings stashed during tentative evaluation
std::vector<std::pair<const NetGraphNode *, const NetGraphNode *> >::const_iterator p = pairs.begin ();
for (std::list<TentativeNodeMapping>::iterator tn_of_pair = tn_for_pairs.begin (); tn_of_pair != tn_for_pairs.end (); ++tn_of_pair, ++p) {
bool was_ambiguous = equivalent_other_nodes.has_attribute (p->second);
// Note: this would propagate ambiguities to all *derived* mappings. But this probably goes too far:
// bool ambiguous = was_ambiguous;
// Instead we ignore propagated ambiguitied for now:
bool ambiguous = false;
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare || tl::verbosity () >= 40) {
tl::info << indent_s << "propagating from deduced match: " << p->first->net ()->expanded_name () << " vs. " << p->second->net ()->expanded_name ();
}
std::vector<std::pair<NetGraph *, size_t> > nn = tn_of_pair->nodes_tracked ();
for (std::vector<std::pair<NetGraph *, size_t> >::const_iterator i = nn.begin (); i != nn.end (); ++i) {
if (i->first != mp_graph) {
continue;
}
NetGraphNode *n = & mp_graph->node (i->second);
// tentative evaluation paths may render equivalences which are included in the initial node set,
// hence we filter those out here
if (seen.find (n->net ()) != seen.end ()) {
continue;
}
seen.insert (n->net ());
size_t other_net_index = n->other_net_index ();
NetGraphNode *n_other = & mp_other_graph->node (other_net_index);
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare || tl::verbosity () >= 40) {
if (was_ambiguous) {
tl::info << indent_s << "deduced from ambiguous match: " << n->net ()->expanded_name () << " vs. " << n_other->net ()->expanded_name ();
} else {
tl::info << indent_s << "deduced match: " << n->net ()->expanded_name () << " vs. " << n_other->net ()->expanded_name ();
}
}
if (logger && logger->wants_log_entries () && was_ambiguous) {
logger->log_entry (db::NetlistCompareLogger::Info,
tl::sprintf (tl::to_string (tr ("Matching nets %s following an ambiguous match")), nets2string (n->net (), n_other->net ())));
}
if (ambiguous) {
if (logger) {
logger->match_ambiguous_nets (n->net (), n_other->net ());
}
for (db::Net::const_pin_iterator i = n->net ()->begin_pins (); i != n->net ()->end_pins (); ++i) {
pa.push_back (i->pin ()->id ());
}
for (db::Net::const_pin_iterator i = n_other->net ()->begin_pins (); i != n_other->net ()->end_pins (); ++i) {
pb.push_back (i->pin ()->id ());
}
} else if (logger) {
logger->match_nets (n->net (), n_other->net ());
}
}
tn_of_pair->clear ();
}
// marks pins on ambiguous nets as swappable
if (! pa.empty ()) {
circuit_pin_mapper->map_pins (mp_graph->circuit (), pa);
}
if (! pb.empty ()) {
circuit_pin_mapper->map_pins (mp_other_graph->circuit (), pb);
}
} else {
for (std::vector<std::pair<const NetGraphNode *, const NetGraphNode *> >::const_iterator p = pairs.begin (); p != pairs.end (); ++p) {
size_t ni = mp_graph->node_index_for_net (p->first->net ());
size_t other_ni = mp_other_graph->node_index_for_net (p->second->net ());
TentativeNodeMapping::map_pair (tentative, mp_graph, ni, mp_other_graph, other_ni, dm, dm_other, *device_equivalence, scm, scm_other, *subcircuit_equivalence, depth);
}
}
return new_nodes;
}
size_t
NetlistCompareCore::derive_node_identities_from_singular_match (const NetGraphNode *n, const NetGraphNode::edge_iterator &e, const NetGraphNode *n_other, const NetGraphNode::edge_iterator &e_other, DeviceMapperForTargetNode &dm, DeviceMapperForTargetNode &dm_other, SubCircuitMapperForTargetNode &scm, SubCircuitMapperForTargetNode &scm_other, size_t depth, size_t n_branch, TentativeNodeMapping *tentative, bool consider_net_names) const
{
std::string indent_s;
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
indent_s = nl_compare_debug_indent (depth);
indent_s += "*" + tl::to_string (n_branch) + " ";
}
if (! edges_are_compatible (*e, *e_other, *device_equivalence, *subcircuit_equivalence)) {
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
tl::info << nl_compare_debug_indent(depth) << "=> rejected because edges are incompatible with already established device or subcircuit equivalences";
}
return tentative ? failed_match : 0;
} else if ((! n->has_any_other () && ! n_other->has_any_other ()) || (n->has_unknown_other () && n_other->has_unknown_other ())) { // @@@
// in tentative mode, reject this choice if both nets are named and
// their names differ -> this favors net matching by name
if (tentative && consider_net_names && net_names_are_different (n->net (), n_other->net ())) {
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
tl::info << indent_s << "rejecting pair as names are not identical: " << n->net ()->expanded_name () << " vs. " << n_other->net ()->expanded_name ();
}
return failed_match;
}
// A single candidate: just take this one -> this may render
// inexact matches, but further propagates net pairing
size_t ni = mp_graph->node_index_for_net (n->net ());
size_t other_ni = mp_other_graph->node_index_for_net (n_other->net ());
bool exact_match = (mp_graph->node (ni) == mp_other_graph->node (other_ni));
if (n->has_unknown_other ()) {
TentativeNodeMapping::map_pair_from_unknown (tentative, mp_graph, ni, mp_other_graph, other_ni, dm, dm_other, *device_equivalence, scm, scm_other, *subcircuit_equivalence, depth);
} else {
TentativeNodeMapping::map_pair (tentative, mp_graph, ni, mp_other_graph, other_ni, dm, dm_other, *device_equivalence, scm, scm_other, *subcircuit_equivalence, depth, exact_match);
}
if (! tentative) {
++*progress;
if (logger) {
if (! exact_match) {
// this is a mismatch, but we continue with this
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare || tl::verbosity () >= 40) {
tl::info << indent_s << "deduced mismatch (singular): " << n->net ()->expanded_name () << " vs. " << n_other->net ()->expanded_name ();
}
logger->net_mismatch (n->net (), n_other->net ());
} else {
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare || tl::verbosity () >= 40) {
tl::info << indent_s << "deduced match (singular): " << n->net ()->expanded_name () << " vs. " << n_other->net ()->expanded_name ();
}
logger->match_nets (n->net (), n_other->net ());
}
}
}
size_t new_nodes = 1;
if ((depth_first || tentative) && (max_depth == std::numeric_limits<size_t>::max() || depth < max_depth)) {
size_t bt_count = derive_node_identities (ni, depth + 1, n_branch, tentative);
if (bt_count == failed_match) {
if (tentative) {
return failed_match;
}
} else {
new_nodes += bt_count;
}
}
return new_nodes;
} else if (n->has_other ()) {
// this decision leads to a contradiction
if (mp_other_graph->node_index_for_net (n_other->net ()) != n->other_net_index ()) {
return failed_match;
} else {
return 0;
}
} else {
// mismatch of assignment state
return failed_match;
}
}
static size_t distance (const NetGraphNode &a, const NetGraphNode &b)
{
auto i = a.begin ();
auto j = b.begin ();
size_t fuzz = 0;
while (i != a.end () || j != b.end ()) {
if (j == b.end ()) {
++fuzz;
++i;
continue;
}
if (i == a.end ()) {
++fuzz;
++j;
continue;
}
if (i->first < j->first) {
++fuzz;
++i;
continue;
} else if (j->first < i->first) {
++fuzz;
++j;
continue;
}
++i;
++j;
}
return fuzz;
}
static size_t distance3 (const NetGraphNode &a, const NetGraphNode &b1, const NetGraphNode &b2, const NetGraph &gb)
{
bool needs_join = false;
for (auto e = b1.begin (); e != b1.end () && ! needs_join; ++e) {
needs_join = (e->second.second == b1.net () || e->second.second == b2.net ());
}
for (auto e = b2.begin (); e != b2.end () && ! needs_join; ++e) {
needs_join = (e->second.second == b1.net () || e->second.second == b2.net ());
}
if (needs_join) {
return distance (a, gb.joined (b1, b2));
}
auto i = a.begin ();
auto j1 = b1.begin ();
auto j2 = b2.begin ();
size_t fuzz = 0;
while (i != a.end () || j1 != b1.end () || j2 != b2.end ()) {
if (j1 == b1.end () && j2 == b2.end ()) {
++fuzz;
++i;
continue;
}
bool use_j1 = j2 == b2.end () || (j1 != b1.end () && *j1 < *j2);
auto &j = use_j1 ? j1 : j2;
if (i == a.end ()) {
++fuzz;
++j;
continue;
}
if (i->first < j->first) {
++fuzz;
++i;
continue;
} else if (j->first < i->first) {
++fuzz;
++j;
continue;
}
++i;
++j;
}
return fuzz;
}
static void
analyze_nodes_for_close_matches (const std::multimap<size_t, const NetGraphNode *> &nodes_by_edges1, const std::multimap<size_t, const NetGraphNode *> &nodes_by_edges2, bool layout2ref, db::NetlistCompareLogger *logger, const db::NetGraph &g2)
{
size_t max_search = 100;
double max_fuzz_factor = 0.25;
size_t max_fuzz_count = 3;
size_t max_edges_split = 3; // by how many edges joining will reduce the edge count at max
size_t min_edges = 2;
std::string msg;
if (layout2ref) {
msg = tl::to_string (tr ("Net %s may be shorting nets %s and %s from reference netlist (fuzziness %d nodes)"));
} else {
msg = tl::to_string (tr ("Connecting nets %s and %s is making a better match to net %s from reference netlist (fuzziness %d nodes)"));
}
for (auto i = nodes_by_edges1.begin (); i != nodes_by_edges1.end (); ++i) {
if (i->first < min_edges) {
continue;
}
std::set<const db::NetGraphNode *> seen;
for (auto j = nodes_by_edges2.begin (); j != nodes_by_edges2.end (); ++j) {
seen.insert (j->second);
if (j->first > i->first + max_fuzz_count - 1) {
break;
}
size_t ne = i->first > j->first ? i->first - j->first : 0;
if (ne > max_fuzz_count) {
ne -= max_fuzz_count;
}
if (ne == 0 && layout2ref) {
// analyze nets for similarities (only layout -> ref as the other case is symmetric)
size_t fuzz = distance (*i->second, *j->second);
double fuzz_factor = double (fuzz) / ne;
if (fuzz_factor < max_fuzz_factor) {
std::string msg = tl::to_string (tr ("Net %s from netlist approximately matches net %s from reference netlist (fuzziness %d nodes)"));
logger->log_entry (db::NetlistCompareLogger::Info, tl::sprintf (msg,
i->second->net ()->expanded_name (),
j->second->net ()->expanded_name (),
int (fuzz)));
}
}
auto k = nodes_by_edges2.lower_bound (ne);
size_t tries = max_search;
for ( ; k != nodes_by_edges2.end () && j->first + k->first < i->first + max_fuzz_count + max_edges_split && tries > 0; ++k) {
if (seen.find (k->second) != seen.end ()) {
continue;
}
size_t fuzz = distance3 (*i->second, *j->second, *k->second, g2);
double fuzz_factor = double (fuzz) / i->first;
if (fuzz_factor < max_fuzz_factor) {
logger->log_entry (db::NetlistCompareLogger::Info, tl::sprintf (msg,
(layout2ref ? i : j)->second->net ()->expanded_name (),
(layout2ref ? j : k)->second->net ()->expanded_name (),
(layout2ref ? k : i)->second->net ()->expanded_name (),
int (fuzz)));
}
--tries;
}
}
}
}
void
NetlistCompareCore::analyze_failed_matches () const
{
// Determine the range of nodes with same identity
std::vector<NetGraphNode::edge_type> no_edges;
no_edges.push_back (NetGraphNode::edge_type ());
std::vector<NodeEdgePair> nodes, other_nodes;
nodes.reserve (mp_graph->end () - mp_graph->begin ());
for (db::NetGraph::node_iterator i1 = mp_graph->begin (); i1 != mp_graph->end (); ++i1) {
if (i1->net ()) {
nodes.push_back (NodeEdgePair (i1.operator-> (), no_edges.begin ()));
}
}
other_nodes.reserve (mp_other_graph->end () - mp_other_graph->begin ());
for (db::NetGraph::node_iterator i2 = mp_other_graph->begin (); i2 != mp_other_graph->end (); ++i2) {
if (i2->net ()) {
other_nodes.push_back (NodeEdgePair (i2.operator-> (), no_edges.begin ()));
}
}
std::sort (nodes.begin (), nodes.end (), CompareNodeEdgePair ());
std::sort (other_nodes.begin (), other_nodes.end (), CompareNodeEdgePair ());
auto n1 = nodes.begin ();
auto n2 = other_nodes.begin ();
std::vector<const db::NetGraphNode *> singular1, singular2;
while (n1 != nodes.end () || n2 != other_nodes.end ()) {
if (n2 == other_nodes.end ()) {
singular1.push_back (n1->node);
++n1;
continue;
} else if (n1 == nodes.end ()) {
singular2.push_back (n2->node);
++n2;
continue;
}
if (*n1->node < *n2->node) {
singular1.push_back (n1->node);
++n1;
continue;
} else if (*n2->node < *n1->node) {
singular2.push_back (n2->node);
++n2;
continue;
}
++n1;
++n2;
}
for (auto i = singular1.begin (); i != singular1.end (); ++i) {
logger->log_entry (db::NetlistCompareLogger::Error, tl::sprintf (tl::to_string (tr ("Net %s from primary netlist is not matching any net from reference netlist")), (*i)->net ()->expanded_name ()));
}
for (auto i = singular2.begin (); i != singular2.end (); ++i) {
logger->log_entry (db::NetlistCompareLogger::Error, tl::sprintf (tl::to_string (tr ("Net %s from reference netlist is not matching any net from primary netlist")), (*i)->net ()->expanded_name ()));
}
// attempt some analysis for close matches (including shorts / opens)
std::multimap<size_t, const NetGraphNode *> nodes_by_edges1, nodes_by_edges2;
for (auto i = singular1.begin (); i != singular1.end (); ++i) {
const NetGraphNode *n = *i;
nodes_by_edges1.insert (std::make_pair (n->end () - n->begin (), n));
}
for (auto i = singular2.begin (); i != singular2.end (); ++i) {
const NetGraphNode *n = *i;
nodes_by_edges2.insert (std::make_pair (n->end () - n->begin (), n));
}
analyze_nodes_for_close_matches (nodes_by_edges1, nodes_by_edges2, true, logger, *mp_other_graph);
analyze_nodes_for_close_matches (nodes_by_edges2, nodes_by_edges1, false, logger, *mp_graph);
}
size_t
NetlistCompareCore::derive_node_identities_from_node_set (std::vector<NodeEdgePair> &nodes, std::vector<NodeEdgePair> &other_nodes) const
{
return derive_node_identities_from_node_set (nodes, other_nodes, 0, 1, (TentativeNodeMapping *) 0);
}
size_t
NetlistCompareCore::derive_node_identities_from_node_set (std::vector<NodeEdgePair> &nodes, std::vector<NodeEdgePair> &other_nodes, size_t depth, size_t n_branch, TentativeNodeMapping *tentative) const
{
std::string indent_s;
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
indent_s = nl_compare_debug_indent (depth);
indent_s += "*" + tl::to_string (n_branch) + " ";
}
DeviceMapperForTargetNode dm;
SubCircuitMapperForTargetNode scm;
for (std::vector<NodeEdgePair>::const_iterator i = nodes.begin (); i != nodes.end (); ++i) {
dm.insert (*i->edge);
scm.insert (*i->edge);
}
DeviceMapperForTargetNode dm_other;
SubCircuitMapperForTargetNode scm_other;
for (std::vector<NodeEdgePair>::const_iterator i = other_nodes.begin (); i != other_nodes.end (); ++i) {
dm_other.insert (*i->edge);
scm_other.insert (*i->edge);
}
if (nodes.size () == 1 && other_nodes.size () == 1) {
return derive_node_identities_from_singular_match (nodes.front ().node, nodes.front ().edge, other_nodes.front ().node, other_nodes.front ().edge,
dm, dm_other, scm, scm_other, depth, n_branch, tentative, false /*don't consider net names*/);
}
if (max_depth != std::numeric_limits<size_t>::max() && depth > max_depth) {
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
tl::info << indent_s << "max. depth exhausted (" << depth << ">" << max_depth << ")";
}
return failed_match;
}
// Determine the range of nodes with same identity
std::vector<NodeRange> node_ranges;
size_t new_nodes = 0;
std::vector<NodeEdgePair>::iterator n1 = nodes.begin ();
std::vector<NodeEdgePair>::iterator n2 = other_nodes.begin ();
while (n1 != nodes.end () && n2 != other_nodes.end ()) {
if (n1->node->has_other ()) {
++n1;
continue;
} else if (n2->node->has_other ()) {
++n2;
continue;
}
if (*n1->node < *n2->node) {
++n1;
continue;
} else if (*n2->node < *n1->node) {
++n2;
continue;
}
std::vector<NodeEdgePair>::iterator nn1 = n1, nn2 = n2;
++nn1;
++nn2;
size_t num1 = 1;
while (nn1 != nodes.end () && *nn1->node == *n1->node) {
if (! nn1->node->has_other ()) {
++num1;
}
++nn1;
}
size_t num2 = 1;
while (nn2 != other_nodes.end () && *nn2->node == *n2->node) {
if (! nn2->node->has_other ()) {
++num2;
}
++nn2;
}
if ((num1 == 1 && num2 == 1) || with_ambiguous) {
node_ranges.push_back (NodeRange (num1, n1, nn1, num2, n2, nn2));
}
// in tentative mode ambiguous nodes don't make a match without
// with_ambiguous
if ((num1 > 1 || num2 > 1) && tentative && ! with_ambiguous) {
return failed_match;
}
n1 = nn1;
n2 = nn2;
}
if (with_ambiguous) {
std::stable_sort (node_ranges.begin (), node_ranges.end ());
}
for (std::vector<NodeRange>::iterator nr = node_ranges.begin (); nr != node_ranges.end (); ++nr) {
// node ranges might have changed - adjust to real count and skip leading pairs assigned already
while (nr->n1 != nr->nn1 && nr->n2 != nr->nn2) {
if (nr->n1->node->has_other ()) {
++nr->n1;
} else if (nr->n2->node->has_other ()) {
++nr->n2;
} else {
break;
}
}
nr->num1 = 0;
for (std::vector<NodeEdgePair>::const_iterator i = nr->n1; i != nr->nn1; ++i) {
if (! i->node->has_other ()) {
++nr->num1;
}
}
nr->num2 = 0;
for (std::vector<NodeEdgePair>::const_iterator i = nr->n2; i != nr->nn2; ++i) {
if (! i->node->has_other ()) {
++nr->num2;
}
}
if (nr->num1 < 1 || nr->num2 < 1) {
// ignore this - it got obsolete.
} else if (nr->num1 == 1 && nr->num2 == 1) {
size_t n = derive_node_identities_from_singular_match (nr->n1->node, nr->n1->edge, nr->n2->node, nr->n2->edge, dm, dm_other, scm, scm_other, depth, n_branch, tentative, ! dont_consider_net_names);
if (n == failed_match) {
return failed_match;
}
new_nodes += n;
} else if (max_n_branch != std::numeric_limits<size_t>::max () && double (std::max (nr->num1, nr->num2)) * double (n_branch) > double (max_n_branch)) {
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
tl::info << indent_s << "max. complexity exhausted (" << std::max (nr->num1, nr->num2) << "*" << n_branch << ">" << max_n_branch << ") - mismatch.";
}
return failed_match;
} else {
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
tl::info << indent_s << "analyzing ambiguity group with " << nr->num1 << "/" << nr->num2 << " members";
}
size_t n = derive_node_identities_from_ambiguity_group (*nr, dm, dm_other, scm, scm_other, depth, n_branch, tentative);
if (n == failed_match) {
return failed_match;
}
new_nodes += n;
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
tl::info << indent_s << "finished analysis of ambiguity group with " << nr->num1 << "/" << nr->num2 << " members";
}
}
}
return new_nodes;
}
}