Skip to content

Commit

Permalink
Quantum Week Tutorial Related Improvements (#53)
Browse files Browse the repository at this point in the history
## Description
This PR fixes bugs and code code usability issues along the compilation
side of the stack.
New compilation methods have been added for the default selection of
compilation flags.


## Checklist:

- [x] The pull request only contains commits that are related to it.
- [x] I have added appropriate tests and documentation.
- [x] I have made sure that all CI jobs on GitHub pass.
- [x] The pull request introduces no new warnings and follows the
project's style guidelines.
  • Loading branch information
KevinMTO authored Sep 20, 2024
1 parent dd65231 commit 14dd85a
Show file tree
Hide file tree
Showing 11 changed files with 236 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@ def mini_sim(circuit: QuantumCircuit) -> NDArray[np.complex128]:
return state


def phy_sdit_sim(circuit: QuantumCircuit) -> NDArray[np.complex128]:
assert circuit.mappings is not None
dim = circuit.dimensions[0]
permutation = np.eye(dim)[:, circuit.mappings[0]]
state = np.array(dim * [0.0 + 0.0j])
state[0] = 1.0 + 0.0j
state = permutation @ state

for gate in circuit.instructions:
state = gate.to_matrix(identities=2) @ state
return state


class UnitaryVerifier:
"""Verifies unitary matrices.
Expand Down Expand Up @@ -86,6 +99,7 @@ def verify(self) -> bool:

for rotation in self.decomposition:
target = rotation.to_matrix(identities=0) @ target
target.round(3)

if self.permutation_matrix_final is not None:
target = np.linalg.inv(self.permutation_matrix_final) @ target
Expand Down
51 changes: 42 additions & 9 deletions src/mqt/qudits/compiler/dit_compiler.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import typing
from typing import Optional

from ..core.lanes import Lanes
from ..quantum_circuit.components.extensions.gate_types import GateTypes
Expand Down Expand Up @@ -47,27 +48,58 @@ def compile(self, backend: Backend, circuit: QuantumCircuit, passes_names: list[
elif "Multi" in str(compiler_pass):
passes_dict[GateTypes.MULTI] = decomposition
for gate in circuit.instructions:
decomposer = typing.cast(CompilerPass, passes_dict.get(gate.gate_type))
new_instructions = decomposer.transpile_gate(gate)
new_instr.extend(new_instructions)

circuit.set_instructions(new_instr)
circuit.set_mapping([graph.log_phy_map for graph in backend.energy_level_graphs])
decomposer = typing.cast(Optional[CompilerPass], passes_dict.get(gate.gate_type))
if decomposer is not None:
new_instructions = decomposer.transpile_gate(gate)
new_instr.extend(new_instructions)
else:
new_instr.append(gate)

return circuit
transpiled_circuit = circuit.copy()
mappings = []
for i, graph in enumerate(backend.energy_level_graphs):
if i < circuit.num_qudits:
mappings.append([lev for lev in graph.log_phy_map if lev < circuit.dimensions[i]])
transpiled_circuit.set_mapping(mappings)
return transpiled_circuit.set_instructions(new_instr)

def compile_O0(self, backend: Backend, circuit: QuantumCircuit) -> QuantumCircuit: # noqa: N802
passes = ["PhyLocQRPass", "PhyEntQRCEXPass"]
compiled = self.compile(backend, circuit, passes)

mappings = []
for i, graph in enumerate(backend.energy_level_graphs):
mappings.append([lev for lev in graph.log_phy_map if lev < circuit.dimensions[i]])
if i < circuit.num_qudits:
mappings.append([lev for lev in graph.log_phy_map if lev < circuit.dimensions[i]])
compiled.set_mapping(mappings)
return compiled

@staticmethod
def compile_O1(backend: Backend, circuit: QuantumCircuit) -> QuantumCircuit: # noqa: N802
phyloc = PhyLocQRPass(backend)
phyent = PhyEntQRCEXPass(backend)
resynth = NaiveLocResynthOptPass(backend)

circuit = resynth.transpile(circuit)
new_instructions = []
for gate in circuit.instructions:
ins: list[Gate] = []
if gate.gate_type is GateTypes.SINGLE:
ins = phyloc.transpile_gate(gate)
new_instructions.extend(ins)
else:
ins = phyent.transpile_gate(gate)
new_instructions.extend(ins)
transpiled_circuit = circuit.copy()
mappings = []
for i, graph in enumerate(backend.energy_level_graphs):
if i < circuit.num_qudits:
mappings.append([lev for lev in graph.log_phy_map if lev < circuit.dimensions[i]])
transpiled_circuit.set_mapping(mappings)
return transpiled_circuit.set_instructions(new_instructions)

@staticmethod
def compile_O2(backend: Backend, circuit: QuantumCircuit) -> QuantumCircuit: # noqa: N802
phyent = PhyEntQRCEXPass(backend)

lanes = Lanes(circuit)
Expand All @@ -84,6 +116,7 @@ def compile_O1(backend: Backend, circuit: QuantumCircuit) -> QuantumCircuit: #
transpiled_circuit = circuit.copy()
mappings = []
for i, graph in enumerate(backend.energy_level_graphs):
mappings.append([lev for lev in graph.log_phy_map if lev < circuit.dimensions[i]])
if i < circuit.num_qudits:
mappings.append([lev for lev in graph.log_phy_map if lev < circuit.dimensions[i]])
transpiled_circuit.set_mapping(mappings)
return transpiled_circuit.set_instructions(new_instructions)
7 changes: 6 additions & 1 deletion src/mqt/qudits/compiler/naive_local_resynth/local_resynth.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,10 @@ def transpile(self, circuit: QuantumCircuit) -> QuantumCircuit:

new_instructions = self.lanes.extract_instructions()

transpiled_circuit = self.circuit.copy()
transpiled_circuit = circuit.copy()
mappings = []
for i, graph in enumerate(self.backend.energy_level_graphs):
if i < circuit.num_qudits:
mappings.append([lev for lev in graph.log_phy_map if lev < circuit.dimensions[i]])
transpiled_circuit.set_mapping(mappings)
return transpiled_circuit.set_instructions(new_instructions)
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
from ....quantum_circuit.gate import Gate
from ....simulation.backends.backendv2 import Backend


np.seterr(all="ignore")


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def __init__(self, backend: Backend) -> None:
def transpile_gate(gate: Gate) -> list[Gate]:
eqr = EntangledQRCEX(gate)
decomp, _countcr, _countpsw = eqr.execute()
return decomp
return [op.dag() for op in reversed(decomp)]

def transpile(self, circuit: QuantumCircuit) -> QuantumCircuit:
self.circuit = circuit
Expand Down Expand Up @@ -109,7 +109,6 @@ def execute(self) -> tuple[list[Gate], int, int]:
for rotation in sequence_rotation_involved:
gate_matrix = self.get_gate_matrix(rotation, self.qudit_indices, self.dimensions)
u_ = gate_matrix @ u_

decomp += sequence_rotation_involved

diag_u = np.diag(u_)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
from typing import TYPE_CHECKING, cast

from mqt.qudits.compiler import CompilerPass
from mqt.qudits.compiler.onedit import PhyLocAdaPass
from mqt.qudits.compiler.onedit import PhyLocQRPass
from mqt.qudits.compiler.twodit.entanglement_qr import EntangledQRCEX
from mqt.qudits.quantum_circuit.components.extensions.gate_types import GateTypes
from mqt.qudits.quantum_circuit.gates import Perm
from mqt.qudits.quantum_circuit.gates import CEx, Perm

if TYPE_CHECKING:
from mqt.qudits.quantum_circuit import QuantumCircuit
Expand All @@ -29,15 +29,27 @@ def transpile_gate(self, gate: Gate) -> list[Gate]:
energy_graph_c = self.backend.energy_level_graphs[target_qudits[0]]
energy_graph_t = self.backend.energy_level_graphs[target_qudits[1]]

lp_map_0 = [lev for lev in energy_graph_c.log_phy_map if lev < dimensions[target_qudits[0]]]
lp_map_1 = [lev for lev in energy_graph_t.log_phy_map if lev < dimensions[target_qudits[1]]]
lp_map_0 = [lev for lev in energy_graph_c.log_phy_map if lev < dimensions[0]]
lp_map_1 = [lev for lev in energy_graph_t.log_phy_map if lev < dimensions[1]]

if isinstance(gate, CEx):
parent_circ = gate.parent_circuit
new_ctrl_lev = lp_map_0[gate.ctrl_lev]
new_la = lp_map_1[gate.lev_a]
new_lb = lp_map_1[gate.lev_b]
if new_la < new_lb:
new_parameters = [new_la, new_lb, new_ctrl_lev, gate.phi]
else:
new_parameters = [new_lb, new_la, new_ctrl_lev, gate.phi]
tcex = CEx(parent_circ, "CEx_t" + str(target_qudits), target_qudits, new_parameters, dimensions, None)
return [tcex]

perm_0 = Perm(gate.parent_circuit, "Pm_ent_0", target_qudits[0], lp_map_0, dimensions[0])
perm_1 = Perm(gate.parent_circuit, "Pm_ent_1", target_qudits[1], lp_map_1, dimensions[1])
perm_0_dag = Perm(gate.parent_circuit, "Pm_ent_0", target_qudits[0], lp_map_0, dimensions[0]).dag()
perm_1_dag = Perm(gate.parent_circuit, "Pm_ent_1", target_qudits[1], lp_map_1, dimensions[1]).dag()

phyloc = PhyLocAdaPass(self.backend)
phyloc = PhyLocQRPass(self.backend)
perm_0_seq = phyloc.transpile_gate(perm_0)
perm_1_seq = phyloc.transpile_gate(perm_1)
perm_0_d_seq = phyloc.transpile_gate(perm_0_dag)
Expand Down
12 changes: 11 additions & 1 deletion src/mqt/qudits/quantum_circuit/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,17 @@ def simulate(self) -> NDArray:
result = job.result()
return result.get_state_vector()

def compile(self, backend_name: str) -> QuantumCircuit:
def compileO0(self, backend_name: str) -> QuantumCircuit: # noqa: N802
from mqt.qudits.compiler import QuditCompiler
from mqt.qudits.simulation import MQTQuditProvider

qudit_compiler = QuditCompiler()
provider = MQTQuditProvider()
backend_ion = provider.get_backend(backend_name)

return qudit_compiler.compile_O0(backend_ion, self)

def compileO1(self, backend_name: str) -> QuantumCircuit: # noqa: N802
from mqt.qudits.compiler import QuditCompiler
from mqt.qudits.simulation import MQTQuditProvider

Expand Down
2 changes: 2 additions & 0 deletions src/mqt/qudits/simulation/backends/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from __future__ import annotations

from .innsbruck_01 import Innsbruck01
from .misim import MISim
from .tnsim import TNSim

__all__ = [
"Innsbruck01",
"MISim",
"TNSim",
]
105 changes: 105 additions & 0 deletions src/mqt/qudits/simulation/backends/innsbruck_01.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
from __future__ import annotations

from typing import TYPE_CHECKING

import numpy as np
from typing_extensions import Unpack

# from pyseq.mqt_qudits_runner.sequence_runner import quantum_circuit_runner
from ...core import LevelGraph
from ..jobs import Job, JobResult
from .backendv2 import Backend

if TYPE_CHECKING:
from ...quantum_circuit import QuantumCircuit
from .. import MQTQuditProvider
from ..noise_tools import NoiseModel


class Innsbruck01(Backend):
@property
def version(self) -> int:
return 0

def __init__(
self,
provider: MQTQuditProvider,
**fields: Unpack[Backend.DefaultOptions],
) -> None:
super().__init__(
provider=provider,
name="Innsbruck01",
description="Interface to the Innsbruck machine 01. Interface prototype with MVP.",
**fields,
)
self.outcome: list[int] = []
self.options["noise_model"] = self.__noise_model()
self.author = "<Kevin Mato>"
self._energy_level_graphs: list[LevelGraph] = []

@property
def energy_level_graphs(self) -> list[LevelGraph]:
if len(self._energy_level_graphs) == 0:
e_graphs: list[LevelGraph] = []
# declare the edges on the energy level graph between logic states .
edges = [
(0, 1, {"delta_m": 0, "sensitivity": 3, "carrier": 0}),
(1, 2, {"delta_m": 0, "sensitivity": 4, "carrier": 1}),
]
# name explicitly the logic states .
nodes = [0, 1, 2]
# declare physical levels in order of mapping of the logic states just declared .
# i.e. here we will have Logic 0 -> Phys. 0, have Logic 1 -> Phys. 1, have Logic 2 -> Phys. 2 .
nmap = [0, 1, 2]
graph_0 = LevelGraph(edges, nodes, nmap, [1])

edges_1 = [
(0, 1, {"delta_m": 0, "sensitivity": 3, "carrier": 0}),
(1, 2, {"delta_m": 0, "sensitivity": 4, "carrier": 1}),
]
# name explicitly the logic states .
nodes_1 = [0, 1, 2]
# declare physical levels in order of mapping of the logic states just declared .
# i.e. here we will have Logic 0 -> Phys. 0, have Logic 1 -> Phys. 1, have Logic 2 -> Phys. 2 .
nmap_1 = [0, 1, 2]

graph_1 = LevelGraph(edges_1, nodes_1, nmap_1, [1])

e_graphs.extend((graph_0, graph_1))

self._energy_level_graphs = e_graphs
return self._energy_level_graphs

def edge_to_carrier(self, leva: int, levb: int, graph_index: int) -> int:
e_graph = self.energy_level_graphs[graph_index]
edge_data: dict[str, int] = e_graph.get_edge_data(leva, levb)
return edge_data["carrier"]

def __noise_model(self) -> NoiseModel | None:
return self.noise_model

def run(self, circuit: QuantumCircuit, **options: Unpack[Backend.DefaultOptions]) -> Job:
job = Job(self)

self._options.update(options)
self.noise_model = self._options.get("noise_model", None)
self.shots = self._options.get("shots", 50)
self.memory = self._options.get("memory", False)
self.full_state_memory = self._options.get("full_state_memory", False)
self.file_path = self._options.get("file_path", None)
self.file_name = self._options.get("file_name", None)

assert self.shots >= 50, "Number of shots should be above 50"
self.execute(circuit)
job.set_result(JobResult(state_vector=np.array([]), counts=self.outcome))

return job

def execute(self, circuit: QuantumCircuit, noise_model: NoiseModel | None = None) -> None:
_ = noise_model # Silences the unused argument warning
self.system_sizes = circuit.dimensions
self.circ_operations = circuit.instructions

# quantum_circuit_runner(self.circ_operations)

self.outcome = [] # quantum_circuit_runner(metadata, self.system_sizes)
3 changes: 2 additions & 1 deletion src/mqt/qudits/simulation/qudit_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import re
from typing import TYPE_CHECKING, Any, ClassVar

from .backends import MISim, TNSim
from .backends import Innsbruck01, MISim, TNSim
from .backends.fake_backends import FakeIonTraps2Six, FakeIonTraps2Trits, FakeIonTraps3Six

if TYPE_CHECKING:
Expand All @@ -18,6 +18,7 @@ def version(self) -> int:
__backends: ClassVar[dict[str, type[Backend]]] = {
"tnsim": TNSim,
"misim": MISim,
"innsbruck01": Innsbruck01,
"faketraps2trits": FakeIonTraps2Trits,
"faketraps2six": FakeIonTraps2Six,
"faketraps3six": FakeIonTraps3Six,
Expand Down
Loading

0 comments on commit 14dd85a

Please sign in to comment.