Skip to main content

oxgraph_hyper_bcsr/internal/
view.rs

1//! Borrowed bipartite-CSR hypergraph view and its caller-facing borrowed
2//! section parameter struct.
3
4use crate::{
5    error::BcsrError,
6    internal::validation::{BcsrValidation, DerivedCounts, validate_sections},
7    word::BcsrWord,
8};
9
10/// Borrowed input slices for a bipartite-CSR hypergraph view.
11///
12/// Offset slices use `OffsetWord` and decode to the incidence index width.
13/// Participant value slices use `VertexWord` and decode to the vertex index
14/// width. Vertex-major relation slices use `RelationWord` and decode to the
15/// relation index width.
16///
17/// # Performance
18///
19/// `perf: unspecified`; this is a borrowed parameter struct.
20#[derive(Clone, Copy, Debug)]
21pub struct BcsrSections<'view, OffsetWord, VertexWord, RelationWord>
22where
23    OffsetWord: BcsrWord,
24    VertexWord: BcsrWord,
25    RelationWord: BcsrWord,
26{
27    /// Hyperedge-major head offsets, length `hyperedge_count + 1`.
28    pub head_offsets: &'view [OffsetWord],
29    /// Flat vertex IDs in head sets, length `P_head`.
30    pub head_participants: &'view [VertexWord],
31    /// Hyperedge-major tail offsets, length `hyperedge_count + 1`.
32    pub tail_offsets: &'view [OffsetWord],
33    /// Flat vertex IDs in tail sets, length `P_tail`.
34    pub tail_participants: &'view [VertexWord],
35    /// Vertex-major outgoing offsets, length `vertex_count + 1`.
36    pub vertex_outgoing_offsets: &'view [OffsetWord],
37    /// Flat hyperedge IDs where the vertex is in head, length `P_outgoing`.
38    pub vertex_outgoing_hyperedges: &'view [RelationWord],
39    /// Vertex-major incoming offsets, length `vertex_count + 1`.
40    pub vertex_incoming_offsets: &'view [OffsetWord],
41    /// Flat hyperedge IDs where the vertex is in tail, length `P_incoming`.
42    pub vertex_incoming_hyperedges: &'view [RelationWord],
43}
44
45#[cfg(kani)]
46impl<'view, OffsetWord, VertexWord, RelationWord>
47    BcsrSections<'view, OffsetWord, VertexWord, RelationWord>
48where
49    OffsetWord: BcsrWord,
50    VertexWord: BcsrWord,
51    RelationWord: BcsrWord,
52{
53    /// Builds a [`BcsrSections`] from eight ordered borrowed slices.
54    ///
55    /// Exists only to deduplicate the kani proof harnesses: each `#[kani::proof]`
56    /// otherwise repeats the eight-field struct literal verbatim. Argument
57    /// order matches the field declaration order above (head, tail,
58    /// vertex-outgoing, vertex-incoming; offsets paired with their value
59    /// slice).
60    ///
61    /// # Performance
62    ///
63    /// `O(1)` field assignment.
64    #[expect(
65        clippy::too_many_arguments,
66        reason = "eight slice arguments mirror the eight BcsrSections fields in the documented declaration order"
67    )]
68    pub(crate) fn for_kani(
69        head_offsets: &'view [OffsetWord],
70        head_participants: &'view [VertexWord],
71        tail_offsets: &'view [OffsetWord],
72        tail_participants: &'view [VertexWord],
73        vertex_outgoing_offsets: &'view [OffsetWord],
74        vertex_outgoing_hyperedges: &'view [RelationWord],
75        vertex_incoming_offsets: &'view [OffsetWord],
76        vertex_incoming_hyperedges: &'view [RelationWord],
77    ) -> Self {
78        Self {
79            head_offsets,
80            head_participants,
81            tail_offsets,
82            tail_participants,
83            vertex_outgoing_offsets,
84            vertex_outgoing_hyperedges,
85            vertex_incoming_offsets,
86            vertex_incoming_hyperedges,
87        }
88    }
89}
90
91/// Borrowed bipartite compressed-sparse-row hypergraph view.
92///
93/// `BcsrHypergraph` borrows the eight section payloads supplied through
94/// [`BcsrSections`] without copying or allocating. Construction validates the
95/// borrowed slices according to the chosen [`BcsrValidation`] level. Once
96/// constructed, every traversal is `O(degree)` in either direction.
97///
98/// # Performance
99///
100/// Construction is `O(P_head + P_tail + P_outgoing + P_incoming)` at
101/// [`BcsrValidation::Layout`]. [`BcsrValidation::Strict`] adds an
102/// `O((P_head + P_tail) · log d)` cross-direction walk where `d` is the
103/// maximum vertex outgoing or incoming degree.
104#[derive(Clone, Copy, Debug)]
105pub struct BcsrHypergraph<
106    'view,
107    VertexIndex,
108    RelationIndex,
109    IncidenceIndex,
110    OffsetWord,
111    VertexWord,
112    RelationWord,
113> where
114    OffsetWord: BcsrWord<Index = IncidenceIndex>,
115    VertexWord: BcsrWord<Index = VertexIndex>,
116    RelationWord: BcsrWord<Index = RelationIndex>,
117    VertexIndex: crate::word::BcsrIndex,
118    RelationIndex: crate::word::BcsrIndex,
119    IncidenceIndex: crate::word::BcsrIndex,
120{
121    /// Validated counts cached for `O(1)` access.
122    counts: DerivedCounts,
123    /// The eight borrowed sections backing this view.
124    sections: BcsrSections<'view, OffsetWord, VertexWord, RelationWord>,
125}
126
127impl<'view, VertexIndex, RelationIndex, IncidenceIndex, OffsetWord, VertexWord, RelationWord>
128    BcsrHypergraph<
129        'view,
130        VertexIndex,
131        RelationIndex,
132        IncidenceIndex,
133        OffsetWord,
134        VertexWord,
135        RelationWord,
136    >
137where
138    OffsetWord: BcsrWord<Index = IncidenceIndex>,
139    VertexWord: BcsrWord<Index = VertexIndex>,
140    RelationWord: BcsrWord<Index = RelationIndex>,
141    VertexIndex: crate::word::BcsrIndex,
142    RelationIndex: crate::word::BcsrIndex,
143    IncidenceIndex: crate::word::BcsrIndex,
144{
145    /// Validates `sections` at [`BcsrValidation::Layout`] and returns a view.
146    ///
147    /// # Errors
148    ///
149    /// Returns [`BcsrError`] when any layout invariant fails. See
150    /// [`BcsrValidation::Layout`] for the full list.
151    ///
152    /// # Performance
153    ///
154    /// `O(P_head + P_tail + P_outgoing + P_incoming)`.
155    pub fn open(
156        sections: BcsrSections<'view, OffsetWord, VertexWord, RelationWord>,
157    ) -> Result<Self, BcsrError> {
158        Self::open_with(sections, BcsrValidation::Layout)
159    }
160
161    /// Validates `sections` at the requested level and returns a view.
162    ///
163    /// # Errors
164    ///
165    /// Returns [`BcsrError`] when any invariant visible at `level` fails.
166    ///
167    /// # Performance
168    ///
169    /// `O(P_head + P_tail + P_outgoing + P_incoming)` at
170    /// [`BcsrValidation::Layout`]; adds `O((P_head + P_tail) · log d)` at
171    /// [`BcsrValidation::Strict`].
172    pub fn open_with(
173        sections: BcsrSections<'view, OffsetWord, VertexWord, RelationWord>,
174        level: BcsrValidation,
175    ) -> Result<Self, BcsrError> {
176        let counts = validate_sections(&sections, level)?;
177        Ok(Self { counts, sections })
178    }
179
180    /// Returns the number of vertices in this view.
181    ///
182    /// # Performance
183    ///
184    /// This method is `O(1)`.
185    #[must_use]
186    pub const fn vertex_count(&self) -> usize {
187        self.counts.vertex_count
188    }
189
190    /// Returns the number of hyperedges in this view.
191    ///
192    /// # Performance
193    ///
194    /// This method is `O(1)`.
195    #[must_use]
196    pub const fn hyperedge_count(&self) -> usize {
197        self.counts.hyperedge_count
198    }
199
200    /// Returns the number of outgoing incidences (`P_head == P_outgoing`).
201    ///
202    /// # Performance
203    ///
204    /// This method is `O(1)`.
205    #[must_use]
206    pub const fn outgoing_incidence_count(&self) -> usize {
207        self.counts.p_outgoing
208    }
209
210    /// Returns the number of incoming incidences (`P_tail == P_incoming`).
211    ///
212    /// # Performance
213    ///
214    /// This method is `O(1)`.
215    #[must_use]
216    pub const fn incoming_incidence_count(&self) -> usize {
217        self.counts.p_incoming
218    }
219
220    /// Returns the validated count cache.
221    pub(in crate::internal) const fn counts(&self) -> DerivedCounts {
222        self.counts
223    }
224
225    /// Returns the borrowed sections.
226    pub(in crate::internal) const fn sections(
227        &self,
228    ) -> &BcsrSections<'view, OffsetWord, VertexWord, RelationWord> {
229        &self.sections
230    }
231}