From c63bb0e0f2efb206219470aa483f5b730e45d902 Mon Sep 17 00:00:00 2001 From: Seyon Sivarajah Date: Fri, 19 Jan 2024 13:41:27 +0000 Subject: [PATCH 1/7] docs: add repository to readme (#117) --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 97ca65ac..f9c775a4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,6 +5,7 @@ description = "" authors = ["Mark Koch "] license = "Apache-2.0" readme = "README.md" +repository = "https://github.com/CQCL/guppy" [tool.poetry.dependencies] python = "^3.10" From bd4ff8b4189a42179bcee1af784522a0f4c59d93 Mon Sep 17 00:00:00 2001 From: Mark Koch <48097969+mark-koch@users.noreply.github.com> Date: Fri, 19 Jan 2024 14:46:06 +0000 Subject: [PATCH 2/7] doc: Update readme (#121) --- README.md | 91 +++++++++++++++++++++++------------ examples/1-Getting-Started.md | 7 +++ 2 files changed, 68 insertions(+), 30 deletions(-) create mode 100644 examples/1-Getting-Started.md diff --git a/README.md b/README.md index 7f57ee83..ecdadb81 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,68 @@ # Guppy -## About +Guppy is a quantum programming language that is fully embedded into Python. +It allows you to write high-level hybrid quantum programs with classical control flow and mid-circuit measurements using Pythonic syntax: + +```python +from guppylang import guppy, Qubit + +@guppy +def teleport(src: Qubit, tgt: Qubit) -> Qubit: + """Teleports the state in `src` to `tgt`.""" + # Create ancilla and entangle it with src and tgt + tmp = Qubit() + tmp, tgt = cx(h(tmp), tgt) + src, tmp = cx(src, tmp) + + # Apply classical corrections + if measure(h(src)): + tgt = z(tgt) + if measure(tmp): + tgt = x(tgt) + return tgt +``` + +More examples and tutorials are available [here][examples]. + +[examples]: ./examples/ + -TODO +## Install + +Guppy can be installed via `pip`. Requires Python >= 3.10. + +```sh +pip install guppylang +``` -## Getting Started + +## Usage + +See the [Getting Started][getting-started] guide and the other [examples]. + +[getting-started]: ./examples/1-Getting-Started.md + + +## Development These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. ### Prerequisites -- Python >=3.10 +- Python >= 3.10 +- [Poetry](https://python-poetry.org/docs/#installation) +- [Rust](https://www.rust-lang.org/tools/install) >= 1.75.0 (only needed for tests) ### Installing - -First make sure poetry [is -installed](https://python-poetry.org/docs/#installation). - -Then run the following to setup your virtual environment and install dependencies: +Run the following to setup your virtual environment and install dependencies: ```sh -poetry install +poetry install --with validation ``` +Note that the `--with validation` flag is optional and only needed to run integration tests. + You can then activate the virtual environment and work within it with: ```sh @@ -35,50 +74,42 @@ automate this when entering and leaving a directory. To run a single command in the shell, just prefix it with `poetry run`. +### Pre-commit -### Git blame - -You can configure Git to ignore formatting commits when using `git blame` by running +Install the pre-commit hook by running: ```sh -git config blame.ignoreRevsFile .git-blame-ignore-revs +poetry run pre-commit install ``` -## Usage -TODO +### Testing -## Testing - -First, build the PyO3 Hugr validation library from the `validator` directory using +Run tests using ```sh -maturin develop +poetry run pytest -v ``` -Run tests using +You have to install extra dependencies to test automatic circuit conversion from `pytket`: ```sh -poetry run pytest -v +poetry install --extras=pytket +poetry run pytest -v # Now rerun tests ``` + Integration test cases can be exported to a directory using ```sh poetry run pytest --export-test-cases=guppy-exports - ``` which will create a directory `./guppy-exports` populated with hugr modules serialised in msgpack. -## Packaging - -```sh -poetry build -``` ## License -This project is licensed under Apache License, Version 2.0 ([LICENSE][] or http://www.apache.org/licenses/LICENSE-2.0). +This project is licensed under Apache License, Version 2.0 ([LICENCE][] or http://www.apache.org/licenses/LICENSE-2.0). - [LICENSE]: ./LICENSE + [LICENCE]: ./LICENCE diff --git a/examples/1-Getting-Started.md b/examples/1-Getting-Started.md new file mode 100644 index 00000000..047f1a56 --- /dev/null +++ b/examples/1-Getting-Started.md @@ -0,0 +1,7 @@ + +# 1. Getting Started + +This file explains how you can get started using Guppy. + +TODO + From 7fb321b91c94d35fe32cca03fe7dd248cdf2e3fa Mon Sep 17 00:00:00 2001 From: Seyon Sivarajah Date: Fri, 19 Jan 2024 15:16:57 +0000 Subject: [PATCH 3/7] chore: use poetry for hugr/tket2 dependencies (#118) this workflow needs clarifying in readme, should be done in conjunction with guide to runnings tests, TODO. --- .github/workflows/pull-request.yaml | 39 ++++---------- README.md | 2 +- devenv.nix | 17 +++++- guppylang/hugr/hugr.py | 6 +-- guppylang/hugr/raw.py | 9 ---- poetry.lock | 82 ++++++++++++++--------------- pyproject.toml | 12 +++-- tests/integration/conftest.py | 24 +++++---- tests/integration/util.py | 10 ---- validator/Cargo.lock | 49 ++++------------- validator/Cargo.toml | 5 +- validator/pyproject.toml | 4 +- validator/src/lib.rs | 11 +--- 13 files changed, 107 insertions(+), 163 deletions(-) diff --git a/.github/workflows/pull-request.yaml b/.github/workflows/pull-request.yaml index 0acd0081..2249c44b 100644 --- a/.github/workflows/pull-request.yaml +++ b/.github/workflows/pull-request.yaml @@ -2,6 +2,10 @@ name: Pull Request on: pull_request +env: + SCCACHE_GHA_ENABLED: "true" + RUSTC_WRAPPER: "sccache" + jobs: check: name: Check Python @@ -13,6 +17,8 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Run sccache-cache + uses: mozilla-actions/sccache-action@v0.0.3 - name: Install poetry run: pipx install poetry - name: Set up Python ${{ matrix.python-version }} @@ -21,20 +27,9 @@ jobs: python-version: ${{ matrix.python-version }} cache: "poetry" - - name: Build Hugr validator - uses: PyO3/maturin-action@v1 - with: - command: build - sccache: 'true' - rust-toolchain: '1.75' - working-directory: validator - - name: Install Guppy run: poetry install - - name: Install Hugr validator - run: poetry add validator/target/wheels/* - - name: Type check with mypy run: poetry run mypy guppylang @@ -50,26 +45,14 @@ jobs: src: "./guppylang" args: check + - name: Install Guppy with validation + run: poetry install --with validation + - name: Run tests run: poetry run pytest - - name: Checkout tket2 - uses: actions/checkout@v4 - with: - repository: CQCL/tket2 - path: tket2 - - - name: Build tket2-py - uses: PyO3/maturin-action@v1 - with: - command: build - sccache: 'true' - rust-toolchain: '1.75' - working-directory: tket2/tket2-py - - - name: Install tket2-py validator - run: poetry add tket2/target/wheels/* + - name: Install tket2 dependencies + run: poetry install --extras=pytket - name: Rerun `py(...)` expression tests with tket2 installed run: poetry run pytest tests/integration/test_py.py tests/error/test_py_errors.py - diff --git a/README.md b/README.md index ecdadb81..8ea7f6cc 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ Integration test cases can be exported to a directory using poetry run pytest --export-test-cases=guppy-exports ``` -which will create a directory `./guppy-exports` populated with hugr modules serialised in msgpack. +which will create a directory `./guppy-exports` populated with hugr modules serialised in JSON. ## License diff --git a/devenv.nix b/devenv.nix index 15cc23a3..f62c2fcf 100644 --- a/devenv.nix +++ b/devenv.nix @@ -1,6 +1,21 @@ -{ ... }: +{ pkgs, lib, ... }: { + # for building optional tket2 dependency + # see https://github.com/CQCL/tket2/blob/main/devenv.nix + packages = [ + pkgs.just + ] + ++ lib.optionals pkgs.stdenv.isLinux [ + pkgs.stdenv.cc.cc.lib + ] + ++ lib.optionals pkgs.stdenv.isDarwin ( + with pkgs.darwin.apple_sdk; [ + frameworks.CoreServices + frameworks.CoreFoundation + ] + ); + languages.python = { enable = true; poetry = { diff --git a/guppylang/hugr/hugr.py b/guppylang/hugr/hugr.py index 9059c3c7..01a6cd30 100644 --- a/guppylang/hugr/hugr.py +++ b/guppylang/hugr/hugr.py @@ -794,10 +794,6 @@ def to_raw(self) -> raw.RawHugr: raise ValueError("Raw Hugr requires a root node") return raw.RawHugr(nodes=nodes, edges=edges) - def serialize(self) -> bytes: - """Serialize this Hugr in binary format.""" - return self.to_raw().packb() - - def serialize_json(self) -> str: + def serialize(self) -> str: """Serialize this Hugr in JSON format.""" return self.to_raw().to_json() diff --git a/guppylang/hugr/raw.py b/guppylang/hugr/raw.py index 12592672..3a56dea5 100644 --- a/guppylang/hugr/raw.py +++ b/guppylang/hugr/raw.py @@ -1,6 +1,5 @@ from typing import Any, Literal -import ormsgpack from pydantic import BaseModel from guppylang.hugr.ops import NodeID, OpType @@ -14,18 +13,10 @@ class RawHugr(BaseModel): nodes: list[OpType] edges: list[Edge] - 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.""" diff --git a/poetry.lock b/poetry.lock index fd004c01..aab547c6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -90,6 +90,19 @@ dev = ["flake8", "pep8-naming", "tox (>=3)", "twine", "wheel"] docs = ["sphinx (>=5)", "sphinx-autodoc-typehints", "sphinx-rtd-theme"] test = ["coverage", "mock (>=4)", "pytest (>=7)", "pytest-cov", "pytest-mock (>=3)"] +[[package]] +name = "guppyval" +version = "0.2.0" +description = "" +optional = false +python-versions = ">=3.10" +files = [] +develop = false + +[package.source] +type = "directory" +url = "validator" + [[package]] name = "identify" version = "2.5.33" @@ -119,7 +132,7 @@ files = [ name = "jinja2" version = "3.1.3" description = "A very fast and expressive template engine." -optional = true +optional = false python-versions = ">=3.7" files = [ {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, @@ -136,7 +149,7 @@ i18n = ["Babel (>=2.7)"] name = "lark-parser" version = "0.12.0" description = "a modern parsing library" -optional = true +optional = false python-versions = "*" files = [ {file = "lark-parser-0.12.0.tar.gz", hash = "sha256:15967db1f1214013dca65b1180745047b9be457d73da224fcda3d9dd4e96a138"}, @@ -152,7 +165,7 @@ regex = ["regex"] name = "markupsafe" version = "2.1.3" description = "Safely add untrusted strings to HTML/XML markup." -optional = true +optional = false python-versions = ">=3.7" files = [ {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, @@ -250,7 +263,7 @@ zig = ["ziglang (>=0.10.0,<0.11.0)"] name = "mpmath" version = "1.3.0" description = "Python library for arbitrary-precision floating-point arithmetic" -optional = true +optional = false python-versions = "*" files = [ {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"}, @@ -357,7 +370,7 @@ setuptools = "*" name = "numpy" version = "1.26.3" description = "Fundamental package for array computing in Python" -optional = true +optional = false python-versions = ">=3.9" files = [ {file = "numpy-1.26.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:806dd64230dbbfaca8a27faa64e2f414bf1c6622ab78cc4264f7f5f028fee3bf"}, @@ -398,36 +411,6 @@ files = [ {file = "numpy-1.26.3.tar.gz", hash = "sha256:697df43e2b6310ecc9d95f05d5ef20eacc09c7c4ecc9da3f235d39e71b7da1e4"}, ] -[[package]] -name = "ormsgpack" -version = "1.4.1" -description = "Fast, correct Python msgpack library supporting dataclasses, datetimes, and numpy" -optional = false -python-versions = ">=3.8" -files = [ - {file = "ormsgpack-1.4.1-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:969f6acdfd9af9fb51f65cd5a92e902e384409df3567ce85c2f5ff3510ec4c45"}, - {file = "ormsgpack-1.4.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e7e0bd764b414dbb51196cdb8c6a892b237149ade79eaadcdeff25f775d85feb"}, - {file = "ormsgpack-1.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c391fe8edaae80a3e66e6a26ce58edb32d5a6d10bd3b7ec8e2e364095fa7f30f"}, - {file = "ormsgpack-1.4.1-cp310-none-win_amd64.whl", hash = "sha256:a48a11d8826468385681b12ce5b403b8404390a2410c39a3d0ccc79e46381266"}, - {file = "ormsgpack-1.4.1-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:6987ee003c13e2b826a5219a8edc2d300d1c0dcb602489fa5e7bfd7ccfcefed6"}, - {file = "ormsgpack-1.4.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:eda6e815612b803f8dc8bb5cef9e68453b6f7b5c7b6a160f0dca1e75522c6dbd"}, - {file = "ormsgpack-1.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b44bfe29dc00dee76691fbd5ab1802148321915e902c5f32be1c40c07bbab0a2"}, - {file = "ormsgpack-1.4.1-cp311-none-win_amd64.whl", hash = "sha256:8c58b0b7160fd8526401d02111e057b91b7359df7b4e7960926e91f8c39e385a"}, - {file = "ormsgpack-1.4.1-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:01a60c0bad98c5b657f4f2259429fba0c5922806c8bd2546c91e2b3c950dcfd6"}, - {file = "ormsgpack-1.4.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9cf3efaf4dabd5c67f2cfb81f2920fca9f48c707bfceecbcabd9a53826436d9c"}, - {file = "ormsgpack-1.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fee368927bf96e517da98fa41ccd02511f310a78c3c86e09226833edd840773"}, - {file = "ormsgpack-1.4.1-cp312-none-win_amd64.whl", hash = "sha256:62d361e1f518763f90e6d7b53be21f5ba3950185a053514d5c2f7bad7de03106"}, - {file = "ormsgpack-1.4.1-cp38-cp38-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:ddbbe15f3e305e3eaf6cc9071e4a792cef6c8de399096b58b2fb1b9a424ab6c8"}, - {file = "ormsgpack-1.4.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:c401b50d78784b07b4795a728260d37610a0084c773f718040c7671bbe86dfa5"}, - {file = "ormsgpack-1.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:088dbdea632b08350870ec17d88875c497516de65a48b5d0aac4f8cd740e0ad5"}, - {file = "ormsgpack-1.4.1-cp38-none-win_amd64.whl", hash = "sha256:f078e33505eae7f2a14b4d4e71961494353d4d4afae1341b66a889c1968526e9"}, - {file = "ormsgpack-1.4.1-cp39-cp39-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:fad94fc3238781ed56283b5a0ab0edc990a9591a1987517d725dd7996100ee97"}, - {file = "ormsgpack-1.4.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a9d2e1ab48f852b92657f7ae31a6ba23664541237f899c2b6a24a746dcccdf0b"}, - {file = "ormsgpack-1.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce3062c405c4fcdd4f9cf374053cf4168f0d6606e5411c9db14e404ac8839010"}, - {file = "ormsgpack-1.4.1-cp39-none-win_amd64.whl", hash = "sha256:a8cd00e082ac3752d289d8f61e6aa91a9ce7d75e7d17a68767149fc45f3f4e10"}, - {file = "ormsgpack-1.4.1.tar.gz", hash = "sha256:52f4336e7c14e30e42bedba438b5f1eb3e8c8921314f868a5a7a13b6d6ecb17d"}, -] - [[package]] name = "packaging" version = "23.2" @@ -649,7 +632,7 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no name = "pytket" version = "1.24.0" description = "Python module for interfacing with the CQC tket library of quantum software" -optional = true +optional = false python-versions = ">=3.10" files = [ {file = "pytket-1.24.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:70f203d2efbf07f8d9857dd92aac7bf3b79bcf121ac2a23a92ac5e1dd3c7ea98"}, @@ -747,7 +730,7 @@ files = [ name = "qwasm" version = "1.0.1" description = "WebAssembly decoder & disassembler" -optional = true +optional = false python-versions = "*" files = [ {file = "qwasm-1.0.1-py3-none-any.whl", hash = "sha256:c4c82a3f962d29314634868e06375f0cb4676c3d5266fbe137f6cd67321b0ef1"}, @@ -787,7 +770,7 @@ files = [ name = "scipy" version = "1.11.4" description = "Fundamental algorithms for scientific computing in Python" -optional = true +optional = false python-versions = ">=3.9" files = [ {file = "scipy-1.11.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc9a714581f561af0848e6b69947fda0614915f072dfd14142ed1bfe1b806710"}, @@ -845,7 +828,7 @@ testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jar name = "sympy" version = "1.12" description = "Computer algebra system (CAS) in Python" -optional = true +optional = false python-versions = ">=3.8" files = [ {file = "sympy-1.12-py3-none-any.whl", hash = "sha256:c3588cd4295d0c0f603d0f2ae780587e64e2efeedb3521e46b9bb1d08d184fa5"}, @@ -855,6 +838,21 @@ files = [ [package.dependencies] mpmath = ">=0.19" +[[package]] +name = "tket2-py" +version = "0.0.0-alpha.1" +description = "pytket extension for the tket 2 compiler" +optional = true +python-versions = ">=3.10" +files = [] +develop = false + +[package.source] +type = "git" +url = "https://github.com/CQCL/tket2.git" +reference = "9e941f3" +resolved_reference = "9e941f33489fcdd1f6f885989bfa65b2c0c2519c" + [[package]] name = "tomli" version = "2.0.1" @@ -870,7 +868,7 @@ files = [ name = "types-pkg-resources" version = "0.1.3" description = "Typing stubs for pkg_resources" -optional = true +optional = false python-versions = "*" files = [ {file = "types-pkg_resources-0.1.3.tar.gz", hash = "sha256:834a9b8d3dbea343562fd99d5d3359a726f6bf9d3733bccd2b4f3096fbab9dae"}, @@ -909,9 +907,9 @@ docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx- test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] [extras] -pytket = ["pytket"] +pytket = ["pytket", "tket2-py"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "1b1691859b5dded386a32787705bd482adbcf31ec7b5b1249831419ccde9fb80" +content-hash = "38603201ab598596fc0671e329e91573b78f55f1420767cfbffae55a291fe32c" diff --git a/pyproject.toml b/pyproject.toml index f9c775a4..b3cce387 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,10 +11,10 @@ repository = "https://github.com/CQCL/guppy" python = "^3.10" graphviz = "^0.20.1" networkx = "^3.2.1" -ormsgpack = "^1.4.1" pydantic = "^2.5.3" typing-extensions = "^4.9.0" pytket = { version = "^1.24.0", optional = true } +tket2-py = { git = "https://github.com/CQCL/tket2.git", optional = true, rev = "9e941f3" } [tool.poetry.group.dev.dependencies] pytest = "^7.4.4" @@ -24,9 +24,15 @@ ruff = "^0.1.11" maturin = "^1.4.0" pytket = "*" +[tool.poetry.group.validation] +optional = true + +[tool.poetry.group.validation.dependencies] +guppyval = {path = "validator"} + [tool.poetry.extras] -pytket = ["pytket"] -# TODO add tket2 dependency here +pytket = ["pytket", "tket2-py"] + [build-system] requires = ["poetry-core"] diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 9ed7e8e5..cf84d7d9 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -1,6 +1,6 @@ import pytest -from . import util +from pathlib import Path @pytest.fixture() @@ -12,19 +12,23 @@ 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) +def validate(request, export_test_cases_dir: Path): + def validate_json(hugr: str): + try: + import guppyval + + guppyval.validate_json(hugr) + except ImportError: + pytest.skip("Skipping validation") + def validate_impl(hugr, name=None): # Validate via the json encoding - js = hugr.serialize_json() - util.validate_json(js) + js = hugr.serialize() + validate_json(js) if export_test_cases_dir: - file_name = f"{request.node.name}{f'_{name}' if name else ''}.msgpack" + file_name = f"{request.node.name}{f'_{name}' if name else ''}.json" export_file = export_test_cases_dir / file_name - export_file.write_bytes(bs) + export_file.write_text(js) return validate_impl diff --git a/tests/integration/util.py b/tests/integration/util.py index 999f883c..40232e58 100644 --- a/tests/integration/util.py +++ b/tests/integration/util.py @@ -1,15 +1,5 @@ from typing import Any -import validator - - -def validate_bytes(hugr: bytes): - validator.validate_bytes(hugr) - - -def validate_json(hugr: str): - validator.validate_json(hugr) - class Decorator: def __matmul__(self, other): diff --git a/validator/Cargo.lock b/validator/Cargo.lock index ade968f5..dc19fc87 100644 --- a/validator/Cargo.lock +++ b/validator/Cargo.lock @@ -45,12 +45,6 @@ dependencies = [ "wyz", ] -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "cfg-if" version = "1.0.0" @@ -166,6 +160,16 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +[[package]] +name = "guppyval" +version = "0.2.0" +dependencies = [ + "lazy_static", + "pyo3", + "quantinuum-hugr", + "serde_json", +] + [[package]] name = "hashbrown" version = "0.14.3" @@ -516,28 +520,6 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" -[[package]] -name = "rmp" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9860a6cc38ed1da53456442089b4dfa35e7cedaa326df63017af88385e6b20" -dependencies = [ - "byteorder", - "num-traits", - "paste", -] - -[[package]] -name = "rmp-serde" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bffea85eea980d8a74453e5d02a8d93028f3c34725de143085a844ebe953258a" -dependencies = [ - "byteorder", - "rmp", - "serde", -] - [[package]] name = "rustc_version" version = "0.4.0" @@ -751,17 +733,6 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" -[[package]] -name = "validator" -version = "0.2.0" -dependencies = [ - "lazy_static", - "pyo3", - "quantinuum-hugr", - "rmp-serde", - "serde_json", -] - [[package]] name = "windows-targets" version = "0.48.5" diff --git a/validator/Cargo.toml b/validator/Cargo.toml index de2e314a..f9471264 100644 --- a/validator/Cargo.toml +++ b/validator/Cargo.toml @@ -1,16 +1,15 @@ [package] -name = "validator" +name = "guppyval" version = "0.2.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] -name = "validator" +name = "guppyval" crate-type = ["cdylib"] [dependencies] pyo3 = "0.19.0" quantinuum-hugr = "0.1.0" -rmp-serde = "1.1.1" lazy_static = "1.4.0" serde_json = "1.0.111" diff --git a/validator/pyproject.toml b/validator/pyproject.toml index a7074d7c..19b53fa5 100644 --- a/validator/pyproject.toml +++ b/validator/pyproject.toml @@ -3,8 +3,8 @@ requires = ["maturin>=1.1,<2.0"] build-backend = "maturin" [project] -name = "validator" -requires-python = ">=3.7" +name = "guppyval" +requires-python = ">=3.10" classifiers = [ "Programming Language :: Rust", "Programming Language :: Python :: Implementation :: CPython", diff --git a/validator/src/lib.rs b/validator/src/lib.rs index 57f561b7..e52f86eb 100644 --- a/validator/src/lib.rs +++ b/validator/src/lib.rs @@ -18,14 +18,6 @@ lazy_static! { .unwrap(); } -/// Validate a msgpack-encoded Hugr -#[pyfunction] -fn validate_bytes(hugr: Vec) -> PyResult<()> { - let hg: hugr::Hugr = rmp_serde::from_slice(&hugr).unwrap(); - hg.validate(®ISTRY).unwrap(); - Ok(()) -} - /// Validate a json-encoded Hugr #[pyfunction] fn validate_json(hugr: String) -> PyResult<()> { @@ -35,8 +27,7 @@ fn validate_json(hugr: String) -> PyResult<()> { } #[pymodule] -fn validator(_py: Python, m: &PyModule) -> PyResult<()> { - m.add_function(wrap_pyfunction!(validate_bytes, m)?)?; +fn guppyval(_py: Python, m: &PyModule) -> PyResult<()> { m.add_function(wrap_pyfunction!(validate_json, m)?)?; Ok(()) } From 52048508231ca942acc9e4559375d9c5ee60a5fb Mon Sep 17 00:00:00 2001 From: Mark Koch <48097969+mark-koch@users.noreply.github.com> Date: Fri, 19 Jan 2024 16:12:21 +0000 Subject: [PATCH 4/7] feat: Add module load method to guppy (#126) --- README.md | 3 +++ guppylang/decorator.py | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/README.md b/README.md index 8ea7f6cc..e0375287 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,9 @@ It allows you to write high-level hybrid quantum programs with classical control ```python from guppylang import guppy, Qubit +import guppylang.prelude.quantum as quantum +guppy.load(quantum) + @guppy def teleport(src: Qubit, tgt: Qubit) -> Qubit: """Teleports the state in `src` to `tgt`.""" diff --git a/guppylang/decorator.py b/guppylang/decorator.py index 77538f9e..cb1836a6 100644 --- a/guppylang/decorator.py +++ b/guppylang/decorator.py @@ -258,6 +258,13 @@ def dummy(*args: Any, **kwargs: Any) -> Any: return dec + def load(self, m: ModuleType | GuppyModule) -> None: + caller = self._get_python_caller() + if caller not in self._modules: + self._modules[caller] = GuppyModule(caller.name) + module = self._modules[caller] + module.load(m) + def take_module(self, id: ModuleIdentifier | None = None) -> GuppyModule: """Returns the local GuppyModule, removing it from the local state.""" orig_id = id From e3da1eca85e67890e4bd96154fdcde8b129b0243 Mon Sep 17 00:00:00 2001 From: Seyon Sivarajah Date: Fri, 19 Jan 2024 16:18:14 +0000 Subject: [PATCH 5/7] feat: add top-level imports (#125) Closes #123 Closes #100 Closes #127 --- README.md | 5 ++--- guppylang/__init__.py | 6 +++++- ruff.toml | 1 + 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e0375287..2675c763 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,13 @@ Guppy is a quantum programming language that is fully embedded into Python. It allows you to write high-level hybrid quantum programs with classical control flow and mid-circuit measurements using Pythonic syntax: ```python -from guppylang import guppy, Qubit +from guppylang import guppy, Qubit, quantum -import guppylang.prelude.quantum as quantum guppy.load(quantum) +# Teleports the state in `src` to `tgt`. @guppy def teleport(src: Qubit, tgt: Qubit) -> Qubit: - """Teleports the state in `src` to `tgt`.""" # Create ancilla and entangle it with src and tgt tmp = Qubit() tmp, tgt = cx(h(tmp), tgt) diff --git a/guppylang/__init__.py b/guppylang/__init__.py index bbe52f53..a6ac6ec0 100644 --- a/guppylang/__init__.py +++ b/guppylang/__init__.py @@ -1 +1,5 @@ -__all__ = ["types.py"] +from guppylang.decorator import guppy +from guppylang.module import GuppyModule +from guppylang.prelude import builtins, quantum +from guppylang.prelude.builtins import Bool, Float, Int, List, linst +from guppylang.prelude.quantum import Qubit diff --git a/ruff.toml b/ruff.toml index cfe3fd0a..6ba9dbb8 100644 --- a/ruff.toml +++ b/ruff.toml @@ -75,6 +75,7 @@ ignore = [ "guppy/decorator.py" = ["B010"] "tests/integration/*" = ["F841", "C416", "RUF005"] "tests/{hugr,integration}/*" = ["B", "FBT", "SIM", "I"] +"__init__.py" = ["F401"] # module imported but unused # [pydocstyle] # convention = "google" From 3a69336cf4b7300a546e83abf3db8c99bfe65c0f Mon Sep 17 00:00:00 2001 From: Seyon Sivarajah Date: Fri, 19 Jan 2024 16:52:17 +0000 Subject: [PATCH 6/7] fix: make tket2 group rather than extra (#128) pypi doesn't allow git dependencies in extras --- .github/workflows/pull-request.yaml | 2 +- README.md | 2 +- poetry.lock | 7 ++----- pyproject.toml | 13 ++++++++----- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/pull-request.yaml b/.github/workflows/pull-request.yaml index 2249c44b..fe08f535 100644 --- a/.github/workflows/pull-request.yaml +++ b/.github/workflows/pull-request.yaml @@ -52,7 +52,7 @@ jobs: run: poetry run pytest - name: Install tket2 dependencies - run: poetry install --extras=pytket + run: poetry install --with pytket - name: Rerun `py(...)` expression tests with tket2 installed run: poetry run pytest tests/integration/test_py.py tests/error/test_py_errors.py diff --git a/README.md b/README.md index 2675c763..f9898092 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ poetry run pytest -v You have to install extra dependencies to test automatic circuit conversion from `pytket`: ```sh -poetry install --extras=pytket +poetry install --with pytket poetry run pytest -v # Now rerun tests ``` diff --git a/poetry.lock b/poetry.lock index aab547c6..4bad347d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -842,7 +842,7 @@ mpmath = ">=0.19" name = "tket2-py" version = "0.0.0-alpha.1" description = "pytket extension for the tket 2 compiler" -optional = true +optional = false python-versions = ">=3.10" files = [] develop = false @@ -906,10 +906,7 @@ platformdirs = ">=3.9.1,<5" docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] -[extras] -pytket = ["pytket", "tket2-py"] - [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "38603201ab598596fc0671e329e91573b78f55f1420767cfbffae55a291fe32c" +content-hash = "f1ece99102a0240654d7058ebefd3eb8a48dfc9d1e6a30116e4e8b50bcc44d01" diff --git a/pyproject.toml b/pyproject.toml index b3cce387..35facba9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,8 +13,6 @@ graphviz = "^0.20.1" networkx = "^3.2.1" pydantic = "^2.5.3" typing-extensions = "^4.9.0" -pytket = { version = "^1.24.0", optional = true } -tket2-py = { git = "https://github.com/CQCL/tket2.git", optional = true, rev = "9e941f3" } [tool.poetry.group.dev.dependencies] pytest = "^7.4.4" @@ -28,10 +26,15 @@ pytket = "*" optional = true [tool.poetry.group.validation.dependencies] -guppyval = {path = "validator"} +guppyval = { path = "validator" } -[tool.poetry.extras] -pytket = ["pytket", "tket2-py"] + +[tool.poetry.group.pytket] +optional = true + +[tool.poetry.group.pytket.dependencies] +pytket = { version = "^1.24.0" } +tket2-py = {git = "https://github.com/CQCL/tket2.git", rev = "9e941f3"} [build-system] From 45c2bf010a719785527e1c5cc2ac650975e84d4d Mon Sep 17 00:00:00 2001 From: Seyon Sivarajah Date: Mon, 22 Jan 2024 11:09:04 +0000 Subject: [PATCH 7/7] docs: add reference to runner to readme (#129) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Agustín Borgna <121866228+aborgna-q@users.noreply.github.com> --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index f9898092..7129502e 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,9 @@ poetry run pytest --export-test-cases=guppy-exports which will create a directory `./guppy-exports` populated with hugr modules serialised in JSON. +### Experimental: Execution + +See the [guppy-runner](https://github.com/CQCL/guppy-runner) repository for in-progress work for compiling Guppy source programs and executing them. ## License