__qp__.cc

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 2000-2012 Gabriele Pannocchia
00004 
00005 This file is part of Octave.
00006 
00007 Octave is free software; you can redistribute it and/or modify it
00008 under the terms of the GNU General Public License as published by the
00009 Free Software Foundation; either version 3 of the License, or (at your
00010 option) any later version.
00011 
00012 Octave is distributed in the hope that it will be useful, but WITHOUT
00013 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00015 for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Octave; see the file COPYING.  If not, see
00019 <http://www.gnu.org/licenses/>.
00020 
00021 */
00022 
00023 #ifdef HAVE_CONFIG_H
00024 #include <config.h>
00025 #endif
00026 
00027 #include <cfloat>
00028 
00029 #include "dbleCHOL.h"
00030 #include "dbleSVD.h"
00031 #include "mx-m-dm.h"
00032 #include "EIG.h"
00033 
00034 #include "defun-dld.h"
00035 #include "error.h"
00036 #include "gripes.h"
00037 #include "oct-obj.h"
00038 #include "pr-output.h"
00039 #include "utils.h"
00040 
00041 static Matrix
00042 null (const Matrix& A, octave_idx_type& rank)
00043 {
00044   Matrix retval;
00045 
00046   rank = 0;
00047 
00048   if (! A.is_empty ())
00049     {
00050       SVD A_svd (A);
00051 
00052       DiagMatrix S = A_svd.singular_values ();
00053 
00054       ColumnVector s = S.diag ();
00055 
00056       Matrix V = A_svd.right_singular_matrix ();
00057 
00058       octave_idx_type A_nr = A.rows ();
00059       octave_idx_type A_nc = A.cols ();
00060 
00061       octave_idx_type tmp = A_nr > A_nc ? A_nr : A_nc;
00062 
00063       double tol = tmp * s(0) * DBL_EPSILON;
00064 
00065       octave_idx_type n = s.length ();
00066 
00067       for (octave_idx_type i = 0; i < n; i++)
00068         {
00069           if (s(i) > tol)
00070             rank++;
00071         }
00072 
00073       if (rank < A_nc)
00074         retval = V.extract (0, rank, A_nc-1, A_nc-1);
00075       else
00076         retval.resize (A_nc, 0);
00077 
00078       for (octave_idx_type i = 0; i < retval.numel (); i++)
00079         if (std::abs (retval(i)) < DBL_EPSILON)
00080           retval(i) = 0;
00081     }
00082 
00083   return retval;
00084 }
00085 
00086 static int
00087 qp (const Matrix& H, const ColumnVector& q,
00088     const Matrix& Aeq, const ColumnVector& beq,
00089     const Matrix& Ain, const ColumnVector& bin,
00090     int maxit,
00091     ColumnVector& x, ColumnVector& lambda, int& iter)
00092 {
00093   int info = 0;
00094 
00095   iter = 0;
00096 
00097   double rtol = sqrt (DBL_EPSILON);
00098 
00099   // Problem dimension.
00100   octave_idx_type n = x.length ();
00101 
00102   // Dimension of constraints.
00103   octave_idx_type n_eq = beq.length ();
00104   octave_idx_type n_in = bin.length ();
00105 
00106   // Filling the current active set.
00107 
00108   octave_idx_type n_act = n_eq;
00109 
00110   octave_idx_type n_tot = n_eq + n_in;
00111 
00112   // Equality constraints come first.  We won't check the sign of the
00113   // Lagrange multiplier for those.
00114 
00115   Matrix Aact = Aeq;
00116   ColumnVector bact = beq;
00117   ColumnVector Wact;
00118 
00119   if (n_in > 0)
00120     {
00121       ColumnVector res = Ain*x - bin;
00122 
00123       for (octave_idx_type i = 0; i < n_in; i++)
00124         {
00125           res(i) /= (1.0 + std::abs (bin(i)));
00126 
00127           if (res(i) < rtol)
00128             {
00129               n_act++;
00130               Aact = Aact.stack (Ain.row (i));
00131               bact.resize (n_act, bin(i));
00132               Wact.resize (n_act-n_eq, i);
00133             }
00134         }
00135     }
00136 
00137   // Computing the ???
00138 
00139   EIG eigH (H);
00140 
00141   if (error_state)
00142     {
00143       error ("qp: failed to compute eigenvalues of H");
00144       return -1;
00145     }
00146 
00147   ColumnVector eigenvalH = real (eigH.eigenvalues ());
00148   Matrix eigenvecH = real (eigH.eigenvectors ());
00149   double minReal = eigenvalH.min ();
00150   octave_idx_type indminR = 0;
00151   for (octave_idx_type i = 0; i < n; i++)
00152     {
00153       if (minReal == eigenvalH(i))
00154         {
00155           indminR = i;
00156           break;
00157         }
00158     }
00159 
00160   bool done = false;
00161 
00162   double alpha = 0.0;
00163 
00164   Matrix R;
00165   Matrix Y (n, 0, 0.0);
00166 
00167   ColumnVector g (n, 0.0);
00168   ColumnVector p (n, 0.0);
00169 
00170   ColumnVector lambda_tmp (n_in, 0.0);
00171 
00172   while (! done)
00173     {
00174       iter++;
00175 
00176       // Current Gradient
00177       // g = q + H * x;
00178 
00179       g = q + H * x;
00180 
00181       if (n_act == 0)
00182         {
00183           // There are no active constraints.
00184 
00185           if (minReal > 0.0)
00186             {
00187               // Inverting the Hessian.  Using the Cholesky
00188               // factorization since the Hessian is positive
00189               // definite.
00190 
00191               CHOL cholH (H);
00192 
00193               R = cholH.chol_matrix ();
00194 
00195               Matrix Hinv = chol2inv (R);
00196 
00197               // Computing the unconstrained step.
00198               // p = -Hinv * g;
00199 
00200               p = -Hinv * g;
00201 
00202               info = 0;
00203             }
00204           else
00205             {
00206               // Finding the negative curvature of H.
00207 
00208               p = eigenvecH.column (indminR);
00209 
00210               // Following the negative curvature of H.
00211 
00212               if (p.transpose () * g > DBL_EPSILON)
00213                 p = -p;
00214 
00215               info = 1;
00216             }
00217 
00218           // Multipliers are zero.
00219           lambda_tmp.fill (0.0);
00220         }
00221       else
00222         {
00223           // There are active constraints.
00224 
00225           // Computing the null space.
00226 
00227           octave_idx_type rank;
00228 
00229           Matrix Z = null (Aact, rank);
00230 
00231           octave_idx_type dimZ = n - rank;
00232 
00233           // FIXME -- still remain to handle the case of
00234           // non-full rank active set matrix.
00235 
00236           // Computing the Y matrix (orthogonal to Z)
00237           Y = Aact.pseudo_inverse ();
00238 
00239           // Reduced Hessian
00240           Matrix Zt = Z.transpose ();
00241           Matrix rH = Zt * H * Z;
00242 
00243           octave_idx_type pR = 0;
00244 
00245           if (dimZ > 0)
00246             {
00247               // Computing the Cholesky factorization (pR = 0 means
00248               // that the reduced Hessian was positive definite).
00249 
00250               CHOL cholrH (rH, pR);
00251               Matrix tR = cholrH.chol_matrix ();
00252               if (pR == 0)
00253                 R = tR;
00254             }
00255 
00256           if (pR == 0)
00257             {
00258               info = 0;
00259 
00260               // Computing the step pz.
00261               if (dimZ > 0)
00262                 {
00263                   // Using the Cholesky factorization to invert rH
00264 
00265                   Matrix rHinv = chol2inv (R);
00266 
00267                   ColumnVector pz = -rHinv * Zt * g;
00268 
00269                   // Global step.
00270                   p = Z * pz;
00271                 }
00272               else
00273                 {
00274                   // Global step.
00275                   p.fill (0.0);
00276                 }
00277             }
00278           else
00279             {
00280               info = 1;
00281 
00282               // Searching for the most negative curvature.
00283 
00284               EIG eigrH (rH);
00285 
00286               if (error_state)
00287                 {
00288                   error ("qp: failed to compute eigenvalues of rH");
00289                   return -1;
00290                 }
00291 
00292               ColumnVector eigenvalrH = real (eigrH.eigenvalues ());
00293               Matrix eigenvecrH = real (eigrH.eigenvectors ());
00294               double mRrH = eigenvalrH.min ();
00295               indminR = 0;
00296               for (octave_idx_type i = 0; i < n; i++)
00297                 {
00298                   if (mRrH == eigenvalH(i))
00299                     {
00300                       indminR = i;
00301                       break;
00302                     }
00303                 }
00304 
00305               ColumnVector eVrH = eigenvecrH.column (indminR);
00306 
00307               // Computing the step pz.
00308               p = Z * eVrH;
00309 
00310               if (p.transpose () * g > DBL_EPSILON)
00311                 p = -p;
00312             }
00313         }
00314 
00315       // Checking the step-size.
00316       ColumnVector abs_p (n);
00317       for (octave_idx_type i = 0; i < n; i++)
00318         abs_p(i) = std::abs (p(i));
00319       double max_p = abs_p.max ();
00320 
00321       if (max_p < rtol)
00322         {
00323           // The step is null.  Checking constraints.
00324           if (n_act - n_eq == 0)
00325             // Solution is found because no inequality
00326             // constraints are active.
00327             done = true;
00328           else
00329             {
00330               // Computing the multipliers only for the inequality
00331               // constraints that are active.  We do NOT compute
00332               // multipliers for the equality constraints.
00333               Matrix Yt = Y.transpose ();
00334               Yt = Yt.extract_n (n_eq, 0, n_act-n_eq, n);
00335               lambda_tmp = Yt * (g + H * p);
00336 
00337               // Checking the multipliers.  We remove the most
00338               // negative from the set (if any).
00339               double min_lambda = lambda_tmp.min ();
00340               if (min_lambda >= 0)
00341                 {
00342                   // Solution is found.
00343                   done = true;
00344                 }
00345               else
00346                 {
00347                   octave_idx_type which_eig = 0;
00348                   for (octave_idx_type i = 0; i < n_act; i++)
00349                     {
00350                       if (lambda_tmp(i) == min_lambda)
00351                         {
00352                           which_eig = i;
00353                           break;
00354                         }
00355                     }
00356 
00357                   // At least one multiplier is negative, we
00358                   // remove it from the set.
00359 
00360                   n_act--;
00361                   for (octave_idx_type i = which_eig; i < n_act - n_eq; i++)
00362                     {
00363                       Wact(i) = Wact(i+1);
00364                       for (octave_idx_type j = 0; j < n; j++)
00365                         Aact(n_eq+i,j) = Aact(n_eq+i+1,j);
00366                       bact(n_eq+i) = bact(n_eq+i+1);
00367                     }
00368 
00369                   // Resizing the active set.
00370                   Wact.resize (n_act-n_eq);
00371                   bact.resize (n_act);
00372                   Aact.resize (n_act, n);
00373                 }
00374             }
00375         }
00376       else
00377         {
00378           // The step is not null.
00379           if (n_act - n_eq == n_in)
00380             {
00381               // All inequality constraints were active.  We can
00382               // add the whole step.
00383               x += p;
00384             }
00385           else
00386             {
00387               // Some constraints were not active.  Checking if
00388               // there is a blocking constraint.
00389               alpha = 1.0;
00390               octave_idx_type is_block = -1;
00391 
00392               for (octave_idx_type i = 0; i < n_in; i++)
00393                 {
00394                   bool found = false;
00395 
00396                   for (octave_idx_type j = 0; j < n_act-n_eq; j++)
00397                     {
00398                       if (Wact(j) == i)
00399                         {
00400                           found = true;
00401                           break;
00402                         }
00403                     }
00404 
00405                   if (! found)
00406                     {
00407                       // The i-th constraint was not in the set.  Is it a
00408                       // blocking constraint?
00409 
00410                       RowVector tmp_row = Ain.row (i);
00411                       double tmp = tmp_row * p;
00412                       double res = tmp_row * x;
00413 
00414                       if (tmp < 0.0)
00415                         {
00416                           double alpha_tmp = (bin(i) - res) / tmp;
00417 
00418                           if (alpha_tmp < alpha)
00419                             {
00420                               alpha = alpha_tmp;
00421                               is_block = i;
00422                             }
00423                         }
00424                     }
00425                 }
00426 
00427               // In is_block there is the index of the blocking
00428               // constraint (if any).
00429               if (is_block >= 0)
00430                 {
00431                   // There is a blocking constraint (index in
00432                   // is_block) which is added to the active set.
00433                   n_act++;
00434                   Aact = Aact.stack (Ain.row (is_block));
00435                   bact.resize (n_act, bin(is_block));
00436                   Wact.resize (n_act-n_eq, is_block);
00437 
00438                   // Adding the reduced step
00439                   x += alpha * p;
00440                 }
00441               else
00442                 {
00443                   // There are no blocking constraints.  Adding the
00444                   // whole step.
00445                   x += alpha * p;
00446                 }
00447             }
00448         }
00449 
00450       if (iter == maxit)
00451         {
00452           done = true;
00453           // warning ("qp_main: maximum number of iteration reached");
00454           info = 3;
00455         }
00456     }
00457 
00458   lambda_tmp = Y.transpose () * (g + H * p);
00459 
00460   // Reordering the Lagrange multipliers.
00461 
00462   lambda.resize (n_tot);
00463   lambda.fill (0.0);
00464   for (octave_idx_type i = 0; i < n_eq; i++)
00465     lambda(i) = lambda_tmp(i);
00466 
00467   for (octave_idx_type i = n_eq; i < n_tot; i++)
00468     {
00469       for (octave_idx_type j = 0; j < n_act-n_eq; j++)
00470         {
00471           if (Wact(j) == i - n_eq)
00472             {
00473               lambda(i) = lambda_tmp(n_eq+j);
00474               break;
00475             }
00476         }
00477     }
00478 
00479   return info;
00480 }
00481 
00482 DEFUN_DLD (__qp__, args, ,
00483   "-*- texinfo -*-\n\
00484 @deftypefn {Loadable Function} {[@var{x}, @var{lambda}, @var{info}, @var{iter}] =} __qp__ (@var{x0}, @var{H}, @var{q}, @var{Aeq}, @var{beq}, @var{Ain}, @var{bin}, @var{maxit})\n\
00485 Undocumented internal function.\n\
00486 @end deftypefn")
00487 {
00488   octave_value_list retval;
00489 
00490   if (args.length () == 8)
00491     {
00492       const ColumnVector x0  (args(0) . vector_value ());
00493       const Matrix H         (args(1) . matrix_value ());
00494       const ColumnVector q   (args(2) . vector_value ());
00495       const Matrix Aeq       (args(3) . matrix_value ());
00496       const ColumnVector beq (args(4) . vector_value ());
00497       const Matrix Ain       (args(5) . matrix_value ());
00498       const ColumnVector bin (args(6) . vector_value ());
00499       const int maxit        (args(7) . int_value ());
00500 
00501       if (! error_state)
00502         {
00503           int iter = 0;
00504 
00505           // Copying the initial guess in the working variable
00506           ColumnVector x = x0;
00507 
00508           // Reordering the Lagrange multipliers
00509           ColumnVector lambda;
00510 
00511           int info = qp (H, q, Aeq, beq, Ain, bin, maxit, x, lambda, iter);
00512 
00513           if (! error_state)
00514             {
00515               retval(3) = iter;
00516               retval(2) = info;
00517               retval(1) = lambda;
00518               retval(0) = x;
00519             }
00520           else
00521             error ("qp: internal error");
00522         }
00523       else
00524         error ("__qp__: invalid arguments");
00525     }
00526   else
00527     print_usage ();
00528 
00529   return retval;
00530 }
00531 
00532 /*
00533 
00534 ## No test needed for internal helper function.
00535 %!assert (1)
00536 
00537 */
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Defines