@@ -16,20 +16,21 @@ This implementation follows the algorithm presented in Mukherjee et al. (2006).[
1616"""
1717mutable 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
2323end
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))
2930end
3031Ellipsoid (ndim:: Integer ) = Ellipsoid (Float64, ndim)
3132Ellipsoid (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
3435Base. broadcastable (e:: Ellipsoid ) = (e,)
3536
@@ -42,8 +43,6 @@ volume(ell::Ellipsoid) = ell.volume
4243# Returns the principal axes
4344axes (ell:: Ellipsoid ) = ell. axes
4445
45- decompose (A:: AbstractMatrix ) = decompose (Symmetric (A)) # ensure that eigen() always returns real values
46-
4746function decompose (A:: Symmetric )
4847 E = eigen (A)
4948 axlens = @. 1 / sqrt (E. values)
@@ -58,7 +57,7 @@ decompose(ell::Ellipsoid) = ell.axes, ell.axlens
5857function 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
166161end
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 minimum (E. values) / maxval < 1 / 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))
178172end
179-
180- make_eigvals_positive (cov:: AbstractMatrix , targetprod) = make_eigvals_positive! (copy (cov), targetprod)
0 commit comments