Quantum Machine Learning API using QPanda2 #################################################### .. warning:: 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 ================================= 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: .. math:: 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: .. math:: 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``. .. math:: \nabla O(\theta)= \frac{1}{2}\left[O\left(\theta+\frac{\pi}{2}\right)-O\left(\theta-\frac{\pi}{2}\right)\right] .. py: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. :param qprog_with_measure: callable quantum circuits functions ,cosntructed by pyQPanda2 :param para_num: `int` - Number of parameter :param machine_type_or_cloud_token: qpanda machine type or pyQPanda2 QCLOUD token : https://pyqpanda-toturial.readthedocs.io/zh/latest/Realchip.html :param num_of_qubits: num of qubits :param num_of_cbits: num of classic bits :param diff_method: 'parameter_shift' or 'finite_diff' :param delta: delta for diff :param dtype: The data type of the parameter, defaults: None, use the default data type kfloat32, which represents a 32-bit floating point number. :param name: name of the output layer :return: a module can calculate quantum circuits . .. note:: 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. .. note:: 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. .. py: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. :param qprog_with_measure: callable quantum circuits functions ,cosntructed by pyQPanda2 :param para_num: `int` - Number of parameter :param diff_method: 'parameter_shift' or 'finite_diff' :param delta: delta for diff :param dtype: The data type of the parameter, defaults: None, use the default data type kfloat32, which represents a 32-bit floating point number. :param name: name of the output layer :return: a module can calculate quantum circuits . .. note:: 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 .. note:: 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. .. py: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 . .. note:: 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. . :param origin_qprog_func: The variational quantum circuit function built by pyQPanda2 must return type of QProg. :param qcloud_token: `str` - The type of quantum machine or cloud token used for execution. :param para_num: `int` - Number of parameters, the parameter is a QTensor of size [para_num]. :param num_qubits: `int` - Number of qubits in the quantum circuit. :param num_cubits: `int` - The number of classical bits used for measurement in quantum circuits. :param pauli_str_dict: `dict|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. :param shot: `int` - Number of measurements. The default value is 1000. :param initializer: Initializer for parameter values. The default is "None", using 0~2*pi normal distribution. :param dtype: The data type of the parameter. The default value is None, which uses the default data type pyvqnet.kfloat32. :param name: The name of the module. Defaults to empty string. :param 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 . :param 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. :param query_kwargs: Additional keyword parameters for querying quantum results, default: {"timeout":2,"print_query_info":True,"sub_circuits_split_size":1}. :return: 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. .. py: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. :param qprog_with_measure: callable quantum circuits functions ,cosntructed by pyQPanda2. :param para_num: `int` - Number of parameter :param num_of_qubits: num of qubits. :param num_of_cbits: num of classic bits. :param diff_method: 'parameter_shift' or 'finite_diff'. :param delta: delta for diff. :param dtype: The data type of the parameter, defaults: None, use the default data type kfloat32, which represents a 32-bit floating point number. :param name: name of the output layer :return: a module can calculate quantum circuits . .. note:: 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. .. py: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``. :param qprog_with_measure: callable quantum circuits functions ,cosntructed by pyQPanda2 :param para_num: `int` - Number of para_num :param machine_type: pyQPanda2 machine type :param num_of_qubits: num of qubits :param num_of_cbits: num of cbits :param diff_method: 'parameter_shift' or 'finite_diff' :param delta: delta for diff :param noise_set_config: noise set function :param dtype: The data type of the parameter, defaults: None, use the default data type kfloat32, which represents a 32-bit floating point number. :param name: name of the output layer :return: a module can calculate quantum circuits with noise model. .. note:: 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. .. code-block:: 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 ============================================================ .. py:class:: pyvqnet.qnn.DataParallelHybirdVQCQpandaQVMLayer(vqc_module: Module,qcloud_token: str,num_qubits: int,num_cubits: int,pauli_str_dict: Union[List[Dict], Dict, None] = None,shots: int = 1000 ,dtype: Union[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. .. note:: 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] :param vqc_module: with forward() of vqc_module. :param qcloud_token: `str` - Type of quantum machine or cloud token to use for execution. :param num_qubits: `int` - Number of qubits in the quantum circuit. :param num_cubits: `int` - The number of classical bits used for measurement in the quantum circuit. :param pauli_str_dict: `dict|list` - A dictionary or list of dictionaries representing the Pauli operators in the quantum circuit. Default is None. :param shots: `int` - The number of shots in the quantum circuit. Number of line measurements. The default value is 1000. :param name: Module name. The default value is an empty string. :param 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}. :param 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 :ref:`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 ================================= .. py:function:: pyvqnet.qnn.template.AmplitudeEmbeddingCircuit(input_feat, qubits) Encodes :math:`2^n` features into the amplitude vector of :math:`n` qubits. To represent a valid quantum state vector, the L2-norm of ``features`` must be one. :param input_feat: numpy array which represents paramters :param qubits: qubits allocated by pyQPanda :return: 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. .. image:: ./images/qgan-arch.PNG :width: 600 px :align: center | 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. .. code-block:: 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. .. code-block:: # 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. .. code-block:: # 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. .. code-block:: # 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. .. image:: ./images/qgan-loss.png :width: 600 px :align: center | .. image:: ./images/qgan-pdf.png :width: 600 px :align: center | 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. .. image:: ./images/VQC-SVM.png :width: 600 px :align: center | .. code-block:: """ 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 :math:`K(i,j)` , use quantum kernel function to define the inner product of classical data in quantum feature space :math:`\phi(\mathbf{x}_i)` : .. math:: |\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: .. image:: ./images/qsvm-kernel.png :width: 600 px :align: center | .. code-block:: 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 ================================================================= .. py:function:: 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 `__. :param maxiter: The maximum number of iterations to perform. Default value: 1000. :param save_steps: Save the intermediate information of each save_steps step. Default value: 1. :param last_avg: Averaging parameter for last_avg iterations. If last_avg = 1, only the last iteration is considered. Default value: 1. :param c0: initial a. Step size for updating parameters. Default value: 0.2*pi :param c1: initial c. The step size used to approximate the gradient. Default: 0.1. :param c2: alpha from the paper, used to adjust a(c0) at each iteration. Default value: 0.602. :param c3: gamma in the paper, used to adjust c(c1) at each iteration. Default value: 0.101. :param c4: Also used to control the parameters of a. Default value: 0. :param init_para: Initialization parameters. Default: None. :param model: Parametric model: model. Default: None. :param calibrate_flag: whether to calibrate hpyer parameters a and c, default value: False. :return: an SPSA optimizer instance .. warning:: 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, ) .. py:function:: pyvqnet.qnn.SPSA._step(input_data) use SPSA to optimize input data. :param input_data: input data :return: 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 ======================================================== .. py: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. .. math:: \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::math:`\left|\partial_j \psi(\boldsymbol{\theta})\right\rangle:=\frac{\partial}{\partial \theta_j}|\psi(\boldsymbol{\theta})\rangle`. .. note:: Currently only RX,RY,RZ are supported. :param params: Variable parameters in circuits. :param target_gate_type_lists: Supports "RX", "RY", "RZ" or lists. :param target_gate_bits_lists: Which quantum bit or bits the parameterised gate acts on . :param qcir_lists: The list of quantum circles before the target parameterised gate to compute the metric tensor, see the following example. :param 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])