Skip to content
Open
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ releases may include breaking changes.

### Added

- ✨ Add BQSKit's `QSDPass` to the RL synthesis actions ([#795])
([**@flowerthrower**])
- ✨ Add Qiskit's `TrivialLayout`, `ElidePermutations`, `SabreSwap`,
`BasicSwap`, `LookaheadSwap`, `RemoveIdentityEquivalent`, and
`Optimize1qGatesSimpleCommutation` passes and the optional IBM-backed
Expand Down Expand Up @@ -100,6 +102,7 @@ for previous changelogs._

[#773]: https://github.com/munich-quantum-toolkit/predictor/pull/771
[#769]: https://github.com/munich-quantum-toolkit/predictor/pull/769
[#795]: https://github.com/munich-quantum-toolkit/predictor/pull/795
[#794]: https://github.com/munich-quantum-toolkit/predictor/pull/794
[#758]: https://github.com/munich-quantum-toolkit/predictor/pull/758
[#755]: https://github.com/munich-quantum-toolkit/predictor/pull/755
Expand Down
3 changes: 2 additions & 1 deletion UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ The new actions include:
- the `GeneralizedSabreRoutingPass` routing action;
- the `BQSKitSABREMapping` mapping action; and
- the `QSearchSynthesisPass`, `LEAPSynthesisPass`, `WalshDiagonalSynthesisPass`,
`FullQSDPass`, `BlockZXZPass`, and `FullBlockZXZPass` synthesis actions.
`QSDPass`, `FullQSDPass`, `BlockZXZPass`, and `FullBlockZXZPass` synthesis
actions.

See the
[framework setup](docs/setup.md#step-2-train-reinforcement-learning-models) for
Expand Down
28 changes: 26 additions & 2 deletions src/mqt/predictor/rl/actions/bqskit_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from functools import cache
from typing import TYPE_CHECKING, TypeAlias, cast

from bqskit import MachineModel
from bqskit import Circuit, MachineModel
from bqskit.compiler import Compiler, Workflow
from bqskit.compiler.compile import (
build_multi_qudit_retarget_workflow,
Expand All @@ -39,11 +39,13 @@
IfThenElsePass,
LEAPSynthesisPass,
ManyQuditGatesPredicate,
QSDPass,
QSearchSynthesisPass,
RestoreMeasurements,
SetModelPass,
SetRandomSeedPass,
StaticPlacementPass,
SynthesisPass,
TrivialPlacementPass,
UnfoldPass,
WalshDiagonalSynthesisPass,
Expand All @@ -59,11 +61,11 @@
if TYPE_CHECKING:
from collections.abc import Callable

from bqskit import Circuit
from bqskit.compiler.basepass import BasePass
from bqskit.compiler.passdata import PassData
from bqskit.compiler.workflow import WorkflowLike
from bqskit.ir import Gate
from bqskit.qis import StateSystem, StateVector, UnitaryMatrix
from qiskit import QuantumCircuit
from qiskit.circuit import Qubit as QiskitQubit
from qiskit.transpiler import Target
Expand All @@ -81,6 +83,22 @@
_BQSKIT_NUM_WORKERS = 1 if os.getenv("GITHUB_ACTIONS") == "true" else -1


class _QSDUnitarySynthesisPass(SynthesisPass):
"""Apply one QSD level to each multi-qubit synthesis target."""

async def synthesize(
self,
target: UnitaryMatrix | StateVector | StateSystem,
data: PassData,
) -> Circuit:
"""Synthesize a partition target with one QSD level."""
del data
unitary = cast("UnitaryMatrix", target)
if unitary.num_qudits == 1:
return Circuit.from_unitary(unitary)
return QSDPass.qsd(unitary)


def _r_gate(theta: float, phi: float) -> Instruction:
"""Construct an RGate with the given parameters."""
return RGate(theta, phi)
Expand Down Expand Up @@ -309,6 +327,12 @@ def bqskit_synthesis_actions() -> list[Action]:
IfThenElsePass(DiagonalPredicate(1e-9), WalshDiagonalSynthesisPass()),
),
),
DeferredDeviceAction(
"QSDPass",
CompilationOrigin.BQSKIT,
PassType.SYNTHESIS,
transpile_pass=lambda device: _bqskit_partitioned_synthesis_factory(device, _QSDUnitarySynthesisPass()),
),
DeferredDeviceAction(
"FullQSDPass",
CompilationOrigin.BQSKIT,
Expand Down
21 changes: 20 additions & 1 deletion tests/compilation/test_integration_further_SDKs.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@

from __future__ import annotations

import asyncio
from typing import TYPE_CHECKING

import pytest
from bqskit.compiler.passdata import PassData
from bqskit.ir.gates import MPRYGate, MPRZGate, VariableUnitaryGate
from mqt.bench.targets import get_device
from qiskit import QuantumCircuit
from qiskit.circuit import StandardEquivalenceLibrary
Expand All @@ -26,7 +29,7 @@
TrivialLayout,
)

from mqt.predictor.rl.actions import CompilationOrigin, PassType
from mqt.predictor.rl.actions import CompilationOrigin, PassType, bqskit_actions
from mqt.predictor.rl.predictorenv import PredictorEnv

if TYPE_CHECKING:
Expand Down Expand Up @@ -126,6 +129,22 @@ def env(target: Target) -> PredictorEnv:
return PredictorEnv(device=target, reward_function="expected_fidelity")


@pytest.mark.filterwarnings("ignore:__array__ implementation doesn't accept a copy keyword:DeprecationWarning")
def test_qsd_unitary_synthesis_pass_applies_one_qsd_level(simple_circuit: QuantumCircuit) -> None:
"""The QSD adapter performs one equivalent decomposition of a reachable partition target."""
bqskit_circuit = bqskit_actions.qiskit_to_bqskit(simple_circuit)
unitary = bqskit_circuit.get_unitary()
synthesis_pass = bqskit_actions._QSDUnitarySynthesisPass() # ruff: ignore[private-member-access]

decomposed = asyncio.run(synthesis_pass.synthesize(unitary, PassData(bqskit_circuit)))

assert decomposed.get_unitary().get_distance_from(unitary) < 1e-7
assert sum(count for gate, count in decomposed.gate_counts.items() if isinstance(gate, VariableUnitaryGate)) == 4
assert all(gate.num_qudits == 2 for gate in decomposed.gate_set if isinstance(gate, VariableUnitaryGate))
assert sum(count for gate, count in decomposed.gate_counts.items() if isinstance(gate, MPRZGate)) == 2
assert sum(count for gate, count in decomposed.gate_counts.items() if isinstance(gate, MPRYGate)) == 1


def test_synthesis_actions_produce_native_gates(
simple_circuit: QuantumCircuit,
env: PredictorEnv,
Expand Down
Loading