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, DenseElementIndex, DenseIncidenceIndex,
28    DenseRelationIndex, ElementIncidenceCount, ElementIncidences, ElementPredecessors,
29    ElementSuccessors, ElementWeight, IncidenceBase, IncidenceCounts, IncidenceElement,
30    IncidenceRelation, IncidenceRole, IncidenceWeight, LocalElementIdentity,
31    LocalIncidenceIdentity, LocalRelationIdentity, RelationIncidenceCount, RelationIncidences,
32    RelationWeight, TopologyBase, 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
75impl<T> GraphCounts for T where T: TopologyCounts {}
76
77/// Dense node-index capability for graph views.
78///
79/// Graph-facing name for [`DenseElementIndex`]. Lets graph algorithms allocate
80/// per-node scratch storage such as visited sets or distance arrays.
81pub trait DenseNodeIndex: DenseElementIndex {
82    /// Returns the exclusive upper bound for node indexes in this graph view.
83    fn node_bound(&self) -> usize {
84        self.element_bound()
85    }
86
87    /// Returns the dense index for `node` in this graph view.
88    fn node_index(&self, node: Self::ElementId) -> usize {
89        self.element_index(node)
90    }
91}
92
93impl<T> DenseNodeIndex for T where T: DenseElementIndex {}
94
95/// Dense edge-index capability for graph views.
96///
97/// Graph-facing name for [`DenseRelationIndex`].
98pub trait DenseEdgeIndex: DenseRelationIndex {
99    /// Returns the exclusive upper bound for edge indexes in this graph view.
100    fn edge_bound(&self) -> usize {
101        self.relation_bound()
102    }
103
104    /// Returns the dense index for `edge` in this graph view.
105    fn edge_index(&self, edge: Self::RelationId) -> usize {
106        self.relation_index(edge)
107    }
108}
109
110impl<T> DenseEdgeIndex for T where T: DenseRelationIndex {}
111
112/// Dense endpoint-index capability for graph views with incidences.
113///
114/// Graph-facing name for [`DenseIncidenceIndex`].
115pub trait DenseEndpointIndex: DenseIncidenceIndex {
116    /// Returns the exclusive upper bound for endpoint indexes in this graph view.
117    fn endpoint_bound(&self) -> usize {
118        self.incidence_bound()
119    }
120
121    /// Returns the dense index for `endpoint` in this graph view.
122    fn endpoint_index(&self, endpoint: Self::IncidenceId) -> usize {
123        self.incidence_index(endpoint)
124    }
125}
126
127impl<T> DenseEndpointIndex for T where T: DenseIncidenceIndex {}
128
129/// Node-ID containment capability for graph views.
130///
131/// Graph-facing name for [`ContainsElement`].
132pub trait ContainsNode: ContainsElement {
133    /// Returns whether `node` is valid and visible in this graph view.
134    fn contains_node(&self, node: Self::ElementId) -> bool {
135        self.contains_element(node)
136    }
137}
138
139impl<T> ContainsNode for T where T: ContainsElement {}
140
141/// Edge-ID containment capability for graph views.
142///
143/// Graph-facing name for [`ContainsRelation`].
144pub trait ContainsEdge: ContainsRelation {
145    /// Returns whether `edge` is valid and visible in this graph view.
146    fn contains_edge(&self, edge: Self::RelationId) -> bool {
147        self.contains_relation(edge)
148    }
149}
150
151impl<T> ContainsEdge for T where T: ContainsRelation {}
152
153/// Endpoint-ID containment capability for graph views with incidences.
154///
155/// Graph-facing name for [`ContainsIncidence`].
156pub trait ContainsEndpoint: ContainsIncidence {
157    /// Returns whether `endpoint` is valid and visible in this graph view.
158    fn contains_endpoint(&self, endpoint: Self::IncidenceId) -> bool {
159        self.contains_incidence(endpoint)
160    }
161}
162
163impl<T> ContainsEndpoint for T where T: ContainsIncidence {}
164
165/// Capability for resolving directed edge sources.
166///
167/// Separated from [`EdgeTargetGraph`] because some layouts (e.g. outgoing-only
168/// CSR) can resolve targets cheaply but need extra indexing for sources.
169pub trait EdgeSourceGraph: TopologyBase {
170    /// Returns the source node of `edge`. `edge` must be a valid edge ID.
171    fn source(&self, edge: Self::RelationId) -> Self::ElementId;
172}
173
174/// Capability for resolving directed edge targets.
175///
176/// The endpoint capability needed by outgoing traversal: an outgoing edge ID
177/// can be mapped to the node it reaches without requiring the backend to
178/// support reverse source lookup.
179pub trait EdgeTargetGraph: TopologyBase {
180    /// Returns the target node of `edge`. `edge` must be a valid edge ID.
181    fn target(&self, edge: Self::RelationId) -> Self::ElementId;
182}
183
184/// Capability for resolving both directed edge endpoints.
185///
186/// Binary graph form of complete relation endpoint lookup. Implementations may
187/// back it with an edge table, compact arrays, generated edges, or validated
188/// snapshot sections.
189pub trait EdgeEndpointGraph: EdgeSourceGraph + EdgeTargetGraph {
190    /// Returns `(source, target)` for `edge`. `edge` must be a valid edge ID.
191    fn endpoints(&self, edge: Self::RelationId) -> (Self::ElementId, Self::ElementId) {
192        (self.source(edge), self.target(edge))
193    }
194}
195
196impl<T> EdgeEndpointGraph for T where T: EdgeSourceGraph + EdgeTargetGraph {}
197
198/// Capability for traversing outgoing edges from a source node.
199///
200/// The generic associated iterator type lets each backend return its own
201/// concrete iterator without allocation or dynamic dispatch.
202pub trait OutgoingGraph: TopologyBase {
203    /// Iterator over edge IDs leaving one source node.
204    type OutEdges<'view>: Iterator<Item = Self::RelationId>
205    where
206        Self: 'view;
207
208    /// Returns edges whose source is `node`.
209    fn outgoing_edges(&self, node: Self::ElementId) -> Self::OutEdges<'_>;
210}
211
212/// Capability for traversing incoming edges to a target node.
213///
214/// Separate from [`OutgoingGraph`] because many layouts only provide one
215/// direction cheaply.
216pub trait IncomingGraph: TopologyBase {
217    /// Iterator over edge IDs entering one target node.
218    type InEdges<'view>: Iterator<Item = Self::RelationId>
219    where
220        Self: 'view;
221
222    /// Returns edges whose target is `node`.
223    fn incoming_edges(&self, node: Self::ElementId) -> Self::InEdges<'_>;
224}
225
226/// Capability for traversing directly reachable outgoing neighbor nodes.
227///
228/// Graph-facing name for [`ElementSuccessors`]. Answers `node -> successor
229/// nodes` without requiring callers to materialize outgoing edge IDs and
230/// resolve each edge target. Implementations define whether parallel edges
231/// produce repeated neighbor nodes; those that preserve graph edge order and
232/// multiplicity should document that behavior.
233pub trait OutgoingNeighborsGraph: ElementSuccessors {
234    /// Iterator over nodes directly reachable from one source node.
235    type OutNeighbors<'view>: Iterator<Item = Self::ElementId>
236    where
237        Self: 'view;
238
239    /// Returns neighbor nodes reached by outgoing edges from `node`.
240    fn outgoing_neighbors(&self, node: Self::ElementId) -> Self::OutNeighbors<'_>;
241}
242
243impl<T> OutgoingNeighborsGraph for T
244where
245    T: ElementSuccessors,
246{
247    type OutNeighbors<'view>
248        = <T as ElementSuccessors>::Successors<'view>
249    where
250        T: 'view;
251
252    fn outgoing_neighbors(&self, node: Self::ElementId) -> Self::OutNeighbors<'_> {
253        <Self as ElementSuccessors>::element_successors(self, node)
254    }
255}
256
257/// Capability for traversing directly preceding incoming neighbor nodes.
258///
259/// Graph-facing name for [`ElementPredecessors`]. Answers `node ->
260/// predecessor nodes` without requiring callers to materialize incoming edge
261/// IDs and resolve each edge source.
262pub trait IncomingNeighborsGraph: ElementPredecessors {
263    /// Iterator over nodes that have incoming edges to one target node.
264    type InNeighbors<'view>: Iterator<Item = Self::ElementId>
265    where
266        Self: 'view;
267
268    /// Returns predecessor nodes with incoming edges to `node`.
269    fn incoming_neighbors(&self, node: Self::ElementId) -> Self::InNeighbors<'_>;
270}
271
272impl<T> IncomingNeighborsGraph for T
273where
274    T: ElementPredecessors,
275{
276    type InNeighbors<'view>
277        = <T as ElementPredecessors>::Predecessors<'view>
278    where
279        T: 'view;
280
281    fn incoming_neighbors(&self, node: Self::ElementId) -> Self::InNeighbors<'_> {
282        <Self as ElementPredecessors>::element_predecessors(self, node)
283    }
284}
285
286/// Exact outgoing-edge count capability.
287///
288/// Pairs with [`OutgoingGraph`] for backends that can report out-degree
289/// without traversal; does not require outgoing traversal support by itself.
290pub trait OutgoingEdgeCount: TopologyBase {
291    /// Returns the number of edges leaving `node`.
292    fn out_degree(&self, node: Self::ElementId) -> usize;
293}
294
295/// Exact incoming-edge count capability.
296///
297/// Pairs with [`IncomingGraph`] for backends that can report in-degree
298/// without traversal; does not require incoming traversal support by itself.
299pub trait IncomingEdgeCount: TopologyBase {
300    /// Returns the number of edges entering `node`.
301    fn in_degree(&self, node: Self::ElementId) -> usize;
302}
303
304/// Convenience marker bundling endpoint resolution and both traversal
305/// directions. One-direction layouts should implement the smaller capability
306/// traits they can provide instead.
307pub trait DirectedGraph: EdgeEndpointGraph + OutgoingGraph + IncomingGraph {}
308
309impl<T> DirectedGraph for T where T: EdgeEndpointGraph + OutgoingGraph + IncomingGraph {}
310
311/// Convenience marker for forward directed graph traversal (target lookup +
312/// outgoing edge iteration).
313pub trait ForwardGraph: EdgeTargetGraph + OutgoingGraph {}
314
315impl<T> ForwardGraph for T where T: EdgeTargetGraph + OutgoingGraph {}
316
317/// Convenience marker for reverse directed graph traversal (source lookup +
318/// incoming edge iteration). Expected to be provided by CSC-backed views.
319pub trait ReverseGraph: EdgeSourceGraph + IncomingGraph {}
320
321impl<T> ReverseGraph for T where T: EdgeSourceGraph + IncomingGraph {}