diff --git a/README.md b/README.md index 5fbb4ced8..0c3c191b7 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Crate feature flags: - `graphmap` (default) enable `GraphMap`. - `stable_graph` (default) enable `StableGraph`. - `matrix_graph` (default) enable `MatrixGraph`. -- `serde-1` (optional) enable serialization for `Graph, StableGraph` +- `serde-1` (optional) enable serialization for `Graph, StableGraph, GraphMap` using serde 1.0. Requires Rust version as required by serde. ## Recent Changes diff --git a/serialization-tests/Cargo.toml b/serialization-tests/Cargo.toml index 04eeb35a3..0543d8f88 100644 --- a/serialization-tests/Cargo.toml +++ b/serialization-tests/Cargo.toml @@ -10,7 +10,7 @@ authors = [ description = "" documentation = "" -repository = "https://github.com/bluss/petgraph" +repository = "https://github.com/petgraph/petgraph" [dependencies] @@ -18,7 +18,9 @@ petgraph = { path = "..", features = ["serde-1", "quickcheck"] } itertools = { version = "0.10.1" } [dev-dependencies] +serde = "1.0" serde_json = "1.0" +serde_derive = "1.0" quickcheck = { version = "0.8", default-features = false } bincode = "1.3.3" defmac = "0.2.1" diff --git a/serialization-tests/tests/serialization.rs b/serialization-tests/tests/serialization.rs index cebffa581..0b87fdb25 100644 --- a/serialization-tests/tests/serialization.rs +++ b/serialization-tests/tests/serialization.rs @@ -3,6 +3,7 @@ extern crate petgraph; extern crate quickcheck; extern crate bincode; extern crate itertools; +extern crate serde_derive; extern crate serde_json; #[macro_use] extern crate defmac; @@ -486,4 +487,80 @@ quickcheck! { let g2: StableGraph = recode!(&g1); assert_stable_graph_eq(&g1, &g2); } + + fn json_graphmap_to_graphmap(g1: DiGraphMap) -> () { + let g2: DiGraphMap = rejson!(&g1); + assert!(petgraph::algo::is_isomorphic(&g1, &g2)); + } + + fn bincode_graphmap_to_graphmap(g1: DiGraphMap) -> () { + let g2: DiGraphMap = recode!(&g1); + assert!(petgraph::algo::is_isomorphic(&g1, &g2)); + } + + fn bincode_graphmap_to_graph(g1: DiGraphMap) -> () { + let g2: DiGraph = recode!(&g1); + assert!(petgraph::algo::is_isomorphic(&g1, &g2)); + } + + // graph to graphmap is not always possible because of parallel edges +} + +#[test] +fn json_graphmap_integer() { + let mut gr: GraphMap = GraphMap::from_edges(&[ + (6, 0, 0), + (0, 3, 1), + (3, 6, 2), + (8, 6, 3), + (8, 2, 4), + (2, 5, 5), + (5, 8, 6), + (7, 5, 7), + (1, 7, 8), + (7, 4, 9), + (4, 1, 10), + ]); + // unconnected node + gr.add_node(42); + + let gr_deser: GraphMap = rejson!(&gr); + assert!(petgraph::algo::is_isomorphic(&gr, &gr_deser)); + assert_eq!(gr_deser[(4, 1)], 10); +} + +#[test] +fn json_graphmap_struct() { + use serde_derive::{Deserialize, Serialize}; + + #[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] + struct TestingNode { + pub a: u32, + pub b: i32, + } + let mut gr: GraphMap = GraphMap::from_edges(&[ + ( + TestingNode { a: 42, b: -1 }, + TestingNode { a: 12, b: -2 }, + (1, 2.), + ), + ( + TestingNode { a: 12, b: -2 }, + TestingNode { a: 13, b: -3 }, + (99, 99.), + ), + ( + TestingNode { a: 13, b: -3 }, + TestingNode { a: 42, b: -1 }, + (99, 98.), + ), + ]); + gr.add_node(TestingNode { a: 0, b: 0 }); + + let gr_deser: GraphMap = rejson!(&gr); + assert!(petgraph::algo::is_isomorphic(&gr, &gr_deser)); + assert_eq!( + gr_deser[(TestingNode { a: 42, b: -1 }, TestingNode { a: 12, b: -2 })], + (1, 2.) + ); } diff --git a/src/graphmap.rs b/src/graphmap.rs index 3d047424a..75a6019a1 100644 --- a/src/graphmap.rs +++ b/src/graphmap.rs @@ -116,6 +116,51 @@ impl PartialEq for CompactDirection { } } +#[cfg(feature = "serde-1")] +impl serde::Serialize for GraphMap +where + Ty: EdgeType, + N: NodeTrait + serde::Serialize, + E: serde::Serialize, + GraphMap: Clone, +{ + /// Serializes the given `GraphMap` into the same format as the standard + /// `Graph`. Needs feature `serde-1`. + /// + /// Note: the graph has to be `Clone` for this to work. + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let cloned_graph: GraphMap = GraphMap::clone(self); + let equivalent_graph: Graph = cloned_graph.into_graph(); + equivalent_graph.serialize(serializer) + } +} + +#[cfg(feature = "serde-1")] +impl<'de, N, E, Ty> serde::Deserialize<'de> for GraphMap +where + Ty: EdgeType, + N: NodeTrait + serde::Deserialize<'de>, + E: Clone + serde::Deserialize<'de>, +{ + /// Deserializes into a new `GraphMap` from the same format as the standard + /// `Graph`. Needs feature `serde-1`. + /// + /// **Warning**: When deseralizing a graph that was not originally a `GraphMap`, + /// the restrictions from [`from_graph`](#method.from_graph) apply. + /// + /// Note: The edge weights have to be `Clone` for this to work. + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let equivalent_graph: Graph = Graph::deserialize(deserializer)?; + Ok(GraphMap::from_graph(equivalent_graph)) + } +} + impl GraphMap where N: NodeTrait, @@ -477,6 +522,37 @@ where } gr } + + /// Creates a `GraphMap` that corresponds to the given `Graph`. + /// + /// **Warning**: Nodes with the same weight are merged and only the last parallel edge + /// is kept. Node and edge indices of the `Graph` are lost. Only use this function + /// if the node weights are distinct and there are no parallel edges. + /// + /// Computes in **O(|V| + |E|)** time (average). + pub fn from_graph(graph: Graph) -> Self + where + Ix: crate::graph::IndexType, + E: Clone, + { + let mut new_graph: GraphMap = + GraphMap::with_capacity(graph.node_count(), graph.edge_count()); + + for node in graph.raw_nodes() { + new_graph.add_node(node.weight); + } + + for edge in graph.edge_indices() { + let (a, b) = graph.edge_endpoints(edge).unwrap(); + new_graph.add_edge( + *graph.node_weight(a).unwrap(), + *graph.node_weight(b).unwrap(), + graph.edge_weight(edge).unwrap().clone(), + ); + } + + new_graph + } } /// Create a new `GraphMap` from an iterable of edges. diff --git a/src/lib.rs b/src/lib.rs index 0d68648d6..e7be95e90 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -93,7 +93,7 @@ //! # Crate features //! //! * **serde-1** - -//! Defaults off. Enables serialization for ``Graph, StableGraph`` using +//! Defaults off. Enables serialization for ``Graph, StableGraph, GraphMap`` using //! [`serde 1.0`](https://crates.io/crates/serde). May require a more recent version //! of Rust than petgraph alone. //! * **graphmap** - diff --git a/tests/graphmap.rs b/tests/graphmap.rs index 42b9e2aa5..cc023258a 100644 --- a/tests/graphmap.rs +++ b/tests/graphmap.rs @@ -325,6 +325,26 @@ fn test_into_graph() { } } +#[test] +fn test_from_graph() { + let mut gr: Graph = Graph::new(); + let node_a = gr.add_node(12); + let node_b = gr.add_node(13); + let node_c = gr.add_node(14); + gr.add_edge(node_a, node_b, 1000); + gr.add_edge(node_b, node_c, 999); + gr.add_edge(node_c, node_a, 1111); + gr.add_node(42); + let gr = gr; + + let graph: GraphMap = GraphMap::from_graph(gr.clone()); + println!("{}", Dot::new(&gr)); + println!("{}", Dot::new(&graph)); + + assert!(petgraph::algo::is_isomorphic(&gr, &graph)); + assert_eq!(graph[(12, 13)], 1000); +} + #[test] fn test_all_edges_mut() { // graph with edge weights equal to in+out