diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d9b8cee..3d572de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,19 +21,19 @@ jobs: fail-fast: false matrix: version: - - '1.8' + - 'min' - '1' - 'nightly' os: - ubuntu-latest - arch: - - x64 steps: - - uses: actions/checkout@v2 - - uses: julia-actions/setup-julia@v1 + - uses: actions/checkout@v5 + - uses: julia-actions/setup-julia@v2 with: version: ${{ matrix.version }} - arch: ${{ matrix.arch }} + - uses: julia-actions/cache@v2 + with: + cache-compiled: "true" - uses: julia-actions/cache@v1 - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-runtest@v1 diff --git a/.github/workflows/documenter.yml b/.github/workflows/documenter.yml index 9ff0cf9..82de86d 100644 --- a/.github/workflows/documenter.yml +++ b/.github/workflows/documenter.yml @@ -20,11 +20,11 @@ jobs: name: Documentation runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: julia-actions/setup-julia@v1 + - uses: actions/checkout@v5 + - uses: julia-actions/setup-julia@v2 + - uses: julia-actions/cache@v2 with: - version: 1 - - uses: julia-actions/cache@v1 + cache-compiled: "true" - uses: julia-actions/julia-buildpkg@latest - uses: julia-actions/julia-docdeploy@latest env: diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index e84e538..35bba5f 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -1,38 +1,34 @@ -name: YASG-enforcer +name: Style-Enforcer on: - push: - branches: - - 'main' - tags: '*' pull_request: types: [opened, synchronize, reopened, ready_for_review] - # note: keep in sync with `format/run.jl` - paths: - - 'src/**' - - 'test/**' - - 'docs/**' - - '.github/workflows/style.yml' - - 'format/**' + paths-ignore: + - 'README.md' + - '.gitignore' jobs: format-check: - name: YASG Enforcement (Julia ${{ matrix.julia-version }} - ${{ github.event_name }}) - # Run on push's or non-draft PRs - if: (github.event_name == 'push') || (github.event.pull_request.draft == false) + name: Style Enforcement runs-on: ubuntu-latest strategy: matrix: - julia-version: [1.7] + julia-version: [min] steps: + - uses: actions/checkout@v5 - uses: julia-actions/setup-julia@latest with: version: ${{ matrix.julia-version }} - - uses: actions/checkout@v1 - - name: Instantiate `format` environment and format + - name: Install JuliaFormatter + shell: julia --project=@format --color=yes {0} run: | - julia --project=format -e 'using Pkg; Pkg.instantiate()' - julia --project=format 'format/run.jl' + using Pkg + Pkg.add(PackageSpec(; name="JuliaFormatter", version="1")) + - name: Check formatting + shell: julia --project=@format --color=yes {0} + run: | + using JuliaFormatter + format(".", YASStyle(); verbose=true) || exit(1) + # Add formatting suggestions to non-draft PRs even if "Check formatting" fails - uses: reviewdog/action-suggester@v1 - if: github.event_name == 'pull_request' + if: ${{ !cancelled() && github.event.pull_request.draft == false }} with: tool_name: JuliaFormatter - fail_on_error: true diff --git a/.gitignore b/.gitignore index 29126e4..defe761 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ docs/site/ # committed for packages, but should be committed for applications that require a static # environment. Manifest.toml +Manifest-v*.toml diff --git a/Project.toml b/Project.toml index 47b2a98..27b1bd6 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "MixedModelsSerialization" uuid = "b32ace64-3998-4ca6-afd0-a0db4a0482b2" authors = ["Phillip Alday "] -version = "0.1.1" +version = "0.2.0" [deps] GLM = "38e38edf-8417-5370-95a0-9cbb8c7f171a" @@ -14,12 +14,32 @@ StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" StatsFuns = "4c63d2b9-4356-54db-8cca-17b64c39e42c" StatsModels = "3eaba693-59b7-5ba5-a881-562e759f1c8d" +[weakdeps] +DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" +Effects = "8f03c58b-bd97-4933-a826-f71b64d2cca2" + +[extensions] +MixedModelsSerializationEffectsExt = ["DataFrames", "Effects"] + [compat] +DataFrames = "1" +Effects = "1" GLM = "1" JLD2 = "0.4.22" -MixedModels = "4" +MixedModels = "5.0.3" StatsAPI = "1" StatsBase = "0.33, 0.34" StatsFuns = "1" StatsModels = "0.7" -julia = "1.8" +julia = "1.10" + +[extras] +Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" +Effects = "8f03c58b-bd97-4933-a826-f71b64d2cca2" +MixedModelsDatasets = "7e9fb7ac-9f67-43bf-b2c8-96ba0796cbb6" +Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +TestSetExtensions = "98d24dd4-01ad-11ea-1b02-c9a08f80db04" + +[targets] +test = ["Aqua", "Effects", "MixedModelsDatasets", "Suppressor", "Test", "TestSetExtensions"] diff --git a/ext/MixedModelsSerializationEffectsExt.jl b/ext/MixedModelsSerializationEffectsExt.jl new file mode 100644 index 0000000..7208199 --- /dev/null +++ b/ext/MixedModelsSerializationEffectsExt.jl @@ -0,0 +1,15 @@ +module MixedModelsSerializationEffectsExt + +using DataFrames +using Effects +using MixedModels +using MixedModelsSerialization +using StatsBase + +function Effects.effects!(reference_grid::DataFrame, model::LinearMixedModelSummary; + kwargs...) + # we don't want to use the MixedModel specific version since we don't store all the things + return @invoke effects!(reference_grid::DataFrame, model::RegressionModel; kwargs...) +end + +end # module diff --git a/format/Manifest.toml b/format/Manifest.toml deleted file mode 100644 index 49f0c9e..0000000 --- a/format/Manifest.toml +++ /dev/null @@ -1,197 +0,0 @@ -# This file is machine-generated - editing it directly is not advised - -julia_version = "1.7.3" -manifest_format = "2.0" - -[[deps.ArgTools]] -uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" - -[[deps.Artifacts]] -uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" - -[[deps.Base64]] -uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" - -[[deps.CSTParser]] -deps = ["Tokenize"] -git-tree-sha1 = "3ddd48d200eb8ddf9cb3e0189fc059fd49b97c1f" -uuid = "00ebfdb7-1f24-5e51-bd34-a7502290713f" -version = "3.3.6" - -[[deps.CommonMark]] -deps = ["Crayons", "JSON", "URIs"] -git-tree-sha1 = "4cd7063c9bdebdbd55ede1af70f3c2f48fab4215" -uuid = "a80b9123-70ca-4bc0-993e-6e3bcb318db6" -version = "0.8.6" - -[[deps.Compat]] -deps = ["Dates", "LinearAlgebra", "UUIDs"] -git-tree-sha1 = "924cdca592bc16f14d2f7006754a621735280b74" -uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "4.1.0" - -[[deps.CompilerSupportLibraries_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" - -[[deps.Crayons]] -git-tree-sha1 = "249fe38abf76d48563e2f4556bebd215aa317e15" -uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f" -version = "4.1.1" - -[[deps.DataStructures]] -deps = ["Compat", "InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "d1fff3a548102f48987a52a2e0d114fa97d730f0" -uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.18.13" - -[[deps.Dates]] -deps = ["Printf"] -uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" - -[[deps.Downloads]] -deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] -uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" - -[[deps.FileWatching]] -uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" - -[[deps.InteractiveUtils]] -deps = ["Markdown"] -uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" - -[[deps.JSON]] -deps = ["Dates", "Mmap", "Parsers", "Unicode"] -git-tree-sha1 = "3c837543ddb02250ef42f4738347454f95079d4e" -uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" -version = "0.21.3" - -[[deps.JuliaFormatter]] -deps = ["CSTParser", "CommonMark", "DataStructures", "Pkg", "Tokenize"] -git-tree-sha1 = "6f13ba89febc5c12f882902e1f5dcd11a8913fa5" -uuid = "98e50ef6-434e-11e9-1051-2b60c6c9e899" -version = "1.0.7" - -[[deps.LibCURL]] -deps = ["LibCURL_jll", "MozillaCACerts_jll"] -uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" - -[[deps.LibCURL_jll]] -deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] -uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" - -[[deps.LibGit2]] -deps = ["Base64", "NetworkOptions", "Printf", "SHA"] -uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" - -[[deps.LibSSH2_jll]] -deps = ["Artifacts", "Libdl", "MbedTLS_jll"] -uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" - -[[deps.Libdl]] -uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" - -[[deps.LinearAlgebra]] -deps = ["Libdl", "libblastrampoline_jll"] -uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" - -[[deps.Logging]] -uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" - -[[deps.Markdown]] -deps = ["Base64"] -uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" - -[[deps.MbedTLS_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" - -[[deps.Mmap]] -uuid = "a63ad114-7e13-5084-954f-fe012c677804" - -[[deps.MozillaCACerts_jll]] -uuid = "14a3606d-f60d-562e-9121-12d972cd8159" - -[[deps.NetworkOptions]] -uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" - -[[deps.OpenBLAS_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] -uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" - -[[deps.OrderedCollections]] -git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" -uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" -version = "1.4.1" - -[[deps.Parsers]] -deps = ["Dates"] -git-tree-sha1 = "0044b23da09b5608b4ecacb4e5e6c6332f833a7e" -uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "2.3.2" - -[[deps.Pkg]] -deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] -uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" - -[[deps.Printf]] -deps = ["Unicode"] -uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" - -[[deps.REPL]] -deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] -uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" - -[[deps.Random]] -deps = ["SHA", "Serialization"] -uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" - -[[deps.SHA]] -uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" - -[[deps.Serialization]] -uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" - -[[deps.Sockets]] -uuid = "6462fe0b-24de-5631-8697-dd941f90decc" - -[[deps.TOML]] -deps = ["Dates"] -uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" - -[[deps.Tar]] -deps = ["ArgTools", "SHA"] -uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" - -[[deps.Tokenize]] -git-tree-sha1 = "2b3af135d85d7e70b863540160208fa612e736b9" -uuid = "0796e94c-ce3b-5d07-9a54-7f471281c624" -version = "0.5.24" - -[[deps.URIs]] -git-tree-sha1 = "e59ecc5a41b000fa94423a578d29290c7266fc10" -uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" -version = "1.4.0" - -[[deps.UUIDs]] -deps = ["Random", "SHA"] -uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" - -[[deps.Unicode]] -uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" - -[[deps.Zlib_jll]] -deps = ["Libdl"] -uuid = "83775a58-1f1d-513f-b197-d71354ab007a" - -[[deps.libblastrampoline_jll]] -deps = ["Artifacts", "Libdl", "OpenBLAS_jll"] -uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" - -[[deps.nghttp2_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" - -[[deps.p7zip_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" diff --git a/format/Project.toml b/format/Project.toml deleted file mode 100644 index f3aab8b..0000000 --- a/format/Project.toml +++ /dev/null @@ -1,2 +0,0 @@ -[deps] -JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899" diff --git a/format/run.jl b/format/run.jl deleted file mode 100644 index 938e45f..0000000 --- a/format/run.jl +++ /dev/null @@ -1,20 +0,0 @@ -using JuliaFormatter - -function main() - perfect = true - # note: keep in sync with `.github/workflows/format-check.yml` - for d in ["src/", "test/", "docs/"] - @info "...linting $d ..." - dir_perfect = format(d; style=YASStyle()) - perfect = perfect && dir_perfect - end - if perfect - @info "Linting complete - no files altered" - else - @info "Linting complete - files altered" - run(`git status`) - end - return nothing -end - -main() diff --git a/src/MixedModelsSerialization.jl b/src/MixedModelsSerialization.jl index 0a344f9..4295c1a 100644 --- a/src/MixedModelsSerialization.jl +++ b/src/MixedModelsSerialization.jl @@ -12,6 +12,7 @@ using StatsModels using JLD2 using Base: Ryu +using MixedModels: lowerbd using StatsModels: TupleTerm export MixedModelSummary, LinearMixedModelSummary @@ -77,6 +78,7 @@ struct LinearMixedModelSummary{T<:AbstractFloat} <: MixedModelSummary{T} varcorr::VarCorr formula::FormulaTerm optsum::OptSummary{T} + lowerbd::Vector{T} objective::T # we can compute deviance, AIC, AICc, BIC from this varcov::Matrix{T} pca::NamedTuple # MixedModels.PCA @@ -103,8 +105,7 @@ function LinearMixedModelSummary(m::LinearMixedModel{T}) where {T} pca = MixedModels.PCA(m) return LinearMixedModelSummary{T}(β, cnames, se, θ, dims, reterms, varcorr, formula, - optsum, - obj, varcov, pca) + optsum, lowerbd(m), obj, varcov, pca) end # we can skip store this explicitly if we store @@ -234,12 +235,18 @@ end ##### MixedModels ##### -# freebies: MixedModels.issingular +function MixedModels.issingular(mms::MixedModelSummary; atol::Real=0, + rtol::Real=atol > 0 ? 0 : √eps()) + return any(zip(lowerbd(mms), mms.θ)) do (x, y) + return isapprox(x, y; atol, rtol) + end +end + # MixedModels.fixef[names] # MixedModels.nθ # MixedModels.nlevs MixedModels.fnames(mms::MixedModelSummary) = keys(mms.pca) -MixedModels.lowerbd(mms::MixedModelSummary) = mms.optsum.lowerbd +MixedModels.lowerbd(mms::MixedModelSummary) = mms.lowerbd MixedModels.objective(mms::MixedModelSummary) = mms.objective MixedModels.VarCorr(mms::MixedModelSummary) = mms.varcorr # only stored on the covariance scale diff --git a/test/Project.toml b/test/Project.toml deleted file mode 100644 index d67194a..0000000 --- a/test/Project.toml +++ /dev/null @@ -1,7 +0,0 @@ -[deps] -Effects = "8f03c58b-bd97-4933-a826-f71b64d2cca2" -MixedModels = "ff71e718-51f3-5ec2-a782-8ffcbfa3c316" -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - -[compat] -Effects = "1" diff --git a/test/runtests.jl b/test/runtests.jl index 327921e..2d314b3 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,25 +1,8 @@ -using Effects -using MixedModels -using MixedModelsSerialization -using Test - -using MixedModels: dataset - -kb07 = dataset(:kb07) -progress = false - -fm1 = fit(MixedModel, - @formula(rt_trunc ~ 1 + spkr * prec * load + - (1 + spkr + prec | subj) + - (1 + load | item)), kb07; progress) -mms = MixedModelSummary(fm1) - -fm2 = fit(MixedModel, - @formula(reaction ~ 1 + days + (1 | subj)), - dataset(:sleepstudy); - progress, - REML=true) -mms2 = MixedModelSummary(fm2) +include("set_up_tests.jl") + +@testset ExtendedTestSet "Aqua" begin + Aqua.test_all(Effects; ambiguities=false) +end @testset "StatsAPI" begin statsapi = [coef, coefnames, diff --git a/test/set_up_tests.jl b/test/set_up_tests.jl new file mode 100644 index 0000000..90216e0 --- /dev/null +++ b/test/set_up_tests.jl @@ -0,0 +1,24 @@ +using Aqua +using Effects +using MixedModels +using MixedModelsSerialization +using Test +using TestSetExtensions + +using MixedModelsDatasets: dataset + +kb07 = dataset(:kb07) +progress = false + +fm1 = fit(MixedModel, + @formula(rt_trunc ~ 1 + spkr * prec * load + + (1 + spkr + prec | subj) + + (1 + load | item)), kb07; progress) +mms = MixedModelSummary(fm1) + +fm2 = fit(MixedModel, + @formula(reaction ~ 1 + days + (1 | subj)), + dataset(:sleepstudy); + progress, + REML=true) +mms2 = MixedModelSummary(fm2)