1#![allow(clippy::type_complexity)]
14
15use std::borrow::Borrow;
16use std::collections::HashMap;
17use std::iter::Iterator;
18use std::path::{Path, PathBuf};
19
20use anyhow::{anyhow, bail, Context, Result};
21use dsi_progress_logger::{ConcurrentProgressLog, ProgressLog};
22use rayon::prelude::*;
23use webgraph::graphs::vec_graph::LabeledVecGraph;
24use webgraph::prelude::*;
25
26use crate::arc_iterators::{
27 DelabelingIterator, LabelTypingSuccessorIterator, LabeledArcIterator, LabeledSuccessorIterator,
28};
29pub use crate::arc_iterators::IntoFlattenedLabeledArcsIterator;
31use crate::labeling::SwhLabeling;
32use crate::labels::{EdgeLabel, UntypedEdgeLabel};
33use crate::mph::LoadableSwhidMphf;
34use crate::properties;
35pub use crate::underlying_graph::DefaultUnderlyingGraph;
36use crate::utils::shuffle::par_iter_shuffled_range;
37use crate::utils::suffix_path;
38use crate::NodeType;
39
40pub type NodeId = usize;
42
43pub trait UnderlyingGraph: RandomAccessLabeling {
52 type UnlabeledSuccessors<'succ>: IntoIterator<Item = NodeId>
53 where
54 Self: 'succ;
55
56 fn num_arcs(&self) -> u64;
63 fn has_arc(&self, src_node_id: NodeId, dst_node_id: NodeId) -> bool;
64 fn unlabeled_successors(&self, node_id: NodeId) -> Self::UnlabeledSuccessors<'_>;
65}
66
67impl<F: RandomAccessDecoderFactory> UnderlyingGraph for BvGraph<F> {
68 type UnlabeledSuccessors<'succ>
69 = <Self as RandomAccessLabeling>::Labels<'succ>
70 where
71 Self: 'succ;
72
73 fn num_arcs(&self) -> u64 {
74 <Self as RandomAccessLabeling>::num_arcs(self)
75 }
76 fn has_arc(&self, src_node_id: NodeId, dst_node_id: NodeId) -> bool {
77 <Self as RandomAccessGraph>::has_arc(self, src_node_id, dst_node_id)
78 }
79 fn unlabeled_successors(&self, node_id: NodeId) -> Self::UnlabeledSuccessors<'_> {
80 <Self as RandomAccessGraph>::successors(self, node_id)
81 }
82}
83
84impl<G: UnderlyingGraph, L: RandomAccessLabeling> UnderlyingGraph for Zip<G, L> {
85 type UnlabeledSuccessors<'succ>
86 = <G as UnderlyingGraph>::UnlabeledSuccessors<'succ>
87 where
88 Self: 'succ;
89
90 fn num_arcs(&self) -> u64 {
91 <G as UnderlyingGraph>::num_arcs(&self.0)
92 }
93 fn has_arc(&self, src_node_id: NodeId, dst_node_id: NodeId) -> bool {
94 self.0.has_arc(src_node_id, dst_node_id)
95 }
96 fn unlabeled_successors(&self, node_id: NodeId) -> Self::UnlabeledSuccessors<'_> {
97 self.0.unlabeled_successors(node_id)
98 }
99}
100
101impl UnderlyingGraph for VecGraph {
102 type UnlabeledSuccessors<'succ>
103 = <Self as RandomAccessLabeling>::Labels<'succ>
104 where
105 Self: 'succ;
106
107 fn num_arcs(&self) -> u64 {
108 <Self as RandomAccessLabeling>::num_arcs(self)
109 }
110 fn has_arc(&self, src_node_id: NodeId, dst_node_id: NodeId) -> bool {
111 <Self as RandomAccessGraph>::has_arc(self, src_node_id, dst_node_id)
112 }
113 fn unlabeled_successors(&self, node_id: NodeId) -> Self::UnlabeledSuccessors<'_> {
114 <Self as RandomAccessGraph>::successors(self, node_id)
115 }
116}
117
118impl<L: Clone> UnderlyingGraph for LabeledVecGraph<L> {
119 type UnlabeledSuccessors<'succ>
120 = DelabelingIterator<<Self as RandomAccessLabeling>::Labels<'succ>>
121 where
122 Self: 'succ;
123
124 fn num_arcs(&self) -> u64 {
125 <Self as RandomAccessLabeling>::num_arcs(self)
126 }
127 fn has_arc(&self, src_node_id: NodeId, dst_node_id: NodeId) -> bool {
128 for succ in self.unlabeled_successors(src_node_id) {
129 if succ == dst_node_id {
130 return true;
131 }
132 }
133 false
134 }
135 fn unlabeled_successors(&self, node_id: NodeId) -> Self::UnlabeledSuccessors<'_> {
136 DelabelingIterator {
137 successors: <Self as RandomAccessLabeling>::labels(self, node_id),
138 }
139 }
140}
141
142pub trait SwhGraph {
143 fn path(&self) -> &Path;
145
146 fn is_transposed(&self) -> bool;
149
150 fn num_nodes(&self) -> usize;
158
159 fn has_node(&self, node_id: NodeId) -> bool {
164 node_id < self.num_nodes()
165 }
166
167 fn num_arcs(&self) -> u64;
169
170 fn num_nodes_by_type(&self) -> Result<HashMap<NodeType, usize>> {
172 let path = self.path().with_extension("nodes.stats.txt");
173 std::fs::read_to_string(&path)
174 .with_context(|| format!("Could not read {}", path.display()))?
175 .lines()
176 .map(|line| {
177 let (type_, count) = line
178 .split_once(' ')
179 .with_context(|| format!("Unexpected line in {}: {}", path.display(), line))?;
180 let type_: NodeType = type_
181 .parse()
182 .map_err(|_| anyhow!("Unknown node type in {}: {}", path.display(), type_))?;
183 let count = count
184 .parse()
185 .map_err(|_| anyhow!("Invalid integer in {}: {}", path.display(), count))?;
186 Ok((type_, count))
187 })
188 .collect()
189 }
190 fn actual_num_nodes(&self) -> Result<usize> {
192 Ok(self.num_nodes_by_type()?.values().sum())
193 }
194 fn num_arcs_by_type(&self) -> Result<HashMap<(NodeType, NodeType), usize>> {
196 let path = self.path().with_extension("edges.stats.txt");
197 std::fs::read_to_string(&path)
198 .with_context(|| format!("Could not read {}", path.display()))?
199 .lines()
200 .map(|line| {
201 let (type_, count) = line
202 .split_once(' ')
203 .with_context(|| format!("Unexpected line in {}: {}", path.display(), line))?;
204 let (src_type, dst_type) = type_
205 .split_once(':')
206 .with_context(|| format!("Unexpected line in {}: {}", path.display(), line))?;
207 let src_type: NodeType = src_type.parse().map_err(|_| {
208 anyhow!("Unknown node type in {}: {}", path.display(), src_type)
209 })?;
210 let dst_type: NodeType = dst_type.parse().map_err(|_| {
211 anyhow!("Unknown node type in {}: {}", path.display(), dst_type)
212 })?;
213 let count = count
214 .parse()
215 .map_err(|_| anyhow!("Invalid integer in {}: {}", path.display(), count))?;
216 Ok(((src_type, dst_type), count))
217 })
218 .collect()
219 }
220
221 fn has_arc(&self, src_node_id: NodeId, dst_node_id: NodeId) -> bool;
223
224 fn iter_nodes<'a>(
231 &'a self,
232 mut pl: impl ProgressLog + 'a,
233 ) -> impl Iterator<Item = NodeId> + 'a {
234 (0..self.num_nodes())
235 .inspect(move |_| pl.light_update())
236 .filter(|&node_id| self.has_node(node_id))
237 }
238 fn par_iter_nodes<'a>(
245 &'a self,
246 pl: impl ConcurrentProgressLog + 'a,
247 ) -> impl ParallelIterator<Item = NodeId> + 'a
248 where
249 Self: Sync,
250 {
251 par_iter_shuffled_range(0..self.num_nodes())
252 .map_with(pl, move |pl, node_id| {
253 pl.update();
254 node_id
255 })
256 .filter(|&node_id| self.has_node(node_id))
257 }
258}
259
260pub trait SwhForwardGraph: SwhGraph {
261 type Successors<'succ>: IntoIterator<Item = usize>
262 where
263 Self: 'succ;
264
265 fn successors(&self, node_id: NodeId) -> Self::Successors<'_>;
267 fn outdegree(&self, node_id: NodeId) -> usize;
269}
270
271#[diagnostic::on_unimplemented(
272 label = "does not have forward labels loaded",
273 note = "Use `let graph = graph.load_labels()` to load them"
274)]
275pub trait SwhLabeledForwardGraph:
276 SwhForwardGraph + SwhGraphWithProperties<Maps: properties::Maps>
277{
278 type LabeledArcs<'arc>: Iterator<Item = UntypedEdgeLabel>
279 where
280 Self: 'arc;
281 type LabeledSuccessors<'node>: IntoIterator<Item = (usize, Self::LabeledArcs<'node>)>
282 + IntoFlattenedLabeledArcsIterator<UntypedEdgeLabel>
283 where
284 Self: 'node;
285
286 fn untyped_labeled_successors(&self, node_id: NodeId) -> Self::LabeledSuccessors<'_>;
289
290 fn labeled_successors(
293 &self,
294 node_id: NodeId,
295 ) -> impl Iterator<Item = (usize, impl Iterator<Item = EdgeLabel>)>
296 + IntoFlattenedLabeledArcsIterator<EdgeLabel>
297 + '_ {
298 LabelTypingSuccessorIterator {
299 graph: self,
300 is_transposed: false,
301 src: node_id,
302 successors: self.untyped_labeled_successors(node_id).into_iter(),
303 }
304 }
305}
306
307#[diagnostic::on_unimplemented(
308 label = "does not have backward arcs loaded",
309 note = "Use SwhBidirectionalGraph instead of SwhUnidirectionalGraph if you don't already"
310)]
311pub trait SwhBackwardGraph: SwhGraph {
312 type Predecessors<'succ>: IntoIterator<Item = usize>
313 where
314 Self: 'succ;
315
316 fn predecessors(&self, node_id: NodeId) -> Self::Predecessors<'_>;
318 fn indegree(&self, node_id: NodeId) -> usize;
320}
321
322#[diagnostic::on_unimplemented(
323 label = "does not have backward labels loaded",
324 note = "Use SwhBidirectionalGraph instead of SwhUnidirectionalGraph if you don't already",
325 note = "and `let graph = graph.load_labels()`"
326)]
327pub trait SwhLabeledBackwardGraph:
328 SwhBackwardGraph + SwhGraphWithProperties<Maps: properties::Maps>
329{
330 type LabeledArcs<'arc>: Iterator<Item = UntypedEdgeLabel>
331 where
332 Self: 'arc;
333 type LabeledPredecessors<'node>: IntoIterator<Item = (usize, Self::LabeledArcs<'node>)>
334 + IntoFlattenedLabeledArcsIterator<UntypedEdgeLabel>
335 where
336 Self: 'node;
337
338 fn untyped_labeled_predecessors(&self, node_id: NodeId) -> Self::LabeledPredecessors<'_>;
341
342 fn labeled_predecessors(
345 &self,
346 node_id: NodeId,
347 ) -> impl IntoIterator<Item = (usize, impl Iterator<Item = crate::labels::EdgeLabel>)>
348 + IntoFlattenedLabeledArcsIterator<EdgeLabel>
349 + '_ {
350 LabelTypingSuccessorIterator {
351 graph: self,
352 is_transposed: true,
353 src: node_id,
354 successors: self.untyped_labeled_predecessors(node_id).into_iter(),
355 }
356 }
357}
358
359pub trait SwhGraphWithProperties: SwhGraph {
360 type Maps: properties::MaybeMaps;
361 type Timestamps: properties::MaybeTimestamps;
362 type Persons: properties::MaybePersons;
363 type Contents: properties::MaybeContents;
364 type Strings: properties::MaybeStrings;
365 type LabelNames: properties::MaybeLabelNames;
366
367 fn properties(
368 &self,
369 ) -> &properties::SwhGraphProperties<
370 Self::Maps,
371 Self::Timestamps,
372 Self::Persons,
373 Self::Contents,
374 Self::Strings,
375 Self::LabelNames,
376 >;
377}
378
379pub trait SwhFullGraph:
382 SwhLabeledForwardGraph
383 + SwhLabeledBackwardGraph
384 + SwhGraphWithProperties<
385 Maps: properties::Maps,
386 Timestamps: properties::Timestamps,
387 Persons: properties::Persons,
388 Contents: properties::Contents,
389 Strings: properties::Strings,
390 LabelNames: properties::LabelNames,
391 >
392{
393}
394
395impl<
396 G: SwhLabeledForwardGraph
397 + SwhLabeledBackwardGraph
398 + SwhGraphWithProperties<
399 Maps: properties::Maps,
400 Timestamps: properties::Timestamps,
401 Persons: properties::Persons,
402 Contents: properties::Contents,
403 Strings: properties::Strings,
404 LabelNames: properties::LabelNames,
405 >,
406 > SwhFullGraph for G
407{
408}
409
410pub struct SwhUnidirectionalGraph<P, G: UnderlyingGraph = DefaultUnderlyingGraph> {
420 basepath: PathBuf,
421 graph: G,
422 properties: P,
423}
424
425impl SwhUnidirectionalGraph<()> {
426 pub fn new(basepath: impl AsRef<Path>) -> Result<Self> {
427 let basepath = basepath.as_ref().to_owned();
428 let graph = DefaultUnderlyingGraph::new(&basepath)?;
429 Ok(Self::from_underlying_graph(basepath, graph))
430 }
431}
432
433impl<G: UnderlyingGraph> SwhUnidirectionalGraph<(), G> {
434 pub fn from_underlying_graph(basepath: PathBuf, graph: G) -> Self {
435 SwhUnidirectionalGraph {
436 basepath,
437 graph,
438 properties: (),
439 }
440 }
441}
442
443impl<P, G: UnderlyingGraph> SwhGraph for SwhUnidirectionalGraph<P, G> {
444 fn path(&self) -> &Path {
445 self.basepath.as_path()
446 }
447
448 fn is_transposed(&self) -> bool {
449 false
455 }
456
457 fn num_nodes(&self) -> usize {
458 self.graph.num_nodes()
459 }
460
461 fn num_arcs(&self) -> u64 {
462 UnderlyingGraph::num_arcs(&self.graph)
463 }
464
465 fn has_arc(&self, src_node_id: NodeId, dst_node_id: NodeId) -> bool {
466 self.graph.has_arc(src_node_id, dst_node_id)
467 }
468}
469
470impl<P, G: UnderlyingGraph> SwhForwardGraph for SwhUnidirectionalGraph<P, G> {
471 type Successors<'succ>
472 = <G as UnderlyingGraph>::UnlabeledSuccessors<'succ>
473 where
474 Self: 'succ;
475
476 fn successors(&self, node_id: NodeId) -> Self::Successors<'_> {
478 self.graph.unlabeled_successors(node_id)
479 }
480
481 fn outdegree(&self, node_id: NodeId) -> usize {
483 self.graph.outdegree(node_id)
484 }
485}
486
487impl<P, G: UnderlyingGraph> SwhLabeledForwardGraph for SwhUnidirectionalGraph<P, G>
488where
489 <G as SequentialLabeling>::Label: Pair<Left = NodeId, Right: IntoIterator<Item: Borrow<u64>>>,
490 for<'succ> <G as RandomAccessLabeling>::Labels<'succ>:
491 Iterator<Item = (usize, <<G as SequentialLabeling>::Label as Pair>::Right)>,
492 Self: SwhGraphWithProperties<Maps: properties::Maps>,
493{
494 type LabeledArcs<'arc> = LabeledArcIterator<<<<<G as RandomAccessLabeling>::Labels<'arc> as Iterator>::Item as Pair>::Right as IntoIterator>::IntoIter> where Self: 'arc;
495 type LabeledSuccessors<'succ>
496 = LabeledSuccessorIterator<<G as RandomAccessLabeling>::Labels<'succ>>
497 where
498 Self: 'succ;
499
500 fn untyped_labeled_successors(&self, node_id: NodeId) -> Self::LabeledSuccessors<'_> {
501 LabeledSuccessorIterator {
502 successors: self.graph.labels(node_id),
503 }
504 }
505}
506
507impl<
508 M: properties::MaybeMaps,
509 T: properties::MaybeTimestamps,
510 P: properties::MaybePersons,
511 C: properties::MaybeContents,
512 S: properties::MaybeStrings,
513 N: properties::MaybeLabelNames,
514 G: UnderlyingGraph,
515 > SwhUnidirectionalGraph<properties::SwhGraphProperties<M, T, P, C, S, N>, G>
516{
517 pub fn load_properties<
534 M2: properties::MaybeMaps,
535 T2: properties::MaybeTimestamps,
536 P2: properties::MaybePersons,
537 C2: properties::MaybeContents,
538 S2: properties::MaybeStrings,
539 N2: properties::MaybeLabelNames,
540 >(
541 self,
542 loader: impl FnOnce(
543 properties::SwhGraphProperties<M, T, P, C, S, N>,
544 ) -> Result<properties::SwhGraphProperties<M2, T2, P2, C2, S2, N2>>,
545 ) -> Result<SwhUnidirectionalGraph<properties::SwhGraphProperties<M2, T2, P2, C2, S2, N2>, G>>
546 {
547 Ok(SwhUnidirectionalGraph {
548 properties: loader(self.properties)?,
549 basepath: self.basepath,
550 graph: self.graph,
551 })
552 }
553}
554
555impl<G: UnderlyingGraph> SwhUnidirectionalGraph<(), G> {
556 pub fn init_properties(
558 self,
559 ) -> SwhUnidirectionalGraph<
560 properties::SwhGraphProperties<
561 properties::NoMaps,
562 properties::NoTimestamps,
563 properties::NoPersons,
564 properties::NoContents,
565 properties::NoStrings,
566 properties::NoLabelNames,
567 >,
568 G,
569 > {
570 SwhUnidirectionalGraph {
571 properties: properties::SwhGraphProperties::new(&self.basepath, self.graph.num_nodes()),
572 basepath: self.basepath,
573 graph: self.graph,
574 }
575 }
576
577 pub fn load_all_properties<MPHF: LoadableSwhidMphf>(
591 self,
592 ) -> Result<
593 SwhUnidirectionalGraph<
594 properties::SwhGraphProperties<
595 properties::MappedMaps<MPHF>,
596 properties::MappedTimestamps,
597 properties::MappedPersons,
598 properties::MappedContents,
599 properties::MappedStrings,
600 properties::MappedLabelNames,
601 >,
602 G,
603 >,
604 > {
605 self.init_properties()
606 .load_properties(|properties| properties.load_all())
607 }
608}
609
610impl<
611 MAPS: properties::MaybeMaps,
612 TIMESTAMPS: properties::MaybeTimestamps,
613 PERSONS: properties::MaybePersons,
614 CONTENTS: properties::MaybeContents,
615 STRINGS: properties::MaybeStrings,
616 LABELNAMES: properties::MaybeLabelNames,
617 G: UnderlyingGraph,
618 > SwhGraphWithProperties
619 for SwhUnidirectionalGraph<
620 properties::SwhGraphProperties<MAPS, TIMESTAMPS, PERSONS, CONTENTS, STRINGS, LABELNAMES>,
621 G,
622 >
623{
624 type Maps = MAPS;
625 type Timestamps = TIMESTAMPS;
626 type Persons = PERSONS;
627 type Contents = CONTENTS;
628 type Strings = STRINGS;
629 type LabelNames = LABELNAMES;
630
631 fn properties(
632 &self,
633 ) -> &properties::SwhGraphProperties<MAPS, TIMESTAMPS, PERSONS, CONTENTS, STRINGS, LABELNAMES>
634 {
635 &self.properties
636 }
637}
638
639impl<P, G: RandomAccessGraph + UnderlyingGraph> SwhUnidirectionalGraph<P, G> {
640 pub fn load_labels(self) -> Result<SwhUnidirectionalGraph<P, Zip<G, SwhLabeling>>> {
642 Ok(SwhUnidirectionalGraph {
643 graph: zip_labels(self.graph, suffix_path(&self.basepath, "-labelled"))
645 .context("Could not load forward labels")?,
646 properties: self.properties,
647 basepath: self.basepath,
648 })
649 }
650}
651
652pub struct SwhBidirectionalGraph<
664 P,
665 FG: UnderlyingGraph = DefaultUnderlyingGraph,
666 BG: UnderlyingGraph = DefaultUnderlyingGraph,
667> {
668 basepath: PathBuf,
669 forward_graph: FG,
670 backward_graph: BG,
671 properties: P,
672}
673
674impl SwhBidirectionalGraph<()> {
675 pub fn new(basepath: impl AsRef<Path>) -> Result<Self> {
676 let basepath = basepath.as_ref().to_owned();
677 let forward_graph = DefaultUnderlyingGraph::new(&basepath)?;
678 let backward_graph = DefaultUnderlyingGraph::new(suffix_path(&basepath, "-transposed"))?;
679 Ok(Self::from_underlying_graphs(
680 basepath,
681 forward_graph,
682 backward_graph,
683 ))
684 }
685}
686
687impl<FG: UnderlyingGraph, BG: UnderlyingGraph> SwhBidirectionalGraph<(), FG, BG> {
688 pub fn from_underlying_graphs(
689 basepath: PathBuf,
690 forward_graph: FG,
691 backward_graph: BG,
692 ) -> Self {
693 SwhBidirectionalGraph {
694 basepath,
695 backward_graph,
696 forward_graph,
697 properties: (),
698 }
699 }
700}
701
702impl<P, FG: UnderlyingGraph, BG: UnderlyingGraph> SwhGraph for SwhBidirectionalGraph<P, FG, BG> {
703 fn path(&self) -> &Path {
704 self.basepath.as_path()
705 }
706
707 fn is_transposed(&self) -> bool {
708 false
714 }
715
716 fn num_nodes(&self) -> usize {
717 self.forward_graph.num_nodes()
718 }
719
720 fn num_arcs(&self) -> u64 {
721 UnderlyingGraph::num_arcs(&self.forward_graph)
722 }
723
724 fn has_arc(&self, src_node_id: NodeId, dst_node_id: NodeId) -> bool {
725 self.forward_graph.has_arc(src_node_id, dst_node_id)
726 }
727}
728
729impl<P, FG: UnderlyingGraph, BG: UnderlyingGraph> SwhForwardGraph
730 for SwhBidirectionalGraph<P, FG, BG>
731{
732 type Successors<'succ>
733 = <FG as UnderlyingGraph>::UnlabeledSuccessors<'succ>
734 where
735 Self: 'succ;
736
737 fn successors(&self, node_id: NodeId) -> Self::Successors<'_> {
738 self.forward_graph.unlabeled_successors(node_id)
739 }
740 fn outdegree(&self, node_id: NodeId) -> usize {
741 self.forward_graph.outdegree(node_id)
742 }
743}
744
745impl<P, FG: UnderlyingGraph, BG: UnderlyingGraph> SwhLabeledForwardGraph
746 for SwhBidirectionalGraph<P, FG, BG>
747where
748 <FG as SequentialLabeling>::Label: Pair<Left = NodeId, Right: IntoIterator<Item: Borrow<u64>>>,
749 for<'succ> <FG as RandomAccessLabeling>::Labels<'succ>:
750 Iterator<Item = (usize, <<FG as SequentialLabeling>::Label as Pair>::Right)>,
751 Self: SwhGraphWithProperties<Maps: properties::Maps>,
752{
753 type LabeledArcs<'arc> = LabeledArcIterator<<<<<FG as RandomAccessLabeling>::Labels<'arc> as Iterator>::Item as Pair>::Right as IntoIterator>::IntoIter> where Self: 'arc;
754 type LabeledSuccessors<'succ>
755 = LabeledSuccessorIterator<<FG as RandomAccessLabeling>::Labels<'succ>>
756 where
757 Self: 'succ;
758
759 fn untyped_labeled_successors(&self, node_id: NodeId) -> Self::LabeledSuccessors<'_> {
760 LabeledSuccessorIterator {
761 successors: self.forward_graph.labels(node_id),
762 }
763 }
764}
765
766impl<P, FG: UnderlyingGraph, BG: UnderlyingGraph> SwhBackwardGraph
767 for SwhBidirectionalGraph<P, FG, BG>
768{
769 type Predecessors<'succ>
770 = <BG as UnderlyingGraph>::UnlabeledSuccessors<'succ>
771 where
772 Self: 'succ;
773
774 fn predecessors(&self, node_id: NodeId) -> Self::Predecessors<'_> {
775 self.backward_graph.unlabeled_successors(node_id)
776 }
777
778 fn indegree(&self, node_id: NodeId) -> usize {
779 self.backward_graph.outdegree(node_id)
780 }
781}
782
783impl<P, FG: UnderlyingGraph, BG: UnderlyingGraph> SwhLabeledBackwardGraph
784 for SwhBidirectionalGraph<P, FG, BG>
785where
786 <BG as SequentialLabeling>::Label: Pair<Left = NodeId, Right: IntoIterator<Item: Borrow<u64>>>,
787 for<'succ> <BG as RandomAccessLabeling>::Labels<'succ>:
788 Iterator<Item = (usize, <<BG as SequentialLabeling>::Label as Pair>::Right)>,
789 Self: SwhGraphWithProperties<Maps: properties::Maps>,
790{
791 type LabeledArcs<'arc> = LabeledArcIterator<<<<<BG as RandomAccessLabeling>::Labels<'arc> as Iterator>::Item as Pair>::Right as IntoIterator>::IntoIter> where Self: 'arc;
792 type LabeledPredecessors<'succ>
793 = LabeledSuccessorIterator<<BG as RandomAccessLabeling>::Labels<'succ>>
794 where
795 Self: 'succ;
796
797 fn untyped_labeled_predecessors(&self, node_id: NodeId) -> Self::LabeledPredecessors<'_> {
798 LabeledSuccessorIterator {
799 successors: self.backward_graph.labels(node_id),
800 }
801 }
802}
803
804impl<
805 M: properties::MaybeMaps,
806 T: properties::MaybeTimestamps,
807 P: properties::MaybePersons,
808 C: properties::MaybeContents,
809 S: properties::MaybeStrings,
810 N: properties::MaybeLabelNames,
811 BG: UnderlyingGraph,
812 FG: UnderlyingGraph,
813 > SwhBidirectionalGraph<properties::SwhGraphProperties<M, T, P, C, S, N>, FG, BG>
814{
815 pub fn load_properties<
833 M2: properties::MaybeMaps,
834 T2: properties::MaybeTimestamps,
835 P2: properties::MaybePersons,
836 C2: properties::MaybeContents,
837 S2: properties::MaybeStrings,
838 N2: properties::MaybeLabelNames,
839 >(
840 self,
841 loader: impl FnOnce(
842 properties::SwhGraphProperties<M, T, P, C, S, N>,
843 ) -> Result<properties::SwhGraphProperties<M2, T2, P2, C2, S2, N2>>,
844 ) -> Result<SwhBidirectionalGraph<properties::SwhGraphProperties<M2, T2, P2, C2, S2, N2>, FG, BG>>
845 {
846 Ok(SwhBidirectionalGraph {
847 properties: loader(self.properties)?,
848 basepath: self.basepath,
849 forward_graph: self.forward_graph,
850 backward_graph: self.backward_graph,
851 })
852 }
853}
854
855impl<FG: UnderlyingGraph, BG: UnderlyingGraph> SwhBidirectionalGraph<(), FG, BG> {
856 pub fn init_properties(
858 self,
859 ) -> SwhBidirectionalGraph<
860 properties::SwhGraphProperties<
861 properties::NoMaps,
862 properties::NoTimestamps,
863 properties::NoPersons,
864 properties::NoContents,
865 properties::NoStrings,
866 properties::NoLabelNames,
867 >,
868 FG,
869 BG,
870 > {
871 SwhBidirectionalGraph {
872 properties: properties::SwhGraphProperties::new(
873 &self.basepath,
874 self.forward_graph.num_nodes(),
875 ),
876 basepath: self.basepath,
877 forward_graph: self.forward_graph,
878 backward_graph: self.backward_graph,
879 }
880 }
881
882 pub fn load_all_properties<MPHF: LoadableSwhidMphf>(
896 self,
897 ) -> Result<
898 SwhBidirectionalGraph<
899 properties::SwhGraphProperties<
900 properties::MappedMaps<MPHF>,
901 properties::MappedTimestamps,
902 properties::MappedPersons,
903 properties::MappedContents,
904 properties::MappedStrings,
905 properties::MappedLabelNames,
906 >,
907 FG,
908 BG,
909 >,
910 > {
911 self.init_properties()
912 .load_properties(|properties| properties.load_all())
913 }
914}
915
916impl<
917 MAPS: properties::MaybeMaps,
918 TIMESTAMPS: properties::MaybeTimestamps,
919 PERSONS: properties::MaybePersons,
920 CONTENTS: properties::MaybeContents,
921 STRINGS: properties::MaybeStrings,
922 LABELNAMES: properties::MaybeLabelNames,
923 BG: UnderlyingGraph,
924 FG: UnderlyingGraph,
925 > SwhGraphWithProperties
926 for SwhBidirectionalGraph<
927 properties::SwhGraphProperties<MAPS, TIMESTAMPS, PERSONS, CONTENTS, STRINGS, LABELNAMES>,
928 FG,
929 BG,
930 >
931{
932 type Maps = MAPS;
933 type Timestamps = TIMESTAMPS;
934 type Persons = PERSONS;
935 type Contents = CONTENTS;
936 type Strings = STRINGS;
937 type LabelNames = LABELNAMES;
938
939 fn properties(
940 &self,
941 ) -> &properties::SwhGraphProperties<MAPS, TIMESTAMPS, PERSONS, CONTENTS, STRINGS, LABELNAMES>
942 {
943 &self.properties
944 }
945}
946
947impl<P, FG: RandomAccessGraph + UnderlyingGraph, BG: UnderlyingGraph>
948 SwhBidirectionalGraph<P, FG, BG>
949{
950 pub fn load_forward_labels(self) -> Result<SwhBidirectionalGraph<P, Zip<FG, SwhLabeling>, BG>> {
952 Ok(SwhBidirectionalGraph {
953 forward_graph: zip_labels(self.forward_graph, suffix_path(&self.basepath, "-labelled"))
954 .context("Could not load forward labels")?,
955 backward_graph: self.backward_graph,
956 properties: self.properties,
957 basepath: self.basepath,
958 })
959 }
960}
961
962impl<P, FG: UnderlyingGraph, BG: RandomAccessGraph + UnderlyingGraph>
963 SwhBidirectionalGraph<P, FG, BG>
964{
965 pub fn load_backward_labels(
967 self,
968 ) -> Result<SwhBidirectionalGraph<P, FG, Zip<BG, SwhLabeling>>> {
969 Ok(SwhBidirectionalGraph {
970 forward_graph: self.forward_graph,
971 backward_graph: zip_labels(
972 self.backward_graph,
973 suffix_path(&self.basepath, "-transposed-labelled"),
975 )
976 .context("Could not load backward labels")?,
977 properties: self.properties,
978 basepath: self.basepath,
979 })
980 }
981}
982
983impl<P, FG: RandomAccessGraph + UnderlyingGraph, BG: RandomAccessGraph + UnderlyingGraph>
984 SwhBidirectionalGraph<P, FG, BG>
985{
986 pub fn load_labels(
990 self,
991 ) -> Result<SwhBidirectionalGraph<P, Zip<FG, SwhLabeling>, Zip<BG, SwhLabeling>>> {
992 self.load_forward_labels()
993 .context("Could not load forward labels")?
994 .load_backward_labels()
995 .context("Could not load backward labels")
996 }
997}
998
999#[deprecated(
1000 since = "5.2.0",
1001 note = "please use `SwhUnidirectionalGraph::new` instead"
1002)]
1003pub fn load_unidirectional(basepath: impl AsRef<Path>) -> Result<SwhUnidirectionalGraph<()>> {
1005 SwhUnidirectionalGraph::new(basepath)
1006}
1007
1008#[deprecated(
1009 since = "5.2.0",
1010 note = "please use `SwhBidirectionalGraph::new` instead"
1011)]
1012pub fn load_bidirectional(basepath: impl AsRef<Path>) -> Result<SwhBidirectionalGraph<()>> {
1014 SwhBidirectionalGraph::new(basepath)
1015}
1016
1017pub fn load_full<MPHF: LoadableSwhidMphf>(
1044 basepath: impl AsRef<Path>,
1045) -> Result<
1046 SwhBidirectionalGraph<
1047 properties::SwhGraphProperties<
1048 properties::MappedMaps<MPHF>,
1049 properties::MappedTimestamps,
1050 properties::MappedPersons,
1051 properties::MappedContents,
1052 properties::MappedStrings,
1053 properties::MappedLabelNames,
1054 >,
1055 Zip<DefaultUnderlyingGraph, SwhLabeling>,
1056 Zip<DefaultUnderlyingGraph, SwhLabeling>,
1057 >,
1058> {
1059 SwhBidirectionalGraph::new(basepath)
1060 .context("Could not load graph")?
1061 .load_all_properties()
1062 .context("Could not load properties")?
1063 .load_labels()
1064 .context("Could not load labels")
1065}
1066
1067fn zip_labels<G: RandomAccessGraph + UnderlyingGraph, P: AsRef<Path>>(
1068 graph: G,
1069 base_path: P,
1070) -> Result<Zip<G, SwhLabeling>> {
1071 let properties_path = suffix_path(&base_path, ".properties");
1072 let f = std::fs::File::open(&properties_path)
1073 .with_context(|| format!("Could not open {}", properties_path.display()))?;
1074 let map = java_properties::read(std::io::BufReader::new(f))?;
1075
1076 let graphclass = map
1077 .get("graphclass")
1078 .with_context(|| format!("Missing 'graphclass' from {}", properties_path.display()))?;
1079 if graphclass != "it.unimi.dsi.big.webgraph.labelling.BitStreamArcLabelledImmutableGraph" {
1081 bail!(
1082 "Expected graphclass in {} to be \"it.unimi.dsi.big.webgraph.labelling.BitStreamArcLabelledImmutableGraph\", got {:?}",
1083 properties_path.display(),
1084 graphclass
1085 );
1086 }
1087
1088 let labelspec = map
1089 .get("labelspec")
1090 .with_context(|| format!("Missing 'labelspec' from {}", properties_path.display()))?;
1091 let width = labelspec
1092 .strip_prefix("org.softwareheritage.graph.labels.SwhLabel(DirEntry,")
1093 .and_then(|labelspec| labelspec.strip_suffix(')'))
1094 .and_then(|labelspec| labelspec.parse::<usize>().ok());
1095 let width = match width {
1096 None =>
1097 bail!("Expected labelspec in {} to be \"org.softwareheritage.graph.labels.SwhLabel(DirEntry,<integer>)\" (where <integer> is a small integer, usually under 30), got {:?}", properties_path.display(), labelspec),
1098 Some(width) => width
1099 };
1100
1101 let labels = crate::labeling::mmap(&base_path, crate::labeling::SwhDeserializer::new(width))
1102 .with_context(|| {
1103 format!(
1104 "Could not load labeling from {}",
1105 base_path.as_ref().display()
1106 )
1107 })?;
1108
1109 Ok(Zip(graph, labels))
1110}