Faster hierarchical edges.

This commit is contained in:
Matthias Koefferlein 2021-04-06 21:05:02 +02:00
parent a863bbd0ba
commit 9b7879b2a9
13 changed files with 273 additions and 38 deletions

View File

@ -1070,9 +1070,114 @@ DeepRegion::snapped (db::Coord gx, db::Coord gy)
return res.release ();
}
namespace
{
class PolygonToEdgeLocalOperation
: public local_operation<db::PolygonRef, db::PolygonRef, db::Edge>
{
public:
PolygonToEdgeLocalOperation ()
: local_operation<db::PolygonRef, db::PolygonRef, db::Edge> ()
{
// .. nothing yet ..
}
virtual db::Coord dist () const { return 1; }
virtual bool requests_single_subjects () const { return true; }
virtual std::string description () const { return std::string ("polygon to edges"); }
virtual void do_compute_local (db::Layout * /*layout*/, const shape_interactions<db::PolygonRef, db::PolygonRef> &interactions, std::vector<std::unordered_set<db::Edge> > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const
{
db::EdgeProcessor ep;
for (shape_interactions<db::PolygonRef, db::PolygonRef>::subject_iterator s = interactions.begin_subjects (); s != interactions.end_subjects (); ++s) {
ep.insert (s->second);
}
if (interactions.num_intruders () == 0) {
db::EdgeToEdgeSetGenerator eg (results.front ());
db::MergeOp op (0);
ep.process (eg, op);
} else {
// With intruders: to compute our local contribution we take the edges without and with intruders
// and deliver what is in both sets
db::MergeOp op (0);
std::vector<Edge> edges1;
db::EdgeContainer ec1 (edges1);
ep.process (ec1, op);
ep.clear ();
for (shape_interactions<db::PolygonRef, db::PolygonRef>::subject_iterator s = interactions.begin_subjects (); s != interactions.end_subjects (); ++s) {
ep.insert (s->second);
}
for (shape_interactions<db::PolygonRef, db::PolygonRef>::intruder_iterator i = interactions.begin_intruders (); i != interactions.end_intruders (); ++i) {
ep.insert (i->second.second);
}
std::vector<Edge> edges2;
db::EdgeContainer ec2 (edges2);
ep.process (ec2, op);
// Runs the boolean AND between the result with and without intruders
db::box_scanner<db::Edge, size_t> scanner;
scanner.reserve (edges1.size () + edges2.size ());
for (std::vector<Edge>::const_iterator i = edges1.begin (); i != edges1.end (); ++i) {
scanner.insert (i.operator-> (), 0);
}
for (std::vector<Edge>::const_iterator i = edges2.begin (); i != edges2.end (); ++i) {
scanner.insert (i.operator-> (), 1);
}
EdgeBooleanClusterCollector<std::unordered_set<db::Edge> > cluster_collector (&results.front (), EdgeAnd);
scanner.process (cluster_collector, 1, db::box_convert<db::Edge> ());
}
}
};
}
EdgesDelegate *
DeepRegion::edges (const EdgeFilterBase *filter) const
{
if (! filter && merged_semantics ()) {
// Hierarchical edge detector - no pre-merge required
const db::DeepLayer &polygons = deep_layer ();
db::PolygonToEdgeLocalOperation op;
db::local_processor<db::PolygonRef, db::PolygonRef, db::Edge> proc (const_cast<db::Layout *> (&polygons.layout ()),
const_cast<db::Cell *> (&polygons.initial_cell ()),
polygons.breakout_cells ());
proc.set_description (progress_desc ());
proc.set_report_progress (report_progress ());
proc.set_base_verbosity (base_verbosity ());
proc.set_threads (polygons.store ()->threads ());
// a boolean core makes somewhat better hierarchy
proc.set_boolean_core (true);
std::unique_ptr<db::DeepEdges> res (new db::DeepEdges (polygons.derived ()));
proc.run (&op, polygons.layer (), foreign_idlayer (), res->deep_layer ().layer ());
return res.release ();
} else {
const db::DeepLayer &polygons = merged_deep_layer ();
std::unique_ptr<VariantsCollectorBase> vars;
@ -1120,6 +1225,8 @@ DeepRegion::edges (const EdgeFilterBase *filter) const
res->set_is_merged (merged_semantics () || is_merged ());
return res.release ();
}
}
RegionDelegate *

View File

@ -409,6 +409,16 @@ local_processor_cell_contexts<TS, TI, TR>::create (const context_key_type &intru
return &m_contexts[intruders];
}
template <class TR>
static void
subtract_set (std::unordered_set<TR> &res, const std::unordered_set<TR> &other)
{
// for everything else, we don't use a boolean core but just set intersection
for (typename std::unordered_set<TR>::const_iterator o = other.begin (); o != other.end (); ++o) {
res.erase (*o);
}
}
template <class TS, class TI>
static void
subtract (std::unordered_set<db::PolygonRef> &res, const std::unordered_set<db::PolygonRef> &other, db::Layout *layout, const db::local_processor<TS, TI, db::PolygonRef> *proc)
@ -417,6 +427,11 @@ subtract (std::unordered_set<db::PolygonRef> &res, const std::unordered_set<db::
return;
}
if (! proc->boolean_core ()) {
subtract_set (res, other);
return;
}
size_t max_vertex_count = proc->max_vertex_count ();
double area_ratio = proc->area_ratio ();
@ -449,14 +464,60 @@ subtract (std::unordered_set<db::PolygonRef> &res, const std::unordered_set<db::
ep.process (pg, op);
}
template <class TS, class TI>
static void
subtract (std::unordered_set<db::Edge> &res, const std::unordered_set<db::Edge> &other, db::Layout *layout, const db::local_processor<TS, TI, db::Edge> *proc)
{
if (other.empty ()) {
return;
}
if (! proc->boolean_core ()) {
subtract_set (res, other);
return;
}
db::box_scanner<db::Edge, size_t> scanner;
scanner.reserve (res.size () + other.size ());
for (std::unordered_set<Edge>::const_iterator i = res.begin (); i != res.end (); ++i) {
scanner.insert (i.operator-> (), 0);
}
for (std::unordered_set<Edge>::const_iterator i = other.begin (); i != other.end (); ++i) {
scanner.insert (i.operator-> (), 1);
}
std::unordered_set<db::Edge> result;
EdgeBooleanClusterCollector<std::unordered_set<db::Edge> > cluster_collector (&result, EdgeNot);
scanner.process (cluster_collector, 1, db::box_convert<db::Edge> ());
res.swap (result);
}
template <class TS, class TI, class TR>
static void
subtract (std::unordered_set<TR> &res, const std::unordered_set<TR> &other, db::Layout * /*layout*/, const db::local_processor<TS, TI, TR> * /*proc*/)
{
// for edges, we don't use a boolean core but just set intersection
for (typename std::unordered_set<TR>::const_iterator o = other.begin (); o != other.end (); ++o) {
res.erase (*o);
}
subtract_set (res, other);
}
// determines the default boolean core flag per result type
namespace
{
template <class TR>
struct default_boolean_core
{
bool operator() () const { return false; }
};
template <>
struct default_boolean_core<db::PolygonRef>
{
bool operator() () const { return true; }
};
}
namespace {
@ -1221,7 +1282,7 @@ local_processor<TS, TI, TR>::local_processor (db::Layout *layout, db::Cell *top,
: mp_subject_layout (layout), mp_intruder_layout (layout),
mp_subject_top (top), mp_intruder_top (top),
mp_subject_breakout_cells (breakout_cells), mp_intruder_breakout_cells (breakout_cells),
m_report_progress (true), m_nthreads (0), m_max_vertex_count (0), m_area_ratio (0.0), m_base_verbosity (30), m_progress (0), mp_progress (0)
m_report_progress (true), m_nthreads (0), m_max_vertex_count (0), m_area_ratio (0.0), m_boolean_core (default_boolean_core<TR> () ()), m_base_verbosity (30), m_progress (0), mp_progress (0)
{
// .. nothing yet ..
}
@ -1231,7 +1292,7 @@ local_processor<TS, TI, TR>::local_processor (db::Layout *subject_layout, db::Ce
: mp_subject_layout (subject_layout), mp_intruder_layout (intruder_layout),
mp_subject_top (subject_top), mp_intruder_top (intruder_top),
mp_subject_breakout_cells (subject_breakout_cells), mp_intruder_breakout_cells (intruder_breakout_cells),
m_report_progress (true), m_nthreads (0), m_max_vertex_count (0), m_area_ratio (0.0), m_base_verbosity (30), m_progress (0), mp_progress (0)
m_report_progress (true), m_nthreads (0), m_max_vertex_count (0), m_area_ratio (0.0), m_boolean_core (default_boolean_core<TR> () ()), m_base_verbosity (30), m_progress (0), mp_progress (0)
{
// .. nothing yet ..
}
@ -1535,6 +1596,11 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
scanner.process (rec, dist, inst_bcs, db::box_convert<TI> ());
}
// this cache should reduce the effort of checking array vs. array
typedef std::pair<unsigned int, std::pair<db::cell_index_type, db::ICplxTrans> > effective_instance_cache_key_type;
typedef std::map<effective_instance_cache_key_type, std::pair<bool, db::CellInstArray> > effective_instance_cache_type;
effective_instance_cache_type effective_instance_cache;
for (typename std::unordered_map<const db::CellInstArray *, interaction_value_type>::const_iterator i = interactions.begin (); i != interactions.end (); ++i) {
db::Cell &subject_child_cell = mp_subject_layout->cell (i->first->object ().cell_index ());
@ -1568,17 +1634,28 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
db::box_convert <db::CellInst, true> inst_bcii (*mp_intruder_layout, ail);
for (std::unordered_set<const db::CellInstArray *>::const_iterator j = i->second.first.begin (); j != i->second.first.end (); ++j) {
for (db::CellInstArray::iterator k = (*j)->begin_touching (safe_box_enlarged (nbox, -1, -1), inst_bcii); ! k.at_end (); ++k) {
db::ICplxTrans tk = (*j)->complex_trans (*k);
// NOTE: no self-interactions
if (i->first != *j || tn != tk) {
// optimize the intruder instance so it will be as low as possible
effective_instance_cache_key_type key (ail, std::make_pair ((*j)->object ().cell_index (), tni * tk));
effective_instance_cache_type::iterator cached = effective_instance_cache.find (key);
if (cached == effective_instance_cache.end ()) {
std::pair<bool, db::CellInstArray> ei = effective_instance (contexts.subject_layer (), i->first->object ().cell_index (), ail, (*j)->object ().cell_index (), tni * tk, dist);
if (ei.first) {
intruders_below.first.insert (ei.second);
cached = effective_instance_cache.insert (std::make_pair (key, ei)).first;
}
if (cached->second.first) {
intruders_below.first.insert (cached->second.second);
}
}
}
}
}

View File

@ -479,6 +479,16 @@ public:
return m_area_ratio;
}
void set_boolean_core (double boolean_core)
{
m_boolean_core = boolean_core;
}
double boolean_core () const
{
return m_boolean_core;
}
private:
template<typename, typename, typename> friend class local_processor_cell_contexts;
template<typename, typename, typename> friend class local_processor_context_computation_task;
@ -494,6 +504,7 @@ private:
unsigned int m_nthreads;
size_t m_max_vertex_count;
double m_area_ratio;
bool m_boolean_core;
int m_base_verbosity;
mutable std::unique_ptr<tl::Job<local_processor_context_computation_worker<TS, TI, TR> > > mp_cc_job;
mutable size_t m_progress;

View File

@ -800,7 +800,7 @@ TEST(13_Edges)
db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss);
db::Edges r3edges = r3.edges ();
EXPECT_EQ (r3edges.is_merged (), true);
EXPECT_EQ (r3edges.is_merged (), false);
db::EdgeLengthFilter f (0, 500, true);
db::Edges r3edges_filtered = r3.edges (f);
@ -816,6 +816,46 @@ TEST(13_Edges)
db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au13.gds");
}
TEST(13b_Edges)
{
db::Layout ly;
{
std::string fn (tl::testsrc ());
fn += "/testdata/algo/deep_region_edges.gds";
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (ly);
}
db::cell_index_type top_cell_index = *ly.begin_top_down ();
db::Cell &top_cell = ly.cell (top_cell_index);
db::DeepShapeStore dss;
unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0));
unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0));
db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1), dss);
db::Edges r1edges = r1.edges ();
EXPECT_EQ (r1edges.is_merged (), false);
db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss);
db::Edges r2edges = r2.edges ();
EXPECT_EQ (r2edges.is_merged (), false);
db::Layout target;
unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (1, 0)), r1);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (2, 0)), r2);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1edges);
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r2edges);
CHECKPOINT();
db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au13b.gds");
}
TEST(14_Interacting)
{
db::Layout ly;
@ -936,7 +976,7 @@ TEST(14_Interacting)
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (32, 0)), r6r.selected_interacting (r1er));
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (33, 0)), r6r.selected_not_interacting (r1er));
EXPECT_EQ (r6.selected_interacting (r1e).is_merged (), true);
EXPECT_EQ (r6.selected_interacting (r1e).is_merged (), false);
EXPECT_EQ (r6.selected_interacting (r1er).is_merged (), false);
EXPECT_EQ (r6r.selected_interacting (r1e).is_merged (), false);
EXPECT_EQ (r6r.selected_interacting (r1er).is_merged (), false);

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
testdata/algo/deep_region_au13b.gds vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
testdata/algo/deep_region_edges.gds vendored Normal file

Binary file not shown.

Binary file not shown.