1use std::collections::{BTreeMap, BTreeSet};
4
5use oxgraph_csr::build::GraphBuilder;
6use oxgraph_graph::{
7 DenseElementIndex, DenseRelationIndex, EdgeSourceGraph, EdgeTargetGraph, ElementPredecessors,
8 ElementSuccessors, IncomingEdgeCount, IncomingGraph, LocalElementIdentity,
9 LocalRelationIdentity, OutgoingEdgeCount, OutgoingGraph, TopologyBase, TopologyCounts,
10};
11use oxgraph_hyper::{
12 DenseIncidenceIndex, DirectedHyperedgeIncidences, DirectedHyperedgeParticipants,
13 DirectedVertexHyperedges, ElementIncidenceCount, IncidenceBase, IncidenceCounts,
14 IncidenceElement, IncidenceRelation, IncidenceRole, IncidentHyperedges, RelationIncidenceCount,
15 RelationIncidences,
16};
17use oxgraph_hyper_bcsr::build::{HyperVertexId, HypergraphBuilder};
18use serde::{Deserialize, Serialize};
19
20use crate::{
21 DbError, ElementId, IncidenceId, RelationId, RelationTypeId, RoleId,
22 catalog::{GraphProjectionDefinition, HypergraphProjectionDefinition},
23 overlay::StateView,
24 state::{IncidenceRecord, PropertySubject, RelationRecord},
25};
26
27#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
33pub struct ProjectionElementId(u32);
34
35impl ProjectionElementId {
36 #[must_use]
42 pub const fn new(value: u32) -> Self {
43 Self(value)
44 }
45
46 #[must_use]
52 pub const fn get(self) -> u32 {
53 self.0
54 }
55}
56
57#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
63pub struct ProjectionRelationId(u32);
64
65impl ProjectionRelationId {
66 #[must_use]
72 pub const fn new(value: u32) -> Self {
73 Self(value)
74 }
75
76 #[must_use]
82 pub const fn get(self) -> u32 {
83 self.0
84 }
85}
86
87#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
93pub struct ProjectionIncidenceId(u32);
94
95impl ProjectionIncidenceId {
96 #[must_use]
102 pub const fn new(value: u32) -> Self {
103 Self(value)
104 }
105
106 #[must_use]
112 pub const fn get(self) -> u32 {
113 self.0
114 }
115}
116
117#[derive(Clone, Debug, Eq, PartialEq)]
127pub struct GraphProjection {
128 definition: GraphProjectionDefinition,
130 elements: Vec<ElementId>,
132 element_index: BTreeMap<ElementId, ProjectionElementId>,
134 relations: Vec<RelationId>,
136 relation_index: BTreeMap<RelationId, ProjectionRelationId>,
138 endpoints: Vec<(ProjectionElementId, ProjectionElementId)>,
140 out_edges: Vec<Vec<ProjectionRelationId>>,
142 in_edges: Vec<Vec<ProjectionRelationId>>,
144 successors: Vec<Vec<ProjectionElementId>>,
146 predecessors: Vec<Vec<ProjectionElementId>>,
148}
149
150impl GraphProjection {
151 pub(crate) fn from_state(
163 state: &impl StateView,
164 definition: GraphProjectionDefinition,
165 ) -> Result<Self, DbError> {
166 let mut builder = GraphProjectionBuilder::new(definition);
167 for relation in state.relations() {
168 if relation_selected(relation.as_ref(), &builder.definition.relation_types) {
169 builder.push_relation(state, relation.as_ref())?;
170 }
171 }
172 builder.finish()
173 }
174
175 #[must_use]
181 pub const fn definition(&self) -> &GraphProjectionDefinition {
182 &self.definition
183 }
184
185 pub(crate) fn subjects(&self) -> Vec<PropertySubject> {
187 let mut subjects = Vec::with_capacity(self.elements.len() + self.relations.len());
188 subjects.extend(self.elements.iter().copied().map(PropertySubject::Element));
189 subjects.extend(
190 self.relations
191 .iter()
192 .copied()
193 .map(PropertySubject::Relation),
194 );
195 subjects.sort_unstable();
196 subjects
197 }
198}
199
200struct GraphProjectionBuilder {
202 definition: GraphProjectionDefinition,
204 elements: Vec<ElementId>,
206 element_index: BTreeMap<ElementId, ProjectionElementId>,
208 relations: Vec<RelationId>,
210 relation_index: BTreeMap<RelationId, ProjectionRelationId>,
212 endpoints: Vec<(ProjectionElementId, ProjectionElementId)>,
214}
215
216impl GraphProjectionBuilder {
217 const fn new(definition: GraphProjectionDefinition) -> Self {
219 Self {
220 definition,
221 elements: Vec::new(),
222 element_index: BTreeMap::new(),
223 relations: Vec::new(),
224 relation_index: BTreeMap::new(),
225 endpoints: Vec::new(),
226 }
227 }
228
229 fn push_relation(
231 &mut self,
232 state: &impl StateView,
233 relation: &RelationRecord,
234 ) -> Result<(), DbError> {
235 let (source, target) = graph_endpoints(
236 state,
237 relation.id,
238 self.definition.source_role,
239 self.definition.target_role,
240 )?;
241 let source = intern_element(&mut self.element_index, &mut self.elements, source)?;
242 let target = intern_element(&mut self.element_index, &mut self.elements, target)?;
243 let edge = projection_relation_id(self.relations.len())?;
244 self.relations.push(relation.id);
245 self.relation_index.insert(relation.id, edge);
246 self.endpoints.push((source, target));
247 Ok(())
248 }
249
250 fn finish(self) -> Result<GraphProjection, DbError> {
258 let node_count = self.elements.len();
259 let mut builder = GraphBuilder::<u32, u32>::new();
260 let mut nodes = Vec::with_capacity(node_count);
261 for _element in &self.elements {
262 nodes.push(
263 builder
264 .add_node()
265 .map_err(|_error| DbError::id_overflow(crate::error::IdFamily::Element))?,
266 );
267 }
268 for (source, target) in self.endpoints.iter().copied() {
269 builder
270 .add_edge(nodes[local_index(source)], nodes[local_index(target)])
271 .map_err(|_error| DbError::id_overflow(crate::error::IdFamily::Relation))?;
272 }
273 let forward = builder
274 .freeze()
275 .map_err(|_error| DbError::id_overflow(crate::error::IdFamily::Relation))?;
276 let reverse = forward
277 .transpose()
278 .map_err(|_error| DbError::id_overflow(crate::error::IdFamily::Relation))?;
279
280 let successors = grouped_elements(forward.offsets(), forward.targets(), node_count)?;
286 let out_edges = grouped_relations(forward.offsets(), forward.edge_ids(), node_count)?;
287 let predecessors = grouped_elements(reverse.offsets(), reverse.targets(), node_count)?;
288 let in_edges = grouped_relations(reverse.offsets(), reverse.edge_ids(), node_count)?;
289
290 Ok(GraphProjection {
291 definition: self.definition,
292 elements: self.elements,
293 element_index: self.element_index,
294 relations: self.relations,
295 relation_index: self.relation_index,
296 endpoints: self.endpoints,
297 out_edges,
298 in_edges,
299 successors,
300 predecessors,
301 })
302 }
303}
304
305fn grouped_elements(
314 offsets: &[u32],
315 flat: &[u32],
316 node_count: usize,
317) -> Result<Vec<Vec<ProjectionElementId>>, DbError> {
318 grouped(offsets, flat, node_count, |raw| {
319 Ok(ProjectionElementId(raw))
320 })
321}
322
323fn grouped_relations(
329 offsets: &[u32],
330 flat: &[u32],
331 node_count: usize,
332) -> Result<Vec<Vec<ProjectionRelationId>>, DbError> {
333 grouped(offsets, flat, node_count, |raw| {
334 Ok(ProjectionRelationId(raw))
335 })
336}
337
338fn grouped<T>(
344 offsets: &[u32],
345 flat: &[u32],
346 node_count: usize,
347 map: impl Fn(u32) -> Result<T, DbError>,
348) -> Result<Vec<Vec<T>>, DbError> {
349 let mut rows = Vec::with_capacity(node_count);
350 for node in 0..node_count {
351 let start = usize::try_from(offsets[node])
352 .map_err(|_error| DbError::id_overflow(crate::error::IdFamily::Relation))?;
353 let end = usize::try_from(offsets[node + 1])
354 .map_err(|_error| DbError::id_overflow(crate::error::IdFamily::Relation))?;
355 let slice = flat
356 .get(start..end)
357 .ok_or_else(|| DbError::invalid_projection("csr offset range out of bounds"))?;
358 rows.push(
359 slice
360 .iter()
361 .copied()
362 .map(&map)
363 .collect::<Result<Vec<T>, DbError>>()?,
364 );
365 }
366 Ok(rows)
367}
368
369impl TopologyBase for GraphProjection {
370 type ElementId = ProjectionElementId;
371 type RelationId = ProjectionRelationId;
372}
373
374impl TopologyCounts for GraphProjection {
375 fn element_count(&self) -> usize {
376 self.elements.len()
377 }
378
379 fn relation_count(&self) -> usize {
380 self.relations.len()
381 }
382}
383
384impl DenseElementIndex for GraphProjection {
385 fn element_bound(&self) -> usize {
386 self.elements.len()
387 }
388
389 fn element_index(&self, element: Self::ElementId) -> usize {
390 local_index(element)
391 }
392}
393
394impl DenseRelationIndex for GraphProjection {
395 fn relation_bound(&self) -> usize {
396 self.relations.len()
397 }
398
399 fn relation_index(&self, relation: Self::RelationId) -> usize {
400 local_relation_index(relation)
401 }
402}
403
404impl oxgraph_graph::ContainsElement for GraphProjection {
405 fn contains_element(&self, element: Self::ElementId) -> bool {
406 local_index(element) < self.elements.len()
407 }
408}
409
410impl oxgraph_graph::ContainsRelation for GraphProjection {
411 fn contains_relation(&self, relation: Self::RelationId) -> bool {
412 local_relation_index(relation) < self.relations.len()
413 }
414}
415
416impl oxgraph_graph::CanonicalElementIdentity for GraphProjection {
417 type CanonicalElementId = ElementId;
418
419 fn canonical_element_id(&self, element: Self::ElementId) -> Self::CanonicalElementId {
420 self.elements[local_index(element)]
421 }
422}
423
424impl LocalElementIdentity for GraphProjection {
425 fn local_element_id(&self, canonical: Self::CanonicalElementId) -> Option<Self::ElementId> {
426 self.element_index.get(&canonical).copied()
427 }
428}
429
430impl oxgraph_graph::CanonicalRelationIdentity for GraphProjection {
431 type CanonicalRelationId = RelationId;
432
433 fn canonical_relation_id(&self, relation: Self::RelationId) -> Self::CanonicalRelationId {
434 self.relations[local_relation_index(relation)]
435 }
436}
437
438impl LocalRelationIdentity for GraphProjection {
439 fn local_relation_id(&self, canonical: Self::CanonicalRelationId) -> Option<Self::RelationId> {
440 self.relation_index.get(&canonical).copied()
441 }
442}
443
444impl EdgeSourceGraph for GraphProjection {
445 fn source(&self, edge: Self::RelationId) -> Self::ElementId {
446 self.endpoints[local_relation_index(edge)].0
447 }
448}
449
450impl EdgeTargetGraph for GraphProjection {
451 fn target(&self, edge: Self::RelationId) -> Self::ElementId {
452 self.endpoints[local_relation_index(edge)].1
453 }
454}
455
456impl OutgoingGraph for GraphProjection {
457 type OutEdges<'view>
458 = std::iter::Copied<std::slice::Iter<'view, ProjectionRelationId>>
459 where
460 Self: 'view;
461
462 fn outgoing_edges(&self, node: Self::ElementId) -> Self::OutEdges<'_> {
463 self.out_edges[local_index(node)].iter().copied()
464 }
465}
466
467impl IncomingGraph for GraphProjection {
468 type InEdges<'view>
469 = std::iter::Copied<std::slice::Iter<'view, ProjectionRelationId>>
470 where
471 Self: 'view;
472
473 fn incoming_edges(&self, node: Self::ElementId) -> Self::InEdges<'_> {
474 self.in_edges[local_index(node)].iter().copied()
475 }
476}
477
478impl ElementSuccessors for GraphProjection {
479 type Successors<'view>
480 = std::iter::Copied<std::slice::Iter<'view, ProjectionElementId>>
481 where
482 Self: 'view;
483
484 fn element_successors(&self, element: Self::ElementId) -> Self::Successors<'_> {
485 self.successors[local_index(element)].iter().copied()
486 }
487}
488
489impl ElementPredecessors for GraphProjection {
490 type Predecessors<'view>
491 = std::iter::Copied<std::slice::Iter<'view, ProjectionElementId>>
492 where
493 Self: 'view;
494
495 fn element_predecessors(&self, element: Self::ElementId) -> Self::Predecessors<'_> {
496 self.predecessors[local_index(element)].iter().copied()
497 }
498}
499
500impl OutgoingEdgeCount for GraphProjection {
501 fn out_degree(&self, node: Self::ElementId) -> usize {
502 self.out_edges[local_index(node)].len()
503 }
504}
505
506impl IncomingEdgeCount for GraphProjection {
507 fn in_degree(&self, node: Self::ElementId) -> usize {
508 self.in_edges[local_index(node)].len()
509 }
510}
511
512#[derive(Clone, Debug, Eq, PartialEq)]
522pub struct HypergraphProjection {
523 definition: HypergraphProjectionDefinition,
525 elements: Vec<ElementId>,
527 element_index: BTreeMap<ElementId, ProjectionElementId>,
529 relations: Vec<RelationId>,
531 relation_index: BTreeMap<RelationId, ProjectionRelationId>,
533 incidences: Vec<IncidenceRecord>,
535 incidence_index: BTreeMap<IncidenceId, ProjectionIncidenceId>,
537 relation_incidences: Vec<Vec<ProjectionIncidenceId>>,
539 element_incidences: Vec<Vec<ProjectionIncidenceId>>,
541 source_incidences: Vec<Vec<ProjectionIncidenceId>>,
543 target_incidences: Vec<Vec<ProjectionIncidenceId>>,
545 source_vertices: Vec<Vec<ProjectionElementId>>,
547 target_vertices: Vec<Vec<ProjectionElementId>>,
549 outgoing_hyperedges: Vec<Vec<ProjectionRelationId>>,
551 incoming_hyperedges: Vec<Vec<ProjectionRelationId>>,
553 successors: Vec<Vec<ProjectionElementId>>,
555 predecessors: Vec<Vec<ProjectionElementId>>,
557}
558
559impl HypergraphProjection {
560 pub(crate) fn from_state(
572 state: &impl StateView,
573 definition: HypergraphProjectionDefinition,
574 ) -> Result<Self, DbError> {
575 if definition.source_roles.is_empty() || definition.target_roles.is_empty() {
576 return Err(DbError::invalid_projection(
577 "hypergraph projection requires non-empty source and target roles",
578 ));
579 }
580 let mut builder = HypergraphProjectionBuilder::new(definition);
581 for relation in state.relations() {
582 if relation_selected(relation.as_ref(), &builder.definition.relation_types) {
583 builder.push_relation(state, relation.as_ref())?;
584 }
585 }
586 builder.finish()
587 }
588
589 #[must_use]
595 pub const fn definition(&self) -> &HypergraphProjectionDefinition {
596 &self.definition
597 }
598
599 pub(crate) fn subjects(&self) -> Vec<PropertySubject> {
601 let mut subjects =
602 Vec::with_capacity(self.elements.len() + self.relations.len() + self.incidences.len());
603 subjects.extend(self.elements.iter().copied().map(PropertySubject::Element));
604 subjects.extend(
605 self.relations
606 .iter()
607 .copied()
608 .map(PropertySubject::Relation),
609 );
610 subjects.extend(
611 self.incidences
612 .iter()
613 .map(|record| PropertySubject::Incidence(record.id)),
614 );
615 subjects.sort_unstable();
616 subjects
617 }
618}
619
620struct HypergraphProjectionBuilder {
622 definition: HypergraphProjectionDefinition,
624 elements: Vec<ElementId>,
626 element_index: BTreeMap<ElementId, ProjectionElementId>,
628 relations: Vec<RelationId>,
630 relation_index: BTreeMap<RelationId, ProjectionRelationId>,
632 incidences: Vec<IncidenceRecord>,
634 incidence_index: BTreeMap<IncidenceId, ProjectionIncidenceId>,
636 relation_incidences: Vec<Vec<ProjectionIncidenceId>>,
638 source_incidences: Vec<Vec<ProjectionIncidenceId>>,
640 target_incidences: Vec<Vec<ProjectionIncidenceId>>,
642 source_vertices: Vec<Vec<ProjectionElementId>>,
644 target_vertices: Vec<Vec<ProjectionElementId>>,
646}
647
648impl HypergraphProjectionBuilder {
649 const fn new(definition: HypergraphProjectionDefinition) -> Self {
651 Self {
652 definition,
653 elements: Vec::new(),
654 element_index: BTreeMap::new(),
655 relations: Vec::new(),
656 relation_index: BTreeMap::new(),
657 incidences: Vec::new(),
658 incidence_index: BTreeMap::new(),
659 relation_incidences: Vec::new(),
660 source_incidences: Vec::new(),
661 target_incidences: Vec::new(),
662 source_vertices: Vec::new(),
663 target_vertices: Vec::new(),
664 }
665 }
666
667 fn push_relation(
669 &mut self,
670 state: &impl StateView,
671 relation: &RelationRecord,
672 ) -> Result<(), DbError> {
673 let hyperedge = projection_relation_id(self.relations.len())?;
674 self.relations.push(relation.id);
675 self.relation_index.insert(relation.id, hyperedge);
676 let mut relation_ids = Vec::new();
677 let mut source_ids = Vec::new();
678 let mut target_ids = Vec::new();
679 let mut sources = Vec::new();
680 let mut targets = Vec::new();
681 for incidence in state.relation_incidences(relation.id) {
682 let local = self.push_incidence(&incidence)?;
683 let vertex = intern_element(
684 &mut self.element_index,
685 &mut self.elements,
686 incidence.element,
687 )?;
688 relation_ids.push(local);
689 if self.definition.source_roles.contains(&incidence.role) {
690 source_ids.push(local);
691 sources.push(vertex);
692 }
693 if self.definition.target_roles.contains(&incidence.role) {
694 target_ids.push(local);
695 targets.push(vertex);
696 }
697 }
698 if sources.is_empty() || targets.is_empty() {
699 return Err(DbError::invalid_projection(
700 "selected hyperedge lacks source or target participants",
701 ));
702 }
703 self.relation_incidences.push(relation_ids);
704 self.source_incidences.push(source_ids);
705 self.target_incidences.push(target_ids);
706 self.source_vertices.push(sources);
707 self.target_vertices.push(targets);
708 Ok(())
709 }
710
711 fn push_incidence(
713 &mut self,
714 incidence: &IncidenceRecord,
715 ) -> Result<ProjectionIncidenceId, DbError> {
716 if let Some(id) = self.incidence_index.get(&incidence.id) {
717 return Ok(*id);
718 }
719 let local = projection_incidence_id(self.incidences.len())?;
720 self.incidences.push(*incidence);
721 self.incidence_index.insert(incidence.id, local);
722 Ok(local)
723 }
724
725 fn finish(self) -> Result<HypergraphProjection, DbError> {
727 let element_incidences =
728 build_element_incidences(self.elements.len(), &self.incidences, &self.element_index)?;
729 let (outgoing_hyperedges, incoming_hyperedges, successors, predecessors) =
730 build_directed_vertex_arrays(
731 self.elements.len(),
732 &self.source_vertices,
733 &self.target_vertices,
734 )?;
735 Ok(HypergraphProjection {
736 definition: self.definition,
737 elements: self.elements,
738 element_index: self.element_index,
739 relations: self.relations,
740 relation_index: self.relation_index,
741 incidences: self.incidences,
742 incidence_index: self.incidence_index,
743 relation_incidences: self.relation_incidences,
744 element_incidences,
745 source_incidences: self.source_incidences,
746 target_incidences: self.target_incidences,
747 source_vertices: self.source_vertices,
748 target_vertices: self.target_vertices,
749 outgoing_hyperedges,
750 incoming_hyperedges,
751 successors,
752 predecessors,
753 })
754 }
755}
756
757impl TopologyBase for HypergraphProjection {
758 type ElementId = ProjectionElementId;
759 type RelationId = ProjectionRelationId;
760}
761
762impl IncidenceBase for HypergraphProjection {
763 type IncidenceId = ProjectionIncidenceId;
764 type Role = RoleId;
765}
766
767impl TopologyCounts for HypergraphProjection {
768 fn element_count(&self) -> usize {
769 self.elements.len()
770 }
771
772 fn relation_count(&self) -> usize {
773 self.relations.len()
774 }
775}
776
777impl IncidenceCounts for HypergraphProjection {
778 fn incidence_count(&self) -> usize {
779 self.incidences.len()
780 }
781}
782
783impl DenseElementIndex for HypergraphProjection {
784 fn element_bound(&self) -> usize {
785 self.elements.len()
786 }
787
788 fn element_index(&self, element: Self::ElementId) -> usize {
789 local_index(element)
790 }
791}
792
793impl DenseRelationIndex for HypergraphProjection {
794 fn relation_bound(&self) -> usize {
795 self.relations.len()
796 }
797
798 fn relation_index(&self, relation: Self::RelationId) -> usize {
799 local_relation_index(relation)
800 }
801}
802
803impl DenseIncidenceIndex for HypergraphProjection {
804 fn incidence_bound(&self) -> usize {
805 self.incidences.len()
806 }
807
808 fn incidence_index(&self, incidence: Self::IncidenceId) -> usize {
809 local_incidence_index(incidence)
810 }
811}
812
813impl oxgraph_hyper::ContainsElement for HypergraphProjection {
814 fn contains_element(&self, element: Self::ElementId) -> bool {
815 local_index(element) < self.elements.len()
816 }
817}
818
819impl oxgraph_hyper::ContainsRelation for HypergraphProjection {
820 fn contains_relation(&self, relation: Self::RelationId) -> bool {
821 local_relation_index(relation) < self.relations.len()
822 }
823}
824
825impl oxgraph_hyper::ContainsIncidence for HypergraphProjection {
826 fn contains_incidence(&self, incidence: Self::IncidenceId) -> bool {
827 local_incidence_index(incidence) < self.incidences.len()
828 }
829}
830
831impl oxgraph_hyper::CanonicalElementIdentity for HypergraphProjection {
832 type CanonicalElementId = ElementId;
833
834 fn canonical_element_id(&self, element: Self::ElementId) -> Self::CanonicalElementId {
835 self.elements[local_index(element)]
836 }
837}
838
839impl oxgraph_hyper::LocalElementIdentity for HypergraphProjection {
840 fn local_element_id(&self, canonical: Self::CanonicalElementId) -> Option<Self::ElementId> {
841 self.element_index.get(&canonical).copied()
842 }
843}
844
845impl oxgraph_hyper::CanonicalRelationIdentity for HypergraphProjection {
846 type CanonicalRelationId = RelationId;
847
848 fn canonical_relation_id(&self, relation: Self::RelationId) -> Self::CanonicalRelationId {
849 self.relations[local_relation_index(relation)]
850 }
851}
852
853impl oxgraph_hyper::LocalRelationIdentity for HypergraphProjection {
854 fn local_relation_id(&self, canonical: Self::CanonicalRelationId) -> Option<Self::RelationId> {
855 self.relation_index.get(&canonical).copied()
856 }
857}
858
859impl oxgraph_hyper::CanonicalIncidenceIdentity for HypergraphProjection {
860 type CanonicalIncidenceId = IncidenceId;
861
862 fn canonical_incidence_id(&self, incidence: Self::IncidenceId) -> Self::CanonicalIncidenceId {
863 self.incidences[local_incidence_index(incidence)].id
864 }
865}
866
867impl oxgraph_hyper::LocalIncidenceIdentity for HypergraphProjection {
868 fn local_incidence_id(
869 &self,
870 canonical: Self::CanonicalIncidenceId,
871 ) -> Option<Self::IncidenceId> {
872 self.incidence_index.get(&canonical).copied()
873 }
874}
875
876impl RelationIncidences for HypergraphProjection {
877 type Incidences<'view>
878 = std::iter::Copied<std::slice::Iter<'view, ProjectionIncidenceId>>
879 where
880 Self: 'view;
881
882 fn relation_incidences(&self, relation: Self::RelationId) -> Self::Incidences<'_> {
883 self.relation_incidences[local_relation_index(relation)]
884 .iter()
885 .copied()
886 }
887}
888
889impl oxgraph_hyper::ElementIncidences for HypergraphProjection {
890 type Incidences<'view>
891 = std::iter::Copied<std::slice::Iter<'view, ProjectionIncidenceId>>
892 where
893 Self: 'view;
894
895 fn element_incidences(&self, element: Self::ElementId) -> Self::Incidences<'_> {
896 self.element_incidences[local_index(element)]
897 .iter()
898 .copied()
899 }
900}
901
902impl IncidenceElement for HypergraphProjection {
903 fn incidence_element(&self, incidence: Self::IncidenceId) -> Self::ElementId {
904 let canonical = self.incidences[local_incidence_index(incidence)].element;
905 self.element_index[&canonical]
906 }
907}
908
909impl IncidenceRelation for HypergraphProjection {
910 fn incidence_relation(&self, incidence: Self::IncidenceId) -> Self::RelationId {
911 let canonical = self.incidences[local_incidence_index(incidence)].relation;
912 self.relation_index[&canonical]
913 }
914}
915
916impl IncidenceRole for HypergraphProjection {
917 fn incidence_role(&self, incidence: Self::IncidenceId) -> Self::Role {
918 self.incidences[local_incidence_index(incidence)].role
919 }
920}
921
922impl RelationIncidenceCount for HypergraphProjection {
923 fn relation_incidence_count(&self, relation: Self::RelationId) -> usize {
924 self.relation_incidences[local_relation_index(relation)].len()
925 }
926}
927
928impl ElementIncidenceCount for HypergraphProjection {
929 fn element_incidence_count(&self, element: Self::ElementId) -> usize {
930 self.element_incidences[local_index(element)].len()
931 }
932}
933
934impl oxgraph_hyper::HyperedgeParticipants for HypergraphProjection {
935 type Participants<'view>
936 = HyperedgeParticipants<'view>
937 where
938 Self: 'view;
939
940 fn hyperedge_participants(&self, hyperedge: Self::RelationId) -> Self::Participants<'_> {
941 HyperedgeParticipants {
942 view: self,
943 inner: self.relation_incidences[local_relation_index(hyperedge)].iter(),
944 }
945 }
946}
947
948impl IncidentHyperedges for HypergraphProjection {
949 type IncidentHyperedges<'view>
950 = IncidentHyperedgesIter<'view>
951 where
952 Self: 'view;
953
954 fn incident_hyperedges(&self, vertex: Self::ElementId) -> Self::IncidentHyperedges<'_> {
955 IncidentHyperedgesIter {
956 view: self,
957 inner: self.element_incidences[local_index(vertex)].iter(),
958 }
959 }
960}
961
962impl DirectedHyperedgeParticipants for HypergraphProjection {
967 type SourceParticipants<'view>
968 = std::iter::Copied<std::slice::Iter<'view, ProjectionElementId>>
969 where
970 Self: 'view;
971
972 type TargetParticipants<'view>
973 = std::iter::Copied<std::slice::Iter<'view, ProjectionElementId>>
974 where
975 Self: 'view;
976
977 fn source_participants(&self, hyperedge: Self::RelationId) -> Self::SourceParticipants<'_> {
978 self.source_vertices[local_relation_index(hyperedge)]
979 .iter()
980 .copied()
981 }
982
983 fn target_participants(&self, hyperedge: Self::RelationId) -> Self::TargetParticipants<'_> {
984 self.target_vertices[local_relation_index(hyperedge)]
985 .iter()
986 .copied()
987 }
988}
989
990impl DirectedHyperedgeIncidences for HypergraphProjection {
991 type SourceIncidences<'view>
992 = std::iter::Copied<std::slice::Iter<'view, ProjectionIncidenceId>>
993 where
994 Self: 'view;
995
996 type TargetIncidences<'view>
997 = std::iter::Copied<std::slice::Iter<'view, ProjectionIncidenceId>>
998 where
999 Self: 'view;
1000
1001 fn source_incidences(&self, hyperedge: Self::RelationId) -> Self::SourceIncidences<'_> {
1002 self.source_incidences[local_relation_index(hyperedge)]
1003 .iter()
1004 .copied()
1005 }
1006
1007 fn target_incidences(&self, hyperedge: Self::RelationId) -> Self::TargetIncidences<'_> {
1008 self.target_incidences[local_relation_index(hyperedge)]
1009 .iter()
1010 .copied()
1011 }
1012}
1013
1014impl DirectedVertexHyperedges for HypergraphProjection {
1015 type OutgoingHyperedges<'view>
1016 = std::iter::Copied<std::slice::Iter<'view, ProjectionRelationId>>
1017 where
1018 Self: 'view;
1019
1020 type IncomingHyperedges<'view>
1021 = std::iter::Copied<std::slice::Iter<'view, ProjectionRelationId>>
1022 where
1023 Self: 'view;
1024
1025 fn outgoing_hyperedges(&self, vertex: Self::ElementId) -> Self::OutgoingHyperedges<'_> {
1026 self.outgoing_hyperedges[local_index(vertex)]
1027 .iter()
1028 .copied()
1029 }
1030
1031 fn incoming_hyperedges(&self, vertex: Self::ElementId) -> Self::IncomingHyperedges<'_> {
1032 self.incoming_hyperedges[local_index(vertex)]
1033 .iter()
1034 .copied()
1035 }
1036}
1037
1038impl ElementSuccessors for HypergraphProjection {
1039 type Successors<'view>
1040 = std::iter::Copied<std::slice::Iter<'view, ProjectionElementId>>
1041 where
1042 Self: 'view;
1043
1044 fn element_successors(&self, element: Self::ElementId) -> Self::Successors<'_> {
1045 self.successors[local_index(element)].iter().copied()
1046 }
1047}
1048
1049impl ElementPredecessors for HypergraphProjection {
1050 type Predecessors<'view>
1051 = std::iter::Copied<std::slice::Iter<'view, ProjectionElementId>>
1052 where
1053 Self: 'view;
1054
1055 fn element_predecessors(&self, element: Self::ElementId) -> Self::Predecessors<'_> {
1056 self.predecessors[local_index(element)].iter().copied()
1057 }
1058}
1059
1060pub struct HyperedgeParticipants<'view> {
1062 view: &'view HypergraphProjection,
1064 inner: std::slice::Iter<'view, ProjectionIncidenceId>,
1066}
1067
1068impl Iterator for HyperedgeParticipants<'_> {
1069 type Item = ProjectionElementId;
1070
1071 fn next(&mut self) -> Option<Self::Item> {
1072 self.inner
1073 .next()
1074 .map(|incidence| self.view.incidence_element(*incidence))
1075 }
1076}
1077
1078pub struct IncidentHyperedgesIter<'view> {
1080 view: &'view HypergraphProjection,
1082 inner: std::slice::Iter<'view, ProjectionIncidenceId>,
1084}
1085
1086impl Iterator for IncidentHyperedgesIter<'_> {
1087 type Item = ProjectionRelationId;
1088
1089 fn next(&mut self) -> Option<Self::Item> {
1090 self.inner
1091 .next()
1092 .map(|incidence| self.view.incidence_relation(*incidence))
1093 }
1094}
1095
1096fn relation_selected(record: &RelationRecord, selected: &BTreeSet<RelationTypeId>) -> bool {
1098 record
1099 .relation_type
1100 .map_or(selected.is_empty(), |relation_type| {
1101 selected.is_empty() || selected.contains(&relation_type)
1102 })
1103}
1104
1105fn graph_endpoints(
1107 state: &impl StateView,
1108 relation: RelationId,
1109 source_role: RoleId,
1110 target_role: RoleId,
1111) -> Result<(ElementId, ElementId), DbError> {
1112 let mut source = None;
1113 let mut target = None;
1114 for incidence in state.relation_incidences(relation) {
1115 update_endpoint(&mut source, &incidence, source_role, "source")?;
1116 update_endpoint(&mut target, &incidence, target_role, "target")?;
1117 }
1118 match (source, target) {
1119 (Some(source), Some(target)) => Ok((source, target)),
1120 _missing => Err(DbError::invalid_projection(
1121 "selected graph relation lacks source or target endpoint",
1122 )),
1123 }
1124}
1125
1126fn update_endpoint(
1128 slot: &mut Option<ElementId>,
1129 incidence: &IncidenceRecord,
1130 role: RoleId,
1131 name: &'static str,
1132) -> Result<(), DbError> {
1133 if incidence.role != role {
1134 return Ok(());
1135 }
1136 if slot.replace(incidence.element).is_some() {
1137 return Err(DbError::invalid_projection(format!(
1138 "selected graph relation has duplicate {name} endpoint"
1139 )));
1140 }
1141 Ok(())
1142}
1143
1144fn intern_element(
1146 index: &mut BTreeMap<ElementId, ProjectionElementId>,
1147 elements: &mut Vec<ElementId>,
1148 canonical: ElementId,
1149) -> Result<ProjectionElementId, DbError> {
1150 if let Some(local) = index.get(&canonical) {
1151 return Ok(*local);
1152 }
1153 let local = projection_element_id(elements.len())?;
1154 elements.push(canonical);
1155 index.insert(canonical, local);
1156 Ok(local)
1157}
1158
1159fn build_element_incidences(
1161 element_count: usize,
1162 incidences: &[IncidenceRecord],
1163 element_index: &BTreeMap<ElementId, ProjectionElementId>,
1164) -> Result<Vec<Vec<ProjectionIncidenceId>>, DbError> {
1165 let mut rows = vec![Vec::new(); element_count];
1166 for (index, incidence) in incidences.iter().enumerate() {
1167 let local = projection_incidence_id(index)?;
1168 let element = element_index
1169 .get(&incidence.element)
1170 .ok_or_else(|| DbError::invalid_projection("incidence element not projected"))?;
1171 rows[local_index(*element)].push(local);
1172 }
1173 Ok(rows)
1174}
1175
1176fn build_directed_vertex_arrays(
1188 element_count: usize,
1189 source_vertices: &[Vec<ProjectionElementId>],
1190 target_vertices: &[Vec<ProjectionElementId>],
1191) -> Result<DirectedVertexArrays, DbError> {
1192 let mut builder = HypergraphBuilder::<u32, u32, u32>::new();
1193 let mut vertices = Vec::with_capacity(element_count);
1194 for _element in 0..element_count {
1195 vertices.push(
1196 builder
1197 .add_vertex()
1198 .map_err(|_error| DbError::id_overflow(crate::error::IdFamily::Element))?,
1199 );
1200 }
1201 for (sources, targets) in source_vertices.iter().zip(target_vertices) {
1202 let source_vertices = unique_vertices(&vertices, sources);
1203 let target_vertices = unique_vertices(&vertices, targets);
1204 builder
1205 .add_hyperedge(&source_vertices, &target_vertices)
1206 .map_err(|_error| {
1207 DbError::invalid_projection("hyperedge participants are not directed-unique")
1208 })?;
1209 }
1210 let frozen = builder
1211 .freeze()
1212 .map_err(|_error| DbError::id_overflow(crate::error::IdFamily::Incidence))?;
1213
1214 let mut outgoing = Vec::with_capacity(element_count);
1215 let mut incoming = Vec::with_capacity(element_count);
1216 let mut successors = Vec::with_capacity(element_count);
1217 let mut predecessors = Vec::with_capacity(element_count);
1218 for vertex in vertices.iter().copied() {
1219 outgoing.push(
1220 DirectedVertexHyperedges::outgoing_hyperedges(&frozen, vertex)
1221 .map(|hyperedge| ProjectionRelationId(hyperedge.get()))
1222 .collect(),
1223 );
1224 incoming.push(
1225 DirectedVertexHyperedges::incoming_hyperedges(&frozen, vertex)
1226 .map(|hyperedge| ProjectionRelationId(hyperedge.get()))
1227 .collect(),
1228 );
1229 successors.push(unique_sorted(
1230 frozen
1231 .element_successors(vertex)
1232 .map(|target| ProjectionElementId(target.get())),
1233 ));
1234 predecessors.push(unique_sorted(
1235 frozen
1236 .element_predecessors(vertex)
1237 .map(|source| ProjectionElementId(source.get())),
1238 ));
1239 }
1240 Ok((outgoing, incoming, successors, predecessors))
1241}
1242
1243fn unique_vertices(
1253 vertices: &[HyperVertexId<u32>],
1254 locals: &[ProjectionElementId],
1255) -> Vec<HyperVertexId<u32>> {
1256 let mut seen = BTreeSet::new();
1257 locals
1258 .iter()
1259 .filter(|local| seen.insert(**local))
1260 .map(|local| vertices[local_index(*local)])
1261 .collect()
1262}
1263
1264fn unique_sorted(elements: impl Iterator<Item = ProjectionElementId>) -> Vec<ProjectionElementId> {
1270 elements.collect::<BTreeSet<_>>().into_iter().collect()
1271}
1272
1273type DirectedVertexArrays = (
1275 Vec<Vec<ProjectionRelationId>>,
1276 Vec<Vec<ProjectionRelationId>>,
1277 Vec<Vec<ProjectionElementId>>,
1278 Vec<Vec<ProjectionElementId>>,
1279);
1280
1281fn projection_element_id(index: usize) -> Result<ProjectionElementId, DbError> {
1283 u32::try_from(index)
1284 .map(ProjectionElementId)
1285 .map_err(|_error| DbError::id_overflow(crate::error::IdFamily::Element))
1286}
1287
1288fn projection_relation_id(index: usize) -> Result<ProjectionRelationId, DbError> {
1290 u32::try_from(index)
1291 .map(ProjectionRelationId)
1292 .map_err(|_error| DbError::id_overflow(crate::error::IdFamily::Element))
1293}
1294
1295fn projection_incidence_id(index: usize) -> Result<ProjectionIncidenceId, DbError> {
1297 u32::try_from(index)
1298 .map(ProjectionIncidenceId)
1299 .map_err(|_error| DbError::id_overflow(crate::error::IdFamily::Element))
1300}
1301
1302fn local_index(id: ProjectionElementId) -> usize {
1304 usize::try_from(id.0).unwrap_or(usize::MAX)
1305}
1306
1307fn local_relation_index(id: ProjectionRelationId) -> usize {
1309 usize::try_from(id.0).unwrap_or(usize::MAX)
1310}
1311
1312fn local_incidence_index(id: ProjectionIncidenceId) -> usize {
1314 usize::try_from(id.0).unwrap_or(usize::MAX)
1315}