quantumComputation量子计算

量子计算

  • $U = e^{i\alpha} *R_n(\theta)$
  • $ {R_n(\theta) = cos(\theta/2)- isin(\theta /2) (n_xX + n_yY + n_zZ) }$
  • $\mid+> = \frac{1}{\sqrt2} (\mid0> + \mid1>)$
  • $\mid-> = \frac{1}{\sqrt2} (\mid0> - \mid1>)$

解决python多版本pip问题

1
2
python -m pip install qiskit
py -3.5 -m pip install numpy

利用量子神经网络来完成手写字体识别任务报告_BH-NOVA团队

代码思路介绍

初始代码版本

已在附录中给出,具体见附录代码1。

我们根据学习指南中的代码,使用了经典迁移神经网络,与指南中相同,使用了三层全连接层,第三层全连接层替换为量子电路层,这样所使用的量子比特数大大减少,训练100轮后loss大致最后收敛到15%左右。具体损失图像如下:

modelLoss

内部量子层电路图:

4-bit

提交版本

由于题目中要求不得使用经典机器学习相关代码和操作,因此附录代码2中给出了未切割的全量子版本,将输入的5*5像素的图片数据使用25个量子比特编码到量子电路上后,先后经过量子卷积层和池化层编码为6维的2*2矩阵,最后通过量子全连接层映射为1位结果上。

量子比特的编码方式使用单量子比特H门和单量子比特RZ门实现,如下电路图的最左侧所示。通过将每个像素的rgb值除以255进行归一化后,使用RZ(qubits[i], math.acos(args[i]) - math.pi / 4)实现将像素的归一化值嵌入到量子比特的相位之中。(args[i]代表每一个输入的像素值)

图像输入后,通过QuantumLayer层的处理得到输出,QuantumLayer的电路结构如下电路图所示,总体电路深度使用q_depth参数表示,即相同的U操作进行的次数。每一个U操作包含一个纠缠层(先对偶数相邻的两个量子比特应用CNOT门,后对奇数相邻的两个量子比特应用CNOT门,实现整体25个量子比特的纠缠态制备),后面连接一个单比特的RY门,每个RY门的旋转角度是该变分量子电路的可变参数,也是模型待训练的权重参数。

经过QuantumLayer层的处理后,对输出执行量子卷积,卷积核大小为2*2,维度为6。卷积执行后的结果为1*6*4*4的原始特征图,将该原始特征图使用RELU函数进行激活,随后进行池化操作(2*2大小),最后得到1*6*2*2大小的处理后特征图。

将处理过后的特征图作为输入传入量子全连接层,24输入通道、1输出通道。通过量子全连接层内部蕴含的参数计算将特征图映射到1位输出(0代表3,1代表6)。

总体数据处理如下:

代码思路

内部电路如下:

cal

展望版本

对25-qubit的电路采用门切割的方法,将电路分为[0-12]qubit和[13-24]qubit两部分,将第12个量子比特和第13个量子比特之间的CNOT门按照文献[1]中的方法拆分为单比特量子门的线性相加。

具体到本题的切割实现上,我们采用如下的切割方式:(图例为对受控Z门,CZ的分解示例)

image-20240610165244930

通过将原先的CNOT门划分为两个不互相作用的单量子比特门的形式,将量子电路从中分割开来。但由于每一次拆分都需要执行6次拆分后的电路并对结果求和,因此拆分的电路会增加经典计算的复杂度。所以本工作对电路执行一次拆分,在12和13号比特之间进行拆分。具体拆分电路图如下所示。

原电路:

1
2
3
4
5
          └────┘ ┌──┴─┐         ├────────────┤ └────┘ ┌──┴─┐         ├────────────┤ 
q_12: |0>────■── ┤CNOT├──────── ┤RY(0.302819)├ ───■── ┤CNOT├──────── ┤RY(0.700017)├
┌──┴─┐ └────┘ ├────────────┤ ┌──┴─┐ └────┘ ├────────────┤
q_13: |0>─┤CNOT├ ───■────────── ┤RY(0.491620)├ ┤CNOT├ ───■────────── ┤RY(0.048755)├
└────┘ ┌──┴─┐ ├────────────┤ └────┘ ┌──┴─┐ ├────────────┤

针对我们设计的电路,主要通过CNOT门使得各线路处于纠缠状态,切割电路主要工作就是解耦这部分,切割完毕后达到去掉CNOT作用。

切割后:

1
2
3
4
5
          └────┘ ┌──┴─┐         ├────────────┤ └────┘ ┌──┴─┐         ├────────────┤ 
q_12: |0>───U ── ┤CNOT├──────── ┤RY(0.302819)├ ──U ── ┤CNOT├──────── ┤RY(0.700017)├
└────┘ ├────────────┤ └────┘ ├────────────┤
q_13: |0>───U ──────■────────── ┤RY(0.491620)├───U ── ───■────────── ┤RY(0.048755)├
┌──┴─┐ ├────────────┤ ┌──┴─┐ ├────────────┤

将双量子比特门转化为单量子比特门

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
for runid in range(6 ** depth): # depth is the times we cut
# ...
gate_ver = runid + 1
# ...

# add the virtual two qubit gate
qa = qr[12] # 12th qubit in origin circuit
qb = qr2[0] # 13th qubit in origin circuit
if gate_ver == 1:
coef *= 1/2
elif gate_ver == 2:
qc.insert(RZ(qa))
qc2.insert(RZ(qb))
coef *= 1/2
elif gate_ver == 3:
qc2.insert(RZ(-np.pi * 1 / 2, qb))
coef *= -1/2*1
elif gate_ver == 4:
qc2.insert(RZ(-np.pi * -1 / 2, qb))
coef *= -1/2*-1
elif gate_ver == 5:
qc.insert(RZ(-np.pi * 1 / 2, qa))
coef *= -1/2*1
elif gate_ver == 6:
qc.insert(RZ(-np.pi * -1 / 2, qa))
coef *= -1/2*-1

用线性组合后处理真实结果,拟合复杂电路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#post-processing the results (linear combination of precalculated coefficients)
gate_ver = runid + 1
for key1 in dict_1: # dict_1 is the output of circuit 1 after being cut
for key2 in dict_2: # dict_2 is the output of circuit 2 after being cut
if gate_ver == 3 or gate_ver == 4:
t = int(key1[0])
for t_replace in range(2):
key = key2 + str(t_replace) + key1[1:]
final_result[key] += coef*(1-t*2)*dict_1[key1]*dict_2[key2]*pre_prob_qubit2[t][t_replace]
# final_result is the output of origin circuit
if gate_ver == 5 or gate_ver == 6:
t = int(key2[-1])
for t_replace in range(2):
key = key2[:-1] + str(t_replace) + key1
final_result[key] += coef*(1-t*2)*dict_1[key1]*dict_2[key2]*pre_prob_qubit3[t][t_replace]

if gate_ver == 1 or gate_ver == 2:
key = key2 + key1
final_result[key] += coef*dict_1[key1]*dict_2[key2]

具体实现参考了[2]中附录给出的代码,在附录给出。

已有代码改进方案

目前的5*5输入数据已经很小,考虑去掉已有的量子卷积计算层、池化层。直接将输入传入电路中,通过不断优化器优化传入参数并在此过程中进行测量切割,优势如下:

  1. 代码处理少,相比已实现版本训练速度得到大幅提升
  2. 切割次数少,原思路由于大量CNOT门切割后的纠缠,需要对某些线路进行重复切割
1
2
3
pqc = QuantumLayer(quantumNet, n_qubits * q_depth, "CPU", n_qubits, 1)
input = QTensor(train_images)
rlt = pqc(input, q_weights_flat)

附录

代码1

核心参考:量子机器学习示例 — VQNET v2.11.0 documentation (vqnet20-tutorial.readthedocs.io)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
import os
import os.path
import numpy as np
import sys
from pyvqnet.nn.module import Module
from pyvqnet.nn.linear import Linear
from pyvqnet.nn.conv import Conv2D
from pyvqnet.utils.storage import load_parameters, save_parameters
from pyvqnet.nn import activation as F
from pyvqnet.nn.pooling import MaxPool2D

from pyvqnet.nn.loss import SoftmaxCrossEntropy
from pyvqnet.optim.sgd import SGD
from pyvqnet.optim.adam import Adam
from pyvqnet.data.data import data_generator
from pyvqnet.tensor import tensor
from pyvqnet.tensor.tensor import QTensor
from pyvqnet.qnn.quantumlayer import QuantumLayer
import pyqpanda as pq
import matplotlib.pyplot as plt
import matplotlib
sys.path.insert(0,"../")
try:
matplotlib.use("TkAgg")
except: #pylint:disable=bare-except
print("Can not use matplot TkAgg")
pass

if not os.path.exists("./result"):
os.makedirs("./result")
else:
pass

class CNN(Module):
"""
Classical CNN
"""
def __init__(self):
super(CNN, self).__init__()

self.conv1 = Conv2D(input_channels=1,
output_channels=16,
kernel_size=(3, 3),
stride=(1, 1),
padding="valid")
self.relu1 = F.ReLu()

self.conv2 = Conv2D(input_channels=16,
output_channels=32,
kernel_size=(3, 3),
stride=(1, 1),
padding="valid")
self.relu2 = F.ReLu()
self.maxpool2 = MaxPool2D([2, 2], [2, 2], padding="valid")

self.conv3 = Conv2D(input_channels=32,
output_channels=64,
kernel_size=(3, 3),
stride=(1, 1),
padding="valid")
self.relu3 = F.ReLu()

self.conv4 = Conv2D(input_channels=64,
output_channels=128,
kernel_size=(3, 3),
stride=(1, 1),
padding="valid")

self.relu4 = F.ReLu()
self.maxpool4 = MaxPool2D([2, 2], [2, 2], padding="valid")

self.fc1 = Linear(input_channels=128 * 4 * 4, output_channels=1024)
self.fc2 = Linear(input_channels=1024, output_channels=128)
self.fc3 = Linear(input_channels=128, output_channels=10)

def forward(self, x):

x = self.relu1(self.conv1(x))
x = self.maxpool2(self.relu2(self.conv2(x)))
x = self.relu3(self.conv3(x))
x = self.maxpool4(self.relu4(self.conv4(x)))
x = tensor.flatten(x, 1)
x = F.ReLu()(self.fc1(x))

x = F.ReLu()(self.fc2(x))

x = self.fc3(x)

return x


def load_mnist(dataset="training_data", path="./"):
if dataset == "training_data":
fname_image = os.path.join(path, "train_data.npy").replace("\\", "/")
fname_label = os.path.join(path, "train_label.npy").replace("\\", "/")
size = 2500
elif dataset == "testing_data":
fname_image = os.path.join(path, "test_data.npy").replace("\\", "/")
fname_label = os.path.join(path, "test_label.npy").replace("\\", "/")
size = 500
else:
raise ValueError("dataset must be 'training_data' or 'testing_data'")
images = np.load(fname_image)
lbl = np.load(fname_label)
labels = np.zeros((size, 1), dtype=int)
pad_width = ((0, 0), (11, 12), (11, 12))
padded_images = np.pad(images, pad_width, mode='constant', constant_values=0)
for i in range(size):
if lbl[i][0] == 1:
labels[i] = 3
else:
labels[i] = 6
return padded_images, labels


train_size = 2500
eval_size = 500
EPOCHES = 15


def classcal_cnn_model_training():
x_train, y_train = load_mnist("training_data")
x_test, y_test = load_mnist("testing_data")

x_train = x_train[:train_size]
y_train = y_train[:train_size]
x_test = x_test[:eval_size]
y_test = y_test[:eval_size]

x_train = x_train / 255
x_test = x_test / 255
y_train = np.eye(10)[y_train].reshape(-1, 10)
y_test = np.eye(10)[y_test].reshape(-1, 10)

model = CNN()

optimizer = SGD(model.parameters(), lr=0.005)
loss_func = SoftmaxCrossEntropy()

epochs = EPOCHES
loss_list = []
model.train()

save_flag = True
temp_loss = 0
for epoch in range(1, epochs):
total_loss = []
for x, y in data_generator(x_train,
y_train,
batch_size=4,
shuffle=True):

x = x.reshape(-1, 1, 28, 28)
optimizer.zero_grad()
output = model(x)
loss = loss_func(y, output) # target output
loss_np = np.array(loss.data)
loss.backward()
optimizer._step()

total_loss.append(loss_np)

loss_list.append(np.sum(total_loss) / len(total_loss))

print("{:.0f} loss is : {:.10f}".format(epoch, loss_list[-1]))

if save_flag:
temp_loss = loss_list[-1]
save_parameters(model.state_dict(), "./result/QCNN_TL_1.model")
save_flag = False
else:
if temp_loss > loss_list[-1]:
temp_loss = loss_list[-1]
save_parameters(model.state_dict(), "./result/QCNN_TL_1.model")

model.eval()
correct = 0
n_eval = 0

for x, y in data_generator(x_test, y_test, batch_size=4, shuffle=True):
x = x.reshape(-1, 1, 28, 28)
output = model(x)
loss = loss_func(y, output)
np_output = np.array(output.data, copy=False)
mask = (np_output.argmax(1) == y.argmax(1))
correct += np.sum(np.array(mask))
n_eval += 1
print(f"Eval Accuracy: {correct / n_eval}")

n_samples_show = 6
count = 0
_, axes = plt.subplots(nrows=1, ncols=n_samples_show, figsize=(10, 3))
model.eval()
for x, y in data_generator(x_test, y_test, batch_size=1, shuffle=True):
if count == n_samples_show:
break
x = x.reshape(-1, 1, 28, 28)
output = model(x)
pred = QTensor.argmax(output, [1],False)
axes[count].imshow(x[0].squeeze(), cmap="gray")
axes[count].set_xticks([])
axes[count].set_yticks([])
axes[count].set_title("Predicted {}".format(np.array(pred.data)))
count += 1
plt.show()


def classical_cnn_transferlearning_predict():
x_test, y_test = load_mnist("testing_data")

x_test = x_test[:eval_size]
y_test = y_test[:eval_size]

x_test = x_test / 255

y_test = np.eye(10)[y_test].reshape(-1, 10)

model = CNN()

model_parameter = load_parameters("./result/QCNN_TL_1.model")
model.load_state_dict(model_parameter)
model.eval()
correct = 0
n_eval = 0

for x, y in data_generator(x_test, y_test, batch_size=1, shuffle=True):
x = x.reshape(-1, 1, 28, 28)
output = model(x)

np_output = np.array(output.data, copy=False)
mask = (np_output.argmax(1) == y.argmax(1))
correct += np.sum(np.array(mask))
n_eval += 1

print(f"Eval Accuracy: {correct / n_eval}")

n_samples_show = 6
count = 0
_, axes = plt.subplots(nrows=1, ncols=n_samples_show, figsize=(10, 3))
model.eval()
for x, y in data_generator(x_test, y_test, batch_size=1, shuffle=True):
if count == n_samples_show:
break
x = x.reshape(-1, 1, 28, 28)
output = model(x)
pred = QTensor.argmax(output, [1],False)
axes[count].imshow(x[0].squeeze(), cmap="gray")
axes[count].set_xticks([])
axes[count].set_yticks([])
axes[count].set_title("Predicted {}".format(np.array(pred.data)))
count += 1
plt.show()

n_qubits = 4
q_depth = 6

def Q_H_layer(qubits, nqubits):
circuit = pq.QCircuit()
for idx in range(nqubits):
circuit.insert(pq.H(qubits[idx]))
return circuit

def Q_RY_layer(qubits, w):
circuit = pq.QCircuit()
for idx, element in enumerate(w):
circuit.insert(pq.RY(qubits[idx], element))
return circuit

def Q_entangling_layer(qubits, nqubits):
circuit = pq.QCircuit()
for i in range(0, nqubits - 1,
2): # Loop over even indices: i=0,2,...N-2
circuit.insert(pq.CNOT(qubits[i], qubits[i + 1]))
for i in range(1, nqubits - 1,
2): # Loop over odd indices: i=1,3,...N-3
circuit.insert(pq.CNOT(qubits[i], qubits[i + 1]))
return circuit

def quantum_net(q_input_features, q_weights_flat, qubits, cubits, machine):
machine = pq.CPUQVM()
machine.init_qvm()
qubits = machine.qAlloc_many(n_qubits)
circuit = pq.QCircuit()

# Reshape weights
q_weights = q_weights_flat.reshape([q_depth, n_qubits])
circuit.insert(Q_H_layer(qubits, n_qubits))

# Embed features in the quantum node
circuit.insert(Q_RY_layer(qubits, q_input_features))
for k in range(q_depth):
circuit.insert(Q_entangling_layer(qubits, n_qubits))
circuit.insert(Q_RY_layer(qubits, q_weights[k]))

# Expectation values in the Z basis
prog = pq.QProg()
prog.insert(circuit)
exp_vals = []
for position in range(n_qubits):
pauli_str = "Z" + str(position)
pauli_map = pq.PauliOperator(pauli_str, 1)
hamiltion = pauli_map.toHamiltonian(True)
exp = machine.get_expectation(prog, hamiltion, qubits)
exp_vals.append(exp)
return exp_vals

def quantum_cnn_transferlearning():

class Q_DressedQuantumNet(Module):
def __init__(self):
super().__init__()
self.pre_net = Linear(128, n_qubits)
self.post_net = Linear(n_qubits, 10)
self.qlayer = QuantumLayer(quantum_net, q_depth * n_qubits,
"cpu", n_qubits, n_qubits)

def forward(self, input_features):
pre_out = self.pre_net(input_features)
q_in = tensor.tanh(pre_out) * np.pi / 2.0
q_out_elem = self.qlayer(q_in)

result = q_out_elem
return self.post_net(result)

x_train, y_train = load_mnist("training_data") # 下载训练数据
x_test, y_test = load_mnist("testing_data")
x_train = x_train[:train_size]
y_train = y_train[:train_size]
x_test = x_test[:eval_size]
y_test = y_test[:eval_size]

x_train = x_train / 255
x_test = x_test / 255
y_train = np.eye(10)[y_train].reshape(-1, 10)
y_test = np.eye(10)[y_test].reshape(-1, 10)
model = CNN()
model_param = load_parameters("./result/QCNN_TL_1.model")
model.load_state_dict(model_param)

loss_func = SoftmaxCrossEntropy()

epochs = EPOCHES
loss_list = []

eval_losses = []

model_hybrid = model
print(model_hybrid)

for param in model_hybrid.parameters():
param.requires_grad = False

model_hybrid.fc3 = Q_DressedQuantumNet()

optimizer_hybrid = Adam(model_hybrid.fc3.parameters(), lr=0.001)
model_hybrid.train()

save_flag = True
temp_loss = 0
for epoch in range(1, epochs):
total_loss = []
for x, y in data_generator(x_train,
y_train,
batch_size=4,
shuffle=True):
x = x.reshape(-1, 1, 28, 28)
optimizer_hybrid.zero_grad()
# Forward pass
output = model_hybrid(x)
loss = loss_func(y, output) # target output
loss_np = np.array(loss.data)
# Backward pass
loss.backward()
# Optimize the weights
optimizer_hybrid._step()
total_loss.append(loss_np)

loss_list.append(np.sum(total_loss) / len(total_loss))
print("{:.0f} loss is : {:.10f}".format(epoch, loss_list[-1]))
if save_flag:
temp_loss = loss_list[-1]
save_parameters(model_hybrid.fc3.state_dict(),
"./result/QCNN_TL_FC3.model")
save_parameters(model_hybrid.state_dict(),
"./result/QCNN_TL_ALL.model")
save_flag = False
else:
if temp_loss > loss_list[-1]:
temp_loss = loss_list[-1]
save_parameters(model_hybrid.fc3.state_dict(),
"./result/QCNN_TL_FC3.model")
save_parameters(model_hybrid.state_dict(),
"./result/QCNN_TL_ALL.model")

correct = 0
n_eval = 0
loss_temp = []
for x1, y1 in data_generator(x_test,
y_test,
batch_size=4,
shuffle=True):
x1 = x1.reshape(-1, 1, 28, 28)
output = model_hybrid(x1)
loss = loss_func(y1, output)
np_loss = np.array(loss.data)
np_output = np.array(output.data, copy=False)
mask = (np_output.argmax(1) == y1.argmax(1))
correct += np.sum(np.array(mask))
n_eval += 1
loss_temp.append(np_loss)
eval_losses.append(np.sum(loss_temp) / n_eval)
print("{:.0f} eval loss is : {:.10f}".format(epoch, eval_losses[-1]))

plt.title("model loss")
plt.plot(loss_list, color="green", label="train_losses")
plt.plot(eval_losses, color="red", label="eval_losses")
plt.ylabel("loss")
plt.legend(["train_losses", "eval_losses"])
plt.savefig("qcnn_transfer_learning_classical")
plt.show()
plt.close()

n_samples_show = 6
count = 0
_, axes = plt.subplots(nrows=1, ncols=n_samples_show, figsize=(10, 3))
model_hybrid.eval()
for x, y in data_generator(x_test, y_test, batch_size=1, shuffle=True):
if count == n_samples_show:
break
x = x.reshape(-1, 1, 28, 28)
output = model_hybrid(x)
pred = QTensor.argmax(output, [1],False)
axes[count].imshow(x[0].squeeze(), cmap="gray")
axes[count].set_xticks([])
axes[count].set_yticks([])
axes[count].set_title("Predicted {}".format(np.array(pred.data)))
count += 1
plt.show()


def quantum_cnn_transferlearning_predict():
n_qubits = 4 # Number of qubits
q_depth = 6 # Depth of the quantum circuit (number of variational layers)

def Q_H_layer(qubits, nqubits):
circuit = pq.QCircuit()
for idx in range(nqubits):
circuit.insert(pq.H(qubits[idx]))
return circuit

def Q_RY_layer(qubits, w):
circuit = pq.QCircuit()
for idx, element in enumerate(w):
circuit.insert(pq.RY(qubits[idx], element))
return circuit

def Q_entangling_layer(qubits, nqubits):
circuit = pq.QCircuit()
for i in range(0, nqubits - 1,
2): # Loop over even indices: i=0,2,...N-2
circuit.insert(pq.CNOT(qubits[i], qubits[i + 1]))
for i in range(1, nqubits - 1,
2): # Loop over odd indices: i=1,3,...N-3
circuit.insert(pq.CNOT(qubits[i], qubits[i + 1]))
return circuit

def quantum_net(q_input_features, q_weights_flat, qubits, cubits,machine):

machine = pq.CPUQVM()
machine.init_qvm()
qubits = machine.qAlloc_many(n_qubits)
circuit = pq.QCircuit()

# Reshape weights
print(q_weights_flat)
q_weights = q_weights_flat.reshape([q_depth, n_qubits])

# Start from state |+> , unbiased w.r.t. |0> and |1>
circuit.insert(Q_H_layer(qubits, n_qubits))

# Embed features in the quantum node
circuit.insert(Q_RY_layer(qubits, q_input_features))

# Sequence of trainable variational layers
for k in range(q_depth):
circuit.insert(Q_entangling_layer(qubits, n_qubits))
circuit.insert(Q_RY_layer(qubits, q_weights[k]))

# Expectation values in the Z basis
prog = pq.QProg()
prog.insert(circuit)
exp_vals = []
for position in range(n_qubits):
pauli_str = "Z" + str(position)
pauli_map = pq.PauliOperator(pauli_str, 1)
hamiltion = pauli_map.toHamiltonian(True)
exp = machine.get_expectation(prog, hamiltion, qubits)
exp_vals.append(exp)

return exp_vals

class Q_DressedQuantumNet(Module):

def __init__(self):
super().__init__()
self.pre_net = Linear(128, n_qubits)
self.post_net = Linear(n_qubits, 10)
self.qlayer = QuantumLayer(quantum_net, q_depth * n_qubits,
"cpu", n_qubits, n_qubits)

def forward(self, input_features):
pre_out = self.pre_net(input_features)
q_in = tensor.tanh(pre_out) * np.pi / 2.0
q_out_elem = self.qlayer(q_in)

result = q_out_elem
# return the two-dimensional prediction from the postprocessing layer
return self.post_net(result)

x_train, y_train = load_mnist("training_data")
x_test, y_test = load_mnist("testing_data")
x_train = x_train[:2000]
y_train = y_train[:2000]
x_test = x_test[:500]
y_test = y_test[:500]

x_train = x_train / 255
x_test = x_test / 255
y_train = np.eye(10)[y_train].reshape(-1, 10)
y_test = np.eye(10)[y_test].reshape(-1, 10)

# The second method: unified storage and unified reading
model = CNN()
model_hybrid = model
model_hybrid.fc3 = Q_DressedQuantumNet()
for param in model_hybrid.parameters():
param.requires_grad = False
model_param_quantum = load_parameters("./result/QCNN_TL_ALL.model")

model_hybrid.load_state_dict(model_param_quantum)
model_hybrid.eval()

loss_func = SoftmaxCrossEntropy()
eval_losses = []

correct = 0
n_eval = 0
loss_temp = []
eval_batch_size = 4
for x1, y1 in data_generator(x_test,
y_test,
batch_size=eval_batch_size,
shuffle=True):
x1 = x1.reshape(-1, 1, 28, 28)
output = model_hybrid(x1)
loss = loss_func(y1, output)
np_loss = np.array(loss.data)
np_output = np.array(output.data, copy=False)
mask = (np_output.argmax(1) == y1.argmax(1))
correct += np.sum(np.array(mask))

n_eval += 1
loss_temp.append(np_loss)

eval_losses.append(np.sum(loss_temp) / n_eval)
print(f"Eval Accuracy: {correct / (eval_batch_size*n_eval)}")

n_samples_show = 6
count = 0
_, axes = plt.subplots(nrows=1, ncols=n_samples_show, figsize=(10, 3))
model_hybrid.eval()
for x, _ in data_generator(x_test, y_test, batch_size=1, shuffle=True):
if count == n_samples_show:
break
x = x.reshape(-1, 1, 28, 28)
output = model_hybrid(x)
pred = QTensor.argmax(output, [1],False)
axes[count].imshow(x[0].squeeze(), cmap="gray")
axes[count].set_xticks([])
axes[count].set_yticks([])
axes[count].set_title("Predicted {}".format(np.array(pred.data)))
count += 1
plt.show()


if __name__ == "__main__":

if not os.path.exists("./result/QCNN_TL_1.model"):
classcal_cnn_model_training()
classical_cnn_transferlearning_predict()
#train quantum circuits.

quantum_cnn_transferlearning()
#eval quantum circuits.
quantum_cnn_transferlearning_predict()
代码2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
import os.path
import numpy as np
import sys
import math
from pyvqnet.nn.module import Module
from pyvqnet.tensor import tensor
from pyvqnet.utils.storage import save_parameters
from pyvqnet.nn import activation as activation
from pyvqnet.nn.pooling import MaxPool2D
from pyvqnet.nn.loss import SoftmaxCrossEntropy
from pyvqnet.optim.adam import Adam
from pyvqnet.tensor.tensor import QTensor
from pyvqnet.qnn.quantumlayer import QuantumLayer
import pyqpanda as pq
import matplotlib
from pyvqnet.qnn.qcnn.qconv import QConv
from pyvqnet.qnn.qlinear import QLinear

sys.path.insert(0, "./")
try:
matplotlib.use("TkAgg")
except:
print("Can not use matplot TkAgg")
pass

if not os.path.exists("./finalResult"):
os.makedirs("./finalResult")
else:
pass

train_size = 2500
eval_size = 500
EPOCHS = 2
n_qubits = 25
q_depth = 2
path = "./"

train_image_path = os.path.join(path, "train_data.npy").replace("\\", "/")
test_image_path = os.path.join(path, "test_data.npy").replace("\\", "/")
train_label_path = os.path.join(path, "train_label.npy").replace("\\", "/")
test_label_path = os.path.join(path, "test_label.npy").replace("\\", "/")
train_images = np.load(train_image_path)
test_images = np.load(test_image_path)
temp_train_labels = np.load(train_label_path)
temp_test_labels = np.load(test_label_path)
train_labels = np.zeros((len(temp_train_labels), 1), dtype=np.int64)
test_labels = np.zeros((len(temp_test_labels), 1), dtype=np.int64)
for i in range(len(temp_train_labels)):
if temp_train_labels[i][0] == 1:
train_labels[i] = 0
elif temp_train_labels[i][1] == 1:
train_labels[i] = 1
for i in range(len(temp_test_labels)):
if temp_test_labels[i][0] == 1:
test_labels[i] = 0
elif temp_test_labels[i][1] == 1:
test_labels[i] = 1
train_images = train_images[:train_size]
train_labels = train_labels[:train_size]
test_images = test_images[:eval_size]
test_labels = test_labels[:eval_size]
train_images = train_images / 255.0
test_images = test_images / 255.0
shuffle_index = np.random.permutation(train_images.shape[0])
train_images = train_images[shuffle_index]
train_labels = train_labels[shuffle_index]
train_images = train_images.astype(np.float32)
test_images = test_images.astype(np.float32)
q_weights_flat = np.random.rand(q_depth * n_qubits)



def Q_H_layer(qubits, nqubits):
circuit = pq.QCircuit()
for idx in range(nqubits):
circuit.insert(pq.H(qubits[idx]))
return circuit


def Q_RY_layer(qubits, w):
circuit = pq.QCircuit()
for idx, element in enumerate(w):
circuit.insert(pq.RY(qubits[idx], element))
return circuit


def Q_entangling_layer(qubits, nqubits):
circuit = pq.QCircuit()
for i in range(0, nqubits - 1,
2): # Loop over even indices: i=0,2,...N-2
circuit.insert(pq.CNOT(qubits[i], qubits[i + 1]))
for i in range(1, nqubits - 1,
2): # Loop over odd indices: i=1,3,...N-3
circuit.insert(pq.CNOT(qubits[i], qubits[i + 1]))
return circuit


def Q_RZ_layer(qubits, nqubits, args):
args = args.reshape([nqubits])
circuit = pq.QCircuit()
for i in range(nqubits):
circuit.insert(pq.H(qubits[i]))
circuit.insert(pq.RZ(qubits[i], math.acos(args[i]) - math.pi / 4))
return circuit


def quantum_net(image, q_weights_flat, qubits, cubits, machine):
machine = pq.CPUQVM()
machine.init_qvm()
qubits = machine.qAlloc_many(n_qubits)
circuit = pq.QCircuit()
q_weights = q_weights_flat.reshape([q_depth, n_qubits])
circuit.insert(Q_RZ_layer(qubits, n_qubits, image))
for k in range(q_depth):
circuit.insert(Q_entangling_layer(qubits, n_qubits))
circuit.insert(Q_RY_layer(qubits, q_weights[k]))
prog = pq.QProg()
prog.insert(circuit)
print(prog)
exp_vals = []
for position in range(n_qubits):
pauli_str = "Z" + str(position)
pauli_map = pq.PauliOperator(pauli_str, 1)
hamiltion = pauli_map.toHamiltonian(True)
exp = machine.get_expectation(prog, hamiltion, qubits)
exp_vals.append(exp)
return exp_vals

class CNN(Module):

def __init__(self):
super(CNN, self).__init__()
self.layer = QuantumLayer(quantum_net, q_depth * n_qubits, "cpu", n_qubits, n_qubits)
self.conv = QConv(input_channels=1,
output_channels=6,
quantum_number=4)
self.relu = activation.ReLu()
self.pool = MaxPool2D([2, 2], [2, 2], padding="valid")
self.fc = QLinear(input_channels=6 * 2 * 2,
output_channels=1)

def forward(self, x):
x = self.relu(self.conv(x))
x = self.pool(x)
x = tensor.flatten(x, 1)
x = self.fc(x)
return x

model = CNN()


def quantum_train():
optimizer = Adam(model.parameters(), lr=0.01)
criterion = SoftmaxCrossEntropy()
loss_list = []
save_flag = True
temp_loss = 0

for epoch in range(EPOCHS):
total_correct = 0
total_loss = []
for i in range(train_images.shape[0]):
image = train_images[i]
label = train_labels[i]
image = np.expand_dims(image, axis=0)
image = np.expand_dims(image, axis=0)
image = QTensor(image)
label = np.array([label])
label = QTensor(label)
optimizer.zero_grad()
output = model(image)
loss = criterion(label, output)
loss.backward()
optimizer.step()
total_loss.append(np.array(loss.data))
if (abs(np.argmax(output.data) == 0)):
print([1,0])
else:
print([0,1])
if (abs(np.argmax(output.data) - label.data)) < 0.5:
total_correct += 1
loss_list.append(np.sum(total_loss) / len(total_loss))
print("Epoch: {}, Loss: {}, Accuracy: {}".format(epoch, loss_list[-1], total_correct / train_images.shape[0]))

if save_flag:
temp_loss = loss_list[-1]
save_parameters(model.state_dict(), "./finalResult/final_model.model")
save_flag = False
else:
if temp_loss > loss_list[-1]:
temp_loss = loss_list[-1]
save_parameters(model.state_dict(), "./finalResult/final_model.model")


def quantum_predict():
total_correct = 0
for i in range(test_images.shape[0]):
image = test_images[i]
label = test_labels[i]
image = np.expand_dims(image, axis=0)
image = np.expand_dims(image, axis=0)
image = QTensor(image)
label = np.array([label])
label = QTensor(label)
output = model(image)
if (abs(np.argmax(output.data) - label.data)) < 0.5:
total_correct += 1
print("Predict Accuracy: {}".format(total_correct / test_images.shape[0]))


if __name__ == "__main__":
quantum_train()
print("Training finished.")
quantum_predict()

代码3

VQE变分电路,将6-qubit电路拆分为3-qubit电路的代码,摘自Cluster-Simulation-Scheme/VQE experiments/cluster-simulation-scheme.ipynb at master · TianyiPeng/Cluster-Simulation-Scheme (github.com)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
def compute_pre_prob(alpha, beta, gamma):
alpha = float(alpha)
beta = float(beta)
gamma = float(gamma)
A = np.matrix([[np.exp(-1j * alpha / 2), 0], [0, np.exp(1j * alpha / 2)]])
B = np.matrix([[np.cos(beta / 2), -1j * np.sin(beta / 2)], [-1j * np.sin(beta / 2), np.cos(beta / 2)]])
C = np.matrix([[np.exp(-1j * gamma / 2), 0], [0, np.exp(1j * gamma / 2)]])

state0 = np.matrix([[1], [0]])
state1 = np.matrix([[0], [1]])
pre_prob = [[0, 0], [0, 0]]

result0 = C * B * A * state0
pre_prob[0][0] = float(np.abs(result0[0]) ** 2)
pre_prob[0][1] = float(np.abs(result0[1]) ** 2)

result1 = C * B * A * state1
pre_prob[1][0] = float(np.abs(result1[0]) ** 2)
pre_prob[1][1] = float(np.abs(result1[1]) ** 2)

return pre_prob

def get_real_device_reduced_result(n, depth, thetas, backend, simulated_noise=False):
'''
returns the result in n qubit space
by default the qubit to be disconnected is the last qubit with index n-1
n: number of qubits:
depth: number of entanglements;
thetas: the control parameters for VQE; dimension = (n,depth*3+2)
backend: the simulator or the real device
simulated_noise: adding noise to the simulator
'''
# construct container for n-1 qubit results
final_result = {}
for idx in range(2 ** n):
final_result[format(idx, '0%db' % n)] = 0

pre_prob_qubit2 = compute_pre_prob(thetas[2][2], thetas[2][3], thetas[2][4])

pre_prob_qubit3 = compute_pre_prob(thetas[3][2], thetas[3][3], thetas[3][4])

# We suppose n = 6, indexed by 0, 1, 2, 3, 4, 5
# We cut the circuit at between 2 and 3
# enumerate different version of subcircuits
for runid in range(6 ** depth):
# construct qubits and classical bits
qr = qk.QuantumRegister(n // 2, 'q')
cr = qk.ClassicalRegister(n // 2, 'c')
qc = qk.QuantumCircuit(qr, cr)
qr2 = qk.QuantumRegister(n // 2, 'q')
cr2 = qk.ClassicalRegister(n // 2, 'c')
qc2 = qk.QuantumCircuit(qr2, cr2)

# coef for each runid
coef = 1

# Add 1-qubit gates
for qb in range(n // 2):
qc.rx(thetas[qb][0], qr[qb])
qc.rz(thetas[qb][1], qr[qb])

for qb in range(n // 2):
qc2.rx(thetas[qb + n // 2][0], qr2[qb])
qc2.rz(thetas[qb + n // 2][1], qr2[qb])

gate_ver = runid + 1

# add barrier
qc.barrier(qr)
qc2.barrier(qr2)

# Add 2-qubit gates
# pairwise control Z
for cqb in range(n // 2 - 1):
qc.cz(qr[cqb], qr[cqb + 1])

for cqb in range(n // 2 - 1):
qc2.cz(qr2[cqb], qr2[cqb + 1])

# add the virtual two qubit gate
qa = qr[2]
qb = qr2[0]
if gate_ver == 1:
coef *= 1 / 2
elif gate_ver == 2:
qc.z(qa)
qc2.z(qb)
coef *= 1 / 2
elif gate_ver == 3:
qc2.rz(-np.pi * 1 / 2, qb)

coef *= -1 / 2 * 1
elif gate_ver == 4:
qc2.rz(-np.pi * -1 / 2, qb)

coef *= -1 / 2 * -1
elif gate_ver == 5:
qc.rz(-np.pi * 1 / 2, qa)

coef *= -1 / 2 * 1
elif gate_ver == 6:
qc.rz(-np.pi * -1 / 2, qa)

coef *= -1 / 2 * -1

qc.rz(-np.pi / 2, qa)
qc2.rz(-np.pi / 2, qb)

# Add 1-qubit gates
for qb in range(n // 2):
if ((gate_ver == 3 or gate_ver == 4) and qb == 2):
continue
qc.rz(thetas[qb][2], qr[qb])
qc.rx(thetas[qb][3], qr[qb])
qc.rz(thetas[qb][4], qr[qb])

for qb in range(n // 2):
if ((gate_ver == 5 or gate_ver == 6) and qb == 0):
continue
qc2.rz(thetas[qb + n // 2][2], qr2[qb])
qc2.rx(thetas[qb + n // 2][3], qr2[qb])
qc2.rz(thetas[qb + n // 2][4], qr2[qb])

qc.barrier(qr)
qc2.barrier(qr2)

# measure z
for qb in range(n // 2):
qc.measure(qr[qb], cr[qb])

for qb in range(n // 2):
qc2.measure(qr2[qb], cr2[qb])

# execute experiments
if (simulated_noise):
job_tmp_1 = qk.execute(qc, backend, shots=NSHOTS, coupling_map=coupling_map_5, noise_model=noise_model_5,
basis_gates=basis_gates_5,
initial_layout={qr[0]: 0, qr[1]: 1, qr[2]: 2})

job_tmp_2 = qk.execute(qc2, backend, shots=NSHOTS, coupling_map=coupling_map_5, noise_model=noise_model_5,
basis_gates=basis_gates_5,
initial_layout={qr2[0]: 0, qr2[1]: 1, qr2[2]: 2})
else:
job_tmp_1 = qk.execute(qc, backend, shots=NSHOTS)
job_tmp_2 = qk.execute(qc2, backend, shots=NSHOTS)
result_tmp_2 = job_tmp_2.result()
result_tmp_1 = job_tmp_1.result()
dict_1 = result_tmp_1.get_counts(0)
dict_2 = result_tmp_2.get_counts(0)

# post-processing the results (linear combination of precalculated coefficients)
gate_ver = runid + 1
for key1 in dict_1:
for key2 in dict_2:

if gate_ver == 3 or gate_ver == 4:
t = int(key1[0])
for t_replace in range(2):
key = key2 + str(t_replace) + key1[1:]
final_result[key] += coef * (1 - t * 2) * dict_1[key1] * dict_2[key2] * pre_prob_qubit2[t][
t_replace]

if gate_ver == 5 or gate_ver == 6:
t = int(key2[-1])
for t_replace in range(2):
key = key2[:-1] + str(t_replace) + key1
final_result[key] += coef * (1 - t * 2) * dict_1[key1] * dict_2[key2] * pre_prob_qubit3[t][
t_replace]

if gate_ver == 1 or gate_ver == 2:
key = key2 + key1
final_result[key] += coef * dict_1[key1] * dict_2[key2]

ddict = {}
# reverse the output, index from 0-th qubit
for bit_string in final_result:
ddict[bit_string[::-1]] = final_result[bit_string] / NSHOTS
# print(final_result)
return ddict

参考文献

[1] Mitarai and K. Fujii, “Constructing a virtual two-qubit gate from single-qubit operations,” (2019), 1909.07534.

[2] T. Peng, A. Harrow, M. Ozols, and X. Wu, (2019), arXiv:1904.00102.


quantumComputation量子计算
https://fantasylee21.github.io/2024/04/27/quantumComputation/
作者
Fantasylee
发布于
2024年4月27日
更新于
2024年7月14日
许可协议