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

Open
wants to merge 2 commits 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
2 changes: 1 addition & 1 deletion .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
workflow_dispatch:
pull_request:
paths:
- .github/workflows/cd.yml
- .github/workflows/cd.yml # This is to make sure changes to the CD workflow are tested before deployment.

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
Expand Down
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ repos:
additional_dependencies:
- 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_DDSIM_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 "9be91674dddcf1f73143d9509e8e4ad806f47592"
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
1 change: 0 additions & 1 deletion docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ We appreciate any feedback and contributions to the project. If you want to cont
Installation
Quickstart
Simulators
Usage
Primitives
FAQ
Publications
Expand Down
11 changes: 8 additions & 3 deletions docs/source/simulators/CircuitSimulator.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,12 @@
"metadata": {},
"outputs": [],
"source": [
"from mqt.core import load\n",
"from mqt.ddsim import CircuitSimulator\n",
"\n",
"# create a CircuitSimulator object\n",
"sim = CircuitSimulator(filename)\n",
"qc = load(filename)\n",
"sim = CircuitSimulator(qc)\n",
"\n",
"# run the simulation\n",
"result = sim.simulate(shots=1024)\n",
Expand Down Expand Up @@ -178,7 +180,8 @@
"qc.measure([0, 1], [0, 1])\n",
"\n",
"# create a CircuitSimulator object\n",
"sim = CircuitSimulator(qc)\n",
"circ = load(qc)\n",
"sim = CircuitSimulator(circ)\n",
"\n",
"# run the simulation\n",
"result = sim.simulate(shots=1024)\n",
Expand Down Expand Up @@ -274,10 +277,12 @@
"metadata": {},
"outputs": [],
"source": [
"from mqt.core import load\n",
"from mqt.ddsim import CircuitSimulator\n",
"\n",
"# create a CircuitSimulator object\n",
"sim = CircuitSimulator(filename)\n",
"circ = load(filename)\n",
"sim = CircuitSimulator(circ)\n",
"\n",
"# run the simulation\n",
"result = sim.simulate(shots=1024)\n",
Expand Down
28 changes: 21 additions & 7 deletions docs/source/simulators/UnitarySimulator.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,12 @@
"source": [
"import graphviz\n",
"\n",
"from mqt.core import load\n",
"from mqt.ddsim import UnitarySimulator\n",
"\n",
"# Create the simulator\n",
"sim = UnitarySimulator(qc)\n",
"circ = load(qc)\n",
"sim = UnitarySimulator(circ)\n",
"\n",
"# Construct the decision diagram representation of the unitary\n",
"sim.construct()\n",
Expand Down Expand Up @@ -117,10 +119,12 @@
"source": [
"import graphviz\n",
"\n",
"from mqt.core import load\n",
"from mqt.ddsim import UnitarySimulator\n",
"\n",
"# Create the simulator\n",
"sim = UnitarySimulator(qc)\n",
"circ = load(qc)\n",
"sim = UnitarySimulator(circ)\n",
"\n",
"# Construct the decision diagram representation of the unitary\n",
"sim.construct()\n",
Expand Down Expand Up @@ -181,10 +185,12 @@
"source": [
"import graphviz\n",
"\n",
"from mqt.core import load\n",
"from mqt.ddsim import UnitarySimulator\n",
"\n",
"# Create the simulator\n",
"sim = UnitarySimulator(qc)\n",
"circ = load(qc)\n",
"sim = UnitarySimulator(circ)\n",
"\n",
"# Construct the decision diagram representation of the unitary\n",
"sim.construct()\n",
Expand Down Expand Up @@ -248,10 +254,12 @@
"source": [
"import graphviz\n",
"\n",
"from mqt.core import load\n",
"from mqt.ddsim import UnitarySimulator\n",
"\n",
"# Create the simulator\n",
"sim = UnitarySimulator(qc)\n",
"circ = load(qc)\n",
"sim = UnitarySimulator(circ)\n",
"\n",
"# Construct the decision diagram representation of the unitary\n",
"sim.construct()\n",
Expand Down Expand Up @@ -317,10 +325,12 @@
"source": [
"import graphviz\n",
"\n",
"from mqt.core import load\n",
"from mqt.ddsim import UnitarySimulator\n",
"\n",
"# Create the simulator\n",
"sim = UnitarySimulator(qc)\n",
"circ = load(qc)\n",
"sim = UnitarySimulator(circ)\n",
"\n",
"# Construct the decision diagram representation of the unitary\n",
"sim.construct()\n",
Expand Down Expand Up @@ -390,10 +400,12 @@
"source": [
"import graphviz\n",
"\n",
"from mqt.core import load\n",
"from mqt.ddsim import UnitarySimulator\n",
"\n",
"# Create the simulator\n",
"sim = UnitarySimulator(qc)\n",
"circ = load(qc)\n",
"sim = UnitarySimulator(circ)\n",
"\n",
"# Construct the decision diagram representation of the unitary\n",
"sim.construct()\n",
Expand Down Expand Up @@ -502,6 +514,7 @@
"import graphviz\n",
"from qiskit import QuantumCircuit\n",
"\n",
"from mqt.core import load\n",
"from mqt.ddsim import ConstructionMode, UnitarySimulator\n",
"\n",
"qc = QuantumCircuit(3)\n",
Expand All @@ -511,7 +524,8 @@
"qc.cx(2, 1)\n",
"\n",
"# Create the simulator\n",
"sim = UnitarySimulator(qc, mode=ConstructionMode.recursive)\n",
"circ = load(qc)\n",
"sim = UnitarySimulator(circ, mode=ConstructionMode.recursive)\n",
"\n",
"# Construct the decision diagram representation of the unitary\n",
"sim.construct()\n",
Expand Down
22 changes: 22 additions & 0 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,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.ddsim", 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]", "--reinstall-package", "mqt.ddsim")
session.chdir("docs")

Expand Down
25 changes: 18 additions & 7 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@shared-libs",
]
build-backend = "scikit_build_core.build"

Expand Down Expand Up @@ -39,7 +40,7 @@ classifiers = [
]
requires-python = ">=3.8"
dependencies = [
"qiskit[qasm3-import]>=1.0.0"
"mqt.core[qiskit] @ git+https://github.com/cda-tum/mqt-core@shared-libs",
]
dynamic = ["version"]

Expand All @@ -51,7 +52,7 @@ tnflow = [
"quimb",
"pandas",
"numpy",
"kahypar",
"kahypar; python_version < '3.12'", # getting kahypar installed on Python 3.12+ is hard
"networkx",
]
test = ["pytest>=7.0"]
Expand Down Expand Up @@ -294,13 +295,23 @@ 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-dd.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"
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-dd.dll\""""
environment = { CMAKE_ARGS = "-T ClangCL", SKBUILD_CMAKE_ARGS="--fresh" }
17 changes: 17 additions & 0 deletions src/mqt/ddsim/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@

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 .provider import DDSIMProvider
from .pyddsim import (
Expand Down
4 changes: 3 additions & 1 deletion src/mqt/ddsim/deterministicnoisesimulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from qiskit.result.models import ExperimentResult, ExperimentResultData

from mqt import ddsim
from mqt.core import load

from .header import DDSIMHeader
from .qasmsimulator import QasmSimulatorBackend
Expand Down Expand Up @@ -55,8 +56,9 @@ def _run_experiment(qc: QuantumCircuit, **options: dict[str, Any]) -> Experiment
seed = cast(int, options.get("simulator_seed", -1))
shots = cast(int, options.get("shots", 1024))

circ = load(qc)
sim = ddsim.DeterministicNoiseSimulator(
circ=qc,
circ=circ,
seed=seed,
noise_effects=noise_effects,
noise_probability=noise_probability,
Expand Down
5 changes: 4 additions & 1 deletion src/mqt/ddsim/hybridqasmsimulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
from qiskit.transpiler import Target
from qiskit.utils.multiprocessing import local_hardware_info

from mqt.core import load

from .header import DDSIMHeader
from .pyddsim import HybridCircuitSimulator, HybridMode
from .qasmsimulator import QasmSimulatorBackend
Expand Down Expand Up @@ -81,7 +83,8 @@ def _run_experiment(self, qc: QuantumCircuit, **options: Any) -> ExperimentResul
msg = f"Simulation mode{mode} not supported by hybrid simulator. Available modes are 'amplitude' and 'dd'."
raise QiskitError(msg)

sim = HybridCircuitSimulator(qc, seed=seed, mode=hybrid_mode, nthreads=nthreads)
circuit = load(qc)
sim = HybridCircuitSimulator(circuit, seed=seed, mode=hybrid_mode, nthreads=nthreads)

shots = options.get("shots", 1024)
if self._SHOW_STATE_VECTOR and shots > 0:
Expand Down
Loading
Loading