Mercurial > hg > octave-nkf
view scripts/optimization/qp.m @ 15063:36cbcc37fdb8
Refactor configure.ac to make it more understandable.
Use common syntax for messages in config.h
Correct typos, refer to libraries in all caps, use two spaces after period.
Follow Autoconf guidelines and place general tests before specific tests.
* configure.ac, m4/acinclude.m4: Use common syntax for messages in config.h
Correct typos, refer to libraries in all caps, use two spaces after period.
Follow Autoconf guidelines and place general tests before specific tests.
author | Rik <rik@octave.org> |
---|---|
date | Tue, 31 Jul 2012 10:28:51 -0700 |
parents | 5d3a684236b0 |
children | 1c89599167a6 |
line wrap: on
line source
## Copyright (C) 2000-2012 Gabriele Pannocchia. ## ## This file is part of Octave. ## ## Octave is free software; you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or (at ## your option) any later version. ## ## Octave is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with Octave; see the file COPYING. If not, see ## <http://www.gnu.org/licenses/>. ## -*- texinfo -*- ## @deftypefn {Function File} {[@var{x}, @var{obj}, @var{info}, @var{lambda}] =} qp (@var{x0}, @var{H}) ## @deftypefnx {Function File} {[@var{x}, @var{obj}, @var{info}, @var{lambda}] =} qp (@var{x0}, @var{H}, @var{q}) ## @deftypefnx {Function File} {[@var{x}, @var{obj}, @var{info}, @var{lambda}] =} qp (@var{x0}, @var{H}, @var{q}, @var{A}, @var{b}) ## @deftypefnx {Function File} {[@var{x}, @var{obj}, @var{info}, @var{lambda}] =} qp (@var{x0}, @var{H}, @var{q}, @var{A}, @var{b}, @var{lb}, @var{ub}) ## @deftypefnx {Function File} {[@var{x}, @var{obj}, @var{info}, @var{lambda}] =} qp (@var{x0}, @var{H}, @var{q}, @var{A}, @var{b}, @var{lb}, @var{ub}, @var{A_lb}, @var{A_in}, @var{A_ub}) ## @deftypefnx {Function File} {[@var{x}, @var{obj}, @var{info}, @var{lambda}] =} qp (@dots{}, @var{options}) ## Solve the quadratic program ## @tex ## $$ ## \min_x {1 \over 2} x^T H x + x^T q ## $$ ## @end tex ## @ifnottex ## ## @example ## @group ## min 0.5 x'*H*x + x'*q ## x ## @end group ## @end example ## ## @end ifnottex ## subject to ## @tex ## $$ ## Ax = b \qquad lb \leq x \leq ub \qquad A_{lb} \leq A_{in} \leq A_{ub} ## $$ ## @end tex ## @ifnottex ## ## @example ## @group ## A*x = b ## lb <= x <= ub ## A_lb <= A_in*x <= A_ub ## @end group ## @end example ## ## @end ifnottex ## @noindent ## using a null-space active-set method. ## ## Any bound (@var{A}, @var{b}, @var{lb}, @var{ub}, @var{A_lb}, ## @var{A_ub}) may be set to the empty matrix (@code{[]}) if not ## present. If the initial guess is feasible the algorithm is faster. ## ## @table @var ## @item options ## An optional structure containing the following ## parameter(s) used to define the behavior of the solver. Missing elements ## in the structure take on default values, so you only need to set the ## elements that you wish to change from the default. ## ## @table @code ## @item MaxIter (default: 200) ## Maximum number of iterations. ## @end table ## @end table ## ## @table @var ## @item info ## Structure containing run-time information about the algorithm. The ## following fields are defined: ## ## @table @code ## @item solveiter ## The number of iterations required to find the solution. ## ## @item info ## An integer indicating the status of the solution. ## ## @table @asis ## @item 0 ## The problem is feasible and convex. Global solution found. ## ## @item 1 ## The problem is not convex. Local solution found. ## ## @item 2 ## The problem is not convex and unbounded. ## ## @item 3 ## Maximum number of iterations reached. ## ## @item 6 ## The problem is infeasible. ## @end table ## @end table ## @end table ## @end deftypefn ## PKG_ADD: ## Discard result to avoid polluting workspace with ans at startup. ## PKG_ADD: [~] = __all_opts__ ("qp"); function [x, obj, INFO, lambda] = qp (x0, H, varargin) nargs = nargin; if (nargin == 1 && ischar (x0) && strcmp (x0, 'defaults')) x = optimset ("MaxIter", 200); return; endif if (nargs > 2 && isstruct (varargin{end})) options = varargin{end}; nargs--; else options = struct (); endif if (nargs >= 3) q = varargin{1}; else q = []; endif if (nargs >= 5) A = varargin{2}; b = varargin{3}; else A = []; b = []; endif if (nargs >= 7) lb = varargin{4}; ub = varargin{5}; else lb = []; ub = []; endif if (nargs == 10) A_lb = varargin{6}; A_in = varargin{7}; A_ub = varargin{8}; else A_lb = []; A_in = []; A_ub = []; endif if (nargs == 2 || nargs == 3 || nargs == 5 || nargs == 7 || nargs == 10) maxit = optimget (options, "MaxIter", 200); ## Checking the quadratic penalty if (! issquare (H)) error ("qp: quadratic penalty matrix not square"); elseif (! ishermitian (H)) ## warning ("qp: quadratic penalty matrix not hermitian"); H = (H + H')/2; endif n = rows (H); ## Checking the initial guess (if empty it is resized to the ## right dimension and filled with 0) if (isempty (x0)) x0 = zeros (n, 1); elseif (numel (x0) != n) error ("qp: the initial guess has incorrect length"); endif ## Linear penalty. if (isempty (q)) q = zeros (n, 1); elseif (numel (q) != n) error ("qp: the linear term has incorrect length"); endif ## Equality constraint matrices if (isempty (A) || isempty (b)) A = zeros (0, n); b = zeros (0, 1); n_eq = 0; else [n_eq, n1] = size (A); if (n1 != n) error ("qp: equality constraint matrix has incorrect column dimension"); endif if (numel (b) != n_eq) error ("qp: equality constraint matrix and vector have inconsistent dimension"); endif endif ## Bound constraints Ain = zeros (0, n); bin = zeros (0, 1); n_in = 0; if (nargs > 5) if (! isempty (lb)) if (numel (lb) != n) error ("qp: lower bound has incorrect length"); elseif (isempty (ub)) Ain = [Ain; eye(n)]; bin = [bin; lb]; endif endif if (! isempty (ub)) if (numel (ub) != n) error ("qp: upper bound has incorrect length"); elseif (isempty (lb)) Ain = [Ain; -eye(n)]; bin = [bin; -ub]; endif endif if (! isempty (lb) && ! isempty (ub)) rtol = sqrt (eps); for i = 1:n if (abs (lb (i) - ub(i)) < rtol*(1 + max (abs (lb(i) + ub(i))))) ## These are actually an equality constraint tmprow = zeros (1,n); tmprow(i) = 1; A = [A;tmprow]; b = [b; 0.5*(lb(i) + ub(i))]; n_eq = n_eq + 1; else tmprow = zeros (1,n); tmprow(i) = 1; Ain = [Ain; tmprow; -tmprow]; bin = [bin; lb(i); -ub(i)]; n_in = n_in + 2; endif endfor endif endif ## Inequality constraints if (nargs > 7) [dimA_in, n1] = size (A_in); if (n1 != n) error ("qp: inequality constraint matrix has incorrect column dimension"); else if (! isempty (A_lb)) if (numel (A_lb) != dimA_in) error ("qp: inequality constraint matrix and lower bound vector inconsistent"); elseif (isempty (A_ub)) Ain = [Ain; A_in]; bin = [bin; A_lb]; endif endif if (! isempty (A_ub)) if (numel (A_ub) != dimA_in) error ("qp: inequality constraint matrix and upper bound vector inconsistent"); elseif (isempty (A_lb)) Ain = [Ain; -A_in]; bin = [bin; -A_ub]; endif endif if (! isempty (A_lb) && ! isempty (A_ub)) rtol = sqrt (eps); for i = 1:dimA_in if (abs (A_lb(i) - A_ub(i)) < rtol*(1 + max (abs (A_lb(i) + A_ub(i))))) ## These are actually an equality constraint tmprow = A_in(i,:); A = [A;tmprow]; b = [b; 0.5*(A_lb(i) + A_ub(i))]; n_eq = n_eq + 1; else tmprow = A_in(i,:); Ain = [Ain; tmprow; -tmprow]; bin = [bin; A_lb(i); -A_ub(i)]; n_in = n_in + 2; endif endfor endif endif endif ## Now we should have the following QP: ## ## min_x 0.5*x'*H*x + x'*q ## s.t. A*x = b ## Ain*x >= bin ## Discard inequality constraints that have -Inf bounds since those ## will never be active. idx = isinf (bin) & bin < 0; bin(idx) = []; Ain(idx,:) = []; n_in = numel (bin); ## Check if the initial guess is feasible. if (isa (x0, "single") || isa (H, "single") || isa (q, "single") || isa (A, "single") || isa (b, "single")) rtol = sqrt (eps ("single")); else rtol = sqrt (eps); endif eq_infeasible = (n_eq > 0 && norm (A*x0-b) > rtol*(1+abs (b))); in_infeasible = (n_in > 0 && any (Ain*x0-bin < -rtol*(1+abs (bin)))); info = 0; if (eq_infeasible || in_infeasible) ## The initial guess is not feasible. ## First define xbar that is feasible with respect to the equality ## constraints. if (eq_infeasible) if (rank (A) < n_eq) error ("qp: equality constraint matrix must be full row rank"); endif xbar = pinv (A) * b; else xbar = x0; endif ## Check if xbar is feasible with respect to the inequality ## constraints also. if (n_in > 0) res = Ain * xbar - bin; if (any (res < -rtol * (1 + abs (bin)))) ## xbar is not feasible with respect to the inequality ## constraints. Compute a step in the null space of the ## equality constraints, by solving a QP. If the slack is ## small, we have a feasible initial guess. Otherwise, the ## problem is infeasible. if (n_eq > 0) Z = null (A); if (isempty (Z)) ## The problem is infeasible because A is square and full ## rank, but xbar is not feasible. info = 6; endif endif if (info != 6) ## Solve an LP with additional slack variables to find ## a feasible starting point. gamma = eye (n_in); if (n_eq > 0) Atmp = [Ain*Z, gamma]; btmp = -res; else Atmp = [Ain, gamma]; btmp = bin; endif ctmp = [zeros(n-n_eq, 1); ones(n_in, 1)]; lb = [-Inf(n-n_eq,1); zeros(n_in,1)]; ub = []; ctype = repmat ("L", n_in, 1); [P, dummy, status] = glpk (ctmp, Atmp, btmp, lb, ub, ctype); if ((status == 180 || status == 181 || status == 151) && all (abs (P(n-n_eq+1:end)) < rtol * (1 + norm (btmp)))) ## We found a feasible starting point if (n_eq > 0) x0 = xbar + Z*P(1:n-n_eq); else x0 = P(1:n); endif else ## The problem is infeasible info = 6; endif endif else ## xbar is feasible. We use it a starting point. x0 = xbar; endif else ## xbar is feasible. We use it a starting point. x0 = xbar; endif endif if (info == 0) ## The initial (or computed) guess is feasible. ## We call the solver. [x, lambda, info, iter] = __qp__ (x0, H, q, A, b, Ain, bin, maxit); else iter = 0; x = x0; lambda = []; endif obj = 0.5 * x' * H * x + q' * x; INFO.solveiter = iter; INFO.info = info; else print_usage (); endif endfunction