@@ -16,20 +16,21 @@ This implementation follows the algorithm presented in Mukherjee et al. (2006).[
16
16
"""
17
17
mutable struct Ellipsoid{T} <: AbstractBoundingSpace{T}
18
18
center:: Vector{T}
19
- A:: Matrix{T}
19
+ A:: Symmetric{T, Matrix{T} }
20
20
axes:: Matrix{T}
21
21
axlens:: Vector{T}
22
22
volume:: T
23
23
end
24
24
25
25
26
- function Ellipsoid (center:: AbstractVector , A:: AbstractMatrix )
26
+ Ellipsoid (center:: AbstractVector , A:: AbstractMatrix ) = Ellipsoid (center, Symmetric (A))
27
+ function Ellipsoid (center:: AbstractVector , A:: Symmetric )
27
28
axes, axlens = decompose (A)
28
29
Ellipsoid (center, A, axes, axlens, _volume (A))
29
30
end
30
31
Ellipsoid (ndim:: Integer ) = Ellipsoid (Float64, ndim)
31
32
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))
33
34
34
35
Base. broadcastable (e:: Ellipsoid ) = (e,)
35
36
@@ -42,8 +43,6 @@ volume(ell::Ellipsoid) = ell.volume
42
43
# Returns the principal axes
43
44
axes (ell:: Ellipsoid ) = ell. axes
44
45
45
- decompose (A:: AbstractMatrix ) = decompose (Symmetric (A)) # ensure that eigen() always returns real values
46
-
47
46
function decompose (A:: Symmetric )
48
47
E = eigen (A)
49
48
axlens = @. 1 / sqrt (E. values)
@@ -58,7 +57,7 @@ decompose(ell::Ellipsoid) = ell.axes, ell.axlens
58
57
function scale! (ell:: Ellipsoid , factor)
59
58
# linear factor
60
59
f = factor^ (1 / ndims (ell))
61
- ell. A . /= f^ 2
60
+ ell. A /= f^ 2
62
61
ell. axes .*= f
63
62
ell. axlens .*= f
64
63
ell. volume *= factor
@@ -96,16 +95,12 @@ function fit(E::Type{<:Ellipsoid{R}}, x::AbstractMatrix{S}; pointvol = 0) where
96
95
return Ellipsoid (vec (x), A)
97
96
end
98
97
# get estimators
99
- center, cov = mean_and_cov (x, 2 )
98
+ center, cov_ = mean_and_cov (x, 2 )
100
99
delta = x .- center
101
100
# 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)
109
104
110
105
# calculate expansion factor necessary to bound each points
111
106
f = diag (delta' * (A * delta))
@@ -115,7 +110,7 @@ function fit(E::Type{<:Ellipsoid{R}}, x::AbstractMatrix{S}; pointvol = 0) where
115
110
# x^T A x < 1 - √eps
116
111
flex = 1 - sqrt (eps (T))
117
112
if fmax > flex
118
- A . *= flex / fmax
113
+ A *= flex / fmax
119
114
end
120
115
121
116
ell = E (vec (center), A)
@@ -165,16 +160,13 @@ function randball(rng::AbstractRNG, T::Type, D::Integer)
165
160
return z
166
161
end
167
162
168
- function make_eigvals_positive! (cov:: AbstractMatrix , targetprod )
163
+ function make_eigvals_positive (cov:: Symmetric )
169
164
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))
176
170
end
177
- return cov
171
+ return cov, Symmetric ( inv (E))
178
172
end
179
-
180
- make_eigvals_positive (cov:: AbstractMatrix , targetprod) = make_eigvals_positive! (copy (cov), targetprod)
0 commit comments