mod graph;
mod phase;
mod scalar;
pub use phase::PhaseOptions;
use crate::graph::VType;
use crate::hash_graph::{EType, GraphLike};
use derive_more::{Display, Error, From};
use serde::{de, Deserialize, Serialize};
use std::collections::HashMap;
use std::path::Path;
pub fn encode_graph(graph: &impl crate::graph::GraphLike) -> Result<String, JsonError> {
let jg = JsonGraph::from_graph(graph)?;
let s = serde_json::to_string(&jg)?;
Ok(s)
}
pub fn write_graph(graph: &impl crate::graph::GraphLike, filename: &Path) -> Result<(), JsonError> {
let jg = JsonGraph::from_graph(graph)?;
let file = std::fs::File::create(filename).unwrap();
let writer = std::io::BufWriter::new(file);
serde_json::to_writer(writer, &jg)?;
Ok(())
}
pub fn decode_graph<G: GraphLike>(s: &str) -> Result<G, JsonError> {
let jg: JsonGraph = serde_json::from_str(s)?;
jg.to_graph()
}
pub fn read_graph<G: GraphLike>(filename: &Path) -> Result<G, JsonError> {
let file = std::fs::File::open(filename).unwrap();
let reader = std::io::BufReader::new(file);
let jg: JsonGraph = serde_json::from_reader(reader)?;
jg.to_graph()
}
type VertexName = String;
type EdgeName = String;
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct JsonGraph {
#[serde(default)]
wire_vertices: HashMap<VertexName, VertexAttrs>,
#[serde(default)]
node_vertices: HashMap<VertexName, VertexAttrs>,
#[serde(default)]
undir_edges: HashMap<EdgeName, EdgeAttrs>,
#[serde(default)]
variable_types: HashMap<String, String>,
#[serde(skip_serializing_if = "is_default")]
#[serde(default)]
#[serde(deserialize_with = "deserialize_scalar")]
#[serde(serialize_with = "serialize_scalar")]
scalar: Option<JsonScalar>,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
struct VertexAttrs {
#[serde(default)]
annotation: VertexAnnotations,
#[serde(default)]
data: VertexData,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
struct VertexData {
#[serde(rename = "type")]
typ: VType,
#[serde(skip_serializing_if = "is_default")]
#[serde(default)]
value: JsonPhase,
#[serde(skip_serializing_if = "is_default")]
#[serde(default)]
ground: bool,
#[serde(skip_serializing_if = "is_default")]
#[serde(default)]
#[serde(deserialize_with = "deserialize_bool")]
#[serde(serialize_with = "serialize_bool")]
is_edge: bool,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
struct VertexAnnotations {
#[serde(skip_serializing_if = "is_default")]
#[serde(default)]
boundary: bool,
#[serde(default)]
coord: (f64, f64),
#[serde(skip_serializing_if = "is_default")]
#[serde(default)]
#[serde(deserialize_with = "deserialize_io_attribute")]
input: Option<usize>,
#[serde(skip_serializing_if = "is_default")]
#[serde(default)]
#[serde(deserialize_with = "deserialize_io_attribute")]
output: Option<usize>,
#[serde(skip_serializing_if = "is_default")]
#[serde(default)]
label: Option<String>,
#[serde(flatten)]
other: HashMap<String, String>,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
struct EdgeAttrs {
src: VertexName,
tgt: VertexName,
#[serde(skip_serializing_if = "is_default")]
#[serde(default)]
#[serde(rename = "type")]
typ: EType,
}
#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(transparent)]
pub struct JsonPhase(String);
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct JsonScalar {
#[serde(default)]
power2: i32,
#[serde(default)]
phase: JsonPhase,
#[serde(default)]
#[serde(skip_serializing_if = "is_default")]
floatfactor: f64,
#[serde(default)]
#[serde(skip_serializing_if = "is_default")]
phasenodes: Vec<JsonPhase>,
#[serde(default)]
#[serde(skip_serializing_if = "is_default")]
is_zero: bool,
#[serde(default)]
#[serde(skip_serializing_if = "is_default")]
is_unknown: bool,
}
pub(crate) fn is_default<T: Default + PartialEq>(t: &T) -> bool {
*t == Default::default()
}
fn serialize_bool<S>(b: &bool, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match *b {
true => serializer.serialize_str("true"),
false => serializer.serialize_str("false"),
}
}
fn deserialize_bool<'de, D>(deserializer: D) -> Result<bool, D::Error>
where
D: de::Deserializer<'de>,
{
let s: &str = de::Deserialize::deserialize(deserializer)?;
match s {
"true" => Ok(true),
"false" => Ok(false),
_ => Err(de::Error::unknown_variant(s, &["true", "false"])),
}
}
fn deserialize_io_attribute<'de, D>(deserializer: D) -> Result<Option<usize>, D::Error>
where
D: de::Deserializer<'de>,
{
let val: serde_json::Value = de::Deserialize::deserialize(deserializer)?;
match val {
serde_json::Value::Number(n) => Ok(Some(n.as_u64().unwrap() as usize)),
serde_json::Value::Bool(b) => Ok(b.then_some(0)),
_ => Err(de::Error::invalid_value(
de::Unexpected::Str(&val.to_string()),
&"a number or a boolean",
)),
}
}
fn deserialize_scalar<'de, D>(deserializer: D) -> Result<Option<JsonScalar>, D::Error>
where
D: de::Deserializer<'de>,
{
let s: String = de::Deserialize::deserialize(deserializer)?;
if s.is_empty() {
return Ok(None);
}
let scalar = serde_json::from_str(&s).map_err(|_| {
de::Error::invalid_value(
de::Unexpected::Str(&s),
&"a json-encoded string representing a scalar",
)
})?;
Ok(Some(scalar))
}
fn serialize_scalar<S>(scalar: &Option<JsonScalar>, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if scalar.is_none() {
return serializer.serialize_str("");
}
let s = serde_json::to_string(scalar.as_ref().unwrap()).map_err(serde::ser::Error::custom)?;
serializer.serialize_str(&s)
}
#[derive(Debug, Display, Error, From)]
#[non_exhaustive]
pub enum JsonError {
#[display("Got an invalid phase value {phase}")]
InvalidPhase { phase: String },
#[display("Got an invalid phase value {phase} for node {name}")]
InvalidNodePhase { name: String, phase: String },
#[from]
SerdeError(serde_json::Error),
}
#[cfg(test)]
mod test {
use crate::graph::GraphLike;
use crate::vec_graph::{Graph, V};
use super::*;
use rstest::{fixture, rstest};
#[fixture]
fn simple_graph() -> (Graph, Vec<V>) {
let mut g = Graph::new();
let vs = vec![
g.add_vertex(VType::B),
g.add_vertex(VType::B),
g.add_vertex(VType::Z),
g.add_vertex(VType::X),
g.add_vertex(VType::X),
g.add_vertex(VType::Z),
g.add_vertex(VType::B),
g.add_vertex(VType::B),
];
g.set_inputs(vec![vs[0], vs[1]]);
g.set_outputs(vec![vs[6], vs[7]]);
g.add_edge(vs[0], vs[2]);
g.add_edge(vs[1], vs[3]);
g.add_edge(vs[2], vs[4]);
g.add_edge_with_type(vs[2], vs[3], EType::H);
g.add_edge(vs[3], vs[5]);
g.add_edge(vs[4], vs[6]);
g.add_edge(vs[5], vs[7]);
(g, vs)
}
const TEST_JSON_4Q_UNITARY: &str = include_str!("../../test_files/4-qubit-unitary.qgraph");
#[rstest]
fn json_roundtrip(simple_graph: (Graph, Vec<V>)) -> Result<(), JsonError> {
let (g, _) = simple_graph;
let jg = JsonGraph::from_graph(&g)?;
let s = serde_json::to_string(&jg).unwrap();
let g2: Graph = decode_graph(&s).unwrap();
assert_eq!(g.num_vertices(), g2.num_vertices());
assert_eq!(g.num_edges(), g2.num_edges());
let inp1 = g.inputs();
let inp2 = g2.inputs();
assert_eq!(inp1.len(), inp2.len());
for (v1, v2) in inp1.iter().zip(inp2.iter()) {
assert_eq!(g.vertex_type(*v1), g2.vertex_type(*v2));
let ns1 = g.neighbors(*v1);
let ns2 = g2.neighbors(*v2);
for (n1, n2) in ns1.zip(ns2) {
assert_eq!(g.vertex_type(n1), g2.vertex_type(n2));
}
}
Ok(())
}
#[rstest]
#[case::unitary_4q(TEST_JSON_4Q_UNITARY, 26, 30)]
fn json_decode(#[case] json: &str, #[case] num_vertices: usize, #[case] num_edges: usize) {
let g: Graph = decode_graph(json).unwrap();
assert_eq!(g.num_vertices(), num_vertices);
assert_eq!(g.num_edges(), num_edges);
}
}