使用pyqpanda2量子机器学习模块

警告

以下接口的量子计算部分使用pyqpanda2 https://pyqpanda-toturial.readthedocs.io/zh/latest/

您需要自行安装pyqpanda2, pip install pyqpanda

量子计算层

QuantumLayer

QuantumLayer是一个支持量子含参线路作为参数的自动求导模块的封装类。用户定义一个函数作为参数 qprog_with_measure ,该函数需要包含pyqpanda2定义的量子线路:一般包含量子线路的编码线路,演化线路和测量操作。 该类可以嵌入量子经典混合机器学习模型,通过经典的梯度下降法,使得量子经典混合模型的目标函数或损失函数最小。 用户可通过参数 diff_method 指定 QuantumLayer 层中量子线路参数的梯度计算方式, QuantumLayer 当前支持有限差分法 finite_diff 以及 parameter-shift 方法。

有限差分法是估算函数梯度最传统和最常用的数值方法之一。主要思想是用差分代替偏导数:

\[f^{\prime}(x)=\lim _{h \rightarrow 0} \frac{f(x+h)-f(x)}{h}\]

若使用 parameter-shift 方法,我们使用如下目标函数:

\[O(\theta)=\left\langle 0\left|U^{\dagger}(\theta) H U(\theta)\right| 0\right\rangle\]

理论上可以通过 parameter-shift 这一更精确的方法计算量子线路中参数对哈密顿量的梯度:

\[\nabla O(\theta)= \frac{1}{2}\left[O\left(\theta+\frac{\pi}{2}\right)-O\left(\theta-\frac{\pi}{2}\right)\right]\]
class pyvqnet.qnn.quantumlayer.QuantumLayer(qprog_with_measure, para_num, machine_type_or_cloud_token, num_of_qubits: int, num_of_cbits: int = 1, diff_method: str = 'parameter_shift', delta: float = 0.01, dtype=None, name='')

变分量子层的抽象计算模块。对一个参数化的量子线路进行仿真,得到测量结果。该变分量子层继承了VQNet框架的梯度计算模块,可以计算线路参数的梯度,训练变分量子线路模型或将变分量子线路嵌入混合量子和经典模型。

参数:
  • qprog_with_measure – 用pyqpanda2构建的量子线路运行和测量函数。

  • para_numint - 参数个数。

  • machine_type_or_cloud_token – qpanda量子虚拟机类型 “CPU”。

  • num_of_qubits – 量子比特数。

  • num_of_cbits – 经典比特数,默认为1。

  • diff_method – 求解量子线路参数梯度的方法,“参数位移”或“有限差分”,默认参数偏移。

  • delta – 有限差分计算梯度时的 delta。

  • dtype – 参数的数据类型,defaults:None,使用默认数据类型:kfloat32,代表32位浮点数。

  • name – 这个模块的名字, 默认为””。

返回:

一个可以计算量子线路的模块。

备注

qprog_with_measure是pyqpanda2中定义的量子线路函数 :https://pyqpanda-toturial.readthedocs.io/zh/latest/QCircuit.html

此函数必须包含以下参数作为函数入参(即使某个参数未实际使用),否则无法在QuantumLayer中正常运行。

qprog_with_measure (input,param,qubits,cbits,machine)

input: 输入一维经典数据。如果没有输入可以输入 None。

param: 输入一维的变分量子线路的待训练参数。

qubits: 该QuantumLayer分配的量子比特,类型为pyQpanda.Qubits。

cbits: 由QuantumLayer分配的经典比特,用来辅助测量函数,类型为 pyQpanda.ClassicalCondition。如果线路不使用cbits,也应保留此参数。

machine: 由QuantumLayer创建的模拟器,例如CPUQVM,GPUQVM,QCloud等。

使用QuantumLayer的 m_para 属性获取变分量子线路的训练参数。该参数为QTensor类,可使用to_numpy()接口转化为numpy数组。

备注

该类具有别名 QpandaQCircuitVQCLayer

备注

该类计算梯度需要额外使用pyqpanda进行线路计算个数与参数个数、数据个数、数据维度的乘积线性相关。

Example:

import pyqpanda as pq
from pyvqnet.qnn.measure import ProbsMeasure
from pyvqnet.qnn.quantumlayer import QuantumLayer
import numpy as np
from pyvqnet.tensor import QTensor
def pqctest (input,param,qubits,cbits,machine):
    circuit = pq.QCircuit()
    circuit.insert(pq.H(qubits[0]))
    circuit.insert(pq.H(qubits[1]))
    circuit.insert(pq.H(qubits[2]))
    circuit.insert(pq.H(qubits[3]))

    circuit.insert(pq.RZ(qubits[0],input[0]))
    circuit.insert(pq.RZ(qubits[1],input[1]))
    circuit.insert(pq.RZ(qubits[2],input[2]))
    circuit.insert(pq.RZ(qubits[3],input[3]))

    circuit.insert(pq.CNOT(qubits[0],qubits[1]))
    circuit.insert(pq.RZ(qubits[1],param[0]))
    circuit.insert(pq.CNOT(qubits[0],qubits[1]))

    circuit.insert(pq.CNOT(qubits[1],qubits[2]))
    circuit.insert(pq.RZ(qubits[2],param[1]))
    circuit.insert(pq.CNOT(qubits[1],qubits[2]))

    circuit.insert(pq.CNOT(qubits[2],qubits[3]))
    circuit.insert(pq.RZ(qubits[3],param[2]))
    circuit.insert(pq.CNOT(qubits[2],qubits[3]))

    prog = pq.QProg()
    prog.insert(circuit)
    # pauli_dict  = {'Z0 X1':10,'Y2':-0.543}
    rlt_prob = ProbsMeasure([0,2],prog,machine,qubits)
    return rlt_prob

pqc = QuantumLayer(pqctest,3,"CPU",4,1)
#classic data as input
input = QTensor([[1,2,3,4],[40,22,2,3],[33,3,25,2.0]] )
#forward circuits
rlt = pqc(input)
grad =  QTensor(np.ones(rlt.data.shape)*1000)
#backward circuits
rlt.backward(grad)
print(rlt)
# [
# [0.2500000, 0.2500000, 0.2500000, 0.2500000],
# [0.2500000, 0.2500000, 0.2500000, 0.2500000],
# [0.2500000, 0.2500000, 0.2500000, 0.2500000]
# ]

如果使用GPU,参考下面的例子:

Example:

import pyqpanda as pq
from pyvqnet.qnn.measure import ProbsMeasure
from pyvqnet.qnn.quantumlayer import QuantumLayer
import numpy as np
from pyvqnet.tensor import QTensor,DEV_GPU_0
def pqctest (input,param,qubits,cbits,machine):
    circuit = pq.QCircuit()
    circuit.insert(pq.H(qubits[0]))
    circuit.insert(pq.H(qubits[1]))
    circuit.insert(pq.H(qubits[2]))
    circuit.insert(pq.H(qubits[3]))

    circuit.insert(pq.RZ(qubits[0],input[0]))
    circuit.insert(pq.RZ(qubits[1],input[1]))
    circuit.insert(pq.RZ(qubits[2],input[2]))
    circuit.insert(pq.RZ(qubits[3],input[3]))

    circuit.insert(pq.CNOT(qubits[0],qubits[1]))
    circuit.insert(pq.RZ(qubits[1],param[0]))
    circuit.insert(pq.CNOT(qubits[0],qubits[1]))

    circuit.insert(pq.CNOT(qubits[1],qubits[2]))
    circuit.insert(pq.RZ(qubits[2],param[1]))
    circuit.insert(pq.CNOT(qubits[1],qubits[2]))

    circuit.insert(pq.CNOT(qubits[2],qubits[3]))
    circuit.insert(pq.RZ(qubits[3],param[2]))
    circuit.insert(pq.CNOT(qubits[2],qubits[3]))

    prog = pq.QProg()
    prog.insert(circuit)
    # pauli_dict  = {'Z0 X1':10,'Y2':-0.543}
    rlt_prob = ProbsMeasure([0,2],prog,machine,qubits)
    return rlt_prob

#这里的"CPU" 指的是qpanda量子计算模拟器使用CPU,跟pyvqnet是否使用GPU无关。
pqc = QuantumLayer(pqctest,3,"CPU",4,1)
#这里使用toGPU将QuantumLayer 移动到GPU上
pqc.toGPU()
#classic data as input
input = QTensor([[1,2,3,4],[40,22,2,3],[33,3,25,2.0]] )
input.toGPU()
#forward circuits
rlt = pqc(input)
grad =  QTensor(np.ones(rlt.data.shape)*1000,device=DEV_GPU_0)
#backward circuits
rlt.backward(grad)
print(rlt)

QpandaQCircuitVQCLayerLite

如您更加熟悉pyqpanda2语法,可以使用该接口QpandaQCircuitVQCLayerLite,自定义量子比特 qubits ,经典比特 cbits ,后端模拟器 machine 加入QpandaQCircuitVQCLayerLite的参数 qprog_with_measure 函数中。

class pyvqnet.qnn.quantumlayer.QpandaQCircuitVQCLayerLite(qprog_with_measure, para_num, diff_method: str = 'parameter_shift', delta: float = 0.01, dtype=None, name='')

变分量子层的抽象计算模块。对一个参数化的量子线路使用pyqpanda2进行仿真,得到测量结果。该变分量子层继承了VQNet框架的梯度计算模块,可以使用参数漂移法等计算线路参数的梯度,训练变分量子线路模型或将变分量子线路嵌入混合量子和经典模型。

参数:
  • qprog_with_measure – 用pyQPand构建的量子线路运行和测量函数。

  • para_numint - 参数个数。

  • diff_method – 求解量子线路参数梯度的方法,“参数位移”或“有限差分”,默认参数偏移。

  • delta – 有限差分计算梯度时的 delta。

  • dtype – 参数的数据类型,defaults:None,使用默认数据类型:kfloat32,代表32位浮点数。

  • name – 这个模块的名字, 默认为””。

返回:

一个可以计算量子线路的模块。

备注

qprog_with_measure是pyqpanda2中定义的量子线路函数 :https://pyqpanda-toturial.readthedocs.io/zh/latest/QCircuit.html

此函数必须包含以下参数作为函数入参(即使某个参数未实际使用),否则无法在QpandaQCircuitVQCLayerLite中正常运行。

与QuantumLayer相比。该接口传入的变分线路运行函数中,用户应该手动创建量子比特和模拟器: https://pyqpanda-toturial.readthedocs.io/zh/latest/QuantumMachine.html,

如果qprog_with_measure需要quantum measure,用户还需要手动创建需要分配cbits: https://pyqpanda-toturial.readthedocs.io/zh/latest/Measure.html

量子线路函数 qprog_with_measure (input,param,nqubits,ncubits)的使用可参考下面的例子。

input: 输入一维经典数据。如果没有,输入 None。

param: 输入一维的变分量子线路的待训练参数。

备注

该类具有别名 QuantumLayerV2

备注

该类计算梯度需要额外使用pyqpanda进行线路计算个数与参数个数、数据个数、数据维度的乘积线性相关。

Example:

import pyqpanda as pq
from pyvqnet.qnn.measure import ProbsMeasure
from pyvqnet.qnn.quantumlayer import QpandaQCircuitVQCLayerLite
import numpy as np
from pyvqnet.tensor import QTensor
def pqctest (input,param):
    num_of_qubits = 4

    machine = pq.CPUQVM()
    machine.init_qvm()
    qubits = machine.qAlloc_many(num_of_qubits)

    circuit = pq.QCircuit()
    circuit.insert(pq.H(qubits[0]))
    circuit.insert(pq.H(qubits[1]))
    circuit.insert(pq.H(qubits[2]))
    circuit.insert(pq.H(qubits[3]))

    circuit.insert(pq.RZ(qubits[0],input[0]))
    circuit.insert(pq.RZ(qubits[1],input[1]))
    circuit.insert(pq.RZ(qubits[2],input[2]))
    circuit.insert(pq.RZ(qubits[3],input[3]))

    circuit.insert(pq.CNOT(qubits[0],qubits[1]))
    circuit.insert(pq.RZ(qubits[1],param[0]))
    circuit.insert(pq.CNOT(qubits[0],qubits[1]))

    circuit.insert(pq.CNOT(qubits[1],qubits[2]))
    circuit.insert(pq.RZ(qubits[2],param[1]))
    circuit.insert(pq.CNOT(qubits[1],qubits[2]))

    circuit.insert(pq.CNOT(qubits[2],qubits[3]))
    circuit.insert(pq.RZ(qubits[3],param[2]))
    circuit.insert(pq.CNOT(qubits[2],qubits[3]))

    prog = pq.QProg()
    prog.insert(circuit)
    rlt_prob = ProbsMeasure([0,2],prog,machine,qubits)
    return rlt_prob

pqc = QpandaQCircuitVQCLayerLite(pqctest,3)

#classic data as input
input = QTensor([[1.0,2,3,4],[4,2,2,3],[3,3,2,2]] )

#forward circuits
rlt = pqc(input)

grad =  QTensor(np.ones(rlt.data.shape)*1000)
#backward circuits
rlt.backward(grad)
print(rlt)

# [
# [0.2500000, 0.2500000, 0.2500000, 0.2500000],
# [0.2500000, 0.2500000, 0.2500000, 0.2500000],
# [0.2500000, 0.2500000, 0.2500000, 0.2500000]
# ]

如果使用GPU,参考下面的例子:

Example:

import pyqpanda as pq
from pyvqnet.qnn.measure import ProbsMeasure
from pyvqnet.qnn.quantumlayer import QpandaQCircuitVQCLayerLite
import numpy as np
from pyvqnet.tensor import QTensor,DEV_GPU_0
def pqctest (input,param):
    num_of_qubits = 4

    machine = pq.CPUQVM()
    machine.init_qvm()
    qubits = machine.qAlloc_many(num_of_qubits)

    circuit = pq.QCircuit()
    circuit.insert(pq.H(qubits[0]))
    circuit.insert(pq.H(qubits[1]))
    circuit.insert(pq.H(qubits[2]))
    circuit.insert(pq.H(qubits[3]))

    circuit.insert(pq.RZ(qubits[0],input[0]))
    circuit.insert(pq.RZ(qubits[1],input[1]))
    circuit.insert(pq.RZ(qubits[2],input[2]))
    circuit.insert(pq.RZ(qubits[3],input[3]))

    circuit.insert(pq.CNOT(qubits[0],qubits[1]))
    circuit.insert(pq.RZ(qubits[1],param[0]))
    circuit.insert(pq.CNOT(qubits[0],qubits[1]))

    circuit.insert(pq.CNOT(qubits[1],qubits[2]))
    circuit.insert(pq.RZ(qubits[2],param[1]))
    circuit.insert(pq.CNOT(qubits[1],qubits[2]))

    circuit.insert(pq.CNOT(qubits[2],qubits[3]))
    circuit.insert(pq.RZ(qubits[3],param[2]))
    circuit.insert(pq.CNOT(qubits[2],qubits[3]))

    prog = pq.QProg()
    prog.insert(circuit)
    rlt_prob = ProbsMeasure([0,2],prog,machine,qubits)
    return rlt_prob

pqc = QpandaQCircuitVQCLayerLite(pqctest,3)
#layer move to gpu
pqc.toGPU()
#classic data as input
input = QTensor([[1.0,2,3,4],[4,2,2,3],[3,3,2,2]] )
#data move to gpu
input.toGPU(DEV_GPU_0)
#forward circuits
rlt = pqc(input)

grad =  QTensor(np.ones(rlt.data.shape)*1000,device= DEV_GPU_0)
#backward circuits
rlt.backward(grad)
print(rlt)

QuantumBatchAsyncQcloudLayer

当您安装最新版本pyqpanda,可以使用本接口定义一个变分线路,并提交到originqc的真实芯片上运行。

class pyvqnet.qnn.quantumlayer.QuantumBatchAsyncQcloudLayer(origin_qprog_func, qcloud_token, para_num, num_qubits, num_cubits, pauli_str_dict=None, shots=1000, initializer=None, dtype=None, name='', diff_method='parameter_shift', submit_kwargs={}, query_kwargs={})

使用 pyqpanda QCloud 从版本 3.8.2.2 开始的本源量子真实芯片的抽象计算模块。 它提交参数化量子电路到真实芯片并获得测量结果。 如果 diff_method == “random_coordinate_descent” ,该层将随机选择单个参数来计算梯度,其他参数将保持为零。参考:https://arxiv.org/abs/2311.00088

备注

qcloud_token 为您到 https://qcloud.originqc.com.cn/ 中申请的api token。 origin_qprog_func 需要返回pypqanda.QProg类型的数据,如果没有设置pauli_str_dict,需要保证该QProg中已经插入了measure。 origin_qprog_func 的形式必须按照如下:

origin_qprog_func(input,param,qubits,cbits,machine)

input: 输入1~2维经典数据,二维的情况下,第一个维度为批处理大小。

param: 输入一维的变分量子线路的待训练参数。

machine: 由QuantumBatchAsyncQcloudLayer创建的模拟器QCloud,无需用户额外在函数中定义。

qubits: 由QuantumBatchAsyncQcloudLayer创建的模拟器QCloud创建的量子比特,数量为 num_qubits, 类型为pyQpanda.Qubits,无需用户额外在函数中定义。

cbits: 由QuantumBatchAsyncQcloudLayer分配的经典比特, 数量为 num_cubits, 类型为 pyQpanda.ClassicalCondition,无需用户额外在函数中定义。。

备注

该类计算梯度需要额外使用真实芯片进行线路计算个数与参数个数、数据个数、数据维度的乘积线性相关。

参数:
  • origin_qprog_func – QPanda 构建的变分量子电路函数,必须返回QProg。

  • qcloud_tokenstr - 量子机的类型或用于执行的云令牌。

  • para_numint - 参数数量,参数是大小为[para_num]的QTensor。

  • num_qubitsint - 量子电路中的量子比特数量。

  • num_cubitsint - 量子电路中用于测量的经典比特数量。

  • pauli_str_dictdict|list - 表示量子电路中泡利运算符的字典或字典列表。 默认为“无”,则进行测量操作,如果输入泡利算符的字典,则会计算单个期望或者多个期望。

  • shotint - 测量次数。 默认值为 1000。

  • initializer – 参数值的初始化器。 默认为“无”,使用0~2*pi正态分布。

  • dtype – 参数的数据类型。 默认值为 None,即使用默认数据类型pyvqnet.kfloat32。

  • name – 模块的名称。 默认为空字符串。

  • diff_method – 梯度计算的微分方法。 默认为“parameter_shift”,”random_coordinate_descent”。

  • submit_kwargs – 用于提交量子电路的附加关键字参数,默认:{“chip_id”:pyqpanda.real_chip_type.origin_72,”is_amend”:True,”is_mapping”:True,”is_optimization”:True,”compile_level”:3,”default_task_group_size”:200,”test_qcloud_fake”:False},当设置test_qcloud_fake为True则本地CPUQVM模拟。

  • query_kwargs – 用于查询量子结果的附加关键字参数,默认:{“timeout”:2,”print_query_info”:True,”sub_circuits_split_size”:1}。

返回:

一个可以计算量子电路的模块。

Example:

import numpy as np
import pyqpanda as pq
import pyvqnet
from pyvqnet.qnn import QuantumLayer,QuantumBatchAsyncQcloudLayer
from pyvqnet.qnn import expval_qcloud

def qfun(input,param, m_machine, m_qlist,cubits):
    measure_qubits = [0,2]
    m_prog = pq.QProg()
    cir = pq.QCircuit()
    cir.insert(pq.RZ(m_qlist[0],input[0]))
    cir.insert(pq.CNOT(m_qlist[0],m_qlist[1]))
    cir.insert(pq.RY(m_qlist[1],param[0]))
    cir.insert(pq.CNOT(m_qlist[0],m_qlist[2]))
    cir.insert(pq.RZ(m_qlist[1],input[1]))
    cir.insert(pq.RY(m_qlist[2],param[1]))
    cir.insert(pq.H(m_qlist[2]))
    m_prog.insert(cir)

    for idx, ele in enumerate(measure_qubits):
        m_prog << pq.Measure(m_qlist[ele], cubits[idx])
    return m_prog

l = QuantumBatchAsyncQcloudLayer(qfun,
                "3047DE8A59764BEDAC9C3282093B16AF1",
                2,
                6,
                6,
                pauli_str_dict=None,
                shots = 1000,
                initializer=None,
                dtype=None,
                name="",
                diff_method="parameter_shift",
                submit_kwargs={},
                query_kwargs={})
x = pyvqnet.tensor.QTensor([[0.56,1.2],[0.56,1.2],[0.56,1.2],[0.56,1.2],[0.56,1.2]],requires_grad= True)
y = l(x)
print(y)
y.backward()
print(l.m_para.grad)
print(x.grad)

def qfun2(input,param, m_machine, m_qlist,cubits):
    measure_qubits = [0,2]
    m_prog = pq.QProg()
    cir = pq.QCircuit()
    cir.insert(pq.RZ(m_qlist[0],input[0]))
    cir.insert(pq.CNOT(m_qlist[0],m_qlist[1]))
    cir.insert(pq.RY(m_qlist[1],param[0]))
    cir.insert(pq.CNOT(m_qlist[0],m_qlist[2]))
    cir.insert(pq.RZ(m_qlist[1],input[1]))
    cir.insert(pq.RY(m_qlist[2],param[1]))
    cir.insert(pq.H(m_qlist[2]))
    m_prog.insert(cir)

    return m_prog
l = QuantumBatchAsyncQcloudLayer(qfun2,
            "3047DE8A59764BEDAC9C3282093B16AF",
            2,
            6,
            6,
            pauli_str_dict={'Z0 X1':10,'':-0.5,'Y2':-0.543},
            shots = 1000,
            initializer=None,
            dtype=None,
            name="",
            diff_method="parameter_shift",
            submit_kwargs={},
            query_kwargs={})
x = pyvqnet.tensor.QTensor([[0.56,1.2],[0.56,1.2],[0.56,1.2],[0.56,1.2]],requires_grad= True)
y = l(x)
print(y)
y.backward()
print(l.m_para.grad)
print(x.grad)

NoiseQuantumLayer

在真实的量子计算机中,受制于量子比特自身的物理特性,常常存在不可避免的计算误差。为了能在量子虚拟机中更好的模拟这种误差,VQNet同样支持含噪声量子虚拟机。含噪声量子虚拟机的模拟更贴近真实的量子计算机,我们可以自定义支持的逻辑门类型,自定义逻辑门支持的噪声模型。 现有可支持的量子噪声模型依据QPanda中定义,具体参考链接 QPANDA2 中的介绍。

使用 NoiseQuantumLayer 定义一个量子线路自动微分类,该类支持QPanda噪声虚拟机。用户定义一个函数作为参数 qprog_with_measure ,该函数需要包含pyqpanda2定义的量子线路,同样需要传入一个参数 noise_set_config,使用pyqpanda2接口,设置噪声模型。

class pyvqnet.qnn.quantumlayer.NoiseQuantumLayer(qprog_with_measure, para_num, machine_type, num_of_qubits: int, num_of_cbits: int = 1, diff_method: str = 'parameter_shift', delta: float = 0.01, noise_set_config=None, dtype=None, name='')

变分量子层的抽象计算模块。对一个参数化的量子线路进行仿真,得到测量结果。该变分量子层继承了VQNet框架的梯度计算模块,可以计算线路参数的梯度,训练变分量子线路模型或将变分量子线路嵌入混合量子和经典模型。

这一层可以在量子线路中使用噪声模型。

参数:
  • qprog_with_measure – 用pyqpanda2构建的量子线路运行和测量函数。

  • para_numint - 参数个数。

  • machine_type – qpanda机器类型。

  • num_of_qubits – 量子比特数。

  • num_of_cbits – 经典比特数,默认为1。

  • diff_method – 求解量子线路参数梯度的方法,“参数位移”或“有限差分”,默认参数偏移。

  • delta – 有限差分计算梯度时的 delta。

  • noise_set_config – 噪声设置函数。

  • dtype – 参数的数据类型,defaults:None,使用默认数据类型:kfloat32,代表32位浮点数。

  • name – 这个模块的名字, 默认为””。

返回:

一个可以计算含噪声量子线路的模块。

备注

qprog_with_measure是pyqpanda2中定义的量子线路函数 :https://pyqpanda-toturial.readthedocs.io/zh/latest/QCircuit.html

此函数必须包含以下参数作为函数入参(即使某个参数未实际使用),否则无法在NoiseQuantumLayer中正常运行。

qprog_with_measure (input,param,qubits,cbits,machine)

input: 输入一维经典数据。如果没有输入可以输入 None。

param: 输入一维的变分量子线路的待训练参数。

qubits: 该NoiseQuantumLayer分配的量子比特,类型为pyQpanda.Qubits。

cbits: cbits由NoiseQuantumLayer分配的经典比特,用来辅助测量函数,类型为 pyQpanda.ClassicalCondition。如果线路不使用cbits,也应保留此参数。

machine: 由NoiseQuantumLayer创建的模拟器。

备注

该类计算梯度需要额外使用pyqpanda进行线路计算个数与参数个数、数据个数、数据维度的乘积线性相关。

Example:

import pyqpanda as pq
from pyvqnet.qnn.measure import ProbsMeasure
from pyvqnet.qnn.quantumlayer import NoiseQuantumLayer
import numpy as np
from pyqpanda import *
from pyvqnet.tensor import QTensor
def circuit(weights,param,qubits,cbits,machine):

    circuit = pq.QCircuit()

    circuit.insert(pq.H(qubits[0]))
    circuit.insert(pq.RY(qubits[0], weights[0]))
    circuit.insert(pq.RY(qubits[0], param[0]))
    prog = pq.QProg()
    prog.insert(circuit)
    prog << measure_all(qubits, cbits)

    result = machine.run_with_configuration(prog, cbits, 100)

    counts = np.array(list(result.values()))
    states = np.array(list(result.keys())).astype(float)
    # Compute probabilities for each state
    probabilities = counts / 100
    # Get state expectation
    expectation = np.sum(states * probabilities)
    return expectation

def default_noise_config(qvm,q):

    p = 0.01
    qvm.set_noise_model(NoiseModel.BITFLIP_KRAUS_OPERATOR, GateType.PAULI_X_GATE, p)
    qvm.set_noise_model(NoiseModel.BITFLIP_KRAUS_OPERATOR, GateType.PAULI_Y_GATE, p)
    qvm.set_noise_model(NoiseModel.BITFLIP_KRAUS_OPERATOR, GateType.PAULI_Z_GATE, p)
    qvm.set_noise_model(NoiseModel.BITFLIP_KRAUS_OPERATOR, GateType.RX_GATE, p)
    qvm.set_noise_model(NoiseModel.BITFLIP_KRAUS_OPERATOR, GateType.RY_GATE, p)
    qvm.set_noise_model(NoiseModel.BITFLIP_KRAUS_OPERATOR, GateType.RZ_GATE, p)
    qvm.set_noise_model(NoiseModel.BITFLIP_KRAUS_OPERATOR, GateType.RY_GATE, p)
    qvm.set_noise_model(NoiseModel.BITFLIP_KRAUS_OPERATOR, GateType.HADAMARD_GATE, p)
    qves =[]
    for i in range(len(q)-1):
        qves.append([q[i],q[i+1]])#
    qves.append([q[len(q)-1],q[0]])
    qvm.set_noise_model(NoiseModel.DAMPING_KRAUS_OPERATOR, GateType.CNOT_GATE, p, qves)

    return qvm

qvc = NoiseQuantumLayer(circuit,24,"noise",1,1,diff_method= "parameter_shift", delta=0.01,noise_set_config = default_noise_config)
input = QTensor([
    [0., 1., 1., 1.],

    [0., 0., 1., 1.],

    [1., 0., 1., 1.]
    ] )
rlt = qvc(input)
grad =  QTensor(np.ones(rlt.data.shape)*1000)

rlt.backward(grad)
print(qvc.m_para.grad)

#[1195., 105., 70., 0.,
# 45., -45., 50., 15.,
# -80., 50., 10., -30.,
# 10., 60., 75., -110.,
# 55., 45., 25., 5.,
# 5., 50., -25., -15.]

下面给出一个 noise_set_config 的例子,这里使得 RX , RY , RZ , X , Y , Z , H 等逻辑门加入了 p = 0.01 的 BITFLIP_KRAUS_OPERATOR噪声模型。

def noise_set_config(qvm,q):

        p = 0.01
        qvm.set_noise_model(NoiseModel.BITFLIP_KRAUS_OPERATOR, GateType.PAULI_X_GATE, p)
        qvm.set_noise_model(NoiseModel.BITFLIP_KRAUS_OPERATOR, GateType.PAULI_Y_GATE, p)
        qvm.set_noise_model(NoiseModel.BITFLIP_KRAUS_OPERATOR, GateType.PAULI_Z_GATE, p)
        qvm.set_noise_model(NoiseModel.BITFLIP_KRAUS_OPERATOR, GateType.RX_GATE, p)
        qvm.set_noise_model(NoiseModel.BITFLIP_KRAUS_OPERATOR, GateType.RY_GATE, p)
        qvm.set_noise_model(NoiseModel.BITFLIP_KRAUS_OPERATOR, GateType.RZ_GATE, p)
        qvm.set_noise_model(NoiseModel.BITFLIP_KRAUS_OPERATOR, GateType.RY_GATE, p)
        qvm.set_noise_model(NoiseModel.BITFLIP_KRAUS_OPERATOR, GateType.HADAMARD_GATE, p)
        qves =[]
        for i in range(len(q)-1):
                qves.append([q[i],q[i+1]])#
        qves.append([q[len(q)-1],q[0]])
        qvm.set_noise_model(NoiseModel.DAMPING_KRAUS_OPERATOR, GateType.CNOT_GATE, p, qves)

        return qvm

QiskitLayer

class pyvqnet.qnn.QiskitLayer(qiskit_circuits, para_num)

一个用于在 vqnet 中实现前向和反向传播的 qiskit 电路封装层。QISKIT_VQC 是一个定义 qiskit 量子电路及其 run 函数的类。 以下示例展示了它的工作原理。此层仅支持电路的输入和权重作为参数。

参数:
  • cirq_vqc – 定义 qiskit 电路的定义、后端和运行函数的类。

  • para_numint - para_num 的数量。

返回:

一个可以运行 qiskit 量子电路模型的类。

Example:

"""
调用qiskit训练

qiskit                        2.1.1
qiskit-aer                    0.17.2
opencv-python
"""
import sys
sys.path.insert(0,"../")
import os
import os.path
import urllib
import gzip
import numpy as np
import random
import sys
sys.path.insert(0,"../../")
random.seed(42)
np.random.seed(42)
from pyvqnet.nn.module import Module
from pyvqnet.optim import Adam
import pyvqnet
pyvqnet.utils.set_random_seed(42)
from pyvqnet.nn.loss import MeanSquaredError
from pyvqnet.qnn.utils import QiskitLayer

import qiskit
from qiskit.quantum_info import Statevector
from qiskit import  QuantumRegister, ClassicalRegister

from qiskit.quantum_info.operators import  Pauli
max_parallel_threads = 24
gpu = False
method = "statevector"
backend_options = {
    "method": method,
    "precision": "double",
    "max_parallel_threads": max_parallel_threads,
    "fusion_enable": True,
    "fusion_threshold": 14,
    "fusion_max_qubit": 5,
}
from qiskit_aer import StatevectorSimulator
simulator = StatevectorSimulator()

simulator.set_options(**backend_options)


url_base = 'https://ossci-datasets.s3.amazonaws.com/mnist/'
key_file = {
    'train_img':'train-images-idx3-ubyte.gz',
    'train_label':'train-labels-idx1-ubyte.gz',
    'test_img':'t10k-images-idx3-ubyte.gz',
    'test_label':'t10k-labels-idx1-ubyte.gz'
}

def _download(dataset_dir,file_name):
    file_path = dataset_dir + "/" + file_name

    if os.path.exists(file_path):
        with gzip.GzipFile(file_path) as f:
            file_path_ungz = file_path[:-3].replace('\\', '/')
            if not os.path.exists(file_path_ungz):
                open(file_path_ungz,"wb").write(f.read())
        return

    print("Downloading " + file_name + " ... ")
    urllib.request.urlretrieve(url_base + file_name, file_path)
    if os.path.exists(file_path):
            with gzip.GzipFile(file_path) as f:
                file_path_ungz = file_path[:-3].replace('\\', '/')
                file_path_ungz = file_path_ungz.replace('-idx', '.idx')
                if not os.path.exists(file_path_ungz):
                    open(file_path_ungz,"wb").write(f.read())
    print("Done")

def download_mnist(dataset_dir):
    for v in key_file.values():
        _download(dataset_dir,v)

def dataloader(data,label,batch_size, shuffle = True)->np:
    if shuffle:
        for _ in range(len(data)//batch_size):
            random_index = np.random.randint(0, len(data), (batch_size, 1))
            yield data[random_index].reshape(batch_size,-1),label[random_index].reshape(batch_size,-1)
    else:
        for i in range(0,len(data)-batch_size+1,batch_size):
            yield data[i:i+batch_size], label[i:i+batch_size]

def get_accuary(result,label):
    result,label = np.array(result.data), np.array(label.data)

    is_correct = (np.abs(result - label) < 0.5)
    is_correct = np.count_nonzero(is_correct)
    acc = is_correct

    return acc

def load_mnist_4_4(dataset="training_data", digits=np.arange(10),
                path="."):
    import os, struct
    from array import array as pyarray
    download_mnist(path)
    if dataset == "training_data":
        fname_image = os.path.join(path, 'train-images.idx3-ubyte').replace('\\', '/')
        fname_label = os.path.join(path, 'train-labels.idx1-ubyte').replace('\\', '/')
    elif dataset == "testing_data":
        fname_image = os.path.join(path, 't10k-images.idx3-ubyte').replace('\\', '/')
        fname_label = os.path.join(path, 't10k-labels.idx1-ubyte').replace('\\', '/')
    else:
        raise ValueError("dataset must be 'training_data' or 'testing_data'")

    flbl = open(fname_label, 'rb')
    magic_nr, size = struct.unpack(">II", flbl.read(8))

    lbl = pyarray("b", flbl.read())
    flbl.close()

    fimg = open(fname_image, 'rb')
    magic_nr, size, rows, cols = struct.unpack(">IIII", fimg.read(16))
    img = pyarray("B", fimg.read())
    fimg.close()

    ind = [k for k in range(size) if lbl[k] in digits]
    N = len(ind)
    images = np.zeros((N, rows, cols))
    images_new = []# = np.zeros((N, 4, 4))
    labels = np.zeros((N, 1), dtype=int)
    import cv2
    for i in range(len(ind)):
        tmp1 = np.array(img[ind[i] * rows * cols: (ind[i] + 1) * rows * cols]).reshape((rows, cols))
        tmp1 = tmp1[4:24,4:24]
        tmp = cv2.resize(tmp1,(4,4))

        if np.max(tmp) ==0:
            continue
        images_new.append(tmp)
        if lbl[ind[i]] ==digits[1]:
            labels[i] = 1
        else:
            labels[i] = 0

    return np.array(images_new), labels


class QISKIT_VQC:

    def __init__(self, n_qubits, backend, shots):
        # --- Circuit definition ---

        qc = ClassicalRegister(1)
        self.qc = qc
        self.n_qubits = n_qubits

        all_qubits = [i for i in range(n_qubits)]
        self.all_qubits= all_qubits

        self.backend = backend
        self.shots = shots

    def run(self,**kwargs):

        x  = kwargs['x']
        weights  = kwargs['w']

        weights = weights.astype(np.float64)
        x = x.astype(np.float64)

        sum_feature = np.power(np.sum([t**2 for t in x]),0.5)
        normalize_feat = x/sum_feature

        self._circuit = qiskit.QuantumCircuit(QuantumRegister(4))

        self.theta = weights.reshape([4,6])
        self._circuit.initialize(normalize_feat, [0,1,2,3])

        # 先搭建广义的旋转层
        for i in range(self.n_qubits):
            self._circuit.rz(self.theta[i,0], i)
            self._circuit.ry(self.theta[i,1], i)
            self._circuit.rz(self.theta[i,2], i)

        for d in range(3, 6):
            # 搭建纠缠层
            for i in range(self.n_qubits-1):
                self._circuit.cx(i, i + 1)
            self._circuit.cx(self.n_qubits-1, 0)
            # 对每一个量子比特搭建Ry
            for i in range(self.n_qubits):
                self._circuit.ry(self.theta[i,d], i)

        statevec = Statevector(self._circuit)
        Expectation = np.real(statevec.expectation_value(Pauli('ZIII')))
        return Expectation

#define qiskit circuits class
circuit = QISKIT_VQC(4, simulator, 1000)

class Model_qiskit(Module):
    def __init__(self):
        super(Model_qiskit, self).__init__()
        self.qvc = QiskitLayer(circuit,24)

    def forward(self, x):

        return self.qvc(x)*0.5 + 0.5

def Run_qiskit():

    x_train, y_train = load_mnist_4_4("training_data",digits=[3,6])

    y_train = y_train.reshape(-1, 1)

    x_test, y_test = load_mnist_4_4("testing_data",digits=[3,6])

    x_train = x_train.astype(np.float32)
    x_test = x_test.astype(np.float32)
    y_train = y_train.astype(np.float32)
    y_test = y_test.astype(np.float32)
    x_train = x_train *np.pi / 255
    x_test = x_test *np.pi / 255
    x_train = x_train[:100]
    y_train = y_train[:100]

    x_test = x_test[:50]
    y_test = y_test[:50]

    model = Model_qiskit()

    optimizer = Adam(model.parameters(),lr =0.01)
    batch_size = 10
    epoch = 2

    loss = MeanSquaredError()
    print("start training..............")
    model.train()

    TL=[]

    TA=[]

    for i in range(epoch):
        count=0
        sum_loss = 0
        accuary = 0
        t = 0
        model.train()
        for data,label in dataloader(x_train,y_train,batch_size,True):

            optimizer.zero_grad()

            result = model(data)

            loss_b = loss(label,result)

            loss_b.backward()
            optimizer._step()
            sum_loss += loss_b.item()
            count+=batch_size
            accuary += get_accuary(result,label)
            t = t + 1

            print(f"epoch:{i}, iter{t} #### loss:{sum_loss*batch_size/count} #####accuray:{accuary/count}")
        TL.append(sum_loss*batch_size/count)
        TA.append(accuary/count)
    print(f"qiskit epoch {epoch},最终准确率 {TA[-1]}")


if __name__=="__main__":

    Run_qiskit()

CirqLayer

class pyvqnet.qnn.CirqLayer(cirq_vqc, para_num)

一个用于在 vqnet 中实现前向和反向传播的 cirq 电路封装层。CIRQ_VQC是需要用户定义 cirq 量子电路及其 run 函数的类。以下示例展示了它的工作原理。 此层仅支持电路的输入和权重作为参数。

参数:
  • cirq_vqc – 定义 cirq 电路的定义、后端和运行函数的类。

  • para_numint - para_num 的数量。

返回:

一个可以运行 cirq 量子电路模型的类。

备注

以下示例代码需要 cirq==1.5.0, numpy <2.

Example:

import numpy as np
import random
random.seed(42)
np.random.seed(42)
from pyvqnet.nn.module import Module
import pyvqnet
pyvqnet.utils.set_random_seed(42)
from pyvqnet.optim import Adam

from pyvqnet.nn.loss import MeanSquaredError
from pyvqnet.qnn.utils import CirqLayer


import cirq
import sympy
from pyvqnet.utils.utils import get_circuit_symbols


def dataloader(data,label,batch_size, shuffle = True)->np:
    if shuffle:
        for _ in range(len(data)//batch_size):
            random_index = np.random.randint(0, len(data), (batch_size, 1))
            yield data[random_index].reshape(batch_size,-1),label[random_index].reshape(batch_size,-1)
    else:
        for i in range(0,len(data)-batch_size+1,batch_size):
            yield data[i:i+batch_size].reshape(batch_size,-1), label[i:i+batch_size].reshape(batch_size,-1)

def get_accuary(result,label):
    result,label = np.array(result.data), np.array(label.data)

    is_correct = (np.abs(result - label) < 0.5)
    is_correct = np.count_nonzero(is_correct)
    acc = is_correct

    return acc

def load_mnist_4_4(dataset="training_data", digits=np.arange(10),
                path=".",encoding = "raw" ):
    import os, struct
    from array import array as pyarray
    if dataset == "training_data":
        fname_image = os.path.join(path, 'train-images.idx3-ubyte').replace('\\', '/')
        fname_label = os.path.join(path, 'train-labels.idx1-ubyte').replace('\\', '/')
    elif dataset == "testing_data":
        fname_image = os.path.join(path, 't10k-images.idx3-ubyte').replace('\\', '/')
        fname_label = os.path.join(path, 't10k-labels.idx1-ubyte').replace('\\', '/')
    else:
        raise ValueError("dataset must be 'training_data' or 'testing_data'")

    flbl = open(fname_label, 'rb')
    magic_nr, size = struct.unpack(">II", flbl.read(8))

    lbl = pyarray("b", flbl.read())
    flbl.close()

    fimg = open(fname_image, 'rb')
    magic_nr, size, rows, cols = struct.unpack(">IIII", fimg.read(16))
    img = pyarray("B", fimg.read())
    fimg.close()

    ind = [k for k in range(size) if lbl[k] in digits]
    N = len(ind)

    images_new = []# = np.zeros((N, 4, 4))
    labels = np.zeros((N, 1), dtype=int)
    import cv2
    for i in range(len(ind)):
        tmp1 = np.array(img[ind[i] * rows * cols: (ind[i] + 1) * rows * cols]).reshape((rows, cols))
        tmp1 = tmp1[4:24,4:24]
        tmp = cv2.resize(tmp1,(4,4))

        if np.max(tmp) ==0:
            continue
        if encoding == "normalized":
            sum_feature = np.power(np.sum([t**2 for t in tmp.flatten()]),0.5)

            normalize_feat = tmp/sum_feature
        images_new.append(normalize_feat)
        if lbl[ind[i]] ==digits[1]:
            labels[i] = 1
        else:
            labels[i] = 0

    return np.array(images_new), labels



class CIRQ_VQC:

    def __init__(self,simulator = cirq.Simulator ()):

        self._circuit = cirq.Circuit()
        n_qubits =4
        ###define qubits
        q0 = cirq.NamedQubit ('q0')
        q1 = cirq.NamedQubit ('q1')
        q2 = cirq.NamedQubit ('q2')
        q3 = cirq.NamedQubit ('q3')
        qubits = [q0,q1,q2,q3]
        self.qubits = [q0,q1,q2,q3]
        ###define varational parameters
        param = sympy.symbols(f'theta(0:24)')
        self.theta = np.asarray(param).reshape((4,6))

        ###define circuits
        circuit = cirq.Circuit()

        for i ,q in enumerate(qubits):
            circuit.append(cirq.rz(self.theta[i][0])(q))
            circuit.append(cirq.ry(self.theta[i][1])(q))
            circuit.append(cirq.rz(self.theta[i][2])(q))

        for d in range(3, 6):
            for i in range(n_qubits-1):
                circuit.append(cirq.CNOT(qubits[i], qubits[i + 1]))
            circuit.append(cirq.CNOT(qubits[n_qubits-1], qubits[0]))

            for i ,q in enumerate(qubits):
                circuit.append(cirq.ry(self.theta[i][d])(q))

        self._circuit = circuit

        ###define backend
        self._backend = simulator

        self._param_symbols_list,self._input_symbols_list = get_circuit_symbols(self._circuit)


    def run(self,resolver,init_state):

        rlt = self._backend.simulate(self._circuit,resolver,initial_state=init_state).final_state_vector
        z0 = cirq.Z(self.qubits[0])

        qubit_map={self.qubits[0]: 0}

        expectation = z0.expectation_from_state_vector(rlt, qubit_map).real

        return expectation

#define cirq circuits class
circuit = CIRQ_VQC()

class Model_cirq(Module):
    def __init__(self):
        super(Model_cirq, self).__init__()
        self.qvc = CirqLayer(circuit,24)

    def forward(self, x):

        y = self.qvc(x)*0.5 + 0.5
        return y.astype(x.dtype)


def run_cirq():

    x_train, y_train = load_mnist_4_4("training_data",digits=[3,6],encoding="normalized")
    y_train = y_train.reshape(-1, 1)

    x_test, y_test = load_mnist_4_4("testing_data",digits=[3,6],encoding="normalized")

    x_train = x_train.astype(np.float32)
    x_test = x_test.astype(np.float32)
    y_test = y_test.astype(np.float32)
    y_train = y_train.astype(np.float32)
    x_train = x_train[:100]

    y_train = y_train[:100]

    x_test = x_test[:50]

    y_test = y_test[:50]

    model = Model_cirq()

    optimizer = Adam(model.parameters(),lr =0.01)
    batch_size = 10
    epoch = 5

    loss = MeanSquaredError()
    print("start training..............")
    model.train()

    TL=[]
    TA=[]

    for i in range(epoch):
        count=0
        sum_loss = 0
        accuary = 0
        t = 0
        for data,label in dataloader(x_train,y_train,batch_size,False):

            optimizer.zero_grad()
            result = model(data)
            loss_b = loss(label,result)

            loss_b.backward()
            optimizer._step()
            sum_loss += loss_b.item()
            count+=batch_size
            accuary += get_accuary(result,label)
            t = t + 1

            print(f"epoch:{i},  #### loss:{sum_loss*batch_size/count} #####accuray:{accuary/count}")
        TL.append(sum_loss*batch_size/count)
        TA.append(accuary/count)
    print(f"cirq epoch {epoch},最终准确率 {TA[-1]}")

if __name__=="__main__":

    run_cirq()

量子逻辑门

处理量子比特的方式就是量子逻辑门。 使用量子逻辑门,我们有意识的使量子态发生演化。量子逻辑门是构成量子算法的基础。

基本量子逻辑门

在VQNet中,我们使用本源量子自研的 pyqpanda2 的各个逻辑门搭建量子线路,进行量子模拟。 当前pyqpanda2支持的逻辑门可参考pyqpanda2 量子逻辑门 部分的定义。 此外VQNet还封装了部分在量子机器学习中常用的量子逻辑门组合,这里仅留下了pyqpanda2版本的振幅编码,更多接口可使用参考 :ref: pq3_gate 中集成pyqpanda3的接口。

AmplitudeEmbeddingCircuit

pyvqnet.qnn.template.AmplitudeEmbeddingCircuit(input_feat, qubits)

\(2^n\) 特征编码为 \(n\) 量子比特的振幅向量。为了表示一个有效的量子态向量, features 的L2范数必须是1。

参数:
  • input_feat – 表示参数的numpy数组。

  • qubits – pyqpanda2分配的量子比特列表。

返回:

量子线路。

Example:

import numpy as np
import pyqpanda as pq
from pyvqnet.qnn.template import AmplitudeEmbeddingCircuit
input_feat = np.array([2.2, 1, 4.5, 3.7])
machine = pq.init_quantum_machine(pq.QMachineType.CPU)
m_qlist = machine.qAlloc_many(2)
m_clist = machine.cAlloc_many(2)
m_prog = pq.QProg()
cir = AmplitudeEmbeddingCircuit(input_feat,m_qlist)
print(cir)
pq.destroy_quantum_machine(machine)

#                              ┌────────────┐     ┌────────────┐
# q_0:  |0>─────────────── ─── ┤RY(0.853255)├ ─── ┤RY(1.376290)├
#           ┌────────────┐ ┌─┐ └──────┬─────┘ ┌─┐ └──────┬─────┘
# q_1:  |0>─┤RY(2.355174)├ ┤X├ ───────■────── ┤X├ ───────■──────
#           └────────────┘ └─┘                └─┘

基于pyqpanda2量子机器学习算法封装接口

以下使用pyqpanda2构建线路,并在pyvqnet中实现了一些经典量子机器学习算法。

QGAN制备任意分布初态

基于2019年 Christa Zoufal 的论文 Quantum Generative Adversarial Networks for learning and loading random distributions , VQNet提供了一个QGAN制备任意分布初态的例子。该算法使用纯量子变分线路制备特定随机分布的生成量子态,可以减少原先生成特定量子态所需的逻辑门,降低量子线路复杂度。 QGAN使用经典的GAN模型结构,分为Generator生成器与Discriminator鉴别器两个子模型,Generator为量子线路产生特定分布,而Generator生成的分布generated data samples 以及真实的随机分布training data samples 输入Discriminator模型进行鉴别真伪。

../_images/qgan-arch.PNG

构建VQNet的量子生成对抗网络接口 QGANAPI 类,我们可以对真实分布的数据 real_data 使用量子生成器进行初态制备。这里使用量子比特数为3,量子生成器内部含参线路模块重复次数为1。 使用的评价指标为KL散度。

import pickle
import os
import pyqpanda as pq
from pyvqnet.qnn.qgan.qgan_utils import QGANAPI
import numpy as np


##################################
num_of_qubits = 3  # paper config
rep = 1

number_of_data = 10000
# Load data samples from different distributions
mu = 1
sigma = 1
real_data = np.random.lognormal(mean=mu, sigma=sigma, size=number_of_data)


# intial
save_dir = None
qgan_model = QGANAPI(
    real_data,
    # numpy generated data distribution, 1 - dim.
    num_of_qubits,
    batch_size=2000,
    num_epochs=2000,
    q_g_cir=None,
    bounds = [0.0,2**num_of_qubits -1],
    reps=rep,
    metric="kl",
    tol_rel_ent=0.01,
    if_save_param_dir=save_dir
)

接下来使用其训练接口 train 训练。

# train
qgan_model.train()  # train qgan

eval 画出其与真实分布之间的概率分布函数对比:

# show probability distribution function of generated distribution and real distribution
qgan_model.eval(real_data)  #draw pdf

get_trained_quantum_parameters 获取训练参数并输出为一个numpy数组形式。如果 save_dir 不为空,则该类将保存参数到文件中。可以通过 load_param_and_eval 函数载入参数,并可以通过 get_circuits_with_trained_param 获取训练完参数的量子生成器pyqpanda2线路。

# get trained quantum parameters
param = qgan_model.get_trained_quantum_parameters()
print(f" trained param {param}")

#load saved parameters files
if save_dir is not None:
    path = os.path.join(
        save_dir, qgan_model._start_time + "trained_qgan_param.pickle")
    with open(path, "rb") as file:
        t3 = pickle.load(file)
    param = t3["quantum_parameters"]
    print(f" trained param {param}")

#show probability distribution function of generated distribution and real distribution
qgan_model.load_param_and_eval(param)

#calculate metric
print(qgan_model.eval_metric(param, "kl"))

#get generator quantum circuit
machine = pq.CPUQVM()
machine.init_qvm()
qubits = machine.qAlloc_many(num_of_qubits)
qpanda_cir = qgan_model.get_circuits_with_trained_param(qubits)
print(qpanda_cir)

生成lognormal分布的损失函数以及概率分布函数图,一般来说需要使用不同的随机种子多次训练该模型可得到较好结果:

../_images/qgan-loss.png

../_images/qgan-pdf.png

量子核SVM算法

在机器学习任务中,数据通常不能被原始空间中的超平面分隔。寻找此类超平面的一种常见技术是对数据应用非线性变换函数。 此函数称为特征映射,通过特征映射,我们可以在这个新的特征空间中计算数据点之间的距离有多近,从而进行机器学习的分类任务。

本例参照 Supervised learning with quantum enhanced feature spaces 论文的第一个方法构建变分线路进行数据分类任务。 gen_vqc_qsvm_data 为生成该例子所需的数据。 vqc_qsvm 为变分量子线路类,用来对输入数据进行分类。 vqc_qsvm.plot() 函数可视化了数据的分布情况。

../_images/VQC-SVM.png

from pyvqnet.qnn.svm import vqc_qsvm, gen_vqc_qsvm_data
import matplotlib.pyplot as plt
import numpy as np

batch_size = 40
maxiter = 40
training_size = 20
test_size = 10
gap = 0.3
#线路模块重复次数
rep = 3

#定义接口类
VQC_QSVM = vqc_qsvm(batch_size, maxiter, rep)
#随机生成数据
train_features, test_features, train_labels, test_labels, samples = \
    gen_vqc_qsvm_data(training_size=training_size, test_size=test_size, gap=gap)
VQC_QSVM.plot(train_features, test_features, train_labels, test_labels, samples)
#训练
VQC_QSVM.train(train_features, train_labels)
#测试数据测试
rlt, acc_1 = VQC_QSVM.predict(test_features, test_labels)
print(f"testing_accuracy {acc_1}")

除了上述直接用变分量子线路将经典数据特征映射到量子特征空间,在论文 Supervised learning with quantum enhanced feature spaces 中还介绍了使用量子线路直接估计核函数,并使用经典支持向量机进行分类的方法。类比经典SVM中的各种核函数 \(K(i,j)\) , 使用量子核函数定义经典数据在量子特征空间 \(\phi(\mathbf{x}_i)\) 的内积 :

\[|\langle \phi(\mathbf{x}_j) | \phi(\mathbf{x}_i) \rangle |^2 = |\langle 0 | U^\dagger(\mathbf{x}_j) U(\mathbf{x}_i) | 0 \rangle |^2\]

使用VQNet和pyqpanda2,我们定义一个 QuantumKernel_VQNet 产生量子核函数,并使用 sklearnSVC 进行分类:

../_images/qsvm-kernel.png

import numpy as np
import pyqpanda as pq
from sklearn.svm import SVC
from pyqpanda import *
from pyqpanda.Visualization.circuit_draw import *
from pyvqnet.qnn.svm import QuantumKernel_VQNet, gen_vqc_qsvm_data
import matplotlib
try:
    matplotlib.use('TkAgg')
except:
    pass

train_features, test_features,train_labels, test_labels, samples = gen_vqc_qsvm_data(20,5,0.3)
quantum_kernel = QuantumKernel_VQNet(n_qbits=2)
quantum_svc = SVC(kernel=quantum_kernel.evaluate)
quantum_svc.fit(train_features, train_labels)
score = quantum_svc.score(test_features, test_labels)
print(f"quantum kernel classification test score: {score}")

同时扰动随机近似优化器

class pyvqnet.qnn.SPSA(maxiter: int = 1000, last_avg: int = 1, c0: float = _C0, c1: float = 0.2, c2: float = 0.602, c3: float = 0.101, c4: float = 0, init_para=None, model=None, calibrate_flag=False)

同时扰动随机近似 (SPSA) 优化器。

SPSA 提供了一种用于逼近多元可微成本函数梯度的随机方法。 为实现这一点,使用扰动参数向量对成本函数进行两次评估:原始参数向量的每个分量同时随随机生成的值移动。 SPSA 网站 上提供了进一步的介绍。

参数:
  • maxiter – 要执行的最大迭代次数。默认值:1000。

  • last_avg – last_avg 迭代的平均参数。 如果 last_avg = 1,则只考虑最后一次迭代。默认值:1。

  • c0 – 初始a。更新参数的步长。默认值:0.2*pi

  • c1 – 初始的c。用于近似梯度的步长。默认值:0.1。

  • c2 – 论文中的alpha,用于在每次迭代时调整a(c0)。默认值:0.602。

  • c3 – 论文中的gamma,每次迭代时用来调整c(c1)。默认值:0.101。

  • c4 – 同样用来控制a的参数。默认值:0。

  • init_para – 初始化参数。默认值:无。

  • model – 参数模型:模型。默认值:无。

  • calibrate_flag – 是否校准超参数 a 和 c,默认值:False。

返回:

一个SPSA优化器实例

警告

SPSA只支持一维的输入参数。

Example:

from pyvqnet.qnn import AngleEmbeddingCircuit, expval, QpandaQCircuitVQCLayerLite, SPSA
from pyvqnet.qnn.template import BasicEntanglerTemplate
import pyqpanda as pq
from pyvqnet.nn.module import Module
#定义一个量子变分线路模型
class Model_spsa(Module):
    def __init__(self):
        super(Model_spsa, self).__init__()
        self.qvc = QpandaQCircuitVQCLayerLite(layer_fn_spsa_pq, 3)

    def forward(self, x):
        y = self.qvc(x)
        return y

#本例线路是最小化该VQC的期望值
def layer_fn_spsa_pq(input, weights):
    num_of_qubits = 1

    machine = pq.CPUQVM()
    machine.init_qvm()
    qubits = machine.qAlloc_many(num_of_qubits)
    c1 = AngleEmbeddingCircuit(input, qubits)
    weights =weights.reshape([4,1])
    bc_class = BasicEntanglerTemplate(weights, 1)
    c2 = bc_class.create_circuit(qubits)
    m_prog = pq.QProg()
    m_prog.insert(c1)
    m_prog.insert(c2)
    pauli_dict = {'Z0': 1}
    exp2 = expval(machine, m_prog, pauli_dict, qubits)

    return exp2

model = Model_spsa()
#定义一个SPSA优化器
optimizer = SPSA(maxiter=20,
    init_para=model.parameters(),
    model=model,
)
pyvqnet.qnn.SPSA._step(input_data)

优化 sapa 优化器

参数:

input_data – 输入训练数据QTensor

返回:

train_para:最终参数。

theta_best:最后 last_avg 次优化后的平均参数。

Example:

import numpy as np
import pyqpanda as pq

import sys
sys.path.insert(0, "../")
import pyvqnet

from pyvqnet.nn.module import Module
from pyvqnet.qnn import SPSA
from pyvqnet.tensor.tensor import QTensor
from pyvqnet.qnn import AngleEmbeddingCircuit, expval, QpandaQCircuitVQCLayerLite, expval
from pyvqnet.qnn.template import BasicEntanglerTemplate

#定义一个量子变分线路模型
class Model_spsa(Module):
    def __init__(self):
        super(Model_spsa, self).__init__()
        self.qvc = QpandaQCircuitVQCLayerLite(layer_fn_spsa_pq, 3)

    def forward(self, x):
        y = self.qvc(x)
        return y

#本例线路是最小化该VQC的期望值
def layer_fn_spsa_pq(input, weights):
    num_of_qubits = 1

    machine = pq.CPUQVM()
    machine.init_qvm()
    qubits = machine.qAlloc_many(num_of_qubits)
    c1 = AngleEmbeddingCircuit(input, qubits)
    weights =weights.reshape([4,1])
    bc_class = BasicEntanglerTemplate(weights, 1)
    c2 = bc_class.create_circuit(qubits)
    m_prog = pq.QProg()
    m_prog.insert(c1)
    m_prog.insert(c2)
    pauli_dict = {'Z0': 1}
    exp2 = expval(machine, m_prog, pauli_dict, qubits)

    return exp2

model = Model_spsa()
#定义一个SPSA优化器
optimizer = SPSA(maxiter=20,
    init_para=model.parameters(),
    model=model,
)
#初始化参数
data = QTensor(np.array([[0.27507603]]))
p = model.parameters()
p[0].data = pyvqnet._core.Tensor( np.array([3.97507603, 3.12950603, 1.00854038,
                1.25907603]))
#调用SPSA进行迭代优化
optimizer._step(input_data=data)

#计算优化后的VQC期望值
y = model(data)
print(y)