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

⬆️πŸͺ update pre-commit hooks #287

Merged
merged 13 commits into from
Jul 4, 2023
Merged
12 changes: 6 additions & 6 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,10 @@ repos:

# Run ruff (subsumes pyupgrade, isort, flake8+plugins, and more)
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.272
rev: v0.0.276
hooks:
- id: ruff
args: ["--fix"]
args: ["--fix", "--show-fixes"]

# Run code formatting with Black
- repo: https://github.com/psf/black
Expand All @@ -63,7 +63,7 @@ repos:

# Also run Black on examples in the documentation
- repo: https://github.com/asottile/blacken-docs
rev: 1.13.0
rev: 1.14.0
hooks:
- id: blacken-docs
additional_dependencies:
Expand All @@ -80,7 +80,7 @@ repos:

# Clang-format the C++ part of the code base automatically
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v16.0.4
rev: v16.0.6
hooks:
- id: clang-format
types_or: [c++, c, cuda]
Expand All @@ -94,7 +94,7 @@ repos:

# Check static types with mypy
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.3.0
rev: v1.4.1
hooks:
- id: mypy
files: ^(mqt/qcec|test/python|setup.py)
Expand All @@ -107,7 +107,7 @@ repos:

# Check for spelling
- repo: https://github.com/codespell-project/codespell
rev: v2.2.4
rev: v2.2.5
hooks:
- id: codespell
args: ["-L", "wille,linz", "--skip", "*.ipynb"]
9 changes: 7 additions & 2 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Sphinx configuration file."""
from __future__ import annotations

import sys

Expand All @@ -7,12 +8,16 @@
else:
from importlib import metadata

from typing import TYPE_CHECKING

import pybtex.plugin
from pybtex.database import Entry
from pybtex.richtext import HRef
from pybtex.style.formatting.unsrt import Style as UnsrtStyle
from pybtex.style.template import field, href

if TYPE_CHECKING:
from pybtex.database import Entry
from pybtex.richtext import HRef

# -- Project information -----------------------------------------------------
project = "QCEC"
author = "Lukas Burgholzer"
Expand Down
1 change: 1 addition & 0 deletions mqt/qcec/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
This file is part of the MQT QCEC library released under the MIT license.
See README.md or go to https://github.com/cda-tum/qcec for more information.
"""
from __future__ import annotations

from mqt.qcec.compilation_flow_profiles import AncillaMode, generate_profile
from mqt.qcec.pyqcec import (
Expand Down
39 changes: 25 additions & 14 deletions mqt/qcec/compilation_flow_profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,38 @@

from __future__ import annotations

import random
from enum import Enum
from enum import Enum, unique
from pathlib import Path
from typing import Any

import numpy as np
from qiskit import QuantumCircuit, transpile


class AncillaMode(str, Enum):
@unique
class AncillaMode(Enum):
"""Enum for the ancilla mode."""

NO_ANCILLA = "noancilla"
RECURSION = "recursion"
V_CHAIN = "v-chain"

def __eq__(self, other: object) -> bool:
"""Check if two AncillaMode objects are equal. Supports string comparison."""
if isinstance(other, str):
return self.value == other
if isinstance(other, self.__class__):
return self.value == other.value
return False

def __hash__(self) -> int:
"""Return the hash of the AncillaMode."""
return hash(self.value)

def __str__(self) -> str:
"""Return the string representation of the AncillaMode."""
return self.value


single_qubit_gates_no_params = {
"qubits": 1,
Expand Down Expand Up @@ -132,9 +148,8 @@ def create_general_gate(qubits: int, params: int, controls: int, identifier: str
qc = QuantumCircuit(required_qubits)
gate_identifier = "c" * controls + identifier

parameter_list = []
for _ in range(params):
parameter_list.append(random.uniform(-np.pi, np.pi))
rng = np.random.default_rng()
parameter_list = [rng.uniform(-np.pi, np.pi) for _ in range(params)]

getattr(qc, gate_identifier)(*parameter_list, *range(required_qubits))
return qc
Expand Down Expand Up @@ -165,9 +180,8 @@ def create_multi_controlled_gate(
qc = QuantumCircuit(required_qubits)
gate_identifier = "mc" + identifier

parameter_list = []
for _ in range(params):
parameter_list.append(random.uniform(-np.pi, np.pi))
rng = np.random.default_rng()
parameter_list = [rng.uniform(-np.pi, np.pi) for _ in range(params)]

if mode is not None:
getattr(qc, gate_identifier)(
Expand Down Expand Up @@ -268,7 +282,7 @@ def add_special_case_data(

def generate_profile_name(optimization_level: int = 1, mode: AncillaMode = AncillaMode.NO_ANCILLA) -> str:
"""Generate a profile name based on the given optimization level and ancilla mode."""
return "qiskit_O" + str(optimization_level) + "_" + mode + ".profile"
return "qiskit_O" + str(optimization_level) + "_" + str(mode) + ".profile"


def write_profile_data_to_file(profile_data: dict[tuple[str, int], int], filename: Path) -> None:
Expand Down Expand Up @@ -311,10 +325,7 @@ def find_continuation(
prediction_cutoff: float = 1e6,
) -> None:
"""Extrapolate from the given profile data by finding recurrence relations."""
sequence = []
for (g, _), cost in profile_data.items():
if g is gate:
sequence.append(cost)
sequence = [cost for (g, _), cost in profile_data.items() if g == gate]

# sort the sequence
sequence = sorted(sequence)
Expand Down
10 changes: 6 additions & 4 deletions mqt/qcec/parameterized.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def is_expr(x: float | Parameter | ParameterExpression) -> bool:
return isinstance(x, (Parameter, ParameterExpression))

symb_params: list[Parameter | ParameterExpression] = [param for param in p if is_expr(param)]
symb_params.sort(key=lambda param: param.name) # type: ignore[no-any-return]
symb_params.sort(key=lambda param: param.name)
symb_exprs = list(filter(is_expr, exprs))

offsets = np.zeros(len(symb_exprs))
Expand Down Expand Up @@ -96,16 +96,17 @@ def check_instantiated_random(
) -> EquivalenceCheckingManager.Results:
"""Check whether circuits are equivalent for random instantiation of symbolic parameters."""
param_map = {}
rng = np.random.default_rng()
for p in params:
param_map[p] = np.random.rand() * 2 * np.pi
param_map[p] = rng.random() * 2 * np.pi

circ1_inst = circ1.bind_parameters(param_map)
circ2_inst = circ2.bind_parameters(param_map)

return check_instantiated(circ1_inst, circ2_inst, configuration)


def check_parameterized( # noqa: PLR0915
def check_parameterized(
circ1: QuantumCircuit | str, circ2: QuantumCircuit | str, configuration: Configuration
) -> EquivalenceCheckingManager.Results:
"""Equivalence checking flow for parameterized circuit."""
Expand Down Expand Up @@ -178,7 +179,8 @@ def instantiate_params_phases(
qc1: QuantumCircuit, qc2: QuantumCircuit
) -> tuple[QuantumCircuit, QuantumCircuit, float]:
phases = [0, np.pi, np.pi / 2, -np.pi / 2, np.pi / 4, -np.pi / 4]
b = np.random.choice(phases, size=len(offsets)) + offsets
rng = np.random.default_rng()
b = rng.choice(phases, size=len(offsets)) + offsets
return instantiate_params(qc1, qc2, b)

circ1_inst, circ2_inst, runtime = instantiate_params_zero(circ1, circ2)
Expand Down
9 changes: 3 additions & 6 deletions mqt/qcec/pyqcec.pyi
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
from __future__ import annotations
from typing import Any, ClassVar, overload

from typing import TYPE_CHECKING, Any, ClassVar, overload
from qiskit import QuantumCircuit

if TYPE_CHECKING:
from qiskit import QuantumCircuit

from mqt.qcec.types import ApplicationSchemeName, EquivalenceCriterionName, StateTypeName
from mqt.qcec.types import ApplicationSchemeName, EquivalenceCriterionName, StateTypeName

class ApplicationScheme:
__members__: ClassVar[dict[ApplicationScheme, int]] = ... # read-only
Expand Down
1 change: 1 addition & 0 deletions mqt/qcec/types.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Types for the QCEC module."""
from __future__ import annotations

from typing import Literal

Expand Down
45 changes: 16 additions & 29 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -143,20 +143,25 @@ module = ["qiskit.*"]
ignore_missing_imports = true

[tool.ruff]
include = ["*.py", "*.pyi", "**/pyproject.toml", "*.ipynb"]
select = [
"E", "F", "W", # flake8
"A", # flake8-builtins
"ANN", # flake8-annotations
"ARG", # flake8-unused-arguments
"ASYNC", # flake8-async
"B", "B904", # flake8-bugbear
"C4", # flake8-comprehensions
"D", # pydocstyle
"EM", # flake8-errmsg
"EXE", # flake8-executable
"FA", # flake8-future-annotations
"I", # isort
"ICN", # flake8-import-conventions
"ISC", # flake8-implicit-str-concat
"N", # flake8-naming
"NPY", # numpy
"PERF", # perflint
"PGH", # pygrep-hooks
"PIE", # flake8-pie
"PL", # pylint
Expand All @@ -168,48 +173,30 @@ select = [
"RSE", # flake8-raise
"RUF", # Ruff-specific
"SLF", # flake8-self
"SLOT", # flake8-slots
"SIM", # flake8-simplify
"TCH", # flake8-type-checking
"TID", # flake8-tidy-imports
"TRY", # tryceratops
"UP", # pyupgrade
"YTT", # flake8-2020
]
ignore = [
extend-ignore = [
"ANN101", # Missing type annotation for self in method
"ANN102", # Missing type annotation for cls in classmethod
"E501", # Line too long (Black is enough)
"PLR2004", # Magic values
"PLR0913", # Too many arguments
]

# Exclude a variety of commonly ignored directories.
exclude = [
".bzr",
".direnv",
".eggs",
".git",
".hg",
".mypy_cache",
".nox",
".pants.d",
".ruff_cache",
".svn",
".tox",
".venv",
"__pypackages__",
"_build",
"buck-out",
"build",
"dist",
"node_modules",
"venv",
"PLR", # Design related pylint codes
]

flake8-unused-arguments.ignore-variadic-names = true
isort.required-imports = ["from __future__ import annotations"]
line-length = 120

[tool.ruff.per-file-ignores]
"*.pyi" = [
"D", # pydocstyle
"*.pyi" = ["D"] # pydocstyle
"*.ipynb" = [
"D", # pydocstyle
"E402", # Allow imports to appear anywhere in Jupyter notebooks
"I002", # Allow missing `from __future__ import annotations` import
]

[tool.ruff.pydocstyle]
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Setup script for the MQT QCEC package."""
from __future__ import annotations

import os
import re
Expand Down
8 changes: 8 additions & 0 deletions test/python/test_compilation_flow_profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ def ancilla_mode(request: Any) -> qcec.AncillaMode: # noqa: ANN401
return cast(qcec.AncillaMode, request.param)


def test_ancilla_mode_conversion(ancilla_mode: qcec.AncillaMode) -> None:
"""Test conversion and equality of ancilla modes."""
ancilla_str = str(ancilla_mode)
assert qcec.AncillaMode(ancilla_str) == ancilla_mode
assert ancilla_str == ancilla_mode
assert ancilla_mode == ancilla_str


@pytest.mark.skipif(
sys.version_info < (3, 11, 0) or sys.platform != "linux",
reason="Since this check takes quite some time, it is only executed if the current platform is Linux and the Python version is 3.11 or higher.",
Expand Down