feat(autorouter/permuter): Implement permutation by ratline cut

Not yet used, and can get stuck in an infinite loop, but the cut seems
to work.
This commit is contained in:
Mikolaj Wielgus 2025-09-09 15:50:44 +02:00
parent fe7c258851
commit 1b485e81a6
8 changed files with 134 additions and 44 deletions

View File

@ -9,7 +9,7 @@
feature = "serde", feature = "serde",
serde(bound(deserialize = "T: serde::Deserialize<'de> + Ord")) serde(bound(deserialize = "T: serde::Deserialize<'de> + Ord"))
)] )]
pub struct OrderedPair<T>(T, T); pub struct OrderedPair<T>(pub T, pub T);
impl<T> core::ops::Index<bool> for OrderedPair<T> { impl<T> core::ops::Index<bool> for OrderedPair<T> {
type Output = T; type Output = T;

View File

@ -7,6 +7,8 @@
use std::ops::ControlFlow; use std::ops::ControlFlow;
use derive_getters::Getters;
use crate::{ use crate::{
board::{ board::{
edit::{BoardDataEdit, BoardEdit}, edit::{BoardDataEdit, BoardEdit},
@ -38,6 +40,7 @@ pub enum AutorouteContinueStatus {
} }
/// Manages the autorouting process across multiple ratlines. /// Manages the autorouting process across multiple ratlines.
#[derive(Getters)]
pub struct AutorouteExecutionStepper { pub struct AutorouteExecutionStepper {
/// The ratlines which we are routing. /// The ratlines which we are routing.
ratlines: Vec<RatlineIndex>, ratlines: Vec<RatlineIndex>,

View File

@ -130,13 +130,7 @@ impl<M: AccessMesadata> Autorouter<M> {
ratlines: Vec<RatlineIndex>, ratlines: Vec<RatlineIndex>,
) -> Result<(), AutorouterError> { ) -> Result<(), AutorouterError> {
for ratline in ratlines.iter() { for ratline in ratlines.iter() {
let band = self let band = ratline.ref_(self).band_termseg();
.ratsnest
.graph()
.edge_weight(*ratline)
.unwrap()
.band_termseg
.unwrap();
self.board self.board
.layout_mut() .layout_mut()
.remove_band(&mut LayoutEdit::new(), band) .remove_band(&mut LayoutEdit::new(), band)

View File

@ -10,7 +10,8 @@ use crate::{
autorouter::{ autorouter::{
autoroute::{AutorouteContinueStatus, AutorouteExecutionStepper}, autoroute::{AutorouteContinueStatus, AutorouteExecutionStepper},
invoker::GetDebugOverlayData, invoker::GetDebugOverlayData,
permuter::{PermuteRatlines, RatlinesPermuter, SccPermutationsRatlinePermuter}, permuter::{PermuteRatlines, RatlinesPermuter},
presorter::{PresortRatlines, SccIntersectionsAndLengthPresorter},
ratline::RatlineIndex, ratline::RatlineIndex,
Autorouter, AutorouterError, AutorouterOptions, Autorouter, AutorouterError, AutorouterOptions,
}, },
@ -33,10 +34,12 @@ impl AutorouteExecutionPermutator {
ratlines: Vec<RatlineIndex>, ratlines: Vec<RatlineIndex>,
options: AutorouterOptions, options: AutorouterOptions,
) -> Result<Self, AutorouterError> { ) -> Result<Self, AutorouterError> {
let mut permuter = RatlinesPermuter::SccPermutations(SccPermutationsRatlinePermuter::new( let presorter = SccIntersectionsAndLengthPresorter::new(autorouter, &ratlines);
autorouter, ratlines, &options, let initially_sorted_ratlines = presorter.presort_ratlines(autorouter, &ratlines);
)); /*let permuter = RatlinesPermuter::SccPermutations(SccPermutationsRatlinePermuter::new(
let initially_sorted_ratlines = permuter.next_ratlines_permutation(autorouter).unwrap(); autorouter, ratlines, presorter, &options,
));*/
let permuter = RatlinesPermuter::new(autorouter, ratlines, presorter, &options);
Ok(Self { Ok(Self {
stepper: AutorouteExecutionStepper::new( stepper: AutorouteExecutionStepper::new(
@ -68,7 +71,8 @@ impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, AutorouteContinue
} }
loop { loop {
let Some(permutation) = self.permuter.next_ratlines_permutation(autorouter) let Some(permutation) =
self.permuter.permute_ratlines(autorouter, &self.stepper)
else { else {
return Ok(ControlFlow::Break(None)); return Ok(ControlFlow::Break(None));
}; };

View File

@ -2,26 +2,35 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
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 petgraph::graph::NodeIndex;
use specctra_core::mesadata::AccessMesadata; use specctra_core::mesadata::AccessMesadata;
use crate::autorouter::{ use crate::{
presorter::SccIntersectionsAndLengthPresorter, ratline::RatlineIndex, Autorouter, autorouter::{
AutorouterOptions, autoroute::AutorouteExecutionStepper, presorter::SccIntersectionsAndLengthPresorter,
ratline::RatlineIndex, Autorouter, AutorouterOptions,
},
drawing::graph::MakePrimitive,
geometry::{GenericNode, GetLayer},
graph::MakeRef,
}; };
#[enum_dispatch] #[enum_dispatch]
pub trait PermuteRatlines { pub trait PermuteRatlines {
fn next_ratlines_permutation( fn permute_ratlines(
&mut self, &mut self,
autorouter: &mut Autorouter<impl AccessMesadata>, autorouter: &mut Autorouter<impl AccessMesadata>,
stepper: &AutorouteExecutionStepper,
) -> Option<Vec<RatlineIndex>>; ) -> Option<Vec<RatlineIndex>>;
} }
#[enum_dispatch(PermuteRatlines)] #[enum_dispatch(PermuteRatlines)]
pub enum RatlinesPermuter { pub enum RatlinesPermuter {
RatlineCuts(RatlineCutsRatlinePermuter),
SccPermutations(SccPermutationsRatlinePermuter), SccPermutations(SccPermutationsRatlinePermuter),
} }
@ -29,48 +38,53 @@ impl RatlinesPermuter {
pub fn new( pub fn new(
autorouter: &mut Autorouter<impl AccessMesadata>, autorouter: &mut Autorouter<impl AccessMesadata>,
ratlines: Vec<RatlineIndex>, ratlines: Vec<RatlineIndex>,
presorter: SccIntersectionsAndLengthPresorter,
options: &AutorouterOptions, options: &AutorouterOptions,
) -> Self { ) -> Self {
RatlinesPermuter::SccPermutations(SccPermutationsRatlinePermuter::new( RatlinesPermuter::SccPermutations(SccPermutationsRatlinePermuter::new(
autorouter, ratlines, options, autorouter, ratlines, presorter, options,
)) ))
/*RatlinesPermuter::RatlineCuts(RatlineCutsRatlinePermuter::new(
autorouter, ratlines, presorter, options,
))*/
} }
} }
pub struct SccPermutationsRatlinePermuter { pub struct SccPermutationsRatlinePermuter {
sccs_permutations_iter: Permutations<std::vec::IntoIter<Vec<NodeIndex<usize>>>>, sccs_permutations_iter: Skip<Permutations<std::vec::IntoIter<Vec<NodeIndex<usize>>>>>,
ratlines: Vec<RatlineIndex>, original_ratlines: Vec<RatlineIndex>,
} }
impl SccPermutationsRatlinePermuter { impl SccPermutationsRatlinePermuter {
pub fn new( pub fn new(
autorouter: &mut Autorouter<impl AccessMesadata>, _autorouter: &mut Autorouter<impl AccessMesadata>,
ratlines: Vec<RatlineIndex>, ratlines: Vec<RatlineIndex>,
presorter: SccIntersectionsAndLengthPresorter,
_options: &AutorouterOptions, _options: &AutorouterOptions,
) -> Self { ) -> Self {
// TODO: Instead of instantiating presorter again here, get it from // TODO: Instead of instantiating presorter again here, get it from
// an argument. // an argument.
let presorter = SccIntersectionsAndLengthPresorter::new(autorouter, &ratlines);
let sccs = presorter.dissolve(); let sccs = presorter.dissolve();
let sccs_len = sccs.len(); let sccs_len = sccs.len();
Self { Self {
sccs_permutations_iter: sccs.into_iter().permutations(sccs_len), sccs_permutations_iter: sccs.into_iter().permutations(sccs_len).skip(1),
ratlines, original_ratlines: ratlines,
} }
} }
} }
impl PermuteRatlines for SccPermutationsRatlinePermuter { impl PermuteRatlines for SccPermutationsRatlinePermuter {
fn next_ratlines_permutation( fn permute_ratlines(
&mut self, &mut self,
autorouter: &mut Autorouter<impl AccessMesadata>, autorouter: &mut Autorouter<impl AccessMesadata>,
_stepper: &AutorouteExecutionStepper,
) -> Option<Vec<RatlineIndex>> { ) -> Option<Vec<RatlineIndex>> {
let scc_permutation = self.sccs_permutations_iter.next()?; let scc_permutation = self.sccs_permutations_iter.next()?;
let mut ratlines = vec![]; let mut ratlines = vec![];
for scc in scc_permutation { for scc in scc_permutation {
for ratline in self.ratlines.iter() { for ratline in self.original_ratlines.iter() {
if scc.contains( if scc.contains(
&autorouter &autorouter
.ratsnest() .ratsnest()
@ -94,3 +108,68 @@ impl PermuteRatlines for SccPermutationsRatlinePermuter {
Some(ratlines) Some(ratlines)
} }
} }
pub struct RatlineCutsRatlinePermuter {
//sccs: Vec<Vec<NodeIndex<usize>>>,
}
impl RatlineCutsRatlinePermuter {
pub fn new(
_autorouter: &mut Autorouter<impl AccessMesadata>,
_ratlines: Vec<RatlineIndex>,
_presorter: SccIntersectionsAndLengthPresorter,
_options: &AutorouterOptions,
) -> Self {
/*Self {
sccs: presorter.dissolve(),
}*/
Self {}
}
}
impl PermuteRatlines for RatlineCutsRatlinePermuter {
fn permute_ratlines(
&mut self,
autorouter: &mut Autorouter<impl AccessMesadata>,
stepper: &AutorouteExecutionStepper,
) -> Option<Vec<RatlineIndex>> {
let curr_ratline = stepper.ratlines()[*stepper.curr_ratline_index()];
let endpoint_dots = curr_ratline.ref_(autorouter).endpoint_dots();
let bands_cut_by_ratline: Vec<_> = autorouter
.board()
.layout()
.bands_between_nodes(
endpoint_dots
.0
.primitive(autorouter.board().layout().drawing())
.layer(),
GenericNode::Primitive(endpoint_dots.0.into()),
GenericNode::Primitive(endpoint_dots.1.into()),
)
.collect();
// Find the first ratline corresponding to a band that is cut.
let first_cut_ratline_index = stepper
.ratlines()
.iter()
.position(|ratline| {
for (band_uid, _) in &bands_cut_by_ratline {
if ratline.ref_(autorouter).band_termseg() == band_uid.0
|| ratline.ref_(autorouter).band_termseg() == band_uid.1
{
return true;
}
}
false
})
.unwrap();
// Swap the first ratline corresponding to a band that is cut with the
// ratline that we have failed routing.
let mut ratlines = stepper.ratlines().clone();
ratlines.swap(*stepper.curr_ratline_index(), first_cut_ratline_index);
Some(ratlines)
}
}

View File

@ -18,9 +18,9 @@ use crate::{
#[enum_dispatch] #[enum_dispatch]
pub trait PresortRatlines { pub trait PresortRatlines {
fn presort_ratlines( fn presort_ratlines(
&mut self, &self,
autorouter: &mut Autorouter<impl AccessMesadata>, autorouter: &mut Autorouter<impl AccessMesadata>,
ratlines: Vec<RatlineIndex>, ratlines: &[RatlineIndex],
) -> Vec<RatlineIndex>; ) -> Vec<RatlineIndex>;
} }
@ -37,14 +37,13 @@ pub struct SccIntersectionsAndLengthPresorter {
impl SccIntersectionsAndLengthPresorter { impl SccIntersectionsAndLengthPresorter {
pub fn new( pub fn new(
autorouter: &mut Autorouter<impl AccessMesadata>, autorouter: &mut Autorouter<impl AccessMesadata>,
ratlines: &Vec<RatlineIndex>, ratlines: &[RatlineIndex],
) -> Self { ) -> Self {
// FIXME: Unnecessary copy. // FIXME: Unnecessary copy.
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 = tarjan_scc(&filtered_ratsnest);
let sccs_len = sccs.len();
sccs.sort_unstable_by(|a, b| { sccs.sort_unstable_by(|a, b| {
// FIXME: These calculations should probably be stored somewhere // FIXME: These calculations should probably be stored somewhere
@ -149,9 +148,9 @@ impl SccIntersectionsAndLengthPresorter {
impl PresortRatlines for SccIntersectionsAndLengthPresorter { impl PresortRatlines for SccIntersectionsAndLengthPresorter {
fn presort_ratlines( fn presort_ratlines(
&mut self, &self,
autorouter: &mut Autorouter<impl AccessMesadata>, autorouter: &mut Autorouter<impl AccessMesadata>,
ratlines: Vec<RatlineIndex>, ratlines: &[RatlineIndex],
) -> Vec<RatlineIndex> { ) -> Vec<RatlineIndex> {
let mut presorted_ratlines = vec![]; let mut presorted_ratlines = vec![];

View File

@ -39,6 +39,16 @@ impl<'a, M: AccessMesadata> RatlineRef<'a, M> {
Self { index, autorouter } Self { index, autorouter }
} }
pub fn band_termseg(&self) -> BandTermsegIndex {
self.autorouter
.ratsnest()
.graph()
.edge_weight(self.index)
.unwrap()
.band_termseg
.unwrap()
}
pub fn endpoint_dots(&self) -> (FixedDotIndex, FixedDotIndex) { pub fn endpoint_dots(&self) -> (FixedDotIndex, FixedDotIndex) {
let (source, target) = self let (source, target) = self
.autorouter .autorouter

View File

@ -89,7 +89,8 @@ impl Ratsnest {
let node_bound = layout.drawing().geometry().graph().node_bound(); let node_bound = layout.drawing().geometry().graph().node_bound();
for layer in 0..layout.drawing().layer_count() { for layer in 0..layout.drawing().layer_count() {
let mut handle_rvw = |maybe_net: Option<usize>, vertex: RatvertexIndex, pos: Point| { let mut handle_ratvertex_weight =
|maybe_net: Option<usize>, vertex: RatvertexIndex, pos: Point| {
if let Some(net) = maybe_net { if let Some(net) = maybe_net {
triangulations triangulations
.entry((layer, net)) .entry((layer, net))
@ -104,7 +105,7 @@ impl Ratsnest {
// Dots that are parts of polys are ignored because ratlines // Dots that are parts of polys are ignored because ratlines
// should only go to their centerpoints. // should only go to their centerpoints.
if layout.drawing().compounds(dot).next().is_none() { if layout.drawing().compounds(dot).next().is_none() {
handle_rvw( handle_ratvertex_weight(
layout.drawing().primitive(dot).maybe_net(), layout.drawing().primitive(dot).maybe_net(),
RatvertexIndex::FixedDot(dot), RatvertexIndex::FixedDot(dot),
node.primitive(layout.drawing()).shape().center(), node.primitive(layout.drawing()).shape().center(),
@ -114,7 +115,7 @@ impl Ratsnest {
} }
for poly in layout.layer_poly_nodes(layer) { for poly in layout.layer_poly_nodes(layer) {
handle_rvw( handle_ratvertex_weight(
layout.drawing().compound_weight(poly.into()).maybe_net(), layout.drawing().compound_weight(poly.into()).maybe_net(),
RatvertexIndex::Poly(poly), RatvertexIndex::Poly(poly),
poly.ref_(layout).shape().center(), poly.ref_(layout).shape().center(),