Skip to content

Commit f64fa06

Browse files
authored
Add files via upload
Uploading code accompanying ICASSP'20 submission.
1 parent 998239f commit f64fa06

15 files changed

+632
-0
lines changed

DistEuclideanPiotrDollar.m

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
% Calculates the Euclidean distance between vectors [FAST].
2+
%
3+
% Assume X is an m-by-p matrix representing m points in p-dimensional space and Y is an
4+
% n-by-p matrix representing another set of points in the same space. This function
5+
% compute the m-by-n distance matrix D where D(i,j) is the SQUARED Euclidean distance
6+
% between X(i,:) and Y(j,:). Running time is O(m*n*p).
7+
%
8+
% If x is a single data point, here is a faster, inline version to use:
9+
% D = sum( (Y - ones(size(Y,1),1)*x).^2, 2 )';
10+
%
11+
% INPUTS
12+
% X - m-by-p matrix of m p dimensional vectors
13+
% Y - n-by-p matrix of n p dimensional vectors
14+
%
15+
% OUTPUTS
16+
% D - m-by-n distance matrix
17+
%
18+
% EXAMPLE
19+
% X=[randn(100,5)]; Y=randn(40,5)+2;
20+
% D = dist_euclidean( [X; Y], [X; Y] ); im(D)
21+
%
22+
% DATESTAMP
23+
% 29-Sep-2005 2:00pm
24+
%
25+
% See also DIST_CHISQUARED, DIST_EMD
26+
27+
% Piotr's Image&Video Toolbox Version 1.03
28+
% Written and maintained by Piotr Dollar pdollar-at-cs.ucsd.edu
29+
% Please email me if you find bugs, or have suggestions or questions!
30+
31+
function D = DistEuclideanPiotrDollar( X, Y )
32+
if( ~isa(X,'double') || ~isa(Y,'double'))
33+
% disp( 'Inputs must be of type double - converting...');
34+
X = double(X); Y=double(Y);
35+
end
36+
m = size(X,1); n = size(Y,1);
37+
Yt = Y';
38+
XX = sum(X.*X,2);
39+
YY = sum(Yt.*Yt,1);
40+
D = XX(:,ones(1,n)) + YY(ones(1,m),:) - 2*X*Yt;
41+
42+
43+
44+
45+
%%%% code from Charles Elkan with variables renamed
46+
% m = size(X,1); n = size(Y,1);
47+
% D = sum(X.^2, 2) * ones(1,n) + ones(m,1) * sum(Y.^2, 2)' - 2.*X*Y';
48+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
49+
50+
51+
52+
%%%% LOOP METHOD - SLOW
53+
% [m p] = size(X);
54+
% [n p] = size(Y);
55+
%
56+
% D = zeros(m,n);
57+
% ones_m_1 = ones(m,1);
58+
% for i=1:n
59+
% y = Y(i,:);
60+
% d = X - y(ones_m_1,:);
61+
% D(:,i) = sum( d.*d, 2 );
62+
% end
63+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
64+
65+
66+
%%% PARALLEL METHOD THAT IS SUPER SLOW (slower then loop)!
67+
% % Code taken from "MATLAB array manipulation tips and tricks" by Peter J. Acklam
68+
% Xb = permute(X, [1 3 2]);
69+
% Yb = permute(Y, [3 1 2]);
70+
% D = sum( (Xb(:,ones(1,n),:) - Yb(ones(1,m),:,:)).^2, 3);
71+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
72+
73+
74+
%%%%% USELESS FOR EVEN VERY LARGE ARRAYS X=16000x1000!! and Y=100x1000
75+
% % call recursively to save memory
76+
% if( (m+n)*p > 10^5 && (m>1 || n>1))
77+
% if( m>n )
78+
% X1 = X(1:floor(end/2),:);
79+
% X2 = X((floor(end/2)+1):end,:);
80+
% D1 = dist_euclidean( X1, Y );
81+
% D2 = dist_euclidean( X2, Y );
82+
% D = cat( 1, D1, D2 );
83+
% else
84+
% Y1 = Y(1:floor(end/2),:);
85+
% Y2 = Y((floor(end/2)+1):end,:);
86+
% D1 = dist_euclidean( X, Y1 );
87+
% D2 = dist_euclidean( X, Y2 );
88+
% D = cat( 2, D1, D2 );
89+
% end
90+
% return;
91+
% end
92+
%

GD_BuildDirectedKnnGraph.m

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
function W = GD_BuildDirectedKnnGraph(M,k,which_matrix)
2+
% Usage: W = GD_BuildDirectedKnnGraph(M,k,which_matrix)
3+
%
4+
% Input:
5+
% M = either the distance or the similarity matrix, needs to be square, symmetric, non-negative
6+
% k = connectivity parameter of the kNN graph
7+
% which_matrix = either 'sim' or 'dist' (similarity or distance matrix)
8+
%
9+
% Output:
10+
% W = adjacency matrix of the directed knn graph
11+
%
12+
% For a similarity matrix S, returns the directed knn graph, edges are weighted by S.
13+
% For a distance matrix D, returns the directed (unweighted!) knn graph. If you want to get
14+
% a weighted graph in this case, you need to take care of transforming D to S yourself and then
15+
% call the function with a similarity matrix.
16+
% Self-edges are excluded in both graphs.
17+
18+
% implemented by brute force sorting
19+
20+
% % testing whether matrix is square:
21+
% if (size(M,1) ~= size(M,2))
22+
% error('Matrix not square!')
23+
% end
24+
[n, m] = size(M);
25+
26+
% to exclude self-edges, set diagonal of sim/dissim matrix to Inf or 0
27+
for it=1:min(n,m)
28+
if (strcmp(which_matrix,'sim'))
29+
M(it,it) = 0;
30+
elseif (strcmp(which_matrix, 'dist'))
31+
M(it,it) = Inf;
32+
else
33+
error('Unknown matrix type')
34+
end
35+
end
36+
37+
38+
% now do it:
39+
W = M;
40+
41+
if (strcmp(which_matrix, 'sim'))
42+
43+
for it = 1:n
44+
% sort points according to similarities:
45+
[sorted_row,order] = sort(M(it,:), 'descend');
46+
47+
% for all points which are not among the k nearest neighbors, set W to 0:
48+
W(it, order(k+1:end)) = 0;
49+
%W(it,order(1:k) just stays the same
50+
end
51+
52+
elseif (strcmp(which_matrix, 'dist'))
53+
54+
for it = 1:n
55+
% sort points according to distances:
56+
[sorted_row,order] = sort(M(it,:), 'ascend');
57+
% for all points which are not among the k nearest neighbors, set W to 0:
58+
W(it, order(k+1:end)) = 0;
59+
W(it, order(1:k)) = 1; %unweighted!
60+
end
61+
62+
else
63+
error('build_directed_knn_graph: unknown matrix type')
64+
end
65+
66+
67+

GD_BuildSymmetricKnnGraph.m

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
function W = GD_BuildSymmetricKnnGraph(M,k,which_matrix)
2+
% Usage: W = GD_BuildSymmetricKnnGraph(M,k,which_matrix)
3+
%
4+
% Input:
5+
% M = either the distance or the similarity matrix, needs to be square
6+
% k = parameter of the knn graph
7+
% which_matrix = either 'sim' or 'dist'
8+
%
9+
% Output:
10+
% W = adjacency matrix of the symmetric knn graph without self-edges
11+
%
12+
%
13+
% For a similarity matrix S, returns the undirected knn graph, edges are weighted by S.
14+
% For a distance matrix D, returns the undirected (unweighted!) knn graph. If you want to get
15+
% a weighted graph in this case, you need to take care of transforming D to S yourself and then
16+
% call the function with a similarity matrix
17+
%
18+
% Excludes self-edges.
19+
20+
%implemented brute force
21+
22+
23+
24+
if (size(M,1) ~= size(M,2))
25+
error('Matrix not square!')
26+
end
27+
28+
% compute directed KNN graph:
29+
W = GD_BuildDirectedKnnGraph(M,k+1,which_matrix);
30+
% transform it to symmetric one:
31+
W = max(W,W');

compute_error.m

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
function err = compute_error(labels,labels_hat,samples)
2+
% Error ratio on unlabeled nodes
3+
n = length(labels);
4+
unlabeled = true(n,1);
5+
unlabeled(samples) = false; % true => node is unlabeled
6+
err = sum(labels(unlabeled) ~= labels_hat(unlabeled))/sum(unlabeled);
7+

compute_laplacians.m

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
function [L, Ln, Lr] = compute_laplacians(A)
2+
% Computes L, symm. norm. L and RW L
3+
4+
N = length(A);
5+
% Remove self edges if any
6+
A = A - diag(diag(A));
7+
deg = sum(A,2);
8+
L = spdiags(deg,0,N,N) - A;
9+
10+
d = deg;
11+
d = d.^(-1/2);
12+
d(deg==0) = 0;
13+
Dinv = spdiags(d,0,N,N);
14+
Ln = speye(N)-Dinv*A*Dinv;
15+
Ln = 0.5*(Ln+Ln');
16+
17+
d = deg;
18+
d = d.^(-1);
19+
d(deg==0) = 0;
20+
Lr = speye(N)-spdiags(d,0,N,N)* A;
21+
22+
23+
% % normalization by number of links
24+
% % remove self loops
25+
% A = A - diag(diag(A));
26+
% d_link = sum(A~=0, 2);
27+
% Dinv_link = spdiags(d_link.^(-1/2), 0, N, N);
28+
% Ln_link = Dinv_link*Lun*Dinv_link;
29+
% Ln_link = 0.5*(Ln_link+Ln_link');
30+
31+

gfhf_recon_multiclass.m

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
function [err,labels_hat] = gfhf_recon_multiclass(L,labeled,y)
2+
% SSL using Zhu et al. 2003
3+
% L = Laplacian (or any other HP function of L e.g. L^k)
4+
% labeled = indices of labeled nodes
5+
% y = ground truth labels (one vs rest 1-0 membership)
6+
% (Assumes non-empty labeled set)
7+
8+
[~,labels] = max(y,[],2); % true labels
9+
unlabeled = setdiff(1:1:size(y,1),labeled);
10+
y_hat = y;
11+
full_matrix = full(L(unlabeled,unlabeled));
12+
nn = any(isnan(full_matrix(:)));
13+
in = any(isinf(full_matrix(:)));
14+
if (nn || in)
15+
disp('NaN or Inf detected')
16+
end
17+
18+
y_hat(unlabeled,:) = -pseudo_inverse(L(unlabeled,unlabeled))*(L(unlabeled,labeled)*y(labeled,:));
19+
labels_hat = labels;
20+
[~,labels_hat(unlabeled)] = max(y_hat(unlabeled,:),[],2); % unknown predicted labels
21+
err = compute_error(labels,labels_hat,labeled);
22+
end

nnk_graph_demo.m

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
clc;
2+
clear;
3+
close all;
4+
%% read data
5+
knn_param=10; k_choice=10; reg=1e-6;
6+
data = 'two_moons_1k';
7+
load([data,'.mat']); % data: X (dim x num) and labels: y
8+
9+
N = size(X,2);
10+
results_folder = ['results/'];
11+
dir_result = mkdir(results_folder);
12+
%% compute graphs
13+
tic
14+
D = DistEuclideanPiotrDollar(X',X'); % pairwise squared Euclidean distances
15+
directed_knn_mask = sparse(GD_BuildDirectedKnnGraph(D,knn_param,'dist'));
16+
distance_mask_time = toc;
17+
kD = sort(sqrt(D), 'ascend');
18+
sigma = full(mean(kD(k_choice, :)))/3;
19+
G = exp(-D./(2*sigma*sigma));
20+
similarity_time = toc;
21+
knn_mask = max(directed_knn_mask, directed_knn_mask');
22+
symmetrization_time = toc;
23+
%%
24+
fprintf('Computing the adj and L of %d-NN graph with sigma %0.4f...\n', knn_param, sigma)
25+
W_knn = G .* knn_mask;
26+
W_knn(W_knn<reg) = 0;
27+
knn_time = toc;
28+
29+
%%
30+
fprintf('Computing the adj and L NNK...\n')
31+
tic
32+
W_nnk = nnk_inverse_kernel_graph(G, directed_knn_mask, knn_param, reg); % choose the min k-NN sim
33+
nnk_time = toc + similarity_time;
34+
35+
%%
36+
time_values = {knn_time, nnk_time};
37+
sparsity_values = {length(find(W_knn))/2, length(find(W_nnk))/2};
38+
%%
39+
fname = [results_folder, data,'_k_',num2str(knn_param),'_sig_', num2str(round(sigma))]; %
40+
save([fname, '.mat'], 'knn_param', 'sigma', 'W_knn', ...%
41+
'W_nnk', 'time_values', 'sparsity_values');
42+
%% Adjacency plots
43+
figure();
44+
subplot(1,2,1); axis off
45+
spy(W_knn);
46+
title(['KNN (t=' num2str(time_values{1}) ', edges=' num2str(sparsity_values{1}) ')'])
47+
48+
subplot(1,2,2); axis off
49+
spy(W_nnk);
50+
title(['NNK (t=' num2str(time_values{2}) ', edges=' num2str(sparsity_values{2}) ')'])

nnk_inverse_kernel_graph.m

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
function W = nnk_inverse_kernel_graph(G, mask, knn_param, reg)
2+
% Constructs NNK graph
3+
% G = similairty or kernel matrix
4+
% mask - Neighbors to consider at each node (each row corresponds to neighbors)
5+
% k_choice - Not used. For adaptive kernels
6+
% reg - for removing weights smaller than reg value
7+
8+
if nargin < 4
9+
reg=1e-6;
10+
end
11+
n = length(G);
12+
neighbor_indices = zeros(n, knn_param);
13+
weight_values = zeros(n, knn_param);
14+
error_values = zeros(n,knn_param);
15+
16+
for i = 1:n
17+
nodes_i = find(mask(i,:)); % only consider similar enough neighbors
18+
nodes_i(nodes_i==i) = [];
19+
G_i = full(G(nodes_i, nodes_i));% removing i-th row and column from G
20+
g_i = full(G(nodes_i,i)); % removing the i-th elem from
21+
22+
qp_output = nonnegative_qp_solver(G_i, g_i, reg, g_i);
23+
qpsol = qp_output.xopt;
24+
25+
neighbor_indices(i,:) = nodes_i;
26+
weight_values(i,:) = qpsol;
27+
error_values(i,:) = (G(i,i) - 2*qpsol'*g_i + qpsol'*G_i*qpsol);
28+
end
29+
30+
%%
31+
row_indices = repmat(1:n, knn_param,1)';
32+
W = sparse(row_indices(:), neighbor_indices(:), weight_values(:), n, n);
33+
error = sparse(row_indices(:), neighbor_indices(:), error_values(:), n, n);
34+
W(error > error') = 0;
35+
W = max(W, W');
36+
37+
38+

0 commit comments

Comments
 (0)