QuLab 2.11.15__py3-none-any.whl → 2.12.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
qulab/__init__.py CHANGED
@@ -1,10 +1,11 @@
1
1
  from qlisp import (CR, CX, CZ, SWAP, A, B, BellPhiM, BellPhiP, BellPsiM,
2
2
  BellPsiP, H, S, Sdag, SQiSWAP, T, Tdag, U, Unitary2Angles,
3
- applySeq, draw, fSim, iSWAP, kak_decomposition, kak_vector,
4
- make_immutable, measure, phiminus, phiplus, psiminus,
5
- psiplus, regesterGateMatrix, rfUnitary, seq2mat, sigmaI,
6
- sigmaM, sigmaP, sigmaX, sigmaY, sigmaZ,
7
- synchronize_global_phase)
3
+ applySeq)
4
+ from qlisp import draw as draw_qlisp
5
+ from qlisp import (fSim, iSWAP, kak_decomposition, kak_vector, make_immutable,
6
+ measure, phiminus, phiplus, psiminus, psiplus,
7
+ regesterGateMatrix, rfUnitary, seq2mat, sigmaI, sigmaM,
8
+ sigmaP, sigmaX, sigmaY, sigmaZ, synchronize_global_phase)
8
9
  from qlispc import (COMMAND, FREE, NOTSET, PUSH, READ, SYNC, TRIG, WRITE,
9
10
  compile, get_arch, libraries, mapping_qubits,
10
11
  register_arch)
@@ -30,4 +31,6 @@ from .executor.template import VAR
30
31
  from .executor.utils import debug_analyze
31
32
  from .scan import Scan, get_record, load_record, lookup, lookup_list
32
33
  from .version import __version__
33
- from .visualization import autoplot
34
+ from .visualization import autoplot, plot_mat
35
+ from .visualization.plot_layout import draw as draw_layout
36
+ from .visualization.plot_layout import fill_layout
@@ -2,7 +2,7 @@ import copy
2
2
  import importlib
3
3
  import os
4
4
  import sys
5
- from typing import Any
5
+ from typing import Any, Callable, cast
6
6
 
7
7
  from ..cli.config import get_config_value
8
8
  from .storage import Report, save_item
@@ -66,7 +66,7 @@ def _export_config() -> dict:
66
66
  return parameters
67
67
 
68
68
 
69
- def _clear_config():
69
+ def _clear_config() -> None:
70
70
  import pickle
71
71
 
72
72
  try:
@@ -142,13 +142,13 @@ def set_config_api(query_method,
142
142
 
143
143
 
144
144
  def _init():
145
- code = get_config_value("code", str, default=None)
145
+ code = cast(str, get_config_value("code", str, default=None))
146
146
  if code is not None:
147
147
  code = os.path.expanduser(code)
148
148
  if code not in sys.path:
149
149
  sys.path.insert(0, code)
150
150
 
151
- api = get_config_value('api', str, None)
151
+ api = cast(str, get_config_value('api', str, None))
152
152
  if api is not None:
153
153
  api = importlib.import_module(api)
154
154
  set_config_api(api.query_config, api.update_config, api.delete_config,
@@ -283,29 +283,34 @@ class RegistrySnapshot:
283
283
  class Registry():
284
284
 
285
285
  def __init__(self):
286
- self.api = (query_config, update_config, delete_config, export_config,
287
- clear_config)
286
+ self.api: dict[str, Callable] = {
287
+ 'query': query_config,
288
+ 'update': update_config,
289
+ 'delete': delete_config,
290
+ 'export': export_config,
291
+ 'clear': clear_config, # type: ignore
292
+ }
288
293
 
289
294
  def query(self, key: str) -> Any:
290
- return self.api[0](key)
295
+ return self.api['query'](key)
291
296
 
292
297
  def get(self, key: str) -> Any:
293
298
  return self.query(key)
294
299
 
295
300
  def export(self) -> dict[str, dict[str, Any]]:
296
- return self.api[3]()
301
+ return self.api['export']()
297
302
 
298
303
  def set(self, key: str, value: Any):
299
- return self.api[1]({key: value})
304
+ return self.api['update']({key: value})
300
305
 
301
306
  def delete(self, key: str):
302
- return self.api[2](key)
307
+ return self.api['delete'](key)
303
308
 
304
- def clear(self):
305
- return self.api[4]()
309
+ def clear(self) -> None:
310
+ return self.api['clear']()
306
311
 
307
312
  def update(self, parameters: dict[str, Any]):
308
- return self.api[1](parameters)
313
+ return self.api['update'](parameters)
309
314
 
310
315
  def snapshot(self) -> RegistrySnapshot:
311
316
  return RegistrySnapshot(self.export())
@@ -6,7 +6,7 @@ import pickle
6
6
  import re
7
7
  import string
8
8
  import textwrap
9
- from typing import Any
9
+ from typing import Any, cast
10
10
 
11
11
  _notset = object()
12
12
 
@@ -14,8 +14,7 @@ _notset = object()
14
14
  def VAR(name: str, /, *, default: Any = _notset) -> Any:
15
15
  if default is _notset:
16
16
  raise TemplateKeyError(
17
- f"The variable '{name}' is not provided in mapping."
18
- )
17
+ f"The variable '{name}' is not provided in mapping.")
19
18
  else:
20
19
  return default
21
20
 
@@ -60,20 +59,22 @@ class TemplateVarExtractor(ast.NodeVisitor):
60
59
  def visit_Constant(self, node):
61
60
  if isinstance(node.value, str):
62
61
  self._process_string(node.value, node.lineno, node.col_offset,
63
- node.end_lineno, node.end_col_offset)
62
+ cast(int, node.end_lineno),
63
+ cast(int, node.end_col_offset))
64
64
 
65
65
  def visit_JoinedStr(self, node):
66
66
  for value in node.values:
67
67
  if isinstance(value, ast.Constant) and isinstance(
68
68
  value.value, str):
69
69
  self._process_string(value.value, value.lineno,
70
- value.col_offset, value.end_lineno,
71
- value.end_col_offset)
70
+ value.col_offset,
71
+ cast(int, value.end_lineno),
72
+ cast(int, value.end_col_offset))
72
73
  self.generic_visit(node)
73
74
 
74
75
  def visit_FunctionDef(self, node):
75
76
  if node.name == 'VAR':
76
- self.var_func_def = (node.lineno, node.end_lineno)
77
+ self.var_func_def = (node.lineno, cast(int, node.end_lineno))
77
78
  self.generic_visit(node)
78
79
 
79
80
  def visit_Call(self, node):
@@ -81,15 +82,11 @@ class TemplateVarExtractor(ast.NodeVisitor):
81
82
  arg = node.args[0]
82
83
  if not isinstance(arg, ast.Constant) or not isinstance(
83
84
  arg.value, str):
84
- raise SyntaxError(
85
- f"Argument of VAR function must be a string."
86
- f" {self.fname}:{node.lineno}"
87
- )
85
+ raise SyntaxError(f"Argument of VAR function must be a string."
86
+ f" {self.fname}:{node.lineno}")
88
87
  if len(node.args) != 1:
89
- raise SyntaxError(
90
- f"VAR function only accept one argument."
91
- f" {self.fname}:{node.lineno}"
92
- )
88
+ raise SyntaxError(f"VAR function only accept one argument."
89
+ f" {self.fname}:{node.lineno}")
93
90
  default = _notset
94
91
  for k in node.keywords:
95
92
  if k.arg == 'default':
@@ -105,8 +102,7 @@ class TemplateVarExtractor(ast.NodeVisitor):
105
102
  else:
106
103
  raise SyntaxError(
107
104
  f"VAR function only accept keyword argument 'default'."
108
- f" {self.fname}:{node.lineno}"
109
- )
105
+ f" {self.fname}:{node.lineno}")
110
106
 
111
107
  if default is _notset:
112
108
  # new_node = ast.Subscript(value=ast.Name(id="__VAR",
@@ -116,8 +112,7 @@ class TemplateVarExtractor(ast.NodeVisitor):
116
112
  if arg.value not in self.mapping:
117
113
  raise TemplateKeyError(
118
114
  f"The variable '{arg.value}' is not provided in mapping."
119
- f" {self.fname}:{node.lineno}"
120
- )
115
+ f" {self.fname}:{node.lineno}")
121
116
  self.replacements[(node.lineno, node.end_lineno,
122
117
  node.col_offset,
123
118
  node.end_col_offset)] = ('VAR', arg.value,
@@ -174,7 +169,7 @@ class TemplateVarExtractor(ast.NodeVisitor):
174
169
 
175
170
 
176
171
  def inject_mapping(source: str, mapping: dict[str, Any],
177
- fname: str) -> list[tuple[str, int]]:
172
+ fname: str) -> tuple[str, str]:
178
173
  hash_str, mapping_code = encode_mapping(mapping)
179
174
 
180
175
  tree = ast.parse(source)
qulab/monitor/dataset.py CHANGED
@@ -10,7 +10,7 @@ contain multiple named columns of numerical data.
10
10
  """
11
11
 
12
12
  from collections import defaultdict, deque
13
- from typing import Union, Sequence
13
+ from typing import Union, Sequence, cast
14
14
 
15
15
  from .config import ROLL_BUFFER_SIZE
16
16
 
@@ -126,10 +126,10 @@ class Dataset:
126
126
  return
127
127
  try:
128
128
  # Test if dataframe is a list of lists (trace data)
129
- iter(dataframe[0])
130
- self._append_traces(dataframe)
129
+ iter(dataframe[0]) # type: ignore
130
+ self._append_traces(cast(Sequence[Sequence[Number]], dataframe))
131
131
  except TypeError:
132
- self._append_points(dataframe)
132
+ self._append_points(cast(Sequence[Number], dataframe))
133
133
 
134
134
  def roll(self) -> None:
135
135
  """
@@ -70,14 +70,16 @@ class EventQueue:
70
70
  if not isinstance(event, tuple):
71
71
  raise ValueError("Queue event must be a tuple")
72
72
  if len(event) != 2:
73
- raise ValueError("Queue event must contain exactly two elements (command, data)")
74
-
73
+ raise ValueError(
74
+ "Queue event must contain exactly two elements (command, data)"
75
+ )
76
+
75
77
  command, data = event
76
78
  if not isinstance(command, str):
77
79
  raise ValueError("Command must be a string")
78
-
80
+
79
81
  self.handle(command, data)
80
-
82
+
81
83
  except (ValueError, AssertionError) as error:
82
84
  warnings.warn(f"Invalid event format: {error}")
83
85
  continue
@@ -103,25 +105,25 @@ class EventQueue:
103
105
  case "PN": # Set point data column names
104
106
  self.point_dataset.set_column_names(data)
105
107
  self.toolbar.refresh_comb()
106
-
108
+
107
109
  case "TN": # Set trace data column names
108
110
  self.trace_dataset.set_column_names(data)
109
111
  self.toolbar.refresh_comb()
110
-
112
+
111
113
  case "PD": # Append point data
112
114
  self.point_dataset.append(data)
113
-
115
+
114
116
  case "TD": # Append trace data
115
117
  self.trace_dataset.append(data)
116
-
118
+
117
119
  case "ROLL": # Create new data frame
118
120
  self.point_dataset.roll()
119
-
121
+
120
122
  case "PXY": # Update point plot configuration
121
123
  self.toolbar.set_point_text(data)
122
-
124
+
123
125
  case "TXY": # Update trace plot configuration
124
126
  self.toolbar.set_trace_text(data)
125
-
127
+
126
128
  case _:
127
129
  warnings.warn(f"Unknown command: {command}")
@@ -7,15 +7,16 @@ interactive features like axis linking and data transformation.
7
7
  """
8
8
 
9
9
  from multiprocessing import Queue
10
- from typing import Literal
10
+ from typing import Literal, cast
11
11
 
12
- from .config import TRANSFORMS, ROLL_INDICES, STYLE
12
+ from .config import ROLL_INDICES, STYLE, TRANSFORMS
13
13
  from .dataset import Dataset
14
14
  from .event_queue import EventQueue
15
15
  from .ploter import PlotWidget
16
- from .qt_compat import (BottomDockWidgetArea, QtCore, QtWidgets,
17
- ScrollBarAlwaysOff, ScrollBarAlwaysOn,
18
- TopDockWidgetArea)
16
+ from .qt_compat import QtCore # type: ignore
17
+ from .qt_compat import QtWidgets # type: ignore
18
+ from .qt_compat import (BottomDockWidgetArea, ScrollBarAlwaysOff,
19
+ ScrollBarAlwaysOn, TopDockWidgetArea)
19
20
  from .toolbar import ToolBar
20
21
 
21
22
 
@@ -50,13 +51,13 @@ class MainWindow(QtWidgets.QMainWindow):
50
51
  self.plot_minimum_height = plot_minimum_height
51
52
  self.plot_widgets: list[PlotWidget] = []
52
53
  self.plot_colors = plot_colors
53
-
54
+
54
55
  # Initialize components
55
56
  self.toolbar = ToolBar()
56
57
  self.trace_data_box = Dataset()
57
58
  self.point_data_box = Dataset()
58
59
  self.queue = EventQueue(queue, self.toolbar, self.point_data_box,
59
- self.trace_data_box)
60
+ self.trace_data_box)
60
61
 
61
62
  self.init_ui()
62
63
 
@@ -70,7 +71,7 @@ class MainWindow(QtWidgets.QMainWindow):
70
71
  self.setStyleSheet(STYLE)
71
72
  self.setMinimumHeight(500)
72
73
  self.setMinimumWidth(700)
73
-
74
+
74
75
  # Create scroll area
75
76
  self.scroll = QtWidgets.QScrollArea()
76
77
  self.widget = QtWidgets.QWidget()
@@ -100,7 +101,7 @@ class MainWindow(QtWidgets.QMainWindow):
100
101
  @property
101
102
  def mode(self) -> Literal["P", "T"]:
102
103
  """Current plotting mode (Points or Traces)."""
103
- return self.toolbar.mode
104
+ return cast(Literal["P", "T"], self.toolbar.mode)
104
105
 
105
106
  @property
106
107
  def dataset(self) -> Dataset:
@@ -119,7 +120,7 @@ class MainWindow(QtWidgets.QMainWindow):
119
120
  plot_count = len(self.plot_widgets)
120
121
  plot_widget = PlotWidget(self.plot_minimum_height, self.plot_colors)
121
122
  self.plot_widgets.append(plot_widget)
122
-
123
+
123
124
  grid_row = plot_count // self.num_columns
124
125
  grid_col = plot_count % self.num_columns
125
126
  self.layout.addWidget(plot_widget, grid_row + 1, grid_col)
@@ -175,9 +176,9 @@ class MainWindow(QtWidgets.QMainWindow):
175
176
 
176
177
  def do_link(self):
177
178
  """Link plots that share the same X or Y axis."""
178
- same_x_axis = {}
179
+ same_x_axis: dict[str, list[int]] = {}
179
180
  xy_pairs = self.toolbar.xypairs
180
-
181
+
181
182
  # Group plots by X axis
182
183
  for idx, (x_name, y_name) in enumerate(xy_pairs):
183
184
  if x_name not in same_x_axis:
@@ -250,19 +251,20 @@ class MainWindow(QtWidgets.QMainWindow):
250
251
  if self.dataset.dirty or self.toolbar.xyfm_dirty or needs_rescale:
251
252
  self.dataset.dirty = False
252
253
  self.toolbar.xyfm_dirty = False
253
-
254
+
254
255
  # Update plot data
255
256
  for plot_widget in self.plot_widgets:
256
- x_transform = TRANSFORMS[self.toolbar.fx]
257
- y_transform = TRANSFORMS[self.toolbar.fy]
258
-
257
+ x_transform = TRANSFORMS[cast(str, self.toolbar.fx)]
258
+ y_transform = TRANSFORMS[cast(str, self.toolbar.fy)]
259
+
259
260
  for idx in ROLL_INDICES:
260
- x_data, y_data = self.dataset.get_data(idx, plot_widget.x_name, plot_widget.y_name)
261
+ x_data, y_data = self.dataset.get_data(
262
+ idx, plot_widget.x_name, plot_widget.y_name)
261
263
  data_length = min(len(x_data), len(y_data))
262
264
  x_data = x_transform(x_data[:data_length], 0)
263
265
  y_data = y_transform(y_data[:data_length], 0)
264
266
  plot_widget.set_data(idx, x_data, y_data)
265
-
267
+
266
268
  plot_widget.update()
267
269
  if needs_rescale:
268
270
  plot_widget.auto_range()
qulab/monitor/monitor.py CHANGED
@@ -11,6 +11,8 @@ Classes:
11
11
 
12
12
  import multiprocessing as mp
13
13
  import sys
14
+ from typing import cast
15
+
14
16
  import zmq
15
17
 
16
18
  # try:
@@ -33,10 +35,11 @@ def main(data_queue: mp.Queue,
33
35
  plot_colors: List of RGB color tuples for plot lines
34
36
  """
35
37
  from .mainwindow import MainWindow
36
- from .qt_compat import QtWidgets
38
+ from .qt_compat import QtWidgets # type: ignore
37
39
 
38
40
  app = QtWidgets.QApplication(sys.argv)
39
- main_window = MainWindow(data_queue, num_columns, minimum_height, plot_colors)
41
+ main_window = MainWindow(data_queue, num_columns, minimum_height,
42
+ plot_colors)
40
43
  sys.exit(app.exec())
41
44
 
42
45
 
@@ -71,8 +74,8 @@ class MonitorUI:
71
74
  return
72
75
  self.queue = mp.Queue(20)
73
76
  self.process = mp.Process(target=main,
74
- args=(self.queue, self.number_of_columns,
75
- self.minimum_height, self.colors))
77
+ args=(self.queue, self.number_of_columns,
78
+ self.minimum_height, self.colors))
76
79
  self.process.start()
77
80
 
78
81
  def _put(self, message: tuple) -> None:
@@ -119,18 +122,19 @@ class MonitorUI:
119
122
 
120
123
  def is_alive(self) -> bool:
121
124
  """Check if the monitoring process is running."""
122
- return self.process.is_alive()
125
+ return self.process is not None and self.process.is_alive()
123
126
 
124
127
  def __del__(self):
125
128
  """Clean up resources when the Monitor object is deleted."""
126
129
  try:
127
- self.process.kill()
130
+ cast(mp.Process, self.process).kill()
128
131
  except:
129
132
  pass
130
133
 
131
134
 
132
135
  class Monitor:
133
- def __init__(self, address: str="127.0.0.1", port: int=5555):
136
+
137
+ def __init__(self, address: str = "127.0.0.1", port: int = 5555):
134
138
  self.context = zmq.Context()
135
139
  self.socket = self.context.socket(zmq.REQ)
136
140
  self.socket.connect(f"tcp://{address}:{port}")
@@ -174,7 +178,11 @@ class Monitor:
174
178
 
175
179
 
176
180
  class MonitorServer:
177
- def __init__(self, address: str="*", port: int=5555, number_of_columns: int = 4,
181
+
182
+ def __init__(self,
183
+ address: str = "*",
184
+ port: int = 5555,
185
+ number_of_columns: int = 4,
178
186
  minimum_height: int = 400,
179
187
  colors: list[tuple[int, int, int]] = []):
180
188
  self.address = address
@@ -189,8 +197,9 @@ class MonitorServer:
189
197
  def _run(self):
190
198
  try:
191
199
  # Create Monitor instance in the child process
192
- self.monitor = MonitorUI(self.number_of_columns, self.minimum_height, self.colors)
193
-
200
+ self.monitor = MonitorUI(self.number_of_columns,
201
+ self.minimum_height, self.colors)
202
+
194
203
  # Create ZMQ context and socket in the child process
195
204
  self.context = zmq.Context()
196
205
  self.socket = self.context.socket(zmq.REP)
@@ -253,12 +262,13 @@ def get_monitor(auto_open: bool = True) -> MonitorUI:
253
262
  if auto_open and (_monitor is None or not _monitor.is_alive()):
254
263
  _monitor = MonitorUI()
255
264
 
256
- return _monitor
265
+ return cast(MonitorUI, _monitor)
257
266
 
258
267
 
259
268
  if __name__ == "__main__":
260
269
  # Example usage and testing code
261
270
  import time
271
+
262
272
  import numpy as np
263
273
 
264
274
  # Example 1: Using Monitor directly
@@ -290,11 +300,11 @@ if __name__ == "__main__":
290
300
  def run_client():
291
301
  client = Monitor("127.0.0.1", 5555)
292
302
  time.sleep(1) # Wait for server to start
293
-
303
+
294
304
  client.set_column_names("index", "H", "S")
295
305
  client.set_plots("(index,H);(index,S)")
296
306
  client.roll()
297
-
307
+
298
308
  for i in range(100):
299
309
  client.add_point(i, np.random.randn(), np.sin(i / 20))
300
310
  time.sleep(0.2)
qulab/monitor/ploter.py CHANGED
@@ -6,18 +6,18 @@ data visualization. It includes features like auto-ranging, mouse interaction
6
6
  for data selection, and clipboard integration.
7
7
  """
8
8
 
9
- from .config import (COLOR_SELECTED, COLOR_UNSELECTED, SYMBOL_SIZES,
10
- DEFAULT_COLORS, ROLL_INDICES, LINE_WIDTHS)
11
- from .qt_compat import QtWidgets
9
+ from .config import (COLOR_SELECTED, COLOR_UNSELECTED, DEFAULT_COLORS,
10
+ LINE_WIDTHS, ROLL_INDICES, SYMBOL_SIZES)
11
+ from .qt_compat import QtWidgets # type: ignore
12
12
 
13
13
  # the plotting widget
14
14
  try:
15
- import pyqtgraph as pg
15
+ import pyqtgraph as pg # type: ignore
16
16
  except ImportError:
17
17
  raise ImportError("Please install pyqtgraph first")
18
18
 
19
19
  try:
20
- import pyperclip as pc
20
+ import pyperclip as pc # type: ignore
21
21
  HAS_CLIPBOARD = True
22
22
  except ImportError:
23
23
  HAS_CLIPBOARD = False
@@ -122,14 +122,16 @@ class PlotWidget(pg.PlotWidget):
122
122
  def mousePressEvent(self, event):
123
123
  """Handle mouse press events for data selection."""
124
124
  if event.button() == 4 and HAS_CLIPBOARD:
125
- self.clip_pos_start = self.plotItem.vb.mapSceneToView(event.pos()).x()
125
+ self.clip_pos_start = self.plotItem.vb.mapSceneToView(
126
+ event.pos()).x()
126
127
  else:
127
128
  super().mousePressEvent(event)
128
129
 
129
130
  def mouseReleaseEvent(self, event):
130
131
  """Handle mouse release events and copy selected data to clipboard."""
131
132
  if event.button() == 4 and HAS_CLIPBOARD:
132
- self.clip_pos_end = self.plotItem.vb.mapSceneToView(event.pos()).x()
133
+ self.clip_pos_end = self.plotItem.vb.mapSceneToView(
134
+ event.pos()).x()
133
135
  if self.range_select:
134
136
  pc.copy(f"{self.clip_pos_start},{self.clip_pos_end}")
135
137
  else:
@@ -13,7 +13,8 @@ The module exports the following constants:
13
13
  - TopDockWidgetArea: Top dock widget area constant
14
14
  """
15
15
 
16
- from matplotlib.backends.qt_compat import QT_API, QtCore, QtWidgets
16
+ from matplotlib.backends.qt_compat import (QT_API, QtCore, # type: ignore
17
+ QtWidgets) # type: ignore
17
18
 
18
19
  # Define Qt constants based on the Qt binding being used
19
20
  if QT_API in ['PySide6', 'PyQt6']:
qulab/monitor/toolbar.py CHANGED
@@ -14,7 +14,7 @@ import re
14
14
  from typing import Callable, List, Tuple
15
15
 
16
16
  from .config import STYLE, TRANSFORM_NAMES
17
- from .qt_compat import AlignRight, QtWidgets
17
+ from .qt_compat import AlignRight, QtWidgets # type: ignore
18
18
 
19
19
 
20
20
  def matched_xy_pairs(patterns: str, column_names: list[str]) -> list[tuple[str, str]]:
@@ -28,11 +28,11 @@ def matched_xy_pairs(patterns: str, column_names: list[str]) -> list[tuple[str,
28
28
  Returns:
29
29
  List of matched (x, y) column name pairs
30
30
  """
31
- patterns = patterns.replace(" ", "").split(";")
31
+ pattern_list = patterns.replace(" ", "").split(";")
32
32
  pairs = []
33
33
  for x, y in itertools.product(column_names, repeat=2):
34
34
  test = f"{x},{y}"
35
- for pattern in patterns:
35
+ for pattern in pattern_list:
36
36
  r = re.match(pattern, test)
37
37
  if r and r.group(0) == test:
38
38
  pairs.append((x, y))
qulab/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "2.11.15"
1
+ __version__ = "2.12.0"
@@ -114,7 +114,7 @@ class MyLogFormatter(EngFormatter):
114
114
  return super().format_eng(10.0**x)
115
115
 
116
116
 
117
- def imshow_logx(x, y, z, x_unit=None, ax=None, **kwargs):
117
+ def imshow_logx(x, y, z, x_unit: str | None = None, ax=None, **kwargs):
118
118
  if ax is None:
119
119
  ax = plt.gca()
120
120
 
@@ -127,13 +127,13 @@ def imshow_logx(x, y, z, x_unit=None, ax=None, **kwargs):
127
127
  img = ax.imshow(z, extent=extent, **kwargs)
128
128
 
129
129
  ax.set_xticks(major_ticks, minor=False)
130
- ax.xaxis.set_major_formatter(MyLogFormatter(x_unit))
130
+ ax.xaxis.set_major_formatter(MyLogFormatter(x_unit)) # type: ignore
131
131
  ax.set_xticks(minor_ticks, minor=True)
132
132
 
133
133
  return img
134
134
 
135
135
 
136
- def imshow_logy(x, y, z, y_unit=None, ax=None, **kwargs):
136
+ def imshow_logy(x, y, z, y_unit: str | None = None, ax=None, **kwargs):
137
137
  if ax is None:
138
138
  ax = plt.gca()
139
139
 
@@ -146,13 +146,13 @@ def imshow_logy(x, y, z, y_unit=None, ax=None, **kwargs):
146
146
  img = ax.imshow(z, extent=extent, **kwargs)
147
147
 
148
148
  ax.set_yticks(major_ticks, minor=False)
149
- ax.yaxis.set_major_formatter(MyLogFormatter(y_unit))
149
+ ax.yaxis.set_major_formatter(MyLogFormatter(y_unit)) # type: ignore
150
150
  ax.set_yticks(minor_ticks, minor=True)
151
151
 
152
152
  return img
153
153
 
154
154
 
155
- def imshow_loglog(x, y, z, x_unit=None, y_unit=None, ax=None, **kwargs):
155
+ def imshow_loglog(x, y, z, x_unit: str | None = None, y_unit: str | None = None, ax=None, **kwargs):
156
156
  if ax is None:
157
157
  ax = plt.gca()
158
158
 
@@ -166,11 +166,11 @@ def imshow_loglog(x, y, z, x_unit=None, y_unit=None, ax=None, **kwargs):
166
166
  img = ax.imshow(z, extent=extent, **kwargs)
167
167
 
168
168
  ax.set_xticks(x_major_ticks, minor=False)
169
- ax.xaxis.set_major_formatter(MyLogFormatter(x_unit))
169
+ ax.xaxis.set_major_formatter(MyLogFormatter(x_unit)) # type: ignore
170
170
  ax.set_xticks(x_minor_ticks, minor=True)
171
171
 
172
172
  ax.set_yticks(y_major_ticks, minor=False)
173
- ax.yaxis.set_major_formatter(MyLogFormatter(y_unit))
173
+ ax.yaxis.set_major_formatter(MyLogFormatter(y_unit)) # type: ignore
174
174
  ax.set_yticks(y_minor_ticks, minor=True)
175
175
 
176
176
  return img
@@ -186,9 +186,9 @@ def plot_lines(x,
186
186
  y_unit,
187
187
  z_unit,
188
188
  ax,
189
- xscale='linear',
190
- yscale='linear',
191
- zscale='linear',
189
+ xscale: str = 'linear',
190
+ yscale: str = 'linear',
191
+ zscale: str = 'linear',
192
192
  index=None,
193
193
  **kwds):
194
194
  z = np.asarray(z)
@@ -230,9 +230,9 @@ def plot_img(x,
230
230
  z_unit,
231
231
  fig,
232
232
  ax,
233
- xscale='linear',
234
- yscale='linear',
235
- zscale='linear',
233
+ xscale: str = 'linear',
234
+ yscale: str = 'linear',
235
+ zscale: str = 'linear',
236
236
  resolution=None,
237
237
  **kwds):
238
238
  kwds.setdefault('origin', 'lower')
@@ -325,9 +325,9 @@ def plot_scatter(x,
325
325
  z_unit,
326
326
  fig,
327
327
  ax,
328
- xscale='linear',
329
- yscale='linear',
330
- zscale='linear',
328
+ xscale: str = 'linear',
329
+ yscale: str = 'linear',
330
+ zscale: str = 'linear',
331
331
  **kwds):
332
332
  if np.any(np.iscomplex(z)):
333
333
  s = np.abs(z)
@@ -356,9 +356,9 @@ def autoplot(x,
356
356
  fig=None,
357
357
  ax=None,
358
358
  index=None,
359
- xscale='auto',
360
- yscale='auto',
361
- zscale='auto',
359
+ xscale: str = 'auto',
360
+ yscale: str = 'auto',
361
+ zscale: str = 'auto',
362
362
  max_lines=3,
363
363
  scatter_lim=1000,
364
364
  resolution=None,
@@ -33,13 +33,13 @@ def make_path(fx, fy, dfx, dfy, t):
33
33
 
34
34
 
35
35
  def plot_square(ax,
36
- x=0,
37
- y=0,
38
- width=1,
39
- hight=1,
36
+ x=0.0,
37
+ y=0.0,
38
+ width=1.0,
39
+ hight=1.0,
40
40
  radius=0.2,
41
41
  ls='-',
42
- lw=2,
42
+ lw=2.0,
43
43
  fc='none',
44
44
  ec='black'):
45
45
  r = radius
@@ -1,5 +1,6 @@
1
1
  import functools
2
2
  import operator
3
+ from typing import cast
3
4
 
4
5
  import matplotlib.pyplot as plt
5
6
  import numpy as np
@@ -53,6 +54,85 @@ def complete_layout(layout):
53
54
  return layout
54
55
 
55
56
 
57
+ def read_xlsx_to_dict(filepath: str,
58
+ key_col: int = 0,
59
+ value_col: int = 4,
60
+ sheet_name: str = 0) -> dict:
61
+ """
62
+ 读取 .xlsx 文件,将第 key_col 列和第 value_col 列的值分别作为 key、value,返回一个字典。
63
+
64
+ :param filepath: Excel 文件路径
65
+ :param key_col: 用作字典键的列索引(0 表示第一列)
66
+ :param value_col: 用作字典值的列索引(0 表示第一列)
67
+ :param sheet_name: 要读取的 sheet 名称或索引,默认第一个 sheet
68
+ :return: { key: value, ... } 形式的字典
69
+ """
70
+ import pandas as pd
71
+
72
+ # 读取整个表格
73
+ # df = pd.read_excel(filepath, sheet_name=sheet_name, header=None)
74
+ df = pd.read_excel(filepath, sheet_name=sheet_name)
75
+
76
+ # 提取指定列,生成键值对并返回字典
77
+ keys = df.iloc[:, key_col]
78
+ values = df.iloc[:, value_col]
79
+ return dict(zip(keys, values))
80
+
81
+
82
+ def load_layout_from_xlsx(qubit_info, coupler_info, pad_info):
83
+ """
84
+ 从 .xlsx 文件中加载布局信息。
85
+
86
+ :param qubit_info: 量子比特信息文件路径
87
+ :param coupler_info: 耦合器信息文件路径
88
+ :param pad_info: 垫片信息文件路径
89
+ :return: 布局信息
90
+ """
91
+ import pandas as pd
92
+
93
+ _qubits = {}
94
+
95
+ qubit_pad = read_xlsx_to_dict(qubit_info, value_col=4)
96
+ coupler_pad = read_xlsx_to_dict(coupler_info, value_col=3)
97
+ pads = {
98
+ v: k
99
+ for k, v in qubit_pad.items()
100
+ } | {
101
+ v: k
102
+ for k, v in coupler_pad.items()
103
+ }
104
+ pad_info = read_xlsx_to_dict(pad_info, value_col=2)
105
+
106
+ lyt = {'qubits': {}, 'couplers': {}, 'feedlines': {}}
107
+
108
+ for pad, script in pad_info.items():
109
+ info = eval(script)
110
+ match info['style']:
111
+ case 'Q':
112
+ qubit = pads[pad]
113
+ lyt['qubits'][qubit] = {'pos': info['qb'], 'pad': pad}
114
+ _qubits[info['qb']] = qubit
115
+ case 'C':
116
+ coupler = pads[pad]
117
+ if 'cpl' in info:
118
+ pos = tuple(info['cpl'])
119
+ else:
120
+ pos = tuple(info['qb'])
121
+ lyt['couplers'][coupler] = {'qubits': pos, 'pad': pad}
122
+ case 'TL':
123
+ l = info['tl']
124
+ if f"T{l}" in lyt['feedlines']:
125
+ lyt['feedlines'][f"T{l}"]['pads'].append(pad)
126
+ else:
127
+ lyt['feedlines'][f"T{l}"] = {'pads': [pad]}
128
+
129
+ for coupler in lyt['couplers']:
130
+ qubits = lyt['couplers'][coupler]['qubits']
131
+ lyt['couplers'][coupler]['qubits'] = [_qubits[p] for p in qubits]
132
+
133
+ return complete_layout(lyt)
134
+
135
+
56
136
  def get_shared_coupler(layout, q1, q2):
57
137
  for c in layout['qubits'][q1]['couplers']:
58
138
  if q2 in layout['couplers'][c]['qubits']:
@@ -245,6 +325,10 @@ def _draw_coupler(ax, coupler, layout, q1, q2, pos1, pos2):
245
325
 
246
326
  text_rotation = 180 * np.arctan2(pos2[1] - pos1[1],
247
327
  pos2[0] - pos1[0]) / np.pi
328
+ if text_rotation > 90:
329
+ text_rotation -= 180
330
+ elif text_rotation < -90:
331
+ text_rotation += 180
248
332
 
249
333
  path = circle_link_path(pos1, pos2, r1, r2, width)
250
334
  plot_range(ax,
@@ -452,15 +536,19 @@ def fill_layout(layout,
452
536
  coupler_cmap = plt.get_cmap(coupler_cmap)
453
537
 
454
538
  if qubit_norm is None:
455
- qubit_norm = get_norm(params,
456
- layout['qubits'].keys(),
457
- vmin=qubit_vmin,
458
- vmax=qubit_vmax)
539
+ qubit_norm = cast(
540
+ Normalize,
541
+ get_norm(params,
542
+ layout['qubits'].keys(),
543
+ vmin=qubit_vmin,
544
+ vmax=qubit_vmax))
459
545
  if coupler_norm is None:
460
- coupler_norm = get_norm(params,
461
- layout['couplers'].keys(),
462
- vmin=coupler_vmin,
463
- vmax=coupler_vmax)
546
+ coupler_norm = cast(
547
+ Normalize,
548
+ get_norm(params,
549
+ layout['couplers'].keys(),
550
+ vmin=coupler_vmin,
551
+ vmax=coupler_vmax))
464
552
  layout['__colorbar__'] = {
465
553
  'coupler': {
466
554
  'cmap': coupler_cmap,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: QuLab
3
- Version: 2.11.15
3
+ Version: 2.12.0
4
4
  Summary: contral instruments and manage data
5
5
  Author-email: feihoo87 <feihoo87@gmail.com>
6
6
  Maintainer-email: feihoo87 <feihoo87@gmail.com>
@@ -1,8 +1,8 @@
1
- qulab/__init__.py,sha256=hmf6R3jiM5NtJY1mSptogYnH5DMTR2dTzlXykqLxQzg,2027
1
+ qulab/__init__.py,sha256=ijh3NWuSCU_KWRrPfddX4vfPcYPmvKs64vSTl9qd-SU,2178
2
2
  qulab/__main__.py,sha256=fjaRSL_uUjNIzBGNgjlGswb9TJ2VD5qnkZHW3hItrD4,68
3
3
  qulab/typing.py,sha256=vg62sGqxuD9CI5677ejlzAmf2fVdAESZCQjAE_xSxPg,69
4
4
  qulab/utils.py,sha256=B_8QdY9OMY7WU-F200v93BDJXJpQcKAHihnOXeEvv_w,3966
5
- qulab/version.py,sha256=Xwyp7A0xHh2GDh3MUxhzaH2tfp0EQ4tK8L1vQV9D1ow,23
5
+ qulab/version.py,sha256=9qeMwbCMEAjH-vk_63Tak43katUwSOLexvq8N_IKi8c,22
6
6
  qulab/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  qulab/cli/commands.py,sha256=ywKmwbGNBCVASx8OsgrRttaLz9ogwY38ZdKj7I-tdJ4,813
8
8
  qulab/cli/config.py,sha256=Z-lePcDMeeQmwFpdfQHqZuaXTPhWUHN5KrlSqrya73Y,6324
@@ -11,21 +11,21 @@ qulab/executor/__init__.py,sha256=LosPzOMaljSZY1thy_Fxtbrgq7uubJszMABEB7oM7tU,10
11
11
  qulab/executor/analyze.py,sha256=VYTfiOYJe5gDbtGA4wOkrQhhu55iIrRVByx-gBLUUm4,5672
12
12
  qulab/executor/cli.py,sha256=fTB7V8xNCvpU8EMa__ysBvRCzruP-QFbXOyLpeTPx0E,25711
13
13
  qulab/executor/load.py,sha256=eMlzOrrn8GpbP3J3uY5JJ8iO7tL5B1DWP1z2qiGyNhU,20385
14
- qulab/executor/registry.py,sha256=CPY5PNTbE4Rw13Ucsq1wBk3fgvoSB3_J-iyRFdNNl0g,9044
14
+ qulab/executor/registry.py,sha256=yWO6nJ1nMVve_HF5xzSd7QmZEIjoGvD_Txg73_f30gE,9279
15
15
  qulab/executor/schedule.py,sha256=cDFM_tIMvxHLxb-tBA6fEPF9u-4LcgnQDxbaquZIQKc,21727
16
16
  qulab/executor/storage.py,sha256=8K73KGLAVgchJdtd4rKHXkr1CQOJORWH-Gi57w8IYsw,21081
17
- qulab/executor/template.py,sha256=Fxou5lglaCnEhJTKDotFaRrWT4csAZIgmHz9EFbPnYs,10811
17
+ qulab/executor/template.py,sha256=XyZKt2b0J-gSEA9mHoWmA7ADLWrUwOAL5oxYkX-UNg8,10837
18
18
  qulab/executor/utils.py,sha256=YSUEYjyYTJTjiJmd-l5GTEnSyD6bqEdNoWhivEAfGZA,7112
19
19
  qulab/monitor/__init__.py,sha256=FNYMIUr8ycRM4XmsAGpRK-A-SQsJZlcRWghXcGTgAi0,44
20
20
  qulab/monitor/__main__.py,sha256=Txy4d7vgVYUWwiHxX3uwtsi1ewde-MaiSC_K0ePoPiM,1372
21
21
  qulab/monitor/config.py,sha256=lMMRHy6wnVgMHiRMQqev5lj8mbF2g29TLrK82dwp_2k,1950
22
- qulab/monitor/dataset.py,sha256=9ctuod8SE0yRfWABC2dyosDHwjr0higR4lTP4n4usJk,6152
23
- qulab/monitor/event_queue.py,sha256=rfD9qC8miT96myBcfERJfzi7-wotzEiq2ld-4e277tk,4615
24
- qulab/monitor/mainwindow.py,sha256=c4anPDroq_ik8wbc300bgSmrnM9mcz2NxM-IaJKPkao,9969
25
- qulab/monitor/monitor.py,sha256=RwmjKJZVTyfDs_bYigh576zwUG_crgxKhbly2rWFFbE,9604
26
- qulab/monitor/ploter.py,sha256=Fl63M7kz9dcnmDYoXZxcvehd_oZRFpSofXEzRsVEDd8,4799
27
- qulab/monitor/qt_compat.py,sha256=f45fGhmHiHBxaFeaajRs1NnzFHH1qnXzU5Sgv6-s7MI,1648
28
- qulab/monitor/toolbar.py,sha256=T_Snin6N3NpzTkSVMVTfKsbXyyv5Tj6hJEbXvTefEqY,10339
22
+ qulab/monitor/dataset.py,sha256=q9ex0MnC0k-_h1dMFDXZWLERCjM2xPqEc-Obt4AsVZQ,6232
23
+ qulab/monitor/event_queue.py,sha256=Pp9MQ5NtOHBxohHNjv9YfXI1H4T-eDshv3AhjOKp7tE,4501
24
+ qulab/monitor/mainwindow.py,sha256=sGrSDUUZv0ilaw-T-7h4fa-TtmDluMsLNRnUADKgEQE,10043
25
+ qulab/monitor/monitor.py,sha256=3vJeMRKtlDwQHsOz7cZlbNNAQbaCoKRFCgLxri5dtpo,9814
26
+ qulab/monitor/ploter.py,sha256=cPyc1TyHHoKsaCbzIDHMUVwjKqPZLjjvBQb-fm8VKaw,4882
27
+ qulab/monitor/qt_compat.py,sha256=vbblAzlw4pKo-F9yPZt4DOc-KdsSBucud1F8jv00G18,1730
28
+ qulab/monitor/toolbar.py,sha256=A4UtLCnBWyk_JZ16u66dmcoTwY6-Cg4h6QQEEp3Ub-0,10363
29
29
  qulab/scan/__init__.py,sha256=Z88CzGsvr1VdKbF6aYgQv3j-fsEkKmqvpvX4LlzOM98,87
30
30
  qulab/scan/curd.py,sha256=tiJ0oo2DqirK2fpAAAA-6OFAjAuKG5W21nLqfLvTLyQ,6895
31
31
  qulab/scan/models.py,sha256=JFofv-RH0gpS3--M6izXiQg7iGkS9a_em2DwhS5kTTk,17626
@@ -89,16 +89,16 @@ qulab/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
89
89
  qulab/tools/connection_helper.py,sha256=o1t6UpJQPyj4mWY66axgQkgsqUzdkhEGBJo44x9U1iE,946
90
90
  qulab/visualization/__init__.py,sha256=26cuHt3QIJXUb3VaMxlJx3IQTOUVJFKlYBZr7WMP53M,6129
91
91
  qulab/visualization/__main__.py,sha256=9zKK3yZFy0leU40ou6BpRC1Fsetfc1gjjFzIZYIwP6Y,1639
92
- qulab/visualization/_autoplot.py,sha256=31B7pn1pK19abpQDGYBU9a_27cDL87LBpx9vKqlcYAo,14165
93
- qulab/visualization/plot_circ.py,sha256=qZS3auNG4qIyUvC-ijTce17xJwGBvpRvPUqPEx64KUY,8759
94
- qulab/visualization/plot_layout.py,sha256=n9yEmCOl7RJw2l0hXXRVsnLfDOMlK7A52kQZG_Y_NgM,17630
92
+ qulab/visualization/_autoplot.py,sha256=2ECb2USU3_MSwo2d8qzMMO4DK9OU1bA6RYr7FCss1D0,14369
93
+ qulab/visualization/plot_circ.py,sha256=Lxi-Nnik9MBtsNur0iyp9DTdy_IMALRsHBg2hZbPkGc,8769
94
+ qulab/visualization/plot_layout.py,sha256=Q8yOGYQ-oOle1W7cN4am9ABI8T-Xtk3W6BEX5bFSeWg,20464
95
95
  qulab/visualization/plot_seq.py,sha256=UWTS6p9nfX_7B8ehcYo6UnSTUCjkBsNU9jiOeW2calY,6751
96
96
  qulab/visualization/qdat.py,sha256=ZeevBYWkzbww4xZnsjHhw7wRorJCBzbG0iEu-XQB4EA,5735
97
97
  qulab/visualization/rot3d.py,sha256=CuLfG1jw1032HNNZxzC0OWtNzKsbj2e2WRYhMWDgvMQ,1321
98
98
  qulab/visualization/widgets.py,sha256=6KkiTyQ8J-ei70LbPQZAK35wjktY47w2IveOa682ftA,3180
99
- qulab-2.11.15.dist-info/licenses/LICENSE,sha256=PRzIKxZtpQcH7whTG6Egvzl1A0BvnSf30tmR2X2KrpA,1065
100
- qulab-2.11.15.dist-info/METADATA,sha256=wwaZbMBIXvh3I9Gc2xzjmwqK10kmhsFaTHVTs11Ypl0,3945
101
- qulab-2.11.15.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
102
- qulab-2.11.15.dist-info/entry_points.txt,sha256=b0v1GXOwmxY-nCCsPN_rHZZvY9CtTbWqrGj8u1m8yHo,45
103
- qulab-2.11.15.dist-info/top_level.txt,sha256=3T886LbAsbvjonu_TDdmgxKYUn939BVTRPxPl9r4cEg,6
104
- qulab-2.11.15.dist-info/RECORD,,
99
+ qulab-2.12.0.dist-info/licenses/LICENSE,sha256=PRzIKxZtpQcH7whTG6Egvzl1A0BvnSf30tmR2X2KrpA,1065
100
+ qulab-2.12.0.dist-info/METADATA,sha256=ciOrjSwiDMrPsQizRcfSzDphr_TCmgrDFjx3Njx6qto,3944
101
+ qulab-2.12.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
102
+ qulab-2.12.0.dist-info/entry_points.txt,sha256=b0v1GXOwmxY-nCCsPN_rHZZvY9CtTbWqrGj8u1m8yHo,45
103
+ qulab-2.12.0.dist-info/top_level.txt,sha256=3T886LbAsbvjonu_TDdmgxKYUn939BVTRPxPl9r4cEg,6
104
+ qulab-2.12.0.dist-info/RECORD,,