Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
StatsFuns = "4c63d2b9-4356-54db-8cca-17b64c39e42c"

[weakdeps]
Optim = "429524aa-4258-5aef-a3af-852621145aeb"

[extras]
Optim = "429524aa-4258-5aef-a3af-852621145aeb"

[extensions]
DistributionFitsOptimExt = "Optim"

[compat]
Distributions = "0.25"
FillArrays = "0.12, 0.13"
Expand All @@ -22,3 +31,5 @@ StaticArrays = "1.2"
StatsBase = "0.33"
StatsFuns = "0.9, 1"
julia = "1.6"
Optim = "1.7"

19 changes: 19 additions & 0 deletions ext/DistributionFitsOptimExt.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module DistributionFitsOptimExt

isdefined(Base, :get_extension) ? (using Optim) : (using ..Optim)
using DistributionFits: DistributionFits, optimize, AbstractDistributionFitOptimizer

struct OptimOptimizer <: AbstractDistributionFitOptimizer; end

function DistributionFits.optimize(f, ::OptimOptimizer, lower, upper)
result = Optim.optimize(f, lower, upper)
(;minimizer = result.minimizer, converged = result.converged, result)
end

function __init__()
@info "DistributionFits: setting OptimOptimizer"
#DistributionFits.set_optimizer(DistributionFitsOptimExt.OptimOptimizer())
DistributionFits.set_optimizer(OptimOptimizer())
end

end
16 changes: 13 additions & 3 deletions src/DistributionFits.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ using Reexport

using FillArrays, StaticArrays
using StatsFuns: logit, logistic, normcdf
using Requires: @require

if !isdefined(Base, :get_extension)
using Requires
end

# for extension
import Distributions: mean, var, mode
Expand All @@ -21,6 +24,11 @@ export
@qs_cf90, @qs_cf95,
fit_mean_relerror

# document but do not export - need to qualify by 'DistributionFits.'
# export
# # Optimizer detail
# optimize, set_optimizer

# LogNormal
export AbstractΣstar, Σstar, σstar

Expand All @@ -30,11 +38,13 @@ export fit_mode_flat, shifloNormal
# dependency inversion: need to define DistributionFits.optimize by user
export AbstractDistributionFitOptimizer, optimize


include("optimizer.jl")


function __init__()
@require Optim="429524aa-4258-5aef-a3af-852621145aeb" include("requires_optim.jl")
@static if !isdefined(Base, :get_extension)
@require Optim="429524aa-4258-5aef-a3af-852621145aeb" include("../ext/DistributionFitsOptimExt.jl")
end
end

# fitting distributions to stats
Expand Down
19 changes: 12 additions & 7 deletions src/optimizer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ function `f` on bounded interval `[lower,upper]`:
Returning an object with fields `minimizer` and `converged`.

`DistributionFits.set_optimizer(::AbstractDistributionFitOptimizer)`
sets the optimizer used in calling the optimize function.
sets the specific AbstractDistributionFitOptimizer that is used
throughout the package for calling the optimize function.
Specializing this function with a concrete type, allows using different
optimization packages.

Expand All @@ -20,21 +21,25 @@ package is in scope, the
`OptimOptimizer` is set, which implements the interface by using Optim's
[optimize](https://julianlsolvers.github.io/Optim.jl/stable/#user/minimization/#minimizing-a-univariate-function-on-a-bounded-interval) function.

Hence, the module using DistribuitonFits.jl has to either
Hence, the module using `DistributionFits.jl` has to either

- explicitly invoke `using Optim`, or
- call `set_optimizer` with the concrete subtype of `AbstractDistributionFitOptimizer`
for which the corresponding optimize method is implemented.
for which the corresponding `optimize` method is implemented.

Developers implementing usage of a different specific optimizer see code in
ext/DistributionFitsOptimExt.
"""
abstract type AbstractDistributionFitOptimizer end
struct OptimOptimizer <: AbstractDistributionFitOptimizer; end
struct NotSetOptimizer <: AbstractDistributionFitOptimizer; end

# see to ext/DistributionFitsOptimExt.jl

optimize(f, ::NotSetOptimizer, lower, upper) = error(
"Optimizer not set yet. Either invoke 'using Optim' or 'DistributionFits.set_optimizer(...)'.")


optimizer = NotSetOptimizer();
optimize(f, lower, upper) = optimize(f, optimizer, lower, upper)
set_optimizer(opt::AbstractDistributionFitOptimizer) = (global optimizer = opt)
df_optimizer = NotSetOptimizer();
optimize(f, lower, upper) = optimize(f, df_optimizer, lower, upper)
set_optimizer(opt::AbstractDistributionFitOptimizer) = (global df_optimizer = opt)

7 changes: 0 additions & 7 deletions src/requires_optim.jl

This file was deleted.

8 changes: 6 additions & 2 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,20 @@ using Test
using Random: Random

@testset "optimize error" begin
@test_throws Exception DistributionFits.optimize(x -> x*x, DistributionFits.optimizer, -1, 1)
@test_throws Exception DistributionFits.optimize(x -> x*x, -1, 1)
end
# Optim package for interactive testing
i_loadlibs = () -> begin
push!(LOAD_PATH, expanduser("~/julia/scimltools/")) # access local package repo
push!(LOAD_PATH, expanduser("~/julia/18_tools/scimltools/")) # access local package repo
end
using Optim: Optim, optimize

DistributionFitsOptimExt = isdefined(Base, :get_extension) ? Base.get_extension(DistributionFits, :DistributionFitsOptimExt) : DistributionFits.DistributionFitsOptimExt

@testset "optimize set in __init__ after using Optim" begin
# set in __init__
@test DistributionFits.optimizer isa DistributionFits.OptimOptimizer
@test DistributionFits.df_optimizer isa DistributionFitsOptimExt.OptimOptimizer
end

const tests = [
Expand Down