1function [ zerosave,w,reccalls ] = gengausslegquadrule( nmax,tol,numpoint,reccalls,maxrec)
2%
this function tested to work
for N < 300, larger values suffer from
3%underflow issues in the weights and exponential.
4%
this function generates a gauss Laguerre quadrature rule
for a given value n, it
5%does so by generating via reccursion a table of the Laguerre functions
6% (i.e. a Laguerre polynomial times the root of a weight function) in
7%the positive domain and then looking
for zeros via a change of sign and
8%interpolating (quadratically) between the two values (or finding an exact zero)
9%Using more points will give better accuracy, a warning
is output
if zeros
10%are missed due to lack of points
11%The function will then focus in on the zeros until a tolerance
is reached,
12%
this tolerance
is based on absolute uncertainty in each root
13%note such rules are used to approximate integrals of the form
14% int_{0}^{inf} dx f(x) exp(-x) to sum_{i=1}^N w_i*f(x_i)
15%recalls follows how many times the function has called itself
16%Author David Holdaway: year 2012
20 tol(1) = 1.0e-12; %
default
26 maxrec = 8; %maximum number of recalls to achieve convergence
28if tol(1) < 5*eps && reccalls ==0
29 warning(
'this position tolerance is of order eps, function may fail')
32 numpoint = 100*(nmax+5); %usually an adequate amount
35 zerosave=nan; w=nan; return
38 zerosave=0; w=1; return
40if length(numpoint) == 1 %initial course zero findings
41x = linspace(0,2*sqrt(nmax+1),numpoint); %needs lots of points for resolution
42x = x.^2; %want increasing spacing towards inf, reduced towards zero
43else %previous zeros fed back in
47%% generate Laguerre function table
48ltable = zeros(3,length(x)); %need to store three to generate
49ltable(1,:) = exp(-x/2);
50underflowchk = ltable(1,:)==0;
51if sum(underflowchk)~=0
52 warning('some x values cut off due to underflow, may still be fine or may cut off zeros')
53 %this
is a problem for large N
54 x = x(ltable(1,:)~=0); %cut x
55 ltable = zeros(3,length(x));
56 ltable(1,:) = exp(-x/2);
58ltable(2,:) = (1-x).*ltable(1,:);
60 %L_{n} = (1/n) [ (2n-1 - x) L_{n-1} - (n-1) L_{n-2} ]
61 ltable(3,:) = ((2*k-1 - x).*ltable(2,:) -(k-1).*ltable(1,:))/k;
62 ltable([1,2,3],:) = ltable([2,3,1],:); %permute to use again
73%% find zeros only in +ve
74zerosave = zeros(1,nmax);
75zcount = 0; zerosave2 = zerosave;
78 if sign(lfin(lp)) == 0 %found
"exact" zero!
80 zerosave(zcount) = x(lp);
81 zerosave2(zcount) = lp;
82 %skip loop an extra one
83 elseif sign(lfin(lp-1)) == 0
84 %skip
this as it will screw up next logic
85 elseif sign(lfin(lp-1)) ~= sign(lfin(lp))
86 %change of sign implies zero
88 %linearly interpolate between two x points to approximate zero
89 xpsave = x(lp)-x(lp-1); %spacing
90 root = xpsave*abs(lfin(lp-1))/abs(lfin(lp)-lfin(lp-1));
91 zerosave(zcount) = x(lp-1) + root;
92 zerosave2(zcount) = lp;
99 warning(
'missed zeros, doubling number of points')
101 x = linspace(0,nmax^2*20+4*(1+nmax),numpoint*2);
103 [ zerosave,w,reccalls ] = gengausslegquadrule( nmax,tol,x,reccalls,maxrec );
105 end %call again with
double number of points
106 warning('failed: not enough zeros found use more points or nmax too large')
108 w=nan; zerosave=zcount ;
112 warning('failed to distinguish zeros, tolerance likely set low, removing extra zeros')
114tmp = diff(sqrt(zerosave)) > 1e-5; %find repeated zeros.. low x has too much resolution for dp
117 zerosave = zerosave(tmp); %remove repeated
118 zerosave2 = zerosave2(tmp); %remove repeated
120 zerosave=zcount; w = nan; return %fail
125 for k=1:length(zerosave2)
126 xpsave(k) = x(zerosave2(k)) - x(zerosave2(k)-1);
128 converged = xpsave <= tol;
129 notconv = logical(1-converged);
130 convcond = sum(notconv) ==0;
132clear x %no longer required
134%% used to refine results by feeding back zero estimate
135if convcond==false %feed back x to function, create refined x range
137% finex = kron(zerosave,ones(1,9));
138% finex = sort([0,finex + kron(xpsave.*ones(1,length(zerosave)),...
139% [-1,-1/4,-1/16,-1/64,0,1/64,1/16,1/4,1])]);
141% finex = finex( diff(finex)>eps); %if these points are not distinct, remove them
143 tmp1 = kron(zerosave(notconv),ones(1,9)) + ...
144 kron(xpsave(notconv).*ones(1,sum(notconv)),...
145 [-1,-1/4,-1/16,-1/32,0,1/32,1/16,1/4,1]);
146 tmp2 = kron(zerosave(converged),ones(1,3)) + ...
147 kron(xpsave(converged).*ones(1,sum(converged)),...
148 [-1,0,1]); %converged so don't both refining much
149 finex = sort([0,tmp1,tmp2]);
151 reccalls = reccalls+1;
152 [ zerosave,w,reccalls ] = gengausslegquadrule( nmax,tol,finex,reccalls,maxrec );
155 warning('failed to converge to desired tolerence, after max reccursions')
156 bestachieved1 = max(xpsave)
159%% find weights if zeros to tolerance
160% find weights, regenerate legtable using zero positions only and no
162%w_i = x_i/(n+1)^2(L_{n+1}(x_i))^2
163ltable = zeros(nmax+2,length(zerosave));
164ltable(1,:) = ones(size(zerosave));
165ltable(2,:) = (1-zerosave).*ltable(1,:);
166for k = 2:nmax+1 %up to n+1th, note again indexes start from zero
167 %L_{n} = (1/n) [ (2n-1 - x) L_{n-1} - (n-1) L_{n-2} ]
168 ltable(k+1,:) = ((2*k-1 - zerosave).*ltable(k,:) -(k-1).*ltable(k-1,:))/k;
172for k = 1:length(zerosave)
173 w(k) = zerosave(k)./ltable(nmax+2,k).^2;
179% diffsave(k)=(sum(x.^k.*w)-factorial(k))/factorial(k);