From 4d237b66dd736b4d987b05bca76dabe93437d133 Mon Sep 17 00:00:00 2001 From: Simone Carlo Surace Date: Fri, 6 Oct 2023 14:08:01 +0200 Subject: [PATCH 1/4] Stop using deprecated signatures from Distances.jl --- src/distances/pairwise.jl | 4 +- src/utils.jl | 8 +-- test.jl | 121 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 6 deletions(-) create mode 100644 test.jl diff --git a/src/distances/pairwise.jl b/src/distances/pairwise.jl index 8b5cb43e7..cb43ec041 100644 --- a/src/distances/pairwise.jl +++ b/src/distances/pairwise.jl @@ -21,13 +21,13 @@ function pairwise(d::PreMetric, x::AbstractVector{<:Real}, y::AbstractVector{<:R end function pairwise!(out::AbstractMatrix, d::PreMetric, x::AbstractVector{<:Real}) - return Distances.pairwise!(out, d, reshape(x, :, 1); dims=1) + return Distances.pairwise!(d, out, reshape(x, :, 1); dims=1) end function pairwise!( out::AbstractMatrix, d::PreMetric, x::AbstractVector{<:Real}, y::AbstractVector{<:Real} ) - return Distances.pairwise!(out, d, reshape(x, :, 1), reshape(y, :, 1); dims=1) + return Distances.pairwise!(d, out, reshape(x, :, 1), reshape(y, :, 1); dims=1) end # Also defines the colwise method for abstractvectors diff --git a/src/utils.jl b/src/utils.jl index 0942fa5f7..c0aa6f5cb 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -108,10 +108,10 @@ function pairwise(d::PreMetric, x::ColVecs, y::AbstractVector{<:AbstractVector{< return Distances_pairwise(d, x.X, reduce(hcat, y); dims=2) end function pairwise!(out::AbstractMatrix, d::PreMetric, x::ColVecs) - return Distances.pairwise!(out, d, x.X; dims=2) + return Distances.pairwise!(d, out, x.X; dims=2) end function pairwise!(out::AbstractMatrix, d::PreMetric, x::ColVecs, y::ColVecs) - return Distances.pairwise!(out, d, x.X, y.X; dims=2) + return Distances.pairwise!(d, out, x.X, y.X; dims=2) end """ @@ -179,10 +179,10 @@ function pairwise(d::PreMetric, x::RowVecs, y::AbstractVector{<:AbstractVector{< return Distances_pairwise(d, x.X, permutedims(reduce(hcat, y)); dims=1) end function pairwise!(out::AbstractMatrix, d::PreMetric, x::RowVecs) - return Distances.pairwise!(out, d, x.X; dims=1) + return Distances.pairwise!(d, out, x.X; dims=1) end function pairwise!(out::AbstractMatrix, d::PreMetric, x::RowVecs, y::RowVecs) - return Distances.pairwise!(out, d, x.X, y.X; dims=1) + return Distances.pairwise!(d, out, x.X, y.X; dims=1) end # Resolve ambiguity error for ColVecs vs RowVecs. #346 diff --git a/test.jl b/test.jl new file mode 100644 index 000000000..a42474558 --- /dev/null +++ b/test.jl @@ -0,0 +1,121 @@ +using TestEnv; TestEnv.activate() +using Distances: pairwise +using KernelFunctions: Sinus +using Zygote +using KernelFunctions.ChainRulesCore, Distances +using Test + +function _pairwise!(metric::Sinus, r::AbstractMatrix, a::AbstractMatrix) + require_one_based_indexing(r) + n = size(a, 2) + size(r) == (n, n) || throw(DimensionMismatch("Incorrect size of r.")) + @inbounds for j = 1:n + for i = 1:(j - 1) + r[i, j] = r[j, i] # leveraging the symmetry of SemiMetric + end + r[j, j] = zero(eltype(r)) + aj = view(a, :, j) + for i = (j + 1):n + s = sinpi.(view(a, :, i) - aj) ./ p + r[i, j] = dot(s, s) + end + end + r +end + + +function ChainRulesCore.rrule( + ::typeof(Distances.pairwise), + d::Sinus, + x::AbstractMatrix; + dims = 2 +) + project_x = ProjectTo(x) + function pairwise_pullback(Δ) + n = size(x, 2) + x̄ = zero(x) + @inbounds for j in 1:n, i in 1:n + xi = view(x, :, i) + xj = view(x, :, j) + ds = Δ[i, j] .* sinpi.(xi - xj) .* cospi.(xi - xj) ./ d.r + x̄[:, i] = ds + x̄[:, j] = -ds + end + NoTangent(), NoTangent(), x̄ + end + return Distances.pairwise(d, x), pairwise_pullback +end +Zygote.refresh() + +@testset "pairwise" begin + +@testset "Single argument, vector" begin + @testset "p = $p" for p in rand(5) + testfun1(x) = sum(pairwise(Sinus(p), x)) + testfun_simple1(x) = sum(abs2.(sinpi.(x .- x') ./ p)) + x1 = rand(100) + + z1 = testfun1(x1) + z_simple1 = testfun_simple1(x1) + + @test z1 ≈ z_simple1 + + g1 = Zygote.gradient(testfun1, x1) |> only + g_simple1 = Zygote.gradient(testfun_simple1, x1) |> only + + @test g1 ≈ g_simple1 + end +end + +@testset "Double argument, vector" begin + @testset "p = $p" for p in rand(5) + testfun2(x, y) = sum(pairwise(Sinus(p), x, y)) + testfun_simple2(x, y) = sum(abs2.(sinpi.(x .- y') ./ p)) + x2 = rand(150) + + z2 = testfun2(x2[1:100], x2[101:150]) + z_simple2 = testfun_simple2(x2[1:100], x2[101:150]) + + @test z2 ≈ z_simple2 + + g2 = Zygote.gradient(_x -> testfun2(_x[1:100], _x[101:150]), x2) |> only + g_simple2 = Zygote.gradient(_x -> testfun_simple2(_x[1:100], _x[101:150]), x2) |> only + + @test g2 ≈ g_simple2 + end +end + +@testset "Single argument, matrix, dims = 2" begin + @testset "p = $p" for p in [rand(2) for i in 1:5] + testfun3(x) = sum(pairwise(Sinus(p), x; dims=2)) + function testfun_simple3(a) + n = size(a, 2) + r = zeros(n, n) + @inbounds for j = 1:n + for i = 1:(j - 1) + r[i, j] = r[j, i] # leveraging the symmetry of SemiMetric + end + r[j, j] = zero(eltype(r)) + aj = view(a, :, j) + for i = (j + 1):n + s = sinpi.(view(a, :, i) - aj) ./ p + r[i, j] = dot(s, s) + end + end + sum(r) + end + x3 = rand(2, 100) + + z3 = testfun3(x3) + z_simple3 = testfun_simple3(x3) + + @test z3 ≈ z_simple3 + + g1 = Zygote.gradient(testfun3, x3) |> only + # g_simple1 = Zygote.gradient(testfun_simple3, x3) |> only + @show g1 + # @test g1 ≈ g_simple1 + end +end + +end From 5e089e741d687359972939a8c0130b8718a10127 Mon Sep 17 00:00:00 2001 From: Simone Carlo Surace Date: Fri, 6 Oct 2023 14:10:32 +0200 Subject: [PATCH 2/4] Delete file that is not needed --- test.jl | 121 -------------------------------------------------------- 1 file changed, 121 deletions(-) delete mode 100644 test.jl diff --git a/test.jl b/test.jl deleted file mode 100644 index a42474558..000000000 --- a/test.jl +++ /dev/null @@ -1,121 +0,0 @@ -using TestEnv; TestEnv.activate() -using Distances: pairwise -using KernelFunctions: Sinus -using Zygote -using KernelFunctions.ChainRulesCore, Distances -using Test - -function _pairwise!(metric::Sinus, r::AbstractMatrix, a::AbstractMatrix) - require_one_based_indexing(r) - n = size(a, 2) - size(r) == (n, n) || throw(DimensionMismatch("Incorrect size of r.")) - @inbounds for j = 1:n - for i = 1:(j - 1) - r[i, j] = r[j, i] # leveraging the symmetry of SemiMetric - end - r[j, j] = zero(eltype(r)) - aj = view(a, :, j) - for i = (j + 1):n - s = sinpi.(view(a, :, i) - aj) ./ p - r[i, j] = dot(s, s) - end - end - r -end - - -function ChainRulesCore.rrule( - ::typeof(Distances.pairwise), - d::Sinus, - x::AbstractMatrix; - dims = 2 -) - project_x = ProjectTo(x) - function pairwise_pullback(Δ) - n = size(x, 2) - x̄ = zero(x) - @inbounds for j in 1:n, i in 1:n - xi = view(x, :, i) - xj = view(x, :, j) - ds = Δ[i, j] .* sinpi.(xi - xj) .* cospi.(xi - xj) ./ d.r - x̄[:, i] = ds - x̄[:, j] = -ds - end - NoTangent(), NoTangent(), x̄ - end - return Distances.pairwise(d, x), pairwise_pullback -end -Zygote.refresh() - -@testset "pairwise" begin - -@testset "Single argument, vector" begin - @testset "p = $p" for p in rand(5) - testfun1(x) = sum(pairwise(Sinus(p), x)) - testfun_simple1(x) = sum(abs2.(sinpi.(x .- x') ./ p)) - x1 = rand(100) - - z1 = testfun1(x1) - z_simple1 = testfun_simple1(x1) - - @test z1 ≈ z_simple1 - - g1 = Zygote.gradient(testfun1, x1) |> only - g_simple1 = Zygote.gradient(testfun_simple1, x1) |> only - - @test g1 ≈ g_simple1 - end -end - -@testset "Double argument, vector" begin - @testset "p = $p" for p in rand(5) - testfun2(x, y) = sum(pairwise(Sinus(p), x, y)) - testfun_simple2(x, y) = sum(abs2.(sinpi.(x .- y') ./ p)) - x2 = rand(150) - - z2 = testfun2(x2[1:100], x2[101:150]) - z_simple2 = testfun_simple2(x2[1:100], x2[101:150]) - - @test z2 ≈ z_simple2 - - g2 = Zygote.gradient(_x -> testfun2(_x[1:100], _x[101:150]), x2) |> only - g_simple2 = Zygote.gradient(_x -> testfun_simple2(_x[1:100], _x[101:150]), x2) |> only - - @test g2 ≈ g_simple2 - end -end - -@testset "Single argument, matrix, dims = 2" begin - @testset "p = $p" for p in [rand(2) for i in 1:5] - testfun3(x) = sum(pairwise(Sinus(p), x; dims=2)) - function testfun_simple3(a) - n = size(a, 2) - r = zeros(n, n) - @inbounds for j = 1:n - for i = 1:(j - 1) - r[i, j] = r[j, i] # leveraging the symmetry of SemiMetric - end - r[j, j] = zero(eltype(r)) - aj = view(a, :, j) - for i = (j + 1):n - s = sinpi.(view(a, :, i) - aj) ./ p - r[i, j] = dot(s, s) - end - end - sum(r) - end - x3 = rand(2, 100) - - z3 = testfun3(x3) - z_simple3 = testfun_simple3(x3) - - @test z3 ≈ z_simple3 - - g1 = Zygote.gradient(testfun3, x3) |> only - # g_simple1 = Zygote.gradient(testfun_simple3, x3) |> only - @show g1 - # @test g1 ≈ g_simple1 - end -end - -end From 624f7adc71dedb41049d775b44df01e91f27e136 Mon Sep 17 00:00:00 2001 From: Simone Carlo Surace Date: Fri, 6 Oct 2023 14:20:15 +0200 Subject: [PATCH 3/4] Bump version and compat --- Project.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index 2830de211..3986c1f59 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "KernelFunctions" uuid = "ec8451be-7e33-11e9-00cf-bbf324bd1392" -version = "0.10.57" +version = "0.10.58" [deps] ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" @@ -25,7 +25,7 @@ ZygoteRules = "700de1a5-db45-46bc-99cf-38207098b444" ChainRulesCore = "1" Compat = "3.7, 4" CompositionsBase = "0.1" -Distances = "0.10" +Distances = "0.10.9" FillArrays = "0.10, 0.11, 0.12, 0.13, 1" Functors = "0.1, 0.2, 0.3, 0.4" IrrationalConstants = "0.1, 0.2" From 043bab853b2237a142e2966a84120320d9a70f5d Mon Sep 17 00:00:00 2001 From: Simone Carlo Surace Date: Fri, 6 Oct 2023 14:20:52 +0200 Subject: [PATCH 4/4] Change signature of `KernelFunctions.pairwise!` --- src/basekernels/fbm.jl | 4 ++-- src/distances/pairwise.jl | 8 ++++---- src/matrix/kernelmatrix.jl | 4 ++-- src/utils.jl | 8 ++++---- test/distances/pairwise.jl | 6 +++--- test/utils.jl | 8 ++++---- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/basekernels/fbm.jl b/src/basekernels/fbm.jl index 422400ac4..18dbdea14 100644 --- a/src/basekernels/fbm.jl +++ b/src/basekernels/fbm.jl @@ -57,7 +57,7 @@ end function kernelmatrix!(K::AbstractMatrix, κ::FBMKernel, x::AbstractVector) modx = _mod(x) - pairwise!(K, SqEuclidean(), x) + pairwise!(SqEuclidean(), K, x) K .= _fbm.(modx, modx', K, κ.h) return K end @@ -72,7 +72,7 @@ end function kernelmatrix!( K::AbstractMatrix, κ::FBMKernel, x::AbstractVector, y::AbstractVector ) - pairwise!(K, SqEuclidean(), x, y) + pairwise!(SqEuclidean(), K, x, y) K .= _fbm.(_mod(x), _mod(y)', K, κ.h) return K end diff --git a/src/distances/pairwise.jl b/src/distances/pairwise.jl index cb43ec041..ab925fba6 100644 --- a/src/distances/pairwise.jl +++ b/src/distances/pairwise.jl @@ -6,11 +6,11 @@ end pairwise(d::PreMetric, X::AbstractVector) = pairwise(d, X, X) -function pairwise!(out::AbstractMatrix, d::PreMetric, X::AbstractVector, Y::AbstractVector) +function pairwise!(d::PreMetric, out::AbstractMatrix, X::AbstractVector, Y::AbstractVector) return broadcast!(d, out, X, permutedims(Y)) end -pairwise!(out::AbstractMatrix, d::PreMetric, X::AbstractVector) = pairwise!(out, d, X, X) +pairwise!(d::PreMetric, out::AbstractMatrix, X::AbstractVector) = pairwise!(d, out, X, X) function pairwise(d::PreMetric, x::AbstractVector{<:Real}) return Distances_pairwise(d, reshape(x, :, 1); dims=1) @@ -20,12 +20,12 @@ function pairwise(d::PreMetric, x::AbstractVector{<:Real}, y::AbstractVector{<:R return Distances_pairwise(d, reshape(x, :, 1), reshape(y, :, 1); dims=1) end -function pairwise!(out::AbstractMatrix, d::PreMetric, x::AbstractVector{<:Real}) +function pairwise!(d::PreMetric, out::AbstractMatrix, x::AbstractVector{<:Real}) return Distances.pairwise!(d, out, reshape(x, :, 1); dims=1) end function pairwise!( - out::AbstractMatrix, d::PreMetric, x::AbstractVector{<:Real}, y::AbstractVector{<:Real} + d::PreMetric, out::AbstractMatrix, x::AbstractVector{<:Real}, y::AbstractVector{<:Real} ) return Distances.pairwise!(d, out, reshape(x, :, 1), reshape(y, :, 1); dims=1) end diff --git a/src/matrix/kernelmatrix.jl b/src/matrix/kernelmatrix.jl index e778f79eb..0b0f851d6 100644 --- a/src/matrix/kernelmatrix.jl +++ b/src/matrix/kernelmatrix.jl @@ -134,7 +134,7 @@ kernelmatrix_diag(κ::Kernel, x::AbstractVector, y::AbstractVector) = map(κ, x, function kernelmatrix!(K::AbstractMatrix, κ::SimpleKernel, x::AbstractVector) validate_inplace_dims(K, x) - pairwise!(K, metric(κ), x) + pairwise!(metric(κ), K, x) return map!(x -> kappa(κ, x), K, K) end @@ -142,7 +142,7 @@ function kernelmatrix!( K::AbstractMatrix, κ::SimpleKernel, x::AbstractVector, y::AbstractVector ) validate_inplace_dims(K, x, y) - pairwise!(K, metric(κ), x, y) + pairwise!(metric(κ), K, x, y) return map!(x -> kappa(κ, x), K, K) end diff --git a/src/utils.jl b/src/utils.jl index c0aa6f5cb..29087259b 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -107,10 +107,10 @@ end function pairwise(d::PreMetric, x::ColVecs, y::AbstractVector{<:AbstractVector{<:Real}}) return Distances_pairwise(d, x.X, reduce(hcat, y); dims=2) end -function pairwise!(out::AbstractMatrix, d::PreMetric, x::ColVecs) +function pairwise!(d::PreMetric, out::AbstractMatrix, x::ColVecs) return Distances.pairwise!(d, out, x.X; dims=2) end -function pairwise!(out::AbstractMatrix, d::PreMetric, x::ColVecs, y::ColVecs) +function pairwise!(d::PreMetric, out::AbstractMatrix, x::ColVecs, y::ColVecs) return Distances.pairwise!(d, out, x.X, y.X; dims=2) end @@ -178,10 +178,10 @@ end function pairwise(d::PreMetric, x::RowVecs, y::AbstractVector{<:AbstractVector{<:Real}}) return Distances_pairwise(d, x.X, permutedims(reduce(hcat, y)); dims=1) end -function pairwise!(out::AbstractMatrix, d::PreMetric, x::RowVecs) +function pairwise!(d::PreMetric, out::AbstractMatrix, x::RowVecs) return Distances.pairwise!(d, out, x.X; dims=1) end -function pairwise!(out::AbstractMatrix, d::PreMetric, x::RowVecs, y::RowVecs) +function pairwise!(d::PreMetric, out::AbstractMatrix, x::RowVecs, y::RowVecs) return Distances.pairwise!(d, out, x.X, y.X; dims=1) end diff --git a/test/distances/pairwise.jl b/test/distances/pairwise.jl index 486097f52..6635579cb 100644 --- a/test/distances/pairwise.jl +++ b/test/distances/pairwise.jl @@ -11,10 +11,10 @@ @test KernelFunctions.pairwise(d, x, y) ≈ pairwise(d, X, Y; dims=2) @test KernelFunctions.pairwise(d, x) ≈ pairwise(d, X; dims=2) - KernelFunctions.pairwise!(K, d, x, y) + KernelFunctions.pairwise!(d, K, x, y) @test K ≈ pairwise(d, X, Y; dims=2) K = zeros(Ns[1], Ns[1]) - KernelFunctions.pairwise!(K, d, x) + KernelFunctions.pairwise!(d, K, x) @test K ≈ pairwise(d, X; dims=2) x = randn(rng, 10) @@ -24,6 +24,6 @@ K = zeros(10, 11) @test KernelFunctions.pairwise(d, x, y) ≈ pairwise(d, X, Y; dims=1) @test KernelFunctions.pairwise(d, x) ≈ pairwise(d, X; dims=1) - KernelFunctions.pairwise!(K, d, x, y) + KernelFunctions.pairwise!(d, K, x, y) @test K ≈ pairwise(d, X, Y; dims=1) end diff --git a/test/utils.jl b/test/utils.jl index 42784548a..a7cabbd05 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -47,10 +47,10 @@ @test vcat(DX, DY) isa ColVecs @test vcat(DX, DY).X == hcat(X, Y) K = zeros(N, N) - KernelFunctions.pairwise!(K, SqEuclidean(), DX) + KernelFunctions.pairwise!(SqEuclidean(), K, DX) @test K ≈ pairwise(SqEuclidean(), X; dims=2) K = zeros(N, N + 1) - KernelFunctions.pairwise!(K, SqEuclidean(), DX, DY) + KernelFunctions.pairwise!(SqEuclidean(), K, DX, DY) @test K ≈ pairwise(SqEuclidean(), X, Y; dims=2) let @@ -105,10 +105,10 @@ @test vcat(DX, DY) isa RowVecs @test vcat(DX, DY).X == vcat(X, Y) K = zeros(D, D) - KernelFunctions.pairwise!(K, SqEuclidean(), DX) + KernelFunctions.pairwise!(SqEuclidean(), K, DX) @test K ≈ pairwise(SqEuclidean(), X; dims=1) K = zeros(D, D + 1) - KernelFunctions.pairwise!(K, SqEuclidean(), DX, DY) + KernelFunctions.pairwise!(SqEuclidean(), K, DX, DY) @test K ≈ pairwise(SqEuclidean(), X, Y; dims=1) let