├── MinMaxSelection ├── Contents.txt ├── Internal_mxArray.h ├── Internal_mxArray_2010B.h ├── buildInternal_mxArrayDef.m ├── getmexopts.m ├── inplacecolumnmex.c ├── inplacecolumnmex.mexmaci64 ├── inplacecolumnmex.mexw64 ├── license.txt ├── maxk.m ├── maxkmex.c ├── maxkmex.m ├── maxkmex.mexmaci64 ├── maxkmex.mexw64 ├── mink.m ├── minkmex.c ├── minkmex.m ├── minkmex.mexmaci64 ├── minkmex.mexw64 ├── minmax_install.m ├── minmaxk.m ├── releaseinplace.c ├── releaseinplace.mexmaci64 ├── releaseinplace.mexw64 └── testminmax.m ├── OSDL.m ├── README.md ├── Script.m ├── lena.png └── utilities ├── ApplySparseDict.m ├── CSRec_SP.m ├── CroppedWaveletsDictionary.m ├── HardThres.m ├── NormDict.m ├── NormDictSep.m ├── OrderDict.m ├── ParallelSP.m ├── Results_aux_17-Jul-2018_pursuit_sp_Gram=1_parallel=0_colSort=1_colMin=8_Tdict=80_randomness=7744.mat ├── SP.m ├── ShowState.m ├── SparseDict.m ├── SparseDictT.m ├── SparseDictT_old.m ├── SparseDict_old.m ├── cgls.m └── getPatches2D.m /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.mexmaci64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsulam/OSDL/4964903f332b528b69c1776b17c6777fd3e4b6c0/MinMaxSelection/inplacecolumnmex.mexmaci64 -------------------------------------------------------------------------------- /MinMaxSelection/inplacecolumnmex.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsulam/OSDL/4964903f332b528b69c1776b17c6777fd3e4b6c0/MinMaxSelection/inplacecolumnmex.mexw64 -------------------------------------------------------------------------------- /MinMaxSelection/license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Bruno Luong 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the distribution 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 18 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /MinMaxSelection/maxk.m: -------------------------------------------------------------------------------- 1 | function varargout = maxk(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(@maxkmex, varargin{:}); 20 | varargout=nout; 21 | 22 | -------------------------------------------------------------------------------- /MinMaxSelection/maxkmex.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/maxkmex.m: -------------------------------------------------------------------------------- 1 | % function [res loc] = maxkmex(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 maxkmex.c'); -------------------------------------------------------------------------------- /MinMaxSelection/maxkmex.mexmaci64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsulam/OSDL/4964903f332b528b69c1776b17c6777fd3e4b6c0/MinMaxSelection/maxkmex.mexmaci64 -------------------------------------------------------------------------------- /MinMaxSelection/maxkmex.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsulam/OSDL/4964903f332b528b69c1776b17c6777fd3e4b6c0/MinMaxSelection/maxkmex.mexw64 -------------------------------------------------------------------------------- /MinMaxSelection/mink.m: -------------------------------------------------------------------------------- 1 | function varargout = mink(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(@minkmex, varargin{:}); 21 | varargout=nout; 22 | -------------------------------------------------------------------------------- /MinMaxSelection/minkmex.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/minkmex.m: -------------------------------------------------------------------------------- 1 | % function [res loc] = minkmex(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 minkmex.c') -------------------------------------------------------------------------------- /MinMaxSelection/minkmex.mexmaci64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsulam/OSDL/4964903f332b528b69c1776b17c6777fd3e4b6c0/MinMaxSelection/minkmex.mexmaci64 -------------------------------------------------------------------------------- /MinMaxSelection/minkmex.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsulam/OSDL/4964903f332b528b69c1776b17c6777fd3e4b6c0/MinMaxSelection/minkmex.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 ~isempty(strfind(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{:},'minkmex.c'); 47 | mex(mexopts{:},'maxkmex.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,@minkmex) 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 | if ~verLessThan('MATLAB', '8.3') || issparse(list) 92 | for n=1:N 93 | [res(:,n) loc(:,n)] = mexfun(list(:,n),k); %#ok 94 | end 95 | else 96 | for n=1:N 97 | cn = inplacecolumnmex(list,n); % inplace column 98 | [res(:,n) loc(:,n)] = mexfun(cn,k); 99 | releaseinplace(cn); 100 | %[res(:,n) loc(:,n)] = mexfun(list(:,n),k); 101 | end 102 | end 103 | else 104 | if ~verLessThan('MATLAB', '8.3') || issparse(list) 105 | for n=1:N 106 | res(:,n) = mexfun(list(:,n),k); 107 | end 108 | else 109 | for n=1:N 110 | cn = inplacecolumnmex(list,n); % inplace column 111 | res(:,n) = mexfun(cn,k); 112 | releaseinplace(cn); 113 | %res(:,n) = mexfun(list(:,n),k); 114 | end 115 | end 116 | end 117 | catch 118 | % If something is wrong 119 | % It crashes if cn is not released properly 120 | if exist('cn','var') && ~isempty(cn) 121 | releaseinplace(cn); 122 | end 123 | % rethrow the error (likely memory) 124 | rethrow(lasterror); 125 | end 126 | end 127 | 128 | % This is the post processing step of sorting the selection data 129 | % The purpose is to have a nicely formatted output, that's all 130 | 131 | if getloc 132 | if postsorting 133 | [res is] = sort(res,1,smode); 134 | j=(0:N-1)*k; 135 | % Use reshape instead of bsxfun for backward compatible 136 | if exist('bsxfun','builtin') 137 | is = bsxfun(@plus, reshape(is,[k N]), j); 138 | else 139 | is = reshape(is,[k N]) + repmat(j,[k 1]); 140 | end 141 | loc = reshape(loc(is),size(loc)); 142 | end 143 | % Put the operating dimension at the right place 144 | loc = shiftdim(loc,nd+1-dim); 145 | else 146 | if postsorting 147 | res = sort(res,1,smode); 148 | end 149 | end 150 | 151 | % Put the operating dimension at the right place 152 | res = shiftdim(res,nd+1-dim); 153 | 154 | end % minmaxk 155 | 156 | function val = getoptionpair(name, defaultval, vargin) 157 | % Get the value from property/value pairs 158 | val = defaultval; 159 | for k=1:2:length(vargin) 160 | if strmatch(vargin{k},name) 161 | val = vargin{k+1}; 162 | return 163 | end 164 | end 165 | 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.mexmaci64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsulam/OSDL/4964903f332b528b69c1776b17c6777fd3e4b6c0/MinMaxSelection/releaseinplace.mexmaci64 -------------------------------------------------------------------------------- /MinMaxSelection/releaseinplace.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsulam/OSDL/4964903f332b528b69c1776b17c6777fd3e4b6c0/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 | minkmex(1,1); 9 | minkmex(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(list,k); 29 | tmink(i)=toc; 30 | 31 | tic 32 | mx=maxk(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)); -------------------------------------------------------------------------------- /OSDL.m: -------------------------------------------------------------------------------- 1 | function [A,err,errG,times,times_test,V] = OSDL(params) 2 | % Online Sparse Dictionary Learning algorithm (OSDL). 3 | % 4 | % [A,err,errG,times] = OSDL (params) 5 | % 6 | % Main parameters: 7 | % - dictsep: Separable dictionaries per dimension, in matrix 8 | % form. 9 | % - Tdict : Atom cardinality. Number of non zeros in each 10 | % atom. 11 | % - Ytrain: Training data (OSDL does not include the 12 | % randomization of the data). 13 | % - datadir: If training data is not provided, OSDL can also 14 | % recieve a directory to data mini-batches. If 15 | % this is the case, dataname should be also 16 | % provided. 17 | % - dataname: Name of the matlab variable in the mini-batches 18 | % provided in DataDir. Refer to README for 19 | % further details and an example on this usage. 20 | % - Ytest: Testing data (optional) 21 | % - initA: Initial Sparse Dictionary (optional). 22 | % Otherwise, the dictionary is initialized with 23 | % the identity matrix. 24 | % - BatchSize: Batch size, if Ytrain is provided. Otherwise, 25 | % the BatchSize is the determined as the size of 26 | % the training data in each file in DataDir. 27 | % - Iter: Number of iterations/epochs. 28 | % - pursuit: The pursuit algorithm to use: either 'omp' or 29 | % 'sp'. If 'sp', the provided mode parameter is 30 | % ignored (treated as 'sparse'). Default: 'omp' 31 | % - useGram Whether to save Gram matrix for omp 32 | % calculations in order to improve performance. 33 | % - pursuit_test The pursuit algorithm to use while testing: 34 | % either 'omp' or 'sp'. If pursuit is 'sp' or 35 | % 'useGram'is false, then pursuit_test will 36 | % always be 'sp'. 37 | % - mode: 'sparse'/'error', constraint for the Sparse 38 | % Coding Stage. 'sparse' provides Tdata number of 39 | % nonzeros per training example. 'error' codes 40 | % each example so that its energy is not larger 41 | % than Tdata. 42 | % - Tdata: Data Sparsity constraint (if mode = 'sparse') 43 | % or Data fidelity contraint (if mode = 'error') 44 | % - columnSort Boolean indicates wether to use column sorting 45 | % in hardThres steps (true) or a full matrix 46 | % sort (false). default is true. 47 | % - colMin integer represents minimum number of non zero 48 | % indices in each column for hardThres function. 49 | % default is Tdata*0.1 50 | % - colMax integer represents maximum number of non zero 51 | % indices in each column for hardThres function. 52 | % default is Tdata*1.4 53 | % - atomNumberForMutualCheck 54 | % number of atoms to check when clearing the 55 | % dictionary from similar atoms 56 | % - sp_iterations number of iterations for sp when it is used as 57 | % the pursuit method. Default: 15 58 | % - cgls_iterations number of iterations for cgls (conjugate 59 | % gradient least squares) when sp is used as the 60 | % pursuit method. Default: 100 61 | % 62 | % Auxiliary parameters: 63 | % - TestingInterval: Number of dictionary updates before sparse 64 | % coding testing data 65 | % - NCleanDict: Times per data sweep to clean/prune dictionary 66 | % (default: 1) 67 | % - muthresh: Mutual Threshold for cleaning Dictionary (0.99 68 | % default) 69 | % - gamma: Momentum parameter. (0.9 default) 70 | % - Tmax: Maximal Training Time. (default = []: 71 | % deactivated) 72 | % - num_cores: option for parallel computing (Default 0). If 73 | % 1, it enables (or uses) matlab's parfor for 74 | % distributing the sparse coding stage. 75 | % 76 | % Output: 77 | % - A: Trained Sparse Dictionary 78 | % - err: Residual: average representation error for 79 | % 'sparse' mode or the average NNZ coeffitients 80 | % in 'error' mode 81 | % - errG: Generalization error: representation error over 82 | % the Testing Data, if provided 83 | % - times: A times vector corresponding to each iteration. 84 | % 85 | % 86 | % References: 87 | % "Trainlets: Dictionary Learning in High Dimensions", J. Sulam, B. Ophir, 88 | % M. Zibulevsky and M. Elad, to appear in IEEE Transactions on Signal Processing, 89 | % arXiv:1602.00212v3. 90 | 91 | 92 | 93 | % AVAILABLE ONLINE: 94 | % Version 1.0 95 | 96 | % My Versions 97 | % Version: 1.1 98 | % - Added times_gen to register the time at testing 99 | % - Added parallelization of sparse coding: NOT WORKING WELL 100 | % - Added TestingInterval option 101 | % - Added: Time does not include testing time now. 102 | % - Added saving intermediate results functionality 103 | % 104 | % Jeremias Sulam 105 | % jsulam@cs.technion.ac.il 106 | % Technion - IIT 107 | % January 2016 108 | 109 | % addpath('utilities\'); 110 | 111 | % ----- Checking for packages ----- 112 | 113 | if ~exist('omp') 114 | error('OMP Package missing!'); 115 | end 116 | if ~exist('omps') 117 | error('Sparse OMP Package missing!'); 118 | end 119 | 120 | % ----- Input Parameters ------- 121 | 122 | if isfield(params,'dictsep') 123 | phi = params.dictsep; 124 | dictsep{1} = phi; 125 | dictsep{2} = phi; 126 | else 127 | error('Base dictionary missing!'); 128 | end 129 | 130 | 131 | if isfield(params,'Tdict') 132 | Tdict = params.Tdict; 133 | else 134 | error('Dictionary Sparsity missing!'); 135 | end 136 | 137 | 138 | if isfield(params,'initA') 139 | A = params.initA; 140 | if ~issparse(A) 141 | error('Sparse Dictionary matrix A should be sparse!') 142 | end 143 | else 144 | m_ = size(dictsep{1},2)^2; 145 | A = zeros(m_,m_); 146 | for i = 1 : m_ 147 | A(randperm(m_,Tdict)',i) = randn(Tdict,1); 148 | end 149 | A = NormDictSep(phi,sparse(A)); 150 | end 151 | 152 | if isfield(params,'Ytrain') 153 | Ytrain = params.Ytrain; 154 | params.Ytrain = []; 155 | EXTERNAL_DATA = 0; 156 | else 157 | if isfield(params,'datadir') 158 | DATADIR = params.datadir; 159 | EXTERNAL_DATA = 1; 160 | if isfield(params,'dataname') 161 | DATANAME = params.dataname; 162 | else 163 | error('Data name not specified'); 164 | end 165 | else 166 | error('Training data or data directory missing!'); 167 | end 168 | end 169 | 170 | if isfield(params,'save_dir') 171 | save_dir = params.save_dir; 172 | else 173 | save_dir = []; 174 | end 175 | 176 | if isfield(params,'Tmax') 177 | Tmax = params.Tmax; 178 | else 179 | Tmax = []; 180 | end 181 | bandExit = 0; 182 | 183 | 184 | if isfield(params,'BatchSize') 185 | K = params.BatchSize; 186 | else 187 | K = 512; 188 | end 189 | 190 | if isfield(params,'Iter') 191 | Iter = params.Iter; 192 | else 193 | Iter = 4; 194 | end 195 | 196 | 197 | if isfield(params,'Ytest') 198 | Ytest = params.Ytest; 199 | else 200 | Ytest = []; 201 | end 202 | 203 | if isfield(params,'Tdata') 204 | Tdata = params.Tdata; 205 | else 206 | error('Sparsity or Error constraint needed.') 207 | end 208 | 209 | if isfield(params,'pursuit') 210 | pursuit = params.pursuit; 211 | else 212 | pursuit = 'omp'; 213 | end 214 | 215 | 216 | % define omp_bool according to pursuit algorithm 217 | if strcmp(pursuit, 'omp') 218 | omp_bool=1; 219 | if isfield(params,'mode') 220 | mode = params.mode; 221 | else 222 | error('MODE needs to be specified (sparse\error).') 223 | end 224 | else 225 | omp_bool=0; 226 | mode = 'sparse'; 227 | end 228 | 229 | if isfield(params, 'sp_iterations') 230 | spIter = params.sp_iterations; 231 | else 232 | spIter = 15; 233 | end 234 | if isfield(params,'cgls_iterations') 235 | cglsIter = params.cgls_iterations; 236 | else 237 | cglsIter = 100; 238 | end 239 | 240 | if isfield(params,'gamma') 241 | gamma = params.gamma; 242 | else 243 | gamma = 0.92; 244 | end 245 | 246 | % Extra regularization: How often to perform Dictionary Cleaning or to use 247 | % dictionary subset per minibatch (DropAtoms) 248 | 249 | if isfield(params,'NCleanDict') 250 | Nclean = params.NCleanDict; 251 | DropAtoms = 0; 252 | if isfield(params,'DropAtoms') 253 | warning('Do not specify both NCleanDict and DropAtoms options.'); 254 | end 255 | else 256 | if isfield(params,'DropAtoms') 257 | Nclean = 0; 258 | DropAtoms = params.DropAtoms; 259 | if DropAtoms < 0 || DropAtoms > 1 260 | DropAtoms = .5; 261 | end 262 | if isfield(params,'StopDropOut') 263 | StopDropOut = params.StopDropOut; 264 | else 265 | StopDropOut = 1; % stop option after 1 epoch 266 | end 267 | else % default options 268 | Nclean = 1; 269 | DropAtoms = 0; 270 | end 271 | end 272 | 273 | 274 | % mutual threshold for atoms cleaning 275 | if isfield(params,'muthresh') 276 | muthresh = params.muthresh; 277 | else 278 | muthresh = 0.98; 279 | end 280 | 281 | % hard threshold params 282 | if isfield(params,'columnSort') 283 | columnSort = params.columnSort; 284 | else 285 | columnSort = true; 286 | end 287 | 288 | if isfield(params,'colMin') 289 | colMin = params.colMin; 290 | else 291 | colMin = ceil(Tdict*0.1); 292 | end 293 | 294 | if isfield(params,'colMax') 295 | colMax = params.colMax; 296 | else 297 | colMax = ceil(Tdict*1.4); 298 | end 299 | 300 | if isfield(params,'atomNumberForMutualCheck') 301 | atomNumberForMutualCheck = params.atomNumberForMutualCheck; 302 | else 303 | atomNumberForMutualCheck = ceil(size(A, 2)); 304 | end 305 | 306 | % Momentum variable 307 | if isfield(params,'V') 308 | if ~isempty(params.V) 309 | V = params.V; 310 | params.V = []; 311 | V = double(V); 312 | else 313 | V = zeros(size(A)); 314 | end 315 | else 316 | V = zeros(size(A)); 317 | end 318 | 319 | % Parallel options 320 | if isfield(params,'num_cores') 321 | parallel_opt = params.num_cores; 322 | if parallel_opt 323 | pool = gcp('nocreate'); 324 | if isempty(pool) && params.num_cores > 1 325 | pool = parpool(params.num_cores); 326 | end 327 | Ncores = pool.NumWorkers; 328 | end 329 | else 330 | parallel_opt = 0; 331 | end 332 | 333 | if isfield(params,'useGram') && ~parallel_opt 334 | useGram = params.useGram; 335 | else 336 | useGram = 1; 337 | end 338 | 339 | if isfield(params,'pursuit_test') 340 | pursuit_test = params.pursuit_test; 341 | if strcmp(pursuit_test, 'omp') && omp_bool && useGram 342 | omp_test = 1; 343 | else 344 | omp_test = 0; 345 | end 346 | else 347 | omp_test = 0; 348 | end 349 | 350 | % Init Variables for external data, if enabled 351 | if EXTERNAL_DATA 352 | DATALIST = what(DATADIR); 353 | DATALIST = DATALIST.mat; 354 | NumBatches = length(DATALIST); 355 | end 356 | 357 | if ~EXTERNAL_DATA 358 | N = size(Ytrain,2); 359 | NumBatches = floor(N/K); 360 | means = mean(Ytrain); 361 | for i = 1 : length(means) 362 | Ytrain(:,i) = Ytrain(:,i) - means(i); 363 | end 364 | % Ytrain = Ytrain./max(Ytrain(:)); 365 | end 366 | 367 | if ~isempty(Ytest) 368 | means = mean(Ytest); 369 | for i = 1 : length(means) 370 | Ytest(:,i) = Ytest(:,i) - means(i); 371 | end 372 | end 373 | TIME_TEST = 0; 374 | TIME_SAVE = 0; 375 | 376 | % How often to evaluate Test, if enabled 377 | if isfield(params,'TestingInterval') 378 | TESTING_INTERVAL = params.TestingInterval; 379 | else 380 | TESTING_INTERVAL = NumBatches; 381 | end 382 | 383 | % Saving intermediate results? 384 | if isfield(params,'SavingInterval') 385 | SAVE_FLAG = 1; 386 | if params.SavingInterval > 0 387 | SAVING_INTERVAL = params.SavingInterval; 388 | else 389 | SAVING_INTERVAL = NumBatches; 390 | end 391 | else 392 | SAVE_FLAG = 0; 393 | end 394 | 395 | 396 | % ---- Init and Variable Definition --------- 397 | 398 | [m1,m2] = size(A); 399 | 400 | Ik = speye(m1); 401 | 402 | % control variables 403 | err = zeros(Iter*NumBatches,1); 404 | if ~isempty(Ytest) 405 | times_test = zeros(Iter*floor(NumBatches/TESTING_INTERVAL),1); 406 | errG = zeros(Iter*floor(NumBatches/TESTING_INTERVAL),1); 407 | else 408 | times_test = []; 409 | errG = []; 410 | end 411 | times = zeros(Iter*NumBatches,1); 412 | 413 | % Dictionary variables 414 | PHI = SparseDict(phi,[],speye(size(phi,2)^2)); 415 | Dnorms = sqrt(sum(SparseDict(phi,[],A).^2,1)); 416 | Inorms = sparse(1:length(Dnorms),1:length(Dnorms),Dnorms.^(-1),length(Dnorms),length(Dnorms),length(Dnorms)); 417 | An = sparse(A*Inorms); 418 | if omp_bool 419 | GPHI = SparseDictT(phi,[],PHI); 420 | PHI = []; 421 | G = A'*GPHI*A; 422 | if useGram 423 | Gn = Inorms*G*Inorms; 424 | else 425 | Gn = []; 426 | end 427 | else 428 | GPHI=[]; 429 | G=[]; 430 | Gn=[]; 431 | end 432 | 433 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 434 | % Main Loop % 435 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 436 | 437 | 438 | Gene = tic; 439 | 440 | tic_test = tic; 441 | 442 | atoms_dropout_histogram = zeros(m2,1); 443 | 444 | for iter = 1 : Iter 445 | 446 | usecount = zeros(m2,1); % counts how many times each atom has been used per datasweep.* 447 | TotErr = 0; 448 | TotPsnr = 0; 449 | samples_counter = 0; 450 | 451 | %%% MiniBatches Iterations %%% 452 | for k = 1 : NumBatches 453 | 454 | fprintf('Batch %i of %i. ',k,NumBatches) 455 | 456 | if ~EXTERNAL_DATA 457 | Ybatch = Ytrain(:,1+(k-1)*K:k*K); 458 | else 459 | load([DATADIR,'/',DATALIST{k}]); 460 | Ybatch = double(eval(DATANAME)); 461 | meansBatch = mean(Ybatch); 462 | for i = 1 : length(meansBatch) 463 | Ybatch(:,i) = Ybatch(:,i) - meansBatch(i); 464 | end 465 | %Ybatch = bsxfun(@minus, Ybatch, mean(Ybatch)); 466 | 467 | clear(DATANAME); 468 | end 469 | 470 | %%% Sparse Coding %%% 471 | 472 | % option for sub-dictionary 473 | if DropAtoms~=0 && iter <= StopDropOut 474 | mask = sort(randperm(size(An,2),round(size(An,2)*DropAtoms))); 475 | atoms_dropout_histogram(mask) = atoms_dropout_histogram(mask)+1; 476 | Asub = An(:,mask); 477 | if omp_bool 478 | Gn_sub = Gn(:,mask); 479 | Gn_sub = Gn_sub(mask,:); 480 | end 481 | else 482 | Asub = An; 483 | if omp_bool 484 | Gn_sub = Gn; 485 | end 486 | mask = 1:size(An,2); 487 | end 488 | 489 | Xt = zeros(size(A,2),size(Ybatch,2)); 490 | if omp_bool 491 | if strcmp(mode,'sparse') 492 | % if parallel_opt 493 | % Xt = OMP_p_G(An,phi,Ybatch,Tdata,Ncores,Gn); 494 | % else 495 | if useGram 496 | proj = SparseDictT(phi,Asub,Ybatch); 497 | Xt(mask,:) = omp(proj,Gn_sub,Tdata); 498 | else 499 | Xt = zeros(size(A,2),size(Ybatch,2)); 500 | dict = SparseDict(phi, [], Asub); 501 | Xt(mask,:) = omp(dict,Ybatch, [],Tdata); 502 | end 503 | % end 504 | else 505 | % if parallel_opt 506 | % Xt = omp2_par(proj,sum(Ybatch.*Ybatch),Gn,Tdata); 507 | % else 508 | if useGram 509 | proj = SparseDictT(phi,Asub,Ybatch); 510 | Xt(mask,:) = omp2(proj,sum(Ybatch.*Ybatch),Gn_sub,Tdata); 511 | else 512 | Xt = zeros(size(A,2),size(Ybatch,2)); 513 | dict = SparseDict(phi, [], Asub); 514 | Xt(mask,:) = omp2(dict,Ybatch, [],Tdata); 515 | end 516 | % end 517 | end 518 | Xt = Inorms*Xt; 519 | else 520 | N = size(Ybatch, 2); 521 | Xt(mask,:) = ParallelSP(size(Asub,2), N, Tdata, phi, Asub, Ybatch, spIter, cglsIter); 522 | Xt = Inorms*Xt; 523 | end 524 | 525 | %%% Fidelity Error calc %%% 526 | X_rec = SparseDict(phi,A,Xt); 527 | Er = X_rec - Ybatch; 528 | 529 | %%% gradient calc %%% 530 | aux = sum(abs(Xt),2)'; 531 | [~,ChosenAtoms] = find(aux~=0); 532 | ALFA = (Er*Xt(ChosenAtoms,:)'); 533 | gradA = zeros(size(A)); 534 | gradA(:,ChosenAtoms) = SparseDictT(phi,[],ALFA); 535 | 536 | %%% Step Size %%% 537 | num = norm(gradA(:,ChosenAtoms),'fro'); 538 | BETA = gradA(:,ChosenAtoms)*Xt(ChosenAtoms,:); 539 | AUX = SparseDict(phi,[],BETA); 540 | den = norm(AUX,'fro'); 541 | mu = num / den; 542 | 543 | %%% update of Sparse Dictionary A %%% 544 | V = gamma*V + mu*gradA(:,:); 545 | update = A(:,ChosenAtoms) - V(:,ChosenAtoms); 546 | A(:,ChosenAtoms) = sparse(HardThres(update,Tdict,columnSort,colMin,colMax)); 547 | 548 | %%% Gram and Dictionary Update %%% 549 | if ~isempty(ChosenAtoms) 550 | %%% update of normalization %%% 551 | Dnorms(ChosenAtoms) = sqrt(sum(SparseDict(phi,[],A(:,ChosenAtoms)).^2,1)); 552 | Inorms = sparse(1:m2,1:m2,Dnorms.^(-1),m2,m2,m2); 553 | An(:,ChosenAtoms) = sparse(A(:,ChosenAtoms)*Inorms(ChosenAtoms,ChosenAtoms)); 554 | if omp_bool 555 | new_atoms = SparseDict(phi,[],A(:,ChosenAtoms)); 556 | gram_cols = SparseDictT(phi,A,new_atoms); 557 | G(:,ChosenAtoms) = gram_cols; 558 | G(ChosenAtoms,:) = gram_cols'; 559 | if useGram 560 | Gn = Inorms*G*Inorms; 561 | end 562 | end 563 | end 564 | 565 | usecount = usecount + sum(abs(Xt)>1e-7, 2); 566 | 567 | if (Nclean~=0) && (rem(k,floor(NumBatches/Nclean)) == 0) && (iterTmax, 620 | bandExit = 1; 621 | break; 622 | end 623 | end 624 | 625 | % stats 626 | TotErr = TotErr+ (err(k+(iter-1)*NumBatches))*K; 627 | samples_counter = samples_counter + K; 628 | AvErr = TotErr/samples_counter; 629 | TotPsnr = TotPsnr + psnr(X_rec/255,Ybatch/255); 630 | AvPSNR = TotPsnr / samples_counter; 631 | fprintf('Error: %.2f. Current PSNR: %.2f. Av. Time: %.2f s\n',AvErr, psnr(X_rec/255,Ybatch/255),toc(Gene)/k) 632 | 633 | end 634 | 635 | if bandExit 636 | fprintf('\nFinishing for Maximal Time Condition\n\n'); 637 | break; 638 | end 639 | 640 | end 641 | 642 | save([ 'Results_aux_', date, '_pursuit_', pursuit, '_Gram=', num2str(useGram), '_parallel=', num2str(parallel_opt) , '_colSort=', num2str(columnSort), '_colMin=', num2str(colMin),'_Tdict=', num2str(Tdict), '_randomness=', num2str(atomNumberForMutualCheck)],'A','err','times','errG', 'times_test', 'NumBatches') 643 | 644 | if parallel_opt 645 | delete(gcp()); 646 | end 647 | 648 | end 649 | 650 | 651 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 652 | % cleardict % 653 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 654 | 655 | 656 | function [A,G,cleared_atoms] = cleardict(basedict,PHI,A,An,X,baseG,G,Gn,Ik,Tdict,Tdata,useGram,muthresh,atomNumberForMutualCheck,usecount,omp_bool) 657 | % check for similar atoms 658 | use_thresh = 1; % at least this number of samples must use the atom to be kept 659 | nnz = 3; 660 | [m1,dictsize] = size(A); 661 | phi = basedict{1}; 662 | s = size(X,2); 663 | K = min(1e3,s); 664 | inds = randperm(s,K); % random set of K signals for computation of representation error 665 | Y=X(:,inds); 666 | if omp_bool 667 | Gamma = omps(basedict,An,Y,Gn,nnz); 668 | else 669 | dict = SparseDict(phi, [], A); 670 | Gamma = ParallelSP(dictsize, K, nnz, phi, A, Y, 5, 10); 671 | end 672 | err = sum((Y - dictsep(basedict,A,Gamma)).^2); 673 | 674 | cleared_atoms = []; 675 | inds2 = randperm(dictsize, min(dictsize, atomNumberForMutualCheck)); 676 | 677 | if omp_bool && useGram 678 | for j = 1:dictsize 679 | if usecount(j)muthresh 695 | [atom, err] = makeAlternativeAtomOMP(A, err, basedict, Ik, X, baseG, Tdict, inds); 696 | A(:, inds2(j)) = atom; 697 | cleared_atoms(length(cleared_atoms)+1) = inds2(j); 698 | end 699 | end 700 | 701 | cleared_atoms = sort(cleared_atoms); 702 | 703 | else 704 | for j = 1:dictsize 705 | if usecount(j) < use_thresh 706 | [atom, err] = makeAlternativeAtomSP(A, err, Tdict, phi, X, inds); 707 | A(:, j) = atom; 708 | end 709 | end 710 | for j1 = 1:length(inds2) 711 | inds3 = inds2; 712 | inds3(j1) = []; 713 | atom1 = dict(:, inds2(j1)); 714 | norm1 = norm(atom1); 715 | for j2 = 1:length(inds3) 716 | atom2 = dict(:, inds3(j2)); 717 | if abs(dot(atom1, atom2))/norm1/norm(atom2) > muthresh 718 | [atom, err] = makeAlternativeAtomSP(A, err, Tdict, phi, X, inds); 719 | A(:, inds2(j1)) = atom; 720 | break; 721 | end 722 | end 723 | end 724 | end 725 | 726 | if ~isempty(cleared_atoms) && omp_bool 727 | % update D and G 728 | new_atoms = SparseDict(phi,[],A(:,cleared_atoms)); 729 | gram_cols = A'*SparseDictT(phi,[],new_atoms); 730 | G(:,cleared_atoms) = gram_cols; 731 | G(cleared_atoms,:) = gram_cols'; 732 | end 733 | end 734 | 735 | function [atom, err] = makeAlternativeAtomSP(A, err, Tdict, phi, X, inds) 736 | if sum(err~=0) == 0 737 | i = randi([1 length(inds)], 1, 1); 738 | else 739 | [~,i] = max(err); 740 | err(i) = 0; 741 | end 742 | rec = SP(Tdict, phi, speye(size(phi, 2)^2), X(:,inds(i)), [], 10); 743 | atom = rec.x_hat; 744 | end 745 | 746 | function [atom, err] = makeAlternativeAtomOMP(A, err, basedict, Ik, X, baseG, Tdict, inds) 747 | if sum(err~=0) == 0 748 | i = randi([1 length(inds)], 1, 1); 749 | else 750 | [~,i] = max(err); 751 | err(i) = 0; 752 | end 753 | atom = omps(basedict, Ik, X(:,inds(i)), baseG, Tdict, 'checkdict', 'off'); 754 | d = dictsep(basedict, Ik, atom); 755 | atom = atom./norm(d); 756 | end 757 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OSDL 2 | Online Sparse Dictionary Learning Algorithm 3 | 4 | 5 | This is the Matlab Package for the Online Sparse Dictionary Learning (OSDL) 6 | algorithm, presented in: 7 | J. Sulam, B. Ophir, M. Zibulevsky and M. Elad, "Trainlets: Dictionary 8 | Learning in High Dimensions," in IEEE Transactions on Signal Processing, 9 | vol. 64, no. 12, pp. 3180-3193, June15, 2016. 10 | 11 | Version 1.1 12 | 13 | INSTALLATION: 14 | - The OSDL requieres to have the OMP-BOX and the OMPS-BOX installed, which 15 | can be freely downloaded from 16 | http://www.cs.technion.ac.il/~ronrubin/software.html 17 | - Once these packages have their corresponding mex files compiled 18 | (check their instructions for further details), make sure they are added in 19 | the matlab path. 20 | 21 | USAGE: 22 | The main function is OSDL.m, which performs dictionary learning on the 23 | indictaded training data and outputs a sparse dictionary (refer to the 24 | referenced paper for more details). OSDL has two basic modes of operation, 25 | in terms of the training data: 26 | 1) If all training data can be stored in memory, it can be provided through 27 | the parameter Ytrain. Other details (such as mini-batch size, etc) can also 28 | be specified. 29 | 2) If the training data cannot be stored in memory, OSDL can receive a 30 | directory to where there training data is stored. In this case, the data 31 | should be saved in a directory in separate .mat files. Each .mat file 32 | corresponds to a mini-batch. The name of the variable (with the training 33 | data in matrix form) in the files should be specified through the 34 | parameters dataname. 35 | For example, data could be stored in a 'Data/' directory in minibatches 36 | Data1.mat, Data2.mat, ... DataN.mat. Each file would contain a matrix 37 | variable called DataMiniBatch, containing the training samples for that 38 | mini-batch ordered columnwise. 39 | 40 | A demo Script is provided to show the basic usage of OSDL. 41 | 42 | ------------------------------------------------------- 43 | 44 | UPDATES FROM V.1.0: 45 | 46 | - Subspace Pursuit (SP) included as an alternative pursuit option. This provides 47 | a faster and lighter alternative to OMP which which does not scale as well 48 | for high dimensional signals (dimension of 10,000+). Unlike OMP, the 49 | number of iterations of SP can be much smaller than the target sparsity, 50 | and this implementaion does not require to store in memory the Gram matrix, 51 | which can be prohibitely large in such cases. This enables efficient 52 | parallelization of the pursuit as well. 53 | 54 | - This version, unlike the previous one, does not require the MatrixMult 55 | package (thanks to Grisha Varksman). 56 | 57 | - Another external regularization option was added. In the previous version, 58 | one could select how many times per epoch the dictionary to be cleaned from 59 | unused or repeated atoms. Now it is possible to use an alternative method: 60 | selecting a random subset of atoms (given by the parameter DropAtoms 61 | between 0 and 1) for the first StopDropOut epochs. 62 | 63 | 64 | ------------------------------------------------------- 65 | 66 | 67 | All comments are welcomed at jsulam@cs.technion.ac.il 68 | 69 | Jeremias Sulam 70 | Computer Science Department - Technion 71 | March, 2016. 72 | -------------------------------------------------------------------------------- /Script.m: -------------------------------------------------------------------------------- 1 | clc 2 | clear 3 | 4 | addpath('utilities\'); 5 | 6 | %% loading Image and data formation 7 | 8 | p = 32; % patch size 9 | 10 | Im = double(imread('lena.png')); 11 | Y = getPatches2D( Im , p , []); 12 | Y = reshape(Y,[p^2,size(Y,3)]); 13 | Y = bsxfun(@minus, Y, mean(Y)); 14 | 15 | Ytraining = Y; 16 | Ytest = Y(:,end-2e3+1:end);% 17 | clear Y 18 | 19 | %% Cropped Wavelets Base Dictionary 20 | 21 | phi = CroppedWaveletsDictionary(p,'sym8',2); 22 | m = size(phi,2); 23 | Aini = speye(m^2); 24 | BaseDict{1} = phi;BaseDict{2} = phi; 25 | Tdata = 15; 26 | disp('First OMP') 27 | tic, 28 | Err0 = norm( SparseDict(phi,Aini,omps(BaseDict,Aini,Ytest,[],Tdata)) - Ytest,'fro')/sqrt(numel(Ytest)), 29 | toc, 30 | 31 | %% OSDL 32 | 33 | params.BatchSize = 512; 34 | params.Tdict = 64; 35 | params.Iter = 2; 36 | params.mode = 'sparse'; 37 | params.Tdata = Tdata; 38 | params.dictsep = phi; 39 | params.Ytrain = Ytraining; 40 | 41 | [A,err,errG,times] = OSDL(params); 42 | A = NormDictSep(phi,A); 43 | 44 | ErrOSDL = norm( SparseDict(phi,A,omps(BaseDict,A,Ytest,[],Tdata)) - Ytest,'fro')/sqrt(numel(Ytest)), 45 | -------------------------------------------------------------------------------- /lena.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsulam/OSDL/4964903f332b528b69c1776b17c6777fd3e4b6c0/lena.png -------------------------------------------------------------------------------- /utilities/ApplySparseDict.m: -------------------------------------------------------------------------------- 1 | function X = ApplySparseDict(phi,A,y,flag) 2 | 3 | switch flag 4 | case 1 5 | X = SparseDict(phi,A,y); 6 | case 2 7 | X = SparseDictT(phi,A,y); 8 | end 9 | 10 | end -------------------------------------------------------------------------------- /utilities/CSRec_SP.m: -------------------------------------------------------------------------------- 1 | function Rec = CSRec_SP(K,Phi,y) 2 | % For algorithm description, explanation and analysis, please check 3 | % Wei Dai and Olgica Milenkovic 4 | % "Subspace Pursuit for Compressive Sensing: Closing the 5 | % Gap Between Performance and Complexity"% 6 | 7 | [m,N]=size(Phi); 8 | 9 | y_r = y; 10 | in = 1; 11 | 12 | cv = abs( y_r'*Phi ); 13 | [cv_sort, cv_index] = sort(cv,'descend'); 14 | cv_index = sort( cv_index(1:K) ); 15 | Phi_x = Phi(:,cv_index); 16 | Index_save(in,:) = cv_index; 17 | 18 | x_p = inv(Phi_x'*Phi_x)*Phi_x' * y; 19 | y_r = y - Phi_x*x_p; 20 | norm_save(in) = norm(y_r); 21 | 22 | while 1 23 | in = in+1; 24 | 25 | % find T^{\prime} and add it to \hat{T} 26 | cv = abs( y_r'*Phi ); 27 | [cv_sort, cv_index] = sort(cv,'descend'); 28 | cv_index = sort( cv_index(1:K) ); 29 | cv_add = union(Index_save(in-1,:), cv_index); 30 | Phi_x = Phi(:,cv_add); 31 | 32 | % find the most significant K indices 33 | x_p = inv(Phi_x'*Phi_x)*Phi_x' * y; 34 | [x_p_sort, i_sort] = sort( abs(x_p) , 'descend' ); 35 | cv_index = cv_add( i_sort(1:K) ); 36 | cv_index = sort( cv_index ); 37 | Phi_x = Phi(:,cv_index); 38 | Index_save(in,:)=cv_index; 39 | 40 | % calculate the residue 41 | x_p = inv(Phi_x'*Phi_x)*Phi_x' * y; 42 | y_r = y - Phi_x*x_p; 43 | 44 | norm_save(in) = norm(y_r); 45 | 46 | if ( norm_save(in) == 0 ) | ... 47 | (norm_save(in)/norm_save(in-1) >= 1) 48 | break; 49 | end 50 | end 51 | 52 | x_hat = zeros(N,1); 53 | x_hat( Index_save(in,:) ) = reshape(x_p,K,1); 54 | Rec.T = Index_save; 55 | Rec.x_hat = x_hat; 56 | Rec.PResidue = norm_save; -------------------------------------------------------------------------------- /utilities/CroppedWaveletsDictionary.m: -------------------------------------------------------------------------------- 1 | function [Wc] = CroppedWaveletsDictionary(n,wname,rd) 2 | % function Wc = CroppedWaveletsDictionary(n,wname,rd) 3 | % 4 | % Cropped Wavelets (Synthesis) Dictionary construction. 5 | % 6 | % Input: n: signal dimension 7 | % wname: Wavelet name 8 | % rd: (optinal) Redundancy option (default 1). If rd==1, then 9 | % the dictionary is redundant by cropping. If rd==0, then 10 | % the output is a regular wavelet (1-D) synthesis 11 | % dictionary. 12 | % 13 | % Output: Wc: Cropped Wavelet Synthesis Dictionary 14 | % 15 | % References: 16 | % J. Sulam et at, Trainlets: Dictionary Learning in High Dimensions, 2016 17 | 18 | if nargin < 3 19 | rd = 1; 20 | end 21 | 22 | if rd 23 | Nw = 2^(ceil(log2(n))+1); 24 | else 25 | Nw = n; 26 | end 27 | dwtmode('per','nodisp'); 28 | 29 | 30 | % Inverse Transform / synthesis dictionary 31 | [c,L]=wavedec(ones(1,Nw),floor(log2(Nw)),wname); 32 | I = eye(length(c)); 33 | Wc = zeros(Nw,length(c)); 34 | for i=1:size(Wc,2); 35 | Wc(:,i)=waverec(I(i,:),L,wname); 36 | end 37 | % Cropping 38 | Wc = Wc ( round(Nw/2)-n/2+1 : round(Nw/2)+n/2 , 1:end ); 39 | % deleting zero atoms 40 | norms = diag(Wc'*Wc); 41 | % Normalizing atoms 42 | Wc = NormDict(Wc(:,norms~=0)); 43 | 44 | end -------------------------------------------------------------------------------- /utilities/HardThres.m: -------------------------------------------------------------------------------- 1 | function X = HardThres2(X,k,columnSort, colMin, colMax) 2 | % X = HardThres(X,k) 3 | % 4 | % HardThresholding function that keeps the k highest coefficients (in 5 | % absolute value) PER COLUMN in the matrix X 6 | % MegaFace_dataset.tar.gz.part 7 | % Jeremias Sulam 8 | % July 2015 9 | % CS - Technion 10 | k = ceil(k); 11 | [n,m] = size(X); 12 | 13 | if columnSort 14 | [~,inds] = sort(abs(X),1,'descend'); 15 | 16 | for i = 1 : m 17 | X(inds(k+1:end,i),i) = 0; 18 | end 19 | 20 | X = sparse(X); 21 | 22 | else 23 | Ktotal = k*m; 24 | kMin = m*colMin; 25 | K = Ktotal - kMin; % total number of nonzeros to take from the middle portion 26 | colMax = min(K,n); 27 | Signs = sign(X); 28 | [vals,indsX] = maxk(abs(X),colMax,1); 29 | indsMed = indsX(colMin+1:colMax,:); 30 | vals_med = vals(colMin+1:colMax,:); 31 | vals_med = vals_med(:); 32 | [~,inds] = maxk(vals_med,K,1,'sorting',false); 33 | 34 | [indsToKeep1, indsToKeep2] = ind2sub([colMax - colMin, m], inds); 35 | X = zeros(size(X)); 36 | for i = 1:m 37 | X(indsX(1:colMin,i),i) = vals(1:colMin,i); 38 | end 39 | 40 | for i = 1:K 41 | X(indsMed(indsToKeep1(i), indsToKeep2(i)),indsToKeep2(i)) = vals_med(inds(i)); 42 | end 43 | 44 | X = X.*Signs; 45 | 46 | end 47 | 48 | 49 | end -------------------------------------------------------------------------------- /utilities/NormDict.m: -------------------------------------------------------------------------------- 1 | function D = NormDict (A) 2 | D=zeros(size(A)); 3 | for i=1:size(A,2) 4 | if norm(A(:,i))~=0, 5 | D(:,i)=A(:,i)/norm(A(:,i)); 6 | end 7 | end 8 | end -------------------------------------------------------------------------------- /utilities/NormDictSep.m: -------------------------------------------------------------------------------- 1 | function [Aout,atomnorms] = NormDictSep(phi,A) 2 | %NORMDICTSEP Sparse-Separable Dictionary Normalization. 3 | % 4 | % Normalizes the sparse dictionary given by D = PHI*A, where PHI = 5 | % kron(phi,phi), to unit norm per atom. 6 | % 7 | % Input: phi: base dictionary (1-D) 8 | % A: Sparse Matrix 9 | % 10 | % Output: Aout: Normalized Sparse Matrix 11 | % atomnorms: The norms of the input Dictionary 12 | % 13 | % J. Sulam - Technion IIT% 14 | % Januray 2016 15 | 16 | M = size(A,2); 17 | atomnorms = sqrt(sum(SparseDict(phi,[],A).^2,1)); 18 | Aout = A*sparse(1:M,1:M,1./atomnorms,M,M); 19 | 20 | end 21 | -------------------------------------------------------------------------------- /utilities/OrderDict.m: -------------------------------------------------------------------------------- 1 | function Dout = OrderDict(Din,opt,mode) 2 | 3 | % function Dout = OrderDict(Din,opt) 4 | % Sorts dictionary atoms by decreasing variance (opt=1) 5 | % or decreasing entropy (opt2) 6 | % 7 | % Jeremias Sulam - Technion 8 | 9 | [n,m] = size(Din); 10 | Dout = zeros(size(Din)); 11 | 12 | 13 | switch opt 14 | case 1 % variance 15 | vars = std(Din,0,1); 16 | [~,ind] = sort(vars,mode); 17 | Dout = Din(:,ind); 18 | return 19 | case 2 % entropy 20 | ent = zeros(m,1); 21 | for i = 1 : m 22 | ent(i) = entropy(reshape(Din(:,i),[sqrt(n),sqrt(n)])); 23 | end 24 | [~,ind] = sort(ent,mode); 25 | Dout = Din(:,ind); 26 | return 27 | case 3 28 | Tv = zeros(m,1); 29 | for i = 1 : m 30 | [Gx,Gy] = gradient(reshape(Din(:,i),[sqrt(n),sqrt(n)])); 31 | Tv(i) = sum(abs(Gx(:))+abs(Gy(:))); 32 | end 33 | [~,ind] = sort(Tv,mode); 34 | Dout = Din(:,ind); 35 | 36 | end 37 | 38 | 39 | end -------------------------------------------------------------------------------- /utilities/ParallelSP.m: -------------------------------------------------------------------------------- 1 | function X = ParallelSP(m, n, k, phi, A, Y, spIterations, cglsIterations) 2 | X = spalloc(m, n, n * k); 3 | phi2 = parallel.pool.Constant(phi); 4 | A2 = parallel.pool.Constant(A); 5 | parfor j = 1:n 6 | rec = SP(k, phi2.Value, A2.Value, Y(:, j), spIterations, cglsIterations); 7 | X(:, j) = rec.x_hat; 8 | end 9 | clear phi2; 10 | clear A2; 11 | end -------------------------------------------------------------------------------- /utilities/Results_aux_17-Jul-2018_pursuit_sp_Gram=1_parallel=0_colSort=1_colMin=8_Tdict=80_randomness=7744.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsulam/OSDL/4964903f332b528b69c1776b17c6777fd3e4b6c0/utilities/Results_aux_17-Jul-2018_pursuit_sp_Gram=1_parallel=0_colSort=1_colMin=8_Tdict=80_randomness=7744.mat -------------------------------------------------------------------------------- /utilities/SP.m: -------------------------------------------------------------------------------- 1 | function Rec = SP(K, phi, A, y, spIterations, cglsIterations) 2 | % For algorithm description, explanation and analysis, please check 3 | % Wei Dai and Olgica Milenkovic 4 | % "Subspace Pursuit for Compressive Sensing: Closing the 5 | % Gap Between Performance and Complexity"% 6 | 7 | if (nargin < 4 || isempty(spIterations)) 8 | spIterations = 100; 9 | end 10 | 11 | if (nargin < 5 || isempty(spIterations)) 12 | cglsIterations = []; 13 | end 14 | 15 | m = size(phi, 1)^2; 16 | N = size(A, 2); 17 | 18 | y_r = y; 19 | in = 1; 20 | 21 | %cv = abs(y_r' * Phi); 22 | cv = abs(SparseDictT(phi, A, y_r))'; 23 | [~, cv_index] = maxk(cv, K, 2, 'sorting', false); 24 | cv_index = sort(cv_index); 25 | %Phi_x = Phi(:, cv_index); 26 | A_x = A(:, cv_index); 27 | Index_save(in, :) = cv_index; 28 | 29 | %x_p = cgls(Phi_x, y, [], [], cglsIterations); 30 | func_h = @(y, tflag) ApplySparseDict(phi, A_x, y, tflag); 31 | x_p = cgls(func_h, y, [], [], cglsIterations); 32 | %y_r = y - Phi_x * x_p; 33 | y_r = y - SparseDict(phi, A_x, x_p); 34 | norm_save(in) = norm(y_r); 35 | 36 | for j = 1 : spIterations 37 | in = in + 1; 38 | 39 | % find T^{\prime} and add it to \hat{T} 40 | %cv = abs(y_r' * Phi); 41 | cv = abs(SparseDictT(phi, A, y_r))'; 42 | [~, cv_index] = maxk(cv, K, 2, 'sorting', false); 43 | cv_add = union(Index_save(in - 1, :), sort(cv_index)); 44 | %Phi_x = Phi(:,cv_add); 45 | A_x = A(:, cv_add); 46 | 47 | % find the most significant K indices 48 | %x_p = cgls(Phi_x, y, [], [], cglsIterations); 49 | func_h = @(y, tflag) ApplySparseDict(phi, A_x, y, tflag); 50 | x_p = cgls(func_h, y, [], [], cglsIterations); 51 | [~, i_sort] = maxk(abs(x_p), K, 1, 'sorting', false); 52 | cv_index = cv_add(sort(i_sort)); 53 | %Phi_x = Phi(:, cv_index); 54 | A_x = A(:, cv_index); 55 | Index_save(in, :) = cv_index; 56 | 57 | % calculate the residue 58 | %x_p = cgls(Phi_x, y, [], [], cglsIterations); 59 | func_h = @(y, tflag) ApplySparseDict(phi, A_x, y, tflag); 60 | x_p = cgls(func_h, y, [], [], cglsIterations); 61 | %y_r = y - Phi_x*x_p; 62 | y_r = y - SparseDict(phi, A_x, x_p); 63 | 64 | norm_save(in) = norm(y_r); 65 | 66 | if ( norm_save(in) <= eps ) | ... 67 | (norm_save(in)/norm_save(in-1) >= 1) 68 | break; 69 | end 70 | end 71 | 72 | x_hat = zeros(N, 1); 73 | x_hat(Index_save(in,:)) = reshape(x_p, K, 1); 74 | Rec.T = Index_save; 75 | Rec.x_hat = x_hat; 76 | Rec.PResidue = norm_save; 77 | end -------------------------------------------------------------------------------- /utilities/ShowState.m: -------------------------------------------------------------------------------- 1 | function [] = ShowState(i,I) 2 | % This function is intended to show an approximate progress of the training 3 | % process. 4 | % 5 | % [] = ShowState(i,I), i: current State. 6 | % I: Maximum State 7 | % 8 | % J. Sulam - Technion 9 | % Jan. 2016 10 | 11 | C = 5; % every 5% 12 | 13 | if i==1 14 | fprintf('\n \n') 15 | fprintf('Training <<--') 16 | end 17 | 18 | if i> Finished. \n \n ') 27 | end 28 | end -------------------------------------------------------------------------------- /utilities/SparseDict.m: -------------------------------------------------------------------------------- 1 | function Y = SparseDict(phi,A,X) 2 | % SparseDict: Applies the Sparse Dictionary A, with base dictionary phi, to 3 | % the data in X. 4 | % 5 | % [Y] = SparseDict(phi,A,X): returns the results of the product PHI*A*X 6 | % 7 | % [Y] = SparseDict(phi,[],X): returns the results of the product PHI*X 8 | % 9 | % J. Sulam - Technion IIT% 10 | % Januray 2016 11 | 12 | N = size(X,2); 13 | [n, m] = size(phi); 14 | 15 | if ~isempty(A) 16 | tmp = full(A * X); 17 | else 18 | tmp = full(X); 19 | end 20 | tmp = phi * reshape(tmp, [m, m * N]); 21 | tmp = phi * reshape(tmp', m, n * N); 22 | tmp = reshape(tmp, n * N, n)'; 23 | Y = reshape(tmp, [n^2, N]); 24 | 25 | 26 | end -------------------------------------------------------------------------------- /utilities/SparseDictT.m: -------------------------------------------------------------------------------- 1 | function X = SparseDictT(phi,A,Y) 2 | % SparseDictT: Applies the Transpose of the sparse Dictionary A, with base 3 | % dictionary phi, to the data in Y. 4 | % 5 | % [X] = SparseDictT(phi,A,Y): returns the results of the product 6 | % (PHI*A)^T*Y 7 | % 8 | % [X] = SparseDict(phi,[],Y): returns the results of the product PHI^T*Y 9 | % 10 | % J. Sulam - Technion IIT% 11 | % Januray 2016 12 | 13 | 14 | N = size(Y,2); 15 | [n, m] = size(phi); 16 | 17 | tmp = phi' * reshape(Y, [n, n * N]); 18 | tmp = phi' * reshape(tmp', n, m * N); 19 | tmp = reshape(tmp, m * N, m)'; 20 | if ~isempty(A) 21 | X = A' * reshape(tmp, [m^2, N]); 22 | else 23 | X = reshape(tmp, [m^2, N]); 24 | end 25 | 26 | end -------------------------------------------------------------------------------- /utilities/SparseDictT_old.m: -------------------------------------------------------------------------------- 1 | function X = SparseDictT(phi,A,Y) 2 | % SparseDictT: Applies the Transpose of the sparse Dictionary A, with base 3 | % dictionary phi, to the data in Y. 4 | % 5 | % [X] = SparseDictT(phi,A,Y): returns the results of the product 6 | % (PHI*A)^T*Y 7 | % 8 | % [X] = SparseDict(phi,[],Y): returns the results of the product PHI^T*Y 9 | % 10 | % J. Sulam - Technion IIT% 11 | % Januray 2016 12 | 13 | 14 | [m1] = size(phi,2)^2; 15 | n = size(phi,1)^2; 16 | N = size(Y,2); 17 | 18 | if ~isempty(A) 19 | X = A'*reshape( mtimesx(mtimesx(phi',reshape(Y,[sqrt(n) sqrt(n) N]),'speed'),phi,'speed'),[m1,N]); 20 | else 21 | X = reshape( mtimesx(mtimesx(phi',reshape(Y,[sqrt(n) sqrt(n) N]),'speed'),phi,'speed'),[m1,N]); 22 | end 23 | 24 | end -------------------------------------------------------------------------------- /utilities/SparseDict_old.m: -------------------------------------------------------------------------------- 1 | function Y = SparseDict(phi,A,X) 2 | % SparseDict: Applies the Sparse Dictionary A, with base dictionary phi, to 3 | % the data in X. 4 | % 5 | % [Y] = SparseDict(phi,A,X): returns the results of the product PHI*A*X 6 | % 7 | % [Y] = SparseDict(phi,[],X): returns the results of the product PHI*X 8 | % 9 | % J. Sulam - Technion IIT% 10 | % Januray 2016 11 | 12 | [m1] = size(phi,2)^2; 13 | n = size(phi,1)^2; 14 | N = size(X,2); 15 | 16 | if ~isempty(A) 17 | Y = reshape( mtimesx(mtimesx(phi,reshape(full(A*X),[sqrt(m1) sqrt(m1) N])),phi'),[n,N]); 18 | else 19 | Y = reshape( mtimesx(mtimesx(phi,reshape(full(X),[sqrt(m1) sqrt(m1) N])),phi'),[n,N]); 20 | end 21 | 22 | 23 | end -------------------------------------------------------------------------------- /utilities/cgls.m: -------------------------------------------------------------------------------- 1 | function [x,flag,resNE,iter] = cgls(A,b,shift,tol,maxit,prnt,x0) 2 | %CGLS Conjugate Gradient Least Squares 3 | % X = CGLS(A,B) attempts to solve the system of linear equations A*X=B 4 | % for X. The M-by-N coefficient matrix A and right hand side column 5 | % vector B of length N are required input arguments. 6 | % 7 | % X = CGLS(AFUN,B) accepts a function handle AFUN instead of the matrix 8 | % A. AFUN(X,1) accepts a vector input X and returns the matrix-vector 9 | % product A*X. AFUN(X,2) returns the matrix-vector product A'*X instead. 10 | % In all of the following syntaxes, you can replace A by AFUN. 11 | % 12 | % X = CGLS(A,B,SHIFT) specifies a regularization parameter SHIFT. If 13 | % SHIFT is 0, then CGLS is Hestenes and Stiefel's specialized form of the 14 | % conjugate-gradient method for least-squares problems. If SHIFT is 15 | % nonzero, the system (A'*A + SHIFT*I)*X = A'*B is solved. Here I is the 16 | % N-by-N identity matrix. 17 | % 18 | % X = CGLS(A,B,SHIFT,TOL) specifies the tolerance of the method. If TOL 19 | % is [] then CGLS uses the default, 1e-6. 20 | % 21 | % X = CGLS(A,B,SHIFT,TOL,MAXIT) specifies the maximum number of 22 | % iterations. If MAXIT is [] then CGLS uses the default, 20. 23 | % 24 | % X = CGLS(A,B,SHIFT,TOL,MAXIT,PRNT) specifies if output should be 25 | % generated during each iteration (PRNT == true). If PRNT is [] then no 26 | % output is given. 27 | % 28 | % X = CGLS(A,B,SHIFT,TOL,MAXIT,PRNT,X0) specifies the N-by-1 initial 29 | % solution that is used. If X0 is [] then CGLS uses the default, 30 | % X0 = zeros(N,1). 31 | % 32 | % [X,FLAG] = CGLS(A,B,...) also returns a convergence FLAG: 33 | % 1. CGLS converged to the desired tolerance TOL within MAXIT 34 | % iterations. 35 | % 2. CGLS iterated MAXIT times but did not converge. 36 | % 3. Matrix (A'*A + SHIFT*I) seems to be singular or indefinite. 37 | % 4. Instability seems likely meaning (A'*A + SHIFT*I) indefinite and 38 | % NORM(X) decreased. 39 | % 40 | % [X,FLAG,RESNE] = CGLS(A,B,...) also returns the relative residual for 41 | % the normal equations NORM(A'*B - (A'*A + SHIFT*I)*X)/NORM(A'*B). 42 | % 43 | % [X,FLAG,RESNE,ITER] = CGLS(A,B,...) also returns the iteration number 44 | % at which X was computed: 0 <= ITER <= MAXIT. 45 | % 46 | % See also LSQR, PCG, FUNCTION_HANDLE. 47 | 48 | % 01 Sep 1999: First version. 49 | % Per Christian Hansen (DTU) and Michael Saunders (visiting 50 | % DTU). 51 | % 22 Jan 2013: Updated syntax and documentation. 52 | % Folkert Bleichrodt (CWI). 53 | 54 | 55 | % Assign default values to unspecified parameters 56 | if (nargin < 3 || isempty(shift)), shift = 0; end 57 | if (nargin < 4 || isempty(tol)) , tol = 1e-6; end 58 | if (nargin < 5) , maxit = []; end 59 | if (nargin < 6 || isempty(prnt)) , prnt = 0; end 60 | if (nargin < 7) , x0 = []; end 61 | 62 | 63 | if isa(A, 'numeric') 64 | explicitA = true; 65 | elseif isa(A, 'function_handle') 66 | explicitA = false; 67 | else 68 | error('A must be numeric or a function handle.'); 69 | end 70 | 71 | % handle initial guess, if passed as argument 72 | if explicitA 73 | [m,n] = size(A); 74 | 75 | if ~isempty(x0) 76 | x = x0; 77 | else 78 | x = zeros(n,1); 79 | end 80 | 81 | r = b - A*x; 82 | s = A'*r-shift*x; 83 | else 84 | m = size(b,1); 85 | 86 | if ~isempty(x0) 87 | x = x0; 88 | r = b - A(x,1); 89 | s = A(r,2) - shift*x; 90 | n = size(s,1); 91 | else 92 | r = b; 93 | s = A(b,2); 94 | n = size(s,1); 95 | x = zeros(n,1); 96 | end 97 | 98 | end 99 | 100 | % determine default for maxit 101 | if isempty(maxit) 102 | maxit = min([m,n,20]); 103 | end 104 | 105 | % Initialize 106 | p = s; 107 | norms0 = norm(s); 108 | gamma = norms0^2; 109 | normx = norm(x); 110 | xmax = normx; 111 | k = 0; 112 | flag = 0; 113 | 114 | if prnt 115 | head = ' k x(1) x(n) normx resNE'; 116 | form = '%5.0f %16.10g %16.10g %9.2g %12.5g\n'; 117 | disp(' '); disp(head); 118 | fprintf(form, k, x(1), x(n), normx, 1); 119 | end 120 | 121 | indefinite = 0; 122 | 123 | %-------------------------------------------------------------------------- 124 | % Main loop 125 | %-------------------------------------------------------------------------- 126 | while (k < maxit) && (flag == 0) 127 | 128 | k = k+1; 129 | 130 | % q = A p 131 | if explicitA 132 | q = A*p; 133 | else 134 | q = A(p,1); 135 | end 136 | 137 | delta = norm(q)^2 + shift*norm(p)^2; 138 | if delta <= 0, indefinite = 1; end 139 | if delta == 0, delta = eps; end 140 | alpha = gamma / delta; 141 | 142 | x = x + alpha*p; 143 | r = r - alpha*q; 144 | 145 | if explicitA 146 | s = A'*r - shift*x; 147 | else 148 | s = A(r,2) - shift*x; 149 | end 150 | 151 | norms = norm(s); 152 | gamma1 = gamma; 153 | gamma = norms^2; 154 | beta = gamma / gamma1; 155 | p = s + beta*p; 156 | 157 | % Convergence 158 | normx = norm(x); 159 | xmax = max(xmax, normx); 160 | flag = (norms <= norms0 * tol) || (normx * tol >= 1); 161 | 162 | % Output 163 | resNE = norms / norms0; 164 | if prnt, fprintf(form, k, x(1), x(n), normx, resNE); end 165 | end % while 166 | 167 | iter = k; 168 | 169 | shrink = normx/xmax; 170 | if k == maxit, flag = 2; end 171 | if indefinite, flag = 3; end 172 | if shrink <= sqrt(tol), flag = 4; end 173 | -------------------------------------------------------------------------------- /utilities/getPatches2D.m: -------------------------------------------------------------------------------- 1 | function [ Y ] = getPatches2D( I , p , Npatches) 2 | % [ Y ] = getPatches2D( I , p , Npatches) 3 | % 4 | % Gets Npatches 2D patches at random location from image I 5 | % 6 | % Inputs: I: Image 7 | % p: patch size 8 | % Npatches: # of patches to take. If empty, takes all possible 9 | % patches 10 | % 11 | % Output: Y: Patches in matrix of size (p x p x Npatches) 12 | % 13 | % J. Sulam - Technion 14 | % Jan. 2016 15 | 16 | [N,M]=size(I); 17 | if isempty(Npatches) || (Npatches > (N-p+1)*(M-p+1)) 18 | Npatches = (N-p+1)*(M-p+1); 19 | end 20 | Y=zeros(p,p,Npatches); 21 | 22 | i=1; 23 | j=1; 24 | cords = randperm((M-p+1)*(N-p+1),Npatches); 25 | inds_x = zeros(Npatches,1); 26 | inds_y = zeros(Npatches,1); 27 | for i=1:Npatches 28 | inds_x(i) = floor(cords(i)/(M-p+1)-.0001)+1; 29 | inds_y(i) = cords(i)-(inds_x(i)-1)*(M-p+1); 30 | end 31 | 32 | for n = 1:Npatches 33 | patch=I(inds_x(n):inds_x(n)+p-1,inds_y(n):inds_y(n)+p-1); 34 | Y(:,:,n)=patch; 35 | j=j+1; 36 | if (j>M-p+1) 37 | j=1; 38 | i=i+1; 39 | end 40 | end 41 | 42 | end 43 | 44 | --------------------------------------------------------------------------------