Skip to content

inverting singular matrixes with non-zero determinants #231

@josherrickson

Description

@josherrickson

solve() will sometimes produce an undesirable result when the matrix is singular but the determinant is extremely close but not equal to 0.

> a <- cov(model.matrix(~ as.factor(gear) + 0, data = mtcars))
> a[1] <- a[1] + 1e-10
> det(a)
[1] 2.926638e-12
> det(a) == 0
[1] FALSE
> all.equal(det(a), 0)
[1] TRUE
> solve(a)
                 as.factor(gear)3 as.factor(gear)4 as.factor(gear)5
as.factor(gear)3            1e+10            1e+10            1e+10
as.factor(gear)4            1e+10            1e+10            1e+10
as.factor(gear)5            1e+10            1e+10            1e+10

Our generalized inverse code produces a more sensible inverse:

> s <- svd(a)
> nz <- (s$d > sqrt(.Machine$double.eps) * s$d[1])
> s$v[, nz] %*% (t(s$u[, nz])/s$d[nz])
           [,1]       [,2]      [,3]
[1,]  1.8944444 -0.3444444 -1.550000
[2,] -0.3444444  2.0666667 -1.722222
[3,] -1.5500000 -1.7222222  3.272222

This is related to #230 in the sense that the issue in 230 arose when an externally calculated distance matrix had extreme values due to this bug, however this is a separate concern as this happens internally.

Fix is to drop down to generalized inverse if either solve() errors OR the determinant is "close" to 0. I have a rough solution coded up but don't really have time right now to finish testing and polishing.

How to determine close to 0? .Machine$double.eps is 2e-16 on my macs, but that feels too small. MatchIt will be using 1e-8 for their threshold in their Mahalanobis calculation. I'm tempted to go even higher like 1e4 (adjusting the nz calculation as well) - is there a big downside to carrying out the generalized inverse in situations like this?

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions