Skip to content
Merged
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: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This project adheres to [Semantic Versioning], with the exception that minor rel

### Changed

- 🐛 Support BQSKit conversion of IQM's native `r` gate ([#679]) ([**@flowerthrower**])
- 🔧 Replace `mypy` with `ty` ([#572]) ([**@denialhaag**])
- 🐛 Fix instruction duration unit in estimated success probability calculation ([#445]) ([**@Shaobo-Zhou**])
- ✨ Remove support for custom names of trained models ([#489]) ([**@bachase**])
Expand Down Expand Up @@ -46,6 +47,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool

<!-- PR links -->

[#679]: https://github.com/munich-quantum-toolkit/predictor/pull/679
[#572]: https://github.com/munich-quantum-toolkit/predictor/pull/572
[#489]: https://github.com/munich-quantum-toolkit/predictor/pull/489
[#445]: https://github.com/munich-quantum-toolkit/predictor/pull/445
Expand Down
49 changes: 44 additions & 5 deletions src/mqt/predictor/rl/parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,30 @@
from __future__ import annotations

import operator
import re
from functools import cache
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, cast

from bqskit.ir import gates
from bqskit.ir.lang.qasm2 import OPENQASM2Language
from pytket import Qubit
from pytket.circuit import Node
from pytket.placement import place_with_map
from qiskit.circuit import QuantumRegister
from qiskit import qasm2
from qiskit.circuit import QuantumCircuit, QuantumRegister
from qiskit.circuit.library import RGate
from qiskit.converters import circuit_to_dag, dag_to_circuit
from qiskit.transpiler import Layout, TranspileLayout
from qiskit.transpiler.passes import ApplyLayout

if TYPE_CHECKING:
from collections.abc import Callable

from bqskit.ir import Circuit as BqskitCircuit
from bqskit.ir import Gate
from pytket import Circuit
from pytket import Circuit as TketCircuit
from qiskit import QuantumCircuit
from qiskit.circuit import Instruction
from qiskit.transpiler import Target


Expand Down Expand Up @@ -87,7 +95,7 @@ class PreProcessTKETRoutingAfterQiskitLayout:

"""

def apply(self, circuit: Circuit) -> None:
def apply(self, circuit: TketCircuit) -> None:
"""Applies the pre-processing step to route a circuit with tket after a Qiskit Layout pass has been applied."""
mapping = {Qubit(i): Node(i) for i in range(circuit.n_qubits)}
place_with_map(circuit=circuit, qmap=mapping)
Expand Down Expand Up @@ -121,6 +129,7 @@ def get_bqskit_native_gates(device: Target) -> list[Gate]:
"rx": gates.RXGate(),
"ry": gates.RYGate(),
"rz": gates.RZGate(),
"r": gates.U1qGate(),
"u1": gates.U1Gate(),
"u2": gates.U2Gate(),
"u3": gates.U3Gate(),
Expand Down Expand Up @@ -178,7 +187,37 @@ def get_bqskit_native_gates(device: Target) -> list[Gate]:
return native_gates


def final_layout_pytket_to_qiskit(pytket_circuit: Circuit, qiskit_circuit: QuantumCircuit) -> Layout:
def bqskit_to_qiskit(circuit: BqskitCircuit) -> QuantumCircuit:
"""Convert a BQSKit Circuit to Qiskit's QuantumCircuit.

This function extends BQSKit's built-in conversion by adding support for
IQM's native 'r' gate. BQSKit represents this as U1qGate, which is converted
to Qiskit's RGate by rewriting the OpenQASM 2 output.

Args:
circuit: The BQSKit circuit to convert.

Returns:
The equivalent Qiskit QuantumCircuit with 'r' gates properly mapped.
"""
qasm = OPENQASM2Language().encode(circuit)
qasm = re.sub(r"\bU1q\(", "r(", qasm)

def r_gate(theta: float, phi: float) -> RGate:
return RGate(theta, phi)

r_gate_constructor = cast("Callable[[tuple[int | float, ...]], Instruction]", r_gate)

return qasm2.loads(
qasm,
custom_instructions=(
*qasm2.LEGACY_CUSTOM_INSTRUCTIONS,
qasm2.CustomInstruction("r", 2, 1, r_gate_constructor, builtin=True),
),
)
Comment thread
burgholzer marked this conversation as resolved.


def final_layout_pytket_to_qiskit(pytket_circuit: TketCircuit, qiskit_circuit: QuantumCircuit) -> Layout:
"""Converts a final layout from pytket to qiskit."""
pytket_layout = pytket_circuit.qubit_readout
size_circuit = pytket_circuit.n_qubits
Expand Down
3 changes: 2 additions & 1 deletion src/mqt/predictor/rl/predictorenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from typing import cast

import numpy as np
from bqskit.ext import bqskit_to_qiskit, qiskit_to_bqskit
from bqskit.ext import qiskit_to_bqskit
from gymnasium import Env
from gymnasium.spaces import Box, Dict, Discrete
from joblib import load
Expand All @@ -56,6 +56,7 @@
from mqt.predictor.rl.helper import create_feature_dict, get_path_training_circuits, get_state_sample
from mqt.predictor.rl.parsing import (
PreProcessTKETRoutingAfterQiskitLayout,
bqskit_to_qiskit,
final_layout_bqskit_to_qiskit,
final_layout_pytket_to_qiskit,
postprocess_vf2postlayout,
Expand Down
21 changes: 20 additions & 1 deletion tests/compilation/test_helper_rl.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from typing import TYPE_CHECKING, cast

import numpy as np
from bqskit.ir import Circuit, gates
from mqt.bench import BenchmarkLevel, get_benchmark
from mqt.bench.targets import get_device
from qiskit import transpile
Expand All @@ -22,7 +23,7 @@

from mqt.predictor.rl.actions import PassType, get_actions_by_pass_type
from mqt.predictor.rl.helper import create_feature_dict, get_path_trained_model, get_path_training_circuits
from mqt.predictor.rl.parsing import postprocess_vf2postlayout
from mqt.predictor.rl.parsing import bqskit_to_qiskit, get_bqskit_native_gates, postprocess_vf2postlayout

if TYPE_CHECKING:
from collections.abc import Callable
Expand Down Expand Up @@ -53,6 +54,24 @@ def test_get_path_training_circuits() -> None:
assert isinstance(path, Path)


def test_get_bqskit_native_gates_supports_iqm_r_gate() -> None:
"""Test that IQM's native RGate is represented in BQSKit."""
native_gates = get_bqskit_native_gates(get_device("iqm_crystal_20"))

assert any(isinstance(gate, gates.U1qGate) for gate in native_gates)


def test_bqskit_to_qiskit_converts_u1q_to_r_gate() -> None:
"""Test that BQSKit's U1qGate is converted to Qiskit's RGate."""
circuit = Circuit(1)
circuit.append_gate(gates.U1qGate(), [0], [0.1, 0.2])

qc = bqskit_to_qiskit(circuit)

assert qc.data[0].operation.name == "r"
assert qc.data[0].operation.params == [0.1, 0.2]

Comment thread
burgholzer marked this conversation as resolved.

def test_vf2_layout_and_postlayout() -> None:
"""Test the VF2Layout and VF2PostLayout passes."""
qc = get_benchmark("ghz", BenchmarkLevel.ALG, 3)
Expand Down
9 changes: 5 additions & 4 deletions tests/compilation/test_integration_further_SDKs.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from typing import TYPE_CHECKING, cast

import pytest
from bqskit.ext import bqskit_to_qiskit, qiskit_to_bqskit
from bqskit.ext import qiskit_to_bqskit
from bqskit.ir.circuit import Circuit
from mqt.bench.targets import get_available_device_names, get_device
from pytket.circuit import Qubit
Expand All @@ -26,6 +26,7 @@

from mqt.predictor.rl.actions import CompilationOrigin, PassType, get_actions_by_pass_type
from mqt.predictor.rl.parsing import (
bqskit_to_qiskit,
final_layout_bqskit_to_qiskit,
final_layout_pytket_to_qiskit,
)
Expand Down Expand Up @@ -83,22 +84,22 @@ def test_bqskit_synthesis_action(device: Target, available_actions_dict: dict[Pa
qc.h(0)
qc.cx(0, 1)

check_nat_gates = GatesInBasis(target=device)
check_nat_gates = GatesInBasis(basis_gates=device.operation_names)
check_nat_gates(qc)
assert not check_nat_gates.property_set["all_gates_in_basis"]

factory = cast("Callable[[Target], Callable[[Circuit], Circuit]]", action_bqskit_synthesis_action.transpile_pass)
lambda_ = factory(device)
bqskit_qc = qiskit_to_bqskit(qc)
if "rigetti" in device.description or "ionq" in device.description or "iqm" in device.description:
if "rigetti" in device.description or "ionq" in device.description:
with pytest.raises(ValueError, match=re.escape("not supported in BQSKIT")):
bqskit_qc_compiled = lambda_(bqskit_qc)
return
bqskit_qc_compiled = lambda_(bqskit_qc)
assert isinstance(bqskit_qc_compiled, Circuit)
native_gates_qc = bqskit_to_qiskit(bqskit_qc_compiled)

check_nat_gates = GatesInBasis(target=device)
check_nat_gates = GatesInBasis(basis_gates=device.operation_names)
check_nat_gates(native_gates_qc)
only_nat_gates = check_nat_gates.property_set["all_gates_in_basis"]
assert only_nat_gates
Expand Down
Loading