From fe7c25885189c81246d8f776ddf9cd7fc151d221 Mon Sep 17 00:00:00 2001 From: Mikolaj Wielgus Date: Sun, 7 Sep 2025 16:40:29 +0200 Subject: [PATCH] refactor(autorouter/permutator): Move presorting to new file, `presorter.rs` --- committed.toml | 1 + src/autorouter/mod.rs | 1 + src/autorouter/permutator.rs | 14 +-- src/autorouter/permuter.rs | 143 ++++++--------------------- src/autorouter/presorter.rs | 182 +++++++++++++++++++++++++++++++++++ 5 files changed, 219 insertions(+), 122 deletions(-) create mode 100644 src/autorouter/presorter.rs diff --git a/committed.toml b/committed.toml index 2bc8d2a..ab8f2dd 100644 --- a/committed.toml +++ b/committed.toml @@ -29,6 +29,7 @@ allowed_scopes = [ "autorouter/permuter", "autorouter/place_via", "autorouter/pointroute", + "autorouter/presorter", "autorouter/ratsnest", "autorouter/ratline", "autorouter/remove_bands", diff --git a/src/autorouter/mod.rs b/src/autorouter/mod.rs index a6e3cbf..5ce80bd 100644 --- a/src/autorouter/mod.rs +++ b/src/autorouter/mod.rs @@ -13,6 +13,7 @@ pub mod permutator; pub mod permuter; pub mod place_via; pub mod pointroute; +pub mod presorter; pub mod ratline; pub mod ratsnest; pub mod remove_bands; diff --git a/src/autorouter/permutator.rs b/src/autorouter/permutator.rs index 852b69b..0e25242 100644 --- a/src/autorouter/permutator.rs +++ b/src/autorouter/permutator.rs @@ -10,7 +10,7 @@ use crate::{ autorouter::{ autoroute::{AutorouteContinueStatus, AutorouteExecutionStepper}, invoker::GetDebugOverlayData, - permuter::{PermuteRatlines, RatlinePermuter, SccRatlinePermuter}, + permuter::{PermuteRatlines, RatlinesPermuter, SccPermutationsRatlinePermuter}, ratline::RatlineIndex, Autorouter, AutorouterError, AutorouterOptions, }, @@ -23,7 +23,7 @@ use crate::{ pub struct AutorouteExecutionPermutator { stepper: AutorouteExecutionStepper, - permuter: RatlinePermuter, + permuter: RatlinesPermuter, options: AutorouterOptions, } @@ -33,9 +33,10 @@ impl AutorouteExecutionPermutator { ratlines: Vec, options: AutorouterOptions, ) -> Result { - let mut permuter = - RatlinePermuter::Scc(SccRatlinePermuter::new(autorouter, ratlines, &options)); - let initially_sorted_ratlines = permuter.next_permutation(autorouter).unwrap(); + let mut permuter = RatlinesPermuter::SccPermutations(SccPermutationsRatlinePermuter::new( + autorouter, ratlines, &options, + )); + let initially_sorted_ratlines = permuter.next_ratlines_permutation(autorouter).unwrap(); Ok(Self { stepper: AutorouteExecutionStepper::new( @@ -67,7 +68,8 @@ impl Step, Option, AutorouteContinue } loop { - let Some(permutation) = self.permuter.next_permutation(autorouter) else { + let Some(permutation) = self.permuter.next_ratlines_permutation(autorouter) + else { return Ok(ControlFlow::Break(None)); }; diff --git a/src/autorouter/permuter.rs b/src/autorouter/permuter.rs index 72f2508..23d1a2f 100644 --- a/src/autorouter/permuter.rs +++ b/src/autorouter/permuter.rs @@ -2,147 +2,58 @@ // // SPDX-License-Identifier: MIT -use std::cmp::Ordering; - use enum_dispatch::enum_dispatch; use itertools::{Itertools, Permutations}; -use petgraph::{algo::tarjan_scc, graph::NodeIndex}; +use petgraph::graph::NodeIndex; use specctra_core::mesadata::AccessMesadata; -use crate::{ - autorouter::{ratline::RatlineIndex, Autorouter, AutorouterOptions}, - geometry::shape::MeasureLength, - graph::MakeRef, +use crate::autorouter::{ + presorter::SccIntersectionsAndLengthPresorter, ratline::RatlineIndex, Autorouter, + AutorouterOptions, }; #[enum_dispatch] pub trait PermuteRatlines { - fn next_permutation( + fn next_ratlines_permutation( &mut self, autorouter: &mut Autorouter, ) -> Option>; } #[enum_dispatch(PermuteRatlines)] -pub enum RatlinePermuter { - Scc(SccRatlinePermuter), +pub enum RatlinesPermuter { + SccPermutations(SccPermutationsRatlinePermuter), } -pub struct SccRatlinePermuter { +impl RatlinesPermuter { + pub fn new( + autorouter: &mut Autorouter, + ratlines: Vec, + options: &AutorouterOptions, + ) -> Self { + RatlinesPermuter::SccPermutations(SccPermutationsRatlinePermuter::new( + autorouter, ratlines, options, + )) + } +} + +pub struct SccPermutationsRatlinePermuter { sccs_permutations_iter: Permutations>>>, ratlines: Vec, } -impl SccRatlinePermuter { +impl SccPermutationsRatlinePermuter { pub fn new( autorouter: &mut Autorouter, ratlines: Vec, _options: &AutorouterOptions, ) -> Self { - // FIXME: Unnecessary copy. - let mut filtered_ratsnest = autorouter.ratsnest().graph().clone(); - filtered_ratsnest.retain_edges(|_g, i| ratlines.contains(&i)); - - let mut sccs = tarjan_scc(&filtered_ratsnest); + // TODO: Instead of instantiating presorter again here, get it from + // an argument. + let presorter = SccIntersectionsAndLengthPresorter::new(autorouter, &ratlines); + let sccs = presorter.dissolve(); let sccs_len = sccs.len(); - sccs.sort_unstable_by(|a, b| { - // FIXME: These calculations should probably be stored somewhere - // instead of being done every time. - - let mut a_intersector_count = 0; - let mut b_intersector_count = 0; - let mut a_length = 0.0; - let mut b_length = 0.0; - - // FIXME: It's inefficient to iterate over the ratlines on every - // sort comparison. But this is the simplest solution I arrived - // at after realizing that `.tarjan_scc(...)` does not sort nodes - // inside components. - for ratline in ratlines.iter() { - if a.contains(&filtered_ratsnest.edge_endpoints(*ratline).unwrap().0) - && a.contains(&filtered_ratsnest.edge_endpoints(*ratline).unwrap().1) - { - a_length += ratline.ref_(autorouter).length(); - a_intersector_count += ratline - .ref_(autorouter) - .interior_obstacle_ratlines() - .count(); - } - } - - for ratline in ratlines.iter() { - if b.contains(&filtered_ratsnest.edge_endpoints(*ratline).unwrap().0) - && b.contains(&filtered_ratsnest.edge_endpoints(*ratline).unwrap().1) - { - b_length += ratline.ref_(autorouter).length(); - b_intersector_count += ratline - .ref_(autorouter) - .interior_obstacle_ratlines() - .count(); - } - } - - let primary_ordering = a_intersector_count.cmp(&b_intersector_count); - - if primary_ordering != Ordering::Equal { - primary_ordering - } else { - let secondary_ordering = a_length.total_cmp(&b_length); - - secondary_ordering - } - - // Below is how I tried to do this before I realized that - // `.tarjan_scc(...)` does not sort nodes inside components. - - /*let a_intersector_count: usize = a - .windows(2) - .map(|window| { - let ratline = filtered_ratsnest.find_edge(window[0], window[1]).unwrap(); - ratline - .ref_(autorouter) - .interior_obstacle_ratlines() - .count() - }) - .sum(); - let b_intersector_count: usize = b - .windows(2) - .map(|window| { - let ratline = filtered_ratsnest.find_edge(window[0], window[1]).unwrap(); - ratline - .ref_(autorouter) - .interior_obstacle_ratlines() - .count() - }) - .sum(); - - let primary_ordering = a_intersector_count.cmp(&b_intersector_count); - - if primary_ordering != Ordering::Equal { - primary_ordering - } else { - let a_length: f64 = a - .windows(2) - .map(|window| { - let ratline = filtered_ratsnest.find_edge(window[0], window[1]).unwrap(); - ratline.ref_(autorouter).length() - }) - .sum(); - let b_length: f64 = b - .windows(2) - .map(|window| { - let ratline = filtered_ratsnest.find_edge(window[0], window[1]).unwrap(); - ratline.ref_(autorouter).length() - }) - .sum(); - - let secondary_ordering = a_length.total_cmp(&b_length); - - secondary_ordering - }*/ - }); - Self { sccs_permutations_iter: sccs.into_iter().permutations(sccs_len), ratlines, @@ -150,8 +61,8 @@ impl SccRatlinePermuter { } } -impl PermuteRatlines for SccRatlinePermuter { - fn next_permutation( +impl PermuteRatlines for SccPermutationsRatlinePermuter { + fn next_ratlines_permutation( &mut self, autorouter: &mut Autorouter, ) -> Option> { diff --git a/src/autorouter/presorter.rs b/src/autorouter/presorter.rs new file mode 100644 index 0000000..776f9b3 --- /dev/null +++ b/src/autorouter/presorter.rs @@ -0,0 +1,182 @@ +// SPDX-FileCopyrightText: 2025 Topola contributors +// +// SPDX-License-Identifier: MIT + +use std::cmp::Ordering; + +use derive_getters::{Dissolve, Getters}; +use enum_dispatch::enum_dispatch; +use petgraph::{algo::tarjan_scc, graph::NodeIndex}; +use specctra_core::mesadata::AccessMesadata; + +use crate::{ + autorouter::{ratline::RatlineIndex, Autorouter}, + geometry::shape::MeasureLength, + graph::MakeRef, +}; + +#[enum_dispatch] +pub trait PresortRatlines { + fn presort_ratlines( + &mut self, + autorouter: &mut Autorouter, + ratlines: Vec, + ) -> Vec; +} + +#[enum_dispatch(PresortRatlines)] +pub enum RatlinesPresorter { + SccIntersectionsLength(SccIntersectionsAndLengthPresorter), +} + +#[derive(Getters, Dissolve)] +pub struct SccIntersectionsAndLengthPresorter { + sccs: Vec>>, +} + +impl SccIntersectionsAndLengthPresorter { + pub fn new( + autorouter: &mut Autorouter, + ratlines: &Vec, + ) -> Self { + // FIXME: Unnecessary copy. + let mut filtered_ratsnest = autorouter.ratsnest().graph().clone(); + filtered_ratsnest.retain_edges(|_g, i| ratlines.contains(&i)); + + let mut sccs = tarjan_scc(&filtered_ratsnest); + let sccs_len = sccs.len(); + + sccs.sort_unstable_by(|a, b| { + // FIXME: These calculations should probably be stored somewhere + // instead of being done every time. + + let mut a_intersector_count = 0; + let mut b_intersector_count = 0; + let mut a_length = 0.0; + let mut b_length = 0.0; + + // FIXME: It's inefficient to iterate over the ratlines on every + // sort comparison. But this is the simplest solution I arrived + // at after realizing that `.tarjan_scc(...)` does not sort nodes + // inside components. + for ratline in ratlines.iter() { + if a.contains(&filtered_ratsnest.edge_endpoints(*ratline).unwrap().0) + && a.contains(&filtered_ratsnest.edge_endpoints(*ratline).unwrap().1) + { + a_length += ratline.ref_(autorouter).length(); + a_intersector_count += ratline + .ref_(autorouter) + .interior_obstacle_ratlines() + .count(); + } + } + + for ratline in ratlines.iter() { + if b.contains(&filtered_ratsnest.edge_endpoints(*ratline).unwrap().0) + && b.contains(&filtered_ratsnest.edge_endpoints(*ratline).unwrap().1) + { + b_length += ratline.ref_(autorouter).length(); + b_intersector_count += ratline + .ref_(autorouter) + .interior_obstacle_ratlines() + .count(); + } + } + + let primary_ordering = a_intersector_count.cmp(&b_intersector_count); + + if primary_ordering != Ordering::Equal { + primary_ordering + } else { + let secondary_ordering = a_length.total_cmp(&b_length); + + secondary_ordering + } + + // Below is how I tried to do this before I realized that + // `.tarjan_scc(...)` does not sort nodes inside components. + + /*let a_intersector_count: usize = a + .windows(2) + .map(|window| { + let ratline = filtered_ratsnest.find_edge(window[0], window[1]).unwrap(); + ratline + .ref_(autorouter) + .interior_obstacle_ratlines() + .count() + }) + .sum(); + let b_intersector_count: usize = b + .windows(2) + .map(|window| { + let ratline = filtered_ratsnest.find_edge(window[0], window[1]).unwrap(); + ratline + .ref_(autorouter) + .interior_obstacle_ratlines() + .count() + }) + .sum(); + + let primary_ordering = a_intersector_count.cmp(&b_intersector_count); + + if primary_ordering != Ordering::Equal { + primary_ordering + } else { + let a_length: f64 = a + .windows(2) + .map(|window| { + let ratline = filtered_ratsnest.find_edge(window[0], window[1]).unwrap(); + ratline.ref_(autorouter).length() + }) + .sum(); + let b_length: f64 = b + .windows(2) + .map(|window| { + let ratline = filtered_ratsnest.find_edge(window[0], window[1]).unwrap(); + ratline.ref_(autorouter).length() + }) + .sum(); + + let secondary_ordering = a_length.total_cmp(&b_length); + + secondary_ordering + }*/ + }); + + Self { sccs } + } +} + +impl PresortRatlines for SccIntersectionsAndLengthPresorter { + fn presort_ratlines( + &mut self, + autorouter: &mut Autorouter, + ratlines: Vec, + ) -> Vec { + let mut presorted_ratlines = vec![]; + + for scc in self.sccs.iter() { + for ratline in ratlines.iter() { + if scc.contains( + &autorouter + .ratsnest() + .graph() + .edge_endpoints(*ratline) + .unwrap() + .0, + ) && scc.contains( + &autorouter + .ratsnest() + .graph() + .edge_endpoints(*ratline) + .unwrap() + .1, + ) { + presorted_ratlines.push(*ratline); + } + } + } + + presorted_ratlines + } +}