Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pre-commit hooks and update formatting / linting to use ruff #20

Merged
merged 3 commits into from
Jul 4, 2024
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
48 changes: 22 additions & 26 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,36 @@ 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
matrix:
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
83 changes: 83 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# 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
#
# 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"

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
3 changes: 0 additions & 3 deletions .pylintrc

This file was deleted.

52 changes: 27 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -21,14 +22,14 @@ def circuit(x):
```pycon
>>> qml.draw(circuit)(0.3)
0: ──GPI2(0.00)─╭MS──GPI2(-1.57)─────────────────────────┤ <Z>
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

Expand All @@ -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))
```

Expand All @@ -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.
Expand All @@ -108,32 +110,32 @@ 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():
qfunc()
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,
Expand Down
7 changes: 6 additions & 1 deletion ionizer/decompositions.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"""
Custom decompositions of operations into the {GPI, GPI2, MS} native gate set.
"""

from pennylane import math
import numpy as np

Expand Down Expand Up @@ -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):
Expand Down
7 changes: 5 additions & 2 deletions ionizer/identity_hunter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
6 changes: 5 additions & 1 deletion ionizer/ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"""
Native gates for IonQ hardware as PennyLane operations.
"""

import numpy as np

import pennylane as qml
Expand Down Expand Up @@ -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):
Expand Down
6 changes: 5 additions & 1 deletion ionizer/transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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))

Expand Down
1 change: 1 addition & 0 deletions ionizer/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"""
Utility functions.
"""

import numpy as np
from pennylane import math

Expand Down
Loading