mirror of https://codeberg.org/topola/topola.git
refactor(autorouter/permutator): Move presorting to new file, `presorter.rs`
This commit is contained in:
parent
12ee2f3984
commit
fe7c258851
|
|
@ -29,6 +29,7 @@ allowed_scopes = [
|
||||||
"autorouter/permuter",
|
"autorouter/permuter",
|
||||||
"autorouter/place_via",
|
"autorouter/place_via",
|
||||||
"autorouter/pointroute",
|
"autorouter/pointroute",
|
||||||
|
"autorouter/presorter",
|
||||||
"autorouter/ratsnest",
|
"autorouter/ratsnest",
|
||||||
"autorouter/ratline",
|
"autorouter/ratline",
|
||||||
"autorouter/remove_bands",
|
"autorouter/remove_bands",
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ pub mod permutator;
|
||||||
pub mod permuter;
|
pub mod permuter;
|
||||||
pub mod place_via;
|
pub mod place_via;
|
||||||
pub mod pointroute;
|
pub mod pointroute;
|
||||||
|
pub mod presorter;
|
||||||
pub mod ratline;
|
pub mod ratline;
|
||||||
pub mod ratsnest;
|
pub mod ratsnest;
|
||||||
pub mod remove_bands;
|
pub mod remove_bands;
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use crate::{
|
||||||
autorouter::{
|
autorouter::{
|
||||||
autoroute::{AutorouteContinueStatus, AutorouteExecutionStepper},
|
autoroute::{AutorouteContinueStatus, AutorouteExecutionStepper},
|
||||||
invoker::GetDebugOverlayData,
|
invoker::GetDebugOverlayData,
|
||||||
permuter::{PermuteRatlines, RatlinePermuter, SccRatlinePermuter},
|
permuter::{PermuteRatlines, RatlinesPermuter, SccPermutationsRatlinePermuter},
|
||||||
ratline::RatlineIndex,
|
ratline::RatlineIndex,
|
||||||
Autorouter, AutorouterError, AutorouterOptions,
|
Autorouter, AutorouterError, AutorouterOptions,
|
||||||
},
|
},
|
||||||
|
|
@ -23,7 +23,7 @@ use crate::{
|
||||||
|
|
||||||
pub struct AutorouteExecutionPermutator {
|
pub struct AutorouteExecutionPermutator {
|
||||||
stepper: AutorouteExecutionStepper,
|
stepper: AutorouteExecutionStepper,
|
||||||
permuter: RatlinePermuter,
|
permuter: RatlinesPermuter,
|
||||||
options: AutorouterOptions,
|
options: AutorouterOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,9 +33,10 @@ impl AutorouteExecutionPermutator {
|
||||||
ratlines: Vec<RatlineIndex>,
|
ratlines: Vec<RatlineIndex>,
|
||||||
options: AutorouterOptions,
|
options: AutorouterOptions,
|
||||||
) -> Result<Self, AutorouterError> {
|
) -> Result<Self, AutorouterError> {
|
||||||
let mut permuter =
|
let mut permuter = RatlinesPermuter::SccPermutations(SccPermutationsRatlinePermuter::new(
|
||||||
RatlinePermuter::Scc(SccRatlinePermuter::new(autorouter, ratlines, &options));
|
autorouter, ratlines, &options,
|
||||||
let initially_sorted_ratlines = permuter.next_permutation(autorouter).unwrap();
|
));
|
||||||
|
let initially_sorted_ratlines = permuter.next_ratlines_permutation(autorouter).unwrap();
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
stepper: AutorouteExecutionStepper::new(
|
stepper: AutorouteExecutionStepper::new(
|
||||||
|
|
@ -67,7 +68,8 @@ impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, AutorouteContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
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));
|
return Ok(ControlFlow::Break(None));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,147 +2,58 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
|
||||||
|
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
use itertools::{Itertools, Permutations};
|
use itertools::{Itertools, Permutations};
|
||||||
use petgraph::{algo::tarjan_scc, graph::NodeIndex};
|
use petgraph::graph::NodeIndex;
|
||||||
use specctra_core::mesadata::AccessMesadata;
|
use specctra_core::mesadata::AccessMesadata;
|
||||||
|
|
||||||
use crate::{
|
use crate::autorouter::{
|
||||||
autorouter::{ratline::RatlineIndex, Autorouter, AutorouterOptions},
|
presorter::SccIntersectionsAndLengthPresorter, ratline::RatlineIndex, Autorouter,
|
||||||
geometry::shape::MeasureLength,
|
AutorouterOptions,
|
||||||
graph::MakeRef,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[enum_dispatch]
|
#[enum_dispatch]
|
||||||
pub trait PermuteRatlines {
|
pub trait PermuteRatlines {
|
||||||
fn next_permutation(
|
fn next_ratlines_permutation(
|
||||||
&mut self,
|
&mut self,
|
||||||
autorouter: &mut Autorouter<impl AccessMesadata>,
|
autorouter: &mut Autorouter<impl AccessMesadata>,
|
||||||
) -> Option<Vec<RatlineIndex>>;
|
) -> Option<Vec<RatlineIndex>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[enum_dispatch(PermuteRatlines)]
|
#[enum_dispatch(PermuteRatlines)]
|
||||||
pub enum RatlinePermuter {
|
pub enum RatlinesPermuter {
|
||||||
Scc(SccRatlinePermuter),
|
SccPermutations(SccPermutationsRatlinePermuter),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SccRatlinePermuter {
|
impl RatlinesPermuter {
|
||||||
|
pub fn new(
|
||||||
|
autorouter: &mut Autorouter<impl AccessMesadata>,
|
||||||
|
ratlines: Vec<RatlineIndex>,
|
||||||
|
options: &AutorouterOptions,
|
||||||
|
) -> Self {
|
||||||
|
RatlinesPermuter::SccPermutations(SccPermutationsRatlinePermuter::new(
|
||||||
|
autorouter, ratlines, options,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SccPermutationsRatlinePermuter {
|
||||||
sccs_permutations_iter: Permutations<std::vec::IntoIter<Vec<NodeIndex<usize>>>>,
|
sccs_permutations_iter: Permutations<std::vec::IntoIter<Vec<NodeIndex<usize>>>>,
|
||||||
ratlines: Vec<RatlineIndex>,
|
ratlines: Vec<RatlineIndex>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SccRatlinePermuter {
|
impl SccPermutationsRatlinePermuter {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
autorouter: &mut Autorouter<impl AccessMesadata>,
|
autorouter: &mut Autorouter<impl AccessMesadata>,
|
||||||
ratlines: Vec<RatlineIndex>,
|
ratlines: Vec<RatlineIndex>,
|
||||||
_options: &AutorouterOptions,
|
_options: &AutorouterOptions,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// FIXME: Unnecessary copy.
|
// TODO: Instead of instantiating presorter again here, get it from
|
||||||
let mut filtered_ratsnest = autorouter.ratsnest().graph().clone();
|
// an argument.
|
||||||
filtered_ratsnest.retain_edges(|_g, i| ratlines.contains(&i));
|
let presorter = SccIntersectionsAndLengthPresorter::new(autorouter, &ratlines);
|
||||||
|
let sccs = presorter.dissolve();
|
||||||
let mut sccs = tarjan_scc(&filtered_ratsnest);
|
|
||||||
let sccs_len = sccs.len();
|
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 {
|
Self {
|
||||||
sccs_permutations_iter: sccs.into_iter().permutations(sccs_len),
|
sccs_permutations_iter: sccs.into_iter().permutations(sccs_len),
|
||||||
ratlines,
|
ratlines,
|
||||||
|
|
@ -150,8 +61,8 @@ impl SccRatlinePermuter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PermuteRatlines for SccRatlinePermuter {
|
impl PermuteRatlines for SccPermutationsRatlinePermuter {
|
||||||
fn next_permutation(
|
fn next_ratlines_permutation(
|
||||||
&mut self,
|
&mut self,
|
||||||
autorouter: &mut Autorouter<impl AccessMesadata>,
|
autorouter: &mut Autorouter<impl AccessMesadata>,
|
||||||
) -> Option<Vec<RatlineIndex>> {
|
) -> Option<Vec<RatlineIndex>> {
|
||||||
|
|
|
||||||
|
|
@ -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<impl AccessMesadata>,
|
||||||
|
ratlines: Vec<RatlineIndex>,
|
||||||
|
) -> Vec<RatlineIndex>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[enum_dispatch(PresortRatlines)]
|
||||||
|
pub enum RatlinesPresorter {
|
||||||
|
SccIntersectionsLength(SccIntersectionsAndLengthPresorter),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Getters, Dissolve)]
|
||||||
|
pub struct SccIntersectionsAndLengthPresorter {
|
||||||
|
sccs: Vec<Vec<NodeIndex<usize>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SccIntersectionsAndLengthPresorter {
|
||||||
|
pub fn new(
|
||||||
|
autorouter: &mut Autorouter<impl AccessMesadata>,
|
||||||
|
ratlines: &Vec<RatlineIndex>,
|
||||||
|
) -> 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<impl AccessMesadata>,
|
||||||
|
ratlines: Vec<RatlineIndex>,
|
||||||
|
) -> Vec<RatlineIndex> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue