QuLab 2.11.8__py3-none-any.whl → 2.11.9__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/executor/analyze.py +1 -0
- qulab/executor/cli.py +180 -42
- qulab/executor/utils.py +16 -2
- qulab/monitor/__init__.py +1 -1
- qulab/monitor/__main__.py +31 -3
- qulab/monitor/config.py +55 -30
- qulab/monitor/dataset.py +145 -38
- qulab/monitor/event_queue.py +98 -25
- qulab/monitor/mainwindow.py +165 -131
- qulab/monitor/monitor.py +220 -30
- qulab/monitor/ploter.py +98 -73
- qulab/monitor/qt_compat.py +30 -1
- qulab/monitor/toolbar.py +152 -121
- qulab/utils.py +16 -17
- qulab/version.py +1 -1
- {qulab-2.11.8.dist-info → qulab-2.11.9.dist-info}/METADATA +1 -1
- {qulab-2.11.8.dist-info → qulab-2.11.9.dist-info}/RECORD +21 -21
- {qulab-2.11.8.dist-info → qulab-2.11.9.dist-info}/WHEEL +1 -1
- {qulab-2.11.8.dist-info → qulab-2.11.9.dist-info}/entry_points.txt +0 -0
- {qulab-2.11.8.dist-info → qulab-2.11.9.dist-info}/licenses/LICENSE +0 -0
- {qulab-2.11.8.dist-info → qulab-2.11.9.dist-info}/top_level.txt +0 -0
qulab/executor/analyze.py
CHANGED
qulab/executor/cli.py
CHANGED
@@ -25,7 +25,12 @@ from .utils import workflow_template
|
|
25
25
|
|
26
26
|
@logger.catch(reraise=True)
|
27
27
|
def run_script(script_path, extra_paths=None):
|
28
|
-
"""Run a script in a new process, inheriting current PYTHONPATH plus any extra paths.
|
28
|
+
"""Run a script in a new process, inheriting current PYTHONPATH plus any extra paths.
|
29
|
+
|
30
|
+
Args:
|
31
|
+
script_path (str): Path to the script to be executed
|
32
|
+
extra_paths (list, optional): Additional paths to be added to PYTHONPATH
|
33
|
+
"""
|
29
34
|
import subprocess
|
30
35
|
import sys
|
31
36
|
|
@@ -103,8 +108,17 @@ def command_option(command_name):
|
|
103
108
|
help='The path of the code.')
|
104
109
|
@log_options('create')
|
105
110
|
def create(workflow, code):
|
106
|
-
"""
|
107
|
-
|
111
|
+
"""Create a new workflow file.
|
112
|
+
|
113
|
+
This command creates a new workflow file with a template structure. The template includes
|
114
|
+
basic workflow setup and any unreferenced workflows as potential dependencies.
|
115
|
+
|
116
|
+
Args:
|
117
|
+
workflow: Name of the workflow to create
|
118
|
+
code: Directory path where the workflow file will be created. Defaults to current directory.
|
119
|
+
|
120
|
+
Example:
|
121
|
+
$ qulab create my_workflow --code ./workflows
|
108
122
|
"""
|
109
123
|
logger.info(f'[CMD]: create {workflow} --code {code}')
|
110
124
|
if code is None:
|
@@ -133,8 +147,19 @@ def create(workflow, code):
|
|
133
147
|
help='The modlule name of the api.')
|
134
148
|
@log_options('set')
|
135
149
|
def set(key, value, api):
|
136
|
-
"""
|
137
|
-
|
150
|
+
"""Set a configuration value in the registry.
|
151
|
+
|
152
|
+
This command allows you to set key-value pairs in the configuration registry.
|
153
|
+
The value can be any valid Python expression that can be evaluated.
|
154
|
+
|
155
|
+
Args:
|
156
|
+
key: The configuration key to set
|
157
|
+
value: The value to set (can be a Python expression)
|
158
|
+
api: Optional API module for custom config handling
|
159
|
+
|
160
|
+
Example:
|
161
|
+
$ qulab set Q1.channel.Z NS_DDS.CH1
|
162
|
+
$ qulab set gate.R.Q3.params.frequency 5.0e9
|
138
163
|
"""
|
139
164
|
logger.info(f'[CMD]: reg set {key} {value} --api {api}')
|
140
165
|
reg = Registry()
|
@@ -157,8 +182,17 @@ def set(key, value, api):
|
|
157
182
|
help='The modlule name of the api.')
|
158
183
|
@log_options('get')
|
159
184
|
def get(key, api):
|
160
|
-
"""
|
161
|
-
|
185
|
+
"""Get a configuration value from the registry.
|
186
|
+
|
187
|
+
Retrieves and displays the value associated with a given key from the configuration registry.
|
188
|
+
|
189
|
+
Args:
|
190
|
+
key: The configuration key to retrieve
|
191
|
+
api: Optional API module for custom config handling
|
192
|
+
|
193
|
+
Example:
|
194
|
+
$ qulab get Q1.channel
|
195
|
+
$ qulab get gate.R.Q3.params.frequency
|
162
196
|
"""
|
163
197
|
logger.info(f'[CMD]: reg get {key} --api {api}')
|
164
198
|
reg = Registry()
|
@@ -177,8 +211,16 @@ def get(key, api):
|
|
177
211
|
help='The modlule name of the api.')
|
178
212
|
@log_options('delete')
|
179
213
|
def delete(key, api):
|
180
|
-
"""
|
181
|
-
|
214
|
+
"""Delete a configuration key from the registry.
|
215
|
+
|
216
|
+
Removes a key and its associated value from the configuration registry.
|
217
|
+
|
218
|
+
Args:
|
219
|
+
key: The configuration key to delete
|
220
|
+
api: Optional API module for custom config handling
|
221
|
+
|
222
|
+
Example:
|
223
|
+
$ qulab delete gate.R.Q3.params.block_freq
|
182
224
|
"""
|
183
225
|
logger.info(f'[CMD]: reg delete {key} --api {api}')
|
184
226
|
reg = Registry()
|
@@ -201,8 +243,24 @@ def delete(key, api):
|
|
201
243
|
help='The format of the config.')
|
202
244
|
@log_options('export')
|
203
245
|
def export(file, api, format):
|
204
|
-
"""
|
205
|
-
|
246
|
+
"""Export the configuration registry to a file.
|
247
|
+
|
248
|
+
Exports all configuration settings to a file in the specified format.
|
249
|
+
|
250
|
+
Args:
|
251
|
+
file: Path to the output file
|
252
|
+
api: Optional API module for custom config handling
|
253
|
+
format: Output format (pickle, json, or yaml)
|
254
|
+
|
255
|
+
Supported formats:
|
256
|
+
- pickle: Binary format (default)
|
257
|
+
- json: JSON text format
|
258
|
+
- yaml: YAML text format
|
259
|
+
|
260
|
+
Example:
|
261
|
+
$ qulab export config.pkl
|
262
|
+
$ qulab export config.json --format json
|
263
|
+
$ qulab export config.yaml --format yaml
|
206
264
|
"""
|
207
265
|
logger.info(f'[CMD]: reg export {file} --api {api}')
|
208
266
|
reg = Registry()
|
@@ -239,8 +297,27 @@ def export(file, api, format):
|
|
239
297
|
help='The format of the config.')
|
240
298
|
@log_options('load')
|
241
299
|
def load(file, api, format):
|
242
|
-
"""
|
243
|
-
|
300
|
+
"""Load configuration settings from a file.
|
301
|
+
|
302
|
+
Imports configuration settings from a file in the specified format.
|
303
|
+
Existing configuration will be cleared before loading the new settings.
|
304
|
+
|
305
|
+
Args:
|
306
|
+
file: Path to the input file or report index number
|
307
|
+
api: Optional API module for custom config handling
|
308
|
+
format: Input format (pickle, json, yaml, or report)
|
309
|
+
|
310
|
+
Supported formats:
|
311
|
+
- pickle: Binary format (default)
|
312
|
+
- json: JSON text format
|
313
|
+
- yaml: YAML text format
|
314
|
+
- report: Load from a saved report by index
|
315
|
+
|
316
|
+
Example:
|
317
|
+
$ qulab load config.pkl
|
318
|
+
$ qulab load config.json --format json
|
319
|
+
$ qulab load config.yaml --format yaml
|
320
|
+
$ qulab load 123 --format report # Load from report #123
|
244
321
|
"""
|
245
322
|
logger.info(f'[CMD]: reg load {file} --api {api}')
|
246
323
|
reg = Registry()
|
@@ -278,8 +355,16 @@ def load(file, api, format):
|
|
278
355
|
default=lambda: get_config_value("bootstrap", Path),
|
279
356
|
help='The path of the bootstrap.')
|
280
357
|
def boot(bootstrap):
|
281
|
-
"""
|
282
|
-
|
358
|
+
"""Run a bootstrap script.
|
359
|
+
|
360
|
+
Executes a bootstrap script to set up the environment or perform initialization tasks.
|
361
|
+
The bootstrap script runs in a new process with the current Python environment.
|
362
|
+
|
363
|
+
Args:
|
364
|
+
bootstrap: Path to the bootstrap script
|
365
|
+
|
366
|
+
Example:
|
367
|
+
$ qulab boot --bootstrap setup.py
|
283
368
|
"""
|
284
369
|
if bootstrap is not None:
|
285
370
|
run_script(bootstrap)
|
@@ -291,7 +376,16 @@ def parse_dynamic_option_value(value):
|
|
291
376
|
parsed_value = ast.literal_eval(value)
|
292
377
|
except (ValueError, SyntaxError):
|
293
378
|
# 如果解析失败,返回原始字符串
|
294
|
-
|
379
|
+
if ',' in value:
|
380
|
+
parsed_value = []
|
381
|
+
for item in value.split(','):
|
382
|
+
try:
|
383
|
+
parsed_value.append(ast.literal_eval(item))
|
384
|
+
except (ValueError, SyntaxError):
|
385
|
+
parsed_value.append(item)
|
386
|
+
parsed_value = tuple(parsed_value)
|
387
|
+
else:
|
388
|
+
parsed_value = value
|
295
389
|
return parsed_value
|
296
390
|
|
297
391
|
|
@@ -311,17 +405,31 @@ def parse_dynamic_options(args):
|
|
311
405
|
return result
|
312
406
|
|
313
407
|
|
314
|
-
help_doc = """
|
315
|
-
|
408
|
+
help_doc = """Run a workflow with specified parameters and options.
|
409
|
+
|
410
|
+
This command executes a workflow and its dependencies based on the provided configuration.
|
411
|
+
|
412
|
+
Arguments:
|
413
|
+
workflow: The name or path of the workflow to run
|
414
|
+
|
415
|
+
Options:
|
416
|
+
--code, -c: The path containing the workflow code (default: current directory)
|
417
|
+
--data, -d: The path for storing logs and data (default: ./logs)
|
418
|
+
--api, -a: The module name of the API for configuration handling
|
419
|
+
--plot, -p: Generate plots after workflow execution
|
420
|
+
--no-dependents, -n: Run only the specified workflow without dependencies
|
421
|
+
--retry, -r: Number of retry attempts for failed calibrations (default: 1)
|
422
|
+
--freeze: Freeze the configuration table during execution
|
423
|
+
--fail-fast, -f: Stop execution on first error
|
424
|
+
--veryfy-source-code: Verify source code before execution
|
316
425
|
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
If `--data` is not set, use the `logs` directory in the code path.
|
426
|
+
Additional parameters can be passed as key=value pairs and will be available to the workflow.
|
427
|
+
|
428
|
+
Examples:
|
429
|
+
$ qulab run my_workflow
|
430
|
+
$ qulab run my_workflow --plot --retry 3
|
431
|
+
$ qulab run my_workflow param1=value1 param2=value2
|
432
|
+
$ qulab run my_workflow --no-dependents --freeze
|
325
433
|
"""
|
326
434
|
|
327
435
|
|
@@ -478,15 +586,30 @@ async def maintain(workflow,
|
|
478
586
|
plot,
|
479
587
|
fail_fast,
|
480
588
|
veryfy_source_code=True):
|
481
|
-
"""
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
589
|
+
"""Maintain a workflow and its dependencies.
|
590
|
+
|
591
|
+
This command checks and maintains the workflow and its dependencies without executing them.
|
592
|
+
It verifies configurations, dependencies, and can generate plots from existing data.
|
593
|
+
|
594
|
+
Args:
|
595
|
+
workflow: Name or path of the workflow to maintain
|
596
|
+
code: Directory containing the workflow code
|
597
|
+
data: Directory for logs and data
|
598
|
+
api: Module name for configuration API
|
599
|
+
retry: Number of retry attempts for failed calibrations
|
600
|
+
plot: Generate plots from existing data
|
601
|
+
fail_fast: Stop on first error
|
602
|
+
veryfy_source_code: Verify source code integrity
|
603
|
+
|
604
|
+
The maintenance process includes:
|
605
|
+
1. Verifying workflow dependencies
|
606
|
+
2. Checking configuration consistency
|
607
|
+
3. Validating source code (if enabled)
|
608
|
+
4. Generating plots (if enabled)
|
609
|
+
|
610
|
+
Example:
|
611
|
+
$ qulab maintain my_workflow --retry 3 --plot
|
612
|
+
$ qulab maintain my_workflow --fail-fast
|
490
613
|
"""
|
491
614
|
logger.info(
|
492
615
|
f'[CMD]: maintain {workflow} --code {code} --data {data} --api {api}'
|
@@ -556,13 +679,28 @@ async def maintain(workflow,
|
|
556
679
|
@command_option('reproduce')
|
557
680
|
@async_command
|
558
681
|
async def reproduce(report_id, code, data, api, plot):
|
559
|
-
"""
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
682
|
+
"""Reproduce a workflow execution from a saved report.
|
683
|
+
|
684
|
+
This command loads a previous execution report and attempts to reproduce the workflow
|
685
|
+
with the exact same configuration and conditions.
|
686
|
+
|
687
|
+
Args:
|
688
|
+
report_id: The ID number of the report to reproduce
|
689
|
+
code: Directory containing the workflow code
|
690
|
+
data: Directory for logs and data
|
691
|
+
api: Module name for configuration API
|
692
|
+
plot: Generate plots after reproduction
|
693
|
+
|
694
|
+
The reproduction process:
|
695
|
+
1. Loads the original report by ID
|
696
|
+
2. Restores the exact configuration state
|
697
|
+
3. Executes the workflow with frozen configuration
|
698
|
+
4. Optionally generates plots
|
699
|
+
5. Restores the previous configuration state
|
700
|
+
|
701
|
+
Example:
|
702
|
+
$ qulab reproduce 123 --plot
|
703
|
+
$ qulab reproduce 456 --code ./workflows --data ./results
|
566
704
|
"""
|
567
705
|
logger.info(
|
568
706
|
f'[CMD]: reproduce {report_id} --code {code} --data {data} --api {api}'
|
qulab/executor/utils.py
CHANGED
@@ -38,6 +38,7 @@ import numpy as np
|
|
38
38
|
from loguru import logger
|
39
39
|
|
40
40
|
from qulab import VAR
|
41
|
+
from qulab import manual_analysis
|
41
42
|
from qulab.typing import Report
|
42
43
|
|
43
44
|
|
@@ -73,7 +74,8 @@ async def analyze(report: Report, history: Report | None = None) -> Report:
|
|
73
74
|
history: Report | None
|
74
75
|
上次校准实验数据和分析结果,如果有的话。
|
75
76
|
\"\"\"
|
76
|
-
|
77
|
+
# 如果需要手动分析,请取消注释下面这行
|
78
|
+
# return manual_analysis(report, history)
|
77
79
|
|
78
80
|
# 这里添加你的分析过程,运行 calibrate 得到的数据,在 report.data 里
|
79
81
|
# 你可以得到校准的结果,然后根据这个结果进行分析。
|
@@ -82,6 +84,7 @@ async def analyze(report: Report, history: Report | None = None) -> Report:
|
|
82
84
|
# 完整校准后的状态有两种:OK 和 Bad,分别对应校准成功和校准失败。
|
83
85
|
# 校准失败是指出现坏数据,无法简单通过重新运行本次校准解决,需要
|
84
86
|
# 检查前置步骤。
|
87
|
+
import random
|
85
88
|
report.state = random.choice(['OK', 'Bad'])
|
86
89
|
|
87
90
|
# 参数是一个字典,包含了本次校准得到的参数,后续会更新到config表中。
|
@@ -144,7 +147,7 @@ async def oracle(report: Report,
|
|
144
147
|
|
145
148
|
当校准失败时,根据 analyze 的结果,尝试改变某些配置再重新校准整个系统。
|
146
149
|
比如通常我们在死活测不到 rabi 或能谱时,会换一个 idle bias 再试试。这
|
147
|
-
里我们凭直觉设的那个 bias
|
150
|
+
里我们凭直觉设的那个 bias 值,就称作一个谕示,可以通过 oracle 来设定。
|
148
151
|
|
149
152
|
该函数代入的参数 report 是 analyze 函数的返回值。
|
150
153
|
\"\"\"
|
@@ -161,6 +164,17 @@ async def debug_analyze(
|
|
161
164
|
code_path: str | Path = get_config_value('code', Path),
|
162
165
|
data_path: str | Path = get_config_value('data', Path),
|
163
166
|
) -> None:
|
167
|
+
"""
|
168
|
+
调试 workflow 的分析过程。
|
169
|
+
|
170
|
+
该函数会从 data_path 中读取报告,并重新加载对应的 workflow 代码,然后
|
171
|
+
运行 analyze 函数,如果有 plot 函数,还会运行 plot 函数。最后返回包含
|
172
|
+
分析结果的 report 对象。
|
173
|
+
|
174
|
+
当你完成一个 workflow 的 calibrate 开发后,可以现运行该 workflow 采
|
175
|
+
集一组数据,然后通过反复修改 analyze 函数,运行该函数,并查看分析结果,
|
176
|
+
来调试 analyze 函数,以期达到满意的效果。
|
177
|
+
"""
|
164
178
|
from .storage import get_report_by_index
|
165
179
|
|
166
180
|
report = get_report_by_index(report_index, data_path)
|
qulab/monitor/__init__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
from .monitor import
|
1
|
+
from .monitor import MonitorUI, get_monitor
|
qulab/monitor/__main__.py
CHANGED
@@ -1,8 +1,36 @@
|
|
1
|
+
"""
|
2
|
+
QuLab Monitor Command Line Interface
|
3
|
+
|
4
|
+
This module provides a command-line interface for launching the QuLab monitor.
|
5
|
+
It allows users to start a standalone monitor server with configurable settings.
|
6
|
+
"""
|
7
|
+
|
1
8
|
import click
|
2
9
|
|
3
|
-
from .monitor import
|
10
|
+
from .monitor import MonitorServer
|
4
11
|
|
5
12
|
|
6
13
|
@click.command(name='monitor')
|
7
|
-
|
8
|
-
|
14
|
+
@click.option('--columns', '-c', default=4, help='Number of columns in the plot grid')
|
15
|
+
@click.option('--height', '-h', default=400, help='Minimum height of each plot in pixels')
|
16
|
+
@click.option('--address', '-a', default='127.0.0.1', help='Address to bind the server')
|
17
|
+
@click.option('--port', '-p', default=5555, help='Port to bind the server')
|
18
|
+
def main(columns: int, height: int, address: str, port: int):
|
19
|
+
"""Launch a QuLab monitor server.
|
20
|
+
|
21
|
+
Args:
|
22
|
+
columns: Number of columns in the plot grid
|
23
|
+
height: Minimum height of each plot in pixels
|
24
|
+
address: Address to bind the server (default: 127.0.0.1)
|
25
|
+
port: Port to bind the server (default: 5555)
|
26
|
+
"""
|
27
|
+
server = MonitorServer(address=address,
|
28
|
+
port=port,
|
29
|
+
number_of_columns=columns,
|
30
|
+
minimum_height=height)
|
31
|
+
try:
|
32
|
+
while True:
|
33
|
+
import time
|
34
|
+
time.sleep(1) # Keep the script running while the server is active
|
35
|
+
except KeyboardInterrupt:
|
36
|
+
print("\nShutting down monitor server...")
|
qulab/monitor/config.py
CHANGED
@@ -1,41 +1,66 @@
|
|
1
|
+
"""
|
2
|
+
QuLab Monitor Configuration Module
|
3
|
+
|
4
|
+
This module defines the global configuration settings for the QuLab monitor
|
5
|
+
application, including:
|
6
|
+
- Visual styles and themes
|
7
|
+
- Data transformation functions
|
8
|
+
- Plot appearance settings
|
9
|
+
- Buffer sizes and indices
|
10
|
+
|
11
|
+
The configuration values are used throughout the monitor application to maintain
|
12
|
+
consistent appearance and behavior.
|
13
|
+
"""
|
14
|
+
|
15
|
+
from typing import Callable, Dict, List, Tuple, Union
|
1
16
|
import numpy as np
|
2
17
|
|
3
|
-
|
18
|
+
# Qt stylesheet for the application
|
19
|
+
STYLE = '''
|
4
20
|
QWidget {
|
5
21
|
font: medium Ubuntu;
|
6
22
|
background-color: #011F2F;
|
7
23
|
font-size: 16px;
|
8
|
-
|
9
|
-
color:#FFFFFF;
|
24
|
+
color: #FFFFFF;
|
10
25
|
}
|
11
26
|
'''
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
"
|
21
|
-
"
|
27
|
+
|
28
|
+
# Number of data frames to keep in the rolling buffer
|
29
|
+
ROLL_BUFFER_SIZE = 6
|
30
|
+
|
31
|
+
# Data transformation functions for complex data visualization
|
32
|
+
DataTransform = Callable[[Union[np.ndarray, list], float], np.ndarray]
|
33
|
+
|
34
|
+
TRANSFORMS: Dict[str, DataTransform] = {
|
35
|
+
"mag": lambda data, _: np.abs(data),
|
36
|
+
"phase": lambda data, _: np.angle(data),
|
37
|
+
"real": lambda data, _: np.real(data),
|
38
|
+
"imag": lambda data, _: np.imag(data),
|
39
|
+
"rot": lambda data, angle: np.real(np.exp(1j * angle) * np.array(data))
|
22
40
|
}
|
23
|
-
|
24
|
-
#
|
25
|
-
|
26
|
-
|
27
|
-
#
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
(
|
34
|
-
(
|
35
|
-
(
|
41
|
+
|
42
|
+
# List of available transformation names
|
43
|
+
TRANSFORM_NAMES: List[str] = list(TRANSFORMS.keys())
|
44
|
+
|
45
|
+
# Colors for selected and unselected states (RGB values)
|
46
|
+
COLOR_SELECTED: Tuple[int, int, int] = (0, 0, 0)
|
47
|
+
COLOR_UNSELECTED: Tuple[int, int, int] = (6, 6, 8)
|
48
|
+
|
49
|
+
# Default colors for plot lines (RGB values)
|
50
|
+
DEFAULT_COLORS: List[Tuple[int, int, int]] = [
|
51
|
+
(200, 0, 0), # Bright red
|
52
|
+
(55, 100, 180), # Blue
|
53
|
+
(40, 80, 150), # Medium blue
|
54
|
+
(30, 50, 110), # Dark blue
|
55
|
+
(25, 40, 70), # Navy
|
56
|
+
(25, 30, 50), # Dark navy
|
36
57
|
]
|
37
58
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
59
|
+
# Line widths for each plot line
|
60
|
+
LINE_WIDTHS: List[int] = [3, 2, 2, 2, 1, 1]
|
61
|
+
|
62
|
+
# Symbol sizes for each plot line (0 means no symbols)
|
63
|
+
SYMBOL_SIZES: List[int] = [5, 0, 0, 0, 0, 0]
|
64
|
+
|
65
|
+
# Reversed indices for the rolling buffer (newest to oldest)
|
66
|
+
ROLL_INDICES: List[int] = list(range(ROLL_BUFFER_SIZE))[::-1]
|