Skip to content

Commit

Permalink
Merge branch 'main' into doc/readme
Browse files Browse the repository at this point in the history
  • Loading branch information
mark-koch authored Jan 19, 2024
2 parents 5ec2071 + c63bb0e commit 0c4442d
Show file tree
Hide file tree
Showing 9 changed files with 466 additions and 3 deletions.
21 changes: 21 additions & 0 deletions .github/workflows/pull-request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,24 @@ jobs:

- 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: Rerun `py(...)` expression tests with tket2 installed
run: poetry run pytest tests/integration/test_py.py tests/error/test_py_errors.py

23 changes: 23 additions & 0 deletions guppylang/checker/expr_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
NoneType,
Subst,
TupleType,
row_to_type,
unify,
)
from guppylang.nodes import (
Expand Down Expand Up @@ -918,4 +919,26 @@ def python_value_to_guppy_type(
return None
return TupleType(cast(list[GuppyType], tys))
case _:
# Pytket conversion is an optional feature
try:
import pytket

if isinstance(v, pytket.circuit.Circuit):
# We also need tket2 installed
try:
import tket2 # type: ignore[import-untyped, import-not-found, unused-ignore] # noqa: F401

qubit = globals.types["Qubit"].build()
return FunctionType(
[qubit] * v.n_qubits,
row_to_type([qubit] * v.n_qubits + [BoolType()] * v.n_bits),
)
except ImportError:
raise GuppyError(
"Pytket compatibility requires `tket2` to be installed. "
"See https://github.com/CQCL/tket2/tree/main/tket2-py",
node,
) from None
except ImportError:
pass
return None
14 changes: 14 additions & 0 deletions guppylang/compiler/expr_compiler.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import ast
import json
from collections.abc import Iterator
from contextlib import contextmanager
from typing import Any
Expand Down Expand Up @@ -314,4 +315,17 @@ def python_value_to_hugr(v: Any) -> val.Value | None:
return None
return val.Tuple(vs=vs)
case _:
# Pytket conversion is an optional feature
try:
import pytket

if isinstance(v, pytket.circuit.Circuit):
from tket2.circuit import ( # type: ignore[import-untyped, import-not-found, unused-ignore]
Tk2Circuit,
)

hugr = json.loads(Tk2Circuit(v).to_hugr_json())
return val.FunctionVal(hugr=hugr)
except ImportError:
pass
return None
289 changes: 288 additions & 1 deletion poetry.lock

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ description = ""
authors = ["Mark Koch <[email protected]>"]
license = "Apache-2.0"
readme = "README.md"
repository = "https://github.com/CQCL/guppy"

[tool.poetry.dependencies]
python = "^3.10"
Expand All @@ -13,13 +14,19 @@ networkx = "^3.2.1"
ormsgpack = "^1.4.1"
pydantic = "^2.5.3"
typing-extensions = "^4.9.0"
pytket = { version = "^1.24.0", optional = true }

[tool.poetry.group.dev.dependencies]
pytest = "^7.4.4"
mypy = "^1.8.0"
pre-commit = "^3.6.0"
ruff = "^0.1.11"
maturin = "^1.4.0"
pytket = "*"

[tool.poetry.extras]
pytket = ["pytket"]
# TODO add tket2 dependency here

[build-system]
requires = ["poetry-core"]
Expand All @@ -31,4 +38,4 @@ strict = true
allow_redefinition = true
# mypy doesn't support TypeAliasType fully yet
# https://github.com/python/mypy/issues/16614
disable_error_code = "valid-type"
disable_error_code = "valid-type"
7 changes: 7 additions & 0 deletions tests/error/py_errors/tket2_not_installed.err
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Guppy compilation failed. Error in file $FILE:16

14: @guppy(module)
15: def foo(q: Qubit) -> Qubit:
16: f = py(circ)
^^^^^^^^
GuppyError: Pytket compatibility requires `tket2` to be installed. See https://github.com/CQCL/tket2/tree/main/tket2-py
20 changes: 20 additions & 0 deletions tests/error/py_errors/tket2_not_installed.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from pytket import Circuit

from guppylang.decorator import guppy
from guppylang.module import GuppyModule
from guppylang.prelude.quantum import quantum, Qubit

circ = Circuit(1)
circ.H(0)

module = GuppyModule("test")
module.load(quantum)


@guppy(module)
def foo(q: Qubit) -> Qubit:
f = py(circ)
return f(q)


module.compile()
17 changes: 16 additions & 1 deletion tests/error/test_py_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,21 @@

from tests.error.util import run_error_test


try:
import tket2

tket2_installed = True
except ImportError:
tket2_installed = False


path = pathlib.Path(__file__).parent.resolve() / "py_errors"
files = [
x
for x in path.iterdir()
if x.is_file()
if x.suffix == ".py" and x.name != "__init__.py"
if x.suffix == ".py" and x.name not in ("__init__.py", "tket2_not_installed.py")
]

# Turn paths into strings, otherwise pytest doesn't display the names
Expand All @@ -18,3 +27,9 @@
@pytest.mark.parametrize("file", files)
def test_py_errors(file, capsys):
run_error_test(file, capsys)


@pytest.mark.skipif(tket2_installed, reason="tket2 is installed")
def test_tket2_not_installed(capsys):
path = pathlib.Path(__file__).parent.resolve() / "py_errors" / "tket2_not_installed.py"
run_error_test(str(path), capsys)
69 changes: 69 additions & 0 deletions tests/integration/test_py.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
import pytest

from guppylang.decorator import guppy
from guppylang.module import GuppyModule
from guppylang.prelude.quantum import Qubit, quantum
from tests.integration.util import py
from tests.util import compile_guppy


try:
import tket2

tket2_installed = True
except ImportError:
tket2_installed = False


def test_basic(validate):
x = 42

Expand Down Expand Up @@ -60,3 +73,59 @@ def foo() -> int:
return x

validate(foo)


@pytest.mark.skipif(not tket2_installed, reason="Tket2 is not installed")
def test_pytket_single_qubit(validate):
from pytket import Circuit

circ = Circuit(1)
circ.H(0)

module = GuppyModule("test")
module.load(quantum)

@guppy(module)
def foo(q: Qubit) -> Qubit:
f = py(circ)
return f(q)

validate(module.compile())


@pytest.mark.skipif(not tket2_installed, reason="Tket2 is not installed")
def test_pytket_multi_qubit(validate):
from pytket import Circuit

circ = Circuit(3)
circ.CX(0, 1)
circ.H(2)
circ.T(0)
circ.CZ(2, 0)

module = GuppyModule("test")
module.load(quantum)

@guppy(module)
def foo(q1: Qubit, q2:Qubit, q3:Qubit) -> tuple[Qubit, Qubit, Qubit]:
return py(circ)(q1, q2, q3)

validate(module.compile())


@pytest.mark.skipif(not tket2_installed, reason="Tket2 is not installed")
def test_pytket_measure(validate):
from pytket import Circuit

circ = Circuit(1)
circ.H(0)
circ.measure_all()

module = GuppyModule("test")
module.load(quantum)

@guppy(module)
def foo(q: Qubit) -> tuple[Qubit, bool]:
return py(circ)(q)

validate(module.compile())

0 comments on commit 0c4442d

Please sign in to comment.