1use arrow_array::{Array, PrimitiveArray, types::ArrowPrimitiveType};
9use oxgraph_topology::{
10 ElementIndex, ElementWeight, IncidenceBase, IncidenceIndex, IncidenceWeight, RelationIndex,
11 RelationWeight, TopologyBase,
12};
13
14use crate::{
15 model::{IdFamily, PropertyError, PropertyLayer, PropertyLayerData, ensure_no_nulls},
16 width::{AxisIndex, ElementAxis, IncidenceAxis, PropertyAxis, PropertyIndex, RelationAxis},
17};
18
19#[derive(Clone, Copy, Debug)]
25pub struct GraphPropertyLayers<'view, Id, NodeIndex, EdgeIndex>
26where
27 NodeIndex: PropertyIndex,
28 EdgeIndex: PropertyIndex,
29{
30 pub element: &'view [PropertyLayer<Id, NodeIndex>],
32 pub relation: &'view [PropertyLayer<Id, EdgeIndex>],
34}
35
36#[derive(Clone, Copy, Debug)]
42pub struct HyperPropertyLayers<'view, Id, VertexIndex, RelationIndex, IncidenceIndex>
43where
44 VertexIndex: PropertyIndex,
45 RelationIndex: PropertyIndex,
46 IncidenceIndex: PropertyIndex,
47{
48 pub element: &'view [PropertyLayer<Id, VertexIndex>],
50 pub relation: &'view [PropertyLayer<Id, RelationIndex>],
52 pub incidence: &'view [PropertyLayer<Id, IncidenceIndex>],
54}
55
56macro_rules! impl_axis_topology_base {
62 ($storage:ident, $axis:ty, $index_trait:ident) => {
63 impl<T, Id, I, P> TopologyBase for $storage<'_, $axis, T, Id, I, P>
64 where
65 T: $index_trait,
66 I: PropertyIndex,
67 P: ArrowPrimitiveType,
68 {
69 type ElementId = T::ElementId;
70 type RelationId = T::RelationId;
71 }
72 };
73}
74
75macro_rules! impl_dense_axis_weight {
85 (
86 $axis:ty, $index_trait:ident, $id_ty:ident, $index_fn:ident,
87 $weight_trait:ident, $weight_fn:ident
88 ) => {
89 impl<T, Id, I, P> $weight_trait for DenseWeights<'_, $axis, T, Id, I, P>
90 where
91 T: $index_trait,
92 I: PropertyIndex,
93 P: ArrowPrimitiveType,
94 P::Native: Copy,
95 {
96 type Weight = P::Native;
97
98 fn $weight_fn(&self, id: Self::$id_ty) -> Self::Weight {
99 self.values.value(self.topology.$index_fn(id))
100 }
101 }
102 };
103}
104
105macro_rules! impl_sparse_axis_weight {
112 (
113 $axis:ty, $index_trait:ident, $id_ty:ident, $index_fn:ident,
114 $weight_trait:ident, $weight_fn:ident
115 ) => {
116 impl<T, Id, I, P> $weight_trait for SparseWeights<'_, $axis, T, Id, I, P>
117 where
118 T: $index_trait,
119 I: PropertyIndex,
120 P: ArrowPrimitiveType,
121 P::Native: Copy,
122 {
123 type Weight = P::Native;
124
125 fn $weight_fn(&self, id: Self::$id_ty) -> Self::Weight {
126 sparse_value::<I, P>(
127 self.indices,
128 self.values,
129 self.default,
130 self.topology.$index_fn(id),
131 )
132 }
133 }
134 };
135}
136
137macro_rules! impl_axis_incidence_base {
142 ($storage:ident) => {
143 impl<T, Id, I, P> IncidenceBase for $storage<'_, IncidenceAxis, T, Id, I, P>
144 where
145 T: IncidenceIndex,
146 I: PropertyIndex,
147 P: ArrowPrimitiveType,
148 {
149 type IncidenceId = T::IncidenceId;
150 type Role = T::Role;
151 }
152 };
153}
154
155pub struct DenseWeights<'view, A, T, Id, I, P>
164where
165 A: PropertyAxis,
166 I: PropertyIndex,
167 P: ArrowPrimitiveType,
168{
169 topology: &'view T,
171 values: &'view PrimitiveArray<P>,
173 property: core::marker::PhantomData<(A, Id, I)>,
175}
176
177impl<'view, A, T, Id, I, P> DenseWeights<'view, A, T, Id, I, P>
178where
179 A: PropertyAxis,
180 T: AxisIndex<A>,
181 I: PropertyIndex,
182 P: ArrowPrimitiveType,
183{
184 pub fn new(
196 topology: &'view T,
197 layer: &'view PropertyLayer<Id, I>,
198 ) -> Result<Self, PropertyError> {
199 let values = validate_dense_primitive_selection::<Id, I, P>(
200 layer,
201 A::id_family(),
202 topology.axis_bound(),
203 )?;
204 Ok(Self {
205 topology,
206 values,
207 property: core::marker::PhantomData,
208 })
209 }
210}
211
212impl_axis_topology_base!(DenseWeights, ElementAxis, ElementIndex);
213impl_axis_topology_base!(DenseWeights, RelationAxis, RelationIndex);
214impl_axis_topology_base!(DenseWeights, IncidenceAxis, IncidenceIndex);
215impl_axis_incidence_base!(DenseWeights);
216
217impl_dense_axis_weight!(
218 ElementAxis,
219 ElementIndex,
220 ElementId,
221 element_index,
222 ElementWeight,
223 element_weight
224);
225impl_dense_axis_weight!(
226 RelationAxis,
227 RelationIndex,
228 RelationId,
229 relation_index,
230 RelationWeight,
231 relation_weight
232);
233impl_dense_axis_weight!(
234 IncidenceAxis,
235 IncidenceIndex,
236 IncidenceId,
237 incidence_index,
238 IncidenceWeight,
239 incidence_weight
240);
241
242pub struct SparseWeights<'view, A, T, Id, I, P>
251where
252 A: PropertyAxis,
253 I: PropertyIndex,
254 P: ArrowPrimitiveType,
255{
256 topology: &'view T,
258 indices: &'view PrimitiveArray<I::ArrowType>,
260 values: &'view PrimitiveArray<P>,
262 default: P::Native,
264 property: core::marker::PhantomData<(A, Id)>,
266}
267
268impl<'view, A, T, Id, I, P> SparseWeights<'view, A, T, Id, I, P>
269where
270 A: PropertyAxis,
271 T: AxisIndex<A>,
272 I: PropertyIndex,
273 P: ArrowPrimitiveType,
274 P::Native: Copy,
275{
276 pub fn new(
289 topology: &'view T,
290 layer: &'view PropertyLayer<Id, I>,
291 ) -> Result<Self, PropertyError> {
292 let (indices, values, default) = validate_sparse_primitive_selection::<I, P, Id>(
293 layer,
294 A::id_family(),
295 topology.axis_bound(),
296 )?;
297 Ok(Self {
298 topology,
299 indices,
300 values,
301 default,
302 property: core::marker::PhantomData,
303 })
304 }
305}
306
307impl_axis_topology_base!(SparseWeights, ElementAxis, ElementIndex);
308impl_axis_topology_base!(SparseWeights, RelationAxis, RelationIndex);
309impl_axis_topology_base!(SparseWeights, IncidenceAxis, IncidenceIndex);
310impl_axis_incidence_base!(SparseWeights);
311
312impl_sparse_axis_weight!(
313 ElementAxis,
314 ElementIndex,
315 ElementId,
316 element_index,
317 ElementWeight,
318 element_weight
319);
320impl_sparse_axis_weight!(
321 RelationAxis,
322 RelationIndex,
323 RelationId,
324 relation_index,
325 RelationWeight,
326 relation_weight
327);
328impl_sparse_axis_weight!(
329 IncidenceAxis,
330 IncidenceIndex,
331 IncidenceId,
332 incidence_index,
333 IncidenceWeight,
334 incidence_weight
335);
336
337fn validate_dense_primitive_selection<Id, I, P>(
343 layer: &PropertyLayer<Id, I>,
344 expected: IdFamily,
345 required: usize,
346) -> Result<&PrimitiveArray<P>, PropertyError>
347where
348 I: PropertyIndex,
349 P: ArrowPrimitiveType,
350{
351 if layer.descriptor().id_family != expected {
352 return Err(PropertyError::IdFamilyMismatch {
353 expected,
354 actual: layer.descriptor().id_family,
355 });
356 }
357 if layer.len() < required {
358 return Err(PropertyError::LayerTooShort {
359 required,
360 actual: layer.len(),
361 });
362 }
363 let PropertyLayerData::Dense { values } = layer.data() else {
364 return Err(PropertyError::ExpectedDenseStorage {
365 name: layer.descriptor().name.clone(),
366 });
367 };
368 let primitive = values
369 .as_any()
370 .downcast_ref::<PrimitiveArray<P>>()
371 .ok_or_else(|| PropertyError::ArrowTypeMismatch {
372 name: layer.descriptor().name.clone(),
373 })?;
374 ensure_no_nulls(primitive)?;
375 Ok(primitive)
376}
377
378type SparsePrimitiveSelection<'layer, I, P> = (
380 &'layer PrimitiveArray<<I as PropertyIndex>::ArrowType>,
381 &'layer PrimitiveArray<P>,
382 <P as ArrowPrimitiveType>::Native,
383);
384
385fn validate_sparse_primitive_selection<I, P, Id>(
391 layer: &PropertyLayer<Id, I>,
392 expected: IdFamily,
393 required: usize,
394) -> Result<SparsePrimitiveSelection<'_, I, P>, PropertyError>
395where
396 I: PropertyIndex,
397 P: ArrowPrimitiveType,
398 P::Native: Copy,
399{
400 if layer.descriptor().id_family != expected {
401 return Err(PropertyError::IdFamilyMismatch {
402 expected,
403 actual: layer.descriptor().id_family,
404 });
405 }
406 if layer.len() < required {
407 return Err(PropertyError::LayerTooShort {
408 required,
409 actual: layer.len(),
410 });
411 }
412 let PropertyLayerData::Sparse {
413 indices,
414 values,
415 default,
416 } = layer.data()
417 else {
418 return Err(PropertyError::ExpectedSparseStorage {
419 name: layer.descriptor().name.clone(),
420 });
421 };
422 let Some(default_array) = default else {
423 return Err(PropertyError::SparseNullMissingNotTotal {
424 name: layer.descriptor().name.clone(),
425 });
426 };
427 let primitive = values
428 .as_any()
429 .downcast_ref::<PrimitiveArray<P>>()
430 .ok_or_else(|| PropertyError::ArrowTypeMismatch {
431 name: layer.descriptor().name.clone(),
432 })?;
433 ensure_no_nulls(primitive)?;
434 let default_primitive = default_array
435 .as_any()
436 .downcast_ref::<PrimitiveArray<P>>()
437 .ok_or_else(|| PropertyError::ArrowTypeMismatch {
438 name: layer.descriptor().name.clone(),
439 })?;
440 if default_primitive.len() != 1 || default_primitive.is_null(0) {
441 return Err(PropertyError::DefaultPolicyMismatch {
442 name: layer.descriptor().name.clone(),
443 });
444 }
445 Ok((indices.as_ref(), primitive, default_primitive.value(0)))
446}
447
448fn sparse_value<I, P>(
454 indices: &PrimitiveArray<I::ArrowType>,
455 values: &PrimitiveArray<P>,
456 default: P::Native,
457 index: usize,
458) -> P::Native
459where
460 I: PropertyIndex,
461 P: ArrowPrimitiveType,
462 P::Native: Copy,
463{
464 let Some(target) = I::from_usize(index) else {
465 return default;
466 };
467 let mut low = 0_usize;
468 let mut high = indices.len();
469 while low < high {
470 let mid = low + ((high - low) / 2);
471 let value = indices.value(mid);
472 if value < target {
473 low = mid + 1;
474 } else {
475 high = mid;
476 }
477 }
478 if low < indices.len() && indices.value(low) == target {
479 values.value(low)
480 } else {
481 default
482 }
483}