Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace NodeId implementation with global hash #309

Merged
merged 1 commit into from
Oct 23, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 74 additions & 70 deletions common/src/graph.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
#![allow(clippy::len_without_is_empty)]

use std::convert::TryFrom;
use std::fmt;
use std::num::NonZeroU32;
use std::collections::VecDeque;

use blake3::Hasher;
use fxhash::{FxHashMap, FxHashSet};
use serde::{Deserialize, Serialize};

use crate::{
Expand All @@ -16,16 +15,18 @@ use crate::{
/// Graph of the right dodecahedral tiling of H^3
#[derive(Debug, Clone)]
pub struct Graph<N> {
nodes: Vec<Node<N>>,
nodes: FxHashMap<NodeId, Node<N>>,
Ralith marked this conversation as resolved.
Show resolved Hide resolved
/// This field stores implicitly added nodes to ensure that they're initialized in the correct
/// order
fresh: Vec<NodeId>,
}

impl<N> Graph<N> {
pub fn new() -> Self {
let mut nodes = FxHashMap::default();
nodes.insert(NodeId::ROOT, Node::new(None, 0));
Self {
nodes: vec![Node::new(None, 0, 0)],
nodes,
fresh: vec![NodeId::ROOT],
}
}
Expand All @@ -37,7 +38,7 @@ impl<N> Graph<N> {

#[inline]
pub fn contains(&self, node: NodeId) -> bool {
node.0.get() as usize <= self.nodes.len()
self.nodes.contains_key(&node)
}

/// Nodes created since the last call to `clear_fresh`
Expand Down Expand Up @@ -93,22 +94,22 @@ impl<N> Graph<N> {

#[inline]
pub fn get(&self, node: NodeId) -> &Option<N> {
&self.nodes[node.idx()].value
&self.nodes[&node].value
}

#[inline]
pub fn get_mut(&mut self, node: NodeId) -> &mut Option<N> {
&mut self.nodes[node.idx()].value
&mut self.nodes.get_mut(&node).unwrap().value
}

#[inline]
pub fn neighbor(&self, node: NodeId, which: Side) -> Option<NodeId> {
self.nodes[node.idx()].neighbors[which as usize]
self.nodes[&node].neighbors[which as usize]
}

#[inline]
pub fn length(&self, node: NodeId) -> u32 {
self.nodes[node.idx()].length
self.nodes[&node].length
}

/// Given a `transform` relative to a `reference` node, computes the node
Expand Down Expand Up @@ -141,21 +142,18 @@ impl<N> Graph<N> {

#[inline]
pub fn parent(&self, node: NodeId) -> Option<Side> {
self.nodes[node.idx()].parent_side
self.nodes[&node].parent_side
}

/// Iterate over every node and its parent
/// Iterate over every node and its parent except the root
pub fn tree(&self) -> TreeIter<'_, N> {
TreeIter {
id: NodeId::from_idx(1),
remaining: &self.nodes[1..],
}
TreeIter::new(self)
}

/// Ensures that the neighbour node at a particular side of a particular node exists in the graph,
/// as well as the nodes from the origin to the neighbour node.
pub fn ensure_neighbor(&mut self, node: NodeId, side: Side) -> NodeId {
let v = &self.nodes[node.idx()];
let v = &self.nodes[&node];
if let Some(x) = v.neighbors[side as usize] {
// Neighbor already exists
return x;
Expand All @@ -173,7 +171,7 @@ impl<N> Graph<N> {
}

// Neighbor is closer to the origin; find it, backfilling if necessary
let x = self.nodes[v.parent().unwrap().idx()].neighbors[side as usize].unwrap();
let x = self.nodes[&v.parent().unwrap()].neighbors[side as usize].unwrap();
let parent_side = v.parent_side.unwrap();
let neighbor = self.ensure_neighbor(x, parent_side);
self.link_neighbors(node, neighbor, side);
Expand All @@ -182,8 +180,8 @@ impl<N> Graph<N> {

/// Whether `node`'s neighbor along `side` is closer than it to the origin
fn is_near_side(&self, node: NodeId, side: Side) -> bool {
let v = &self.nodes[node.idx()];
v.neighbors[side as usize].map_or(false, |x| self.nodes[x.idx()].length < v.length)
let v = &self.nodes[&node];
v.neighbors[side as usize].map_or(false, |x| self.nodes[&x].length < v.length)
}

pub fn insert_child(&mut self, parent: NodeId, side: Side) -> NodeId {
Expand All @@ -199,16 +197,15 @@ impl<N> Graph<N> {
.min_by_key(|&(side, _)| side)
.unwrap();
let mut hasher = Hasher::new();
hasher.update(&self.nodes[predecessor.idx()].hash.to_le_bytes());
hasher.update(&predecessor.0.to_le_bytes());
hasher.update(&[path_side as u8]);
let mut xof = hasher.finalize_xof();
let mut hash = [0; 16];
xof.fill(&mut hash);
let hash = u128::from_le_bytes(hash);
let id = NodeId(u128::from_le_bytes(hash));

let id = NodeId::from_idx(self.nodes.len());
let length = self.nodes[parent.idx()].length + 1;
self.nodes.push(Node::new(Some(side), length, hash));
let length = self.nodes[&parent].length + 1;
self.nodes.insert(id, Node::new(Some(side), length));
self.link_neighbors(id, parent, side);
for (side, neighbor) in shorter_neighbors {
self.link_neighbors(id, neighbor, side);
Expand All @@ -219,7 +216,7 @@ impl<N> Graph<N> {

#[inline]
pub fn hash_of(&self, node: NodeId) -> u128 {
self.nodes[node.idx()].hash
node.0
}

/// Ensure all shorter neighbors of a not-yet-created child node exist and return them, excluding the given parent node
Expand All @@ -237,7 +234,7 @@ impl<N> Graph<N> {
{
continue;
}
let x = self.nodes[parent.idx()].neighbors[neighbor_side as usize].unwrap();
let x = self.nodes[&parent].neighbors[neighbor_side as usize].unwrap();
let neighbor = self.ensure_neighbor(x, parent_side);
neighbors[count] = Some((neighbor_side, neighbor));
count += 1;
Expand All @@ -248,11 +245,11 @@ impl<N> Graph<N> {
/// Register `a` and `b` as adjacent along `side`
fn link_neighbors(&mut self, a: NodeId, b: NodeId, side: Side) {
debug_assert!(
self.nodes[a.idx()].neighbors[side as usize].is_none()
&& self.nodes[b.idx()].neighbors[side as usize].is_none()
self.nodes[&a].neighbors[side as usize].is_none()
&& self.nodes[&b].neighbors[side as usize].is_none()
);
self.nodes[a.idx()].neighbors[side as usize] = Some(b);
self.nodes[b.idx()].neighbors[side as usize] = Some(a);
self.nodes.get_mut(&a).unwrap().neighbors[side as usize] = Some(b);
self.nodes.get_mut(&b).unwrap().neighbors[side as usize] = Some(a);
}
}

Expand All @@ -262,31 +259,11 @@ impl<N> Default for Graph<N> {
}
}

#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct NodeId(NonZeroU32);
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct NodeId(u128);

impl NodeId {
pub const ROOT: Self = Self(unsafe { NonZeroU32::new_unchecked(1) });

fn from_idx(x: usize) -> Self {
Self(NonZeroU32::new(u32::try_from(x + 1).expect("graph grew too large")).unwrap())
}

fn idx(self) -> usize {
(self.0.get() - 1) as usize
}
}

impl From<NodeId> for u32 {
fn from(x: NodeId) -> u32 {
x.0.get() - 1
}
}

impl fmt::Debug for NodeId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(self.0.get() - 1).fmt(f)
}
pub const ROOT: Self = Self(0);
}

#[derive(Debug, Clone)]
Expand All @@ -296,17 +273,15 @@ struct Node<N> {
/// Distance to origin via parents
length: u32,
neighbors: [Option<NodeId>; SIDE_COUNT],
hash: u128,
}

impl<N> Node<N> {
fn new(parent_side: Option<Side>, length: u32, hash: u128) -> Self {
fn new(parent_side: Option<Side>, length: u32) -> Self {
Self {
value: None,
parent_side,
length,
neighbors: [None; SIDE_COUNT],
hash,
}
}

Expand All @@ -315,27 +290,58 @@ impl<N> Node<N> {
}
}

// Iterates through the graph with breadth-first search
pub struct TreeIter<'a, N> {
id: NodeId,
remaining: &'a [Node<N>],
queue: VecDeque<NodeId>,
visited: FxHashSet<NodeId>,
nodes: &'a FxHashMap<NodeId, Node<N>>,
}

impl<'a, N> TreeIter<'a, N> {
fn new(graph: &'a Graph<N>) -> Self {
let mut result = TreeIter {
queue: VecDeque::from([NodeId::ROOT]),
visited: FxHashSet::from_iter([NodeId::ROOT]),
nodes: &graph.nodes,
};

// Skip the root node
let _ = result.next_node();

result
}

// Returns the next Node in the traversal. The iterator returns its parent and parent side.
fn next_node(&mut self) -> Option<&Node<N>> {
let node_id = self.queue.pop_front()?;
let node = &self.nodes[&node_id];
for side in Side::iter() {
if let Some(neighbor) = node.neighbors[side as usize] {
if !self.visited.contains(&neighbor) {
self.queue.push_back(neighbor);
self.visited.insert(neighbor);
}
}
}
Some(node)
}
}

impl<N> Iterator for TreeIter<'_, N> {
type Item = (Side, NodeId);

fn next(&mut self) -> Option<Self::Item> {
let (node, rest) = self.remaining.split_first()?;
self.remaining = rest;
self.id = NodeId::from_idx(self.id.idx() + 1);
Ralith marked this conversation as resolved.
Show resolved Hide resolved
let node = self.next_node()?;
let side = node.parent_side.unwrap();
Some((side, node.neighbors[side as usize].unwrap()))
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::{proto::Position, traversal::ensure_nearby};

use super::*;
use approx::*;

#[test]
Expand All @@ -348,14 +354,14 @@ mod tests {
assert_eq!(graph.len(), 2);
assert_eq!(a, a2);
assert_eq!(graph.ensure_neighbor(a, Side::A), NodeId::ROOT);
assert_eq!(graph.nodes[a.idx()].length, 1);
assert_eq!(graph.nodes[&a].length, 1);
let b = graph.ensure_neighbor(NodeId::ROOT, Side::B);
assert_eq!(graph.len(), 3);
assert_eq!(graph.ensure_neighbor(b, Side::B), NodeId::ROOT);
let c = graph.ensure_neighbor(a, Side::C);
assert!(graph.len() > 4);
assert_eq!(graph.ensure_neighbor(c, Side::C), a);
assert_eq!(graph.nodes[c.idx()].length, 2);
assert_eq!(graph.nodes[&c].length, 2);
}

#[test]
Expand All @@ -382,7 +388,7 @@ mod tests {
);
assert!(common.contains(&NodeId::ROOT));
let other = common.iter().cloned().find(|&x| x != NodeId::ROOT).unwrap();
assert_eq!(graph.nodes[other.idx()].length, 2);
assert_eq!(graph.nodes[&other].length, 2);
}

#[test]
Expand Down Expand Up @@ -424,16 +430,14 @@ mod tests {
let n1 = g.ensure_neighbor(NodeId::ROOT, Side::A);
let n2 = g.ensure_neighbor(n1, Side::B);
let n3 = g.ensure_neighbor(n2, Side::C);
let n4 = g.ensure_neighbor(n3, Side::D);
g.hash_of(n4)
g.ensure_neighbor(n3, Side::D)
};
let h2 = {
let mut g = Graph::<()>::new();
let n1 = g.ensure_neighbor(NodeId::ROOT, Side::C);
let n2 = g.ensure_neighbor(n1, Side::A);
let n3 = g.ensure_neighbor(n2, Side::B);
let n4 = g.ensure_neighbor(n3, Side::D);
g.hash_of(n4)
g.ensure_neighbor(n3, Side::D)
};

assert_eq!(h1, h2);
Expand Down
Loading