From 3b565a2cc9a02f0bce2400d94b015b3267f448fb Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Mon, 4 Nov 2024 16:58:12 -0600 Subject: [PATCH 01/14] start work on restoreoptsum for glmm --- src/serialization.jl | 24 +++++++++++++++++++++++- test/pirls.jl | 15 +++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/serialization.jl b/src/serialization.jl index 52ec1dafe..8635c0352 100644 --- a/src/serialization.jl +++ b/src/serialization.jl @@ -27,7 +27,21 @@ function restoreoptsum!( if length(setdiff(allowed_missing, keys(dict))) > 1 # 1 because :lowerbd @warn "optsum was saved with an older version of MixedModels.jl: consider resaving." end + + # GLMM case with fast=slow + theta_beta_len = length(m.θ) + length(m.β) + if length(dict.initial) == theta_beta_len + resize!(ops.initial, theta_beta_len) + resize!(ops.final, theta_beta_len) + if length(ops.lowerbd) == length(m.θ) + prepend!(ops.lowerbd, fill(-Inf, length(m.β))) + end + end + if any(ops.lowerbd .> dict.initial) || any(ops.lowerbd .> dict.final) + @debug "" ops.lowerbd + @debug "" dict.initial + @debug "" dict.final throw(ArgumentError("initial or final parameters in io do not satisfy lowerbd")) end for fld in (:feval, :finitial, :fmin, :ftol_rel, :ftol_abs, :maxfeval, :nAGQ, :REML) @@ -68,6 +82,12 @@ function restoreoptsum!(m::LinearMixedModel{T}, filename; kwargs...) where {T} end end +function restoreoptsum!(m::GeneralizedLinearMixedModel, fname; kwargs...) + restoreoptsum!(m.LMM, fname; kwargs...) + deviance!(m) + return m +end + """ saveoptsum(io::IO, m::LinearMixedModel) saveoptsum(filename, m::LinearMixedModel) @@ -84,5 +104,7 @@ function saveoptsum(filename, m::LinearMixedModel) end end -# TODO: write methods for GLMM +function saveoptsum(io, m::GeneralizedLinearMixedModel) + return saveoptsum(io, m.LMM) +end # TODO, maybe: something nice for the MixedModelBootstrap diff --git a/test/pirls.jl b/test/pirls.jl index 96a005033..04a8373b1 100644 --- a/test/pirls.jl +++ b/test/pirls.jl @@ -239,3 +239,18 @@ end @test isapprox(first(gm5.β), -0.13860166843315044, atol=1.e-3) @test isapprox(last(gm5.β), -0.034414458364713504, atol=1.e-3) end + +@testset "GLMM saveoptsum" begin + cbpp = dataset(:cbpp) + gm_original = GeneralizedLinearMixedModel(first(gfms[:cbpp]), cbpp, Binomial(); wts=cbpp.hsz) + gm_restored = GeneralizedLinearMixedModel(first(gfms[:cbpp]), cbpp, Binomial(); wts=cbpp.hsz) + io = IOBuffer() + + fit!(gm_original; progress=false, nAGQ=1) + saveoptsum(io, gm_original) + + restoreoptsum!(gm_restored, seekstart(io)) + # println(read(seekstart(io), String)) + + save +end From 9622f369d2d058a9c2dbfbabfe3cca50184940f3 Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Mon, 4 Nov 2024 22:24:04 -0600 Subject: [PATCH 02/14] unfit! now wipes more state --- src/generalizedlinearmixedmodel.jl | 6 ++++-- src/linearmixedmodel.jl | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/generalizedlinearmixedmodel.jl b/src/generalizedlinearmixedmodel.jl index cfc06a778..8cf23aedd 100644 --- a/src/generalizedlinearmixedmodel.jl +++ b/src/generalizedlinearmixedmodel.jl @@ -767,7 +767,6 @@ function stderror!(v::AbstractVector{T}, m::GeneralizedLinearMixedModel{T}) wher end function unfit!(model::GeneralizedLinearMixedModel{T}) where {T} - deviance!(model, 1) reevaluateAend!(model.LMM) reterms = model.LMM.reterms @@ -775,11 +774,14 @@ function unfit!(model::GeneralizedLinearMixedModel{T}) where {T} # we need to reset optsum so that it # plays nice with the modifications fit!() does optsum.lowerbd = mapfoldl(lowerbd, vcat, reterms) - optsum.initial = mapfoldl(getθ, vcat, reterms) + # for variances (bounded at zero), we have ones, while + # for everything else (bounded at -Inf), we have zeros + optsum.initial = map(T ∘ iszero, optsum.lowerbd) optsum.final = copy(optsum.initial) optsum.xtol_abs = fill!(copy(optsum.initial), 1.0e-10) optsum.initial_step = T[] optsum.feval = -1 + deviance!(model, 1) return model end diff --git a/src/linearmixedmodel.jl b/src/linearmixedmodel.jl index 14f297835..6786f6f6f 100644 --- a/src/linearmixedmodel.jl +++ b/src/linearmixedmodel.jl @@ -1287,6 +1287,9 @@ Mark a model as unfitted. function unfit!(model::LinearMixedModel{T}) where {T} model.optsum.feval = -1 model.optsum.initial_step = T[] + # for variances (bounded at zero), we have ones, while + # for everything else (bounded at -Inf), we have zeros + model.optsum.initial .= map(T ∘ iszero, model.optsum.lowerbd) reevaluateAend!(model) return model From 6f14225be79c89310e3e5c29d77a0eb46a84172c Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Mon, 4 Nov 2024 22:24:34 -0600 Subject: [PATCH 03/14] restore and save optsum for GLMM --- src/optsummary.jl | 6 +++ src/serialization.jl | 105 +++++++++++++++++++++++++------------------ test/pirls.jl | 19 ++++++-- test/pls.jl | 2 +- 4 files changed, 84 insertions(+), 48 deletions(-) diff --git a/src/optsummary.jl b/src/optsummary.jl index 2ee69144c..864f09cd6 100644 --- a/src/optsummary.jl +++ b/src/optsummary.jl @@ -162,3 +162,9 @@ function _check_nlopt_return(ret, failure_modes=_NLOPT_FAILURE_MODES) @warn("NLopt optimization failure: $ret") end end + +function Base.:(==)(o1::OptSummary{T}, o2::OptSummary{T}) where {T} + return all(fieldnames(OptSummary)) do fn + return getfield(o1, fn) == getfield(o2, fn) + end +end diff --git a/src/serialization.jl b/src/serialization.jl index 8635c0352..b2a8a7ce2 100644 --- a/src/serialization.jl +++ b/src/serialization.jl @@ -1,15 +1,66 @@ """ - restoreoptsum!(m::LinearMixedModel, io::IO; atol::Real=0, rtol::Real=atol>0 ? 0 : √eps) - restoreoptsum!(m::LinearMixedModel, filename; atol::Real=0, rtol::Real=atol>0 ? 0 : √eps) + restoreoptsum!(m::MixedModel, io::IO; atol::Real=0, rtol::Real=atol>0 ? 0 : √eps) + restoreoptsum!(m::MixedModel, filename; atol::Real=0, rtol::Real=atol>0 ? 0 : √eps) Read, check, and restore the `optsum` field from a JSON stream or filename. """ -function restoreoptsum!( - m::LinearMixedModel{T}, io::IO; atol::Real=zero(T), - rtol::Real=atol > 0 ? zero(T) : √eps(T), -) where {T} +function restoreoptsum!(m::MixedModel, filename; kwargs...) + return open(filename, "r") do io + return restoreoptsum!(m, io; kwargs...) + end +end + +function restoreoptsum!(m::LinearMixedModel{T}, io::IO; + atol::Real=zero(T), + rtol::Real=atol > 0 ? zero(T) : √eps(T)) where {T} + dict = JSON3.read(io) + ops = restoreoptsum!(m.optsum, dict) + for (par, obj_at_par) in (:initial => :finitial, :final => :fmin) + if !isapprox( + objective(updateL!(setθ!(m, getfield(ops, par)))), getfield(ops, obj_at_par); rtol, atol + ) + throw(ArgumentError("model m at $par does not give stored $obj_at_par within given tolerances")) + end + end + return m +end + +function restoreoptsum!(m::GeneralizedLinearMixedModel{T}, io::IO; + atol::Real=zero(T), + rtol::Real=atol > 0 ? zero(T) : √eps(T)) where {T} dict = JSON3.read(io) ops = m.optsum + + # need to accommodate fast and slow fits + resize!(ops.initial, length(dict.initial)) + resize!(ops.final, length(dict.final)) + + theta_beta_len = length(m.θ) + length(m.β) + if length(dict.initial) == theta_beta_len # fast=false + if length(ops.lowerbd) == length(m.θ) + prepend!(ops.lowerbd, fill(-Inf, length(m.β))) + end + setpar! = setβθ! + varyβ = false + else # fast=true + setpar! = setθ! + varyβ = true + if length(ops.lowerbd) != length(m.θ) + deleteat!(ops.lowerbd, 1:length(m.β)) + end + end + restoreoptsum!(ops, dict) + for (par, obj_at_par) in (:initial => :finitial, :final => :fmin) + if !isapprox( + deviance(pirls!(setpar!(m, getfield(ops, par)), varyβ), dict.nAGQ), getfield(ops, obj_at_par); rtol, atol + ) + throw(ArgumentError("model m at $par does not give stored $obj_at_par within given tolerances")) + end + end + return m +end + +function restoreoptsum!(ops::OptSummary{T}, dict::AbstractDict) where {T} allowed_missing = ( :lowerbd, # never saved, -Inf not allowed in JSON :xtol_zero_abs, # added in v4.25.0 @@ -28,16 +79,6 @@ function restoreoptsum!( @warn "optsum was saved with an older version of MixedModels.jl: consider resaving." end - # GLMM case with fast=slow - theta_beta_len = length(m.θ) + length(m.β) - if length(dict.initial) == theta_beta_len - resize!(ops.initial, theta_beta_len) - resize!(ops.final, theta_beta_len) - if length(ops.lowerbd) == length(m.θ) - prepend!(ops.lowerbd, fill(-Inf, length(m.β))) - end - end - if any(ops.lowerbd .> dict.initial) || any(ops.lowerbd .> dict.final) @debug "" ops.lowerbd @debug "" dict.initial @@ -51,13 +92,6 @@ function restoreoptsum!( ops.xtol_rel = copy(dict.xtol_rel) copyto!(ops.initial, dict.initial) copyto!(ops.final, dict.final) - for (v, f) in (:initial => :finitial, :final => :fmin) - if !isapprox( - objective(updateL!(setθ!(m, getfield(ops, v)))), getfield(ops, f); rtol, atol - ) - throw(ArgumentError("model m at $v does not give stored $f")) - end - end ops.optimizer = Symbol(dict.optimizer) ops.returnvalue = Symbol(dict.returnvalue) # compatibility with fits saved before the introduction of various extensions @@ -73,38 +107,23 @@ function restoreoptsum!( else [(convert(Vector{T}, first(entry)), T(last(entry))) for entry in fitlog] end - return m -end - -function restoreoptsum!(m::LinearMixedModel{T}, filename; kwargs...) where {T} - open(filename, "r") do io - restoreoptsum!(m, io; kwargs...) - end -end - -function restoreoptsum!(m::GeneralizedLinearMixedModel, fname; kwargs...) - restoreoptsum!(m.LMM, fname; kwargs...) - deviance!(m) - return m + return ops end """ - saveoptsum(io::IO, m::LinearMixedModel) - saveoptsum(filename, m::LinearMixedModel) + saveoptsum(io::IO, m::MixedModel) + saveoptsum(filename, m::MixedModel) Save `m.optsum` (w/o the `lowerbd` field) in JSON format to an IO stream or a file The reason for omitting the `lowerbd` field is because it often contains `-Inf` values that are not allowed in JSON. """ -saveoptsum(io::IO, m::LinearMixedModel) = JSON3.write(io, m.optsum) -function saveoptsum(filename, m::LinearMixedModel) +saveoptsum(io::IO, m::MixedModel) = JSON3.write(io, m.optsum) +function saveoptsum(filename, m::MixedModel) open(filename, "w") do io saveoptsum(io, m) end end -function saveoptsum(io, m::GeneralizedLinearMixedModel) - return saveoptsum(io, m.LMM) -end # TODO, maybe: something nice for the MixedModelBootstrap diff --git a/test/pirls.jl b/test/pirls.jl index 04a8373b1..6b31245fb 100644 --- a/test/pirls.jl +++ b/test/pirls.jl @@ -244,13 +244,24 @@ end cbpp = dataset(:cbpp) gm_original = GeneralizedLinearMixedModel(first(gfms[:cbpp]), cbpp, Binomial(); wts=cbpp.hsz) gm_restored = GeneralizedLinearMixedModel(first(gfms[:cbpp]), cbpp, Binomial(); wts=cbpp.hsz) + fit!(gm_original; progress=false, nAGQ=1) + io = IOBuffer() - fit!(gm_original; progress=false, nAGQ=1) - saveoptsum(io, gm_original) + saveoptsum(seekstart(io), gm_original) + restoreoptsum!(gm_restored, seekstart(io)) + @test gm_original.optsum == gm_restored.optsum + @test deviance(gm_original) ≈ deviance(gm_restored) + refit!(gm_original; progress=false, nAGQ=3) + saveoptsum(seekstart(io), gm_original) restoreoptsum!(gm_restored, seekstart(io)) - # println(read(seekstart(io), String)) + @test gm_original.optsum == gm_restored.optsum + @test deviance(gm_original) ≈ deviance(gm_restored) - save + refit!(gm_original; progress=false, fast=true) + saveoptsum(seekstart(io), gm_original) + restoreoptsum!(gm_restored, seekstart(io)) + @test gm_original.optsum == gm_restored.optsum + @test deviance(gm_original) ≈ deviance(gm_restored) end diff --git a/test/pls.jl b/test/pls.jl index cc84b296e..1953236ea 100644 --- a/test/pls.jl +++ b/test/pls.jl @@ -530,7 +530,7 @@ end fm_mod = deepcopy(fm) fm_mod.optsum.fmin += 1 saveoptsum(seekstart(io), fm_mod) - @test_throws(ArgumentError("model m at final does not give stored fmin"), + @test_throws(ArgumentError("model m at final does not give stored fmin within given tolerances"), restoreoptsum!(m, seekstart(io))) restoreoptsum!(m, seekstart(io); atol=1) @test m.optsum.fmin - fm.optsum.fmin ≈ 1 From e1cfcaa468f1c3da54d17e759174907457668244 Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Mon, 4 Nov 2024 22:29:50 -0600 Subject: [PATCH 04/14] NEWS, version bump --- NEWS.md | 6 ++++++ Project.toml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 84cae83e9..23ee68b32 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,8 @@ +MixedModels v4.27.0 Release Notes +============================== +- `saveoptsum` and `restoreoptsum!` now support `GeneralizedLinearMixedModel`s [#791] +- `unfit!` (called internally by `refit!`) now does a better job of fully resetting the model state [#791] + MixedModels v4.26.1 Release Notes ============================== - lower and upper edges of profile confidence intervals for REML-fitted models are no longer flipped [#785] @@ -569,3 +574,4 @@ Package dependencies [#778]: https://github.com/JuliaStats/MixedModels.jl/issues/778 [#783]: https://github.com/JuliaStats/MixedModels.jl/issues/783 [#785]: https://github.com/JuliaStats/MixedModels.jl/issues/785 +[#791]: https://github.com/JuliaStats/MixedModels.jl/issues/791 diff --git a/Project.toml b/Project.toml index a08cd0c5f..42348b1eb 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "MixedModels" uuid = "ff71e718-51f3-5ec2-a782-8ffcbfa3c316" author = ["Phillip Alday ", "Douglas Bates ", "Jose Bayoan Santiago Calderon "] -version = "4.26.1" +version = "4.27.0" [deps] Arrow = "69666777-d1a9-59fb-9406-91d4454c9d45" From 69d9968cfd5ccb3ccf2a0797d6da88a932c823fb Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Mon, 4 Nov 2024 22:31:01 -0600 Subject: [PATCH 05/14] Blue Style --- src/serialization.jl | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/serialization.jl b/src/serialization.jl index b2a8a7ce2..93ed6bdc7 100644 --- a/src/serialization.jl +++ b/src/serialization.jl @@ -11,23 +11,28 @@ function restoreoptsum!(m::MixedModel, filename; kwargs...) end function restoreoptsum!(m::LinearMixedModel{T}, io::IO; - atol::Real=zero(T), - rtol::Real=atol > 0 ? zero(T) : √eps(T)) where {T} + atol::Real=zero(T), + rtol::Real=atol > 0 ? zero(T) : √eps(T)) where {T} dict = JSON3.read(io) ops = restoreoptsum!(m.optsum, dict) for (par, obj_at_par) in (:initial => :finitial, :final => :fmin) if !isapprox( - objective(updateL!(setθ!(m, getfield(ops, par)))), getfield(ops, obj_at_par); rtol, atol + objective(updateL!(setθ!(m, getfield(ops, par)))), getfield(ops, obj_at_par); + rtol, atol ) - throw(ArgumentError("model m at $par does not give stored $obj_at_par within given tolerances")) + throw( + ArgumentError( + "model m at $par does not give stored $obj_at_par within given tolerances", + ), + ) end end return m end function restoreoptsum!(m::GeneralizedLinearMixedModel{T}, io::IO; - atol::Real=zero(T), - rtol::Real=atol > 0 ? zero(T) : √eps(T)) where {T} + atol::Real=zero(T), + rtol::Real=atol > 0 ? zero(T) : √eps(T)) where {T} dict = JSON3.read(io) ops = m.optsum @@ -52,9 +57,14 @@ function restoreoptsum!(m::GeneralizedLinearMixedModel{T}, io::IO; restoreoptsum!(ops, dict) for (par, obj_at_par) in (:initial => :finitial, :final => :fmin) if !isapprox( - deviance(pirls!(setpar!(m, getfield(ops, par)), varyβ), dict.nAGQ), getfield(ops, obj_at_par); rtol, atol + deviance(pirls!(setpar!(m, getfield(ops, par)), varyβ), dict.nAGQ), + getfield(ops, obj_at_par); rtol, atol ) - throw(ArgumentError("model m at $par does not give stored $obj_at_par within given tolerances")) + throw( + ArgumentError( + "model m at $par does not give stored $obj_at_par within given tolerances", + ), + ) end end return m From fafc53485faaad7b9da04c562963d61965836a02 Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Tue, 5 Nov 2024 04:33:17 +0000 Subject: [PATCH 06/14] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/serialization.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/serialization.jl b/src/serialization.jl index 93ed6bdc7..166c42589 100644 --- a/src/serialization.jl +++ b/src/serialization.jl @@ -18,11 +18,11 @@ function restoreoptsum!(m::LinearMixedModel{T}, io::IO; for (par, obj_at_par) in (:initial => :finitial, :final => :fmin) if !isapprox( objective(updateL!(setθ!(m, getfield(ops, par)))), getfield(ops, obj_at_par); - rtol, atol + rtol, atol, ) throw( ArgumentError( - "model m at $par does not give stored $obj_at_par within given tolerances", + "model m at $par does not give stored $obj_at_par within given tolerances" ), ) end @@ -58,11 +58,11 @@ function restoreoptsum!(m::GeneralizedLinearMixedModel{T}, io::IO; for (par, obj_at_par) in (:initial => :finitial, :final => :fmin) if !isapprox( deviance(pirls!(setpar!(m, getfield(ops, par)), varyβ), dict.nAGQ), - getfield(ops, obj_at_par); rtol, atol + getfield(ops, obj_at_par); rtol, atol, ) throw( ArgumentError( - "model m at $par does not give stored $obj_at_par within given tolerances", + "model m at $par does not give stored $obj_at_par within given tolerances" ), ) end From ee70f7d4f03863cf5fde2836dce4243c716ebfe9 Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Tue, 5 Nov 2024 09:30:55 -0600 Subject: [PATCH 07/14] undo change to LMM? --- src/linearmixedmodel.jl | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/linearmixedmodel.jl b/src/linearmixedmodel.jl index 6786f6f6f..14f297835 100644 --- a/src/linearmixedmodel.jl +++ b/src/linearmixedmodel.jl @@ -1287,9 +1287,6 @@ Mark a model as unfitted. function unfit!(model::LinearMixedModel{T}) where {T} model.optsum.feval = -1 model.optsum.initial_step = T[] - # for variances (bounded at zero), we have ones, while - # for everything else (bounded at -Inf), we have zeros - model.optsum.initial .= map(T ∘ iszero, model.optsum.lowerbd) reevaluateAend!(model) return model From 725f18c598d3e3c1e782f908c266cb191f9c44fa Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Tue, 5 Nov 2024 09:49:35 -0600 Subject: [PATCH 08/14] news tweak --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 23ee68b32..5fc9f88f7 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,7 +1,7 @@ MixedModels v4.27.0 Release Notes ============================== - `saveoptsum` and `restoreoptsum!` now support `GeneralizedLinearMixedModel`s [#791] -- `unfit!` (called internally by `refit!`) now does a better job of fully resetting the model state [#791] +- `unfit!(::GeneralizedLinearMixedModel)` (called internally by `refit!`) now does a better job of fully resetting the model state [#791] MixedModels v4.26.1 Release Notes ============================== From c1c12e174be4718ad4b0b09a46a4e7e60aaf74ac Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Fri, 8 Nov 2024 03:41:33 +0000 Subject: [PATCH 09/14] He's always watching your style Co-authored-by: Alex Arslan --- src/serialization.jl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/serialization.jl b/src/serialization.jl index 166c42589..eddba322e 100644 --- a/src/serialization.jl +++ b/src/serialization.jl @@ -10,9 +10,10 @@ function restoreoptsum!(m::MixedModel, filename; kwargs...) end end -function restoreoptsum!(m::LinearMixedModel{T}, io::IO; - atol::Real=zero(T), - rtol::Real=atol > 0 ? zero(T) : √eps(T)) where {T} +function restoreoptsum!( + m::LinearMixedModel{T}, io::IO; atol::Real=zero(T), + rtol::Real=atol > 0 ? zero(T) : √eps(T) +) where {T} dict = JSON3.read(io) ops = restoreoptsum!(m.optsum, dict) for (par, obj_at_par) in (:initial => :finitial, :final => :fmin) From 0fa833da626e75c9428066cb4f50a3aa57e88e20 Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Fri, 8 Nov 2024 03:41:45 +0000 Subject: [PATCH 10/14] in the dark Co-authored-by: Alex Arslan --- src/serialization.jl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/serialization.jl b/src/serialization.jl index eddba322e..441f33915 100644 --- a/src/serialization.jl +++ b/src/serialization.jl @@ -31,9 +31,10 @@ function restoreoptsum!( return m end -function restoreoptsum!(m::GeneralizedLinearMixedModel{T}, io::IO; - atol::Real=zero(T), - rtol::Real=atol > 0 ? zero(T) : √eps(T)) where {T} +function restoreoptsum!( + m::GeneralizedLinearMixedModel{T}, io::IO; atol::Real=zero(T), + rtol::Real=atol > 0 ? zero(T) : √eps(T) +) where {T} dict = JSON3.read(io) ops = m.optsum From ec6a6d582ae48557fb96f128926769958d57ce5d Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Fri, 8 Nov 2024 03:42:43 +0000 Subject: [PATCH 11/14] tweaks Co-authored-by: Alex Arslan --- src/serialization.jl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/serialization.jl b/src/serialization.jl index 441f33915..51c635dca 100644 --- a/src/serialization.jl +++ b/src/serialization.jl @@ -23,7 +23,7 @@ function restoreoptsum!( ) throw( ArgumentError( - "model m at $par does not give stored $obj_at_par within given tolerances" + "model at $par does not match stored $obj_at_par within atol=$atol, rtol=$rtol" ), ) end @@ -64,7 +64,7 @@ function restoreoptsum!( ) throw( ArgumentError( - "model m at $par does not give stored $obj_at_par within given tolerances" + "model at $par does not match stored $obj_at_par within atol=$atol, rtol=$rtol" ), ) end @@ -92,9 +92,7 @@ function restoreoptsum!(ops::OptSummary{T}, dict::AbstractDict) where {T} end if any(ops.lowerbd .> dict.initial) || any(ops.lowerbd .> dict.final) - @debug "" ops.lowerbd - @debug "" dict.initial - @debug "" dict.final + @debug "" ops.lowerbd dict.initial dict.final throw(ArgumentError("initial or final parameters in io do not satisfy lowerbd")) end for fld in (:feval, :finitial, :fmin, :ftol_rel, :ftol_abs, :maxfeval, :nAGQ, :REML) From e7da51f0a931737543d03369859324ef85115470 Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Fri, 8 Nov 2024 03:51:37 +0000 Subject: [PATCH 12/14] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/serialization.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serialization.jl b/src/serialization.jl index 51c635dca..708504c18 100644 --- a/src/serialization.jl +++ b/src/serialization.jl @@ -12,7 +12,7 @@ end function restoreoptsum!( m::LinearMixedModel{T}, io::IO; atol::Real=zero(T), - rtol::Real=atol > 0 ? zero(T) : √eps(T) + rtol::Real=atol > 0 ? zero(T) : √eps(T), ) where {T} dict = JSON3.read(io) ops = restoreoptsum!(m.optsum, dict) @@ -33,7 +33,7 @@ end function restoreoptsum!( m::GeneralizedLinearMixedModel{T}, io::IO; atol::Real=zero(T), - rtol::Real=atol > 0 ? zero(T) : √eps(T) + rtol::Real=atol > 0 ? zero(T) : √eps(T), ) where {T} dict = JSON3.read(io) ops = m.optsum From 82c67b19582fc5aeab0124d93155ab195936b05e Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Thu, 7 Nov 2024 21:55:27 -0600 Subject: [PATCH 13/14] test update --- test/pls.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/pls.jl b/test/pls.jl index 1953236ea..4777d0728 100644 --- a/test/pls.jl +++ b/test/pls.jl @@ -530,8 +530,8 @@ end fm_mod = deepcopy(fm) fm_mod.optsum.fmin += 1 saveoptsum(seekstart(io), fm_mod) - @test_throws(ArgumentError("model m at final does not give stored fmin within given tolerances"), - restoreoptsum!(m, seekstart(io))) + @test_throws(ArgumentError("model at final does not give stored fmin within atol=0.0, rtol=1.0e-8"), + restoreoptsum!(m, seekstart(io); atol=0.0, rtol=1e-8)) restoreoptsum!(m, seekstart(io); atol=1) @test m.optsum.fmin - fm.optsum.fmin ≈ 1 From 61ed6f34965e4219c53c24cc7dd9dc2bdb617f1e Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Thu, 7 Nov 2024 22:02:10 -0600 Subject: [PATCH 14/14] :Facepalm: --- test/pls.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pls.jl b/test/pls.jl index 4777d0728..98d239ec3 100644 --- a/test/pls.jl +++ b/test/pls.jl @@ -530,7 +530,7 @@ end fm_mod = deepcopy(fm) fm_mod.optsum.fmin += 1 saveoptsum(seekstart(io), fm_mod) - @test_throws(ArgumentError("model at final does not give stored fmin within atol=0.0, rtol=1.0e-8"), + @test_throws(ArgumentError("model at final does not match stored fmin within atol=0.0, rtol=1.0e-8"), restoreoptsum!(m, seekstart(io); atol=0.0, rtol=1e-8)) restoreoptsum!(m, seekstart(io); atol=1) @test m.optsum.fmin - fm.optsum.fmin ≈ 1