GNU Octave  6.2.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
__qp__.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2000-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include <cmath>
31 
32 #include <limits>
33 
34 #include "chol.h"
35 #include "svd.h"
36 #include "mx-m-dm.h"
37 #include "EIG.h"
38 
39 #include "defun.h"
40 #include "error.h"
41 #include "errwarn.h"
42 #include "ovl.h"
43 #include "pr-output.h"
44 #include "utils.h"
45 
46 static octave_idx_type
48 {
49  double min_val = x.min ();
50 
51  for (octave_idx_type i = 0; i < x.numel (); i++)
52  if (min_val == x.xelem (i))
53  return i;
54 
55  return 0;
56 }
57 
58 static Matrix
59 null (const Matrix& A, octave_idx_type& rank)
60 {
61  Matrix retval;
62 
63  rank = 0;
64 
65  if (! A.isempty ())
66  {
68 
69  DiagMatrix S = A_svd.singular_values ();
70 
71  ColumnVector s = S.extract_diag ();
72 
73  Matrix V = A_svd.right_singular_matrix ();
74 
75  octave_idx_type A_nr = A.rows ();
76  octave_idx_type A_nc = A.cols ();
77 
78  octave_idx_type tmp = (A_nr > A_nc ? A_nr : A_nc);
79 
80  double tol = tmp * s(0) * std::numeric_limits<double>::epsilon ();
81 
82  octave_idx_type n = s.numel ();
83 
84  for (octave_idx_type i = 0; i < n; i++)
85  {
86  if (s(i) > tol)
87  rank++;
88  }
89 
90  if (rank < A_nc)
91  retval = V.extract (0, rank, A_nc-1, A_nc-1);
92  else
93  retval.resize (A_nc, 0);
94 
95  for (octave_idx_type i = 0; i < retval.numel (); i++)
96  if (std::abs (retval(i)) < std::numeric_limits<double>::epsilon ())
97  retval(i) = 0;
98  }
99 
100  return retval;
101 }
102 
103 static int
104 qp (const Matrix& H, const ColumnVector& q,
105  const Matrix& Aeq, const ColumnVector& beq,
106  const Matrix& Ain, const ColumnVector& bin,
107  int maxit, double rtol,
108  ColumnVector& x, ColumnVector& lambda, int& iter)
109 {
110  int info = 0;
111 
112  iter = 0;
113 
114  // Problem dimension.
115  octave_idx_type n = x.numel ();
116 
117  // Dimension of constraints.
118  octave_idx_type n_eq = beq.numel ();
119  octave_idx_type n_in = bin.numel ();
120 
121  // Filling the current active set.
122 
123  octave_idx_type n_act = n_eq;
124 
125  octave_idx_type n_tot = n_eq + n_in;
126 
127  // Equality constraints come first. We won't check the sign of the
128  // Lagrange multiplier for those.
129 
130  Matrix Aact = Aeq;
131  ColumnVector bact = beq;
132  ColumnVector Wact;
133 
134  if (n_in > 0)
135  {
136  ColumnVector res = Ain*x - bin;
137 
138  for (octave_idx_type i = 0; i < n_in; i++)
139  {
140  res(i) /= (1.0 + std::abs (bin(i)));
141 
142  if (res(i) < rtol)
143  {
144  n_act++;
145  Aact = Aact.stack (Ain.row (i));
146  bact.resize (n_act, bin(i));
147  Wact.resize (n_act-n_eq, i);
148  }
149  }
150  }
151 
152  // Computing the ???
153 
154  EIG eigH;
155 
156  try
157  {
158  eigH = EIG (H);
159  }
160  catch (octave::execution_exception& e)
161  {
162  error (e, "qp: failed to compute eigenvalues of H");
163  }
164 
165  ColumnVector eigenvalH = real (eigH.eigenvalues ());
166  Matrix eigenvecH = real (eigH.right_eigenvectors ());
167  octave_idx_type indminR = min_index (eigenvalH);
168 
169  bool done = false;
170 
171  double alpha = 0.0;
172 
173  Matrix R;
174  Matrix Y (n, 0, 0.0);
175 
176  ColumnVector g (n, 0.0);
177  ColumnVector p (n, 0.0);
178 
179  ColumnVector lambda_tmp (n_in, 0.0);
180 
181  while (! done)
182  {
183  iter++;
184 
185  // Current Gradient
186  // g = q + H * x;
187 
188  g = q + H * x;
189 
190  if (n_act == 0)
191  {
192  // There are no active constraints.
193 
194  double minReal = eigenvalH.xelem (indminR);
195 
196  if (minReal > 0.0)
197  {
198  // Inverting the Hessian. Using the Cholesky
199  // factorization since the Hessian is positive
200  // definite.
201 
202  octave::math::chol<Matrix> cholH (H);
203 
204  R = cholH.chol_matrix ();
205 
206  Matrix Hinv = octave::math::chol2inv (R);
207 
208  // Computing the unconstrained step.
209  // p = -Hinv * g;
210 
211  p = -Hinv * g;
212 
213  info = 0;
214  }
215  else
216  {
217  // Finding the negative curvature of H.
218 
219  p = eigenvecH.column (indminR);
220 
221  // Following the negative curvature of H.
222 
223  if (p.transpose () * g > std::numeric_limits<double>::epsilon ())
224  p = -p;
225 
226  info = 1;
227  }
228 
229  // Multipliers are zero.
230  lambda_tmp.fill (0.0);
231  }
232  else
233  {
234  // There are active constraints.
235 
236  // Computing the null space.
237 
238  octave_idx_type rank;
239 
240  Matrix Z = null (Aact, rank);
241 
242  octave_idx_type dimZ = n - rank;
243 
244  // FIXME: still remain to handle the case of
245  // non-full rank active set matrix.
246 
247  // Computing the Y matrix (orthogonal to Z)
248  Y = Aact.pseudo_inverse ();
249 
250  // Reduced Hessian
251  Matrix Zt = Z.transpose ();
252  Matrix rH = Zt * H * Z;
253 
254  octave_idx_type pR = 0;
255 
256  if (dimZ > 0)
257  {
258  // Computing the Cholesky factorization (pR = 0 means
259  // that the reduced Hessian was positive definite).
260 
261  octave::math::chol<Matrix> cholrH (rH, pR);
262  Matrix tR = cholrH.chol_matrix ();
263  if (pR == 0)
264  R = tR;
265  }
266 
267  if (pR == 0)
268  {
269  info = 0;
270 
271  // Computing the step pz.
272  if (dimZ > 0)
273  {
274  // Using the Cholesky factorization to invert rH
275 
276  Matrix rHinv = octave::math::chol2inv (R);
277 
278  ColumnVector pz = -rHinv * Zt * g;
279 
280  // Global step.
281  p = Z * pz;
282  }
283  else
284  {
285  // Global step.
286  p.fill (0.0);
287  }
288  }
289  else
290  {
291  info = 1;
292 
293  // Searching for the most negative curvature.
294 
295  EIG eigrH;
296 
297  try
298  {
299  eigrH = EIG (rH);
300  }
301  catch (octave::execution_exception& e)
302  {
303  error (e, "qp: failed to compute eigenvalues of rH");
304  }
305 
306  ColumnVector eigenvalrH = real (eigrH.eigenvalues ());
307  Matrix eigenvecrH = real (eigrH.right_eigenvectors ());
308  indminR = min_index (eigenvalrH);
309 
310  ColumnVector eVrH = eigenvecrH.column (indminR);
311 
312  // Computing the step pz.
313  p = Z * eVrH;
314 
315  if (p.transpose () * g > std::numeric_limits<double>::epsilon ())
316  p = -p;
317  }
318  }
319 
320  // Checking the step-size.
321  ColumnVector abs_p (n);
322  for (octave_idx_type i = 0; i < n; i++)
323  abs_p(i) = std::abs (p(i));
324  double max_p = abs_p.max ();
325 
326  if (max_p < rtol)
327  {
328  // The step is null. Checking constraints.
329  if (n_act - n_eq == 0)
330  // Solution is found because no inequality
331  // constraints are active.
332  done = true;
333  else
334  {
335  // Computing the multipliers only for the inequality
336  // constraints that are active. We do NOT compute
337  // multipliers for the equality constraints.
338  Matrix Yt = Y.transpose ();
339  Yt = Yt.extract_n (n_eq, 0, n_act-n_eq, n);
340  lambda_tmp = Yt * (g + H * p);
341 
342  // Checking the multipliers. We remove the most
343  // negative from the set (if any).
344  double min_lambda = lambda_tmp.min ();
345  if (min_lambda >= 0)
346  {
347  // Solution is found.
348  done = true;
349  }
350  else
351  {
352  octave_idx_type which_eig = 0;
353  for (octave_idx_type i = 0; i < n_act; i++)
354  {
355  if (lambda_tmp(i) == min_lambda)
356  {
357  which_eig = i;
358  break;
359  }
360  }
361 
362  // At least one multiplier is negative, we
363  // remove it from the set.
364 
365  n_act--;
366  for (octave_idx_type i = which_eig; i < n_act - n_eq; i++)
367  {
368  Wact(i) = Wact(i+1);
369  for (octave_idx_type j = 0; j < n; j++)
370  Aact(n_eq+i,j) = Aact(n_eq+i+1,j);
371  bact(n_eq+i) = bact(n_eq+i+1);
372  }
373 
374  // Resizing the active set.
375  Wact.resize (n_act-n_eq);
376  bact.resize (n_act);
377  Aact.resize (n_act, n);
378  }
379  }
380  }
381  else
382  {
383  // The step is not null.
384  if (n_act - n_eq == n_in)
385  {
386  // All inequality constraints were active. We can
387  // add the whole step.
388  x += p;
389  }
390  else
391  {
392  // Some constraints were not active. Checking if
393  // there is a blocking constraint.
394  alpha = 1.0;
395  octave_idx_type is_block = -1;
396 
397  for (octave_idx_type i = 0; i < n_in; i++)
398  {
399  bool found = false;
400 
401  for (octave_idx_type j = 0; j < n_act-n_eq; j++)
402  {
403  if (Wact(j) == i)
404  {
405  found = true;
406  break;
407  }
408  }
409 
410  if (! found)
411  {
412  // The i-th constraint was not in the set. Is it a
413  // blocking constraint?
414 
415  RowVector tmp_row = Ain.row (i);
416  double tmp = tmp_row * p;
417  double res = tmp_row * x;
418 
419  if (tmp < 0.0)
420  {
421  double alpha_tmp = (bin(i) - res) / tmp;
422 
423  if (alpha_tmp < alpha)
424  {
425  alpha = alpha_tmp;
426  is_block = i;
427  }
428  }
429  }
430  }
431 
432  // In is_block there is the index of the blocking
433  // constraint (if any).
434  if (is_block >= 0)
435  {
436  // There is a blocking constraint (index in
437  // is_block) which is added to the active set.
438  n_act++;
439  Aact = Aact.stack (Ain.row (is_block));
440  bact.resize (n_act, bin(is_block));
441  Wact.resize (n_act-n_eq, is_block);
442 
443  // Adding the reduced step
444  x += alpha * p;
445  }
446  else
447  {
448  // There are no blocking constraints. Adding the
449  // whole step.
450  x += alpha * p;
451  }
452  }
453  }
454 
455  if (iter == maxit)
456  {
457  done = true;
458  // warning ("qp_main: maximum number of iteration reached");
459  info = 3;
460  }
461  }
462 
463  lambda_tmp = Y.transpose () * (g + H * p);
464 
465  // Reordering the Lagrange multipliers.
466 
467  lambda.resize (n_tot);
468  lambda.fill (0.0);
469  for (octave_idx_type i = 0; i < n_eq; i++)
470  lambda(i) = lambda_tmp(i);
471 
472  for (octave_idx_type i = n_eq; i < n_tot; i++)
473  {
474  for (octave_idx_type j = 0; j < n_act-n_eq; j++)
475  {
476  if (Wact(j) == i - n_eq)
477  {
478  lambda(i) = lambda_tmp(n_eq+j);
479  break;
480  }
481  }
482  }
483 
484  return info;
485 }
486 
487 DEFUN (__qp__, args, ,
488  doc: /* -*- texinfo -*-
489 @deftypefn {} {[@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}, @var{rtol})
490 Undocumented internal function.
491 @end deftypefn */)
492 {
493  if (args.length () != 9)
494  print_usage ();
495 
496  const ColumnVector x0 (args(0).vector_value ());
497  const Matrix H (args(1).matrix_value ());
498  const ColumnVector q (args(2).vector_value ());
499  const Matrix Aeq (args(3).matrix_value ());
500  const ColumnVector beq (args(4).vector_value ());
501  const Matrix Ain (args(5).matrix_value ());
502  const ColumnVector bin (args(6).vector_value ());
503  const int maxit (args(7).int_value ());
504  const double rtol (args(8).double_value());
505 
506  int iter = 0;
507 
508  // Copy the initial guess into the working variable
509  ColumnVector x = x0;
510 
511  // Reordering the Lagrange multipliers
512  ColumnVector lambda;
513 
514  int info = qp (H, q, Aeq, beq, Ain, bin, maxit, rtol, x, lambda, iter);
515 
516  return ovl (x, lambda, info, iter);
517 }
518 
519 /*
520 ## No test needed for internal helper function.
521 %!assert (1)
522 */
static octave_idx_type min_index(const ColumnVector &x)
Definition: __qp__.cc:47
static int qp(const Matrix &H, const ColumnVector &q, const Matrix &Aeq, const ColumnVector &beq, const Matrix &Ain, const ColumnVector &bin, int maxit, double rtol, ColumnVector &x, ColumnVector &lambda, int &iter)
Definition: __qp__.cc:104
void resize(const dim_vector &dv, const T &rfv)
Size of the specified dimension.
Definition: Array.cc:1011
T & xelem(octave_idx_type n)
Size of the specified dimension.
Definition: Array.h:469
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:377
double max(void) const
Definition: dColVector.cc:261
void resize(octave_idx_type n, const double &rfv=0)
Definition: dColVector.h:109
ColumnVector & fill(double val)
Definition: dColVector.cc:77
RowVector transpose(void) const
Definition: dColVector.cc:125
double min(void) const
Definition: dColVector.cc:245
ColumnVector extract_diag(octave_idx_type k=0) const
Definition: dDiagMatrix.h:105
Definition: EIG.h:41
ComplexMatrix right_eigenvectors(void) const
Definition: EIG.h:122
ComplexColumnVector eigenvalues(void) const
Definition: EIG.h:121
Definition: dMatrix.h:42
Matrix pseudo_inverse(double tol=0.0) const
Definition: dMatrix.cc:675
RowVector row(octave_idx_type i) const
Definition: dMatrix.cc:416
Matrix stack(const Matrix &a) const
Definition: dMatrix.cc:325
Matrix extract_n(octave_idx_type r1, octave_idx_type c1, octave_idx_type nr, octave_idx_type nc) const
Definition: dMatrix.cc:407
Matrix transpose(void) const
Definition: dMatrix.h:135
void resize(octave_idx_type nr, octave_idx_type nc, double rfv=0)
Definition: dMatrix.h:151
ColumnVector column(octave_idx_type i) const
Definition: dMatrix.cc:422
T chol_matrix(void) const
Definition: chol.h:74
DM_T singular_values(void) const
Definition: svd.h:88
T right_singular_matrix(void) const
Definition: svd.cc:61
ColumnVector real(const ComplexColumnVector &a)
Definition: dColVector.cc:137
OCTINTERP_API void print_usage(void)
Definition: defun.cc:53
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition: defun.h:56
void error(const char *fmt,...)
Definition: error.cc:968
F77_RET_T const F77_INT const F77_INT const F77_INT const F77_DBLE const F77_DBLE F77_INT F77_DBLE * V
F77_RET_T const F77_INT const F77_INT const F77_INT F77_DBLE const F77_INT F77_DBLE const F77_INT F77_DBLE const F77_INT F77_DBLE * Z
F77_RET_T const F77_INT F77_CMPLX * A
F77_RET_T const F77_DBLE * x
octave_idx_type n
Definition: mx-inlines.cc:753
T chol2inv(const T &r)
Definition: chol.cc:242
octave_value::octave_value(const Array< char > &chm, char type) return retval
Definition: ov.cc:811
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:211
static T abs(T x)
Definition: pr-output.cc:1678