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

✨ ship shared C++ libraries with mqt-core Python package #662

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
6 changes: 3 additions & 3 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: CD
name: CD 🚀
on:
push:
branches: [main]
Expand All @@ -21,8 +21,8 @@ jobs:
with:
# Do not include local version information on pushes to main to facilitate TestPyPI uploads.
no-local-version: ${{ github.ref == 'refs/heads/main' && github.event_name == 'push' }}
# Do not build emulated wheels on pushes to main to reduce runner load for CD.
build-emulated-wheels: ${{ github.ref != 'refs/heads/main' || github.event_name != 'push' }}
# Do not build emulated wheels for mqt-core to avoid issues with PyPI space limitations.
build-emulated-wheels: false

# Downloads the previously generated artifacts and deploys to TestPyPI on pushes to main
deploy-test-pypi:
Expand Down
4 changes: 0 additions & 4 deletions cmake/CompilerOptions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,6 @@ function(enable_project_options target_name)

option(BINDINGS "Configure for building Python bindings")
if(BINDINGS)
check_cxx_compiler_flag(-fvisibility=hidden HAS_VISIBILITY_HIDDEN)
if(HAS_VISIBILITY_HIDDEN)
target_compile_options(${target_name} INTERFACE -fvisibility=hidden)
endif()
include(CheckPIESupported)
check_pie_supported()
set_target_properties(${target_name} PROPERTIES INTERFACE_POSITION_INDEPENDENT_CODE ON)
Expand Down
5 changes: 5 additions & 0 deletions cmake/StandardProjectSettings.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,8 @@ if(DEPLOY)
"10.15"
CACHE STRING "" FORCE)
endif()

# export all symbols by default on Windows
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS
ON
CACHE BOOL "Export all symbols on Windows")
7 changes: 4 additions & 3 deletions include/mqt-core/dd/RealNumber.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#pragma once

#include "dd/DDDefinitions.hpp"
#include "mqt_core_dd_export.h"

#include <istream>
#include <limits>
Expand Down Expand Up @@ -239,11 +240,11 @@ struct RealNumber {
namespace constants {
// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables)
/// The constant zero.
extern RealNumber zero;
MQT_CORE_DD_EXPORT extern RealNumber zero;
/// The constant one.
extern RealNumber one;
MQT_CORE_DD_EXPORT extern RealNumber one;
/// The constant sqrt(2)/2 = 1/sqrt(2).
extern RealNumber sqrt2over2;
MQT_CORE_DD_EXPORT extern RealNumber sqrt2over2;
// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)

/**
Expand Down
56 changes: 0 additions & 56 deletions include/mqt-core/python/qiskit/QuantumCircuit.hpp

This file was deleted.

29 changes: 22 additions & 7 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ qiskit = [

[project.scripts]
mqt-core-dd-compare = "mqt.core.dd.evaluation:main"
mqt-core-cli = "mqt.core.__main__:main"

[project.urls]
Homepage = "https://github.com/cda-tum/mqt-core"
Expand All @@ -68,11 +69,17 @@ ninja.version = ">=1.10"
# Setuptools-style build caching in a local directory
build-dir = "build/{wheel_tag}/{build_type}"

# Only build the Python bindings target
build.targets = ["ir"]

# Only install the Python package component
install.components = ["mqt-core_Python"]
# All the targets to build
build.targets = [
"mqt-core-ir",
"mqt-core-algorithms",
"mqt-core-circuit-optimizer",
"mqt-core-dd",
"mqt-core-zx",
"mqt-core-ds",
"mqt-core-na",
"ir",
]

metadata.version.provider = "scikit_build_core.metadata.setuptools_scm"
sdist.include = ["src/mqt/core/_version.py"]
Expand All @@ -96,6 +103,7 @@ git-only = [
[tool.scikit-build.cmake.define]
BUILD_MQT_CORE_BINDINGS = "ON"
BUILD_MQT_CORE_TESTS = "OFF"
BUILD_SHARED_LIBS = "ON"

[[tool.scikit-build.overrides]]
if.python-version = ">=3.13"
Expand All @@ -109,6 +117,13 @@ cmake.define.DISABLE_GIL = "1"
write_to = "src/mqt/core/_version.py"


[tool.check-wheel-contents]
ignore = [
"W002", # Wheel contains duplicate files (.so symlinks)
"W004", # Module is not located at importable path (/lib/mqt-core-*.so)
]


[tool.pytest.ini_options]
minversion = "7.2"
addopts = ["-ra", "--strict-markers", "--strict-config"]
Expand Down Expand Up @@ -238,8 +253,8 @@ environment = { DEPLOY = "ON" }
environment = { MACOSX_DEPLOYMENT_TARGET = "10.15" }

[tool.cibuildwheel.windows]
before-build = "uv pip install delvewheel>=1.7.3"
repair-wheel-command = "delvewheel repair -w {dest_dir} {wheel} --namespace-pkg mqt"
before-build = "uv pip install delvewheel>=1.9.0"
repair-wheel-command = "delvewheel repair -w {dest_dir} {wheel} --namespace-pkg mqt --ignore-existing"
environment = { CMAKE_GENERATOR = "Ninja" }

[[tool.cibuildwheel.overrides]]
Expand Down
24 changes: 0 additions & 24 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -87,30 +87,6 @@ add_subdirectory(zx)
# add NA library
add_subdirectory(na)

# ** Note ** The following target will soon be removed from the project. All top-level projects
# should switch to using the mqt-core Python package.
if(BINDINGS AND NOT TARGET mqt-core-python)
# add Python interface library
add_library(
${MQT_CORE_TARGET_NAME}-python ${MQT_CORE_INCLUDE_BUILD_DIR}/python/qiskit/QuantumCircuit.hpp
python/qiskit/QuantumCircuit.cpp)

# link with main project library and pybind11 libraries
target_link_libraries(
${MQT_CORE_TARGET_NAME}-python
PUBLIC MQT::CoreIR pybind11::pybind11
PRIVATE MQT::ProjectOptions MQT::ProjectWarnings)

# add MQT alias
add_library(MQT::CorePython ALIAS ${MQT_CORE_TARGET_NAME}-python)
set_target_properties(
${MQT_CORE_TARGET_NAME}-python
PROPERTIES VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
EXPORT_NAME CorePython)
list(APPEND MQT_CORE_TARGETS ${MQT_CORE_TARGET_NAME}-python)
endif()

if(BUILD_MQT_CORE_BINDINGS)
add_subdirectory(python)
endif()
Expand Down
15 changes: 15 additions & 0 deletions src/mqt/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,22 @@
from __future__ import annotations

import os
import sys
from pathlib import Path

# 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 sysconfig

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

_dll_patch()
del _dll_patch

from typing import TYPE_CHECKING

from ._version import version as __version__
Expand Down
54 changes: 54 additions & 0 deletions src/mqt/core/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Copyright (c) 2025 Chair for Design Automation, TUM
# All rights reserved.
#
# SPDX-License-Identifier: MIT
#
# Licensed under the MIT License

"""Command line interface for mqt-core."""

from __future__ import annotations

import argparse
import sys

from ._commands import cmake_dir, include_dir
from ._version import version as __version__


def main() -> None:
"""Entry point for the mqt-core command line interface.

This function is called when running the `mqt-core-cli` script.

.. code-block:: bash

mqt-core-cli [--version] [--include_dir] [--cmake_dir]

It provides the following command line options:

- :code:`--version`: Print the version and exit.
- :code:`--include_dir`: Print the path to the mqt-core C++ include directory.
- :code:`--cmake_dir`: Print the path to the mqt-core CMake module directory.

"""
parser = argparse.ArgumentParser()
parser.add_argument("--version", action="version", version=__version__, help="Print version and exit.")

parser.add_argument(
"--include_dir", action="store_true", help="Print the path to the mqt-core C++ include directory."
)
parser.add_argument(
"--cmake_dir", action="store_true", help="Print the path to the mqt-core CMake module directory."
)
args = parser.parse_args()
if not sys.argv[1:]:
parser.print_help()
if args.include_dir:
print(include_dir().resolve())
if args.cmake_dir:
print(cmake_dir().resolve())


if __name__ == "__main__":
main()
51 changes: 51 additions & 0 deletions src/mqt/core/_commands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Copyright (c) 2025 Chair for Design Automation, TUM
# All rights reserved.
#
# SPDX-License-Identifier: MIT
#
# Licensed under the MIT License

"""Useful commands for obtaining information about mqt-core."""

from __future__ import annotations

from importlib.metadata import PackageNotFoundError, distribution
from pathlib import Path


def include_dir() -> Path:
"""Return the path to the mqt-core include directory.

Raises:
FileNotFoundError: If the include directory is not found.
ImportError: If mqt-core is not installed.
"""
try:
dist = distribution("mqt-core")
located_include_dir = Path(dist.locate_file("mqt/core/include/mqt-core"))
if located_include_dir.exists() and located_include_dir.is_dir():
return located_include_dir
msg = "mqt-core include files not found."
raise FileNotFoundError(msg)
except PackageNotFoundError:
msg = "mqt-core not installed, installation required to access the include files."
raise ImportError(msg) from None


def cmake_dir() -> Path:
"""Return the path to the mqt-core CMake module directory.

Raises:
FileNotFoundError: If the CMake module directory is not found.
ImportError: If mqt-core is not installed.
"""
try:
dist = distribution("mqt-core")
located_cmake_dir = Path(dist.locate_file("mqt/core/share/cmake"))
if located_cmake_dir.exists() and located_cmake_dir.is_dir():
return located_cmake_dir
msg = "mqt-core CMake files not found."
raise FileNotFoundError(msg)
except PackageNotFoundError:
msg = "mqt-core not installed, installation required to access the CMake files."
raise ImportError(msg) from None
Loading
Loading