forked from chebfun/chebfun
-
Notifications
You must be signed in to change notification settings - Fork 1
/
cheb2leg.m
182 lines (157 loc) · 6.19 KB
/
cheb2leg.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
function c_leg = cheb2leg(c_cheb, varargin)
%CHEB2LEG Convert Chebyshev coefficients to Legendre coefficients.
% C_LEG = CHEB2LEG(C_CHEB) converts the vector C_CHEB of Chebyshev
% coefficients to a vector C_LEG of Legendre coefficients such that
% C_CHEB(1)*T0 + ... + C_CHEB(N)*T{N-1} = ...
% C_LEG(1)*P0 + ... + C_LEG(N)*P{N-1},
% where P{k} is the degree k Legendre polynomial normalized so that
% max(|P{k}|) = 1.
%
% C_LEG = CHEB2LEG(C_CHEB, 'norm') is as above, but with the Legendre
% polynomials normalized to be orthonormal.
%
% If C_CHEB is a matrix then the CHEB2LEG operation is applied to each column.
%
% For N >= 513 the algorithm used is the one described in [1].
%
% References:
% [1] A. Townsend, M. Webb, and S. Olver, "Fast polynomial transforms based
% on Toeplitz and Hankel matrices", submitted, 2016.
%
% See also LEG2CHEB, CHEB2JAC, JAC2CHEB, JAC2JAC.
% Copyright 2017 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.
normalize = false; % Default - no normalize.
if ( any(strncmpi(varargin, 'normalize', 4)) )
normalize = true;
end
[N, n] = size(c_cheb);
if ( N < 2 )
% Trivial case:
c_leg = c_cheb;
elseif ( N < 513 ) % <-- determined experimentally
% Use direct approach:
c_leg = cheb2leg_direct( c_cheb, normalize );
else
% Use fast algorithm:
c_leg = cheb2leg_fast( c_cheb, normalize );
end
end
function c_leg = cheb2leg_fast(c_cheb, normalize)
% BRIEF IDEA:
% Let A be the upper-triangular conversion matrix. We observe that A can be
% decomposed as A = D1(T.*H)D2, where D1 and D2 are diagonal, T is Toeplitz,
% and H is a real, symmetric, positive definite Hankel matrix. The Hankel part
% can be approximated, up to an error of tol, by a rank O( log N log(1/tol) )
% matrix. A low rank approximation is constructed via pivoted Cholesky
% factorization.
%
% For more information on the algorithm, see Section 5.3 of [1].
[N, n] = size(c_cheb);
%%%%%%%%%%%%%%%%%%%%%% Initialise %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Evaluate the symbol of the Hankel part of M:
% This for-loop is a faster and more accurate way of doing:
% Lambda = @(z) exp(gammaln(z+1/2) - gammaln(z+1));
% vals = Lambda( (0:2*N-1)'/2 );)
vals = [sqrt(pi) ; 2/sqrt(pi) ; zeros(2*N-2,1)];
for i = 2:2:2*(N-1)
vals(i+1) = vals(i-1)*(1-1/i);
vals(i+2) = vals(i)*(1-1/(i+1));
end
% First row of cheb2leg matrix:
num = 0:N-1;
l1 = [1, 1, vals(1:N-2)'] ./ num;
l2 = [1, vals(1:N-1)'] ./ (num+1);
L_row1 = (-.5*num).*l1.*l2;
L_row1(2:2:end) = 0; L_row1(1) = 1;
%%%%%%%%%%%%%%%%%% Pivoted Cholesky algorithm %%%%%%%%%%%%%%%%%%%%%%%%%%
% Calculate quasi-SVD of hankel part by using Cholesky factorization.
%
% Find the numerical rank and pivot locations. This is equivalent to
% Cholesky factorization on the matrix A with diagonal pivoting, except
% here only the diagonal of the matrix is updated.
num = (1:N-1)';
% Diagonal of Hankel matrix:
d = vals(2*num).*(num.^2./(2*num+1));
pivotValues = []; % Store Cholesky pivots.
C = []; % Store Cholesky columns.
tol = 1e-14*log(N); % Tolerance of low rank approx.
[mx, idx] = max(d); % Max on diagonal (searching for first Cholesky pivot).
while ( mx > tol )
newCol = vals(idx+1:idx+N-1) .* (num.*num(idx) ./ (idx + num + 1));
if ( size(C, 2) > 0)
newCol = newCol - C*(C(idx,:).' .* pivotValues);
end
pivotValues = [pivotValues ; 1./mx]; %#ok<AGROW> % Append pivtoValues.
C = [C, newCol]; %#ok<AGROW> % Append newCol to C.
d = d - newCol.^2 ./ mx; % Update diagonal.
[mx, idx] = max(d); % Find next pivot.
end
sz = size(C, 2); % Numerical rank of H.
C = C * spdiags(sqrt(pivotValues), 0, sz, sz); % Share out scaling.
% Second row of Toeplitz part:
T_row2 = [0, 0, vals(1:N-3)'] ./ (num'-1);
T_row2([1, 2:2:end]) = 0;
% Diagonal part of the matrix:
d = .5*sqrt(pi) ./ vals(3:2:2*N);
%%%%%%%%%%%%%%%%%% Multiply D1(T.*H)D2 %%%%%%%%%%%%%%%%%%%%%%%%%%
% Initialise:
Z = zeros(N, 1);
c_leg = zeros(N, n);
scl = (num+1/2)./num;
a = fft( [Z ; T_row2(end:-1:2)'] );
if ( n == 1 )
tmp1 = bsxfun(@times, C, -c_cheb(2:end));
f1 = fft( tmp1, 2*N-2 );
tmp2 = bsxfun(@times, f1, a);
b = ifft( tmp2 );
b = b(1:N-1,:);
c_leg(2:end) = scl.*sum(C.*b, 2) + d.*c_cheb(2:end);
else
for k = 1:n
tmp1 = bsxfun(@times, C, -c_cheb(2:end,k));
f1 = fft( tmp1, 2*N-2, 1 );
tmp2 = bsxfun(@times, f1, a);
b = ifft( tmp2, [], 1 );
b = b(1:N-1,:);
c_leg(2:end,k) = sum(C.*b, 2);
end
c_leg(2:end,:) = bsxfun(@times, scl, c_leg(2:end,:)) + ...
bsxfun(@times, d, c_cheb(2:end,:));
end
c_leg(1,:) = L_row1 * c_cheb;
% Normalize:
if ( normalize ),
c_leg = bsxfun(@times, c_leg, 1./sqrt((0:N-1)'+.5) );
end
end
function c_leg = cheb2leg_direct(c_cheb, normalize)
%CHEB2LEG_DIRECT Convert Cheb to Leg coeffs using the 3-term recurrence.
[N, m] = size(c_cheb); % Number of columns.
N = N - 1; % Degree of polynomial.
x = cos(.5*pi*(0:2*N).'/N); % 2*N+1 Chebyshev grid (reversed order).
f = dct1([c_cheb ; zeros(N, m)]); % Values on 2*N+1 Chebyshev grid.
w = chebtech2.quadwts(2*N+1).'; % Clenshaw-Curtis quadrature weights.
Pm2 = 1; Pm1 = x; % Initialise.
L = zeros(2*N+1, N+1); % Vandermonde matrix.
L(:,1:2) = [1+0*x, x]; % P_0 and P_1.
for k = 1:N-1 % Recurrence relation:
P = (2-1/(k+1))*Pm1.*x - (1-1/(k+1))*Pm2;
Pm2 = Pm1; Pm1 = P;
L(:,2+k) = P;
end
scale = (2*(0:N).'+1)/2; % Scaling in coefficients [NIST, (18.3.1)].
c_leg = bsxfun(@times, L.'*(bsxfun(@times, f ,w)), scale); % Legendre coeffs.
% Normalize:
if ( normalize ),
c_leg = bsxfun(@times, c_leg, 1./sqrt((0:N)'+.5) );
end
end
function v = dct1(c)
%DCT1 Compute a (scaled) DCT of type 1 using the FFT.
% DCT1(C) returns T(X)*C, where X = cos(pi*(0:N)/N) and T(X) = [T_0, T_1, ...,
% T_N](X) (where T_k is the kth 1st-kind Chebyshev polynomial), and N =
% length(C) - 1;
c([1,end],:) = 2*c([1,end],:); % Scale.
v = chebfun.dct(c, 1); % DCT-I.
end