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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ to
- to median and upper quantile point
- to lower and upper quantiles, i.e confidence range

This can also be used to approximate a different distribution by matching its moments
This can also be used to approximate one distribution via a different distribution by matching its moments.

User needs to [explicitly use Optim.jl](https://bgctw.github.io/DistributionFits.jl/stable/set_optimize/) for DitributionFits.jl to work properly:
```julia
Expand Down
2 changes: 1 addition & 1 deletion docs/src/set_optimize.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ CurrentModule = DistributionFits
# Setting the optimize configuration

```@docs
set_optimize
AbstractDistributionFitOptimizer
```

29 changes: 3 additions & 26 deletions src/DistributionFits.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,38 +27,15 @@ export AbstractΣstar, Σstar, σstar
export fit_mode_flat

# dependency inversion: need to define DistributionFits.optimize by user
export set_optimize
"""
set_optimize(f_optimize)
export AbstractDistributionFitOptimizer, optimize

`DistributionFits.jl` uses the following interface to opimize an univariate
function `f` on bounded interval `[lower,upper]`:

optimize(f, lower, upper)
include("optimizer.jl")

Returning an object with fields `minimizer` and `converged`.

`set_optimize` tells the concrete implementation passed by `f_optimize`
to `DistributionFits.jl`.

If package [Optim](https://julianlsolvers.github.io/Optim.jl/stable/) is in scope, then its
[optimize](https://julianlsolvers.github.io/Optim.jl/stable/#user/minimization/#minimizing-a-univariate-function-on-a-bounded-interval)
function is set automatically.

Hence, the module using DistribuitonFits.jl has to either

- explicitly tell `using Optim`, or
- call `set_optimize` with the concrete implementation of the interface

If the optimizer has not been set yet, then several functions in `Distribution.jl`
fail with the error: `UndefVarError: optimize not defined`.
"""
set_optimize(f_optimize) = (global optimize = f_optimize)
function __init__()
@require Optim="429524aa-4258-5aef-a3af-852621145aeb" optimize = Optim.optimize
@require Optim="429524aa-4258-5aef-a3af-852621145aeb" include("requires_optim.jl")
end


# fitting distributions to stats
include("fitstats.jl")
include("univariates.jl")
Expand Down
40 changes: 40 additions & 0 deletions src/optimizer.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""
AbstractDistributionFitOptimizer
OptimOptimizer <: AbstractDistributionFitOptimizer
set_optimizer(::AbstractDistributionFitOptimizer)

`DistributionFits.jl` uses the following interface to opimize an univariate
function `f` on bounded interval `[lower,upper]`:

optimize(f, ::AbstractDistributionFitOptimizer, lower, upper)

Returning an object with fields `minimizer` and `converged`.

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

By default, when the [Optim.jl](https://julianlsolvers.github.io/Optim.jl/stable/)
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

- explicitly invoke `using Optim`, or
- call `set_optimizer` with the concrete subtype of `AbstractDistributionFitOptimizer`
for which the corresponding optimize method is implemented.
"""
abstract type AbstractDistributionFitOptimizer end
struct OptimOptimizer <: AbstractDistributionFitOptimizer; end
struct NotSetOptimizer <: AbstractDistributionFitOptimizer; end


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


optimizer = NotSetOptimizer();
optimize(f, lower, upper) = optimize(f, optimizer, lower, upper)
set_optimizer(opt::AbstractDistributionFitOptimizer) = (global optimizer = opt)
7 changes: 7 additions & 0 deletions src/requires_optim.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
function optimize(f, ::OptimOptimizer, lower, upper)
result = Optim.optimize(f, lower, upper)
(;minimizer = result.minimizer, converged = result.converged, result)
end

@info "DistributionFits: setting OptimOptimizer"
DistributionFits.set_optimizer(OptimOptimizer())
3 changes: 3 additions & 0 deletions test/logitnormal.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,11 @@ end
d = fit(LogitNormal, qpu, qpl) # sort
@test quantile.(d, [qpl.p, qpu.p]) ≈ [qpl.q, qpu.q]
end;

@testset "fit to quantilepoint and mean" begin
# not implemented
end;

@testset "fit to quantilepoint and mode" begin
# see LogitNormals.jl
# d = LogitNormal(1,1)
Expand All @@ -59,6 +61,7 @@ end;
# dfit = fit_mode_quantile(LogitNormal, mode(d), qp)
# @test mode(dfit) ≈ mode(d) && quantile(dfit, qp.p) ≈ qp.q
end;

@testset "fit to quantilepoint and median" begin
d = LogitNormal(1,1)
qp = @qp(quantile(d,0.95),0.95)
Expand Down
11 changes: 5 additions & 6 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@ using DistributionFits
using Test
using Random: Random

# @testset "optimize error" begin
# @test_throws UndefVarError DistributionFits.optimize
# end
@testset "optimize error" begin
@test_throws Exception DistributionFits.optimize(x -> x*x, DistributionFits.optimizer, -1, 1)
end
using Optim: Optim, optimize
@testset "optimize set in __init__ after using Optim" begin
# set in __init__
@test DistributionFits.optimize == Optim.optimize
set_optimize(Optim.optimize)
@test DistributionFits.optimize == Optim.optimize
@test DistributionFits.optimizer isa DistributionFits.OptimOptimizer
end

const tests = [
Expand All @@ -19,6 +17,7 @@ const tests = [
"lognormal",
"logitnormal",
]
#tests = ["logitnormal"]


for t in tests
Expand Down