diff --git a/.gitignore b/.gitignore index 247fff7be..9774e30e2 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ devenv.local.nix .coverage coverage.xml lcov.info + +# python hypothesis testing +.hypothesis \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index bec2c2b33..9c6421f04 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,12 +14,13 @@ packages = [{ include = "tket2-py" }] [tool.poetry.dependencies] python = ">=3.10" -[tool.poetry.dev-dependencies] +[tool.poetry.group.dev.dependencies] maturin = "^1.3.0" pytket = "*" pytest = "^7.1.2" pytest-cov = "^4.1.0" ruff = "^0.1.3" +hypothesis = "^6.91.1" [build-system] requires = ["maturin~=1.3"] diff --git a/tket2-py/test/test_pass.py b/tket2-py/test/test_pass.py index 366f34ff3..fd2a2aca4 100644 --- a/tket2-py/test/test_pass.py +++ b/tket2-py/test/test_pass.py @@ -1,9 +1,43 @@ from pytket import Circuit, OpType from dataclasses import dataclass - +from typing import Callable, Any from tket2.passes import badger_pass, greedy_depth_reduce, chunks from tket2.circuit import Tk2Circuit from tket2.pattern import Rule, RuleMatcher +import hypothesis.strategies as st +from hypothesis.strategies._internal import SearchStrategy +from hypothesis import given, settings + + +@st.composite +def circuits( + draw: Callable[[SearchStrategy[Any]], Any], + n_qubits: SearchStrategy[int] = st.integers(min_value=0, max_value=8), + depth: SearchStrategy[int] = st.integers(min_value=5, max_value=50), +) -> Circuit: + total_qubits = draw(n_qubits) + circuit = Circuit(total_qubits) + if total_qubits == 0: + return circuit + for _ in range(draw(depth)): + gates = [circuit.Rz, circuit.H] + if total_qubits > 1: + gates.extend([circuit.CX]) + gate = draw(st.sampled_from(gates)) + control = draw(st.integers(min_value=0, max_value=total_qubits - 1)) + if gate in (circuit.CX,): + target = draw( + st.integers(min_value=0, max_value=total_qubits - 1).filter( + lambda x: x != control + ) + ) + gate(control, target) + if gate == circuit.Rz: + angle = draw(st.floats(min_value=-2.0, max_value=2.0)) + gate(angle, control) + if gate == circuit.H: + gate(control) + return circuit def test_simple_badger_pass_no_opt(): @@ -30,6 +64,27 @@ def test_depth_optimise(): assert c.depth() == 2 +def test_depth_optimise_buggy(): + # bug https://github.com/CQCL/tket2/issues/253 + c = Circuit(3).H(2).CX(2, 1).CX(0, 2).CX(0, 1) + + assert c.depth() == 4 + + c, moves = greedy_depth_reduce(c) + assert moves == 1 + assert c == Circuit(3).H(2).CX(0, 1).CX(2, 1).CX(0, 2) + assert c.depth() == 3 + + + +@given(circ=circuits()) +@settings(print_blob=True, deadline=None) +def test_depth_hyp(circ: Circuit) -> None: + new, _ = greedy_depth_reduce(circ) + + assert circ.n_gates == new.n_gates + assert new.depth() <= circ.depth() + def test_chunks(): c = Circuit(4).CX(0, 2).CX(1, 3).CX(1, 2).CX(0, 3).CX(1, 3)