Skip to content

Commit

Permalink
Add serde support for graphmap (#496)
Browse files Browse the repository at this point in the history
* Add from_graph function for graphmap

* Add serde support for graphmap
  • Loading branch information
Indeximal committed Jul 27, 2022
1 parent 302dec9 commit ffb085f
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 3 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion serialization-tests/Cargo.toml
Expand Up @@ -10,15 +10,17 @@ authors = [

description = ""
documentation = ""
repository = "https://github.com/bluss/petgraph"
repository = "https://github.com/petgraph/petgraph"


[dependencies]
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"
77 changes: 77 additions & 0 deletions serialization-tests/tests/serialization.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -486,4 +487,80 @@ quickcheck! {
let g2: StableGraph<i32, i32> = recode!(&g1);
assert_stable_graph_eq(&g1, &g2);
}

fn json_graphmap_to_graphmap(g1: DiGraphMap<i32, i32>) -> () {
let g2: DiGraphMap<i32, i32> = rejson!(&g1);
assert!(petgraph::algo::is_isomorphic(&g1, &g2));
}

fn bincode_graphmap_to_graphmap(g1: DiGraphMap<i8, ()>) -> () {
let g2: DiGraphMap<i8, ()> = recode!(&g1);
assert!(petgraph::algo::is_isomorphic(&g1, &g2));
}

fn bincode_graphmap_to_graph(g1: DiGraphMap<i8, i8>) -> () {
let g2: DiGraph<i8, i8> = 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<i32, u32, Directed> = 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<i32, u32, Directed> = 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<TestingNode, (u8, f32), Undirected> = 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<TestingNode, (u8, f32), Undirected> = 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.)
);
}
76 changes: 76 additions & 0 deletions src/graphmap.rs
Expand Up @@ -116,6 +116,51 @@ impl PartialEq<Direction> for CompactDirection {
}
}

#[cfg(feature = "serde-1")]
impl<N, E, Ty> serde::Serialize for GraphMap<N, E, Ty>
where
Ty: EdgeType,
N: NodeTrait + serde::Serialize,
E: serde::Serialize,
GraphMap<N, E, Ty>: 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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let cloned_graph: GraphMap<N, E, Ty> = GraphMap::clone(self);
let equivalent_graph: Graph<N, E, Ty, u32> = cloned_graph.into_graph();
equivalent_graph.serialize(serializer)
}
}

#[cfg(feature = "serde-1")]
impl<'de, N, E, Ty> serde::Deserialize<'de> for GraphMap<N, E, Ty>
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<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let equivalent_graph: Graph<N, E, Ty, u32> = Graph::deserialize(deserializer)?;
Ok(GraphMap::from_graph(equivalent_graph))
}
}

impl<N, E, Ty> GraphMap<N, E, Ty>
where
N: NodeTrait,
Expand Down Expand Up @@ -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<Ix>(graph: Graph<N, E, Ty, Ix>) -> Self
where
Ix: crate::graph::IndexType,
E: Clone,
{
let mut new_graph: GraphMap<N, E, Ty> =
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.
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Expand Up @@ -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** -
Expand Down
20 changes: 20 additions & 0 deletions tests/graphmap.rs
Expand Up @@ -325,6 +325,26 @@ fn test_into_graph() {
}
}

#[test]
fn test_from_graph() {
let mut gr: Graph<u32, u32, Directed> = 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<u32, u32, Directed> = 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
Expand Down

0 comments on commit ffb085f

Please sign in to comment.