├── .gitattributes ├── DGF.m ├── EProjSimplex_new.m ├── MinMaxSelection ├── Contents.txt ├── Internal_mxArray.h ├── Internal_mxArray_2010B.h ├── buildInternal_mxArrayDef.m ├── getmexopts.m ├── inplacecolumnmex.c ├── inplacecolumnmex.mexw64 ├── maxk_new.m ├── maxk_newmex.c ├── maxk_newmex.m ├── maxk_newmex.mexw64 ├── mingw.mlpkginstall ├── mink_new.m ├── mink_newmex.c ├── mink_newmex.m ├── mink_newmex.mexw64 ├── minmax_install.m ├── minmaxk.m ├── releaseinplace.c ├── releaseinplace.mexw64 └── testminmax.m ├── NMImax.m ├── README.md ├── SGF.m ├── SpectralClustering.m ├── consistent_graph.m ├── data ├── MSRCv1.mat ├── bbcsport2v.mat └── handwritten.mat ├── distance_kNN.m ├── extract_from_idx.m ├── kNN.m ├── logical_extraction.m └── make_affinity_matrix.m /.gitattributes: -------------------------------------------------------------------------------- 1 | *.c linguist-language=MATLAB 2 | *.cpp linguist-language=MATLAB 3 | -------------------------------------------------------------------------------- /DGF.m: -------------------------------------------------------------------------------- 1 | function [nmi, label] = DGF(data, truth, knn, beta, gamma, tol, tol2) 2 | % Use distance graph fusion (DGF) algorithm to perform multi-view clustering. 3 | % Inputs: 4 | % data - a cell matrix which contains some feature matrices with each row being an instance 5 | % truth - a column vector, the target label for all instances 6 | % knn - number of k-nearest neighbors (set knn=0 if using fully connected graph) 7 | % beta, gamma - hyperparameters for the algorithm 8 | % Optional Inputs: 9 | % tol, tol2 - the tolerance that determines convergence of algorithm 10 | % Outputs: 11 | % nmi - normalized mutual information 12 | % label - label generated by spectral clustering on the learned unified graph 13 | % See "Youwei Liang, Dong Huang, and Chang-Dong Wang. Consistency Meets 14 | % Inconsistency: A Unified Graph Learning Framework for Multi-view 15 | % Clustering. 2019 IEEE International Conference on Data Mining(ICDM)." 16 | % for a detailed description of the algorithm. 17 | % Author: Youwei Liang 18 | % 2019/08/31 19 | 20 | if nargin < 6 21 | tol = 1e-4; 22 | tol2 = 1e-2; 23 | end 24 | self_b = beta; cross_b = gamma; 25 | v = length(data); % number of views 26 | distance = cell(1, v); 27 | original_distance = cell(1, v); 28 | idx = cell(1, v); 29 | n = length(truth); 30 | numClust = length(unique(truth)); 31 | ONE = ones(n); 32 | knn_idx = false(n); 33 | 34 | for i=1:v 35 | try 36 | s = sprintf('dist%d.mat', i); % try to read the weight matrix (if saved previously) 37 | load(s) 38 | catch 39 | [~, W] = make_affinity_matrix(data{i}, 'euclidean'); 40 | % save(s, 'W') % weight matrix may be saved for repeating experiments 41 | end 42 | original_distance{i} = W; 43 | if knn ~= 0 44 | [W, idx{i}] = distance_kNN(W, knn); 45 | [~, tp] = extract_from_idx(ONE, idx{i}); 46 | knn_idx = knn_idx | logical(tp); 47 | end 48 | end 49 | 50 | % make knn index symmetric 51 | if knn~=0 52 | knn_idx = knn_idx | knn_idx'; %#ok<*BDSCA> 53 | else 54 | knn_idx = true(n); 55 | end 56 | 57 | for i=1:v 58 | if knn ~= 0 59 | distance{i} = logical_extraction(original_distance{i}, knn_idx); 60 | else 61 | distance{i} = original_distance{i}; 62 | end 63 | end 64 | 65 | com_distance = consistent_graph(distance, knn_idx, self_b, cross_b, tol, tol2); 66 | sigma = mean(mean(com_distance)); 67 | affinity_matrix = zeros(n,n); 68 | affinity_matrix(knn_idx) = exp(-com_distance(knn_idx)/(2*sigma)); 69 | for i=1:n 70 | affinity_matrix(i, i) = 0; 71 | end 72 | 73 | % do kNN again 74 | com_a = kNN(affinity_matrix, knn); 75 | [label] = SpectralClustering(com_a, numClust, 3); % obtain lable for each instance, label # starts from 1 76 | nmi = NMImax(truth, label); 77 | fprintf('knn:%2d, beta:%1.0e, gamma:%1.0e, NMI: %.3f\n', knn, self_b, cross_b, nmi) 78 | 79 | 80 | -------------------------------------------------------------------------------- /EProjSimplex_new.m: -------------------------------------------------------------------------------- 1 | function [x ft] = EProjSimplex_new(v, k) 2 | 3 | % 4 | %% Problem 5 | % 6 | % min 1/2 || x - v||^2 7 | % s.t. x>=0, 1'x=k 8 | % 9 | 10 | if nargin < 2 11 | k = 1; 12 | end; 13 | 14 | ft=1; 15 | n = length(v); 16 | 17 | v0 = v-mean(v) + k/n; 18 | %vmax = max(v0); 19 | vmin = min(v0); 20 | if vmin < 0 21 | f = 1; 22 | lambda_m = 0; 23 | while abs(f) > 10^-10 24 | v1 = v0 - lambda_m; 25 | posidx = v1>0; 26 | npos = sum(posidx); 27 | g = -npos; 28 | f = sum(v1(posidx)) - k; 29 | lambda_m = lambda_m - f/g; 30 | ft=ft+1; 31 | if ft > 100 32 | x = max(v1,0); 33 | break; 34 | end; 35 | end; 36 | x = max(v1,0); 37 | 38 | else 39 | x = v0; 40 | end; -------------------------------------------------------------------------------- /MinMaxSelection/Contents.txt: -------------------------------------------------------------------------------- 1 | Main Functions: 2 | mink.m -> Matlab file to look for k-smallest elements 3 | maxk.m -> Matlab file to look for k-largest elements 4 | 5 | Other files: 6 | minmaxk.m -> Common Matlab wrapper for mink.m and maxk.m 7 | minkmex.c -> Mex engine for mink.m 8 | minkmex.m -> Help file for Mex minkmex 9 | maxkmex.c -> Mex engine for maxk.m 10 | maxkmex.m -> Help file for Mex maxkmex 11 | buildInternal_mxArrayDef.m -> building the typedef for mxArray 12 | inplacecolumnmex.c -> Create inplace column of a full matrix 13 | releaseinplace.c -> release the data of the inplace array 14 | minmax_install.m -> Installation function (mex build) 15 | getmexopts.m -> Tool to get the current MEX setup 16 | testminmax.m -> Test program of Min/Max Selection Package 17 | Contents.txt -> This file 18 | Internal_mxArray_2010B.h -> Prototype file for R2010b 19 | 20 | Author Bruno Luong 21 | Contributor: Matt Fig, James Tursa 22 | Last update: 27-August-2011 23 | -------------------------------------------------------------------------------- /MinMaxSelection/Internal_mxArray.h: -------------------------------------------------------------------------------- 1 | /* 2 | Internal_mxArray.h 3 | Matlab version: 2010B 4 | */ 5 | 6 | typedef struct { 7 | void *reserved; 8 | int reserved1[2]; 9 | void *reserved2; 10 | size_t number_of_dims; 11 | unsigned int reserved3; 12 | struct { 13 | unsigned int flag0 : 1; 14 | unsigned int flag1 : 1; 15 | unsigned int flag2 : 1; 16 | unsigned int flag3 : 1; 17 | unsigned int flag4 : 1; 18 | unsigned int flag5 : 1; 19 | unsigned int flag6 : 1; 20 | unsigned int flag7 : 1; 21 | unsigned int flag7a: 1; 22 | unsigned int flag8 : 1; 23 | unsigned int flag9 : 1; 24 | unsigned int flag10 : 1; 25 | unsigned int flag11 : 4; 26 | unsigned int flag12 : 8; 27 | unsigned int flag13 : 8; 28 | } flags; 29 | size_t reserved4[2]; 30 | union { 31 | struct { 32 | void *pdata; 33 | void *pimag_data; 34 | void *reserved5; 35 | size_t reserved6[3]; 36 | } number_array; 37 | } data; 38 | } Internal_mxArray; -------------------------------------------------------------------------------- /MinMaxSelection/Internal_mxArray_2010B.h: -------------------------------------------------------------------------------- 1 | /* 2 | Internal_mxArray.h 3 | Matlab version: 2010B 4 | */ 5 | 6 | typedef struct { 7 | void *reserved; 8 | int reserved1[2]; 9 | void *reserved2; 10 | size_t number_of_dims; 11 | unsigned int reserved3; 12 | struct { 13 | unsigned int flag0 : 1; 14 | unsigned int flag1 : 1; 15 | unsigned int flag2 : 1; 16 | unsigned int flag3 : 1; 17 | unsigned int flag4 : 1; 18 | unsigned int flag5 : 1; 19 | unsigned int flag6 : 1; 20 | unsigned int flag7 : 1; 21 | unsigned int flag7a: 1; 22 | unsigned int flag8 : 1; 23 | unsigned int flag9 : 1; 24 | unsigned int flag10 : 1; 25 | unsigned int flag11 : 4; 26 | unsigned int flag12 : 8; 27 | unsigned int flag13 : 8; 28 | } flags; 29 | size_t reserved4[2]; 30 | union { 31 | struct { 32 | void *pdata; 33 | void *pimag_data; 34 | void *reserved5; 35 | size_t reserved6[3]; 36 | } number_array; 37 | } data; 38 | } Internal_mxArray; -------------------------------------------------------------------------------- /MinMaxSelection/buildInternal_mxArrayDef.m: -------------------------------------------------------------------------------- 1 | function content = buildInternal_mxArrayDef(mxArraydefFilename) 2 | % function content = buildInternal_mxArrayDef(mxArraydefFilename) 3 | % 4 | % Building the typedef of internal structure MxArray by looking inside 5 | % the header file include file MATRIX.H. This ensure the definition used 6 | % is compatible with the Matlab version 7 | % The internal definition will be used by MEX file inplacecolumnmex and 8 | % releaseinplace 9 | % 10 | % EXAMPLE USAGE: 11 | % buildInternal_mxArrayDef('Internal_mxArray.h') 12 | % 13 | % Author: Bruno Luong 14 | % 15 | % History 16 | % Original: 28-Jun-2009 17 | 18 | % Location of the header file matrix.h 19 | MLincludepath = [matlabroot() filesep 'extern' filesep 'include']; 20 | matrixhfile = 'matrix.h'; 21 | 22 | fid = fopen([MLincludepath filesep matrixhfile]); 23 | if fid>0 24 | c = textscan(fid, '%s', 'Delimiter', '\n', 'Whitespace', ''); 25 | try 26 | fclose(fid); 27 | end 28 | 29 | content = c{1}; 30 | 31 | % Look for the line containing "struct mxArray_tag {" 32 | idxmxArray_tag = strfind(content,'struct mxArray_tag {'); 33 | l1 = find(~cellfun('isempty',idxmxArray_tag),1,'first'); 34 | if isempty(l1) 35 | error('Cannot parse matrix.h file'); 36 | end 37 | 38 | % Modify the mxArray_tag to typedef definition 39 | content{l1} = strrep(content{l1}, ... 40 | 'struct mxArray_tag', 'typedef struct'); 41 | 42 | % Loop on the line and stop when the last curly bracket after 43 | % find the corresponding closed curly bracket 44 | % "struct mxArray_tag { ... }" 45 | l9 = 0; 46 | ncurlybrackets = 0; 47 | nlevels = 0; 48 | for l=l1:length(content) 49 | line = content{l}; 50 | nopen = sum(line=='{'); 51 | nclose = sum(line=='}'); 52 | ncurlybrackets = ncurlybrackets + (nopen + nclose); 53 | nlevels = nlevels + (nopen - nclose); 54 | if ncurlybrackets>0 && nlevels==0 55 | l9 = l; 56 | % Modify the last line with the typedef name 'Internal_mxArray' 57 | lastcurly = find(line=='}',1,'last'); 58 | line = [line(1:lastcurly) ... 59 | ' Internal_mxArray' ... 60 | line(lastcurly+1:end)]; 61 | content{l} = line; 62 | break; 63 | end 64 | end 65 | if l9==0 66 | error('Cannot parse matrix.h file'); 67 | end 68 | % Here is the definition we are interested in 69 | content = content(l1:l9); 70 | 71 | if nargin>=1 72 | thisfile = mfilename(); 73 | fid = fopen(mxArraydefFilename,'wt'); 74 | % Write a comment header 75 | fprintf(fid, ['/* Built automatically by ' thisfile '.m\n']); 76 | fprintf(fid, ['\tBuilt date: ' datestr(now) '\n']); 77 | fprintf(fid, ['\tMatlab version: ' version('-release') '\n']); 78 | fprintf(fid, '*/\n\n'); 79 | 80 | if fid>0 81 | % Write the content to header file 82 | for l=1:length(content) 83 | fprintf(fid, '%s\n', content{l}); 84 | end 85 | try 86 | fclose(fid); 87 | end 88 | else 89 | error('Cannot write the header file %s', mxArraydefFilename); 90 | end 91 | end 92 | else % fail to open matrix.h 93 | error('Cannot find ML file'); 94 | end -------------------------------------------------------------------------------- /MinMaxSelection/getmexopts.m: -------------------------------------------------------------------------------- 1 | function res = getmexopts(Tag) 2 | % function res = getmexopts(Tag) 3 | % Get the MCC or MEX configuration 4 | % Author Bruno Luong 5 | % Last update: 29-Jun-2009 6 | 7 | if ispc() 8 | optpath=prefdir; 9 | optfile=[optpath filesep 'compopts.bat']; 10 | mexoptfile=[optpath filesep 'mexopts.bat']; 11 | else 12 | optpath=matlabroot; 13 | optfile=[optpath '/bin/mbuildopts.sh']; 14 | mexoptfile=[optpath '/bin/mexopts.sh']; % not sure correct path 15 | end 16 | 17 | % Try to get MEX option first 18 | fid=fopen(mexoptfile,'r'); 19 | if fid<=0 20 | % Next MCC options 21 | fid=fopen(optfile,'r'); 22 | end 23 | 24 | if fid>0 25 | iscompilerline=@(S) (strcmp(S,['set ' Tag])); 26 | C=textscan(fid,'%s %s', 'delimiter', '=', 'whitespace', ''); 27 | fclose(fid); 28 | cline=find(cellfun(iscompilerline,C{1})); 29 | if isempty(cline) 30 | error('getmexopt [Bruno]: cannot get Tag %s', Tag) 31 | end 32 | res=C{2}{cline}; 33 | root=regexprep(matlabroot,'\\','\\\\'); 34 | res = regexprep(res,'%MATLAB%',root); 35 | else 36 | error('getmexopts [Bruno]: cannot open comopts.bat file') 37 | end 38 | 39 | % Bruno -------------------------------------------------------------------------------- /MinMaxSelection/inplacecolumnmex.c: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * function B = inplacecolumnmex(A, k) 3 | * Return the inplace-column A(:,k) 4 | * Important notes: 5 | * - use MEX function releaseinplace(B) to release properly shared-data 6 | * pointer before clear/reuse B. 7 | * - All inplace variables shared data with A must be released before 8 | * the original array A is cleared/reused. 9 | * Thanks to James Tursa 10 | ************************************************************************/ 11 | #include "mex.h" 12 | #include "matrix.h" 13 | 14 | /* Uncomment this on older Matlab version where size_t has not been 15 | defined */ 16 | /* 17 | #define mwSize int 18 | #define size_t int 19 | */ 20 | 21 | /* The following file defines the internal representation of mxArray, 22 | * inspired from mxArray_tag declared in the header . 23 | * This file is built by calling the MATLAB function 24 | * buildInternal_mxArrayDef.m */ 25 | #include "Internal_mxArray.h" 26 | 27 | /* Gateway of inplacecolumnmex */ 28 | void mexFunction(int nlhs, mxArray *plhs[], 29 | int nrhs, const mxArray *prhs[]) { 30 | 31 | mwSize k, N, M; 32 | double *Pr; 33 | 34 | /* Check arguments */ 35 | if (nrhs!=2) 36 | mexErrMsgTxt("INPLACECOLUMN: Two input arguments required."); 37 | 38 | if (!mxIsNumeric(prhs[0])) 39 | mexErrMsgTxt("INPLACECOLUMN: First input A argument must be numeric."); 40 | 41 | if (!mxIsNumeric(prhs[1])) 42 | mexErrMsgTxt("INPLACECOLUMN: Second input K must be numeric."); 43 | 44 | /* Get the size */ 45 | M = mxGetM(prhs[0]); 46 | N = mxGetN(prhs[0]); 47 | 48 | /* Get the column number k from the second input */ 49 | if (mxGetM(prhs[1])!=1 || mxGetN(prhs[1])!=1) 50 | mexErrMsgTxt("INPLACECOLUMN: Second input K must be a scalar."); 51 | 52 | if (mxGetClassID(prhs[1]) != mxDOUBLE_CLASS) 53 | mexErrMsgTxt("INPLACECOLUMN: Second input K must be a double."); 54 | 55 | k = (mwSize)(*mxGetPr(prhs[1])); 56 | /* Make sure k is valid */ 57 | if (k<1 || k>N) 58 | mexErrMsgTxt("INPLACECOLUMN: K is not valid."); 59 | 60 | /* Create the Matrix result (first output) */ 61 | plhs[0] = mxCreateNumericMatrix(0, 0, mxDOUBLE_CLASS, mxREAL); 62 | mxFree(mxGetPr(plhs[0])); /* Free the data, normally Pr is NULL and 63 | * this call does nothing */ 64 | 65 | /* Set the dimension as one column */ 66 | mxSetM(plhs[0], M); 67 | mxSetN(plhs[0], 1); 68 | 69 | /* Inplace data pointer of A */ 70 | Pr = mxGetPr(prhs[0]); 71 | Pr += (k-1)*M; /* Point to the column #k */ 72 | /* Equivalent to doing this: mxSetPr(plhs[0], Pr); 73 | but access directly to data pointer in order to by pass Matlab 74 | checking */ 75 | ((Internal_mxArray*)(plhs[0]))->data.number_array.pdata = Pr; 76 | 77 | return; 78 | 79 | } /* Gateway of INPLACECOLUMNMEX.c */ 80 | 81 | -------------------------------------------------------------------------------- /MinMaxSelection/inplacecolumnmex.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/youweiliang/ConsistentGraphLearning/63b053c07e019c57507892f63a510cdfcdece655/MinMaxSelection/inplacecolumnmex.mexw64 -------------------------------------------------------------------------------- /MinMaxSelection/maxk_new.m: -------------------------------------------------------------------------------- 1 | function varargout = maxk_new(varargin) 2 | % function res = MAXK(list, k) 3 | % 4 | % If LIST is a vector, MAXK returns in RES the K largest elements of LIST 5 | % RES is sorted in descending order 6 | % [res loc] = MAXK(...) 7 | % Location of the largest elements: RES=LIST(LOC) 8 | % If list is a matrix, MAXK operates along the first dimension 9 | % Use MAXK(..., dim) to operate along the dimension dim 10 | % MAXK(..., dim, 'sorting', false) to disable the post-sorting step 11 | % (true by default) 12 | % 13 | % Author Bruno Luong 14 | % Contributor: Matt Fig 15 | % Last update: 07/April/2009 16 | % 10/Jan/2010: possibility to disable post-sorting step 17 | 18 | nout=cell(1,max(1,nargout)); 19 | [nout{:}] = minmaxk(@maxk_newmex, varargin{:}); 20 | varargout=nout; 21 | 22 | -------------------------------------------------------------------------------- /MinMaxSelection/maxk_newmex.c: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * function [res loc] = maxkmex(list, k) 3 | * Matlab C-Mex 4 | * Purpose: Same as MAXK, i.e., 5 | * Return in RES the K largest elements of 2D matrix 6 | * LOC is Location of the largest values 7 | * - For full matrix, LOC contains linear indexing of the matrix. 8 | * RES == LIST(LOC) 9 | * - For sparse, location is returned in subindexes form by calling 10 | * [RES I J] = minkmex(list, k) 11 | * RES == getsparse(list,I,J) 12 | * This MEX works on double array only, and output RES is unsorted column 13 | * Complexity O(n) where n is size of list 14 | * Note: Implementation of type "non-destructive", i.e., the original data 15 | * will not be effectively swapped, but we keep track of a table of 16 | * permutation indexes. 17 | * Algorithm according to http://en.wikipedia.org/wiki/Selection_algorithm 18 | * Compilation: mex -O -v maxkmex.c 19 | * Author Bruno Luong 20 | * Last update: 10-Jan-2010 21 | * 16-August-2010: change type to mwSignedIndex and 22 | * check nansplit>=0 23 | * 27-Aug-2011: correct bug for sparse array 24 | * 26-Apr-2013: fix memset warning and remove C++ comment style 25 | *************************************************************************/ 26 | 27 | #include "mex.h" 28 | #include "matrix.h" 29 | 30 | /* Define correct type depending on platform */ 31 | #if defined(_MSC_VER) || defined(__BORLANDC__) 32 | typedef unsigned __int64 ulong64; 33 | #elif defined(_LCC) 34 | typedef long long long64; 35 | typedef unsigned long long ulong64; 36 | #else 37 | typedef unsigned long long ulong64; 38 | #endif 39 | 40 | /* Global variables, used to avoid stacking them during recusive call since 41 | they do not change */ 42 | mwIndex k; 43 | mwIndex *pos; 44 | double *list; 45 | 46 | #define MIDPOINT 0 47 | #define MEDIAN3 1 48 | #define MEDIANMEDIANS 2 49 | 50 | /* Pivot Strategy, use one of the above */ 51 | #define PIVOT MIDPOINT 52 | 53 | /*************************************************************************/ 54 | /*Find the index of the Median of the elements 55 | of array that occur at every "shift" positions.*/ 56 | mwIndex findMedianIndex(mwIndex left, mwIndex right, mwIndex shift) 57 | { 58 | mwIndex tmp, groups, k; 59 | double maxValue; 60 | mwIndex *pi, *pj, *pk, *pright, *pmaxIndex; 61 | 62 | groups = (right-left)/shift + 1; 63 | pk = pos + (k = left + (groups/2)*shift); 64 | pright = pos + right; 65 | for (pi=pos+left; pi<=pk; pi+= shift) 66 | { 67 | pmaxIndex = pi; 68 | maxValue = list[*pmaxIndex]; 69 | 70 | for (pj=pi; pj<=pright; pj+=shift) 71 | if (list[*pj]>maxValue) /* Comparison */ 72 | maxValue = list[*(pmaxIndex=pj)]; 73 | /* Swap pos[i] with pos[maxIndex] */ 74 | tmp = *pi; 75 | *pi = *pmaxIndex; 76 | *pmaxIndex = tmp; 77 | } 78 | 79 | return k; 80 | 81 | } /* findMedianIndex */ 82 | 83 | /*Computes the median of each group of 5 elements and stores 84 | it as the first element of the group (left). Recursively does this 85 | till there is only one group and hence only one Median */ 86 | mwIndex findMedianOfMedians(mwIndex left, mwIndex right) 87 | { 88 | mwIndex i, shift, step, tmp; 89 | mwIndex endIndex, medianIndex; 90 | 91 | if (left==right) return left; 92 | 93 | shift = 1; 94 | while (shift <= (right-left)) 95 | { 96 | step=shift*5; 97 | for (i=left; i<=right; i+=step) 98 | { 99 | if ((endIndex=i+step-1)>=right) 100 | endIndex=right; 101 | medianIndex = findMedianIndex(i, endIndex, shift); 102 | /* Swap pos[i] with pos[medianIndex] */ 103 | tmp = pos[i]; 104 | pos[i] = pos[medianIndex]; 105 | pos[medianIndex] = tmp; 106 | } 107 | shift = step; 108 | } 109 | return left; 110 | } /* findMedianOfMedians */ 111 | 112 | /*************************************************************************/ 113 | /*Computes the median of three points (left,right,and mid) */ 114 | mwIndex findMedianThree(mwIndex left, mwIndex right) 115 | { 116 | double vleft, vright, vmid; 117 | mwIndex mid; 118 | 119 | if (left==right) return left; 120 | 121 | vleft = list[pos[left]]; 122 | vright = list[pos[right]]; 123 | vmid = list[pos[mid = (left+right+1)/2]]; 124 | 125 | if (vleftvright) 128 | return right; 129 | else if (vmid=vright) */ 135 | 136 | if (vmid>vleft) 137 | return left; 138 | else if (vmid=pfirst && ISNAN(list[*pright])) 180 | pright--; 181 | return (pright-pos); 182 | } 183 | } /* for-loop */ 184 | } /* partNaN */ 185 | 186 | /*************************************************************************/ 187 | 188 | /* Partitioning the list around pivot pivotValue := l[pivotIndex]; 189 | After runing, at exit we obtain: 190 | l[left]...l[index-1] > pivotValue >= l[index] ... l[right] 191 | where l[i] := list[pos[i]] for all i */ 192 | mwIndex partition(mwIndex left, mwIndex right, mwIndex pivotIndex) { 193 | 194 | double pivotValue; 195 | mwIndex *pindex, *pi, *pright; 196 | mwIndex tmp; 197 | 198 | pright=pos+right; 199 | pindex=pos+pivotIndex; 200 | pivotValue = list[tmp = *pindex]; 201 | /* Swap pos[pivotIndex] with pos[right] */ 202 | *pindex = *pright; 203 | *pright = tmp; 204 | 205 | pindex=pos+left; 206 | for (pi=pindex; pi pivotValue) { 209 | /* if larger; Swap pos[index] with pos[i] */ 210 | tmp = *pi; 211 | *pi = *pindex; 212 | *(pindex++) = tmp; 213 | } 214 | 215 | /* Swap pos[index] with pos[right] */ 216 | tmp = *pindex; 217 | *pindex = *pright; 218 | *pright = tmp; 219 | 220 | return (pindex-pos); /* Pointer arithmetic */ 221 | } /* Partition */ 222 | 223 | /* Partitioning the list around pivot 0; 224 | * After runing, at exit we obtain: 225 | l[left]...l[index-1] > 0 >= l[index] ... l[right] 226 | where l[i] := list[pos[i]] for all i 227 | Note: at return, index might be larger than right (if all elements are 228 | strictly greater than zero) */ 229 | mwIndex part0(mwIndex left, mwIndex right) { 230 | 231 | mwIndex *pindex, *pi, *pright; 232 | mwIndex tmp; 233 | 234 | pright=pos+right; 235 | pindex=pos+left; 236 | for (pi=pindex; pi<=pright; pi++) 237 | /* Compare with pivotValue of zero */ 238 | if (list[*pi] > 0.0) { /* Compare */ 239 | /* if larger; Swap pos[index] with pos[i] */ 240 | tmp = *pi; 241 | *pi = *pindex; 242 | *(pindex++) = tmp; 243 | } 244 | 245 | return (pindex-pos); /* Pointer arithmetic */ 246 | } /* part0 */ 247 | 248 | /* Recursive engine (partial quicksort) */ 249 | void findFirstK(mwIndex left, mwIndex right) { 250 | 251 | mwIndex pivotIndex; 252 | 253 | if (right > left) { 254 | 255 | #if (PIVOT==MEDIANMEDIANS) 256 | pivotIndex = findMedianOfMedians(left, right); 257 | #elif (PIVOT==MEDIAN3) 258 | pivotIndex = findMedianThree(left, right); 259 | #else /* MIDPOINT */ 260 | pivotIndex = (left+right+1)/2; 261 | #endif 262 | 263 | pivotIndex = partition(left, right, pivotIndex); 264 | if (pivotIndex > k) 265 | findFirstK(left, pivotIndex-1); 266 | else if (pivotIndex < k) 267 | findFirstK(pivotIndex+1, right); 268 | } 269 | 270 | return; 271 | } /* findFirstK */ 272 | 273 | /* Create the result contains k largest values */ 274 | mxArray* MinMaxResult(mwIndex k, mwIndex p0, mwIndex nz, 275 | mwIndex kout) 276 | { 277 | mwIndex i; 278 | mwSize dims[2]; 279 | mxArray* Result; 280 | double *data; 281 | 282 | /* Create the Matrix result (first output) */ 283 | dims[0] = kout; dims[1] = 1; 284 | Result = mxCreateNumericArray(2, dims, mxDOUBLE_CLASS, mxREAL); 285 | if (Result == NULL) 286 | mexErrMsgTxt("Out of memory."); 287 | data = mxGetPr(Result); 288 | /* copy positive part (p0) */ 289 | for (i=0; ikout-p0) 292 | nz = kout-p0; 293 | /* Fill nz zeros */ 294 | memset((void*)(data+p0), 0, sizeof(double)*nz); 295 | 296 | /* copy negative part (kout - (p0+nz)) */ 297 | for (i=p0+nz; ikout-p0) 321 | nz = kout-p0; 322 | /* Fill nz zeros */ 323 | memset((void*)(data+p0), 0, sizeof(double)*nz); 324 | 325 | /* index of negative part */ 326 | for (i=p0+nz; inzS) nz=(mwIndex)nzS; 359 | 360 | /* Get the sparse index pointers */ 361 | irs = mxGetIr(S); 362 | jcs = mxGetJc(S); 363 | 364 | /* i is index of I J */ 365 | i = 0; 366 | /* (ai,aj) current subindex of zero element */ 367 | ai = aj = 0; 368 | 369 | /* (bi,bj) current subindex of nonzero element */ 370 | if ((ib=0)kout-p0) 444 | nz = kout-p0; 445 | /* Find the place where zeros are hidden */ 446 | FindSPzero(S, nz, dataI+p0, dataJ+p0); 447 | 448 | /* index of positive part */ 449 | for (i=p0+nz; il) k=l; 516 | 517 | /* Clip kout */ 518 | if (kout>nelem) kout=nelem; 519 | 520 | /* Clean programming */ 521 | pos=NULL; 522 | 523 | /* Work for non-empty array */ 524 | if (l>0) { 525 | /* Vector of index */ 526 | pos = mxMalloc(sizeof(mwSize)*l); 527 | if (pos==NULL) 528 | mexErrMsgTxt("Out of memory."); 529 | /* Initialize the array of position (zero-based index) */ 530 | for (i=0; i=0) 536 | findFirstK(0, nansplit); 537 | 538 | /* Look for the split of positive/negative numbers */ 539 | if (sparseflag) { 540 | p0 = part0(0, k); /* number of strict negative elements */ 541 | if (p0 < k) /* There are at least two positive elements */ 542 | { 543 | /* Number of implicite zeros */ 544 | nz = nelem-l; 545 | if (nz) /* in case the positive set is unordered */ 546 | { 547 | k -= nz; 548 | findFirstK(p0, nansplit); 549 | k += nz; 550 | } 551 | } 552 | /* ++ to restore one-based Matlab index */ 553 | k++; 554 | } 555 | else 556 | /* ++ to Restore one-based Matlab index */ 557 | p0 = ++k; 558 | } /* if (l>0) */ 559 | else p0 = 0; 560 | 561 | /* Number of implicite zero in (sparse) */ 562 | nz = nelem-l; 563 | /* Create the Matrix result (first output) */ 564 | plhs[0] = MinMaxResult(k, p0, nz, kout); 565 | 566 | /* Create the Matrix position (second output) */ 567 | if (nlhs>=2) 568 | { 569 | if (sparseflag) 570 | SpLocResult(k, p0, nz, kout, prhs[0], &(plhs[1]), &(plhs[2])); 571 | else 572 | plhs[1] = LocResult(k, p0, nz, kout); 573 | } 574 | 575 | /* Free the array of position */ 576 | if (pos) mxFree(pos); 577 | pos = NULL; /* clean programming */ 578 | 579 | return; 580 | 581 | } /* Gateway of maxkmex.c */ 582 | 583 | 584 | -------------------------------------------------------------------------------- /MinMaxSelection/maxk_newmex.m: -------------------------------------------------------------------------------- 1 | % function [res loc] = maxk_newmex(list, k) 2 | % 3 | % Matlab C-Mex 4 | % Purpose: Same as MAXK, i.e., 5 | % Return in RES the K largest elements of LIST 6 | % LOC is Location of the largest values: RES=LIST(LOC) 7 | % This MEX works on double only, and output RES is unsorted 8 | % Algorithm according to http://en.wikipedia.org/wiki/Selection_algorithm 9 | % Compilation: mex -O -v maxkmex.c 10 | % Author Bruno Luong 11 | % Last update: 07/April/2009 12 | % 13 | 14 | error('Mex file not yet compiled. Action: mex -O -v maxk_newmex.c'); -------------------------------------------------------------------------------- /MinMaxSelection/maxk_newmex.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/youweiliang/ConsistentGraphLearning/63b053c07e019c57507892f63a510cdfcdece655/MinMaxSelection/maxk_newmex.mexw64 -------------------------------------------------------------------------------- /MinMaxSelection/mingw.mlpkginstall: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | MathWorks 16 | mingw 17 | MATLAB 18 | MinGW-w64 a C/C++ compiler from TDM-GCC 19 | ML_MINGW 20 | 21 | 513C8EA2EB2C07B9F1B65AD58C66CD0B 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /MinMaxSelection/mink_new.m: -------------------------------------------------------------------------------- 1 | function varargout = mink_new(varargin) 2 | % function res = MINK(list, k) 3 | % 4 | % If LIST is a vector, MINK returns in RES the K smallest elements of LIST 5 | % RES is sorted in ascending order 6 | % [res loc] = MINK(...) 7 | % Location of the smallest elements: RES=LIST(LOC) 8 | % If list is a matrix, MINK operates along the first dimension 9 | % Use MINK(..., dim) to operate along the dimension dim 10 | % MINK(..., dim, 'sorting', false) to disable the post-sorting step 11 | % (true by default) 12 | % 13 | % Author Bruno Luong 14 | % Contributor: Matt Fig 15 | % Last update: 07/April/2009 16 | % 10/Jan/2010: possibility to disable post-sorting step 17 | 18 | 19 | nout=cell(1,max(1,nargout)); 20 | [nout{:}] = minmaxk(@mink_newmex, varargin{:}); 21 | varargout=nout; 22 | -------------------------------------------------------------------------------- /MinMaxSelection/mink_newmex.c: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * function [res loc] = minkmex(list, k) 3 | * Matlab C-Mex 4 | * Purpose: Same as MINK, i.e., 5 | * Return in RES the K smallest elements of 2D matrix 6 | * LOC is Location of the smallest values 7 | * - For full matrix, LOC contains linear indexing of the matrix. 8 | * RES == LIST(LOC) 9 | * - For sparse, location is returned in subindexes form by calling 10 | * [RES I J] = minkmex(list, k) 11 | * RES == getsparse(list,I,J) 12 | * This MEX works on double array only, and output RES is unsorted column 13 | * Complexity O(n) where n is size of list 14 | * Note: Implementation of type "non-destructive", i.e., the original data 15 | * will not be effectively swapped, but we keep track of a table of 16 | * permutation indexes. 17 | * Algorithm according to http://en.wikipedia.org/wiki/Selection_algorithm 18 | * Compilation: mex -O -v minkmex.c 19 | * Author Bruno Luong 20 | * Last update: 10-Jan-2010 21 | * 16-August-2010: change type to mwSignedIndex and 22 | * check nansplit>=0 23 | * 27-Aug-2011: correct bug for sparse array 24 | * 26-Apr-2013: fix memset warning and remove C++ comment style 25 | *************************************************************************/ 26 | 27 | #include "mex.h" 28 | #include "matrix.h" 29 | 30 | /* Define correct type depending on platform */ 31 | #if defined(_MSC_VER) || defined(__BORLANDC__) 32 | typedef unsigned __int64 ulong64; 33 | #elif defined(_LCC) 34 | typedef long long long64; 35 | typedef unsigned long long ulong64; 36 | #else 37 | typedef unsigned long long ulong64; 38 | #endif 39 | 40 | /* Global variables, used to avoid stacking them during recusive call since 41 | they do not change */ 42 | mwIndex k; 43 | mwIndex *pos; 44 | double *list; 45 | 46 | #define MIDPOINT 0 47 | #define MEDIAN3 1 48 | #define MEDIANMEDIANS 2 49 | 50 | /* Pivot Strategy, use one of the above */ 51 | #define PIVOT MIDPOINT 52 | 53 | /*************************************************************************/ 54 | /*Find the index of the Median of the elements 55 | of array that occur at every "shift" positions.*/ 56 | mwSize findMedianIndex(mwSize left, mwSize right, mwSize shift) 57 | { 58 | mwSize tmp, groups, k; 59 | double minValue; 60 | mwSize *pi, *pj, *pk, *pright, *pminIndex; 61 | 62 | groups = (right-left)/shift + 1; 63 | pk = pos + (k = left + (groups/2)*shift); 64 | pright = pos + right; 65 | for (pi=pos+left; pi<=pk; pi+= shift) 66 | { 67 | pminIndex = pi; 68 | minValue = list[*pminIndex]; 69 | 70 | for (pj=pi; pj<=pright; pj+=shift) 71 | if (list[*pj]=right) 100 | endIndex=right; 101 | medianIndex = findMedianIndex(i, endIndex, shift); 102 | /* Swap pos[i] with pos[medianIndex] */ 103 | tmp = pos[i]; 104 | pos[i] = pos[medianIndex]; 105 | pos[medianIndex] = tmp; 106 | } 107 | shift = step; 108 | } 109 | return left; 110 | } /* findMedianOfMedians */ 111 | 112 | /*************************************************************************/ 113 | /*Computes the median of three points (left,right,and mid) */ 114 | mwIndex findMedianThree(mwIndex left, mwIndex right) 115 | { 116 | double vleft, vright, vmid; 117 | mwIndex mid; 118 | 119 | if (left==right) return left; 120 | 121 | vleft = list[pos[left]]; 122 | vright = list[pos[right]]; 123 | vmid = list[pos[mid = (left+right+1)/2]]; 124 | 125 | if (vleftvright) 128 | return right; 129 | else if (vmid=vright) */ 135 | 136 | if (vmid>vleft) 137 | return left; 138 | else if (vmid=pfirst && ISNAN(list[*pright])) 180 | pright--; 181 | return (pright-pos); 182 | } 183 | } /* for-loop */ 184 | } /* partNaN */ 185 | 186 | /*************************************************************************/ 187 | /* Partitioning the list around pivot pivotValue := l[pivotIndex]; 188 | After runing, at exit we obtain: 189 | l[left]...l[index-1] < pivotValue <= l[index] ... l[right] 190 | where l[i] := list[pos[i]] for all i */ 191 | mwSize partition(mwSize left, mwSize right, mwSize pivotIndex) { 192 | 193 | double pivotValue; 194 | mwSize *pindex, *pi, *pright; 195 | mwSize tmp; 196 | 197 | pright=pos+right; 198 | pindex=pos+pivotIndex; 199 | pivotValue = list[tmp = *pindex]; 200 | /* Swap pos[pivotIndex] with pos[right] */ 201 | *pindex = *pright; 202 | *pright = tmp; 203 | 204 | pindex=pos+left; 205 | for (pi=pindex; pi left) { 254 | 255 | #if (PIVOT==MEDIANMEDIANS) 256 | pivotIndex = findMedianOfMedians(left, right); 257 | #elif (PIVOT==MEDIAN3) 258 | pivotIndex = findMedianThree(left, right); 259 | #else /* MIDPOINT */ 260 | pivotIndex = (left+right+1)/2; 261 | #endif 262 | 263 | pivotIndex = partition(left, right, pivotIndex); 264 | if (pivotIndex > k) 265 | findFirstK(left, pivotIndex-1); 266 | else if (pivotIndex < k) 267 | findFirstK(pivotIndex+1, right); 268 | } 269 | 270 | return; 271 | } /* findFirstK */ 272 | 273 | /* Create the result contains k smallest values */ 274 | mxArray* MinMaxResult(mwIndex k, mwIndex p0, mwIndex nz, 275 | mwIndex kout) 276 | { 277 | mwIndex i; 278 | mwSize dims[2]; 279 | mxArray* Result; 280 | double *data; 281 | 282 | /* Create the Matrix result (first output) */ 283 | dims[0] = kout; dims[1] = 1; 284 | Result = mxCreateNumericArray(2, dims, mxDOUBLE_CLASS, mxREAL); 285 | if (Result == NULL) 286 | mexErrMsgTxt("Out of memory."); 287 | data = mxGetPr(Result); 288 | /* copy negative part (p0) */ 289 | for (i=0; ikout-p0) 292 | nz = kout-p0; 293 | /* Fill nz zeros */ 294 | memset((void*)(data+p0), 0, sizeof(double)*nz); 295 | 296 | /* copy positive part (kout - (p0+nz)) */ 297 | for (i=p0+nz; ikout-p0) 321 | nz = kout-p0; 322 | /* Fill nz zeros */ 323 | memset((void*)(data+p0), 0, sizeof(double)*nz); 324 | 325 | /* index of positive part */ 326 | for (i=p0+nz; inzS) nz=(mwIndex)nzS; 359 | 360 | /* Get the sparse index pointers */ 361 | irs = mxGetIr(S); 362 | jcs = mxGetJc(S); 363 | 364 | /* i is index of I J */ 365 | i = 0; 366 | /* (ai,aj) current subindex of zero element */ 367 | ai = aj = 0; 368 | 369 | /* (bi,bj) current subindex of nonzero element */ 370 | if ((ib=0)kout-p0) 444 | nz = kout-p0; 445 | /* Find the place where zeros are hidden */ 446 | FindSPzero(S, nz, dataI+p0, dataJ+p0); 447 | 448 | /* index of positive part */ 449 | for (i=p0+nz; il) k=l; 516 | 517 | /* Clip kout */ 518 | if (kout>nelem) kout=nelem; 519 | 520 | /* Clean programming */ 521 | pos=NULL; 522 | 523 | /* Work for non-empty array */ 524 | if (l>0) { 525 | /* Vector of index */ 526 | pos = mxMalloc(sizeof(mwSize)*l); 527 | if (pos==NULL) 528 | mexErrMsgTxt("Out of memory."); 529 | /* Initialize the array of position (zero-based index) */ 530 | for (i=0; i=0) 536 | findFirstK(0, nansplit); 537 | 538 | /* Look for the split of positive/negative numbers */ 539 | if (sparseflag) { 540 | p0 = part0(0, k); /* number of strict negative elements */ 541 | if (p0 < k) /* There are at least two positive elements */ 542 | { 543 | /* Number of implicite zeros */ 544 | nz = nelem-l; 545 | if (nz) /* in case the positive set is unordered */ 546 | { 547 | k -= nz; 548 | findFirstK(p0, nansplit); 549 | k += nz; 550 | } 551 | } 552 | /* ++ to restore one-based Matlab index */ 553 | k++; 554 | } 555 | else 556 | /* ++ to Restore one-based Matlab index */ 557 | p0 = ++k; 558 | } /* if (l>0) */ 559 | else p0 = 0; 560 | 561 | /* Number of implicite zero in (sparse) */ 562 | nz = nelem-l; 563 | /* Create the Matrix result (first output) */ 564 | plhs[0] = MinMaxResult(k, p0, nz, kout); 565 | 566 | /* Create the Matrix position (second output) */ 567 | if (nlhs>=2) 568 | { 569 | if (sparseflag) 570 | SpLocResult(k, p0, nz, kout, prhs[0], &(plhs[1]), &(plhs[2])); 571 | else 572 | plhs[1] = LocResult(k, p0, nz, kout); 573 | } 574 | 575 | /* Free the array of position */ 576 | if (pos) mxFree(pos); 577 | pos = NULL; /* clean programming */ 578 | 579 | return; 580 | 581 | } /* Gateway of minkmex.c */ 582 | 583 | 584 | -------------------------------------------------------------------------------- /MinMaxSelection/mink_newmex.m: -------------------------------------------------------------------------------- 1 | % function [res loc] = mink_newmex(list, k) 2 | % 3 | % Matlab C-Mex 4 | % Purpose: Same as MINK, i.e., 5 | % Return in RES the K smallest elements of LIST 6 | % LOC is Location of the smallest values: RES=LIST(LOC) 7 | % This MEX works on double only, and output RES is unsorted 8 | % Algorithm according to http://en.wikipedia.org/wiki/Selection_algorithm 9 | % Compilation: mex -O -v minkmex.c 10 | % Author Bruno Luong 11 | % Last update: 07/April/2009 12 | % 13 | 14 | error('Mex file not yet compiled. Action: mex -O -v mink_newmex.c') -------------------------------------------------------------------------------- /MinMaxSelection/mink_newmex.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/youweiliang/ConsistentGraphLearning/63b053c07e019c57507892f63a510cdfcdece655/MinMaxSelection/mink_newmex.mexw64 -------------------------------------------------------------------------------- /MinMaxSelection/minmax_install.m: -------------------------------------------------------------------------------- 1 | function minmax_install 2 | % function minmax_install 3 | % Installation by building the C-mex files for min/max selection package 4 | % 5 | % Author Bruno Luong 6 | % Last update: 29-Jun-2009 built inplace functions 7 | 8 | thisfile = mfilename('fullpath'); 9 | path = fileparts(thisfile); 10 | oldpath = cd(path); 11 | 12 | arch=computer('arch'); 13 | mexopts = {'-v' '-O' ['-' arch]}; 14 | % 64-bit platform 15 | if contains(computer(),'64') 16 | mexopts(end+1) = {'-largeArrayDims'}; 17 | end 18 | 19 | if ispc() && datenum(version('-date')) < datenum('January 11, 2014') 20 | compiler = getmexopts('COMPILER'); 21 | islcc = strcmpi(compiler,'lcc'); 22 | % Define the C-symbol for LCC compiler 23 | if islcc 24 | mexopts(end+1) = {'-D_LCC'}; 25 | end 26 | end 27 | 28 | % Internal representation of mxArray 29 | try 30 | buildInternal_mxArrayDef('Internal_mxArray.h'); 31 | catch 32 | if ispc() 33 | cpcmd = 'copy'; 34 | else 35 | cpcmd = ' cp'; 36 | end 37 | cmd = [cpcmd ' Internal_mxArray_2010B.h Internal_mxArray.h']; 38 | system(cmd); 39 | end 40 | 41 | % Inplace tool 42 | mex(mexopts{:},'inplacecolumnmex.c'); 43 | mex(mexopts{:},'releaseinplace.c'); 44 | 45 | % Mex MIN/MAX functions 46 | mex(mexopts{:},'mink_newmex.c'); 47 | mex(mexopts{:},'maxk_newmex.c'); 48 | 49 | cd(oldpath); 50 | 51 | end -------------------------------------------------------------------------------- /MinMaxSelection/minmaxk.m: -------------------------------------------------------------------------------- 1 | function [res, loc] = minmaxk(mexfun, list, k, dim, varargin) 2 | % function [res loc] = minmaxk(mexfun, list, k, dim) 3 | % 4 | % Return in RES the K smallest/largest elements of LIST 5 | % RES is sorted in ascending/descending order 6 | % [res loc] = minmaxk(...) 7 | % Location of the smallest/largest: RES=LIST(LOC) 8 | % [res loc] = minmaxk(..., dim) 9 | % specify the dimension to operate 10 | % [res loc] = minmaxk(..., dim, 'sorting', false) 11 | % to disable the post-sorting step (true by default) 12 | % 13 | % Author Bruno Luong 14 | % Contributor: Matt Fig (suggest return same loc as SORT for full blowed 15 | % result) 16 | % Last update: 24/May/2009: work on sparse matrix list 17 | % 28/Jun/2009: used inplace columns for full-array 18 | % 10/Aug/2009: releaseinplace is called to cleanup 19 | % if MEX fails 20 | % 10/Jan/2010: possibility to disable post-sorting step 21 | 22 | clist=class(list); 23 | % Mex functions requires input in double 24 | if ~strcmpi(clist,'double') 25 | list=double(list); 26 | end 27 | 28 | % Look for single selection value by default 29 | if nargin<3 || isempty(k) 30 | k=1; 31 | else 32 | k=double(k); 33 | end 34 | 35 | szlist = size(list); 36 | if nargin<4 37 | if isvector(list) && szlist(1)==1 38 | dim=2; 39 | else 40 | dim=1; 41 | end 42 | end 43 | 44 | if mod(length(varargin),2) 45 | error('MINMAXK: options must come as property/value pairs'); 46 | end 47 | 48 | postsorting = getoptionpair({'postsorting', 'sorting', 'sort'}, ... 49 | true, varargin); 50 | 51 | nd=ndims(list); 52 | if dim<1 || dim>nd 53 | error('MINMAXK: dim must be between 1 and ndims(LIST)=%d', nd); 54 | end 55 | 56 | % Will be used for sorting 57 | if isequal(mexfun,@mink_newmex) 58 | smode='ascend'; 59 | else 60 | smode='descend'; 61 | end 62 | 63 | % Do we need to get location? 64 | getloc=nargout>=2; 65 | 66 | % Put operating dimension to the first 67 | list=shiftdim(list,dim-1); 68 | 69 | % operating length 70 | l=size(list,1); 71 | % Number of vectors 72 | szl=size(list); 73 | N=prod(szl(2:end)); 74 | 75 | szres=szl; 76 | k=min(k,l); 77 | szres(1)=k; 78 | res=zeros(szres,clist); % Allocate array having the same class with list 79 | if getloc 80 | loc=zeros(szres,'double'); 81 | end 82 | if k>=l % Matt Fig's suggestion 83 | res = list; 84 | if getloc 85 | repvec=size(loc); repvec(1)=1; 86 | loc = repmat((1:k)',repvec); 87 | end 88 | else 89 | try % use try/catch instead of onCleanup for better compatibility 90 | if getloc 91 | for n=1:N 92 | [res(:,n), loc(:,n)] = mexfun(list(:,n), k); 93 | end 94 | else 95 | for n=1:N 96 | res(:,n) = mexfun(list(:,n),k); 97 | end 98 | end 99 | catch 100 | % If something is wrong 101 | % It crashes if cn is not released properly 102 | if exist('cn','var') && ~isempty(cn) 103 | releaseinplace(cn); 104 | end 105 | % rethrow the error (likely memory) 106 | rethrow(lasterror); 107 | end 108 | end 109 | 110 | % This is the post processing step of sorting the selection data 111 | % The purpose is to have a nicely formatted output, that's all 112 | 113 | if getloc 114 | if postsorting 115 | [res is] = sort(res,1,smode); 116 | j=(0:N-1)*k; 117 | % Use reshape instead of bsxfun for backward compatible 118 | if exist('bsxfun','builtin') 119 | is = bsxfun(@plus, reshape(is,[k N]), j); 120 | else 121 | is = reshape(is,[k N]) + repmat(j,[k 1]); 122 | end 123 | loc = reshape(loc(is),size(loc)); 124 | end 125 | % Put the operating dimension at the right place 126 | loc = shiftdim(loc,nd+1-dim); 127 | else 128 | if postsorting 129 | res = sort(res,1,smode); 130 | end 131 | end 132 | 133 | % Put the operating dimension at the right place 134 | res = shiftdim(res,nd+1-dim); 135 | 136 | end % minmaxk 137 | 138 | function val = getoptionpair(name, defaultval, vargin) 139 | % Get the value from property/value pairs 140 | val = defaultval; 141 | for k=1:2:length(vargin) 142 | if strmatch(vargin{k},name) 143 | val = vargin{k+1}; 144 | return 145 | end 146 | end 147 | end % getoptionpair -------------------------------------------------------------------------------- /MinMaxSelection/releaseinplace.c: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * function releaseinplace(b) 3 | * Release the data from an inplace column mxArray that was created 4 | * with the inplacecolumnmex function. 5 | * Author Bruno Luong 6 | * Last update: 27/June/2009 7 | ************************************************************************/ 8 | 9 | #include "mex.h" 10 | #include "matrix.h" 11 | 12 | /* Uncomment this on older Matlab version where size_t has not been 13 | defined */ 14 | /*#define size_t int*/ 15 | 16 | /* The following file defines the internal representation of mxArray, 17 | * inspired from mxArray_tag declared in the header . 18 | * This file is built by calling the MATLAB function 19 | * buildInternal_mxArrayDef.m */ 20 | #include "Internal_mxArray.h" 21 | 22 | /* Gateway of releaseinplace */ 23 | void mexFunction(int nlhs, mxArray *plhs[], 24 | int nrhs, const mxArray *prhs[]) { 25 | 26 | /* Check arguments */ 27 | if (nrhs!=1) 28 | mexErrMsgTxt("RELEASEINPLACE: One input argument required."); 29 | 30 | if( nlhs != 0 ) { 31 | mexErrMsgTxt("RELEASEINPLACE: Zero output arguments required."); 32 | } 33 | 34 | mxSetM((mxArray *)prhs[0], 0); 35 | mxSetN((mxArray *)prhs[0], 0); 36 | /* Equivalent to doing this: mxSetPr(prhs[0], NULL); 37 | but access directly to data pointer in order to by pass Matlab 38 | checking - Thanks to James Tursa */ 39 | ((Internal_mxArray*)(prhs[0]))->data.number_array.pdata = NULL; 40 | ((Internal_mxArray*)(prhs[0]))->data.number_array.pimag_data = NULL; 41 | 42 | return; 43 | 44 | } /* Gateway of releaseinplace.c */ -------------------------------------------------------------------------------- /MinMaxSelection/releaseinplace.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/youweiliang/ConsistentGraphLearning/63b053c07e019c57507892f63a510cdfcdece655/MinMaxSelection/releaseinplace.mexw64 -------------------------------------------------------------------------------- /MinMaxSelection/testminmax.m: -------------------------------------------------------------------------------- 1 | % Author Bruno Luong 2 | % Last update: 07/April/2009 3 | % Script to tets min/max selection 4 | 5 | clear 6 | 7 | try 8 | mink_newmex(1,1); 9 | mink_newmex(1,1); 10 | catch 11 | minmax_install(); 12 | end 13 | 14 | n=1e7; 15 | k=10; 16 | 17 | ntest=10; 18 | 19 | % Timing 20 | disp('Time the algorithms for few seconds...'); 21 | tmink=zeros(1,ntest); 22 | tmaxk=zeros(1,ntest); 23 | tsort=zeros(1,ntest); 24 | for i=1:ntest 25 | list=rand(1,n); 26 | 27 | tic 28 | mn=mink_new(list,k); 29 | tmink(i)=toc; 30 | 31 | tic 32 | mx=maxk_new(list,k); 33 | tmaxk(i)=toc; 34 | 35 | tic 36 | s=sort(list); 37 | smn=s(1:k); 38 | smx=s(end:-1:end-k+1); 39 | tsort(i)=toc; 40 | 41 | if ~isequal(mn,smn) || ~isequal(mx,smx) 42 | keyboard; 43 | end 44 | end 45 | 46 | fprintf('Timing mink: %f [s]\n',median(tmink)); 47 | fprintf('Timing maxk: %f [s]\n',median(tmaxk)); 48 | fprintf('Timing sort: %f [s]\n',median(tsort)); -------------------------------------------------------------------------------- /NMImax.m: -------------------------------------------------------------------------------- 1 | function NMImax = NMImax(x, y) 2 | % Compute nomalized mutual information I(x,y)/sqrt(H(x)*H(y)). 3 | % Written by Michael Chen (sth4nth@gmail.com). 4 | % Modified by Nejc Ilc (log2 -> log). 5 | 6 | assert(numel(x) == numel(y)); 7 | n = numel(x); 8 | x = reshape(x,1,n); 9 | y = reshape(y,1,n); 10 | 11 | l = min(min(x),min(y)); 12 | x = x-l+1; 13 | y = y-l+1; 14 | k = max(max(x),max(y)); 15 | 16 | idx = 1:n; 17 | Mx = sparse(idx,x,1,n,k,n); 18 | My = sparse(idx,y,1,n,k,n); 19 | Pxy = nonzeros(Mx'*My/n); %joint distribution of x and y 20 | Hxy = -dot(Pxy,log(Pxy+eps)); 21 | 22 | Px = mean(Mx,1); 23 | Py = mean(My,1); 24 | 25 | % entropy of Py and Px 26 | Hx = -dot(Px,log(Px+eps)); 27 | Hy = -dot(Py,log(Py+eps)); 28 | 29 | % mutual information 30 | MI = Hx + Hy - Hxy; 31 | 32 | % maximum normalized mutual information 33 | NMImax = MI/max(Hx,Hy); 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Distance Graph Fusion (DGF) and Similarity Graph Fusion (SGF) 2 | 3 | This repository contains the MATLAB code for DGF and SGF introduced in the following paper 4 | 5 | Consistency Meets Inconsistency: A Unified Graph Learning Framework for Multi-view Clustering (ICDM 2019) 6 | 7 | 8 | ### Preparation 9 | * **Windows 64bit**: 10 | Add some helper files to MATLAB path by `addpath('MinMaxSelection')` command in MATLAB command window. 11 | * **Linux, Windows 32bit and Mac OS**: 12 | Add some helper files to MATLAB path by `addpath('MinMaxSelection')` command in MATLAB command window. Then recompile the helper functions by running `minmax_install`. 13 | 14 | 15 | ### Example usage 16 | ```MATLAB 17 | load('data\handwritten.mat'); 18 | knn=15; beta=1e-6; gamma=1e1; 19 | [nmi, label] = DGF(X, Y, knn, beta, gamma); 20 | ``` 21 | 22 | ### Multi-view Data 23 | More multi-view data are available on [Google Drive](https://drive.google.com/drive/folders/1vzJ19eGy7sAyLTFtM4IWkKzZhFJsi134?usp=sharing "multi-view data"). 24 | 25 | 26 | ### Citation 27 | If you find this algorithm useful in your research, please consider citing: 28 | 29 | @inproceedings{liang2019consistency, 30 | title={Consistency Meets Inconsistency: A Unified Graph Learning Framework for Multi-view Clustering}, 31 | author={Youwei Liang, Dong Huang, and Chang-Dong Wang}, 32 | booktitle={2019 IEEE International Conference on Data Mining (ICDM)}, 33 | year={2019} 34 | } 35 | -------------------------------------------------------------------------------- /SGF.m: -------------------------------------------------------------------------------- 1 | function [nmi, label] = SGF(data, truth, knn, beta, gamma, tol, tol2) 2 | % Use similarity graph fusion (SGF) algorithm to perform multi-view clustering. 3 | % Inputs: 4 | % data - a cell matrix which contains some feature matrices with each row being an instance 5 | % truth - a column vector, the target label for all instances 6 | % knn - number of k-nearest neighbors (set knn=0 if using fully connected graph) 7 | % beta, gamma - hyperparameters for the algorithm 8 | % Optional Inputs: 9 | % tol, tol2 - the tolerance that determines convergence of algorithm 10 | % Outputs: 11 | % nmi - normalized mutual information 12 | % label - label generated by spectral clustering on the learned unified graph 13 | % See "Youwei Liang, Dong Huang, and Chang-Dong Wang. Consistency Meets 14 | % Inconsistency: A Unified Graph Learning Framework for Multi-view 15 | % Clustering. 2019 IEEE International Conference on Data Mining(ICDM)." 16 | % for a detailed description of the algorithm. 17 | % Author: Youwei Liang 18 | % 2019/08/31 19 | 20 | if nargin < 6 21 | tol = 1e-4; 22 | tol2 = 1e-2; 23 | end 24 | self_b = beta; cross_b = gamma; 25 | v = length(data); % number of views 26 | affinity = cell(1, v); 27 | original_affinity = cell(1, v); 28 | idx = cell(1, v); 29 | n = length(truth); 30 | numClust = length(unique(truth)); 31 | ONE = ones(n); 32 | knn_idx = false(n); 33 | 34 | for i=1:v 35 | try 36 | s = sprintf('kernel%d.mat', i); % try to read the weight matrix (if saved previously) 37 | load(s) 38 | catch 39 | W = make_affinity_matrix(data{i}, 'euclidean'); 40 | % save(s, 'W') % weight matrix may be saved for repeating experiments 41 | end 42 | original_affinity{i} = W; 43 | if knn ~= 0 % not using fully connected graph 44 | [W, idx{i}] = kNN(W, knn); 45 | [~, tp] = extract_from_idx(ONE, idx{i}); 46 | knn_idx = knn_idx | logical(tp); % common kNN index for all views 47 | end 48 | affinity{i} = W; 49 | end 50 | 51 | if knn ~= 0 52 | for i=1:v 53 | for j=1:v 54 | if j~=i 55 | [~, tp] = extract_from_idx(original_affinity{i}, idx{j}); 56 | affinity{i} = affinity{i} + (tp + tp')/2; 57 | end 58 | end 59 | end 60 | end 61 | 62 | % make knn index symmetric 63 | if knn~=0 64 | knn_idx = knn_idx | knn_idx'; %#ok<*BDSCA> 65 | else 66 | knn_idx = true(n); 67 | end 68 | 69 | [com_affinity, E, A] = consistent_graph(affinity, knn_idx, self_b, cross_b, tol, tol2); 70 | 71 | % do kNN again 72 | com_a = kNN(com_affinity, knn); 73 | 74 | [label] = SpectralClustering(com_a, numClust, 3); % obtain lable for each instance, label # starts from 1 75 | 76 | nmi = NMImax(truth, label); 77 | % [ACC, MIhat, Purity] = ClusteringMeasure(truth, label); 78 | fprintf('knn:%2d, beta:%1.0e, gamma:%1.0e, NMI: %.3f\n', knn, self_b, cross_b, nmi) 79 | 80 | -------------------------------------------------------------------------------- /SpectralClustering.m: -------------------------------------------------------------------------------- 1 | function [C, L, U, D] = SpectralClustering(W, k, Type) 2 | %SPECTRALCLUSTERING Executes spectral clustering algorithm 3 | % Executes the spectral clustering algorithm defined by 4 | % Type on the adjacency matrix W and returns the k cluster 5 | % indicator vectors as columns in C. 6 | % If L and U are also called, the (normalized) Laplacian and 7 | % eigenvectors will also be returned. 8 | % 9 | % 'W' - Adjacency matrix, needs to be square 10 | % 'k' - Number of clusters to look for 11 | % 'Type' - Defines the type of spectral clustering algorithm 12 | % that should be used. Choices are: 13 | % 1 - Unnormalized 14 | % 2 - Normalized according to Shi and Malik (2000) 15 | % 3 - Normalized according to Ng, Jordan and Weiss (2002) 16 | % 17 | % References: 18 | % - Ulrike von Luxburg, "A Tutorial on Spectral Clustering", 19 | % Statistics and Computing 17 (4), 2007 20 | % 21 | % Author: Ingo Buerk 22 | % Year : 2011/2012 23 | % Bachelor Thesis 24 | 25 | % calculate degree matrix 26 | % d = (sum(W, 2)); 27 | % d = d ./ norm(d); 28 | % d = d.^2; 29 | % d = d ./ norm(d); 30 | % D = sparse(1:size(W, 1), 1:size(W, 2), d); 31 | % W = D*W*D; 32 | 33 | degs = sum(W, 2); 34 | D = sparse(1:size(W, 1), 1:size(W, 2), degs); 35 | 36 | % compute unnormalized Laplacian 37 | L = D - W; 38 | 39 | % compute normalized Laplacian if needed 40 | switch Type 41 | case 2 42 | % avoid dividing by zero 43 | degs(degs == 0) = eps; 44 | % calculate inverse of D 45 | D_ = spdiags(1./degs, 0, size(D, 1), size(D, 2)); 46 | 47 | % calculate normalized Laplacian 48 | L = D_ * L; 49 | case 3 50 | % avoid dividing by zero 51 | degs(degs == 0) = eps; 52 | % calculate D^(-1/2) 53 | D_ = spdiags(1./(degs.^0.5), 0, size(D, 1), size(D, 2)); 54 | 55 | % calculate normalized Laplacian 56 | L = D_ * L * D_; 57 | end 58 | 59 | % L may be almost symmetric due to error in numerical calculation, so we make it symmetric. 60 | L = (L + L')/2; 61 | 62 | % compute the eigenvectors corresponding to the k smallest 63 | % eigenvalues 64 | try 65 | [U, ~] = eigs(L, k, eps); 66 | catch 67 | fprintf('**************** eigs error ****************\n') 68 | U = rand(size(L,1), k); 69 | end 70 | % eigcut(U, data) 71 | 72 | % in case of the Jordan-Weiss algorithm, we need to normalize 73 | % the eigenvectors row-wise 74 | if Type == 3 75 | U_n = U ./ sqrt(sum(U.^2, 2)); 76 | end 77 | 78 | % now use the k-means algorithm to cluster U row-wise 79 | % C will be a n-by-1 matrix containing the cluster number for 80 | % each data point 81 | if any(isnan(U_n)) 82 | U_n(isnan(U_n)) = 0; 83 | end 84 | C = kmeans(U_n, k,'Replicates',30,'MaxIter',1000); 85 | 86 | % now convert C to a n-by-k matrix containing the k indicator 87 | % vectors as columns 88 | % I = sparse(1:size(D, 1), C, 1); 89 | % s = sqrt(sum(D * I, 1)); 90 | % H = I ./ s; 91 | 92 | end -------------------------------------------------------------------------------- /consistent_graph.m: -------------------------------------------------------------------------------- 1 | function [con_graph, E, A] = consistent_graph(W, knn_idx, self_b, cross_b, tol, tol2) 2 | % Learn a consistent graph from multiple graphs. 3 | % Inputs: 4 | % W - weight matrix of a graph 5 | % knn_idx - common kNN index for all views 6 | % Optional Inputs: 7 | % tol, tol2 - the tolerance that determines convergence of algorithm 8 | % Outputs: 9 | % con_graph - weight matrix of the learned unified graph 10 | % E - a cell matrix containing the inconsistent part of all views 11 | % A - a cell matrix containing the consistent part of all views 12 | % See "Youwei Liang, Dong Huang, and Chang-Dong Wang. Consistency Meets 13 | % Inconsistency: A Unified Graph Learning Framework for Multi-view 14 | % Clustering. 2019 IEEE International Conference on Data Mining(ICDM)." 15 | % for a detailed description of the algorithm. 16 | % Author: Youwei Liang 17 | % 2019/08/31 18 | 19 | if nargin < 5 20 | tol = 1e-8; tol2 = 1e-6; 21 | end 22 | v = length(W); 23 | if nnz(W{1})/numel(W{1}) < 0.4 % if W contains a large proportion of zeros, use sparse mode 24 | for i=1:v 25 | W{i} = sparse(W{i}); 26 | end 27 | sparse_mode = true; 28 | else 29 | for i=1:v 30 | W{i} = full(W{i}); 31 | end 32 | sparse_mode = false; 33 | end 34 | v = length(W); 35 | b = cross_b*ones(v) - diag(cross_b*ones(1,v)) + diag(self_b*ones(1,v)); 36 | b_coef = b + eye(v); 37 | n = size(W{1}, 1); 38 | baW = cell(v,1); 39 | special_baW = cell(v,1); 40 | true_baW = cell(v,1); 41 | A = cell(v,1); 42 | B = cell(v,1); 43 | E = cell(v,1); 44 | up_knn_idx = triu(knn_idx); 45 | 46 | obj_change = 10; 47 | iter = 0; 48 | maxiter = 40; 49 | changes = zeros(maxiter,1); 50 | obj = zeros(maxiter,1); 51 | 52 | zz = 2.^(0:v-1); 53 | ww = 1:2^v-2; % alpha can't be all zeros, so -2 54 | logww = log2(ww); 55 | yy = ww(abs(floor(logww)-logww)>eps); 56 | alpha_zeros_ones = de2bi([0,zz,yy]); 57 | n_eye_coef = -eye(v); 58 | 59 | % initialize A{i}, alpha, con_graph 60 | alpha = ones(v,1) / v; 61 | con_graph = W{1}; 62 | if sparse_mode 63 | D = sparse(n, n); 64 | else 65 | D = zeros(n,n); 66 | end 67 | for i=1:v 68 | D = max(D, W{i}); 69 | A{i} = full(W{i}); 70 | % temp = A{i}(knn_idx); 71 | % A{i}(knn_idx) = temp.*(1.5 - rand(m,1)); 72 | % A{i} = (A{i}+A{i}')/2; 73 | % A{i}(knn_idx) = temp.*(2 - 1.9*rand(m,1)); 74 | end 75 | for i=1:v 76 | if sparse_mode 77 | A{i} = sparse(A{i}); 78 | end 79 | A{i} = min(A{i}, D); 80 | end 81 | % con_graph = con_graph/v + rand(m,1); 82 | 83 | warning('off','MATLAB:nearlySingularMatrix') 84 | 85 | while obj_change > tol && iter < maxiter 86 | iter = iter + 1; 87 | % fix A{i}, update con_graph and alpha 88 | obj1 = 0; 89 | for i=1:v 90 | E{i} = W{i} - A{i}; 91 | temp = norm(alpha(i)*A{i}-con_graph, 'fro'); 92 | obj1 = obj1 + temp*temp; 93 | end 94 | coef = zeros(v); 95 | coef2 = zeros(v); 96 | for i=1:v 97 | for j=i:v 98 | coef(i,j) = sum(sum(A{i}.*A{j})); 99 | coef(j,i) = coef(i,j); 100 | coef2(i,j) = sum(sum(E{i}.*E{j})); 101 | coef2(j,i) = coef2(i,j); 102 | end 103 | end 104 | coef2 = coef2 .* b; 105 | 106 | obj2 = sum(sum(coef2 .* (alpha * alpha'))); 107 | last_obj = obj1+obj2; 108 | obj(iter) = last_obj; 109 | 110 | % compute coefficient for the linear equation 111 | H = 2*(diag(diag(coef)) - coef/v + coef2); 112 | one = ones(v, 1); 113 | 114 | old_alpha = alpha; 115 | old_con_graph = con_graph; 116 | 117 | for i=1:1 118 | mpl = alpha_zeros_ones(i,:); 119 | coef3 = H .* ~mpl + n_eye_coef .* mpl; 120 | X = [coef3, one; 1-mpl, 0]; 121 | temp_b = [zeros(v,1); 1]; 122 | if det(X) == 0 % abs(det(X)) <= eps 123 | fprintf('*************') 124 | solution = pinv(X)*temp_b; 125 | else 126 | solution = X \ temp_b; 127 | end 128 | alpha = EProjSimplex_new(solution(1:v)); 129 | end 130 | % fprintf('best_obj:%.3f\n', best_obj) 131 | con_graph = alpha(1)*A{1}; 132 | for j=2:v 133 | con_graph = con_graph + alpha(j)*A{j}; 134 | end 135 | con_graph = con_graph/v; 136 | 137 | alpha_change = norm(alpha-old_alpha, 'fro'); 138 | con_graph_change = norm(con_graph-old_con_graph, 'fro'); 139 | 140 | % fix con_graph and alpha, update A{i} 141 | alp_coef = alpha * alpha'; 142 | coef = alp_coef .* b_coef; 143 | if sparse_mode 144 | commom_baW = sparse(n, n); 145 | else 146 | commom_baW = zeros(n,n); 147 | end 148 | for i=1:v 149 | baW{i} = cross_b*alpha(i)*W{i}; 150 | special_baW{i} = self_b*alpha(i)*W{i}; 151 | commom_baW = commom_baW + baW{i}; 152 | end 153 | 154 | for i=1:v 155 | true_baW{i} = commom_baW-baW{i}+special_baW{i}; 156 | temp = full(alpha(i)*(con_graph + true_baW{i})); 157 | B{i} = temp(up_knn_idx); 158 | end 159 | right_b = cat(2, B{:})'; 160 | if det(coef) == 0 161 | solution = (pinv(coef) * right_b)'; 162 | fprintf('------------') 163 | else 164 | solution = (coef \ right_b)'; 165 | end 166 | solution(solution<0) = 0; 167 | A_change = 0; 168 | for i=1:v 169 | temp = solution(:,i); 170 | oldA = A{i}; 171 | A{i} = zeros(n, n); 172 | A{i}(up_knn_idx) = temp; 173 | A{i} = max(A{i}, A{i}'); 174 | A{i} = min(W{i}, A{i}); 175 | if sparse_mode 176 | A{i} = sparse(A{i}); 177 | end 178 | A_change = A_change + norm(oldA-A{i}, 'fro'); 179 | end 180 | 181 | change = A_change + alpha_change + con_graph_change; 182 | if iter > 1 183 | obj_change = min(abs(obj(iter)-obj(1:iter-1)))/abs(obj(1) - obj(iter)); 184 | end 185 | changes(iter) = change; 186 | if rem(iter, 5) == 0 187 | % fprintf('*%2d:%.2f | ', iter, obj_change) 188 | end 189 | if iter > 20 190 | tol = tol2; 191 | elseif iter > 30 192 | tol = tol2*10; 193 | end 194 | end 195 | 196 | warning('on','MATLAB:nearlySingularMatrix') 197 | % plot(1:iter, obj(1:iter)) 198 | end 199 | -------------------------------------------------------------------------------- /data/MSRCv1.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/youweiliang/ConsistentGraphLearning/63b053c07e019c57507892f63a510cdfcdece655/data/MSRCv1.mat -------------------------------------------------------------------------------- /data/bbcsport2v.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/youweiliang/ConsistentGraphLearning/63b053c07e019c57507892f63a510cdfcdece655/data/bbcsport2v.mat -------------------------------------------------------------------------------- /data/handwritten.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/youweiliang/ConsistentGraphLearning/63b053c07e019c57507892f63a510cdfcdece655/data/handwritten.mat -------------------------------------------------------------------------------- /distance_kNN.m: -------------------------------------------------------------------------------- 1 | function [A, idx] = distance_kNN(B, knn) 2 | [A, idx] = mink_new(B, knn, 2, 'sorting', false); 3 | n = size(A, 1); 4 | 5 | % adjacency_matrix = zeros(n,n); 6 | % for i=1:n 7 | % adjacency_matrix(i, idx(i,:)) = A(i, :); 8 | % end 9 | % A = sparse(adjacency_matrix); 10 | 11 | rowidx = ones(knn, n) .* [1:n]; 12 | A = sparse(rowidx, idx', A', n, n); 13 | 14 | % A = max(A, A'); 15 | A = (A + A')/2; 16 | % A = power(A .* A', 1/2); 17 | end -------------------------------------------------------------------------------- /extract_from_idx.m: -------------------------------------------------------------------------------- 1 | function [B, D, rowidx, colidx] = extract_from_idx(A, idx_matrix) 2 | % Pick elements from A accroding to idx_matrix. 3 | % Inputs: 4 | % A - the sourse matrix 5 | % idx_matrix - an index matrix, pick the elements from A if its colomn index in 6 | % the corespongding row of idx_matrix 7 | % Outputs: 8 | % B - the extracted matrix, same shape as idx_matrix 9 | % D - the extracted matrix, same shape as A 10 | 11 | 12 | [m, n] = size(idx_matrix); 13 | rowidx = reshape(ones(n, m) .* [1:m], 1, n*m); 14 | colidx = reshape(idx_matrix', 1, n*m); 15 | idx = sub2ind(size(A), rowidx, colidx); 16 | C = A(idx); 17 | B = reshape(C, n, m); 18 | B = B'; 19 | 20 | [i, j] = size(A); 21 | D = sparse(rowidx, colidx, C, i, j); 22 | 23 | % A = max(A, A'); 24 | % squareB = (D + D')/2; 25 | 26 | end -------------------------------------------------------------------------------- /kNN.m: -------------------------------------------------------------------------------- 1 | function [A, idx] = kNN(B, knn) 2 | [A, idx] = maxk_new(B, knn, 2, 'sorting', false); 3 | n = size(A, 1); 4 | 5 | % adjacency_matrix = zeros(n,n); 6 | % for i=1:n 7 | % adjacency_matrix(i, idx(i,:)) = A(i, :); 8 | % end 9 | % A = sparse(adjacency_matrix); 10 | 11 | rowidx = ones(knn, n) .* [1:n]; 12 | A = sparse(rowidx, idx', A', n, n); 13 | 14 | % A = max(A, A'); 15 | A = (A + A')/2; 16 | % A = power(A .* A', 1/2); 17 | end -------------------------------------------------------------------------------- /logical_extraction.m: -------------------------------------------------------------------------------- 1 | function [C] = logical_extraction(A, id) 2 | % Extract elements from A according to the id 3 | % id is a logical matrix, A is the target matrix, 4 | % it extracts the elements in A corresponding to the nonzero elements in id 5 | % size(C) == size(A) == size(id) 6 | 7 | extracted = A(id); 8 | linear_idx = find(id); 9 | [n, m] = size(A); 10 | B = sparse(linear_idx, ones(length(linear_idx),1), extracted, n*m, 1); 11 | C = reshape(B, n, m); 12 | 13 | end 14 | 15 | -------------------------------------------------------------------------------- /make_affinity_matrix.m: -------------------------------------------------------------------------------- 1 | function [affinity_matrix, distance_matrix] = make_affinity_matrix(data, metric) 2 | % Construct the weight matrix (a graph) for data. 3 | % Inputs: 4 | % data - a data feature matrix with each row being an instance (data point), each column representing a feature 5 | % metric - the metric used to measure the distance between two instances 6 | % Outputs: 7 | % affinity_matrix - a matrix representing the similarity between data points 8 | % distance_matrix - a matrix representing the distance (dissimilarity) between data points 9 | 10 | [N, M] = size(data); 11 | if strcmp(metric, 'cosine') 12 | distance_matrix = pdist2(data, data, 'cosine'); 13 | elseif strcmp(metric, 'original') 14 | distance_matrix = data; 15 | elseif strcmp(metric, 'euclidean') 16 | distance_matrix = pdist2(data, data, 'squaredeuclidean'); 17 | else 18 | error('unknown metric') 19 | end 20 | 21 | 22 | sigma = mean(mean(distance_matrix)); 23 | % sigma = median(dist); 24 | affinity_matrix = exp(-distance_matrix/(2*sigma)); 25 | 26 | for i=1:N 27 | affinity_matrix(i, i) = 0; 28 | end 29 | 30 | end --------------------------------------------------------------------------------