Skip to main content

uqa_graph/memory_store/
trait_impl.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! `GraphStore` mutation, traversal, statistics, and id-allocation contract.
8
9use super::{
10    make_graphid, usize_to_f64_exact, BTreeMap, BTreeSet, Direction, Edge, EdgeId, GraphStore,
11    GraphStoreError, GraphStoreResult, LabelKind, MemoryGraphStore, Vertex, VertexId,
12};
13
14impl GraphStore for MemoryGraphStore {
15    fn vertex_id_page(
16        &self,
17        graph: &str,
18        after: Option<u64>,
19        limit: usize,
20    ) -> GraphStoreResult<Vec<u64>> {
21        if !(1..=uqa_storage::MAX_GRAPH_ID_PAGE).contains(&limit) {
22            return Err(GraphStoreError::InvalidQuery(
23                "invalid graph page size".into(),
24            ));
25        }
26        Ok(self
27            .require_partition(graph)?
28            .vertex_ids
29            .iter()
30            .copied()
31            .filter(|id| after.is_none_or(|after| *id > after))
32            .take(limit)
33            .collect())
34    }
35    fn edge_id_page(
36        &self,
37        graph: &str,
38        after: Option<u64>,
39        limit: usize,
40    ) -> GraphStoreResult<Vec<u64>> {
41        if !(1..=uqa_storage::MAX_GRAPH_ID_PAGE).contains(&limit) {
42            return Err(GraphStoreError::InvalidQuery(
43                "invalid graph page size".into(),
44            ));
45        }
46        Ok(self
47            .require_partition(graph)?
48            .edge_ids
49            .iter()
50            .copied()
51            .filter(|id| after.is_none_or(|after| *id > after))
52            .take(limit)
53            .collect())
54    }
55    fn transaction<T>(
56        &mut self,
57        operation: impl FnOnce(&mut Self) -> GraphStoreResult<T>,
58    ) -> GraphStoreResult<T> {
59        let backup = self.clone();
60        match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| operation(self))) {
61            Ok(Ok(value)) => Ok(value),
62            Ok(Err(error)) => {
63                *self = backup;
64                Err(error)
65            }
66            Err(panic) => {
67                *self = backup;
68                std::panic::resume_unwind(panic)
69            }
70        }
71    }
72
73    fn create_graph(&mut self, name: &str) -> GraphStoreResult<()> {
74        MemoryGraphStore::create_graph(self, name);
75        Ok(())
76    }
77
78    fn drop_graph(&mut self, name: &str) -> GraphStoreResult<()> {
79        MemoryGraphStore::drop_graph(self, name);
80        Ok(())
81    }
82
83    fn graph_names(&self) -> GraphStoreResult<Vec<String>> {
84        Ok(MemoryGraphStore::graph_names(self))
85    }
86
87    fn has_graph(&self, name: &str) -> GraphStoreResult<bool> {
88        Ok(MemoryGraphStore::has_graph(self, name))
89    }
90
91    fn union_graphs(&mut self, g1: &str, g2: &str, target: &str) -> GraphStoreResult<()> {
92        let v_union: BTreeSet<VertexId> = self
93            .require_partition(g1)?
94            .vertex_ids
95            .union(&self.require_partition(g2)?.vertex_ids)
96            .copied()
97            .collect();
98        let e_union: BTreeSet<EdgeId> = self
99            .require_partition(g1)?
100            .edge_ids
101            .union(&self.require_partition(g2)?.edge_ids)
102            .copied()
103            .collect();
104        self.populate_graph_from_ids(&v_union, &e_union, target)
105    }
106
107    fn intersect_graphs(&mut self, g1: &str, g2: &str, target: &str) -> GraphStoreResult<()> {
108        let v_inter: BTreeSet<VertexId> = self
109            .require_partition(g1)?
110            .vertex_ids
111            .intersection(&self.require_partition(g2)?.vertex_ids)
112            .copied()
113            .collect();
114        let e_inter: BTreeSet<EdgeId> = self
115            .require_partition(g1)?
116            .edge_ids
117            .intersection(&self.require_partition(g2)?.edge_ids)
118            .copied()
119            .collect();
120        self.populate_graph_from_ids(&v_inter, &e_inter, target)
121    }
122
123    fn difference_graphs(&mut self, g1: &str, g2: &str, target: &str) -> GraphStoreResult<()> {
124        let v_diff: BTreeSet<VertexId> = self
125            .require_partition(g1)?
126            .vertex_ids
127            .difference(&self.require_partition(g2)?.vertex_ids)
128            .copied()
129            .collect();
130        let e_diff: BTreeSet<EdgeId> = self
131            .require_partition(g1)?
132            .edge_ids
133            .difference(&self.require_partition(g2)?.edge_ids)
134            .copied()
135            .collect();
136        self.populate_graph_from_ids(&v_diff, &e_diff, target)
137    }
138
139    fn copy_graph(&mut self, source: &str, target: &str) -> GraphStoreResult<()> {
140        let v_copy = self.require_partition(source)?.vertex_ids.clone();
141        let e_copy = self.require_partition(source)?.edge_ids.clone();
142        self.populate_graph_from_ids(&v_copy, &e_copy, target)
143    }
144
145    fn add_vertex(&mut self, vertex: Vertex, graph: &str) -> GraphStoreResult<()> {
146        self.require_partition(graph)?;
147        let vid = vertex.vertex_id;
148        let next_vertex_id = if vid >= self.next_vertex_id {
149            vid.checked_add(1).ok_or_else(|| {
150                GraphStoreError::IdExhausted("vertex id counter overflow".to_string())
151            })?
152        } else {
153            self.next_vertex_id
154        };
155        let mut owners = self
156            .vertex_membership
157            .get(&vid)
158            .cloned()
159            .unwrap_or_default();
160        owners.insert(graph.to_owned());
161        for owner in &owners {
162            self.require_partition(owner)?;
163        }
164        let previous = self.vertices.insert(vid, vertex.clone());
165        for owner in &owners {
166            let partition = self.graphs.get_mut(owner).expect("validated vertex owner");
167            if let Some(previous) = &previous {
168                if let Some(ids) = partition.vertex_label_index.get_mut(&previous.label) {
169                    ids.remove(&vid);
170                }
171            }
172            partition.add_vertex(&vertex);
173        }
174        self.vertex_membership.insert(vid, owners);
175        self.next_vertex_id = next_vertex_id;
176        Ok(())
177    }
178
179    fn add_edge(&mut self, edge: Edge, graph: &str) -> GraphStoreResult<()> {
180        let mut owners = self
181            .edge_membership
182            .get(&edge.edge_id)
183            .cloned()
184            .unwrap_or_default();
185        owners.insert(graph.to_owned());
186        for owner in &owners {
187            let partition = self.require_partition(owner)?;
188            if !partition.vertex_ids.contains(&edge.source_id)
189                || !partition.vertex_ids.contains(&edge.target_id)
190            {
191                return Err(GraphStoreError::InvalidMutation(format!(
192                    "edge {} references endpoint outside graph {owner:?}: {} -> {}",
193                    edge.edge_id, edge.source_id, edge.target_id
194                )));
195            }
196            self.require_partition_vertex(partition, edge.source_id, owner)?;
197            self.require_partition_vertex(partition, edge.target_id, owner)?;
198        }
199        let eid = edge.edge_id;
200        let next_edge_id = if eid >= self.next_edge_id {
201            eid.checked_add(1).ok_or_else(|| {
202                GraphStoreError::IdExhausted("edge id counter overflow".to_string())
203            })?
204        } else {
205            self.next_edge_id
206        };
207        let previous = self.edges.insert(eid, edge.clone());
208        for owner in &owners {
209            let partition = self.graphs.get_mut(owner).expect("validated edge owner");
210            if let Some(previous) = &previous {
211                partition.remove_edge(previous);
212            }
213            partition.add_edge(&edge);
214        }
215        self.edge_membership.insert(eid, owners);
216        self.next_edge_id = next_edge_id;
217        Ok(())
218    }
219
220    fn remove_vertex(&mut self, vertex_id: VertexId, graph: &str) -> GraphStoreResult<()> {
221        let partition = self.require_partition_mut(graph)?;
222        if !partition.vertex_ids.remove(&vertex_id) {
223            return Ok(());
224        }
225        for vids in partition.vertex_label_index.values_mut() {
226            vids.remove(&vertex_id);
227        }
228        // Remove all incident edges from this graph.
229        let out_edges: Vec<EdgeId> = partition
230            .adj_out
231            .remove(&vertex_id)
232            .map(|s| s.into_iter().collect())
233            .unwrap_or_default();
234        let in_edges: Vec<EdgeId> = partition
235            .adj_in
236            .remove(&vertex_id)
237            .map(|s| s.into_iter().collect())
238            .unwrap_or_default();
239        let edge_ids_to_drop: Vec<EdgeId> = out_edges.into_iter().chain(in_edges).collect();
240        for eid in &edge_ids_to_drop {
241            if let Some(edge) = self.edges.get(eid).cloned() {
242                if let Some(part) = self.graphs.get_mut(graph) {
243                    part.remove_edge(&edge);
244                }
245                if let Some(set) = self.edge_membership.get_mut(eid) {
246                    set.remove(graph);
247                }
248            }
249        }
250        if let Some(set) = self.vertex_membership.get_mut(&vertex_id) {
251            set.remove(graph);
252        }
253        for eid in edge_ids_to_drop {
254            self.release_edge_if_orphan(eid);
255        }
256        self.release_vertex_if_orphan(vertex_id);
257        Ok(())
258    }
259
260    fn remove_edge(&mut self, edge_id: EdgeId, graph: &str) -> GraphStoreResult<()> {
261        self.require_partition(graph)?;
262        let Some(edge) = self.edges.get(&edge_id).cloned() else {
263            return Ok(());
264        };
265        let partition = self.require_partition_mut(graph)?;
266        if !partition.edge_ids.contains(&edge_id) {
267            return Ok(());
268        }
269        partition.remove_edge(&edge);
270        if let Some(set) = self.edge_membership.get_mut(&edge_id) {
271            set.remove(graph);
272        }
273        self.release_edge_if_orphan(edge_id);
274        Ok(())
275    }
276
277    fn neighbors(
278        &self,
279        vertex_id: VertexId,
280        label: Option<&str>,
281        direction: Direction,
282        graph: &str,
283    ) -> GraphStoreResult<Vec<VertexId>> {
284        let partition = self.require_partition(graph)?;
285        self.require_query_vertex(partition, vertex_id, graph)?;
286        let mut result = Vec::new();
287        let mut collect = |set: &BTreeSet<EdgeId>, take_target: bool| -> GraphStoreResult<()> {
288            for eid in set {
289                let edge = self.require_partition_edge(partition, *eid, graph)?;
290                let expected_endpoint = if take_target {
291                    edge.source_id
292                } else {
293                    edge.target_id
294                };
295                if expected_endpoint != vertex_id {
296                    return Err(GraphStoreError::CorruptGraph(format!(
297                        "graph {graph:?} adjacency for vertex {vertex_id} references edge {eid} with endpoints {} -> {}",
298                        edge.source_id, edge.target_id
299                    )));
300                }
301                if let Some(want) = label {
302                    if edge.label != want {
303                        continue;
304                    }
305                }
306                result.push(if take_target {
307                    edge.target_id
308                } else {
309                    edge.source_id
310                });
311            }
312            Ok(())
313        };
314        match direction {
315            Direction::Out => {
316                if let Some(set) = partition.adj_out.get(&vertex_id) {
317                    collect(set, true)?;
318                }
319            }
320            Direction::In => {
321                if let Some(set) = partition.adj_in.get(&vertex_id) {
322                    collect(set, false)?;
323                }
324            }
325            Direction::Both => {
326                if let Some(set) = partition.adj_out.get(&vertex_id) {
327                    collect(set, true)?;
328                }
329                if let Some(set) = partition.adj_in.get(&vertex_id) {
330                    collect(set, false)?;
331                }
332                result.sort_unstable();
333                result.dedup();
334            }
335        }
336        Ok(result)
337    }
338
339    fn vertices_by_label(&self, label: &str, graph: &str) -> GraphStoreResult<Vec<Vertex>> {
340        let partition = self.require_partition(graph)?;
341        partition
342            .vertex_label_index
343            .get(label)
344            .into_iter()
345            .flat_map(|set| set.iter())
346            .map(|vertex_id| {
347                self.require_partition_vertex(partition, *vertex_id, graph)
348                    .cloned()
349            })
350            .collect()
351    }
352
353    fn vertex_ids_by_label(&self, label: &str, graph: &str) -> GraphStoreResult<Vec<VertexId>> {
354        let partition = self.require_partition(graph)?;
355        partition
356            .vertex_label_index
357            .get(label)
358            .into_iter()
359            .flat_map(|set| set.iter())
360            .map(|vertex_id| {
361                self.require_partition_vertex(partition, *vertex_id, graph)?;
362                Ok(*vertex_id)
363            })
364            .collect()
365    }
366
367    fn vertices_in_graph(&self, graph: &str) -> GraphStoreResult<Vec<Vertex>> {
368        let partition = self.require_partition(graph)?;
369        partition
370            .vertex_ids
371            .iter()
372            .map(|vertex_id| {
373                self.require_partition_vertex(partition, *vertex_id, graph)
374                    .cloned()
375            })
376            .collect()
377    }
378
379    fn edges_in_graph(&self, graph: &str) -> GraphStoreResult<Vec<Edge>> {
380        let partition = self.require_partition(graph)?;
381        partition
382            .edge_ids
383            .iter()
384            .map(|edge_id| {
385                self.require_partition_edge(partition, *edge_id, graph)
386                    .cloned()
387            })
388            .collect()
389    }
390
391    fn vertex_graphs(&self, vertex_id: VertexId) -> GraphStoreResult<BTreeSet<String>> {
392        Ok(MemoryGraphStore::vertex_graphs(self, vertex_id))
393    }
394
395    fn edge_graphs(&self, edge_id: EdgeId) -> GraphStoreResult<BTreeSet<String>> {
396        Ok(self
397            .edge_membership
398            .get(&edge_id)
399            .cloned()
400            .unwrap_or_default())
401    }
402
403    fn out_edge_ids(&self, vertex_id: VertexId, graph: &str) -> GraphStoreResult<BTreeSet<EdgeId>> {
404        let partition = self.require_partition(graph)?;
405        self.require_query_vertex(partition, vertex_id, graph)?;
406        let edges = partition
407            .adj_out
408            .get(&vertex_id)
409            .cloned()
410            .unwrap_or_default();
411        for edge_id in &edges {
412            let edge = self.require_partition_edge(partition, *edge_id, graph)?;
413            if edge.source_id != vertex_id {
414                return Err(GraphStoreError::CorruptGraph(format!(
415                    "graph {graph:?} outgoing adjacency for vertex {vertex_id} references edge {edge_id} sourced at {}",
416                    edge.source_id
417                )));
418            }
419        }
420        Ok(edges)
421    }
422
423    fn in_edge_ids(&self, vertex_id: VertexId, graph: &str) -> GraphStoreResult<BTreeSet<EdgeId>> {
424        let partition = self.require_partition(graph)?;
425        self.require_query_vertex(partition, vertex_id, graph)?;
426        let edges = partition
427            .adj_in
428            .get(&vertex_id)
429            .cloned()
430            .unwrap_or_default();
431        for edge_id in &edges {
432            let edge = self.require_partition_edge(partition, *edge_id, graph)?;
433            if edge.target_id != vertex_id {
434                return Err(GraphStoreError::CorruptGraph(format!(
435                    "graph {graph:?} incoming adjacency for vertex {vertex_id} references edge {edge_id} targeted at {}",
436                    edge.target_id
437                )));
438            }
439        }
440        Ok(edges)
441    }
442
443    fn edge_ids_by_label(&self, label: &str, graph: &str) -> GraphStoreResult<BTreeSet<EdgeId>> {
444        let partition = self.require_partition(graph)?;
445        let edges = partition
446            .label_index
447            .get(label)
448            .cloned()
449            .unwrap_or_default();
450        for edge_id in &edges {
451            let edge = self.require_partition_edge(partition, *edge_id, graph)?;
452            if edge.label != label {
453                return Err(GraphStoreError::CorruptGraph(format!(
454                    "graph {graph:?} label index {label:?} references edge {edge_id} labelled {:?}",
455                    edge.label
456                )));
457            }
458        }
459        Ok(edges)
460    }
461
462    fn vertex_ids_in_graph(&self, graph: &str) -> GraphStoreResult<BTreeSet<VertexId>> {
463        let partition = self.require_partition(graph)?;
464        for vertex_id in &partition.vertex_ids {
465            self.require_partition_vertex(partition, *vertex_id, graph)?;
466        }
467        Ok(partition.vertex_ids.clone())
468    }
469
470    fn require_vertex_in_graph(&self, vertex_id: VertexId, graph: &str) -> GraphStoreResult<()> {
471        let partition = self.require_partition(graph)?;
472        self.require_query_vertex(partition, vertex_id, graph)
473    }
474
475    fn degree_distribution(&self, graph: &str) -> GraphStoreResult<BTreeMap<VertexId, u64>> {
476        let partition = self.require_partition(graph)?;
477        let mut out = BTreeMap::new();
478        for vid in &partition.vertex_ids {
479            self.require_partition_vertex(partition, *vid, graph)?;
480            let edge_ids = partition.adj_out.get(vid);
481            if let Some(edge_ids) = edge_ids {
482                for edge_id in edge_ids {
483                    let edge = self.require_partition_edge(partition, *edge_id, graph)?;
484                    if edge.source_id != *vid {
485                        return Err(GraphStoreError::CorruptGraph(format!(
486                            "graph {graph:?} outgoing adjacency for vertex {vid} references edge {edge_id} sourced at {}",
487                            edge.source_id
488                        )));
489                    }
490                }
491            }
492            let degree = u64::try_from(edge_ids.map_or(0, BTreeSet::len)).map_err(|_| {
493                GraphStoreError::CorruptGraph(format!("out-degree for vertex {vid} exceeds u64"))
494            })?;
495            out.insert(*vid, degree);
496        }
497        Ok(out)
498    }
499
500    fn label_degree(&self, label: &str, graph: &str) -> GraphStoreResult<f64> {
501        let partition = self.require_partition(graph)?;
502        let Some(eids) = partition.label_index.get(label) else {
503            return Ok(0.0);
504        };
505        if eids.is_empty() {
506            return Ok(0.0);
507        }
508        let mut sources: BTreeSet<VertexId> = BTreeSet::new();
509        for eid in eids {
510            let edge = self.require_partition_edge(partition, *eid, graph)?;
511            if edge.label != label {
512                return Err(GraphStoreError::CorruptGraph(format!(
513                    "graph {graph:?} label index {label:?} references edge {eid} labelled {:?}",
514                    edge.label
515                )));
516            }
517            sources.insert(edge.source_id);
518        }
519        if sources.is_empty() {
520            Ok(0.0)
521        } else {
522            Ok(usize_to_f64_exact(eids.len(), "edge label count")?
523                / usize_to_f64_exact(sources.len(), "edge label source count")?)
524        }
525    }
526
527    fn vertex_label_counts(&self, graph: &str) -> GraphStoreResult<BTreeMap<String, u64>> {
528        let partition = self.require_partition(graph)?;
529        let mut out = BTreeMap::new();
530        for (label, vids) in &partition.vertex_label_index {
531            for vertex_id in vids {
532                let vertex = self.require_partition_vertex(partition, *vertex_id, graph)?;
533                if vertex.label != *label {
534                    return Err(GraphStoreError::CorruptGraph(format!(
535                        "graph {graph:?} vertex label index {label:?} references vertex {vertex_id} labelled {:?}",
536                        vertex.label
537                    )));
538                }
539            }
540            let count = u64::try_from(vids.len()).map_err(|_| {
541                GraphStoreError::CorruptGraph(format!(
542                    "vertex count for label {label:?} exceeds u64"
543                ))
544            })?;
545            if count > 0 {
546                out.insert(label.clone(), count);
547            }
548        }
549        Ok(out)
550    }
551
552    fn get_vertex(&self, vertex_id: VertexId) -> GraphStoreResult<Option<Vertex>> {
553        Ok(MemoryGraphStore::get_vertex(self, vertex_id).cloned())
554    }
555
556    fn get_edge(&self, edge_id: EdgeId) -> GraphStoreResult<Option<Edge>> {
557        Ok(MemoryGraphStore::get_edge(self, edge_id).cloned())
558    }
559
560    fn next_vertex_id(&mut self) -> GraphStoreResult<VertexId> {
561        let id = self.next_vertex_id;
562        self.next_vertex_id = id.checked_add(1).ok_or_else(|| {
563            GraphStoreError::IdExhausted("vertex id counter overflow".to_string())
564        })?;
565        Ok(id)
566    }
567
568    fn next_edge_id(&mut self) -> GraphStoreResult<EdgeId> {
569        let id = self.next_edge_id;
570        self.next_edge_id = id
571            .checked_add(1)
572            .ok_or_else(|| GraphStoreError::IdExhausted("edge id counter overflow".to_string()))?;
573        Ok(id)
574    }
575
576    fn allocate_vertex_id(&mut self, label: &str, graph: &str) -> GraphStoreResult<VertexId> {
577        if !self.has_graph(graph) {
578            return Err(GraphStoreError::UnknownGraph(graph.to_string()));
579        }
580        let mut candidate = self
581            .label_registries
582            .get(graph)
583            .cloned()
584            .unwrap_or_default();
585        let label_id = candidate.label_id(label, LabelKind::Vertex)?;
586        let id = make_graphid(label_id, candidate.next_sequence(label_id)?)?;
587        self.label_registries.insert(graph.to_string(), candidate);
588        Ok(id)
589    }
590
591    fn allocate_edge_id(&mut self, label: &str, graph: &str) -> GraphStoreResult<EdgeId> {
592        if !self.has_graph(graph) {
593            return Err(GraphStoreError::UnknownGraph(graph.to_string()));
594        }
595        let mut candidate = self
596            .label_registries
597            .get(graph)
598            .cloned()
599            .unwrap_or_default();
600        let label_id = candidate.label_id(label, LabelKind::Edge)?;
601        let id = make_graphid(label_id, candidate.next_sequence(label_id)?)?;
602        self.label_registries.insert(graph.to_string(), candidate);
603        Ok(id)
604    }
605
606    fn clear(&mut self) -> GraphStoreResult<()> {
607        MemoryGraphStore::clear(self);
608        Ok(())
609    }
610
611    fn vertices(&self) -> GraphStoreResult<BTreeMap<VertexId, Vertex>> {
612        Ok(MemoryGraphStore::vertices(self))
613    }
614
615    fn edges(&self) -> GraphStoreResult<BTreeMap<EdgeId, Edge>> {
616        Ok(MemoryGraphStore::edges(self))
617    }
618}
619
620// Concrete memory-only accessors remain infallible; generic graph execution
621// uses the fallible, owned-record storage interface above.
622impl MemoryGraphStore {
623    pub fn create_graph(&mut self, name: &str) {
624        self.graphs.entry(name.to_string()).or_default();
625    }
626
627    pub fn drop_graph(&mut self, name: &str) {
628        // AGE drops every per-graph label table with the graph, so a
629        // re-created graph starts a fresh label / sequence space.
630        self.label_registries.remove(name);
631        let Some(partition) = self.graphs.remove(name) else {
632            return;
633        };
634        for vid in &partition.vertex_ids {
635            if let Some(set) = self.vertex_membership.get_mut(vid) {
636                set.remove(name);
637            }
638        }
639        for eid in &partition.edge_ids {
640            if let Some(set) = self.edge_membership.get_mut(eid) {
641                set.remove(name);
642            }
643        }
644        // Drop any vertex / edge that no longer has any membership.
645        let orphan_vertices: Vec<VertexId> = partition.vertex_ids.iter().copied().collect();
646        for vid in orphan_vertices {
647            self.release_vertex_if_orphan(vid);
648        }
649        let orphan_edges: Vec<EdgeId> = partition.edge_ids.iter().copied().collect();
650        for eid in orphan_edges {
651            self.release_edge_if_orphan(eid);
652        }
653    }
654
655    pub fn graph_names(&self) -> Vec<String> {
656        self.graphs.keys().cloned().collect()
657    }
658
659    pub fn has_graph(&self, name: &str) -> bool {
660        self.graphs.contains_key(name)
661    }
662
663    pub fn vertex_graphs(&self, vertex_id: VertexId) -> BTreeSet<String> {
664        self.vertex_membership
665            .get(&vertex_id)
666            .cloned()
667            .unwrap_or_default()
668    }
669
670    pub fn get_vertex(&self, vertex_id: VertexId) -> Option<&Vertex> {
671        self.vertices.get(&vertex_id)
672    }
673
674    pub fn get_edge(&self, edge_id: EdgeId) -> Option<&Edge> {
675        self.edges.get(&edge_id)
676    }
677
678    pub fn clear(&mut self) {
679        self.vertices.clear();
680        self.edges.clear();
681        self.graphs.clear();
682        self.vertex_membership.clear();
683        self.edge_membership.clear();
684        self.label_registries.clear();
685        self.next_vertex_id = 1;
686        self.next_edge_id = 1;
687    }
688
689    pub fn vertices(&self) -> BTreeMap<VertexId, Vertex> {
690        self.vertices.clone()
691    }
692
693    pub fn edges(&self) -> BTreeMap<EdgeId, Edge> {
694        self.edges.clone()
695    }
696}