前面文章 如何利用 MindQuantum 中自定义的量子门,实现对多个哈密顿量求期望值和梯度? 中已经提到了,采用 Projectq
后端的 MindQuantum 0.7.0 及之前的版本不支持对损失函数涉及多个哈密顿量的自定义量子门组成的量子线路求梯度,我们在那篇文章中就采用中心差分,自己动手编了一个函数来完成该任务。在本文中,我们更进一步,综合起之前的几篇文章(如何利用 MindQuantum 中自定义的量子门,实现对多个哈密顿量求期望值和梯度? ,MindQuantum 中 get_exceptation_with_grad 和 grad_ops 之间的关系,MindQuantum自定义损失函数和目标标签训练部分量子线路等)来实现一个更高级的任务:构建框架,实现对含有自定义量子门且损失函数涉及多个哈密顿量的量子线路的训练。我们希望随着 MindQuantum 下个版本推出的新的自研后端,能天然地解决这个问题,而不必像现在这样麻烦。
我们先导入依赖库:
import mindspore as ms
import numpy as np
from mindspore import context, nn
from mindspore.ops import operations
from mindquantum import *
from mindspore.nn import Adam, TrainOneStepCell, LossBase
from mindspore.common.initializer import initializer
from mindspore.common.parameter import Parameter
ms.context.set_context(mode=ms.context.PYNATIVE_MODE, device_target="CPU")
import copy
自定义量子门。此处 gate_x
和 gate_z
实际就是 MindQuantum 中的 RX
和 RZ
,但采用自定义的方式。虽然本质是一样的,但因为当前的后端 Projectq
不支持自定义的量子门,所以才出现了本文要解决的这个问题。
def matrix_x(theta):
return np.array([[np.cos(theta / 2), -1j * np.sin(theta / 2)],
[-1j * np.sin(theta / 2), np.cos(theta / 2)]])
def diff_matrix_x(theta):
return 0.5 * np.array([[-np.sin(theta / 2), -1j * np.cos(theta / 2)],
[-1j * np.cos(theta / 2), -np.sin(theta / 2)]])
def matrix_z(theta):
return np.array([[np.exp(-1j * theta / 2), 0], [0, np.exp(1j * theta / 2)]])
def diff_matrix_z(theta):
return 0.5j * np.array([[-np.exp(-1j * theta / 2), 0], [0, np.exp(1j * theta / 2)]])
gate_x = gene_univ_parameterized_gate('gete_x', matrix_x, diff_matrix_x) # 自定义量子门
gate_z = gene_univ_parameterized_gate('gete_z', matrix_z, diff_matrix_z) # 自定义量子门
仿照 MindQuantum 中 Simulator
搭建自定义的模拟器类,但只有求期望值和梯度的功能,且梯度的计算使用差分法。
class my_simulator:
def get_expectation_with_grad(self, hams, circ): # 输入为 hams, circ_right, simulator_left 等,这里就只用 hams 代替。
sim = Simulator('projectq', 1)
def grad_ops(inputs): # 输入为各量子门的参数
h = 1e-4
grad = []
sim.reset()
sim.apply_circuit(circ, inputs)
exceptation = [sim.get_expectation(ham).real for ham in hams]
for i in range(inputs.size):
params_p, params_n = copy.deepcopy(inputs), copy.deepcopy(inputs)
params_p[i] += h
params_n[i] -= h
sim.reset()
sim.apply_circuit(circ, params_p)
g_p = [sim.get_expectation(ham) for ham in hams]
sim.reset()
sim.apply_circuit(circ, params_n)
g_n = [sim.get_expectation(ham) for ham in hams]
grad.append([(p.real - n.real)/(2*h) for p, n in zip(g_p, g_n)])
sim.reset()
return exceptation, np.array(grad).T # 运行结果为期望值和梯度
return grad_ops # MindQuantum 中,这里返回的是一个封装了 hams, grad_ops 等的一个封装器,这里简化为单独的 grad_ops
仿照 MindQuantum 中的 AnsatzOnlyOps
搭建的一个 ops
。
class ansatz_only_ops(nn.Cell):
def __init__(self, expectation_with_grad):
super().__init__()
self.expectation_with_grad = expectation_with_grad
self.shape_ops = operations.Shape()
self.g = None
def construct(self, arg):
fval, g_ans = self.expectation_with_grad(arg.asnumpy())
self.g = np.real(g_ans)
return ms.Tensor(np.real(fval), dtype=ms.float32)
def bprop(self, arg, out, dout):
dout = dout.asnumpy()
grad = dout @ self.g
return ms.Tensor(grad, dtype=ms.float32)
仿照 MindQuantum 中的 AnsatzOnlyLayer
搭建的一个 Layer
,用于封装量子线路,就可以和 MindSpore 的参数优化功能完美融合在一起了。
class ansatz_only_layer(nn.Cell):
def __init__(self, expectation_with_grad, weight='ones'):
super().__init__()
self.evolution = ansatz_only_ops(expectation_with_grad)
weight_size = len(circ.params_name)
self.weight = Parameter(initializer(weight, weight_size, dtype=ms.float32), name='ansatz_weight')
def construct(self):
return self.evolution(self.weight)
损失函数,为展示此工程功能,这里的损失函数设定为两个哈密顿量的和,即 Z0 + X0
。
class MyLoss(LossBase):
def __init__(self, reduction='mean'):
super(MyLoss, self).__init__(reduction)
def construct(self, logits):
out = logits[0] + logits[1]
return self.get_loss(out)
将量子网络和损失函数合并起来。
class MyWithLossCell(nn.Cell):
def __init__(self, backbone, loss_fn):
super(MyWithLossCell, self).__init__(auto_prefix=False)
self._backbone = backbone
self._loss_fn = loss_fn
def construct(self):
out = self._backbone()
return self._loss_fn(out)
@property
def backbone_network(self):
return self._backbone
包含有自定义量子门的量子线路和构建损失函数哈密顿量。其中,量子线路采用 ZXZ
分解,而损失函数涉及多个哈密顿量(Z0
和 X0
)。
circ = Circuit()
circ += gate_z('a').on(0)
circ += gate_x('b').on(0)
circ += gate_z('c').on(0)
circ.as_ansatz()
hams = [Hamiltonian(QubitOperator('Z0')),Hamiltonian(QubitOperator('X0'))] # 多个哈密顿量
实例化自定义的类,且将量子网络和损失函数整合起来。
my_sim = my_simulator()
grad_ops = my_sim.get_expectation_with_grad(hams, circ)
qnet = ansatz_only_layer(grad_ops)
loss = MyLoss()
net_with_criterion = MyWithLossCell(qnet, loss)
opti = Adam(qnet.trainable_params(), learning_rate=0.5)
net = TrainOneStepCell(net_with_criterion, opti)
训练并得到最终结果:
for i in range(100):
res = net()
if i % 10 == 0:
print(f'当前训练次数为:{i}, 损失函数值为:{res}')
当前训练次数为:0, 损失函数值为:1.2483757
当前训练次数为:10, 损失函数值为:-1.0459888
当前训练次数为:20, 损失函数值为:-1.3248273
当前训练次数为:30, 损失函数值为:-1.4057021
当前训练次数为:40, 损失函数值为:-1.4104203
当前训练次数为:50, 损失函数值为:-1.4127097
当前训练次数为:60, 损失函数值为:-1.4128952
当前训练次数为:70, 损失函数值为:-1.4132676
当前训练次数为:80, 损失函数值为:-1.4137471
当前训练次数为:90, 损失函数值为:-1.4141047
验证结果:
sim = Simulator('projectq', 1)
sim.apply_circuit(circ, pr=qnet.weight.asnumpy())
print('\n最终的量子态为:\n', sim.get_qs(ket=True))
z0 = sim.get_expectation(Hamiltonian(QubitOperator('Z0')))
x0 = sim.get_expectation(Hamiltonian(QubitOperator('X0')))
print('\n最终损失函数值为:', (z0+x0).real)
最终的量子态为:
(0.36679647134390286+0.10726773357418498j)¦0⟩
(-0.8864696281552857-0.2610087743769323j)¦1⟩
最终损失函数值为: -1.4142114663496177