qubitclient 0.1.4__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.
Files changed (81) hide show
  1. qubitclient/__init__.py +5 -0
  2. qubitclient/draw/__init__.py +0 -0
  3. qubitclient/draw/optpipulsepltplotter.py +75 -0
  4. qubitclient/draw/optpipulseplyplotter.py +114 -0
  5. qubitclient/draw/pltmanager.py +50 -0
  6. qubitclient/draw/pltplotter.py +20 -0
  7. qubitclient/draw/plymanager.py +57 -0
  8. qubitclient/draw/plyplotter.py +21 -0
  9. qubitclient/draw/powershiftpltplotter.py +108 -0
  10. qubitclient/draw/powershiftplyplotter.py +194 -0
  11. qubitclient/draw/rabicospltplotter.py +74 -0
  12. qubitclient/draw/rabicosplyplotter.py +90 -0
  13. qubitclient/draw/rabipltplotter.py +66 -0
  14. qubitclient/draw/rabiplyplotter.py +86 -0
  15. qubitclient/draw/s21peakpltplotter.py +67 -0
  16. qubitclient/draw/s21peakplyplotter.py +124 -0
  17. qubitclient/draw/s21vfluxpltplotter.py +84 -0
  18. qubitclient/draw/s21vfluxplyplotter.py +163 -0
  19. qubitclient/draw/singleshotpltplotter.py +149 -0
  20. qubitclient/draw/singleshotplyplotter.py +324 -0
  21. qubitclient/draw/spectrum2dpltplotter.py +107 -0
  22. qubitclient/draw/spectrum2dplyplotter.py +244 -0
  23. qubitclient/draw/spectrum2dscopepltplotter.py +72 -0
  24. qubitclient/draw/spectrum2dscopeplyplotter.py +195 -0
  25. qubitclient/draw/spectrumpltplotter.py +106 -0
  26. qubitclient/draw/spectrumplyplotter.py +133 -0
  27. qubitclient/draw/t1fitpltplotter.py +76 -0
  28. qubitclient/draw/t1fitplyplotter.py +109 -0
  29. qubitclient/draw/t2fitpltplotter.py +70 -0
  30. qubitclient/draw/t2fitplyplotter.py +111 -0
  31. qubitclient/nnscope/nnscope.py +51 -0
  32. qubitclient/nnscope/nnscope_api/curve/__init__.py +0 -0
  33. qubitclient/nnscope/nnscope_api/curve/curve_type.py +15 -0
  34. qubitclient/nnscope/task.py +170 -0
  35. qubitclient/nnscope/utils/data_convert.py +114 -0
  36. qubitclient/nnscope/utils/data_parser.py +41 -0
  37. qubitclient/nnscope/utils/request_tool.py +41 -0
  38. qubitclient/nnscope/utils/result_parser.py +55 -0
  39. qubitclient/scope/scope.py +50 -0
  40. qubitclient/scope/scope_api/__init__.py +8 -0
  41. qubitclient/scope/scope_api/api/__init__.py +1 -0
  42. qubitclient/scope/scope_api/api/defined_tasks/__init__.py +1 -0
  43. qubitclient/scope/scope_api/api/defined_tasks/get_task_result_api_v1_tasks_demo_pk_get.py +155 -0
  44. qubitclient/scope/scope_api/api/defined_tasks/get_task_result_api_v1_tasks_scope_pk_get.py +155 -0
  45. qubitclient/scope/scope_api/api/defined_tasks/optpipulse_api_v1_tasks_scope_optpipulse_post.py +218 -0
  46. qubitclient/scope/scope_api/api/defined_tasks/powershift_api_v1_tasks_scope_powershift_post.py +218 -0
  47. qubitclient/scope/scope_api/api/defined_tasks/rabi_api_v1_tasks_scope_rabi_post.py +218 -0
  48. qubitclient/scope/scope_api/api/defined_tasks/rabicos_api_v1_tasks_scope_rabicospeak_post.py +218 -0
  49. qubitclient/scope/scope_api/api/defined_tasks/s21peak_api_v1_tasks_scope_s21peak_post.py +218 -0
  50. qubitclient/scope/scope_api/api/defined_tasks/s21vflux_api_v1_tasks_scope_s21vflux_post.py +218 -0
  51. qubitclient/scope/scope_api/api/defined_tasks/singleshot_api_v1_tasks_scope_singleshot_post.py +218 -0
  52. qubitclient/scope/scope_api/api/defined_tasks/spectrum2d_api_v1_tasks_scope_spectrum2d_post.py +218 -0
  53. qubitclient/scope/scope_api/api/defined_tasks/spectrum_api_v1_tasks_scope_spectrum_post.py +218 -0
  54. qubitclient/scope/scope_api/api/defined_tasks/t1fit_api_v1_tasks_scope_t1fit_post.py +218 -0
  55. qubitclient/scope/scope_api/api/defined_tasks/t1fit_api_v1_tasks_scope_t2fit_post.py +218 -0
  56. qubitclient/scope/scope_api/client.py +268 -0
  57. qubitclient/scope/scope_api/errors.py +16 -0
  58. qubitclient/scope/scope_api/models/__init__.py +31 -0
  59. qubitclient/scope/scope_api/models/body_optpipulse_api_v1_tasks_scope_optpipulse_post.py +83 -0
  60. qubitclient/scope/scope_api/models/body_powershift_api_v1_tasks_scope_powershift_post.py +83 -0
  61. qubitclient/scope/scope_api/models/body_rabi_api_v1_tasks_scope_rabi_post.py +83 -0
  62. qubitclient/scope/scope_api/models/body_rabicos_api_v1_tasks_scope_rabicospeak_post.py +83 -0
  63. qubitclient/scope/scope_api/models/body_s21_peak_api_v1_tasks_scope_s21_peak_post.py +83 -0
  64. qubitclient/scope/scope_api/models/body_s21_vflux_api_v1_tasks_scope_s21_vflux_post.py +83 -0
  65. qubitclient/scope/scope_api/models/body_singleshot_api_v1_tasks_scope_singleshot_post.py +83 -0
  66. qubitclient/scope/scope_api/models/body_spectrum_2d_api_v1_tasks_scope_spectrum_2d_post.py +83 -0
  67. qubitclient/scope/scope_api/models/body_spectrum_api_v1_tasks_scope_spectrum_post.py +83 -0
  68. qubitclient/scope/scope_api/models/body_t1_fit_api_v1_tasks_scope_t1_fit_post.py +83 -0
  69. qubitclient/scope/scope_api/models/body_t1_fit_api_v1_tasks_scope_t2_fit_post.py +83 -0
  70. qubitclient/scope/scope_api/models/http_validation_error.py +75 -0
  71. qubitclient/scope/scope_api/models/validation_error.py +88 -0
  72. qubitclient/scope/scope_api/types.py +54 -0
  73. qubitclient/scope/task.py +163 -0
  74. qubitclient/scope/utils/__init__.py +0 -0
  75. qubitclient/scope/utils/data_parser.py +20 -0
  76. qubitclient-0.1.4.dist-info/METADATA +173 -0
  77. qubitclient-0.1.4.dist-info/RECORD +81 -0
  78. qubitclient-0.1.4.dist-info/WHEEL +5 -0
  79. qubitclient-0.1.4.dist-info/licenses/LICENSE +674 -0
  80. qubitclient-0.1.4.dist-info/top_level.txt +1 -0
  81. qubitclient-0.1.4.dist-info/zip-safe +1 -0
@@ -0,0 +1,163 @@
1
+ from .plyplotter import QuantumDataPlyPlotter
2
+ import numpy as np
3
+ import plotly.graph_objects as go
4
+ from plotly.subplots import make_subplots
5
+
6
+ from scipy.stats import norm
7
+
8
+ class S21VfluxDataPlyPlotter(QuantumDataPlyPlotter):
9
+
10
+ def __init__(self):
11
+ super().__init__("s21vflux")
12
+
13
+ def plot_result_npy(self, **kwargs):
14
+ result = kwargs.get('result')
15
+ dict_param = kwargs.get('dict_param')
16
+
17
+ data = dict_param.item()
18
+ image = data["image"]
19
+ q_list = image.keys()
20
+
21
+ # 数据提取
22
+ volt_list = []
23
+ freq_list = []
24
+ s_list = []
25
+ q_name_list = []
26
+
27
+ for idx, q_name in enumerate(q_list):
28
+ image_q = image[q_name]
29
+ volt = image_q[0]
30
+ freq = image_q[1]
31
+ s = image_q[2]
32
+
33
+ volt_list.append(volt)
34
+ freq_list.append(freq)
35
+ s_list.append(s)
36
+ q_name_list.append(q_name)
37
+
38
+ # 结果数据
39
+ coscurves_list = result['coscurves_list']
40
+ cosconfs_list = result['cosconfs_list']
41
+ lines_list = result['lines_list']
42
+ lineconfs_list = result['lineconfs_list']
43
+
44
+ # 计算子图布局
45
+ nums = len(volt_list) * 2
46
+ rows = (nums // 2) + 1 if nums % 2 != 0 else nums // 2
47
+ cols = 2
48
+
49
+ # 创建子图布局
50
+ fig = make_subplots(
51
+ rows=rows,
52
+ cols=cols,
53
+ shared_xaxes=True, # 共享X轴以增强交互一致性
54
+ shared_yaxes=True,
55
+ subplot_titles=[f"{q_name_list[i // 2]}_Heatmap" if i % 2 == 0
56
+ else f"{q_name_list[i // 2]}_WithCurves" for i in range(nums)],
57
+ horizontal_spacing=0.1,
58
+ vertical_spacing=0.01
59
+ )
60
+
61
+ # 遍历所有子图位置
62
+ for ii in range(nums):
63
+ row_pos = (ii // cols) + 1
64
+ col_pos = (ii % cols) + 1
65
+
66
+ volt = volt_list[ii // 2]
67
+ freq = freq_list[ii // 2]
68
+ s = s_list[ii // 2]
69
+ q_name = q_name_list[ii // 2]
70
+
71
+ # 热力图数据
72
+ heatmap_trace = go.Heatmap(
73
+ x=freq,
74
+ y=volt,
75
+ z=s.T,
76
+ colorscale='Viridis',
77
+ showscale=True,
78
+ colorbar=dict(title='Intensity')
79
+ )
80
+
81
+ fig.add_trace(heatmap_trace, row=row_pos, col=col_pos)
82
+
83
+ # 在奇数编号的子图中添加曲线和线条
84
+ if (ii % 2 != 0):
85
+ centcol = len(freq) // 2
86
+
87
+ # 添加余弦曲线
88
+ for j, curve in enumerate(coscurves_list[ii // 2]):
89
+ if curve: # 确保曲线数据不为空
90
+ final_x_cos = [item[0] for item in curve]
91
+ final_y_cos = [item[1] for item in curve]
92
+
93
+ # 余弦曲线轨迹
94
+ cos_trace = go.Scatter(
95
+ x=final_x_cos,
96
+ y=final_y_cos,
97
+ mode='lines',
98
+ line=dict(color='red', width=2),
99
+ name=f'Cosine Curve {j + 1}'
100
+ )
101
+ fig.add_trace(cos_trace, row=row_pos, col=col_pos)
102
+
103
+ # 添加置信度文本
104
+ if centcol < len(final_x_cos):
105
+ fig.add_annotation(
106
+ x=final_x_cos[centcol],
107
+ y=final_y_cos[centcol],
108
+ text=f"conf:{cosconfs_list[ii // 2][j]:.2f}",
109
+ showarrow=False,
110
+ font=dict(color='red', size=12),
111
+ row=row_pos,
112
+ col=col_pos
113
+ )
114
+
115
+ # 添加直线
116
+ if lines_list[ii // 2]:
117
+ for j, line in enumerate(lines_list[ii // 2]):
118
+ if line: # 确保直线数据不为空
119
+ final_x_line = [item[0] for item in line]
120
+ final_y_line = [item[1] for item in line]
121
+
122
+ line_trace = go.Scatter(
123
+ x=final_x_line,
124
+ y=final_y_line,
125
+ mode='lines',
126
+ line=dict(color='blue', width=2),
127
+ name=f'Line {j + 1}'
128
+ )
129
+ fig.add_trace(line_trace, row=row_pos, col=col_pos)
130
+ if centcol < len(final_x_line):
131
+ fig.add_annotation(
132
+ x=final_x_line[centcol],
133
+ y=final_y_line[centcol],
134
+ text=f"conf:{lineconfs_list[ii // 2][j]:.2f}",
135
+ showarrow=False,
136
+ font=dict(color='red', size=12),
137
+ row=row_pos,
138
+ col=col_pos
139
+ )
140
+ # 更新布局设置
141
+ fig.update_layout(
142
+ height=500 * rows,
143
+ width=900 * cols,
144
+ margin=dict(r=60, t=60, b=60, l=60),
145
+ legend=dict(
146
+ font=dict(family="Courier", size=12, color="black"),
147
+ borderwidth=1
148
+ )
149
+ )
150
+
151
+ # 更新坐标轴标签
152
+ fig.update_xaxes(title_text="频率", row=rows, col=1)
153
+ fig.update_yaxes(title_text="电压", col=1)
154
+
155
+ # 隐藏未使用的子图
156
+ for ii in range(nums, rows * cols):
157
+ row_pos = (ii // cols) + 1
158
+ col_pos = (ii % cols) + 1
159
+ fig.update_xaxes(visible=False, row=row_pos, col=col_pos)
160
+ fig.update_yaxes(visible=False, row=row_pos, col=col_pos)
161
+
162
+ return fig
163
+
@@ -0,0 +1,149 @@
1
+ import numpy as np
2
+ import matplotlib.pyplot as plt
3
+ from .pltplotter import QuantumDataPltPlotter
4
+
5
+ from scipy.stats import norm
6
+
7
+ class SingleShotDataPltPlotter(QuantumDataPltPlotter):
8
+ def __init__(self):
9
+ super().__init__("singleshot")
10
+
11
+ def plotEllipse(self, c0, a, b, phi, ax, **kwargs):
12
+ t = np.linspace(0, 1, 1001) * 2 * np.pi # 生成1001个角度点
13
+ c = np.exp(1j * t) # 计算椭圆坐标点(包含旋转)
14
+ s = c0 + (c.real * a + 1j * c.imag * b) * np.exp(1j * phi)
15
+ ax.plot(s.real, s.imag, **kwargs)
16
+ def plot_result_npy(self, **kwargs):
17
+ result = kwargs.get('result')
18
+ dict_param = kwargs.get('dict_param')
19
+
20
+ dict_param = dict_param.item()
21
+
22
+ image = dict_param["image"]
23
+ q_list = image.keys()
24
+ s0_list = []
25
+ s1_list = []
26
+ q_name_list =[]
27
+
28
+ for idx, q_name in enumerate(q_list):
29
+ image_q = image[q_name]
30
+
31
+ s0 = image_q[0]
32
+ s1 = image_q[1]
33
+ s0_list.append(s0)
34
+ s1_list.append(s1)
35
+ q_name_list.append(q_name)
36
+
37
+
38
+ sep_score_list = result['sep_score_list']
39
+ threshold_list = result['threshold_list']
40
+ phi_list = result['phi_list']
41
+ signal_list = result['signal_list']
42
+ idle_list = result['idle_list']
43
+ params_list = result['params_list']
44
+ std_list = result['std_list']
45
+ cdf_list = result['cdf_list']
46
+ hotThresh = 10000 # 切换为热力图模式的样本数阈值
47
+ nums = len(s0_list) * 2
48
+ row = (nums // 6) + 1 if nums % 6 != 0 else nums // 6
49
+ col = 6
50
+ fig = plt.figure(figsize=(20, 20)) # 创建Figure
51
+ for i in range(len(s0_list)):
52
+ s0 = s0_list[i]
53
+ s1 = s1_list[i]
54
+ _, *bins = np.histogram2d(np.real(np.hstack([s0, s1])),
55
+ np.imag(np.hstack([s0, s1])),
56
+ bins=50) # 构建二维直方图
57
+ H0, *_ = np.histogram2d(np.real(s0),
58
+ np.imag(s0),
59
+ bins=bins,
60
+ density=True) # 计算各组密度分
61
+ H1, *_ = np.histogram2d(np.real(s1),
62
+ np.imag(s1),
63
+ bins=bins,
64
+ density=True)
65
+ vlim = max(np.max(np.abs(H0)), np.max(np.abs(H1))) # 确定可视化范围
66
+
67
+ sep_score = sep_score_list[i]
68
+
69
+ thr = threshold_list[i]
70
+ phi = phi_list[i]
71
+
72
+ # 子图1:复平面图
73
+ ax1 = fig.add_subplot(row, col, 2 * i + 1)
74
+ if (len(s0) + len(s1)) < hotThresh:
75
+ ax1.plot(np.real(s0), np.imag(s0), '.', alpha=0.8)
76
+ ax1.plot(np.real(s1), np.imag(s1), '.', alpha=0.8)
77
+ else:
78
+ ax1.imshow(H1.T - H0.T,
79
+ alpha=(np.fmax(H0.T, H1.T) / vlim).clip(0, 1),
80
+ interpolation='nearest',
81
+ origin='lower',
82
+ cmap='coolwarm',
83
+ vmin=-vlim,
84
+ vmax=vlim,
85
+ extent=(bins[0][0], bins[0][-1], bins[1][0], bins[1][-1]))
86
+ ax1.text(0.95, 0.95, f'Separation Degree: {sep_score:.3f}',
87
+ transform=ax1.transAxes, ha='right', va='top',
88
+ bbox=dict(facecolor='white', alpha=0.8, edgecolor='gray'))
89
+ ax1.axis('equal')
90
+ for s in ax1.spines.values():
91
+ s.set_visible(False)
92
+
93
+ # 椭圆参数提取和绘制
94
+ params = params_list[i]
95
+ r0, i0, r1, i1 = params[0][0], params[1][0], params[0][1], params[1][1]
96
+ a0, b0, a1, b1 = params[0][2], params[1][2], params[0][3], params[1][3]
97
+ c0 = (r0 + 1j * i0) * np.exp(1j * phi)
98
+ c1 = (r1 + 1j * i1) * np.exp(1j * phi)
99
+ phi0 = phi + params[0][6]
100
+ phi1 = phi + params[1][6]
101
+ self.plotEllipse(c0, 2 * a0, 2 * b0, phi0, ax1)
102
+ self.plotEllipse(c1, 2 * a1, 2 * b1, phi1, ax1)
103
+ im0, im1 = idle_list[i]
104
+ im0 = np.array(im0)
105
+ im1 = np.array(im1)
106
+ lim = min(im0.min(), im1.min()), max(im0.max(), im1.max())
107
+ t = (np.linspace(lim[0], lim[1], 3) + 1j * thr) * np.exp(-1j * phi)
108
+ ax1.plot(t.imag, t.real, 'k--')
109
+ ax1.plot(np.real(c0), np.imag(c0), 'o', color='C3')
110
+ ax1.plot(np.real(c1), np.imag(c1), 'o', color='C4')
111
+ ax1.axis('off')
112
+
113
+ # 子图2:投影信号分布图 + CDF
114
+ re0, re1 = signal_list[i]
115
+ x, a, b, c = cdf_list[i]
116
+ re0 = np.array(re0)
117
+ re1 = np.array(re1)
118
+ xrange = (min(re0.min(), re1.min()), max(re0.max(), re1.max()))
119
+
120
+ ax2 = fig.add_subplot(row, col, 2 * i + 2)
121
+ n0, bins0, *_ = ax2.hist(re0, bins=100, density=True, range=xrange, alpha=0.5)
122
+ n1, bins1, *_ = ax2.hist(re1, bins=100, density=True, range=xrange, alpha=0.5)
123
+
124
+ mu1_y, std1_y = norm.fit(re0)
125
+ mu2_y, std2_y = norm.fit(re1)
126
+ y_range = np.linspace(min(min(re0), min(re1)), max(max(re0), max(re1)), 100)
127
+ pdf1_y = norm.pdf(y_range, mu1_y, std1_y)
128
+ pdf2_y = norm.pdf(y_range, mu2_y, std2_y)
129
+ ax2.plot(y_range, pdf1_y, 'b-', linewidth=2)
130
+ ax2.plot(y_range, pdf2_y, 'r-', linewidth=2)
131
+
132
+ x = np.array(x)
133
+ x_range = np.linspace(x.min(), x.max(), 1001)
134
+ *_, cov0, cov1 = std_list[i]
135
+
136
+ ax3 = ax2.twinx()
137
+ ax3.plot(x, a, '--', lw=1, color='C0')
138
+ ax3.plot(x, b, '--', lw=1, color='C1')
139
+ ax3.plot(x, c, 'k--', alpha=0.5, lw=1)
140
+ ax3.set_ylim(0, 1.1)
141
+ ax3.vlines(thr, 0, 1.1, 'k', alpha=0.5)
142
+ ax3.set_ylabel('Probability')
143
+
144
+ ax2.axis('off')
145
+
146
+ plt.tight_layout()
147
+ return fig # ✅ 返回 Figure 对象
148
+
149
+
@@ -0,0 +1,324 @@
1
+ from .plyplotter import QuantumDataPlyPlotter
2
+ import numpy as np
3
+ import plotly.graph_objects as go
4
+ from plotly.subplots import make_subplots
5
+ from scipy.stats import norm
6
+
7
+ class SingleShotDataPlyPlotter(QuantumDataPlyPlotter):
8
+
9
+ def __init__(self):
10
+ super().__init__("singleshot")
11
+
12
+ def plotEllipse(self, c0, a, b, phi):
13
+ """生成椭圆坐标点"""
14
+ t = np.linspace(0, 1, 1001) * 2 * np.pi
15
+ c = np.exp(1j * t)
16
+ s = c0 + (c.real * a + 1j * c.imag * b) * np.exp(1j * phi)
17
+ return s.real, s.imag
18
+
19
+ def plot_result_npy(self, **kwargs):
20
+
21
+ result = kwargs.get('result')
22
+ dict_param = kwargs.get('dict_param')
23
+
24
+ dict_param = dict_param.item()
25
+ image = dict_param["image"]
26
+ q_list = image.keys()
27
+ s0_list = []
28
+ s1_list = []
29
+ q_name_list = []
30
+
31
+ # 提取数据
32
+ for idx, q_name in enumerate(q_list):
33
+ image_q = image[q_name]
34
+ s0 = image_q[0]
35
+ s1 = image_q[1]
36
+ s0_list.append(s0)
37
+ s1_list.append(s1)
38
+ q_name_list.append(q_name)
39
+
40
+ sep_score_list = result['sep_score_list']
41
+ threshold_list = result['threshold_list']
42
+ phi_list = result['phi_list']
43
+ signal_list = result['signal_list']
44
+ idle_list = result['idle_list']
45
+ params_list = result['params_list']
46
+ cdf_list = result['cdf_list']
47
+ num_qubits = len(s0_list)
48
+
49
+ # 创建子图布局 - 每行最多3个量子比特,每个量子比特2个子图
50
+ rows = (num_qubits + 2) // 3 # 每行最多3个量子比特
51
+ cols = 6 # 每个量子比特2列
52
+
53
+ # 创建子图
54
+ fig = make_subplots(
55
+ rows=rows,
56
+ cols=cols,
57
+ subplot_titles=[f'{q_name_list[i // 2]} - {"复平面" if i % 2 == 0 else "投影分布"}'
58
+ for i in range(rows * cols)],
59
+ horizontal_spacing=0.05,
60
+ vertical_spacing=0.08
61
+ )
62
+
63
+ hotThresh = 10000
64
+
65
+ for i in range(len(s0_list)):
66
+ s0 = s0_list[i]
67
+ s1 = s1_list[i]
68
+
69
+ sep_score = sep_score_list[i]
70
+ thr = threshold_list[i]
71
+ phi = phi_list[i]
72
+
73
+ # 计算行和列位置
74
+ row_pos = (i // 3) + 1
75
+ col_pos_left = (i % 3) * 2 + 1
76
+ col_pos_right = col_pos_left + 1
77
+
78
+ # 子图1:复平面图
79
+ if (len(s0) + len(s1)) < hotThresh:
80
+ # 散点图模式
81
+ fig.add_trace(
82
+ go.Scatter(
83
+ x=np.real(s0), y=np.imag(s0),
84
+ mode='markers',
85
+ marker=dict(size=7, color='blue', opacity=0.6),
86
+ name=f'{q_name_list[i]}|0⟩'
87
+ ),
88
+ row=row_pos, col=col_pos_left
89
+ )
90
+
91
+ fig.add_trace(
92
+ go.Scatter(
93
+ x=np.real(s1), y=np.imag(s1),
94
+ mode='markers',
95
+ marker=dict(size=7, color='red', opacity=0.6),
96
+ name=f'{q_name_list[i]}|1⟩'
97
+ ),
98
+ row=row_pos, col=col_pos_left
99
+ )
100
+ else:
101
+ # 热力图模式
102
+ _, *bins = np.histogram2d(
103
+ np.real(np.hstack([s0, s1])),
104
+ np.imag(np.hstack([s0, s1])),
105
+ bins=50
106
+ )
107
+ H0, *_ = np.histogram2d(
108
+ np.real(s0), np.imag(s0),
109
+ bins=bins, density=True
110
+ )
111
+ H1, *_ = np.histogram2d(
112
+ np.real(s1), np.imag(s1),
113
+ bins=bins, density=True
114
+ )
115
+
116
+ heatmap_data = H1.T - H0.T
117
+ fig.add_trace(
118
+ go.Heatmap(
119
+ z=heatmap_data,
120
+ x=bins[0],
121
+ y=bins[1],
122
+ colorscale='RdBu',
123
+ showscale=False
124
+ ),
125
+ row=row_pos, col=col_pos_left
126
+ )
127
+
128
+ # 添加分离度文本注释
129
+ fig.add_annotation(
130
+ x=0.95, y=0.95,
131
+ xref="paper",
132
+ yref="paper",
133
+ text=f'Separation: {sep_score:.3f}',
134
+ showarrow=False,
135
+ bgcolor="white",
136
+ bordercolor="gray",
137
+ borderwidth=1,
138
+ row=row_pos, col=col_pos_left
139
+ )
140
+
141
+ # 椭圆绘制
142
+ params = params_list[i]
143
+ r0, i0, r1, i1 = params[0][0], params[1][0], params[0][1], params[1][1]
144
+ a0, b0, a1, b1 = params[0][2], params[1][2], params[0][3], params[1][3]
145
+ c0 = (r0 + 1j * i0) * np.exp(1j * phi)
146
+ c1 = (r1 + 1j * i1) * np.exp(1j * phi)
147
+ phi0 = phi + params[0][6]
148
+ phi1 = phi + params[1][6]
149
+
150
+ # 绘制两个椭圆
151
+ ellipse0_x, ellipse0_y = self.plotEllipse(c0, 2 * a0, 2 * b0, phi0)
152
+ ellipse1_x, ellipse1_y = self.plotEllipse(c1, 2 * a1, 2 * b1, phi1)
153
+
154
+ fig.add_trace(
155
+ go.Scatter(
156
+ x=ellipse0_x, y=ellipse0_y,
157
+ mode='lines',
158
+ line=dict(color='darkblue', width=2),
159
+ showlegend=False
160
+ ),
161
+ row=row_pos, col=col_pos_left
162
+ )
163
+
164
+ fig.add_trace(
165
+ go.Scatter(
166
+ x=ellipse1_x, y=ellipse1_y,
167
+ mode='lines',
168
+ line=dict(color='darkred', width=2),
169
+ showlegend=False
170
+ ),
171
+ row=row_pos, col=col_pos_left
172
+ )
173
+
174
+ im0, im1 = idle_list[i]
175
+ im0 = np.array(im0)
176
+ im1 = np.array(im1)
177
+ lim = min(im0.min(), im1.min()), max(im0.max(), im1.max())
178
+ t = (np.linspace(lim[0], lim[1], 3) + 1j * thr) * np.exp(-1j * phi)
179
+ fig.add_trace(go.Scatter(
180
+ x=t.imag, # x轴数据
181
+ y=t.real, # y轴数据
182
+ mode='lines',
183
+ line=dict(dash='dash', color='black')),
184
+ row=row_pos,
185
+ col=col_pos_left
186
+ )
187
+ # 绘制中心点
188
+ fig.add_trace(
189
+ go.Scatter(
190
+ x=[np.real(c0)], y=[np.imag(c0)],
191
+ mode='markers',
192
+ marker=dict(size=8, color='darkblue', symbol='circle'),
193
+ showlegend=False
194
+ ),
195
+ row=row_pos, col=col_pos_left
196
+ )
197
+
198
+ fig.add_trace(
199
+ go.Scatter(
200
+ x=[np.real(c1)], y=[np.imag(c1)],
201
+ mode='markers',
202
+ marker=dict(size=8, color='darkred', symbol='circle'),
203
+ showlegend=False
204
+ ),
205
+ row=row_pos, col=col_pos_left
206
+ )
207
+
208
+ # 子图2:投影信号分布图
209
+ re0, re1 = signal_list[i]
210
+ x, a, b, c = cdf_list[i]
211
+ re0 = np.array(re0)
212
+ re1 = np.array(re1)
213
+ xrange = (min(re0.min(), re1.min()), max(re0.max(), re1.max()))
214
+
215
+ # 直方图
216
+ fig.add_trace(
217
+ go.Histogram(
218
+ x=re0,
219
+ nbinsx=100,
220
+ opacity=0.5,
221
+ xbins=dict(start=xrange[0], end=xrange[1]), # 对应 range=xrange
222
+ name='|0⟩',
223
+ marker_color='blue',
224
+ histnorm="probability density"
225
+ ),
226
+ row=row_pos, col=col_pos_right
227
+ )
228
+
229
+ fig.add_trace(
230
+ go.Histogram(
231
+ x=re1,
232
+ nbinsx=100,
233
+ opacity=0.5,
234
+ xbins=dict(start=xrange[0], end=xrange[1]), # 对应 range=xrange
235
+ name='|1⟩',
236
+ marker_color='red',
237
+ histnorm="probability density"
238
+ ),
239
+ row=row_pos, col=col_pos_right
240
+ )
241
+
242
+ # 高斯拟合曲线
243
+ mu1_y, std1_y = norm.fit(re0)
244
+ mu2_y, std2_y = norm.fit(re1)
245
+ y_range = np.linspace(
246
+ min(min(re0), min(re1)),
247
+ max(max(re0), max(re1)),
248
+ 100
249
+ )
250
+ pdf1_y = norm.pdf(y_range, mu1_y, std1_y)
251
+ pdf2_y = norm.pdf(y_range, mu2_y, std2_y)
252
+
253
+ fig.add_trace(
254
+ go.Scatter(
255
+ x=y_range, y=pdf1_y,
256
+ mode='lines',
257
+ line=dict(color='darkblue', width=3),
258
+ name='|0⟩拟合'
259
+ ),
260
+ row=row_pos, col=col_pos_right
261
+ )
262
+
263
+ fig.add_trace(
264
+ go.Scatter(
265
+ x=y_range, y=pdf2_y,
266
+ mode='lines',
267
+ line=dict(color='darkred', width=3),
268
+ name='|1⟩拟合'
269
+ ),
270
+ row=row_pos, col=col_pos_right
271
+ )
272
+ #
273
+ # CDF 曲线 - 使用次坐标轴
274
+ fig.add_trace(
275
+ go.Scatter(
276
+ x=x, y=a,
277
+ mode='lines',
278
+ line=dict(color='blue', width=2, dash='dash'),
279
+ name='|0⟩ CDF',
280
+ yaxis=f"y{len(fig.data)}"
281
+ ),
282
+ row=row_pos, col=col_pos_right
283
+ )
284
+
285
+ fig.add_trace(
286
+ go.Scatter(
287
+ x=x, y=b,
288
+ mode='lines',
289
+ line=dict(color='red', width=2, dash='dash'),
290
+ name='|1⟩ CDF',
291
+ yaxis=f"y{len(fig.data)}"
292
+ ),
293
+ row=row_pos, col=col_pos_right
294
+ )
295
+
296
+ fig.add_trace(
297
+ go.Scatter(
298
+ x=x, y=c,
299
+ mode='lines',
300
+ line=dict(color='black', width=2, dash='dot'),
301
+ name='阈值 CDF',
302
+ yaxis=f"y{len(fig.data)}"
303
+ ),
304
+ row=row_pos, col=col_pos_right
305
+ )
306
+
307
+ # 阈值线
308
+ fig.add_vline(
309
+ x=thr,
310
+ line_dash="dash",
311
+ line_color="black",
312
+ opacity=0.7,
313
+ row=row_pos, col=col_pos_right
314
+ )
315
+
316
+ # 更新布局
317
+ fig.update_layout(
318
+ height=400 * rows,
319
+ width=1600,
320
+ showlegend=False
321
+ )
322
+
323
+
324
+ return fig