From 34acdc5ab7ac1f64cbb09fea870a7c23defe2e00 Mon Sep 17 00:00:00 2001 From: Olivia Di Matteo Date: Thu, 4 Jul 2024 08:40:08 -0700 Subject: [PATCH 1/3] Add pre-commit hooks. Convert CI to use ruff instead of pylint and black --- .github/workflows/python-package.yml | 48 +++++------ .pre-commit-config.yaml | 81 +++++++++++++++++ .pylintrc | 2 +- README.md | 52 +++++------ ionizer/decompositions.py | 7 +- ionizer/identity_hunter.py | 7 +- ionizer/ops.py | 6 +- ionizer/transforms.py | 6 +- ionizer/utils.py | 1 + poetry.lock | 124 ++++++--------------------- pyproject.toml | 18 +++- setup.py | 7 +- tests/test_decompositions.py | 13 ++- tests/test_transform_utils.py | 13 ++- tests/test_transforms.py | 38 +++++--- tests/test_utils.py | 19 ++-- 16 files changed, 259 insertions(+), 183 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index e06b4d0..524b7ea 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -5,13 +5,12 @@ name: Python package on: push: - branches: [ "main" ] + branches: ["main"] pull_request: - branches: [ "main", "rc-0.*" ] + branches: ["main", "rc-0.*"] jobs: build: - runs-on: ubuntu-latest strategy: fail-fast: false @@ -19,26 +18,23 @@ jobs: python-version: ["3.9", "3.10", "3.11"] steps: - - uses: actions/checkout@v4 - with: - repository: ${{ github.event.pull_request.head.repo.full_name }} - ref: ${{ github.event.pull_request.head.ref }} - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - cache: 'pip' - - name: Install dependencies - run: | - python -m pip install -U pip poetry - poetry --version - poetry install - - name: Check formatting with black - run: | - poetry run black -l 100 ionizer/*.py tests/*.py - - name: Lint with pylint - run: | - poetry run pylint ionizer/*.py tests/*.py - - name: Test with pytest - run: | - poetry run pytest tests/test_*.py + - uses: actions/checkout@v4 + with: + repository: ${{ github.event.pull_request.head.repo.full_name }} + ref: ${{ github.event.pull_request.head.ref }} + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: "pip" + - name: Install dependencies + run: | + python -m pip install -U pip poetry + poetry --version + poetry install + - name: Check linting and formatting with ruff + run: | + poetry run ruff check ionizer/*.py tests/*.py + - name: Test with pytest + run: | + poetry run pytest tests/test_*.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..0059ae6 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,81 @@ +# To run all pre-commit checks, use: +# +# pre-commit run -a +# +# To install pre-commit hooks that run every time you commit: +# +# pre-commit install +# + +ci: + autoupdate_commit_msg: "⬆️🪝 update pre-commit hooks" + autofix_commit_msg: "🎨 pre-commit fixes" + +repos: + # Standard hooks + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: "v4.6.0" + hooks: + - id: check-added-large-files + - id: check-case-conflict + - id: check-docstring-first + - id: check-merge-conflict + - id: check-toml + - id: check-yaml + - id: debug-statements + - id: end-of-file-fixer + - id: mixed-line-ending + - id: trailing-whitespace + + # Handling unwanted unicode characters + - repo: https://github.com/sirosen/texthooks + rev: "0.6.6" + hooks: + - id: fix-ligatures + - id: fix-smartquotes + + # Check for common mistakes + - repo: https://github.com/pre-commit/pygrep-hooks + rev: v1.10.0 + hooks: + - id: rst-backticks + - id: rst-directive-colons + - id: rst-inline-touching-normal + + # Check for spelling + - repo: https://github.com/codespell-project/codespell + rev: "v2.3.0" + hooks: + - id: codespell + + # Format configuration files with prettier + - repo: https://github.com/pre-commit/mirrors-prettier + rev: "v4.0.0-alpha.8" + hooks: + - id: prettier + types_or: [yaml, markdown, html, css, javascript, json] + + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.4.9 + hooks: + - id: ruff + args: ["--fix", "--show-fixes"] + types_or: [python, pyi, jupyter] + - id: ruff-format + types_or: [python, pyi, jupyter] + + # Also run Black on examples in the documentation + - repo: https://github.com/adamchainz/blacken-docs + rev: 1.16.0 + hooks: + - id: blacken-docs + additional_dependencies: [black==24.*] + + # Catch common capitalization mistakes + - repo: local + hooks: + - id: disallow-caps + name: Disallow improper capitalization + language: pygrep + entry: PyBind|Numpy|Github|PyTest|Pennylane + exclude: .pre-commit-config.yaml diff --git a/.pylintrc b/.pylintrc index 6e4a048..516d126 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,3 +1,3 @@ [MESSAGE CONTROL] -disable=fixme \ No newline at end of file +disable=fixme diff --git a/README.md b/README.md index 932145a..ff1d2c4 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,10 @@ of code! ```python from ionizer.transforms import ionize + @qml.qnode(dev) @ionize -def circuit(x): +def circuit(x): qml.Hadamard(wires=0) qml.CNOT(wires=[0, 1]) qml.RX(x, wires=1) @@ -21,14 +22,14 @@ def circuit(x): ```pycon >>> qml.draw(circuit)(0.3) 0: ──GPI2(0.00)─╭MS──GPI2(-1.57)─────────────────────────┤ -1: ──GPI2(3.14)─╰MS──GPI2(1.57)───GPI(-1.42)──GPI2(1.57)─┤ +1: ──GPI2(3.14)─╰MS──GPI2(1.57)───GPI(-1.42)──GPI2(1.57)─┤ ``` - ## Installation Requirements: - * PennyLane >= 0.33 + +- PennyLane >= 0.33 The Ionizer is not currently available via a package manager. To install, clone the repository and run @@ -52,30 +53,31 @@ transforms](https://arxiv.org/abs/2202.13414), similar to PennyLane's [existing compilation tools](https://docs.pennylane.ai/en/stable/introduction/compiling_circuits.html). To compile and execute the circuit using trapped ion gates, the -``@ionize`` decorator performs the following steps: +`@ionize` decorator performs the following steps: - * Decomposes all operations into Paulis/Pauli rotations, Hadamard, and CNOT - * Merges all single-qubit rotations - * Converts everything except RZ to GPI/GPI2/MS gates (`@ionizer.transforms.convert_to_gpi`) - * Virtually applies all RZ gates (`@ionizer.transforms.virtualize_rz_gates`) - * Repeatedly applies gate fusion and commutation through MS gates which performs simplification based on some circuit identities (`@ionizer.transforms.single_qubit_fusion_gpi` and `@ionizer.transforms.commute_through_ms_gates`) +- Decomposes all operations into Paulis/Pauli rotations, Hadamard, and CNOT +- Merges all single-qubit rotations +- Converts everything except RZ to GPI/GPI2/MS gates (`@ionizer.transforms.convert_to_gpi`) +- Virtually applies all RZ gates (`@ionizer.transforms.virtualize_rz_gates`) +- Repeatedly applies gate fusion and commutation through MS gates which performs simplification based on some circuit identities (`@ionizer.transforms.single_qubit_fusion_gpi` and `@ionizer.transforms.commute_through_ms_gates`) ```python from ionizer.transforms import ionize + @qml.qnode(dev) @ionize def circuit_ionized(params): for idx in range(5): qml.Hadamard(wires=idx) - + for idx in range(4): qml.RY(params[idx], wires=idx) - qml.CNOT(wires=[idx+1, idx]) - + qml.CNOT(wires=[idx + 1, idx]) + for wire in dev.wires: qml.PauliX(wires=wire) - + return qml.expval(qml.PauliX(0)) ``` @@ -97,7 +99,7 @@ tensor(0.99500417, requires_grad=True) ``` -Note that while this comes packaged together as the ``@ionize`` transform, the +Note that while this comes packaged together as the `@ionize` transform, the individual transforms can also be accessed and used independently. There is currently not direct support for other frameworks. However, if you would like to do this with a Qiskit circuit, it can be accomplished as follows through the [`pennylane-qiskit`](https://github.com/PennyLaneAI/pennylane-qiskit) package. @@ -108,6 +110,7 @@ qiskit_circuit = QuantumCircuit(...) # Turns a Qiskit circuit into a PennyLane quantum function qfunc = qml.from_qiskit(qiskit_circuit) + @qml.qnode(dev) @ionize def pennylane_circuit(): @@ -115,25 +118,24 @@ def pennylane_circuit(): return qml.expval(qml.PauliX(0)) ``` - ## Notes This package is a work in progress. While it has been verified to work on some fairly large circuits, we still need to work on: - * finding circuit identities involving the 2-qubit gate - * improving the documentation and usage instructions - * ensuring differentiability of variational parameters - * writing more tests (compile at your own risk!) +- finding circuit identities involving the 2-qubit gate +- improving the documentation and usage instructions +- ensuring differentiability of variational parameters +- writing more tests (compile at your own risk!) ## Resources - * [IonQ documentation](https://ionq.com/docs/getting-started-with-native-gates) - * [Basic circuit compilation techniques for an ion-trap quantum machine](https://arxiv.org/abs/1603.07678) - - ## Citing +- [IonQ documentation](https://ionq.com/docs/getting-started-with-native-gates) +- [Basic circuit compilation techniques for an ion-trap quantum machine](https://arxiv.org/abs/1603.07678) + +## Citing - If you use the Ionizer as part of your workflow, we would appreciate if you cite it using the BibTeX below. +If you use the Ionizer as part of your workflow, we would appreciate if you cite it using the BibTeX below. ``` @software{di_matteo_2024_10761367, diff --git a/ionizer/decompositions.py b/ionizer/decompositions.py index ad82207..af39354 100644 --- a/ionizer/decompositions.py +++ b/ionizer/decompositions.py @@ -17,6 +17,7 @@ """ Custom decompositions of operations into the {GPI, GPI2, MS} native gate set. """ + from pennylane import math import numpy as np @@ -136,7 +137,11 @@ def gpi_ry(phi, wires): List[Operation]: The sequence of GPI/GPI2 rotations that implements the gate up to a global phase. """ - return [GPI2(np.pi, wires=wires), GPI(phi / 2, wires=wires), GPI2(np.pi, wires=wires)] + return [ + GPI2(np.pi, wires=wires), + GPI(phi / 2, wires=wires), + GPI2(np.pi, wires=wires), + ] def gpi_rz(phi, wires): diff --git a/ionizer/identity_hunter.py b/ionizer/identity_hunter.py index 7737460..5c6b9be 100644 --- a/ionizer/identity_hunter.py +++ b/ionizer/identity_hunter.py @@ -36,8 +36,11 @@ def _test_inclusion_in_identity_db(db_subset, single_gates, candidate_angles, ca """ # Loop over GPI/GPI2 for all the special angles for id_gate, gate_angle_list in single_gates.items(): - # Get their explicit reference angles and matrix reprensentations - angles, matrices = [x[0] for x in gate_angle_list], [x[1] for x in gate_angle_list] + # Get their explicit reference angles and matrix representations + angles, matrices = ( + [x[0] for x in gate_angle_list], + [x[1] for x in gate_angle_list], + ) # Test each reference against the candidate to see if any are equivalent for ref_angle, ref_matrix in zip(angles, matrices): diff --git a/ionizer/ops.py b/ionizer/ops.py index a9fb5bb..325290f 100644 --- a/ionizer/ops.py +++ b/ionizer/ops.py @@ -17,6 +17,7 @@ """ Native gates for IonQ hardware as PennyLane operations. """ + import numpy as np import pennylane as qml @@ -114,7 +115,10 @@ def compute_matrix(phi): # pylint: disable=arguments-differ """ exponent = -1j * phi return qml.math.stack( - [[1, -1j * qml.math.exp(exponent)], [-1j * qml.math.exp(qml.math.conj(exponent)), 1]] + [ + [1, -1j * qml.math.exp(exponent)], + [-1j * qml.math.exp(qml.math.conj(exponent)), 1], + ] ) / np.sqrt(2) def adjoint(self): diff --git a/ionizer/transforms.py b/ionizer/transforms.py index 40794f3..b1f7a16 100644 --- a/ionizer/transforms.py +++ b/ionizer/transforms.py @@ -21,6 +21,7 @@ expansions and simplifications of the tape. The transforms it uses during this process can also be called individually. """ + from typing import Sequence, Callable from functools import partial @@ -211,7 +212,10 @@ def virtualize_rz_gates(tape: QuantumTape) -> (Sequence[QuantumTape], Callable): # this way we can commute it through. if apply_accumulated_phase_gate: new_operations.append( - GPI(-rescale_angles(accumulated_rz_phase) / 2, wires=current_gate.wires) + GPI( + -rescale_angles(accumulated_rz_phase) / 2, + wires=current_gate.wires, + ) ) new_operations.append(GPI(0.0, wires=current_gate.wires)) diff --git a/ionizer/utils.py b/ionizer/utils.py index 1eee85f..c13dd86 100644 --- a/ionizer/utils.py +++ b/ionizer/utils.py @@ -17,6 +17,7 @@ """ Utility functions. """ + import numpy as np from pennylane import math diff --git a/poetry.lock b/poetry.lock index f6c0bd0..3227780 100644 --- a/poetry.lock +++ b/poetry.lock @@ -11,20 +11,6 @@ files = [ {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, ] -[[package]] -name = "astroid" -version = "3.1.0" -description = "An abstract syntax tree for Python with inference support." -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"}, - {file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} - [[package]] name = "autograd" version = "1.6.2" @@ -258,21 +244,6 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -[[package]] -name = "dill" -version = "0.3.8" -description = "serialize all of Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, - {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, -] - -[package.extras] -graph = ["objgraph (>=1.7.2)"] -profile = ["gprof2dot (>=2022.7.29)"] - [[package]] name = "distlib" version = "0.3.8" @@ -361,31 +332,6 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] -[[package]] -name = "isort" -version = "5.13.2" -description = "A Python utility / library to sort Python imports." -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, - {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, -] - -[package.extras] -colors = ["colorama (>=0.4.6)"] - -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = ">=3.6" -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - [[package]] name = "mypy-extensions" version = "1.0.0" @@ -612,36 +558,6 @@ nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" -[[package]] -name = "pylint" -version = "3.1.0" -description = "python code static checker" -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"}, - {file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"}, -] - -[package.dependencies] -astroid = ">=3.1.0,<=3.2.0-dev0" -colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} -dill = [ - {version = ">=0.2", markers = "python_version < \"3.11\""}, - {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, - {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, -] -isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" -mccabe = ">=0.6,<0.8" -platformdirs = ">=2.2.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -tomlkit = ">=0.10.1" -typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} - -[package.extras] -spelling = ["pyenchant (>=3.2,<4.0)"] -testutils = ["gitpython (>3)"] - [[package]] name = "pytest" version = "8.2.0" @@ -745,6 +661,33 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "ruff" +version = "0.5.0" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.5.0-py3-none-linux_armv6l.whl", hash = "sha256:ee770ea8ab38918f34e7560a597cc0a8c9a193aaa01bfbd879ef43cb06bd9c4c"}, + {file = "ruff-0.5.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:38f3b8327b3cb43474559d435f5fa65dacf723351c159ed0dc567f7ab735d1b6"}, + {file = "ruff-0.5.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7594f8df5404a5c5c8f64b8311169879f6cf42142da644c7e0ba3c3f14130370"}, + {file = "ruff-0.5.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:adc7012d6ec85032bc4e9065110df205752d64010bed5f958d25dbee9ce35de3"}, + {file = "ruff-0.5.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d505fb93b0fabef974b168d9b27c3960714d2ecda24b6ffa6a87ac432905ea38"}, + {file = "ruff-0.5.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9dc5cfd3558f14513ed0d5b70ce531e28ea81a8a3b1b07f0f48421a3d9e7d80a"}, + {file = "ruff-0.5.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:db3ca35265de239a1176d56a464b51557fce41095c37d6c406e658cf80bbb362"}, + {file = "ruff-0.5.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b1a321c4f68809fddd9b282fab6a8d8db796b270fff44722589a8b946925a2a8"}, + {file = "ruff-0.5.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c4dfcd8d34b143916994b3876b63d53f56724c03f8c1a33a253b7b1e6bf2a7d"}, + {file = "ruff-0.5.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81e5facfc9f4a674c6a78c64d38becfbd5e4f739c31fcd9ce44c849f1fad9e4c"}, + {file = "ruff-0.5.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e589e27971c2a3efff3fadafb16e5aef7ff93250f0134ec4b52052b673cf988d"}, + {file = "ruff-0.5.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d2ffbc3715a52b037bcb0f6ff524a9367f642cdc5817944f6af5479bbb2eb50e"}, + {file = "ruff-0.5.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:cd096e23c6a4f9c819525a437fa0a99d1c67a1b6bb30948d46f33afbc53596cf"}, + {file = "ruff-0.5.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:46e193b36f2255729ad34a49c9a997d506e58f08555366b2108783b3064a0e1e"}, + {file = "ruff-0.5.0-py3-none-win32.whl", hash = "sha256:49141d267100f5ceff541b4e06552e98527870eafa1acc9dec9139c9ec5af64c"}, + {file = "ruff-0.5.0-py3-none-win_amd64.whl", hash = "sha256:e9118f60091047444c1b90952736ee7b1792910cab56e9b9a9ac20af94cd0440"}, + {file = "ruff-0.5.0-py3-none-win_arm64.whl", hash = "sha256:ed5c4df5c1fb4518abcb57725b576659542bdbe93366f4f329e8f398c4b71178"}, + {file = "ruff-0.5.0.tar.gz", hash = "sha256:eb641b5873492cf9bd45bc9c5ae5320648218e04386a5f0c264ad6ccce8226a1"}, +] + [[package]] name = "rustworkx" version = "0.15.1" @@ -853,17 +796,6 @@ files = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] -[[package]] -name = "tomlkit" -version = "0.12.5" -description = "Style preserving TOML library" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomlkit-0.12.5-py3-none-any.whl", hash = "sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f"}, - {file = "tomlkit-0.12.5.tar.gz", hash = "sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c"}, -] - [[package]] name = "typing-extensions" version = "4.12.2" @@ -915,4 +847,4 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "b2fb9a03fd892a6a27300fe6d8f973969a31e395078ec6c6fc452f62f6b0a233" +content-hash = "8ef803dbbe32244c2199cfad69f02ca50ea8dad2071bb55a7a8d2491b43ced99" diff --git a/pyproject.toml b/pyproject.toml index 9163e8b..a3552d4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,3 @@ -[tool.black] -line-length = 100 - [tool.poetry] name = "ionizer" version = "0.3.0" @@ -18,10 +15,23 @@ pennylane = "^0.36" [tool.poetry.group.dev.dependencies] pytest = "8.2" -pylint = "3.1" black = "24.2" pre-commit = "3.7.1" +ruff = "^0.5.0" [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" + +[tool.ruff] +line-length = 100 +indent-width = 4 + +[tool.ruff.format] +docstring-code-format = true + +[tool.ruff.lint.per-file-ignores] +"__init__.py" = ["F401"] + +[tool.codespell] +skip = "poetry.lock" diff --git a/setup.py b/setup.py index c92cf9d..a6331d5 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -from setuptools import setup, find_packages, find_namespace_packages +from setuptools import setup requirements = [ "pennylane>=0.33", @@ -15,6 +15,7 @@ package_data={ "ionizer.resources": [ "double_gate_identities.pkl", - "triple_gate_identities.pkl" - ]}, + "triple_gate_identities.pkl", + ] + }, ) diff --git a/tests/test_decompositions.py b/tests/test_decompositions.py index 014dee1..d88c8c4 100644 --- a/tests/test_decompositions.py +++ b/tests/test_decompositions.py @@ -17,6 +17,7 @@ """ Test the decompositions of standard operations in to the {GPI, GPI2, MS} gate set. """ + import pytest import pennylane as qml @@ -43,10 +44,18 @@ single_qubit_unitaries = [ (np.eye(2), []), - (np.array([[0.0, 0.54030231 - 0.84147098j], [0.54030231 + 0.84147098j, 0.0]]), ["GPI"]), + ( + np.array([[0.0, 0.54030231 - 0.84147098j], [0.54030231 + 0.84147098j, 0.0]]), + ["GPI"], + ), (qml.RZ.compute_matrix(0.2), ["GPI", "GPI"]), ( - np.array([[0.70710678, -0.59500984 - 0.38205142j], [0.59500984 - 0.38205142j, 0.70710678]]), + np.array( + [ + [0.70710678, -0.59500984 - 0.38205142j], + [0.59500984 - 0.38205142j, 0.70710678], + ] + ), ["GPI2"], ), (qml.RY.compute_matrix(1.0), ["GPI2", "GPI", "GPI2"]), diff --git a/tests/test_transform_utils.py b/tests/test_transform_utils.py index c0cd566..1b0d1e7 100644 --- a/tests/test_transform_utils.py +++ b/tests/test_transform_utils.py @@ -1,5 +1,5 @@ """ -Test the utility functions for the transpilation transforms. +Test the utility functions for the transpilation transforms. """ import pytest @@ -147,7 +147,10 @@ def test_valid_identity_different_wires(self): @pytest.mark.parametrize( "input_ops,expected_ops", [ - ([GPI2(np.pi, wires=0), GPI(0, wires=0), GPI2(-np.pi, wires=0)], []), # Simplifies to I + ( + [GPI2(np.pi, wires=0), GPI(0, wires=0), GPI2(-np.pi, wires=0)], + [], + ), # Simplifies to I ( [GPI2(0, wires=0), GPI(np.pi / 4, wires=0), GPI2(0, wires=0)], [GPI2(-np.pi / 2, wires=0)], @@ -161,7 +164,11 @@ def test_valid_identity_different_wires(self): [GPI2(0.3, wires=0)], ), # Second two gates only ( - [GPI2(-np.pi / 2, wires=0), GPI(-np.pi / 4, wires=0), GPI2(-np.pi / 2, wires=0)], + [ + GPI2(-np.pi / 2, wires=0), + GPI(-np.pi / 4, wires=0), + GPI2(-np.pi / 2, wires=0), + ], [GPI2(-np.pi, wires=0)], ), # 3-gate identity ], diff --git a/tests/test_transforms.py b/tests/test_transforms.py index 3091902..f7daf56 100644 --- a/tests/test_transforms.py +++ b/tests/test_transforms.py @@ -1,5 +1,5 @@ """ -Test the suite of transpilation transforms. +Test the suite of transpilation transforms. """ # pylint: disable=function-redefined @@ -219,7 +219,8 @@ def qfunc(): assert len(transformed_tape.operations) == 1 assert are_mats_equivalent( - qml.matrix(tape, wire_order=[0]), qml.matrix(transformed_tape, wire_order=[0]) + qml.matrix(tape, wire_order=[0]), + qml.matrix(transformed_tape, wire_order=[0]), ) def test_rz_gpi2(self): @@ -239,7 +240,8 @@ def qfunc(): op.name == name for op, name in zip(transformed_tape.operations, ["GPI2", "GPI", "GPI"]) ) assert are_mats_equivalent( - qml.matrix(tape, wire_order=[0]), qml.matrix(transformed_tape, wire_order=[0]) + qml.matrix(tape, wire_order=[0]), + qml.matrix(transformed_tape, wire_order=[0]), ) def test_rz_gpi_ms(self): @@ -261,7 +263,8 @@ def qfunc(): for op, name in zip(transformed_tape.operations, ["GPI2", "GPI", "GPI", "MS"]) ) assert are_mats_equivalent( - qml.matrix(tape, wire_order=[0, 1]), qml.matrix(transformed_tape, wire_order=[0, 1]) + qml.matrix(tape, wire_order=[0, 1]), + qml.matrix(transformed_tape, wire_order=[0, 1]), ) def test_rz_gpi_multiqubit(self): @@ -284,7 +287,8 @@ def qfunc(): for op, name in zip(transformed_tape.operations, ["GPI2", "GPI", "GPI", "GPI"]) ) assert are_mats_equivalent( - qml.matrix(tape, wire_order=[0, 1]), qml.matrix(transformed_tape, wire_order=[0, 1]) + qml.matrix(tape, wire_order=[0, 1]), + qml.matrix(transformed_tape, wire_order=[0, 1]), ) def test_rz_gpi_multiqubit_multims(self): @@ -311,7 +315,8 @@ def qfunc(): ) ) assert are_mats_equivalent( - qml.matrix(tape, wire_order=[0, 1]), qml.matrix(transformed_tape, wire_order=[0, 1]) + qml.matrix(tape, wire_order=[0, 1]), + qml.matrix(transformed_tape, wire_order=[0, 1]), ) @@ -337,7 +342,8 @@ def qfunc(): assert _compare_op_lists(tape.operations, transformed_tape.operations) assert are_mats_equivalent( - qml.matrix(tape, wire_order=range(3)), qml.matrix(transformed_tape, wire_order=range(3)) + qml.matrix(tape, wire_order=range(3)), + qml.matrix(transformed_tape, wire_order=range(3)), ) def test_no_fusion_multiple_gates(self): @@ -361,7 +367,8 @@ def qfunc(): assert _compare_op_lists(tape.operations, transformed_tape.operations) assert are_mats_equivalent( - qml.matrix(tape, wire_order=range(3)), qml.matrix(transformed_tape, wire_order=range(3)) + qml.matrix(tape, wire_order=range(3)), + qml.matrix(transformed_tape, wire_order=range(3)), ) def test_fusion_three_gates(self): @@ -385,7 +392,8 @@ def qfunc(): assert _compare_op_lists(tape.operations, transformed_tape.operations) assert are_mats_equivalent( - qml.matrix(tape, wire_order=range(3)), qml.matrix(transformed_tape, wire_order=range(3)) + qml.matrix(tape, wire_order=range(3)), + qml.matrix(transformed_tape, wire_order=range(3)), ) def test_fusion_four_gates(self): @@ -413,7 +421,8 @@ def qfunc(): assert len(transformed_tape.operations) == len(tape.operations) - 2 assert are_mats_equivalent( - qml.matrix(tape, wire_order=range(3)), qml.matrix(transformed_tape, wire_order=range(3)) + qml.matrix(tape, wire_order=range(3)), + qml.matrix(transformed_tape, wire_order=range(3)), ) @@ -436,7 +445,8 @@ def qfunc(): assert all(op.name in ["GPI", "GPI2", "MS"] for op in transformed_tape.operations) assert are_mats_equivalent( - qml.matrix(tape, wire_order=range(3)), qml.matrix(transformed_tape, wire_order=range(3)) + qml.matrix(tape, wire_order=range(3)), + qml.matrix(transformed_tape, wire_order=range(3)), ) def test_convert_tape_to_gpi_known_gates_exclusion(self): @@ -457,7 +467,8 @@ def qfunc(): assert all(op.name in ["GPI", "GPI2", "MS", "RY"] for op in transformed_tape.operations) assert transformed_tape.operations[3].name == "RY" assert are_mats_equivalent( - qml.matrix(tape, wire_order=range(3)), qml.matrix(transformed_tape, wire_order=range(3)) + qml.matrix(tape, wire_order=range(3)), + qml.matrix(transformed_tape, wire_order=range(3)), ) @@ -486,7 +497,8 @@ def qfunc(): assert all(op.name in ["GPI", "GPI2", "MS"] for op in transformed_tape.operations) assert are_mats_equivalent( - qml.matrix(tape, wire_order=range(4)), qml.matrix(transformed_tape, wire_order=range(4)) + qml.matrix(tape, wire_order=range(4)), + qml.matrix(transformed_tape, wire_order=range(4)), ) def test_ionize_qnode(self): diff --git a/tests/test_utils.py b/tests/test_utils.py index cd1941c..d240aee 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -26,13 +26,19 @@ class TestMatrixAngleUtilities: "mat1, mat2", [ (np.eye(2), 1j * np.eye(2)), - (qml.PauliX.compute_matrix(), np.exp(1j * np.pi / 2) * qml.PauliX.compute_matrix()), - (qml.SX.compute_matrix(), np.exp(-1j * np.pi / 2) * qml.SX.compute_matrix()), + ( + qml.PauliX.compute_matrix(), + np.exp(1j * np.pi / 2) * qml.PauliX.compute_matrix(), + ), + ( + qml.SX.compute_matrix(), + np.exp(-1j * np.pi / 2) * qml.SX.compute_matrix(), + ), (qml.Hadamard.compute_matrix(), -qml.Hadamard.compute_matrix()), ], ) def test_equivalent_matrices(self, mat1, mat2): - """Test that we correcty identify matrices that are equivalent + """Test that we correctly identify matrices that are equivalent up to a global phase.""" assert are_mats_equivalent(mat1, mat2) @@ -45,7 +51,7 @@ def test_equivalent_matrices(self, mat1, mat2): ], ) def test_inequivalent_matrices(self, mat1, mat2): - """Test that we correcty identify matrices that are equivalent + """Test that we correctly identify matrices that are equivalent up to a global phase.""" assert not are_mats_equivalent(mat1, mat2) @@ -56,7 +62,10 @@ def test_inequivalent_matrices(self, mat1, mat2): (np.pi, np.pi), (-np.pi / 2, -np.pi / 2), (3 * np.pi / 2, -np.pi / 2), - (np.array([np.pi / 2, 5 * np.pi / 4]), np.array([np.pi / 2, -3 * np.pi / 4])), + ( + np.array([np.pi / 2, 5 * np.pi / 4]), + np.array([np.pi / 2, -3 * np.pi / 4]), + ), ], ) def test_rescale_angles(self, angles, rescaled_angles): From e63d1262ceac92de6b12e81ec9dd4d321ae4885c Mon Sep 17 00:00:00 2001 From: Olivia Di Matteo Date: Thu, 4 Jul 2024 08:42:14 -0700 Subject: [PATCH 2/3] Delete unused config files --- .pylintrc | 3 --- setup.py | 21 --------------------- 2 files changed, 24 deletions(-) delete mode 100644 .pylintrc delete mode 100644 setup.py diff --git a/.pylintrc b/.pylintrc deleted file mode 100644 index 516d126..0000000 --- a/.pylintrc +++ /dev/null @@ -1,3 +0,0 @@ -[MESSAGE CONTROL] - -disable=fixme diff --git a/setup.py b/setup.py deleted file mode 100644 index a6331d5..0000000 --- a/setup.py +++ /dev/null @@ -1,21 +0,0 @@ -from setuptools import setup - -requirements = [ - "pennylane>=0.33", -] - -setup( - name="Ionizer", - version="0.3", - description="PennyLane tools for compilation into trapped-ion native gates.", - author="UBC Quantum Software and Algorithms Research Group", - url="https://github.com/QSAR-UBC/ionizer", - packages=["ionizer", "ionizer.resources"], - include_package_data=True, - package_data={ - "ionizer.resources": [ - "double_gate_identities.pkl", - "triple_gate_identities.pkl", - ] - }, -) From cc5788d17ce23be53a83a1d6234c5a643817a1cd Mon Sep 17 00:00:00 2001 From: Olivia Di Matteo Date: Thu, 4 Jul 2024 09:09:42 -0700 Subject: [PATCH 3/3] Update preconfig --- .pre-commit-config.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0059ae6..66b3710 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,10 +6,12 @@ # # pre-commit install # +# This file was constructed based on an example file provided by our friends +# at TUM Chair for Design Automation - thanks! ci: - autoupdate_commit_msg: "⬆️🪝 update pre-commit hooks" - autofix_commit_msg: "🎨 pre-commit fixes" + autoupdate_commit_msg: "update pre-commit hooks" + autofix_commit_msg: "pre-commit fixes" repos: # Standard hooks