diff --git a/Project.toml b/Project.toml index d97edba..66f73f4 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "ExpFamilyPCA" uuid = "9c724b78-6801-4402-8a63-53f028696012" authors = ["Logan-Mondal-Bhamidipaty"] -version = "1.1.0" +version = "2.0.0" [deps] CompressedBeliefMDPs = "0a809e47-b8eb-4578-b4e8-4c2c5f9f833c" @@ -9,7 +9,6 @@ Distances = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7" FunctionWrappers = "069b7b12-0de2-55c6-9aab-29f3d0a68a2e" LogExpFunctions = "2ab3a3ac-af41-5b50-aa03-7779005ae688" Optim = "429524aa-4258-5aef-a3af-852621145aeb" -Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Sobol = "ed01d8cd-4d21-5b2a-85b4-cc3bdc58bad4" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" @@ -21,11 +20,10 @@ Distances = "0.10" FunctionWrappers = "1" LogExpFunctions = "0.3" Optim = "1" -Parameters = "0.12" Sobol = "1" Statistics = "1" Symbolics = "6" -julia = "1" +julia = "1.10" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/src/ExpFamilyPCA.jl b/src/ExpFamilyPCA.jl index 4dcff02..4d107bb 100644 --- a/src/ExpFamilyPCA.jl +++ b/src/ExpFamilyPCA.jl @@ -4,7 +4,6 @@ using Distances using FunctionWrappers: FunctionWrapper using LogExpFunctions using Optim -using Parameters using Sobol using Symbolics diff --git a/src/compressor.jl b/src/compressor.jl index f2ca877..b5f15de 100644 --- a/src/compressor.jl +++ b/src/compressor.jl @@ -3,10 +3,10 @@ import CompressedBeliefMDPs # `import` rather than `using` to keep tidey namesp """ EPCACompressor(epca::EPCA) - Compressor for `CompressedBeliefMDPs.jl`. +Compressor for `CompressedBeliefMDPs.jl`. """ -struct EPCACompressor <: CompressedBeliefMDPs.Compressor - epca::EPCA +struct EPCACompressor{E<:EPCA} <: CompressedBeliefMDPs.Compressor + epca::E end function (c::EPCACompressor)(beliefs) @@ -20,4 +20,4 @@ end function CompressedBeliefMDPs.fit!(c::EPCACompressor, beliefs) ExpFamilyPCA.fit!(c.epca, beliefs) -end \ No newline at end of file +end diff --git a/src/constructors/epca1.jl b/src/constructors/epca1.jl index 64d45d3..fb7375e 100644 --- a/src/constructors/epca1.jl +++ b/src/constructors/epca1.jl @@ -1,13 +1,18 @@ -struct EPCA1 <: EPCA - F::Union{Function, FunctionWrapper} # Legendre dual of the log-partition - g::Union{Function, FunctionWrapper} # link function - V::AbstractMatrix{<:Real} - options::Options +struct EPCA1{ + FT1<:Union{Function, FunctionWrapper}, + FT2<:Union{Function, FunctionWrapper}, + MT<:AbstractMatrix{<:Real}, + OT<:Options +} <: EPCA + F::FT1 # Legendre dual of the log-partition + g::FT2 # link function + V::MT + options::OT end function _make_loss(epca::EPCA1, X) - @unpack F, g = epca - @unpack μ, ϵ = epca.options + (; F, g) = epca + (; μ, ϵ) = epca.options @assert ϵ >= 0 "ϵ must be non-negative." L(x, θ) = begin @@ -44,7 +49,7 @@ function EPCA( options = Options() ) @assert isfinite(f(options.μ)) "μ must be in the range of g meaning f(μ) should be finite." - @unpack low, high, tol, maxiter = options + (; low, high, tol, maxiter) = options g = _invert_legendre(f, options) V = _initialize_V(indim, outdim, options) epca = EPCA1(F, g, V, options) diff --git a/src/constructors/epca2.jl b/src/constructors/epca2.jl index 058a943..025b240 100644 --- a/src/constructors/epca2.jl +++ b/src/constructors/epca2.jl @@ -1,13 +1,18 @@ -struct EPCA2 <: EPCA - G::Union{Function, FunctionWrapper} # log-parition function - g::Union{Function, FunctionWrapper} # link function - V::AbstractMatrix{<:Real} - options::Options +struct EPCA2{ + FT1<:Union{Function, FunctionWrapper}, + FT2<:Union{Function, FunctionWrapper}, + MT<:AbstractMatrix{<:Real}, + OT<:Options +} <: EPCA + G::FT1 # log-parition function + g::FT2 # link function + V::MT + options::OT end function _make_loss(epca::EPCA2, X) - @unpack G, g = epca - @unpack tol, μ, ϵ = epca.options + (; G, g) = epca + (; tol, μ, ϵ) = epca.options @assert ϵ >= 0 "ϵ must be non-negative." L(x, θ) = begin diff --git a/src/constructors/epca3.jl b/src/constructors/epca3.jl index 0004e4b..bd9c1e2 100644 --- a/src/constructors/epca3.jl +++ b/src/constructors/epca3.jl @@ -1,13 +1,18 @@ -struct EPCA3 <: EPCA - B::Union{Function, FunctionWrapper, PreMetric} # Bregman divergence - g::Union{Function, FunctionWrapper} # link function - V::AbstractMatrix{<:Real} - options::Options +struct EPCA3{ + FT1<:Union{Function, FunctionWrapper, PreMetric}, + FT2<:Union{Function, FunctionWrapper}, + MT<:AbstractMatrix{<:Real}, + OT<:Options +} <: EPCA + B::FT1 # Bregman divergence + g::FT2 # link function + V::MT + options::OT end function _make_loss(epca::EPCA3, X) - @unpack B, g = epca - @unpack μ, ϵ = epca.options + (; B, g) = epca + (; μ, ϵ) = epca.options @assert ϵ >= 0 "ϵ must be non-negative." L(x, θ) = begin diff --git a/src/constructors/epca4.jl b/src/constructors/epca4.jl index a6639fa..09bc436 100644 --- a/src/constructors/epca4.jl +++ b/src/constructors/epca4.jl @@ -1,13 +1,18 @@ -struct EPCA4 <: EPCA - Bg::Function # Bregman divergence composed with the link function in the 2nd slot, that is Bg(⋅, ⋅) = B_F(⋅, g(⋅)). - g::Function # link function - V::AbstractMatrix{<:Real} - options::Options +struct EPCA4{ + FT1<:Function, + FT2<:Function, + MT<:AbstractMatrix{<:Real}, + OT<:Options +} <: EPCA + Bg::FT1 # Bregman divergence composed with the link function in the 2nd slot, that is Bg(⋅, ⋅) = B_F(⋅, g(⋅)). + g::FT2 # link function + V::MT + options::OT end function _make_loss(epca::EPCA4, X) Bg = epca.Bg - @unpack μ, ϵ = epca.options + (; μ, ϵ) = epca.options @assert ϵ >= 0 "ϵ must be non-negative." L(x, θ) = begin @@ -51,4 +56,4 @@ function EPCA( options = options ) return epca -end \ No newline at end of file +end diff --git a/src/epca.jl b/src/epca.jl index 106923a..2ee474f 100644 --- a/src/epca.jl +++ b/src/epca.jl @@ -121,7 +121,7 @@ function fit!( steps_per_print, epca.options ) - epca.V[:] = V + epca.V[:] = V # TODO: delete this line? return A end diff --git a/src/family/gamma.jl b/src/family/gamma.jl index b2669f4..9a5cf30 100644 --- a/src/family/gamma.jl +++ b/src/family/gamma.jl @@ -21,7 +21,7 @@ function GammaEPCA( indim::Integer, outdim::Integer; options::Options = Options( - A_init_value = -2, + A_init_value = -2.0, A_upper = -eps(), V_lower = eps() ) @@ -47,11 +47,11 @@ function ItakuraSaitoEPCA( indim::Integer, outdim::Integer; options::Options = Options( - A_init_value = -2, + A_init_value = -2.0, A_upper = -eps(), V_lower = eps() ) ) epca = GammaEPCA(indim, outdim; options = options) return epca -end \ No newline at end of file +end diff --git a/src/family/negative_binomial.jl b/src/family/negative_binomial.jl index 594b7d8..d148f4d 100644 --- a/src/family/negative_binomial.jl +++ b/src/family/negative_binomial.jl @@ -23,7 +23,7 @@ function NegativeBinomialEPCA( outdim::Integer, r::Integer; options::Options = Options( - A_init_value = -1, + A_init_value = -1.0, A_upper = -eps(), V_lower = eps() ) @@ -40,4 +40,4 @@ function NegativeBinomialEPCA( options = options ) return epca -end \ No newline at end of file +end diff --git a/src/family/pareto.jl b/src/family/pareto.jl index e8e99c5..cc9291b 100644 --- a/src/family/pareto.jl +++ b/src/family/pareto.jl @@ -22,11 +22,11 @@ function ParetoEPCA( outdim::Integer, m::Real; options::Options = Options( - μ = 2, - A_init_value = 2, + μ = 2.0, + A_init_value = 2.0, A_lower = 1 / outdim, - V_init_value = -2, - V_upper = -1, + V_init_value = -2.0, + V_upper = -1.0, ) ) @assert m > 0 "Minimum value m must be positive." @@ -43,4 +43,4 @@ function ParetoEPCA( options = options ) return epca -end \ No newline at end of file +end diff --git a/src/options.jl b/src/options.jl index 0f54e6b..2dc6e13 100644 --- a/src/options.jl +++ b/src/options.jl @@ -20,29 +20,29 @@ Defines a struct `Options` for configuring various parameters used in optimizati - `tol::Real`: Tolerance for stopping binary search. Default is `1e-10`. - `maxiter::Real`: Maximum iterations for binary search. Default is `1e6`. """ -@with_kw struct Options +@kwdef struct Options{T<:Real} # symbolic calculus metaprogramming::Bool = true # loss hyperparameters - μ::Real = 1 - ϵ::Real = eps() + μ::T = 1.0 + ϵ::T = eps() - A_init_value::Real = 1.0 - A_lower::Union{Real, Nothing} = nothing - A_upper::Union{Real, Nothing} = nothing + A_init_value::T = 1.0 + A_lower::Union{T, Nothing} = nothing + A_upper::Union{T, Nothing} = nothing A_use_sobol::Bool = false - V_init_value::Real = 1.0 - V_lower::Union{Real, Nothing} = nothing - V_upper::Union{Real, Nothing} = nothing + V_init_value::T = 1.0 + V_lower::Union{T, Nothing} = nothing + V_upper::Union{T, Nothing} = nothing V_use_sobol::Bool = false # binary search options - low = -1e10 - high = 1e10 - tol = 1e-10 - maxiter = 1e6 + low::Float64 = -1e10 + high::Float64 = 1e10 + tol::Float64 = 1e-10 + maxiter::Int = 10^6 end """ @@ -60,12 +60,12 @@ Other fields inherit from the `Options` struct. """ function NegativeDomain(; metaprogramming::Bool = true, - μ::Real = 1, + μ::Real = 1.0, ϵ::Real = eps(), low = -1e10, high = 1e10, tol = 1e-10, - maxiter = 1e6, + maxiter = 10^6, ) options = Options( metaprogramming = metaprogramming, @@ -75,9 +75,9 @@ function NegativeDomain(; high = high, tol = tol, maxiter = maxiter, - A_init_value = -1, + A_init_value = -1.0, A_upper = -1e-4, - V_init_value = 1, + V_init_value = 1.0, V_lower = 1e-4, ) return options @@ -98,12 +98,12 @@ Other fields inherit from the `Options` struct. """ function PositiveDomain( metaprogramming::Bool = true, - μ::Real = 1, + μ::Real = 1.0, ϵ::Real = eps(), low = -1e10, high = 1e10, tol = 1e-10, - maxiter = 1e6, + maxiter = 10^6, ) options = Options( metaprogramming = metaprogramming, @@ -113,10 +113,10 @@ function PositiveDomain( high = high, tol = tol, maxiter = maxiter, - A_init_value = 1, + A_init_value = 1.0, A_upper = 1e-4, - V_init_value = 1, + V_init_value = 1.0, V_lower = 1e-4, ) return options -end \ No newline at end of file +end diff --git a/src/utils.jl b/src/utils.jl index 9f7380d..b61e8f0 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -65,7 +65,7 @@ end """Invert Legendre transformation""" function _invert_legendre(f, options::Options) - @unpack low, high, tol, maxiter = options + (; low, high, tol, maxiter) = options _check_binary_search_arguments(low, high, tol, maxiter) g(x) = _binary_search_monotone( f, @@ -84,11 +84,11 @@ function is_constant_matrix(A::AbstractMatrix) end function _optimize( - f::Function, + f::F, lower::Union{Real, Nothing}, upper::Union{Real, Nothing}, x0 -) +) where {F} x0 = Vector(x0) if isnothing(lower) && isnothing(upper) result = optimize(f, x0) @@ -107,7 +107,7 @@ function _optimize( end function _single_compress_iter( - L::Function, + L::F, V::AbstractMatrix{T}, A::AbstractMatrix{T}, X::AbstractMatrix, @@ -116,11 +116,11 @@ function _single_compress_iter( steps_per_print::Integer, maxiter::Integer, options::Options -) where T <: Real - @unpack A_lower, A_upper = options +) where {F<:Function, T <: Real} + (; A_lower, A_upper) = options total_loss = 0.0 - A_new = similar(A) + A_new = copy(A) for (i, a) in enumerate(eachrow(A)) x = X[i, :] a_new, loss = _optimize( @@ -141,7 +141,7 @@ end function _single_fit_iter( - L::Function, + L::F, V::AbstractMatrix{T}, A::AbstractMatrix{T}, X::AbstractMatrix, @@ -150,9 +150,9 @@ function _single_fit_iter( steps_per_print::Integer, maxiter::Integer, options::Options -) where T <: Real - @unpack V_lower, V_upper = options - V_new = similar(V) +) where {F<:Function, T <: Real} + (; V_lower, V_upper) = options + V_new = copy(V) for (i, v) in enumerate(eachcol(V)) x = X[:, i] v_new, _ = _optimize( @@ -203,7 +203,7 @@ function _check_convergence(loss, last_loss; verbose=false) end function _compress( - L::Function, + L::F, V::AbstractMatrix{T}, A::AbstractMatrix{T}, X::AbstractMatrix, @@ -211,7 +211,7 @@ function _compress( verbose::Bool, steps_per_print::Integer, options::Options -) where T <: Real +) where {F<:Function, T <: Real} last_loss = missing for i in 1:maxiter A, loss = _single_compress_iter( @@ -234,7 +234,7 @@ function _compress( end function _fit( - L::Function, + L::F, V::AbstractMatrix{T}, A::AbstractMatrix{T}, X::AbstractMatrix, @@ -242,7 +242,7 @@ function _fit( verbose::Bool, steps_per_print::Integer, options::Options -) where T <: Real +) where {F<:Function, T <: Real} last_loss = missing for i in 1:maxiter V, A, loss = _single_fit_iter( @@ -303,4 +303,4 @@ function _differentiate(H, metaprogramming::Bool) h = _symbolics_to_julia(_h, θ) end return h -end \ No newline at end of file +end