二次規劃——學習筆記2

前面學習筆記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<double>::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 <Eigen/Dense>
#include <stdio.h> 
// 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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章