Skip to content

Commit be37955

Browse files
authored
Issue 519 (#532)
* better job with 519, but still not what is wanted... * more comments on #519 * did extensions get copied incorrectly? * version bump * extension testing * add extensions to test/Project.toml
1 parent bdb1409 commit be37955

File tree

7 files changed

+68
-19
lines changed

7 files changed

+68
-19
lines changed

Project.toml

+6-4
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,29 @@ name = "Polynomials"
22
uuid = "f27b6e38-b328-58d1-80ce-0feddd5e7a45"
33
license = "MIT"
44
author = "JuliaMath"
5-
version = "4.0.1"
5+
version = "4.0.2"
66

77
[deps]
8-
ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
98
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
109
MakieCore = "20f20a25-4f0e-4fdf-b5d1-57303727442b"
11-
MutableArithmetics = "d8a4904e-b15c-11e9-3269-09a3773c0cb0"
1210
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
1311
Setfield = "efcf1570-3423-57d1-acb7-fd33fddbac46"
1412

1513
[weakdeps]
1614
ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
15+
FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
1716
MakieCore = "20f20a25-4f0e-4fdf-b5d1-57303727442b"
1817
MutableArithmetics = "d8a4904e-b15c-11e9-3269-09a3773c0cb0"
1918

2019
[extensions]
2120
PolynomialsChainRulesCoreExt = "ChainRulesCore"
21+
PolynomialsFFTWExt = "FFTW"
2222
PolynomialsMakieCoreExt = "MakieCore"
2323
PolynomialsMutableArithmeticsExt = "MutableArithmetics"
2424

2525
[compat]
2626
ChainRulesCore = "1"
27+
FFTW = "1"
2728
MakieCore = "0.6"
2829
MutableArithmetics = "1"
2930
RecipesBase = "0.7, 0.8, 1"
@@ -35,6 +36,7 @@ Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
3536
ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
3637
ChainRulesTestUtils = "cdddcdb0-9152-4a09-a978-84456f9df70a"
3738
DualNumbers = "fa6b7ba4-c1ee-5f82-b5fc-ecf0adba8f74"
39+
FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
3840
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
3941
MakieCore = "20f20a25-4f0e-4fdf-b5d1-57303727442b"
4042
MutableArithmetics = "d8a4904e-b15c-11e9-3269-09a3773c0cb0"
@@ -44,4 +46,4 @@ SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
4446
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
4547

4648
[targets]
47-
test = ["Aqua", "ChainRulesCore", "DualNumbers", "LinearAlgebra", "SparseArrays", "OffsetArrays", "SpecialFunctions", "Test"]
49+
test = ["Aqua", "ChainRulesCore", "DualNumbers", "FFTW", "LinearAlgebra", "MakieCore", "MutableArithmetics", "SparseArrays", "OffsetArrays", "SpecialFunctions", "Test"]

ext/PolynomialsFFTWExt.jl

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
module PolynomialsFFTWExt
2+
3+
using Polynomials
4+
import Polynomials: MutableDensePolynomial, StandardBasis, Pad
5+
import FFTW
6+
import FFTW: fft, ifft
7+
function Polynomials.poly_multiplication_fft(p::P, q::Q) where {B <: StandardBasis,X,
8+
T <: AbstractFloat, P<:MutableDensePolynomial{B,T,X},
9+
S <: AbstractFloat, Q<:MutableDensePolynomial{B,S,X}}
10+
11+
N = 1 + degree(p) + degree(q)
12+
as = Pad(p.coeffs, N)
13+
bs = Pad(q.coeffs, N)
14+
us = fft(as)
15+
vs = fft(bs)
16+
cs = ifft(us .* vs)
17+
map!(real, cs, cs)
18+
MutableDensePolynomial{B, eltype(cs), X}(cs)
19+
20+
end
21+
22+
23+
24+
end

src/Polynomials.jl

-2
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,7 @@ include("legacy/misc.jl")
5353
include("legacy/Poly.jl")
5454

5555
if !isdefined(Base, :get_extension)
56-
include("../ext/PolynomialsChainRulesCoreExt.jl")
5756
include("../ext/PolynomialsMakieCoreExt.jl")
58-
include("../ext/PolynomialsMutableArithmeticsExt.jl")
5957
end
6058

6159
include("precompiles.jl")

src/polynomials/standard-basis/standard-basis.jl

+30-12
Original file line numberDiff line numberDiff line change
@@ -865,10 +865,31 @@ function practical_polynomial_composition(f::StandardBasisPolynomial, g::Standar
865865
end
866866

867867
## issue #519 polynomial multiplication via FFT
868+
##
869+
## This implements [Cooley-Tukey](https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm) radix-2
868870
## cf. http://www.cs.toronto.edu/~denisp/csc373/docs/tutorial3-adv-writeup.pdf
869-
## Compute recursive_fft
870-
## assumes length(as) = 2^k for some k
871-
## ωₙ is exp(-2pi*im/n) or Cyclotomics.E(n), the latter slower but non-lossy
871+
##
872+
## This is **much slower** than that of FFTW.jl (and more restrictive). However it does allow for exact computation
873+
## using `Cyclotomics.jl`, or with `Mods.jl`.
874+
## This assumes length(as) = 2^k for some k
875+
## ωₙ is an nth root of unity, for example `exp(-2pi*im/n)` (also available with `sincos(2pi/n)`) for floating point
876+
## or Cyclotomics.E(n), the latter much slower but non-lossy.
877+
##
878+
## Should implement NTT https://www.nayuki.io/page/number-theoretic-transform-integer-dft to close #519
879+
880+
struct Pad{T} <: AbstractVector{T}
881+
a::Vector{T}
882+
n::Int
883+
end
884+
Base.length(a::Pad) = a.n
885+
Base.size(a::Pad) = (a.n,)
886+
function Base.getindex(a::Pad, i)
887+
u = length(a.a)
888+
i u && return a.a[i]
889+
return zero(first(a.a))
890+
end
891+
892+
872893
function recursive_fft(as, ωₙ = nothing)
873894
n = length(as)
874895
N = 2^ceil(Int, log2(n))
@@ -886,7 +907,7 @@ function inverse_fft(as, ωₙ=nothing)
886907
recursive_fft(as, conj(ω)) / n
887908
end
888909

889-
# note: can write version for big coefficients, but still allocates a bit
910+
# note: could write version for big coefficients, but still allocates a bit
890911
function recursive_fft!(ys, as, ωₙ)
891912

892913
n = length(as)
@@ -915,22 +936,19 @@ end
915936

916937
# This *should* be faster -- (O(nlog(n)), but this version is definitely not so.
917938
# when `ωₙ = Cyclotomics.E` and T,S are integer, this can be exact
939+
# using `FFTW.jl` over `Float64` types is much better and is
940+
# implemented in an extension
918941
function poly_multiplication_fft(p::P, q::Q, ωₙ=nothing) where {T,P<:StandardBasisPolynomial{T},
919942
S,Q<:StandardBasisPolynomial{S}}
920943
as, bs = coeffs0(p), coeffs0(q)
921944
n = maximum(length, (as, bs))
922945
N = 2^ceil(Int, log2(n))
923-
924-
as′ = zeros(promote_type(T,S), 2N)
925-
copy!(view(as′, 1:length(as)), as)
946+
as′ = Pad(as, 2N)
947+
bs′ = Pad(bs, 2N)
926948

927949
ω = something(ωₙ, n -> exp(-2im*pi/n))(2N)
928950
âs = recursive_fft(as′, ω)
929-
930-
as′ .= 0
931-
copy!(view(as′, 1:length(bs)), bs)
932-
b̂s = recursive_fft(as′, ω)
933-
951+
b̂s = recursive_fft(bs′, ω)
934952
âb̂s = âs .* b̂s
935953

936954
PP = promote_type(P,Q)

test/Project.toml

+3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
[deps]
22
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
3+
ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
34
ChainRulesTestUtils = "cdddcdb0-9152-4a09-a978-84456f9df70a"
5+
FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
46
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
7+
MakieCore = "20f20a25-4f0e-4fdf-b5d1-57303727442b"
58
MutableArithmetics = "d8a4904e-b15c-11e9-3269-09a3773c0cb0"
69
OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
710
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"

test/runtests.jl

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ using OffsetArrays
1313
@testset "Rational functions" begin include("rational-functions.jl") end
1414
@testset "Poly, Pade (compatibility)" begin include("Poly.jl") end
1515
if VERSION >= v"1.9.0-"
16-
@testset "MutableArithmetics" begin include("mutable-arithmetics.jl") end
1716
@testset "Aqua" begin include("aqua.jl") end
17+
@testset "MutableArithmetics" begin include("mutable-arithmetics.jl") end
18+
@testset "Extensions" begin include("test-extensions.jl") end
1819
end

test/test-extensions.jl

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
using FFTW
2+
using MakieCore
3+
using ChainRulesCore

0 commit comments

Comments
 (0)