Skip to content

Commit 33f09e1

Browse files
committed
improve make_eigvals_positive; use Symmetric everywhere
1 parent 15b7a39 commit 33f09e1

File tree

1 file changed

+17
-25
lines changed

1 file changed

+17
-25
lines changed

src/bounds/ellipsoid.jl

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,21 @@ This implementation follows the algorithm presented in Mukherjee et al. (2006).[
1616
"""
1717
mutable struct Ellipsoid{T} <: AbstractBoundingSpace{T}
1818
center::Vector{T}
19-
A::Matrix{T}
19+
A::Symmetric{T, Matrix{T}}
2020
axes::Matrix{T}
2121
axlens::Vector{T}
2222
volume::T
2323
end
2424

2525

26-
function Ellipsoid(center::AbstractVector, A::AbstractMatrix)
26+
Ellipsoid(center::AbstractVector, A::AbstractMatrix) = Ellipsoid(center, Symmetric(A))
27+
function Ellipsoid(center::AbstractVector, A::Symmetric)
2728
axes, axlens = decompose(A)
2829
Ellipsoid(center, A, axes, axlens, _volume(A))
2930
end
3031
Ellipsoid(ndim::Integer) = Ellipsoid(Float64, ndim)
3132
Ellipsoid(T::Type, ndim::Integer) = Ellipsoid(zeros(T, ndim), diagm(0 => ones(T, ndim)))
32-
Ellipsoid{T}(center::AbstractVector, A::AbstractMatrix) where {T} = Ellipsoid(T.(center), T.(A))
33+
Ellipsoid{T}(center::AbstractVector, A::AbstractMatrix) where {T} = Ellipsoid(convert(AbstractVector{T}, center), convert(AbstractMatrix{T}, A))
3334

3435
Base.broadcastable(e::Ellipsoid) = (e,)
3536

@@ -42,8 +43,6 @@ volume(ell::Ellipsoid) = ell.volume
4243
# Returns the principal axes
4344
axes(ell::Ellipsoid) = ell.axes
4445

45-
decompose(A::AbstractMatrix) = decompose(Symmetric(A)) # ensure that eigen() always returns real values
46-
4746
function decompose(A::Symmetric)
4847
E = eigen(A)
4948
axlens = @. 1 / sqrt(E.values)
@@ -58,7 +57,7 @@ decompose(ell::Ellipsoid) = ell.axes, ell.axlens
5857
function scale!(ell::Ellipsoid, factor)
5958
# linear factor
6059
f = factor^(1 / ndims(ell))
61-
ell.A ./= f^2
60+
ell.A /= f^2
6261
ell.axes .*= f
6362
ell.axlens .*= f
6463
ell.volume *= factor
@@ -96,16 +95,12 @@ function fit(E::Type{<:Ellipsoid{R}}, x::AbstractMatrix{S}; pointvol = 0) where
9695
return Ellipsoid(vec(x), A)
9796
end
9897
# get estimators
99-
center, cov = mean_and_cov(x, 2)
98+
center, cov_ = mean_and_cov(x, 2)
10099
delta = x .- center
101100
# Covariance is smaller than r^2 by a factor of 1/(n+2)
102-
cov .*= ndim + 2
103-
# Ensure cov is nonsingular
104-
targetprod = (npoints * pointvol / volume_prefactor(ndim))^2
105-
make_eigvals_positive!(cov, targetprod)
106-
107-
# get transformation matrix. Note: use pinv to avoid error when cov is all zeros
108-
A = pinv(cov)
101+
cov = Symmetric(cov_) * (ndim + 2)
102+
# ensure cov is posdef and get transformation matrix
103+
cov, A = make_eigvals_positive(cov)
109104

110105
# calculate expansion factor necessary to bound each points
111106
f = diag(delta' * (A * delta))
@@ -115,7 +110,7 @@ function fit(E::Type{<:Ellipsoid{R}}, x::AbstractMatrix{S}; pointvol = 0) where
115110
# x^T A x < 1 - √eps
116111
flex = 1 - sqrt(eps(T))
117112
if fmax > flex
118-
A .*= flex / fmax
113+
A *= flex / fmax
119114
end
120115

121116
ell = E(vec(center), A)
@@ -165,16 +160,13 @@ function randball(rng::AbstractRNG, T::Type, D::Integer)
165160
return z
166161
end
167162

168-
function make_eigvals_positive!(cov::AbstractMatrix, targetprod)
163+
function make_eigvals_positive(cov::Symmetric)
169164
E = eigen(cov)
170-
mask = (E.values ./ maximum(E.values)) .< 1e-10
171-
if any(mask)
172-
nzprod = prod(E.values[.!mask])
173-
nzeros = count(mask)
174-
E.values[mask] .= (targetprod / nzprod)^(1 / nzeros)
175-
cov .= E.vectors * Diagonal(E.values) / E.vectors
165+
maxcond = 1e10
166+
maxval = max(maximum(E.values), 1/1e10)
167+
if maxval / minimum(E.values) > maxcond
168+
E.values .= max.(E.values, maxval / maxcond)
169+
return Symmetric(Matrix(E)), Symmetric(inv(E))
176170
end
177-
return cov
171+
return cov, Symmetric(inv(E))
178172
end
179-
180-
make_eigvals_positive(cov::AbstractMatrix, targetprod) = make_eigvals_positive!(copy(cov), targetprod)

0 commit comments

Comments
 (0)