mirror of https://codeberg.org/topola/topola.git
feat(autorouter/permutator): Preorder and permutate over whole ratline conn. comps
This is not enough to autoroute 4x4_1206_led_matrix_breakout, but is as usual, is a step forward.
This commit is contained in:
parent
19c6ede09a
commit
4326925bbf
|
|
@ -91,7 +91,7 @@ impl AutorouteExecutionStepper {
|
||||||
autorouter: &mut Autorouter<impl AccessMesadata>,
|
autorouter: &mut Autorouter<impl AccessMesadata>,
|
||||||
index: usize,
|
index: usize,
|
||||||
) -> Result<(), AutorouterError> {
|
) -> Result<(), AutorouterError> {
|
||||||
if index > self.board_data_edits.len() {
|
if index >= self.board_data_edits.len() {
|
||||||
return Err(AutorouterError::NothingToUndoForPermutation);
|
return Err(AutorouterError::NothingToUndoForPermutation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -232,11 +232,13 @@ impl<M: AccessMesadata> Permutate<Autorouter<M>> for AutorouteExecutionStepper {
|
||||||
autorouter: &mut Autorouter<M>,
|
autorouter: &mut Autorouter<M>,
|
||||||
permutation: Vec<RatlineIndex>,
|
permutation: Vec<RatlineIndex>,
|
||||||
) -> Result<(), AutorouterError> {
|
) -> Result<(), AutorouterError> {
|
||||||
let new_index = permutation
|
let Some(new_index) = permutation
|
||||||
.iter()
|
.iter()
|
||||||
.zip(self.ratlines.iter())
|
.zip(self.ratlines.iter())
|
||||||
.position(|(permuted, original)| *permuted != *original)
|
.position(|(permuted, original)| *permuted != *original)
|
||||||
.unwrap();
|
else {
|
||||||
|
return Err(AutorouterError::NothingToUndoForPermutation);
|
||||||
|
};
|
||||||
self.ratlines = permutation;
|
self.ratlines = permutation;
|
||||||
|
|
||||||
self.backtrace_to_index(autorouter, new_index)?;
|
self.backtrace_to_index(autorouter, new_index)?;
|
||||||
|
|
|
||||||
|
|
@ -7,18 +7,16 @@ 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::{cmp::Ordering, collections::BTreeSet};
|
use std::collections::BTreeSet;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
autorouter::permutator::AutorouteExecutionPermutator,
|
autorouter::permutator::AutorouteExecutionPermutator,
|
||||||
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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
use std::{cmp::Ordering, iter::Skip, ops::ControlFlow};
|
use std::{cmp::Ordering, ops::ControlFlow};
|
||||||
|
|
||||||
use itertools::{Itertools, Permutations};
|
use itertools::{Itertools, Permutations};
|
||||||
|
use petgraph::{algo::tarjan_scc, graph::NodeIndex};
|
||||||
use specctra_core::mesadata::AccessMesadata;
|
use specctra_core::mesadata::AccessMesadata;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
@ -12,7 +13,7 @@ use crate::{
|
||||||
autoroute::{AutorouteContinueStatus, AutorouteExecutionStepper},
|
autoroute::{AutorouteContinueStatus, AutorouteExecutionStepper},
|
||||||
invoker::GetDebugOverlayData,
|
invoker::GetDebugOverlayData,
|
||||||
ratline::RatlineIndex,
|
ratline::RatlineIndex,
|
||||||
Autorouter, AutorouterError, AutorouterOptions, PresortBy,
|
Autorouter, AutorouterError, AutorouterOptions,
|
||||||
},
|
},
|
||||||
board::edit::BoardEdit,
|
board::edit::BoardEdit,
|
||||||
drawing::graph::PrimitiveIndex,
|
drawing::graph::PrimitiveIndex,
|
||||||
|
|
@ -22,54 +23,183 @@ use crate::{
|
||||||
stepper::{Abort, EstimateProgress, Permutate, Step},
|
stepper::{Abort, EstimateProgress, Permutate, Step},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct RatlineSccPermuter {
|
||||||
|
sccs_permutations_iter: Permutations<std::vec::IntoIter<Vec<NodeIndex<usize>>>>,
|
||||||
|
ratlines: Vec<RatlineIndex>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RatlineSccPermuter {
|
||||||
|
pub fn new(
|
||||||
|
autorouter: &mut Autorouter<impl AccessMesadata>,
|
||||||
|
ratlines: Vec<RatlineIndex>,
|
||||||
|
_options: &AutorouterOptions,
|
||||||
|
) -> 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_permutations_iter: sccs.into_iter().permutations(sccs_len),
|
||||||
|
ratlines,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_permutation(
|
||||||
|
&mut self,
|
||||||
|
autorouter: &mut Autorouter<impl AccessMesadata>,
|
||||||
|
) -> 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() {
|
||||||
|
if scc.contains(
|
||||||
|
&autorouter
|
||||||
|
.ratsnest()
|
||||||
|
.graph()
|
||||||
|
.edge_endpoints(*ratline)
|
||||||
|
.unwrap()
|
||||||
|
.0,
|
||||||
|
) && scc.contains(
|
||||||
|
&autorouter
|
||||||
|
.ratsnest()
|
||||||
|
.graph()
|
||||||
|
.edge_endpoints(*ratline)
|
||||||
|
.unwrap()
|
||||||
|
.1,
|
||||||
|
) {
|
||||||
|
ratlines.push(*ratline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(ratlines)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct AutorouteExecutionPermutator {
|
pub struct AutorouteExecutionPermutator {
|
||||||
stepper: AutorouteExecutionStepper,
|
stepper: AutorouteExecutionStepper,
|
||||||
permutations_iter: Skip<Permutations<std::vec::IntoIter<RatlineIndex>>>,
|
permuter: RatlineSccPermuter,
|
||||||
options: AutorouterOptions,
|
options: AutorouterOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AutorouteExecutionPermutator {
|
impl AutorouteExecutionPermutator {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
autorouter: &mut Autorouter<impl AccessMesadata>,
|
autorouter: &mut Autorouter<impl AccessMesadata>,
|
||||||
mut ratlines: Vec<RatlineIndex>,
|
ratlines: Vec<RatlineIndex>,
|
||||||
options: AutorouterOptions,
|
options: AutorouterOptions,
|
||||||
) -> Result<Self, AutorouterError> {
|
) -> Result<Self, AutorouterError> {
|
||||||
let ratlines_len = ratlines.len();
|
let mut permuter = RatlineSccPermuter::new(autorouter, ratlines, &options);
|
||||||
|
let initially_sorted_ratlines = permuter.next_permutation(autorouter).unwrap();
|
||||||
match options.presort_by {
|
|
||||||
PresortBy::RatlineIntersectionCountAndLength => ratlines.sort_unstable_by(|a, b| {
|
|
||||||
let a_intersector_count = a.ref_(autorouter).interior_obstacle_ratlines().count();
|
|
||||||
let b_intersector_count = b.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 a_length = a.ref_(autorouter).length();
|
|
||||||
let b_length = b.ref_(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 = autorouter
|
|
||||||
.compare_detours_ratlines(*a, *b, options)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if let Ok((al, bl)) = compare_detours.finish(autorouter) {
|
|
||||||
PartialOrd::partial_cmp(&al, &bl).unwrap()
|
|
||||||
} else {
|
|
||||||
Ordering::Equal
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
stepper: AutorouteExecutionStepper::new(autorouter, ratlines.clone(), options)?,
|
stepper: AutorouteExecutionStepper::new(
|
||||||
|
autorouter,
|
||||||
|
initially_sorted_ratlines,
|
||||||
|
options,
|
||||||
|
)?,
|
||||||
// Note: I assume here that the first permutation is the same as the original order.
|
// Note: I assume here that the first permutation is the same as the original order.
|
||||||
permutations_iter: ratlines.into_iter().permutations(ratlines_len).skip(1),
|
permuter,
|
||||||
options,
|
options,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -92,7 +222,7 @@ impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, AutorouteContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let Some(permutation) = self.permutations_iter.next() else {
|
let Some(permutation) = self.permuter.next_permutation(autorouter) else {
|
||||||
return Ok(ControlFlow::Break(None));
|
return Ok(ControlFlow::Break(None));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -111,7 +241,7 @@ impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, AutorouteContinue
|
||||||
|
|
||||||
impl<M: AccessMesadata> Abort<Autorouter<M>> for AutorouteExecutionPermutator {
|
impl<M: AccessMesadata> Abort<Autorouter<M>> for AutorouteExecutionPermutator {
|
||||||
fn abort(&mut self, autorouter: &mut Autorouter<M>) {
|
fn abort(&mut self, autorouter: &mut Autorouter<M>) {
|
||||||
self.permutations_iter.all(|_| true);
|
//self.permutations_iter.all(|_| true); // Why did I add this code here???
|
||||||
self.stepper.abort(autorouter);
|
self.stepper.abort(autorouter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,18 +2,14 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
//! Defines data structures and methods for managing a graph
|
|
||||||
//! used in layout triangulation and routing tasks. It includes vertex and edge
|
|
||||||
//! structures for representing graph nodes and edges with associated metadata,
|
|
||||||
//! as well as functions for constructing and manipulating these graphs.
|
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
use geo::Point;
|
use geo::Point;
|
||||||
use petgraph::{
|
use petgraph::{
|
||||||
data::Element,
|
data::Element,
|
||||||
graph::{NodeIndex, UnGraph},
|
graph::NodeIndex,
|
||||||
|
prelude::StableUnGraph,
|
||||||
unionfind::UnionFind,
|
unionfind::UnionFind,
|
||||||
visit::{EdgeRef, IntoEdgeReferences, NodeIndexable},
|
visit::{EdgeRef, IntoEdgeReferences, NodeIndexable},
|
||||||
};
|
};
|
||||||
|
|
@ -74,7 +70,7 @@ impl HasPosition for RatvertexWeight {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Ratsnest {
|
pub struct Ratsnest {
|
||||||
graph: UnGraph<RatvertexWeight, RatlineWeight, usize>,
|
graph: StableUnGraph<RatvertexWeight, RatlineWeight, usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ratsnest {
|
impl Ratsnest {
|
||||||
|
|
@ -86,7 +82,7 @@ impl Ratsnest {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
graph: UnGraph::default(),
|
graph: StableUnGraph::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut triangulations = BTreeMap::new();
|
let mut triangulations = BTreeMap::new();
|
||||||
|
|
@ -166,7 +162,7 @@ impl Ratsnest {
|
||||||
self.graph.edge_weight_mut(ratline).unwrap().band_termseg = Some(termseg);
|
self.graph.edge_weight_mut(ratline).unwrap().band_termseg = Some(termseg);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn graph(&self) -> &UnGraph<RatvertexWeight, RatlineWeight, usize> {
|
pub fn graph(&self) -> &StableUnGraph<RatvertexWeight, RatlineWeight, usize> {
|
||||||
&self.graph
|
&self.graph
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue