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

🎨 Support for Qiskit v1.0.0 #210

Merged
merged 32 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
becb6c5
🎨 WIP for qiskit v1.0.0 support
nquetschlich Mar 15, 2024
9841855
🎨 pre-commit fixes
pre-commit-ci[bot] Mar 15, 2024
225ba47
📌 adjusted the BQSKit dependency temporarily
nquetschlich Mar 16, 2024
5fc5b21
Merge remote-tracking branch 'origin/qiskit_v1_update' into qiskit_v1…
nquetschlich Mar 16, 2024
9ec1d58
🚨
nquetschlich Mar 16, 2024
841a7a7
📌 adjusted MQT Bench Dependency
nquetschlich Mar 21, 2024
f6c1623
Merge branch 'main' into qiskit_v1_update
nquetschlich Apr 3, 2024
4368112
🎨 removed commented code
nquetschlich Apr 3, 2024
5fe46db
🐛 fixed bug when VF2Layout was not succesfull and no previous layout …
nquetschlich Apr 8, 2024
2153160
🎨 pre-commit fixes
pre-commit-ci[bot] Apr 8, 2024
5a5bb40
📌 added lark dependency since pytket is depending on the (deprecated)…
nquetschlich Apr 8, 2024
62aaf59
💄improved ruff config
nquetschlich Apr 9, 2024
6484a14
📌 added pytket dependency explicitly since the lower bound of MQT Ben…
nquetschlich Apr 11, 2024
ed3022d
📌 updated dependency
nquetschlich Apr 11, 2024
138ba76
✅ adusted test to be faster
nquetschlich Apr 11, 2024
57cb0ff
✅ adusted test to be faster
nquetschlich Apr 11, 2024
9b63416
✅ adusted test to be faster
nquetschlich Apr 11, 2024
b7596f6
✅ adusted RL compilation test
nquetschlich Apr 11, 2024
d6237d0
✅ adusted RL compilation test
nquetschlich Apr 11, 2024
ca03b9a
📌 adjusted BQSKit dependency
nquetschlich Apr 15, 2024
270d3c4
Update pyproject.toml
nquetschlich Apr 15, 2024
dff5322
Update pyproject.toml
nquetschlich Apr 15, 2024
3ebbe4b
Update pyproject.toml
nquetschlich Apr 15, 2024
ceff819
Update pyproject.toml
nquetschlich Apr 16, 2024
3baa013
👷 adjusted pre-commit config
nquetschlich Apr 16, 2024
11fd66b
👷 updated pre-commit config and implemented feedback
nquetschlich Apr 16, 2024
273d14e
🔥 removed fragment from copying from mqt bench
nquetschlich Apr 16, 2024
af90feb
🔥 removed old configuration files which are now part of MQT Bench
nquetschlich Apr 16, 2024
c6be168
🔧 added warning to be ignored
nquetschlich Apr 16, 2024
0ad9df1
🐛 fixed a bug triggered by warning
nquetschlich Apr 16, 2024
db46432
✅ added noxfile
nquetschlich Apr 16, 2024
bc73c69
🎨 pre-commit fixes
pre-commit-ci[bot] Apr 16, 2024
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 pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ description = "MQT Predictor - A MQT tool for Determining Good Quantum Circuit C
readme = "README.md"
authors = [
{ name = "Nils Quetschlich", email = "[email protected]" },
{ name = "Lukas Burgholzer", email = "lukas.burgholzer@jku.at"},
{ name = "Lukas Burgholzer", email = "lukas.burgholzer@tum.de"},
]
keywords = ["MQT", "quantum computing", "compilation", "machine learning", "prediction"]
license = { file = "LICENSE" }
Expand Down
7 changes: 5 additions & 2 deletions src/mqt/predictor/ml/Predictor.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import numpy as np
from joblib import Parallel, delayed, load
from qiskit import QuantumCircuit
from qiskit.qasm2 import dump
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV, train_test_split

Expand Down Expand Up @@ -96,7 +97,8 @@ def generate_compiled_circuits_for_single_training_circuit(
res = utils.timeout_watcher(rl.qcompile, [qc, figure_of_merit, dev.name], timeout)
if res:
compiled_qc = res[0]
compiled_qc.qasm(filename=Path(target_path) / (target_filename + ".qasm"))
with Path(target_path / (target_filename + ".qasm")).open("w") as f:
dump(compiled_qc, f)

except Exception as e:
print(e, filename, "inner")
Expand Down Expand Up @@ -153,7 +155,8 @@ def compile_all_circuits_devicewise(
res = utils.timeout_watcher(rl.qcompile, [qc, figure_of_merit, device_name, rl_pred], timeout)
if res:
compiled_qc = res[0]
compiled_qc.qasm(filename=Path(target_path) / (target_filename + ".qasm"))
with Path(target_path / (target_filename + ".qasm")).open("w") as f:
dump(compiled_qc, f)

except Exception as e:
print(e, filename, device_name)
Expand Down
32 changes: 18 additions & 14 deletions src/mqt/predictor/rl/PredictorEnv.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from pytket.circuit import Qubit
from pytket.extensions.qiskit import qiskit_to_tk, tk_to_qiskit
from qiskit import QuantumCircuit
from qiskit.passmanager.flow_controllers import DoWhileController
from qiskit.transpiler import CouplingMap, PassManager, TranspileLayout
from qiskit.transpiler.passes import CheckMap, GatesInBasis

Expand Down Expand Up @@ -215,11 +216,13 @@
if action["name"] == "QiskitO3":
pm = PassManager()
pm.append(
action["transpile_pass"](
self.device.basis_gates,
CouplingMap(self.device.coupling_map) if self.layout is not None else None,
DoWhileController(
action["transpile_pass"](
self.device.basis_gates,
CouplingMap(self.device.coupling_map) if self.layout is not None else None,
),
do_while=action["do_while"],
),
do_while=action["do_while"],
)
else:
pm = PassManager(transpile_pass)
Expand All @@ -239,16 +242,17 @@
+ self.actions_mapping_indices
+ self.actions_final_optimization_indices
):
if action["name"] == "VF2Layout":
if pm.property_set["layout"]:
altered_qc, pm = rl.helper.postprocess_VF2Layout(
altered_qc,
pm.property_set["layout"],
pm.property_set["original_qubit_indices"],
pm.property_set["final_layout"],
self.device,
)
elif action["name"] == "VF2PostLayout":
# if action["name"] == "VF2Layout":
# if pm.property_set["layout"]:
# altered_qc, pm = rl.helper.postprocess_VF2Layout(
# altered_qc,
# pm.property_set["layout"],
# pm.property_set["original_qubit_indices"],
# pm.property_set["final_layout"],
# self.device,
Fixed Show fixed Hide fixed
# )
# el
if action["name"] == "VF2PostLayout":
assert pm.property_set["VF2PostLayout_stop_reason"] is not None
post_layout = pm.property_set["post_layout"]
if post_layout:
Expand Down
92 changes: 49 additions & 43 deletions src/mqt/predictor/rl/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,14 @@
VF2Layout,
VF2PostLayout,
)
from qiskit.transpiler.passes.layout.vf2_layout import VF2LayoutStopReason
from sb3_contrib import MaskablePPO
from tqdm import tqdm

from mqt.bench.utils import calc_supermarq_features
from mqt.predictor import reward, rl

if TYPE_CHECKING:
from collections.abc import Callable

from numpy.typing import NDArray

from mqt.bench.devices import Device
Expand All @@ -75,9 +74,9 @@
from bqskit import compile as bqskit_compile
from bqskit.ir import gates
from qiskit import QuantumRegister
from qiskit.providers.fake_provider import FakeGuadalupe, FakeMontreal, FakeQuito, FakeWashington
from qiskit.passmanager import ConditionalController
from qiskit.transpiler.preset_passmanagers import common
from qiskit.transpiler.runningpassmanager import ConditionalController
from qiskit_ibm_runtime.fake_provider import FakeGuadalupe, FakeMontreal, FakeQuito, FakeWashington

logger = logging.getLogger("mqt-predictor")

Expand Down Expand Up @@ -187,13 +186,9 @@ def get_actions_opt() -> list[dict[str, Any]]:
CommutativeCancellation(basis_gates=native_gate),
GatesInBasis(native_gate),
ConditionalController(
[
pass_
for x in common.generate_translation_passmanager(
target=None, basis_gates=native_gate, coupling_map=coupling_map
).passes()
for pass_ in x["passes"]
],
common.generate_translation_passmanager(
target=None, basis_gates=native_gate, coupling_map=coupling_map
).to_flow_controller(),
condition=lambda property_set: not property_set["all_gates_in_basis"],
),
Depth(recurse=True),
Expand Down Expand Up @@ -257,6 +252,15 @@ def get_actions_layout() -> list[dict[str, Any]]:
coupling_map=CouplingMap(device.coupling_map),
properties=get_ibm_backend_properties_by_device_name(device.name),
),
ConditionalController(
[
FullAncillaAllocation(coupling_map=CouplingMap(device.coupling_map)),
EnlargeWithAncilla(),
ApplyLayout(),
],
condition=lambda property_set: property_set["VF2Layout_stop_reason"]
== VF2LayoutStopReason.SOLUTION_FOUND,
),
],
"origin": "qiskit",
},
Expand Down Expand Up @@ -684,42 +688,44 @@ def get_ibm_backend_properties_by_device_name(device_name: str) -> Any:
return None


def get_layout_postprocessing_qiskit_pass() -> (
Callable[[Device], list[FullAncillaAllocation | EnlargeWithAncilla | ApplyLayout]]
):
return lambda device: [
FullAncillaAllocation(coupling_map=CouplingMap(device.coupling_map)),
EnlargeWithAncilla(),
ApplyLayout(),
]


def postprocess_VF2Layout(
qc: QuantumCircuit,
initial_layout: Layout,
original_qubit_indices: dict[QuantumRegister, int],
final_layout: Layout,
device: Device,
) -> tuple[QuantumCircuit, PassManager]:
"""Postprocesses the given quantum circuit with the given layout and returns the altered quantum circuit and the respective PassManager."""
postprocessing_action = rl.helper.get_layout_postprocessing_qiskit_pass()(device)
pm = PassManager(postprocessing_action)
pm.property_set["layout"] = initial_layout
pm.property_set["original_qubit_indices"] = original_qubit_indices
pm.property_set["final_layout"] = final_layout
altered_qc = pm.run(qc)
return altered_qc, pm
# def get_layout_postprocessing_qiskit_pass() -> (
# Callable[[Device], list[FullAncillaAllocation | EnlargeWithAncilla | ApplyLayout]]
# ):
# return lambda device: [
# FullAncillaAllocation(coupling_map=CouplingMap(device.coupling_map)),
# EnlargeWithAncilla(),
# ApplyLayout(),
# ]


# def postprocess_VF2Layout(
# qc: QuantumCircuit,
# initial_layout: Layout,
# original_qubit_indices: dict[QuantumRegister, int],
# final_layout: Layout,
# device: Device,
# ) -> tuple[QuantumCircuit, PassManager]:
# """Postprocesses the given quantum circuit with the given layout and returns the altered quantum circuit and the respective PassManager."""
# postprocessing_action = rl.helper.get_layout_postprocessing_qiskit_pass()(device)
# pm = PassManager(postprocessing_action)
# pm.property_set["layout"] = initial_layout
# pm.property_set["original_qubit_indices"] = original_qubit_indices
# pm.property_set["final_layout"] = final_layout
# assert pm.property_set["layout"] is not None
# altered_qc = pm.run(qc)
# return altered_qc, pm


def postprocess_VF2PostLayout(
qc: QuantumCircuit, post_layout: Layout, layout_before: TranspileLayout
) -> tuple[QuantumCircuit, PassManager]:
"""Postprocesses the given quantum circuit with the post_layout and returns the altered quantum circuit and the respective PassManager."""
pm = PassManager(ApplyLayout())
apply_layout = ApplyLayout()
assert layout_before is not None
pm.property_set["layout"] = layout_before.initial_layout
pm.property_set["original_qubit_indices"] = layout_before.input_qubit_mapping
pm.property_set["final_layout"] = layout_before.final_layout
pm.property_set["post_layout"] = post_layout
altered_qc = pm.run(qc)
return altered_qc, pm
apply_layout.property_set["layout"] = layout_before.initial_layout
apply_layout.property_set["original_qubit_indices"] = layout_before.input_qubit_mapping
apply_layout.property_set["final_layout"] = layout_before.final_layout
apply_layout.property_set["post_layout"] = post_layout

altered_qc = apply_layout(qc)
return altered_qc, apply_layout
18 changes: 3 additions & 15 deletions tests/compilation/test_helper_rl.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,7 @@ def test_VF2_layout_and_postlayout() -> None:
layout_pass = layout_action["transpile_pass"](dev)
break
pm = PassManager(layout_pass)
altered_qc = pm.run(qc)
assert len(altered_qc.layout.initial_layout) == 3
assert pm.property_set["VF2Layout_stop_reason"] is not None
layouted_qc, pm = rl.helper.postprocess_VF2Layout(
altered_qc,
pm.property_set["layout"],
pm.property_set["original_qubit_indices"],
pm.property_set["final_layout"],
dev,
)
layouted_qc = pm.run(qc)
assert len(layouted_qc.layout.initial_layout) == dev.num_qubits

dev_success = get_device_by_name("ibm_montreal")
Expand All @@ -70,13 +61,10 @@ def test_VF2_layout_and_postlayout() -> None:
pm = PassManager(post_layout_pass)
altered_qc = pm.run(qc_transpiled)

assert pm.property_set["VF2PostLayout_stop_reason"] is not None
assert pm.property_set["VF2PostLayout_stop_reason"] == VF2PostLayoutStopReason.SOLUTION_FOUND

assert len(layouted_qc.layout.initial_layout) == dev.num_qubits
layouted_qc, pm = rl.helper.postprocess_VF2PostLayout(
postprocessed_VF2PostLayout_qc, _ = rl.helper.postprocess_VF2PostLayout(
altered_qc, pm.property_set["post_layout"], qc_transpiled.layout
)
initial_layout_after = layouted_qc.layout.initial_layout

assert initial_layout_before != initial_layout_after
assert initial_layout_before != postprocessed_VF2PostLayout_qc.layout.initial_layout
4 changes: 2 additions & 2 deletions tests/compilation/test_integration_further_SDKs.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from qiskit import QuantumCircuit
from qiskit.transpiler import CouplingMap, PassManager
from qiskit.transpiler.passes import CheckMap, GatesInBasis
from qiskit.transpiler.runningpassmanager import TranspileLayout
from qiskit.transpiler.passmanager import TranspileLayout

from mqt.bench.devices import Device, get_available_devices, get_device_by_name
from mqt.predictor.rl import helper
Expand Down Expand Up @@ -106,7 +106,7 @@ def check_mapped_circuit(

# each qubit of the initial layout is part of the initial quantum circuit and the register name is correctly set
for assigned_physical_qubit in layout.initial_layout._p2v.values(): # noqa: SLF001
qreg = assigned_physical_qubit.register
qreg = assigned_physical_qubit._register
assert qreg.name in {"q", "ancilla"}

# assigned_physical_qubit is part of the original quantum circuit
Expand Down
4 changes: 3 additions & 1 deletion tests/compilation/test_predictor_rl.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import pytest
from qiskit import QuantumCircuit
from qiskit.qasm2 import dump

from mqt.bench import get_benchmark
from mqt.predictor import reward, rl
Expand All @@ -13,7 +14,8 @@ def test_predictor_env_reset_from_string() -> None:
predictor = rl.Predictor(figure_of_merit="expected_fidelity", device_name="ionq_harmony")
qasm_path = Path("test.qasm")
qc = get_benchmark("dj", 1, 3)
qc.qasm(filename=str(qasm_path))
with Path(qasm_path).open("w") as f:
dump(qc, f)
assert predictor.env.reset(qc=qasm_path)[0] == rl.helper.create_feature_dict(qc)


Expand Down
9 changes: 6 additions & 3 deletions tests/device_selection/test_predictor_ml.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import numpy as np
import pytest
from qiskit.qasm2 import dump, dumps

from mqt.bench import benchmark_generator
from mqt.bench.devices import get_available_device_names, get_available_devices
Expand Down Expand Up @@ -35,15 +36,16 @@ def test_predict() -> None:
filename = "test_qasm.qasm"
figure_of_merit: reward.figure_of_merit = "expected_fidelity"
qc = benchmark_generator.get_benchmark("dj", 1, 8)
qc.qasm(filename=filename)
with Path(filename).open("w") as f:
dump(qc, f)
predictor = ml.Predictor()
predictions = predictor.predict_probs(filename, figure_of_merit=figure_of_merit)
assert predictor.clf is not None
classes = predictor.clf.classes_ # type: ignore[unreachable]
predicted_device_indices = classes[np.argsort(predictions)[::-1]]
devices = get_available_devices()
assert all(0 <= i < len(devices) for i in predicted_device_indices)
predictions = predictor.predict_probs(qc.qasm(), figure_of_merit=figure_of_merit)
predictions = predictor.predict_probs(dumps(qc), figure_of_merit=figure_of_merit)
predicted_device_indices = classes[np.argsort(predictions)[::-1]]
assert all(0 <= i < len(devices) for i in predicted_device_indices)
Path(filename).unlink()
Expand Down Expand Up @@ -96,7 +98,8 @@ def test_compile_all_circuits_for_dev_and_fom() -> None:

qc = benchmark_generator.get_benchmark("dj", 1, 3)
qasm_path = Path("test.qasm")
qc.qasm(filename=str(qasm_path))
with Path(qasm_path).open("w") as f:
dump(qc, f)
predictor.compile_all_circuits_devicewise(
device_name="ionq_harmony",
timeout=100,
Expand Down
4 changes: 3 additions & 1 deletion tests/test_pretrained_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import pytest
from qiskit import QuantumCircuit
from qiskit.qasm2 import dump

from mqt.bench import get_benchmark
from mqt.bench.devices import get_available_device_names
Expand Down Expand Up @@ -40,7 +41,8 @@ def test_qcompile() -> None:
def test_evaluate_sample_circuit() -> None:
qc = get_benchmark("ghz", 1, 3)
filename = "test_3.qasm"
qc.qasm(filename=filename)
with Path(filename).open("w") as f:
dump(qc, f)
res = evaluate_sample_circuit(filename)
expected_keys = []
for compilation_setup in ["qiskit", "tket", "mqt-predictor_expected_fidelity", "mqt-predictor_critical_depth"]:
Expand Down
Loading