Repository: JuliaStats/MixedModels.jl (julia)

Issue:
parametricbootstrap does not support zerocorr model
Many thanks for making this great package!

When I tried to apply `parametricbootstrap()` to a `zerocorr` model, it raises the error:

`MethodError: no method matching MixedModels.MixedModelBootstrap(::Vector{NamedTuple{(:objective, :σ, :β, :se, :θ), Tuple{Float64, Float64, NamedTuple{(Symbol("(Intercept)"), :days), Tuple{Float64, Float64}}, StaticArrays.SVector{2, Float64}, StaticArrays.SVector{2, Float64}}}}, ::Vector{LinearAlgebra.Diagonal{Float64, Vector{Float64}}}, ::Vector{Vector{Int64}}, ::Vector{Float64}, ::NamedTuple{(:subj,), Tuple{Tuple{String, String}}})`


For example:
```julia
using MixedModels
using Random

lmm_zero = fit(LinearMixedModel,
	@formula(reaction ~ 1+days+zerocorr(1+days|subj)),
	MixedModels.dataset("sleepstudy"))
parametricbootstrap(MersenneTwister(42), 100, lmm_zero)
```
For this `lmm_zero` model, `parametricbootstrap` will raise the above error. However, when `zerocorr` is removed, it works well:
```julia
lmm = fit(LinearMixedModel,
	@formula(reaction ~ 1+days+(1+days|subj)),
	MixedModels.dataset("sleepstudy"))
parametricbootstrap(MersenneTwister(42), 100, lmm)
```

It will be great if `parametricbootstrap()` can also support `zerocorr` model.

Pull request:
- expand the type definition for the original fit (which I view as bugfix-esque, so patch)
- added a `Base.length` method (technically new functionality so minor release)

Closes #560

Patch:
diff --git a/NEWS.md b/NEWS.md
index e927a76ee..e48cc16bb 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,3 +1,8 @@
+MixedModels v4.2.0 Release Notes
+========================
+* Add support for zerocorr models to the bootstrap [#561]
+* Add a `Base.length(::MixedModelsFitCollection)` method  [#561]
+
 MixedModels v4.1.0 Release Notes
 ========================
 * Add support for specifying a fixed value of `σ`, the residual standard deviation,
@@ -286,3 +291,4 @@ Package dependencies
 [#551]: https://github.com/JuliaStats/MixedModels.jl/issues/551
 [#552]: https://github.com/JuliaStats/MixedModels.jl/issues/552
 [#553]: https://github.com/JuliaStats/MixedModels.jl/issues/553
+[#561]: https://github.com/JuliaStats/MixedModels.jl/issues/561
diff --git a/Project.toml b/Project.toml
index 22b755bef..4fe4abb4b 100644
--- a/Project.toml
+++ b/Project.toml
@@ -1,7 +1,7 @@
 name = "MixedModels"
 uuid = "ff71e718-51f3-5ec2-a782-8ffcbfa3c316"
 author = ["Phillip Alday <me@phillipalday.com>", "Douglas Bates <dmbates@gmail.com>", "Jose Bayoan Santiago Calderon <jbs3hp@virginia.edu>"]
-version = "4.1.1"
+version = "4.2.0"
 
 [deps]
 Arrow = "69666777-d1a9-59fb-9406-91d4454c9d45"
diff --git a/src/bootstrap.jl b/src/bootstrap.jl
index 61ac4de75..32cac5b15 100644
--- a/src/bootstrap.jl
+++ b/src/bootstrap.jl
@@ -28,7 +28,7 @@ and correlations of the random-effects terms.
 """
 struct MixedModelBootstrap{T<:AbstractFloat} <: MixedModelFitCollection{T}
     fits::Vector
-    λ::Vector{LowerTriangular{T,Matrix{T}}}
+    λ::Vector{<:Union{LowerTriangular{T,Matrix{T}},Diagonal{T,Vector{T}}}}
     inds::Vector{Vector{Int}}
     lowerbd::Vector{T}
     fcnames::NamedTuple
@@ -214,6 +214,8 @@ See also [`issingular(::MixedModel)`](@ref).
 """
 issingular(bsamp::MixedModelFitCollection) = map(θ -> any(θ .== bsamp.lowerbd), bsamp.θ)
 
+Base.length(x::MixedModelFitCollection) = length(x.fits)
+
 function Base.propertynames(bsamp::MixedModelFitCollection)
     return [
         :allpars,


Test patch:
diff --git a/test/bootstrap.jl b/test/bootstrap.jl
index 000fde641..64aa067c6 100644
--- a/test/bootstrap.jl
+++ b/test/bootstrap.jl
@@ -35,6 +35,11 @@ include("modelcache.jl")
         @test_throws DimensionMismatch refit!(fm, zeros(29); progress=false)
         # restore the original state
         refit!(fm, vec(float.(ds.yield)); progress=false)
+
+        @testset "zerocorr" begin
+            fmzc = models(:sleepstudy)[2]
+            @test length(simulate(fmzc)) == length(response(fmzc))
+        end
     end
     @testset "Poisson" begin
         center(v::AbstractVector) = v .- (sum(v) / length(v))
@@ -99,6 +104,12 @@ end
         @test sum(issingular(bsamp)) == sum(issingular(bsamp_threaded))
     end
 
+    @testset "zerocorr + Base.length" begin
+        fmzc = models(:sleepstudy)[2]
+        pbzc = parametricbootstrap(MersenneTwister(42), 5, fmzc)
+        @test length(pbzc) == 5
+    end
+
     @testset "Bernoulli simulate! and GLMM boostrap" begin
         contra = dataset(:contra)
         # need a model with fast=false to test that we only