Skip to content

Commit cf3dfb9

Browse files
committed
✨ switch to mqt-core Python package
Signed-off-by: burgholzer <burgholzer@me.com>
1 parent a50b525 commit cf3dfb9

17 files changed

+153
-125
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ repos:
103103
additional_dependencies:
104104
- numpy
105105
- pytest
106+
- mqt.core~=2.2.2
106107

107108
# Check for spelling
108109
- repo: https://github.com/codespell-project/codespell

cmake/ExternalDependencies.cmake

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,19 @@ include(FetchContent)
44
set(FETCH_PACKAGES "")
55

66
if(BUILD_MQT_DDSIM_BINDINGS)
7+
# Manually detect the installed mqt-core package.
8+
execute_process(
9+
COMMAND "${Python_EXECUTABLE}" -m mqt.core --cmake_dir
10+
OUTPUT_STRIP_TRAILING_WHITESPACE
11+
OUTPUT_VARIABLE mqt-core_DIR
12+
ERROR_QUIET)
13+
14+
# Add the detected directory to the CMake prefix path.
15+
if(mqt-core_DIR)
16+
list(APPEND CMAKE_PREFIX_PATH "${mqt-core_DIR}")
17+
message(STATUS "Found mqt-core package: ${mqt-core_DIR}")
18+
endif()
19+
720
if(NOT SKBUILD)
821
# Manually detect the installed pybind11 package.
922
execute_process(
@@ -19,12 +32,6 @@ if(BUILD_MQT_DDSIM_BINDINGS)
1932
find_package(pybind11 CONFIG REQUIRED)
2033
endif()
2134

22-
set(FETCHCONTENT_SOURCE_DIR_MQT-CORE
23-
${PROJECT_SOURCE_DIR}/extern/mqt-core
24-
CACHE
25-
PATH
26-
"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."
27-
)
2835
set(MQT_CORE_VERSION
2936
2.2.2
3037
CACHE STRING "MQT Core version")

noxfile.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
PYTHON_ALL_VERSIONS = ["3.8", "3.9", "3.10", "3.11", "3.12"]
1818

1919
BUILD_REQUIREMENTS = [
20+
"mqt.core~=2.2.2",
2021
"scikit-build-core[pyproject]>=0.6.1",
2122
"setuptools_scm>=7",
2223
"pybind11>=2.11",
@@ -54,7 +55,12 @@ def _run_tests(
5455
_extras.append("coverage")
5556
posargs.append("--cov-config=pyproject.toml")
5657

57-
session.install(*BUILD_REQUIREMENTS, *install_args, env=env)
58+
# On Linux, `mqt-core` needs to be installed with `--no-binary` to avoid ABI
59+
# incompatibility issues caused by compiling with different GCC versions.
60+
if sys.platform == "linux":
61+
install_args = ["--no-binary", "mqt.core", *install_args]
62+
63+
session.install("-v", *BUILD_REQUIREMENTS, *install_args, env=env)
5864
install_arg = f"-ve.[{','.join(_extras)}]"
5965
session.install("--no-build-isolation", install_arg, *install_args, env=env)
6066
session.run("pytest", *run_args, *posargs, env=env)
@@ -89,8 +95,11 @@ def docs(session: nox.Session) -> None:
8995
session.error("Must not specify non-HTML builder with --serve")
9096

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

96105
if args.builder == "linkcheck":

pyproject.toml

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
[build-system]
2-
requires = ["scikit-build-core>=0.6.1", "setuptools-scm>=7", "pybind11>=2.11"]
2+
requires = [
3+
"scikit-build-core>=0.6.1",
4+
"setuptools-scm>=7",
5+
"pybind11>=2.11",
6+
"mqt.core~=2.2.2",
7+
]
38
build-backend = "scikit_build_core.build"
49

510
[project]
@@ -34,7 +39,7 @@ classifiers = [
3439
]
3540
requires-python = ">=3.8"
3641
dependencies = [
37-
"qiskit[qasm3-import]>=0.45.0"
42+
"mqt.core[qiskit]~=2.2.2",
3843
]
3944
dynamic = ["version"]
4045

@@ -105,14 +110,6 @@ sdist.exclude = [
105110
"**/plots",
106111
"**/test",
107112
"**/tests",
108-
"extern/mqt-core/extern/json/include",
109-
"extern/mqt-core/extern/googletest",
110-
"extern/mqt-core/extern/boost/config/checks",
111-
"extern/mqt-core/extern/boost/config/tools",
112-
"extern/mqt-core/extern/boost/multiprecision/config",
113-
"extern/mqt-core/extern/boost/multiprecision/example",
114-
"extern/mqt-core/extern/boost/multiprecision/performance",
115-
"extern/mqt-core/extern/boost/multiprecision/tools"
116113
]
117114

118115
[tool.scikit-build.cmake.define]

src/mqt/ddsim/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
from __future__ import annotations
22

3+
import sys
4+
5+
# under Windows, make sure to add the appropriate DLL directory to the PATH
6+
if sys.platform == "win32": # pragma: no cover
7+
import os
8+
import sysconfig
9+
from pathlib import Path
10+
11+
bin_dir = Path(sysconfig.get_paths()["purelib"]) / "mqt" / "core" / "bin"
12+
os.add_dll_directory(str(bin_dir))
13+
314
from ._version import version as __version__
415
from .provider import DDSIMProvider
516
from .pyddsim import (

src/mqt/ddsim/hybridqasmsimulator.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
from qiskit.transpiler import Target
1515
from qiskit.utils.multiprocessing import local_hardware_info
1616

17+
from mqt.core.io import load
18+
1719
from .header import DDSIMHeader
1820
from .pyddsim import HybridCircuitSimulator, HybridMode
1921
from .qasmsimulator import QasmSimulatorBackend
@@ -74,7 +76,8 @@ def _run_experiment(self, qc: QuantumCircuit, **options: Any) -> ExperimentResul
7476
msg = f"Simulation mode{mode} not supported by hybrid simulator. Available modes are 'amplitude' and 'dd'."
7577
raise QiskitError(msg)
7678

77-
sim = HybridCircuitSimulator(qc, seed=seed, mode=hybrid_mode, nthreads=nthreads)
79+
circuit = load(qc)
80+
sim = HybridCircuitSimulator(circuit, seed=seed, mode=hybrid_mode, nthreads=nthreads)
7881

7982
shots = options.get("shots", 1024)
8083
if self._SHOW_STATE_VECTOR and shots > 0:

src/mqt/ddsim/pathqasmsimulator.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@
77
from typing import TYPE_CHECKING, Any
88

99
if TYPE_CHECKING:
10+
from qiskit import QuantumCircuit
1011
from quimb.tensor import Tensor, TensorNetwork
1112

12-
from qiskit import QuantumCircuit
13+
from mqt.core import QuantumComputation
14+
1315
from qiskit.providers import Options
1416
from qiskit.result.models import ExperimentResult, ExperimentResultData
1517
from qiskit.transpiler import Target
1618

19+
from mqt.core.io import load
20+
1721
from .header import DDSIMHeader
1822
from .pyddsim import PathCircuitSimulator, PathSimulatorConfiguration, PathSimulatorMode
1923
from .qasmsimulator import QasmSimulatorBackend
@@ -38,18 +42,14 @@ def read_tensor_network_file(filename: str) -> list[Tensor]:
3842
return tensors
3943

4044

41-
def create_tensor_network(qc: QuantumCircuit) -> TensorNetwork:
45+
def create_tensor_network(qc: QuantumComputation) -> TensorNetwork:
4246
import quimb.tensor as qtn
4347
import sparse
4448

4549
from mqt.ddsim import dump_tensor_network
4650

47-
if isinstance(qc, QuantumCircuit):
48-
filename = qc.name + "_" + str(qc.num_qubits) + ".tensor"
49-
nqubits = qc.num_qubits
50-
else:
51-
filename = "tensor.tensor"
52-
nqubits = qc.header.n_qubits
51+
filename = qc.name + "_" + str(qc.num_qubits) + ".tensor"
52+
nqubits = qc.num_qubits
5353

5454
dump_tensor_network(qc, filename)
5555
tensors = read_tensor_network_file(filename)
@@ -78,7 +78,7 @@ def create_tensor_network(qc: QuantumCircuit) -> TensorNetwork:
7878

7979

8080
def get_simulation_path(
81-
qc: QuantumCircuit,
81+
qc: QuantumComputation,
8282
max_time: int = 60,
8383
max_repeats: int = 1024,
8484
parallel_runs: int = 1,
@@ -101,7 +101,7 @@ def get_simulation_path(
101101
path = linear_to_ssa(info.path)
102102

103103
if dump_path:
104-
filename = qc.name + "_" + str(qc.num_qubits) + ".path" if isinstance(qc, QuantumCircuit) else "simulation.path"
104+
filename = qc.name + "_" + str(qc.num_qubits) + ".path"
105105
with pathlib.Path(filename).open("w") as file:
106106
file.write(str(path))
107107

@@ -181,7 +181,8 @@ def _run_experiment(self, qc: QuantumCircuit, **options: Any) -> ExperimentResul
181181
if seed is not None:
182182
pathsim_configuration.seed = seed
183183

184-
sim = PathCircuitSimulator(qc, config=pathsim_configuration)
184+
circuit = load(qc)
185+
sim = PathCircuitSimulator(circuit, config=pathsim_configuration)
185186

186187
# determine the contraction path using cotengra in case this is requested
187188
if pathsim_configuration.mode == PathSimulatorMode.cotengra:
@@ -190,7 +191,11 @@ def _run_experiment(self, qc: QuantumCircuit, **options: Any) -> ExperimentResul
190191
dump_path = options.get("cotengra_dump_path", False)
191192
plot_ring = options.get("cotengra_plot_ring", False)
192193
path = get_simulation_path(
193-
qc, max_time=max_time, max_repeats=max_repeats, dump_path=dump_path, plot_ring=plot_ring
194+
circuit,
195+
max_time=max_time,
196+
max_repeats=max_repeats,
197+
dump_path=dump_path,
198+
plot_ring=plot_ring,
194199
)
195200
sim.set_simulation_path(path, False)
196201

src/mqt/ddsim/primitives/estimator.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
)
1717
from qiskit.quantum_info import Pauli, PauliList, SparsePauliOp
1818

19+
from mqt.core.io import load
1920
from mqt.ddsim.pyddsim import CircuitSimulator
2021
from mqt.ddsim.qasmsimulator import QasmSimulatorBackend
2122

@@ -235,15 +236,16 @@ def _run_experiment(
235236
approximation_strategy = options.get("approximation_strategy", "fidelity")
236237
seed = options.get("seed_simulator", -1)
237238

239+
qc = load(circ)
238240
sim = CircuitSimulator(
239-
circ,
241+
qc,
240242
approximation_step_fidelity=approximation_step_fidelity,
241243
approximation_steps=approximation_steps,
242244
approximation_strategy=approximation_strategy,
243245
seed=seed,
244246
)
245247

246-
return [sim.expectation_value(observable=obs) for obs in obs_circ_list]
248+
return [sim.expectation_value(observable=load(obs)) for obs in obs_circ_list]
247249

248250
@staticmethod
249251
def _postprocessing(result_list: list[float], accum: list[int], metadata: list[dict]) -> EstimatorResult:

src/mqt/ddsim/qasmsimulator.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
from qiskit.transpiler import Target
1616
from qiskit.utils.multiprocessing import local_hardware_info
1717

18+
from mqt.core.io import load
19+
1820
from . import __version__
1921
from .header import DDSIMHeader
2022
from .job import DDSIMJob
@@ -161,8 +163,9 @@ def _run_experiment(self, qc: QuantumCircuit, **options: dict[str, Any]) -> Expe
161163
seed = options.get("seed_simulator", -1)
162164
shots = options.get("shots", 1024)
163165

166+
circuit = load(qc)
164167
sim = CircuitSimulator(
165-
qc,
168+
circuit,
166169
approximation_step_fidelity=approximation_step_fidelity,
167170
approximation_steps=approximation_steps,
168171
approximation_strategy=approximation_strategy,

src/mqt/ddsim/unitarysimulator.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
from qiskit.result.models import ExperimentResult, ExperimentResultData
1313
from qiskit.transpiler import Target
1414

15+
from mqt.core.io import load
16+
1517
from .header import DDSIMHeader
1618
from .pyddsim import ConstructionMode, UnitarySimulator, get_matrix
1719
from .qasmsimulator import QasmSimulatorBackend
@@ -66,7 +68,8 @@ def _run_experiment(cls, qc: QuantumCircuit, **options: Any) -> ExperimentResult
6668
)
6769
raise QiskitError(msg)
6870

69-
sim = UnitarySimulator(qc, seed=seed, mode=construction_mode)
71+
circuit = load(qc)
72+
sim = UnitarySimulator(circuit, seed=seed, mode=construction_mode)
7073
sim.construct()
7174
# Extract resulting matrix from final DD and write data
7275
unitary: npt.NDArray[np.complex128] = np.zeros((2**qc.num_qubits, 2**qc.num_qubits), dtype=np.complex128)

0 commit comments

Comments
 (0)