前面学习笔记1提到了需要对QuadProg++的代码进行修改,才能够求解只含有不等式约束的严格二次凸规划问题。其修改的内容只有一小部分。为了保持源代码的完整性,我粘贴复制之后,增加了一个函数。对应的QuadProg++.hh也需要做对应的修改,多声明一个函数。
double solve_quadprog_CI(Matrix<double>& G, Vector<double>& g0,
const Matrix<double>& CI, const Vector<double>& ci0,
Vector<double>& x)
{
std::ostringstream msg;
unsigned int n = G.ncols(), m = CI.ncols();
unsigned int p = 0; // modified
if (G.nrows() != n)
{
msg << "The matrix G is not a squared matrix (" << G.nrows() << " x " << G.ncols() << ")";
throw std::logic_error(msg.str());
}
if (CI.nrows() != n)
{
msg << "The matrix CI is incompatible (incorrect number of rows " << CI.nrows() << " , expecting " << n << ")";
throw std::logic_error(msg.str());
}
if (ci0.size() != m)
{
msg << "The vector ci0 is incompatible (incorrect dimension " << ci0.size() << ", expecting " << m << ")";
throw std::logic_error(msg.str());
}
x.resize(n);
register unsigned int i, j, k, l; /* indices */
int ip; // this is the index of the constraint to be added to the active set
Matrix<double> R(n, n), J(n, n);
Vector<double> s(m + p), z(n), r(m + p), d(n), np(n), u(m + p), x_old(n), u_old(m + p);
double f_value, psi, c1, c2, sum, ss, R_norm;
double inf;
if (std::numeric_limits<double>::has_infinity)
inf = std::numeric_limits<double>::infinity();
else
inf = 1.0E300;
double t, t1, t2; /* t is the step lenght, which is the minimum of the partial step length t1
* and the full step length t2 */
Vector<int> A(m + p), A_old(m + p), iai(m + p);
unsigned int iq, iter = 0;
Vector<bool> iaexcl(m + p);
/* p is the number of equality constraints */
/* m is the number of inequality constraints */
#ifdef TRACE_SOLVER
std::cout << std::endl << "Starting solve_quadprog" << std::endl;
print_matrix("G", G);
print_vector("g0", g0);
print_matrix("CI", CI);
print_vector("ci0", ci0);
#endif
/*
* Preprocessing phase
*/
/* compute the trace of the original matrix G */
c1 = 0.0;
for (i = 0; i < n; i++)
{
c1 += G[i][i];
}
/* decompose the matrix G in the form L^T L */
cholesky_decomposition(G);
#ifdef TRACE_SOLVER
print_matrix("G", G);
#endif
/* initialize the matrix R */
for (i = 0; i < n; i++)
{
d[i] = 0.0;
for (j = 0; j < n; j++)
R[i][j] = 0.0;
}
R_norm = 1.0; /* this variable will hold the norm of the matrix R */
/* compute the inverse of the factorized matrix G^-1, this is the initial value for H */
c2 = 0.0;
for (i = 0; i < n; i++)
{
d[i] = 1.0;
forward_elimination(G, z, d);
for (j = 0; j < n; j++)
J[i][j] = z[j];
c2 += z[i];
d[i] = 0.0;
}
#ifdef TRACE_SOLVER
print_matrix("J", J);
#endif
/* c1 * c2 is an estimate for cond(G) */
/*
* Find the unconstrained minimizer of the quadratic form 0.5 * x G x + g0 x
* this is a feasible point in the dual space
* x = G^-1 * g0
*/
cholesky_solve(G, x, g0);
for (i = 0; i < n; i++)
x[i] = -x[i];
/* and compute the current solution value */
f_value = 0.5 * scalar_product(g0, x);
#ifdef TRACE_SOLVER
std::cout << "Unconstrained solution: " << f_value << std::endl;
print_vector("x", x);
#endif
/* Add equality constraints to the working set A */
iq = 0; t2 = 0.0;
// for (i = 0; i < p; i++)
// {
// for (j = 0; j < n; j++)
// np[j] = CE[j][i];
// compute_d(d, J, np);
// update_z(z, J, d, iq);
// update_r(R, r, d, iq);
// #ifdef TRACE_SOLVER
// print_matrix("R", R, n, iq);
// print_vector("z", z);
// print_vector("r", r, iq);
// print_vector("d", d);
// #endif
// /* compute full step length t2: i.e., the minimum step in primal space s.t. the contraint
// becomes feasible */
// t2 = 0.0;
// if (fabs(scalar_product(z, z)) > std::numeric_limits::epsilon()) // i.e. z != 0
// t2 = (-scalar_product(np, x) - ce0[i]) / scalar_product(z, np);
// /* set x = x + t2 * z */
// for (k = 0; k < n; k++)
// x[k] += t2 * z[k];
// /* set u = u+ */
// u[iq] = t2;
// for (k = 0; k < iq; k++)
// u[k] -= t2 * r[k];
// /* compute the new solution value */
// f_value += 0.5 * (t2 * t2) * scalar_product(z, np);
// A[i] = -i - 1;
// if (!add_constraint(R, J, d, iq, R_norm))
// {
// // Equality constraints are linearly dependent
// throw std::runtime_error("Constraints are linearly dependent");
// return f_value;
// }
// }
注意,这里是把double solve_quadprog_CI(Matrix& G, Vector& g0, const Matrix& CI, const Vector& ci0, Vector& x)函数替换调前面部分就可以了。实际上只是注释了等式约束部分的代码,然后令等式约束的p变量等于0,删去跟CE和ce0维度检查的代码。最后就可以用了,按照我博客前面的笔记进行安装。这里需要特别注意的是,如果需要使用Eigen库进行矩阵运算,则需要安装完毕后,将安装路径下的include文件夹里面的Array.hh文件中的#define inverse lu_inverse,进行修改,我个人是将其修改为#define _inverse lu_inverse。
测试代码如下,使用了Eigen:
#include "QuadProg++/QuadProg++.hh"
#include "QuadProg++/Array.hh"
#include
#include
// using namespace quadprogpp;
using namespace Eigen;
using namespace std;
typedef quadprogpp::Matrix<double> QPMatrixType;
typedef quadprogpp::Vector<double> QPVectorType;
MatrixXd Converse_QPMat2EigenMat(QPMatrixType QPMat)
{
int nrows = QPMat.nrows();
int nclos = QPMat.ncols();
MatrixXd EigenMat(nrows,nclos);
for (int r=0;r<nrows;r++){
for (int c=0;c<nclos;c++){
EigenMat(r,c)=QPMat[r][c];
}
}
return EigenMat;
}
VectorXd Converse_QPMat2EigenMat(QPVectorType QPVec)
{
int nrows = QPVec.size();
VectorXd EigenVec(nrows);
for (int r=0;r<nrows;r++){
EigenVec[r]=QPVec[r];
}
return EigenVec;
}
QPMatrixType Converse_EigenMat2QPMat(MatrixXd EigenMat)
{
int nclos = EigenMat.cols();
int nrows = EigenMat.rows();
QPMatrixType QPMat(nrows,nclos);
for (int r=0;r<nrows;r++){
for (int c=0;c<nclos;c++){
QPMat[r][c]=EigenMat(r,c);
}
}
return QPMat;
}
QPVectorType Converse_EigenVec2QPVec(VectorXd EigenVec)
{
int nrows = EigenVec.rows();
QPVectorType QPVec(nrows);
for (int r=0;r<nrows;r++){
QPVec[r]=EigenVec[r];
}
return QPVec;
}
int main()
{
MatrixXd eig_G(2,2);
eig_G<<2, 0,
0, 2;
VectorXd eig_g(2);
eig_g<<-2,-4;
MatrixXd eig_CI(2,3);
eig_CI<<-1,1,0,
-1,0,1;
VectorXd eig_ci(3);
eig_ci<<1,0,0;
VectorXd eig_x;
eig_x=VectorXd::Zero(2);
QPMatrixType G = Converse_EigenMat2QPMat(eig_G);
QPVectorType g = Converse_EigenVec2QPVec(eig_g);
QPMatrixType CI = Converse_EigenMat2QPMat(eig_CI);
QPVectorType ci = Converse_EigenVec2QPVec(eig_ci);
QPVectorType x = Converse_EigenVec2QPVec(eig_x);
clock_t startTime, endTime;
startTime = clock();
quadprogpp::solve_quadprog_CI(G, g, CI, ci, x);
endTime = clock();
double total_time = (double)(endTime - startTime);
total_time = total_time *1000.0/ CLOCKS_PER_SEC;
cout << total_time << endl;
cout << "---------final:\n" << endl;
cout << x[0] << " " << x[1] << endl;
std::cout << "Double checking cost: ";
double sum=0;
for (int i = 0; i < 2; i++)
for (int j = 0; j < 2; j++)
sum += x[i] * G[i][j] * x[j];
sum *= 0.5;
for (int i = 0; i < 2; i++)
sum += g[i] * x[i];
cout << sum << endl;
return 0;
}
如果没有记错的话,这个不等书约束的最优解是0, 1。 大家可以测试一下。
DianyeHuang
2018.12.17