www.gusucode.com > funfun工具箱matlab源码程序 > funfun/private/integralCalc.m
function [q,errbnd] = integralCalc(fun,a,b,opstruct) %INTEGRALCALC Perform INTEGRAL calculation. % Based on "quadva" by Lawrence F. Shampine. % Ref: L.F. Shampine, "Vectorized Adaptive Quadrature in Matlab", % Journal of Computational and Applied Mathematics 211, 2008, pp.131-140 % % Strong algebraic singularities are handled with a technique from % L.F. Shampine, "Weighted Quadrature by Change of Variable", Neural, % Parallel, and Scientific Computations 18, 2010, pp. 195-206. % % Copyright 2007-2015 The MathWorks, Inc. % Variable names in all caps are referenced in nested functions. % Fixed parameters. DEFAULT_MAXINTERVALCOUNT = 16384; % 2^14 % Unpackage options. rule = opstruct.Rule; NODES = rule.Nodes(:); NNODES = numel(NODES); WT = rule.HighWeights; EWT = WT - rule.LowWeights; ATOL = opstruct.AbsTol; RTOL = opstruct.RelTol; INITIALINTERVALCOUNT = opstruct.InitialIntervalCount; ARRAYVALUED = opstruct.ArrayValued; MAXINTERVALCOUNT = ceil(DEFAULT_MAXINTERVALCOUNT*opstruct.Persistence); waypoints = opstruct.Waypoints(:).'; FUN = fun; if ~(isreal(a) && isreal(b) && isreal(waypoints)) % Handle contour integration. tinterval = [a,waypoints,b]; if any(~isfinite(tinterval)) error(message('MATLAB:integral:nonFiniteContourError')); end A = a; B = b; [q,errbnd] = vadapt(@identityTransform,tinterval); else % Define A and B and note the direction of integration on real axis. if b < a % Integrate left to right and change sign at the end. reversedir = true; A = b; B = a; else reversedir = false; A = a; B = b; end % Trim and sort the waypoints vector. waypoints = sort(waypoints(waypoints>A & waypoints<B)); % Construct interval vector with waypoints. interval = [A,waypoints,B]; % Extract A and B from interval vector to regularize possible mixed % single/double inputs. A = interval(1); B = interval(end); ZERO = zeros(class(interval)); ONE = ones(class(interval)); % Identify the task and perform the integration. if A == B % Handles both finite and infinite cases. % Return zero or nan of the appropriate class. q = midpArea(FUN,A,B); errbnd = q; elseif isfinite(A) && isfinite(B) if numel(interval) > 2 % Analytical transformation suggested by K.L. Metlov: alpha = 2*sin( asin((A + B - 2*interval(2:end-1))/(A - B))/3 ); interval = [-ONE,alpha,ONE]; else interval = [-ONE,ONE]; end [q,errbnd] = vadapt(@AtoBInvTransform,interval); elseif isfinite(A) && isinf(B) if numel(interval) > 2 alpha = sqrt(interval(2:end-1) - A); interval = [ZERO,alpha./(ONE+alpha),ONE]; else interval = [ZERO,ONE]; end [q,errbnd] = vadapt(@AToInfInvTransform,interval); elseif isinf(A) && isfinite(B) if numel(interval) > 2 alpha = sqrt(B - interval(2:end-1)); interval = [-ONE,-alpha./(ONE+alpha),ZERO]; else interval = [-ONE,ZERO]; end [q,errbnd] = vadapt(@minusInfToBInvTransform,interval); elseif isinf(A) && isinf(B) if numel(interval) > 2 % Analytical transformation suggested by K.L. Metlov: alpha = tanh( asinh(2*interval(2:end-1))/2 ); interval = [-ONE,alpha,ONE]; else interval = [-ONE,ONE]; end if isa(A,'single') || isa(B,'single') interval = single(interval); end [q,errbnd] = vadapt(@minusInfToInfInvTransform,interval); else % i.e., if isnan(a) || isnan(b) q = midpArea(FUN,A,B); errbnd = q; end % Account for integration direction. if reversedir q = -q; end end %-------------------------------------------------------------------------- function [q,errbnd] = vadapt(u,tinterval) % Iterative routine to perform the integration. % Compute the path length and split tinterval if needed. [tinterval,pathlen] = split(tinterval,INITIALINTERVALCOUNT); if pathlen == 0 % Test case: integral(@(x)x,1+1i,1+1i); q = midpArea(FUN,A,B); errbnd = q; return end % Set MAXINTERVALCOUNT high enough that each initial subinterval % can be refined at least once. MAXINTERVALCOUNT = max(MAXINTERVALCOUNT,2*(numel(tinterval)-1)); if ARRAYVALUED [q,errbnd] = iterateArrayValued(u,tinterval,pathlen); else [q,errbnd] = iterateScalarValued(u,tinterval,pathlen); end end % vadapt %-------------------------------------------------------------------------- function [q,errbnd] = iterateArrayValued(u,tinterval,pathlen) firstFunEval = true; % Initialize array of subintervals of [a,b]. subs = [tinterval(1:end-1);tinterval(2:end)]; nsubs = size(subs,2); % Initialize main loop while true % SUBS contains subintervals of [a,b] where the integral is not % sufficiently accurate. The first row of SUBS holds the left % end points and the second row, the corresponding right % endpoints. midpt = sum(subs)/2; % midpoints of the subintervals halfh = diff(subs)/2; % half the lengths of the subintervals x = NODES*halfh + midpt; % NNODES x nsubs if firstFunEval % Unwrap the first function evaluation to get size and % class information. [t,w] = u(x(:,1)); fxj = FUN(t(1)).*w(1); nfx = numel(fxj); outsize = size(fxj); outcls = superiorfloat(x,fxj); finalInputChecks(x,fxj); % Define initializers. if issparse(fxj) zeros_nfx_by_nsubs = sparse(nfx,nsubs); zeros_outsize = sparse(outsize(1),outsize(2)); zeros_nfx_by_1 = sparse(nfx,1); else zeros_nfx_by_nsubs = zeros([nfx,nsubs],outcls); zeros_outsize = zeros(outsize,outcls); zeros_nfx_by_1 = zeros(nfx,1,outcls); end qsubs = zeros_nfx_by_nsubs; errsubs = zeros_nfx_by_nsubs; qsubsk = fxj*WT(1); errsubsk = fxj*EWT(1); % Finish up the first subinterval. for j = 2:NNODES fxj = FUN(t(j)).*w(j); qsubsk = qsubsk + fxj*WT(j); errsubsk = errsubsk + fxj*EWT(j); end qsubsk = qsubsk.*halfh(1); errsubsk = errsubsk.*halfh(1); q = qsubsk; qsubs(:,1) = qsubsk(:); errsubs(:,1) = errsubsk(:); % Now process the remaining subintervals. We don't check % the mesh spacing on the first iteration. for k = 2:nsubs qsubsk = zeros_outsize; errsubsk = zeros_outsize; [t,w] = u(x(:,k)); for j = 1:NNODES fxj = FUN(t(j)).*w(j); qsubsk = qsubsk + fxj*WT(j); errsubsk = errsubsk + fxj*EWT(j); end qsubsk = qsubsk.*halfh(k); errsubsk = errsubsk.*halfh(k); q = q + qsubsk; qsubs(:,k) = qsubsk(:); errsubs(:,k) = errsubsk(:); end firstFunEval = false; % Initialize partial sums. q_ok = zeros_nfx_by_1; err_ok = zeros_nfx_by_1; else qsubs = zeros_nfx_by_nsubs; errsubs = zeros_nfx_by_nsubs; q = reshape(q_ok,outsize); for k = 1:nsubs qsubsk = zeros_outsize; errsubsk = zeros_outsize; [t,w] = u(x(:,k)); too_close = checkSpacing(t); if too_close break end for j = 1:NNODES fxj = FUN(t(j)).*w(j); qsubsk = qsubsk + fxj*WT(j); errsubsk = errsubsk + fxj*EWT(j); end qsubsk = qsubsk.*halfh(k); errsubsk = errsubsk.*halfh(k); q = q + qsubsk; qsubs(:,k) = qsubsk(:); errsubs(:,k) = errsubsk(:); end if too_close break end end % Locate subintervals where the approximate integrals are % sufficiently accurate and use them to update the partial % error sum. nleft = 0; tol = RTOL*abs(q(:)); tolr = 2*tol/pathlen; tola = 2*ATOL/pathlen; err_not_ok = zeros_nfx_by_1; for k = 1:nsubs abshalfhk = abs(halfh(k)); abserrsubsk = abs(errsubs(:,k)); if all(abserrsubsk <= tolr*abshalfhk | ... abserrsubsk <= tola*abshalfhk) q_ok = q_ok + qsubs(:,k); err_ok = err_ok + errsubs(:,k); else err_not_ok = err_not_ok + abserrsubsk; nleft = nleft + 1; subs(:,nleft) = subs(:,k); midpt(nleft) = midpt(k); end end subs = subs(:,1:nleft); % Trim subs array. midpt = midpt(1:nleft); % Trim midpt array. % The approximate error bound is constructed by adding the % approximate error bounds for the subintervals with accurate % approximations to the 1-norm of the approximate error bounds % for the remaining subintervals. This guards against % excessive cancellation of the errors of the remaining % subintervals. errbnd = reshape(abs(err_ok) + err_not_ok, outsize); % Check for nonfinites. if ~all(isfinite(q(:)) & isfinite(errbnd(:))) warning(message('MATLAB:integral:NonFiniteValue')); if opstruct.ThrowOnFail error(message('MATLAB:integral:unsuccessful')); end break end % Test for convergence. if nleft < 1 || all(errbnd(:) <= tol | errbnd(:) <= ATOL) break end % Split the remaining subintervals in half. Quit if splitting % results in too many subintervals. nsubs = 2*nleft; if nsubs > MAXINTERVALCOUNT % newPersistence = ceil(nsubs/DEFAULT_MAXINTERVALCOUNT); warning(message('MATLAB:integral:MaxIntervalCountReached', ... sprintf('%9.1e',max(errbnd(:))))); if opstruct.ThrowOnFail error(message('MATLAB:integral:unsuccessful')); end break end subs = reshape([subs(1,:); midpt; midpt; subs(2,:)],2,[]); end end %-------------------------------------------------------------------------- function [q,errbnd] = iterateScalarValued(u,tinterval,pathlen) firstFunEval = true; % Initialize array of subintervals of [a,b]. subs = [tinterval(1:end-1);tinterval(2:end)]; % Initialize partial sums. q_ok = 0; err_ok = 0; % Initialize main loop while true % SUBS contains subintervals of [a,b] where the integral is not % sufficiently accurate. The first row of SUBS holds the left % end points and the second row, the corresponding right % endpoints. midpt = sum(subs)/2; % midpoints of the subintervals halfh = diff(subs)/2; % half the lengths of the subintervals x = NODES*halfh + midpt; % NNODES x nsubs x = reshape(x,1,[]); [t,w] = u(x); % Transform back to the original domain. if firstFunEval fx = FUN(t); finalInputChecks(x,fx); fx = fx.*w; firstFunEval = false; else too_close = checkSpacing(t); if too_close break end fx = FUN(t).*w; end fx = reshape(fx,numel(WT),[]); % Quantities for subintervals. qsubs = (WT*fx) .* halfh; errsubs = (EWT*fx) .* halfh; % Calculate current values of q and tol. q = sum(qsubs) + q_ok; tol = max(ATOL,RTOL*abs(q)); % Locate subintervals where the approximate integrals are % sufficiently accurate and use them to update the partial % error sum. ndx = find(abs(errsubs) <= (2*tol/pathlen)*abs(halfh)); err_ok = err_ok + sum(errsubs(ndx)); % Remove errsubs entries for subintervals with accurate % approximations. errsubs(ndx) = []; % The approximate error bound is constructed by adding the % approximate error bounds for the subintervals with accurate % approximations to the 1-norm of the approximate error bounds % for the remaining subintervals. This guards against % excessive cancellation of the errors of the remaining % subintervals. errbnd = abs(err_ok) + norm(errsubs,1); % Check for nonfinites. if ~(isfinite(q) && isfinite(errbnd)) warning(message('MATLAB:integral:NonFiniteValue')); if opstruct.ThrowOnFail error(message('MATLAB:integral:unsuccessful')); end break end % Test for convergence. if errbnd <= tol break end % Remove subintervals with accurate approximations. subs(:,ndx) = []; % Update the partial sum for the integral. q_ok = q_ok + sum(qsubs(ndx)); midpt(ndx) = []; % Remove unneeded midpoints. if isempty(subs) break end % Split the remaining subintervals in half. Quit if splitting % results in too many subintervals. nsubs = 2*size(subs,2); if nsubs > MAXINTERVALCOUNT % newPersistence = ceil(nsubs/DEFAULT_MAXINTERVALCOUNT); warning(message('MATLAB:integral:MaxIntervalCountReached', ... sprintf('%9.1e',errbnd))); if opstruct.ThrowOnFail error(message('MATLAB:integral:unsuccessful')); end break end subs = reshape([subs(1,:); midpt; midpt; subs(2,:)],2,[]); end end %-------------------------------------------------------------------------- function q = midpArea(f,a,b) % Return q = (b-a)*f((a+b)/2). Although formally correct as a low % order quadrature formula, this function is only used to return % nan or zero of the appropriate class when a == b, isnan(a), or % isnan(b). x = (a + b)/2; if isfinite(a) && isfinite(b) && ~isfinite(x) % Treat overflow, e.g. when finite a and b > realmax/2 x = a/2 + b/2; end fx = f(x); if ~all(isfinite(fx(:))) warning(message('MATLAB:integral:NonFiniteValue')); if opstruct.ThrowOnFail error(message('MATLAB:integral:unsuccessful')); end end q = (b - a)*fx; end % midpArea %-------------------------------------------------------------------------- function [x,w] = identityTransform(t) x = t; w = ones(size(t),class(t)); end %-------------------------------------------------------------------------- function [x,w] = AtoBInvTransform(t) % Transform to weaken singularities at both ends: [a,b] -> [-1,1] x = 0.25*(B-A)*t.*(3 - t.^2) + 0.5*(B+A); w = 0.75*(B-A)*(1 - t.^2); end %-------------------------------------------------------------------------- function [x,w] = AToInfInvTransform(t) % Transform to weaken singularity at left end: [a,Inf) -> [0,Inf). % Then transform to finite interval: [0,Inf) -> [0,1]. tt = t ./ (1 - t); x = A + tt.^2; w = 2*tt ./ (1 - t).^2; end %-------------------------------------------------------------------------- function [x,w] = minusInfToBInvTransform(t) % Transform to weaken singularity at right end: (-Inf,b] -> (-Inf,b]. % Then transform to finite interval: (-Inf,b] -> (-1,0]. tt = t ./ (1 + t); x = B - tt.^2; w = -2*tt ./ (1 + t).^2; end %-------------------------------------------------------------------------- function [x,w] = minusInfToInfInvTransform(t) % Transform to finite interval: (-Inf,Inf) -> (-1,1). x = t ./ (1 - t.^2); w = (1 + t.^2) ./ (1 - t.^2).^2; end %-------------------------------------------------------------------------- function too_close = checkSpacing(x) ax = abs(x); tcidx = find(abs(diff(x)) <= 100*eps(class(x))* ... max(ax(1:end-1),ax(2:end)),1); too_close = ~isempty(tcidx); if too_close warning(message('MATLAB:integral:MinStepSize', ... num2str(x(tcidx),6))); if opstruct.ThrowOnFail error(message('MATLAB:integral:unsuccessful')); end end end % checkSpacing %-------------------------------------------------------------------------- function [x,pathlen] = split(x,minsubs) % Split subintervals in the interval vector X so that, to working % precision, no subinterval is longer than 1/MINSUBS times the % total path length. Removes subintervals of zero length, except % that the resulting X will always has at least two elements on % return, i.e., if the total path length is zero, X will be % collapsed into a single interval of zero length. Also returns % the integration path length. absdx = abs(diff(x)); if isreal(x) pathlen = x(end) - x(1); else pathlen = sum(absdx); end if minsubs > 1 if pathlen > 0 udelta = minsubs/pathlen; nnew = ceil(absdx*udelta) - 1; idxnew = find(nnew > 0); nnew = nnew(idxnew); for j = numel(idxnew):-1:1 k = idxnew(j); nnj = nnew(j); % Calculate new points. newpts = x(k) + (1:nnj)./(nnj+1)*(x(k+1)-x(k)); % Insert the new points. x = [x(1:k),newpts,x(k+1:end)]; end end % Remove useless subintervals. x(abs(diff(x))==0) = []; if isscalar(x) % Return at least two elements. x = [x(1),x(1)]; end end end % split %-------------------------------------------------------------------------- function finalInputChecks(x,fx) % Do final input validation with sample input and outputs to the % integrand function. % Check classes. if ~isfloat(fx) error(message('MATLAB:integral:UnsupportedClass',class(fx))); end % Check sizes. if ~ARRAYVALUED && ~isequal(size(x),size(fx)) error(message('MATLAB:integral:FxNotSameSizeAsX')); end outcls = superiorfloat(x,fx); outdbl = strcmp(outcls,'double'); % Make sure that RTOL >= 100*eps(outcls) except when using pure % absolute error control (ATOL>0 && RTOL==0). Obviously we're % assuming that the default tolerances are OK. if ~(ATOL > 0 && RTOL == 0) && RTOL < 100*eps(outcls) RTOL = 100*eps(outcls); end if outdbl % Single RTOL or ATOL should not force any single precision % computations. RTOL = double(RTOL); ATOL = double(ATOL); else WT = cast(WT,class(fx)); EWT = cast(EWT,class(fx)); NODES = cast(NODES,class(x)); end end % finalInputChecks %-------------------------------------------------------------------------- end % integralCalc