QPanda3
Supported by OriginQ
载入中...
搜索中...
未找到
变分量子线路

上一章: 泡利算子
下一章: 量子态


基本概念

变分量子算法

变分量子算法(Variational Quantum Algorithm, VQA)提供了一个通用的框架,可用于解决各种各样的问题。对于一个优化问题,由一个参数化量子线路产生的可观测状态的期望值来定义损失函数,然后使用经典优化器来优化参数。量子近似优化算法(Quantum Approximate Optimization Algorithm,QAOA)和变分量子本征求解器(Variational Quantum Eigensolver,VQE)是经典的应用案例。

一个QAOA示例

参数化量子线路

参数化量子线路的酉矩阵为 \( U\left ( \theta \right ) \)。其中, \( \theta \)是可变的参数, \( U\left ( \theta \right ) \)由 \( \theta \)的实际数值与参数化量子线路中各量子门的组成方式决定。(使用QPanda3准备参数化量子线路

参数化量子态

参数化量子态由变分量子线路作用于初始态得到(使用QPanda3获取给定参数值后的参数化量子态): \(|\psi(\theta)\rangle=U(\theta)|0\rangle^{{ }^{\otimes n}} \)

变分量子算法中的哈密顿期望

  • 量子态 \( \left | \psi \right \rangle \)和哈密顿算子 \( \ H \)对应的哈密顿期望定义为: \( \langle H\rangle=\langle\psi| H|\psi\rangle \)
  • 参数化量子态 \(\left | \psi \left ( \theta \right ) \right \rangle \) 和哈密顿算子 \( H \)对应的哈密顿期望为: \( \left \langle H \right \rangle = \left \langle \psi\left ( \theta \right )  \right | H \left | \psi \left ( \theta \right ) \right \rangle \)
  • 变分量子线路 \( U\left ( \theta \right ) \)和哈密顿算子 \( \ H \)对应的哈密顿期望为(使用QPanda3计算哈密顿期望值): \( \left \langle H \right \rangle = \left \langle 0 \right | ^{\otimes n}U^{\dagger }\left ( \theta \right ) HU\left ( \theta \right ) \left | 0 \right \rangle ^{\otimes n}\)

变分量子算法中的梯度计算

这里以向量形式描述梯度概念(并不限制梯度只能由向量表达):

梯度是目标函数 \( \left \langle H \right \rangle \) (这里是哈密顿期望)关于参数 \( \theta \)(这里以向量形式描述)的偏导数向量(m是参数的个数)(使用QPanda3计算梯度值): \( \nabla \left \langle H \right \rangle = \left ( \frac{\nabla \left \langle H\right \rangle }{\partial \theta_{1} },\frac{\nabla \left \langle H\right \rangle }{\partial \theta_{2} },\dots ,\frac{\nabla \left \langle H\right \rangle }{\partial \theta_{m} } \right ) \)

基本使用方法

VQCircuit的API文档

一般应用流程

准备参数化量子线路

步骤

顺序 步骤 QPanda3接口
1 约定参数 \( \theta \)的形式 set_Param接口
2 设计变分量子线路 \( U\left ( \theta \right ) \)的结构 <<运算符和append接口

API文档

示例代码

from pyqpanda3.vqcircuit import VQCircuit
from pyqpanda3.core import QCircuit,X,RX,RY,Y
def get_vqc1():
vqc = VQCircuit()
vqc.set_Param([2]) # 1>约定参数θ为长度是2的一维向量,2>参数θ与变分量子线路U(θ)绑定,不可以用于其他变分量子线路
p0 = vqc.Param([0],'p0') # 给θ_0加名称p0
p1 = vqc.Param([1],'p1') # 给θ_1加名称p1
e = 3.14*p0*p1+p1+4 #由参数θ的元素组成的表达式(支持加、乘和标量乘混合运算)
vqc << X(0) #向变分量子线路U(θ)中加入X门
vqc << RX(0,3.14) #向变分量子线路U(θ)中加入RX门,该门的参数为固定数值3.14
vqc << RY(0,e) #向变分量子线路U(θ)中加入RY门,该门的参数是可变的,是由参数θ的元素组成的表达式
return vqc
def get_vqc2():
vqc2 = VQCircuit()
vqc2.set_Param([3])
P0 = vqc2.Param([0],'P0')
P1 = vqc2.Param([1],'P1')
P2 = vqc2.Param([2],'P2')
cir = QCircuit()
cir << Y(0)
vqc2 << cir # 向变分量子线路U(θ)中加入QCircuit对象中的所有量子逻辑门
vqc1= get_vqc1() #为了方便区分,将vqc1对应的参数θ称为θ1,+将vqc2对应的参数θ称为θ2;θ1的两个元素分别为θ1[0],θ1[1],各自的名称为p0,p1;θ2的3个元素分别为θ2[0],θ2[1],θ2[2],各自的名称为P0,P1,P2
vqc2.append(vqc1,[(vqc1.Param([0]),vqc2.Param([0])),(vqc1.Param([1]),vqc2.Param([1]))])#复用vqc1的结构。对于参数θ,θ1[0]将使用θ2[0]替换,θ1[1]将使用θ2[1]替换
return vqc2
vqc1 = get_vqc1()
print('vqc1:')
vqc1.display_ansatz() #打印变分量子线路的结构
print()
vqc2 = get_vqc2()
print('vqc2:')
vqc2.display_ansatz()
定义 __init__.py:1
定义 __init__.py:1

示例代码输出结果

vqc1:
X q[0]
RX q[0],(3.14000000)
RY q[0],(((3.14*p0*p1+p1)+4))
vqc2:
Y q[0]
X q[0]
RX q[0],(3.14000000)
RY q[0],(((3.14*P0*P1+P1)+4))
注解

获取给定参数值后的参数化量子态

目标

已准备好变分量子线路 \( U\left ( \theta \right ) \)和参数 \( \theta \)的实际数值 \( \theta val \),获取 \( \left | \psi\left ( \theta val \right ) \right \rangle \)

示例代码

from pyqpanda3.vqcircuit import VQCircuit
from pyqpanda3.core import QCircuit,X,RX,RY,Y,CPUQVM,QProg
def get_vqc():
# 准备变分量子线路U(θ)
vqc = VQCircuit()
vqc.set_Param([2])# 1>约定参数θ为长度是2的一维向量,2>参数θ与变分量子线路U(θ)绑定,不可以用于其他变分量子线路
vqc << RX(0,vqc.Param([0])) #加入参数化量子门RX,该门的参数对应向量θ的第一个元素
vqc << RY(1,vqc.Param([1]))
return vqc
def get_param_val():
# 准备参数θ的实际数值θval
param_val = [5.14,6.14] # 元素个数应该与参数θ(向量)的元素个数保持一致
return param_val
def get_qstate(vqc,param_val):
# 获取∣ψ(θval)⟩
cir = vqc(param_val).at([0]) # 获得U(θval)
qvm = CPUQVM()
qvm.run(QProg(cir),1) # 演化
stv = qvm.result().get_state_vector()
return stv
vqc = get_vqc() # 准备变分量子线路U(θ)
param_val = get_param_val() # 准备参数θ的实际数值θval
stv = get_qstate(vqc,param_val) # 获取∣ψ(θval)⟩
print('∣ψ(θval)⟩:',stv)

示例代码输出结果

∣ψ(θval)⟩: [(0.8388860014815892+0j), 0.5395864336991233j, (-0.06016089509492038+0j), -0.03869656040878351j]

计算哈密顿期望值

目标

已准备好变分量子线路 \( U\left ( \theta \right ) \)、参数 \( \theta \)的实际数值 \( \theta val \)和哈密顿算子 \( \ H \),计算哈密顿期望值 \( \left \langle H \right \rangle \)

API文档

示例代码

from pyqpanda3.vqcircuit import VQCircuit,DiffMethod
from pyqpanda3.core import QCircuit,X,RX,RY,Y,CPUQVM,QProg
from pyqpanda3.hamiltonian import Hamiltonian
def get_vqc():
# 准备变分量子线路U(θ)
vqc = VQCircuit()
vqc.set_Param([2])# 1>约定参数θ为长度是2的一维向量,2>参数θ与变分量子线路U(θ)绑定,不可以用于其他变分量子线路
vqc << RX(0,vqc.Param([0])) #加入参数化量子门RX,该门的参数对应向量θ的第一个元素
vqc << RY(1,vqc.Param([1]))
return vqc
def get_param_val():
# 准备参数θ的实际数值θval
param_val = [2.14,3.14] # 元素个数应该与参数θ(向量)的元素个数保持一致
return param_val
def get_hamiltonian():
# 准备哈密顿算子H
paulis = [
('YY', [0, 1], (12.36525580995888 + 14.85172018664403j)), #(泡利基,泡利基作用的量子比特索引,系数)
('YX', [0, 1], (12.920260765526914 + 26.29613065236354j)) #用到的量子比特索引应该与量子线路的用到的比特索引一致
]
return Hamiltonian(paulis)
def get_hamiltonian_expectation(vqc,param_val,ham):
# 获取哈密顿期望值(方法1)
res = vqc(param_val).expval_hamiltonian(ham,[0],used_threads=4,backend='CPU')
return res
def get_hamiltonian_expectation2(vqc:VQCircuit,param_val,ham):
# 获取哈密顿期望值(方法2)
# 接口get_gradients_and_expectation可同时获得梯度值和期望值,这里只返回期望值
res = vqc.get_gradients_and_expectation(params=param_val,observable=ham,diff_method=DiffMethod.ADJOINT_DIFF).expectation_val()
return res
vqc = get_vqc() # 准备变分量子线路U(θ)
param_val = get_param_val() # 准备参数θ的实际数值θval
ham = get_hamiltonian() #建议变量名不要使用H,以避免与H门发生名称冲突.
ham_expectation = get_hamiltonian_expectation(vqc,param_val,ham)# 获取哈密顿期望值(方法1)
ham_expectation2 = get_hamiltonian_expectation2(vqc,param_val,ham)# 获取哈密顿期望值(方法2)
print('ham_expectation:',ham_expectation)
print('ham_expectation2:',ham_expectation2)
定义 __init__.py:1

示例代码输出结果

ham_expectation: -0.01733304686759475
ham_expectation2: -0.01733304686759475

计算梯度值

目标

已准备好变分量子线路 \( U\left ( \theta \right ) \)、参数 \( \theta \)的实际数值 \( \theta val \)和哈密顿算子 \( \ H \),计算梯度值

API文档

注解
QPanda3目前支持求梯度的参数化量子门包括RX、RY、RZ、CRX、CRY、CRZ。

示例代码

from pyqpanda3.vqcircuit import VQCircuit,DiffMethod
from pyqpanda3.core import QCircuit,X,RX,RY,Y,CPUQVM,QProg
from pyqpanda3.hamiltonian import Hamiltonian
def get_vqc():
# 准备变分量子线路U(θ)
vqc = VQCircuit()
vqc.set_Param([2])# 1>约定参数θ为长度是2的一维向量,2>参数θ与变分量子线路U(θ)绑定,不可以用于其他变分量子线路
vqc << RX(0,vqc.Param([0])) #加入参数化量子门RX,该门的参数对应向量θ的第一个元素
vqc << RY(1,vqc.Param([1]))
return vqc
def get_param_val():
# 准备参数θ的实际数值θval
param_val = [2.14,3.14] # 元素个数应该与参数θ(向量)的元素个数保持一致
return param_val
def get_hamiltonian():
# 准备哈密顿算子H
paulis = [
('YY', [0, 1], (12.36525580995888 + 14.85172018664403j)), #(泡利基,泡利基作用的量子比特索引,系数)
('YX', [0, 1], (12.920260765526914 + 26.29613065236354j)) #用到的量子比特索引应该与量子线路的用到的比特索引一致
]
return Hamiltonian(paulis)
def get_gradient(vqc,param_val,ham):
# 获取梯度值(方法1)
return vqc.get_gradients(params=param_val,observable=ham,diff_method=DiffMethod.ADJOINT_DIFF)
def get_gradient2(vqc:VQCircuit,param_val,ham):
# 获取梯度值(方法2)
# 接口get_gradients_and_expectation可同时获得梯度值和期望值
return vqc.get_gradients_and_expectation(params=param_val,observable=ham,diff_method=DiffMethod.ADJOINT_DIFF)
vqc = get_vqc() # 准备变分量子线路U(θ)
param_val = get_param_val() # 准备参数θ的实际数值θval
ham = get_hamiltonian() #建议变量名不要使用H,以避免与H门发生名称冲突.
res1 = get_gradient(vqc,param_val,ham)# 获取梯度值(方法1)
res2 = get_gradient2(vqc,param_val,ham)# 获取梯度值(方法2)
print('res1:',res1)
print('res1 gradient:',res1.gradients())
print('res2:',res2)
print('res2 gradient:',res2.gradients())

示例代码输出结果

res1: {gradient(double):{Parameter.at([0]):0.0110905,Parameter.at([1]):23.3932}}
res1 gradient: [0.011090474368968659, 23.39317090007428]
res2: (gradient and expectation){"expectation":-0.017333,"gradients":{Parameter.at([0]):0.0110905,Parameter.at([1]):23.3932}}
res2 gradient: [0.011090474368968659, 23.39317090007428]
注解

其他使用方式

矩阵或张量形式的参数θ

对应于编程语言中的2维数组或者多维数组

API文档

示例代码

from pyqpanda3.vqcircuit import VQCircuit,DiffMethod
from pyqpanda3.core import QCircuit,X,RX,RY,Y,CPUQVM,QProg,RZ
from pyqpanda3.hamiltonian import Hamiltonian
def get_vqc():
# 准备变分量子线路U(θ)
vqc = VQCircuit()
vqc.set_Param([2, 2, 2]) # 1>约定参数θ为现状为(2,2,2)的3维张量,2>参数θ与变分量子线路U(θ)绑定,不可以用于其他变分量子线路
vqc << RX(0, vqc.Param([0, 0, 0], 'P000')) # 加入参数化量子门RX,该门的参数对应向量θ的第0个元素
vqc << RY(0, vqc.Param([0, 0, 1], 'P001')) # 该门的参数对应向量θ的第1个元素
vqc << RZ(1, vqc.Param([0, 1, 0], 'P010')) # 该门的参数对应向量θ的第2个元素
vqc << RY(1, vqc.Param([0, 1, 1], 'P011')) # 该门的参数对应向量θ的第3个元素
vqc << RX(0, vqc.Param([1, 0, 0], 'P100')) # 该门的参数对应向量θ的第4个元素
vqc << RY(0, vqc.Param([1, 0, 1], 'P101')) # 该门的参数对应向量θ的第5个元素
vqc << RZ(1, vqc.Param([1, 1, 0], 'P110')) # 该门的参数对应向量θ的第6个元素
vqc << RY(1, vqc.Param([1, 1, 1], 'P111')) # 该门的参数对应向量θ的第7个元素
return vqc
def get_param_val():
# 准备参数θ的实际数值θval
param_val = [
[
[2.00, 3.01],
[4.10, 5.11]
],
[
[20.00, 30.01],
[40.10, 50.11]
]
# 元素个数应该与参数θ(向量)的元素个数保持一致
]
return param_val
def get_hamiltonian():
# 准备哈密顿算子H
paulis = [
('YY', [0, 1], (12.36525580995888 + 14.85172018664403j)), #(泡利基,泡利基作用的量子比特索引,系数)
('YX', [0, 1], (12.920260765526914 + 26.29613065236354j)) #用到的量子比特索引应该与量子线路的用到的比特索引一致
]
return Hamiltonian(paulis)
def get_cir(vqc,param_val):
# 获取∣ψ(θval)⟩
return vqc(param_val).at([0]) # 获得U(θval)
def get_expectation_and_gradient(vqc:VQCircuit,param_val,ham):
# 获取梯度值(方法2)
# 接口get_gradients_and_expectation可同时获得梯度值和期望值
return vqc.get_gradients_and_expectation(params=param_val,observable=ham,diff_method=DiffMethod.ADJOINT_DIFF)
vqc = get_vqc() # 准备变分量子线路U(θ)
param_val = get_param_val() # 准备参数θ的实际数值θval
ham = get_hamiltonian() #建议变量名不要使用H,以避免与H门发生名称冲突.
res = get_expectation_and_gradient(vqc,param_val,ham)# 获取梯度值(方法2)
print('vqc:')
vqc.display_ansatz()
print('\ncir ir:\n',get_cir(vqc,param_val).originir())
print('res:',res)
print('res gradient:',res.gradients())
print('res hamiltonian expectation:',res.expectation_val())

示例代码输出结果

UserWarning: VQCircuit.get_gradients_and_expectation : The input parameter A is not a one-dimensional array.
The elements of A will be processed in row-major order.
return vqc.get_gradients_and_expectation(params=param_val,observable=ham,diff_method=DiffMethod.ADJOINT_DIFF)
vqc:
RX q[0],(P000)
RY q[0],(P001)
RZ q[1],(P010)
RY q[1],(P011)
RX q[0],(P100)
RY q[0],(P101)
RZ q[1],(P110)
RY q[1],(P111)
cir ir:
QINIT 2
CREG 1
RX q[0],(2.00000000)
RY q[0],(3.01000000)
RZ q[1],(4.10000000)
RY q[1],(5.11000000)
RX q[0],(20.00000000)
RY q[0],(30.01000000)
RZ q[1],(40.10000000)
RY q[1],(50.11000000)
res: (gradient and expectation){"expectation":-0.162402,"gradients":{Parameter.at([0]):-0.963576,Parameter.at([1]):-2.8101,Parameter.at([2]):5.12893,Parameter.at([3]):-19.6122,Parameter.at([4]):0.143741,Parameter.at([5]):-6.85959,Parameter.at([6]):-15.2735,Parameter.at([7]):6.39117}}
res gradient: [-0.9635762918972298, -2.8101035948737088, 5.128933515832114, -19.612201408718036, 0.14374054299616468, -6.859590953208836, -15.273535159436635, 6.3911729646014805]
res hamiltonian expectation: -0.16240194962603027
注解
  • 对于矩阵(2维数组)或者张量(多维数组),QPanda3按行序处理内部数据。如果[0,1,2,3,4,5]、[[0,1],[2,3],[4,5]]和[[0,1,2],[3,4,5]]作为 \( \theta \)的值,QPanda3将打印警告信息、使用相同的方式处理它们并获得相同的结果。警告信息旨在提醒用户保证数组各维度信息的合理性。
  • 这里的变分量子线路的结构以 OriginIR的形式呈现

参数θ元素个数的确定

  • 参数 \( \theta \)元素总数与使用set_Param设置的数据形式保持一致
  • 向set_Param传入的向量的元素的乘积就是参数 \( \theta \)元素的总数
  • QPanda3提供了接口mutable_parameter_total来获得参数 \( \theta \)元素的总数

API文档

示例代码

from pyqpanda3.vqcircuit import VQCircuit,DiffMethod
vqc = VQCircuit()
vqc.set_Param([3,4,5])
res = vqc.mutable_parameter_total()
print('res:',res)

示例代码输出结果

res: 60

表达式求值

支持的运算:加、乘和标量数乘

示例代码

from pyqpanda3.vqcircuit import VQCircuit,DiffMethod
vqc = VQCircuit()
def prepare_theta():
# 准备参数向量θ,准备组成表达式的元素
vqc.set_Param([3]) # 设置参数θ是一个元素个数是3的一维向量
a = vqc.Param([0],'a')
b = vqc.Param([1],'b')
c = vqc.Param([2],'c')
return a,b,c
def get_expression(a,b,c):
# 准备由向量θ的元素组成的表达式
return 3.14*a*b+c+4.14
def update_param_vals():
# 为参数向量θ赋值
param_val = [5,6,7]
vqc(param_val) # 使用生成QCircuit对象的接口或者计算梯度的接口,均可以完成对向量θ的赋值
def get_expression_val():
# 获取表达式的值
d.calculate_expression_val() #计算值
return d.get_expression_val() #返回值
a,b,c = prepare_theta() # 准备参数向量θ,准备组成表达式的元素
d = get_expression(a,b,c)# 准备由向量θ的元素组成的表达式
update_param_vals() # 为参数向量θ赋值
d_val = get_expression_val() # 获取表达式的值
print('d:',d)
print('val of d:',d_val)

示例代码输出结果

d: ((3.14*a*b+c)+4.14)
val of d: 105.34

梯度计算与哈密顿期望计算的其它接口

序号 接口 说明 API
1 def get_gradients(self, params: numpy.ndarray[numpy.float64], observable, diff_method: DiffMethod) 提供参数向量 \( \theta \)的一组数值,计算一组参数值对应的梯度值和一个哈密顿期望值 文档
2 def get_gradients(self, params: numpy.ndarray[numpy.float64], observable, param_group_total: int, diff_method: DiffMethod) 提供参数向量 \( \theta \)的param_group_total组数值,计算param_group_total组参数值对应的param_group_total组梯度值 文档
3 def get_gradients_and_expectation(self, params: numpy.ndarray[numpy.float64], observable, diff_method: DiffMethod) 提供参数向量 \( \theta \)的一组数值,计算一组参数值对应的一组梯度值和一个哈密顿期望值 文档
4 def get_gradients_and_expectation(self, params: numpy.ndarray[numpy.float64], observable, param_group_total: int, diff_method: DiffMethod) 提供参数向量 \( \theta \)的param_group_total组数值,计算param_group_total组参数值对应的param_group_total组梯度值和param_group_total个哈密顿期望值 文档

示例代码

from pyqpanda3.vqcircuit import VQCircuit,DiffMethod
from pyqpanda3.core import QCircuit,X,RX,RY,Y,CPUQVM,QProg
from pyqpanda3.hamiltonian import Hamiltonian
def get_vqc():
# 准备变分量子线路U(θ)
vqc = VQCircuit()
vqc.set_Param([2])# 1>约定参数θ为长度是2的一维向量,2>参数θ与变分量子线路U(θ)绑定,不可以用于其他变分量子线路
vqc << RX(0,vqc.Param([0])) #加入参数化量子门RX,该门的参数对应向量θ的第一个元素
vqc << RY(1,vqc.Param([1]))
return vqc
def get_hamiltonian():
# 准备哈密顿算子H
paulis = [
('YY', [0, 1], (12.36525580995888 + 14.85172018664403j)), #(泡利基,泡利基作用的量子比特索引,系数)
('YX', [0, 1], (12.920260765526914 + 26.29613065236354j)) #用到的量子比特索引应该与量子线路的用到的比特索引一致
]
return Hamiltonian(paulis)
def fun1(vqc,ham):
param_val = [2.14, 3.14]
pq3_res1 = vqc.get_gradients(param_val, ham, DiffMethod.ADJOINT_DIFF)
print('pq3_res1:',pq3_res1)
print('pq3_res1.gradients():', pq3_res1.gradients()) #以list形式返回一维数组,存储θ所有元素对应的梯度值
print()
def fun2(vqc,ham):
param_val = [2.14, 3.14,4.14,5.14] #或者[[2.14,3.14],[4.14,5.14]]
pq3_res2 = vqc.get_gradients(param_val, ham,param_group_total=2,diff_method= DiffMethod.ADJOINT_DIFF)
print('pq3_res2:', pq3_res2)
print('pq3_res2.data():',pq3_res2.data())#以2层列表形式返回2维数组,存储param_group_total组θ数值对应的梯度值
print()
def fun3(vqc,ham):
param_val = [2.14, 3.14]
pq3_res3 = vqc.get_gradients_and_expectation(param_val,ham,diff_method=DiffMethod.ADJOINT_DIFF)
print('pq3_res3:',pq3_res3)
print('pq3_res3.data():',pq3_res3.data())#以元组形式返回结果.元组第一个元素是哈密顿期望值.元组第二个元素是以list形式表示的一维数组,存储θ所有元素值对应的梯度值.
print('pq3_res3.gradients():', pq3_res3.gradients())#以列表形式返回一维数组,存储θ所有元素对应的梯度值
print('pq3_res3.expectation_val():', pq3_res3.expectation_val()) #返回哈密顿期望值
print()
def fun4(vqc,ham):
param_val = [2.14, 3.14,4.14, 5.14] # 或者[[2.14,3.14],[4.14,5.14]]
pq3_res4 = vqc.get_gradients_and_expectation(param_val, ham, param_group_total=2,diff_method=DiffMethod.ADJOINT_DIFF)
print('pq3_res4:', pq3_res4)
print('pq3_res4.data():', pq3_res4.data())#以元组的列表形式返回结果.存储param_group_total组θ数值对应的梯度值和哈密顿期望值。元组第一个元素是哈密顿期望值.元组第二个元素是以列表形式表示的一维数组,存储θ所有元素值对应的梯度值.
print()
vqc = get_vqc() # 准备变分量子线路U(θ)
ham = get_hamiltonian() #准备哈密顿算子,建议变量名不要使用H,以避免与H门发生名称冲突.
fun1(vqc,ham) # 展示接口1
fun2(vqc,ham) # 展示接口2
fun3(vqc,ham) # 展示接口3
fun4(vqc,ham) # 展示接口4

示例代码输出结果

pq3_res1: {gradient(double):{Parameter.at([0]):0.0110905,Parameter.at([1]):23.3932}}
pq3_res1.gradients(): [0.011090474368968659, 23.39317090007428]
pq3_res2: (gradient(double)):[{Parameter.at([0]):0.0110905,Parameter.at([1]):23.3932},{Parameter.at([0]):0.0110905,Parameter.at([1]):23.3932}]
pq3_res2.data(): [[0.011090474368968659, 23.39317090007428], [0.011090474368968659, 23.39317090007428]]
pq3_res3: (gradient and expectation){"expectation":-0.017333,"gradients":{Parameter.at([0]):0.0110905,Parameter.at([1]):23.3932}}
pq3_res3.data(): (-0.01733304686759475, [0.011090474368968659, 23.39317090007428])
pq3_res3.gradients(): [0.011090474368968659, 23.39317090007428]
pq3_res3.expectation_val(): -0.01733304686759475
pq3_res4: ((gradient and expectation)):[{"expectation":-0.017333,"gradients":{Parameter.at([0]):0.0110905,Parameter.at([1]):23.3932}},{"expectation":-0.017333,"gradients":{Parameter.at([0]):0.0110905,Parameter.at([1]):23.3932}}]
pq3_res4.data(): [(-0.01733304686759475, [0.011090474368968659, 23.39317090007428]), (-0.01733304686759475, [0.011090474368968659, 23.39317090007428])]

在VQA中应用

一个QAOA示例

此示例代码要解决的组合优化问题是最大切割问题[5]:给定一张图,图上有N个顶点,给每个顶点标注0/1,使得对边的切割最大(边的切割指的是,如果一条边的顶点标注不同就算切割)

示例代码

from pyqpanda3.hamiltonian import PauliOperator,Hamiltonian
from pyqpanda3.vqcircuit import VQCircuit, VQCResult,ParamExpression,DiffMethod
from pyqpanda3.core import H, RZ, T, RX, RY,CNOT,S,X,I,QCircuit, QProg, CPUQVM
import numpy as np
import networkx as nx
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use('TkAgg')
def generate_random_graph(n):
g = nx.random_regular_graph(4, n)
for u, v in g.edges:
g[u][v]['weight'] = np.random.uniform(-1, 1)
# g[u][v]['weight'] = 2
return g
def test_qaoa():
"""
<1>QAOA 的核心思想是通过在量子计算机上准备一个参数化的量子态,然后利用经典优化算法调整这些参数,使得量子态的测量结果能够以较高的概率对应于组合优化问题的近似最优解
<2>参数化量子态可以由参数化量子线路作用在初态上得到
<3> 此代码要解决的优化问题是:给定一张图,图上有N个顶点,给每个顶点标注0/1,使得对边的切割最大(边的切割指的是,如果一条边的顶点标注不同就算切割)
"""
def generate_ham(g):
#问题编码:将优化问题编码为一个哈密顿量(Hamiltonian),其基态对应于问题的最优解。
ham_list = []
n = len(g.nodes)
for u, v in g.edges:
pauli_string = ['I'] * n
pauli_string[u] = 'Z'
pauli_string[v] = 'Z'
ham_list.append((''.join(pauli_string), list(range(n)), g.edges[u, v].get('weight', -1)))
return Hamiltonian(ham_list)
def generate_qaoa_circuit(g, layers):
#准备参数化量子线路
n = len(g.nodes)
cir = VQCircuit()
cir.set_Param([layers, 2])
for i in range(n):
cir << H(i)
for layer in range(layers):
for edge in g.edges:
q0, q1 = edge
weight = g.edges[edge].get('weight', 1)
cir << CNOT(q0, q1) << RZ(q1, -2 * cir.Param([layer, 0]) * weight) << CNOT(q0, q1)
for q in g.nodes:
cir << RX(q, 2 * cir.Param([layer, 1]))
return cir
def optimize_params(vqc,ham):
# 参数优化:得到最右参数值确定量子线路(用于确定最优的量子态)
# x0 = np.random.random(layers * 2)
x0 = np.random.random(vqc.mutable_parameter_total())
def get_expectation_and_grad(params, vqc, ham):
params = np.reshape(params, vqc.get_Param_dims())
exp_and_grad = vqc.get_gradients_and_expectation(params, ham, diff_method=DiffMethod.ADJOINT_DIFF)
return exp_and_grad.data()
# return exp_and_grad.expectation_val(), exp_and_grad.gradients() #与exp_and_grad.data()等价
from scipy.optimize import minimize
final_res = minimize(get_expectation_and_grad, x0, args=(vqc, ham), jac=True)
""" 代码说明
(1)scipy.optimize.minimize简介
①函数签名
scipy.optimize.minimize(fun, x0, args=(), method=None, jac=None, hess=None, hessp=None, bounds=None, constraints=(), tol=None, callback=None, options=None)
②参数说明
fun: 要最小化的目标函数。它应该接受一个数组作为输入,并返回一个标量值。
x0: 初始猜测值,是一个数组,表示优化变量的初始值。
args: 传递给目标函数和梯度函数的额外参数,以元组形式传递。
method: 优化算法的字符串标识符。默认是 'BFGS',但也可以选择其他算法,如 'Nelder-Mead'、'Powell'、'CG'、'Newton-CG'、'L-BFGS-B'、'TNC'、'COBYLA'、'SLSQP' 等。
jac: 目标函数的梯度。如果 jac 是一个布尔值且为 True,则假定 fun 返回的元组中包含梯度。如果 jac 是一个可调用对象,则它应该返回梯度。
③返回值
scipy.optimize.minimize
<1>函数的返回值是一个OptimizeResult 对象,它包含了优化过程的详细信息以及最终结果
<2>关键属性
1> 其属性x存储了优化后的参数值
2> success:一个布尔值,True 表示优化成功,False 表示优化失败
3> status:一个整数,表示优化器的终止状态。不同的优化算法可能有不同的状态码。0 通常表示成功,其他值可能表示不同的失败原因。
4> fun:优化后的目标函数值,即 fun(x) 的值。
5> message:一个字符串,描述优化器的终止消息。
6> nfev:目标函数的评估次数。
7> nit:优化器的迭代次数。
8> jac:优化结束时的梯度值(如果提供或计算了梯度)。
9> hess_inv: 优化结束时的 Hessian 矩阵的逆矩阵(如果提供或计算了 Hessian 矩阵)。
(2)final_res = minimize(get_expectation_and_grad, x0, args=(cir, ham), jac=True)
get_expectation_and_grad:该函数根据输入的参数(一维数组)计算得到哈密顿(相应的哈密顿量算子由args=(vqc,ham)中的ham指定)期望值(对应要返回的标量值)和梯度值(对应参数jac=True)
x0: 参数的初值,与get_expectation_and_grad要求的数组大小相同(由函数get_expectation_and_grad使用的变分量子线路(对应args=(vqc,ham)中的vqc)决定))
args=(vqc,ham):get_expectation_and_grad的入参分别为可变参数的数值、变分量子线路和哈密顿算子,vqc和ham分别对应变分量子线路和哈密顿算子
jac=True: 函数get_expectation_and_grad会返回梯度值
"""
return vqc(final_res.x.reshape((layers, 2))).at([0])
def measure_and_sample(cir):
# 测量与采样:在优化后的参数下,对量子态进行测量,获得经典比特的样本,这些样本对应于问题的近似解
prog = QProg()
prog <<cir
machine = CPUQVM()
machine.run(prog, shots=1000) #采样1000次
final_measure = machine.result().get_prob_dict() #获取测量结果
return final_measure
def post_process(measure_res):
# 结果后处理与可视化
final_measure = sorted(measure_res.items(), key=lambda i: i[1], reverse=True)[:10]
labels = dict(zip(sorted(g.nodes), [i for i in final_measure[0][0][::-1]]))
nx.set_node_attributes(g, labels, 'label')
# print(final_res)
# print(final_measure)
plt.figure(figsize=(8, 6))
pos = nx.spring_layout(g, seed=42) # 布局算法
# 绘制节点和边
nx.draw_networkx_nodes(g, pos, node_color='lightblue', node_size=500)
nx.draw_networkx_edges(g, pos, edge_color='gray', width=1.5)
node_labels = nx.get_node_attributes(g, 'label')
nx.draw_networkx_labels(g, pos, labels=node_labels, font_color='darkred', font_size=12)
edge_labels = {k: round(v, 3) for k, v in nx.get_edge_attributes(g, 'weight').items()}
nx.draw_networkx_edge_labels(g, pos, edge_labels=edge_labels, font_color='green')
plt.axis('off')
plt.show(block=True)
n = 8
layers = 3
g = generate_random_graph(n) # 此代码要解决的优化问题是:给定一张图,图上有N个顶点,给每个顶点标注0/1,使得对边的切割最大(边的切割指的是,如果一条边的顶点标注不同就算切割)
ham = generate_ham(g) # (1)问题编码:将优化问题编码为一个哈密顿量(Hamiltonian),其基态对应于问题的最优解。
vqc = generate_qaoa_circuit(g, layers) # (2)准备参数化量子线路
circuit = optimize_params(vqc, ham) # (3)参数优化:得到最优参数值确定量子线路(用于确定最优的量子态)
measure_res = measure_and_sample(circuit) # (4)测量与采样:在优化后的参数下,对量子态进行测量,获得经典比特的样本,这些样本对应于问题的近似解
post_process(measure_res) # (5)结果后处理与可视化
if __name__ == '__main__':
test_qaoa()

示例代码输出结果

qaoa
注解
上述代码中图的拓扑结构是随机生成的,因此代码运行的结果是不一样的。上述图片只用于展示程序运行的效果。

参考文献

[1] Cerezo M, Arrasmith A, Babbush R, et al. Variational quantum algorithms[J]. Nature Reviews Physics, 2021, 3(9): 625-644.

[2] Baydin A G, Pearlmutter B A, Radul A A, et al. Automatic differentiation in machine learning: a survey[J]. Journal of machine learning research, 2018, 18(153): 1-43.

[3] Jones T, Gacon J. Efficient calculation of gradients in classical simulations of variational quantum algorithms[J]. arXiv preprint arXiv:2009.02823, 2020.

[4] Luo X Z, Liu J G, Zhang P, et al. Yao. jl: Extensible, efficient framework for quantum algorithm design[J]. Quantum, 2020, 4: 341.

[5] Original Quantum. QAOA算法[EB/OL]. https://quantum-book-by-originq.readthedocs.io/en/latest/rst/4.5QAOA算法.html