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

✨ Switch to mqt-core Python package #432

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion .github/workflows/cd.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: CD
name: CD # trigger CD
on:
push:
branches: [main]
Expand Down
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ repos:
- nox
- numpy
- pytest
- mqt.core @ git+https://github.com/cda-tum/mqt-core@shared-libs

# Check for spelling
- repo: https://github.com/crate-ci/typos
Expand Down
15 changes: 14 additions & 1 deletion cmake/ExternalDependencies.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@ include(FetchContent)
set(FETCH_PACKAGES "")

if(BUILD_MQT_QCEC_BINDINGS)
# Manually detect the installed mqt-core package.
execute_process(
COMMAND "${Python_EXECUTABLE}" -m mqt.core --cmake_dir
OUTPUT_STRIP_TRAILING_WHITESPACE
OUTPUT_VARIABLE mqt-core_DIR
ERROR_QUIET)

# Add the detected directory to the CMake prefix path.
if(mqt-core_DIR)
list(APPEND CMAKE_PREFIX_PATH "${mqt-core_DIR}")
message(STATUS "Found mqt-core package: ${mqt-core_DIR}")
endif()

if(NOT SKBUILD)
# Manually detect the installed pybind11 package.
execute_process(
Expand All @@ -22,7 +35,7 @@ endif()
# cmake-format: off
set(MQT_CORE_VERSION 2.6.1
CACHE STRING "MQT Core version")
set(MQT_CORE_REV "3529a4435339af3b400e976b1a5e7393d8e5bf4f"
set(MQT_CORE_REV "shared-libs"
CACHE STRING "MQT Core identifier (tag, branch or commit hash)")
set(MQT_CORE_REPO_OWNER "cda-tum"
CACHE STRING "MQT Core repository owner (change when using a fork)")
Expand Down
24 changes: 23 additions & 1 deletion noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def _run_tests(
posargs = list(session.posargs)
env = {}
if os.environ.get("CI", None) and sys.platform == "win32":
env["SKBUILD_CMAKE_ARGS"] = "-T ClangCL;--fresh"
env["SKBUILD_CMAKE_ARGS"] = "-T ClangCL"

if shutil.which("cmake") is None and shutil.which("cmake3") is None:
session.install("cmake")
Expand All @@ -67,6 +67,19 @@ def _run_tests(
posargs.append("--cov-config=pyproject.toml")

session.install(*BUILD_REQUIREMENTS, *install_args, env=env)

# install mqt.core from source to avoid ABI incompatibilities
session.install(
"--no-build-isolation",
"mqt.core @ git+https://github.com/cda-tum/mqt-core@shared-libs",
"--no-binary",
"mqt.core",
*install_args,
env={
"CMAKE_GENERATOR": "Ninja",
},
)

install_arg = f"-ve.[{','.join(_extras)}]"
session.install("--no-build-isolation", install_arg, *install_args, env=env)
session.run("pytest", *run_args, *posargs, env=env)
Expand Down Expand Up @@ -103,6 +116,15 @@ def docs(session: nox.Session) -> None:
serve = args.builder == "html" and session.interactive
extra_installs = ["sphinx-autobuild"] if serve else []
session.install(*BUILD_REQUIREMENTS, *extra_installs)

# install mqt.core from source to avoid ABI incompatibilities
session.install(
"--no-build-isolation",
"mqt.core @ git+https://github.com/cda-tum/mqt-core@shared-libs",
"--no-binary",
"mqt.core",
)

session.install("--no-build-isolation", "-ve.[docs]")

if args.builder == "linkcheck":
Expand Down
33 changes: 27 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ requires = [
"scikit-build-core>=0.10.1",
"setuptools-scm>=8.1",
"pybind11>=2.13.5",
"mqt.core @ git+https://github.com/cda-tum/mqt-core@shared-libs",
]
build-backend = "scikit_build_core.build"

Expand Down Expand Up @@ -39,16 +40,21 @@ classifiers = [
]
requires-python = ">=3.9"
dependencies = [
"mqt.core @ git+https://github.com/cda-tum/mqt-core@shared-libs",
"importlib_resources>=5.0; python_version < '3.10'",
"typing_extensions>=4.2; python_version < '3.11'", # used for typing.Unpack
"qiskit[qasm3-import]>=1.0.0",
"numpy>=1.26; python_version >= '3.12'",
"numpy>=1.24; python_version >= '3.11'",
"numpy>=1.22",
]
dynamic = ["version"]

[project.optional-dependencies]
test = ["pytest>=7.2"]
qiskit = ["mqt.core[qiskit]"]
test = ["mqt.qcec[qiskit]", "pytest>=7.2"]
coverage = ["mqt.qcec[test]", "pytest-cov>=4"]
docs = [
"mqt.qcec[qiskit]",
"furo>=2023.9.10",
"qiskit[visualization]",
"setuptools-scm>=8.1",
Expand Down Expand Up @@ -83,7 +89,7 @@ wheel.install-dir = "mqt/qcec"
ninja.version = ">=1.10"

# Setuptools-style build caching in a local directory
build-dir = "build/{build_type}"
build-dir = "build/{wheel_tag}/{build_type}"

# Explicitly set the package directory
wheel.packages = ["src/mqt"]
Expand All @@ -94,6 +100,9 @@ build.targets = ["pyqcec"]
# Only install the Python package component
install.components = ["mqt-qcec_Python"]

build.verbose = true
logging.level = "DEBUG"

metadata.version.provider = "scikit_build_core.metadata.setuptools_scm"
sdist.include = ["src/mqt/qcec/_version.py"]
sdist.exclude = [
Expand Down Expand Up @@ -294,16 +303,28 @@ manylinux-aarch64-image = "manylinux_2_28"
manylinux-ppc64le-image = "manylinux_2_28"
manylinux-s390x-image = "manylinux_2_28"

# The mqt-core shared libraries are provided by the mqt-core Python package.
# They should not be vendorized into the mqt-qcec wheel. This requires
# excluding the shared libraries from the repair process.

[tool.cibuildwheel.linux]
environment = { DEPLOY="ON" }
# The SOVERSION needs to be updated when the shared libraries are updated.
repair-wheel-command = """auditwheel repair -w {dest_dir} {wheel} \
--exclude libmqt-core-ir.so.2.6 \
--exclude libmqt-core-circuit-optimizer.so.2.6 \
--exclude libmqt-core-algorithms.so.2.6 \
--exclude libmqt-core-dd.so.2.6 \
--exclude libmqt-core-zx.so.2.6"""

[tool.cibuildwheel.macos]
environment = { MACOSX_DEPLOYMENT_TARGET = "10.15" }
repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel} --ignore-missing-dependencies "

[tool.cibuildwheel.windows]
before-build = "pip install delvewheel>=1.7.3"
repair-wheel-command = "delvewheel repair -v -w {dest_dir} {wheel} --namespace-pkg mqt"
environment = { CMAKE_ARGS = "-T ClangCL", SKBUILD_CMAKE_ARGS="--fresh" }
before-build = "uv pip install delvewheel>=1.7.3"
repair-wheel-command = """delvewheel repair -w {dest_dir} {wheel} --namespace-pkg mqt --no-dll \"mqt-core-ir.dll;mqt-core-circuit-optimizer.dll;mqt-core-algorithms.dll;mqt-core-dd.dll;mqt-core-zx.dll\""""
environment = { CMAKE_ARGS = "-T ClangCL" }

[[tool.cibuildwheel.overrides]]
select = "*-macosx_arm64"
Expand Down
33 changes: 28 additions & 5 deletions src/mqt/qcec/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,24 @@

from __future__ import annotations

import sys

# under Windows, make sure to add the appropriate DLL directory to the PATH
if sys.platform == "win32":

def _dll_patch() -> None:
"""Add the DLL directory to the PATH."""
import os
import sysconfig
from pathlib import Path

bin_dir = Path(sysconfig.get_paths()["purelib"]) / "mqt" / "core" / "bin"
os.add_dll_directory(str(bin_dir))

_dll_patch()
del _dll_patch

from ._version import version as __version__
from .compilation_flow_profiles import AncillaMode, generate_profile
from .pyqcec import (
ApplicationScheme,
Configuration,
Expand All @@ -16,17 +32,24 @@
StateType,
)
from .verify import verify
from .verify_compilation_flow import verify_compilation

# Conditionally import functionality depending on the availability of Qiskit
try:
from .compilation_flow_profiles import AncillaMode, generate_profile
from .verify_compilation_flow import verify_compilation

qiskit_installed = True
except ImportError:
qiskit_installed = False

__all__ = [
"AncillaMode",
"ApplicationScheme",
"Configuration",
"EquivalenceCheckingManager",
"EquivalenceCriterion",
"StateType",
"__version__",
"generate_profile",
"verify",
"verify_compilation",
]
if qiskit_installed:
__all__ += ["AncillaMode", "generate_profile", "verify_compilation"]
Loading
Loading