Skip to main content

physdes/
global_router.rs

1//! Global router for Steiner tree-based routing.
2//!
3//! Provides data structures and algorithms for constructing rectilinear
4//! Steiner routing trees with support for simple routing, Steiner point
5//! insertion, and wirelength-constrained routing.
6
7use std::collections::HashMap;
8use std::fmt;
9
10use crate::generic::MinDist;
11use crate::interval::Interval;
12use crate::point::Point;
13
14/// Type of a routing node.
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum NodeType {
17    Steiner,
18    Terminal,
19    Source,
20}
21
22impl fmt::Display for NodeType {
23    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24        match self {
25            NodeType::Steiner => write!(f, "Steiner"),
26            NodeType::Terminal => write!(f, "Terminal"),
27            NodeType::Source => write!(f, "Source"),
28        }
29    }
30}
31
32/// A node in the routing tree.
33#[derive(Debug, Clone)]
34pub struct RoutingNode {
35    pub id: String,
36    pub node_type: NodeType,
37    pub pt: Point<i32, i32>,
38    pub children: Vec<usize>,
39    pub parent: Option<usize>,
40    pub capacitance: f64,
41    pub delay: f64,
42    pub path_length: i32,
43}
44
45impl RoutingNode {
46    pub fn new(id: &str, node_type: NodeType, pt: Point<i32, i32>) -> Self {
47        RoutingNode {
48            id: id.to_string(),
49            node_type,
50            pt,
51            children: Vec::new(),
52            parent: None,
53            capacitance: 0.0,
54            delay: 0.0,
55            path_length: 0,
56        }
57    }
58
59    pub fn manhattan_distance(&self, other: &RoutingNode) -> i32 {
60        self.pt.min_dist_with(&other.pt) as i32
61    }
62}
63
64/// A rectilinear Steiner routing tree.
65pub struct GlobalRoutingTree {
66    nodes: Vec<RoutingNode>,
67    node_map: HashMap<String, usize>,
68    source_idx: usize,
69    next_steiner_id: i32,
70    next_terminal_id: i32,
71    pub worst_wirelength: i32,
72}
73
74impl GlobalRoutingTree {
75    pub fn new(source_position: Point<i32, i32>) -> Self {
76        let source = RoutingNode::new("source", NodeType::Source, source_position);
77        let mut nodes = Vec::new();
78        let mut node_map = HashMap::new();
79        node_map.insert("source".to_string(), 0usize);
80        nodes.push(source);
81        GlobalRoutingTree {
82            nodes,
83            node_map,
84            source_idx: 0,
85            next_steiner_id: 1,
86            next_terminal_id: 1,
87            worst_wirelength: 0,
88        }
89    }
90
91    pub fn get_source(&self) -> &RoutingNode {
92        &self.nodes[self.source_idx]
93    }
94
95    pub fn get_source_mut(&mut self) -> &mut RoutingNode {
96        &mut self.nodes[self.source_idx]
97    }
98
99    fn add_node(&mut self, node: RoutingNode) -> usize {
100        let idx = self.nodes.len();
101        self.node_map.insert(node.id.clone(), idx);
102        self.nodes.push(node);
103        idx
104    }
105
106    fn _find_nearest_node(&self, point: Point<i32, i32>, exclude_id: Option<&str>) -> usize {
107        if self.nodes.len() <= 1 {
108            return self.source_idx;
109        }
110        let mut nearest = self.source_idx;
111        let mut min_dist = i32::MAX;
112        for (idx, node) in self.nodes.iter().enumerate() {
113            if let Some(ex) = exclude_id {
114                if node.id == ex {
115                    continue;
116                }
117            }
118            let dist = node.pt.min_dist_with(&point) as i32;
119            if dist < min_dist {
120                min_dist = dist;
121                nearest = idx;
122            }
123        }
124        nearest
125    }
126
127    pub fn insert_steiner_node(
128        &mut self,
129        point: Point<i32, i32>,
130        parent_id: Option<&str>,
131    ) -> String {
132        let id = format!("steiner_{}", self.next_steiner_id);
133        self.next_steiner_id += 1;
134        let idx = self.add_node(RoutingNode::new(&id, NodeType::Steiner, point));
135
136        let parent_idx = match parent_id {
137            Some(pid) => *self.node_map.get(pid).expect("Parent node not found"),
138            None => self.source_idx,
139        };
140        self.nodes[idx].parent = Some(parent_idx);
141        self.nodes[parent_idx].children.push(idx);
142        id
143    }
144
145    pub fn insert_terminal_node(
146        &mut self,
147        point: Point<i32, i32>,
148        parent_id: Option<&str>,
149    ) -> String {
150        let id = format!("terminal_{}", self.next_terminal_id);
151        self.next_terminal_id += 1;
152        let idx = self.add_node(RoutingNode::new(&id, NodeType::Terminal, point));
153
154        let parent_idx = match parent_id {
155            Some(pid) => *self.node_map.get(pid).expect("Parent node not found"),
156            None => self._find_nearest_node(point, None),
157        };
158        self.nodes[idx].parent = Some(parent_idx);
159        self.nodes[parent_idx].children.push(idx);
160        id
161    }
162
163    fn _find_nearest_insertion_with_constraints(
164        &self,
165        pt: Point<i32, i32>,
166        _allowed_wirelength: i32,
167        _keepouts: &Option<Vec<Interval<i32>>>,
168    ) -> (Option<usize>, usize) {
169        // Simplified: find nearest node directly
170        let nearest = self._find_nearest_node(pt, None);
171        (None, nearest)
172    }
173
174    fn _insert_terminal_impl(
175        &mut self,
176        point: Point<i32, i32>,
177        allowed_wirelength: i32,
178        keepouts: Option<Vec<Interval<i32>>>,
179    ) {
180        let terminal_id = format!("terminal_{}", self.next_terminal_id);
181        self.next_terminal_id += 1;
182        let terminal_idx = self.add_node(RoutingNode::new(&terminal_id, NodeType::Terminal, point));
183
184        let (parent_node, nearest_node) =
185            self._find_nearest_insertion_with_constraints(point, allowed_wirelength, &keepouts);
186
187        let nearest_idx = nearest_node;
188        match parent_node {
189            None => {
190                self.nodes[terminal_idx].parent = Some(nearest_idx);
191                self.nodes[nearest_idx].children.push(terminal_idx);
192                let dist = self.nodes[nearest_idx].pt.min_dist_with(&point) as i32;
193                self.nodes[terminal_idx].path_length = self.nodes[nearest_idx].path_length + dist;
194            }
195            Some(parent_idx) => {
196                let steiner_id = format!("steiner_{}", self.next_steiner_id);
197                self.next_steiner_id += 1;
198                let nearest_pt = point; // simplified
199                let steiner_idx =
200                    self.add_node(RoutingNode::new(&steiner_id, NodeType::Steiner, nearest_pt));
201
202                // Rewire
203                self.nodes[parent_idx]
204                    .children
205                    .retain(|&c| c != nearest_idx);
206                self.nodes[nearest_idx].parent = None;
207
208                self.nodes[steiner_idx].parent = Some(parent_idx);
209                self.nodes[parent_idx].children.push(steiner_idx);
210
211                let dist_ps = self.nodes[parent_idx].pt.min_dist_with(&nearest_pt) as i32;
212                self.nodes[steiner_idx].path_length = self.nodes[parent_idx].path_length + dist_ps;
213
214                self.nodes[nearest_idx].parent = Some(steiner_idx);
215                self.nodes[steiner_idx].children.push(nearest_idx);
216
217                self.nodes[terminal_idx].parent = Some(steiner_idx);
218                self.nodes[steiner_idx].children.push(terminal_idx);
219
220                let dist_st = nearest_pt.min_dist_with(&point) as i32;
221                self.nodes[terminal_idx].path_length =
222                    self.nodes[steiner_idx].path_length + dist_st;
223            }
224        }
225    }
226
227    pub fn insert_terminal_with_steiner(
228        &mut self,
229        point: Point<i32, i32>,
230        keepouts: Option<Vec<Interval<i32>>>,
231    ) {
232        self._insert_terminal_impl(point, i32::MAX, keepouts);
233    }
234
235    pub fn insert_terminal_with_constraints(
236        &mut self,
237        point: Point<i32, i32>,
238        allowed_wirelength: i32,
239        keepouts: Option<Vec<Interval<i32>>>,
240    ) {
241        self._insert_terminal_impl(point, allowed_wirelength, keepouts);
242    }
243
244    pub fn calculate_total_wirelength(&self) -> i32 {
245        let mut total = 0;
246        for node in &self.nodes {
247            if let Some(parent_idx) = node.parent {
248                total += self.nodes[parent_idx].manhattan_distance(node);
249            }
250        }
251        total
252    }
253
254    pub fn calculate_worst_wirelength(&self) -> i32 {
255        fn traverse(tree: &GlobalRoutingTree, idx: usize) -> i32 {
256            let node = &tree.nodes[idx];
257            let mut worst = 0;
258            for &child in &node.children {
259                let child_len = node.manhattan_distance(&tree.nodes[child]);
260                let child_path = traverse(tree, child);
261                worst = worst.max(child_len + child_path);
262            }
263            worst
264        }
265        traverse(self, self.source_idx)
266    }
267
268    pub fn find_path_to_source(&self, node_id: &str) -> Vec<&RoutingNode> {
269        let mut idx = *self.node_map.get(node_id).expect("Node not found");
270        let mut path = Vec::new();
271        loop {
272            path.push(&self.nodes[idx]);
273            match self.nodes[idx].parent {
274                Some(p) => idx = p,
275                None => break,
276            }
277        }
278        path.reverse();
279        path
280    }
281
282    pub fn get_all_terminals(&self) -> Vec<&RoutingNode> {
283        self.nodes
284            .iter()
285            .filter(|n| n.node_type == NodeType::Terminal)
286            .collect()
287    }
288
289    pub fn get_all_steiner_nodes(&self) -> Vec<&RoutingNode> {
290        self.nodes
291            .iter()
292            .filter(|n| n.node_type == NodeType::Steiner)
293            .collect()
294    }
295
296    pub fn get_tree_structure(&self) -> String {
297        fn fmt_node(tree: &GlobalRoutingTree, idx: usize, level: usize) -> String {
298            let node = &tree.nodes[idx];
299            let mut s = format!(
300                "{}{}({}, {})",
301                "  ".repeat(level),
302                node.node_type,
303                node.id,
304                node.pt
305            );
306            s.push('\n');
307            for &child in &node.children {
308                s.push_str(&fmt_node(tree, child, level + 1));
309            }
310            s
311        }
312        fmt_node(self, self.source_idx, 0)
313    }
314
315    pub fn visualize_tree(&self) {
316        println!("Global Routing Tree Structure:");
317        println!("================================");
318        print!("{}", self.get_tree_structure());
319        println!("Total wirelength: {}", self.calculate_total_wirelength());
320        println!("Total nodes: {}", self.nodes.len());
321        println!("Terminals: {}", self.get_all_terminals().len());
322        println!("Steiner points: {}", self.get_all_steiner_nodes().len());
323    }
324
325    pub fn optimize_steiner_points(&mut self) {
326        let to_remove: Vec<usize> = self
327            .nodes
328            .iter()
329            .enumerate()
330            .filter(|(_, n)| {
331                n.node_type == NodeType::Steiner && n.children.len() == 1 && n.parent.is_some()
332            })
333            .map(|(i, _)| i)
334            .collect();
335
336        for idx in to_remove.into_iter().rev() {
337            let parent = self.nodes[idx].parent;
338            let child = self.nodes[idx].children[0];
339            if let Some(p) = parent {
340                self.nodes[p].children.retain(|&c| c != idx);
341                self.nodes[p].children.push(child);
342            }
343            self.nodes[child].parent = parent;
344            self.node_map.remove(&self.nodes[idx].id);
345        }
346        self.nodes.retain(|n| self.node_map.contains_key(&n.id));
347        // Remap indices
348        self.remap_indices();
349    }
350
351    fn remap_indices(&mut self) {
352        let old_indices: Vec<usize> = (0..self.nodes.len()).collect();
353        let mut new_map = HashMap::new();
354        let mut new_nodes = Vec::new();
355        for node in self.nodes.drain(..) {
356            let new_idx = new_nodes.len();
357            new_map.insert(node.id.clone(), new_idx);
358            new_nodes.push(node);
359        }
360        self.nodes = new_nodes;
361        for node in &mut self.nodes {
362            if let Some(p) = node.parent {
363                if let Some(&old_p) = old_indices.get(p) {
364                    node.parent = Some(old_p);
365                } else {
366                    node.parent = None;
367                }
368            }
369            node.children = node
370                .children
371                .iter()
372                .filter_map(|c| old_indices.get(*c).copied())
373                .collect();
374        }
375        self.node_map = new_map;
376    }
377}
378
379/// High-level global router that constructs a routing tree.
380pub struct GlobalRouter {
381    terminal_positions: Vec<Point<i32, i32>>,
382    tree: GlobalRoutingTree,
383    worst_wirelength: i32,
384    keepouts: Option<Vec<Interval<i32>>>,
385}
386
387impl GlobalRouter {
388    pub fn new(
389        source_pos: Point<i32, i32>,
390        terminal_positions: Vec<Point<i32, i32>>,
391        keepout_regions: Option<Vec<Interval<i32>>>,
392    ) -> Self {
393        let mut sorted = terminal_positions.clone();
394        sorted.sort_by(|a, b| {
395            let da = source_pos.min_dist_with(a) as i32;
396            let db = source_pos.min_dist_with(b) as i32;
397            da.cmp(&db)
398        });
399
400        let worst = if sorted.is_empty() {
401            0
402        } else {
403            source_pos.min_dist_with(&sorted[sorted.len() - 1]) as i32
404        };
405
406        GlobalRouter {
407            terminal_positions: sorted,
408            tree: GlobalRoutingTree::new(source_pos),
409            worst_wirelength: worst,
410            keepouts: keepout_regions,
411        }
412    }
413
414    pub fn route_simple(&mut self) {
415        for &terminal in &self.terminal_positions {
416            self.tree.insert_terminal_node(terminal, None);
417        }
418    }
419
420    pub fn route_with_steiners(&mut self) {
421        self.tree.worst_wirelength = self.worst_wirelength;
422        for &terminal in &self.terminal_positions {
423            self.tree
424                .insert_terminal_with_steiner(terminal, self.keepouts.clone());
425        }
426    }
427
428    pub fn route_with_constraints(&mut self, multiplier: f64) {
429        let allowed = (self.worst_wirelength as f64 * multiplier).round() as i32;
430        self.tree.worst_wirelength = self.worst_wirelength;
431        for &terminal in &self.terminal_positions {
432            self.tree
433                .insert_terminal_with_constraints(terminal, allowed, self.keepouts.clone());
434        }
435    }
436
437    pub fn get_tree(&self) -> &GlobalRoutingTree {
438        &self.tree
439    }
440}