Skip to content

Commit 825773f

Browse files
Merge branch 'main' into bugfix
2 parents 934e46c + 26e3b01 commit 825773f

File tree

10 files changed

+331
-318
lines changed

10 files changed

+331
-318
lines changed

.github/workflows/cd.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ on:
1010
jobs:
1111
build-sdist:
1212
name: 🐍 Packaging
13-
uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-sdist.yml@d6314c45667c131055a0389afc110e8dedc6da3f # v1.17.11
13+
uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-sdist.yml@c54be38945ae206affae9925ecd9c346f54c71b7 # v1.17.12
1414

1515
build-wheel:
1616
name: 🐍 Packaging
17-
uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-wheel-build.yml@d6314c45667c131055a0389afc110e8dedc6da3f # v1.17.11
17+
uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-wheel-build.yml@c54be38945ae206affae9925ecd9c346f54c71b7 # v1.17.12
1818

1919
deploy:
2020
if: github.event_name == 'release' && github.event.action == 'published'

.github/workflows/ci.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ concurrency:
1414
jobs:
1515
change-detection:
1616
name: 🔍 Change
17-
uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-change-detection.yml@d6314c45667c131055a0389afc110e8dedc6da3f # v1.17.11
17+
uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-change-detection.yml@c54be38945ae206affae9925ecd9c346f54c71b7 # v1.17.12
1818

1919
python-tests:
2020
name: 🐍 Test
@@ -24,15 +24,15 @@ jobs:
2424
fail-fast: false
2525
matrix:
2626
runs-on: [ubuntu-24.04, macos-15, windows-2025]
27-
uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-tests.yml@d6314c45667c131055a0389afc110e8dedc6da3f # v1.17.11
27+
uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-tests.yml@c54be38945ae206affae9925ecd9c346f54c71b7 # v1.17.12
2828
with:
2929
runs-on: ${{ matrix.runs-on }}
3030

3131
python-coverage:
3232
name: 🐍 Coverage
3333
needs: [change-detection, python-tests]
3434
if: fromJSON(needs.change-detection.outputs.run-python-tests)
35-
uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-coverage.yml@d6314c45667c131055a0389afc110e8dedc6da3f # v1.17.11
35+
uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-coverage.yml@c54be38945ae206affae9925ecd9c346f54c71b7 # v1.17.12
3636
permissions:
3737
contents: read
3838
id-token: write
@@ -41,7 +41,7 @@ jobs:
4141
name: 🐍 Lint
4242
needs: change-detection
4343
if: fromJSON(needs.change-detection.outputs.run-python-tests)
44-
uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-linter.yml@d6314c45667c131055a0389afc110e8dedc6da3f # v1.17.11
44+
uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-linter.yml@c54be38945ae206affae9925ecd9c346f54c71b7 # v1.17.12
4545
with:
4646
enable-ty: true
4747
enable-mypy: false
@@ -50,13 +50,13 @@ jobs:
5050
name: 🚀 CD
5151
needs: change-detection
5252
if: fromJSON(needs.change-detection.outputs.run-cd)
53-
uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-sdist.yml@d6314c45667c131055a0389afc110e8dedc6da3f # v1.17.11
53+
uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-sdist.yml@c54be38945ae206affae9925ecd9c346f54c71b7 # v1.17.12
5454

5555
build-wheel:
5656
name: 🚀 CD
5757
needs: change-detection
5858
if: fromJSON(needs.change-detection.outputs.run-cd)
59-
uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-wheel-build.yml@d6314c45667c131055a0389afc110e8dedc6da3f # v1.17.11
59+
uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-wheel-build.yml@c54be38945ae206affae9925ecd9c346f54c71b7 # v1.17.12
6060

6161
# this job does nothing and is only used for branch protection
6262
required-checks-pass:

.pre-commit-config.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ repos:
3030

3131
## Check the pyproject.toml file
3232
- repo: https://github.com/henryiii/validate-pyproject-schema-store
33-
rev: 2026.02.15
33+
rev: 2026.02.22
3434
hooks:
3535
- id: validate-pyproject
3636
priority: 0
@@ -56,7 +56,7 @@ repos:
5656

5757
## Check for spelling
5858
- repo: https://github.com/adhtruong/mirrors-typos
59-
rev: v1.43.4
59+
rev: v1.43.5
6060
hooks:
6161
- id: typos
6262
priority: 0
@@ -78,7 +78,7 @@ repos:
7878

7979
## Ensure uv lock file is up-to-date
8080
- repo: https://github.com/astral-sh/uv-pre-commit
81-
rev: 0.10.3
81+
rev: 0.10.4
8282
hooks:
8383
- id: uv-lock
8484
priority: 0
@@ -114,7 +114,7 @@ repos:
114114

115115
## Python linting using ruff
116116
- repo: https://github.com/astral-sh/ruff-pre-commit
117-
rev: v0.15.1
117+
rev: v0.15.2
118118
hooks:
119119
- id: ruff-format
120120
priority: 1

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ docs = [
8686
dev = [
8787
{include-group = "test"},
8888
"nox>=2025.11.12",
89-
"ty==0.0.17",
89+
"ty==0.0.18",
9090
]
9191

9292
[project.urls]

src/mqt/predictor/rl/helper.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,9 @@ def get_state_sample(max_qubits: int, path_training_circuits: Path, rng: Generat
6363

6464
try:
6565
qc = QuantumCircuit.from_qasm_file(file_list[random_index]) # ty: ignore[invalid-argument-type]
66-
except Exception:
67-
raise RuntimeError("Could not read QuantumCircuit from: " + str(file_list[random_index])) from None
66+
except Exception as e:
67+
msg = f"Could not read QuantumCircuit from: {file_list[random_index]}"
68+
raise RuntimeError(msg) from e
6869

6970
return qc, str(file_list[random_index])
7071

src/mqt/predictor/rl/parsing.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,8 @@ def final_layout_pytket_to_qiskit(pytket_circuit: Circuit, qiskit_circuit: Quant
185185

186186

187187
def final_layout_bqskit_to_qiskit(
188-
bqskit_initial_layout: list[int],
189-
bqskit_final_layout: list[int],
188+
bqskit_initial_layout: tuple[int, ...],
189+
bqskit_final_layout: tuple[int, ...],
190190
compiled_qc: QuantumCircuit,
191191
initial_qc: QuantumCircuit,
192192
) -> TranspileLayout:

src/mqt/predictor/rl/predictorenv.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -320,17 +320,19 @@ def apply_action(self, action_index: int) -> QuantumCircuit | None:
320320

321321
def _apply_qiskit_action(self, action: Action, action_index: int) -> QuantumCircuit:
322322
if action.name == "QiskitO3" and isinstance(action, DeviceDependentAction):
323-
assert callable(action.transpile_pass)
324-
passes_ = action.transpile_pass(
323+
factory = cast("Callable[[list[str], CouplingMap | None], list[Task]]", action.transpile_pass)
324+
passes = factory(
325325
self.device.operation_names,
326326
CouplingMap(self.device.build_coupling_map()) if self.layout else None,
327327
)
328-
passes = cast("list[Task]", passes_)
329328
assert action.do_while is not None
330329
pm = PassManager([DoWhileController(passes, do_while=action.do_while)])
331330
else:
332-
passes_ = action.transpile_pass(self.device) if callable(action.transpile_pass) else action.transpile_pass
333-
passes = cast("list[Task]", passes_)
331+
if callable(action.transpile_pass):
332+
factory = cast("Callable[[Target], list[Task]]", action.transpile_pass)
333+
passes = factory(self.device)
334+
else:
335+
passes = cast("list[Task]", action.transpile_pass)
334336
pm = PassManager(passes)
335337

336338
altered_qc = pm.run(self.state)
@@ -376,8 +378,11 @@ def _handle_qiskit_layout_postprocessing(
376378

377379
def _apply_tket_action(self, action: Action, action_index: int) -> QuantumCircuit:
378380
tket_qc = qiskit_to_tk(self.state, preserve_param_uuid=True)
379-
passes = action.transpile_pass(self.device) if callable(action.transpile_pass) else action.transpile_pass
380-
assert isinstance(passes, list)
381+
if callable(action.transpile_pass):
382+
factory = cast("Callable[[Target], list[Task]]", action.transpile_pass)
383+
passes = factory(self.device)
384+
else:
385+
passes = cast("list[Task]", action.transpile_pass)
381386
for pass_ in passes:
382387
assert isinstance(pass_, TketBasePass | PreProcessTKETRoutingAfterQiskitLayout)
383388
pass_.apply(tket_qc)
@@ -406,7 +411,6 @@ def _apply_bqskit_action(self, action: Action, action_index: int) -> QuantumCirc
406411
ValueError: If the action index is not in the action set or if the action origin is not supported.
407412
"""
408413
bqskit_qc = qiskit_to_bqskit(self.state)
409-
assert callable(action.transpile_pass)
410414
if action_index in self.actions_opt_indices:
411415
transpile = cast("Callable[[Circuit], Circuit]", action.transpile_pass)
412416
bqskit_compiled_qc = transpile(bqskit_qc)
@@ -415,7 +419,7 @@ def _apply_bqskit_action(self, action: Action, action_index: int) -> QuantumCirc
415419
bqskit_compiled_qc = factory(self.device)(bqskit_qc)
416420
elif action_index in self.actions_mapping_indices:
417421
factory = cast(
418-
"Callable[[Target], Callable[[Circuit], tuple[Circuit, list[int], list[int]]]]",
422+
"Callable[[Target], Callable[[Circuit], tuple[Circuit, tuple[int, ...], tuple[int, ...]]]]",
419423
action.transpile_pass,
420424
)
421425
bqskit_compiled_qc, initial, final = factory(self.device)(bqskit_qc)

tests/compilation/test_helper_rl.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@
2525
from mqt.predictor.rl.parsing import postprocess_vf2postlayout
2626

2727
if TYPE_CHECKING:
28+
from collections.abc import Callable
29+
2830
from qiskit.passmanager.base_tasks import Task
31+
from qiskit.transpiler import Target
2932

3033

3134
def test_create_feature_dict() -> None:
@@ -58,8 +61,8 @@ def test_vf2_layout_and_postlayout() -> None:
5861
passes: list[Task] | None = None
5962
for layout_action in get_actions_by_pass_type()[PassType.LAYOUT]:
6063
if layout_action.name == "VF2Layout":
61-
assert callable(layout_action.transpile_pass)
62-
passes = cast("list[Task]", layout_action.transpile_pass(dev))
64+
factory = cast("Callable[[Target], list[Task]]", layout_action.transpile_pass)
65+
passes = factory(dev)
6366
break
6467
assert passes is not None
6568
pm = PassManager(passes)
@@ -76,8 +79,8 @@ def test_vf2_layout_and_postlayout() -> None:
7679
post_layout_passes: list[Task] | None = None
7780
for layout_action in get_actions_by_pass_type()[PassType.FINAL_OPT]:
7881
if layout_action.name == "VF2PostLayout":
79-
assert callable(layout_action.transpile_pass)
80-
post_layout_passes = cast("list[Task]", layout_action.transpile_pass(dev_success))
82+
factory = cast("Callable[[Target], list[Task]]", layout_action.transpile_pass)
83+
post_layout_passes = factory(dev_success)
8184
break
8285
assert post_layout_passes is not None
8386

tests/compilation/test_integration_further_SDKs.py

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
from bqskit.ext import bqskit_to_qiskit, qiskit_to_bqskit
1818
from bqskit.ir.circuit import Circuit
1919
from mqt.bench.targets import get_available_device_names, get_device
20-
from pytket._tket.passes import BasePass as TketBasePass # noqa: PLC2701
2120
from pytket.circuit import Qubit
2221
from pytket.extensions.qiskit import qiskit_to_tk, tk_to_qiskit
2322
from qiskit import QuantumCircuit
@@ -27,16 +26,21 @@
2726

2827
from mqt.predictor.rl.actions import CompilationOrigin, PassType, get_actions_by_pass_type
2928
from mqt.predictor.rl.parsing import (
30-
PreProcessTKETRoutingAfterQiskitLayout,
3129
final_layout_bqskit_to_qiskit,
3230
final_layout_pytket_to_qiskit,
3331
)
3432

3533
if TYPE_CHECKING:
34+
from collections.abc import Callable
35+
36+
from pytket._tket.passes import BasePass as TketBasePass
3637
from qiskit.passmanager.base_tasks import Task
3738
from qiskit.transpiler import Target
3839

3940
from mqt.predictor.rl.actions import Action
41+
from mqt.predictor.rl.parsing import (
42+
PreProcessTKETRoutingAfterQiskitLayout,
43+
)
4044

4145

4246
@pytest.fixture
@@ -58,8 +62,8 @@ def test_bqskit_o2_action(available_actions_dict: dict[PassType, list[Action]])
5862
qc.cx(0, 1)
5963

6064
bqskit_qc = qiskit_to_bqskit(qc)
61-
assert callable(action_bqskit_o2.transpile_pass)
62-
bqskit_qc_optimized = action_bqskit_o2.transpile_pass(bqskit_qc)
65+
factory = cast("Callable[[Circuit], Circuit]", action_bqskit_o2.transpile_pass)
66+
bqskit_qc_optimized = factory(bqskit_qc)
6367
assert isinstance(bqskit_qc_optimized, Circuit)
6468
optimized_qc = bqskit_to_qiskit(bqskit_qc_optimized)
6569

@@ -83,9 +87,8 @@ def test_bqskit_synthesis_action(device: Target, available_actions_dict: dict[Pa
8387
check_nat_gates(qc)
8488
assert not check_nat_gates.property_set["all_gates_in_basis"]
8589

86-
assert callable(action_bqskit_synthesis_action.transpile_pass)
87-
lambda_ = action_bqskit_synthesis_action.transpile_pass(device)
88-
assert callable(lambda_)
90+
factory = cast("Callable[[Target], Callable[[Circuit], Circuit]]", action_bqskit_synthesis_action.transpile_pass)
91+
lambda_ = factory(device)
8992
bqskit_qc = qiskit_to_bqskit(qc)
9093
if "rigetti" in device.description or "ionq" in device.description or "iqm" in device.description:
9194
with pytest.raises(ValueError, match=re.escape("not supported in BQSKIT")):
@@ -122,10 +125,11 @@ def test_bqskit_mapping_action_swaps_necessary(available_actions_dict: dict[Pass
122125

123126
device = get_device("ibm_falcon_27")
124127
bqskit_qc = qiskit_to_bqskit(qc)
125-
assert callable(bqskit_mapping_action.transpile_pass)
126-
lambda_ = bqskit_mapping_action.transpile_pass(device)
127-
assert callable(lambda_)
128-
bqskit_qc_mapped, input_mapping, output_mapping = lambda_(bqskit_qc)
128+
factory = cast(
129+
"Callable[[Target], Callable[[Circuit], tuple[Circuit, tuple[int, ...], tuple[int, ...]]]]",
130+
bqskit_mapping_action.transpile_pass,
131+
)
132+
bqskit_qc_mapped, input_mapping, output_mapping = factory(device)(bqskit_qc)
129133
mapped_qc = bqskit_to_qiskit(bqskit_qc_mapped)
130134
layout = final_layout_bqskit_to_qiskit(input_mapping, output_mapping, mapped_qc, qc)
131135

@@ -186,10 +190,11 @@ def test_bqskit_mapping_action_no_swaps_necessary(available_actions_dict: dict[P
186190
device = get_device("quantinuum_h2_56")
187191

188192
bqskit_qc = qiskit_to_bqskit(qc_no_swap_needed)
189-
assert callable(bqskit_mapping_action.transpile_pass)
190-
lambda_ = bqskit_mapping_action.transpile_pass(device)
191-
assert callable(lambda_)
192-
bqskit_qc_mapped, input_mapping, output_mapping = lambda_(bqskit_qc)
193+
factory = cast(
194+
"Callable[[Target], Callable[[Circuit], tuple[Circuit, tuple[int, ...], tuple[int, ...]]]]",
195+
bqskit_mapping_action.transpile_pass,
196+
)
197+
bqskit_qc_mapped, input_mapping, output_mapping = factory(device)(bqskit_qc)
193198
mapped_qc = bqskit_to_qiskit(bqskit_qc_mapped)
194199
layout = final_layout_bqskit_to_qiskit(input_mapping, output_mapping, mapped_qc, qc_no_swap_needed)
195200
assert layout is not None
@@ -211,8 +216,8 @@ def test_tket_routing(available_actions_dict: dict[PassType, list[Action]]) -> N
211216
device = get_device("quantinuum_h2_56")
212217

213218
layout_action = available_actions_dict[PassType.LAYOUT][0]
214-
assert callable(layout_action.transpile_pass)
215-
passes_ = cast("list[Task]", layout_action.transpile_pass(device))
219+
factory = cast("Callable[[Target], list[Task]]", layout_action.transpile_pass)
220+
passes_ = factory(device)
216221
pm = PassManager(passes_)
217222
layouted_qc = pm.run(qc)
218223
initial_layout = pm.property_set["layout"]
@@ -225,11 +230,11 @@ def test_tket_routing(available_actions_dict: dict[PassType, list[Action]]) -> N
225230
assert routing_action is not None
226231

227232
tket_qc = qiskit_to_tk(layouted_qc, preserve_param_uuid=True)
228-
assert callable(routing_action.transpile_pass)
229-
passes = routing_action.transpile_pass(device)
230-
assert isinstance(passes, list)
233+
factory = cast(
234+
"Callable[[Target], list[TketBasePass | PreProcessTKETRoutingAfterQiskitLayout]]", routing_action.transpile_pass
235+
)
236+
passes = factory(device)
231237
for pass_ in passes:
232-
assert isinstance(pass_, TketBasePass | PreProcessTKETRoutingAfterQiskitLayout)
233238
pass_.apply(tket_qc)
234239

235240
qbs = tket_qc.qubits

0 commit comments

Comments
 (0)