function [x,status,lambda,nu] = gpposy(A,b,szs,varargin) % -------------------------------------------------------------------------------- % gpposy: A Matlab solver for Geometric Programs (GP) in posynomial form % % Kwangmoo Koh, Seung-Jean Kim, Almir Mutapcic, and Stephen Boyd. (2006) % gpposy is part of the ggplab package: http://www.stanford.edu/~boyd/ggplab/ % -------------------------------------------------------------------------------- % % [x,status,lambda,nu] = gpposy(A,b,szs,G,h,l,u,quiet) % % solves the geometric program in posynomial form % % minimize sum_k b0_k*x1^A0_{k1}*...*xn^A0_{kn} % subject to sum_k bi_k*x1^Ai_{k1}*...*xn^Ai_{kn} <= 1, i=1,...,m, % hi*x1^G_{i1}*...*xn^G_{in} = 1, i=1,...,p, % li <= xi <= ui, i=1,...,n, % % where variables are x1,...,xn and the problem data are bi_k, Ai_{kj}, % for i = 1,...,m, hi, G_{ij} for i = 1,...,p. % % Calling sequences: % % [x,status,lambda,nu] = gpposy(A,b,szs) % [x,status,lambda,nu] = gpposy(A,b,szs,G,h) % [x,status,lambda,nu] = gpposy(A,b,szs,G,h,l,u) % [x,status,lambda,nu] = gpposy(A,b,szs,G,h,l,u,quiet) % % Examples: % % [x,status,lambda,nu] = gpposy(A,b,szs,G,h) % [x,status,lambda,nu] = gpposy(A,b,szs,[],[],[],[],quiet) % % Input arguments: % % - A: (sum_i n_i) x n matrix; A = [A0' A1' ... Am' ]' % - b: (sum_i n_i) vector; b = [b0' b1' ... bm' ]' % - szs: dimensions of Ai and bi; szs = [n0 n1 ... nm]' % where Ai is (ni x n) and bi is (ni x 1) % - G: p x n matrix % - h: p-vector % - l: n-vector; lower bound for x % - u: n-vector; upper bound for x % - quiet: boolean variable; suppress all the print messages if true % % Output arguments: % % - x: n-vector; primal optimal point % - nu: (sum_i n_i) vector; nu = [nu0' nu1' ... num']' % optimal sensitivity for constraints Ai*x + bi = yi % - mu: (sum_i l_i) vector; mu = [mu0' mu1' ... mul']' % optimal sensitivity for constraints G*x + h = 0 % - lambda: m-vector, optimal sensitivity for inequality constraints % - status: string; 'INFEASIBLE', 'SOLVED', or 'FAILED' % % gpposy changes the problem data into posynomial form and calls the the % sovler, gpcvx.m. %---------------------------------------------------------------------- % INITIALIZATION %---------------------------------------------------------------------- % PARAMETERS LOWER_BOUND = 1e-100; UPPER_BOUND = 1e+100; n = size(A,2); % # of variables (x1,..,xn) % VARIABLE ARGUMENT HANDLING defaults = {[],[],LOWER_BOUND*ones(n,1),UPPER_BOUND*ones(n,1),false}; givenArgs = ~cellfun('isempty',varargin); defaults(givenArgs) = varargin(givenArgs); [G,h,l,u,quiet] = deal(defaults{:}); % CONVERT PROBLEM DATA INTO CONVEX FORM b = log(b); h = log(h); l = log(l); u = log(u); if ( ~(all(isfinite(b)) && all(isfinite(h))) ) disp('ERROR: Too small value of b or h'); x = []; status = 'FAILED'; lambda = []; nu = []; return; end %---------------------------------------------------------------------- % Call gpcvx %---------------------------------------------------------------------- [x,status,lambda,empty,nu] = gpcvx(A,b,szs,G,h,l,u,quiet); % CONVERT SOLUTION TO POSYNOMIAL FORM x = exp(x); %********************************************************************** % helper functions for gpposy.m % (merged by almir into a single file, 12/6/06) %********************************************************************** %********************************************************************** % gpcvx.m %********************************************************************** function [x,status,lambda,nu,mu] = gpcvx(A,b,szs,varargin) % [x,status,lambda,nu,mu] = gpcvx(A,b,szs,G,h,l,u,quiet) % % solves the geometric program in convex form % % minimize lse(y0) % subject to lse(yi) <= 0, i=1,...,m, % Ai*x+bi = yi, i=0,...,m, % G*x+h = 0, % li <= xi <= ui, i=1,...,n % % where lse is defined as lse(y) = log sum_i exp yi, % and the dual problem, % % maximize b0'*nu0 + ... + bm'*num + h'*mu + lambdal'*l - lambdau'*u + % entr(nu0) + lambda1*entr(nu1/lambda1) + % ,..., + lambdam*entr(num/lambdam) % subject to nui >= 0, i=0,...,m, % lambdai >= 0, i=1,...,m, % 1'*nu0 = 1 % 1'*nui = lambdai, i=1,...,m, % A0'*nu0 + ... + Am'*num + G'*mu + lambdau - lambdal = 0, % % where entr is defined as entr(y) = -sum_i yi*log(yi). % % Calling sequences: % % [x,status,lambda,nu,mu] = gpcvx(A,b,szs) % [x,status,lambda,nu,mu] = gpcvx(A,b,szs,G,h) % [x,status,lambda,nu,mu] = gpcvx(A,b,szs,G,h,l,u) % [x,status,lambda,nu,mu] = gpcvx(A,b,szs,G,h,l,u,quiet) % % Examples: % % [x,status,lambda,nu,mu] = gpcvx(A,b,szs,G,h) % [x,status,lambda,nu,mu] = gpcvx(A,b,szs,[],[],[],[],quite) % % Input arguments: % % - A: (sum_i n_i) x n matrix; A = [A0' A1' ... Am' ]' % - b: (sum_i n_i) vector; b = [b0' b1' ... bm' ]' % - szs: dimensions of Ai and bi; szs = [n0 n1 ... nm]' % where Ai is (ni x n) and bi is (ni x 1) % - G: p x n matrix % - h: p-vector % - l: n-vector; lower bound for x % - u: n-vector; upper bound for x % - quiet: boolean variable; suppress all the print messages if true % % Output arguments: % % - x: n-vector; primal optimal point % - nu: (sum_i n_i) vector; nu = [nu0' nu1' ... num']' % dual variables for constraints Ai*x + bi = yi % - mu: (sum_i l_i) vector; mu = [mu0' mu1' ... mul']' % dual variables for constraints G*x + h = 0 % - lambda: m-vector, dual variables for inequality constraints % - status: string; 'Infeasible', 'Solved', or 'Failed' % % gpcvx sets up phase 1 and phase 2 and calls the real sovler, gppd2.m. %---------------------------------------------------------------------- % INITIALIZATION %---------------------------------------------------------------------- % PARAMETERS LOWER_BOUND = -250; UPPER_BOUND = +250; % DIMENSIONS N = size(A,1); % # of terms in the obj. and inequalities n = size(A,2); % # of variables (x1,..,xn) m = length(szs)-1; % # of inequalities n0 = szs(1); % # of terms in the objective % VARIABLE ARGUMENT HANDLING defaults = {[],[],LOWER_BOUND*ones(n,1),UPPER_BOUND*ones(n,1),false}; givenArgs = ~cellfun('isempty',varargin); defaults(givenArgs) = varargin(givenArgs); [G,h,l,u,quiet] = deal(defaults{:}); % MATLAB LIMIT OF LOWER/UPPER BOUNDS l(lUPPER_BOUND) = UPPER_BOUND; if (isempty(G)) G = zeros(0,n); h = zeros(0,1); end p = size(G,1); % # of (terms in) the equality constraints % E is a matrix s.t. [1'*y0 1'*y1 ... 1'*ym ]' = E*y indsl = cumsum(szs); indsf = indsl-szs+1; lx = zeros(N,1); lx(indsf) = 1; E = sparse(cumsum(lx),[1:N],ones(N,1)); %---------------------------------------------------------------------- % PHASE I %---------------------------------------------------------------------- % solves the feasibility problem % % minimize s % subject to lse(yi) <= s, i=1,...,m, % Ai*x+bi = yi, i=0,...,m, % G*x+h = 0, % li <= xi <= ui, i=1,...,n % % where lse is defined as lse(y) = log sum_i exp yi, % % For phase I % 1) change objective function to s % 2) change constraints from fi(x) <= 0 to fi(x) <= s % 3) add bound constraints; li <= xi <= ui % % Hence, we set up a new objective and constraints, % i.e., A,b,G,h and szs for Phase I optimization. % % Change the size vector, szs. % % Change A and b % s xi % A1 = [ 1 | 0 0 0 b1 = [ 0 <- (a1) new objective % ---+------ - % -1 | % -1 |A(ineq) b <- (a2) new inequalities % -1 | % ---+------ - % -1 |-1 0 0 l1 % -1 | 0-1 0 l2 <- (a4) l <= xi % -1 | 0 0-1 l3 % ---+------ - % -1 | 1 0 0 -u1 % -1 | 0 1 0 -u2 <- (a6) xi <= u % -1 | 0 0 1 ]; -u3 ]; % FORM SZS szs1 = [1; szs(2:end); ones(2*n,1) ]; % FORM INITIAL X if (p == 0) xinit = zeros(n,1); else xinit = G'*((G*G')\h); end % FORM INITIAL S % sinit = max(fi,0) since fi <= si and 0 <= si y = A*xinit+b; [f,expyy] = lse(E,y); finit = f(2:m+1); linit = -xinit+l; uinit = +xinit-u; sinit = max([0; finit; linit; uinit]) + 1; % + 1 is for margin. % FORM A AND B A1 = [+1 , sparse(1,n);... -ones(N-n0,1) , A(n0+1:N,:);... -ones(n,1) ,-speye(n) ;... -ones(n,1) ,+speye(n) ]; b1 = [ 0; b((n0+1):N); l; -u ]; % FORM G AND H G1 = [spalloc(size(G,1),1,0), G]; h1 = [h]; % CALL THE INTERNAL GP SOLVER [x,status,lambda,nu,mu] = gppd2(A1,b1,szs1,[sinit;xinit],G1,h1,true,quiet); % EXTRACT X FROM [S; X] x0 = x(2:n+1); y = A*x0+b; [f,expyy] = lse(E,y); f1m = f(2:m+1); % FEASIBILITY CHECK OF PHASE I SOLUTION if (status <= 0 || max([f1m; -Inf]) >= 0) status = 'Infeasible'; if (~quiet) disp(status); end return end clear A1 b1 G1 h1 x01 szs1; %---------------------------------------------------------------------- % PHASE II %---------------------------------------------------------------------- % solves the geometric program in convex form % % minimize lse(y0) % subject to lse(yi) <= 0, i=1,...,m, % Ai*x+bi = yi, i=0,...,m, % G*x+h = 0, % li <= xi <= ui, i=1,...,n % % where lse is defined as lse(y) = log sum_i exp yi, % % Change A and b to add the bound of x into the inequality constraints. % % A2 = [ A b2 = [ b % ------ ---- % -1 0 0 l1 % 0-1 0 l2 <- li <= xi % 0 0-1 l3 % ------ ---- % 1 0 0 -u1 <- xi <= ui % 0 1 0 -u2 % 0 0 1 ]; -u3 ]; szs2 = [ szs ; ones(2*n,1) ]; A2 = [ sparse(A); -speye(n); speye(n) ]; b2 = [ b; l; -u ]; % CALL THE INTERNAL GP SOLVER [x,status,lambda,nu,mu] = gppd2(A2,b2,szs2,x0,G,h,false,quiet); if (status <= 0) status = 'Failed'; if (~quiet) disp(status); end return else status = 'Solved'; if (~quiet) disp(status); end return end %********************************************************************** % gppd2.m %********************************************************************** function [x,status,la,nu,mu] = gppd2(A,b,szs,x0,G,h,phase1,quiet) % [x,nu,mu,la,status] = gppd2(A,b,szs,x0,G,h,phase1,quiet) % % solves the geometric program in convex form with a starting point. % % minimize lse(y0) % subject to lse(yi) <= 0, i=1,...,m, % Ai*x+bi = yi, i=0,...,m, % G*x+h = 0, % % where lse is defined as lse(y) = log sum_i exp yi, % % and the dual problem, % % maximize b0'*nu0 + ... + bm'*num + h'*mu + % entr(nu0) + la1*entr(nu1/la1) + % ,..., + lam*entr(num/lam) % subject to nui >= 0, i=0,...,m, % lai >= 0, i=1,...,m, % 1'*nu0 = 1 % 1'*nui = lai, i=1,...,m, % A0'*nu0 + ... + Am'*num + G'*mu = 0, % % where entr is defined as entr(y) = -sum_i yi*log(yi). % % x0 should satisfy the primal inequality constraints. % % Input arguments: % % - A: (sum_i n_i) x n matrix; A = [A0' A1' ... Am' ]' % - b: (sum_i n_i) vector; b = [b0' b1' ... bm' ]' % - szs: dimensions of Ai and bi; szs = [Nob n1 ... nm]' % where Ai is (ni x n) and bi is (ni x 1) % - x0: n-vector; MUST BE STRICTLY FEASIBLE FOR INEQUALITIES % - G: p x n matrix % - h: p-vector % - phase1: boolean variable; indicator for phase I and phase II % true -> Phase I, false -> Phase II % - quiet: boolean variable; suppress all the print messages if true % % Output arguments: % % - x: n-vector; primal optimal point % - nu: (sum_i n_i) vector; nu = [nu0' nu1' ... num']' % dual variables for constraints Ai*x + bi = yi % - mu: p vector; mu = [mu1 ... mup]' % dual variables for constraints G*x + h = 0 % - la: m vector; la = [lambda1 ... lambdam]' % dual variables lambda; la_i = sum(nu_i) % - status: scalar; % 2 Function converged to a solution x. % 1 Phase I success; x(1:szs(1)) <= 0. % -1 Number of iterations exceeded MAXITERS. % -2 Starting point is not strictly feasible. % -3 Newton step calculation failure. %---------------------------------------------------------------------- % INITIALIZATION %---------------------------------------------------------------------- % PARAMETERS ALPHA = 0.01; % backtracking linesearch parameter (0,0.5] BETA = 0.5; % backtracking linesearch parameter (0,1) MU = 2; % IPM parameter: t update MAXITER = 500; % IPM parameter: max iteration of IPM EPS = 1e-5; % IPM parameter: tolerance of surrogate duality gap EPSfeas = 1e-5; % IPM parameter: tolerance of feasibility % DIMENSIONS [N,n] = size(A); m = length(szs)-1; p = size(G,1); n0 = szs(1); if (isempty(G)), G = zeros(0,n); h = zeros(0,1); end warning off all; % SPARSE ZERO MATRIX Opxp = sparse(p,p); % SUM MATRIX: E is a matrix s.t. [1'*y0 1'*y1 ... 1'*ym ]' = E*y indsl = cumsum(szs); indsf = indsl-szs+1; lx = zeros(N,1); lx(indsf) = 1; E = sparse(cumsum(lx),[1:N],ones(N,1)); x = x0; % f1m is a LHS vector of inequality constraints s.t [f1' ... fm']' y = A*x+b; [f,expyy] = lse(E,y); f1m = f(2:m+1); % CHECK THE INITIAL CONDITIONS if (max(f1m) >= 0) if (~quiet) disp(['x0 is not strictly feasible.']); end la = []; nu = []; mu = []; status = -2; return; end; % INITIAL DUAL POINT la = -1./f1m; % positive value with duality gap 1. nu = ones(N,1); % ANY value. mu = ones(p,1); % ANY value. step = Inf; pp = []; if (~quiet) disp(sprintf('\n%s %15s %11s %20s %18s \n',... 'Iteration','primal obj.','gap','dual residual','previous step.')); end %---------------------------------------------------------------------- % MAIN LOOP %---------------------------------------------------------------------- for iters = 1:MAXITER gap = -f1m'*la; % UPDATE T % update t only when the current x is not to far from the cetural path. if (step > 0.5) t = m*MU/gap; end % CALCULATE RESIDUALS % gradfy = exp(y)./(E'*(E*exp(y))); gradfy = expyy./(E'*(E*expyy)); resDual = A'*(gradfy.*(E'*[1;la])) + G'*mu; resPrim = G*x + h; resCent = [-la.*f1m-1/t]; residual = [resDual; resCent; resPrim]; if (~quiet) disp(sprintf('%4d %20.5e %16.5e %14.2e %16.2e',... iters,f(1),-f1m'*la,norm(resDual),step)); end % STOPPING CRITERION FOR PHASE I if ((phase1 == true) & max(x(1:szs(1))) < 0) nu = gradfy.*(E'*[1;la]); status = 1; return; end; % STOPPING CRITERION FOR PHASE I & II if ( (gap <= EPS) & ... (norm(resDual) <= EPSfeas) & (norm(resPrim) <= EPSfeas) ) % this calculation of nu is correct only when reached to optimal nu = gradfy.*(E'*[1;la]); status = 2; return; end; % CALCULATE NEWTON STEP diagL1 = sparse(1:m+1,1:m+1,[0;-la./f1m]); diagL2 = sparse(1:m+1,1:m+1,[1;la]); diagG1 = sparse(1:N,1:N,gradfy); diagG2 = sparse(1:N,1:N,gradfy.*(E'*[1;la])); EGA = E*diagG1*A; H1 = EGA'*(diagL1-diagL2)*EGA + A'*diagG2*A; if( 0 ) dz = -[ H1, G' ; ... G , Opxp ] ... \ ... [EGA'*[1;-1./(t*f1m)]+G'*mu ; resPrim ]; else dz = -H1 \ [EGA'*[1;-1./(t*f1m)]]; end % PERTURB KKT WHEN (ALMOST) SINGULAR % add small diagonal terms when the matrix is almost singular. perturb = EPS; while (any(isinf(dz)) || any(isnan(dz))) dz = -([ H1, G'; G , Opxp ] + sparse(1:n+p,1:n+p,perturb))... \ ... [EGA'*[1;-1./(t*f1m)]+G'*mu ; resPrim ]; % increase the size of diagonal term if still singular perturb = perturb*10; if (perturb > 1) break; end end % ERROR CHECK FOR NEWTON STEP CALCULATION if (any(isnan(dz))) nu = gradfy.*(E'*[1;la]); status = -3; return; end dx = dz(1:n); dy = A*dx; dmu = dz(n+[1:p]'); dla = -la./f1m.*(E(2:m+1,:)*(gradfy.*dy))+resCent./f1m; % BACKTRACKING LINESEARCH negIdx = (dla < 0); if (any(negIdx)) step = min( 1, 0.99*min(-la(negIdx)./dla(negIdx)) ); else step = 1; end while (1) newx = x + step*dx; newy = y + step*dy; newla = la + step*dla; newmu = mu + step*dmu; [newf,newexpyy] = lse(E,newy); newf1m = newf(2:end); % UPDATE RESIDUAL % newGradfy = exp(newy)./(E'*(E*exp(newy))); newGradfy = newexpyy./(E'*(E*newexpyy)); newResDual = A'*(newGradfy.*(E'*[1;newla])) + G'*newmu; newResPrim = G*newx + h; newResCent = [-newla.*newf1m-1/t]; newResidual = [newResDual; newResCent; newResPrim]; if ( max(newf1m) < 0 && ... norm(newResidual) <= (1-ALPHA*step)*norm(residual) ) break; end step = BETA*step; end; % UPDATE PRIMAL AND DUAL VARIABLES x = newx; mu = newmu; la = newla; y = A*x+b; [f,expyy] = lse(E,y); f1m = f(2:m+1); end if (iters >= MAXITER) if (~quiet) disp(['Maxiters exceeded.']); end nu = gradfy.*(E'*[1;la]); status = -1; return; end %********************************************************************** % lse.m %********************************************************************** function [f,expyy] = lse(E,y) % % LSE finds log-sum-exp values. % % It calculates log(E*exp(y)), with special handling of extreme y values. % % ymax is a vector of maximum exponent values of each posynomial. % % ex: log(exp(y1)+exp(y2)+exp(y3)), log(exp(y4)+exp(y5)) % ymax = [max(y1,y2,y3); max(y4,y5)] % % note: min(y) are substracted and added to handle the case when all the % exponents of a posynomial are negative. % i.e., 0 0 0 -3 -4 -> max value should be -3 ymax = full(max(E*sparse(1:length(y),1:length(y),y-min(y)),[],2))+min(y); expyy = exp(y-E'*ymax); f = log(E*expyy)+ymax;