% Script: nonlinearLS
% Programmer: t. shores
% Last Rev: 7/1/02
% Description:
% A package of functions for solving the unconstrained nonlinear
% least squares problem of minimizing f(x) = 0.5*R(x)^T*R(x)
% where R(x) = (r_1(x),...,r_n(x)) and x = (x_1,...,x_m).

% not a function file:
1;

% preliminary tools

function retval = arrayf(fname,arr)
% usage: arrayf(fname,arr)
% description: this returns the matrix resulting from replacing
% each coordinate of arr by its evaluation by the function with
% name fname.

% local variables
% ii,jj: index variables
% nr,nc: number of rows, cols

[nr,nc] = size(arr);
retval = zeros(nr,nc);
for ii = 1:nr
  for jj = 1:nc
    retval(ii,jj) = feval(fname,arr(ii,jj));
  end;
end;

endfunction

%

function retval = testfn(x)

retval = zeros(3,1);
retval(1) = 3*x(1)-cos(x(2)*x(3))-0.5;
retval(2) = x(1)^2-81*(x(2)+0.1)^2+sin(x(3))+1.06;
retval(3) = exp(-x(1)*x(2))+20*x(3)+(10*pi-3)/3;

endfunction

%

function retval = testf(x)

retval = testfn(x)'*testfn(x);

endfunction

%

function retval = testR(x)
% usage: retval = testR(x)
% description: a modification of extended Rosenbrock
% function, which it is when cnst k = 0. User sets
% both k and dimension of problem within fcn dfn.
% Standard starter: (-1,2,1,-1,2,1,...)
% With k=0, root is (1,1,...).

n = 10; % size of problem is 3 x n.
k = 0;  % must be >= 0.
retval = zeros(3*n,0);
for kk = 1:n
  n1 = 3*kk - 2;
  n2 = 3*kk - 1;
  n3 = 3*kk;
  retval(n1) = 10*(x(n2) - x(n1));
  retval(n2) = 1 - x(n1);
  retval(n3) = k*sin(x(n2)^2+x(n3)^2);
end

endfunction

%

function retval = jacob(fname,x,h,m)
% usage: retval = jacob(fname,x,h,m)
% description: An unsophisticated Jacobian calculation using
% certered differences. fname(x) and x are *assumed* vector and
% fname(x) is *assumed* a column vector of length m.

% local variables
% jj: index variable
% nx: length of x

nx = length(x);
retval = zeros(m,nx);
for jj = 1:nx
  dx = zeros(size(x));
  dx(jj) = h;
  retval(:,jj) = (feval(fname,x+dx)-feval(fname,x-dx))/(2*h);
end;

endfunction

%

function retval = hess(fname,x,h)
% usage: retval = hess(fname,x,h)
% description: An unsophisticated Hessian calculation using
% forward differences. fname(x) is assumed scalar and x is
% assumed vector.

% local variables
% ii,jj: index variable
% nx: length of x

nx = length(x);
retval = zeros(nx,nx);
fx = zeros(nx,1);
h2 = h*h;
fnamex = feval(fname,x);
for jj = 1:nx
  dx = zeros(nx,1);
  dx(jj) = h;
  fx(jj) = feval(fname,x+dx);
end
for jj = 1:nx
  for ii = 1:jj
    dx = zeros(nx,1);
    dx(ii) = h;
    dx(jj) = dx(jj)+h;
    retval(ii,jj) = (feval(fname,x+dx)-fx(ii)-fx(jj)+fnamex)/h2
  end
end;
retval = retval + triu(retval,1)';

endfunction

%


% the minimizers

function [xnew,resid,kk] = levmar(Rname,x0,nu,itlim,h)
% usage: [x,resid,k] = levmar(Rname,x0,nu0,itlim,h)
% description: Levenberg-Marcquard for minimization of
% nonlinear least squares problem:    min f(x)=0.5*R(x)^T*R(x).
% Input function name, initial vector guess,  fixed L-M
% parameter, iteration limit, derivative stepsize. Output final x, optionally
% norm of residual at final x and number of iterations.

% local variables
% h: stepsize used for numerical derivatives in this algorithm
% stopr,stopa: relative and absolute stopping coefficients
% kk: iteration counter
% nx,nR: length of x0, R
% xold,xnew: current and next value of x
% strial: value of search direction vector
% Rxold: R(xold)
% fpold,Rpold: grad(f) and R'(xold)
% normfp0, normfpold: ||fp0||, ||f'(xold)||
% uu,ss,vv: the U,S,V of svd for (R'(xold))^T*R'(xold)
% ared, pred: actual and predicted reductions in f

% Initialization code
% first the constants of method
stopr = 1e-5;
stopa = 1e-9;
% next the rest of initialization
nx = length(x0);

% make sure x0 is a column
if (columns(x0)>1)
  x0 = x0';
end
xnew = x0;
xold = x0;
strial = x0;
% do calculation of grad(f)(xold)
nR = length(feval(Rname,xold));
Rxold = feval(Rname,xold);
Rpold = jacob(Rname,xold,h,nR);
% use the fact that grad(f)(x) = R'(x)^T*R(x);
fpold = Rpold'*Rxold;
normfp0 = norm(fpold);
resid = norm(Rxold); %degub
% main loop
for kk = 1:itlim
  xold = xnew;
  Rxold = feval(Rname,xold);
  Rpold = jacob(Rname,xold,h,nR);
  % use the fact that grad(f)(x) = R'(x)^T*R(x);
  fpold = Rpold'*Rxold;
  normfpold = norm(fpold);
  % test for termination
  if (normfpold <= stopr*normfp0 + stopa)
    break;
  end
  strial = -inv(Rpold'*Rpold + nu*eye(nx))*fpold;
  xnew = xold + strial;
  resid = norm(feval(Rname,xnew),inf); %degub
end % for
resid = norm(feval(Rname,xnew),inf);

endfunction

%
function [xnew,resid,kk] = levmart(Rname,x0,nu0,itlim,h)
% usage: [x,resid,k] = levmart(Rname,x0,nu0,itlim,h)
% description: Levenberg-Marcquard with pseudo-trust region via
% variable L-M parameter, for minimization of nonlinear least
% squares problem:    min f(x)=0.5*R(x)^T*R(x).  Input
% function name, initial vector guess,  initial L-M parameter,
% iteration limit, derivative stepsize. Output final x, optionally
% norm of residual at final x and number of iterations.

% local variables
% h: stepsize used for numerical derivatives in this algorithm
% stopr,stopa: relative and absolute stopping coefficients
% scaledn,scaleup: trust region contraction, expansion factors
% mu0,umlow,mumax: trust region parameters
% kk: iteration counter
% nx,nR: length of x0, R
% nuold: working L-M parameter
% xold,xtrial,xnew: current, trial and next value of x
% strial: trial value of search direction vector
% Rxold,Rxtrial: R(xold) and R(xtrial)
% fpold,Rpold: grad(f) and R'(xold)
% normfp0, normfpold: ||fp0||, ||f'(xold)||
% uu,ss,vv: the U,S,V of svd for (R'(xold))^T*R'(xold)
% ared, pred: actual and predicted reductions in f

global storage;%degub

% Initialization code
% first the constants of method
scaledn = 0.5;
scaleup = 2.0;
mu0 = 1e-4;
mulow = 0.25;
muhigh = 0.75;
stopr = 1e-5;
stopa = 1e-9;
% next the rest of initialization
nx = length(x0);
nuold = nu0;
% make sure x0 is a column
if (columns(x0)>1)
  x0 = x0';
end
xtmp = x0;
xtrial = x0;
xnew = x0;
xold = x0;
strial = x0;
ared = 0;
pred = 0;
% do calculation of grad(f)(xold)
nR = length(feval(Rname,xold));
Rxold = feval(Rname,xold);
Rpold = jacob(Rname,xold,h,nR);
% use the fact that grad(f)(x) = R'(x)^T*R(x);
fpold = Rpold'*Rxold;
normfp0 = norm(fpold);
% main loop
for kk = 1:itlim
  xold = xnew;
  Rxold = feval(Rname,xold);
  Rpold = jacob(Rname,xold,h,nR);
  % use the fact that grad(f)(x) = R'(x)^T*R(x);
  fpold = Rpold'*Rxold;
  normfpold = norm(fpold);
  normfold = norm(Rxold);
  % test for termination
  if (normfpold <= stopr*normfp0 + stopa)
    break;
  end
  storage(:,kk)=xold;%degub
  % set up to compute trial steps
  % set looping flag in trust region test to true
  trflag = 1;
  xnew = xold;
  % now loop
  while (trflag)
    % use identity (A+alpha*I)^{-1}=V*(S^2+alpha*I)*V^T
    % to solve for trial step in L-M formula
    %strial = -vv*diag(1./(diag(ss).^2 + nuold))*fp;
    strial = -inv(Rpold'*Rpold + nuold*eye(nx))*fpold;
    xtrial = xold + strial;
    Rxtrial = feval(Rname,xtrial);
    ared = 0.5*(Rxold'*Rxold - Rxtrial'*Rxtrial);
    pred = -0.5*(Rpold'*Rxold)'*strial;
    if (ared/pred < mu0)
      nuold = max(scaleup*nuold,nu0);
    elseif (ared/pred < mulow)
      trflag = 0;
      xnew = xtrial;
      nuold = max(scaleup*nuold,nu0);
    else
      trflag = 0;
      xnew = xtrial;
      if (ared/pred > muhigh)
        nuold = scaledn*nuold;
      end
      if (nuold < nu0)
        nuold = 0;
      end
    end %if
  end % while
end % for
resid = norm(feval(Rname,xnew));

endfunction

%

function [xbest,resid,kk,nn] = bfgs(Rname,x0,nu,iterlim,h)
% usage: [x,res,kk,nr] = bfgs(Rname,x0,nu,iterlim,h)
% description: a modified Newton BFGS method applied to
% least squares optimization problem: min f(x)=0.5*R(x)^T*R(x).
% Input function name, initial vector guess, regularization
% parameter, iteration limit, stepsize for numerical derivative.
% Output final x, optionally norm of residual
% at final x, iteration number and restart number.
% Uses Armijo search, restart and stopping criteria. Stores
% full Jacobian approximation.
% Just added: memory, so that the x with least norm residual
% gets returned.

global storage; %degub

% local variables:
% stopr,stopa: relative and absolute stopping coefficients
% kk,nn: iteration counter, restart counter
% xold, xnew: vectors for iteration
% fpold, fpnew: f'(xold) and f'(xnew)
% nx: length of x
% nR: length
% B: inverse of Jacobian or approximation matrix
% s: search direction
% lambda: search direction parameter
% s: difference in x steps
% y: difference of gradients of f
% u,v: temporaries in BFGS update
% restartf: restart flag, value 0 for no, 1 for L-M H, 2 for I
% normfp0: norm of initial function gradient value

% initialization
stopr = 1e-5;
stopa = 1e-4;
tau = 1e-4; %acceptable reduction factor
maxhalf = 6; % maximum number of halfing steps
xold = x0;
xbest = xold;
nx = length(xold);
% do calculation of grad(f)(x0)
Rxold = feval(Rname,xold);
nR = length(Rxold);
Rpold = jacob(Rname,xold,h,nR);
% use the fact that grad(f)(x) = R'(x)^T*R(x);
fpold = Rpold'*Rxold;
normfp0 = norm(fpold);
normfp = normfp0;
resid = norm(Rxold); %degub
bestresid = resid;
bestindex = 1;
nn = 1;
restartf = 1;
% construct L-M restart matrix
B = eye(nx);
%B = inv(Rpold'*Rpold + nu*eye(nx));
kk = 0;
while (kk < iterlim)
  % find a search direction
  s = -B*fpold;
  % do a line search
  jj = 0;
  lambda = min(1,1/(1+normfp));
  while (jj < maxhalf)
    xtmp = xold + lambda*s;
    normftmp = norm(feval(Rname,xtmp));
    if (normftmp < (1-tau*lambda)*resid)
      break;
    end;
    lambda = lambda/2;
    jj = jj + 1;
  end
  if (jj >= maxhalf)
     restartf = restartf + 1;
    if (restartf > 2)
      disp('Failure of search direction to reduce ||f(x)||');
      return;
    else % need to really do a restart
      nn = nn + 1;
      if (restartf == 2) % construct the L-M restart matrix
        Rxold = feval(Rname,xold);
        Rpold = jacob(Rname,xold,h,nR);
        fpold = Rpold'*Rxold;
        B = inv(Rpold'*Rpold + nu*eye(nx));
      else  % construct the identity restart
        B = eye(nx);
      end
      continue;
    end
  end
  % can now assume search direction succeeded
  kk = kk + 1;
  xnew = xold + lambda*s;
  % we need fpnew
  Rxnew = feval(Rname,xnew);
  Rpnew = jacob(Rname,xnew,h,nR);
  fpnew = Rpnew'*Rxnew;
  % now do a BFGS update
  s = xnew - xold;
  y = fpnew - fpold;
  p = y'*s;
  B = B - s*(y'*B)/p;
  B = B - (B*y)*s'/p + s*s'/p;
  restartf = 0;
  xold = xnew;
  Rxold = feval(Rname,xold);
  Rpold = jacob(Rname,xold,h,nR);
  fpold = Rpold'*Rxold;
  normfp = norm(fpold);
  resid = norm(Rxold);
  storage(:,kk)=xold;%degub
  % test for best residual
  if (resid <= bestresid)
    bestresid = resid;
    xbest = xold;
    bestindex = kk;
  end
  disp([resid,normfp,resid + normfp,bestindex])
  % test for termination
  if (normfp <= stopr*normfp0 + stopa)
    return;
  end
end

endfunction

%

function [xold,resid,kk,nn] = quasinewton(Rname,x0,nu,iterlim,h)
% usage: [x,res,kk,nr] = bfgs(Rname,x0,nu,iterlim,h)
% description: a quasiNewton BFGS method applied to
% least squares optimization problem: min f(x)=0.5*R(x)^T*R(x).
% Input function name, initial vector guess, regularization
% parameter, iteration limit, stepsize for numerical derivative.
% Output final x, optionally norm of residual
% at final x, iteration number and restart number.
% Uses Armijo search, restart and stopping criteria. Stores
% full Jacobian approximation.

% local variables:
% stopr,stopa: relative and absolute stopping coefficients
% kk,nn: iteration counter, restart counter
% xold, xnew: vectors for iteration
% fpold, fpnew: f'(xold) and f'(xnew)
% nx: length of x
% nR: length
% B: inverse of Jacobian or approximation matrix
% s: search direction
% lambda: search direction parameter
% s: difference in x steps
% y: difference of gradients of f
% u,v: temporaries in BFGS update
% restartf: restart flag, value 0 for no, 1 for L-M H, 2 for I
% normfp0: norm of initial function gradient value

global storage;

% initialization
stopr = 1e-5;
stopa = 1e-9;
tau = 1e-4; %acceptable reduction factor
maxhalf = 6; % maximum number of halfing steps
xold = x0;
nx = length(xold);
% do calculation of grad(f)(x0)
Rxold = feval(Rname,xold);
nR = length(Rxold);
Rpold = jacob(Rname,xold,h,nR);
% use the fact that grad(f)(x) = R'(x)^T*R(x);
fpold = Rpold'*Rxold;
normfp0 = norm(fpold);
resid = norm(Rxold); %degub
nn = 1;
restartf = 1;
% calculate approximate full Hessian for restart
B = zeros(nx,nx);
fx = zeros(nx,1);
h2 = h*h;
fnamex = Rxold'*Rxold;
for jj = 1:nx
  dx = zeros(nx,1);
  dx(jj) = h;
  Rtmp = feval(Rname,xold+dx);
  fx(jj) = Rtmp'*Rtmp;
end
for jj = 1:nx
  for ii = 1:jj
    dx = zeros(nx,1);
    dx(ii) = h;
    dx(jj) = dx(jj)+h;
    Rtmp = feval(Rname,xold+dx);
    B(ii,jj) = (Rtmp'*Rtmp-fx(ii)-fx(jj)+fnamex)/h2;
  end
end;
B = inv(B + triu(B,1)');
for kk = 1:iterlim
  s = -B*fpold;
  % do a line search
  jj = 0;
  lambda = 2;
  normfx = norm(feval(Rname,xold));
  while (jj < maxhalf)
    lambda = lambda/2;
    xtmp = xold + lambda*s;
    normftmp = norm(feval(Rname,xtmp));
    if (normftmp < (1-tau*lambda)*normfx)
      break;
    end;
    jj = jj + 1;
  end
  if (jj == maxhalf)
    lambda = 0;
  end
  if (lambda == 0)
    if (restartf > 3)
      disp('Failure of search direction to reduce ||f(x)||');
      resid = norm(feval(Rname,xold),inf);
      return;
    else % need to really do a restart
      nn = nn + 1;
      if (restartf == 1) % construct full hessian restart matrix
        % calculate approximate full Hessian for restart
        B = zeros(nx,nx);
        fx = zeros(nx,1);
        h2 = h*h;
        fnamex = Rxold'*Rxold;
        for jj = 1:nx
          dx = zeros(nx,1);
          dx(jj) = h;
          Rtmp = feval(Rname,xold+dx);
          fx(jj) = Rtmp'*Rtmp;
        end
        for jj = 1:nx
          for ii = 1:jj
            dx = zeros(nx,1);
            dx(ii) = h;
            dx(jj) = dx(jj)+h;
            Rtmp = feval(Rname,xold+dx);
            B(ii,jj) = (Rtmp'*Rtmp-fx(ii)-fx(jj)+fnamex)/h2;
          end
        end;
        B = inv(B + triu(B,1)');
        restartf = 2;
      else  % construct the identity restart
        B = eye(nx);
        restartf = 3;
      end
      continue;
    end
  end
  % can now assume search direction succeeded
  xnew = xold + lambda*s;
  % we need fpnew
  Rxnew = feval(Rname,xnew);
  Rpnew = jacob(Rname,xnew,h,nR);
  fpnew = Rpnew'*Rxnew;
  % now do a BFGS update
  s = xnew - xold;
  y = fpnew - fpold;
  p = y'*s;
  B = B - s*(y'*B)/p;
  B = B - (B*y)*s'/p + s*s'/p;
  restartf = 0;
  xold = xnew;
  Rxold = feval(Rname,xold);
  Rpold = jacob(Rname,xold,h,nR);
  fpold = Rpold'*Rxold;
  normfp = norm(fpold);
  resid = norm(Rxold); %degub
  % test for termination
  if (normfp <= stopr*normfp0 + stopa)
    break;
  end
  %storage(:,kk)=xold; %degub
  disp('kk is')
  kk
end
resid = norm(Rxold);

endfunction

%



