libreda_db/
region_search.rs

1// SPDX-FileCopyrightText: 2018-2022 Thomas Kramer
2//
3// SPDX-License-Identifier: AGPL-3.0-or-later
4
5//! Add fast region queries to layouts.
6
7// TODO: Remove this, once implemented.
8#![allow(unused_variables)]
9
10use fnv::FnvHashMap;
11use num_traits::{PrimInt, Signed};
12use rstar::{RTree, RTreeObject};
13
14use crate::prelude::*;
15
16/// Wrapper around netlist, layout and L2N structures that allows fast region queries.
17///
18/// # Types
19/// * `T`: Underlying data structure.
20pub struct RegionSearchAdapter<T>
21where
22    T: LayoutBase,
23    T::Coord: PrimInt + Signed + std::fmt::Debug,
24{
25    /// Underlying data structure.
26    chip: T,
27    /// RTrees containing geometric shapes.
28    shape_rtrees:
29        FnvHashMap<T::CellId, FnvHashMap<T::LayerId, RTree<ShapeEntry<T::ShapeId, T::Coord>>>>,
30    /// RTree containing child instances.
31    instance_rtree: FnvHashMap<T::CellId, RTree<CellInstanceEntry<T>>>,
32    /// Cache for bounding boxes of cells.
33    cell_bounding_boxes: FnvHashMap<T::CellId, Rect<T::Coord>>,
34}
35
36/// Wrapper for shapes.
37#[derive(Debug, Clone, PartialEq)]
38pub struct ShapeEntry<ShapeId, Coord> {
39    bounding_box: Rect<Coord>,
40    shape_id: ShapeId,
41}
42
43/// Wrapper for cell instances
44#[derive(Debug, Clone, PartialEq)]
45pub struct CellInstanceEntry<L: LayoutBase> {
46    bounding_box: Rect<L::Coord>,
47    cell_inst_id: L::CellInstId,
48}
49
50// Make `ShapeEntry` usable within RTrees.
51impl<ShapeId, Coord> BoundingBox<Coord> for ShapeEntry<ShapeId, Coord>
52where
53    Coord: PrimInt,
54{
55    fn bounding_box(&self) -> Rect<Coord> {
56        self.bounding_box
57    }
58}
59
60// Make `ShapeEntry` usable within RTrees.
61impl<ShapeId, Coord> RTreeObject for ShapeEntry<ShapeId, Coord>
62where
63    Coord: PrimInt + Signed + std::fmt::Debug,
64{
65    type Envelope = rstar::AABB<[Coord; 2]>;
66
67    fn envelope(&self) -> Self::Envelope {
68        let bbox = self.bounding_box();
69
70        rstar::AABB::from_corners(bbox.lower_left().into(), bbox.upper_right().into())
71    }
72}
73
74// Make `CellInstanceEntry` usable within RTrees.
75impl<L> BoundingBox<L::Coord> for CellInstanceEntry<L>
76where
77    L: LayoutBase,
78    L::Coord: PrimInt,
79{
80    fn bounding_box(&self) -> Rect<L::Coord> {
81        self.bounding_box
82    }
83}
84
85// Make `CellInstanceEntry` usable within RTrees.
86impl<L> RTreeObject for CellInstanceEntry<L>
87where
88    L: LayoutBase,
89    L::Coord: PrimInt + Signed + std::fmt::Debug,
90{
91    type Envelope = rstar::AABB<[L::Coord; 2]>;
92
93    fn envelope(&self) -> Self::Envelope {
94        let bbox = self.bounding_box();
95
96        rstar::AABB::from_corners(bbox.lower_left().into(), bbox.upper_right().into())
97    }
98}
99
100impl<T> RegionSearchAdapter<T>
101where
102    T: LayoutBase,
103    T::Coord: PrimInt + Signed + std::fmt::Debug,
104{
105    /// Add fast region query capability to `chip`.
106    pub fn new(chip: T) -> Self {
107        let mut region_search = Self {
108            chip,
109            shape_rtrees: Default::default(),
110            instance_rtree: Default::default(),
111            cell_bounding_boxes: Default::default(),
112        };
113
114        region_search.create_shape_trees();
115        region_search.create_cell_instance_trees();
116
117        region_search
118    }
119
120    fn create_shape_trees(&mut self) {
121        // Process cells starting with leaf-cells to top cells.
122        let mut cell_rtrees: FnvHashMap<_, _> = Default::default();
123        for cell in self.chip.each_cell_bottom_to_top() {
124            let mut rtrees: FnvHashMap<_, RTree<_>> = Default::default();
125
126            for layer in self.chip.each_layer() {
127                // Collect all shapes which have a bounding box.
128                let mut all_shapes = vec![];
129                self.chip
130                    .for_each_shape(&cell, &layer, |shape_id, geometry| {
131                        if let Some(bounding_box) = geometry.try_bounding_box() {
132                            let rtree_entry = ShapeEntry {
133                                bounding_box,
134                                shape_id: shape_id.clone(),
135                            };
136                            all_shapes.push(rtree_entry)
137                        }
138                    });
139
140                // Create RTree.
141                let rtree = RTree::bulk_load(all_shapes);
142
143                rtrees.insert(layer, rtree);
144            }
145            cell_rtrees.insert(cell, rtrees);
146        }
147
148        self.shape_rtrees = cell_rtrees;
149    }
150
151    fn create_cell_instance_trees(&mut self) {
152        for cell in self.chip.each_cell_bottom_to_top() {
153            // Compute bounding box of the shapes (without subcells).
154            let shape_bbox = self.compute_shape_bbox(&cell);
155
156            // Register all instances in the rtree (their bounding boxes are known by now).
157            let instance_entries: Vec<_> = self
158                .chip
159                .each_cell_instance(&cell)
160                .filter_map(|inst| {
161                    self.cell_bounding_boxes
162                        .get(&self.chip.template_cell(&inst))
163                        .map(|bbox| (inst, bbox))
164                })
165                .map(|(inst, bbox)| {
166                    let tf = self.chip.get_transform(&inst);
167                    let bbox_transformed = bbox.transform(|p| tf.transform_point(p));
168
169                    CellInstanceEntry {
170                        bounding_box: bbox_transformed,
171                        cell_inst_id: inst,
172                    }
173                })
174                .collect();
175
176            let rtree = RTree::bulk_load(instance_entries);
177
178            let cell_inst_bbox = if rtree.size() > 0 {
179                let envelope = rtree.root().envelope();
180                let bbox = Rect::new(envelope.lower(), envelope.upper());
181                Some(bbox)
182            } else {
183                None
184            };
185
186            // Combine both bounding boxes.
187            let bbox = cell_inst_bbox
188                .into_iter()
189                .chain(shape_bbox)
190                .reduce(|acc, b| acc.add_rect(&b));
191
192            if let Some(bbox) = bbox {
193                self.cell_bounding_boxes.insert(cell.clone(), bbox);
194            }
195
196            self.instance_rtree.insert(cell, rtree);
197        }
198    }
199
200    /// Compute the bounding box of all shapes in a cell, excluding sub cells.
201    fn compute_shape_bbox(&self, cell_id: &T::CellId) -> Option<Rect<T::Coord>> {
202        self.shape_rtrees
203            .get(cell_id)
204            .into_iter()
205            // Get rtree of each layer.
206            .flat_map(|layer_trees| layer_trees.values())
207            // Get the bounding box of the layer.
208            .map(|rtree| rtree.root().envelope())
209            // Convert to Rect
210            .map(|envelope| Rect::new(envelope.lower(), envelope.upper()))
211            // Reduce to a single bounding box.
212            .reduce(|acc, b| acc.add_rect(&b))
213    }
214
215    /// Compute bounding box of all subcells, excluding shapes in the current cell.
216    #[allow(unused)]
217    fn compute_subcell_bboxes(&self, cell_id: &T::CellId) -> Option<Rect<T::Coord>> {
218        if let Some(instance_rtree) = self.instance_rtree.get(cell_id) {
219            if instance_rtree.size() > 0 {
220                let envelope = instance_rtree.root().envelope();
221                Some(Rect::new(envelope.lower(), envelope.upper()))
222            } else {
223                None
224            }
225        } else {
226            None
227        }
228        // let subcell_bboxes = self.chip.each_cell_instance(cell_id)
229        //     .filter_map(|inst| {
230        //         self.cell_bounding_boxes.get(&self.chip.template_cell(&inst))
231        //             .map(|bbox| (inst, bbox))
232        //     })
233        //     .map(|(inst, bbox)| {
234        //         let tf = self.chip.get_transform(&inst);
235        //         let bbox_transformed = bbox.transform(|p| tf.transform_point(p));
236        //         bbox_transformed
237        //     });
238        //
239        // subcell_bboxes.reduce(|acc, b| {
240        //     acc.add_rect(&b)
241        // })
242    }
243}
244
245#[portrait::fill(portrait::delegate(H; self.chip))]
246impl<H> HierarchyIds for RegionSearchAdapter<H>
247where
248    H: LayoutBase,
249    H::Coord: PrimInt + Signed + std::fmt::Debug,
250{
251}
252
253// Inherit everything from HierarchyBase.
254#[portrait::fill(portrait::delegate(H; self.chip))]
255impl<H> HierarchyBase for RegionSearchAdapter<H>
256where
257    H: LayoutBase,
258    H::Coord: PrimInt + Signed + std::fmt::Debug,
259{
260    type NameType = H::NameType;
261}
262
263#[portrait::fill(portrait::delegate(L))]
264impl<L> LayoutIds for RegionSearchAdapter<L>
265where
266    L: LayoutBase,
267    L::Coord: PrimInt + Signed + std::fmt::Debug,
268{
269}
270//
271// // Inherit everything from LayoutBase.
272#[portrait::fill(portrait::delegate(L; self.chip))]
273impl<L> LayoutBase for RegionSearchAdapter<L>
274where
275    L: LayoutBase,
276    L::Coord: PrimInt + Signed + std::fmt::Debug,
277{
278}
279
280impl<L> RegionSearch for RegionSearchAdapter<L>
281where
282    L: LayoutBase,
283    L::Coord: PrimInt + Signed + std::fmt::Debug,
284{
285    fn each_shape_in_region_per_layer(
286        &self,
287        cell: &Self::CellId,
288        layer_id: &Self::LayerId,
289        search_region: &Rect<Self::Coord>,
290    ) -> Box<dyn Iterator<Item = Self::ShapeId> + '_> {
291        let aabb = rect2aabb(search_region);
292
293        let intersecting_instances = self
294            .shape_rtrees
295            .get(cell)
296            .expect("cell not found")
297            .get(layer_id)
298            .into_iter()
299            .flat_map(move |rtree| rtree.locate_in_envelope_intersecting(&aabb))
300            .map(|shape_entry| shape_entry.shape_id.clone());
301
302        Box::new(intersecting_instances)
303    }
304
305    fn each_cell_instance_in_region(
306        &self,
307        cell: &Self::CellId,
308        search_region: &Rect<Self::Coord>,
309    ) -> Box<dyn Iterator<Item = Self::CellInstId> + '_> {
310        let intersecting_instances = self
311            .instance_rtree
312            .get(cell)
313            .expect("cell not found")
314            .locate_in_envelope_intersecting(&rect2aabb(search_region))
315            .map(|instance_entry| instance_entry.cell_inst_id.clone());
316
317        Box::new(intersecting_instances)
318    }
319}
320
321/// Convert a rectangle into an axis aligned bounding box used by RStar.
322fn rect2aabb<Crd>(r: &Rect<Crd>) -> rstar::AABB<[Crd; 2]>
323where
324    Crd: PrimInt + Signed + std::fmt::Debug,
325{
326    rstar::AABB::from_corners(r.lower_left().into(), r.upper_right().into())
327}
328
329#[portrait::fill(portrait::delegate(N))]
330impl<N> NetlistIds for RegionSearchAdapter<N>
331where
332    N: NetlistIds + LayoutBase,
333    N::Coord: PrimInt + Signed + std::fmt::Debug,
334{
335}
336
337// Inherit everything from NetlistBase.
338#[portrait::fill(portrait::delegate(N; self.chip))]
339impl<N> NetlistBase for RegionSearchAdapter<N>
340where
341    N: LayoutBase + NetlistBase,
342    N::Coord: PrimInt + Signed + std::fmt::Debug,
343{
344}
345
346#[portrait::fill(portrait::delegate(LN; self.chip))]
347impl<LN> L2NBase for RegionSearchAdapter<LN>
348where
349    LN: L2NBase,
350    LN::Coord: PrimInt + Signed + std::fmt::Debug,
351{
352}
353
354// Inherit everything from HierarchyEdit.
355#[portrait::fill(portrait::delegate(H; self.chip))]
356impl<H> HierarchyEdit for RegionSearchAdapter<H>
357where
358    H: HierarchyEdit + LayoutBase,
359    H::Coord: PrimInt + Signed + std::fmt::Debug,
360{
361    fn create_cell(&mut self, name: H::NameType) -> H::CellId {
362        todo!()
363    }
364
365    fn remove_cell(&mut self, cell_id: &H::CellId) {
366        todo!()
367    }
368
369    fn create_cell_instance(
370        &mut self,
371        parent_cell: &H::CellId,
372        template_cell: &H::CellId,
373        name: Option<H::NameType>,
374    ) -> H::CellInstId {
375        todo!()
376    }
377
378    fn remove_cell_instance(&mut self, inst: &H::CellInstId) {
379        todo!()
380    }
381}
382
383// Inherit everything from LayoutEdit.
384#[portrait::fill(portrait::delegate(L; self.chip))]
385impl<L> LayoutEdit for RegionSearchAdapter<L>
386where
387    L: LayoutEdit,
388    L::Coord: PrimInt + Signed + std::fmt::Debug,
389{
390    fn insert_shape(
391        &mut self,
392        parent_cell: &L::CellId,
393        layer: &L::LayerId,
394        geometry: Geometry<L::Coord>,
395    ) -> L::ShapeId {
396        todo!("update RTree");
397        // let shape_id = self.chip.insert_shape(parent_cell, layer, geometry);
398        // shape_id
399    }
400
401    fn remove_shape(&mut self, shape_id: &L::ShapeId) -> Option<Geometry<L::Coord>> {
402        let _geo = self.chip.remove_shape(shape_id);
403        todo!("update RTree")
404    }
405
406    fn replace_shape(
407        &mut self,
408        shape_id: &L::ShapeId,
409        geometry: Geometry<L::Coord>,
410    ) -> Geometry<L::Coord> {
411        let _geo = self.chip.replace_shape(shape_id, geometry);
412        todo!("update RTree")
413    }
414
415    fn set_transform(&mut self, cell_inst: &L::CellInstId, tf: SimpleTransform<L::Coord>) {
416        self.chip.set_transform(cell_inst, tf);
417        todo!("update RTree")
418    }
419}
420
421// Inherit everything from NetlistBase.
422#[portrait::fill(portrait::delegate(N; self.chip))]
423impl<N: NetlistEdit> NetlistEdit for RegionSearchAdapter<N>
424where
425    N: LayoutBase + NetlistEdit,
426    N::Coord: PrimInt + Signed + std::fmt::Debug,
427{
428}
429
430#[portrait::fill(portrait::delegate(LN; self.chip))]
431impl<LN> L2NEdit for RegionSearchAdapter<LN>
432where
433    LN: L2NEdit,
434    LN::Coord: PrimInt + Signed + std::fmt::Debug,
435{
436}
437
438#[test]
439fn test_create_region_search() {
440    let mut chip = Chip::new();
441
442    let top = chip.create_cell("TOP".to_string().into());
443    let layer1 = chip.create_layer(1, 0);
444    chip.insert_shape(&top, &layer1, Rect::new((0, 0), (10, 10)).into());
445
446    let region_search = RegionSearchAdapter::new(&mut chip);
447
448    assert_eq!(
449        region_search.cell_bounding_boxes[&top],
450        Rect::new((0, 0), (10, 10))
451    );
452}