From d181c7df1b6ff1cd8e79c00bb6f1d62115cac561 Mon Sep 17 00:00:00 2001 From: Mikolaj Wielgus Date: Fri, 18 Jul 2025 23:28:22 +0200 Subject: [PATCH] feat(autorouter/autoroute): Implement basic brute-force permutator Permutates if and until a solution is found. Does not do optimization so far. Thanks to this, it is now possible to route the DE-9 to DE-9 test without having to find the correct sequence of autorouting jobs. --- Cargo.toml | 1 + src/autorouter/autoroute.rs | 93 +++++++++++++++++++++++++++++++++++- src/autorouter/autorouter.rs | 40 ++++++++++++++-- src/autorouter/execution.rs | 4 +- src/autorouter/invoker.rs | 53 +++----------------- 5 files changed, 137 insertions(+), 54 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ddc5c2d..a91862f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,6 +53,7 @@ contracts-try = "0.7" derive-getters.workspace = true enum_dispatch = "0.3" geo.workspace = true +itertools = "0.14" log.workspace = true petgraph.workspace = true ron = "0.10" diff --git a/src/autorouter/autoroute.rs b/src/autorouter/autoroute.rs index 15d745d..2203bb6 100644 --- a/src/autorouter/autoroute.rs +++ b/src/autorouter/autoroute.rs @@ -5,6 +5,7 @@ //! Manages autorouting of ratlines in a layout, tracking status and processed //! routing steps. +use itertools::{Itertools, Permutations}; use std::ops::ControlFlow; use crate::{ @@ -33,7 +34,7 @@ pub enum AutorouteContinueStatus { Running, /// A specific segment has been successfully routed. Routed(BandTermsegIndex), - /// A specific segment was already routed and has been skipped. + /// A specific segment had been already routed and has been skipped. Skipped(BandTermsegIndex), } @@ -103,6 +104,8 @@ impl Step, Option, AutorouteContinue &mut self, autorouter: &mut Autorouter, ) -> Result, AutorouteContinueStatus>, AutorouterError> { + // TODO: Use a proper state machine here for better readability? + if self.curr_ratline_index >= self.ratlines.len() { let recorder = self.dissolve_route_stepper_into_layout_edit(); return Ok(ControlFlow::Break(Some(BoardEdit::new_from_edits( @@ -214,3 +217,91 @@ impl GetDebugOverlayData for AutorouteExecutionStepper { self.route.as_ref().map_or(&[], |route| route.obstacles()) } } + +pub struct AutorouteExecutionPermutator { + stepper: AutorouteExecutionStepper, + permutations_iter: Permutations>, + options: AutorouterOptions, +} + +impl AutorouteExecutionPermutator { + pub fn new( + autorouter: &mut Autorouter, + ratlines: Vec, + options: AutorouterOptions, + ) -> Result { + let ratlines_len = ratlines.len(); + + Ok(Self { + stepper: AutorouteExecutionStepper::new(autorouter, ratlines.clone(), options)?, + permutations_iter: ratlines.into_iter().permutations(ratlines_len), + options, + }) + } +} + +impl Step, Option, AutorouteContinueStatus> + for AutorouteExecutionPermutator +{ + type Error = AutorouterError; + + fn step( + &mut self, + autorouter: &mut Autorouter, + ) -> Result, AutorouteContinueStatus>, AutorouterError> { + match self.stepper.step(autorouter) { + Ok(ok) => Ok(ok), + Err(..) => { + self.stepper.abort(autorouter); + + let Some(new_permutation) = self.permutations_iter.next() else { + return Ok(ControlFlow::Break(None)); + }; + + self.stepper = + AutorouteExecutionStepper::new(autorouter, new_permutation, self.options)?; + + self.stepper.step(autorouter) + } + } + } +} + +impl Abort> for AutorouteExecutionPermutator { + fn abort(&mut self, autorouter: &mut Autorouter) { + self.permutations_iter.all(|_| true); + self.stepper.abort(autorouter); + } +} + +impl EstimateProgress for AutorouteExecutionPermutator { + type Value = f64; + + fn estimate_progress_value(&self) -> f64 { + // TODO. + self.stepper.estimate_progress_value() + } + + fn estimate_progress_maximum(&self) -> f64 { + // TODO. + self.stepper.estimate_progress_maximum() + } +} + +impl GetDebugOverlayData for AutorouteExecutionPermutator { + fn maybe_thetastar(&self) -> Option<&ThetastarStepper> { + self.stepper.maybe_thetastar() + } + + fn maybe_navcord(&self) -> Option<&Navcord> { + self.stepper.maybe_navcord() + } + + fn ghosts(&self) -> &[PrimitiveShape] { + self.stepper.ghosts() + } + + fn obstacles(&self) -> &[PrimitiveIndex] { + self.stepper.obstacles() + } +} diff --git a/src/autorouter/autorouter.rs b/src/autorouter/autorouter.rs index b30005c..ebfa63a 100644 --- a/src/autorouter/autorouter.rs +++ b/src/autorouter/autorouter.rs @@ -7,20 +7,22 @@ use geo::Point; use petgraph::graph::NodeIndex; use serde::{Deserialize, Serialize}; use spade::InsertionError; -use std::collections::BTreeSet; +use std::{cmp::Ordering, collections::BTreeSet}; use thiserror::Error; use crate::{ board::{AccessMesadata, Board}, drawing::{band::BandTermsegIndex, Infringement}, + geometry::shape::MeasureLength, graph::MakeRef, layout::{via::ViaWeight, LayoutEdit}, router::{navmesh::NavmeshError, ng, thetastar::ThetastarError, RouterOptions}, + stepper::Step, triangulation::GetTrianvertexNodeIndex, }; use super::{ - autoroute::AutorouteExecutionStepper, + autoroute::{AutorouteExecutionPermutator, AutorouteExecutionStepper}, compare_detours::CompareDetoursExecutionStepper, measure_length::MeasureLengthExecutionStepper, place_via::PlaceViaExecutionStepper, @@ -105,8 +107,38 @@ impl Autorouter { &mut self, selection: &PinSelection, options: AutorouterOptions, - ) -> Result { - self.autoroute_ratlines(self.selected_ratlines(selection), options) + ) -> Result { + let mut ratlines = self.selected_ratlines(selection); + + match options.presort_by { + PresortBy::RatlineIntersectionCountAndLength => ratlines.sort_unstable_by(|a, b| { + let a_intersector_count = a.ref_(self).find_intersecting_ratlines().count(); + let b_intersector_count = b.ref_(self).find_intersecting_ratlines().count(); + + let primary_ordering = a_intersector_count.cmp(&b_intersector_count); + + if primary_ordering != Ordering::Equal { + primary_ordering + } else { + let a_length = a.ref_(self).length(); + let b_length = b.ref_(self).length(); + let secondary_ordering = a_length.total_cmp(&b_length); + + secondary_ordering + } + }), + PresortBy::PairwiseDetours => ratlines.sort_unstable_by(|a, b| { + let mut compare_detours = self.compare_detours_ratlines(*a, *b, options).unwrap(); + + if let Ok((al, bl)) = compare_detours.finish(self) { + PartialOrd::partial_cmp(&al, &bl).unwrap() + } else { + Ordering::Equal + } + }), + } + + AutorouteExecutionPermutator::new(self, ratlines, options) } pub(super) fn autoroute_ratlines( diff --git a/src/autorouter/execution.rs b/src/autorouter/execution.rs index 77da306..212171a 100644 --- a/src/autorouter/execution.rs +++ b/src/autorouter/execution.rs @@ -15,7 +15,7 @@ use crate::{ }; use super::{ - autoroute::AutorouteExecutionStepper, + autoroute::AutorouteExecutionPermutator, compare_detours::CompareDetoursExecutionStepper, invoker::{GetDebugOverlayData, Invoker, InvokerError}, measure_length::MeasureLengthExecutionStepper, @@ -45,7 +45,7 @@ pub enum Command { #[enum_dispatch(GetDebugOverlayData)] pub enum ExecutionStepper { - Autoroute(AutorouteExecutionStepper), + Autoroute(AutorouteExecutionPermutator), TopoAutoroute(ng::AutorouteExecutionStepper), PlaceVia(PlaceViaExecutionStepper), RemoveBands(RemoveBandsExecutionStepper), diff --git a/src/autorouter/invoker.rs b/src/autorouter/invoker.rs index 0b5c66d..850cb01 100644 --- a/src/autorouter/invoker.rs +++ b/src/autorouter/invoker.rs @@ -4,7 +4,7 @@ //! Manages the execution of routing commands within the autorouting system. -use std::{cmp::Ordering, ops::ControlFlow}; +use std::ops::ControlFlow; use contracts_try::debug_requires; use derive_getters::{Dissolve, Getters}; @@ -15,8 +15,8 @@ use thiserror::Error; use crate::{ board::AccessMesadata, drawing::graph::PrimitiveIndex, - geometry::{primitive::PrimitiveShape, shape::MeasureLength}, - graph::{GenericIndex, MakeRef}, + geometry::primitive::PrimitiveShape, + graph::GenericIndex, layout::poly::PolyWeight, router::{ navcord::Navcord, @@ -28,14 +28,14 @@ use crate::{ }; use super::{ - autoroute::AutorouteExecutionStepper, + autoroute::AutorouteExecutionPermutator, compare_detours::CompareDetoursExecutionStepper, execution::{Command, ExecutionStepper}, history::{History, HistoryError}, measure_length::MeasureLengthExecutionStepper, place_via::PlaceViaExecutionStepper, remove_bands::RemoveBandsExecutionStepper, - Autorouter, AutorouterError, PresortBy, + Autorouter, AutorouterError, }; /// Trait for getting the information to display on the debug overlay, @@ -167,48 +167,7 @@ impl Invoker { fn dispatch_command(&mut self, command: &Command) -> Result, InvokerError> { Ok(match command { Command::Autoroute(selection, options) => { - let mut ratlines = self.autorouter.selected_ratlines(selection); - - match options.presort_by { - PresortBy::RatlineIntersectionCountAndLength => { - ratlines.sort_unstable_by(|a, b| { - let a_intersector_count = a - .ref_(self.autorouter()) - .find_intersecting_ratlines() - .count(); - let b_intersector_count = b - .ref_(self.autorouter()) - .find_intersecting_ratlines() - .count(); - - let primary_ordering = a_intersector_count.cmp(&b_intersector_count); - - if primary_ordering != Ordering::Equal { - primary_ordering - } else { - let a_length = a.ref_(self.autorouter()).length(); - let b_length = b.ref_(self.autorouter()).length(); - let secondary_ordering = a_length.total_cmp(&b_length); - - secondary_ordering - } - }) - } - PresortBy::PairwiseDetours => ratlines.sort_unstable_by(|a, b| { - let mut compare_detours = self - .autorouter - .compare_detours_ratlines(*a, *b, *options) - .unwrap(); - - if let Ok((al, bl)) = compare_detours.finish(&mut self.autorouter) { - PartialOrd::partial_cmp(&al, &bl).unwrap() - } else { - Ordering::Equal - } - }), - } - - ExecutionStepper::Autoroute(self.autorouter.autoroute_ratlines(ratlines, *options)?) + ExecutionStepper::Autoroute(self.autorouter.autoroute(selection, *options)?) } Command::TopoAutoroute { selection,