quarkcircuit 0.3.0.dev2__tar.gz → 0.3.2__tar.gz
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.
- {quarkcircuit-0.3.0.dev2 → quarkcircuit-0.3.2}/PKG-INFO +1 -1
- {quarkcircuit-0.3.0.dev2 → quarkcircuit-0.3.2}/pyproject.toml +1 -1
- {quarkcircuit-0.3.0.dev2 → quarkcircuit-0.3.2}/quark/circuit/backend.py +23 -19
- {quarkcircuit-0.3.0.dev2 → quarkcircuit-0.3.2}/quark/circuit/decompose.py +35 -1
- {quarkcircuit-0.3.0.dev2 → quarkcircuit-0.3.2}/quark/circuit/layout.py +150 -125
- {quarkcircuit-0.3.0.dev2 → quarkcircuit-0.3.2}/quark/circuit/matrix.py +12 -1
- {quarkcircuit-0.3.0.dev2 → quarkcircuit-0.3.2}/quark/circuit/quantumcircuit.py +95 -24
- {quarkcircuit-0.3.0.dev2 → quarkcircuit-0.3.2}/quark/circuit/quantumcircuit_helpers.py +75 -36
- quarkcircuit-0.3.2/quark/circuit/routing.py +660 -0
- {quarkcircuit-0.3.0.dev2 → quarkcircuit-0.3.2}/quark/circuit/test_transpiler.py +7 -4
- {quarkcircuit-0.3.0.dev2 → quarkcircuit-0.3.2}/quark/circuit/translate.py +5 -0
- {quarkcircuit-0.3.0.dev2 → quarkcircuit-0.3.2}/quark/circuit/transpiler.py +20 -6
- {quarkcircuit-0.3.0.dev2 → quarkcircuit-0.3.2}/quarkcircuit.egg-info/PKG-INFO +1 -1
- quarkcircuit-0.3.0.dev2/quark/circuit/routing.py +0 -455
- {quarkcircuit-0.3.0.dev2 → quarkcircuit-0.3.2}/README.md +0 -0
- {quarkcircuit-0.3.0.dev2 → quarkcircuit-0.3.2}/quark/circuit/__init__.py +0 -0
- {quarkcircuit-0.3.0.dev2 → quarkcircuit-0.3.2}/quark/circuit/basepasses.py +0 -0
- {quarkcircuit-0.3.0.dev2 → quarkcircuit-0.3.2}/quark/circuit/dag.py +0 -0
- {quarkcircuit-0.3.0.dev2 → quarkcircuit-0.3.2}/quark/circuit/optimize.py +0 -0
- {quarkcircuit-0.3.0.dev2 → quarkcircuit-0.3.2}/quark/circuit/utils.py +0 -0
- {quarkcircuit-0.3.0.dev2 → quarkcircuit-0.3.2}/quarkcircuit.egg-info/SOURCES.txt +0 -0
- {quarkcircuit-0.3.0.dev2 → quarkcircuit-0.3.2}/quarkcircuit.egg-info/dependency_links.txt +0 -0
- {quarkcircuit-0.3.0.dev2 → quarkcircuit-0.3.2}/quarkcircuit.egg-info/requires.txt +0 -0
- {quarkcircuit-0.3.0.dev2 → quarkcircuit-0.3.2}/quarkcircuit.egg-info/top_level.txt +0 -0
- {quarkcircuit-0.3.0.dev2 → quarkcircuit-0.3.2}/setup.cfg +0 -0
|
@@ -56,7 +56,7 @@ class Backend:
|
|
|
56
56
|
self.priority_qubits = self.chip_info['priority_qubits']
|
|
57
57
|
self.qubits_with_attributes = self._collect_qubits_with_attributes()
|
|
58
58
|
self.couplers_with_attributes = self._collect_couplers_with_attributes()
|
|
59
|
-
elif chip in ['Baihua','Dongling','Haituo','Yunmeng','Miaofeng',]:
|
|
59
|
+
elif chip in ['Baihua','Dongling','Haituo','Yunmeng','Miaofeng','Yudu','Hongluo']:
|
|
60
60
|
self.chip_name = chip
|
|
61
61
|
try:
|
|
62
62
|
self.chip_info = load_chip_basic_info(chip)
|
|
@@ -140,14 +140,14 @@ class Backend:
|
|
|
140
140
|
G.add_edges_from(self.couplers_with_attributes)
|
|
141
141
|
return G
|
|
142
142
|
|
|
143
|
-
def draw(self,show_couplers_fidelity:bool = False,
|
|
143
|
+
def draw(self,show_couplers_fidelity:bool = False, show_qubits_attributes:Literal['T1','T2','fidelity','frequency','']='',
|
|
144
144
|
show_qubits_index:bool=False,show_couplers_index:bool=False,
|
|
145
|
-
highlight_nodes:list = [], save_svg_fname: str|None = None
|
|
145
|
+
highlight_nodes:list = [], save_svg_fname: str|None = None):
|
|
146
146
|
"""Draw the chip layout.
|
|
147
147
|
|
|
148
148
|
Args:
|
|
149
149
|
show_couplers_fidelity (bool, optional): Whether to display the fidelity of couplers. Defaults to False.
|
|
150
|
-
|
|
150
|
+
show_qubits_attributes (Literal['T1', 'T2', 'fidelity', 'frequancy', ''], optional):
|
|
151
151
|
Specify which qubit attribute to display. Options include:
|
|
152
152
|
- 'T1': Display the T1 time of qubits (in microseconds).
|
|
153
153
|
- 'T2': Display the T2 time of qubits (in microseconds).
|
|
@@ -167,7 +167,7 @@ class Backend:
|
|
|
167
167
|
|
|
168
168
|
Notes:
|
|
169
169
|
- `highlight_nodes` specifies the qubits to be visually highlighted.
|
|
170
|
-
- `show_couplers_fidelity` and `
|
|
170
|
+
- `show_couplers_fidelity` and `show_qubits_attributes` can be enabled simultaneously without conflicts.
|
|
171
171
|
- When `save_svg_fname` is provided, the drawing will be saved as an SVG file.
|
|
172
172
|
"""
|
|
173
173
|
import matplotlib.pyplot as plt
|
|
@@ -176,15 +176,15 @@ class Backend:
|
|
|
176
176
|
|
|
177
177
|
|
|
178
178
|
edge_fidelity = nx.get_edge_attributes(self.graph, 'fidelity')
|
|
179
|
-
node_attributes = nx.get_node_attributes(self.graph,
|
|
179
|
+
node_attributes = nx.get_node_attributes(self.graph,show_qubits_attributes)
|
|
180
180
|
if show_couplers_fidelity:
|
|
181
|
-
if set(list(edge_fidelity.values())) == {
|
|
181
|
+
if set(list(edge_fidelity.values())) == {0}:
|
|
182
182
|
print('The two-qubit gate fidelity is N/A now.')
|
|
183
183
|
is_edge_info_avaliable = False
|
|
184
184
|
else:
|
|
185
185
|
is_edge_info_avaliable = True
|
|
186
|
-
if
|
|
187
|
-
if set(list(node_attributes.values())) == {
|
|
186
|
+
if show_qubits_attributes:
|
|
187
|
+
if set(list(node_attributes.values())) == {0}:
|
|
188
188
|
print('The qubit attributes is N/A now.')
|
|
189
189
|
is_node_info_avaliable = False
|
|
190
190
|
else:
|
|
@@ -213,13 +213,13 @@ class Backend:
|
|
|
213
213
|
else:
|
|
214
214
|
edge_labels[k] = self.graph.edges[k].get('index')
|
|
215
215
|
|
|
216
|
-
if
|
|
216
|
+
if show_qubits_attributes != '' and is_node_info_avaliable is True:
|
|
217
217
|
min_attributes = sorted(list(node_attributes.values()))[0]
|
|
218
218
|
max_attributes = sorted(list(node_attributes.values()))[-1]
|
|
219
219
|
node_norm = Normalize(vmin = min_attributes, vmax = max_attributes)
|
|
220
220
|
node_cmap = LinearSegmentedColormap.from_list('truncated_blues', plt.get_cmap('Blues')(np.linspace(0.31, 1.0, 1000))) #plt.get_cmap('Blues')
|
|
221
221
|
node_colors = [ScalarMappable(norm = node_norm, cmap = node_cmap).to_rgba(attribute) for attribute in node_attributes.values()]
|
|
222
|
-
if
|
|
222
|
+
if show_qubits_attributes == 'fidelity':
|
|
223
223
|
if show_qubits_index:
|
|
224
224
|
node_labels = {node:node for node in self.graph.nodes()}
|
|
225
225
|
else:
|
|
@@ -244,6 +244,8 @@ class Backend:
|
|
|
244
244
|
figsize = (15, 13)
|
|
245
245
|
|
|
246
246
|
if len(highlight_nodes) > 0:
|
|
247
|
+
if isinstance(highlight_nodes, list) and any(isinstance(i, list) for i in highlight_nodes):
|
|
248
|
+
highlight_nodes = list(set(e for sub in highlight_nodes for e in sub))
|
|
247
249
|
node_colors_update = []
|
|
248
250
|
node_labels_update = {}
|
|
249
251
|
for idx, node in enumerate(self.graph.nodes()):
|
|
@@ -273,16 +275,16 @@ class Backend:
|
|
|
273
275
|
pos = nx.get_node_attributes(self.graph, 'coordinate')
|
|
274
276
|
nx.draw(self.graph, pos,ax=ax, with_labels=False, node_color=node_colors, node_size=800, edgecolors='white',edge_color = edge_colors ,width = 18,)
|
|
275
277
|
nx.draw_networkx_labels(self.graph,pos,labels=node_labels,font_size=node_font_size, font_color='white')
|
|
276
|
-
nx.draw_networkx_edge_labels(self.graph, pos, edge_labels=edge_labels,font_size=8, font_color='white',bbox=dict(facecolor='none', edgecolor='none'))
|
|
278
|
+
nx.draw_networkx_edge_labels(self.graph, pos, edge_labels=edge_labels,font_size=8, font_color='white',bbox=dict(facecolor='none', edgecolor='none'),rotate=False)
|
|
277
279
|
|
|
278
|
-
if
|
|
279
|
-
if
|
|
280
|
+
if show_qubits_attributes != '' and is_node_info_avaliable is True:
|
|
281
|
+
if show_qubits_attributes == 'T1':
|
|
280
282
|
show_label = 'T1 ($\mu s$)'
|
|
281
|
-
elif
|
|
283
|
+
elif show_qubits_attributes == 'T2':
|
|
282
284
|
show_label = 'T2 ($\mu s$)'
|
|
283
|
-
elif
|
|
285
|
+
elif show_qubits_attributes == 'fidelity':
|
|
284
286
|
show_label = 'Single qubit gate fidelity'
|
|
285
|
-
elif
|
|
287
|
+
elif show_qubits_attributes == 'frequency':
|
|
286
288
|
show_label = 'Frequency (GHz)'
|
|
287
289
|
node_sm = ScalarMappable(cmap=node_cmap, norm=node_norm)
|
|
288
290
|
node_sm.set_array(list(node_attributes.values())) # 设置包含数据的数组
|
|
@@ -298,12 +300,14 @@ class Backend:
|
|
|
298
300
|
edge_cbar = fig.colorbar(edge_sm, ax=ax, orientation='horizontal', pad=0.001, fraction=0.0333, aspect=25)# 调整颜色条大小和位置,放置在底部
|
|
299
301
|
edge_cbar.set_label('CZ fidelity')
|
|
300
302
|
|
|
301
|
-
xlim = ax.get_xlim()
|
|
303
|
+
xlim = ax.get_xlim()
|
|
302
304
|
ylim = ax.get_ylim()
|
|
303
305
|
xpos = (xlim[0] + xlim[1]) / 2
|
|
304
|
-
ypos = ylim[1] * 0.
|
|
306
|
+
ypos = ylim[0] - (ylim[1] - ylim[0]) * -0.03 # 计算标题位置
|
|
305
307
|
ax.text(xpos, ypos, f'{self.chip_name}', va='center', ha='center', fontsize=24, fontweight='bold', color='k', family='serif')
|
|
306
308
|
|
|
309
|
+
ax.invert_yaxis() # y 轴正半轴朝下
|
|
310
|
+
|
|
307
311
|
if save_svg_fname:
|
|
308
312
|
plt.savefig(save_svg_fname + '.svg', transparent=True, dpi=300, bbox_inches='tight')
|
|
309
313
|
plt.show()
|
|
@@ -147,7 +147,7 @@ def cx_decompose(control_qubit: int, target_qubit: int,convert_single_qubit_gate
|
|
|
147
147
|
return gates
|
|
148
148
|
|
|
149
149
|
def cy_decompose(control_qubit: int, target_qubit: int, convert_single_qubit_gate_to_u:bool, two_qubit_gate_basis:Literal['cz','cx']) -> list:
|
|
150
|
-
""" Decompose CY gate
|
|
150
|
+
""" Decompose CY gate to U3 gates and CZ gates.
|
|
151
151
|
|
|
152
152
|
Args:
|
|
153
153
|
control_qubit (int): The qubit used as control.
|
|
@@ -366,6 +366,40 @@ def rzz_decompose(theta:float, qubit1:int, qubit2:int, convert_single_qubit_gate
|
|
|
366
366
|
gates += cx_decompose(qubit1,qubit2,convert_single_qubit_gate_to_u,two_qubit_gate_basis)
|
|
367
367
|
return gates
|
|
368
368
|
|
|
369
|
+
|
|
370
|
+
def cp_decompose(theta:float, control_qubit:int, target_qubit:int, convert_single_qubit_gate_to_u:bool) -> list:
|
|
371
|
+
"""Decompose CPhase gate to U3 gates and CZ gates. ref: Quantum Sci. Technol. 7 (2022) 025021
|
|
372
|
+
|
|
373
|
+
Args:
|
|
374
|
+
theta (float): The rotation angle of the gate.
|
|
375
|
+
control_qubit (int): The qubit used as control.
|
|
376
|
+
target_qubit (int): The qubit targeted by the gate.
|
|
377
|
+
|
|
378
|
+
Returns:
|
|
379
|
+
list: A list of U3 gates and CZ gates.
|
|
380
|
+
"""
|
|
381
|
+
gates = []
|
|
382
|
+
if convert_single_qubit_gate_to_u:
|
|
383
|
+
gates.append(h2u(target_qubit))
|
|
384
|
+
else:
|
|
385
|
+
gates.append(('h',target_qubit))
|
|
386
|
+
gates.append(('cz', control_qubit, target_qubit))
|
|
387
|
+
if convert_single_qubit_gate_to_u:
|
|
388
|
+
gates.append(rx2u(theta/2,target_qubit))
|
|
389
|
+
else:
|
|
390
|
+
gates.append(('rx',theta/2,target_qubit))
|
|
391
|
+
gates.append(('cz', control_qubit, target_qubit))
|
|
392
|
+
if convert_single_qubit_gate_to_u:
|
|
393
|
+
gates.append(rz2u(-1*theta/2, control_qubit))
|
|
394
|
+
gates.append(h2u(target_qubit))
|
|
395
|
+
gates.append(rz2u(-1*theta/2,target_qubit))
|
|
396
|
+
else:
|
|
397
|
+
gates.append(('rz',-1*theta/2,control_qubit))
|
|
398
|
+
gates.append(('h',target_qubit))
|
|
399
|
+
gates.append(('rz',-1*theta/2,target_qubit))
|
|
400
|
+
return gates
|
|
401
|
+
|
|
402
|
+
|
|
369
403
|
def ccx_decompose(control_qubit1: int, control_qubit2: int, target_qubit: int,):
|
|
370
404
|
"""Decompose ccx gate. Reference: A biological sequence comparison algorithm using quantum computers
|
|
371
405
|
|
|
@@ -29,16 +29,14 @@ import numpy as np
|
|
|
29
29
|
from typing import Literal
|
|
30
30
|
from itertools import combinations,zip_longest,product
|
|
31
31
|
from multiprocessing import Pool
|
|
32
|
-
from functools import partial
|
|
32
|
+
from functools import partial
|
|
33
33
|
from .backend import Backend
|
|
34
34
|
|
|
35
35
|
class Layout:
|
|
36
36
|
"""
|
|
37
37
|
Responsible for selecting suitable qubit layouts from a given chip for a quantum circuit.
|
|
38
38
|
|
|
39
|
-
This class generates qubit layouts based on the required number of qubits, performance metrics,
|
|
40
|
-
and the topology of the chip. It is designed to help map and execute quantum circuits on
|
|
41
|
-
specific quantum hardware.
|
|
39
|
+
This class generates qubit layouts based on the required number of qubits, performance metrics, and the topology of the chip. It is designed to help map and execute quantum circuits on specific quantum hardware.
|
|
42
40
|
"""
|
|
43
41
|
def __init__(self,nqubits: int, chip_backend: Backend):
|
|
44
42
|
"""Initialize the Layout class with the required number of qubits and chip backend.
|
|
@@ -51,11 +49,11 @@ class Layout:
|
|
|
51
49
|
self.nqubits = nqubits
|
|
52
50
|
self.size = chip_backend.size
|
|
53
51
|
self.priority_qubits = chip_backend.priority_qubits
|
|
54
|
-
self.graph = chip_backend.edge_filtered_graph()
|
|
52
|
+
self.graph = chip_backend.edge_filtered_graph(thres=0.95)
|
|
55
53
|
self.ncore = os.cpu_count() // 2
|
|
56
54
|
self.fidelity_mean_threshold = 0.9
|
|
55
|
+
self.edge_fidelitys = nx.get_edge_attributes(self.graph,'fidelity')
|
|
57
56
|
|
|
58
|
-
@lru_cache(maxsize=128)
|
|
59
57
|
def _get_node_neighbours(self,node:int):
|
|
60
58
|
return list(self.graph.neighbors(node))
|
|
61
59
|
|
|
@@ -154,7 +152,7 @@ class Layout:
|
|
|
154
152
|
"""
|
|
155
153
|
subgraph = self.graph.subgraph(nodes)
|
|
156
154
|
subgraph_degree = dict(subgraph.degree())
|
|
157
|
-
subgraph_fidelity = np.array([
|
|
155
|
+
subgraph_fidelity = np.array([self.edge_fidelitys[(min(edge),max(edge))] for edge in subgraph.edges])
|
|
158
156
|
fidelity_mean = np.mean(subgraph_fidelity)
|
|
159
157
|
fidelity_var = np.var(subgraph_fidelity)
|
|
160
158
|
if fidelity_mean >= self.fidelity_mean_threshold:
|
|
@@ -162,7 +160,7 @@ class Layout:
|
|
|
162
160
|
return nodes_info
|
|
163
161
|
else:
|
|
164
162
|
return None
|
|
165
|
-
|
|
163
|
+
|
|
166
164
|
def collect_all_subgraph_info_in_parallel(self):
|
|
167
165
|
"""Collects information about all subgraphs in parallel.
|
|
168
166
|
|
|
@@ -188,9 +186,8 @@ class Layout:
|
|
|
188
186
|
tuple[list, list, list, list]: A tuple containing four lists, each corresponding
|
|
189
187
|
to one of the four categories of subgraphs.
|
|
190
188
|
"""
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
linear2_subgraph_list = []
|
|
189
|
+
|
|
190
|
+
linear_subgraph_list = []
|
|
194
191
|
nonlinear_subgraph_list = []
|
|
195
192
|
all_subgraph_info = self.collect_all_subgraph_info_in_parallel()
|
|
196
193
|
|
|
@@ -198,22 +195,10 @@ class Layout:
|
|
|
198
195
|
nodes,subgraph_degree,fidelity_mean,fidelity_var = subgraph_info
|
|
199
196
|
nodes_info = (nodes, fidelity_mean, fidelity_var)
|
|
200
197
|
if max(subgraph_degree.values()) <= 2:
|
|
201
|
-
|
|
202
|
-
cycle_subgraph_list.append(nodes_info)
|
|
203
|
-
else:
|
|
204
|
-
in_a_line = []
|
|
205
|
-
for node1, node2 in combinations(nodes,2):
|
|
206
|
-
if abs(node1-node2) <= (self.size[1]-1):
|
|
207
|
-
in_a_line.append(True)
|
|
208
|
-
else:
|
|
209
|
-
in_a_line.append(False)
|
|
210
|
-
if len(set(in_a_line)) == 1:
|
|
211
|
-
linear1_subgraph_list.append(nodes_info)
|
|
212
|
-
else:
|
|
213
|
-
linear2_subgraph_list.append(nodes_info)
|
|
198
|
+
linear_subgraph_list.append(nodes_info)
|
|
214
199
|
else:
|
|
215
200
|
nonlinear_subgraph_list.append(nodes_info)
|
|
216
|
-
return
|
|
201
|
+
return linear_subgraph_list,nonlinear_subgraph_list
|
|
217
202
|
|
|
218
203
|
def sort_subgraph_according_mean_fidelity(self, num:int=1, printdetails: bool = True):
|
|
219
204
|
"""Sort each of the four subgraph categories based on the main of fidelity on the edges (couplers),
|
|
@@ -226,40 +211,29 @@ class Layout:
|
|
|
226
211
|
tuple[list, list, list, list]: Four sorted lists, each corresponding to one of the four
|
|
227
212
|
subgraph categories, with subgraphs sorted by edge fidelity variance.
|
|
228
213
|
"""
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
linear2_subgraph_list_sort = sorted(linear2_subgraph_list,key=lambda x: x[1],reverse=True)
|
|
232
|
-
cycle_subgraph_list_sort = sorted(cycle_subgraph_list,key=lambda x: x[1],reverse=True)
|
|
214
|
+
linear_subgraph_list, nonlinear_subgraph_list = self.classify_all_subgraph_according_topology()
|
|
215
|
+
linear_subgraph_list_sort = sorted(linear_subgraph_list,key=lambda x: x[1],reverse=True)
|
|
233
216
|
nonlinear_subgraph_list_sort = sorted(nonlinear_subgraph_list,key=lambda x: x[1],reverse=True)
|
|
234
217
|
if printdetails:
|
|
235
|
-
print(len(
|
|
218
|
+
print(len(linear_subgraph_list_sort),len(nonlinear_subgraph_list_sort))
|
|
236
219
|
print('The average fidelity is arranged in descending order,only print the first ten.')
|
|
237
220
|
length = self.nqubits*5+22
|
|
238
221
|
|
|
239
|
-
print('{:<3} | {:^{}} | {:^{}}
|
|
240
|
-
'idx','subgraph with
|
|
241
|
-
for i, (
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
nonlinear_subgraph_list_sort, fillvalue=' ')):
|
|
245
|
-
if i >= len(linear1_subgraph_list_sort):
|
|
246
|
-
linear1 = ('( )',0.0,0.0)
|
|
247
|
-
if i >= len(linear2_subgraph_list_sort):
|
|
248
|
-
linear2 = ('( )',0.0,0.0)
|
|
249
|
-
if i >= len(cycle_subgraph_list_sort):
|
|
250
|
-
cycle = ('( )',0.0,0.0)
|
|
222
|
+
print('{:<3} | {:^{}} | {:^{}} '.format(\
|
|
223
|
+
'idx','subgraph with linear topology',length,'subgraph with nonlinear topology',length))
|
|
224
|
+
for i, (linear,nonlinear) in enumerate(zip_longest(linear_subgraph_list_sort,nonlinear_subgraph_list_sort, fillvalue=' ')):
|
|
225
|
+
if i >= len(linear_subgraph_list_sort):
|
|
226
|
+
linear = ('( )',0.0,0.0)
|
|
251
227
|
if i >= len(nonlinear_subgraph_list_sort):
|
|
252
228
|
nonlinear = ('( )',0.0,0.0)
|
|
253
229
|
if i <= num:
|
|
254
|
-
print('{:<3} | {:<{}} {:<10.6f} {:<10.6f} | {:<{}} {:<10.6f} {:<10.6f}
|
|
230
|
+
print('{:<3} | {:<{}} {:<10.6f} {:<10.6f} | {:<{}} {:<10.6f} {:<10.6f} '\
|
|
255
231
|
.format(i, \
|
|
256
|
-
str(
|
|
257
|
-
str(linear2[0]),self.nqubits*5,linear2[1],linear2[2],\
|
|
258
|
-
str(cycle[0]),self.nqubits*5,cycle[1],cycle[2],\
|
|
232
|
+
str(linear[0]),self.nqubits*5,linear[1],linear[2],\
|
|
259
233
|
str(nonlinear[0]),self.nqubits*5,nonlinear[1],nonlinear[2])\
|
|
260
234
|
)
|
|
261
235
|
|
|
262
|
-
return
|
|
236
|
+
return linear_subgraph_list_sort[:num],nonlinear_subgraph_list_sort[:num]
|
|
263
237
|
|
|
264
238
|
def sort_subgraph_according_var_fidelity(self, num:int = 1, printdetails: bool = True):
|
|
265
239
|
"""
|
|
@@ -276,46 +250,47 @@ class Layout:
|
|
|
276
250
|
tuple[list, list, list, list]: Four sorted lists, each corresponding to one of the four
|
|
277
251
|
subgraph categories, with subgraphs sorted by edge fidelity variance.
|
|
278
252
|
"""
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
linear2_subgraph_list_sort = sorted(linear2_subgraph_list,key=lambda x: x[2])
|
|
282
|
-
cycle_subgraph_list_sort = sorted(cycle_subgraph_list,key=lambda x: x[2])
|
|
253
|
+
linear_subgraph_list, nonlinear_subgraph_list = self.classify_all_subgraph_according_topology()
|
|
254
|
+
linear_subgraph_list_sort = sorted(linear_subgraph_list,key=lambda x: x[2])
|
|
283
255
|
nonlinear_subgraph_list_sort = sorted(nonlinear_subgraph_list,key=lambda x: x[2])
|
|
284
256
|
|
|
285
257
|
if printdetails:
|
|
286
|
-
print(len(
|
|
258
|
+
print(len(linear_subgraph_list_sort),len(nonlinear_subgraph_list_sort))
|
|
287
259
|
print('The average fidelity is arranged in descending order, only print the first ten.')
|
|
288
260
|
length = self.nqubits*5+22
|
|
289
261
|
|
|
290
|
-
print('{:<3} | {:^{}} | {:^{}}
|
|
291
|
-
'idx','subgraph with
|
|
292
|
-
for i, (
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
nonlinear_subgraph_list_sort, fillvalue=' ')):
|
|
296
|
-
if i >= len(linear1_subgraph_list_sort):
|
|
297
|
-
linear1 = ('( )',0.0,0.0)
|
|
298
|
-
if i >= len(linear2_subgraph_list_sort):
|
|
299
|
-
linear2 = ('( )',0.0,0.0)
|
|
300
|
-
if i >= len(cycle_subgraph_list_sort):
|
|
301
|
-
cycle = ('( )',0.0,0.0)
|
|
262
|
+
print('{:<3} | {:^{}} | {:^{}} '.format(\
|
|
263
|
+
'idx','subgraph with linear topology',length,'subgraph with nonlinear topology',length))
|
|
264
|
+
for i, (linear,nonlinear) in enumerate(zip_longest(linear_subgraph_list_sort, nonlinear_subgraph_list_sort, fillvalue=' ')):
|
|
265
|
+
if i >= len(linear_subgraph_list_sort):
|
|
266
|
+
linear = ('( )',0.0,0.0)
|
|
302
267
|
if i >= len(nonlinear_subgraph_list_sort):
|
|
303
268
|
nonlinear = ('( )',0.0,0.0)
|
|
304
269
|
|
|
305
270
|
if i <= num:
|
|
306
|
-
print('{:<3} | {:<{}} {:<10.6f} {:<10.6f} | {:<{}} {:<10.6f} {:<10.6f}
|
|
271
|
+
print('{:<3} | {:<{}} {:<10.6f} {:<10.6f} | {:<{}} {:<10.6f} {:<10.6f} '\
|
|
307
272
|
.format(i, \
|
|
308
|
-
str(
|
|
309
|
-
str(linear2[0]),self.nqubits*5,linear2[1],linear2[2],\
|
|
310
|
-
str(cycle[0]),self.nqubits*5,cycle[1],cycle[2],\
|
|
273
|
+
str(linear[0]),self.nqubits*5,linear[1],linear[2],\
|
|
311
274
|
str(nonlinear[0]),self.nqubits*5,nonlinear[1],nonlinear[2])\
|
|
312
275
|
)
|
|
313
276
|
|
|
314
|
-
return
|
|
277
|
+
return linear_subgraph_list_sort[:num], nonlinear_subgraph_list_sort[:num]
|
|
278
|
+
|
|
279
|
+
def select_qubit_from_backend(self,):
|
|
280
|
+
# 当线路中只有一个qubit时,挑选单比特门保真度最大的比特
|
|
281
|
+
|
|
282
|
+
for nodes in nx.connected_components(self.graph):
|
|
283
|
+
if len(nodes) > 1:
|
|
284
|
+
subgraph = self.graph.subgraph(nodes)
|
|
285
|
+
break
|
|
286
|
+
node_fidelity_dic = nx.get_node_attributes(subgraph,'fidelity')
|
|
287
|
+
sorted_dict = dict(sorted(node_fidelity_dic.items(), key=lambda item: item[1], reverse=True))
|
|
288
|
+
qubit = list(sorted_dict.keys())[0]
|
|
289
|
+
return qubit, sorted_dict[qubit]
|
|
315
290
|
|
|
316
291
|
def select_layout_from_backend(self,
|
|
317
292
|
key: Literal['fidelity_mean', 'fidelity_var'] = 'fidelity_var',
|
|
318
|
-
topology: Literal['
|
|
293
|
+
topology: Literal['linear', 'nonlinear'] = 'linear',
|
|
319
294
|
printdetails: bool = False):
|
|
320
295
|
"""
|
|
321
296
|
Select a qubit layout based on the given performance metric and topology.
|
|
@@ -336,28 +311,16 @@ class Layout:
|
|
|
336
311
|
list: A list of qubits representing the selected layout.
|
|
337
312
|
"""
|
|
338
313
|
if key == 'fidelity_mean':
|
|
339
|
-
|
|
314
|
+
linear_list,nonlinear_list = self.sort_subgraph_according_mean_fidelity(printdetails=printdetails)
|
|
340
315
|
elif key == 'fidelity_var':
|
|
341
|
-
|
|
316
|
+
linear_list,nonlinear_list = self.sort_subgraph_according_var_fidelity(printdetails=printdetails)
|
|
342
317
|
|
|
343
|
-
if topology == '
|
|
344
|
-
if len(
|
|
318
|
+
if topology == 'linear':
|
|
319
|
+
if len(linear_list) == 0:
|
|
345
320
|
print(f'There is no {self.nqubits} qubits that meets both key = {key} and topology = {topology}. Please change the conditions.')
|
|
346
321
|
return [] #exit(1)
|
|
347
322
|
else:
|
|
348
|
-
return
|
|
349
|
-
elif topology == 'linear2':
|
|
350
|
-
if len(linear2_list) == 0:
|
|
351
|
-
print(f'There is no {self.nqubits} qubits that meets both key = {key} and topology = {topology}. Please change the conditions.')
|
|
352
|
-
return [] #exit(1)
|
|
353
|
-
else:
|
|
354
|
-
return linear2_list[0][0]
|
|
355
|
-
elif topology == 'cycle':
|
|
356
|
-
if len(cycle_list) == 0:
|
|
357
|
-
print(f'There is no {self.nqubits} qubits that meets both key = {key} and topology = {topology}. Please change the conditions.')
|
|
358
|
-
return [] #exit(1)
|
|
359
|
-
else:
|
|
360
|
-
return cycle_list[0][0]
|
|
323
|
+
return linear_list[0][0]
|
|
361
324
|
elif topology == 'nonlinear':
|
|
362
325
|
if len(nonlinear_list) == 0:
|
|
363
326
|
print(f'There is no {self.nqubits} qubits that meets both key = {key} and topology = {topology}. Please change the conditions.')
|
|
@@ -368,7 +331,7 @@ class Layout:
|
|
|
368
331
|
def select_layout(self,
|
|
369
332
|
target_qubits:list = [],
|
|
370
333
|
use_chip_priority: bool = True,
|
|
371
|
-
select_criteria: dict = {'key':'fidelity_var','topology':'
|
|
334
|
+
select_criteria: dict = {'key':'fidelity_var','topology':'linear'},
|
|
372
335
|
):
|
|
373
336
|
|
|
374
337
|
if target_qubits != []:
|
|
@@ -391,47 +354,109 @@ class Layout:
|
|
|
391
354
|
if is_connected is False:
|
|
392
355
|
raise(ValueError(f'The physical qubit layout {target_qubits} constructed from target_qubits is not connected.'))
|
|
393
356
|
coupling_map = list(subgraph.edges)
|
|
394
|
-
print(f'Physical qubits layout {target_qubits} are user-defined, with the corresponding coupling being {coupling_map}.')
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
357
|
+
print(f'Physical qubits layout {target_qubits} are user-defined, with the corresponding coupling being {coupling_map}.')
|
|
358
|
+
|
|
359
|
+
if len(subgraph.edges())>0:
|
|
360
|
+
subgraph_fidelity = np.array([self.edge_fidelitys[(min(edge),max(edge))] for edge in subgraph.edges])
|
|
361
|
+
fidelity_mean = np.mean(subgraph_fidelity)
|
|
362
|
+
fidelity_var = np.var(subgraph_fidelity)
|
|
363
|
+
print(f'The average fidelity of the coupler(s) between the selected qubits is {fidelity_mean}, and the variance of the fidelity is {fidelity_var}.')
|
|
399
364
|
return subgraph
|
|
400
|
-
if use_chip_priority: #
|
|
365
|
+
if use_chip_priority: # 如果列表中没有满足的比特则启动筛选,如果是单比特则保留原比特,不再筛选,todos:把筛选机制转成数据库
|
|
401
366
|
priority_qubits_list = self.priority_qubits
|
|
402
367
|
for qubits in priority_qubits_list:
|
|
403
368
|
if len(qubits) == self.nqubits:
|
|
404
369
|
subgraph = self.graph.subgraph(qubits)
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
370
|
+
if nx.is_connected(subgraph):
|
|
371
|
+
coupling_map = list(subgraph.edges)
|
|
372
|
+
print(f'Physical qubits layout {list(qubits)} are derived from the chip backend priority qubits, with the corresponding coupling being {coupling_map}.')
|
|
373
|
+
if len(subgraph.edges())>0:
|
|
374
|
+
subgraph_fidelity = np.array([self.edge_fidelitys[(min(edge),max(edge))] for edge in subgraph.edges])
|
|
375
|
+
fidelity_mean = np.mean(subgraph_fidelity)
|
|
376
|
+
fidelity_var = np.var(subgraph_fidelity)
|
|
377
|
+
print(f'The average fidelity of the coupler(s) between the selected qubits is {fidelity_mean}, and the variance of the fidelity is {fidelity_var}.')
|
|
378
|
+
return subgraph
|
|
412
379
|
print(f'No priority qubits with {self.nqubits} qubits found. it will check the select_criteria for search')
|
|
413
380
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
381
|
+
if self.nqubits == 1:
|
|
382
|
+
qubit, fidelity = self.select_qubit_from_backend()
|
|
383
|
+
#The selected qubit has the highest single-qubit gate fidelity.
|
|
384
|
+
print(f'The selected physical qubit {qubit} is recommend, with single-qubit gate fidelity {fidelity}')
|
|
385
|
+
subgraph = self.graph.subgraph([qubit])
|
|
386
|
+
return subgraph
|
|
387
|
+
elif 1< self.nqubits <= 10:
|
|
388
|
+
key_first = select_criteria['key']
|
|
389
|
+
topology_first = select_criteria['topology']
|
|
390
|
+
all_keys = ['fidelity_var','fidelity_mean']
|
|
391
|
+
all_topologys = ['linear','nonlinear']
|
|
392
|
+
all_keys.remove(key_first)
|
|
393
|
+
all_topologys.remove(topology_first)
|
|
394
|
+
sorted_keys = [key_first,] + all_keys
|
|
395
|
+
sorted_topologys = [topology_first,] + all_topologys
|
|
396
|
+
physical_qubits_layout = []
|
|
397
|
+
for key, topology in product(sorted_keys,sorted_topologys):
|
|
398
|
+
physical_qubits_layout = self.select_layout_from_backend(key=key,topology=topology)
|
|
399
|
+
if physical_qubits_layout != []:
|
|
400
|
+
break
|
|
401
|
+
if physical_qubits_layout == []:
|
|
402
|
+
raise(ValueError(f'Unable to find a suitable layout. If this message appears, please contact the developer for assistance.'))
|
|
403
|
+
physical_qubits_layout = list(physical_qubits_layout)
|
|
404
|
+
subgraph = self.graph.subgraph(physical_qubits_layout)
|
|
405
|
+
coupling_map = list(subgraph.edges)
|
|
406
|
+
print(f'Physical qubits layout {physical_qubits_layout} are selected by the Transpile algorithm using key = {key} and topology = {topology}, \nwith the corresponding coupling being {coupling_map}.')
|
|
407
|
+
subgraph_fidelity = np.array([self.edge_fidelitys[(min(edge),max(edge))] for edge in subgraph.edges])
|
|
408
|
+
fidelity_mean = np.mean(subgraph_fidelity)
|
|
409
|
+
fidelity_var = np.var(subgraph_fidelity)
|
|
410
|
+
print(f'The average fidelity of the coupler(s) between the selected qubits is {fidelity_mean}, and the variance of the fidelity is {fidelity_var}.')
|
|
411
|
+
return subgraph
|
|
412
|
+
elif self.nqubits >10:
|
|
413
|
+
physical_qubits_layout = self.get_BFS_layout()
|
|
414
|
+
subgraph = self.graph.subgraph(physical_qubits_layout)
|
|
415
|
+
coupling_map = list(subgraph.edges)
|
|
416
|
+
print(f'Physical qubits layout {physical_qubits_layout} are selected by BFS algorithm with the corresponding coupling being {coupling_map}.')
|
|
417
|
+
subgraph_fidelity = np.array([self.edge_fidelitys[(min(edge),max(edge))] for edge in subgraph.edges])
|
|
418
|
+
fidelity_mean = np.mean(subgraph_fidelity)
|
|
419
|
+
fidelity_var = np.var(subgraph_fidelity)
|
|
420
|
+
print(f'The average fidelity of the coupler(s) between the selected qubits is {fidelity_mean}, and the variance of the fidelity is {fidelity_var}.')
|
|
421
|
+
return subgraph
|
|
422
|
+
else:
|
|
423
|
+
raise(ValueError('Wrong qubits error!'))
|
|
424
|
+
|
|
425
|
+
# large qubit layour
|
|
426
|
+
def get_one_component(self):
|
|
427
|
+
components = list(nx.connected_components(self.graph))
|
|
428
|
+
for i, comp in enumerate(components):
|
|
429
|
+
if len(comp)>=self.nqubits:
|
|
430
|
+
return comp
|
|
431
|
+
raise(ValueError(f'The user circuit requires {self.nqubits} more qubits than are available in the largest connected subgraph.'))
|
|
432
|
+
|
|
433
|
+
def get_BFS_layout(self):
|
|
434
|
+
""" Perform a breadth-first search (BFS) on the graph starting from the start node,
|
|
435
|
+
collecting up to `nqubits` unique nodes including the start node.
|
|
436
|
+
|
|
437
|
+
Returns:
|
|
438
|
+
list: A list of up to `nqubits` unique nodes, discovered in BFS order.
|
|
439
|
+
"""
|
|
440
|
+
comp = self.get_one_component()
|
|
441
|
+
one_subgraph = self.graph.subgraph(comp)
|
|
442
|
+
|
|
443
|
+
if self.priority_qubits is None:
|
|
444
|
+
start_node = np.random.choice(comp)
|
|
445
|
+
else:
|
|
446
|
+
priority_qubits_list = list(set(i for sublist in self.priority_qubits for i in sublist))
|
|
447
|
+
start_node = np.random.choice(priority_qubits_list)
|
|
448
|
+
|
|
449
|
+
visited = set([start_node])
|
|
450
|
+
queue = [(start_node, 0)]
|
|
451
|
+
|
|
452
|
+
while queue and len(visited) < self.nqubits:
|
|
453
|
+
current_node, depth = queue.pop(0)
|
|
454
|
+
if depth >= self.nqubits - 1:
|
|
455
|
+
continue
|
|
456
|
+
for neighbor in one_subgraph.neighbors(current_node):
|
|
457
|
+
if neighbor not in visited:
|
|
458
|
+
visited.add(neighbor)
|
|
459
|
+
queue.append((neighbor, depth + 1))
|
|
460
|
+
if len(visited) == self.nqubits:
|
|
461
|
+
break
|
|
462
|
+
return list(visited)
|
|
@@ -316,6 +316,16 @@ def rzz_mat(theta: float) -> np.ndarray:
|
|
|
316
316
|
], dtype=complex
|
|
317
317
|
)
|
|
318
318
|
|
|
319
|
+
def cp_mat(theta: float) -> np.ndarray:
|
|
320
|
+
return np.array(
|
|
321
|
+
[
|
|
322
|
+
[1, 0, 0, 0],
|
|
323
|
+
[0, 1, 0, 0],
|
|
324
|
+
[0, 0, 1, 0],
|
|
325
|
+
[0, 0, 0, np.exp(1j*theta)]
|
|
326
|
+
], dtype=complex
|
|
327
|
+
)
|
|
328
|
+
|
|
319
329
|
gate_matrix_dict = {
|
|
320
330
|
'id':id_mat, 'x':x_mat, 'y':y_mat, 'z':z_mat, 'h':h_mat,
|
|
321
331
|
's':s_mat, 'sdg':sdg_mat, 't':t_mat, 'tdg':tdg_mat, 'sx':sx_mat, 'sxdg':sxdg_mat,
|
|
@@ -323,5 +333,6 @@ gate_matrix_dict = {
|
|
|
323
333
|
'cx':cx_mat, 'cnot':cx_mat, 'cy':cy_mat, 'cz':cz_mat,
|
|
324
334
|
'rx':rx_mat, 'ry':ry_mat, 'rz':rz_mat,
|
|
325
335
|
'p':p_mat, 'u':u_mat,'r':r_mat,
|
|
326
|
-
'rxx':rxx_mat,'ryy':ryy_mat,'rzz':rzz_mat,'
|
|
336
|
+
'rxx':rxx_mat,'ryy':ryy_mat,'rzz':rzz_mat, 'cp':cp_mat,
|
|
337
|
+
'ccz':ccz_mat,'ccx':ccx_mat,'cswap':cswap_mat,
|
|
327
338
|
}
|