mirror of https://codeberg.org/topola/topola.git
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.
This commit is contained in:
parent
f09aa053b6
commit
d181c7df1b
|
|
@ -53,6 +53,7 @@ contracts-try = "0.7"
|
||||||
derive-getters.workspace = true
|
derive-getters.workspace = true
|
||||||
enum_dispatch = "0.3"
|
enum_dispatch = "0.3"
|
||||||
geo.workspace = true
|
geo.workspace = true
|
||||||
|
itertools = "0.14"
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
petgraph.workspace = true
|
petgraph.workspace = true
|
||||||
ron = "0.10"
|
ron = "0.10"
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
//! Manages autorouting of ratlines in a layout, tracking status and processed
|
//! Manages autorouting of ratlines in a layout, tracking status and processed
|
||||||
//! routing steps.
|
//! routing steps.
|
||||||
|
|
||||||
|
use itertools::{Itertools, Permutations};
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
@ -33,7 +34,7 @@ pub enum AutorouteContinueStatus {
|
||||||
Running,
|
Running,
|
||||||
/// A specific segment has been successfully routed.
|
/// A specific segment has been successfully routed.
|
||||||
Routed(BandTermsegIndex),
|
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),
|
Skipped(BandTermsegIndex),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,6 +104,8 @@ impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, AutorouteContinue
|
||||||
&mut self,
|
&mut self,
|
||||||
autorouter: &mut Autorouter<M>,
|
autorouter: &mut Autorouter<M>,
|
||||||
) -> Result<ControlFlow<Option<BoardEdit>, AutorouteContinueStatus>, AutorouterError> {
|
) -> Result<ControlFlow<Option<BoardEdit>, AutorouteContinueStatus>, AutorouterError> {
|
||||||
|
// TODO: Use a proper state machine here for better readability?
|
||||||
|
|
||||||
if self.curr_ratline_index >= self.ratlines.len() {
|
if self.curr_ratline_index >= self.ratlines.len() {
|
||||||
let recorder = self.dissolve_route_stepper_into_layout_edit();
|
let recorder = self.dissolve_route_stepper_into_layout_edit();
|
||||||
return Ok(ControlFlow::Break(Some(BoardEdit::new_from_edits(
|
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())
|
self.route.as_ref().map_or(&[], |route| route.obstacles())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct AutorouteExecutionPermutator {
|
||||||
|
stepper: AutorouteExecutionStepper,
|
||||||
|
permutations_iter: Permutations<std::vec::IntoIter<RatlineIndex>>,
|
||||||
|
options: AutorouterOptions,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AutorouteExecutionPermutator {
|
||||||
|
pub fn new(
|
||||||
|
autorouter: &mut Autorouter<impl AccessMesadata>,
|
||||||
|
ratlines: Vec<RatlineIndex>,
|
||||||
|
options: AutorouterOptions,
|
||||||
|
) -> Result<Self, AutorouterError> {
|
||||||
|
let ratlines_len = ratlines.len();
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
stepper: AutorouteExecutionStepper::new(autorouter, ratlines.clone(), options)?,
|
||||||
|
permutations_iter: ratlines.into_iter().permutations(ratlines_len),
|
||||||
|
options,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, AutorouteContinueStatus>
|
||||||
|
for AutorouteExecutionPermutator
|
||||||
|
{
|
||||||
|
type Error = AutorouterError;
|
||||||
|
|
||||||
|
fn step(
|
||||||
|
&mut self,
|
||||||
|
autorouter: &mut Autorouter<M>,
|
||||||
|
) -> Result<ControlFlow<Option<BoardEdit>, 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<M: AccessMesadata> Abort<Autorouter<M>> for AutorouteExecutionPermutator {
|
||||||
|
fn abort(&mut self, autorouter: &mut Autorouter<M>) {
|
||||||
|
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<Navmesh, f64>> {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,20 +7,22 @@ use geo::Point;
|
||||||
use petgraph::graph::NodeIndex;
|
use petgraph::graph::NodeIndex;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use spade::InsertionError;
|
use spade::InsertionError;
|
||||||
use std::collections::BTreeSet;
|
use std::{cmp::Ordering, collections::BTreeSet};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
board::{AccessMesadata, Board},
|
board::{AccessMesadata, Board},
|
||||||
drawing::{band::BandTermsegIndex, Infringement},
|
drawing::{band::BandTermsegIndex, Infringement},
|
||||||
|
geometry::shape::MeasureLength,
|
||||||
graph::MakeRef,
|
graph::MakeRef,
|
||||||
layout::{via::ViaWeight, LayoutEdit},
|
layout::{via::ViaWeight, LayoutEdit},
|
||||||
router::{navmesh::NavmeshError, ng, thetastar::ThetastarError, RouterOptions},
|
router::{navmesh::NavmeshError, ng, thetastar::ThetastarError, RouterOptions},
|
||||||
|
stepper::Step,
|
||||||
triangulation::GetTrianvertexNodeIndex,
|
triangulation::GetTrianvertexNodeIndex,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
autoroute::AutorouteExecutionStepper,
|
autoroute::{AutorouteExecutionPermutator, AutorouteExecutionStepper},
|
||||||
compare_detours::CompareDetoursExecutionStepper,
|
compare_detours::CompareDetoursExecutionStepper,
|
||||||
measure_length::MeasureLengthExecutionStepper,
|
measure_length::MeasureLengthExecutionStepper,
|
||||||
place_via::PlaceViaExecutionStepper,
|
place_via::PlaceViaExecutionStepper,
|
||||||
|
|
@ -105,8 +107,38 @@ impl<M: AccessMesadata> Autorouter<M> {
|
||||||
&mut self,
|
&mut self,
|
||||||
selection: &PinSelection,
|
selection: &PinSelection,
|
||||||
options: AutorouterOptions,
|
options: AutorouterOptions,
|
||||||
) -> Result<AutorouteExecutionStepper, AutorouterError> {
|
) -> Result<AutorouteExecutionPermutator, AutorouterError> {
|
||||||
self.autoroute_ratlines(self.selected_ratlines(selection), options)
|
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(
|
pub(super) fn autoroute_ratlines(
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
autoroute::AutorouteExecutionStepper,
|
autoroute::AutorouteExecutionPermutator,
|
||||||
compare_detours::CompareDetoursExecutionStepper,
|
compare_detours::CompareDetoursExecutionStepper,
|
||||||
invoker::{GetDebugOverlayData, Invoker, InvokerError},
|
invoker::{GetDebugOverlayData, Invoker, InvokerError},
|
||||||
measure_length::MeasureLengthExecutionStepper,
|
measure_length::MeasureLengthExecutionStepper,
|
||||||
|
|
@ -45,7 +45,7 @@ pub enum Command {
|
||||||
|
|
||||||
#[enum_dispatch(GetDebugOverlayData)]
|
#[enum_dispatch(GetDebugOverlayData)]
|
||||||
pub enum ExecutionStepper<M> {
|
pub enum ExecutionStepper<M> {
|
||||||
Autoroute(AutorouteExecutionStepper),
|
Autoroute(AutorouteExecutionPermutator),
|
||||||
TopoAutoroute(ng::AutorouteExecutionStepper<M>),
|
TopoAutoroute(ng::AutorouteExecutionStepper<M>),
|
||||||
PlaceVia(PlaceViaExecutionStepper),
|
PlaceVia(PlaceViaExecutionStepper),
|
||||||
RemoveBands(RemoveBandsExecutionStepper),
|
RemoveBands(RemoveBandsExecutionStepper),
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
//! Manages the execution of routing commands within the autorouting system.
|
//! 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 contracts_try::debug_requires;
|
||||||
use derive_getters::{Dissolve, Getters};
|
use derive_getters::{Dissolve, Getters};
|
||||||
|
|
@ -15,8 +15,8 @@ use thiserror::Error;
|
||||||
use crate::{
|
use crate::{
|
||||||
board::AccessMesadata,
|
board::AccessMesadata,
|
||||||
drawing::graph::PrimitiveIndex,
|
drawing::graph::PrimitiveIndex,
|
||||||
geometry::{primitive::PrimitiveShape, shape::MeasureLength},
|
geometry::primitive::PrimitiveShape,
|
||||||
graph::{GenericIndex, MakeRef},
|
graph::GenericIndex,
|
||||||
layout::poly::PolyWeight,
|
layout::poly::PolyWeight,
|
||||||
router::{
|
router::{
|
||||||
navcord::Navcord,
|
navcord::Navcord,
|
||||||
|
|
@ -28,14 +28,14 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
autoroute::AutorouteExecutionStepper,
|
autoroute::AutorouteExecutionPermutator,
|
||||||
compare_detours::CompareDetoursExecutionStepper,
|
compare_detours::CompareDetoursExecutionStepper,
|
||||||
execution::{Command, ExecutionStepper},
|
execution::{Command, ExecutionStepper},
|
||||||
history::{History, HistoryError},
|
history::{History, HistoryError},
|
||||||
measure_length::MeasureLengthExecutionStepper,
|
measure_length::MeasureLengthExecutionStepper,
|
||||||
place_via::PlaceViaExecutionStepper,
|
place_via::PlaceViaExecutionStepper,
|
||||||
remove_bands::RemoveBandsExecutionStepper,
|
remove_bands::RemoveBandsExecutionStepper,
|
||||||
Autorouter, AutorouterError, PresortBy,
|
Autorouter, AutorouterError,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Trait for getting the information to display on the debug overlay,
|
/// Trait for getting the information to display on the debug overlay,
|
||||||
|
|
@ -167,48 +167,7 @@ impl<M: AccessMesadata + Clone> Invoker<M> {
|
||||||
fn dispatch_command(&mut self, command: &Command) -> Result<ExecutionStepper<M>, InvokerError> {
|
fn dispatch_command(&mut self, command: &Command) -> Result<ExecutionStepper<M>, InvokerError> {
|
||||||
Ok(match command {
|
Ok(match command {
|
||||||
Command::Autoroute(selection, options) => {
|
Command::Autoroute(selection, options) => {
|
||||||
let mut ratlines = self.autorouter.selected_ratlines(selection);
|
ExecutionStepper::Autoroute(self.autorouter.autoroute(selection, *options)?)
|
||||||
|
|
||||||
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)?)
|
|
||||||
}
|
}
|
||||||
Command::TopoAutoroute {
|
Command::TopoAutoroute {
|
||||||
selection,
|
selection,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue