Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add serde support for graphmap #496

Merged
merged 2 commits into from Jul 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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