Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Xy block #464

Merged
merged 29 commits into from
Mar 2, 2021
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
4e83b0d
Replace BlockArrays by Vector{AbstractMatrix}
dmbates Jan 15, 2021
af6d5f8
drop BlockArray dependence; modify tests
dmbates Jan 16, 2021
2cd7aec
relax tol on oxide test b/c MKL ans
dmbates Jan 16, 2021
89920dc
copyto! not copy!
dmbates Jan 18, 2021
9b37622
Use Xy in the femat field
dmbates Feb 13, 2021
88cec25
Changes in pls.jl tests
dmbates Feb 13, 2021
c698b8c
Merge branch 'master' into XyBlock
dmbates Feb 13, 2021
f1723fc
Now passing tests.
dmbates Feb 13, 2021
a011a94
Update tests and fix bugs
dmbates Feb 19, 2021
8b11a55
Remove redundant size method
dmbates Feb 23, 2021
a9dcb06
Change tolerance on failing test
dmbates Feb 23, 2021
23bd72c
Repair quote chars in doc string
dmbates Feb 23, 2021
0f33934
Change femat to Xymat. Clean up docs
dmbates Feb 25, 2021
b27089f
Merge branch 'master' of github.com:JuliaStats/MixedModels.jl into Xy…
palday Feb 26, 2021
db24fc3
packedlowertri -> block
palday Feb 26, 2021
402010e
Merge branch 'master' of github.com:JuliaStats/MixedModels.jl into Xy…
palday Feb 26, 2021
32405c1
increment row count to include response
palday Feb 26, 2021
44577c7
Add more information of fields in src/Xymat.jl
dmbates Feb 26, 2021
ac31fae
Correct the name of a testset in test/matrixterm.jl
dmbates Feb 26, 2021
3ee812d
drop describeblocks, v1.7 BLAS changes, tol for MKL
dmbates Feb 26, 2021
92f37d6
fix broken ref
palday Feb 26, 2021
1158585
Merge branch 'XyBlock' of github.com:JuliaStats/MixedModels.jl into X…
palday Feb 26, 2021
a9dbcc5
loosen tolerances on doctest
palday Feb 26, 2021
1f11c6d
restore @static in version test
dmbates Feb 26, 2021
ecc458f
Use more contrasts in benchmarks
dmbates Mar 1, 2021
7a75ebf
Add test for size of BlockedSparse
dmbates Mar 1, 2021
5be2c4f
Reinstitute a couple of tests
dmbates Mar 1, 2021
d5b23cc
Add more tests for Grouping
dmbates Mar 1, 2021
63c32db
doc hints
palday Mar 2, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions docs/src/GaussHermite.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ For a `k`th order normalized Gauss-Hermite rule the tridiagonal matrix has zeros
using DataFrames, LinearAlgebra, Gadfly
sym3 = SymTridiagonal(zeros(3), sqrt.(1:2))
ev = eigen(sym3);
show(ev.values)
show(abs2.(ev.vectors[1,:]))
ev.values
```
```@example Main
abs2.(ev.vectors[1,:])
```
As a function of `k` this can be written as
```@example Main
Expand Down
2 changes: 1 addition & 1 deletion docs/src/bootstrap.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ To bootstrap the model parameters, first initialize a random number generator th

```@example Main
const rng = MersenneTwister(1234321);
samp = parametricbootstrap(rng, 10_000, m1, use_threads=true);
samp = parametricbootstrap(rng, 10_000, m1);
df = DataFrame(samp.allpars);
first(df, 10)
```
Expand Down
25 changes: 13 additions & 12 deletions docs/src/optimization.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ identity matrix where the multiple is
t1.λ
```

Because there is only one random-effects term in the model, the matrix $\bf Z$ is the indicators matrix shown as the result of `Matrix(t1)`, but stored in a special sparse format.
Because there is only one random-effects term in the model, the matrix $\bf Z$ is the indicators matrix shown as the result of `Int.(t1)`, but stored in a special sparse format.
Furthermore, there is only one block in $\Lambda_\theta$.


Expand Down Expand Up @@ -153,19 +153,23 @@ Int.(t31)
For this model the matrix $\bf Z$ is the same as that of model `fm2` but the diagonal blocks of $\Lambda_\theta$ are themselves diagonal.
```@example Main
t31.λ
```
```@example Main
MixedModels.getθ(t31)
```
Random-effects terms with distinct grouping factors generate distinct elements of the `allterms` field of the `LinearMixedModel` object.
Random-effects terms with distinct grouping factors generate distinct elements of the `reterms` field of the `LinearMixedModel` object.
Multiple `ReMat` objects are sorted by decreasing numbers of random effects.
```@example Main
penicillin = MixedModels.dataset(:penicillin)
fm4 = fit(MixedModel,
@formula(diameter ~ 1 + (1|plate) + (1|sample)),
@formula(diameter ~ 1 + (1|sample) + (1|plate)),
penicillin)
Int.(first(fm4.reterms))
```
```@example Main
Int.(last(fm4.reterms))
```
Note that the first `ReMat` in `fm4.terms` corresponds to grouping factor `G` even though the term `(1|G)` occurs in the formula after `(1|H)`.
Note that the first `ReMat` in `fm4.reterms` corresponds to grouping factor `plate` even though the term `(1|plate)` occurs in the formula after `(1|sample)`.

### Progress of the optimization

Expand All @@ -183,6 +187,7 @@ fm2.optsum
## A blocked Cholesky factor

A `LinearMixedModel` object contains two blocked matrices; a symmetric matrix `A` (only the lower triangle is stored) and a lower-triangular `L` which is the lower Cholesky factor of the updated and inflated `A`.
In versions 4.0.0 and later of `MixedModels` only the blocks in the lower triangle are stored in `A` and `L`, as a `Vector{AbstractMatrix{T}}`
```@docs
BlockDescription
```
Expand All @@ -191,6 +196,8 @@ shows the structure of the blocks
BlockDescription(fm2)
```

Another change in v4.0.0 and later is that the last row of blocks is constructed from `m.Xymat` which contains the full-rank model matrix `X` with the response `y` concatenated on the right.

The operation of installing a new value of the variance parameters, `θ`, and updating `L`
```@docs
setθ!
Expand Down Expand Up @@ -246,7 +253,7 @@ In a [*generalized linear model*](https://en.wikipedia.org/wiki/Generalized_line
The scalar distributions of individual responses differ only in their means, which are determined by a *linear predictor* expression $\eta=\bf X\beta$, where, as before, $\bf X$ is a model matrix derived from the values of covariates and $\beta$ is a vector of coefficients.

The unconstrained components of $\eta$ are mapped to the, possiby constrained, components of the mean response, $\mu$, via a scalar function, $g^{-1}$, applied to each component of $\eta$.
For historical reasons, the inverse of this function, taking components of $\mu$ to the corresponding component of $\eta$ is called the *link function* and more frequently used map from $\eta$ to $\mu$ is the *inverse link*.
For historical reasons, the inverse of this function, taking components of $\mu$ to the corresponding component of $\eta$ is called the *link function* and the more frequently used map from $\eta$ to $\mu$ is the *inverse link*.

A *generalized linear mixed-effects model* (GLMM) is defined, for the purposes of this package, by
```math
Expand Down Expand Up @@ -298,7 +305,7 @@ In a call to the `pirls!` function the first argument is a `GeneralizedLinearMix
The second and third arguments are optional logical values indicating if $\beta$ is to be varied and if verbose output is to be printed.

```@example Main
pirls!(mdl, true, true)
pirls!(mdl, true, false);
```

```@example Main
Expand Down Expand Up @@ -344,9 +351,3 @@ mdl1 = @btime fit(MixedModel, vaform, verbagg, Bernoulli())

This fit provided slightly better results (Laplace approximation to the deviance of 8151.400 versus 8151.583) but took 6 times as long.
That is not terribly important when the times involved are a few seconds but can be important when the fit requires many hours or days of computing time.

The comparison of the slow and fast fit is available in the optimization summary after the slow fit.

```@example Main
mdl1.LMM.optsum
```
2 changes: 1 addition & 1 deletion src/MixedModels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ include("pca.jl")
include("datasets.jl")
include("arraytypes.jl")
include("varcorr.jl")
include("femat.jl")
include("Xymat.jl")
include("remat.jl")
include("optsummary.jl")
include("schema.jl")
Expand Down
130 changes: 130 additions & 0 deletions src/Xymat.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
"""
FeTerm{T,S}

Term with an explicit, constant matrix representation

# Fields
palday marked this conversation as resolved.
Show resolved Hide resolved
* `x`: full model matrix
* `piv`: pivot `Vector{Int}` for moving linearly dependent columns to the right
* `rank`: computational rank of `x`
* `cnames`: vector of column names
"""
struct FeTerm{T,S<:AbstractMatrix}
x::S
piv::Vector{Int}
rank::Int
cnames::Vector{String}
end

"""
FeTerm(X::AbstractMatrix, cnms)

Convenience constructor for [`FeTerm`](@ref) that computes the rank and pivot with unit weights.

See the vignette "[Rank deficiency in mixed-effects models](@ref)" for more information on the
computation of the rank and pivot.
"""
function FeTerm(X::AbstractMatrix{T}, cnms) where {T}
if iszero(size(X, 2))
return FeTerm{T,typeof(X)}(X, Int[], 0, cnms)
end
st = statsqr(X)
pivot = st.p
rank = findfirst(<=(0), diff(st.p))
rank = isnothing(rank) ? length(pivot) : rank
Xp = pivot == collect(1:size(X, 2)) ? X : X[:, pivot]
# single-column rank deficiency is the result of a constant column vector
# this generally happens when constructing a dummy response, so we don't
# warn.
if rank < length(pivot) && size(X,2) > 1
@warn "Fixed-effects matrix is rank deficient"
end
FeTerm{T,typeof(X)}(Xp, pivot, rank, cnms[pivot])
end

"""
FeTerm(X::SparseMatrixCSC, cnms)

Convenience constructor for a sparse [`FeTerm`](@ref) assuming full rank, identity pivot and unit weights.

Note: automatic rank deficiency handling may be added to this method in the future, as discused in
the vignette "[Rank deficiency in mixed-effects models](@ref)" for general `FeTerm`.
"""
function FeTerm(X::SparseMatrixCSC, cnms::AbstractVector{String})
@debug "Full rank is assumed for sparse fixed-effect matrices."
rank = size(X,2)
FeTerm{eltype(X),typeof(X)}(X, collect(1:rank), rank, collect(cnms))
end

Base.copyto!(A::FeTerm{T}, src::AbstractVecOrMat{T}) where {T} = copyto!(A.x, src)

Base.eltype(::FeTerm{T}) where {T} = T

function fullrankx(A::FeTerm)
x, rnk = A.x, A.rank
rnk == size(x, 2) ? x : view(x, :, 1:rnk) # this handles the zero-columns case
end

LinearAlgebra.rank(A::FeTerm) = A.rank

"""
isfullrank(A::FeTerm)

Does `A` have full column rank?
"""
isfullrank(A::FeTerm) = A.rank == length(A.piv)
dmbates marked this conversation as resolved.
Show resolved Hide resolved

"""
FeMat{T,S}

A matrix and a (possibly) weighted copy of itself.

# Fields
palday marked this conversation as resolved.
Show resolved Hide resolved
- `xy`: original matrix, called `xy` b/c in practice this is `hcat(fullrank(X), y)`
- `wtxy`: (possibly) weighted copy of `xy`
dmbates marked this conversation as resolved.
Show resolved Hide resolved

Upon construction the `xy` and `wtxy` fields refer to the same matrix
"""
mutable struct FeMat{T,S<:AbstractMatrix} <: AbstractMatrix{T}
xy::S
wtxy::S
end

function FeMat(A::FeTerm{T}, y::AbstractVector{T}) where {T}
xy = hcat(fullrankx(A), y)
FeMat{T,typeof(xy)}(xy, xy)
end

Base.adjoint(A::FeMat) = Adjoint(A)

Base.eltype(::FeMat{T}) where {T} = T

Base.getindex(A::FeMat, i, j) = getindex(A.xy, i, j)

Base.length(A::FeMat) = length(A.wtxy)
palday marked this conversation as resolved.
Show resolved Hide resolved

function *(adjA::Adjoint{T,<:FeMat{T}}, B::FeMat{T}) where {T}
adjoint(adjA.parent.wtxy) * B.wtxy
end

function LinearAlgebra.mul!(R::StridedVecOrMat{T}, A::FeMat{T}, B::StridedVecOrMat{T}) where {T}
mul!(R, A.wtxy, B)
end

function LinearAlgebra.mul!(C, adjA::Adjoint{T,<:FeMat{T}}, B::FeMat{T}) where {T}
mul!(C, adjoint(adjA.parent.wtxy), B.wtxy)
end

function reweight!(A::FeMat{T}, sqrtwts::Vector{T}) where {T}
if !isempty(sqrtwts)
if A.xy === A.wtxy
A.wtxy = similar(A.xy)
end
mul!(A.wtxy, Diagonal(sqrtwts), A.xy)
end
A
end

Base.size(A::FeMat) = size(A.xy)

Base.size(A::FeMat, i::Integer) = size(A.xy, i)
palday marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 1 addition & 1 deletion src/arraytypes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -111,5 +111,5 @@ function LinearAlgebra.mul!(
α,
β
) where {T,P,Ti}
mul!(C.cscmat, A, adjB.parent.cscmat', α, β)
mul!(C.cscmat, A, adjoint(adjB.parent.cscmat), α, β)
end
111 changes: 0 additions & 111 deletions src/femat.jl

This file was deleted.

Loading