fix: get rid of some indeterminism by using B-trees instead of hash maps

I have replaced all instances of `HashMap<...>` with `BTreeMap<...>`,
of `HashSet<...>` with `BTreeSet<...>`, and all the uses of the `Hash`
trait with `Ord`.

I have done a few manual tests and found the behavior to be
deterministic between the GUI application launches. However, undoing an
autoroute command and then manually executing it once again continues to
produce variable results. I suppose this is because of some bug in the
code where edits are applied. Hence, the issue

https://codeberg.org/topola/topola/issues/46

is only partially resolved.
This commit is contained in:
Mikolaj Wielgus 2025-01-04 01:35:42 +01:00
parent 9c007a8ccb
commit 9664f1a31a
17 changed files with 110 additions and 111 deletions

View File

@ -5,8 +5,8 @@
//! Module for handling Specctra's mesadata - design rules, as well as layers //! Module for handling Specctra's mesadata - design rules, as well as layers
//! or net properties //! or net properties
use bimap::BiHashMap; use bimap::BiBTreeMap;
use std::collections::HashMap; use std::collections::BTreeMap;
use crate::{ use crate::{
rules::{AccessRules, Conditions}, rules::{AccessRules, Conditions},
@ -74,22 +74,22 @@ pub struct SpecctraMesadata {
// net class name -> rule // net class name -> rule
/// A map from net class names to their specific `SpecctraRule` constraints. /// A map from net class names to their specific `SpecctraRule` constraints.
/// These rules are applied to all nets belonging to the respective net clas /// These rules are applied to all nets belonging to the respective net clas
class_rules: HashMap<String, SpecctraRule>, class_rules: BTreeMap<String, SpecctraRule>,
// layername <-> layer for Layout // layername <-> layer for Layout
/// A bidirectional map between layer indices and layer names, allowing translation /// A bidirectional map between layer indices and layer names, allowing translation
/// between index-based layers in the layout and user-defined layer names. /// between index-based layers in the layout and user-defined layer names.
pub layer_layername: BiHashMap<usize, String>, pub layer_layername: BiBTreeMap<usize, String>,
// netname <-> net for Layout // netname <-> net for Layout
/// A bidirectional map between network indices and network names in the PCB layout, /// A bidirectional map between network indices and network names in the PCB layout,
/// providing an easy way to reference nets by name or index. /// providing an easy way to reference nets by name or index.
pub net_netname: BiHashMap<usize, String>, pub net_netname: BiBTreeMap<usize, String>,
// net -> netclass // net -> netclass
/// A map that associates network indices with their respective net class names. /// A map that associates network indices with their respective net class names.
/// This is used to apply net class-specific routing rules to each net. /// This is used to apply net class-specific routing rules to each net.
net_netclass: HashMap<usize, String>, net_netclass: BTreeMap<usize, String>,
} }
impl SpecctraMesadata { impl SpecctraMesadata {
@ -98,7 +98,7 @@ impl SpecctraMesadata {
/// This function extracts the necessary metadata from the `Pcb` struct, such as /// This function extracts the necessary metadata from the `Pcb` struct, such as
/// layer-to-layer name mappings, net-to-net name mappings, and net class rules. /// layer-to-layer name mappings, net-to-net name mappings, and net class rules.
pub fn from_pcb(pcb: &Pcb) -> Self { pub fn from_pcb(pcb: &Pcb) -> Self {
let layer_layername = BiHashMap::from_iter( let layer_layername = BiBTreeMap::from_iter(
pcb.structure pcb.structure
.layers .layers
.iter() .iter()
@ -119,11 +119,11 @@ impl SpecctraMesadata {
tmp.sort_unstable(); tmp.sort_unstable();
tmp.dedup(); tmp.dedup();
BiHashMap::from_iter(tmp.into_iter().cloned().enumerate()) BiBTreeMap::from_iter(tmp.into_iter().cloned().enumerate())
}; };
let mut net_netclass = HashMap::new(); let mut net_netclass = BTreeMap::new();
let class_rules = HashMap::from_iter( let class_rules = BTreeMap::from_iter(
pcb.network pcb.network
.classes .classes
.iter() .iter()

View File

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
use std::collections::HashMap; use std::collections::BTreeMap;
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(default)] #[serde(default)]
@ -28,7 +28,7 @@ pub struct Colors {
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct LayerColors { pub struct LayerColors {
default: LayerColor, default: LayerColor,
colors: HashMap<String, LayerColor>, colors: BTreeMap<String, LayerColor>,
} }
impl LayerColors { impl LayerColors {
@ -54,7 +54,7 @@ impl Default for Config {
normal: egui::Color32::from_rgb(255, 255, 255), normal: egui::Color32::from_rgb(255, 255, 255),
highlighted: egui::Color32::from_rgb(255, 255, 255), highlighted: egui::Color32::from_rgb(255, 255, 255),
}, },
colors: HashMap::from([ colors: BTreeMap::from([
( (
"F.Cu".to_string(), "F.Cu".to_string(),
LayerColor { LayerColor {
@ -106,7 +106,7 @@ impl Default for Config {
normal: egui::Color32::from_rgb(0, 0, 0), normal: egui::Color32::from_rgb(0, 0, 0),
highlighted: egui::Color32::from_rgb(0, 0, 0), highlighted: egui::Color32::from_rgb(0, 0, 0),
}, },
colors: HashMap::from([ colors: BTreeMap::from([
( (
"F.Cu".to_string(), "F.Cu".to_string(),
LayerColor { LayerColor {

View File

@ -7,7 +7,7 @@
//! structures for representing graph nodes and edges with associated metadata, //! structures for representing graph nodes and edges with associated metadata,
//! as well as functions for constructing and manipulating these graphs. //! as well as functions for constructing and manipulating these graphs.
use std::collections::HashMap; use std::collections::BTreeMap;
use enum_dispatch::enum_dispatch; use enum_dispatch::enum_dispatch;
use geo::Point; use geo::Point;
@ -27,7 +27,7 @@ use crate::{
primitive::MakePrimitiveShape, primitive::MakePrimitiveShape,
rules::AccessRules, rules::AccessRules,
}, },
geometry::{compound::ManageCompounds, shape::AccessShape}, geometry::shape::AccessShape,
graph::{GenericIndex, GetPetgraphIndex}, graph::{GenericIndex, GetPetgraphIndex},
layout::{ layout::{
poly::{MakePolyShape, PolyWeight}, poly::{MakePolyShape, PolyWeight},
@ -92,7 +92,7 @@ impl Ratsnest {
graph: UnGraph::default(), graph: UnGraph::default(),
}; };
let mut triangulations = HashMap::new(); let mut triangulations = BTreeMap::new();
let node_bound = layout.drawing().geometry().graph().node_bound(); let node_bound = layout.drawing().geometry().graph().node_bound();
for layer in 0..layout.drawing().layer_count() { for layer in 0..layout.drawing().layer_count() {

View File

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
use std::collections::HashSet; use std::collections::BTreeSet;
use rstar::AABB; use rstar::AABB;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -10,12 +10,12 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
board::{mesadata::AccessMesadata, BandName, Board}, board::{mesadata::AccessMesadata, BandName, Board},
drawing::graph::{GetLayer, MakePrimitive, PrimitiveIndex}, drawing::graph::{GetLayer, MakePrimitive, PrimitiveIndex},
geometry::{compound::ManageCompounds, GenericNode}, geometry::GenericNode,
graph::{GenericIndex, GetPetgraphIndex}, graph::{GenericIndex, GetPetgraphIndex},
layout::{poly::PolyWeight, CompoundWeight, NodeIndex}, layout::{poly::PolyWeight, CompoundWeight, NodeIndex},
}; };
#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
pub struct PinSelector { pub struct PinSelector {
pub pin: String, pub pin: String,
pub layer: String, pub layer: String,
@ -58,7 +58,7 @@ impl PinSelector {
} }
#[derive(Debug, Default, Clone, Serialize, Deserialize)] #[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct PinSelection(HashSet<PinSelector>); pub struct PinSelection(BTreeSet<PinSelector>);
impl PinSelection { impl PinSelection {
pub fn new() -> Self { pub fn new() -> Self {
@ -87,7 +87,7 @@ impl PinSelection {
} }
} }
#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
pub struct BandSelector { pub struct BandSelector {
pub band: BandName, pub band: BandName,
} }
@ -118,7 +118,7 @@ impl BandSelector {
} }
#[derive(Debug, Default, Clone, Serialize, Deserialize)] #[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct BandSelection(HashSet<BandSelector>); pub struct BandSelection(BTreeSet<BandSelector>);
impl BandSelection { impl BandSelection {
pub fn new() -> Self { pub fn new() -> Self {

View File

@ -10,9 +10,9 @@ pub mod mesadata {
pub use specctra_core::mesadata::AccessMesadata; pub use specctra_core::mesadata::AccessMesadata;
} }
use std::{cmp::Ordering, collections::HashMap}; use std::{cmp::Ordering, collections::BTreeMap};
use bimap::BiHashMap; use bimap::BiBTreeMap;
use derive_getters::Getters; use derive_getters::Getters;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -35,7 +35,7 @@ use crate::{
}; };
/// Represents a band between two pins. /// Represents a band between two pins.
#[derive(Debug, Hash, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Clone, Debug, Deserialize, Eq, PartialOrd, Ord, PartialEq, Serialize)]
pub struct BandName(String, String); pub struct BandName(String, String);
impl BandName { impl BandName {
@ -60,9 +60,9 @@ pub struct Board<M> {
layout: Layout<M>, layout: Layout<M>,
// TODO: Simplify access logic to these members so that `#[getter(skip)]`s can be removed. // TODO: Simplify access logic to these members so that `#[getter(skip)]`s can be removed.
#[getter(skip)] #[getter(skip)]
node_to_pinname: HashMap<NodeIndex, String>, node_to_pinname: BTreeMap<NodeIndex, String>,
#[getter(skip)] #[getter(skip)]
band_bandname: BiHashMap<BandUid, BandName>, band_bandname: BiBTreeMap<BandUid, BandName>,
} }
impl<M> Board<M> { impl<M> Board<M> {
@ -70,8 +70,8 @@ impl<M> Board<M> {
pub fn new(layout: Layout<M>) -> Self { pub fn new(layout: Layout<M>) -> Self {
Self { Self {
layout, layout,
node_to_pinname: HashMap::new(), node_to_pinname: BTreeMap::new(),
band_bandname: BiHashMap::new(), band_bandname: BiBTreeMap::new(),
} }
} }

View File

@ -2,9 +2,6 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// FIXME (implement Hash for BandUid and such)
#![allow(clippy::derived_hash_with_manual_eq)]
use enum_dispatch::enum_dispatch; use enum_dispatch::enum_dispatch;
use petgraph::stable_graph::NodeIndex; use petgraph::stable_graph::NodeIndex;
@ -22,7 +19,7 @@ use super::{
Drawing, Drawing,
}; };
#[derive(Debug, Hash, Clone, Copy)] #[derive(Clone, Copy, Debug, Ord, PartialOrd)]
pub struct BandUid(pub BandTermsegIndex, pub BandTermsegIndex); pub struct BandUid(pub BandTermsegIndex, pub BandTermsegIndex);
impl BandUid { impl BandUid {
@ -45,7 +42,7 @@ impl PartialEq for BandUid {
impl Eq for BandUid {} impl Eq for BandUid {}
#[enum_dispatch(GetPetgraphIndex)] #[enum_dispatch(GetPetgraphIndex)]
#[derive(Debug, Hash, Clone, Copy)] #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum BandTermsegIndex { pub enum BandTermsegIndex {
Straight(LoneLooseSegIndex), Straight(LoneLooseSegIndex),
Bended(SeqLooseSegIndex), Bended(SeqLooseSegIndex),

View File

@ -18,7 +18,7 @@ use crate::{
use petgraph::stable_graph::NodeIndex; use petgraph::stable_graph::NodeIndex;
#[enum_dispatch(GetPetgraphIndex, MakePrimitive)] #[enum_dispatch(GetPetgraphIndex, MakePrimitive)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum BendIndex { pub enum BendIndex {
Fixed(FixedBendIndex), Fixed(FixedBendIndex),
Loose(LooseBendIndex), Loose(LooseBendIndex),

View File

@ -20,7 +20,7 @@ use crate::{
}; };
#[enum_dispatch(GetPetgraphIndex, MakePrimitive)] #[enum_dispatch(GetPetgraphIndex, MakePrimitive)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum DotIndex { pub enum DotIndex {
Fixed(FixedDotIndex), Fixed(FixedDotIndex),
Loose(LooseDotIndex), Loose(LooseDotIndex),

View File

@ -89,7 +89,7 @@ macro_rules! impl_loose_weight {
// TODO: This enum shouldn't exist: we shouldn't be carrying the tag around like this. Instead we // TODO: This enum shouldn't exist: we shouldn't be carrying the tag around like this. Instead we
// should be getting it from the graph when it's needed. // should be getting it from the graph when it's needed.
#[enum_dispatch(GetPetgraphIndex, MakePrimitive)] #[enum_dispatch(GetPetgraphIndex, MakePrimitive)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum PrimitiveIndex { pub enum PrimitiveIndex {
FixedDot(FixedDotIndex), FixedDot(FixedDotIndex),
LooseDot(LooseDotIndex), LooseDot(LooseDotIndex),

View File

@ -18,7 +18,7 @@ use crate::{
use petgraph::stable_graph::NodeIndex; use petgraph::stable_graph::NodeIndex;
#[enum_dispatch(GetPetgraphIndex, MakePrimitive)] #[enum_dispatch(GetPetgraphIndex, MakePrimitive)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum SegIndex { pub enum SegIndex {
Fixed(FixedSegIndex), Fixed(FixedSegIndex),
LoneLoose(LoneLooseSegIndex), LoneLoose(LoneLooseSegIndex),

View File

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
use std::{collections::HashMap, hash::Hash, marker::PhantomData}; use std::{collections::BTreeMap, marker::PhantomData};
use crate::{ use crate::{
drawing::graph::{GetLayer, Retag}, drawing::graph::{GetLayer, Retag},
@ -17,10 +17,10 @@ pub trait ApplyGeometryEdit<
SW: AccessSegWeight<PW> + GetLayer, SW: AccessSegWeight<PW> + GetLayer,
BW: AccessBendWeight<PW> + GetLayer, BW: AccessBendWeight<PW> + GetLayer,
CW: Copy, CW: Copy,
PI: GetPetgraphIndex + TryInto<DI> + TryInto<SI> + TryInto<BI> + Eq + Hash + Copy, PI: GetPetgraphIndex + TryInto<DI> + TryInto<SI> + TryInto<BI> + Eq + Ord + Copy,
DI: GetPetgraphIndex + Into<PI> + Eq + Hash + Copy, DI: GetPetgraphIndex + Into<PI> + Eq + Ord + Copy,
SI: GetPetgraphIndex + Into<PI> + Eq + Hash + Copy, SI: GetPetgraphIndex + Into<PI> + Eq + Ord + Copy,
BI: GetPetgraphIndex + Into<PI> + Eq + Hash + Copy, BI: GetPetgraphIndex + Into<PI> + Eq + Ord + Copy,
> >
{ {
fn apply(&mut self, edit: &GeometryEdit<PW, DW, SW, BW, CW, PI, DI, SI, BI>); fn apply(&mut self, edit: &GeometryEdit<PW, DW, SW, BW, CW, PI, DI, SI, BI>);
@ -28,10 +28,11 @@ pub trait ApplyGeometryEdit<
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct GeometryEdit<PW, DW, SW, BW, CW, PI, DI, SI, BI> { pub struct GeometryEdit<PW, DW, SW, BW, CW, PI, DI, SI, BI> {
pub(super) dots: HashMap<DI, (Option<DW>, Option<DW>)>, pub(super) dots: BTreeMap<DI, (Option<DW>, Option<DW>)>,
pub(super) segs: HashMap<SI, (Option<((DI, DI), SW)>, Option<((DI, DI), SW)>)>, pub(super) segs: BTreeMap<SI, (Option<((DI, DI), SW)>, Option<((DI, DI), SW)>)>,
pub(super) bends: HashMap<BI, (Option<((DI, DI, DI), BW)>, Option<((DI, DI, DI), BW)>)>, pub(super) bends: BTreeMap<BI, (Option<((DI, DI, DI), BW)>, Option<((DI, DI, DI), BW)>)>,
pub(super) compounds: HashMap<GenericIndex<CW>, (Option<(Vec<PI>, CW)>, Option<(Vec<PI>, CW)>)>, pub(super) compounds:
BTreeMap<GenericIndex<CW>, (Option<(Vec<PI>, CW)>, Option<(Vec<PI>, CW)>)>,
primitive_weight_marker: PhantomData<PW>, primitive_weight_marker: PhantomData<PW>,
} }
@ -41,18 +42,18 @@ impl<
SW: AccessSegWeight<PW> + GetLayer, SW: AccessSegWeight<PW> + GetLayer,
BW: AccessBendWeight<PW> + GetLayer, BW: AccessBendWeight<PW> + GetLayer,
CW: Copy, CW: Copy,
PI: GetPetgraphIndex + TryInto<DI> + TryInto<SI> + TryInto<BI> + Eq + Hash + Copy, PI: GetPetgraphIndex + TryInto<DI> + TryInto<SI> + TryInto<BI> + Eq + Ord + Copy,
DI: GetPetgraphIndex + Into<PI> + Eq + Hash + Copy, DI: GetPetgraphIndex + Into<PI> + Eq + Ord + Copy,
SI: GetPetgraphIndex + Into<PI> + Eq + Hash + Copy, SI: GetPetgraphIndex + Into<PI> + Eq + Ord + Copy,
BI: GetPetgraphIndex + Into<PI> + Eq + Hash + Copy, BI: GetPetgraphIndex + Into<PI> + Eq + Ord + Copy,
> GeometryEdit<PW, DW, SW, BW, CW, PI, DI, SI, BI> > GeometryEdit<PW, DW, SW, BW, CW, PI, DI, SI, BI>
{ {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
dots: HashMap::new(), dots: BTreeMap::new(),
segs: HashMap::new(), segs: BTreeMap::new(),
bends: HashMap::new(), bends: BTreeMap::new(),
compounds: HashMap::new(), compounds: BTreeMap::new(),
primitive_weight_marker: PhantomData, primitive_weight_marker: PhantomData,
} }
} }

View File

@ -64,7 +64,7 @@ pub enum GeometryLabel {
Compound, Compound,
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] #[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
pub enum GenericNode<P, C> { pub enum GenericNode<P, C> {
Primitive(P), Primitive(P),
Compound(C), Compound(C),

View File

@ -2,8 +2,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
use std::collections::hash_map::Entry as HashMapEntry; use std::collections::btree_map::Entry as BTreeMapEntry;
use std::hash::Hash;
use geo::Point; use geo::Point;
use petgraph::stable_graph::StableDiGraph; use petgraph::stable_graph::StableDiGraph;
@ -33,10 +32,10 @@ impl<
SW: AccessSegWeight<PW> + GetLayer, SW: AccessSegWeight<PW> + GetLayer,
BW: AccessBendWeight<PW> + GetLayer, BW: AccessBendWeight<PW> + GetLayer,
CW: Copy, CW: Copy,
PI: GetPetgraphIndex + TryInto<DI> + TryInto<SI> + TryInto<BI> + Eq + Hash + Copy, PI: GetPetgraphIndex + TryInto<DI> + TryInto<SI> + TryInto<BI> + Eq + Ord + Copy,
DI: GetPetgraphIndex + Into<PI> + Eq + Hash + Copy, DI: GetPetgraphIndex + Into<PI> + Eq + Ord + Copy,
SI: GetPetgraphIndex + Into<PI> + Eq + Hash + Copy, SI: GetPetgraphIndex + Into<PI> + Eq + Ord + Copy,
BI: GetPetgraphIndex + Into<PI> + Eq + Hash + Copy, BI: GetPetgraphIndex + Into<PI> + Eq + Ord + Copy,
> RecordingGeometryWithRtree<PW, DW, SW, BW, CW, PI, DI, SI, BI> > RecordingGeometryWithRtree<PW, DW, SW, BW, CW, PI, DI, SI, BI>
{ {
pub fn new(layer_count: usize) -> Self { pub fn new(layer_count: usize) -> Self {
@ -316,23 +315,23 @@ impl<
} }
} }
fn edit_remove_from_map<I, T>( fn edit_remove_from_map<I: Ord, T>(
map: &mut std::collections::HashMap<I, (Option<T>, Option<T>)>, map: &mut std::collections::BTreeMap<I, (Option<T>, Option<T>)>,
index: I, index: I,
data: T, data: T,
) where ) where
I: core::cmp::Eq + Hash, I: core::cmp::Eq + Ord,
{ {
let to_be_inserted = (Some(data), None); let to_be_inserted = (Some(data), None);
match map.entry(index) { match map.entry(index) {
HashMapEntry::Occupied(mut occ) => { BTreeMapEntry::Occupied(mut occ) => {
if let (None, Some(_)) = occ.get() { if let (None, Some(_)) = occ.get() {
occ.remove(); occ.remove();
} else { } else {
*occ.get_mut() = to_be_inserted; *occ.get_mut() = to_be_inserted;
} }
} }
HashMapEntry::Vacant(vac) => { BTreeMapEntry::Vacant(vac) => {
vac.insert(to_be_inserted); vac.insert(to_be_inserted);
} }
} }
@ -344,10 +343,10 @@ impl<
SW: AccessSegWeight<PW> + GetLayer, SW: AccessSegWeight<PW> + GetLayer,
BW: AccessBendWeight<PW> + GetLayer, BW: AccessBendWeight<PW> + GetLayer,
CW: Copy, CW: Copy,
PI: GetPetgraphIndex + TryInto<DI> + TryInto<SI> + TryInto<BI> + Eq + Hash + Copy, PI: GetPetgraphIndex + TryInto<DI> + TryInto<SI> + TryInto<BI> + Eq + Ord + Copy,
DI: GetPetgraphIndex + Into<PI> + Eq + Hash + Copy, DI: GetPetgraphIndex + Into<PI> + Eq + Ord + Copy,
SI: GetPetgraphIndex + Into<PI> + Eq + Hash + Copy, SI: GetPetgraphIndex + Into<PI> + Eq + Ord + Copy,
BI: GetPetgraphIndex + Into<PI> + Eq + Hash + Copy, BI: GetPetgraphIndex + Into<PI> + Eq + Ord + Copy,
> ApplyGeometryEdit<PW, DW, SW, BW, CW, PI, DI, SI, BI> > ApplyGeometryEdit<PW, DW, SW, BW, CW, PI, DI, SI, BI>
for RecordingGeometryWithRtree<PW, DW, SW, BW, CW, PI, DI, SI, BI> for RecordingGeometryWithRtree<PW, DW, SW, BW, CW, PI, DI, SI, BI>
{ {

View File

@ -2,10 +2,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
use std::{ use std::{cmp::Ordering, marker::PhantomData};
hash::{Hash, Hasher},
marker::PhantomData,
};
use enum_dispatch::enum_dispatch; use enum_dispatch::enum_dispatch;
use petgraph::stable_graph::NodeIndex; use petgraph::stable_graph::NodeIndex;
@ -50,6 +47,8 @@ impl<W> core::clone::Clone for GenericIndex<W> {
} }
} }
impl<W> core::marker::Copy for GenericIndex<W> {}
impl<W> core::fmt::Debug for GenericIndex<W> { impl<W> core::fmt::Debug for GenericIndex<W> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("GenericIndex") f.debug_tuple("GenericIndex")
@ -65,12 +64,17 @@ impl<W> PartialEq for GenericIndex<W> {
} }
} }
impl<W> core::marker::Copy for GenericIndex<W> {}
impl<W> Eq for GenericIndex<W> {} impl<W> Eq for GenericIndex<W> {}
impl<W> Hash for GenericIndex<W> { impl<W> PartialOrd for GenericIndex<W> {
fn hash<H: Hasher>(&self, state: &mut H) { fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.node_index.hash(state) self.node_index.partial_cmp(&other.node_index)
}
}
impl<W> Ord for GenericIndex<W> {
fn cmp(&self, other: &Self) -> Ordering {
self.node_index.cmp(&other.node_index)
} }
} }

View File

@ -3,10 +3,8 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::collections::{btree_map::Entry, BTreeMap, BinaryHeap, VecDeque};
use std::collections::{BinaryHeap, HashMap, VecDeque};
use std::hash::Hash;
use std::ops::ControlFlow; use std::ops::ControlFlow;
use petgraph::algo::Measure; use petgraph::algo::Measure;
@ -63,19 +61,19 @@ impl<K: PartialOrd, T> Ord for MinScored<K, T> {
pub struct PathTracker<G> pub struct PathTracker<G>
where where
G: GraphBase, G: GraphBase,
G::NodeId: Eq + Hash, G::NodeId: Eq + Ord,
{ {
came_from: HashMap<G::NodeId, G::NodeId>, came_from: BTreeMap<G::NodeId, G::NodeId>,
} }
impl<G> PathTracker<G> impl<G> PathTracker<G>
where where
G: GraphBase, G: GraphBase,
G::NodeId: Eq + Hash, G::NodeId: Eq + Ord,
{ {
fn new() -> PathTracker<G> { fn new() -> PathTracker<G> {
PathTracker { PathTracker {
came_from: HashMap::new(), came_from: BTreeMap::new(),
} }
} }
@ -101,7 +99,7 @@ where
pub trait AstarStrategy<G, K, R> pub trait AstarStrategy<G, K, R>
where where
G: GraphBase, G: GraphBase,
G::NodeId: Eq + Hash, G::NodeId: Eq + Ord,
for<'a> &'a G: IntoEdges<NodeId = G::NodeId, EdgeId = G::EdgeId> + MakeEdgeRef, for<'a> &'a G: IntoEdges<NodeId = G::NodeId, EdgeId = G::EdgeId> + MakeEdgeRef,
K: Measure + Copy, K: Measure + Copy,
{ {
@ -122,14 +120,14 @@ pub trait MakeEdgeRef: IntoEdgeReferences {
pub struct Astar<G, K> pub struct Astar<G, K>
where where
G: GraphBase, G: GraphBase,
G::NodeId: Eq + Hash, G::NodeId: Eq + Ord,
for<'a> &'a G: IntoEdges<NodeId = G::NodeId, EdgeId = G::EdgeId> + MakeEdgeRef, for<'a> &'a G: IntoEdges<NodeId = G::NodeId, EdgeId = G::EdgeId> + MakeEdgeRef,
K: Measure + Copy, K: Measure + Copy,
{ {
pub graph: G, pub graph: G,
pub visit_next: BinaryHeap<MinScored<K, G::NodeId>>, pub visit_next: BinaryHeap<MinScored<K, G::NodeId>>,
pub scores: HashMap<G::NodeId, K>, pub scores: BTreeMap<G::NodeId, K>,
pub estimate_scores: HashMap<G::NodeId, K>, pub estimate_scores: BTreeMap<G::NodeId, K>,
pub path_tracker: PathTracker<G>, pub path_tracker: PathTracker<G>,
pub maybe_curr_node: Option<G::NodeId>, pub maybe_curr_node: Option<G::NodeId>,
// FIXME: To work around edge references borrowing from the graph we collect then reiterate over tem. // FIXME: To work around edge references borrowing from the graph we collect then reiterate over tem.
@ -154,7 +152,7 @@ pub enum AstarError {
impl<G, K> Astar<G, K> impl<G, K> Astar<G, K>
where where
G: GraphBase, G: GraphBase,
G::NodeId: Eq + Hash, G::NodeId: Eq + Ord,
for<'a> &'a G: IntoEdges<NodeId = G::NodeId, EdgeId = G::EdgeId> + MakeEdgeRef, for<'a> &'a G: IntoEdges<NodeId = G::NodeId, EdgeId = G::EdgeId> + MakeEdgeRef,
K: Measure + Copy, K: Measure + Copy,
{ {
@ -162,8 +160,8 @@ where
let mut this = Self { let mut this = Self {
graph, graph,
visit_next: BinaryHeap::new(), visit_next: BinaryHeap::new(),
scores: HashMap::new(), scores: BTreeMap::new(),
estimate_scores: HashMap::new(), estimate_scores: BTreeMap::new(),
path_tracker: PathTracker::<G>::new(), path_tracker: PathTracker::<G>::new(),
maybe_curr_node: None, maybe_curr_node: None,
edge_ids: VecDeque::new(), edge_ids: VecDeque::new(),
@ -182,7 +180,7 @@ impl<G, K, R, S: AstarStrategy<G, K, R>> Step<S, (K, Vec<G::NodeId>, R), AstarCo
for Astar<G, K> for Astar<G, K>
where where
G: GraphBase, G: GraphBase,
G::NodeId: Eq + Hash, G::NodeId: Eq + Ord,
for<'a> &'a G: IntoEdges<NodeId = G::NodeId, EdgeId = G::EdgeId> + MakeEdgeRef, for<'a> &'a G: IntoEdges<NodeId = G::NodeId, EdgeId = G::EdgeId> + MakeEdgeRef,
K: Measure + Copy, K: Measure + Copy,
{ {
@ -209,7 +207,7 @@ where
let next_score = node_score + edge_cost; let next_score = node_score + edge_cost;
match self.scores.entry(next) { match self.scores.entry(next) {
Occupied(mut entry) => { Entry::Occupied(mut entry) => {
// No need to add neighbors that we have already reached through a // No need to add neighbors that we have already reached through a
// shorter path than now. // shorter path than now.
if *entry.get() <= next_score { if *entry.get() <= next_score {
@ -217,7 +215,7 @@ where
} }
entry.insert(next_score); entry.insert(next_score);
} }
Vacant(entry) => { Entry::Vacant(entry) => {
entry.insert(next_score); entry.insert(next_score);
} }
} }
@ -248,7 +246,7 @@ where
} }
match self.estimate_scores.entry(node) { match self.estimate_scores.entry(node) {
Occupied(mut entry) => { Entry::Occupied(mut entry) => {
// If the node has already been visited with an equal or lower score than // If the node has already been visited with an equal or lower score than
// now, then we do not need to re-visit it. // now, then we do not need to re-visit it.
if *entry.get() <= estimate_score { if *entry.get() <= estimate_score {
@ -256,7 +254,7 @@ where
} }
entry.insert(estimate_score); entry.insert(estimate_score);
} }
Vacant(entry) => { Entry::Vacant(entry) => {
entry.insert(estimate_score); entry.insert(estimate_score);
} }
} }

View File

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
use std::collections::HashMap; use std::collections::BTreeMap;
use enum_dispatch::enum_dispatch; use enum_dispatch::enum_dispatch;
use geo::Point; use geo::Point;
@ -37,7 +37,7 @@ use crate::{
use super::RouterOptions; use super::RouterOptions;
#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct NavvertexIndex(NodeIndex<usize>); pub struct NavvertexIndex(NodeIndex<usize>);
impl GetPetgraphIndex for NavvertexIndex { impl GetPetgraphIndex for NavvertexIndex {
@ -50,7 +50,7 @@ impl GetPetgraphIndex for NavvertexIndex {
/// counterclockwise. Unlike their constituents, binavvertices are themselves /// counterclockwise. Unlike their constituents, binavvertices are themselves
/// not considered navvertices. /// not considered navvertices.
#[enum_dispatch(GetPetgraphIndex, MakePrimitive)] #[enum_dispatch(GetPetgraphIndex, MakePrimitive)]
#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BinavvertexNodeIndex { pub enum BinavvertexNodeIndex {
FixedDot(FixedDotIndex), FixedDot(FixedDotIndex),
FixedBend(FixedBendIndex), FixedBend(FixedBendIndex),
@ -84,7 +84,7 @@ impl From<BinavvertexNodeIndex> for GearIndex {
/// ///
/// The name "trianvertex" is a shortening of "triangulation vertex". /// The name "trianvertex" is a shortening of "triangulation vertex".
#[enum_dispatch(GetPetgraphIndex, MakePrimitive)] #[enum_dispatch(GetPetgraphIndex, MakePrimitive)]
#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
enum TrianvertexNodeIndex { enum TrianvertexNodeIndex {
FixedDot(FixedDotIndex), FixedDot(FixedDotIndex),
FixedBend(FixedBendIndex), FixedBend(FixedBendIndex),
@ -210,8 +210,7 @@ impl Navmesh {
let mut origin_navvertex = None; let mut origin_navvertex = None;
let mut destination_navvertex = None; let mut destination_navvertex = None;
// `HashMap` is obviously suboptimal here. let mut map = BTreeMap::new();
let mut map = HashMap::new();
for trianvertex in triangulation.node_identifiers() { for trianvertex in triangulation.node_identifiers() {
if trianvertex == origin.into() { if trianvertex == origin.into() {
@ -302,7 +301,7 @@ impl Navmesh {
fn add_node_to_graph_and_map_as_binavvertex( fn add_node_to_graph_and_map_as_binavvertex(
graph: &mut UnGraph<NavvertexWeight, (), usize>, graph: &mut UnGraph<NavvertexWeight, (), usize>,
map: &mut HashMap<TrianvertexNodeIndex, Vec<(NodeIndex<usize>, NodeIndex<usize>)>>, map: &mut BTreeMap<TrianvertexNodeIndex, Vec<(NodeIndex<usize>, NodeIndex<usize>)>>,
trianvertex: TrianvertexNodeIndex, trianvertex: TrianvertexNodeIndex,
node: BinavvertexNodeIndex, node: BinavvertexNodeIndex,
) { ) {

View File

@ -6,8 +6,9 @@
//! Design DSN file, creating the [`Board`] object from the file, as well as //! Design DSN file, creating the [`Board`] object from the file, as well as
//! exporting the session file //! exporting the session file
use std::collections::{btree_map::Entry as BTreeMapEntry, BTreeMap};
use geo::{point, Point, Rotate}; use geo::{point, Point, Rotate};
use std::collections::{hash_map::Entry as HashMapEntry, HashMap};
use crate::{ use crate::{
board::{mesadata::AccessMesadata, Board}, board::{mesadata::AccessMesadata, Board},
@ -73,7 +74,7 @@ impl SpecctraDesign {
let mesadata = board.mesadata(); let mesadata = board.mesadata();
let drawing = board.layout().drawing(); let drawing = board.layout().drawing();
let mut net_outs = HashMap::<usize, structure::NetOut>::new(); let mut net_outs = BTreeMap::<usize, structure::NetOut>::new();
for index in drawing.primitive_nodes() { for index in drawing.primitive_nodes() {
let primitive = index.primitive(drawing); let primitive = index.primitive(drawing);
@ -131,8 +132,8 @@ impl SpecctraDesign {
}; };
let net_out = match net_outs.entry(net) { let net_out = match net_outs.entry(net) {
HashMapEntry::Occupied(occ) => occ.into_mut(), BTreeMapEntry::Occupied(occ) => occ.into_mut(),
HashMapEntry::Vacant(vac) => vac.insert(structure::NetOut { BTreeMapEntry::Vacant(vac) => vac.insert(structure::NetOut {
name: mesadata name: mesadata
.net_netname(net) .net_netname(net)
.ok_or_else(|| { .ok_or_else(|| {
@ -208,7 +209,7 @@ impl SpecctraDesign {
}) })
// flatten the nested iters into a single stream of tuples // flatten the nested iters into a single stream of tuples
.flatten() .flatten()
.collect::<HashMap<String, usize>>(); .collect::<BTreeMap<String, usize>>();
// add pins from components // add pins from components
for component in &self.pcb.placement.components { for component in &self.pcb.placement.components {