Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
fe606c4
update git_ignore
inctechs Oct 8, 2025
ad9e877
Merge remote-tracking branch 'origin/main' into code-switching-compiler
inctechs Oct 23, 2025
892cba3
add networkx to mypy ignore
inctechs Oct 27, 2025
9d5fdb8
implement first version of min-cut solution
inctechs Oct 27, 2025
e5e16fc
add one way transveral CNOTs to be allowed
inctechs Oct 28, 2025
bdb5b9b
add edge_type keyword to edges
inctechs Nov 4, 2025
0e9b3cf
change default order of magnitude for temporal edges
inctechs Nov 4, 2025
bb33537
add code bias functionality
inctechs Nov 4, 2025
91df0eb
clear/ reorder code
inctechs Nov 4, 2025
eee407a
read in QC as dag to support depth and prepare idle switching
inctechs Nov 5, 2025
c1d3f52
change qubit activity collection to list instead of set
inctechs Nov 5, 2025
adc87f5
make Global default temporal edge weight
inctechs Nov 5, 2025
3bf1bff
add idle bonus calculation and idle switching funcitonality
inctechs Nov 5, 2025
1c7684e
update docstrings and comments
inctechs Nov 5, 2025
73ffd1c
fix counting of switches
inctechs Nov 5, 2025
5b08d68
Revert "fix counting of switches"
inctechs Nov 5, 2025
ad6ff67
fix counting of switching operations
inctechs Nov 5, 2025
c63e2aa
add placeholder switch operations to qiskit circuit
inctechs Nov 6, 2025
9b5bb38
add extraction of switching position
inctechs Nov 6, 2025
4317cd4
add feature that idle bonus only considered for certain length
inctechs Nov 12, 2025
2c58f77
change return of min-cut function
inctechs Nov 12, 2025
38d1304
add scripts for generating ciruits
inctechs Nov 13, 2025
1914d5b
add function to generate universal random qiskit circuit
inctechs Nov 13, 2025
4b40503
add edge ration
inctechs Nov 13, 2025
796e032
change default folder name of circuit output
inctechs Nov 13, 2025
926fb01
update default number of seeds
inctechs Nov 13, 2025
e330bca
add lookahead function
inctechs Nov 13, 2025
4689579
add scripts for performance analysis
inctechs Nov 14, 2025
f07cc68
change default number of generated cicuits
inctechs Nov 14, 2025
ef3dfd4
remove raw data return form min_cut
inctechs Nov 14, 2025
0ec634d
make lookahead deterministic
inctechs Nov 14, 2025
63c982d
add lookahead to init file
inctechs Nov 14, 2025
a5c02dd
change simulations to be gate distribution sensitive
inctechs Nov 14, 2025
f99ee41
add feature to avoid generating circuits twice
inctechs Nov 16, 2025
6d9e3ac
add script to generate array with steps of 64
inctechs Nov 16, 2025
1436eb8
add all qubits numbers to performance bash
inctechs Nov 16, 2025
80fbb09
add intermediate results
inctechs Nov 17, 2025
a82e97d
add new results
inctechs Nov 18, 2025
c0c39ca
update simulations
inctechs Nov 18, 2025
d37f0ab
move utils functions to utils file
inctechs Nov 21, 2025
a785748
set up code switching compilation test
inctechs Nov 21, 2025
595ecba
Merge remote-tracking branch 'origin/main' into code-switching-compiler
inctechs Nov 21, 2025
4d7b7a1
repair test for idle bonus
inctechs Nov 21, 2025
a333e5d
redefine api by making functions private
inctechs Nov 21, 2025
1d90c17
add full function docstring extract switch locations
inctechs Nov 21, 2025
1af4cd8
add one way transversal test
inctechs Nov 21, 2025
82fdcf1
add CompilerConfig for different settings and keep it tidy
inctechs Nov 21, 2025
e73f6fc
add CompilerConfig to init
inctechs Nov 21, 2025
5c8f2ec
connect biased code to CompilerConfig
inctechs Nov 21, 2025
ffc1375
add code bias test
inctechs Nov 21, 2025
42ae8cf
add test for compilation utils
inctechs Nov 23, 2025
2e4e588
remove dag layer visualization used for debugging previously
inctechs Nov 23, 2025
9f75e39
Merge remote-tracking branch 'origin/main' into code-switching-compiler
inctechs Nov 24, 2025
76a2649
work in CodeRabbitAI code review
inctechs Nov 26, 2025
3c23ca5
fix more CodeRabbitAIs comments
inctechs Nov 26, 2025
0ffd8f1
rename variables in scripts
inctechs Nov 28, 2025
6cb8917
rename class to MinimalCodeSwitchingCompiler
inctechs Nov 28, 2025
2defa2d
generalise Graph construction
inctechs Dec 1, 2025
648fe47
remove unused methods to connect to terminals and connecting CNOTs
inctechs Dec 1, 2025
7e2dd98
fix typo with parameter names
inctechs Dec 1, 2025
c8e66ca
add idle bonus calcualtion from the paper
inctechs Dec 1, 2025
2dfb57d
move random circuit generation from utils to scripts
inctechs Dec 1, 2025
fe44822
rename naive code switching method and adjust collaterals
inctechs Dec 1, 2025
364550b
remove unnecessary comments
inctechs Dec 1, 2025
85b2d35
move node parser back to graph class
inctechs Dec 1, 2025
d9fe652
remove redundancy
inctechs Dec 1, 2025
4afedfb
rename tests for naive switching
inctechs Dec 1, 2025
45a3a97
remove stress test, adapt parsing node test, fix num_switches typo
inctechs Dec 1, 2025
24793dd
improve placeholder test
inctechs Dec 1, 2025
ec589b7
remove personal playground folder
inctechs Dec 2, 2025
7415903
add networkx to pyproject.toml
inctechs Dec 2, 2025
e5b3beb
automatic reordering by uv
inctechs Dec 2, 2025
30509e0
fix logical error where same back to back gates are allowed
inctechs Dec 2, 2025
85fc4b6
rabbit edits
inctechs Dec 2, 2025
e458dae
rename package to code_switching
inctechs Dec 2, 2025
5b529a0
add safe guard for non negative bonus
inctechs Dec 2, 2025
a8d3ca2
save uv.lock
inctechs Dec 2, 2025
b92b2f7
Merge remote-tracking branch 'origin/main' into code-switching-compiler
inctechs Dec 2, 2025
27060ce
add CodeSwitching docs
inctechs Dec 2, 2025
518d63e
rename unary edges to bias edges
inctechs Dec 3, 2025
364b621
generalise one-way transversal gate definition.
inctechs Dec 3, 2025
bd00389
sketch doc structure
inctechs Dec 3, 2025
13d38b8
fix bug in docs
inctechs Dec 3, 2025
b71d9b4
fix bug in code cells
inctechs Dec 3, 2025
f63fec2
fix typos in the docs
inctechs Dec 3, 2025
3b38882
Merge remote-tracking branch 'origin/main' into code-switching-compiler
inctechs Dec 9, 2025
909c3c6
Update docs/CodeSwitching.md
inctechs Dec 9, 2025
dff65bc
Apply suggestions from code review for docs
inctechs Dec 9, 2025
e81fe16
Apply suggestions from code review: fix naming of class in docstrings
inctechs Dec 9, 2025
5674e4f
edit Changelog.md
inctechs Dec 9, 2025
9adb064
Merge branch 'code-switching-compiler' of https://github.com/munich-q…
inctechs Dec 9, 2025
ed674dc
fix latest coderabbit feedback
inctechs Dec 9, 2025
e901ac5
add missing references to changelog
inctechs Dec 9, 2025
2908247
Merge remote-tracking branch 'origin' into code-switching-compiler
inctechs Jan 14, 2026
94f31ea
🎨 pre-commit fixes
pre-commit-ci[bot] Jan 14, 2026
1eb3f39
fix last coderabbit changes
inctechs Jan 14, 2026
7f5b1e5
Merge branch 'code-switching-compiler' of https://github.com/munich-q…
inctechs Jan 14, 2026
a43628c
remove note from README and add key feature description
inctechs Jan 15, 2026
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
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -174,3 +174,11 @@ out/build

node_modules/
wheelhouse/

pytest.ini

# -----------------------------------------------------------------------------
# Personal / local development ignore rules
# (Feel free to add your own scratch or playground folders here)
# -----------------------------------------------------------------------------
playground/
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ exclude = [

[[tool.mypy.overrides]]
module = ["qiskit.*", "qecsim.*", "qiskit_aer.*", "matplotlib.*", "scipy.*", "ldpc.*", "pytest_console_scripts.*",
"z3.*", "bposd.*", "numba.*", "pymatching.*", "stim.*", "multiprocess.*", "sinter.*", "qsample.*", "pandas.*", "tqdm.*"]
"z3.*", "bposd.*", "numba.*", "pymatching.*", "stim.*", "multiprocess.*", "sinter.*", "qsample.*", "pandas.*", "tqdm.*", "networkx.*"]
ignore_missing_imports = true


Expand Down
14 changes: 14 additions & 0 deletions src/mqt/qecc/circuit_compilation/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM
# All rights reserved.
#
# SPDX-License-Identifier: MIT
#
# Licensed under the MIT License

"""Methods and utilities for code switching compilation."""

from __future__ import annotations

from .code_switching_compiler import CodeSwitchGraph

__all__ = ["CodeSwitchGraph"]
198 changes: 198 additions & 0 deletions src/mqt/qecc/circuit_compilation/code_switching_compiler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM
# All rights reserved.
#
# SPDX-License-Identifier: MIT
#
# Licensed under the MIT License

"""Code Switching Compiler to find the minimum number of switches."""

from __future__ import annotations

from typing import TYPE_CHECKING

import networkx as nx

if TYPE_CHECKING:
from qiskit import QuantumCircuit


class CodeSwitchGraph:
"""A directed graph representation of a quantum circuit for code-switching analysis using min-cut / max-flow optimization.

The graph is constructed such that:
- Each quantum operation (T, H, CNOT) corresponds to one or more nodes.
- Source (SRC) and sink (SNK) nodes represent two different codes:
* Source-connected nodes (T, CNOT) → operations that can be done transversally in code A.
* Sink-connected nodes (H, CNOT) → operations that can be done transversally in code B.
- Infinite-capacity edges enforce code consistency between operations (e.g., CNOT links).
- Finite-capacity edges (default 1.0) represent potential code transitions along qubit timelines.

Attributes:
----------
G : nx.DiGraph
Directed graph storing the nodes and edges.
source : str
Identifier for the source node ("SRC").
sink : str
Identifier for the sink node ("SNK").
"""

def __init__(self) -> None:
"""Initialize the CodeSwitchGraph with source and sink nodes."""
self.G: nx.DiGraph = nx.DiGraph()
self.source: str = "SRC"
self.sink: str = "SNK"
self.G.add_node(self.source)
self.G.add_node(self.sink)

def add_gate_node(self, gate_type: str, qubit: int, depth: int) -> str:
"""Add a node representing a quantum gate operation.

Parameters
----------
gate_type : str
The gate type (e.g., "H", "T", "CNOTc", "CNOTt").
qubit : int
Index of the qubit the gate acts on.
depth : int
Depth (or layer index) of the operation in the circuit.

Returns:
-------
str
The unique node identifier created for this operation.
"""
node_id = f"{gate_type}_q{qubit}_d{depth}"
self.G.add_node(node_id, gate=gate_type, qubit=qubit, depth=depth)
return node_id

def add_edge_with_capacity(self, u: str, v: str, capacity: float, bidirectional: bool = True) -> None:
"""Add a directed edge with specified capacity between two nodes.

Parameters
----------
u : str
Source node identifier.
v : str
Target node identifier.
capacity : float
Edge capacity.
"""
self.G.add_edge(u, v, capacity=capacity)
if bidirectional:
self.G.add_edge(v, u, capacity=capacity)

def add_infinite_edge(self, u: str, v: str, bidirectional: bool = True) -> None:
"""Add an edge of infinite capacity between two nodes.

Parameters
----------
u : str
Source node identifier.
v : str
Target node identifier.
"""
self.add_edge_with_capacity(u, v, capacity=float("inf"), bidirectional=bidirectional)

def add_regular_edge(self, u: str, v: str, capacity: float = 1.0, bidirectional: bool = True) -> None:
"""Add a regular (finite-capacity) directed edge.

Parameters
----------
u : str
Source node identifier.
v : str
Target node identifier.
capacity : float, optional
Edge capacity (default is 1.0).
"""
self.add_edge_with_capacity(u, v, capacity=capacity, bidirectional=bidirectional)

def connect_to_code(self, node_id: str, gate_type: str) -> None:
"""Connect a gate node to the source and/or sink according to which code can perform the operation transversally.

Note: Here we fix the convention that the 2D Color Code corresponds to the source (can perform H and CNOT)
and the 3D Surface Code corresponds to the sink (can perform T and CNOT). This convention is arbitrary and can be swapped.
However, swapping the convention requires to change the one-way transversal CNOT setting:
2D Source + 3D Sink <-> (infinite edge) Control -> Target
3D Source + 2D Sink <-> (infinite edge) Control <- Target

Parameters
----------
node_id : str
Node identifier of the gate operation.
gate_type : str
Type of the gate (e.g., "H", "T", "CNOT").
"""
# Source code can perform T and CNOT gates
if gate_type == "T":
self.add_infinite_edge(self.sink, node_id)
# Sink code can perform H and CNOT gates
if gate_type == "H":
self.add_infinite_edge(node_id, self.source)

def add_cnot_links(self, control_node: str, target_node: str, one_way_transversal_cnot: bool = False) -> None:
"""Add bidirectional infinite-capacity edges between two CNOT-related nodes to enforce that both qubits remain in the same code.

Parameters
----------
control_node : str
Node representing the control qubit's CNOT operation.
target_node : str
Node representing the target qubit's CNOT operation.
"""
self.add_infinite_edge(control_node, target_node, bidirectional=not (one_way_transversal_cnot))

def build_from_qiskit(self, circuit: QuantumCircuit, one_way_transversal_cnot: bool = False) -> None:
"""Construct the code-switch graph from a Qiskit QuantumCircuit.

Parameters
----------
circuit : QuantumCircuit
The input quantum circuit containing H, T, and CX (CNOT) gates.

Notes:
-----
- For each gate, a node is created per qubit.
- Temporal ordering along qubit lines is maintained via regular edges.
- CNOT gates create two linked nodes (control, target) with infinite capacity.
"""
qubit_last_node: list[str | None] = [None] * circuit.num_qubits
depth = 0

for depth, instr in enumerate(circuit.data):
gate = instr.operation.name.upper()
qubits = [circuit.find_bit(q).index for q in instr.qubits]

if gate in {"H", "T"}:
q = qubits[0]
node = self.add_gate_node(gate, q, depth)
self.connect_to_code(node, gate)
if qubit_last_node[q]:
self.add_regular_edge(qubit_last_node[q], node)
qubit_last_node[q] = node

elif gate == "CX":
ctrl, tgt = qubits
node_ctrl = self.add_gate_node("CNOTc", ctrl, depth)
node_tgt = self.add_gate_node("CNOTt", tgt, depth)
self.add_cnot_links(node_ctrl, node_tgt, one_way_transversal_cnot=one_way_transversal_cnot)
for q, node in [(ctrl, node_ctrl), (tgt, node_tgt)]:
if qubit_last_node[q]:
self.add_regular_edge(qubit_last_node[q], node)
qubit_last_node[q] = node

def compute_min_cut(self) -> tuple[float, set[str], set[str]]:
"""Compute the minimum s-t cut between the source and sink.

Returns:
-------
Tuple[float, Set[str], Set[str]]
A tuple (cut_value, S, T) where:
- cut_value is the total capacity of the minimum cut,
- S is the set of nodes reachable from the source,
- T is the complementary set of nodes.
"""
cut_value, (S, T) = nx.minimum_cut(self.G, self.source, self.sink, capacity="capacity") # noqa: N806
return cut_value, S, T
Loading