1use std::collections::{BTreeMap, BTreeSet};
4
5use oxgraph_graph::{
6 EdgeSourceGraph, EdgeTargetGraph, ElementIndex, ElementPredecessors, ElementSuccessors,
7 GraphCounts, IncomingEdgeCount, IncomingGraph, LocalElementIdentity, LocalRelationIdentity,
8 OutgoingEdgeCount, OutgoingGraph, RelationIndex, TopologyBase, TopologyCounts,
9};
10use oxgraph_hyper::{
11 DirectedHyperedgeIncidences, DirectedHyperedgeParticipants, DirectedVertexHyperedges,
12 ElementIncidenceCount, HyperedgeParticipantCount, HypergraphCounts, IncidenceBase,
13 IncidenceCounts, IncidenceElement, IncidenceIndex, IncidenceRelation, IncidenceRole,
14 IncidentHyperedgeCount, IncidentHyperedges, RelationIncidenceCount, RelationIncidences,
15};
16use serde::{Deserialize, Serialize};
17
18use crate::{
19 DbError, ElementId, IncidenceId, RelationId, RelationTypeId, RoleId,
20 catalog::{GraphProjectionDefinition, HypergraphProjectionDefinition},
21 state::{DatabaseState, IncidenceRecord, PropertySubject, RelationRecord},
22};
23
24#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
30pub struct ProjectionElementId(u32);
31
32impl ProjectionElementId {
33 #[must_use]
39 pub const fn new(value: u32) -> Self {
40 Self(value)
41 }
42
43 #[must_use]
49 pub const fn get(self) -> u32 {
50 self.0
51 }
52}
53
54#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
60pub struct ProjectionRelationId(u32);
61
62impl ProjectionRelationId {
63 #[must_use]
69 pub const fn new(value: u32) -> Self {
70 Self(value)
71 }
72
73 #[must_use]
79 pub const fn get(self) -> u32 {
80 self.0
81 }
82}
83
84#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
90pub struct ProjectionIncidenceId(u32);
91
92impl ProjectionIncidenceId {
93 #[must_use]
99 pub const fn new(value: u32) -> Self {
100 Self(value)
101 }
102
103 #[must_use]
109 pub const fn get(self) -> u32 {
110 self.0
111 }
112}
113
114#[derive(Clone, Debug, Eq, PartialEq)]
124pub struct GraphProjection {
125 definition: GraphProjectionDefinition,
127 elements: Vec<ElementId>,
129 element_index: BTreeMap<ElementId, ProjectionElementId>,
131 relations: Vec<RelationId>,
133 relation_index: BTreeMap<RelationId, ProjectionRelationId>,
135 endpoints: Vec<(ProjectionElementId, ProjectionElementId)>,
137 out_edges: Vec<Vec<ProjectionRelationId>>,
139 in_edges: Vec<Vec<ProjectionRelationId>>,
141 successors: Vec<Vec<ProjectionElementId>>,
143 predecessors: Vec<Vec<ProjectionElementId>>,
145}
146
147impl GraphProjection {
148 pub(crate) fn from_state(
160 state: &DatabaseState,
161 definition: GraphProjectionDefinition,
162 ) -> Result<Self, DbError> {
163 let mut builder = GraphProjectionBuilder::new(definition);
164 for relation in state.relations() {
165 if relation_selected(relation, &builder.definition.relation_types) {
166 builder.push_relation(state, relation)?;
167 }
168 }
169 builder.finish()
170 }
171
172 #[must_use]
178 pub const fn definition(&self) -> &GraphProjectionDefinition {
179 &self.definition
180 }
181
182 pub(crate) fn subjects(&self) -> Vec<PropertySubject> {
184 let mut subjects = Vec::with_capacity(self.elements.len() + self.relations.len());
185 subjects.extend(self.elements.iter().copied().map(PropertySubject::Element));
186 subjects.extend(
187 self.relations
188 .iter()
189 .copied()
190 .map(PropertySubject::Relation),
191 );
192 subjects.sort_unstable();
193 subjects
194 }
195}
196
197struct GraphProjectionBuilder {
199 definition: GraphProjectionDefinition,
201 elements: Vec<ElementId>,
203 element_index: BTreeMap<ElementId, ProjectionElementId>,
205 relations: Vec<RelationId>,
207 relation_index: BTreeMap<RelationId, ProjectionRelationId>,
209 endpoints: Vec<(ProjectionElementId, ProjectionElementId)>,
211}
212
213impl GraphProjectionBuilder {
214 const fn new(definition: GraphProjectionDefinition) -> Self {
216 Self {
217 definition,
218 elements: Vec::new(),
219 element_index: BTreeMap::new(),
220 relations: Vec::new(),
221 relation_index: BTreeMap::new(),
222 endpoints: Vec::new(),
223 }
224 }
225
226 fn push_relation(
228 &mut self,
229 state: &DatabaseState,
230 relation: &RelationRecord,
231 ) -> Result<(), DbError> {
232 let (source, target) = graph_endpoints(
233 state,
234 relation.id,
235 self.definition.source_role,
236 self.definition.target_role,
237 )?;
238 let source = intern_element(&mut self.element_index, &mut self.elements, source)?;
239 let target = intern_element(&mut self.element_index, &mut self.elements, target)?;
240 let edge = projection_relation_id(self.relations.len())?;
241 self.relations.push(relation.id);
242 self.relation_index.insert(relation.id, edge);
243 self.endpoints.push((source, target));
244 Ok(())
245 }
246
247 fn finish(self) -> Result<GraphProjection, DbError> {
249 let mut out_edges = vec![Vec::new(); self.elements.len()];
250 let mut in_edges = vec![Vec::new(); self.elements.len()];
251 let mut successors = vec![Vec::new(); self.elements.len()];
252 let mut predecessors = vec![Vec::new(); self.elements.len()];
253 for (index, (source, target)) in self.endpoints.iter().copied().enumerate() {
254 let edge = projection_relation_id(index)?;
255 out_edges[local_index(source)].push(edge);
256 in_edges[local_index(target)].push(edge);
257 successors[local_index(source)].push(target);
258 predecessors[local_index(target)].push(source);
259 }
260 Ok(GraphProjection {
261 definition: self.definition,
262 elements: self.elements,
263 element_index: self.element_index,
264 relations: self.relations,
265 relation_index: self.relation_index,
266 endpoints: self.endpoints,
267 out_edges,
268 in_edges,
269 successors,
270 predecessors,
271 })
272 }
273}
274
275impl TopologyBase for GraphProjection {
276 type ElementId = ProjectionElementId;
277 type RelationId = ProjectionRelationId;
278}
279
280impl TopologyCounts for GraphProjection {
281 fn element_count(&self) -> usize {
282 self.elements.len()
283 }
284
285 fn relation_count(&self) -> usize {
286 self.relations.len()
287 }
288}
289
290impl GraphCounts for GraphProjection {}
291
292impl ElementIndex for GraphProjection {
293 fn element_bound(&self) -> usize {
294 self.elements.len()
295 }
296
297 fn element_index(&self, element: Self::ElementId) -> usize {
298 local_index(element)
299 }
300}
301
302impl RelationIndex for GraphProjection {
303 fn relation_bound(&self) -> usize {
304 self.relations.len()
305 }
306
307 fn relation_index(&self, relation: Self::RelationId) -> usize {
308 local_relation_index(relation)
309 }
310}
311
312impl oxgraph_graph::ContainsElement for GraphProjection {
313 fn contains_element(&self, element: Self::ElementId) -> bool {
314 local_index(element) < self.elements.len()
315 }
316}
317
318impl oxgraph_graph::ContainsRelation for GraphProjection {
319 fn contains_relation(&self, relation: Self::RelationId) -> bool {
320 local_relation_index(relation) < self.relations.len()
321 }
322}
323
324impl oxgraph_graph::CanonicalElementIdentity for GraphProjection {
325 type CanonicalElementId = ElementId;
326
327 fn canonical_element_id(&self, element: Self::ElementId) -> Self::CanonicalElementId {
328 self.elements[local_index(element)]
329 }
330}
331
332impl LocalElementIdentity for GraphProjection {
333 fn local_element_id(&self, canonical: Self::CanonicalElementId) -> Option<Self::ElementId> {
334 self.element_index.get(&canonical).copied()
335 }
336}
337
338impl oxgraph_graph::CanonicalRelationIdentity for GraphProjection {
339 type CanonicalRelationId = RelationId;
340
341 fn canonical_relation_id(&self, relation: Self::RelationId) -> Self::CanonicalRelationId {
342 self.relations[local_relation_index(relation)]
343 }
344}
345
346impl LocalRelationIdentity for GraphProjection {
347 fn local_relation_id(&self, canonical: Self::CanonicalRelationId) -> Option<Self::RelationId> {
348 self.relation_index.get(&canonical).copied()
349 }
350}
351
352impl EdgeSourceGraph for GraphProjection {
353 fn source(&self, edge: Self::RelationId) -> Self::ElementId {
354 self.endpoints[local_relation_index(edge)].0
355 }
356}
357
358impl EdgeTargetGraph for GraphProjection {
359 fn target(&self, edge: Self::RelationId) -> Self::ElementId {
360 self.endpoints[local_relation_index(edge)].1
361 }
362}
363
364impl OutgoingGraph for GraphProjection {
365 type OutEdges<'view>
366 = std::iter::Copied<std::slice::Iter<'view, ProjectionRelationId>>
367 where
368 Self: 'view;
369
370 fn outgoing_edges(&self, node: Self::ElementId) -> Self::OutEdges<'_> {
371 self.out_edges[local_index(node)].iter().copied()
372 }
373}
374
375impl IncomingGraph for GraphProjection {
376 type InEdges<'view>
377 = std::iter::Copied<std::slice::Iter<'view, ProjectionRelationId>>
378 where
379 Self: 'view;
380
381 fn incoming_edges(&self, node: Self::ElementId) -> Self::InEdges<'_> {
382 self.in_edges[local_index(node)].iter().copied()
383 }
384}
385
386impl ElementSuccessors for GraphProjection {
387 type Successors<'view>
388 = std::iter::Copied<std::slice::Iter<'view, ProjectionElementId>>
389 where
390 Self: 'view;
391
392 fn element_successors(&self, element: Self::ElementId) -> Self::Successors<'_> {
393 self.successors[local_index(element)].iter().copied()
394 }
395}
396
397impl ElementPredecessors for GraphProjection {
398 type Predecessors<'view>
399 = std::iter::Copied<std::slice::Iter<'view, ProjectionElementId>>
400 where
401 Self: 'view;
402
403 fn element_predecessors(&self, element: Self::ElementId) -> Self::Predecessors<'_> {
404 self.predecessors[local_index(element)].iter().copied()
405 }
406}
407
408impl OutgoingEdgeCount for GraphProjection {
409 fn out_degree(&self, node: Self::ElementId) -> usize {
410 self.out_edges[local_index(node)].len()
411 }
412}
413
414impl IncomingEdgeCount for GraphProjection {
415 fn in_degree(&self, node: Self::ElementId) -> usize {
416 self.in_edges[local_index(node)].len()
417 }
418}
419
420#[derive(Clone, Debug, Eq, PartialEq)]
430pub struct HypergraphProjection {
431 definition: HypergraphProjectionDefinition,
433 elements: Vec<ElementId>,
435 element_index: BTreeMap<ElementId, ProjectionElementId>,
437 relations: Vec<RelationId>,
439 relation_index: BTreeMap<RelationId, ProjectionRelationId>,
441 incidences: Vec<IncidenceRecord>,
443 incidence_index: BTreeMap<IncidenceId, ProjectionIncidenceId>,
445 relation_incidences: Vec<Vec<ProjectionIncidenceId>>,
447 element_incidences: Vec<Vec<ProjectionIncidenceId>>,
449 source_incidences: Vec<Vec<ProjectionIncidenceId>>,
451 target_incidences: Vec<Vec<ProjectionIncidenceId>>,
453 source_vertices: Vec<Vec<ProjectionElementId>>,
455 target_vertices: Vec<Vec<ProjectionElementId>>,
457 outgoing_hyperedges: Vec<Vec<ProjectionRelationId>>,
459 incoming_hyperedges: Vec<Vec<ProjectionRelationId>>,
461 successors: Vec<Vec<ProjectionElementId>>,
463 predecessors: Vec<Vec<ProjectionElementId>>,
465}
466
467impl HypergraphProjection {
468 pub(crate) fn from_state(
480 state: &DatabaseState,
481 definition: HypergraphProjectionDefinition,
482 ) -> Result<Self, DbError> {
483 if definition.source_roles.is_empty() || definition.target_roles.is_empty() {
484 return Err(DbError::invalid_projection(
485 "hypergraph projection requires non-empty source and target roles",
486 ));
487 }
488 let mut builder = HypergraphProjectionBuilder::new(definition);
489 for relation in state.relations() {
490 if relation_selected(relation, &builder.definition.relation_types) {
491 builder.push_relation(state, relation)?;
492 }
493 }
494 builder.finish()
495 }
496
497 #[must_use]
503 pub const fn definition(&self) -> &HypergraphProjectionDefinition {
504 &self.definition
505 }
506
507 pub(crate) fn subjects(&self) -> Vec<PropertySubject> {
509 let mut subjects =
510 Vec::with_capacity(self.elements.len() + self.relations.len() + self.incidences.len());
511 subjects.extend(self.elements.iter().copied().map(PropertySubject::Element));
512 subjects.extend(
513 self.relations
514 .iter()
515 .copied()
516 .map(PropertySubject::Relation),
517 );
518 subjects.extend(
519 self.incidences
520 .iter()
521 .map(|record| PropertySubject::Incidence(record.id)),
522 );
523 subjects.sort_unstable();
524 subjects
525 }
526}
527
528struct HypergraphProjectionBuilder {
530 definition: HypergraphProjectionDefinition,
532 elements: Vec<ElementId>,
534 element_index: BTreeMap<ElementId, ProjectionElementId>,
536 relations: Vec<RelationId>,
538 relation_index: BTreeMap<RelationId, ProjectionRelationId>,
540 incidences: Vec<IncidenceRecord>,
542 incidence_index: BTreeMap<IncidenceId, ProjectionIncidenceId>,
544 relation_incidences: Vec<Vec<ProjectionIncidenceId>>,
546 source_incidences: Vec<Vec<ProjectionIncidenceId>>,
548 target_incidences: Vec<Vec<ProjectionIncidenceId>>,
550 source_vertices: Vec<Vec<ProjectionElementId>>,
552 target_vertices: Vec<Vec<ProjectionElementId>>,
554}
555
556impl HypergraphProjectionBuilder {
557 const fn new(definition: HypergraphProjectionDefinition) -> Self {
559 Self {
560 definition,
561 elements: Vec::new(),
562 element_index: BTreeMap::new(),
563 relations: Vec::new(),
564 relation_index: BTreeMap::new(),
565 incidences: Vec::new(),
566 incidence_index: BTreeMap::new(),
567 relation_incidences: Vec::new(),
568 source_incidences: Vec::new(),
569 target_incidences: Vec::new(),
570 source_vertices: Vec::new(),
571 target_vertices: Vec::new(),
572 }
573 }
574
575 fn push_relation(
577 &mut self,
578 state: &DatabaseState,
579 relation: &RelationRecord,
580 ) -> Result<(), DbError> {
581 let hyperedge = projection_relation_id(self.relations.len())?;
582 self.relations.push(relation.id);
583 self.relation_index.insert(relation.id, hyperedge);
584 let mut relation_ids = Vec::new();
585 let mut source_ids = Vec::new();
586 let mut target_ids = Vec::new();
587 let mut sources = Vec::new();
588 let mut targets = Vec::new();
589 for incidence in state.relation_incidences(relation.id) {
590 let local = self.push_incidence(incidence)?;
591 let vertex = intern_element(
592 &mut self.element_index,
593 &mut self.elements,
594 incidence.element,
595 )?;
596 relation_ids.push(local);
597 if self.definition.source_roles.contains(&incidence.role) {
598 source_ids.push(local);
599 sources.push(vertex);
600 }
601 if self.definition.target_roles.contains(&incidence.role) {
602 target_ids.push(local);
603 targets.push(vertex);
604 }
605 }
606 if sources.is_empty() || targets.is_empty() {
607 return Err(DbError::invalid_projection(
608 "selected hyperedge lacks source or target participants",
609 ));
610 }
611 self.relation_incidences.push(relation_ids);
612 self.source_incidences.push(source_ids);
613 self.target_incidences.push(target_ids);
614 self.source_vertices.push(sources);
615 self.target_vertices.push(targets);
616 Ok(())
617 }
618
619 fn push_incidence(
621 &mut self,
622 incidence: &IncidenceRecord,
623 ) -> Result<ProjectionIncidenceId, DbError> {
624 if let Some(id) = self.incidence_index.get(&incidence.id) {
625 return Ok(*id);
626 }
627 let local = projection_incidence_id(self.incidences.len())?;
628 self.incidences.push(*incidence);
629 self.incidence_index.insert(incidence.id, local);
630 Ok(local)
631 }
632
633 fn finish(self) -> Result<HypergraphProjection, DbError> {
635 let element_incidences =
636 build_element_incidences(self.elements.len(), &self.incidences, &self.element_index)?;
637 let (outgoing_hyperedges, incoming_hyperedges, successors, predecessors) =
638 build_directed_vertex_arrays(
639 self.elements.len(),
640 &self.source_vertices,
641 &self.target_vertices,
642 )?;
643 Ok(HypergraphProjection {
644 definition: self.definition,
645 elements: self.elements,
646 element_index: self.element_index,
647 relations: self.relations,
648 relation_index: self.relation_index,
649 incidences: self.incidences,
650 incidence_index: self.incidence_index,
651 relation_incidences: self.relation_incidences,
652 element_incidences,
653 source_incidences: self.source_incidences,
654 target_incidences: self.target_incidences,
655 source_vertices: self.source_vertices,
656 target_vertices: self.target_vertices,
657 outgoing_hyperedges,
658 incoming_hyperedges,
659 successors,
660 predecessors,
661 })
662 }
663}
664
665impl TopologyBase for HypergraphProjection {
666 type ElementId = ProjectionElementId;
667 type RelationId = ProjectionRelationId;
668}
669
670impl IncidenceBase for HypergraphProjection {
671 type IncidenceId = ProjectionIncidenceId;
672 type Role = RoleId;
673}
674
675impl TopologyCounts for HypergraphProjection {
676 fn element_count(&self) -> usize {
677 self.elements.len()
678 }
679
680 fn relation_count(&self) -> usize {
681 self.relations.len()
682 }
683}
684
685impl HypergraphCounts for HypergraphProjection {}
686
687impl IncidenceCounts for HypergraphProjection {
688 fn incidence_count(&self) -> usize {
689 self.incidences.len()
690 }
691}
692
693impl ElementIndex for HypergraphProjection {
694 fn element_bound(&self) -> usize {
695 self.elements.len()
696 }
697
698 fn element_index(&self, element: Self::ElementId) -> usize {
699 local_index(element)
700 }
701}
702
703impl RelationIndex for HypergraphProjection {
704 fn relation_bound(&self) -> usize {
705 self.relations.len()
706 }
707
708 fn relation_index(&self, relation: Self::RelationId) -> usize {
709 local_relation_index(relation)
710 }
711}
712
713impl IncidenceIndex for HypergraphProjection {
714 fn incidence_bound(&self) -> usize {
715 self.incidences.len()
716 }
717
718 fn incidence_index(&self, incidence: Self::IncidenceId) -> usize {
719 local_incidence_index(incidence)
720 }
721}
722
723impl oxgraph_hyper::ContainsElement for HypergraphProjection {
724 fn contains_element(&self, element: Self::ElementId) -> bool {
725 local_index(element) < self.elements.len()
726 }
727}
728
729impl oxgraph_hyper::ContainsRelation for HypergraphProjection {
730 fn contains_relation(&self, relation: Self::RelationId) -> bool {
731 local_relation_index(relation) < self.relations.len()
732 }
733}
734
735impl oxgraph_hyper::ContainsIncidence for HypergraphProjection {
736 fn contains_incidence(&self, incidence: Self::IncidenceId) -> bool {
737 local_incidence_index(incidence) < self.incidences.len()
738 }
739}
740
741impl oxgraph_hyper::CanonicalElementIdentity for HypergraphProjection {
742 type CanonicalElementId = ElementId;
743
744 fn canonical_element_id(&self, element: Self::ElementId) -> Self::CanonicalElementId {
745 self.elements[local_index(element)]
746 }
747}
748
749impl oxgraph_hyper::LocalElementIdentity for HypergraphProjection {
750 fn local_element_id(&self, canonical: Self::CanonicalElementId) -> Option<Self::ElementId> {
751 self.element_index.get(&canonical).copied()
752 }
753}
754
755impl oxgraph_hyper::CanonicalRelationIdentity for HypergraphProjection {
756 type CanonicalRelationId = RelationId;
757
758 fn canonical_relation_id(&self, relation: Self::RelationId) -> Self::CanonicalRelationId {
759 self.relations[local_relation_index(relation)]
760 }
761}
762
763impl oxgraph_hyper::LocalRelationIdentity for HypergraphProjection {
764 fn local_relation_id(&self, canonical: Self::CanonicalRelationId) -> Option<Self::RelationId> {
765 self.relation_index.get(&canonical).copied()
766 }
767}
768
769impl oxgraph_hyper::CanonicalIncidenceIdentity for HypergraphProjection {
770 type CanonicalIncidenceId = IncidenceId;
771
772 fn canonical_incidence_id(&self, incidence: Self::IncidenceId) -> Self::CanonicalIncidenceId {
773 self.incidences[local_incidence_index(incidence)].id
774 }
775}
776
777impl oxgraph_hyper::LocalIncidenceIdentity for HypergraphProjection {
778 fn local_incidence_id(
779 &self,
780 canonical: Self::CanonicalIncidenceId,
781 ) -> Option<Self::IncidenceId> {
782 self.incidence_index.get(&canonical).copied()
783 }
784}
785
786impl RelationIncidences for HypergraphProjection {
787 type Incidences<'view>
788 = std::iter::Copied<std::slice::Iter<'view, ProjectionIncidenceId>>
789 where
790 Self: 'view;
791
792 fn relation_incidences(&self, relation: Self::RelationId) -> Self::Incidences<'_> {
793 self.relation_incidences[local_relation_index(relation)]
794 .iter()
795 .copied()
796 }
797}
798
799impl oxgraph_hyper::ElementIncidences for HypergraphProjection {
800 type Incidences<'view>
801 = std::iter::Copied<std::slice::Iter<'view, ProjectionIncidenceId>>
802 where
803 Self: 'view;
804
805 fn element_incidences(&self, element: Self::ElementId) -> Self::Incidences<'_> {
806 self.element_incidences[local_index(element)]
807 .iter()
808 .copied()
809 }
810}
811
812impl IncidenceElement for HypergraphProjection {
813 fn incidence_element(&self, incidence: Self::IncidenceId) -> Self::ElementId {
814 let canonical = self.incidences[local_incidence_index(incidence)].element;
815 self.element_index[&canonical]
816 }
817}
818
819impl IncidenceRelation for HypergraphProjection {
820 fn incidence_relation(&self, incidence: Self::IncidenceId) -> Self::RelationId {
821 let canonical = self.incidences[local_incidence_index(incidence)].relation;
822 self.relation_index[&canonical]
823 }
824}
825
826impl IncidenceRole for HypergraphProjection {
827 fn incidence_role(&self, incidence: Self::IncidenceId) -> Self::Role {
828 self.incidences[local_incidence_index(incidence)].role
829 }
830}
831
832impl RelationIncidenceCount for HypergraphProjection {
833 fn relation_incidence_count(&self, relation: Self::RelationId) -> usize {
834 self.relation_incidences[local_relation_index(relation)].len()
835 }
836}
837
838impl ElementIncidenceCount for HypergraphProjection {
839 fn element_incidence_count(&self, element: Self::ElementId) -> usize {
840 self.element_incidences[local_index(element)].len()
841 }
842}
843
844impl oxgraph_hyper::HyperedgeParticipants for HypergraphProjection {
845 type Participants<'view>
846 = HyperedgeParticipants<'view>
847 where
848 Self: 'view;
849
850 fn hyperedge_participants(&self, hyperedge: Self::RelationId) -> Self::Participants<'_> {
851 HyperedgeParticipants {
852 view: self,
853 inner: self.relation_incidences[local_relation_index(hyperedge)].iter(),
854 }
855 }
856}
857
858impl IncidentHyperedges for HypergraphProjection {
859 type IncidentHyperedges<'view>
860 = IncidentHyperedgesIter<'view>
861 where
862 Self: 'view;
863
864 fn incident_hyperedges(&self, vertex: Self::ElementId) -> Self::IncidentHyperedges<'_> {
865 IncidentHyperedgesIter {
866 view: self,
867 inner: self.element_incidences[local_index(vertex)].iter(),
868 }
869 }
870}
871
872impl HyperedgeParticipantCount for HypergraphProjection {
873 fn hyperedge_participant_count(&self, hyperedge: Self::RelationId) -> usize {
874 self.relation_incidences[local_relation_index(hyperedge)].len()
875 }
876}
877
878impl IncidentHyperedgeCount for HypergraphProjection {
879 fn incident_hyperedge_count(&self, vertex: Self::ElementId) -> usize {
880 self.element_incidences[local_index(vertex)].len()
881 }
882}
883
884impl DirectedHyperedgeParticipants for HypergraphProjection {
885 type SourceParticipants<'view>
886 = std::iter::Copied<std::slice::Iter<'view, ProjectionElementId>>
887 where
888 Self: 'view;
889
890 type TargetParticipants<'view>
891 = std::iter::Copied<std::slice::Iter<'view, ProjectionElementId>>
892 where
893 Self: 'view;
894
895 fn source_participants(&self, hyperedge: Self::RelationId) -> Self::SourceParticipants<'_> {
896 self.source_vertices[local_relation_index(hyperedge)]
897 .iter()
898 .copied()
899 }
900
901 fn target_participants(&self, hyperedge: Self::RelationId) -> Self::TargetParticipants<'_> {
902 self.target_vertices[local_relation_index(hyperedge)]
903 .iter()
904 .copied()
905 }
906}
907
908impl DirectedHyperedgeIncidences for HypergraphProjection {
909 type SourceIncidences<'view>
910 = std::iter::Copied<std::slice::Iter<'view, ProjectionIncidenceId>>
911 where
912 Self: 'view;
913
914 type TargetIncidences<'view>
915 = std::iter::Copied<std::slice::Iter<'view, ProjectionIncidenceId>>
916 where
917 Self: 'view;
918
919 fn source_incidences(&self, hyperedge: Self::RelationId) -> Self::SourceIncidences<'_> {
920 self.source_incidences[local_relation_index(hyperedge)]
921 .iter()
922 .copied()
923 }
924
925 fn target_incidences(&self, hyperedge: Self::RelationId) -> Self::TargetIncidences<'_> {
926 self.target_incidences[local_relation_index(hyperedge)]
927 .iter()
928 .copied()
929 }
930}
931
932impl DirectedVertexHyperedges for HypergraphProjection {
933 type OutgoingHyperedges<'view>
934 = std::iter::Copied<std::slice::Iter<'view, ProjectionRelationId>>
935 where
936 Self: 'view;
937
938 type IncomingHyperedges<'view>
939 = std::iter::Copied<std::slice::Iter<'view, ProjectionRelationId>>
940 where
941 Self: 'view;
942
943 fn outgoing_hyperedges(&self, vertex: Self::ElementId) -> Self::OutgoingHyperedges<'_> {
944 self.outgoing_hyperedges[local_index(vertex)]
945 .iter()
946 .copied()
947 }
948
949 fn incoming_hyperedges(&self, vertex: Self::ElementId) -> Self::IncomingHyperedges<'_> {
950 self.incoming_hyperedges[local_index(vertex)]
951 .iter()
952 .copied()
953 }
954}
955
956impl ElementSuccessors for HypergraphProjection {
957 type Successors<'view>
958 = std::iter::Copied<std::slice::Iter<'view, ProjectionElementId>>
959 where
960 Self: 'view;
961
962 fn element_successors(&self, element: Self::ElementId) -> Self::Successors<'_> {
963 self.successors[local_index(element)].iter().copied()
964 }
965}
966
967impl ElementPredecessors for HypergraphProjection {
968 type Predecessors<'view>
969 = std::iter::Copied<std::slice::Iter<'view, ProjectionElementId>>
970 where
971 Self: 'view;
972
973 fn element_predecessors(&self, element: Self::ElementId) -> Self::Predecessors<'_> {
974 self.predecessors[local_index(element)].iter().copied()
975 }
976}
977
978pub struct HyperedgeParticipants<'view> {
980 view: &'view HypergraphProjection,
982 inner: std::slice::Iter<'view, ProjectionIncidenceId>,
984}
985
986impl Iterator for HyperedgeParticipants<'_> {
987 type Item = ProjectionElementId;
988
989 fn next(&mut self) -> Option<Self::Item> {
990 self.inner
991 .next()
992 .map(|incidence| self.view.incidence_element(*incidence))
993 }
994}
995
996pub struct IncidentHyperedgesIter<'view> {
998 view: &'view HypergraphProjection,
1000 inner: std::slice::Iter<'view, ProjectionIncidenceId>,
1002}
1003
1004impl Iterator for IncidentHyperedgesIter<'_> {
1005 type Item = ProjectionRelationId;
1006
1007 fn next(&mut self) -> Option<Self::Item> {
1008 self.inner
1009 .next()
1010 .map(|incidence| self.view.incidence_relation(*incidence))
1011 }
1012}
1013
1014fn relation_selected(record: &RelationRecord, selected: &BTreeSet<RelationTypeId>) -> bool {
1016 record
1017 .relation_type
1018 .map_or(selected.is_empty(), |relation_type| {
1019 selected.is_empty() || selected.contains(&relation_type)
1020 })
1021}
1022
1023fn graph_endpoints(
1025 state: &DatabaseState,
1026 relation: RelationId,
1027 source_role: RoleId,
1028 target_role: RoleId,
1029) -> Result<(ElementId, ElementId), DbError> {
1030 let mut source = None;
1031 let mut target = None;
1032 for incidence in state.relation_incidences(relation) {
1033 update_endpoint(&mut source, incidence, source_role, "source")?;
1034 update_endpoint(&mut target, incidence, target_role, "target")?;
1035 }
1036 match (source, target) {
1037 (Some(source), Some(target)) => Ok((source, target)),
1038 _missing => Err(DbError::invalid_projection(
1039 "selected graph relation lacks source or target endpoint",
1040 )),
1041 }
1042}
1043
1044fn update_endpoint(
1046 slot: &mut Option<ElementId>,
1047 incidence: &IncidenceRecord,
1048 role: RoleId,
1049 name: &'static str,
1050) -> Result<(), DbError> {
1051 if incidence.role != role {
1052 return Ok(());
1053 }
1054 if slot.replace(incidence.element).is_some() {
1055 return Err(DbError::invalid_projection(format!(
1056 "selected graph relation has duplicate {name} endpoint"
1057 )));
1058 }
1059 Ok(())
1060}
1061
1062fn intern_element(
1064 index: &mut BTreeMap<ElementId, ProjectionElementId>,
1065 elements: &mut Vec<ElementId>,
1066 canonical: ElementId,
1067) -> Result<ProjectionElementId, DbError> {
1068 if let Some(local) = index.get(&canonical) {
1069 return Ok(*local);
1070 }
1071 let local = projection_element_id(elements.len())?;
1072 elements.push(canonical);
1073 index.insert(canonical, local);
1074 Ok(local)
1075}
1076
1077fn build_element_incidences(
1079 element_count: usize,
1080 incidences: &[IncidenceRecord],
1081 element_index: &BTreeMap<ElementId, ProjectionElementId>,
1082) -> Result<Vec<Vec<ProjectionIncidenceId>>, DbError> {
1083 let mut rows = vec![Vec::new(); element_count];
1084 for (index, incidence) in incidences.iter().enumerate() {
1085 let local = projection_incidence_id(index)?;
1086 let element = element_index
1087 .get(&incidence.element)
1088 .ok_or_else(|| DbError::invalid_projection("incidence element not projected"))?;
1089 rows[local_index(*element)].push(local);
1090 }
1091 Ok(rows)
1092}
1093
1094fn build_directed_vertex_arrays(
1096 element_count: usize,
1097 source_vertices: &[Vec<ProjectionElementId>],
1098 target_vertices: &[Vec<ProjectionElementId>],
1099) -> Result<DirectedVertexArrays, DbError> {
1100 let mut outgoing = vec![Vec::new(); element_count];
1101 let mut incoming = vec![Vec::new(); element_count];
1102 let mut successor_sets = vec![BTreeSet::new(); element_count];
1103 let mut predecessor_sets = vec![BTreeSet::new(); element_count];
1104 for (relation_index, (sources, targets)) in
1105 source_vertices.iter().zip(target_vertices).enumerate()
1106 {
1107 let relation = projection_relation_id(relation_index)?;
1108 for source in sources {
1109 outgoing[local_index(*source)].push(relation);
1110 for target in targets {
1111 successor_sets[local_index(*source)].insert(*target);
1112 }
1113 }
1114 for target in targets {
1115 incoming[local_index(*target)].push(relation);
1116 for source in sources {
1117 predecessor_sets[local_index(*target)].insert(*source);
1118 }
1119 }
1120 }
1121 let successors = successor_sets
1122 .into_iter()
1123 .map(|set| set.into_iter().collect())
1124 .collect();
1125 let predecessors = predecessor_sets
1126 .into_iter()
1127 .map(|set| set.into_iter().collect())
1128 .collect();
1129 Ok((outgoing, incoming, successors, predecessors))
1130}
1131
1132type DirectedVertexArrays = (
1134 Vec<Vec<ProjectionRelationId>>,
1135 Vec<Vec<ProjectionRelationId>>,
1136 Vec<Vec<ProjectionElementId>>,
1137 Vec<Vec<ProjectionElementId>>,
1138);
1139
1140fn projection_element_id(index: usize) -> Result<ProjectionElementId, DbError> {
1142 u32::try_from(index)
1143 .map(ProjectionElementId)
1144 .map_err(|_error| DbError::IdOverflow)
1145}
1146
1147fn projection_relation_id(index: usize) -> Result<ProjectionRelationId, DbError> {
1149 u32::try_from(index)
1150 .map(ProjectionRelationId)
1151 .map_err(|_error| DbError::IdOverflow)
1152}
1153
1154fn projection_incidence_id(index: usize) -> Result<ProjectionIncidenceId, DbError> {
1156 u32::try_from(index)
1157 .map(ProjectionIncidenceId)
1158 .map_err(|_error| DbError::IdOverflow)
1159}
1160
1161fn local_index(id: ProjectionElementId) -> usize {
1163 usize::try_from(id.0).unwrap_or(usize::MAX)
1164}
1165
1166fn local_relation_index(id: ProjectionRelationId) -> usize {
1168 usize::try_from(id.0).unwrap_or(usize::MAX)
1169}
1170
1171fn local_incidence_index(id: ProjectionIncidenceId) -> usize {
1173 usize::try_from(id.0).unwrap_or(usize::MAX)
1174}