Compare commits

...

2 Commits

Author SHA1 Message Date
Outbreak2096 3208d55272
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (72 of 72 strings)

Translation: Topola/Topola
Translate-URL: https://translate.codeberg.org/projects/topola/topola/zh_Hans/
2025-11-20 02:00:03 +00:00
Mikolaj Wielgus 64669d5d0b feat(autorouter/multilayer_reconfigurer): Use A* to search for multilayer configurations 2025-11-20 02:54:52 +01:00
6 changed files with 106 additions and 84 deletions

View File

@ -51,17 +51,17 @@ tr-menu-route-options-squeeze-through-under-bends = 弯曲处挤线
tr-menu-view-kdb-scroll-delta-factor = 键盘滚动增量因子 tr-menu-view-kdb-scroll-delta-factor = 键盘滚动增量因子
tr-menu-debug-show-pathfinding-scores = 显示路径查找分数 tr-menu-debug-show-pathfinding-scores = 显示路径查找分数
tr-menu-place-place-route-plan = 放置布线规划 tr-menu-place-place-route-plan = 放置布线规划
tr-menu-route-topo-autoroute = 拓扑平面自动布线 tr-menu-route-topo-autoroute = 拓扑平面自动布线
tr-menu-debug-show-triangulation = 显示三角剖分 tr-menu-debug-show-triangulation = 显示三角剖分
tr-menu-debug-show-triangulation-constraints = 显示三角剖分约束条件 tr-menu-debug-show-triangulation-constraints = 显示三角剖分约束条件
tr-menu-route-options-presort-by = 预排序依据 tr-menu-route-options-presort-by = 预排序依据
tr-menu-route-options-presort-by-ratline-intersection-count-and-length = 交叉点数量及长度 tr-menu-route-options-presort-by-ratline-intersection-count-and-length = 交叉点数量及长度
tr-menu-route-options-permutate = 排列组合 tr-menu-route-options-permutate = 排列组合
tr-menu-debug-show-guide-circles = 显示辅助圆 tr-menu-debug-show-guide-circles = 显示辅助圆
tr-menu-debug-show-guide-bitangents = 显示双切线 tr-menu-debug-show-guide-bitangents = 显示双切线
tr-menu-debug-show-primitive-indices = 显示图元索引 tr-menu-debug-show-primitive-indices = 显示图元索引
tr-menu-route-planar-autoroute = 平面自动布线 tr-menu-route-planar-autoroute = 平面自动布线
tr-menu-route-fanout-clearance = 扇出间距 tr-menu-route-fanout-clearance = 扇出间距
tr-menu-debug = 调试 tr-menu-debug = 调试
tr-menu-debug-fix-step-rate = 固定步进速率 tr-menu-debug-fix-step-rate = 固定步进速率
tr-menu-debug-highlight-obstacles = 高亮障碍物 tr-menu-debug-highlight-obstacles = 高亮障碍物

View File

@ -35,7 +35,7 @@ pub struct AnterouterOptions {
pub fanout_clearance: f64, pub fanout_clearance: f64,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct AnterouterPlan { pub struct AnterouterPlan {
pub layer_map: BTreeMap<RatlineUid, usize>, pub layer_map: BTreeMap<RatlineUid, usize>,
pub static_terminating_dot_map: BTreeMap<(RatlineUid, FixedDotIndex, usize), FixedDotIndex>, pub static_terminating_dot_map: BTreeMap<(RatlineUid, FixedDotIndex, usize), FixedDotIndex>,

View File

@ -28,7 +28,7 @@ use crate::{
}, },
}; };
#[derive(Clone, Debug)] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct MultilayerAutorouteConfiguration { pub struct MultilayerAutorouteConfiguration {
pub plan: AnterouterPlan, pub plan: AnterouterPlan,
pub planar: PlanarAutoroutePreconfigurerInput, pub planar: PlanarAutoroutePreconfigurerInput,

View File

@ -2,16 +2,21 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
use std::collections::BTreeMap; use std::{cmp::Ordering, collections::BTreeMap};
use derive_getters::Getters;
use enum_dispatch::enum_dispatch; use enum_dispatch::enum_dispatch;
use specctra_core::mesadata::AccessMesadata; use specctra_core::mesadata::AccessMesadata;
use crate::autorouter::{ use crate::{
astar::Astar,
autorouter::{
multilayer_autoroute::{MultilayerAutorouteConfiguration, MultilayerAutorouteOptions}, multilayer_autoroute::{MultilayerAutorouteConfiguration, MultilayerAutorouteOptions},
planar_autoroute::PlanarAutorouteConfigurationStatus, planar_autoroute::PlanarAutorouteConfigurationStatus,
planar_preconfigurer::PlanarAutoroutePreconfigurerInput, planar_preconfigurer::PlanarAutoroutePreconfigurerInput,
ratline::RatlineUid,
Autorouter, AutorouterError, Autorouter, AutorouterError,
},
}; };
#[enum_dispatch] #[enum_dispatch]
@ -33,10 +38,56 @@ pub enum MultilayerAutorouteReconfigurer {
UniformRandomLayers(IncrementFailedRatlineLayersMultilayerAutorouteReconfigurer), UniformRandomLayers(IncrementFailedRatlineLayersMultilayerAutorouteReconfigurer),
} }
#[derive(Clone, Debug, Getters)]
struct SearchNode {
configuration: MultilayerAutorouteConfiguration,
}
impl Ord for SearchNode {
fn cmp(&self, other: &Self) -> Ordering {
self.configuration.cmp(&other.configuration)
}
}
impl PartialOrd for SearchNode {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Eq for SearchNode {}
impl PartialEq for SearchNode {
fn eq(&self, other: &Self) -> bool {
self.configuration == other.configuration
}
}
impl SearchNode {
pub fn new(configuration: MultilayerAutorouteConfiguration) -> Self {
Self { configuration }
}
pub fn revise_ratline(self, ratline_uid: RatlineUid, layer_count: usize) -> Self {
let mut new_anterouter_plan = self.configuration.plan.clone();
*new_anterouter_plan.layer_map.get_mut(&ratline_uid).unwrap() += 1;
*new_anterouter_plan.layer_map.get_mut(&ratline_uid).unwrap() %= layer_count;
Self {
configuration: MultilayerAutorouteConfiguration {
plan: new_anterouter_plan,
planar: PlanarAutoroutePreconfigurerInput {
ratlines: self.configuration.planar.ratlines.clone(),
terminating_dot_map: BTreeMap::new(),
},
},
}
}
}
pub struct IncrementFailedRatlineLayersMultilayerAutorouteReconfigurer { pub struct IncrementFailedRatlineLayersMultilayerAutorouteReconfigurer {
last_configuration: MultilayerAutorouteConfiguration, configuration_search: Astar<SearchNode, f64>,
maybe_last_planar_status: Option<PlanarAutorouteConfigurationStatus>,
maybe_best_planar_status: Option<PlanarAutorouteConfigurationStatus>,
} }
impl IncrementFailedRatlineLayersMultilayerAutorouteReconfigurer { impl IncrementFailedRatlineLayersMultilayerAutorouteReconfigurer {
@ -46,9 +97,7 @@ impl IncrementFailedRatlineLayersMultilayerAutorouteReconfigurer {
_options: &MultilayerAutorouteOptions, _options: &MultilayerAutorouteOptions,
) -> Self { ) -> Self {
Self { Self {
last_configuration: preconfiguration, configuration_search: Astar::new(SearchNode::new(preconfiguration)),
maybe_last_planar_status: None,
maybe_best_planar_status: None,
} }
} }
} }
@ -58,57 +107,32 @@ impl MakeNextMultilayerAutorouteConfiguration
{ {
fn process_planar_result( fn process_planar_result(
&mut self, &mut self,
_autorouter: &Autorouter<impl AccessMesadata>, autorouter: &Autorouter<impl AccessMesadata>,
planar_result: Result<PlanarAutorouteConfigurationStatus, AutorouterError>, planar_result: Result<PlanarAutorouteConfigurationStatus, AutorouterError>,
) { ) {
let Ok(planar_status) = planar_result else { let Ok(planar_status) = planar_result else {
return; return;
}; };
self.maybe_last_planar_status = Some(planar_status.clone()); self.configuration_search.push((
0.1,
if self (planar_status.configuration.ratlines.len() - planar_status.costs.lengths.len()) as f64,
.maybe_best_planar_status self.configuration_search
.as_ref() .curr_node()
.is_none_or(|status| status.costs.lengths.len() < planar_status.costs.lengths.len()) .clone()
{ .revise_ratline(
self.maybe_best_planar_status = Some(planar_status.clone()); planar_status.configuration.ratlines[planar_status.costs.lengths.len()],
} autorouter.board().layout().drawing().layer_count(),
),
));
} }
fn next_configuration( fn next_configuration(
&mut self, &mut self,
autorouter: &Autorouter<impl AccessMesadata>, _autorouter: &Autorouter<impl AccessMesadata>,
) -> Option<MultilayerAutorouteConfiguration> { ) -> Option<MultilayerAutorouteConfiguration> {
let mut new_anterouter_plan = self.last_configuration.plan.clone(); self.configuration_search
.pop()
let Some(ref last_planar_status) = self.maybe_last_planar_status else { .map(|node| node.configuration().clone())
return None;
};
if let Some(ref best_planar_status) = self.maybe_best_planar_status {
for ratline_index in best_planar_status.costs.lengths.len()
..last_planar_status.configuration.ratlines.len()
{
*new_anterouter_plan
.layer_map
.get_mut(&last_planar_status.configuration.ratlines[ratline_index])
.unwrap() += 1;
*new_anterouter_plan
.layer_map
.get_mut(&last_planar_status.configuration.ratlines[ratline_index])
.unwrap() %= autorouter.board().layout().drawing().layer_count();
}
}
self.last_configuration = MultilayerAutorouteConfiguration {
plan: new_anterouter_plan,
planar: PlanarAutoroutePreconfigurerInput {
ratlines: self.last_configuration.planar.ratlines.clone(),
terminating_dot_map: BTreeMap::new(),
},
};
Some(self.last_configuration.clone())
} }
} }

View File

@ -17,7 +17,7 @@ use crate::{
drawing::dot::FixedDotIndex, drawing::dot::FixedDotIndex,
}; };
#[derive(Clone, Debug)] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct PlanarAutoroutePreconfigurerInput { pub struct PlanarAutoroutePreconfigurerInput {
pub ratlines: BTreeSet<RatlineUid>, pub ratlines: BTreeSet<RatlineUid>,
pub terminating_dot_map: BTreeMap<(RatlineUid, FixedDotIndex), FixedDotIndex>, pub terminating_dot_map: BTreeMap<(RatlineUid, FixedDotIndex), FixedDotIndex>,

View File

@ -60,17 +60,17 @@ impl PlanarAutorouteReconfigurer {
#[derive(Clone, Debug, Getters)] #[derive(Clone, Debug, Getters)]
struct SccSearchNode { struct SccSearchNode {
curr_permutation: Vec<Scc>, permutation: Vec<Scc>,
#[getter(skip)] #[getter(skip)]
permutations: Skip<Permutations<Take<std::vec::IntoIter<Scc>>>>, permutations_iter: Skip<Permutations<Take<std::vec::IntoIter<Scc>>>>,
#[getter(skip)] #[getter(skip)]
length: usize, length: usize,
} }
impl Ord for SccSearchNode { impl Ord for SccSearchNode {
fn cmp(&self, other: &Self) -> Ordering { fn cmp(&self, other: &Self) -> Ordering {
self.curr_permutation self.permutation
.cmp(&other.curr_permutation) .cmp(&other.permutation)
.then(self.length.cmp(&other.length)) .then(self.length.cmp(&other.length))
} }
} }
@ -85,7 +85,7 @@ impl Eq for SccSearchNode {}
impl PartialEq for SccSearchNode { impl PartialEq for SccSearchNode {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.curr_permutation == other.curr_permutation && self.length == other.length self.permutation == other.permutation && self.length == other.length
} }
} }
@ -94,8 +94,8 @@ impl SccSearchNode {
let len = sccs.len(); let len = sccs.len();
Self { Self {
curr_permutation: sccs.clone(), permutation: sccs.clone(),
permutations: sccs.into_iter().take(len).permutations(0).skip(0), permutations_iter: sccs.into_iter().take(len).permutations(0).skip(0),
length: 0, length: 0,
} }
} }
@ -107,7 +107,7 @@ impl SccSearchNode {
if let Some((permuted_resized, changed_count)) = resized.permute() { if let Some((permuted_resized, changed_count)) = resized.permute() {
expanded_nodes.push(( expanded_nodes.push((
changed_count as f64 / 100.0, changed_count as f64 / 100.0,
(self.curr_permutation.len() - permuted_resized.length) as f64, (self.permutation.len() - permuted_resized.length) as f64,
permuted_resized, permuted_resized,
)); ));
} }
@ -116,7 +116,7 @@ impl SccSearchNode {
if let Some((permuted, changed_count)) = self.clone().permute() { if let Some((permuted, changed_count)) = self.clone().permute() {
expanded_nodes.push(( expanded_nodes.push((
changed_count as f64 / 100.0, changed_count as f64 / 100.0,
(self.curr_permutation.len() - permuted.length) as f64, (self.permutation.len() - permuted.length) as f64,
permuted, permuted,
)); ));
} }
@ -130,9 +130,9 @@ impl SccSearchNode {
} }
Some(Self { Some(Self {
curr_permutation: self.curr_permutation.clone(), permutation: self.permutation.clone(),
permutations: self permutations_iter: self
.curr_permutation .permutation
.into_iter() .into_iter()
.take(length) .take(length)
.permutations(length) .permutations(length)
@ -144,11 +144,9 @@ impl SccSearchNode {
fn permute(mut self) -> Option<(Self, usize)> { fn permute(mut self) -> Option<(Self, usize)> {
let mut changed_count = 0; let mut changed_count = 0;
for (i, element) in self.permutations.next()?.iter().enumerate() { for (i, element) in self.permutations_iter.next()?.iter().enumerate() {
if self.curr_permutation[i] != *element { if self.permutation[i] != *element {
self.curr_permutation[i] = element.clone(); self.permutation[i] = element.clone();
// FIXME: Uncommenting this breaks the 4x4_1206_led_matrix test.
changed_count += 1; changed_count += 1;
} }
} }
@ -158,7 +156,7 @@ impl SccSearchNode {
} }
pub struct SccPermutationsPlanarAutorouteReconfigurer { pub struct SccPermutationsPlanarAutorouteReconfigurer {
sccs_search: Astar<SccSearchNode, f64>, configuration_search: Astar<SccSearchNode, f64>,
preconfiguration: PlanarAutorouteConfiguration, preconfiguration: PlanarAutorouteConfiguration,
} }
@ -174,7 +172,7 @@ impl SccPermutationsPlanarAutorouteReconfigurer {
let sccs = presorter.dissolve(); let sccs = presorter.dissolve();
Self { Self {
sccs_search: Astar::new(SccSearchNode::new(sccs)), configuration_search: Astar::new(SccSearchNode::new(sccs)),
preconfiguration, preconfiguration,
} }
} }
@ -187,9 +185,9 @@ impl MakeNextPlanarAutorouteConfiguration for SccPermutationsPlanarAutorouteReco
stepper: &PlanarAutorouteExecutionStepper, stepper: &PlanarAutorouteExecutionStepper,
) -> Option<PlanarAutorouteConfiguration> { ) -> Option<PlanarAutorouteConfiguration> {
let scc_index = self let scc_index = self
.sccs_search .configuration_search
.curr_node() .curr_node()
.curr_permutation() .permutation()
.iter() .iter()
.position(|scc| { .position(|scc| {
scc.scc_ref(autorouter) scc.scc_ref(autorouter)
@ -198,9 +196,9 @@ impl MakeNextPlanarAutorouteConfiguration for SccPermutationsPlanarAutorouteReco
.unwrap(); .unwrap();
let next_search_node = self let next_search_node = self
.sccs_search .configuration_search
.expand(&self.sccs_search.curr_node().expand(scc_index + 1))?; .expand(&self.configuration_search.curr_node().expand(scc_index + 1))?;
let next_permutation = next_search_node.curr_permutation(); let next_permutation = next_search_node.permutation();
let mut ratlines = vec![]; let mut ratlines = vec![];
for scc in next_permutation { for scc in next_permutation {