Robust approximations of compact sets
We consider $\mathcal{K}$, an unknown compact subset of the Euclidean space $(\mathbb{R}^d,\|\cdot\|)$. We dispose of a sample of $n$ points $\mathbb{X} = \{X_1, X_2,\ldots, X_n\}$ generated uniformly on $\mathcal{K}$ or generated in a neighborhood of $\mathcal{K}$. The sample of points may be corrupted by outliers. That is, by points lying far from $\mathcal{K}$.
Given $X_1, X_2,\ldots, X_n$, we aim at recovering $\mathcal{K}$. More precisely, we construct approximations of $\mathcal{K}$ as unions of $k$ balls or $k$ ellipsoids, for $k$ possibly much smaller than the sample size $n$.
Note that $\mathcal{K}$ coincides with $d_{\mathcal{K}}^{-1}((-\infty,0])$, the sublevel set of the distance-to-compact function $d_{\mathcal{K}}$. Then, the approximations we propose for $\mathcal{K}$ are sublevel sets of approximations of the distance function $d_{\mathcal{K}}$, based on $\mathbb{X}$. Since the sample may be corrupted by outliers and the points may not lie exactly on the compact set, approximating $d_{\mathcal{K}}$ by $d_{\mathbb{X}}$ may be terrible. Therefore, we construct approximations of $d_{\mathcal{K}}$ that are robust to noise.
In this page, we present three methods to construct approximations of $d_{\mathcal{K}}$ from a possibly noisy sample $\mathbb{X}$. The first approximation is the well-known distance-to-measure (DTM) function of (Chazal et al., 2011). The two last methods are new. They are based on the following approximations which sublevel sets are unions of $k$ balls or ellispoids: the $k$-PDTM (Brécheteau, 2018) and the $k$-PLM (Brécheteau and Levrard, 2020).
For a sample dataset of size $n$, these functions compute the distance approximations at the points in query_pts
. The parameter q
is a regularity parameter in $\{0,1,\ldots,n\}$, k
is the number of balls or ellispoids for the sublevel sets of the distance approximations. The procedures remove nsignal
points of the sample, cf Section "Detecting outliers".
Example
We consider as a compact set $\mathcal{K}$, the infinity symbol:
using CairoMakie
using GeometricClusterAnalysis
using LinearAlgebra
using NearestNeighbors
using Random
using Statistics
nsignal = 500
nnoise = 50
σ = 0.05
dimension = 2
noise_min = -5
noise_max = 5
rng = MersenneTwister(1234)
dataset = infinity_symbol(rng, nsignal, nnoise, σ, dimension, noise_min, noise_max)
f = Figure(; size = (600, 400))
ax = Axis(f[1, 1], aspect = 1)
limits!(ax, -5, 5, -5, 5)
scatter!(ax, dataset.points[1,:], dataset.points[2,:],
color = dataset.colors, colormap = :blues, markersize=7)
The target is the distance function $d_{\mathcal{K}}$. The graph of $-d_{\mathcal{K}}$ is the following:
function dtm(kdtree, x, y)
idxs, dists = nn(kdtree, [x, y]) # get the closest point
dtm_result = sqrt(sum(dists * dists))
return dtm_result
end
xs = LinRange(-6, 6, 100)
ys = LinRange(-6, 6, 100)
kdtree = KDTree(dataset.points[:,1:nsignal])
zs = [-dtm(kdtree, x, y) for x in xs, y in ys]
f = surface(xs, ys, zs)
We have generated a noisy sample $\mathbb X$. Then, $d_{\mathbb X}$ is a terrible approximation of $d_{\mathcal{K}}$. Indeed, the graph of $-d_{\mathbb X}$ is the following:
kdtree = KDTree(dataset.points)
zs = [-dtm(kdtree, x, y) for x in xs, y in ys]
f = surface(xs, ys, zs)
The distance-to-measure (DTM)
Nonetheless, there exist robust approximations of the distance-to-compact function, such as the distance-to-measure (DTM) function $d_{\mathbb X,q}$ (that depends on a regularity parameter $q$) (Chazal et al., 2011).
The distance-to-measure function (DTM) is a surrogate for the distance-to-compact, robust to noise. It was introduced in 2009 (Chazal et al., 2011). It depends on some regularity parameter $q\in\{0,1,\ldots,n\}$. The distance-to-measure function $d_{\mathbb{X},q}$ is defined by
\[\begin{equation} \label{eqdtm1} d_{\mathbb{X},q}^2:x\mapsto \|x-m(x,\mathbb{X},q)\|^2 + v(x,\mathbb{X},q), \end{equation}\]
where $m(x,\mathbb{X},q) = \frac{1}{q}\sum_{i=1}^qX^{(i)}$ is the barycenter of the $q$ nearest neighbours of $x$ in $\mathbb{X}$, $X^{(1)}, X^{(2)},\ldots, X^{(q)}$, and $v(x,\mathbb{X},q)$ is their variance $\frac{1}{q}\sum_{i=1}^q\|m(x,\mathbb{X},q)-X^{(i)}\|^2$.
Equivalently, the DTM coincides with the mean distance between $x$ and its $q$ nearest neighbours: $d_{\mathbb{X},q}^2(x) = \frac{1}{q}\sum_{i=1}^q\|x-X^{(i)}\|^2.$
The graph of $-d_{\mathbb X,q}$ for some $q$ is the following:
function dtm(kdtree, x, y, q)
idxs, dists = knn(kdtree, [x, y], q)
dtm_result = sqrt(sum(dists .* dists)/q)
return dtm_result
end
q = 10
zs = [-dtm(kdtree, x, y, q) for x in xs, y in ys]
fig = surface(xs, ys, zs)
In this page, we define two functions, the $k$-PDTM $d_{\mathbb X,q,k}$ and the $k$-PLM $d'_{\mathbb X,q,k}$. The sublevel sets of the $k$-PDTM are unions of $k$ balls. The sublevel sets of the $k$-PLM are unions of $k$ ellipsoids. The graphs of $-d_{\mathbb X,q,k}$ and $-d'_{\mathbb X,q,k}$ for some $q$ and $k$ are the following:
function kPDTM(rng, points, x, y, q, k, nsignal, iter_max, nstart)
nx, ny = length(x), length(y)
result = fill(Inf, (nx, ny))
df_kpdtm = kpdtm(rng, points, q, k, nsignal, iter_max, nstart)
for i = eachindex(x), j = eachindex(y)
for (μ, ω) in zip(df_kpdtm.μ, df_kpdtm.ω)
aux = sqrt(sum(((x[i],y[j]) .- μ).^2) + ω)
result[i,j] = min(result[i,j], aux)
end
end
return result, df_kpdtm
end
q, k = 20, 20
iter_max, nstart = 100, 10
xs = LinRange(-10, 10, 200)
ys = LinRange(-10, 10, 200)
zs, df = kPDTM(rng, dataset.points, xs, ys, q, k, nsignal, iter_max, nstart)
fig = surface(xs, ys, -zs, axis=(type=Axis3,))
and
function f_Σ!(Σ) end
function kPLM(rng, points, x, y, q, k, nsignal, iter_max, nstart)
nx, ny = length(x), length(y)
result = fill(Inf, (nx, ny))
df_kplm = kplm(rng, points, q, k, nsignal, iter_max, nstart, f_Σ!)
for i = eachindex(x), j = eachindex(y)
for (μ, Σ, ω) in zip(df_kplm.μ, df_kplm.Σ, df_kplm.ω)
aux = GeometricClusterAnalysis.sqmahalanobis([x[i], y[j]], μ, inv(Σ)) #+ ω
result[i,j] = min(result[i,j], aux)
end
end
return result, df_kplm
end
nsignal = 500
nnoise = 50
σ = 0.1
dimension = 2
noise_min = -5
noise_max = 5
rng = MersenneTwister(1234)
dataset = infinity_symbol(rng, nsignal, nnoise, σ, dimension, noise_min, noise_max)
q, k = 20, 8
iter_max, nstart = 100, 10
xs = LinRange(-10, 10, 200)
ys = LinRange(-10, 10, 200)
zs, df = kPLM(rng, dataset.points, xs, ys, q, k, nsignal, iter_max, nstart)
f = surface( xs, ys, -zs, axis=(type=Axis3,))
DTM computation for a noisy sample on a circle
The points are generated on the circle accordingly to the following function. This whole example was picked from the page DTM-based filtrations: demo of Raphaël Tinarrage.
"""
sample_on_circle(n_obs, n_out)
Sample `n_obs` points (observations) points from the uniform distribution on the unit circle in ``R^2``,
and `n_out` points (outliers) from the uniform distribution on the unit square
Input:
- `n_obs` : number of sample points on the circle
- `n_noise` : number of sample points on the square
Output :
- `data` : a (`n_obs` + `n_out`) x 2 matrix, the sampled points concatenated
"""
function sample_on_circle(n_obs, n_out)
rand_uniform = rand(n_obs) .* 2 .- 1
x_obs = cos.(2pi .* rand_uniform)
y_obs = sin.(2pi .* rand_uniform)
x_out = rand(n_out) .* 2 .- 1
y_out = rand(n_out) .* 2 .- 1
x = vcat(x_obs, x_out)
y = vcat(y_obs, y_out)
return vcat(x',y')
end
Main.sample_on_circle
Sampling on the circle with outlier
n_obs = 150 # number of points sampled on the circle
n_out = 100 # number of outliers
data = sample_on_circle(n_obs, n_out) # sample points with outliers
f = Figure(size = (600, 400))
ax = Axis(f[1, 1],
title = "$(n_obs)-sampling of the unit circle with $(n_out) outliers",
aspect = 1)
scatter!( ax, data[1,1:n_obs], data[2,1:n_obs], color="cyan", label = "data")
scatter!( ax, data[1,(n_obs+1):end], data[2,(n_obs+1):end], color="orange", label = "outliers")
Compute the DTM on X
q = 40
kdtree = KDTree(data)
# compute the values of the DTM of parameter q
function dtm(kdtree, x, y, q)
idxs, dists = knn(kdtree, [x, y], q)
dtm_result = sqrt(sum(dists .* dists)/q)
return dtm_result
end
dtm_values = [dtm(kdtree, px, py, q) for (px,py) in eachcol(data)]
# plot of the opposite of the DTM
f = Figure(;size=(600,400))
ax = Axis(f[1, 1],
title = "Values of -DTM on X with parameter q=$q",
aspect = 1)
scatter!(ax, data[1,:], data[2,:], color=-dtm_values)
colsize!(f.layout, 1, Aspect(1, 1.0)) # reduce size colorbar
Approximating $\mathcal{K}$ with a union of $k$ balls - or - the $k$-power-distance-to-measure ($k$-PDTM)
The $k$-PDTM is an approximation of the DTM, which sublevel sets are unions of $k$ balls. It was introduced and studied in (Brécheteau and Levrard, 2020).
According to the previous expression of the DTM \eqref{eqdtm1}, the DTM rewrites as
\[\begin{equation} \label{eqdtm2} d_{\mathbb{X},q}^2:x\mapsto \inf_{c\in\mathbb{R}^d}\|x-m(c,\mathbb X,q)\|^2+v(c,\mathbb X,q). \end{equation}\]
The $k$-PDTM $d_{\mathbb{X},q,k}$ is an approximation of the DTM that consists in replacing the infimum over $\mathbb{R}^d$ in this new formula \eqref{eqdtm2} with an infimum over a set of $k$ centers $c^*_1,c^*_2,\ldots,c^*_k$:
\[d_{\mathbb{X},q,k}^2:x\mapsto \min_{i\in\{1,2,\ldots,k\}}\|x-m(c^*_i,\mathbb X,q)\|^2+v(c^*_i,\mathbb X,q).\]
These centers are chosen such that the criterion
\[R: (c_1,c_2,\ldots,c_k) \mapsto \sum_{X\in\mathbb X}\min_{i\in\{1,2,\ldots,k\}}\|X-m(c_i,\mathbb X,q)\|^2+v(c_i,\mathbb X,q)\]
is minimal.
Note that these centers $c^*_1,c^*_2,\ldots,c^*_k$ are not necessarily uniquely defined. The following algorithm provides local minimisers of the criterion $R$.
We compute the $k$-PDTM on the same sample of points. Note that when we take $k=250$, that is, when $k$ is equal to the sample size, the DTM and the $k$-PDTM coincide on the points of the sample. The sub-level sets of the $k$-PDTM are unions of $k$ balls which centers are represented by triangles.
function kPDTM(rng, points, query_points, q, k, nsignal, iter_max, nstart)
result = fill(Inf, size(query_points,2))
df_kpdtm = kpdtm(rng, points, q, k, nsignal, iter_max, nstart)
for i = eachindex(result)
for (μ, ω) in zip(df_kpdtm.μ, df_kpdtm.ω)
aux = sqrt(sum((query_points[:,i] .- μ).^2) + ω)
result[i] = min(result[i], aux)
end
end
return result, df_kpdtm
end
q = 40
k = 250
sig = size(data, 2)
iter_max = 100
nstart = 10
values, df = kPDTM(rng, data, data, q, k, sig, iter_max, nstart)
fig = Figure(; size=(600,400))
ax = Axis(fig[1, 1], aspect = 1,
title = "Values of -kPDTM on X with parameter q=$(q) and k=$(k).",
)
scatter!(ax, data[1,:], data[2, :], color=-values)
scatter!(ax, getindex.(df.μ,1), getindex.(df.μ,2), color = "black", marker=:utriangle)
Approximating $\mathcal{K}$ with a union of $k$ ellipsoids - or - the $k$-power-likelihood-to-measure ($k$-PLM)
Sublevel sets of the $k$-PDTM are unions of $k$ balls. The $k$-PLM is a generalisation of the $k$-PDTM. Its sublevel sets are unions of $k$ ellipsoids. It was introduced and studied in (Brécheteau and Levrard, 2020).
The $k$-PLM $d'_{\mathbb{X},q,k}$ is defined from a set of $k$ centers $c^*_1,c^*_2,\ldots,c^*_k$ and a set of $k$ covariance matrices $\Sigma^*_1,\Sigma^*_2,\ldots,\Sigma^*_k$ by
\[{d'}_{\mathbb{X},q,k}^2:x\mapsto \min_{i\in\{1,2,\ldots,k\}}\|x-m(c^*_i,\mathbb X,q,\Sigma^*_i)\|_{\Sigma^*_i}^2+v(c^*_i,\mathbb X,q,\Sigma^*_i)+\log(\det(\Sigma^*_i)),\]
where $\|\cdot\|_{\Sigma}$ denotes the $\Sigma$-Mahalanobis norm, that is defined for $x\in\mathbb{R}^d$ by $\|x\|^2_{\Sigma}=x^T\Sigma^{-1}x$, $m(x,\mathbb X,q,\Sigma)=\frac1q\sum_{i=1}^qX^{(i)}$, where $X^{(1)},X^{(2)},\ldots,X^{(q)}$ are the $q$ nearest neigbours of $x$ in $\mathbb X$ for the $\|\cdot\|_{\Sigma}$-norm. Moreover, the local variance at $x$ for the $\|\cdot\|_{\Sigma}$-norm is defined by $v(x,\mathbb X,q,\Sigma)=\frac1q\sum_{i=1}^q\|X^{(i)}-m(x,\mathbb X,q,\Sigma)\|^2_{\Sigma}$.
These centers and covariances matrices are chosen such that the criterion
\[R':(c_1,c_2,\ldots,c_k,\Sigma_1,\Sigma_2,\ldots,\Sigma_k)\mapsto\sum_{X\in\mathbb{X}}\min_{i\in\{1,2,\ldots,k\}}\|X-m(c_i,\mathbb X,q,\Sigma_i)\|_{\Sigma_i}^2+v(c_i,\mathbb X,q,\Sigma_i)+\log(\det(\Sigma_i))\]
is minimal.
The following algorithm provides local minimisers of the criterion $R'$.
We compute the $k$-PLM on the same sample of points.
The sub-level sets of the $k$-PLM are unions of $k$ ellispoids which centers are represented by triangles.
function f_Σ!(Σ) end
function kPLM(rng, points, query_points, q, k, nsignal, iter_max, nstart)
result = fill(Inf, size(query_points, 2))
df_kplm = kplm(rng, points, q, k, nsignal, iter_max, nstart, f_Σ!)
for i = eachindex(result)
for (μ, Σ, ω) in zip(df_kplm.μ, df_kplm.Σ, df_kplm.ω)
aux = GeometricClusterAnalysis.sqmahalanobis(query_points[:, i], μ, inv(Σ)) #+ ω
result[i] = min(result[i], aux)
end
end
return result, df_kplm
end
q = 40
k = 250
sig = size(data, 2)
iter_max = 10
nstart = 1
values, df = kPLM(rng, data, data, q, k, sig, iter_max, nstart)
# plot of the opposite of the DTM
fig = Figure(; size = (600, 400))
ax = Axis(fig[1,1], aspect = 1,
title = "Values of kPLM on data with parameter q=$(q) and k=$(k).")
scatter!(ax, data[1,:], data[2,:], color = -values)
scatter!(ax, getindex.(df.μ,1), getindex.(df.μ, 2), color = "black", marker = :utriangle)
colsize!(fig.layout, 1, Aspect(1, 1.0)) # reduce size colorbar
Detecting outliers - Trimmed versions of the $k$-PDTM and the $k$-PLM
The criterions $R$ and $R'$ are of the form $\sum_{X\in\mathbb{X}}\min_{i\in\{1,2,\ldots,k\}}\gamma(X,\theta_i)$ for some cost function $\gamma$ and some parameters $\theta_i$ ($c_i$ or $(c_i,\Sigma_i)$). Morally, points $X$ for which $\min_{i\in\{1,2,\ldots,k\}}\gamma(X,\theta_i)$ is small are close to the optimal centers, and then should be close to the compact set $\mathcal{K}$. Then, points $X$ such that $\min_{i\in\{1,2,\ldots,k\}}\gamma(X,\theta_i)$ is large should be considered as outliers, and should be removed from the sample of points $\mathbb X$.
Selecting $o\leq n$ outliers together with computing optimal centers is possible. Such a procedure is called trimming. It consists is finding $(\theta^*_1,\theta^*_2,\ldots,\theta^*_k)$ that minimise the criterion
\[(\theta_1,\theta_2,\ldots,\theta_k)\mapsto\inf_{\mathbb{X}'\subset\mathbb{X}\mid\left|\mathbb{X}'\right|=o}\sum_{X\in\mathbb{X}'}\min_{i\in\{1,2,\ldots,k\}}\gamma(X,\theta_i).\]
The elements of the optimal set $\mathbb X'$ should be considered as signal points, the remaining ones as outliers. The trimmed versions of the $k$-PDTM and the $k$-PLM follow.
The $o = n-sig$ outliers are represented in red in the following figures.
Compute the trimmed k-PDTM values of parameter q
q = 5
k = 100
sig = 150 # Amount of signal points - We will remove o = 250 - 150 points from the sample
iter_max = 100
nstart = 10
values, df = kPDTM(rng, data, data, q, k, sig, iter_max, nstart)
# plot of the opposite of the k-PDTM
fig = Figure(; size=(600,400))
ax = Axis(fig[1, 1], aspect = 1,
title = "Values of -kPDTM on X with parameter q=$(q) and k=$(k).",
)
scatter!(ax, data[1,:], data[2, :], color=-values)
scatter!(ax, getindex.(df.μ ,1), getindex.(df.μ, 2), color = "black", marker=:utriangle)
outliers = df.colors .== 0
scatter!(ax, data[1,outliers], data[2,outliers], color = "red")
Compute the trimmed k-PLM values of parameter q
q = 10
k = 100
sig = 150
iter_max = 10
nstart = 1
values, df = kPLM(rng, data, data, q, k, sig, iter_max, nstart)
# plot of the opposite of the k-PLM
fig = Figure(; size = (600, 400))
ax = Axis(fig[1,1], aspect = 1,
title = "Values of kPLM on data with parameter q=$(q) and k=$(k).")
scatter!(ax, data[1,:], data[2,:], color = -values)
scatter!(ax, getindex.(df.μ,1), getindex.(df.μ, 2), color = "black", marker = :utriangle)
outliers = df.colors .== 0
scatter!(ax, data[1,outliers], data[2,outliers], color = "red")
colsize!(fig.layout, 1, Aspect(1, 1.0)) # reduce size colorbar
The sublevel sets
Sublevel sets of the $k$-PDTM
q = 5
k = 100
sig = 150
iter_max = 10
nstart = 1
values, df = kPDTM(rng, data, data, q, k, sig, iter_max, nstart)
fig = Figure(; size = (600, 400))
ax = Axis(fig[1,1], aspect = 1,
title = "Sublevel sets of the kPDTM on X with parameters q=$q and k=$k .")
scatter!(ax, data[1,:], data[2,:], color = -values)
alpha = 0.5 # Level for the sub-level set of the k-PLM
for (μ,ω) in zip(df.μ, df.ω)
poly!(ax, Circle(Point2(μ), max(0,alpha*alpha - ω)),
color= RGBAf(0.5, 0.5, 1, 0.5), transparency = true, shading = true)
end
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
┌ Warning: `shading = true` is not valid. Use `Makie.automatic`, `NoShading`, `FastShading` or `MultiLightShading`. Defaulting to `MakieCore.Automatic()`.
└ @ Makie ~/.julia/packages/Makie/rEu75/src/lighting.jl:243
Sublevel sets of the $k$-PLM
Compute the sublevel sets of the k-PLM on X
q = 10
k = 100
sig = 150
iter_max = 10
nstart = 1
values, df = kPLM(rng, data, data, q, k, sig, iter_max, nstart)
α = 10 # Level for the sub-level set of the k-PLM
fig = Figure(; size = (600, 400))
ax = Axis(fig[1,1], aspect = 1,
title = "Sublevel sets of the kPLM on X with parameters q=$q and k=$k .")
function covellipse(μ, Σ, α)
λ, U = eigen(Σ)
S = 0.2 * α * U * diagm(.√λ)
θ = range(0, 2π; length = 100)
A = S * [cos.(θ)'; sin.(θ)']
x = μ[1] .+ A[1, :]
y = μ[2] .+ A[2, :]
Makie.Polygon([Point2f(px, py) for (px,py) in zip(x, y)])
end
scatter!(ax, data[1,:], data[2,:], color=-values)
for (μ, Σ, ω) in zip(df.μ, df.Σ, df.ω)
poly!(ax, covellipse(μ, Σ, max(0, α - ω)), color = RGBAf(0.5, 0.5, 1, 0.5) )
end