Mercurial > forge
view main/optim/inst/private/__lm_svd__.m @ 9930:d30cfca46e8a octave-forge
optim: upgrade license to GPLv3+ and mention on DESCRIPTION the other package licenses
author | carandraug |
---|---|
date | Fri, 30 Mar 2012 15:14:48 +0000 |
parents | da32bc7df365 |
children | fba8cdd5f9ad |
line wrap: on
line source
%% Copyright (C) 1992-1994 Richard Shrager %% Copyright (C) 1992-1994 Arthur Jutan %% Copyright (C) 1992-1994 Ray Muzic %% Copyright (C) 2010, 2011 Olaf Till <olaf.till@uni-jena.de> %% %% This program 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. %% %% This program 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 %% this program; if not, see <http://www.gnu.org/licenses/>. function [p, resid, cvg, outp] = __lm_svd__ (F, pin, hook) %% This is a backend for optimization. This code was originally %% contained in leasqr.m, which is now a frontend. %% some backend specific defaults fract_prec_default = 0; max_fract_step_default = Inf; %% needed for some anonymous functions if (exist ('ifelse') ~= 5) ifelse = @ scalar_ifelse; end %% passed constraints mc = hook.mc; % matrix of linear constraints vc = hook.vc; % vector of linear constraints f_cstr = hook.f_cstr; % function of all constraints df_cstr = hook.df_cstr; % function of derivatives of all constraints n_gencstr = hook.n_gencstr; % number of non-linear constraints eq_idx = hook.eq_idx; % logical index of equality constraints in all % constraints lbound = hook.lbound; % bounds, subset of linear inequality ubound = hook.ubound; % constraints in mc and vc %% passed values of constraints for initial parameters pin_cstr = hook.pin_cstr; %% passed return value of F for initial parameters f_pin = hook.f_pin; %% passed derivative of residual function dfdp = hook.dfdp; %% passed function for complementary pivoting cpiv = hook.cpiv; %% passed options maxstep = hook.max_fract_change; maxstep(isna (maxstep)) = max_fract_step_default; pprec = hook.fract_prec; pprec(isna (pprec)) = fract_prec_default; stol = hook.TolFun; niter = hook.MaxIter; if (isempty (niter)) niter = 20; end wt = hook.weights; fixed = hook.fixed; verbose = strcmp (hook.Display, 'iter'); %% only preliminary, for testing if (isfield (hook, 'testing')) testing = hook.testing; else testing = false; end if (isfield (hook, 'new_s')) new_s = hook.new_s; else new_s = false; end %% some useful variables derived from passed variables n_lcstr = size (vc, 1); have_constraints_except_bounds = ... n_lcstr + n_gencstr > ... sum (lbound ~= -Inf) + sum (ubound ~= Inf); n = length (pin); wtl = wt(:); nz = 20 * eps; % This is arbitrary. Constraint function will be % regarded as <= zero if less than nz. %% backend-specific checking of options and constraints if (have_constraints_except_bounds) if (any (pin_cstr.inequ.lin_except_bounds < 0) || ... (n_gencstr > 0 && any (pin_cstr.inequ.gen < 0))) warning ('initial parameters violate inequality constraints'); end if (any (abs (pin_cstr.equ.lin) >= nz) || ... (n_gencstr > 0 && any (abs (pin_cstr.equ.gen) >= nz))) warning ('initial parameters violate equality constraints'); end end idx = lbound == ubound; if (any (idx)) warning ('lower and upper bounds identical for some parameters, fixing the respective parameters'); fixed(idx) = true; end if (all (fixed)) error ('no free parameters'); end lidx = pin < lbound; uidx = pin > ubound; if (any (lidx | uidx) && have_constraints_except_bounds) warning ('initial parameters outside bounds, not corrected since other constraints are given'); else if (any (lidx)) warning ('some initial parameters set to lower bound'); pin(lidx, 1) = lbound(lidx, 1); end if (any (uidx)) warning ('some initial parameters set to upper bound'); pin(uidx, 1) = ubound(uidx, 1); end end if (n_gencstr > 0 && any (~isinf (maxstep))) warning ('setting both a maximum fractional step change of parameters and general constraints may result in inefficiency and failure'); end %% fill constant fields of hook for derivative-functions; some fields %% may be backend-specific dfdp_hook.fixed = fixed; % this may be handled by the frontend, but % the backend still may add to it %% set up for iterations %% p = pin; f = f_pin; fbest=f; pbest=p; m = prod (size (f)); r = wt .* f; r = r(:); if (~isreal (r)) error ('weighted residuals are not real'); end ss = r.' * r; sbest=ss; chgprev=Inf*ones(n,1); cvg=0; epsLlast=1; epstab=[.1, 1, 1e2, 1e4, 1e6]; ac_idx = true (n_lcstr + n_gencstr, 1); % all constraints nc_idx = false (n_lcstr + n_gencstr, 1); % non of all constraints gc_idx = cat (1, false (n_lcstr, 1), true (n_gencstr, 1)); % gen. constr. lc_idx = ~gc_idx; %% do iterations %% for iter = 1:niter deb_printf (testing, '\nstart outer iteration\n'); v_cstr = f_cstr (p, ac_idx); %% index of active constraints c_act = v_cstr < nz | eq_idx; # equality constraints might be # violated at start if (any (c_act)) if (n_gencstr > 0) dct = df_cstr (p, ac_idx, ... setfield (dfdp_hook, 'f', v_cstr)); dct(:, fixed) = 0; % for user supplied dfdp; necessary? dc = dct.'; dcat = dct(c_act, :); else dcat = df_cstr (p, c_act, ... setfield (dfdp_hook, 'f', v_cstr)); dcat(:, fixed) = 0; % for user supplied dfdp; necessary? end dca = dcat.'; end nrm = zeros (1, n); pprev=pbest; prt = dfdp (p, setfield (dfdp_hook, 'f', fbest(:))); prt(:, fixed) = 0; % for user supplied dfdp; necessary? r = wt .* -fbest; r = r(:); if (~isreal (r)) error ('weighted residuals are not real'); end sprev=sbest; sgoal=(1-stol)*sprev; msk = ~fixed; prt(:, msk) = prt(:, msk) .* wtl(:, ones (1, sum (msk))); nrm(msk) = sumsq (prt(:, msk), 1); 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; for jjj=1:length(epstab) deb_printf (testing, '\nstart inner iteration\n'); epsL = max(epsLlast*epstab(jjj),1e-7); %% printf ('epsL: %e\n', epsL); % for testing %% Usage of this 'ser' later is equivalent to pre-multiplying the %% gradient with a positive-definit matrix, but not with a %% diagonal matrix, at epsL -> Inf; so there is a fallback to %% gradient descent, but not in general to descent for each %% gradient component. Using the commented-out 'ser' ((1 / (1 + %% epsL^2)) * (1 ./ se + epsL * s)) would be equivalent to using %% Marquardts diagonal of the Hessian-approximation for epsL -> %% Inf, but currently this gives no advantages in tests, even with %% constraints. %%% ser = 1 ./ sqrt((s.*s)+epsL); se = sqrt ((s.*s) + epsL); if (new_s) %% for testing ser = (1 / (1 + epsL^2)) * (1 ./ se + epsL * s); else ser = 1 ./ se; end tp1 = (v * (g .* ser)) .* nrm; if (any (c_act)) deb_printf (testing, 'constraints are active:\n'); deb_printf (testing, '%i\n', c_act); %% calculate chg by 'quadratic programming' nrme= diag (nrm); ser2 = diag (ser .* ser); mfc1 = nrme * v * ser2 * v.' * nrme; tp2 = mfc1 * dca; a_eq_idx = eq_idx(c_act); [lb, bidx, ridx, tbl] = cpiv (dcat * tp1, dcat * tp2, a_eq_idx); chg = tp1 + tp2(:, bidx) * lb; % if a parameter is 'fixed', % the respective component of chg should % be zero too, even here (with active % constraints) deb_printf (testing, 'change:\n'); deb_printf (testing, '%e\n', chg); deb_printf (testing, '\n'); %% indices for different types of constraints c_inact = ~c_act; % inactive constraints c_binding = nc_idx; c_binding(c_act) = bidx; % constraints selected binding c_unbinding = nc_idx; c_unbinding(c_act) = ridx; % constraints unselected binding c_nonbinding = c_act & ~(c_binding | c_unbinding); % constraints % selected non-binding else %% chg is the Levenberg/Marquardt step chg = tp1; %% indices for different types of constraints c_inact = ac_idx; % inactive constraints consist of all % constraints c_binding = nc_idx; c_unbinding = nc_idx; c_nonbinding = nc_idx; end %% apply constraints to step width (since this is a %% Levenberg/Marquardt algorithm, no line-search is performed %% here) k = 1; c_tp = c_inact(1:n_lcstr); mcit = mc(:, c_tp).'; vci = vc(c_tp); hstep = mcit * chg; idx = hstep < 0; if (any (idx)) k = min (1, min (- (vci(idx) + mcit(idx, :) * pprev) ./ ... hstep(idx))); end if (k < 1) deb_printf (testing, 'stepwidth: linear constraints\n'); end if (n_gencstr > 0) c_tp = gc_idx & (c_nonbinding | c_inact); if (any (c_tp) && any (f_cstr (pprev + k * chg, c_tp) < 0)) [k, fval, info] = ... fzero (@ (x) min (cat (1, ... f_cstr (pprev + x * chg, c_tp), ... k - x, ... ifelse (x < 0, -Inf, Inf))), ... 0); if (info ~= 1 || abs (fval) >= nz) error ('could not find stepwidth to satisfy inactive and non-binding general inequality constraints'); end deb_printf (testing, 'general constraints limit stepwidth\n'); end end chg = k * chg; if (any (gc_idx & c_binding)) % none selected binding => % none unselected binding deb_printf (testing, 'general binding constraints must be regained:\n'); %% regain binding constraints and one of the possibly active %% previously inactive or non-binding constraints ptp1 = pprev + chg; tp = true; nt_nosuc = true; lim = 20; while (nt_nosuc && lim >= 0) deb_printf (testing, 'starting from new value of p in regaining:\n'); deb_printf (testing, '%e\n', ptp1); %% we keep d_p.' * inv (mfc1) * d_p minimal in each step of %% the inner loop; this is both sensible (this metric %% considers a guess of curvature of sum of squared residuals) %% and convenient (we have useful matrices available for it) c_tp0 = c_inact | c_nonbinding; c_tp1 = c_inact | (gc_idx & c_nonbinding); btbl = tbl(bidx, bidx); c_tp2 = c_binding; if (any (tp)) % if none before, does not get true again tp = f_cstr (ptp1, c_tp1) < nz; if (any (tp)) % could be less clumsy, but ml-compatibility.. %% keep only the first true entry in tp tp(tp) = logical (cat (1, 1, zeros (sum (tp) - 1, 1))); %% supplement binding index with one (the first) getting %% binding in c_tp1 c_tp2(c_tp1) = tp; %% gradient of this added constraint caddt = dct(c_tp2 & ~c_binding, :); cadd = caddt.'; C = dct(c_binding, :) * mfc1 * cadd; Ct = C.'; G = [btbl, btbl * C; ... -Ct * btbl, caddt * mfc1 * cadd - Ct * btbl * C]; btbl = gjp (G, size (G, 1)); end end dcbt = dct(c_tp2, :); mfc = - mfc1 * dcbt.' * btbl; deb_printf (testing, 'constraints to regain:\n'); deb_printf (testing, '%i\n', c_tp2); ptp2 = ptp1; nt_niter_start = 100; nt_niter = nt_niter_start; while (nt_nosuc && nt_niter >= 0) hv = f_cstr (ptp2, c_tp2); if (all (abs (hv) < nz)) nt_nosuc = false; chg = ptp2 - pprev; else ptp2 = ptp2 + mfc * hv; % step should be zero for each % component for which the parameter is % 'fixed' end nt_niter = nt_niter - 1; end deb_printf (testing, 'constraints after regaining:\n'); deb_printf (testing, '%e\n', hv); if (nt_nosuc || ... any (abs (chg) > abs (pprev .* maxstep)) || ... any (f_cstr (ptp2, c_tp0) < -nz)) if (nt_nosuc) deb_printf (testing, 'regaining did not converge\n'); else deb_printf (testing, 'regaining violated type 3 and 4\n'); end nt_nosuc = true; ptp1 = (pprev + ptp1) / 2; end if (~nt_nosuc) tp = f_cstr (ptp2, c_unbinding); if (any (tp) < 0) % again ml-compatibility clumsyness.. [discarded, id] = min(tp); tid = find (ridx); id = tid(id); % index within active constraints unsuccessful_exchange = false; if (abs (tbl(id, id)) < nz) % Bard: not absolute value %% exchange this unselected binding constraint against a %% binding constraint, but not against an equality %% constraint tbidx = bidx & ~a_eq_idx; if (~any (tbidx)) unsuccessful_exchange = true; else [discarded, idm] = max (abs (tbl(tbidx, id))); tid = find (tbidx); idm = tid(idm); % -> index within active constraints tbl = gjp (tbl, idm); bidx(idm) = false; ridx(idm) = true; end end if (unsuccessful_exchange) %% It probably doesn't look good now; this desperate %% last attempt is not in the original algortithm, since %% that didn't account for equality constraints. ptp1 = (pprev + ptp1) / 2; else tbl = gjp (tbl, id); bidx(id) = true; ridx(id) = false; c_binding = nc_idx; c_binding(c_act) = bidx; c_unbinding = nc_idx; c_unbinding(c_act) = ridx; end nt_nosuc = true; deb_printf (testing, 'regaining violated type 2\n'); end end if (~nt_nosuc) deb_printf (testing, 'regaining successful, converged with %i iterations:\n', ... nt_niter_start - nt_niter); deb_printf (testing, '%e\n', ptp2); end lim = lim - 1; end if (nt_nosuc) error ('could not regain binding constraints'); end else %% check the maximal stepwidth and apply as necessary ochg=chg; idx = ~isinf(maxstep); limit = abs(maxstep(idx).*pprev(idx)); chg(idx) = min(max(chg(idx),-limit),limit); if (verbose && any(ochg ~= chg)) disp(['Change in parameter(s): ', ... sprintf('%d ',find(ochg ~= chg)), 'maximal fractional stepwidth enforced']); end end aprec=abs(pprec.*pbest); %--- %% ss=scalar sum of squares=sum((wt.*f)^2). if (any(abs(chg) > 0.1*aprec))%--- % only worth evaluating % function if there is some non-miniscule % change p=chg+pprev; %% since the projection method may have slightly violated %% constraints due to inaccuracy, correct parameters to bounds %% --- but only if no further constraints are given, otherwise %% the inaccuracy in honoring them might increase by this if (~have_constraints_except_bounds) lidx = p < lbound; uidx = p > ubound; p(lidx, 1) = lbound(lidx, 1); p(uidx, 1) = ubound(uidx, 1); chg(lidx, 1) = p(lidx, 1) - pprev(lidx, 1); chg(uidx, 1) = p(uidx, 1) - pprev(uidx, 1); end %% f = F (p); r = wt .* f; r = r(:); if (~isreal (r)) error ('weighted residuals are not real'); end ss = r.' * r; deb_printf (testing, 'sbest: %.16e\n', sbest); deb_printf (testing, 'sgoal: %.16e\n', sgoal); deb_printf (testing, ' ss: %.16e\n', ss); if (ss<sbest) pbest=p; fbest=f; sbest=ss; end if (ss<=sgoal) break; end end %--- end %% printf ('epsL no.: %i\n', jjj); % for testing epsLlast = epsL; if (verbose) hook.plot_cmd (f); end if (ss < eps) % in this case ss == sbest cvg = 3; % there is no more suitable flag for this break; end if (ss>sgoal) cvg = 3; break; end aprec=abs(pprec.*pbest); %% [aprec, chg, chgprev] if (all(abs(chg) <= aprec) && all(abs(chgprev) <= aprec)) cvg = 2; if (verbose) fprintf('Parameter changes converged to specified precision\n'); end break; else chgprev=chg; end end %% set further return values %% p = pbest; resid = fbest; outp.niter = iter; function deb_printf (do_printf, varargin) %% for testing if (do_printf) printf (varargin{:}) end function fval = scalar_ifelse (cond, tval, fval) %% needed for some anonymous functions, builtin ifelse only available %% in Octave > 3.2; we need only the scalar case here if (cond) fval = tval; end