Skip to content

Commit

Permalink
feat: Add hugr-to-json encoding, and validate it (#83)
Browse files Browse the repository at this point in the history
  • Loading branch information
aborgna-q authored Jan 15, 2024
2 parents 7fac597 + fb561eb commit b441a3e
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 23 deletions.
4 changes: 4 additions & 0 deletions guppy/hugr/hugr.py
Original file line number Diff line number Diff line change
Expand Up @@ -777,3 +777,7 @@ def to_raw(self) -> raw.RawHugr:
def serialize(self) -> bytes:
"""Serialize this Hugr in binary format."""
return self.to_raw().packb()

def serialize_json(self) -> str:
"""Serialize this Hugr in JSON format."""
return self.to_raw().to_json()
12 changes: 11 additions & 1 deletion guppy/hugr/raw.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Literal
from typing import Any, Literal

import ormsgpack
from pydantic import BaseModel
Expand All @@ -17,6 +17,16 @@ class RawHugr(BaseModel):
def packb(self) -> bytes:
return ormsgpack.packb(self.model_dump(), option=ormsgpack.OPT_NON_STR_KEYS)

def to_json(self) -> str:
"""Return a JSON representation of the Hugr."""
return self.model_dump_json()

@classmethod
def unpackb(cls, b: bytes) -> "RawHugr":
"""Decode a msgpack-encoded Hugr."""
return cls(**ormsgpack.unpackb(b, option=ormsgpack.OPT_NON_STR_KEYS))

@classmethod
def load_json(cls, json: dict[Any, Any]) -> "RawHugr":
"""Decode a JSON-encoded Hugr."""
return cls(**json)
6 changes: 6 additions & 0 deletions tests/integration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,14 @@ def export_test_cases_dir(request):
@pytest.fixture()
def validate(request, export_test_cases_dir):
def validate_impl(hugr, name=None):
# Validate via the msgpack encoding
bs = hugr.serialize()
util.validate_bytes(bs)

# Validate via the json encoding
js = hugr.serialize_json()
util.validate_json(js)

if export_test_cases_dir:
file_name = f"{request.node.name}{f'_{name}' if name else ''}.msgpack"
export_file = export_test_cases_dir / file_name
Expand Down
6 changes: 5 additions & 1 deletion tests/integration/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@


def validate_bytes(hugr: bytes):
validator.validate(hugr)
validator.validate_bytes(hugr)


def validate_json(hugr: str):
validator.validate_json(hugr)


class Decorator:
Expand Down
39 changes: 20 additions & 19 deletions validator/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions validator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ pyo3 = "0.19.0"
quantinuum-hugr = { git = "https://github.com/CQCL/hugr.git", rev = "d07406c13b03a93014ae08a70bb789b25e0b31eb" }
rmp-serde = "1.1.1"
lazy_static = "1.4.0"
serde_json = "1.0.111"
14 changes: 12 additions & 2 deletions validator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,25 @@ lazy_static! {
.unwrap();
}

/// Validate a msgpack-encoded Hugr
#[pyfunction]
fn validate(hugr: Vec<u8>) -> PyResult<()> {
fn validate_bytes(hugr: Vec<u8>) -> PyResult<()> {
let hg: hugr::Hugr = rmp_serde::from_slice(&hugr).unwrap();
hg.validate(&REGISTRY).unwrap();
Ok(())
}

/// Validate a json-encoded Hugr
#[pyfunction]
fn validate_json(hugr: String) -> PyResult<()> {
let hg: hugr::Hugr = serde_json::from_str(&hugr).unwrap();
hg.validate(&REGISTRY).unwrap();
Ok(())
}

#[pymodule]
fn validator(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(validate, m)?)?;
m.add_function(wrap_pyfunction!(validate_bytes, m)?)?;
m.add_function(wrap_pyfunction!(validate_json, m)?)?;
Ok(())
}

0 comments on commit b441a3e

Please sign in to comment.