From f16055ee66b01a7422a57d183e72198d671a3636 Mon Sep 17 00:00:00 2001 From: Alexey Stukalov Date: Thu, 23 Dec 2021 23:55:40 +0100 Subject: [PATCH 1/5] eigs(): cleanup, sort complex eigvals by real part --- src/linalg/LinAlg.jl | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/linalg/LinAlg.jl b/src/linalg/LinAlg.jl index d240d0ff5..803c71fd1 100644 --- a/src/linalg/LinAlg.jl +++ b/src/linalg/LinAlg.jl @@ -51,16 +51,11 @@ export convert, eigs function eigs(A; kwargs...) - schr =partialschur(A; kwargs...) + schr = partialschur(A; kwargs...) vals, vectors = partialeigen(schr[1]) reved = (kwargs[:which] == LR() || kwargs[:which] == LM()) - k::Int = get(kwargs, :nev, length(vals)) - k = min(k, length(vals)) - perm = collect(1:k) - if vals[1] isa(Real) - perm = sortperm(vals, rev=reved) - perm = perm[1:k] - end + k = min(get(kwargs, :nev, length(vals))::Int, length(vals)) + perm = sortperm(vals, by=real, rev=reved)[1:k] λ = vals[perm] Q = vectors[:, perm] return λ, Q From 77e76d64f5d3aeb476db33851d64d21138eeca1d Mon Sep 17 00:00:00 2001 From: Alexey Stukalov Date: Fri, 24 Dec 2021 00:56:21 +0100 Subject: [PATCH 2/5] normalized_cut(): check size of weightmatrix early --- src/graphcut/normalized_cut.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/graphcut/normalized_cut.jl b/src/graphcut/normalized_cut.jl index 31bbb2fec..339fe909f 100644 --- a/src/graphcut/normalized_cut.jl +++ b/src/graphcut/normalized_cut.jl @@ -114,10 +114,9 @@ end function _recursive_normalized_cut(W, thres=thres, num_cuts=num_cuts) m, n = size(W) + (m <= 1) && return ones(Int, m) # trivial D = Diagonal(vec(sum(W, dims=2))) - m == 1 && return [1] - #get eigenvector corresponding to second smallest eigenvalue # v = eigs(D-W, D, nev=2, which=SR())[2][:,2] # At least some versions of ARPACK have a bug, this is a workaround From abecac68e4bee4367b19618fb12e034f1648ff4c Mon Sep 17 00:00:00 2001 From: Alexey Stukalov Date: Fri, 24 Dec 2021 17:05:51 +0100 Subject: [PATCH 3/5] normalized_cut(): take care of isolated vertices because they result in degenerated matrix --- src/graphcut/normalized_cut.jl | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/graphcut/normalized_cut.jl b/src/graphcut/normalized_cut.jl index 339fe909f..d757e545c 100644 --- a/src/graphcut/normalized_cut.jl +++ b/src/graphcut/normalized_cut.jl @@ -117,7 +117,19 @@ function _recursive_normalized_cut(W, thres=thres, num_cuts=num_cuts) (m <= 1) && return ones(Int, m) # trivial D = Diagonal(vec(sum(W, dims=2))) - #get eigenvector corresponding to second smallest eigenvalue + # check that the diagonal is not degenerated as otherwise invDroot errors + dnz = abs.(diag(D)) .>= 1E-16 + if !all(dnz) + # vertices with incident edges summing to almost zero + # are not connected to the rest of the subnetwork, + # put them to separate modules and cut the remaining submatrix + nzlabels = _recursive_normalized_cut(W[dnz, dnz], thres, num_cuts) + nzix = 0 + zix = maximum(nzlabels) + return Int[nz ? nzlabels[nzix += 1] : (zix += 1) for nz in dnz] + end + + # get eigenvector corresponding to the second smallest generalized eigenvalue: # v = eigs(D-W, D, nev=2, which=SR())[2][:,2] # At least some versions of ARPACK have a bug, this is a workaround invDroot = sqrt.(inv(D)) # equal to Cholesky factorization for diagonal D From 04b110e4f366aaed83b8de9c7a64258de8b48407 Mon Sep 17 00:00:00 2001 From: Alexey Stukalov Date: Fri, 24 Dec 2021 00:53:46 +0100 Subject: [PATCH 4/5] normalized_cut(): check if 2nd eigvec is missing --- src/graphcut/normalized_cut.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/graphcut/normalized_cut.jl b/src/graphcut/normalized_cut.jl index d757e545c..503278c2c 100644 --- a/src/graphcut/normalized_cut.jl +++ b/src/graphcut/normalized_cut.jl @@ -134,8 +134,9 @@ function _recursive_normalized_cut(W, thres=thres, num_cuts=num_cuts) # At least some versions of ARPACK have a bug, this is a workaround invDroot = sqrt.(inv(D)) # equal to Cholesky factorization for diagonal D if n > 12 - λ, Q = eigs(invDroot' * (D - W) * invDroot, nev=12, which=SR()) - ret = real(Q[:,2]) + _, Q = eigs(invDroot' * (D - W) * invDroot, nev=12, which=SR()) + (size(Q, 2) <= 1) && return collect(1:m) # no 2nd eigenvector + ret = convert(Vector, real(view(Q, :, 2))) else ret = eigen(Matrix(invDroot' * (D - W) * invDroot)).vectors[:,2] end From eb090eb1f67eb39ea188ea09ec48e04d3efd1917 Mon Sep 17 00:00:00 2001 From: Alexey Stukalov Date: Thu, 23 Dec 2021 23:58:50 +0100 Subject: [PATCH 5/5] normalized_cut(): use real part of eigenvec --- src/graphcut/normalized_cut.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/graphcut/normalized_cut.jl b/src/graphcut/normalized_cut.jl index 503278c2c..b015d0ab7 100644 --- a/src/graphcut/normalized_cut.jl +++ b/src/graphcut/normalized_cut.jl @@ -140,12 +140,13 @@ function _recursive_normalized_cut(W, thres=thres, num_cuts=num_cuts) else ret = eigen(Matrix(invDroot' * (D - W) * invDroot)).vectors[:,2] end - v = invDroot * ret + v = real(invDroot * ret) #perform n-cuts with different partitions of v and find best one min_cost = Inf best_thres = -1 - for t in range(minimum(v), stop=maximum(v), length=num_cuts) + vmin, vmax = extrema(v) + for t in range(vmin, stop=vmax, length=num_cuts) cut = v .> t cost = _normalized_cut_cost(cut, W, D) if cost < min_cost