1use std::collections::{BTreeMap, BTreeSet};
4
5use oxgraph_csr::build::GraphBuilder;
6use oxgraph_graph::{
7 EdgeSourceGraph, EdgeTargetGraph, ElementIndex, ElementPredecessors, ElementSuccessors,
8 GraphCounts, IncomingEdgeCount, IncomingGraph, LocalElementIdentity, LocalRelationIdentity,
9 OutgoingEdgeCount, OutgoingGraph, RelationIndex, TopologyBase, TopologyCounts,
10};
11use oxgraph_hyper::{
12 DirectedHyperedgeIncidences, DirectedHyperedgeParticipants, DirectedVertexHyperedges,
13 ElementIncidenceCount, HypergraphCounts, IncidenceBase, IncidenceCounts, IncidenceElement,
14 IncidenceIndex, 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 state::{DatabaseState, IncidenceRecord, PropertySubject, RelationRecord},
24};
25
26#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
32pub struct ProjectionElementId(u32);
33
34impl ProjectionElementId {
35 #[must_use]
41 pub const fn new(value: u32) -> Self {
42 Self(value)
43 }
44
45 #[must_use]
51 pub const fn get(self) -> u32 {
52 self.0
53 }
54}
55
56#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
62pub struct ProjectionRelationId(u32);
63
64impl ProjectionRelationId {
65 #[must_use]
71 pub const fn new(value: u32) -> Self {
72 Self(value)
73 }
74
75 #[must_use]
81 pub const fn get(self) -> u32 {
82 self.0
83 }
84}
85
86#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
92pub struct ProjectionIncidenceId(u32);
93
94impl ProjectionIncidenceId {
95 #[must_use]
101 pub const fn new(value: u32) -> Self {
102 Self(value)
103 }
104
105 #[must_use]
111 pub const fn get(self) -> u32 {
112 self.0
113 }
114}
115
116#[derive(Clone, Debug, Eq, PartialEq)]
126pub struct GraphProjection {
127 definition: GraphProjectionDefinition,
129 elements: Vec<ElementId>,
131 element_index: BTreeMap<ElementId, ProjectionElementId>,
133 relations: Vec<RelationId>,
135 relation_index: BTreeMap<RelationId, ProjectionRelationId>,
137 endpoints: Vec<(ProjectionElementId, ProjectionElementId)>,
139 out_edges: Vec<Vec<ProjectionRelationId>>,
141 in_edges: Vec<Vec<ProjectionRelationId>>,
143 successors: Vec<Vec<ProjectionElementId>>,
145 predecessors: Vec<Vec<ProjectionElementId>>,
147}
148
149impl GraphProjection {
150 pub(crate) fn from_state(
162 state: &DatabaseState,
163 definition: GraphProjectionDefinition,
164 ) -> Result<Self, DbError> {
165 let mut builder = GraphProjectionBuilder::new(definition);
166 for relation in state.relations() {
167 if relation_selected(relation, &builder.definition.relation_types) {
168 builder.push_relation(state, relation)?;
169 }
170 }
171 builder.finish()
172 }
173
174 #[must_use]
180 pub const fn definition(&self) -> &GraphProjectionDefinition {
181 &self.definition
182 }
183
184 pub(crate) fn subjects(&self) -> Vec<PropertySubject> {
186 let mut subjects = Vec::with_capacity(self.elements.len() + self.relations.len());
187 subjects.extend(self.elements.iter().copied().map(PropertySubject::Element));
188 subjects.extend(
189 self.relations
190 .iter()
191 .copied()
192 .map(PropertySubject::Relation),
193 );
194 subjects.sort_unstable();
195 subjects
196 }
197}
198
199struct GraphProjectionBuilder {
201 definition: GraphProjectionDefinition,
203 elements: Vec<ElementId>,
205 element_index: BTreeMap<ElementId, ProjectionElementId>,
207 relations: Vec<RelationId>,
209 relation_index: BTreeMap<RelationId, ProjectionRelationId>,
211 endpoints: Vec<(ProjectionElementId, ProjectionElementId)>,
213}
214
215impl GraphProjectionBuilder {
216 const fn new(definition: GraphProjectionDefinition) -> Self {
218 Self {
219 definition,
220 elements: Vec::new(),
221 element_index: BTreeMap::new(),
222 relations: Vec::new(),
223 relation_index: BTreeMap::new(),
224 endpoints: Vec::new(),
225 }
226 }
227
228 fn push_relation(
230 &mut self,
231 state: &DatabaseState,
232 relation: &RelationRecord,
233 ) -> Result<(), DbError> {
234 let (source, target) = graph_endpoints(
235 state,
236 relation.id,
237 self.definition.source_role,
238 self.definition.target_role,
239 )?;
240 let source = intern_element(&mut self.element_index, &mut self.elements, source)?;
241 let target = intern_element(&mut self.element_index, &mut self.elements, target)?;
242 let edge = projection_relation_id(self.relations.len())?;
243 self.relations.push(relation.id);
244 self.relation_index.insert(relation.id, edge);
245 self.endpoints.push((source, target));
246 Ok(())
247 }
248
249 fn finish(self) -> Result<GraphProjection, DbError> {
257 let node_count = self.elements.len();
258 let mut builder = GraphBuilder::<u32, u32>::new();
259 let mut nodes = Vec::with_capacity(node_count);
260 for _element in &self.elements {
261 nodes.push(builder.add_node().map_err(|_error| DbError::IdOverflow)?);
262 }
263 for (source, target) in self.endpoints.iter().copied() {
264 builder
265 .add_edge(nodes[local_index(source)], nodes[local_index(target)])
266 .map_err(|_error| DbError::IdOverflow)?;
267 }
268 let forward = builder.freeze().map_err(|_error| DbError::IdOverflow)?;
269 let reverse = forward.transpose().map_err(|_error| DbError::IdOverflow)?;
270
271 let successors = grouped_elements(forward.offsets(), forward.targets(), node_count)?;
277 let out_edges = grouped_relations(forward.offsets(), forward.edge_ids(), node_count)?;
278 let predecessors = grouped_elements(reverse.offsets(), reverse.targets(), node_count)?;
279 let in_edges = grouped_relations(reverse.offsets(), reverse.edge_ids(), node_count)?;
280
281 Ok(GraphProjection {
282 definition: self.definition,
283 elements: self.elements,
284 element_index: self.element_index,
285 relations: self.relations,
286 relation_index: self.relation_index,
287 endpoints: self.endpoints,
288 out_edges,
289 in_edges,
290 successors,
291 predecessors,
292 })
293 }
294}
295
296fn grouped_elements(
305 offsets: &[u32],
306 flat: &[u32],
307 node_count: usize,
308) -> Result<Vec<Vec<ProjectionElementId>>, DbError> {
309 grouped(offsets, flat, node_count, |raw| {
310 Ok(ProjectionElementId(raw))
311 })
312}
313
314fn grouped_relations(
320 offsets: &[u32],
321 flat: &[u32],
322 node_count: usize,
323) -> Result<Vec<Vec<ProjectionRelationId>>, DbError> {
324 grouped(offsets, flat, node_count, |raw| {
325 Ok(ProjectionRelationId(raw))
326 })
327}
328
329fn grouped<T>(
335 offsets: &[u32],
336 flat: &[u32],
337 node_count: usize,
338 map: impl Fn(u32) -> Result<T, DbError>,
339) -> Result<Vec<Vec<T>>, DbError> {
340 let mut rows = Vec::with_capacity(node_count);
341 for node in 0..node_count {
342 let start = usize::try_from(offsets[node]).map_err(|_error| DbError::IdOverflow)?;
343 let end = usize::try_from(offsets[node + 1]).map_err(|_error| DbError::IdOverflow)?;
344 let slice = flat
345 .get(start..end)
346 .ok_or_else(|| DbError::invalid_projection("csr offset range out of bounds"))?;
347 rows.push(
348 slice
349 .iter()
350 .copied()
351 .map(&map)
352 .collect::<Result<Vec<T>, DbError>>()?,
353 );
354 }
355 Ok(rows)
356}
357
358impl TopologyBase for GraphProjection {
359 type ElementId = ProjectionElementId;
360 type RelationId = ProjectionRelationId;
361}
362
363impl TopologyCounts for GraphProjection {
364 fn element_count(&self) -> usize {
365 self.elements.len()
366 }
367
368 fn relation_count(&self) -> usize {
369 self.relations.len()
370 }
371}
372
373impl GraphCounts for GraphProjection {}
374
375impl ElementIndex for GraphProjection {
376 fn element_bound(&self) -> usize {
377 self.elements.len()
378 }
379
380 fn element_index(&self, element: Self::ElementId) -> usize {
381 local_index(element)
382 }
383}
384
385impl RelationIndex for GraphProjection {
386 fn relation_bound(&self) -> usize {
387 self.relations.len()
388 }
389
390 fn relation_index(&self, relation: Self::RelationId) -> usize {
391 local_relation_index(relation)
392 }
393}
394
395impl oxgraph_graph::ContainsElement for GraphProjection {
396 fn contains_element(&self, element: Self::ElementId) -> bool {
397 local_index(element) < self.elements.len()
398 }
399}
400
401impl oxgraph_graph::ContainsRelation for GraphProjection {
402 fn contains_relation(&self, relation: Self::RelationId) -> bool {
403 local_relation_index(relation) < self.relations.len()
404 }
405}
406
407impl oxgraph_graph::CanonicalElementIdentity for GraphProjection {
408 type CanonicalElementId = ElementId;
409
410 fn canonical_element_id(&self, element: Self::ElementId) -> Self::CanonicalElementId {
411 self.elements[local_index(element)]
412 }
413}
414
415impl LocalElementIdentity for GraphProjection {
416 fn local_element_id(&self, canonical: Self::CanonicalElementId) -> Option<Self::ElementId> {
417 self.element_index.get(&canonical).copied()
418 }
419}
420
421impl oxgraph_graph::CanonicalRelationIdentity for GraphProjection {
422 type CanonicalRelationId = RelationId;
423
424 fn canonical_relation_id(&self, relation: Self::RelationId) -> Self::CanonicalRelationId {
425 self.relations[local_relation_index(relation)]
426 }
427}
428
429impl LocalRelationIdentity for GraphProjection {
430 fn local_relation_id(&self, canonical: Self::CanonicalRelationId) -> Option<Self::RelationId> {
431 self.relation_index.get(&canonical).copied()
432 }
433}
434
435impl EdgeSourceGraph for GraphProjection {
436 fn source(&self, edge: Self::RelationId) -> Self::ElementId {
437 self.endpoints[local_relation_index(edge)].0
438 }
439}
440
441impl EdgeTargetGraph for GraphProjection {
442 fn target(&self, edge: Self::RelationId) -> Self::ElementId {
443 self.endpoints[local_relation_index(edge)].1
444 }
445}
446
447impl OutgoingGraph for GraphProjection {
448 type OutEdges<'view>
449 = std::iter::Copied<std::slice::Iter<'view, ProjectionRelationId>>
450 where
451 Self: 'view;
452
453 fn outgoing_edges(&self, node: Self::ElementId) -> Self::OutEdges<'_> {
454 self.out_edges[local_index(node)].iter().copied()
455 }
456}
457
458impl IncomingGraph for GraphProjection {
459 type InEdges<'view>
460 = std::iter::Copied<std::slice::Iter<'view, ProjectionRelationId>>
461 where
462 Self: 'view;
463
464 fn incoming_edges(&self, node: Self::ElementId) -> Self::InEdges<'_> {
465 self.in_edges[local_index(node)].iter().copied()
466 }
467}
468
469impl ElementSuccessors for GraphProjection {
470 type Successors<'view>
471 = std::iter::Copied<std::slice::Iter<'view, ProjectionElementId>>
472 where
473 Self: 'view;
474
475 fn element_successors(&self, element: Self::ElementId) -> Self::Successors<'_> {
476 self.successors[local_index(element)].iter().copied()
477 }
478}
479
480impl ElementPredecessors for GraphProjection {
481 type Predecessors<'view>
482 = std::iter::Copied<std::slice::Iter<'view, ProjectionElementId>>
483 where
484 Self: 'view;
485
486 fn element_predecessors(&self, element: Self::ElementId) -> Self::Predecessors<'_> {
487 self.predecessors[local_index(element)].iter().copied()
488 }
489}
490
491impl OutgoingEdgeCount for GraphProjection {
492 fn out_degree(&self, node: Self::ElementId) -> usize {
493 self.out_edges[local_index(node)].len()
494 }
495}
496
497impl IncomingEdgeCount for GraphProjection {
498 fn in_degree(&self, node: Self::ElementId) -> usize {
499 self.in_edges[local_index(node)].len()
500 }
501}
502
503#[derive(Clone, Debug, Eq, PartialEq)]
513pub struct HypergraphProjection {
514 definition: HypergraphProjectionDefinition,
516 elements: Vec<ElementId>,
518 element_index: BTreeMap<ElementId, ProjectionElementId>,
520 relations: Vec<RelationId>,
522 relation_index: BTreeMap<RelationId, ProjectionRelationId>,
524 incidences: Vec<IncidenceRecord>,
526 incidence_index: BTreeMap<IncidenceId, ProjectionIncidenceId>,
528 relation_incidences: Vec<Vec<ProjectionIncidenceId>>,
530 element_incidences: Vec<Vec<ProjectionIncidenceId>>,
532 source_incidences: Vec<Vec<ProjectionIncidenceId>>,
534 target_incidences: Vec<Vec<ProjectionIncidenceId>>,
536 source_vertices: Vec<Vec<ProjectionElementId>>,
538 target_vertices: Vec<Vec<ProjectionElementId>>,
540 outgoing_hyperedges: Vec<Vec<ProjectionRelationId>>,
542 incoming_hyperedges: Vec<Vec<ProjectionRelationId>>,
544 successors: Vec<Vec<ProjectionElementId>>,
546 predecessors: Vec<Vec<ProjectionElementId>>,
548}
549
550impl HypergraphProjection {
551 pub(crate) fn from_state(
563 state: &DatabaseState,
564 definition: HypergraphProjectionDefinition,
565 ) -> Result<Self, DbError> {
566 if definition.source_roles.is_empty() || definition.target_roles.is_empty() {
567 return Err(DbError::invalid_projection(
568 "hypergraph projection requires non-empty source and target roles",
569 ));
570 }
571 let mut builder = HypergraphProjectionBuilder::new(definition);
572 for relation in state.relations() {
573 if relation_selected(relation, &builder.definition.relation_types) {
574 builder.push_relation(state, relation)?;
575 }
576 }
577 builder.finish()
578 }
579
580 #[must_use]
586 pub const fn definition(&self) -> &HypergraphProjectionDefinition {
587 &self.definition
588 }
589
590 pub(crate) fn subjects(&self) -> Vec<PropertySubject> {
592 let mut subjects =
593 Vec::with_capacity(self.elements.len() + self.relations.len() + self.incidences.len());
594 subjects.extend(self.elements.iter().copied().map(PropertySubject::Element));
595 subjects.extend(
596 self.relations
597 .iter()
598 .copied()
599 .map(PropertySubject::Relation),
600 );
601 subjects.extend(
602 self.incidences
603 .iter()
604 .map(|record| PropertySubject::Incidence(record.id)),
605 );
606 subjects.sort_unstable();
607 subjects
608 }
609}
610
611struct HypergraphProjectionBuilder {
613 definition: HypergraphProjectionDefinition,
615 elements: Vec<ElementId>,
617 element_index: BTreeMap<ElementId, ProjectionElementId>,
619 relations: Vec<RelationId>,
621 relation_index: BTreeMap<RelationId, ProjectionRelationId>,
623 incidences: Vec<IncidenceRecord>,
625 incidence_index: BTreeMap<IncidenceId, ProjectionIncidenceId>,
627 relation_incidences: Vec<Vec<ProjectionIncidenceId>>,
629 source_incidences: Vec<Vec<ProjectionIncidenceId>>,
631 target_incidences: Vec<Vec<ProjectionIncidenceId>>,
633 source_vertices: Vec<Vec<ProjectionElementId>>,
635 target_vertices: Vec<Vec<ProjectionElementId>>,
637}
638
639impl HypergraphProjectionBuilder {
640 const fn new(definition: HypergraphProjectionDefinition) -> Self {
642 Self {
643 definition,
644 elements: Vec::new(),
645 element_index: BTreeMap::new(),
646 relations: Vec::new(),
647 relation_index: BTreeMap::new(),
648 incidences: Vec::new(),
649 incidence_index: BTreeMap::new(),
650 relation_incidences: Vec::new(),
651 source_incidences: Vec::new(),
652 target_incidences: Vec::new(),
653 source_vertices: Vec::new(),
654 target_vertices: Vec::new(),
655 }
656 }
657
658 fn push_relation(
660 &mut self,
661 state: &DatabaseState,
662 relation: &RelationRecord,
663 ) -> Result<(), DbError> {
664 let hyperedge = projection_relation_id(self.relations.len())?;
665 self.relations.push(relation.id);
666 self.relation_index.insert(relation.id, hyperedge);
667 let mut relation_ids = Vec::new();
668 let mut source_ids = Vec::new();
669 let mut target_ids = Vec::new();
670 let mut sources = Vec::new();
671 let mut targets = Vec::new();
672 for incidence in state.relation_incidences(relation.id) {
673 let local = self.push_incidence(incidence)?;
674 let vertex = intern_element(
675 &mut self.element_index,
676 &mut self.elements,
677 incidence.element,
678 )?;
679 relation_ids.push(local);
680 if self.definition.source_roles.contains(&incidence.role) {
681 source_ids.push(local);
682 sources.push(vertex);
683 }
684 if self.definition.target_roles.contains(&incidence.role) {
685 target_ids.push(local);
686 targets.push(vertex);
687 }
688 }
689 if sources.is_empty() || targets.is_empty() {
690 return Err(DbError::invalid_projection(
691 "selected hyperedge lacks source or target participants",
692 ));
693 }
694 self.relation_incidences.push(relation_ids);
695 self.source_incidences.push(source_ids);
696 self.target_incidences.push(target_ids);
697 self.source_vertices.push(sources);
698 self.target_vertices.push(targets);
699 Ok(())
700 }
701
702 fn push_incidence(
704 &mut self,
705 incidence: &IncidenceRecord,
706 ) -> Result<ProjectionIncidenceId, DbError> {
707 if let Some(id) = self.incidence_index.get(&incidence.id) {
708 return Ok(*id);
709 }
710 let local = projection_incidence_id(self.incidences.len())?;
711 self.incidences.push(*incidence);
712 self.incidence_index.insert(incidence.id, local);
713 Ok(local)
714 }
715
716 fn finish(self) -> Result<HypergraphProjection, DbError> {
718 let element_incidences =
719 build_element_incidences(self.elements.len(), &self.incidences, &self.element_index)?;
720 let (outgoing_hyperedges, incoming_hyperedges, successors, predecessors) =
721 build_directed_vertex_arrays(
722 self.elements.len(),
723 &self.source_vertices,
724 &self.target_vertices,
725 )?;
726 Ok(HypergraphProjection {
727 definition: self.definition,
728 elements: self.elements,
729 element_index: self.element_index,
730 relations: self.relations,
731 relation_index: self.relation_index,
732 incidences: self.incidences,
733 incidence_index: self.incidence_index,
734 relation_incidences: self.relation_incidences,
735 element_incidences,
736 source_incidences: self.source_incidences,
737 target_incidences: self.target_incidences,
738 source_vertices: self.source_vertices,
739 target_vertices: self.target_vertices,
740 outgoing_hyperedges,
741 incoming_hyperedges,
742 successors,
743 predecessors,
744 })
745 }
746}
747
748impl TopologyBase for HypergraphProjection {
749 type ElementId = ProjectionElementId;
750 type RelationId = ProjectionRelationId;
751}
752
753impl IncidenceBase for HypergraphProjection {
754 type IncidenceId = ProjectionIncidenceId;
755 type Role = RoleId;
756}
757
758impl TopologyCounts for HypergraphProjection {
759 fn element_count(&self) -> usize {
760 self.elements.len()
761 }
762
763 fn relation_count(&self) -> usize {
764 self.relations.len()
765 }
766}
767
768impl HypergraphCounts for HypergraphProjection {}
769
770impl IncidenceCounts for HypergraphProjection {
771 fn incidence_count(&self) -> usize {
772 self.incidences.len()
773 }
774}
775
776impl ElementIndex for HypergraphProjection {
777 fn element_bound(&self) -> usize {
778 self.elements.len()
779 }
780
781 fn element_index(&self, element: Self::ElementId) -> usize {
782 local_index(element)
783 }
784}
785
786impl RelationIndex for HypergraphProjection {
787 fn relation_bound(&self) -> usize {
788 self.relations.len()
789 }
790
791 fn relation_index(&self, relation: Self::RelationId) -> usize {
792 local_relation_index(relation)
793 }
794}
795
796impl IncidenceIndex for HypergraphProjection {
797 fn incidence_bound(&self) -> usize {
798 self.incidences.len()
799 }
800
801 fn incidence_index(&self, incidence: Self::IncidenceId) -> usize {
802 local_incidence_index(incidence)
803 }
804}
805
806impl oxgraph_hyper::ContainsElement for HypergraphProjection {
807 fn contains_element(&self, element: Self::ElementId) -> bool {
808 local_index(element) < self.elements.len()
809 }
810}
811
812impl oxgraph_hyper::ContainsRelation for HypergraphProjection {
813 fn contains_relation(&self, relation: Self::RelationId) -> bool {
814 local_relation_index(relation) < self.relations.len()
815 }
816}
817
818impl oxgraph_hyper::ContainsIncidence for HypergraphProjection {
819 fn contains_incidence(&self, incidence: Self::IncidenceId) -> bool {
820 local_incidence_index(incidence) < self.incidences.len()
821 }
822}
823
824impl oxgraph_hyper::CanonicalElementIdentity for HypergraphProjection {
825 type CanonicalElementId = ElementId;
826
827 fn canonical_element_id(&self, element: Self::ElementId) -> Self::CanonicalElementId {
828 self.elements[local_index(element)]
829 }
830}
831
832impl oxgraph_hyper::LocalElementIdentity for HypergraphProjection {
833 fn local_element_id(&self, canonical: Self::CanonicalElementId) -> Option<Self::ElementId> {
834 self.element_index.get(&canonical).copied()
835 }
836}
837
838impl oxgraph_hyper::CanonicalRelationIdentity for HypergraphProjection {
839 type CanonicalRelationId = RelationId;
840
841 fn canonical_relation_id(&self, relation: Self::RelationId) -> Self::CanonicalRelationId {
842 self.relations[local_relation_index(relation)]
843 }
844}
845
846impl oxgraph_hyper::LocalRelationIdentity for HypergraphProjection {
847 fn local_relation_id(&self, canonical: Self::CanonicalRelationId) -> Option<Self::RelationId> {
848 self.relation_index.get(&canonical).copied()
849 }
850}
851
852impl oxgraph_hyper::CanonicalIncidenceIdentity for HypergraphProjection {
853 type CanonicalIncidenceId = IncidenceId;
854
855 fn canonical_incidence_id(&self, incidence: Self::IncidenceId) -> Self::CanonicalIncidenceId {
856 self.incidences[local_incidence_index(incidence)].id
857 }
858}
859
860impl oxgraph_hyper::LocalIncidenceIdentity for HypergraphProjection {
861 fn local_incidence_id(
862 &self,
863 canonical: Self::CanonicalIncidenceId,
864 ) -> Option<Self::IncidenceId> {
865 self.incidence_index.get(&canonical).copied()
866 }
867}
868
869impl RelationIncidences for HypergraphProjection {
870 type Incidences<'view>
871 = std::iter::Copied<std::slice::Iter<'view, ProjectionIncidenceId>>
872 where
873 Self: 'view;
874
875 fn relation_incidences(&self, relation: Self::RelationId) -> Self::Incidences<'_> {
876 self.relation_incidences[local_relation_index(relation)]
877 .iter()
878 .copied()
879 }
880}
881
882impl oxgraph_hyper::ElementIncidences for HypergraphProjection {
883 type Incidences<'view>
884 = std::iter::Copied<std::slice::Iter<'view, ProjectionIncidenceId>>
885 where
886 Self: 'view;
887
888 fn element_incidences(&self, element: Self::ElementId) -> Self::Incidences<'_> {
889 self.element_incidences[local_index(element)]
890 .iter()
891 .copied()
892 }
893}
894
895impl IncidenceElement for HypergraphProjection {
896 fn incidence_element(&self, incidence: Self::IncidenceId) -> Self::ElementId {
897 let canonical = self.incidences[local_incidence_index(incidence)].element;
898 self.element_index[&canonical]
899 }
900}
901
902impl IncidenceRelation for HypergraphProjection {
903 fn incidence_relation(&self, incidence: Self::IncidenceId) -> Self::RelationId {
904 let canonical = self.incidences[local_incidence_index(incidence)].relation;
905 self.relation_index[&canonical]
906 }
907}
908
909impl IncidenceRole for HypergraphProjection {
910 fn incidence_role(&self, incidence: Self::IncidenceId) -> Self::Role {
911 self.incidences[local_incidence_index(incidence)].role
912 }
913}
914
915impl RelationIncidenceCount for HypergraphProjection {
916 fn relation_incidence_count(&self, relation: Self::RelationId) -> usize {
917 self.relation_incidences[local_relation_index(relation)].len()
918 }
919}
920
921impl ElementIncidenceCount for HypergraphProjection {
922 fn element_incidence_count(&self, element: Self::ElementId) -> usize {
923 self.element_incidences[local_index(element)].len()
924 }
925}
926
927impl oxgraph_hyper::HyperedgeParticipants for HypergraphProjection {
928 type Participants<'view>
929 = HyperedgeParticipants<'view>
930 where
931 Self: 'view;
932
933 fn hyperedge_participants(&self, hyperedge: Self::RelationId) -> Self::Participants<'_> {
934 HyperedgeParticipants {
935 view: self,
936 inner: self.relation_incidences[local_relation_index(hyperedge)].iter(),
937 }
938 }
939}
940
941impl IncidentHyperedges for HypergraphProjection {
942 type IncidentHyperedges<'view>
943 = IncidentHyperedgesIter<'view>
944 where
945 Self: 'view;
946
947 fn incident_hyperedges(&self, vertex: Self::ElementId) -> Self::IncidentHyperedges<'_> {
948 IncidentHyperedgesIter {
949 view: self,
950 inner: self.element_incidences[local_index(vertex)].iter(),
951 }
952 }
953}
954
955impl DirectedHyperedgeParticipants for HypergraphProjection {
960 type SourceParticipants<'view>
961 = std::iter::Copied<std::slice::Iter<'view, ProjectionElementId>>
962 where
963 Self: 'view;
964
965 type TargetParticipants<'view>
966 = std::iter::Copied<std::slice::Iter<'view, ProjectionElementId>>
967 where
968 Self: 'view;
969
970 fn source_participants(&self, hyperedge: Self::RelationId) -> Self::SourceParticipants<'_> {
971 self.source_vertices[local_relation_index(hyperedge)]
972 .iter()
973 .copied()
974 }
975
976 fn target_participants(&self, hyperedge: Self::RelationId) -> Self::TargetParticipants<'_> {
977 self.target_vertices[local_relation_index(hyperedge)]
978 .iter()
979 .copied()
980 }
981}
982
983impl DirectedHyperedgeIncidences for HypergraphProjection {
984 type SourceIncidences<'view>
985 = std::iter::Copied<std::slice::Iter<'view, ProjectionIncidenceId>>
986 where
987 Self: 'view;
988
989 type TargetIncidences<'view>
990 = std::iter::Copied<std::slice::Iter<'view, ProjectionIncidenceId>>
991 where
992 Self: 'view;
993
994 fn source_incidences(&self, hyperedge: Self::RelationId) -> Self::SourceIncidences<'_> {
995 self.source_incidences[local_relation_index(hyperedge)]
996 .iter()
997 .copied()
998 }
999
1000 fn target_incidences(&self, hyperedge: Self::RelationId) -> Self::TargetIncidences<'_> {
1001 self.target_incidences[local_relation_index(hyperedge)]
1002 .iter()
1003 .copied()
1004 }
1005}
1006
1007impl DirectedVertexHyperedges for HypergraphProjection {
1008 type OutgoingHyperedges<'view>
1009 = std::iter::Copied<std::slice::Iter<'view, ProjectionRelationId>>
1010 where
1011 Self: 'view;
1012
1013 type IncomingHyperedges<'view>
1014 = std::iter::Copied<std::slice::Iter<'view, ProjectionRelationId>>
1015 where
1016 Self: 'view;
1017
1018 fn outgoing_hyperedges(&self, vertex: Self::ElementId) -> Self::OutgoingHyperedges<'_> {
1019 self.outgoing_hyperedges[local_index(vertex)]
1020 .iter()
1021 .copied()
1022 }
1023
1024 fn incoming_hyperedges(&self, vertex: Self::ElementId) -> Self::IncomingHyperedges<'_> {
1025 self.incoming_hyperedges[local_index(vertex)]
1026 .iter()
1027 .copied()
1028 }
1029}
1030
1031impl ElementSuccessors for HypergraphProjection {
1032 type Successors<'view>
1033 = std::iter::Copied<std::slice::Iter<'view, ProjectionElementId>>
1034 where
1035 Self: 'view;
1036
1037 fn element_successors(&self, element: Self::ElementId) -> Self::Successors<'_> {
1038 self.successors[local_index(element)].iter().copied()
1039 }
1040}
1041
1042impl ElementPredecessors for HypergraphProjection {
1043 type Predecessors<'view>
1044 = std::iter::Copied<std::slice::Iter<'view, ProjectionElementId>>
1045 where
1046 Self: 'view;
1047
1048 fn element_predecessors(&self, element: Self::ElementId) -> Self::Predecessors<'_> {
1049 self.predecessors[local_index(element)].iter().copied()
1050 }
1051}
1052
1053pub struct HyperedgeParticipants<'view> {
1055 view: &'view HypergraphProjection,
1057 inner: std::slice::Iter<'view, ProjectionIncidenceId>,
1059}
1060
1061impl Iterator for HyperedgeParticipants<'_> {
1062 type Item = ProjectionElementId;
1063
1064 fn next(&mut self) -> Option<Self::Item> {
1065 self.inner
1066 .next()
1067 .map(|incidence| self.view.incidence_element(*incidence))
1068 }
1069}
1070
1071pub struct IncidentHyperedgesIter<'view> {
1073 view: &'view HypergraphProjection,
1075 inner: std::slice::Iter<'view, ProjectionIncidenceId>,
1077}
1078
1079impl Iterator for IncidentHyperedgesIter<'_> {
1080 type Item = ProjectionRelationId;
1081
1082 fn next(&mut self) -> Option<Self::Item> {
1083 self.inner
1084 .next()
1085 .map(|incidence| self.view.incidence_relation(*incidence))
1086 }
1087}
1088
1089fn relation_selected(record: &RelationRecord, selected: &BTreeSet<RelationTypeId>) -> bool {
1091 record
1092 .relation_type
1093 .map_or(selected.is_empty(), |relation_type| {
1094 selected.is_empty() || selected.contains(&relation_type)
1095 })
1096}
1097
1098fn graph_endpoints(
1100 state: &DatabaseState,
1101 relation: RelationId,
1102 source_role: RoleId,
1103 target_role: RoleId,
1104) -> Result<(ElementId, ElementId), DbError> {
1105 let mut source = None;
1106 let mut target = None;
1107 for incidence in state.relation_incidences(relation) {
1108 update_endpoint(&mut source, incidence, source_role, "source")?;
1109 update_endpoint(&mut target, incidence, target_role, "target")?;
1110 }
1111 match (source, target) {
1112 (Some(source), Some(target)) => Ok((source, target)),
1113 _missing => Err(DbError::invalid_projection(
1114 "selected graph relation lacks source or target endpoint",
1115 )),
1116 }
1117}
1118
1119fn update_endpoint(
1121 slot: &mut Option<ElementId>,
1122 incidence: &IncidenceRecord,
1123 role: RoleId,
1124 name: &'static str,
1125) -> Result<(), DbError> {
1126 if incidence.role != role {
1127 return Ok(());
1128 }
1129 if slot.replace(incidence.element).is_some() {
1130 return Err(DbError::invalid_projection(format!(
1131 "selected graph relation has duplicate {name} endpoint"
1132 )));
1133 }
1134 Ok(())
1135}
1136
1137fn intern_element(
1139 index: &mut BTreeMap<ElementId, ProjectionElementId>,
1140 elements: &mut Vec<ElementId>,
1141 canonical: ElementId,
1142) -> Result<ProjectionElementId, DbError> {
1143 if let Some(local) = index.get(&canonical) {
1144 return Ok(*local);
1145 }
1146 let local = projection_element_id(elements.len())?;
1147 elements.push(canonical);
1148 index.insert(canonical, local);
1149 Ok(local)
1150}
1151
1152fn build_element_incidences(
1154 element_count: usize,
1155 incidences: &[IncidenceRecord],
1156 element_index: &BTreeMap<ElementId, ProjectionElementId>,
1157) -> Result<Vec<Vec<ProjectionIncidenceId>>, DbError> {
1158 let mut rows = vec![Vec::new(); element_count];
1159 for (index, incidence) in incidences.iter().enumerate() {
1160 let local = projection_incidence_id(index)?;
1161 let element = element_index
1162 .get(&incidence.element)
1163 .ok_or_else(|| DbError::invalid_projection("incidence element not projected"))?;
1164 rows[local_index(*element)].push(local);
1165 }
1166 Ok(rows)
1167}
1168
1169fn build_directed_vertex_arrays(
1181 element_count: usize,
1182 source_vertices: &[Vec<ProjectionElementId>],
1183 target_vertices: &[Vec<ProjectionElementId>],
1184) -> Result<DirectedVertexArrays, DbError> {
1185 let mut builder = HypergraphBuilder::<u32, u32, u32>::new();
1186 let mut vertices = Vec::with_capacity(element_count);
1187 for _element in 0..element_count {
1188 vertices.push(builder.add_vertex().map_err(|_error| DbError::IdOverflow)?);
1189 }
1190 for (sources, targets) in source_vertices.iter().zip(target_vertices) {
1191 let source_vertices = unique_vertices(&vertices, sources);
1192 let target_vertices = unique_vertices(&vertices, targets);
1193 builder
1194 .add_hyperedge(&source_vertices, &target_vertices)
1195 .map_err(|_error| {
1196 DbError::invalid_projection("hyperedge participants are not directed-unique")
1197 })?;
1198 }
1199 let frozen = builder.freeze().map_err(|_error| DbError::IdOverflow)?;
1200
1201 let mut outgoing = Vec::with_capacity(element_count);
1202 let mut incoming = Vec::with_capacity(element_count);
1203 let mut successors = Vec::with_capacity(element_count);
1204 let mut predecessors = Vec::with_capacity(element_count);
1205 for vertex in vertices.iter().copied() {
1206 outgoing.push(
1207 DirectedVertexHyperedges::outgoing_hyperedges(&frozen, vertex)
1208 .map(|hyperedge| ProjectionRelationId(hyperedge.get()))
1209 .collect(),
1210 );
1211 incoming.push(
1212 DirectedVertexHyperedges::incoming_hyperedges(&frozen, vertex)
1213 .map(|hyperedge| ProjectionRelationId(hyperedge.get()))
1214 .collect(),
1215 );
1216 successors.push(unique_sorted(
1217 frozen
1218 .element_successors(vertex)
1219 .map(|target| ProjectionElementId(target.get())),
1220 ));
1221 predecessors.push(unique_sorted(
1222 frozen
1223 .element_predecessors(vertex)
1224 .map(|source| ProjectionElementId(source.get())),
1225 ));
1226 }
1227 Ok((outgoing, incoming, successors, predecessors))
1228}
1229
1230fn unique_vertices(
1240 vertices: &[HyperVertexId<u32>],
1241 locals: &[ProjectionElementId],
1242) -> Vec<HyperVertexId<u32>> {
1243 let mut seen = BTreeSet::new();
1244 locals
1245 .iter()
1246 .filter(|local| seen.insert(**local))
1247 .map(|local| vertices[local_index(*local)])
1248 .collect()
1249}
1250
1251fn unique_sorted(elements: impl Iterator<Item = ProjectionElementId>) -> Vec<ProjectionElementId> {
1257 elements.collect::<BTreeSet<_>>().into_iter().collect()
1258}
1259
1260type DirectedVertexArrays = (
1262 Vec<Vec<ProjectionRelationId>>,
1263 Vec<Vec<ProjectionRelationId>>,
1264 Vec<Vec<ProjectionElementId>>,
1265 Vec<Vec<ProjectionElementId>>,
1266);
1267
1268fn projection_element_id(index: usize) -> Result<ProjectionElementId, DbError> {
1270 u32::try_from(index)
1271 .map(ProjectionElementId)
1272 .map_err(|_error| DbError::IdOverflow)
1273}
1274
1275fn projection_relation_id(index: usize) -> Result<ProjectionRelationId, DbError> {
1277 u32::try_from(index)
1278 .map(ProjectionRelationId)
1279 .map_err(|_error| DbError::IdOverflow)
1280}
1281
1282fn projection_incidence_id(index: usize) -> Result<ProjectionIncidenceId, DbError> {
1284 u32::try_from(index)
1285 .map(ProjectionIncidenceId)
1286 .map_err(|_error| DbError::IdOverflow)
1287}
1288
1289fn local_index(id: ProjectionElementId) -> usize {
1291 usize::try_from(id.0).unwrap_or(usize::MAX)
1292}
1293
1294fn local_relation_index(id: ProjectionRelationId) -> usize {
1296 usize::try_from(id.0).unwrap_or(usize::MAX)
1297}
1298
1299fn local_incidence_index(id: ProjectionIncidenceId) -> usize {
1301 usize::try_from(id.0).unwrap_or(usize::MAX)
1302}