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::LayoutWord,
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: LayoutWord,
24 VertexWord: LayoutWord,
25 RelationWord: LayoutWord,
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: LayoutWord,
50 VertexWord: LayoutWord,
51 RelationWord: LayoutWord,
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: LayoutWord<Index = IncidenceIndex>,
115 VertexWord: LayoutWord<Index = VertexIndex>,
116 RelationWord: LayoutWord<Index = RelationIndex>,
117 VertexIndex: crate::word::LayoutIndex,
118 RelationIndex: crate::word::LayoutIndex,
119 IncidenceIndex: crate::word::LayoutIndex,
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: LayoutWord<Index = IncidenceIndex>,
139 VertexWord: LayoutWord<Index = VertexIndex>,
140 RelationWord: LayoutWord<Index = RelationIndex>,
141 VertexIndex: crate::word::LayoutIndex,
142 RelationIndex: crate::word::LayoutIndex,
143 IncidenceIndex: crate::word::LayoutIndex,
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(§ions, 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}