Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions 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 hugr-py/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ bench = false

[dependencies]
bumpalo = { workspace = true, features = ["collections"] }
hugr-core = { version = "0.22.4", path = "../hugr-core", features = ["zstd"] }
hugr-model = { version = "0.22.4", path = "../hugr-model", features = ["pyo3"] }
pastey.workspace = true
pyo3 = { workspace = true, features = ["extension-module", "abi3-py310"] }
16 changes: 16 additions & 0 deletions hugr-py/rust/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
//! Supporting Rust library for the hugr Python bindings.

use hugr_core::{
envelope::{EnvelopeConfig, EnvelopeFormat, read_envelope, write_envelope},
std_extensions::STD_REG,
};
use hugr_model::v0::ast;
use pyo3::{exceptions::PyValueError, prelude::*};

Expand Down Expand Up @@ -50,6 +54,17 @@ fn bytes_to_package(bytes: &[u8]) -> PyResult<ast::Package> {
Ok(package)
}

/// Convert an envelope to a new envelope in JSON format.
#[pyfunction]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small docstring would help

fn to_json_envelope(bytes: &[u8]) -> PyResult<Vec<u8>> {
let (_, pkg) =
read_envelope(bytes, &STD_REG).map_err(|err| PyValueError::new_err(err.to_string()))?;
let config_json = EnvelopeConfig::new(EnvelopeFormat::PackageJson);
let mut json_data: Vec<u8> = Vec::new();
write_envelope(&mut json_data, &pkg, config_json).unwrap();
Ok(json_data)
}

/// Returns the current version of the HUGR model format as a tuple of (major, minor, patch).
#[pyfunction]
fn current_model_version() -> (u64, u64, u64) {
Expand Down Expand Up @@ -79,5 +94,6 @@ fn _hugr(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(symbol_to_string, m)?)?;
m.add_function(wrap_pyfunction!(string_to_symbol, m)?)?;
m.add_function(wrap_pyfunction!(current_model_version, m)?)?;
m.add_function(wrap_pyfunction!(to_json_envelope, m)?)?;
Ok(())
}
1 change: 1 addition & 0 deletions hugr-py/src/hugr/_hugr/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ def string_to_package(string: str) -> hugr.model.Package: ...
def package_to_bytes(package: hugr.model.Package) -> bytes: ...
def bytes_to_package(binary: bytes) -> hugr.model.Package: ...
def current_model_version() -> tuple[int, int, int]: ...
def to_json_envelope(binary: bytes) -> bytes: ...
9 changes: 7 additions & 2 deletions hugr-py/src/hugr/envelope.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@

import pyzstd

import hugr._hugr as rust

if TYPE_CHECKING:
from hugr.hugr.base import Hugr
from hugr.package import Package
Expand Down Expand Up @@ -112,8 +114,11 @@ def read_envelope(envelope: bytes) -> Package:
case EnvelopeFormat.JSON:
return ext_s.Package.model_validate_json(payload).deserialize()
case EnvelopeFormat.MODEL | EnvelopeFormat.MODEL_WITH_EXTS:
msg = "Decoding HUGR envelopes in MODULE format is not supported yet."
raise ValueError(msg)
# TODO Going via JSON is a temporary solution, until we get model import to
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be good to link to an issue here

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added link to #2287 ; will update the description there to clarify that this is the remaining task.

# python properly implemented.
# https://github.com/CQCL/hugr/issues/2287
json_data = rust.to_json_envelope(envelope)
return read_envelope(json_data)


def read_envelope_hugr(envelope: bytes) -> Hugr:
Expand Down
21 changes: 20 additions & 1 deletion hugr-py/tests/test_envelope.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@
import semver

from hugr import ops, tys
from hugr.build.dfg import Dfg
from hugr.build.function import Module
from hugr.envelope import EnvelopeConfig, EnvelopeFormat
from hugr.hugr.node_port import Node
from hugr.package import Package

from .conftest import QUANTUM_EXT, H


@pytest.fixture
def package() -> Package:
Expand All @@ -29,7 +32,11 @@ def package() -> Package:

def test_envelope(package: Package):
# Binary compression roundtrip
for format in [EnvelopeFormat.JSON]:
for format in [
EnvelopeFormat.JSON,
EnvelopeFormat.MODEL,
EnvelopeFormat.MODEL_WITH_EXTS,
]:
for compression in [None, 0]:
encoded = package.to_bytes(EnvelopeConfig(format=format, zstd=compression))
decoded = Package.from_bytes(encoded)
Expand Down Expand Up @@ -63,3 +70,15 @@ def test_legacy_funcdefn():
op2 = h[Node(2)].op
assert isinstance(op2, ops.FuncDefn)
assert op2.visibility == "Private"


def test_model_import_with_ext():
dfg = Dfg(tys.Qubit)
h_outs = dfg.add_op(H, dfg.inputs()[0])
dfg.set_outputs(h_outs)
pkg = Package(modules=[dfg.hugr], extensions=[QUANTUM_EXT])
data = pkg.to_bytes(config=EnvelopeConfig.BINARY)
pkg1 = Package.from_bytes(data)
data1 = pkg1.to_bytes(config=EnvelopeConfig.BINARY)
pkg2 = Package.from_bytes(data1)
assert pkg2.modules[0].num_nodes() == 8
Loading