diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5dcce5378..04b101d09 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,6 @@ jobs: matrix: version: - '1' - - '1.6' - 'lts' os: - ubuntu-latest diff --git a/Project.toml b/Project.toml index bd06018a6..4cfb0b718 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "KernelFunctions" uuid = "ec8451be-7e33-11e9-00cf-bbf324bd1392" -version = "0.10.67" +version = "0.11.0" [deps] ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" @@ -13,7 +13,6 @@ IrrationalConstants = "92d709cd-6900-40b7-9082-c6be49f344b6" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LogExpFunctions = "2ab3a3ac-af41-5b50-aa03-7779005ae688" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" -Requires = "ae029012-a4dd-5104-9daa-d747884805df" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" @@ -21,6 +20,14 @@ TensorCore = "62fd8b95-f654-4bbd-a8a5-9c27f68ccd50" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" ZygoteRules = "700de1a5-db45-46bc-99cf-38207098b444" +[weakdeps] +Kronecker = "2c470bb0-bcc8-11e8-3dad-c9649493f05e" +PDMats = "90014a1f-27ba-587c-ab20-58faa44d9150" + +[extensions] +KernelFunctionsKroneckerExt = "Kronecker" +KernelFunctionsPDMatsExt = "PDMats" + [compat] ChainRulesCore = "1" Compat = "3.7, 4" @@ -29,11 +36,12 @@ Distances = "0.10.9" FillArrays = "0.10, 0.11, 0.12, 0.13, 1" Functors = "0.1, 0.2, 0.3, 0.4, 0.5" IrrationalConstants = "0.1, 0.2" +Kronecker = "0.4, 0.5" LogExpFunctions = "0.2.1, 0.3" -Requires = "1.0.1" +PDMats = "0.11" SpecialFunctions = "0.8, 0.9, 0.10, 1, 2" Statistics = "1" StatsBase = "0.32, 0.33, 0.34" TensorCore = "0.1" ZygoteRules = "0.2" -julia = "1.6" +julia = "1.10" diff --git a/docs/Project.toml b/docs/Project.toml index f915ec6e2..f31af4ff7 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -9,7 +9,7 @@ Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" [compat] Distances = "0.10" Documenter = "1" -KernelFunctions = "0.10" +KernelFunctions = "0.11" Kronecker = "0.4, 0.5" PDMats = "0.11" Statistics = "1" diff --git a/docs/src/api.md b/docs/src/api.md index 9fb241fa4..d0e334b07 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -89,7 +89,9 @@ NystromFact ``` ## Conditional Utilities -To keep the dependencies of KernelFunctions lean, some functionality is only available if specific other packages are explicitly loaded (`using`). +To keep the dependencies of KernelFunctions lean, it uses [package extensions](https://pkgdocs.julialang.org/v1/creating-packages/#Conditional-loading-of-code-in-packages-(Extensions)) to +conditionally load some code. In order to enable the following functions, load the required +packages with `using`. ### Kronecker.jl [*https://github.com/MichielStock/Kronecker.jl*](https://github.com/MichielStock/Kronecker.jl) diff --git a/src/matrix/kernelkroneckermat.jl b/ext/KernelFunctionsKroneckerExt.jl similarity index 70% rename from src/matrix/kernelkroneckermat.jl rename to ext/KernelFunctionsKroneckerExt.jl index 113d0f53d..5ba9b61a3 100644 --- a/src/matrix/kernelkroneckermat.jl +++ b/ext/KernelFunctionsKroneckerExt.jl @@ -1,10 +1,21 @@ +module KernelFunctionsKroneckerExt + +using KernelFunctions: + KernelFunctions, + Kernel, + MOKernel, + IndependentMOKernel, + IntrinsicCoregionMOKernel, + IsotopicMOInputsUnion, + MOInputIsotopicByFeatures, + MOInputIsotopicByOutputs, + kernelmatrix, + _mo_output_covariance +using Kronecker: Kronecker + # Since Kronecker does not implement `TensorCore.:⊗` but instead exports its own function # `Kronecker.:⊗`, only the module is imported and Kronecker.:⊗ and Kronecker.kronecker are # called explicitly. -using .Kronecker: Kronecker - -export kernelkronmat -export kronecker_kernelmatrix @doc raw""" kernelkronmat(κ::Kernel, X::AbstractVector{<:Real}, dims::Int) -> KroneckerPower @@ -16,8 +27,8 @@ where `D` is given by `dims`. Requires `Kronecker.jl` and for `iskroncompatible(κ)` to return `true`. """ -function kernelkronmat(κ::Kernel, X::AbstractVector{<:Real}, dims::Int) - checkkroncompatible(κ) +function KernelFunctions.kernelkronmat(κ::Kernel, X::AbstractVector{<:Real}, dims::Int) + KernelFunctions.checkkroncompatible(κ) K = kernelmatrix(κ, X) return Kronecker.kronecker(K, dims) end @@ -32,32 +43,12 @@ Returns a `KroneckerProduct` matrix on the grid built with the collection of vec Requires `Kronecker.jl` and for `iskroncompatible(κ)` to return `true`. """ -function kernelkronmat(κ::Kernel, X::AbstractVector{<:AbstractVector}) - checkkroncompatible(κ) +function KernelFunctions.kernelkronmat(κ::Kernel, X::AbstractVector{<:AbstractVector}) + KernelFunctions.checkkroncompatible(κ) Ks = kernelmatrix.(κ, X) return reduce(Kronecker.:⊗, Ks) end -@doc raw""" - iskroncompatible(k::Kernel) - -Determine whether kernel `k` is compatible with Kronecker constructions such as [`kernelkronmat`](@ref) - -The function returns `false` by default. If `k` is compatible it must satisfy for all ``x, x' \in \mathbb{R}^D`: -```math -k(x, x') = \prod_{i=1}^D k(x_i, x'_i). -``` -""" -@inline iskroncompatible(κ::Kernel) = false # Default return for kernels - -function checkkroncompatible(κ::Kernel) - return iskroncompatible(κ) || throw( - ArgumentError( - "The chosen kernel is not compatible for Kronecker matrices (see [`iskroncompatible`](@ref))", - ), - ) -end - function _kernelmatrix_kroneckerjl_helper( ::Type{<:MOInputIsotopicByFeatures}, Kfeatures, Koutputs ) @@ -79,7 +70,7 @@ Requires Kronecker.jl: Computes the `kernelmatrix` for the `IndependentMOKernel` `IntrinsicCoregionMOKernel`, but returns a lazy kronecker product. This object can be very efficiently inverted or decomposed. See also [`kernelmatrix`](@ref). """ -function kronecker_kernelmatrix( +function KernelFunctions.kronecker_kernelmatrix( k::Union{IndependentMOKernel,IntrinsicCoregionMOKernel}, x::MOI, y::MOI ) where {MOI<:IsotopicMOInputsUnion} x.out_dim == y.out_dim || @@ -89,7 +80,7 @@ function kronecker_kernelmatrix( return _kernelmatrix_kroneckerjl_helper(MOI, Kfeatures, Koutputs) end -function kronecker_kernelmatrix( +function KernelFunctions.kronecker_kernelmatrix( k::Union{IndependentMOKernel,IntrinsicCoregionMOKernel}, x::MOI ) where {MOI<:IsotopicMOInputsUnion} Kfeatures = kernelmatrix(k.kernel, x.x) @@ -97,7 +88,7 @@ function kronecker_kernelmatrix( return _kernelmatrix_kroneckerjl_helper(MOI, Kfeatures, Koutputs) end -function kronecker_kernelmatrix( +function KernelFunctions.kronecker_kernelmatrix( k::MOKernel, x::IsotopicMOInputsUnion, y::IsotopicMOInputsUnion ) return throw( @@ -105,6 +96,8 @@ function kronecker_kernelmatrix( ) end -function kronecker_kernelmatrix(k::MOKernel, x::IsotopicMOInputsUnion) - return kronecker_kernelmatrix(k, x, x) +function KernelFunctions.kronecker_kernelmatrix(k::MOKernel, x::IsotopicMOInputsUnion) + return KernelFunctions.kronecker_kernelmatrix(k, x, x) +end + end diff --git a/src/matrix/kernelpdmat.jl b/ext/KernelFunctionsPDMatsExt.jl similarity index 72% rename from src/matrix/kernelpdmat.jl rename to ext/KernelFunctionsPDMatsExt.jl index 190444a08..0902c1f27 100644 --- a/src/matrix/kernelpdmat.jl +++ b/ext/KernelFunctionsPDMatsExt.jl @@ -1,6 +1,9 @@ -using .PDMats: PDMat +module KernelFunctionsPDMatsExt -export kernelpdmat +using KernelFunctions: + KernelFunctions, Kernel, ColVecs, RowVecs, kernelmatrix, vec_of_vecs, defaultobs +using LinearAlgebra: I, isposdef +using PDMats: PDMat """ kernelpdmat(k::Kernel, X::AbstractVector) @@ -10,7 +13,7 @@ with the Cholesky decomposition precomputed. The algorithm adds a diagonal "nugget" term to the kernel matrix which is increased until positive definiteness is achieved. The algorithm gives up with an error if the nugget becomes larger than 1% of the largest value in the kernel matrix. """ -function kernelpdmat(κ::Kernel, X::AbstractVector) +function KernelFunctions.kernelpdmat(κ::Kernel, X::AbstractVector) K = kernelmatrix(κ, X) Kmax = maximum(K) α = eps(eltype(K)) @@ -35,6 +38,10 @@ If `obsdim=2`, equivalent to `kernelpdmat(k, ColVecs(X))`. See also: [`ColVecs`](@ref), [`RowVecs`](@ref) """ -function kernelpdmat(κ::Kernel, X::AbstractMatrix; obsdim::Union{Int,Nothing}=defaultobs) - return kernelpdmat(κ, vec_of_vecs(X; obsdim=obsdim)) +function KernelFunctions.kernelpdmat( + κ::Kernel, X::AbstractMatrix; obsdim::Union{Int,Nothing}=defaultobs +) + return KernelFunctions.kernelpdmat(κ, vec_of_vecs(X; obsdim=obsdim)) +end + end diff --git a/src/KernelFunctions.jl b/src/KernelFunctions.jl index 8d5679d9a..2c5512c06 100644 --- a/src/KernelFunctions.jl +++ b/src/KernelFunctions.jl @@ -34,6 +34,9 @@ export median_heuristic_transform export NystromFact, nystrom +export kernelkronmat, kronecker_kernelmatrix, iskroncompatible +export kernelpdmat + export gaborkernel export spectral_mixture_kernel, spectral_mixture_product_kernel @@ -54,7 +57,6 @@ using Distances using FillArrays using Functors using LinearAlgebra -using Requires using SpecialFunctions: loggamma, besselk, polygamma using IrrationalConstants: logtwo, twoπ, invsqrt2 using LogExpFunctions: softplus @@ -126,13 +128,31 @@ include("zygoterules.jl") include("TestUtils.jl") -function __init__() - @require Kronecker = "2c470bb0-bcc8-11e8-3dad-c9649493f05e" begin - include("matrix/kernelkroneckermat.jl") - end - @require PDMats = "90014a1f-27ba-587c-ab20-58faa44d9150" begin - include("matrix/kernelpdmat.jl") - end +# Kronecker extension stubs +@doc raw""" + iskroncompatible(k::Kernel) + +Determine whether kernel `k` is compatible with Kronecker constructions such as [`kernelkronmat`](@ref) + +The function returns `false` by default. If `k` is compatible it must satisfy for all ``x, x' \in \mathbb{R}^D`: +```math +k(x, x') = \prod_{i=1}^D k(x_i, x'_i). +``` +""" +@inline iskroncompatible(κ::Kernel) = false + +function checkkroncompatible(κ::Kernel) + return iskroncompatible(κ) || throw( + ArgumentError( + "The chosen kernel is not compatible for Kronecker matrices (see [`iskroncompatible`](@ref))", + ), + ) end +function kernelkronmat end +function kronecker_kernelmatrix end + +# PDMats extension stub +function kernelpdmat end + end