Quantum Machine Learning API using QPanda2

警告

The quantum computing part of the following interface uses pyQPanda2 https://pyqpanda-toturial.readthedocs.io/zh/latest/.

Due to the compatibility issues between pyQPanda2 and pyqpanda3, you need to install pyqpnda2 yourself, pip install pyqpanda

Quantum Computing Layer

QuantumLayer

QuantumLayer is a package class of autograd module that supports ariational quantum circuits. You can define a function as an argument, such as qprog_with_measure, This function needs to contain the quantum circuit defined by pyQPanda: It generally contains coding-circuit, evolution-circuit and measurement-operation. This QuantumLayer class can be embedded into the hybrid quantum classical machine learning model and minimize the objective function or loss function of the hybrid quantum classical model through the classical gradient descent method. You can specify the gradient calculation method of quantum circuit parameters in QuantumLayer by change the parameter diff_method. QuantumLayer currently supports two methods, one is finite_diff and the other is parameter-shift methods.

The finite_diff method is one of the most traditional and common numerical methods for estimating function gradient.The main idea is to replace partial derivatives with differences:

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

For the parameter-shift method we use the objective function, such as:

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

It is theoretically possible to calculate the gradient of parameters about Hamiltonian in a quantum circuit by the more precise method: 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='')

Abstract calculation module for variational quantum circuits. It simulates a parameterized quantum circuit and gets the measurement result. QuantumLayer inherits from Module ,so that it can calculate gradients of circuits parameters,and train variational quantum circuits model or embed variational quantum circuits into hybird quantum and classic model.

This class dos not need you to initialize virtual machine in the qprog_with_measure function.

参数:
  • qprog_with_measure – callable quantum circuits functions ,cosntructed by pyQPanda2

  • para_numint - Number of parameter

  • machine_type_or_cloud_token – qpanda machine type or pyQPanda2 QCLOUD token : https://pyqpanda-toturial.readthedocs.io/zh/latest/Realchip.html

  • num_of_qubits – num of qubits

  • num_of_cbits – num of classic bits

  • diff_method – ‘parameter_shift’ or ‘finite_diff’

  • delta – delta for diff

  • dtype – The data type of the parameter, defaults: None, use the default data type kfloat32, which represents a 32-bit floating point number.

  • name – name of the output layer

返回:

a module can calculate quantum circuits .

备注

qprog_with_measure is quantum circuits function defined in pyQPanda2 :https://pyqpanda-toturial.readthedocs.io/zh/latest/QCircuit.html.

This function should contain following parameters,otherwise it can not run properly in QuantumLayer.

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

input: array_like input 1-dim classic data

param: array_like input 1-dim quantum circuit’s parameters

qubits: qubits allocated by QuantumLayer

cbits: cbits allocated by QuantumLayer.if your circuits does not use cbits,you should also reserve this parameter.

m_machine: simulator created by QuantumLayer

Use the m_para attribute of QuantumLayer to get the training parameters of the variable quantum circuit. The parameter is a QTensor class, which can be converted into a numpy array using the to_numpy() interface.

备注

The class have alias: QpandaQCircuitVQCLayer .

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,m_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,m_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]
# ]

QuantumLayerV2

If you are more familiar with pyQPanda2 syntax, please using QuantumLayerV2 class, you can define the quantum circuits function by using qubits, cbits and machine, then take it as a argument qprog_with_measure of QuantumLayerV2.

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

Abstract calculation module for variational quantum circuits. It simulates a parameterized quantum circuit and gets the measurement result. QuantumLayer inherits from Module ,so that it can calculate gradients of circuits parameters,and train variational quantum circuits model or embed variational quantum circuits into hybird quantum and classic model.

To use this module, you need to create your quantum virtual machine and allocate qubits and cbits.

参数:
  • qprog_with_measure – callable quantum circuits functions ,cosntructed by pyQPanda2

  • para_numint - Number of parameter

  • diff_method – ‘parameter_shift’ or ‘finite_diff’

  • delta – delta for diff

  • dtype – The data type of the parameter, defaults: None, use the default data type kfloat32, which represents a 32-bit floating point number.

  • name – name of the output layer

返回:

a module can calculate quantum circuits .

备注

qprog_with_measure is quantum circuits function defined in pyQPanda :https://pyqpanda-toturial.readthedocs.io/zh/latest/QCircuit.html.

This function should contains following parameters,otherwise it can not run properly in QuantumLayerV2.

Compare to QuantumLayer.you should allocate qubits and simulator: https://pyqpanda-toturial.readthedocs.io/zh/latest/QuantumMachine.html,

you may also need to allocate cbits if qprog_with_measure needs quantum measure: https://pyqpanda-toturial.readthedocs.io/zh/latest/Measure.html

qprog_with_measure (input,param)

input: array_like input 1-dim classic data

param: array_like input 1-dim quantum circuit’s parameters

备注

The class have alias: QpandaQCircuitVQCLayerLite .

Example:

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

    m_machine = pq.CPUQVM()# outside
    m_machine.init_qvm()# outside
    qubits = m_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,m_machine,qubits)
    return rlt_prob


pqc = QuantumLayerV2(pqctest,3)

#classic data as input
input = QTensor([[1,2,3,4],[4,2,2,3],[3,3,2,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]
# ]

QuantumBatchAsyncQcloudLayer

When you install the latest version of pyqpanda, you can use this interface to define a variational circuit and submit it to originqc for running on the real chip.

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={})

Abstract computing module for originqc real chips using pyqpanda QCLOUD starting with version 3.8.2.2. It submits parameterized quantum circuits to real chips and obtains measurement results. If diff_method == “random_coordinate_descent” , we will randomly select a single parameter to compute the gradient, and the other parameters will remain zero. Ref: https://arxiv.org/abs/2311.00088 .

备注

qcloud_token is the API token you applied for at https://qcloud.originqc.com.cn/. origin_qprog_func needs to return data of type pypqanda.QProg. If pauli_str_dict is not set, you need to ensure that measure has been inserted into the QProg. The form of origin_qprog_func must be as follows:

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

input: Input 1~2-dimensional classic data. In the case of two-dimensional data, the first dimension is the batch size.

param: Enter the parameters to be trained for the one-dimensional variational quantum circuit.

machine: The simulator QCloud created by QuantumBatchAsyncQcloudLayer does not require users to define it in additional functions.

qubits: Qubits created by the simulator QCloud created by QuantumBatchAsyncQcloudLayer, the number is num_qubits, the type is pyQpanda.Qubits, no need for the user to define it in the function.

cbits: Classic bits allocated by QuantumBatchAsyncQcloudLayer, the number is num_cubits, the type is pyQpanda.ClassicalCondition, no need for the user to define it in the function. .

参数:
  • origin_qprog_func – The variational quantum circuit function built by pyQPanda2 must return type of QProg.

  • qcloud_tokenstr - The type of quantum machine or cloud token used for execution.

  • para_numint - Number of parameters, the parameter is a QTensor of size [para_num].

  • num_qubitsint - Number of qubits in the quantum circuit.

  • num_cubitsint - The number of classical bits used for measurement in quantum circuits.

  • pauli_str_dictdict|list - A dictionary or list of dictionaries representing Pauli operators in quantum circuits. The default is “none”, and the measurement operation is performed. If a dictionary of Pauli operators is entered, a single expectation or multiple expectations will be calculated.

  • shotint - Number of measurements. The default value is 1000.

  • initializer – Initializer for parameter values. The default is “None”, using 0~2*pi normal distribution.

  • dtype – The data type of the parameter. The default value is None, which uses the default data type pyvqnet.kfloat32.

  • name – The name of the module. Defaults to empty string.

  • diff_method – Differentiation method for gradient computation. Default is “parameter_shift”. If diff_method == “random_coordinate_descent” , we will randomly select a single parameter to compute the gradient, and the other parameters will remain zero. Ref: https://arxiv.org/abs/2311.00088 .

  • submit_kwargs – Additional keyword parameters for submitting quantum circuits, default: {“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}, when test_qcloud_fake is set to True, the local CPUQVM is simulated.

  • query_kwargs – Additional keyword parameters for querying quantum results, default: {“timeout”:2,”print_query_info”:True,”sub_circuits_split_size”:1}.

返回:

A module that can calculate quantum circuits.

Example:

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

#set_test_qcloud_fake(False) #uncomments this code to use realchip


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])  # pylint: disable=expression-not-assigned
    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)

QuantumLayerMultiProcess

If you are more familiar with pyQPanda syntax, please using QuantumLayerMultiProcess class, you can define the quantum circuits function by using qubits, cbits and machine, then take it as a argument qprog_with_measure of QuantumLayerMultiProcess.

class pyvqnet.qnn.quantumlayer.QuantumLayerMultiProcess(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='')

Abstract calculation module for variational quantum circuits. This class uses multiprocess to accelerate quantum circuit simulation.

It simulates a parameterized quantum circuit and gets the measurement result. QuantumLayer inherits from Module ,so that it can calculate gradients of circuits parameters,and train variational quantum circuits model or embed variational quantum circuits into hybird quantum and classic model.

To use this module, you need to create your quantum virtual machine and allocate qubits and cbits.

参数:
  • qprog_with_measure – callable quantum circuits functions ,cosntructed by pyQPanda2.

  • para_numint - Number of parameter

  • num_of_qubits – num of qubits.

  • num_of_cbits – num of classic bits.

  • diff_method – ‘parameter_shift’ or ‘finite_diff’.

  • delta – delta for diff.

  • dtype – The data type of the parameter, defaults: None, use the default data type kfloat32, which represents a 32-bit floating point number.

  • name – name of the output layer

返回:

a module can calculate quantum circuits .

备注

qprog_with_measure is quantum circuits function defined in pyQPanda : https://github.com/OriginQ/QPanda-2.

This function should contains following parameters,otherwise it can not run properly in QuantumLayerMultiProcess.

Compare to QuantumLayer.you should allocate qubits and simulator,

you may also need to allocate cbits if qprog_with_measure needs quantum Measure.

qprog_with_measure (input,param)

input: array_like input 1-dim classic data

param: array_like input 1-dim quantum circuit’s parameters

Example:

import pyqpanda as pq
from pyvqnet.qnn.measure import ProbsMeasure
from pyvqnet.qnn.quantumlayer import QuantumLayerMultiProcess
import numpy as np
from pyvqnet.tensor import QTensor

def pqctest (input,param,nqubits,ncubits):
    machine = pq.CPUQVM()
    machine.init_qvm()
    qubits = machine.qAlloc_many(nqubits)
    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 = QuantumLayerMultiProcess(pqctest,3,4,1)
#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]
# ]

NoiseQuantumLayer

In the real quantum computer, due to the physical characteristics of the quantum bit, there is always inevitable calculation error. In order to better simulate this error in quantum virtual machine, VQNet also supports quantum virtual machine with noise. The simulation of quantum virtual machine with noise is closer to the real quantum computer. We can customize the supported logic gate type and the noise model supported by the logic gate. The existing supported quantum noise model is defined in pyQPanda2 NoiseQVM .

We can use NoiseQuantumLayer to define an automatic microclassification of quantum circuits. NoiseQuantumLayer supports pyQPanda2 quantum virtual machine with noise. You can define a function as an argument qprog_with_measure. This function needs to contain the quantum circuit defined by pyQPanda, as also you need to pass in a argument noise_set_config, by using the pyQPanda interface to set up the noise model.

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='')

Abstract calculation module for variational quantum circuits. It simulates a parameterized quantum circuit and gets the measurement result. QuantumLayer inherits from Module ,so that it can calculate gradients of circuits parameters,and train variational quantum circuits model or embed variational quantum circuits into hybird quantum and classic model.

This module should be initialized with noise model by noise_set_config.

参数:
  • qprog_with_measure – callable quantum circuits functions ,cosntructed by pyQPanda2

  • para_numint - Number of para_num

  • machine_type – pyQPanda2 machine type

  • num_of_qubits – num of qubits

  • num_of_cbits – num of cbits

  • diff_method – ‘parameter_shift’ or ‘finite_diff’

  • delta – delta for diff

  • noise_set_config – noise set function

  • dtype – The data type of the parameter, defaults: None, use the default data type kfloat32, which represents a 32-bit floating point number.

  • name – name of the output layer

返回:

a module can calculate quantum circuits with noise model.

备注

qprog_with_measure is quantum circuits function defined in pyQPanda :https://pyqpanda-toturial.readthedocs.io/zh/latest/QCircuit.html.

This function should contains following parameters,otherwise it can not run properly in NoiseQuantumLayer.

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

input: array_like input 1-dim classic data

param: array_like input 1-dim quantum circuit’s parameters

qubits: qubits allocated by NoiseQuantumLayer

cbits: cbits allocated by NoiseQuantumLayer.if your circuits does not use cbits,you should also reserve this parameter.

m_machine: simulator created by NoiseQuantumLayer

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.]

Here is an example of noise_set_config, here we add the noise model BITFLIP_KRAUS_OPERATOR where the noise argument p=0.01 to the quantum gate RX , RY , RZ , X , Y , Z , H, etc.

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

DataParallelHybirdVQCQpandaQVMLayer

class pyvqnet.qnn.DataParallelHybirdVQCQpandaQVMLayer(vqc_module: Module, qcloud_token: str, num_qubits: int, num_cubits: int, pauli_str_dict: List[Dict] | Dict | None = None, shots: int = 1000, dtype: int | None = None, name: str = '', submit_kwargs: Dict = {}, query_kwargs: Dict = {})

A data parallel version of HybirdVQCQpandaQVMLayer, where vqc_module is a user-defined quantum variational circuit model, and the QMachine setting save_ir= True. Use data parallelism to batch the first dimension of the input data The number of processes is divided according to the number of processes allocated in CommController, and data parallelism is performed in multiple processes based on mpi or nccl. Please note that one process corresponds to a GPU device on one node. This module in each process Submit the quantum circuit generated by batch processing number/node number data in forward calculation, calculate the gradient contributed by batch processing number/node number data in reverse calculation, and calculate the average gradient of parameters on multiple nodes through all_reduce.

备注

This module splits the input internally and moves the data to the corresponding device. The 0th process calculates [0, batch number/node number] data, and the kth process calculates [(k-1) batch number/node number, k*batch number/node number]

参数:
  • vqc_module – with forward() of vqc_module.

  • qcloud_tokenstr - Type of quantum machine or cloud token to use for execution.

  • num_qubitsint - Number of qubits in the quantum circuit.

  • num_cubitsint - The number of classical bits used for measurement in the quantum circuit.

  • pauli_str_dictdict|list - A dictionary or list of dictionaries representing the Pauli operators in the quantum circuit. Default is None.

  • shotsint - The number of shots in the quantum circuit. Number of line measurements. The default value is 1000.

  • name – Module name. The default value is an empty string.

  • submit_kwargs – Additional keyword parameters for submitting quantum circuits, default value:{“chip_id”:pyqpanda.real_chip_type.origin_72,”is_amend”:True,”is_mapping”:True,”is_optimization”:True,”default_task_group_size”:200 ,”test_qcloud_fake”:True}.

  • query_kwargs – Additional keyword parameters for querying quantum results, default value: {“timeout”:2,”print_query_info”:True,”sub_circuits_split_size”:1}.

The following is calculated using cpu For example, the command for a single node and dual processes is as follows: mpirun -n 2 python xxx.py

Example:

from pyvqnet.distributed import *

Comm_OP = CommController("mpi")
from pyvqnet.qnn import *
from pyvqnet.qnn.vqc import *
import pyvqnet
from pyvqnet.nn import Module, Linear
from pyvqnet.device import DEV_GPU_0
pyvqnet.utils.set_random_seed(42)


class Hybird(Module):
    def __init__(self):
        self.cl1 = Linear(3, 3)
        self.ql = QModel(num_wires=6, dtype=pyvqnet.kcomplex64)
        self.cl2 = Linear(1, 2)

    def forward(self, x):
        x = self.cl1(x)
        x = self.ql(x)
        x = self.cl2(x)
        return x


class QModel(Module):
    def __init__(self, num_wires, dtype, grad_mode=""):
        super(QModel, self).__init__()

        self._num_wires = num_wires
        self._dtype = dtype
        self.qm = QMachine(num_wires,
                        dtype=dtype,
                        grad_mode=grad_mode,
                        save_ir=True)
        self.rx_layer = RX(has_params=True, trainable=False, wires=0)
        self.ry_layer = RY(has_params=True, trainable=False, wires=1)
        self.rz_layer = RZ(has_params=True, trainable=False, wires=1)
        self.u1 = U1(has_params=True, trainable=True, wires=[2])
        self.u2 = U2(has_params=True, trainable=True, wires=[3])
        self.u3 = U3(has_params=True, trainable=True, wires=[1])
        self.i = I(wires=[3])
        self.s = S(wires=[3])
        self.x1 = X1(wires=[3])
        self.y1 = Y1(wires=[3])
        self.z1 = Z1(wires=[3])
        self.x = PauliX(wires=[3])
        self.y = PauliY(wires=[3])
        self.z = PauliZ(wires=[3])
        self.swap = SWAP(wires=[2, 3])
        self.cz = CZ(wires=[2, 3])
        self.cr = CR(has_params=True, trainable=True, wires=[2, 3])
        self.rxx = RXX(has_params=True, trainable=True, wires=[2, 3])
        self.rzz = RYY(has_params=True, trainable=True, wires=[2, 3])
        self.ryy = RZZ(has_params=True, trainable=True, wires=[2, 3])
        self.rzx = RZX(has_params=True, trainable=False, wires=[2, 3])
        self.toffoli = Toffoli(wires=[2, 3, 4], use_dagger=True)

        self.h = Hadamard(wires=[1])

        self.iSWAP = iSWAP(True, True, wires=[0, 2])
        self.tlayer = T(wires=1)
        self.cnot = CNOT(wires=[0, 1])
        self.measure = MeasureAll(obs={'Z0': 2, 'Y3': 3})

    def forward(self, x, *args, **kwargs):
        self.qm.reset_states(x.shape[0])
        self.i(q_machine=self.qm)
        self.s(q_machine=self.qm)
        self.swap(q_machine=self.qm)
        self.cz(q_machine=self.qm)
        self.x(q_machine=self.qm)
        self.x1(q_machine=self.qm)
        self.y(q_machine=self.qm)
        self.y1(q_machine=self.qm)
        self.z(q_machine=self.qm)
        self.z1(q_machine=self.qm)
        self.ryy(q_machine=self.qm)
        self.rxx(q_machine=self.qm)
        self.rzz(q_machine=self.qm)
        self.rzx(q_machine=self.qm, params=x[:, [1]])
        self.cr(q_machine=self.qm)
        self.u1(q_machine=self.qm)
        self.u2(q_machine=self.qm)
        self.u3(q_machine=self.qm)
        self.rx_layer(params=x[:, [0]], q_machine=self.qm)
        self.cnot(q_machine=self.qm)
        self.h(q_machine=self.qm)
        self.iSWAP(q_machine=self.qm)
        self.ry_layer(params=x[:, [1]], q_machine=self.qm)
        self.tlayer(q_machine=self.qm)
        self.rz_layer(params=x[:, [2]], q_machine=self.qm)
        self.toffoli(q_machine=self.qm)
        rlt = self.measure(q_machine=self.qm)

        return rlt

device = Comm_OP.get_rank
input_x = tensor.QTensor([[0.1, 0.2, 0.3]])
input_x = tensor.broadcast_to(input_x, [20, 3])
input_x.requires_grad = True



qunatum_model = QModel(num_wires=6, dtype=pyvqnet.kcomplex64)

l = DataParallelHybirdVQCQpandaQVMLayer(
    Comm_OP,
    qunatum_model,
    "3047DE8A59764BEDAC9C3282093B16AF1",

    num_qubits=6,
    num_cubits=6,
    pauli_str_dict={
        'Z0': 2,
        'Y3': 3
    },
    shots=1000,
    name="",
    submit_kwargs={"test_qcloud_fake": True},
    query_kwargs={})

y = l(input_x)
print(y)
y.backward()
for p in qunatum_model.parameters():
    print(p.grad)

The following is an example of nccl calculation using gpu. The command for single node dual process is as follows: mpirun -n 2 python xxx.py

Example:

from pyvqnet.distributed import *

Comm_OP = CommController("nccl")
#rest code not changed

The following is an example of multi-node multi-process parallel calculation. Please ensure that the script is run in the same path and the same python environment on different nodes, and write the ip address mapping file hosts on each node. The format refers to hostfile, f, hostfile.

Example:

#hosts example
10.10.7.107 slots=2
10.10.7.109 slots=2

To use mpi for 2 nodes, 2 processes per node, and 4 processes in total, you can run vqnetrun -np 4 -f hosts python xxx.py

Example:

from pyvqnet.distributed import *
Comm_OP = CommController("mpi")
#rest code not changed

To use nccl for 2 nodes, 2 processes per node, and 4 processes in total, you can run vqnetrun -np 4 -f hosts python xxx.py

Example:

from pyvqnet.distributed import *
Comm_OP = CommController("nccl")
#rest code not changed

Quantum Gates

The way to deal with qubits is called quantum gates. Using quantum gates, we consciously evolve quantum states. Quantum gates are the basis of quantum algorithms.

Basic quantum gates

In VQNet, we use each logic gate of pyQPanda developed by the original quantum to build quantum circuit and conduct quantum simulation. The gates currently supported by pyQPanda can be defined in pyQPanda’s quantum gate section. In addition, VQNet also encapsulates some quantum gate combinations commonly used in quantum machine learning.

AmplitudeEmbeddingCircuit

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

Encodes \(2^n\) features into the amplitude vector of \(n\) qubits. To represent a valid quantum state vector, the L2-norm of features must be one.

参数:
  • input_feat – numpy array which represents paramters

  • qubits – qubits allocated by pyQPanda

返回:

quantum circuits

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])
m_machine = pq.init_quantum_machine(pq.QMachineType.CPU)
m_qlist = m_machine.qAlloc_many(2)
m_clist = m_machine.cAlloc_many(2)
m_prog = pq.QProg()
cir = AmplitudeEmbeddingCircuit(input_feat,m_qlist)
print(cir)
pq.destroy_quantum_machine(m_machine)

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

Quantum Machine Learning APIs using pyQPanda2

Quantum Generative Adversarial Networks for learning and loading random distributions

Quantum Generative Adversarial Networks(QGAN )algorithm uses pure quantum variational circuits to prepare the generated quantum states with specific random distribution, which can reduce the logic gates required to generate specific quantum states and reduce the complexity of quantum circuits.It uses the classical GAN model structure, which has two sub-models: Generator and Discriminator. The Generator generates a specific distribution for the quantum circuit.And the Discriminator discriminates the generated data samples generated by the Generator and the real randomly distributed training data samples. Here is an example of VQNet implementing QGAN learning and loading random distributions based on the paper Quantum Generative Adversarial Networks for learning and loading random distributions of Christa Zoufal.

../_images/qgan-arch.PNG

In order to realize the construction of QGANAPI class of quantum generative adversarial network by VQNet, the quantum generator is used to prepare the initial state of the real distributed data. The number of quantum bits is 3, and the repetition times of the internal parametric circuit module of the quantum generator is 1. Meanwhile, KL is used as the metric for the QGAN loading random distribution.

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
)

The following is the train module of QGAN.

# train
qgan_model.train()  # train qgan

The eval module of QGAN is designed to draw the loss function curve and probability distribution diagram between the random distribution prepared by QGAN and the real distribution.

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

The get_trained_quantum_parameters module of QGAN is used to get training parameters and output them as a numpy array. If save_DIR is not empty, the training parameters are saved to a file.The Load_param_and_eval module of QGAN loads training parameters, and the get_circuits_with_trained_param module obtains pyQPanda circuit generated by quantum generator after training.

# 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
m_machine = pq.CPUQVM()
m_machine.init_qvm()
qubits = m_machine.qAlloc_many(num_of_qubits)
qpanda_cir = qgan_model.get_circuits_with_trained_param(qubits)
print(qpanda_cir)

In general, QGAN learning and loading random distribution requires multiple training models with different random seeds to obtain the expected results. For example, the following is the graph of the probability distribution function between the lognormal distribution implemented by QGAN and the real lognormal distribution, and the loss function curve between QGAN’s generator and discriminator.

../_images/qgan-loss.png

../_images/qgan-pdf.png

quantum kernal SVM

In machine learning tasks, data often cannot be separated by a hyperplane in the original space. A common technique for finding such hyperplanes is to apply a nonlinear transformation function to the data. This function is called a feature map, through which we can calculate how close the data points are in this new feature space for the classification task of machine learning.

This example refers to the thesis: Supervised learning with quantum enhanced feature spaces . The first method constructs variational circuits for data classification tasks.

gen_vqc_qsvm_data is the data needed to generate this example. vqc_qsvm is a variable sub-circuit class used to classify the input data. The vqc_qsvm.plot() function visualizes the distribution of the data.

../_images/VQC-SVM.png

"""
VQC QSVM
"""
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
#sub-circuits repeat times
rep = 3

#defines QSVM class
VQC_QSVM = vqc_qsvm(batch_size, maxiter, rep)
#randomly generates data from thesis.
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)
#train
VQC_QSVM.train(train_features, train_labels)
#test
rlt, acc_1 = VQC_QSVM.predict(test_features, test_labels)
print(f"testing_accuracy {acc_1}")

In addition to the above-mentioned direct use of variational quantum circuits to map classical data features to quantum feature spaces, in the paper Supervised learning with quantum enhanced feature spaces, the method of directly estimating kernel functions using quantum circuits and classifying them using classical support vector machines is also introduced. Analogy to various kernel functions in classical SVM \(K(i,j)\) , use quantum kernel function to define the inner product of classical data in quantum feature space \(\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\]

Using VQNet and pyQPanda, we define a QuantumKernel_VQNet to generate a quantum kernel function and use sklearn for classification:

../_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
import matplotlib.pyplot as plt

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}")

Simultaneous Perturbation Stochastic Approximation optimizers

pyvqnet.qnn.SPSA(maxiter: int = 1000, save_steps: int = 1, 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)

Simultaneous Perturbation Stochastic Approximation (SPSA) optimizer.

SPSA provides a stochastic method for approximating the gradient of a multivariate differentiable cost function. To achieve this, the cost function is evaluated twice using a perturbed parameter vector: each component of the original parameter vector is simultaneously shifted by a randomly generated value. Further information is available on the SPSA website.

参数:
  • maxiter – The maximum number of iterations to perform. Default value: 1000.

  • save_steps – Save the intermediate information of each save_steps step. Default value: 1.

  • last_avg – Averaging parameter for last_avg iterations. If last_avg = 1, only the last iteration is considered. Default value: 1.

  • c0 – initial a. Step size for updating parameters. Default value: 0.2*pi

  • c1 – initial c. The step size used to approximate the gradient. Default: 0.1.

  • c2 – alpha from the paper, used to adjust a(c0) at each iteration. Default value: 0.602.

  • c3 – gamma in the paper, used to adjust c(c1) at each iteration. Default value: 0.101.

  • c4 – Also used to control the parameters of a. Default value: 0.

  • init_para – Initialization parameters. Default: None.

  • model – Parametric model: model. Default: None.

  • calibrate_flag – whether to calibrate hpyer parameters a and c, default value: False.

返回:

an SPSA optimizer instance

警告

SPSA only supports 1-dim paramters.

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, QuantumLayerV2, expval
from pyvqnet.qnn.template import BasicEntanglerTemplate

class Model_spsa(Module):
    def __init__(self):
        super(Model_spsa, self).__init__()
        self.qvc = QuantumLayerV2(layer_fn_spsa_pq, 3)

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


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()

optimizer = SPSA(maxiter=20,
    init_para=model.parameters(),
    model=model,
)
pyvqnet.qnn.SPSA._step(input_data)

use SPSA to optimize input data.

参数:

input_data – input data

返回:

train_para: final parameter

theta_best: The average parameters of after last 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, QuantumLayerV2, expval
from pyvqnet.qnn.template import BasicEntanglerTemplate


class Model_spsa(Module):
    def __init__(self):
        super(Model_spsa, self).__init__()
        self.qvc = QuantumLayerV2(layer_fn_spsa_pq, 3)

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


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()

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]))

optimizer._step(input_data=data)


y = model(data)
print(y)

Quantum fisher information computation matrix

class pyvqnet.qnn.opt.quantum_fisher(py_qpanda_config, params, target_gate_type_lists, target_gate_bits_lists, qcir_lists, wires)

Returns the quantum fisher information matrix for a quantum circuit.

\[\mathrm{QFIM}_{i, j}=4 \operatorname{Re}\left[\left\langle\partial_i \psi(\boldsymbol{\theta}) \mid \partial_j \psi(\boldsymbol{\theta})\right\rangle-\left\langle\partial_i \psi(\boldsymbol{\theta}) \mid \psi(\boldsymbol{\theta})\right\rangle\left\langle\psi(\boldsymbol{\theta}) \mid \partial_j \psi(\boldsymbol{\theta})\right\rangle\right]\]

The short version is :math:\(\left|\partial_j \psi(\boldsymbol{\theta})\right\rangle:=\frac{\partial}{\partial \theta_j}|\psi(\boldsymbol{\theta})\rangle\).

备注

Currently only RX,RY,RZ are supported.

参数:
  • params – Variable parameters in circuits.

  • target_gate_type_lists – Supports “RX”, “RY”, “RZ” or lists.

  • target_gate_bits_lists – Which quantum bit or bits the parameterised gate acts on .

  • qcir_lists – The list of quantum circles before the target parameterised gate to compute the metric tensor, see the following example.

  • wires – Total Quantum Bit Index for Quantum Circuits.

Example:

import pyqpanda as pq

from pyvqnet import *
from pyvqnet.qnn.opt import pyqpanda_config_wrapper, insert_pauli_for_mt, quantum_fisher
from pyvqnet.qnn import ProbsMeasure
import numpy as np
import pennylane as qml
import pennylane.numpy as pnp

n_wires = 4
def layer_subcircuit_new(config: pyqpanda_config_wrapper, params):
    qcir = pq.QCircuit()
    qcir.insert(pq.RX(config._qubits[0], params[0]))
    qcir.insert(pq.RY(config._qubits[1], params[1]))

    qcir.insert(pq.CNOT(config._qubits[0], config._qubits[1]))

    qcir.insert(pq.RZ(config._qubits[2], params[2]))
    qcir.insert(pq.RZ(config._qubits[3], params[3]))
    return qcir


def get_p1_diagonal_new(config, params, target_gate_type, target_gate_bits,
                    wires):
    qcir = layer_subcircuit_new(config, params)
    qcir2 = insert_pauli_for_mt(config._qubits, target_gate_type,
                                target_gate_bits)
    qcir3 = pq.QCircuit()
    qcir3.insert(qcir)
    qcir3.insert(qcir2)

    m_prog = pq.QProg()
    m_prog.insert(qcir3)
    return ProbsMeasure(wires, m_prog, config._machine, config._qubits)

config = pyqpanda_config_wrapper(n_wires)
qcir = []

qcir.append(get_p1_diagonal_new)

params2 = QTensor([0.5, 0.5, 0.5, 0.25], requires_grad=True)

mt = quantum_fisher(config, params2, [['RX', 'RY', 'RZ', 'RZ']],
                        [[0, 1, 2, 3]], qcir, [0, 1, 2, 3])

# The above example shows that there are no identical gates in the same layer,
# but in the same layer you need to modify the logic gates according to the following example.

n_wires = 3
def layer_subcircuit_01(config: pyqpanda_config_wrapper, params):
    qcir = pq.QCircuit()
    qcir.insert(pq.RX(config._qubits[0], params[0]))
    qcir.insert(pq.RY(config._qubits[1], params[1]))
    qcir.insert(pq.CNOT(config._qubits[0], config._qubits[1]))

    return qcir

def layer_subcircuit_02(config: pyqpanda_config_wrapper, params):
    qcir = pq.QCircuit()
    qcir.insert(pq.RX(config._qubits[0], params[0]))
    qcir.insert(pq.RY(config._qubits[1], params[1]))
    qcir.insert(pq.CNOT(config._qubits[0], config._qubits[1]))
    qcir.insert(pq.RZ(config._qubits[1], params[2]))
    return qcir

def layer_subcircuit_03(config: pyqpanda_config_wrapper, params):
    qcir = pq.QCircuit()
    qcir.insert(pq.RX(config._qubits[0], params[0]))
    qcir.insert(pq.RY(config._qubits[1], params[1]))
    qcir.insert(pq.CNOT(config._qubits[0], config._qubits[1])) #  01 part

    qcir.insert(pq.RZ(config._qubits[1], params[2]))  #  02 part

    qcir.insert(pq.RZ(config._qubits[1], params[3]))
    return qcir

def get_p1_diagonal_01(config, params, target_gate_type, target_gate_bits,
                    wires):
    qcir = layer_subcircuit_01(config, params)
    qcir2 = insert_pauli_for_mt(config._qubits, target_gate_type,
                                target_gate_bits)
    qcir3 = pq.QCircuit()
    qcir3.insert(qcir)
    qcir3.insert(qcir2)

    m_prog = pq.QProg()
    m_prog.insert(qcir3)
    return ProbsMeasure(wires, m_prog, config._machine, config._qubits)

def get_p1_diagonal_02(config, params, target_gate_type, target_gate_bits,
                    wires):
    qcir = layer_subcircuit_02(config, params)
    qcir2 = insert_pauli_for_mt(config._qubits, target_gate_type,
                                target_gate_bits)
    qcir3 = pq.QCircuit()
    qcir3.insert(qcir)
    qcir3.insert(qcir2)

    m_prog = pq.QProg()
    m_prog.insert(qcir3)
    return ProbsMeasure(wires, m_prog, config._machine, config._qubits)

def get_p1_diagonal_03(config, params, target_gate_type, target_gate_bits,
                    wires):
    qcir = layer_subcircuit_03(config, params)
    qcir2 = insert_pauli_for_mt(config._qubits, target_gate_type,
                                target_gate_bits)
    qcir3 = pq.QCircuit()
    qcir3.insert(qcir)
    qcir3.insert(qcir2)

    m_prog = pq.QProg()
    m_prog.insert(qcir3)
    return ProbsMeasure(wires, m_prog, config._machine, config._qubits)

config = pyqpanda_config_wrapper(n_wires)
qcir = []

qcir.append(get_p1_diagonal_01)
qcir.append(get_p1_diagonal_02)
qcir.append(get_p1_diagonal_03)

params2 = QTensor([0.5, 0.5, 0.5, 0.25], requires_grad=True)

mt = quantum_fisher(config, params2, [['RX', 'RY'], ['RZ'], ['RZ']], # rx,ry counts as layer one, first rz as layer two, second rz as layer three.
                        [[0, 1], [1], [1]], qcir, [0, 1])