Skip to content

Commit

Permalink
✨ switch to mqt-core Python package
Browse files Browse the repository at this point in the history
Signed-off-by: burgholzer <burgholzer@me.com>
  • Loading branch information
burgholzer committed Feb 12, 2024
1 parent 6c9d6ee commit 4d680b7
Show file tree
Hide file tree
Showing 12 changed files with 97 additions and 121 deletions.
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ repos:
- importlib_resources
- numpy
- pytest
- mqt.core~=2.2.2
- mqt.qcec

# Check for spelling
Expand Down
19 changes: 13 additions & 6 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 All @@ -26,12 +39,6 @@ if(BUILD_MQT_QMAP_BINDINGS)
find_package(pybind11 CONFIG REQUIRED)
endif()

set(FETCHCONTENT_SOURCE_DIR_MQT-CORE
${PROJECT_SOURCE_DIR}/extern/mqt-core
CACHE
PATH
"Path to the source directory of the mqt-core library. This variable is used by FetchContent to download the library if it is not already available."
)
set(MQT_CORE_VERSION
2.2.2
CACHE STRING "MQT Core version")
Expand Down
15 changes: 12 additions & 3 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
PYTHON_ALL_VERSIONS = ["3.8", "3.9", "3.10", "3.11", "3.12"]

BUILD_REQUIREMENTS = [
"mqt.core~=2.2.2",
"scikit-build-core[pyproject]>=0.6.1",
"setuptools_scm>=7",
"pybind11>=2.11",
Expand Down Expand Up @@ -51,7 +52,12 @@ def _run_tests(
_extras.append("coverage")
posargs.append("--cov-config=pyproject.toml")

session.install(*BUILD_REQUIREMENTS, *install_args, env=env)
# On Linux, `mqt-core` needs to be installed with `--no-binary` to avoid ABI
# incompatibility issues caused by compiling with different GCC versions.
if sys.platform == "linux":
install_args = ["--no-binary", "mqt.core", *install_args]

session.install("-v", *BUILD_REQUIREMENTS, *install_args, env=env)
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 @@ -86,8 +92,11 @@ def docs(session: nox.Session) -> None:
session.error("Must not specify non-HTML builder with --serve")

extra_installs = ["sphinx-autobuild"] if args.serve else []
session.install(*BUILD_REQUIREMENTS, *extra_installs)
session.install("--no-build-isolation", "-ve.[docs]")
# On Linux, `mqt-core` needs to be installed with `--no-binary` to avoid ABI
# incompatibility issues caused by compiling with different GCC versions.
install_args = ["--no-binary", "mqt.core"] if sys.platform == "linux" else []
session.install(*BUILD_REQUIREMENTS, *extra_installs, *install_args)
session.install("--no-build-isolation", "-ve.[docs]", *install_args)
session.chdir("docs")

if args.builder == "linkcheck":
Expand Down
9 changes: 7 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
[build-system]
requires = ["scikit-build-core>=0.6.1", "setuptools-scm>=7", "pybind11>=2.11"]
requires = [
"scikit-build-core>=0.6.1",
"setuptools-scm>=7",
"pybind11>=2.11",
"mqt.core~=2.2.2",
]
build-backend = "scikit_build_core.build"

[project]
Expand Down Expand Up @@ -36,7 +41,7 @@ classifiers = [
]
requires-python = ">=3.8"
dependencies = [
"qiskit[qasm3-import]>=0.46.0",
"mqt.core[qiskit]~=2.2.2",
"rustworkx[all]>=0.13.0",
"importlib_resources>=5.0; python_version < '3.10'",
"typing_extensions>=4.0"
Expand Down
25 changes: 16 additions & 9 deletions src/mqt/qmap/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,22 @@
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": # pragma: no cover
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))

from . import visualization
from ._version import version as __version__
Expand All @@ -35,7 +44,6 @@
LookaheadHeuristic,
MappingResults,
Method,
QuantumComputation,
SwapReduction,
SynthesisConfiguration,
SynthesisResults,
Expand All @@ -58,7 +66,6 @@
"LookaheadHeuristic",
"MappingResults",
"Method",
"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.io 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 import QuantumComputation

from .visualization import SearchVisualizer

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

from mqt.core.io 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
13 changes: 2 additions & 11 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 import QuantumComputation

class Arch:
__members__: ClassVar[dict[Arch, int]] = ... # read-only
Expand Down Expand Up @@ -376,7 +376,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 @@ -468,15 +468,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
29 changes: 20 additions & 9 deletions src/python/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
if(APPLE)
set(BASEPOINT @loader_path)
else()
set(BASEPOINT $ORIGIN)
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
# Prefer thin LTO if available
Expand All @@ -7,15 +25,8 @@ pybind11_add_module(
# Source code goes here
bindings.cpp)
target_compile_definitions(pyqmap PRIVATE Z3_FOUND)
target_link_libraries(
pyqmap
PRIVATE MQT::QMapExact
MQT::QMapHeuristic
MQT::QMapCliffordSynthesis
MQT::CorePython
MQT::ProjectOptions
MQT::ProjectWarnings
pybind11_json)
target_link_libraries(pyqmap PRIVATE MQT::QMapExact MQT::QMapHeuristic MQT::QMapCliffordSynthesis
MQT::ProjectOptions MQT::ProjectWarnings pybind11_json)

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

0 comments on commit 4d680b7

Please sign in to comment.