QuLab 2.0.0__py3-none-any.whl → 2.10.11__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 +33 -1
- qulab/__main__.py +4 -0
- qulab/cli/commands.py +31 -0
- qulab/cli/config.py +170 -0
- qulab/cli/decorators.py +28 -0
- qulab/executor/__init__.py +5 -0
- qulab/executor/analyze.py +188 -0
- qulab/executor/cli.py +447 -0
- qulab/executor/load.py +563 -0
- qulab/executor/registry.py +185 -0
- qulab/executor/schedule.py +543 -0
- qulab/executor/storage.py +615 -0
- qulab/executor/template.py +259 -0
- qulab/executor/utils.py +194 -0
- qulab/monitor/__init__.py +1 -93
- qulab/monitor/__main__.py +8 -0
- qulab/monitor/monitor.py +115 -0
- qulab/scan/__init__.py +2 -4
- qulab/scan/curd.py +221 -0
- qulab/scan/models.py +554 -0
- qulab/scan/optimize.py +76 -0
- qulab/scan/query.py +387 -0
- qulab/scan/record.py +603 -0
- qulab/scan/scan.py +1166 -0
- qulab/scan/server.py +450 -0
- qulab/scan/space.py +213 -0
- qulab/scan/utils.py +230 -34
- qulab/sys/__init__.py +2 -0
- qulab/sys/device/basedevice.py +50 -16
- qulab/sys/device/loader.py +1 -1
- qulab/sys/device/utils.py +46 -13
- qulab/sys/drivers/FakeInstrument.py +36 -20
- qulab/sys/net/nginx.py +16 -14
- qulab/sys/rpc/router.py +35 -0
- qulab/sys/rpc/zmq_socket.py +227 -0
- qulab/tools/__init__.py +0 -0
- qulab/tools/connection_helper.py +39 -0
- qulab/typing.py +2 -0
- qulab/utils.py +95 -0
- qulab/version.py +1 -1
- qulab/visualization/__init__.py +188 -0
- qulab/visualization/__main__.py +71 -0
- qulab/visualization/_autoplot.py +464 -0
- qulab/visualization/plot_circ.py +319 -0
- qulab/visualization/plot_layout.py +408 -0
- qulab/visualization/plot_seq.py +242 -0
- qulab/visualization/qdat.py +152 -0
- qulab/visualization/rot3d.py +23 -0
- qulab/visualization/widgets.py +86 -0
- {QuLab-2.0.0.dist-info → qulab-2.10.11.dist-info}/METADATA +28 -12
- qulab-2.10.11.dist-info/RECORD +104 -0
- {QuLab-2.0.0.dist-info → qulab-2.10.11.dist-info}/WHEEL +1 -1
- qulab-2.10.11.dist-info/entry_points.txt +2 -0
- QuLab-2.0.0.dist-info/RECORD +0 -71
- qulab/monitor/multiploter/__init__.py +0 -1
- qulab/scan/base.py +0 -544
- qulab/scan/expression.py +0 -360
- qulab/scan/scanner.py +0 -244
- qulab/scan/transforms.py +0 -16
- /qulab/{scan/dataset.py → cli/__init__.py} +0 -0
- /qulab/monitor/{multiploter/config.py → config.py} +0 -0
- /qulab/monitor/{multiploter/dataset.py → dataset.py} +0 -0
- /qulab/monitor/{multiploter/event_queue.py → event_queue.py} +0 -0
- /qulab/monitor/{multiploter/main.py → mainwindow.py} +0 -0
- /qulab/monitor/{multiploter/ploter.py → ploter.py} +0 -0
- /qulab/monitor/{multiploter/qt_compat.py → qt_compat.py} +0 -0
- /qulab/monitor/{multiploter/toolbar.py → toolbar.py} +0 -0
- {QuLab-2.0.0.dist-info → qulab-2.10.11.dist-info/licenses}/LICENSE +0 -0
- {QuLab-2.0.0.dist-info → qulab-2.10.11.dist-info}/top_level.txt +0 -0
qulab/__init__.py
CHANGED
@@ -1 +1,33 @@
|
|
1
|
-
from
|
1
|
+
from qlisp import (CR, CX, CZ, SWAP, A, B, BellPhiM, BellPhiP, BellPsiM,
|
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)
|
8
|
+
from qlispc import (COMMAND, FREE, NOTSET, PUSH, READ, SYNC, TRIG, WRITE,
|
9
|
+
compile, get_arch, libraries, mapping_qubits,
|
10
|
+
register_arch)
|
11
|
+
from qlispc.kernel_utils import qcompile
|
12
|
+
from wath import (Interval, Primes, Transmon, Z2probs, complex_amp_to_real,
|
13
|
+
effective_temperature, exception, find_axis_of_symmetry,
|
14
|
+
find_center_of_symmetry, find_cross_point, fit_circle,
|
15
|
+
fit_cosine, fit_k, fit_max, fit_peaks, fit_pole, getFTMatrix,
|
16
|
+
graph, inv_poly, lin_fit, point_in_ellipse, point_in_polygon,
|
17
|
+
point_on_ellipse, point_on_segment, poly_fit, probs2Z,
|
18
|
+
relative_delay_to_absolute, thermal_excitation, viterbi_hmm)
|
19
|
+
from waveforms import (D, chirp, const, cos, cosh, coshPulse, cosPulse, cut,
|
20
|
+
drag, drag_sin, drag_sinx, exp, function, gaussian,
|
21
|
+
general_cosine, hanning, interp, mixing, one, poly,
|
22
|
+
registerBaseFunc, registerDerivative, samplingPoints,
|
23
|
+
sign, sin, sinc, sinh, square, step, t, wave_eval, zero)
|
24
|
+
|
25
|
+
from .executor.analyze import manual_analysis
|
26
|
+
from .executor.registry import Registry
|
27
|
+
from .executor.storage import find_report
|
28
|
+
from .executor.storage import get_report_by_index as get_report
|
29
|
+
from .executor.template import VAR
|
30
|
+
from .executor.utils import debug_analyze
|
31
|
+
from .scan import Scan, get_record, load_record, lookup, lookup_list
|
32
|
+
from .version import __version__
|
33
|
+
from .visualization import autoplot
|
qulab/__main__.py
ADDED
qulab/cli/commands.py
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
import click
|
2
|
+
|
3
|
+
from ..executor.cli import boot, create, get, maintain, reproduce, run, set
|
4
|
+
from ..monitor.__main__ import main as monitor
|
5
|
+
from ..scan.server import server
|
6
|
+
from ..sys.net.cli import dht
|
7
|
+
from ..visualization.__main__ import plot
|
8
|
+
|
9
|
+
|
10
|
+
@click.group()
|
11
|
+
def cli():
|
12
|
+
pass
|
13
|
+
|
14
|
+
|
15
|
+
@cli.command()
|
16
|
+
def hello():
|
17
|
+
"""Print hello world."""
|
18
|
+
click.echo('hello, world')
|
19
|
+
|
20
|
+
|
21
|
+
cli.add_command(monitor)
|
22
|
+
cli.add_command(plot)
|
23
|
+
cli.add_command(dht)
|
24
|
+
cli.add_command(server)
|
25
|
+
cli.add_command(maintain)
|
26
|
+
cli.add_command(run)
|
27
|
+
cli.add_command(reproduce)
|
28
|
+
cli.add_command(create)
|
29
|
+
cli.add_command(set)
|
30
|
+
cli.add_command(get)
|
31
|
+
cli.add_command(boot)
|
qulab/cli/config.py
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
import configparser
|
2
|
+
import functools
|
3
|
+
import os
|
4
|
+
import sys
|
5
|
+
from pathlib import Path
|
6
|
+
|
7
|
+
import click
|
8
|
+
from loguru import logger
|
9
|
+
|
10
|
+
CONFIG_PATH = os.path.expanduser("~/.qulab.ini")
|
11
|
+
ENV_PREFIX = "QULAB_"
|
12
|
+
|
13
|
+
|
14
|
+
def _get_config_value(option_name,
|
15
|
+
type_cast=str,
|
16
|
+
command_name=None,
|
17
|
+
default=None):
|
18
|
+
"""支持命令专属配置的优先级获取"""
|
19
|
+
# 构造环境变量名
|
20
|
+
if command_name:
|
21
|
+
env_var = f"{ENV_PREFIX}{command_name.upper()}_{option_name.upper()}"
|
22
|
+
config_section = command_name
|
23
|
+
else:
|
24
|
+
env_var = f"{ENV_PREFIX}{option_name.upper()}"
|
25
|
+
config_section = "common"
|
26
|
+
|
27
|
+
# 1. 检查环境变量
|
28
|
+
if env_value := os.getenv(env_var):
|
29
|
+
if type_cast is bool:
|
30
|
+
return env_value.lower() in ("true", "1", "yes")
|
31
|
+
if "path" in option_name or issubclass(type_cast, Path):
|
32
|
+
return Path(os.path.expanduser(env_value))
|
33
|
+
return type_cast(env_value)
|
34
|
+
|
35
|
+
# 2. 检查配置文件
|
36
|
+
config = configparser.ConfigParser()
|
37
|
+
# 先加载默认配置防止段不存在
|
38
|
+
config.read_dict({config_section: {}})
|
39
|
+
if Path(CONFIG_PATH).exists():
|
40
|
+
config.read(CONFIG_PATH)
|
41
|
+
|
42
|
+
# 从对应配置段读取
|
43
|
+
if config.has_section(config_section):
|
44
|
+
if config_value := config.get(config_section,
|
45
|
+
option_name,
|
46
|
+
fallback=None):
|
47
|
+
if type_cast is bool:
|
48
|
+
return config_value.lower() in ("true", "1", "yes")
|
49
|
+
if "path" in option_name or issubclass(type_cast, Path):
|
50
|
+
return Path(os.path.expanduser(config_value))
|
51
|
+
return type_cast(config_value)
|
52
|
+
|
53
|
+
return default # 交给 Click 处理默认值
|
54
|
+
|
55
|
+
|
56
|
+
def get_config_value(option_name,
|
57
|
+
type_cast=str,
|
58
|
+
command_name=None,
|
59
|
+
default=None):
|
60
|
+
"""
|
61
|
+
获取配置值,支持命令专属配置的优先级获取
|
62
|
+
|
63
|
+
优先级:
|
64
|
+
1. 命令行参数
|
65
|
+
2. 配置文件专属配置
|
66
|
+
3. 配置文件公共配置
|
67
|
+
4. 环境变量专属配置
|
68
|
+
5. 环境变量公共配置
|
69
|
+
6. 默认值
|
70
|
+
7. Click 处理默认值
|
71
|
+
8. None
|
72
|
+
|
73
|
+
Parameters
|
74
|
+
----------
|
75
|
+
option_name : str
|
76
|
+
配置项名称
|
77
|
+
type_cast : type
|
78
|
+
转换类型
|
79
|
+
command_name : str
|
80
|
+
命令名称
|
81
|
+
如果为 None,则不使用命令专属配置
|
82
|
+
default : any
|
83
|
+
默认值
|
84
|
+
如果为 None,则不使用默认值
|
85
|
+
|
86
|
+
Returns
|
87
|
+
-------
|
88
|
+
any
|
89
|
+
配置值
|
90
|
+
如果没有找到配置值,则返回 None
|
91
|
+
"""
|
92
|
+
value = _get_config_value(option_name,
|
93
|
+
type_cast,
|
94
|
+
command_name,
|
95
|
+
default=default)
|
96
|
+
if value is None and command_name is not None:
|
97
|
+
return _get_config_value(option_name, type_cast, default=default)
|
98
|
+
return value
|
99
|
+
|
100
|
+
|
101
|
+
def log_options(func=None, command_name=None):
|
102
|
+
"""通用配置装饰器(所有命令共用)
|
103
|
+
|
104
|
+
添加 --debug, --log, --debug-log, --quiet 选项
|
105
|
+
1. --debug: 是否开启调试模式
|
106
|
+
2. --log: 日志文件路径
|
107
|
+
3. --debug-log: 调试日志文件路径
|
108
|
+
4. --quiet: 是否静默输出
|
109
|
+
"""
|
110
|
+
|
111
|
+
if isinstance(func, str):
|
112
|
+
return functools.partial(log_options, command_name=func)
|
113
|
+
if func is None:
|
114
|
+
return functools.partial(log_options, command_name=command_name)
|
115
|
+
|
116
|
+
@click.option("--debug",
|
117
|
+
is_flag=True,
|
118
|
+
default=get_config_value("debug",
|
119
|
+
bool,
|
120
|
+
command_name=command_name),
|
121
|
+
help=f"Enable debug mode")
|
122
|
+
@click.option("--log",
|
123
|
+
type=click.Path(),
|
124
|
+
default=lambda: get_config_value(
|
125
|
+
"log", Path, command_name=command_name),
|
126
|
+
help=f"Log file path")
|
127
|
+
@click.option("--debug-log",
|
128
|
+
type=click.Path(),
|
129
|
+
default=lambda: get_config_value(
|
130
|
+
"debug_log", Path, command_name=command_name),
|
131
|
+
help=f"Debug log file path")
|
132
|
+
@click.option("--quiet",
|
133
|
+
is_flag=True,
|
134
|
+
default=get_config_value("quiet",
|
135
|
+
bool,
|
136
|
+
command_name=command_name),
|
137
|
+
help=f"Disable log output")
|
138
|
+
@functools.wraps(func)
|
139
|
+
def wrapper(*args, **kwargs):
|
140
|
+
debug = bool(kwargs.pop("debug"))
|
141
|
+
log = kwargs.pop("log")
|
142
|
+
debug_log = kwargs.pop("debug_log")
|
143
|
+
quiet = bool(kwargs.pop("quiet"))
|
144
|
+
|
145
|
+
if debug:
|
146
|
+
log_level = "DEBUG"
|
147
|
+
else:
|
148
|
+
log_level = "INFO"
|
149
|
+
|
150
|
+
handlers = []
|
151
|
+
if log is not None:
|
152
|
+
handlers.append(
|
153
|
+
dict(sink=log,
|
154
|
+
level="INFO",
|
155
|
+
rotation="monday at 7:00",
|
156
|
+
compression="zip"))
|
157
|
+
if debug_log is not None:
|
158
|
+
handlers.append(
|
159
|
+
dict(sink=debug_log,
|
160
|
+
level="DEBUG",
|
161
|
+
rotation="monday at 7:00",
|
162
|
+
compression="zip"))
|
163
|
+
if not quiet or debug:
|
164
|
+
handlers.append(dict(sink=sys.stderr, level=log_level))
|
165
|
+
|
166
|
+
logger.configure(handlers=handlers)
|
167
|
+
|
168
|
+
return func(*args, **kwargs)
|
169
|
+
|
170
|
+
return wrapper
|
qulab/cli/decorators.py
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
import asyncio
|
2
|
+
import functools
|
3
|
+
import inspect
|
4
|
+
|
5
|
+
|
6
|
+
def async_command(func=None):
|
7
|
+
"""
|
8
|
+
Decorator to mark a function as an asynchronous command.
|
9
|
+
"""
|
10
|
+
if func is None:
|
11
|
+
return async_command
|
12
|
+
|
13
|
+
@functools.wraps(func)
|
14
|
+
def wrapper(*args, **kwargs):
|
15
|
+
return run(func, *args, **kwargs)
|
16
|
+
|
17
|
+
return wrapper
|
18
|
+
|
19
|
+
|
20
|
+
def run(main, *args, **kwds):
|
21
|
+
if inspect.iscoroutinefunction(main):
|
22
|
+
try:
|
23
|
+
import uvloop
|
24
|
+
uvloop.run(main(*args, **kwds))
|
25
|
+
except (ImportError, ModuleNotFoundError):
|
26
|
+
asyncio.run(main(*args, **kwds))
|
27
|
+
else:
|
28
|
+
main(*args, **kwds)
|
@@ -0,0 +1,188 @@
|
|
1
|
+
import pickle
|
2
|
+
import threading
|
3
|
+
import time
|
4
|
+
|
5
|
+
import pyperclip
|
6
|
+
import zmq
|
7
|
+
|
8
|
+
from .storage import Report
|
9
|
+
|
10
|
+
# 需要复制到 Notebook 的代码模板
|
11
|
+
clip_template = """
|
12
|
+
from qulab.executor.analyze import get_report as get_report_{server_port}
|
13
|
+
|
14
|
+
report, history = get_report_{server_port}("tcp://{server_address}:{server_port}")
|
15
|
+
# 在这里插入数据处理逻辑
|
16
|
+
|
17
|
+
{analysis_code}
|
18
|
+
"""
|
19
|
+
|
20
|
+
analysis_code = """
|
21
|
+
# report.state = 'OK'
|
22
|
+
# report.parameters = {}
|
23
|
+
# report.oracle = {}
|
24
|
+
# report.other_infomation = {}
|
25
|
+
|
26
|
+
report.parameters
|
27
|
+
|
28
|
+
"""
|
29
|
+
|
30
|
+
|
31
|
+
# ZeroMQ 服务线程,用于响应 Notebook 的请求
|
32
|
+
class ServerThread(threading.Thread):
|
33
|
+
|
34
|
+
def __init__(self, data, timeout):
|
35
|
+
super().__init__()
|
36
|
+
self.data = data
|
37
|
+
self.result = None
|
38
|
+
self.port = 0
|
39
|
+
self.timeout = timeout
|
40
|
+
self.running = True
|
41
|
+
self.finished = threading.Event()
|
42
|
+
self.context = zmq.Context()
|
43
|
+
|
44
|
+
def find_free_port(self):
|
45
|
+
with zmq.Socket(self.context, zmq.REQ) as s:
|
46
|
+
s.bind_to_random_port("tcp://*")
|
47
|
+
self.port = int(
|
48
|
+
s.getsockopt(zmq.LAST_ENDPOINT).decode().split(":")[-1])
|
49
|
+
s.unbind(s.getsockopt(zmq.LAST_ENDPOINT))
|
50
|
+
return self.port
|
51
|
+
|
52
|
+
def run(self):
|
53
|
+
self.port = self.find_free_port()
|
54
|
+
socket = self.context.socket(zmq.REP)
|
55
|
+
socket.bind(f"tcp://*:{self.port}")
|
56
|
+
# 设置 recv 超时 1 秒
|
57
|
+
socket.RCVTIMEO = 1000
|
58
|
+
start_time = time.time()
|
59
|
+
try:
|
60
|
+
while self.running and (time.time() - start_time < self.timeout):
|
61
|
+
try:
|
62
|
+
msg = socket.recv()
|
63
|
+
except zmq.Again:
|
64
|
+
continue # 超时后继续等待
|
65
|
+
|
66
|
+
# Notebook 端请求数据
|
67
|
+
if msg == b"GET":
|
68
|
+
socket.send(pickle.dumps(self.data))
|
69
|
+
else:
|
70
|
+
# Notebook 端提交了处理结果
|
71
|
+
try:
|
72
|
+
self.result = pickle.loads(msg)
|
73
|
+
except Exception as e:
|
74
|
+
# 如果解析失败,也返回默认 ACK
|
75
|
+
self.result = None
|
76
|
+
socket.send(b"ACK")
|
77
|
+
self.running = False
|
78
|
+
break
|
79
|
+
finally:
|
80
|
+
socket.close()
|
81
|
+
self.context.term()
|
82
|
+
self.finished.set()
|
83
|
+
|
84
|
+
def stop(self):
|
85
|
+
self.running = False
|
86
|
+
self.finished.set()
|
87
|
+
|
88
|
+
def wait_for_result(self):
|
89
|
+
self.finished.wait()
|
90
|
+
return self.result
|
91
|
+
|
92
|
+
|
93
|
+
# 进入分析流程,启动服务并打印等待提示
|
94
|
+
def get_result_or_wait_until_timeout(report: Report, history: Report | None,
|
95
|
+
timeout: float) -> Report:
|
96
|
+
server = ServerThread((report, history), timeout)
|
97
|
+
server.start()
|
98
|
+
|
99
|
+
parameters = report.parameters
|
100
|
+
oracle = report.oracle
|
101
|
+
other_infomation = report.other_infomation
|
102
|
+
state = report.state
|
103
|
+
|
104
|
+
# 格式化代码模板
|
105
|
+
code = clip_template.format(server_address="127.0.0.1",
|
106
|
+
server_port=server.port,
|
107
|
+
analysis_code=analysis_code)
|
108
|
+
|
109
|
+
# 将代码复制到剪切板
|
110
|
+
pyperclip.copy(code)
|
111
|
+
|
112
|
+
# 同时打印到终端,防止误操作导致剪切板内容丢失
|
113
|
+
print("请在 Jupyter Notebook 中运行下面这段代码:")
|
114
|
+
print("-" * 60)
|
115
|
+
print(code)
|
116
|
+
print("-" * 60)
|
117
|
+
print("等待 Notebook 提交处理结果,或等待超时({} 秒)...".format(timeout))
|
118
|
+
|
119
|
+
start_time = time.time()
|
120
|
+
# 采用循环等待提交结果,间隔 0.5 秒检测一次
|
121
|
+
while server.finished.wait(timeout=0.5) is False:
|
122
|
+
elapsed = time.time() - start_time
|
123
|
+
if elapsed >= timeout:
|
124
|
+
# 超时后结束等待
|
125
|
+
server.stop()
|
126
|
+
break
|
127
|
+
|
128
|
+
result = server.wait_for_result()
|
129
|
+
if result is None:
|
130
|
+
return (state, parameters, oracle, other_infomation, code)
|
131
|
+
else:
|
132
|
+
return result
|
133
|
+
|
134
|
+
|
135
|
+
def manual_analysis(report: Report, history=None, timeout=3600):
|
136
|
+
try:
|
137
|
+
(state, parameters, oracle, other_infomation,
|
138
|
+
code) = get_result_or_wait_until_timeout(report, history, timeout)
|
139
|
+
report.parameters = parameters
|
140
|
+
report.oracle = oracle
|
141
|
+
report.state = state
|
142
|
+
report.other_infomation = other_infomation
|
143
|
+
except Exception as e:
|
144
|
+
pass
|
145
|
+
return report
|
146
|
+
|
147
|
+
|
148
|
+
def get_report(address: str) -> Report:
|
149
|
+
import IPython
|
150
|
+
|
151
|
+
ipy = IPython.get_ipython()
|
152
|
+
if ipy is None:
|
153
|
+
raise RuntimeError("请在 Jupyter Notebook 中运行此函数。")
|
154
|
+
ipy.set_next_input(("from qulab.executor.analyze import submit_report\n"
|
155
|
+
"# 处理完成后,提交结果\n"
|
156
|
+
f"# submit_report(report, \"{address}\")"),
|
157
|
+
replace=False)
|
158
|
+
context = zmq.Context()
|
159
|
+
sock = context.socket(zmq.REQ)
|
160
|
+
sock.connect(address)
|
161
|
+
# 请求数据
|
162
|
+
sock.send(b"GET")
|
163
|
+
report, history = pickle.loads(sock.recv())
|
164
|
+
return report, history
|
165
|
+
|
166
|
+
|
167
|
+
def submit_report(report: Report, address: str):
|
168
|
+
import IPython
|
169
|
+
|
170
|
+
ipy = IPython.get_ipython()
|
171
|
+
if ipy is None:
|
172
|
+
raise RuntimeError("请在 Jupyter Notebook 中运行此函数。")
|
173
|
+
|
174
|
+
code = ipy.user_ns['In'][-2]
|
175
|
+
|
176
|
+
parameters = report.parameters
|
177
|
+
oracle = report.oracle
|
178
|
+
other_infomation = report.other_infomation
|
179
|
+
state = report.state
|
180
|
+
|
181
|
+
context = zmq.Context()
|
182
|
+
sock = context.socket(zmq.REQ)
|
183
|
+
sock.connect(address)
|
184
|
+
# 提交处理后的结果
|
185
|
+
sock.send(pickle.dumps(
|
186
|
+
(state, parameters, oracle, other_infomation, code)))
|
187
|
+
ack = sock.recv()
|
188
|
+
print("结果已提交。")
|