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 <[email protected]>
  • Loading branch information
burgholzer committed Jan 29, 2024
1 parent 533574f commit 2643b7b
Show file tree
Hide file tree
Showing 11 changed files with 50 additions and 102 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 @@ -10,6 +10,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 @@ -25,12 +38,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
1 change: 1 addition & 0 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
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.45.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
2 changes: 0 additions & 2 deletions src/mqt/qmap/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
LookaheadHeuristic,
MappingResults,
Method,
QuantumComputation,
SwapReduction,
SynthesisConfiguration,
SynthesisResults,
Expand Down Expand Up @@ -64,7 +63,6 @@
"SynthesisConfiguration",
"SynthesisResults",
"TargetMetric",
"QuantumComputation",
"Tableau",
"CliffordSynthesizer",
"synthesize_clifford",
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 @@ -185,7 +193,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
59 changes: 5 additions & 54 deletions src/python/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,52 +3,32 @@
// See README.md or go to https://github.com/cda-tum/qmap for more information.
//

#include "QuantumComputation.hpp"
#include "cliffordsynthesis/CliffordSynthesizer.hpp"
#include "exact/ExactMapper.hpp"
#include "heuristic/HeuristicMapper.hpp"
#include "nlohmann/json.hpp"
#include "pybind11/pybind11.h"
#include "pybind11/stl.h"
#include "pybind11_json/pybind11_json.hpp"
#include "python/qiskit/QuantumCircuit.hpp"

namespace py = pybind11;
using namespace pybind11::literals;

void loadQC(qc::QuantumComputation& qc, const py::object& circ) {
try {
if (py::isinstance<py::str>(circ)) {
auto&& file = circ.cast<std::string>();
qc.import(file);
} else {
qc::qiskit::QuantumCircuit::import(qc, circ);
}
} catch (std::exception const& e) {
std::stringstream ss{};
ss << "Could not import circuit: " << e.what();
throw std::invalid_argument(ss.str());
}
}

// c++ binding function
MappingResults map(const py::object& circ, Architecture& arch,
MappingResults map(const qc::QuantumComputation& circ, Architecture& arch,
Configuration& config) {
qc::QuantumComputation qc{};

loadQC(qc, circ);

if (config.useTeleportation) {
config.teleportationQubits =
std::min((arch.getNqubits() - qc.getNqubits()) & ~1U,
std::min((arch.getNqubits() - circ.getNqubits()) & ~1U,
static_cast<std::size_t>(8));
}

std::unique_ptr<Mapper> mapper;
try {
if (config.method == Method::Heuristic) {
mapper = std::make_unique<HeuristicMapper>(qc, arch);
mapper = std::make_unique<HeuristicMapper>(circ, arch);
} else if (config.method == Method::Exact) {
mapper = std::make_unique<ExactMapper>(qc, arch);
mapper = std::make_unique<ExactMapper>(circ, arch);
}
} catch (std::exception const& e) {
std::stringstream ss{};
Expand Down Expand Up @@ -657,35 +637,6 @@ PYBIND11_MODULE(pyqmap, m) {
"Constructs a tableau from two lists of Pauli strings, the Stabilizers"
"and Destabilizers.");

auto quantumComputation = py::class_<qc::QuantumComputation>(
m, "QuantumComputation",
"A class for the intermediate representation of quantum circuits in the "
"Munich Quantum Toolkit.");
quantumComputation.def_static(
"from_file",
[](const std::string& filename) {
return qc::QuantumComputation(filename);
},
"filename"_a, "Reads a quantum circuit from a file.");
quantumComputation.def_static(
"from_qasm_str",
[](const std::string& qasm) {
std::stringstream ss(qasm);
qc::QuantumComputation qc{};
qc.import(ss, qc::Format::OpenQASM3);
return qc;
},
"qasm"_a, "Reads a quantum circuit from a qasm string.");
quantumComputation.def_static(
"from_qiskit",
[](const py::object& circuit) {
qc::QuantumComputation qc{};
qc::qiskit::QuantumCircuit::import(qc, circuit);
return qc;
},
"circuit"_a,
"Reads a quantum circuit from a Qiskit :class:`QuantumCircuit`.");

auto synthesizer = py::class_<cs::CliffordSynthesizer>(
m, "CliffordSynthesizer", "A class for synthesizing Clifford circuits.");

Expand Down
1 change: 1 addition & 0 deletions test/python/constraints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ typing_extensions==4.0.0
qiskit==0.45.0
rustworkx==0.13.0
mqt.qcec==2.0.0
mqt.core==2.2.2
distinctipy==1.2.2
plotly==5.15.0
networkx==2.5
Expand Down
7 changes: 0 additions & 7 deletions test/python/test_cliffordsynthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,13 +227,6 @@ def bell_circuit() -> QuantumCircuit:
return circ


def test_optimize_quantum_computation(bell_circuit: QuantumCircuit) -> None:
"""Test that we can optimize an MQT QuantumComputation."""
qc = qmap.QuantumComputation.from_qiskit(bell_circuit)
circ, _ = qmap.optimize_clifford(circuit=qc)
assert qcec.verify(circ, bell_circuit).considered_equivalent()


def test_optimize_from_qasm_file(bell_circuit: QuantumCircuit) -> None:
"""Test that we can optimize from a QASM file."""
with Path("bell.qasm").open("w") as f:
Expand Down

0 comments on commit 2643b7b

Please sign in to comment.