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 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(builder.add_node().map_err(|_error| DbError::IdOverflow)?);
263 }
264 for (source, target) in self.endpoints.iter().copied() {
265 builder
266 .add_edge(nodes[local_index(source)], nodes[local_index(target)])
267 .map_err(|_error| DbError::IdOverflow)?;
268 }
269 let forward = builder.freeze().map_err(|_error| DbError::IdOverflow)?;
270 let reverse = forward.transpose().map_err(|_error| DbError::IdOverflow)?;
271
272 let successors = grouped_elements(forward.offsets(), forward.targets(), node_count)?;
278 let out_edges = grouped_relations(forward.offsets(), forward.edge_ids(), node_count)?;
279 let predecessors = grouped_elements(reverse.offsets(), reverse.targets(), node_count)?;
280 let in_edges = grouped_relations(reverse.offsets(), reverse.edge_ids(), node_count)?;
281
282 Ok(GraphProjection {
283 definition: self.definition,
284 elements: self.elements,
285 element_index: self.element_index,
286 relations: self.relations,
287 relation_index: self.relation_index,
288 endpoints: self.endpoints,
289 out_edges,
290 in_edges,
291 successors,
292 predecessors,
293 })
294 }
295}
296
297fn grouped_elements(
306 offsets: &[u32],
307 flat: &[u32],
308 node_count: usize,
309) -> Result<Vec<Vec<ProjectionElementId>>, DbError> {
310 grouped(offsets, flat, node_count, |raw| {
311 Ok(ProjectionElementId(raw))
312 })
313}
314
315fn grouped_relations(
321 offsets: &[u32],
322 flat: &[u32],
323 node_count: usize,
324) -> Result<Vec<Vec<ProjectionRelationId>>, DbError> {
325 grouped(offsets, flat, node_count, |raw| {
326 Ok(ProjectionRelationId(raw))
327 })
328}
329
330fn grouped<T>(
336 offsets: &[u32],
337 flat: &[u32],
338 node_count: usize,
339 map: impl Fn(u32) -> Result<T, DbError>,
340) -> Result<Vec<Vec<T>>, DbError> {
341 let mut rows = Vec::with_capacity(node_count);
342 for node in 0..node_count {
343 let start = usize::try_from(offsets[node]).map_err(|_error| DbError::IdOverflow)?;
344 let end = usize::try_from(offsets[node + 1]).map_err(|_error| DbError::IdOverflow)?;
345 let slice = flat
346 .get(start..end)
347 .ok_or_else(|| DbError::invalid_projection("csr offset range out of bounds"))?;
348 rows.push(
349 slice
350 .iter()
351 .copied()
352 .map(&map)
353 .collect::<Result<Vec<T>, DbError>>()?,
354 );
355 }
356 Ok(rows)
357}
358
359impl TopologyBase for GraphProjection {
360 type ElementId = ProjectionElementId;
361 type RelationId = ProjectionRelationId;
362}
363
364impl TopologyCounts for GraphProjection {
365 fn element_count(&self) -> usize {
366 self.elements.len()
367 }
368
369 fn relation_count(&self) -> usize {
370 self.relations.len()
371 }
372}
373
374impl GraphCounts for GraphProjection {}
375
376impl ElementIndex for GraphProjection {
377 fn element_bound(&self) -> usize {
378 self.elements.len()
379 }
380
381 fn element_index(&self, element: Self::ElementId) -> usize {
382 local_index(element)
383 }
384}
385
386impl RelationIndex for GraphProjection {
387 fn relation_bound(&self) -> usize {
388 self.relations.len()
389 }
390
391 fn relation_index(&self, relation: Self::RelationId) -> usize {
392 local_relation_index(relation)
393 }
394}
395
396impl oxgraph_graph::ContainsElement for GraphProjection {
397 fn contains_element(&self, element: Self::ElementId) -> bool {
398 local_index(element) < self.elements.len()
399 }
400}
401
402impl oxgraph_graph::ContainsRelation for GraphProjection {
403 fn contains_relation(&self, relation: Self::RelationId) -> bool {
404 local_relation_index(relation) < self.relations.len()
405 }
406}
407
408impl oxgraph_graph::CanonicalElementIdentity for GraphProjection {
409 type CanonicalElementId = ElementId;
410
411 fn canonical_element_id(&self, element: Self::ElementId) -> Self::CanonicalElementId {
412 self.elements[local_index(element)]
413 }
414}
415
416impl LocalElementIdentity for GraphProjection {
417 fn local_element_id(&self, canonical: Self::CanonicalElementId) -> Option<Self::ElementId> {
418 self.element_index.get(&canonical).copied()
419 }
420}
421
422impl oxgraph_graph::CanonicalRelationIdentity for GraphProjection {
423 type CanonicalRelationId = RelationId;
424
425 fn canonical_relation_id(&self, relation: Self::RelationId) -> Self::CanonicalRelationId {
426 self.relations[local_relation_index(relation)]
427 }
428}
429
430impl LocalRelationIdentity for GraphProjection {
431 fn local_relation_id(&self, canonical: Self::CanonicalRelationId) -> Option<Self::RelationId> {
432 self.relation_index.get(&canonical).copied()
433 }
434}
435
436impl EdgeSourceGraph for GraphProjection {
437 fn source(&self, edge: Self::RelationId) -> Self::ElementId {
438 self.endpoints[local_relation_index(edge)].0
439 }
440}
441
442impl EdgeTargetGraph for GraphProjection {
443 fn target(&self, edge: Self::RelationId) -> Self::ElementId {
444 self.endpoints[local_relation_index(edge)].1
445 }
446}
447
448impl OutgoingGraph for GraphProjection {
449 type OutEdges<'view>
450 = std::iter::Copied<std::slice::Iter<'view, ProjectionRelationId>>
451 where
452 Self: 'view;
453
454 fn outgoing_edges(&self, node: Self::ElementId) -> Self::OutEdges<'_> {
455 self.out_edges[local_index(node)].iter().copied()
456 }
457}
458
459impl IncomingGraph for GraphProjection {
460 type InEdges<'view>
461 = std::iter::Copied<std::slice::Iter<'view, ProjectionRelationId>>
462 where
463 Self: 'view;
464
465 fn incoming_edges(&self, node: Self::ElementId) -> Self::InEdges<'_> {
466 self.in_edges[local_index(node)].iter().copied()
467 }
468}
469
470impl ElementSuccessors for GraphProjection {
471 type Successors<'view>
472 = std::iter::Copied<std::slice::Iter<'view, ProjectionElementId>>
473 where
474 Self: 'view;
475
476 fn element_successors(&self, element: Self::ElementId) -> Self::Successors<'_> {
477 self.successors[local_index(element)].iter().copied()
478 }
479}
480
481impl ElementPredecessors for GraphProjection {
482 type Predecessors<'view>
483 = std::iter::Copied<std::slice::Iter<'view, ProjectionElementId>>
484 where
485 Self: 'view;
486
487 fn element_predecessors(&self, element: Self::ElementId) -> Self::Predecessors<'_> {
488 self.predecessors[local_index(element)].iter().copied()
489 }
490}
491
492impl OutgoingEdgeCount for GraphProjection {
493 fn out_degree(&self, node: Self::ElementId) -> usize {
494 self.out_edges[local_index(node)].len()
495 }
496}
497
498impl IncomingEdgeCount for GraphProjection {
499 fn in_degree(&self, node: Self::ElementId) -> usize {
500 self.in_edges[local_index(node)].len()
501 }
502}
503
504#[derive(Clone, Debug, Eq, PartialEq)]
514pub struct HypergraphProjection {
515 definition: HypergraphProjectionDefinition,
517 elements: Vec<ElementId>,
519 element_index: BTreeMap<ElementId, ProjectionElementId>,
521 relations: Vec<RelationId>,
523 relation_index: BTreeMap<RelationId, ProjectionRelationId>,
525 incidences: Vec<IncidenceRecord>,
527 incidence_index: BTreeMap<IncidenceId, ProjectionIncidenceId>,
529 relation_incidences: Vec<Vec<ProjectionIncidenceId>>,
531 element_incidences: Vec<Vec<ProjectionIncidenceId>>,
533 source_incidences: Vec<Vec<ProjectionIncidenceId>>,
535 target_incidences: Vec<Vec<ProjectionIncidenceId>>,
537 source_vertices: Vec<Vec<ProjectionElementId>>,
539 target_vertices: Vec<Vec<ProjectionElementId>>,
541 outgoing_hyperedges: Vec<Vec<ProjectionRelationId>>,
543 incoming_hyperedges: Vec<Vec<ProjectionRelationId>>,
545 successors: Vec<Vec<ProjectionElementId>>,
547 predecessors: Vec<Vec<ProjectionElementId>>,
549}
550
551impl HypergraphProjection {
552 pub(crate) fn from_state(
564 state: &impl StateView,
565 definition: HypergraphProjectionDefinition,
566 ) -> Result<Self, DbError> {
567 if definition.source_roles.is_empty() || definition.target_roles.is_empty() {
568 return Err(DbError::invalid_projection(
569 "hypergraph projection requires non-empty source and target roles",
570 ));
571 }
572 let mut builder = HypergraphProjectionBuilder::new(definition);
573 for relation in state.relations() {
574 if relation_selected(relation.as_ref(), &builder.definition.relation_types) {
575 builder.push_relation(state, relation.as_ref())?;
576 }
577 }
578 builder.finish()
579 }
580
581 #[must_use]
587 pub const fn definition(&self) -> &HypergraphProjectionDefinition {
588 &self.definition
589 }
590
591 pub(crate) fn subjects(&self) -> Vec<PropertySubject> {
593 let mut subjects =
594 Vec::with_capacity(self.elements.len() + self.relations.len() + self.incidences.len());
595 subjects.extend(self.elements.iter().copied().map(PropertySubject::Element));
596 subjects.extend(
597 self.relations
598 .iter()
599 .copied()
600 .map(PropertySubject::Relation),
601 );
602 subjects.extend(
603 self.incidences
604 .iter()
605 .map(|record| PropertySubject::Incidence(record.id)),
606 );
607 subjects.sort_unstable();
608 subjects
609 }
610}
611
612struct HypergraphProjectionBuilder {
614 definition: HypergraphProjectionDefinition,
616 elements: Vec<ElementId>,
618 element_index: BTreeMap<ElementId, ProjectionElementId>,
620 relations: Vec<RelationId>,
622 relation_index: BTreeMap<RelationId, ProjectionRelationId>,
624 incidences: Vec<IncidenceRecord>,
626 incidence_index: BTreeMap<IncidenceId, ProjectionIncidenceId>,
628 relation_incidences: Vec<Vec<ProjectionIncidenceId>>,
630 source_incidences: Vec<Vec<ProjectionIncidenceId>>,
632 target_incidences: Vec<Vec<ProjectionIncidenceId>>,
634 source_vertices: Vec<Vec<ProjectionElementId>>,
636 target_vertices: Vec<Vec<ProjectionElementId>>,
638}
639
640impl HypergraphProjectionBuilder {
641 const fn new(definition: HypergraphProjectionDefinition) -> Self {
643 Self {
644 definition,
645 elements: Vec::new(),
646 element_index: BTreeMap::new(),
647 relations: Vec::new(),
648 relation_index: BTreeMap::new(),
649 incidences: Vec::new(),
650 incidence_index: BTreeMap::new(),
651 relation_incidences: Vec::new(),
652 source_incidences: Vec::new(),
653 target_incidences: Vec::new(),
654 source_vertices: Vec::new(),
655 target_vertices: Vec::new(),
656 }
657 }
658
659 fn push_relation(
661 &mut self,
662 state: &impl StateView,
663 relation: &RelationRecord,
664 ) -> Result<(), DbError> {
665 let hyperedge = projection_relation_id(self.relations.len())?;
666 self.relations.push(relation.id);
667 self.relation_index.insert(relation.id, hyperedge);
668 let mut relation_ids = Vec::new();
669 let mut source_ids = Vec::new();
670 let mut target_ids = Vec::new();
671 let mut sources = Vec::new();
672 let mut targets = Vec::new();
673 for incidence in state.relation_incidences(relation.id) {
674 let local = self.push_incidence(&incidence)?;
675 let vertex = intern_element(
676 &mut self.element_index,
677 &mut self.elements,
678 incidence.element,
679 )?;
680 relation_ids.push(local);
681 if self.definition.source_roles.contains(&incidence.role) {
682 source_ids.push(local);
683 sources.push(vertex);
684 }
685 if self.definition.target_roles.contains(&incidence.role) {
686 target_ids.push(local);
687 targets.push(vertex);
688 }
689 }
690 if sources.is_empty() || targets.is_empty() {
691 return Err(DbError::invalid_projection(
692 "selected hyperedge lacks source or target participants",
693 ));
694 }
695 self.relation_incidences.push(relation_ids);
696 self.source_incidences.push(source_ids);
697 self.target_incidences.push(target_ids);
698 self.source_vertices.push(sources);
699 self.target_vertices.push(targets);
700 Ok(())
701 }
702
703 fn push_incidence(
705 &mut self,
706 incidence: &IncidenceRecord,
707 ) -> Result<ProjectionIncidenceId, DbError> {
708 if let Some(id) = self.incidence_index.get(&incidence.id) {
709 return Ok(*id);
710 }
711 let local = projection_incidence_id(self.incidences.len())?;
712 self.incidences.push(*incidence);
713 self.incidence_index.insert(incidence.id, local);
714 Ok(local)
715 }
716
717 fn finish(self) -> Result<HypergraphProjection, DbError> {
719 let element_incidences =
720 build_element_incidences(self.elements.len(), &self.incidences, &self.element_index)?;
721 let (outgoing_hyperedges, incoming_hyperedges, successors, predecessors) =
722 build_directed_vertex_arrays(
723 self.elements.len(),
724 &self.source_vertices,
725 &self.target_vertices,
726 )?;
727 Ok(HypergraphProjection {
728 definition: self.definition,
729 elements: self.elements,
730 element_index: self.element_index,
731 relations: self.relations,
732 relation_index: self.relation_index,
733 incidences: self.incidences,
734 incidence_index: self.incidence_index,
735 relation_incidences: self.relation_incidences,
736 element_incidences,
737 source_incidences: self.source_incidences,
738 target_incidences: self.target_incidences,
739 source_vertices: self.source_vertices,
740 target_vertices: self.target_vertices,
741 outgoing_hyperedges,
742 incoming_hyperedges,
743 successors,
744 predecessors,
745 })
746 }
747}
748
749impl TopologyBase for HypergraphProjection {
750 type ElementId = ProjectionElementId;
751 type RelationId = ProjectionRelationId;
752}
753
754impl IncidenceBase for HypergraphProjection {
755 type IncidenceId = ProjectionIncidenceId;
756 type Role = RoleId;
757}
758
759impl TopologyCounts for HypergraphProjection {
760 fn element_count(&self) -> usize {
761 self.elements.len()
762 }
763
764 fn relation_count(&self) -> usize {
765 self.relations.len()
766 }
767}
768
769impl HypergraphCounts for HypergraphProjection {}
770
771impl IncidenceCounts for HypergraphProjection {
772 fn incidence_count(&self) -> usize {
773 self.incidences.len()
774 }
775}
776
777impl ElementIndex for HypergraphProjection {
778 fn element_bound(&self) -> usize {
779 self.elements.len()
780 }
781
782 fn element_index(&self, element: Self::ElementId) -> usize {
783 local_index(element)
784 }
785}
786
787impl RelationIndex for HypergraphProjection {
788 fn relation_bound(&self) -> usize {
789 self.relations.len()
790 }
791
792 fn relation_index(&self, relation: Self::RelationId) -> usize {
793 local_relation_index(relation)
794 }
795}
796
797impl IncidenceIndex for HypergraphProjection {
798 fn incidence_bound(&self) -> usize {
799 self.incidences.len()
800 }
801
802 fn incidence_index(&self, incidence: Self::IncidenceId) -> usize {
803 local_incidence_index(incidence)
804 }
805}
806
807impl oxgraph_hyper::ContainsElement for HypergraphProjection {
808 fn contains_element(&self, element: Self::ElementId) -> bool {
809 local_index(element) < self.elements.len()
810 }
811}
812
813impl oxgraph_hyper::ContainsRelation for HypergraphProjection {
814 fn contains_relation(&self, relation: Self::RelationId) -> bool {
815 local_relation_index(relation) < self.relations.len()
816 }
817}
818
819impl oxgraph_hyper::ContainsIncidence for HypergraphProjection {
820 fn contains_incidence(&self, incidence: Self::IncidenceId) -> bool {
821 local_incidence_index(incidence) < self.incidences.len()
822 }
823}
824
825impl oxgraph_hyper::CanonicalElementIdentity for HypergraphProjection {
826 type CanonicalElementId = ElementId;
827
828 fn canonical_element_id(&self, element: Self::ElementId) -> Self::CanonicalElementId {
829 self.elements[local_index(element)]
830 }
831}
832
833impl oxgraph_hyper::LocalElementIdentity for HypergraphProjection {
834 fn local_element_id(&self, canonical: Self::CanonicalElementId) -> Option<Self::ElementId> {
835 self.element_index.get(&canonical).copied()
836 }
837}
838
839impl oxgraph_hyper::CanonicalRelationIdentity for HypergraphProjection {
840 type CanonicalRelationId = RelationId;
841
842 fn canonical_relation_id(&self, relation: Self::RelationId) -> Self::CanonicalRelationId {
843 self.relations[local_relation_index(relation)]
844 }
845}
846
847impl oxgraph_hyper::LocalRelationIdentity for HypergraphProjection {
848 fn local_relation_id(&self, canonical: Self::CanonicalRelationId) -> Option<Self::RelationId> {
849 self.relation_index.get(&canonical).copied()
850 }
851}
852
853impl oxgraph_hyper::CanonicalIncidenceIdentity for HypergraphProjection {
854 type CanonicalIncidenceId = IncidenceId;
855
856 fn canonical_incidence_id(&self, incidence: Self::IncidenceId) -> Self::CanonicalIncidenceId {
857 self.incidences[local_incidence_index(incidence)].id
858 }
859}
860
861impl oxgraph_hyper::LocalIncidenceIdentity for HypergraphProjection {
862 fn local_incidence_id(
863 &self,
864 canonical: Self::CanonicalIncidenceId,
865 ) -> Option<Self::IncidenceId> {
866 self.incidence_index.get(&canonical).copied()
867 }
868}
869
870impl RelationIncidences for HypergraphProjection {
871 type Incidences<'view>
872 = std::iter::Copied<std::slice::Iter<'view, ProjectionIncidenceId>>
873 where
874 Self: 'view;
875
876 fn relation_incidences(&self, relation: Self::RelationId) -> Self::Incidences<'_> {
877 self.relation_incidences[local_relation_index(relation)]
878 .iter()
879 .copied()
880 }
881}
882
883impl oxgraph_hyper::ElementIncidences for HypergraphProjection {
884 type Incidences<'view>
885 = std::iter::Copied<std::slice::Iter<'view, ProjectionIncidenceId>>
886 where
887 Self: 'view;
888
889 fn element_incidences(&self, element: Self::ElementId) -> Self::Incidences<'_> {
890 self.element_incidences[local_index(element)]
891 .iter()
892 .copied()
893 }
894}
895
896impl IncidenceElement for HypergraphProjection {
897 fn incidence_element(&self, incidence: Self::IncidenceId) -> Self::ElementId {
898 let canonical = self.incidences[local_incidence_index(incidence)].element;
899 self.element_index[&canonical]
900 }
901}
902
903impl IncidenceRelation for HypergraphProjection {
904 fn incidence_relation(&self, incidence: Self::IncidenceId) -> Self::RelationId {
905 let canonical = self.incidences[local_incidence_index(incidence)].relation;
906 self.relation_index[&canonical]
907 }
908}
909
910impl IncidenceRole for HypergraphProjection {
911 fn incidence_role(&self, incidence: Self::IncidenceId) -> Self::Role {
912 self.incidences[local_incidence_index(incidence)].role
913 }
914}
915
916impl RelationIncidenceCount for HypergraphProjection {
917 fn relation_incidence_count(&self, relation: Self::RelationId) -> usize {
918 self.relation_incidences[local_relation_index(relation)].len()
919 }
920}
921
922impl ElementIncidenceCount for HypergraphProjection {
923 fn element_incidence_count(&self, element: Self::ElementId) -> usize {
924 self.element_incidences[local_index(element)].len()
925 }
926}
927
928impl oxgraph_hyper::HyperedgeParticipants for HypergraphProjection {
929 type Participants<'view>
930 = HyperedgeParticipants<'view>
931 where
932 Self: 'view;
933
934 fn hyperedge_participants(&self, hyperedge: Self::RelationId) -> Self::Participants<'_> {
935 HyperedgeParticipants {
936 view: self,
937 inner: self.relation_incidences[local_relation_index(hyperedge)].iter(),
938 }
939 }
940}
941
942impl IncidentHyperedges for HypergraphProjection {
943 type IncidentHyperedges<'view>
944 = IncidentHyperedgesIter<'view>
945 where
946 Self: 'view;
947
948 fn incident_hyperedges(&self, vertex: Self::ElementId) -> Self::IncidentHyperedges<'_> {
949 IncidentHyperedgesIter {
950 view: self,
951 inner: self.element_incidences[local_index(vertex)].iter(),
952 }
953 }
954}
955
956impl DirectedHyperedgeParticipants for HypergraphProjection {
961 type SourceParticipants<'view>
962 = std::iter::Copied<std::slice::Iter<'view, ProjectionElementId>>
963 where
964 Self: 'view;
965
966 type TargetParticipants<'view>
967 = std::iter::Copied<std::slice::Iter<'view, ProjectionElementId>>
968 where
969 Self: 'view;
970
971 fn source_participants(&self, hyperedge: Self::RelationId) -> Self::SourceParticipants<'_> {
972 self.source_vertices[local_relation_index(hyperedge)]
973 .iter()
974 .copied()
975 }
976
977 fn target_participants(&self, hyperedge: Self::RelationId) -> Self::TargetParticipants<'_> {
978 self.target_vertices[local_relation_index(hyperedge)]
979 .iter()
980 .copied()
981 }
982}
983
984impl DirectedHyperedgeIncidences for HypergraphProjection {
985 type SourceIncidences<'view>
986 = std::iter::Copied<std::slice::Iter<'view, ProjectionIncidenceId>>
987 where
988 Self: 'view;
989
990 type TargetIncidences<'view>
991 = std::iter::Copied<std::slice::Iter<'view, ProjectionIncidenceId>>
992 where
993 Self: 'view;
994
995 fn source_incidences(&self, hyperedge: Self::RelationId) -> Self::SourceIncidences<'_> {
996 self.source_incidences[local_relation_index(hyperedge)]
997 .iter()
998 .copied()
999 }
1000
1001 fn target_incidences(&self, hyperedge: Self::RelationId) -> Self::TargetIncidences<'_> {
1002 self.target_incidences[local_relation_index(hyperedge)]
1003 .iter()
1004 .copied()
1005 }
1006}
1007
1008impl DirectedVertexHyperedges for HypergraphProjection {
1009 type OutgoingHyperedges<'view>
1010 = std::iter::Copied<std::slice::Iter<'view, ProjectionRelationId>>
1011 where
1012 Self: 'view;
1013
1014 type IncomingHyperedges<'view>
1015 = std::iter::Copied<std::slice::Iter<'view, ProjectionRelationId>>
1016 where
1017 Self: 'view;
1018
1019 fn outgoing_hyperedges(&self, vertex: Self::ElementId) -> Self::OutgoingHyperedges<'_> {
1020 self.outgoing_hyperedges[local_index(vertex)]
1021 .iter()
1022 .copied()
1023 }
1024
1025 fn incoming_hyperedges(&self, vertex: Self::ElementId) -> Self::IncomingHyperedges<'_> {
1026 self.incoming_hyperedges[local_index(vertex)]
1027 .iter()
1028 .copied()
1029 }
1030}
1031
1032impl ElementSuccessors for HypergraphProjection {
1033 type Successors<'view>
1034 = std::iter::Copied<std::slice::Iter<'view, ProjectionElementId>>
1035 where
1036 Self: 'view;
1037
1038 fn element_successors(&self, element: Self::ElementId) -> Self::Successors<'_> {
1039 self.successors[local_index(element)].iter().copied()
1040 }
1041}
1042
1043impl ElementPredecessors for HypergraphProjection {
1044 type Predecessors<'view>
1045 = std::iter::Copied<std::slice::Iter<'view, ProjectionElementId>>
1046 where
1047 Self: 'view;
1048
1049 fn element_predecessors(&self, element: Self::ElementId) -> Self::Predecessors<'_> {
1050 self.predecessors[local_index(element)].iter().copied()
1051 }
1052}
1053
1054pub struct HyperedgeParticipants<'view> {
1056 view: &'view HypergraphProjection,
1058 inner: std::slice::Iter<'view, ProjectionIncidenceId>,
1060}
1061
1062impl Iterator for HyperedgeParticipants<'_> {
1063 type Item = ProjectionElementId;
1064
1065 fn next(&mut self) -> Option<Self::Item> {
1066 self.inner
1067 .next()
1068 .map(|incidence| self.view.incidence_element(*incidence))
1069 }
1070}
1071
1072pub struct IncidentHyperedgesIter<'view> {
1074 view: &'view HypergraphProjection,
1076 inner: std::slice::Iter<'view, ProjectionIncidenceId>,
1078}
1079
1080impl Iterator for IncidentHyperedgesIter<'_> {
1081 type Item = ProjectionRelationId;
1082
1083 fn next(&mut self) -> Option<Self::Item> {
1084 self.inner
1085 .next()
1086 .map(|incidence| self.view.incidence_relation(*incidence))
1087 }
1088}
1089
1090fn relation_selected(record: &RelationRecord, selected: &BTreeSet<RelationTypeId>) -> bool {
1092 record
1093 .relation_type
1094 .map_or(selected.is_empty(), |relation_type| {
1095 selected.is_empty() || selected.contains(&relation_type)
1096 })
1097}
1098
1099fn graph_endpoints(
1101 state: &impl StateView,
1102 relation: RelationId,
1103 source_role: RoleId,
1104 target_role: RoleId,
1105) -> Result<(ElementId, ElementId), DbError> {
1106 let mut source = None;
1107 let mut target = None;
1108 for incidence in state.relation_incidences(relation) {
1109 update_endpoint(&mut source, &incidence, source_role, "source")?;
1110 update_endpoint(&mut target, &incidence, target_role, "target")?;
1111 }
1112 match (source, target) {
1113 (Some(source), Some(target)) => Ok((source, target)),
1114 _missing => Err(DbError::invalid_projection(
1115 "selected graph relation lacks source or target endpoint",
1116 )),
1117 }
1118}
1119
1120fn update_endpoint(
1122 slot: &mut Option<ElementId>,
1123 incidence: &IncidenceRecord,
1124 role: RoleId,
1125 name: &'static str,
1126) -> Result<(), DbError> {
1127 if incidence.role != role {
1128 return Ok(());
1129 }
1130 if slot.replace(incidence.element).is_some() {
1131 return Err(DbError::invalid_projection(format!(
1132 "selected graph relation has duplicate {name} endpoint"
1133 )));
1134 }
1135 Ok(())
1136}
1137
1138fn intern_element(
1140 index: &mut BTreeMap<ElementId, ProjectionElementId>,
1141 elements: &mut Vec<ElementId>,
1142 canonical: ElementId,
1143) -> Result<ProjectionElementId, DbError> {
1144 if let Some(local) = index.get(&canonical) {
1145 return Ok(*local);
1146 }
1147 let local = projection_element_id(elements.len())?;
1148 elements.push(canonical);
1149 index.insert(canonical, local);
1150 Ok(local)
1151}
1152
1153fn build_element_incidences(
1155 element_count: usize,
1156 incidences: &[IncidenceRecord],
1157 element_index: &BTreeMap<ElementId, ProjectionElementId>,
1158) -> Result<Vec<Vec<ProjectionIncidenceId>>, DbError> {
1159 let mut rows = vec![Vec::new(); element_count];
1160 for (index, incidence) in incidences.iter().enumerate() {
1161 let local = projection_incidence_id(index)?;
1162 let element = element_index
1163 .get(&incidence.element)
1164 .ok_or_else(|| DbError::invalid_projection("incidence element not projected"))?;
1165 rows[local_index(*element)].push(local);
1166 }
1167 Ok(rows)
1168}
1169
1170fn build_directed_vertex_arrays(
1182 element_count: usize,
1183 source_vertices: &[Vec<ProjectionElementId>],
1184 target_vertices: &[Vec<ProjectionElementId>],
1185) -> Result<DirectedVertexArrays, DbError> {
1186 let mut builder = HypergraphBuilder::<u32, u32, u32>::new();
1187 let mut vertices = Vec::with_capacity(element_count);
1188 for _element in 0..element_count {
1189 vertices.push(builder.add_vertex().map_err(|_error| DbError::IdOverflow)?);
1190 }
1191 for (sources, targets) in source_vertices.iter().zip(target_vertices) {
1192 let source_vertices = unique_vertices(&vertices, sources);
1193 let target_vertices = unique_vertices(&vertices, targets);
1194 builder
1195 .add_hyperedge(&source_vertices, &target_vertices)
1196 .map_err(|_error| {
1197 DbError::invalid_projection("hyperedge participants are not directed-unique")
1198 })?;
1199 }
1200 let frozen = builder.freeze().map_err(|_error| DbError::IdOverflow)?;
1201
1202 let mut outgoing = Vec::with_capacity(element_count);
1203 let mut incoming = Vec::with_capacity(element_count);
1204 let mut successors = Vec::with_capacity(element_count);
1205 let mut predecessors = Vec::with_capacity(element_count);
1206 for vertex in vertices.iter().copied() {
1207 outgoing.push(
1208 DirectedVertexHyperedges::outgoing_hyperedges(&frozen, vertex)
1209 .map(|hyperedge| ProjectionRelationId(hyperedge.get()))
1210 .collect(),
1211 );
1212 incoming.push(
1213 DirectedVertexHyperedges::incoming_hyperedges(&frozen, vertex)
1214 .map(|hyperedge| ProjectionRelationId(hyperedge.get()))
1215 .collect(),
1216 );
1217 successors.push(unique_sorted(
1218 frozen
1219 .element_successors(vertex)
1220 .map(|target| ProjectionElementId(target.get())),
1221 ));
1222 predecessors.push(unique_sorted(
1223 frozen
1224 .element_predecessors(vertex)
1225 .map(|source| ProjectionElementId(source.get())),
1226 ));
1227 }
1228 Ok((outgoing, incoming, successors, predecessors))
1229}
1230
1231fn unique_vertices(
1241 vertices: &[HyperVertexId<u32>],
1242 locals: &[ProjectionElementId],
1243) -> Vec<HyperVertexId<u32>> {
1244 let mut seen = BTreeSet::new();
1245 locals
1246 .iter()
1247 .filter(|local| seen.insert(**local))
1248 .map(|local| vertices[local_index(*local)])
1249 .collect()
1250}
1251
1252fn unique_sorted(elements: impl Iterator<Item = ProjectionElementId>) -> Vec<ProjectionElementId> {
1258 elements.collect::<BTreeSet<_>>().into_iter().collect()
1259}
1260
1261type DirectedVertexArrays = (
1263 Vec<Vec<ProjectionRelationId>>,
1264 Vec<Vec<ProjectionRelationId>>,
1265 Vec<Vec<ProjectionElementId>>,
1266 Vec<Vec<ProjectionElementId>>,
1267);
1268
1269fn projection_element_id(index: usize) -> Result<ProjectionElementId, DbError> {
1271 u32::try_from(index)
1272 .map(ProjectionElementId)
1273 .map_err(|_error| DbError::IdOverflow)
1274}
1275
1276fn projection_relation_id(index: usize) -> Result<ProjectionRelationId, DbError> {
1278 u32::try_from(index)
1279 .map(ProjectionRelationId)
1280 .map_err(|_error| DbError::IdOverflow)
1281}
1282
1283fn projection_incidence_id(index: usize) -> Result<ProjectionIncidenceId, DbError> {
1285 u32::try_from(index)
1286 .map(ProjectionIncidenceId)
1287 .map_err(|_error| DbError::IdOverflow)
1288}
1289
1290fn local_index(id: ProjectionElementId) -> usize {
1292 usize::try_from(id.0).unwrap_or(usize::MAX)
1293}
1294
1295fn local_relation_index(id: ProjectionRelationId) -> usize {
1297 usize::try_from(id.0).unwrap_or(usize::MAX)
1298}
1299
1300fn local_incidence_index(id: ProjectionIncidenceId) -> usize {
1302 usize::try_from(id.0).unwrap_or(usize::MAX)
1303}