I have implemented kNN (k-nearest neighbors) as follows, but it is very slow. I want to get an exact k-nearest-neighbor, not the approximate ones, so I didn't use the FLANN or ANN libraries.
mexFindNN.cpp
#include <iostream>
using namespace std;
#include "mex.h"
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <string.h>
#include <vector>
#include <algorithm>
struct Pair{
int id;
double value;
Pair(int id, double value){
this->id=id;
this->value=value;
}
};
struct PairCompare {
bool operator()(Pair const &left, Pair const &right) {
return left.value < right.value;
}
};
template<typename T>
void FindNN(T *X, T *Y, int N, int d, int type, int inner_k, int outer_k, mxArray *innerM, mxArray *outerM){
if(!type)//just inner_k
{
vector<size_t> ir;
vector<size_t> jc;jc.push_back(0);
vector<double> pr;
size_t num_ele=0;
for(int i=0;i<N;i++){//X[j*N+i]
vector<Pair> inner;
for(int j=0;j<N;j++){
double temp=0.0;
for(int k=0;k<d;k++){
temp+=(X[k*N+i]-X[k*N+j])*(X[k*N+i]-X[k*N+j]);
}
if(Y[i]==Y[j]){
inner.push_back(Pair(j,sqrt(temp)));
}
}
std::sort(inner.begin(),inner.end(),PairCompare());
for(int j=1;j<=inner_k && j<inner.size();j++){
Pair x=inner[j];
ir.push_back(x.id);
pr.push_back(x.value);
num_ele++;
}
jc.push_back(num_ele);
}
size_t *pIr=(size_t *)mxGetIr(innerM);
size_t *pJc=(size_t *)mxGetJc(innerM);
double *pPr=(double *)mxGetPr(innerM);
memcpy(pIr,&ir[0],ir.size()*sizeof(size_t));
memcpy(pJc,&jc[0],jc.size()*sizeof(size_t));
memcpy(pPr,&pr[0],pr.size()*sizeof(double));
}
else
{
vector<size_t> ir,ir2;
vector<size_t> jc,jc2;jc.push_back(0);jc2.push_back(0);
vector<double> pr,pr2;
size_t num_ele=0;
size_t num_ele2=0;
for(int i=0;i<N;i++){//X[j*N+i]
vector<Pair> inner, outer;
for(int j=0;j<N;j++){
double temp=0.0;
for(int k=0;k<d;k++){
temp+=(X[k*N+i]-X[k*N+j])*(X[k*N+i]-X[k*N+j]);
}
if(Y[i]==Y[j]){
inner.push_back(Pair(j,sqrt(temp)));
}else{
outer.push_back(Pair(j,sqrt(temp)));
}
}
std::sort(inner.begin(),inner.end(),PairCompare());
std::sort(outer.begin(),outer.end(),PairCompare());
for(int j=1;j<=inner_k && j<inner.size();j++){
Pair x=inner[j];
ir.push_back(x.id);
pr.push_back(x.value);
num_ele++;
}
jc.push_back(num_ele);
for(int j=0;j<outer_k && j<outer.size();j++){
Pair x=outer[j];
ir2.push_back(x.id);
pr2.push_back(x.value);
num_ele2++;
}
jc2.push_back(num_ele2);
}
size_t *pIr=(size_t *)mxGetIr(innerM);
size_t *pJc=(size_t *)mxGetJc(innerM);
double *pPr=(double *)mxGetPr(innerM);
memcpy(pIr,&ir[0],ir.size()*sizeof(size_t));
memcpy(pJc,&jc[0],jc.size()*sizeof(size_t));
memcpy(pPr,&pr[0],pr.size()*sizeof(double));
size_t *pIr2=(size_t *)mxGetIr(outerM);
size_t *pJc2=(size_t *)mxGetJc(outerM);
double *pPr2=(double *)mxGetPr(outerM);
memcpy(pIr2,&ir2[0],ir2.size()*sizeof(size_t));
memcpy(pJc2,&jc2[0],jc2.size()*sizeof(size_t));
memcpy(pPr2,&pr2[0],pr2.size()*sizeof(double));
}
}
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
//prhs[0]: X
//prhs[1]: Y
//prhs[2]: inner_k
//prhs[3]: outer_k
//plhs[0]: inner_kNN_Matrix
//plhs[1]: outer_kNN_Matrix
//mwSize dims_n=mxGetNumberOfDimensions(prhs[0]);
const mwSize *dims= mxGetDimensions(prhs[0]);
int type=(int)mxGetScalar(prhs[2]);
int inner_k=(int)mxGetScalar(prhs[3]);
int outer_k=(int)mxGetScalar(prhs[4]);
mxClassID clsID = mxGetClassID(prhs[0]);
if(clsID==mxSINGLE_CLASS){
int N=dims[0];
int d=dims[1];
float *X=(float *)mxGetPr(prhs[0]);
float *Y=(float *)mxGetPr(prhs[1]);
plhs[0]=mxCreateSparse(N,N,N*inner_k,mxREAL);
if(type)
{
plhs[1]=mxCreateSparse(N,N,N*outer_k,mxREAL);
FindNN<float>(X,Y,N,d,type,inner_k,outer_k,plhs[0],plhs[1]);
}
else
{
FindNN<float>(X,Y,N,d,type,inner_k,outer_k,plhs[0],NULL);
}
}else if(clsID==mxDOUBLE_CLASS){
int N=dims[0];
int d=dims[1];
double *X=(double *)mxGetPr(prhs[0]);
double *Y=(double *)mxGetPr(prhs[1]);
plhs[0]=mxCreateSparse(N,N,N*inner_k,mxREAL);
if(type)
{
plhs[1]=mxCreateSparse(N,N,N*outer_k,mxREAL);
FindNN<double>(X,Y,N,d,type,inner_k,outer_k,plhs[0],plhs[1]);
}
else
{
FindNN<double>(X,Y,N,d,type,inner_k,outer_k,plhs[0],NULL);
}
}
}
ConstructNNGraph2.m
function [innerG,outerG]=ConstructNNGraph2(X,Y,inner_k,outer_k)
[N,d]=size(X);
if isempty(Y)
Y=ones(N,1);
end
type=0;
if outer_k>0
type=1;
end
if(type)
[innerG,outerG]=mexFindNN(X,Y,1,inner_k,outer_k);
innerG = max(innerG, innerG');
outerG = max(outerG, outerG');
else
[innerG]=mexFindNN(X,Y,0,inner_k,0);
outerG=[];
end
The code above needs to be compiled in a MATLAB environment. The compile command is
mex -largeArrayDims mexFindNN.cpp
The sample input X
and Y
is as follows:
load fisheriris; Y=zeros(150,1); Y(1:50)=1; Y(51:100)=2; Y(101:end)=3; X=meas; [innerG,outerG]=ConstructNNGraph2(X,Y,3,5);