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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
3 changes: 2 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ repos:
- numpy
- pytest
- rustworkx
- mqt.qcec
- mqt.core
- mqt.qcec @ git+https://github.com/cda-tum/mqt-qcec.git@use-mqt-core-package

# Check for spelling
- repo: https://github.com/crate-ci/typos
Expand Down
13 changes: 13 additions & 0 deletions cmake/ExternalDependencies.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,19 @@ if(NOT Z3_FOUND)
endif()

if(BUILD_MQT_QMAP_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 Down
22 changes: 22 additions & 0 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -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", "--reinstall-package", "mqt.qmap", install_arg, *install_args, env=env)
session.run("pytest", *run_args, *posargs, env=env)
Expand Down Expand Up @@ -99,6 +112,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]", "--reinstall-package", "mqt.qmap")
session.chdir("docs")

Expand Down
17 changes: 11 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
[build-system]
requires = [
"scikit-build-core>=0.10.1",
"setuptools-scm>=7",
"pybind11>=2.13.5",
"scikit-build-core>=0.10.1",
"setuptools-scm>=7",
"pybind11>=2.13.5",
"mqt.core @ git+https://github.com/cda-tum/mqt-core.git@shared-libs",
]
build-backend = "scikit_build_core.build"

Expand Down Expand Up @@ -41,7 +42,7 @@ classifiers = [
]
requires-python = ">=3.8"
dependencies = [
"qiskit[qasm3-import]>=1.0.0",
"mqt.core[qiskit] @ git+https://github.com/cda-tum/mqt-core.git@shared-libs",
"rustworkx[all]>=0.14.0",
"importlib_resources>=5.0; python_version < '3.10'",
"typing_extensions>=4.6; python_version < '3.10'",
Expand Down Expand Up @@ -315,13 +316,17 @@ environment = { Z3_ROOT="/opt/python/cp311-cp311/lib/python3.11/site-packages/z3
before-all = "/opt/python/cp311-cp311/bin/pip install z3-solver==4.12.6"
repair-wheel-command = [
"export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/python/cp311-cp311/lib/python3.11/site-packages/z3/lib",
"auditwheel repair -w {dest_dir} {wheel}",
"""auditwheel repair -w {dest_dir} {wheel} \
--exclude libmqt-core-ir.so.2.6 \
--exclude libmqt-core-circuit-optimizer.so.2.6 \
--exclude libmqt-core-na.so.2.6""",
]

[tool.cibuildwheel.macos]
environment = { MACOSX_DEPLOYMENT_TARGET = "11.0" }
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"
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-na.dll\""""
environment = { CMAKE_GENERATOR = "Ninja", SKBUILD_CMAKE_ARGS="--fresh" }
33 changes: 22 additions & 11 deletions src/mqt/qmap/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,30 @@

from __future__ import annotations

import os
import sys
from pathlib import Path

if sys.platform == "win32" and sys.version_info > (3, 8, 0) and "Z3_ROOT" in os.environ:
lib_path = Path(os.environ["Z3_ROOT"]) / "lib"
if lib_path.exists():
os.add_dll_directory(str(lib_path))
bin_path = Path(os.environ["Z3_ROOT"]) / "bin"
if bin_path.exists():
os.add_dll_directory(str(bin_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 os
import sysconfig
from pathlib import Path

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

if sys.version_info > (3, 8, 0) and "Z3_ROOT" in os.environ:
lib_path = Path(os.environ["Z3_ROOT"]) / "lib"
if lib_path.exists():
os.add_dll_directory(str(lib_path))
bin_path = Path(os.environ["Z3_ROOT"]) / "bin"
if bin_path.exists():
os.add_dll_directory(str(bin_path))

_dll_patch()
del _dll_patch

from ._version import version as __version__
from .clifford_synthesis import optimize_clifford, synthesize_clifford
Expand All @@ -39,7 +52,6 @@
MappingResults,
Method,
NeutralAtomHybridArchitecture,
QuantumComputation,
SwapReduction,
SynthesisConfiguration,
SynthesisResults,
Expand Down Expand Up @@ -67,7 +79,6 @@
"MappingResults",
"Method",
"NeutralAtomHybridArchitecture",
"QuantumComputation",
"SubarchitectureOrder",
"SwapReduction",
"SynthesisConfiguration",
Expand Down
25 changes: 8 additions & 17 deletions src/mqt/qmap/clifford_synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,26 @@

from __future__ import annotations

from typing import Any
from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
from .compile import CircuitInputType

from qiskit import QuantumCircuit, qasm3
from qiskit.quantum_info import Clifford, PauliList
from qiskit.transpiler.layout import TranspileLayout

from mqt.core import load

from .compile import extract_initial_layout_from_qasm
from .pyqmap import (
CliffordSynthesizer,
QuantumComputation,
SynthesisConfiguration,
SynthesisResults,
Tableau,
)


def _import_circuit(circuit: str | QuantumCircuit | QuantumComputation) -> QuantumComputation:
"""Import a circuit from a string, a QuantumCircuit, or a QuantumComputation."""
if isinstance(circuit, QuantumCircuit):
return QuantumComputation.from_qiskit(circuit)
if isinstance(circuit, str):
if circuit.endswith(".qasm"):
return QuantumComputation.from_file(circuit)
return QuantumComputation.from_qasm_str(circuit)
return circuit


def _reverse_paulis(paulis: list[str]) -> list[str]:
return [s[0] + s[:0:-1] if s[0] in "+-" else s[::-1] for s in paulis]

Expand Down Expand Up @@ -84,11 +77,9 @@ def _circuit_from_qasm(qasm: str) -> QuantumCircuit:
"""Create a proper :class:`qiskit.QuantumCircuit` from a QASM string (including layout information)."""
circ = qasm3.loads(qasm)
layout = extract_initial_layout_from_qasm(qasm, circ.qregs)

circ._layout = TranspileLayout( # noqa: SLF001
initial_layout=layout, input_qubit_mapping=layout.get_virtual_bits()
)

return circ


Expand Down Expand Up @@ -138,7 +129,7 @@ def synthesize_clifford(


def optimize_clifford(
circuit: str | QuantumCircuit | QuantumComputation,
circuit: CircuitInputType,
initial_tableau: str | Clifford | PauliList | Tableau | None = None,
include_destabilizers: bool = False,
**kwargs: Any, # noqa: ANN401
Expand Down Expand Up @@ -168,7 +159,7 @@ def optimize_clifford(
"""
config = _config_from_kwargs(kwargs)

qc = _import_circuit(circuit)
qc = load(circuit)
if initial_tableau is not None:
synthesizer = CliffordSynthesizer(_import_tableau(initial_tableau, include_destabilizers), qc)
else:
Expand Down
15 changes: 12 additions & 3 deletions src/mqt/qmap/compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,26 @@

from __future__ import annotations

from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Union

from qiskit import QuantumCircuit, QuantumRegister, qasm3
from qiskit.transpiler import Layout, TranspileLayout

if TYPE_CHECKING:
from os import PathLike

from qiskit.providers import Backend
from qiskit.providers.models import BackendProperties
from qiskit.transpiler.target import Target

from mqt.core.ir import QuantumComputation

from .visualization import SearchVisualizer

CircuitInputType = Union[QuantumComputation, str, PathLike[str], QuantumCircuit]

from mqt.core import load

from .load_architecture import load_architecture
from .load_calibration import load_calibration
from .pyqmap import (
Expand Down Expand Up @@ -59,7 +67,7 @@ def extract_initial_layout_from_qasm(qasm: str, qregs: list[QuantumRegister]) ->


def compile( # noqa: A001
circ: QuantumCircuit | str,
circ: CircuitInputType,
arch: str | Arch | Architecture | Backend | None,
calibration: str | BackendProperties | Target | None = None,
method: str | Method = "heuristic",
Expand Down Expand Up @@ -182,7 +190,8 @@ def compile( # noqa: A001
config.lookaheads = lookaheads
config.lookahead_factor = lookahead_factor

results = map(circ, architecture, config)
qc = load(circ)
results = map(qc, architecture, config)

circ = qasm3.loads(results.mapped_circuit)
layout = extract_initial_layout_from_qasm(results.mapped_circuit, circ.qregs)
Expand Down
17 changes: 5 additions & 12 deletions src/mqt/qmap/pyqmap.pyi
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Any, ClassVar, overload

from qiskit import QuantumCircuit
from mqt.core.ir import QuantumComputation

class Arch:
__members__: ClassVar[dict[Arch, int]] = ... # read-only
Expand Down Expand Up @@ -393,7 +393,7 @@ class SwapReduction:
@property
def value(self) -> int: ...

def map(circ: str | QuantumCircuit, arch: Architecture, config: Configuration) -> MappingResults: ... # noqa: A001
def map(circ: QuantumComputation, arch: Architecture, config: Configuration) -> MappingResults: ... # noqa: A001

class TargetMetric:
__members__: ClassVar[dict[TargetMetric, int]] = ... # read-only
Expand Down Expand Up @@ -488,15 +488,6 @@ class SynthesisResults:
@property
def two_qubit_gates(self) -> int: ...

class QuantumComputation:
def __init__(self) -> None: ...
@staticmethod
def from_file(file: str) -> QuantumComputation: ...
@staticmethod
def from_qasm_str(qasm: str) -> QuantumComputation: ...
@staticmethod
def from_qiskit(circuit: QuantumCircuit) -> QuantumComputation: ...

class Tableau:
@overload
def __init__(self, n: int, include_stabilizers: bool = False) -> None: ...
Expand Down Expand Up @@ -586,7 +577,9 @@ class HybridNAMapper:
def get_init_hw_pos(self) -> dict[int, int]: ...
def get_mapped_qc(self) -> str: ...
def get_mapped_qc_aod(self) -> str: ...
def map(self, circ: object, initial_mapping: InitialCircuitMapping = ..., verbose: bool = ...) -> None: ...
def map(
self, circ: QuantumComputation, initial_mapping: InitialCircuitMapping = ..., verbose: bool = ...
) -> None: ...
def map_qasm_file(
self, filename: str, initial_mapping: InitialCircuitMapping = ..., verbose: bool = ...
) -> None: ...
Expand Down
12 changes: 10 additions & 2 deletions src/python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ endif()
list(APPEND CMAKE_INSTALL_RPATH ${BASEPOINT} ${BASEPOINT}/${CMAKE_INSTALL_LIBDIR})
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
list(
APPEND
CMAKE_INSTALL_RPATH
${BASEPOINT}/../core/${CMAKE_INSTALL_LIBDIR}
${BASEPOINT}/../core/lib
${BASEPOINT}/../core/lib64
${BASEPOINT}/../../core/${CMAKE_INSTALL_LIBDIR}
${BASEPOINT}/../../core/lib
${BASEPOINT}/../../core/lib64)

pybind11_add_module(
pyqmap
Expand All @@ -21,10 +30,9 @@ target_link_libraries(
PRIVATE MQT::QMapExact
MQT::QMapHeuristic
MQT::QMapCliffordSynthesis
MQT::CorePython
MQT::QMapHybrid
MQT::ProjectOptions
MQT::ProjectWarnings
MQT::QMapHybrid
pybind11_json)

# Install directive for scikit-build-core
Expand Down
Loading
Loading