Skip to main content

Adjacency

Struct Adjacency 

Source
pub struct Adjacency { /* private fields */ }
Expand description

The neighbours of every node, under every label, in both directions.

use yo_graph::{Adjacency, Dir};

const FOLLOWS: u32 = 1;

let mut g = Adjacency::new();
g.link(1, 2, FOLLOWS, 100);
g.link(1, 3, FOLLOWS, 101);

assert_eq!(g.neighbours(1, FOLLOWS, Dir::Out), &[2, 3]);
assert_eq!(g.neighbours(2, FOLLOWS, Dir::In), &[1]);
assert_eq!(g.degree(1, FOLLOWS, Dir::Out), 2);

Implementations§

Source§

impl Adjacency

Source

pub fn new() -> Adjacency

An empty plane that indexes both directions, so In answers as well as Out does.

Source

pub fn out_only() -> Adjacency

An empty plane that indexes outgoing edges only.

This halves the memory and halves the work an insert does, and it is the right choice whenever nothing asks the graph who points at a node. neighbours under Dir::In then answers nothing at all, which is why it is a decision at construction rather than a flag on a call: a walk that silently found no incoming edges because of how the plane was built would look exactly like a node that has none.

Source

pub fn indexes_incoming(&self) -> bool

Whether incoming edges are indexed.

Source

pub fn edges(&self) -> usize

How many edges have been linked and not unlinked.

Source

pub fn is_empty(&self) -> bool

Whether any edge is linked.

Source

pub fn runs(&self) -> usize

How many runs hold at least one edge.

Add an edge from src to dst under label, carrying edge as the slot of the edge record.

This appends. It does not look for an edge that is already there, for the reason in the module docs, so linking the same pair twice leaves two entries and unlinking it once leaves one.

Remove one edge from src to dst under label, and answer with the edge slot it was carrying.

Costs a scan of the run at each end, because finding which position an edge sits at is the one thing a plane keyed by node rather than by edge cannot do in a step. A caller that already knows the position wants Adjacency::unlink_at.

Remove the edge at position i of one run, and answer with the neighbour and the edge slot that were there.

This is the O(1) primitive and it touches one end only, so the other end still holds its half of the edge. It is for a caller that tracks positions itself and will do both. Whatever used to be last has moved into i.

Source

pub fn neighbours(&self, node: u64, label: u32, dir: Dir) -> &[u64]

The neighbours of node under label in dir, in one contiguous run.

One probe and then a sequential read. The order is whatever inserting and deleting left behind, because a delete moves the last entry into the hole it made.

Source

pub fn edge_slots(&self, node: u64, label: u32, dir: Dir) -> &[u32]

The edge slots of node under label in dir, lined up one for one with Adjacency::neighbours.

Source

pub fn degree(&self, node: u64, label: u32, dir: Dir) -> usize

How many edges node has under label in dir.

Source

pub fn for_each_run( &self, label: u32, dir: Dir, f: impl FnMut(u64, &[u64], &[u32]), )

Every non empty run under label in dir, as the node and its two slices, in whatever order the table happens to hold them.

This is the read side of promotion. The cold form is built by walking the hot plane once and handing every run to an encoder, and there is no other way to get at a run whose node you have not already been told about, because the table is keyed by the node rather than ordered by it. The order is deliberately not promised: a caller that needs the runs in node order is building something sorted anyway and can sort what it collects.

Source

pub fn prefetch(&self, node: u64, label: u32, dir: Dir)

Ask the cache for the slot a run’s header will be found in.

A multi hop walk knows its whole next frontier before it reads any of it, so it can issue these across the frontier and then come back and read. That is the same two walk shape 04 section 3 drains a command batch with, and it is what the two hop budget in G14 is actually spending: the probes are dependent loads, and the only way to make them cheap is to stop them being serial.

Source

pub fn bytes(&self) -> usize

Resident bytes, counting the table, both arenas, and everything the free lists are still holding.

Source

pub fn compact(&mut self)

Rebuild the table and the arenas with nothing spare in them.

A run that emptied leaves its header behind, a run that shrank leaves slack, and a free list holds blocks nothing has asked for again. None of that is worth chasing on the write path, so this is the sweep that reclaims it, and it is the natural thing to run before a settled part of the graph is promoted into the cold form. Every run comes out sized to exactly what it holds and laid out one after another, which is also the order the promotion wants to read them in.

Trait Implementations§

Source§

impl Debug for Adjacency

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Adjacency

Source§

fn default() -> Adjacency

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.