refactor(autorouter/presorter): Move some SCC code to new file

This commit is contained in:
Mikolaj Wielgus 2025-09-25 14:32:35 +02:00
parent a703b1ec4e
commit c120a43d04
4 changed files with 111 additions and 115 deletions

View File

@ -18,6 +18,7 @@ pub mod presorter;
pub mod ratline; pub mod ratline;
pub mod ratsnest; pub mod ratsnest;
pub mod remove_bands; pub mod remove_bands;
pub mod scc;
pub mod selection; pub mod selection;
pub use autorouter::*; pub use autorouter::*;

View File

@ -6,13 +6,12 @@ use std::iter::Skip;
use enum_dispatch::enum_dispatch; use enum_dispatch::enum_dispatch;
use itertools::{Itertools, Permutations}; use itertools::{Itertools, Permutations};
use petgraph::graph::NodeIndex;
use specctra_core::mesadata::AccessMesadata; use specctra_core::mesadata::AccessMesadata;
use crate::{ use crate::{
autorouter::{ autorouter::{
autoroute::AutorouteExecutionStepper, presorter::SccIntersectionsAndLengthPresorter, autoroute::AutorouteExecutionStepper, presorter::SccIntersectionsAndLengthPresorter,
ratline::RatlineIndex, Autorouter, AutorouterOptions, ratline::RatlineIndex, scc::Scc, Autorouter, AutorouterOptions,
}, },
drawing::graph::MakePrimitiveRef, drawing::graph::MakePrimitiveRef,
geometry::{GenericNode, GetLayer}, geometry::{GenericNode, GetLayer},
@ -51,7 +50,7 @@ impl RatlinesPermuter {
} }
pub struct SccPermutationsRatlinePermuter { pub struct SccPermutationsRatlinePermuter {
sccs_permutations_iter: Skip<Permutations<std::vec::IntoIter<Vec<NodeIndex<usize>>>>>, sccs_permutations_iter: Skip<Permutations<std::vec::IntoIter<Scc>>>,
original_ratlines: Vec<RatlineIndex>, original_ratlines: Vec<RatlineIndex>,
} }
@ -85,14 +84,14 @@ impl PermuteRatlines for SccPermutationsRatlinePermuter {
for scc in scc_permutation { for scc in scc_permutation {
for ratline in self.original_ratlines.iter() { for ratline in self.original_ratlines.iter() {
if scc.contains( if scc.node_indices().contains(
&autorouter &autorouter
.ratsnest() .ratsnest()
.graph() .graph()
.edge_endpoints(*ratline) .edge_endpoints(*ratline)
.unwrap() .unwrap()
.0, .0,
) && scc.contains( ) && scc.node_indices().contains(
&autorouter &autorouter
.ratsnest() .ratsnest()
.graph() .graph()

View File

@ -6,14 +6,10 @@ use std::cmp::Ordering;
use derive_getters::{Dissolve, Getters}; use derive_getters::{Dissolve, Getters};
use enum_dispatch::enum_dispatch; use enum_dispatch::enum_dispatch;
use petgraph::{algo::tarjan_scc, graph::NodeIndex}; use petgraph::algo::tarjan_scc;
use specctra_core::mesadata::AccessMesadata; use specctra_core::mesadata::AccessMesadata;
use crate::{ use crate::autorouter::{ratline::RatlineIndex, scc::Scc, Autorouter};
autorouter::{ratline::RatlineIndex, Autorouter},
geometry::shape::MeasureLength,
graph::MakeRef,
};
#[enum_dispatch] #[enum_dispatch]
pub trait PresortRatlines { pub trait PresortRatlines {
@ -31,7 +27,7 @@ pub enum RatlinesPresorter {
#[derive(Getters, Dissolve)] #[derive(Getters, Dissolve)]
pub struct SccIntersectionsAndLengthPresorter { pub struct SccIntersectionsAndLengthPresorter {
sccs: Vec<Vec<NodeIndex<usize>>>, sccs: Vec<Scc>,
} }
impl SccIntersectionsAndLengthPresorter { impl SccIntersectionsAndLengthPresorter {
@ -43,103 +39,21 @@ impl SccIntersectionsAndLengthPresorter {
let mut filtered_ratsnest = autorouter.ratsnest().graph().clone(); let mut filtered_ratsnest = autorouter.ratsnest().graph().clone();
filtered_ratsnest.retain_edges(|_g, i| ratlines.contains(&i)); filtered_ratsnest.retain_edges(|_g, i| ratlines.contains(&i));
let mut sccs = tarjan_scc(&filtered_ratsnest); let mut sccs: Vec<_> = tarjan_scc(&filtered_ratsnest)
.into_iter()
.map(|node_indices| Scc::new(autorouter, ratlines, &filtered_ratsnest, node_indices))
.collect();
sccs.sort_unstable_by(|a, b| { sccs.sort_unstable_by(|a, b| {
// FIXME: These calculations should probably be stored somewhere let primary_ordering = a.intersector_count().cmp(&b.intersector_count());
// 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).interiorly_cut_ratlines().count();
a_intersector_count +=
ratline.ref_(autorouter).cut_other_net_primitives().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).interiorly_cut_ratlines().count();
b_intersector_count +=
ratline.ref_(autorouter).cut_other_net_primitives().count();
}
}
let primary_ordering = a_intersector_count.cmp(&b_intersector_count);
if primary_ordering != Ordering::Equal { if primary_ordering != Ordering::Equal {
primary_ordering primary_ordering
} else { } else {
let secondary_ordering = a_length.total_cmp(&b_length); let secondary_ordering = a.length().total_cmp(&b.length());
secondary_ordering 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 } Self { sccs }
@ -156,21 +70,7 @@ impl PresortRatlines for SccIntersectionsAndLengthPresorter {
for scc in self.sccs.iter() { for scc in self.sccs.iter() {
for ratline in ratlines.iter() { for ratline in ratlines.iter() {
if scc.contains( if scc.scc_ref(autorouter).contains(*ratline) {
&autorouter
.ratsnest()
.graph()
.edge_endpoints(*ratline)
.unwrap()
.0,
) && scc.contains(
&autorouter
.ratsnest()
.graph()
.edge_endpoints(*ratline)
.unwrap()
.1,
) {
presorted_ratlines.push(*ratline); presorted_ratlines.push(*ratline);
} }
} }

96
src/autorouter/scc.rs Normal file
View File

@ -0,0 +1,96 @@
// SPDX-FileCopyrightText: 2025 Topola contributors
//
// SPDX-License-Identifier: MIT
use derive_getters::Getters;
use petgraph::{graph::NodeIndex, prelude::StableUnGraph};
use specctra_core::mesadata::AccessMesadata;
use crate::{
autorouter::{
ratline::{RatlineIndex, RatlineWeight},
ratsnest::RatvertexWeight,
Autorouter,
},
geometry::shape::MeasureLength,
graph::MakeRef,
};
#[derive(Clone, Debug, Getters)]
pub struct Scc {
node_indices: Vec<NodeIndex<usize>>,
length: f64,
intersector_count: usize,
}
impl Scc {
pub fn new(
autorouter: &mut Autorouter<impl AccessMesadata>,
ratlines: &[RatlineIndex],
filtered_ratsnest: &StableUnGraph<RatvertexWeight, RatlineWeight, usize>,
node_indices: Vec<NodeIndex<usize>>,
) -> Self {
let mut this = Self {
node_indices,
length: 0.0,
intersector_count: 0,
};
for ratline in ratlines.iter() {
if this
.node_indices
.contains(&filtered_ratsnest.edge_endpoints(*ratline).unwrap().0)
&& this
.node_indices
.contains(&filtered_ratsnest.edge_endpoints(*ratline).unwrap().1)
{
this.length += ratline.ref_(autorouter).length();
this.intersector_count +=
ratline.ref_(autorouter).interiorly_cut_ratlines().count();
this.intersector_count +=
ratline.ref_(autorouter).cut_other_net_primitives().count();
}
}
this
}
pub fn scc_ref<'a, M: AccessMesadata>(
&'a self,
autorouter: &'a Autorouter<M>,
) -> SccRef<'a, M> {
SccRef::new(self, autorouter)
}
}
pub struct SccRef<'a, M: AccessMesadata> {
scc: &'a Scc,
autorouter: &'a Autorouter<M>,
}
impl<'a, M: AccessMesadata> SccRef<'a, M> {
pub fn new(scc: &'a Scc, autorouter: &'a Autorouter<M>) -> Self {
Self { scc, autorouter }
}
pub fn contains(&self, ratline: RatlineIndex) -> bool {
self.scc.node_indices().contains(
&self
.autorouter
.ratsnest()
.graph()
.edge_endpoints(ratline)
.unwrap()
.0,
) && self.scc.node_indices().contains(
&self
.autorouter
.ratsnest()
.graph()
.edge_endpoints(ratline)
.unwrap()
.1,
)
}
}