QPanda 2教程资源 | VQNET中的优化算法(梯度下降法)和综合示例QAOA
嗨~周末好啊
上篇为大家推送了
可变量子逻辑门、可变量子线路
今天小编要介绍的是
优化算法(梯度下降法)和综合示例QAOA
跟我们一起来学习吧!
QPanda 2教程资源 | VQNET中的可变量子逻辑门和可变量子线路
在上一篇文章中,小编为大家介绍了VQNet中的可变量子逻辑门和可变量子线路。
可变量子逻辑门(VariationalQuantumGate,别名:VQG)。内部维护着一组变量参数以及一组常量参数。在构造VQG的时候只能对其中一组参数进行赋值。若含有一组常量参数,则可以通过VQG生成含确定参数的普通量子逻辑门, 若含有变量参数,则可以动态修改参数值,并生成对应的参数的普通量子逻辑门。
在VQNet中量子操作qop和qop_pmeasure都需要使用可变量子线路作为参数。可变量子线路(VariationalQuantumCircuit,别名VQC)是用来存储含有可变参数的量子线路信息,VQC主要由可变量子逻辑门(VQG)组成。
接下来,小编给大家介绍的是优化算法(梯度下降法)和综合示例QAOA。
优化算法(梯度下降法)本章节将讲解VQNet中优化算法的使用,包括经典梯度下降算法和改进后的梯度下降算法,它们都是在求解机器学习算法的模型参数,即无约束优化问题时,最常采用的方法之一。我们在QPanda::Variational中实现了这些算法,
VanillaGradientDescentOptimizer、MomentumOptimizer、AdaGradOptimizer、RMSPropOptimizer和AdamOptimizer,它们都继承自Optimizer。
接口介绍我们通过调用梯度下降优化器的minimize接口来生成一个优化器。常见的梯度下降优化器构造方式如下所示。
VanillaGradientDescentOptimizer::minimize(
loss, // 损失函数
0.01, // 学习率
1.e-6); // 结束条件
MomentumOptimizer::minimize(
loss, // 损失函数
0.01, // 学习率
0.9); // 动量系数
AdaGradOptimizer::minimize(
loss, // 损失函数
0.01, // 学习率
0.0, // 累加量起始值
1.e-10);// 很小的数值以避免零分母
RMSOptimizer::minimize(
loss, // 损失函数
0.01, // 学习率
0.9, // 历史或即将到来的梯度的贴现因子
1.e-10);// 很小的数值以避免零分母
AdamOptimizer::minimize(
loss, // 损失函数
0.01, // 学习率
0.9, // 一阶动量衰减系数
0.999, // 二阶动量衰减系数
1.e-10);// 很小的数值以避免零分母
实例示例代码主要演示对离散点用直线进行拟合,我们定义训练数据X和Y,这两个变量表示离散点的坐标。定义两个可微分的变量w和b,其中w表示斜率b表示y轴截距。定义变量Y下划线表示斜率w乘上变量x加上截距。
接着我们定义损失函数loss。计算变量Y和变量Y下划线之间的均方值。
我们调用梯度下降优化器的minimize接口以损失函数,学习率和结束条件作为参数构造生成一个经典梯度下降优化器。
我们通过优化器的get_variables接口可以获得所有可微分的节点。
我们定义迭代次数为1000。然后调用优化器的run接口执行一次优化操作,其第二个参数表示当前的优化次数,目前只有AdamOptimizer这个优化器使用到了这个参数,其它优化器我们直接给0值即可。
我们可以通过优化器get_loss接口获得当前优化后的损失值。我们通过eval接口可以求得可微分变量的当前值。
#include "Variational/Optimizer.h"
int main()
{
using namespace QPanda::Variational;
MatrixXd train_x(17, 1);
MatrixXd train_y(17, 1);
train_x(0, 0) = 3.3; train_x(1, 0) = 4.4; train_x(2, 0) = 5.5;
train_x(3, 0) = 6.71; train_x(4, 0) = 6.93; train_x(5, 0) = 4.168;
train_x(6, 0) = 9.779; train_x(7, 0) = 6.182; train_x(8, 0) = 7.59;
train_x(9, 0) = 2.167; train_x(10, 0) = 7.042; train_x(11, 0) = 10.791;
train_x(12, 0) = 5.313; train_x(13, 0) = 7.997; train_x(14, 0) = 5.654;
train_x(15, 0) = 9.27; train_x(16, 0) = 3.1; train_y(0, 0) = 1.7;
train_y(1, 0) = 2.76; train_y(2, 0) = 2.09; train_y(3, 0) = 3.19;
train_y(4, 0) = 1.694; train_y(5, 0) = 1.573; train_y(6, 0) = 3.366;
train_y(7, 0) = 2.596; train_y(8, 0) = 2.53; train_y(9, 0) = 1.221;
train_y(10, 0) = 2.827; train_y(11, 0) = 3.465; train_y(12, 0) = 1.65;
train_y(13, 0) = 2.904; train_y(14, 0) = 2.42; train_y(15, 0) = 2.94;
train_y(16, 0) = 1.3;
var X(train_x);
var Y(train_y);
var W(1.0, true);
var b(1.0, true);
var Y_ = W * X + b;
auto loss = sum(poly(Y - Y_, 2) / train_x.rows());
auto optimizer = VanillaGradientDescentOptimizer::minimize(loss, 0.01, 1.e-6);
auto leaves = optimizer->get_variables();
for (size_t i = 0u; i < 1000; i++)
{
optimizer->run(leaves);
std::cout << "i: " << i << "\t" << optimizer->get_loss()
<< "\t W:" << QPanda::Variational::eval(W, true)
<< "\t b:" << QPanda::Variational::eval(b, true)
<< std::endl;
}
return 0;
}
我们将散列点和拟合的直线进行绘图。
QAOA是众所周知的量子经典混合算法。对于n对象的MAX-CUT问题,需要n个量子位来对结果进行编码,其中测量结果(二进制串)表示问题的切割配置。
我们通过VQNet可以有效地实现MAX-CUT问题的QAOA算法。VQNet中QAOA的流程图如下所示。
我们给定一个MAX-CUT的问题如下。
首先,我们输入MAX-CUT问题的图形信息,并构造相应的问题哈密顿量。
PauliOperator getHamiltonian()
{
PauliOperator::PauliMap pauli_map{
{"Z0 Z4", 0.73},{"Z2 Z5", 0.88},
{"Z0 Z5", 0.33},{"Z2 Z6", 0.58},
{"Z0 Z6", 0.50},{"Z3 Z5", 0.67},
{"Z1 Z4", 0.69},{"Z3 Z6", 0.43},
{"Z1 Z5", 0.36}
};
return PauliOperator(pauli_map);
}
然后,使用哈密顿量和待优化的变量参数x,构建QAOA的vqc。QOP的输入参数是问题哈密顿量、VQC、一组量子比特和量子运行环境。QOP的输出是问题哈密顿量的期望。在这个问题中,损失函数是问题哈密顿量的期望,因此需要最小化QOP的输出。我们通过使用梯度下降优化器MomentumOptimizer来优化vqc中的变量x。
#include "Core/QPanda.h"
#include "Operator/PauliOperator.h"
#include "Variational/var.h"
#include "Variational/expression.h"
#include "Variational/utils.h"
#include "Variational/Optimizer.h"
#include <fstream>
using namespace std;
using namespace QPanda;
using namespace QPanda::Variational;
VQC parity_check_circuit(QVec &qubit_vec)
{
VQC circuit;
for (auto i = 0; i < qubit_vec.size() - 1; i++)
{
circuit.insert( VQG_CNOT(
qubit_vec[i],
qubit_vec[qubit_vec.size() - 1]));
}
return circuit;
}
VQC simulateZTerm(
QVec &qubit_vec,
var coef,
var t)
{
VQC circuit;
if (0 == qubit_vec.size())
{
return circuit;
}
else if (1 == qubit_vec.size())
{
circuit.insert(VQG_RZ(qubit_vec[0], coef * t*-1));
}
else
{
circuit.insert(parity_check_circuit(qubit_vec));
circuit.insert(VQG_RZ(qubit_vec[qubit_vec.size() - 1], coef * t*-1));
circuit.insert(parity_check_circuit(qubit_vec));
}
return circuit;
}
VQC simulatePauliZHamiltonian(
QVec& qubit_vec,
const QPanda::QHamiltonian & hamiltonian,
var t)
{
VQC circuit;
for (auto j = 0; j < hamiltonian.size(); j++)
{
QVec tmp_vec;
auto item = hamiltonian[j];
auto map = item.first;
for (auto iter = map.begin(); iter != map.end(); iter++)
{
if ('Z' != iter->second)
{
QCERR("Bad pauliZ Hamiltonian");
throw std::string("Bad pauliZ Hamiltonian.");
}
tmp_vec.push_back(qubit_vec[iter->first]);
}
if (!tmp_vec.empty())
{
circuit.insert(simulateZTerm(tmp_vec, item.second, t));
}
}
return circuit;
}
int main()
{
PauliOperator op = getHamiltonian();
QuantumMachine *machine = initQuantumMachine();
QVec qlist;
for (int i = 0; i < op.getMaxIndex(); ++i)
qlist.push_back(machine->allocateQubit());
VQC vqc;
for_each(qlist.begin(), qlist.end(), [&vqc](Qubit* qbit)
{
vqc.insert(VQG_H(qbit));
});
int qaoa_step = 4;
var x(MatrixXd::Random(2 * qaoa_step, 1), true);
for (auto i = 0u; i < 2*qaoa_step; i+=2)
{
vqc.insert(simulatePauliZHamiltonian(qlist, op.toHamiltonian(), x[i + 1]));
for (auto _q : qlist) {
vqc.insert(VQG_RX(_q, x[i]));
}
}
var loss = qop(vqc, op, machine, qlist);
auto optimizer = MomentumOptimizer::minimize(loss, 0.02, 0.9);
auto leaves = optimizer->get_variables();
constexpr size_t iterations = 100;
for (auto i = 0u; i < iterations; i++)
{
optimizer->run(leaves);
std::cout << " iter: " << i << " loss : " << optimizer->get_loss() << std::endl;
}
QProg prog;
QCircuit circuit = vqc.feed();
prog << circuit;
directlyRun(prog);
auto result = quickMeasure(qlist, 100);
for (auto i:result)
{
std::cout << i.first << " : " << i.second << " ";
}
return 0;
}
我们将测量的结果绘制出柱状图,可以看到'0001111'和'1110000'这两个比特串测量得到的概率最大,也正是我们这个问题的解。
★以上即为QPanda 2优化算法(梯度下降法)和综合示例QAOA部分的详细内容介绍。
★感兴趣的欢迎加入"QPanda 2开发交流群"。(关注“本源量子”公众号,回复“加群”,联系小编即可)
★PC端学习量子计算请登录learn-quantum.com
★掌上学习请下载"本源溯知APP"
★如有疑问可上量子互动论坛,与行业大神进行交流讨论。
获取更多QPanda 2教程资源,请点击“阅读原文”。
往期精彩回顾
量子程序序列化以及解析量子程序的二进制文件
逻辑门以及量子程序时钟周期统计
量子门的有效性及判断方法
量子比特的测量方法:量子测量和概率测量
QPanda 2最常用的量子计算模型——量子线路
嗨玩本源QPanda 2,从量子逻辑门实例开始学起!