Skip to content

Commit

Permalink
feat: Add properties to nodes and edges in LatticeGraph and implement…
Browse files Browse the repository at this point in the history
… CollisionChecker for collision detection
  • Loading branch information
Akinori Machino committed Feb 7, 2025
1 parent f515dc2 commit c8200fa
Show file tree
Hide file tree
Showing 3 changed files with 224 additions and 6 deletions.
16 changes: 10 additions & 6 deletions src/qubex/backend/lattice_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,30 @@ class QubitNode(TypedDict):
position: tuple[float, float]
mux_id: int
index_in_mux: int
properties: dict[str, float]


class QubitEdge(TypedDict):
id: tuple[int, int]
label: str
weight: float
position: tuple[tuple[float, float], tuple[float, float], tuple[float, float]]
properties: dict[str, float]


class ResonatorNode(TypedDict):
id: int
label: str
coordinates: tuple[int, int]
position: tuple[float, float]
properties: dict[str, float]


class MuxNode(TypedDict):
id: int
label: str
coordinates: tuple[int, int]
position: tuple[float, float]
properties: dict[str, float]


class LatticeGraph:
Expand Down Expand Up @@ -155,7 +158,7 @@ def qubit_undirected_graph(
return self.qubit_graph.to_undirected(as_view=True)

@cached_property
def qubit_undirected_edges(
def qubit_links(
self,
) -> dict[tuple[int, int], QubitEdge]:
return {
Expand Down Expand Up @@ -185,6 +188,7 @@ def _init_qubit_graph(self):
"position": (x, y),
"mux_id": idx_m,
"index_in_mux": idx_qm,
"properties": {},
}
)
nx.relabel_nodes(
Expand All @@ -199,7 +203,6 @@ def _init_qubit_graph(self):
{
"id": (id0, id1),
"label": f"{node0['label']}-{node1['label']}",
"weight": 1.0,
"position": (
node0["position"],
(
Expand All @@ -208,6 +211,7 @@ def _init_qubit_graph(self):
),
node1["position"],
),
"properties": {},
},
)

Expand All @@ -220,6 +224,7 @@ def _init_resonator_graph(self):
"label": f"{PREFIX_RESONATOR}{id:0{self.resonator_max_digit}d}",
"coordinates": data["coordinates"],
"position": data["position"],
"properties": data["properties"],
}
)

Expand All @@ -235,6 +240,7 @@ def _init_mux_graph(self):
"label": f"{PREFIX_MUX}{idx:0{self.mux_max_digit}d}",
"coordinates": (x, y),
"position": (x * 2 + 0.5, y * 2 + 0.5),
"properties": {},
}
)
nx.relabel_nodes(
Expand Down Expand Up @@ -603,9 +609,7 @@ def _create_qubit_edge_trace(
hovertexts: dict | None = None,
) -> list[go.Scatter]:
if values is None:
values = {
edge["label"]: edge["weight"] for edge in self.qubit_edges.values()
}
values = {edge["label"]: 1.0 for edge in self.qubit_edges.values()}

values = {
key: value
Expand Down
Empty file added src/qubex/collision/__init__.py
Empty file.
214 changes: 214 additions & 0 deletions src/qubex/collision/collision_checker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
from __future__ import annotations

from typing import Literal

import numpy as np

from ..backend.config_loader import ConfigLoader

CollisionType = Literal[
"Type0A",
"Type0B",
"Type1A",
"Type1B",
"Type1C",
"Type2A",
"Type2B",
"Type3A",
"Type3B",
"Type7",
"Type8",
"Type9",
]

CONDITIONS = {
"max_frequency": 9.5,
"min_frequency": 6.5,
"max_detuning": 1.3,
"min_t1": 3e3,
"min_t2": 3e3,
"b1": 0.2,
"b2": 0.75,
}

DEFAULTS = {
"t1": 3e3,
"t2_echo": 3e3,
"coupling": 0.008,
}


class CollisionChecker:
def __init__(
self,
chip_id: str,
params_dir: str | None = None,
conditions: dict | None = None,
):
if params_dir is None:
config_loader = ConfigLoader()
else:
config_loader = ConfigLoader(params_dir=params_dir)

experiment_system = config_loader.get_experiment_system(chip_id)
self.graph = experiment_system.quantum_system._graph
props = config_loader._props_dict[chip_id]

for node in self.graph.qubit_nodes.values():
label = node["label"]
node["properties"] = {
"frequency": props["qubit_frequency"].get(label),
"anharmonicity": props["anharmonicity"].get(label),
"t1": props["t1"].get(label),
"t2_echo": props["t2_echo"].get(label),
}

for edge in self.graph.qubit_edges.values():
label = edge["label"]
edge["properties"] = {
"coupling": props["qubit_qubit_coupling_strength"].get(label),
}

self.conditions = CONDITIONS
if conditions is not None:
self.conditions.update(conditions)

def get_value(
self,
target: int | tuple[int, int],
property: str,
default: float = np.nan,
) -> float:
if isinstance(target, int):
value = self.graph.qubit_nodes[target]["properties"][property]
if value is None or np.isnan(value):
return default
else:
return value
elif isinstance(target, tuple):
value = self.graph.qubit_edges[target]["properties"][property]
if value is None or np.isnan(value):
return default
else:
return value
else:
raise ValueError("Invalid target type.")

def check(
self,
*collision: CollisionType,
) -> dict:
result = {}
for type in collision:
result[type] = getattr(self, f"check_{type.lower()}")()

self.collisions = result
return result

def check_type0a(
self,
min_frequency: float | None = None,
max_frequency: float | None = None,
min_t1: float | None = None,
min_t2: float | None = None,
) -> dict:
"""
conditions:
(1) qubit unmeasured
(2) qubit frequency out of range
(3) qubit t1, t2 is too short
"""
result = {}
for i, data in self.graph.qubit_nodes.items():
flag = False
label = data["label"]
log = []
f = self.get_value(i, "frequency", np.nan)
f_min = min_frequency or self.conditions["min_frequency"]
f_max = max_frequency or self.conditions["max_frequency"]
t1 = self.get_value(i, "t1", DEFAULTS["t1"])
t1_min = min_t1 or self.conditions["min_t1"]
t2 = self.get_value(i, "t2_echo", DEFAULTS["t2_echo"])
t2_min = min_t2 or self.conditions["min_t2"]
if np.isnan(f):
flag = True
log.append(f"Frequency of {label} is not defined.")
if f < f_min:
flag = True
log.append(
f"Frequency of {label} ({f:.3f} GHz) is lower than {f_min:.3f} GHz."
)
if f > f_max:
flag = True
log.append(
f"Frequency of {label} ({f:.3f} GHz) is higher than {f_max:.3f} GHz."
)
if t1 < t1_min:
flag = True
log.append(
f"T1 of {label} ({t1 * 1e-3:.1f} μs) is lower than {t1_min * 1e-3:.1f} μs."
)
if t2 < t2_min:
flag = True
log.append(
f"T2 of {label} ({t2 * 1e-3:.1f} μs) is lower than {t2_min * 1e-3:.1f} μs."
)

result[label] = {
"collision": flag,
"log": log,
}

result = dict(sorted(result.items()))
return result

def check_type0b(
self,
max_detuning: float | None = None,
):
"""
conditions:
(1) ge(i)-ge(j) is too far to implement the fast CR(i>j) or CR(j>i)
"""
result = {}
for (i, j), data in self.graph.qubit_links.items():
flag = False
label = data["label"]
log = []
f_i = self.get_value(i, "frequency")
f_j = self.get_value(j, "frequency")
f_diff = abs(f_i - f_j)
f_diff_max = max_detuning or self.conditions["max_detuning"]
if f_diff > f_diff_max:
flag = True
log.append(
f"Detuning of {label} ({f_diff:.3f} GHz) is higher than {f_diff_max:.3f} GHz."
)

result[label] = {
"collision": flag,
"log": log,
}

result = dict(sorted(result.items()))
return result

def check_type1a(self): ...

def check_type1b(self): ...

def check_type1c(self): ...

def check_type2a(self): ...

def check_type2b(self): ...

def check_type3a(self): ...

def check_type3b(self): ...

def check_type7(self): ...

def check_type8(self): ...

def check_type9(self): ...

0 comments on commit c8200fa

Please sign in to comment.