changeset 6476:11209d9a9e63 octave-forge

main/optim/inst/leasqr.m: only compute additional data if requested and if reliably possible; reject non-real data; accept row-vector for x
author i7tiol
date Mon, 21 Dec 2009 17:06:52 +0000
parents f2cad29de026
children ef9287add8a7
files main/optim/inst/leasqr.m
diffstat 1 files changed, 88 insertions(+), 59 deletions(-) [+]
line wrap: on
line diff
--- a/main/optim/inst/leasqr.m	Sun Dec 20 21:43:29 2009 +0000
+++ b/main/optim/inst/leasqr.m	Mon Dec 21 17:06:52 2009 +0000
@@ -25,22 +25,24 @@
   %%
   %% Version 3.beta
   %% Optional parameters are in braces {}.
-  %% x = column vector or matrix of independent variables, 1 row per
-  %%   observation: x = [x0 x1....xm].
-  %% y = column vector of observed values, same number of rows as x.
-  %% wt = column vector (dim=length(x)) of statistical weights.  These
-  %%   should be set to be proportional to (sqrt of var(y))^-1; (That is,
-  %%   the covariance matrix of the data is assumed to be proportional to
+  %% x = vector or matrix of independent variables, 1 entry or row per
+  %%   observation.
+  %% y = vector of observed values, same length as x or as number of
+  %%   rows of x.
+  %% wt = vector (dim=length(y)) of statistical weights.  These should
+  %%   be set to be proportional to (sqrt of var(y))^-1; (That is, the
+  %%   covariance matrix of the data is assumed to be proportional to
   %%   diagonal with diagonal equal to (wt.^2)^-1.  The constant of
   %%   proportionality will be estimated.); default = ones(length(y),1).
-  %% pin = column vec of initial parameters to be adjusted by leasqr.
+  %% pin = vec of initial parameters to be adjusted by leasqr.
   %% dp = fractional increment of p for numerical partial derivatives;
   %%   default = .001*ones(size(pin))
   %%   dp(j) > 0 means central differences on j-th parameter p(j).
   %%   dp(j) < 0 means one-sided differences on j-th parameter p(j).
   %%   dp(j) = 0 holds p(j) fixed i.e. leasqr wont change initial guess: pin(j)
-  %% F = name of function in quotes; the function shall be of the form y=f(x,p),
-  %%   with y, x, p of the form y, x, pin as described above.
+  %% F = name of function in quotes or function handle; the function
+  %%   shall be of the form y=f(x,p), with y, x, p of the form y, x, pin
+  %%   as described above; the returned y must be a column vector.
   %% dFdp = name of partial derivative function in quotes; default is 'dfdp', a
   %%   slow but general partial derivatives function; the function shall be
   %%   of the form prt=dfdp(x,f,p,dp,F[,bounds]). For backwards
@@ -88,9 +90,9 @@
   %% covr = diag(covariance matrix of the residuals).
   %% stdresid = standardized residuals.
   %% Z = matrix that defines confidence region (see comments in the source).
-  %% r2 = coefficient of multiple determination.
+  %% r2 = coefficient of multiple determination, intercept form.
   %%
-  %% All Zero guesses not acceptable
+  %% All Zero guesses not acceptable. Not suitable for non-real residuals.
 
   %% The following two blocks of comments are chiefly from the original
   %% version for Matlab. For later changes the logs of the Octave Forge
@@ -183,10 +185,11 @@
   %%
 
   y=y(:); wt=wt(:); pin=pin(:); dp=dp(:); %change all vectors to columns
+  if (isvector (x)) x = x(:); end
   %% check data vectors- same length?
-  m=length(y); n=length(pin); [m1,m2]=size(x);
-  if (m1~=m) 
-    error('input(x)/output(y) data must have same number of rows ')
+  m=length(y); n=length(pin);
+  if (size (x, 1) ~= m) 
+    error('input(x)/output(y) data must have same length ')
   end
 
   %% processing of 'options'
@@ -251,9 +254,9 @@
   p = pin;
   f=feval(F,x,p); fbest=f; pbest=p;
   r=wt.*(y-f);
-  ss=r'*r;
+  if (~isreal (r)) error ('weighted residuals are not real'); end
+  ss = r.' * r;
   sbest=ss;
-  nrm=zeros(n,1);
   chgprev=Inf*ones(n,1);
   cvg=0;
   epsLlast=1;
@@ -262,30 +265,23 @@
   %% do iterations
   %%
   for iter=1:niter
+    nrm = zeros (1, n);
     pprev=pbest;
     prt = eval (dfdp_cmd);
     r=wt.*(y-fbest);
+    if (~isreal (r)) error ('weighted residuals are not real'); end
     sprev=sbest;
     sgoal=(1-stol)*sprev;
-    for j=1:n
-      if (dp(j)==0)
-	nrm(j)=0;
-      else
-	prt(:,j)=wt.*prt(:,j);
-	nrm(j)=prt(:,j)'*prt(:,j);
-	if (nrm(j)>0)
-          nrm(j)=1/sqrt(nrm(j));
-	end
-      end
-      prt(:,j)=nrm(j)*prt(:,j);
-    end
-    %% above loop could ? be replaced by:
-    %% prt=prt.*wt(:,ones(1,n)); 
-    %% nrm=dp./sqrt(diag(prt'*prt)); 
-    %% prt=prt.*nrm(:,ones(1,m))';
+    msk = dp ~= 0;
+    prt(:, msk) = prt(:, msk) .* wt(:, ones (1, sum (msk)));
+    nrm(msk) = sumsq (prt(:, msk));
+    msk = nrm > 0;
+    nrm(msk) = 1 ./ sqrt (nrm(msk));
+    prt = prt .* nrm(ones (1, m), :);
+    nrm = nrm.';
     [prt,s,v]=svd(prt,0);
     s=diag(s);
-    g=prt'*r;
+    g = prt.' * r;
     for jjj=1:length(epstab),
       epsL = max(epsLlast*epstab(jjj),1e-7);
       se=sqrt((s.*s)+epsL);
@@ -296,7 +292,7 @@
       idx = ~isinf(maxstep);
       limit = abs(maxstep(idx).*pprev(idx));
       chg(idx) = min(max(chg(idx),-limit),limit);
-      if (verbose & any(ochg ~= chg))
+      if (verbose && any(ochg ~= chg))
 	disp(['Change in parameter(s): ', ...
               sprintf('%d ',find(ochg ~= chg)), 'were constrained']);
       end
@@ -321,7 +317,8 @@
 	%%
 	f=feval(F,x,p);
 	r=wt.*(y-f);
-	ss=r'*r;
+	if (~isreal (r)) error ('weighted residuals are not real'); end
+	ss = r.' * r;
 	if (ss<sbest)
           pbest=p;
           fbest=f;
@@ -341,7 +338,7 @@
     end
     aprec=abs(pprec.*pbest);
     %% [aprec, chg, chgprev]
-    if (all(abs(chg) < aprec) & all(abs(chgprev) < aprec))
+    if (all(abs(chg) < aprec) && all(abs(chgprev) < aprec))
       cvg=1;
       if (verbose)
 	fprintf('Parameter changes converged to specified precision\n');
@@ -363,6 +360,8 @@
   cvg=((sbest>sgoal)|(sbest<=eps)|cvg);
   if (cvg ~= 1) disp(' CONVERGENCE NOT ACHIEVED! '); end
 
+  if (~(verbose || nargout > 4)) return; end
+
   %% CALC VARIANCE COV MATRIX AND CORRELATION MATRIX OF PARAMETERS
   %% re-evaluate the Jacobian at optimal values
   jac = eval (dfdp_cmd);
@@ -384,58 +383,88 @@
     Qinv = diag (tp);
   end
   resid=y-f;                                    %un-weighted residuals
-  tp = resid' * Qinv * resid;
+  if (~isreal (r)) error ('residuals are not real'); end
+  tp = resid.' * Qinv * resid;
   covr = (tp / m) * Q;    %covariance of residuals
 
-  Qinv = ((m - n) / tp) * Qinv; % Qinv now contains the inverse of the
-				% guessed covariance matrix of the data.
-				% No new variable was used, but later
-				% calculations and comments using Qinv
-				% remain valid with the new Qinv.
-  %% argument of inv may be singular
-  covp = inv (jac' * Qinv * jac); % simplified Eq. 7-5-13, Bard %cov of
-				% parm est
-  d=sqrt(diag(covp));
-  corp=covp./(d*d');
+  %% Matlab compatibility and avoiding recomputation make the following
+  %% logic clumsy.
+  compute = 1;
+  if (m <= n)
+    compute = 0;
+  else
+    Qinv = ((m - n) / tp) * Qinv;
+    %% simplified Eq. 7-5-13, Bard; cov of parm est, inverse; outer
+    %% parantheses contain inverse of guessed covariance matrix of data
+    covpinv = jac.' * Qinv * jac;
+    if (exist ('rcond') && rcond (covpinv) <= eps)
+      compute = 0;
+    elseif (rank (covpinv) < n)
+      %% above test is not equivalent to 'rcond' and may unnecessarily
+      %% reject some matrices
+      compute = 0;
+    end
+  end
+  if (compute)
+    covp = inv (covpinv);
+    d=sqrt(diag(covp));
+    corp = covp ./ (d * d.');
+  else
+    covp = NA * ones (n);
+    corp = covp;
+  end
 
   if (exist('sparse'))
     covr=spdiags(covr,0);
-    stdresid=resid ./ sqrt (covr);
   else
     covr=diag(covr);                 % convert returned values to
 				% compact storage
-    stdresid=resid ./ sqrt (covr);
   end
-  Z=((m-n)*jac'*Qinv*jac)/(n*resid'*Qinv*resid);
+  stdresid = resid .* abs (wt) / sqrt (tp / m); % equivalent to resid ./
+				% sqrt (covr)
+
+  if (~(verbose || nargout > 8)) return; end
+
+  if (m > n)
+    Z = ((m - n) / (n * resid.' * Qinv * resid)) * covpinv;
+  else
+    Z = NA * ones (n);
+  end
 
 %%% alt. est. of cov. mat. of parm.:(Delforge, Circulation, 82:1494-1504, 1990
   %%disp('Alternate estimate of cov. of param. est.')
   %%acovp=resid'*Qinv*resid/(m-n)*inv(jac'*Qinv*jac);
 
-  %%Calculate R^2 (Ref Draper & Smith p.46)
+  if (~(verbose || nargout > 9)) return; end
+
+  %%Calculate R^2, intercept form
   %%
-  r=corrcoef([y(:),f(:)]);
-  r2=r(1,2).^2;
+  tp = sumsq (y - mean (y));
+  if (tp > 0)
+    r2 = 1 - sumsq (resid) / tp;
+  else
+    r2 = NA;
+  end
 
   %% if someone has asked for it, let them have it
   %%
   if (verbose)
     eval(plotcmd);
     disp(' Least Squares Estimates of Parameters')
-    disp(p')
+    disp(p.')
     disp(' Correlation matrix of parameters estimated')
     disp(corp)
     disp(' Covariance matrix of Residuals' )
     disp(covr)
     disp(' Correlation Coefficient R^2')
     disp(r2)
-    sprintf(' 95%% conf region: F(0.05)(%.0f,%.0f)>= delta_pvec''*Z*delta_pvec',n,m-n)
+    sprintf(' 95%% conf region: F(0.05)(%.0f,%.0f)>= delta_pvec.''*Z*delta_pvec',n,m-n)
     Z
     %% runs test according to Bard. p 201.
-    n1 = sum((f-y) < 0);
-    n2 = sum((f-y) > 0);
-    nrun=sum(abs(diff((f-y)<0)))+1;
-    if ((n1>10)&(n2>10)) % sufficent data for test?
+    n1 = sum (resid > 0);
+    n2 = sum (resid < 0);
+    nrun=sum(abs(diff(resid > 0)))+1;
+    if ((n1 > 10) && (n2 > 10)) % sufficent data for test?
       zed=(nrun-(2*n1*n2/(n1+n2)+1)+0.5)/(2*n1*n2*(2*n1*n2-n1-n2)...
         /((n1+n2)^2*(n1+n2-1)));
       if (zed < 0)