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",
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> {
type Output = T;

View File

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

View File

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

View File

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

View File

@ -2,26 +2,35 @@
//
// SPDX-License-Identifier: MIT
use std::iter::Skip;
use enum_dispatch::enum_dispatch;
use itertools::{Itertools, Permutations};
use petgraph::graph::NodeIndex;
use specctra_core::mesadata::AccessMesadata;
use crate::autorouter::{
presorter::SccIntersectionsAndLengthPresorter, ratline::RatlineIndex, Autorouter,
AutorouterOptions,
use crate::{
autorouter::{
autoroute::AutorouteExecutionStepper, presorter::SccIntersectionsAndLengthPresorter,
ratline::RatlineIndex, Autorouter, AutorouterOptions,
},
drawing::graph::MakePrimitive,
geometry::{GenericNode, GetLayer},
graph::MakeRef,
};
#[enum_dispatch]
pub trait PermuteRatlines {
fn next_ratlines_permutation(
fn permute_ratlines(
&mut self,
autorouter: &mut Autorouter<impl AccessMesadata>,
stepper: &AutorouteExecutionStepper,
) -> Option<Vec<RatlineIndex>>;
}
#[enum_dispatch(PermuteRatlines)]
pub enum RatlinesPermuter {
RatlineCuts(RatlineCutsRatlinePermuter),
SccPermutations(SccPermutationsRatlinePermuter),
}
@ -29,48 +38,53 @@ impl RatlinesPermuter {
pub fn new(
autorouter: &mut Autorouter<impl AccessMesadata>,
ratlines: Vec<RatlineIndex>,
presorter: SccIntersectionsAndLengthPresorter,
options: &AutorouterOptions,
) -> Self {
RatlinesPermuter::SccPermutations(SccPermutationsRatlinePermuter::new(
autorouter, ratlines, options,
autorouter, ratlines, presorter, options,
))
/*RatlinesPermuter::RatlineCuts(RatlineCutsRatlinePermuter::new(
autorouter, ratlines, presorter, options,
))*/
}
}
pub struct SccPermutationsRatlinePermuter {
sccs_permutations_iter: Permutations<std::vec::IntoIter<Vec<NodeIndex<usize>>>>,
ratlines: Vec<RatlineIndex>,
sccs_permutations_iter: Skip<Permutations<std::vec::IntoIter<Vec<NodeIndex<usize>>>>>,
original_ratlines: Vec<RatlineIndex>,
}
impl SccPermutationsRatlinePermuter {
pub fn new(
autorouter: &mut Autorouter<impl AccessMesadata>,
_autorouter: &mut Autorouter<impl AccessMesadata>,
ratlines: Vec<RatlineIndex>,
presorter: SccIntersectionsAndLengthPresorter,
_options: &AutorouterOptions,
) -> Self {
// 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();
Self {
sccs_permutations_iter: sccs.into_iter().permutations(sccs_len),
ratlines,
sccs_permutations_iter: sccs.into_iter().permutations(sccs_len).skip(1),
original_ratlines: ratlines,
}
}
}
impl PermuteRatlines for SccPermutationsRatlinePermuter {
fn next_ratlines_permutation(
fn permute_ratlines(
&mut self,
autorouter: &mut Autorouter<impl AccessMesadata>,
_stepper: &AutorouteExecutionStepper,
) -> Option<Vec<RatlineIndex>> {
let scc_permutation = self.sccs_permutations_iter.next()?;
let mut ratlines = vec![];
for scc in scc_permutation {
for ratline in self.ratlines.iter() {
for ratline in self.original_ratlines.iter() {
if scc.contains(
&autorouter
.ratsnest()
@ -94,3 +108,68 @@ impl PermuteRatlines for SccPermutationsRatlinePermuter {
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]
pub trait PresortRatlines {
fn presort_ratlines(
&mut self,
&self,
autorouter: &mut Autorouter<impl AccessMesadata>,
ratlines: Vec<RatlineIndex>,
ratlines: &[RatlineIndex],
) -> Vec<RatlineIndex>;
}
@ -37,14 +37,13 @@ pub struct SccIntersectionsAndLengthPresorter {
impl SccIntersectionsAndLengthPresorter {
pub fn new(
autorouter: &mut Autorouter<impl AccessMesadata>,
ratlines: &Vec<RatlineIndex>,
ratlines: &[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
@ -149,9 +148,9 @@ impl SccIntersectionsAndLengthPresorter {
impl PresortRatlines for SccIntersectionsAndLengthPresorter {
fn presort_ratlines(
&mut self,
&self,
autorouter: &mut Autorouter<impl AccessMesadata>,
ratlines: Vec<RatlineIndex>,
ratlines: &[RatlineIndex],
) -> Vec<RatlineIndex> {
let mut presorted_ratlines = vec![];

View File

@ -39,6 +39,16 @@ impl<'a, M: AccessMesadata> RatlineRef<'a, M> {
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) {
let (source, target) = self
.autorouter

View File

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