Skip to content

Commit 34899cf

Browse files
authored
Small performance tweaks (#772)
* minor tweaks to improve type inference * another type restriction * fix NEWS and version number * fixes * simplify convenience constructors for OptSummary * fix use of type parameters in defaults * NEWS * tweaks * JuliaFormatter * abstract near-zero checks into configurable params * technically we've added a feature....
1 parent ce5b604 commit 34899cf

File tree

6 files changed

+61
-68
lines changed

6 files changed

+61
-68
lines changed

NEWS.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
1+
MixedModels v4.25 Release Notes
2+
==============================
3+
- Add type notations in `pwrss(::LinearMixedModel)` and `logdet(::LinearMixedModel)` to enhance type inference. [#773]
4+
- Take advantage of type parameter for `StatsAPI.weights(::LinearMixedModel{T})`. [#772]
5+
- Fix use of kwargs in `fit!((::LinearMixedModel)`: [#772]
6+
- user-specified `σ` is actually used, defaulting to existing value
7+
- `REML` defaults to model's already specified REML value.
8+
- Clean up code of keyword convenience constructor for `OptSummary`. [#772]
9+
- Refactor thresholding parameters for forcing near-zero parameter values into `OptSummary`. [#772]
10+
111
MixedModels v4.24.1 Release Notes
212
==============================
3-
Add type notations in `pwrss(::LinearMixedModel)` and `logdet(::LinearMixedModel)` to enhance type inference. [#773]
13+
- Re-export accidentally dropped export `lrtest`. [#769]
414

515
MixedModels v4.24.0 Release Notes
616
==============================
@@ -525,4 +535,6 @@ Package dependencies
525535
[#755]: https://github.com/JuliaStats/MixedModels.jl/issues/755
526536
[#756]: https://github.com/JuliaStats/MixedModels.jl/issues/756
527537
[#767]: https://github.com/JuliaStats/MixedModels.jl/issues/767
538+
[#769]: https://github.com/JuliaStats/MixedModels.jl/issues/769
539+
[#772]: https://github.com/JuliaStats/MixedModels.jl/issues/772
528540
[#773]: https://github.com/JuliaStats/MixedModels.jl/issues/773

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]>", "Jose Bayoan Santiago Calderon <[email protected]>"]
4-
version = "4.24.1"
4+
version = "4.25.0"
55

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

src/MixedModels.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ using LinearAlgebra: ldiv!, lmul!, logdet, mul!, norm, normalize, normalize!, qr
1919
using LinearAlgebra: rank, rdiv!, rmul!, svd, tril!
2020
using Markdown: Markdown
2121
using MixedModelsDatasets: dataset, datasets
22-
using NLopt: NLopt, Opt, ftol_abs, ftol_rel, initial_step, xtol_abs, xtol_rel
22+
using NLopt: NLopt, Opt
2323
using PooledArrays: PooledArrays, PooledArray
2424
using PrecompileTools: PrecompileTools, @setup_workload, @compile_workload
2525
using ProgressMeter: ProgressMeter, Progress, ProgressUnknown, finish!, next!

src/generalizedlinearmixedmodel.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,13 +309,13 @@ function StatsAPI.fit!(
309309
## check if very small parameter values bounded below by zero can be set to zero
310310
xmin_ = copy(xmin)
311311
for i in eachindex(xmin_)
312-
if iszero(optsum.lowerbd[i]) && zero(T) < xmin_[i] < T(0.001)
312+
if iszero(optsum.lowerbd[i]) && zero(T) < xmin_[i] < optsum.xtol_zero_abs
313313
xmin_[i] = zero(T)
314314
end
315315
end
316316
loglength = length(fitlog)
317317
if xmin xmin_
318-
if (zeroobj = obj(xmin_, T[])) (fmin + 1.e-5)
318+
if (zeroobj = obj(xmin_, T[])) (fmin + optsum.ftol_zero_abs)
319319
fmin = zeroobj
320320
copyto!(xmin, xmin_)
321321
elseif length(fitlog) > loglength

src/linearmixedmodel.jl

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ struct LinearMixedModel{T<:AbstractFloat} <: MixedModel{T}
3636
sqrtwts::Vector{T}
3737
parmap::Vector{NTuple{3,Int}}
3838
dims::NamedTuple{(:n, :p, :nretrms),NTuple{3,Int}}
39-
A::Vector{AbstractMatrix{T}} # cross-product blocks
40-
L::Vector{AbstractMatrix{T}}
39+
A::Vector{<:AbstractMatrix{T}} # cross-product blocks
40+
L::Vector{<:AbstractMatrix{T}}
4141
optsum::OptSummary{T}
4242
end
4343

@@ -175,7 +175,7 @@ function LinearMixedModel(
175175
A, L = createAL(reterms, Xy)
176176
lbd = foldl(vcat, lowerbd(c) for c in reterms)
177177
θ = foldl(vcat, getθ(c) for c in reterms)
178-
optsum = OptSummary(θ, lbd, :LN_BOBYQA; ftol_rel=T(1.0e-12), ftol_abs=T(1.0e-8))
178+
optsum = OptSummary(θ, lbd)
179179
optsum.sigma = isnothing(σ) ? nothing : T(σ)
180180
fill!(optsum.xtol_abs, 1.0e-10)
181181
return LinearMixedModel(
@@ -408,7 +408,7 @@ function createAL(reterms::Vector{<:AbstractReMat{T}}, Xy::FeMat{T}) where {T}
408408
end
409409
end
410410
end
411-
return A, L
411+
return identity.(A), identity.(L)
412412
end
413413

414414
StatsAPI.deviance(m::LinearMixedModel) = objective(m)
@@ -431,8 +431,8 @@ function feL(m::LinearMixedModel)
431431
end
432432

433433
"""
434-
fit!(m::LinearMixedModel; progress::Bool=true, REML::Bool=false,
435-
σ::Union{Real, Nothing}=nothing,
434+
fit!(m::LinearMixedModel; progress::Bool=true, REML::Bool=m.optsum.REML,
435+
σ::Union{Real, Nothing}=m.optsum.sigma,
436436
thin::Int=typemax(Int))
437437
438438
Optimize the objective of a `LinearMixedModel`. When `progress` is `true` a
@@ -445,8 +445,8 @@ saved in `m.optsum.fitlog`.
445445
function StatsAPI.fit!(
446446
m::LinearMixedModel{T};
447447
progress::Bool=true,
448-
REML::Bool=false,
449-
σ::Union{Real,Nothing}=nothing,
448+
REML::Bool=m.optsum.REML,
449+
σ::Union{Real,Nothing}=m.optsum.sigma,
450450
thin::Int=typemax(Int),
451451
) where {T}
452452
optsum = m.optsum
@@ -461,6 +461,7 @@ function StatsAPI.fit!(
461461
end
462462
opt = Opt(optsum)
463463
optsum.REML = REML
464+
optsum.sigma = σ
464465
prog = ProgressUnknown(; desc="Minimizing", showspeed=true)
465466
# start from zero for the initial call to obj before optimization
466467
iter = 0
@@ -511,13 +512,13 @@ function StatsAPI.fit!(
511512
xmin_ = copy(xmin)
512513
lb = optsum.lowerbd
513514
for i in eachindex(xmin_)
514-
if iszero(lb[i]) && zero(T) < xmin_[i] < T(0.001)
515+
if iszero(lb[i]) && zero(T) < xmin_[i] < optsum.xtol_zero_abs
515516
xmin_[i] = zero(T)
516517
end
517518
end
518519
loglength = length(fitlog)
519520
if xmin xmin_
520-
if (zeroobj = obj(xmin_, T[])) (fmin + 1.e-5)
521+
if (zeroobj = obj(xmin_, T[])) (fmin + optsum.ftol_zero_abs)
521522
fmin = zeroobj
522523
copyto!(xmin, xmin_)
523524
elseif length(fitlog) > loglength

src/optsummary.jl

Lines changed: 33 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -19,75 +19,55 @@ Summary of an `NLopt` optimization
1919
* `feval`: the number of function evaluations
2020
* `optimizer`: the name of the optimizer used, as a `Symbol`
2121
* `returnvalue`: the return value, as a `Symbol`
22+
* `xtol_zero_abs`: the tolerance for a near zero parameter to be considered practically zero
23+
* `ftol_zero_abs`: the tolerance for change in the objective for setting a near zero parameter to zero
24+
* `fitlog`: A vector of tuples of parameter and objectives values from steps in the optimization
2225
* `nAGQ`: number of adaptive Gauss-Hermite quadrature points in deviance evaluation for GLMMs
2326
* `REML`: use the REML criterion for LMM fits
2427
* `sigma`: a priori value for the residual standard deviation for LMM
25-
* `fitlog`: A vector of tuples of parameter and objectives values from steps in the optimization
2628
27-
The latter four fields are MixedModels functionality and not related directly to the `NLopt` package or algorithms.
29+
The last three fields are MixedModels functionality and not related directly to the `NLopt` package or algorithms.
2830
2931
!!! note
3032
The internal storage of the parameter values within `fitlog` may change in
3133
the future to use a different subtype of `AbstractVector` (e.g., `StaticArrays.SVector`)
3234
for each snapshot without being considered a breaking change.
3335
"""
34-
mutable struct OptSummary{T<:AbstractFloat}
36+
Base.@kwdef mutable struct OptSummary{T<:AbstractFloat}
3537
initial::Vector{T}
3638
lowerbd::Vector{T}
37-
finitial::T
38-
ftol_rel::T
39-
ftol_abs::T
40-
xtol_rel::T
41-
xtol_abs::Vector{T}
42-
initial_step::Vector{T}
43-
maxfeval::Int
44-
maxtime::T
45-
feval::Int
46-
final::Vector{T}
47-
fmin::T
48-
optimizer::Symbol
49-
returnvalue::Symbol
50-
nAGQ::Integer # don't really belong here but I needed a place to store them
51-
REML::Bool
52-
sigma::Union{T,Nothing}
53-
fitlog::Vector{Tuple{Vector{T},T}} # not SVector because we would need to parameterize on size (which breaks GLMM)
39+
# the @kwdef macro isn't quite smart enough for us to use the type parameter
40+
# for the default values, but we can fake it
41+
finitial::T = Inf * one(eltype(initial))
42+
ftol_rel::T = eltype(initial)(1.0e-12)
43+
ftol_abs::T = eltype(initial)(1.0e-8)
44+
xtol_rel::T = zero(eltype(initial))
45+
xtol_abs::Vector{T} = zero(initial) .+ 1e-10
46+
initial_step::Vector{T} = empty(initial)
47+
maxfeval::Int = -1
48+
maxtime::T = -one(eltype(initial))
49+
feval::Int = -1
50+
final::Vector{T} = copy(initial)
51+
fmin::T = Inf * one(eltype(initial))
52+
optimizer::Symbol = :LN_BOBYQA
53+
returnvalue::Symbol = :FAILURE
54+
xtol_zero_abs::T = eltype(initial)(0.001)
55+
ftol_zero_abs::T = eltype(initial)(1.e-5)
56+
# not SVector because we would need to parameterize on size (which breaks GLMM)
57+
fitlog::Vector{Tuple{Vector{T},T}} = [(initial, fmin)]
58+
# don't really belong here but I needed a place to store them
59+
nAGQ::Int = 1
60+
REML::Bool = false
61+
sigma::Union{T,Nothing} = nothing
5462
end
5563

5664
function OptSummary(
5765
initial::Vector{T},
58-
lowerbd::Vector{T},
59-
optimizer::Symbol;
60-
ftol_rel::T=zero(T),
61-
ftol_abs::T=zero(T),
62-
xtol_rel::T=zero(T),
63-
xtol_abs::Vector{T}=zero(initial) .+ 1e-10,
64-
initial_step::Vector{T}=T[],
65-
maxfeval=-1,
66-
maxtime=T(-1),
67-
) where {T<:AbstractFloat}
68-
fitlog = [(initial, T(Inf))]
69-
70-
return OptSummary(
71-
initial,
72-
lowerbd,
73-
T(Inf),
74-
ftol_rel,
75-
ftol_abs,
76-
xtol_rel,
77-
xtol_abs,
78-
initial_step,
79-
maxfeval,
80-
maxtime,
81-
-1,
82-
copy(initial),
83-
T(Inf),
84-
optimizer,
85-
:FAILURE,
86-
1,
87-
false,
88-
nothing,
89-
fitlog,
90-
)
66+
lowerbd::Vector{S},
67+
optimizer::Symbol=:LN_BOBYQA; kwargs...,
68+
) where {T<:AbstractFloat,S<:AbstractFloat}
69+
TS = promote_type(T, S)
70+
return OptSummary{TS}(; initial, lowerbd, optimizer, kwargs...)
9171
end
9272

9373
"""

0 commit comments

Comments
 (0)