Skip to content

Commit 8dd3943

Browse files
Merge pull request #75 from JuliaApproximation/feat-libfasttransforms
Feat libfasttransforms
2 parents 8e71bff + 89d3e97 commit 8dd3943

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+1326
-5117
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
docs/build/
22
docs/site/
3-
.DS_Store
3+
deps/build.log
4+
deps/libfasttransforms.*

.travis.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,17 @@ language: julia
33
os:
44
- linux
55
- osx
6+
addons:
7+
apt:
8+
sources: ubuntu-toolchain-r-test
9+
packages: ['gcc-8', 'libblas-dev', 'libopenblas-base', 'libfftw3-dev', 'libmpfr-dev']
10+
homebrew:
11+
packages: ['gcc@8', 'fftw', 'mpfr']
12+
update: true
613
julia:
714
- 1.0
815
- 1.1
9-
- 1.2
16+
- 1.2
1017
- 1.3
1118
- nightly
1219
matrix:

Project.toml

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,24 @@
11
name = "FastTransforms"
22
uuid = "057dd010-8810-581a-b7be-e3fc3b93f78c"
3-
version = "0.6"
3+
version = "0.6.0"
44

55
[deps]
66
AbstractFFTs = "621f4979-c628-5d54-868e-fcf4e3e8185c"
7+
BinaryProvider = "b99e7846-7c00-51b0-8f62-c81ae34c0232"
78
DSP = "717857b8-e6f2-59f4-9121-6e50c889abd2"
8-
FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
99
FastGaussQuadrature = "442a2c76-b920-505d-bb47-c5924d526838"
10-
HierarchicalMatrices = "7c893195-952b-5c83-bb6e-be12f22eed51"
10+
FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
11+
Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
1112
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
12-
LowRankApprox = "898213cb-b102-5a47-900c-97e73b919f73"
13-
ProgressMeter = "92933f4c-e287-5a05-a399-4b506db050ca"
1413
SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
14+
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
1515
ToeplitzMatrices = "c751599d-da0a-543b-9d20-d0a503d91d24"
1616

1717
[compat]
18-
AbstractFFTs = "0.4"
19-
DSP = "0.6"
20-
FFTW = "0.3"
21-
HierarchicalMatrices = "0.2"
22-
LowRankApprox = "0.2.3"
23-
ProgressMeter = "1"
24-
SpecialFunctions = "0.8"
25-
ToeplitzMatrices = "0.6"
26-
FastGaussQuadrature = "0.4"
27-
julia = "1"
28-
29-
[extras]
30-
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
31-
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
32-
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
33-
34-
[targets]
35-
test = ["Test","Random","Statistics"]
18+
AbstractFFTs = "≥ 0.4"
19+
DSP = "≥ 0.6"
20+
FastGaussQuadrature = "≥ 0.4"
21+
FFTW = "≥ 0.3"
22+
SpecialFunctions = "≥ 0.8"
23+
ToeplitzMatrices = "≥ 0.6"
24+
julia = "≥ 1.0"

README.md

Lines changed: 59 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -4,71 +4,100 @@
44

55
`FastTransforms.jl` allows the user to conveniently work with orthogonal polynomials with degrees well into the millions.
66

7-
Transforms include conversion between Jacobi polynomial expansions, with Chebyshev, Legendre, and ultraspherical polynomial transforms as special cases. For the signal processor, all three types of nonuniform fast Fourier transforms available. As well, spherical harmonic transforms and transforms between orthogonal polynomials on the triangle allow for the efficient simulation of partial differential equations of evolution.
7+
This package provides a Julia wrapper for the [C library](https://github.com/MikaelSlevinsky/FastTransforms) of the same name. Additionally, all three types of nonuniform fast Fourier transforms available, as well as the Padua transform.
88

9-
Algorithms include methods based on asymptotic formulae to relate the transforms to a small number of fast Fourier transforms, matrix factorizations based on the Hadamard product, hierarchical matrix decompositions à la Fast Multipole Method, and the butterfly algorithm.
9+
## Installation
1010

11-
## The Chebyshev—Legendre Transform
12-
13-
The Chebyshev—Legendre transform allows the fast conversion of Chebyshev expansion coefficients to Legendre expansion coefficients and back.
11+
The build script, which works on macOS and Linux systems with x86_64 processors, downloads precompiled binaries of the latest version of [FastTransforms](https://github.com/MikaelSlevinsky/FastTransforms). This library depends on `FFTW`, `MPFR`, and `OpenBLAS` (on Linux). Therefore, installation may be as straightforward as:
1412

1513
```julia
16-
julia> Pkg.add("FastTransforms")
14+
julia> if Sys.isapple()
15+
run(`brew install gcc@8 fftw mpfr`)
16+
elseif Sys.islinux()
17+
run(`apt-get gcc-8 libblas-dev libopenblas-base libfftw3-dev libmpfr-dev`)
18+
end
19+
20+
pkg> build FastTransforms
1721

1822
julia> using FastTransforms
1923

20-
julia> c = rand(10001);
24+
```
25+
26+
## Fast orthogonal polynomial transforms
27+
28+
The 26 orthogonal polynomial transforms are listed in `FastTransforms.kind2string.(0:25)`. Univariate transforms may be planned with the standard normalization or with orthonormalization. For multivariate transforms, the standard normalization may be too severe for floating-point computations, so it is omitted. Here are two examples:
29+
30+
### The Chebyshev--Legendre transform
31+
32+
```julia
33+
julia> c = rand(8192);
2134

2235
julia> leg2cheb(c);
2336

2437
julia> cheb2leg(c);
2538

26-
julia> norm(cheb2leg(leg2cheb(c))-c)
27-
5.564168202018823e-13
39+
julia> norm(cheb2leg(leg2cheb(c; normcheb=true); normcheb=true)-c)/norm(c)
40+
1.1866591414786334e-14
41+
2842
```
2943

30-
The implementation separates pre-computation into a type of plan. This type is constructed with either `plan_leg2cheb` or `plan_cheb2leg`. Let's see how much faster it is if we pre-compute.
44+
The implementation separates pre-computation into an `FTPlan`. This type is constructed with either `plan_leg2cheb` or `plan_cheb2leg`. Let's see how much faster it is if we pre-compute.
3145

3246
```julia
3347
julia> p1 = plan_leg2cheb(c);
3448

3549
julia> p2 = plan_cheb2leg(c);
3650

3751
julia> @time leg2cheb(c);
38-
0.082615 seconds (11.94 k allocations: 31.214 MiB, 6.75% gc time)
52+
0.433938 seconds (9 allocations: 64.641 KiB)
3953

4054
julia> @time p1*c;
41-
0.004297 seconds (6 allocations: 78.422 KiB)
55+
0.007927 seconds (79 allocations: 68.563 KiB)
4256

4357
julia> @time cheb2leg(c);
44-
0.110388 seconds (11.94 k allocations: 31.214 MiB, 8.16% gc time)
58+
0.423865 seconds (9 allocations: 64.641 KiB)
4559

4660
julia> @time p2*c;
47-
0.004500 seconds (6 allocations: 78.422 KiB)
61+
0.009164 seconds (89 allocations: 69.672 KiB)
62+
63+
```
64+
65+
Furthermore, for orthogonal polynomial connection problems that are degree-preserving, we should expect to be able to apply the transforms in-place:
66+
67+
```julia
68+
julia> lmul!(p1, c);
69+
70+
julia> lmul!(p2, c);
71+
72+
julia> ldiv!(p1, c);
73+
74+
julia> ldiv!(p2, c);
75+
4876
```
4977

50-
## The Chebyshev—Jacobi Transform
78+
### The spherical harmonic transform
5179

52-
The Chebyshev—Jacobi transform allows the fast conversion of Chebyshev expansion coefficients to Jacobi expansion coefficients and back.
80+
Let `F` be an array of spherical harmonic expansion coefficients with columns arranged by increasing order in absolute value, alternating between negative and positive orders. Then `sph2fourier` converts the representation into a bivariate Fourier series, and `fourier2sph` converts it back. Once in a bivariate Fourier series on the sphere, `plan_sph_synthesis` converts the coefficients to function samples on an equiangular grid that does not sample the poles, and `plan_sph_analysis` converts them back.
5381

5482
```julia
55-
julia> c = rand(10001);
83+
julia> F = sphrandn(Float64, 1024, 2047); # convenience method
84+
85+
julia> P = plan_sph2fourier(F);
86+
87+
julia> PS = plan_sph_synthesis(F);
5688

57-
julia> @time norm(icjt(cjt(c, 0.1, -0.2), 0.1, -0.2) - c, Inf)
58-
0.258390 seconds (431 allocations: 6.278 MB)
59-
1.4830359162942841e-12
89+
julia> PA = plan_sph_analysis(F);
6090

61-
julia> p1 = plan_cjt(c, 0.1, -0.2);
91+
julia> G = PS*(P*F);
6292

63-
julia> p2 = plan_icjt(c, 0.1, -0.2);
93+
julia> H = P\(PA*G);
6494

65-
julia> @time norm(p2*(p1*c) - c, Inf)
66-
0.244842 seconds (17 allocations: 469.344 KB)
67-
1.4830359162942841e-12
95+
julia> norm(F-H)/norm(F)
96+
2.1541073345177038e-15
6897

6998
```
7099

71-
Composition of transforms allows the Jacobi—Jacobi transform, computed via `jjt`. The remainder in Hahn's asymptotic expansion is valid for the half-open square `(α,β) ∈ (-1/2,1/2]^2`. Therefore, the fast transform works best when the parameters are inside. If the parameters `(α,β)` are not exceptionally beyond the square, then increment/decrement operators are used with linear complexity (and linear conditioning) in the degree.
100+
Due to the structure of the spherical harmonic connection problem, these transforms may also be performed in-place with `lmul!` and `ldiv!`.
72101

73102
## Nonuniform fast Fourier transforms
74103

@@ -124,41 +153,12 @@ julia> N = div((n+1)*(n+2), 2);
124153

125154
julia> v = rand(N); # The length of v is the number of Padua points
126155

127-
julia> @time norm(ipaduatransform(paduatransform(v)) - v)
128-
0.006571 seconds (846 allocations: 1.746 MiB)
129-
3.123637691861415e-14
156+
julia> @time norm(ipaduatransform(paduatransform(v)) - v)/norm(v)
157+
0.007373 seconds (543 allocations: 1.733 MiB)
158+
3.925164683252905e-16
130159

131160
```
132161

133-
## The Spherical Harmonic Transform
134-
135-
Let `F` be a matrix of spherical harmonic expansion coefficients with columns arranged by increasing order in absolute value, alternating between negative and positive orders. Then `sph2fourier` converts the representation into a bivariate Fourier series, and `fourier2sph` converts it back.
136-
137-
```julia
138-
julia> F = sphrandn(Float64, 256, 256);
139-
140-
julia> G = sph2fourier(F);
141-
142-
julia> H = fourier2sph(G);
143-
144-
julia> norm(F-H)
145-
4.950645831278297e-14
146-
147-
julia> F = sphrandn(Float64, 1024, 1024);
148-
149-
julia> G = sph2fourier(F; sketch = :none);
150-
Pre-computing...100%|███████████████████████████████████████████| Time: 0:00:04
151-
152-
julia> H = fourier2sph(G; sketch = :none);
153-
Pre-computing...100%|███████████████████████████████████████████| Time: 0:00:04
154-
155-
julia> norm(F-H)
156-
1.1510623098225283e-12
157-
158-
```
159-
160-
As with other fast transforms, `plan_sph2fourier` saves effort by caching the pre-computation. Be warned that for dimensions larger than `1,000`, this is no small feat!
161-
162162
# References:
163163

164164
[1] B. Alpert and V. Rokhlin. <a href="http://dx.doi.org/10.1137/0912009">A fast algorithm for the evaluation of Legendre expansions</a>, *SIAM J. Sci. Stat. Comput.*, **12**:158—179, 1991.
@@ -171,7 +171,7 @@ As with other fast transforms, `plan_sph2fourier` saves effort by caching the pr
171171

172172
[5] R. M. Slevinsky. <a href="https://doi.org/10.1093/imanum/drw070">On the use of Hahn's asymptotic formula and stabilized recurrence for a fast, simple, and stable Chebyshev—Jacobi transform</a>, *IMA J. Numer. Anal.*, **38**:102—124, 2018.
173173

174-
[6] R. M. Slevinsky. <a href="https://doi.org/10.1016/j.acha.2017.11.001">Fast and backward stable transforms between spherical harmonic expansions and bivariate Fourier series</a>, in press at *Appl. Comput. Harmon. Anal.*, 2017.
174+
[6] R. M. Slevinsky. <a href="https://doi.org/10.1016/j.acha.2017.11.001">Fast and backward stable transforms between spherical harmonic expansions and bivariate Fourier series</a>, *Appl. Comput. Harmon. Anal.*, **47**:585—606, 2019.
175175

176176
[7] R. M. Slevinsky, <a href="https://arxiv.org/abs/1711.07866">Conquering the pre-computation in two-dimensional harmonic polynomial transforms</a>, arXiv:1711.07866, 2017.
177177

deps/build.jl

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using BinaryProvider
2+
3+
if Sys.isapple()
4+
const libfasttransforms = joinpath(dirname(@__DIR__), "deps", "libfasttransforms.dylib")
5+
GCC = BinaryProvider.detect_compiler_abi().gcc_version
6+
println("Building with ", GCC, ".")
7+
const release = "https://github.com/MikaelSlevinsky/FastTransforms/releases/download/v0.2.6/libfasttransforms.v0.2.6"
8+
if GCC == :gcc4
9+
download(release*".gcc-4.9.dylib", libfasttransforms)
10+
elseif GCC == :gcc5
11+
download(release*".gcc-5.dylib", libfasttransforms)
12+
elseif GCC == :gcc6
13+
download(release*".gcc-6.dylib", libfasttransforms)
14+
elseif GCC == :gcc7
15+
download(release*".gcc-7.dylib", libfasttransforms)
16+
elseif GCC == :gcc8
17+
download(release*".gcc-8.dylib", libfasttransforms)
18+
elseif GCC == :gcc9
19+
download(release*".gcc-9.dylib", libfasttransforms)
20+
else
21+
@warn "Please ensure you have a version of gcc from gcc-4.9 to gcc-9."
22+
end
23+
elseif Sys.islinux()
24+
const libfasttransforms = joinpath(dirname(@__DIR__), "deps", "libfasttransforms.so")
25+
if arch(platform_key_abi()) != :x86_64
26+
@warn "FastTransforms only has compiled binaries for x86_64 architectures."
27+
else
28+
GCC = BinaryProvider.detect_compiler_abi().gcc_version
29+
println("Building with ", GCC, ".")
30+
const release = "https://github.com/MikaelSlevinsky/FastTransforms/releases/download/v0.2.6/libfasttransforms.v0.2.6"
31+
if GCC == :gcc4
32+
download(release*".gcc-4.9.so", libfasttransforms)
33+
elseif GCC == :gcc5
34+
download(release*".gcc-5.so", libfasttransforms)
35+
elseif GCC == :gcc6
36+
download(release*".gcc-6.so", libfasttransforms)
37+
elseif GCC == :gcc7
38+
download(release*".gcc-7.so", libfasttransforms)
39+
elseif GCC == :gcc8
40+
download(release*".gcc-8.so", libfasttransforms)
41+
elseif GCC == :gcc9
42+
download(release*".gcc-9.so", libfasttransforms)
43+
else
44+
@warn "Please ensure you have a version of gcc from gcc-4.9 to gcc-9."
45+
end
46+
end
47+
else
48+
@warn "FastTransforms is not properly installed."
49+
end

examples/chebyshev.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ using FastTransforms
77

88
# first kind points -> first kind polynomials
99
n = 20
10-
p_1 = [sinpi((n-2k-1)/2n) for k=0:n-1]
10+
p_1 = chebyshevpoints(Float64, n; kind=1)
1111
f = exp.(p_1)
1212
= chebyshevtransform(f; kind=1)
1313

@@ -18,7 +18,7 @@ f̃(0.1) ≈ exp(0.1)
1818
ichebyshevtransform(f̌; kind=1) exp.(p_1)
1919

2020
# second kind points -> first kind polynomials
21-
p_2 = [cospi(k/(n-1)) for k=0:n-1]
21+
p_2 = chebyshevpoints(Float64, n; kind=2)
2222
f = exp.(p_2)
2323
= chebyshevtransform(f; kind=2)
2424

@@ -31,7 +31,7 @@ ichebyshevtransform(f̌; kind=2) ≈ exp.(p_2)
3131

3232
# first kind points -> second kind polynomials
3333
n = 20
34-
p_1 = [sinpi((n-2k-1)/2n) for k=0:n-1]
34+
p_1 = chebyshevpoints(Float64, n; kind=1)
3535
f = exp.(p_1)
3636
= chebyshevutransform(f; kind=1)
3737
= x -> [sin((k+1)*acos(x))/sin(acos(x)) for k=0:n-1]' *
@@ -42,7 +42,7 @@ ichebyshevutransform(f̌; kind=1) ≈ exp.(p_1)
4242

4343

4444
# second kind points -> second kind polynomials
45-
p_2 = [cospi(k/(n-1)) for k=1:n-2]
45+
p_2 = chebyshevpoints(Float64, n; kind=2)[2:n-1]
4646
f = exp.(p_2)
4747
= chebyshevutransform(f; kind=2)
4848
= x -> [sin((k+1)*acos(x))/sin(acos(x)) for k=0:n-3]' *

0 commit comments

Comments
 (0)