Skip to main content

oxgraph_graph/
lib.rs

1//! Storage-agnostic traits for binary graph views.
2//!
3//! `oxgraph-graph` is the binary graph specialization above `oxgraph-topology`. Use it
4//! to write generic graph consumers over node/edge vocabulary: endpoint lookup,
5//! outgoing traversal, incoming traversal, and degree queries.
6//!
7//! Graph views use topology elements as nodes and topology relations as edges.
8//! Implement concrete layouts, snapshots, builders, mutation systems, payloads,
9//! and algorithms in higher-level crates by implementing these read-view
10//! capabilities.
11//!
12//! # Performance contract
13//!
14//! Most of the traits and aliases in this crate are graph-vocabulary shadows of
15//! topology traits. Unless a specific item documents otherwise, every method,
16//! iterator, and alias inherits its performance contract from the underlying
17//! topology trait it shadows: `O(1)` for accessors, `O(1)` to construct an
18//! iterator plus `O(k)` to yield `k` items, and `perf: unspecified` for
19//! capability-bundle marker traits and blanket impls.
20#![no_std]
21
22#[cfg(kani)]
23extern crate kani;
24
25pub use oxgraph_topology::{
26    CanonicalElementIdentity, CanonicalIncidenceIdentity, CanonicalRelationIdentity,
27    ContainsElement, ContainsIncidence, ContainsRelation, ElementIncidenceCount, ElementIncidences,
28    ElementIndex, ElementPredecessors, ElementSuccessors, ElementWeight, IncidenceBase,
29    IncidenceCounts, IncidenceElement, IncidenceIndex, IncidenceRelation, IncidenceRole,
30    IncidenceWeight, LocalElementIdentity, LocalIncidenceIdentity, LocalRelationIdentity,
31    RelationIncidenceCount, RelationIncidences, RelationIndex, RelationWeight, TopologyBase,
32    TopologyCounts, TopologyId,
33};
34
35/// Graph-facing alias for a topology element ID (binary graph node).
36pub type NodeId<G> = <G as TopologyBase>::ElementId;
37
38/// Graph-facing alias for a topology relation ID (binary graph edge).
39pub type EdgeId<G> = <G as TopologyBase>::RelationId;
40
41/// Graph-facing alias for a topology incidence ID (binary graph endpoint).
42///
43/// A binary graph endpoint participation is represented as a topology incidence
44/// only when a graph view exposes incidence capabilities.
45pub type EndpointId<G> = <G as IncidenceBase>::IncidenceId;
46
47/// Graph-facing alias for a topology role used to distinguish source and
48/// target endpoint participation on graph views that expose incidences.
49pub type EndpointRole<G> = <G as IncidenceBase>::Role;
50
51/// Base capability for graph views over topology storage.
52///
53/// Graph-facing name for [`TopologyBase`]. Bundles the associated `ElementId`
54/// and `RelationId` types under graph vocabulary so generic code can require a
55/// graph base contract without naming topology traits directly.
56pub trait GraphBase: TopologyBase {}
57
58impl<T> GraphBase for T where T: TopologyBase {}
59
60/// Count capability for a graph view.
61///
62/// Graph-facing name for [`TopologyCounts`].
63pub trait GraphCounts: TopologyCounts {
64    /// Returns the number of nodes visible in this graph view.
65    fn node_count(&self) -> usize {
66        self.element_count()
67    }
68
69    /// Returns the number of edges visible in this graph view.
70    fn edge_count(&self) -> usize {
71        self.relation_count()
72    }
73}
74
75/// Dense node-index capability for graph views.
76///
77/// Graph-facing name for [`ElementIndex`]. Lets graph algorithms allocate
78/// per-node scratch storage such as visited sets or distance arrays.
79pub trait NodeIndex: ElementIndex {
80    /// Returns the exclusive upper bound for node indexes in this graph view.
81    fn node_bound(&self) -> usize {
82        self.element_bound()
83    }
84
85    /// Returns the dense index for `node` in this graph view.
86    fn node_index(&self, node: Self::ElementId) -> usize {
87        self.element_index(node)
88    }
89}
90
91impl<T> NodeIndex for T where T: ElementIndex {}
92
93/// Dense edge-index capability for graph views.
94///
95/// Graph-facing name for [`RelationIndex`].
96pub trait EdgeIndex: RelationIndex {
97    /// Returns the exclusive upper bound for edge indexes in this graph view.
98    fn edge_bound(&self) -> usize {
99        self.relation_bound()
100    }
101
102    /// Returns the dense index for `edge` in this graph view.
103    fn edge_index(&self, edge: Self::RelationId) -> usize {
104        self.relation_index(edge)
105    }
106}
107
108impl<T> EdgeIndex for T where T: RelationIndex {}
109
110/// Dense endpoint-index capability for graph views with incidences.
111///
112/// Graph-facing name for [`IncidenceIndex`].
113pub trait EndpointIndex: IncidenceIndex {
114    /// Returns the exclusive upper bound for endpoint indexes in this graph view.
115    fn endpoint_bound(&self) -> usize {
116        self.incidence_bound()
117    }
118
119    /// Returns the dense index for `endpoint` in this graph view.
120    fn endpoint_index(&self, endpoint: Self::IncidenceId) -> usize {
121        self.incidence_index(endpoint)
122    }
123}
124
125impl<T> EndpointIndex for T where T: IncidenceIndex {}
126
127/// Node-ID containment capability for graph views.
128///
129/// Graph-facing name for [`ContainsElement`].
130pub trait ContainsNode: ContainsElement {
131    /// Returns whether `node` is valid and visible in this graph view.
132    fn contains_node(&self, node: Self::ElementId) -> bool {
133        self.contains_element(node)
134    }
135}
136
137impl<T> ContainsNode for T where T: ContainsElement {}
138
139/// Edge-ID containment capability for graph views.
140///
141/// Graph-facing name for [`ContainsRelation`].
142pub trait ContainsEdge: ContainsRelation {
143    /// Returns whether `edge` is valid and visible in this graph view.
144    fn contains_edge(&self, edge: Self::RelationId) -> bool {
145        self.contains_relation(edge)
146    }
147}
148
149impl<T> ContainsEdge for T where T: ContainsRelation {}
150
151/// Endpoint-ID containment capability for graph views with incidences.
152///
153/// Graph-facing name for [`ContainsIncidence`].
154pub trait ContainsEndpoint: ContainsIncidence {
155    /// Returns whether `endpoint` is valid and visible in this graph view.
156    fn contains_endpoint(&self, endpoint: Self::IncidenceId) -> bool {
157        self.contains_incidence(endpoint)
158    }
159}
160
161impl<T> ContainsEndpoint for T where T: ContainsIncidence {}
162
163/// Capability for resolving directed edge sources.
164///
165/// Separated from [`EdgeTargetGraph`] because some layouts (e.g. outgoing-only
166/// CSR) can resolve targets cheaply but need extra indexing for sources.
167pub trait EdgeSourceGraph: TopologyBase {
168    /// Returns the source node of `edge`. `edge` must be a valid edge ID.
169    fn source(&self, edge: Self::RelationId) -> Self::ElementId;
170}
171
172/// Capability for resolving directed edge targets.
173///
174/// The endpoint capability needed by outgoing traversal: an outgoing edge ID
175/// can be mapped to the node it reaches without requiring the backend to
176/// support reverse source lookup.
177pub trait EdgeTargetGraph: TopologyBase {
178    /// Returns the target node of `edge`. `edge` must be a valid edge ID.
179    fn target(&self, edge: Self::RelationId) -> Self::ElementId;
180}
181
182/// Capability for resolving both directed edge endpoints.
183///
184/// Binary graph form of complete relation endpoint lookup. Implementations may
185/// back it with an edge table, compact arrays, generated edges, or validated
186/// snapshot sections.
187pub trait EdgeEndpointGraph: EdgeSourceGraph + EdgeTargetGraph {
188    /// Returns `(source, target)` for `edge`. `edge` must be a valid edge ID.
189    fn endpoints(&self, edge: Self::RelationId) -> (Self::ElementId, Self::ElementId) {
190        (self.source(edge), self.target(edge))
191    }
192}
193
194impl<T> EdgeEndpointGraph for T where T: EdgeSourceGraph + EdgeTargetGraph {}
195
196/// Capability for traversing outgoing edges from a source node.
197///
198/// The generic associated iterator type lets each backend return its own
199/// concrete iterator without allocation or dynamic dispatch.
200pub trait OutgoingGraph: TopologyBase {
201    /// Iterator over edge IDs leaving one source node.
202    type OutEdges<'view>: Iterator<Item = Self::RelationId>
203    where
204        Self: 'view;
205
206    /// Returns edges whose source is `node`.
207    fn outgoing_edges(&self, node: Self::ElementId) -> Self::OutEdges<'_>;
208}
209
210/// Capability for traversing incoming edges to a target node.
211///
212/// Separate from [`OutgoingGraph`] because many layouts only provide one
213/// direction cheaply.
214pub trait IncomingGraph: TopologyBase {
215    /// Iterator over edge IDs entering one target node.
216    type InEdges<'view>: Iterator<Item = Self::RelationId>
217    where
218        Self: 'view;
219
220    /// Returns edges whose target is `node`.
221    fn incoming_edges(&self, node: Self::ElementId) -> Self::InEdges<'_>;
222}
223
224/// Capability for traversing directly reachable outgoing neighbor nodes.
225///
226/// Graph-facing name for [`ElementSuccessors`]. Answers `node -> successor
227/// nodes` without requiring callers to materialize outgoing edge IDs and
228/// resolve each edge target. Implementations define whether parallel edges
229/// produce repeated neighbor nodes; those that preserve graph edge order and
230/// multiplicity should document that behavior.
231pub trait OutgoingNeighborsGraph: ElementSuccessors {
232    /// Iterator over nodes directly reachable from one source node.
233    type OutNeighbors<'view>: Iterator<Item = Self::ElementId>
234    where
235        Self: 'view;
236
237    /// Returns neighbor nodes reached by outgoing edges from `node`.
238    fn outgoing_neighbors(&self, node: Self::ElementId) -> Self::OutNeighbors<'_>;
239}
240
241impl<T> OutgoingNeighborsGraph for T
242where
243    T: ElementSuccessors,
244{
245    type OutNeighbors<'view>
246        = <T as ElementSuccessors>::Successors<'view>
247    where
248        T: 'view;
249
250    fn outgoing_neighbors(&self, node: Self::ElementId) -> Self::OutNeighbors<'_> {
251        <Self as ElementSuccessors>::element_successors(self, node)
252    }
253}
254
255/// Capability for traversing directly preceding incoming neighbor nodes.
256///
257/// Graph-facing name for [`ElementPredecessors`]. Answers `node ->
258/// predecessor nodes` without requiring callers to materialize incoming edge
259/// IDs and resolve each edge source.
260pub trait IncomingNeighborsGraph: ElementPredecessors {
261    /// Iterator over nodes that have incoming edges to one target node.
262    type InNeighbors<'view>: Iterator<Item = Self::ElementId>
263    where
264        Self: 'view;
265
266    /// Returns predecessor nodes with incoming edges to `node`.
267    fn incoming_neighbors(&self, node: Self::ElementId) -> Self::InNeighbors<'_>;
268}
269
270impl<T> IncomingNeighborsGraph for T
271where
272    T: ElementPredecessors,
273{
274    type InNeighbors<'view>
275        = <T as ElementPredecessors>::Predecessors<'view>
276    where
277        T: 'view;
278
279    fn incoming_neighbors(&self, node: Self::ElementId) -> Self::InNeighbors<'_> {
280        <Self as ElementPredecessors>::element_predecessors(self, node)
281    }
282}
283
284/// Exact outgoing-edge count capability.
285///
286/// Pairs with [`OutgoingGraph`] for backends that can report out-degree
287/// without traversal; does not require outgoing traversal support by itself.
288pub trait OutgoingEdgeCount: TopologyBase {
289    /// Returns the number of edges leaving `node`.
290    fn out_degree(&self, node: Self::ElementId) -> usize;
291}
292
293/// Exact incoming-edge count capability.
294///
295/// Pairs with [`IncomingGraph`] for backends that can report in-degree
296/// without traversal; does not require incoming traversal support by itself.
297pub trait IncomingEdgeCount: TopologyBase {
298    /// Returns the number of edges entering `node`.
299    fn in_degree(&self, node: Self::ElementId) -> usize;
300}
301
302/// Convenience marker bundling endpoint resolution and both traversal
303/// directions. One-direction layouts should implement the smaller capability
304/// traits they can provide instead.
305pub trait DirectedGraph: EdgeEndpointGraph + OutgoingGraph + IncomingGraph {}
306
307impl<T> DirectedGraph for T where T: EdgeEndpointGraph + OutgoingGraph + IncomingGraph {}
308
309/// Convenience marker for forward directed graph traversal (target lookup +
310/// outgoing edge iteration).
311pub trait ForwardGraph: EdgeTargetGraph + OutgoingGraph {}
312
313impl<T> ForwardGraph for T where T: EdgeTargetGraph + OutgoingGraph {}
314
315/// Convenience marker for reverse directed graph traversal (source lookup +
316/// incoming edge iteration). Expected to be provided by CSC-backed views.
317pub trait ReverseGraph: EdgeSourceGraph + IncomingGraph {}
318
319impl<T> ReverseGraph for T where T: EdgeSourceGraph + IncomingGraph {}