// SPDX-FileCopyrightText: 2025 Topola contributors // // SPDX-License-Identifier: MIT use std::{ cmp::Ordering, iter::{Skip, Take}, }; use derive_getters::Getters; use enum_dispatch::enum_dispatch; use itertools::{Itertools, Permutations}; use specctra_core::mesadata::AccessMesadata; use crate::{ astar::Astar, autorouter::{ planar_autoroute::{PlanarAutorouteConfiguration, PlanarAutorouteExecutionStepper}, planar_preconfigurer::SccIntersectionsAndLengthRatlinePlanarAutoroutePreconfigurer, scc::Scc, Autorouter, PlanarAutorouteOptions, }, }; #[enum_dispatch] pub trait MakeNextPlanarAutorouteConfiguration { fn next_configuration( &mut self, autorouter: &Autorouter, stepper: &PlanarAutorouteExecutionStepper, ) -> Option; } #[enum_dispatch(MakeNextPlanarAutorouteConfiguration)] pub enum PlanarAutorouteReconfigurer { //RatlineCuts(RatlineCutsPlanarAutorouteReconfigurer), SccPermutations(SccPermutationsPlanarAutorouteReconfigurer), } impl PlanarAutorouteReconfigurer { pub fn new( autorouter: &mut Autorouter, preconfiguration: PlanarAutorouteConfiguration, presorter: SccIntersectionsAndLengthRatlinePlanarAutoroutePreconfigurer, options: &PlanarAutorouteOptions, ) -> Self { PlanarAutorouteReconfigurer::SccPermutations( SccPermutationsPlanarAutorouteReconfigurer::new( autorouter, preconfiguration, presorter, options, ), ) /*RatlinesPermuter::RatlineCuts(RatlineCutsRatlinePermuter::new( autorouter, ratlines, presorter, options, ))*/ } } #[derive(Clone, Debug, Getters)] struct SccSearchNode { permutation: Vec, #[getter(skip)] permutations_iter: Skip>>>, #[getter(skip)] length: usize, } impl Ord for SccSearchNode { fn cmp(&self, other: &Self) -> Ordering { self.permutation .cmp(&other.permutation) .then(self.length.cmp(&other.length)) } } impl PartialOrd for SccSearchNode { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Eq for SccSearchNode {} impl PartialEq for SccSearchNode { fn eq(&self, other: &Self) -> bool { self.permutation == other.permutation && self.length == other.length } } impl SccSearchNode { pub fn new(sccs: Vec) -> Self { let len = sccs.len(); Self { permutation: sccs.clone(), permutations_iter: sccs.into_iter().take(len).permutations(0).skip(0), length: 0, } } pub fn expand(&self, length: usize) -> Vec<(f64, f64, Self)> { let mut expanded_nodes = vec![]; if let Some(resized) = self.clone().resize(length) { if let Some((permuted_resized, changed_count)) = resized.permute() { expanded_nodes.push(( changed_count as f64 / 100.0, (self.permutation.len() - permuted_resized.length) as f64, permuted_resized, )); } } if let Some((permuted, changed_count)) = self.clone().permute() { expanded_nodes.push(( changed_count as f64 / 100.0, (self.permutation.len() - permuted.length) as f64, permuted, )); } expanded_nodes } fn resize(self, length: usize) -> Option { if length == self.length { return None; } Some(Self { permutation: self.permutation.clone(), permutations_iter: self .permutation .into_iter() .take(length) .permutations(length) .skip(1), length, }) } fn permute(mut self) -> Option<(Self, usize)> { let mut changed_count = 0; for (i, element) in self.permutations_iter.next()?.iter().enumerate() { if self.permutation[i] != *element { self.permutation[i] = element.clone(); changed_count += 1; } } Some((self, changed_count)) } } pub struct SccPermutationsPlanarAutorouteReconfigurer { configuration_search: Astar, preconfiguration: PlanarAutorouteConfiguration, } impl SccPermutationsPlanarAutorouteReconfigurer { pub fn new( _autorouter: &mut Autorouter, preconfiguration: PlanarAutorouteConfiguration, presorter: SccIntersectionsAndLengthRatlinePlanarAutoroutePreconfigurer, _options: &PlanarAutorouteOptions, ) -> Self { // TODO: Instead of instantiating presorter again here, get it from // an argument. let sccs = presorter.dissolve(); Self { configuration_search: Astar::new(SccSearchNode::new(sccs)), preconfiguration, } } } impl MakeNextPlanarAutorouteConfiguration for SccPermutationsPlanarAutorouteReconfigurer { fn next_configuration( &mut self, autorouter: &Autorouter, stepper: &PlanarAutorouteExecutionStepper, ) -> Option { let scc_index = self .configuration_search .curr_node() .permutation() .iter() .position(|scc| { scc.scc_ref(autorouter) .contains(self.preconfiguration.ratlines[*stepper.curr_ratline_index()]) }) .unwrap(); let next_search_node = self .configuration_search .expand(&self.configuration_search.curr_node().expand(scc_index + 1))?; let next_permutation = next_search_node.permutation(); let mut ratlines = vec![]; for scc in next_permutation { for ratline in self.preconfiguration.ratlines.iter() { if scc.node_indices().contains( &autorouter .ratsnests() .on_principal_layer(ratline.principal_layer) .graph() .edge_endpoints(ratline.index) .unwrap() .0, ) && scc.node_indices().contains( &autorouter .ratsnests() .on_principal_layer(ratline.principal_layer) .graph() .edge_endpoints(ratline.index) .unwrap() .1, ) { ratlines.push(*ratline); } } } Some(PlanarAutorouteConfiguration { ratlines, ..self.preconfiguration.clone() }) } } pub struct RatlineCutsPlanarAutorouteReconfigurer { //sccs: Vec>>, } /*impl RatlineCutsPlanarAutorouteReconfigurer { pub fn new( _autorouter: &mut Autorouter, _ratlines: Vec, _presorter: SccIntersectionsAndLengthRatlinePlanarAutoroutePreconfigurer, _options: &PlanarAutorouteOptions, ) -> Self { /*Self { sccs: presorter.dissolve(), }*/ Self {} } }*/ /*impl MakeNextPlanarAutorouteConfiguration for RatlineCutsPlanarAutorouteReconfigurer { fn next_configuration( &mut self, autorouter: &mut Autorouter, stepper: &PlanarAutorouteExecutionStepper, ) -> Option> { let curr_ratline = stepper.configuration().ratlines[*stepper.curr_ratline_index()]; let terminating_dots = stepper .configuration() .terminating_dots .get(&(curr_ratline,)); let bands_cut_by_ratline: Vec<_> = autorouter .board() .layout() .bands_between_nodes( terminating_dots .0 .primitive_ref(autorouter.board().layout().drawing()) .layer(), GenericNode::Primitive(terminating_dots.0.into()), GenericNode::Primitive(terminating_dots.1.into()), ) .collect(); // Find the first ratline corresponding to a band that is cut. let first_cut_ratline_index = stepper .configuration() .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.configuration().ratlines.clone(); ratlines.swap(*stepper.curr_ratline_index(), first_cut_ratline_index); Some(ratlines) } }*/