Skip to content

Commit 1ea4083

Browse files
authored
populate optsum in prfit! (#801)
* populate optsum in prfit! * dummy optimizer value * set finitial * finish progress meter * add initial values to fitlog * disable progress in prfit! test
1 parent 3f81dc2 commit 1ea4083

File tree

5 files changed

+72
-4
lines changed

5 files changed

+72
-4
lines changed

NEWS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
MixedModels v4.29.1 Release Notes
2+
==============================
3+
- Populate `optsum` in `prfit!` call. [#801]
4+
15
MixedModels v4.29.0 Release Notes
26
==============================
37
- Testbed for experimental support for using PRIMA as an optimization backend introduced via the experimental `prfit!` function. [#799]
@@ -590,3 +594,4 @@ Package dependencies
590594
[#792]: https://github.com/JuliaStats/MixedModels.jl/issues/792
591595
[#795]: https://github.com/JuliaStats/MixedModels.jl/issues/795
592596
[#799]: https://github.com/JuliaStats/MixedModels.jl/issues/799
597+
[#801]: https://github.com/JuliaStats/MixedModels.jl/issues/801

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "MixedModels"
22
uuid = "ff71e718-51f3-5ec2-a782-8ffcbfa3c316"
33
author = ["Phillip Alday <[email protected]>", "Douglas Bates <[email protected]>"]
4-
version = "4.29.0"
4+
version = "4.29.1"
55

66
[deps]
77
Arrow = "69666777-d1a9-59fb-9406-91d4454c9d45"

ext/MixedModelsPRIMAExt.jl

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,54 @@
11
module MixedModelsPRIMAExt
22

33
using MixedModels: MixedModels, LinearMixedModel, objective!
4+
using MixedModels: ProgressMeter, ProgressUnknown
45
using PRIMA: PRIMA
56

6-
function MixedModels.prfit!(m::LinearMixedModel)
7-
PRIMA.bobyqa!(objective!(m), copy(m.optsum.initial); xl=m.optsum.lowerbd)
7+
function MixedModels.prfit!(m::LinearMixedModel;
8+
progress::Bool=true,
9+
REML::Bool=m.optsum.REML,
10+
σ::Union{Real,Nothing}=m.optsum.sigma,
11+
thin::Int=1)
12+
optsum = m.optsum
13+
copyto!(optsum.final, optsum.initial)
14+
optsum.REML = REML
15+
optsum.sigma = σ
16+
optsum.finitial = objective!(m, optsum.initial)
17+
18+
prog = ProgressUnknown(; desc="Minimizing", showspeed=true)
19+
# start from zero for the initial call to obj before optimization
20+
iter = 0
21+
fitlog = empty!(optsum.fitlog)
22+
function obj(x)
23+
iter += 1
24+
val = if isone(iter) && x == optsum.initial
25+
optsum.finitial
26+
else
27+
try
28+
objective!(m, x)
29+
catch ex
30+
# This can happen when the optimizer drifts into an area where
31+
# there isn't enough shrinkage. Why finitial? Generally, it will
32+
# be the (near) worst case scenario value, so the optimizer won't
33+
# view it as an optimum. Using Inf messes up the quadratic
34+
# approximation in BOBYQA.
35+
ex isa PosDefException || rethrow()
36+
optsum.finitial
37+
end
38+
end
39+
progress && ProgressMeter.next!(prog; showvalues=[(:objective, val)])
40+
if isone(iter) || iszero(rem(iter, thin))
41+
push!(fitlog, (copy(x), val))
42+
end
43+
return val
44+
end
45+
46+
ProgressMeter.finish!(prog)
47+
info = PRIMA.bobyqa!(obj, optsum.final; xl=m.optsum.lowerbd)
48+
optsum.feval = info.nf
49+
optsum.fmin = info.fx
50+
optsum.returnvalue = Symbol(info.status)
51+
optsum.optimizer = :PRIMA_BOBYQA
852
return m
953
end
1054

src/prima.jl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,25 @@ of the BOBYQA optimizer.
1313
and potentially breaking changes it would take to fully replace the current NLopt
1414
backend with a PRIMA backend or a backend supporting a range of optimizers.
1515
16+
!!! note "OptSummary"
17+
As part of this experimental foray, the structure of [`OptSummary`](@ref) is
18+
not changed. This means that some fields of `OptSummary` are inaccurate when
19+
examining a model fit with PRIMA. The following fields are unused with PRIMA
20+
fits:
21+
22+
- all tolerances (`ftol_rel`, `ftol_abs`, `xtol_rel`, `xtol_abs`)
23+
- optimization timeouts (`maxfeval`, `maxtime`)
24+
- `initial_step`
25+
26+
The following fields have a different meaning when used with PRIMA:
27+
28+
- `returnvalue` is populated with a symbol representing the PRIMA
29+
return value, which PRIMA represents as an enum.
30+
- `optimizer` is populated with a dummy value, indicating that a model was
31+
fit with PRIMA. If you wish to refit the model with the NLOpt backend,
32+
you will need to update the field with an appropriate NLOpt optimizer,
33+
e.g. `:LN_BOBYQA`
34+
1635
!!! note "Package extension"
1736
In order to reduce the dependency burden, all methods of this function are
1837
implemented in a package extension and are only defined when PRIMA.jl is loaded

test/prima.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ include("modelcache.jl")
77
# model = first(models(:sleepstudy))
88

99
@testset "formula($model)" for model in models(:sleepstudy)
10-
prmodel = prfit!(LinearMixedModel(formula(model), dataset(:sleepstudy)))
10+
prmodel = prfit!(LinearMixedModel(formula(model), dataset(:sleepstudy)); progress=false)
1111

1212
@test isapprox(loglikelihood(model), loglikelihood(prmodel))
1313
end

0 commit comments

Comments
 (0)