graphannis_core/graph/storage/
mod.rs1pub mod adjacencylist;
2pub mod dense_adjacency;
3pub mod disk_adjacency;
4pub mod disk_path;
5pub mod linear;
6pub mod prepost;
7pub mod registry;
8pub mod union;
9
10pub(crate) mod legacy;
11
12use crate::annostorage::{EdgeAnnotationStorage, NodeAnnotationStorage};
13use crate::{
14 annostorage::AnnotationStorage,
15 errors::Result,
16 types::{AnnoKey, Annotation, Edge, NodeID},
17};
18use itertools::Itertools;
19use serde::{Deserialize, Serialize};
20use std::{self, path::Path};
21
22#[derive(Serialize, Deserialize, Clone)]
24pub struct GraphStatistic {
25 pub cyclic: bool,
27
28 pub rooted_tree: bool,
30
31 pub nodes: usize,
33
34 pub root_nodes: usize,
36
37 pub avg_fan_out: f64,
39 pub fan_out_99_percentile: usize,
41
42 pub inverse_fan_out_99_percentile: usize,
44
45 pub max_fan_out: usize,
47 pub max_depth: usize,
49
50 pub dfs_visit_ratio: f64,
52}
53
54impl std::fmt::Display for GraphStatistic {
55 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
56 write!(
57 f,
58 "nodes={}, root nodes={}, avg_fan_out={:.2}, max_fan_out={}, fan_out_99%={}, inv_fan_out_99%={}, max_depth={}",
59 self.nodes, self.root_nodes, self.avg_fan_out, self.max_fan_out, self.fan_out_99_percentile, self.inverse_fan_out_99_percentile, self.max_depth
60 )?;
61 if self.cyclic {
62 write!(f, ", cyclic")?;
63 }
64 if self.rooted_tree {
65 write!(f, ", tree")?;
66 }
67 Ok(())
68 }
69}
70
71pub trait EdgeContainer: Sync + Send {
73 fn get_outgoing_edges<'a>(
75 &'a self,
76 node: NodeID,
77 ) -> Box<dyn Iterator<Item = Result<NodeID>> + 'a>;
78
79 fn has_outgoing_edges(&self, node: NodeID) -> Result<bool> {
81 if let Some(outgoing) = self.get_outgoing_edges(node).next() {
82 outgoing?;
83 Ok(true)
84 } else {
85 Ok(false)
86 }
87 }
88
89 fn get_ingoing_edges<'a>(
91 &'a self,
92 node: NodeID,
93 ) -> Box<dyn Iterator<Item = Result<NodeID>> + 'a>;
94
95 fn has_ingoing_edges(&self, node: NodeID) -> Result<bool> {
97 if let Some(ingoing) = self.get_ingoing_edges(node).next() {
98 ingoing?;
99 Ok(true)
100 } else {
101 Ok(false)
102 }
103 }
104
105 fn get_statistics(&self) -> Option<&GraphStatistic> {
106 None
107 }
108
109 fn source_nodes<'a>(&'a self) -> Box<dyn Iterator<Item = Result<NodeID>> + 'a>;
111
112 fn root_nodes<'a>(&'a self) -> Box<dyn Iterator<Item = Result<NodeID>> + 'a> {
114 let it = self
116 .source_nodes()
117 .map(move |n| -> Result<Option<NodeID>> {
118 let n = n?;
119 if !self.has_ingoing_edges(n)? {
120 Ok(Some(n))
121 } else {
122 Ok(None)
123 }
124 })
125 .filter_map_ok(|n| n);
126 Box::new(it)
127 }
128}
129
130pub trait GraphStorage: EdgeContainer {
133 fn find_connected<'a>(
135 &'a self,
136 node: NodeID,
137 min_distance: usize,
138 max_distance: std::ops::Bound<usize>,
139 ) -> Box<dyn Iterator<Item = Result<NodeID>> + 'a>;
140
141 fn find_connected_inverse<'a>(
143 &'a self,
144 node: NodeID,
145 min_distance: usize,
146 max_distance: std::ops::Bound<usize>,
147 ) -> Box<dyn Iterator<Item = Result<NodeID>> + 'a>;
148
149 fn distance(&self, source: NodeID, target: NodeID) -> Result<Option<usize>>;
151
152 fn is_connected(
154 &self,
155 source: NodeID,
156 target: NodeID,
157 min_distance: usize,
158 max_distance: std::ops::Bound<usize>,
159 ) -> Result<bool>;
160
161 fn get_anno_storage(&self) -> &dyn EdgeAnnotationStorage;
163
164 fn copy(
167 &mut self,
168 node_annos: &dyn NodeAnnotationStorage,
169 orig: &dyn GraphStorage,
170 ) -> Result<()>;
171
172 fn as_edgecontainer(&self) -> &dyn EdgeContainer;
174
175 fn as_writeable(&mut self) -> Option<&mut dyn WriteableGraphStorage> {
178 None
179 }
180
181 fn inverse_has_same_cost(&self) -> bool {
183 false
184 }
185
186 fn serialization_id(&self) -> String;
188
189 fn load_from(location: &Path) -> Result<Self>
191 where
192 Self: std::marker::Sized;
193
194 fn save_to(&self, location: &Path) -> Result<()>;
196}
197
198pub fn serialize_gs_field<T>(field: &T, field_name: &str, location: &Path) -> Result<()>
199where
200 T: Serialize,
201{
202 let data_path = location.join(format!("{field_name}.bin"));
203 let f_data = std::fs::File::create(data_path)?;
204 let mut writer = std::io::BufWriter::new(f_data);
205 bincode::serialize_into(&mut writer, field)?;
206 Ok(())
207}
208
209pub fn deserialize_gs_field<T>(location: &Path, field_name: &str) -> Result<T>
210where
211 for<'de> T: std::marker::Sized + Deserialize<'de>,
212{
213 let data_path = location.join(format!("{field_name}.bin"));
214 let f_data = std::fs::File::open(data_path)?;
215 let input = std::io::BufReader::new(f_data);
216
217 let result = bincode::deserialize_from(input)?;
218 Ok(result)
219}
220
221const STATISTICS_FILE_NAME: &str = "stats.toml";
222
223pub fn load_statistics_from_location(location: &Path) -> Result<Option<GraphStatistic>> {
224 let stats_path_toml = location.join(STATISTICS_FILE_NAME);
225 let legacy_stats_path_bin = location.join("edge_stats.bin");
226
227 let stats = if stats_path_toml.is_file() {
228 let file_content = std::fs::read_to_string(stats_path_toml)?;
229 let stats: GraphStatistic = toml::from_str(&file_content)?;
230 Some(stats)
231 } else if legacy_stats_path_bin.is_file() {
232 let f_stats = std::fs::File::open(legacy_stats_path_bin)?;
233 let input = std::io::BufReader::new(f_stats);
234 let legacy_stats: Option<legacy::GraphStatisticV1> = bincode::deserialize_from(input)?;
236 legacy_stats.map(|s| s.into())
237 } else {
238 None
239 };
240 Ok(stats)
241}
242
243pub fn save_statistics_to_toml(location: &Path, stats: Option<&GraphStatistic>) -> Result<()> {
244 let file_path = location.join(STATISTICS_FILE_NAME);
245 if file_path.is_file() {
246 std::fs::remove_file(&file_path)?;
247 }
248
249 if let Some(stats) = stats {
250 let file_content = toml::to_string(stats)?;
251 std::fs::write(file_path, file_content)?;
252 }
253 Ok(())
254}
255
256pub trait WriteableGraphStorage: GraphStorage {
258 fn add_edge(&mut self, edge: Edge) -> Result<()>;
260
261 fn add_edge_annotation(&mut self, edge: Edge, anno: Annotation) -> Result<()>;
264
265 fn delete_edge(&mut self, edge: &Edge) -> Result<()>;
267
268 fn delete_edge_annotation(&mut self, edge: &Edge, anno_key: &AnnoKey) -> Result<()>;
270
271 fn delete_node(&mut self, node: NodeID) -> Result<()>;
274
275 fn calculate_statistics(&mut self) -> Result<()>;
277
278 fn clear(&mut self) -> Result<()>;
280}