/************************************************************************* * K-NN query: approximate K nearest neighbors * * INPUT PARAMETERS * KDT - KD-tree * X - point, array[0..NX-1]. * K - number of neighbors to return, K>=1 * SelfMatch - whether self-matches are allowed: * if True, nearest neighbor may be the point itself * (if it exists in original dataset) * if False, then only points with non-zero distance * are returned * Eps - approximation factor, Eps>=0. eps-approximate nearest * neighbor is a neighbor whose distance from X is at * most (1+eps) times distance of true nearest neighbor. * * RESULT * number of actual neighbors found (either K or N, if K>N). * * NOTES * significant performance gain may be achieved only when Eps is is on * the order of magnitude of 1 or larger. * * This subroutine performs query and stores its result in the internal * structures of the KD-tree. You can use following subroutines to obtain * these results: * KDTreeQueryResultsX() to get X-values * KDTreeQueryResultsXY() to get X- and Y-values * KDTreeQueryResultsTags() to get tag values * KDTreeQueryResultsDistances() to get distances * * -- ALGLIB -- * Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ public static int kdtreequeryaknn(ref kdtree kdt, ref double[] x, int k, bool selfmatch, double eps) { int result = 0; int i = 0; int j = 0; double vx = 0; double vmin = 0; double vmax = 0; System.Diagnostics.Debug.Assert(k > 0, "KDTreeQueryKNN: incorrect K!"); System.Diagnostics.Debug.Assert((double)(eps) >= (double)(0), "KDTreeQueryKNN: incorrect Eps!"); // // Prepare parameters // k = Math.Min(k, kdt.n); kdt.kneeded = k; kdt.rneeded = 0; kdt.selfmatch = selfmatch; if (kdt.normtype == 2) { kdt.approxf = 1 / AP.Math.Sqr(1 + eps); } else { kdt.approxf = 1 / (1 + eps); } kdt.kcur = 0; // // calculate distance from point to current bounding box // kdtreeinitbox(ref kdt, ref x); // // call recursive search // results are returned as heap // kdtreequerynnrec(ref kdt, 0); // // pop from heap to generate ordered representation // // last element is non pop'ed because it is already in // its place // result = kdt.kcur; j = kdt.kcur; for (i = kdt.kcur; i >= 2; i--) { tsort.tagheappopi(ref kdt.r, ref kdt.idx, ref j); } return(result); }
/************************************************************************* * K-NN query: K nearest neighbors * * INPUT PARAMETERS * KDT - KD-tree * X - point, array[0..NX-1]. * K - number of neighbors to return, K>=1 * SelfMatch - whether self-matches are allowed: * if True, nearest neighbor may be the point itself * (if it exists in original dataset) * if False, then only points with non-zero distance * are returned * * RESULT * number of actual neighbors found (either K or N, if K>N). * * This subroutine performs query and stores its result in the internal * structures of the KD-tree. You can use following subroutines to obtain * these results: * KDTreeQueryResultsX() to get X-values * KDTreeQueryResultsXY() to get X- and Y-values * KDTreeQueryResultsTags() to get tag values * KDTreeQueryResultsDistances() to get distances * * -- ALGLIB -- * Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ public static int kdtreequeryknn(ref kdtree kdt, ref double[] x, int k, bool selfmatch) { int result = 0; result = kdtreequeryaknn(ref kdt, ref x, k, selfmatch, 0.0); return(result); }
/************************************************************************* * point tags from last query * * INPUT PARAMETERS * KDT - KD-tree * Tags - pre-allocated array, at least K elements * * OUTPUT PARAMETERS * Tags - first K elements are filled with tags associated with points, * or, when no tags were supplied, with zeros * K - number of points * * NOTE * points are ordered by distance from the query point (first = closest) * * SEE ALSO * KDTreeQueryResultsX() X-values * KDTreeQueryResultsXY() X- and Y-values * KDTreeQueryResultsDistances() distances * * -- ALGLIB -- * Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ public static void kdtreequeryresultstags(ref kdtree kdt, ref int[] tags, ref int k) { int i = 0; k = kdt.kcur; for (i = 0; i <= k - 1; i++) { tags[i] = kdt.tags[kdt.idx[i]]; } }
/************************************************************************* * X- and Y-values from last query * * INPUT PARAMETERS * KDT - KD-tree * XY - pre-allocated array, at least K rows, at least NX+NY columns * * OUTPUT PARAMETERS * X - K rows are filled with points: first NX columns with * X-values, next NY columns - with Y-values. * K - number of points * * NOTE * points are ordered by distance from the query point (first = closest) * * SEE ALSO * KDTreeQueryResultsX() X-values * KDTreeQueryResultsTags() tag values * KDTreeQueryResultsDistances() distances * * -- ALGLIB -- * Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ public static void kdtreequeryresultsxy(ref kdtree kdt, ref double[,] xy, ref int k) { int i = 0; int i_ = 0; int i1_ = 0; k = kdt.kcur; for (i = 0; i <= k - 1; i++) { i1_ = (kdt.nx) - (0); for (i_ = 0; i_ <= kdt.nx + kdt.ny - 1; i_++) { xy[i, i_] = kdt.xy[kdt.idx[i], i_ + i1_]; } } }
/************************************************************************* * KD-tree creation * * This subroutine creates KD-tree from set of X-values and optional Y-values * * INPUT PARAMETERS * XY - dataset, array[0..N-1,0..NX+NY-1]. * one row corresponds to one point. * first NX columns contain X-values, next NY (NY may be zero) * columns may contain associated Y-values * N - number of points, N>=1 * NX - space dimension, NX>=1. * NY - number of optional Y-values, NY>=0. * NormType- norm type: * 0 denotes infinity-norm * 1 denotes 1-norm * 2 denotes 2-norm (Euclidean norm) * * OUTPUT PARAMETERS * KDT - KD-tree * * * NOTES * * 1. KD-tree creation have O(N*logN) complexity and O(N*(2*NX+NY)) memory * requirements. * 2. Although KD-trees may be used with any combination of N and NX, they * are more efficient than brute-force search only when N >> 4^NX. So they * are most useful in low-dimensional tasks (NX=2, NX=3). NX=1 is another * inefficient case, because simple binary search (without additional * structures) is much more efficient in such tasks than KD-trees. * * -- ALGLIB -- * Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ public static void kdtreebuild(ref double[,] xy, int n, int nx, int ny, int normtype, ref kdtree kdt) { int[] tags = new int[0]; int i = 0; System.Diagnostics.Debug.Assert(n >= 1, "KDTreeBuild: N<1!"); System.Diagnostics.Debug.Assert(nx >= 1, "KDTreeBuild: NX<1!"); System.Diagnostics.Debug.Assert(ny >= 0, "KDTreeBuild: NY<0!"); System.Diagnostics.Debug.Assert(normtype >= 0 & normtype <= 2, "KDTreeBuild: incorrect NormType!"); tags = new int[n]; for (i = 0; i <= n - 1; i++) { tags[i] = 0; } kdtreebuildtagged(ref xy, ref tags, n, nx, ny, normtype, ref kdt); }
/************************************************************************* * Distances from last query * * INPUT PARAMETERS * KDT - KD-tree * R - pre-allocated array, at least K elements * * OUTPUT PARAMETERS * R - first K elements are filled with distances * (in corresponding norm) * K - number of points * * NOTE * points are ordered by distance from the query point (first = closest) * * SEE ALSO * KDTreeQueryResultsX() X-values * KDTreeQueryResultsXY() X- and Y-values * KDTreeQueryResultsTags() tag values * * -- ALGLIB -- * Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ public static void kdtreequeryresultsdistances(ref kdtree kdt, ref double[] r, ref int k) { int i = 0; k = kdt.kcur; // // unload norms // // Abs() call is used to handle cases with negative norms // (generated during KFN requests) // if (kdt.normtype == 0) { for (i = 0; i <= k - 1; i++) { r[i] = Math.Abs(kdt.r[i]); } } if (kdt.normtype == 1) { for (i = 0; i <= k - 1; i++) { r[i] = Math.Abs(kdt.r[i]); } } if (kdt.normtype == 2) { for (i = 0; i <= k - 1; i++) { r[i] = Math.Sqrt(Math.Abs(kdt.r[i])); } } }
/************************************************************************* This function allocates all dataset-dependent array fields of KDTree, i.e. such array fields that their dimensions depend on dataset size. This function do not sets KDT.N, KDT.NX or KDT.NY - it just allocates arrays. -- ALGLIB -- Copyright 14.03.2011 by Bochkanov Sergey *************************************************************************/ private static void kdtreeallocdatasetdependent(kdtree kdt, int n, int nx, int ny) { alglib.ap.assert(n>0, "KDTreeAllocDatasetDependent: internal error"); kdt.xy = new double[n, 2*nx+ny]; kdt.tags = new int[n]; kdt.idx = new int[n]; kdt.r = new double[n]; kdt.x = new double[nx]; kdt.buf = new double[Math.Max(n, nx)]; kdt.nodes = new int[splitnodesize*2*n]; kdt.splits = new double[2*n]; }
/************************************************************************* KD-tree creation This subroutine creates KD-tree from set of X-values and optional Y-values INPUT PARAMETERS XY - dataset, array[0..N-1,0..NX+NY-1]. one row corresponds to one point. first NX columns contain X-values, next NY (NY may be zero) columns may contain associated Y-values N - number of points, N>=1 NX - space dimension, NX>=1. NY - number of optional Y-values, NY>=0. NormType- norm type: * 0 denotes infinity-norm * 1 denotes 1-norm * 2 denotes 2-norm (Euclidean norm) OUTPUT PARAMETERS KDT - KD-tree NOTES 1. KD-tree creation have O(N*logN) complexity and O(N*(2*NX+NY)) memory requirements. 2. Although KD-trees may be used with any combination of N and NX, they are more efficient than brute-force search only when N >> 4^NX. So they are most useful in low-dimensional tasks (NX=2, NX=3). NX=1 is another inefficient case, because simple binary search (without additional structures) is much more efficient in such tasks than KD-trees. -- ALGLIB -- Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ public static void kdtreebuild(ref double[,] xy, int n, int nx, int ny, int normtype, ref kdtree kdt) { int[] tags = new int[0]; int i = 0; System.Diagnostics.Debug.Assert(n>=1, "KDTreeBuild: N<1!"); System.Diagnostics.Debug.Assert(nx>=1, "KDTreeBuild: NX<1!"); System.Diagnostics.Debug.Assert(ny>=0, "KDTreeBuild: NY<0!"); System.Diagnostics.Debug.Assert(normtype>=0 & normtype<=2, "KDTreeBuild: incorrect NormType!"); tags = new int[n]; for(i=0; i<=n-1; i++) { tags[i] = 0; } kdtreebuildtagged(ref xy, ref tags, n, nx, ny, normtype, ref kdt); }
/************************************************************************* This function allocates temporaries. This function do not sets KDT.N, KDT.NX or KDT.NY - it just allocates arrays. -- ALGLIB -- Copyright 14.03.2011 by Bochkanov Sergey *************************************************************************/ private static void kdtreealloctemporaries(kdtree kdt, int n, int nx, int ny) { kdt.x = new double[nx]; kdt.idx = new int[n]; kdt.r = new double[n]; kdt.buf = new double[Math.Max(n, nx)]; kdt.curboxmin = new double[nx]; kdt.curboxmax = new double[nx]; }
/************************************************************************* Copies X[] to KDT.X[] Loads distance from X[] to bounding box. Initializes CurBox[]. -- ALGLIB -- Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ private static void kdtreeinitbox(kdtree kdt, double[] x) { int i = 0; double vx = 0; double vmin = 0; double vmax = 0; alglib.ap.assert(kdt.n>0, "KDTreeInitBox: internal error"); // // calculate distance from point to current bounding box // kdt.curdist = 0; if( kdt.normtype==0 ) { for(i=0; i<=kdt.nx-1; i++) { vx = x[i]; vmin = kdt.boxmin[i]; vmax = kdt.boxmax[i]; kdt.x[i] = vx; kdt.curboxmin[i] = vmin; kdt.curboxmax[i] = vmax; if( (double)(vx)<(double)(vmin) ) { kdt.curdist = Math.Max(kdt.curdist, vmin-vx); } else { if( (double)(vx)>(double)(vmax) ) { kdt.curdist = Math.Max(kdt.curdist, vx-vmax); } } } } if( kdt.normtype==1 ) { for(i=0; i<=kdt.nx-1; i++) { vx = x[i]; vmin = kdt.boxmin[i]; vmax = kdt.boxmax[i]; kdt.x[i] = vx; kdt.curboxmin[i] = vmin; kdt.curboxmax[i] = vmax; if( (double)(vx)<(double)(vmin) ) { kdt.curdist = kdt.curdist+vmin-vx; } else { if( (double)(vx)>(double)(vmax) ) { kdt.curdist = kdt.curdist+vx-vmax; } } } } if( kdt.normtype==2 ) { for(i=0; i<=kdt.nx-1; i++) { vx = x[i]; vmin = kdt.boxmin[i]; vmax = kdt.boxmax[i]; kdt.x[i] = vx; kdt.curboxmin[i] = vmin; kdt.curboxmax[i] = vmax; if( (double)(vx)<(double)(vmin) ) { kdt.curdist = kdt.curdist+math.sqr(vmin-vx); } else { if( (double)(vx)>(double)(vmax) ) { kdt.curdist = kdt.curdist+math.sqr(vx-vmax); } } } } }
/************************************************************************* Tags from last query INPUT PARAMETERS KDT - KD-tree Tags - possibly pre-allocated buffer. If X is too small to store result, it is resized. If size(X) is enough to store result, it is left unchanged. OUTPUT PARAMETERS Tags - filled with tags associated with points, or, when no tags were supplied, with zeros NOTES 1. points are ordered by distance from the query point (first = closest) 2. if XY is larger than required to store result, only leading part will be overwritten; trailing part will be left unchanged. So if on input XY = [[A,B],[C,D]], and result is [1,2], then on exit we will get XY = [[1,2],[C,D]]. This is done purposely to increase performance; if you want function to resize array according to result size, use function with same name and suffix 'I'. SEE ALSO * KDTreeQueryResultsX() X-values * KDTreeQueryResultsXY() X- and Y-values * KDTreeQueryResultsDistances() distances -- ALGLIB -- Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ public static void kdtreequeryresultstags(kdtree kdt, ref int[] tags) { int i = 0; int k = 0; if( kdt.kcur==0 ) { return; } if( alglib.ap.len(tags)<kdt.kcur ) { tags = new int[kdt.kcur]; } k = kdt.kcur; for(i=0; i<=k-1; i++) { tags[i] = kdt.tags[kdt.idx[i]]; } }
/************************************************************************* X-values from last query; 'interactive' variant for languages like Python which support constructs like "X = KDTreeQueryResultsXI(KDT)" and interactive mode of interpreter. This function allocates new array on each call, so it is significantly slower than its 'non-interactive' counterpart, but it is more convenient when you call it from command line. -- ALGLIB -- Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ public static void kdtreequeryresultsxi(kdtree kdt, ref double[,] x) { x = new double[0,0]; kdtreequeryresultsx(kdt, ref x); }
/************************************************************************* KD-tree creation This subroutine creates KD-tree from set of X-values, integer tags and optional Y-values INPUT PARAMETERS XY - dataset, array[0..N-1,0..NX+NY-1]. one row corresponds to one point. first NX columns contain X-values, next NY (NY may be zero) columns may contain associated Y-values Tags - tags, array[0..N-1], contains integer tags associated with points. N - number of points, N>=1 NX - space dimension, NX>=1. NY - number of optional Y-values, NY>=0. NormType- norm type: * 0 denotes infinity-norm * 1 denotes 1-norm * 2 denotes 2-norm (Euclidean norm) OUTPUT PARAMETERS KDT - KD-tree NOTES 1. KD-tree creation have O(N*logN) complexity and O(N*(2*NX+NY)) memory requirements. 2. Although KD-trees may be used with any combination of N and NX, they are more efficient than brute-force search only when N >> 4^NX. So they are most useful in low-dimensional tasks (NX=2, NX=3). NX=1 is another inefficient case, because simple binary search (without additional structures) is much more efficient in such tasks than KD-trees. -- ALGLIB -- Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ public static void kdtreebuildtagged(double[,] xy, int[] tags, int n, int nx, int ny, int normtype, kdtree kdt) { int i = 0; int j = 0; int maxnodes = 0; int nodesoffs = 0; int splitsoffs = 0; int i_ = 0; int i1_ = 0; ap.assert(n>=1, "KDTreeBuildTagged: N<1!"); ap.assert(nx>=1, "KDTreeBuildTagged: NX<1!"); ap.assert(ny>=0, "KDTreeBuildTagged: NY<0!"); ap.assert(normtype>=0 & normtype<=2, "KDTreeBuildTagged: incorrect NormType!"); ap.assert(ap.rows(xy)>=n, "KDTreeBuildTagged: rows(X)<N!"); ap.assert(ap.cols(xy)>=nx+ny, "KDTreeBuildTagged: cols(X)<NX+NY!"); ap.assert(apserv.apservisfinitematrix(xy, n, nx+ny), "KDTreeBuildTagged: X contains infinite or NaN values!"); // // initialize // kdt.n = n; kdt.nx = nx; kdt.ny = ny; kdt.normtype = normtype; kdt.distmatrixtype = 0; kdt.xy = new double[n, 2*nx+ny]; kdt.tags = new int[n]; kdt.idx = new int[n]; kdt.r = new double[n]; kdt.x = new double[nx]; kdt.buf = new double[Math.Max(n, nx)]; // // Initial fill // for(i=0; i<=n-1; i++) { for(i_=0; i_<=nx-1;i_++) { kdt.xy[i,i_] = xy[i,i_]; } i1_ = (0) - (nx); for(i_=nx; i_<=2*nx+ny-1;i_++) { kdt.xy[i,i_] = xy[i,i_+i1_]; } kdt.tags[i] = tags[i]; } // // Determine bounding box // kdt.boxmin = new double[nx]; kdt.boxmax = new double[nx]; kdt.curboxmin = new double[nx]; kdt.curboxmax = new double[nx]; for(i_=0; i_<=nx-1;i_++) { kdt.boxmin[i_] = kdt.xy[0,i_]; } for(i_=0; i_<=nx-1;i_++) { kdt.boxmax[i_] = kdt.xy[0,i_]; } for(i=1; i<=n-1; i++) { for(j=0; j<=nx-1; j++) { kdt.boxmin[j] = Math.Min(kdt.boxmin[j], kdt.xy[i,j]); kdt.boxmax[j] = Math.Max(kdt.boxmax[j], kdt.xy[i,j]); } } // // prepare tree structure // * MaxNodes=N because we guarantee no trivial splits, i.e. // every split will generate two non-empty boxes // maxnodes = n; kdt.nodes = new int[splitnodesize*2*maxnodes]; kdt.splits = new double[2*maxnodes]; nodesoffs = 0; splitsoffs = 0; for(i_=0; i_<=nx-1;i_++) { kdt.curboxmin[i_] = kdt.boxmin[i_]; } for(i_=0; i_<=nx-1;i_++) { kdt.curboxmax[i_] = kdt.boxmax[i_]; } kdtreegeneratetreerec(kdt, ref nodesoffs, ref splitsoffs, 0, n, 8); // // Set current query size to 0 // kdt.kcur = 0; }
/************************************************************************* K-NN query: approximate K nearest neighbors INPUT PARAMETERS KDT - KD-tree X - point, array[0..NX-1]. K - number of neighbors to return, K>=1 SelfMatch - whether self-matches are allowed: * if True, nearest neighbor may be the point itself (if it exists in original dataset) * if False, then only points with non-zero distance are returned * if not given, considered True Eps - approximation factor, Eps>=0. eps-approximate nearest neighbor is a neighbor whose distance from X is at most (1+eps) times distance of true nearest neighbor. RESULT number of actual neighbors found (either K or N, if K>N). NOTES significant performance gain may be achieved only when Eps is is on the order of magnitude of 1 or larger. This subroutine performs query and stores its result in the internal structures of the KD-tree. You can use following subroutines to obtain these results: * KDTreeQueryResultsX() to get X-values * KDTreeQueryResultsXY() to get X- and Y-values * KDTreeQueryResultsTags() to get tag values * KDTreeQueryResultsDistances() to get distances -- ALGLIB -- Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ public static int kdtreequeryaknn(kdtree kdt, double[] x, int k, bool selfmatch, double eps) { int result = 0; int i = 0; int j = 0; alglib.ap.assert(k>0, "KDTreeQueryAKNN: incorrect K!"); alglib.ap.assert((double)(eps)>=(double)(0), "KDTreeQueryAKNN: incorrect Eps!"); alglib.ap.assert(alglib.ap.len(x)>=kdt.nx, "KDTreeQueryAKNN: Length(X)<NX!"); alglib.ap.assert(apserv.isfinitevector(x, kdt.nx), "KDTreeQueryAKNN: X contains infinite or NaN values!"); // // Handle special case: KDT.N=0 // if( kdt.n==0 ) { kdt.kcur = 0; result = 0; return result; } // // Prepare parameters // k = Math.Min(k, kdt.n); kdt.kneeded = k; kdt.rneeded = 0; kdt.selfmatch = selfmatch; if( kdt.normtype==2 ) { kdt.approxf = 1/math.sqr(1+eps); } else { kdt.approxf = 1/(1+eps); } kdt.kcur = 0; // // calculate distance from point to current bounding box // kdtreeinitbox(kdt, x); // // call recursive search // results are returned as heap // kdtreequerynnrec(kdt, 0); // // pop from heap to generate ordered representation // // last element is non pop'ed because it is already in // its place // result = kdt.kcur; j = kdt.kcur; for(i=kdt.kcur; i>=2; i--) { tsort.tagheappopi(ref kdt.r, ref kdt.idx, ref j); } return result; }
/************************************************************************* X-values from last query INPUT PARAMETERS KDT - KD-tree X - possibly pre-allocated buffer. If X is too small to store result, it is resized. If size(X) is enough to store result, it is left unchanged. OUTPUT PARAMETERS X - rows are filled with X-values NOTES 1. points are ordered by distance from the query point (first = closest) 2. if XY is larger than required to store result, only leading part will be overwritten; trailing part will be left unchanged. So if on input XY = [[A,B],[C,D]], and result is [1,2], then on exit we will get XY = [[1,2],[C,D]]. This is done purposely to increase performance; if you want function to resize array according to result size, use function with same name and suffix 'I'. SEE ALSO * KDTreeQueryResultsXY() X- and Y-values * KDTreeQueryResultsTags() tag values * KDTreeQueryResultsDistances() distances -- ALGLIB -- Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ public static void kdtreequeryresultsx(kdtree kdt, ref double[,] x) { int i = 0; int k = 0; int i_ = 0; int i1_ = 0; if(kdt.kcur == 0) { return; } if(ap.rows(x) < kdt.kcur | ap.cols(x) < kdt.nx) { x = new double[kdt.kcur, kdt.nx]; } k = kdt.kcur; for(i = 0; i <= k - 1; i++) { i1_ = (kdt.nx) - (0); for(i_ = 0; i_ <= kdt.nx - 1; i_++) { x[i, i_] = kdt.xy[kdt.idx[i], i_ + i1_]; } } }
/************************************************************************* R-NN query: all points within R-sphere centered at X INPUT PARAMETERS KDT - KD-tree X - point, array[0..NX-1]. R - radius of sphere (in corresponding norm), R>0 SelfMatch - whether self-matches are allowed: * if True, nearest neighbor may be the point itself (if it exists in original dataset) * if False, then only points with non-zero distance are returned * if not given, considered True RESULT number of neighbors found, >=0 This subroutine performs query and stores its result in the internal structures of the KD-tree. You can use following subroutines to obtain actual results: * KDTreeQueryResultsX() to get X-values * KDTreeQueryResultsXY() to get X- and Y-values * KDTreeQueryResultsTags() to get tag values * KDTreeQueryResultsDistances() to get distances -- ALGLIB -- Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ public static int kdtreequeryrnn(kdtree kdt, double[] x, double r, bool selfmatch) { int result = 0; int i = 0; int j = 0; ap.assert((double)(r) > (double)(0), "KDTreeQueryRNN: incorrect R!"); ap.assert(ap.len(x) >= kdt.nx, "KDTreeQueryRNN: Length(X)<NX!"); ap.assert(apserv.isfinitevector(x, kdt.nx), "KDTreeQueryRNN: X contains infinite or NaN values!"); // // Prepare parameters // kdt.kneeded = 0; if(kdt.normtype != 2) { kdt.rneeded = r; } else { kdt.rneeded = math.sqr(r); } kdt.selfmatch = selfmatch; kdt.approxf = 1; kdt.kcur = 0; // // calculate distance from point to current bounding box // kdtreeinitbox(kdt, x); // // call recursive search // results are returned as heap // kdtreequerynnrec(kdt, 0); // // pop from heap to generate ordered representation // // last element is not pop'ed because it is already in // its place // result = kdt.kcur; j = kdt.kcur; for(i = kdt.kcur; i >= 2; i--) { tsort.tagheappopi(ref kdt.r, ref kdt.idx, ref j); } return result; }
/************************************************************************* This function allocates all dataset-independent array fields of KDTree, i.e. such array fields that their dimensions do not depend on dataset size. This function do not sets KDT.NX or KDT.NY - it just allocates arrays -- ALGLIB -- Copyright 14.03.2011 by Bochkanov Sergey *************************************************************************/ private static void kdtreeallocdatasetindependent(kdtree kdt, int nx, int ny) { kdt.x = new double[nx]; kdt.boxmin = new double[nx]; kdt.boxmax = new double[nx]; kdt.curboxmin = new double[nx]; kdt.curboxmax = new double[nx]; }
/************************************************************************* * KD-tree creation * * This subroutine creates KD-tree from set of X-values, integer tags and * optional Y-values * * INPUT PARAMETERS * XY - dataset, array[0..N-1,0..NX+NY-1]. * one row corresponds to one point. * first NX columns contain X-values, next NY (NY may be zero) * columns may contain associated Y-values * Tags - tags, array[0..N-1], contains integer tags associated * with points. * N - number of points, N>=1 * NX - space dimension, NX>=1. * NY - number of optional Y-values, NY>=0. * NormType- norm type: * 0 denotes infinity-norm * 1 denotes 1-norm * 2 denotes 2-norm (Euclidean norm) * * OUTPUT PARAMETERS * KDT - KD-tree * * NOTES * * 1. KD-tree creation have O(N*logN) complexity and O(N*(2*NX+NY)) memory * requirements. * 2. Although KD-trees may be used with any combination of N and NX, they * are more efficient than brute-force search only when N >> 4^NX. So they * are most useful in low-dimensional tasks (NX=2, NX=3). NX=1 is another * inefficient case, because simple binary search (without additional * structures) is much more efficient in such tasks than KD-trees. * * -- ALGLIB -- * Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ public static void kdtreebuildtagged(ref double[,] xy, ref int[] tags, int n, int nx, int ny, int normtype, ref kdtree kdt) { int i = 0; int j = 0; int maxnodes = 0; int nodesoffs = 0; int splitsoffs = 0; int i_ = 0; int i1_ = 0; System.Diagnostics.Debug.Assert(n >= 1, "KDTreeBuildTagged: N<1!"); System.Diagnostics.Debug.Assert(nx >= 1, "KDTreeBuildTagged: NX<1!"); System.Diagnostics.Debug.Assert(ny >= 0, "KDTreeBuildTagged: NY<0!"); System.Diagnostics.Debug.Assert(normtype >= 0 & normtype <= 2, "KDTreeBuildTagged: incorrect NormType!"); // // initialize // kdt.n = n; kdt.nx = nx; kdt.ny = ny; kdt.normtype = normtype; kdt.distmatrixtype = 0; kdt.xy = new double[n, 2 * nx + ny]; kdt.tags = new int[n]; kdt.idx = new int[n]; kdt.r = new double[n]; kdt.x = new double[nx]; kdt.buf = new double[Math.Max(n, nx)]; // // Initial fill // for (i = 0; i <= n - 1; i++) { for (i_ = 0; i_ <= nx - 1; i_++) { kdt.xy[i, i_] = xy[i, i_]; } i1_ = (0) - (nx); for (i_ = nx; i_ <= 2 * nx + ny - 1; i_++) { kdt.xy[i, i_] = xy[i, i_ + i1_]; } kdt.tags[i] = tags[i]; } // // Determine bounding box // kdt.boxmin = new double[nx]; kdt.boxmax = new double[nx]; kdt.curboxmin = new double[nx]; kdt.curboxmax = new double[nx]; for (i_ = 0; i_ <= nx - 1; i_++) { kdt.boxmin[i_] = kdt.xy[0, i_]; } for (i_ = 0; i_ <= nx - 1; i_++) { kdt.boxmax[i_] = kdt.xy[0, i_]; } for (i = 1; i <= n - 1; i++) { for (j = 0; j <= nx - 1; j++) { kdt.boxmin[j] = Math.Min(kdt.boxmin[j], kdt.xy[i, j]); kdt.boxmax[j] = Math.Max(kdt.boxmax[j], kdt.xy[i, j]); } } // // prepare tree structure // * MaxNodes=N because we guarantee no trivial splits, i.e. // every split will generate two non-empty boxes // maxnodes = n; kdt.nodes = new int[splitnodesize * 2 * maxnodes]; kdt.splits = new double[2 * maxnodes]; nodesoffs = 0; splitsoffs = 0; for (i_ = 0; i_ <= nx - 1; i_++) { kdt.curboxmin[i_] = kdt.boxmin[i_]; } for (i_ = 0; i_ <= nx - 1; i_++) { kdt.curboxmax[i_] = kdt.boxmax[i_]; } kdtreegeneratetreerec(ref kdt, ref nodesoffs, ref splitsoffs, 0, n, 8); // // Set current query size to 0 // kdt.kcur = 0; }
/************************************************************************* * Copies X[] to KDT.X[] * Loads distance from X[] to bounding box. * Initializes CurBox[]. * * -- ALGLIB -- * Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ private static void kdtreeinitbox(ref kdtree kdt, ref double[] x) { int i = 0; double vx = 0; double vmin = 0; double vmax = 0; // // calculate distance from point to current bounding box // kdt.curdist = 0; if (kdt.normtype == 0) { for (i = 0; i <= kdt.nx - 1; i++) { vx = x[i]; vmin = kdt.boxmin[i]; vmax = kdt.boxmax[i]; kdt.x[i] = vx; kdt.curboxmin[i] = vmin; kdt.curboxmax[i] = vmax; if ((double)(vx) < (double)(vmin)) { kdt.curdist = Math.Max(kdt.curdist, vmin - vx); } else { if ((double)(vx) > (double)(vmax)) { kdt.curdist = Math.Max(kdt.curdist, vx - vmax); } } } } if (kdt.normtype == 1) { for (i = 0; i <= kdt.nx - 1; i++) { vx = x[i]; vmin = kdt.boxmin[i]; vmax = kdt.boxmax[i]; kdt.x[i] = vx; kdt.curboxmin[i] = vmin; kdt.curboxmax[i] = vmax; if ((double)(vx) < (double)(vmin)) { kdt.curdist = kdt.curdist + vmin - vx; } else { if ((double)(vx) > (double)(vmax)) { kdt.curdist = kdt.curdist + vx - vmax; } } } } if (kdt.normtype == 2) { for (i = 0; i <= kdt.nx - 1; i++) { vx = x[i]; vmin = kdt.boxmin[i]; vmax = kdt.boxmax[i]; kdt.x[i] = vx; kdt.curboxmin[i] = vmin; kdt.curboxmax[i] = vmax; if ((double)(vx) < (double)(vmin)) { kdt.curdist = kdt.curdist + AP.Math.Sqr(vmin - vx); } else { if ((double)(vx) > (double)(vmax)) { kdt.curdist = kdt.curdist + AP.Math.Sqr(vx - vmax); } } } } }
/************************************************************************* KD-tree creation This subroutine creates KD-tree from set of X-values, integer tags and optional Y-values INPUT PARAMETERS XY - dataset, array[0..N-1,0..NX+NY-1]. one row corresponds to one point. first NX columns contain X-values, next NY (NY may be zero) columns may contain associated Y-values Tags - tags, array[0..N-1], contains integer tags associated with points. N - number of points, N>=0 NX - space dimension, NX>=1. NY - number of optional Y-values, NY>=0. NormType- norm type: * 0 denotes infinity-norm * 1 denotes 1-norm * 2 denotes 2-norm (Euclidean norm) OUTPUT PARAMETERS KDT - KD-tree NOTES 1. KD-tree creation have O(N*logN) complexity and O(N*(2*NX+NY)) memory requirements. 2. Although KD-trees may be used with any combination of N and NX, they are more efficient than brute-force search only when N >> 4^NX. So they are most useful in low-dimensional tasks (NX=2, NX=3). NX=1 is another inefficient case, because simple binary search (without additional structures) is much more efficient in such tasks than KD-trees. -- ALGLIB -- Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ public static void kdtreebuildtagged(double[,] xy, int[] tags, int n, int nx, int ny, int normtype, kdtree kdt) { int i = 0; int j = 0; int maxnodes = 0; int nodesoffs = 0; int splitsoffs = 0; int i_ = 0; int i1_ = 0; alglib.ap.assert(n>=0, "KDTreeBuildTagged: N<0"); alglib.ap.assert(nx>=1, "KDTreeBuildTagged: NX<1"); alglib.ap.assert(ny>=0, "KDTreeBuildTagged: NY<0"); alglib.ap.assert(normtype>=0 && normtype<=2, "KDTreeBuildTagged: incorrect NormType"); alglib.ap.assert(alglib.ap.rows(xy)>=n, "KDTreeBuildTagged: rows(X)<N"); alglib.ap.assert(alglib.ap.cols(xy)>=nx+ny || n==0, "KDTreeBuildTagged: cols(X)<NX+NY"); alglib.ap.assert(apserv.apservisfinitematrix(xy, n, nx+ny), "KDTreeBuildTagged: XY contains infinite or NaN values"); // // initialize // kdt.n = n; kdt.nx = nx; kdt.ny = ny; kdt.normtype = normtype; kdt.kcur = 0; // // N=0 => quick exit // if( n==0 ) { return; } // // Allocate // kdtreeallocdatasetindependent(kdt, nx, ny); kdtreeallocdatasetdependent(kdt, n, nx, ny); // // Initial fill // for(i=0; i<=n-1; i++) { for(i_=0; i_<=nx-1;i_++) { kdt.xy[i,i_] = xy[i,i_]; } i1_ = (0) - (nx); for(i_=nx; i_<=2*nx+ny-1;i_++) { kdt.xy[i,i_] = xy[i,i_+i1_]; } kdt.tags[i] = tags[i]; } // // Determine bounding box // for(i_=0; i_<=nx-1;i_++) { kdt.boxmin[i_] = kdt.xy[0,i_]; } for(i_=0; i_<=nx-1;i_++) { kdt.boxmax[i_] = kdt.xy[0,i_]; } for(i=1; i<=n-1; i++) { for(j=0; j<=nx-1; j++) { kdt.boxmin[j] = Math.Min(kdt.boxmin[j], kdt.xy[i,j]); kdt.boxmax[j] = Math.Max(kdt.boxmax[j], kdt.xy[i,j]); } } // // prepare tree structure // * MaxNodes=N because we guarantee no trivial splits, i.e. // every split will generate two non-empty boxes // maxnodes = n; kdt.nodes = new int[splitnodesize*2*maxnodes]; kdt.splits = new double[2*maxnodes]; nodesoffs = 0; splitsoffs = 0; for(i_=0; i_<=nx-1;i_++) { kdt.curboxmin[i_] = kdt.boxmin[i_]; } for(i_=0; i_<=nx-1;i_++) { kdt.curboxmax[i_] = kdt.boxmax[i_]; } kdtreegeneratetreerec(kdt, ref nodesoffs, ref splitsoffs, 0, n, 8); }
/************************************************************************* Tags from last query; 'interactive' variant for languages like Python which support constructs like "Tags = KDTreeQueryResultsTagsI(KDT)" and interactive mode of interpreter. This function allocates new array on each call, so it is significantly slower than its 'non-interactive' counterpart, but it is more convenient when you call it from command line. -- ALGLIB -- Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ public static void kdtreequeryresultstagsi(kdtree kdt, ref int[] tags) { tags = new int[0]; kdtreequeryresultstags(kdt, ref tags); }
/************************************************************************* K-NN query: K nearest neighbors INPUT PARAMETERS KDT - KD-tree X - point, array[0..NX-1]. K - number of neighbors to return, K>=1 SelfMatch - whether self-matches are allowed: * if True, nearest neighbor may be the point itself (if it exists in original dataset) * if False, then only points with non-zero distance are returned * if not given, considered True RESULT number of actual neighbors found (either K or N, if K>N). This subroutine performs query and stores its result in the internal structures of the KD-tree. You can use following subroutines to obtain these results: * KDTreeQueryResultsX() to get X-values * KDTreeQueryResultsXY() to get X- and Y-values * KDTreeQueryResultsTags() to get tag values * KDTreeQueryResultsDistances() to get distances -- ALGLIB -- Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ public static int kdtreequeryknn(kdtree kdt, double[] x, int k, bool selfmatch) { int result = 0; alglib.ap.assert(k>=1, "KDTreeQueryKNN: K<1!"); alglib.ap.assert(alglib.ap.len(x)>=kdt.nx, "KDTreeQueryKNN: Length(X)<NX!"); alglib.ap.assert(apserv.isfinitevector(x, kdt.nx), "KDTreeQueryKNN: X contains infinite or NaN values!"); result = kdtreequeryaknn(kdt, x, k, selfmatch, 0.0); return result; }
/************************************************************************* Serializer: allocation -- ALGLIB -- Copyright 14.03.2011 by Bochkanov Sergey *************************************************************************/ public static void kdtreealloc(alglib.serializer s, kdtree tree) { // // Header // s.alloc_entry(); s.alloc_entry(); // // Data // s.alloc_entry(); s.alloc_entry(); s.alloc_entry(); s.alloc_entry(); apserv.allocrealmatrix(s, tree.xy, -1, -1); apserv.allocintegerarray(s, tree.tags, -1); apserv.allocrealarray(s, tree.boxmin, -1); apserv.allocrealarray(s, tree.boxmax, -1); apserv.allocintegerarray(s, tree.nodes, -1); apserv.allocrealarray(s, tree.splits, -1); }
/************************************************************************* X- and Y-values from last query INPUT PARAMETERS KDT - KD-tree XY - possibly pre-allocated buffer. If XY is too small to store result, it is resized. If size(XY) is enough to store result, it is left unchanged. OUTPUT PARAMETERS XY - rows are filled with points: first NX columns with X-values, next NY columns - with Y-values. NOTES 1. points are ordered by distance from the query point (first = closest) 2. if XY is larger than required to store result, only leading part will be overwritten; trailing part will be left unchanged. So if on input XY = [[A,B],[C,D]], and result is [1,2], then on exit we will get XY = [[1,2],[C,D]]. This is done purposely to increase performance; if you want function to resize array according to result size, use function with same name and suffix 'I'. SEE ALSO * KDTreeQueryResultsX() X-values * KDTreeQueryResultsTags() tag values * KDTreeQueryResultsDistances() distances -- ALGLIB -- Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ public static void kdtreequeryresultsxy(kdtree kdt, ref double[,] xy) { int i = 0; int k = 0; int i_ = 0; int i1_ = 0; if( kdt.kcur==0 ) { return; } if( alglib.ap.rows(xy)<kdt.kcur || alglib.ap.cols(xy)<kdt.nx+kdt.ny ) { xy = new double[kdt.kcur, kdt.nx+kdt.ny]; } k = kdt.kcur; for(i=0; i<=k-1; i++) { i1_ = (kdt.nx) - (0); for(i_=0; i_<=kdt.nx+kdt.ny-1;i_++) { xy[i,i_] = kdt.xy[kdt.idx[i],i_+i1_]; } } }
/************************************************************************* Serializer: unserialization -- ALGLIB -- Copyright 14.03.2011 by Bochkanov Sergey *************************************************************************/ public static void kdtreeunserialize(alglib.serializer s, kdtree tree) { int i0 = 0; int i1 = 0; // // check correctness of header // i0 = s.unserialize_int(); alglib.ap.assert(i0==scodes.getkdtreeserializationcode(), "KDTreeUnserialize: stream header corrupted"); i1 = s.unserialize_int(); alglib.ap.assert(i1==kdtreefirstversion, "KDTreeUnserialize: stream header corrupted"); // // Unserialize data // tree.n = s.unserialize_int(); tree.nx = s.unserialize_int(); tree.ny = s.unserialize_int(); tree.normtype = s.unserialize_int(); apserv.unserializerealmatrix(s, ref tree.xy); apserv.unserializeintegerarray(s, ref tree.tags); apserv.unserializerealarray(s, ref tree.boxmin); apserv.unserializerealarray(s, ref tree.boxmax); apserv.unserializeintegerarray(s, ref tree.nodes); apserv.unserializerealarray(s, ref tree.splits); kdtreealloctemporaries(tree, tree.n, tree.nx, tree.ny); }
/************************************************************************* Distances from last query INPUT PARAMETERS KDT - KD-tree R - possibly pre-allocated buffer. If X is too small to store result, it is resized. If size(X) is enough to store result, it is left unchanged. OUTPUT PARAMETERS R - filled with distances (in corresponding norm) NOTES 1. points are ordered by distance from the query point (first = closest) 2. if XY is larger than required to store result, only leading part will be overwritten; trailing part will be left unchanged. So if on input XY = [[A,B],[C,D]], and result is [1,2], then on exit we will get XY = [[1,2],[C,D]]. This is done purposely to increase performance; if you want function to resize array according to result size, use function with same name and suffix 'I'. SEE ALSO * KDTreeQueryResultsX() X-values * KDTreeQueryResultsXY() X- and Y-values * KDTreeQueryResultsTags() tag values -- ALGLIB -- Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ public static void kdtreequeryresultsdistances(kdtree kdt, ref double[] r) { int i = 0; int k = 0; if( kdt.kcur==0 ) { return; } if( alglib.ap.len(r)<kdt.kcur ) { r = new double[kdt.kcur]; } k = kdt.kcur; // // unload norms // // Abs() call is used to handle cases with negative norms // (generated during KFN requests) // if( kdt.normtype==0 ) { for(i=0; i<=k-1; i++) { r[i] = Math.Abs(kdt.r[i]); } } if( kdt.normtype==1 ) { for(i=0; i<=k-1; i++) { r[i] = Math.Abs(kdt.r[i]); } } if( kdt.normtype==2 ) { for(i=0; i<=k-1; i++) { r[i] = Math.Sqrt(Math.Abs(kdt.r[i])); } } }
/************************************************************************* Recursive kd-tree generation subroutine. PARAMETERS KDT tree NodesOffs unused part of Nodes[] which must be filled by tree SplitsOffs unused part of Splits[] I1, I2 points from [I1,I2) are processed NodesOffs[] and SplitsOffs[] must be large enough. -- ALGLIB -- Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ private static void kdtreegeneratetreerec(kdtree kdt, ref int nodesoffs, ref int splitsoffs, int i1, int i2, int maxleafsize) { int n = 0; int nx = 0; int ny = 0; int i = 0; int j = 0; int oldoffs = 0; int i3 = 0; int cntless = 0; int cntgreater = 0; double minv = 0; double maxv = 0; int minidx = 0; int maxidx = 0; int d = 0; double ds = 0; double s = 0; double v = 0; int i_ = 0; int i1_ = 0; alglib.ap.assert(kdt.n>0, "KDTreeGenerateTreeRec: internal error"); alglib.ap.assert(i2>i1, "KDTreeGenerateTreeRec: internal error"); // // Generate leaf if needed // if( i2-i1<=maxleafsize ) { kdt.nodes[nodesoffs+0] = i2-i1; kdt.nodes[nodesoffs+1] = i1; nodesoffs = nodesoffs+2; return; } // // Load values for easier access // nx = kdt.nx; ny = kdt.ny; // // select dimension to split: // * D is a dimension number // d = 0; ds = kdt.curboxmax[0]-kdt.curboxmin[0]; for(i=1; i<=nx-1; i++) { v = kdt.curboxmax[i]-kdt.curboxmin[i]; if( (double)(v)>(double)(ds) ) { ds = v; d = i; } } // // Select split position S using sliding midpoint rule, // rearrange points into [I1,I3) and [I3,I2) // s = kdt.curboxmin[d]+0.5*ds; i1_ = (i1) - (0); for(i_=0; i_<=i2-i1-1;i_++) { kdt.buf[i_] = kdt.xy[i_+i1_,d]; } n = i2-i1; cntless = 0; cntgreater = 0; minv = kdt.buf[0]; maxv = kdt.buf[0]; minidx = i1; maxidx = i1; for(i=0; i<=n-1; i++) { v = kdt.buf[i]; if( (double)(v)<(double)(minv) ) { minv = v; minidx = i1+i; } if( (double)(v)>(double)(maxv) ) { maxv = v; maxidx = i1+i; } if( (double)(v)<(double)(s) ) { cntless = cntless+1; } if( (double)(v)>(double)(s) ) { cntgreater = cntgreater+1; } } if( cntless>0 && cntgreater>0 ) { // // normal midpoint split // kdtreesplit(kdt, i1, i2, d, s, ref i3); } else { // // sliding midpoint // if( cntless==0 ) { // // 1. move split to MinV, // 2. place one point to the left bin (move to I1), // others - to the right bin // s = minv; if( minidx!=i1 ) { for(i=0; i<=2*kdt.nx+kdt.ny-1; i++) { v = kdt.xy[minidx,i]; kdt.xy[minidx,i] = kdt.xy[i1,i]; kdt.xy[i1,i] = v; } j = kdt.tags[minidx]; kdt.tags[minidx] = kdt.tags[i1]; kdt.tags[i1] = j; } i3 = i1+1; } else { // // 1. move split to MaxV, // 2. place one point to the right bin (move to I2-1), // others - to the left bin // s = maxv; if( maxidx!=i2-1 ) { for(i=0; i<=2*kdt.nx+kdt.ny-1; i++) { v = kdt.xy[maxidx,i]; kdt.xy[maxidx,i] = kdt.xy[i2-1,i]; kdt.xy[i2-1,i] = v; } j = kdt.tags[maxidx]; kdt.tags[maxidx] = kdt.tags[i2-1]; kdt.tags[i2-1] = j; } i3 = i2-1; } } // // Generate 'split' node // kdt.nodes[nodesoffs+0] = 0; kdt.nodes[nodesoffs+1] = d; kdt.nodes[nodesoffs+2] = splitsoffs; kdt.splits[splitsoffs+0] = s; oldoffs = nodesoffs; nodesoffs = nodesoffs+splitnodesize; splitsoffs = splitsoffs+1; // // Recirsive generation: // * update CurBox // * call subroutine // * restore CurBox // kdt.nodes[oldoffs+3] = nodesoffs; v = kdt.curboxmax[d]; kdt.curboxmax[d] = s; kdtreegeneratetreerec(kdt, ref nodesoffs, ref splitsoffs, i1, i3, maxleafsize); kdt.curboxmax[d] = v; kdt.nodes[oldoffs+4] = nodesoffs; v = kdt.curboxmin[d]; kdt.curboxmin[d] = s; kdtreegeneratetreerec(kdt, ref nodesoffs, ref splitsoffs, i3, i2, maxleafsize); kdt.curboxmin[d] = v; }
/************************************************************************* XY-values from last query; 'interactive' variant for languages like Python which support constructs like "XY = KDTreeQueryResultsXYI(KDT)" and interactive mode of interpreter. This function allocates new array on each call, so it is significantly slower than its 'non-interactive' counterpart, but it is more convenient when you call it from command line. -- ALGLIB -- Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ public static void kdtreequeryresultsxyi(kdtree kdt, ref double[,] xy) { xy = new double[0,0]; kdtreequeryresultsxy(kdt, ref xy); }
/************************************************************************* This function allocates all dataset-dependent array fields of KDTree, i.e. such array fields that their dimensions depend on dataset size. This function do not sets KDT.N, KDT.NX or KDT.NY - it just allocates arrays. -- ALGLIB -- Copyright 14.03.2011 by Bochkanov Sergey *************************************************************************/ private static void kdtreeallocdatasetdependent(kdtree kdt, int n, int nx, int ny) { kdt.xy = new double[n, 2 * nx + ny]; kdt.tags = new int[n]; kdt.idx = new int[n]; kdt.r = new double[n]; kdt.x = new double[nx]; kdt.buf = new double[Math.Max(n, nx)]; kdt.nodes = new int[splitnodesize * 2 * n]; kdt.splits = new double[2 * n]; }
/************************************************************************* Distances from last query; 'interactive' variant for languages like Python which support constructs like "R = KDTreeQueryResultsDistancesI(KDT)" and interactive mode of interpreter. This function allocates new array on each call, so it is significantly slower than its 'non-interactive' counterpart, but it is more convenient when you call it from command line. -- ALGLIB -- Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ public static void kdtreequeryresultsdistancesi(kdtree kdt, ref double[] r) { r = new double[0]; kdtreequeryresultsdistances(kdt, ref r); }
/************************************************************************* * Rearranges nodes [I1,I2) using partition in D-th dimension with S as threshold. * Returns split position I3: [I1,I3) and [I3,I2) are created as result. * * This subroutine doesn't create tree structures, just rearranges nodes. *************************************************************************/ private static void kdtreesplit(ref kdtree kdt, int i1, int i2, int d, double s, ref int i3) { int i = 0; int j = 0; int ileft = 0; int iright = 0; double v = 0; // // split XY/Tags in two parts: // * [ILeft,IRight] is non-processed part of XY/Tags // // After cycle is done, we have Ileft=IRight. We deal with // this element separately. // // After this, [I1,ILeft) contains left part, and [ILeft,I2) // contains right part. // ileft = i1; iright = i2 - 1; while (ileft < iright) { if ((double)(kdt.xy[ileft, d]) <= (double)(s)) { // // XY[ILeft] is on its place. // Advance ILeft. // ileft = ileft + 1; } else { // // XY[ILeft,..] must be at IRight. // Swap and advance IRight. // for (i = 0; i <= 2 * kdt.nx + kdt.ny - 1; i++) { v = kdt.xy[ileft, i]; kdt.xy[ileft, i] = kdt.xy[iright, i]; kdt.xy[iright, i] = v; } j = kdt.tags[ileft]; kdt.tags[ileft] = kdt.tags[iright]; kdt.tags[iright] = j; iright = iright - 1; } } if ((double)(kdt.xy[ileft, d]) <= (double)(s)) { ileft = ileft + 1; } else { iright = iright - 1; } i3 = ileft; }
/************************************************************************* Serializer: serialization -- ALGLIB -- Copyright 14.03.2011 by Bochkanov Sergey *************************************************************************/ public static void kdtreeserialize(alglib.serializer s, kdtree tree) { // // Header // s.serialize_int(scodes.getkdtreeserializationcode()); s.serialize_int(kdtreefirstversion); // // Data // s.serialize_int(tree.n); s.serialize_int(tree.nx); s.serialize_int(tree.ny); s.serialize_int(tree.normtype); apserv.serializerealmatrix(s, tree.xy, -1, -1); apserv.serializeintegerarray(s, tree.tags, -1); apserv.serializerealarray(s, tree.boxmin, -1); apserv.serializerealarray(s, tree.boxmax, -1); apserv.serializeintegerarray(s, tree.nodes, -1); apserv.serializerealarray(s, tree.splits, -1); }
/************************************************************************* * Recursive kd-tree generation subroutine. * * PARAMETERS * KDT tree * NodesOffs unused part of Nodes[] which must be filled by tree * SplitsOffs unused part of Splits[] * I1, I2 points from [I1,I2) are processed * * NodesOffs[] and SplitsOffs[] must be large enough. * * -- ALGLIB -- * Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ private static void kdtreegeneratetreerec(ref kdtree kdt, ref int nodesoffs, ref int splitsoffs, int i1, int i2, int maxleafsize) { int n = 0; int nx = 0; int ny = 0; int i = 0; int j = 0; int oldoffs = 0; int i3 = 0; int cntless = 0; int cntgreater = 0; double minv = 0; double maxv = 0; int minidx = 0; int maxidx = 0; int d = 0; double ds = 0; double s = 0; double v = 0; int i_ = 0; int i1_ = 0; System.Diagnostics.Debug.Assert(i2 > i1, "KDTreeGenerateTreeRec: internal error"); // // Generate leaf if needed // if (i2 - i1 <= maxleafsize) { kdt.nodes[nodesoffs + 0] = i2 - i1; kdt.nodes[nodesoffs + 1] = i1; nodesoffs = nodesoffs + 2; return; } // // Load values for easier access // nx = kdt.nx; ny = kdt.ny; // // select dimension to split: // * D is a dimension number // d = 0; ds = kdt.curboxmax[0] - kdt.curboxmin[0]; for (i = 1; i <= nx - 1; i++) { v = kdt.curboxmax[i] - kdt.curboxmin[i]; if ((double)(v) > (double)(ds)) { ds = v; d = i; } } // // Select split position S using sliding midpoint rule, // rearrange points into [I1,I3) and [I3,I2) // s = kdt.curboxmin[d] + 0.5 * ds; i1_ = (i1) - (0); for (i_ = 0; i_ <= i2 - i1 - 1; i_++) { kdt.buf[i_] = kdt.xy[i_ + i1_, d]; } n = i2 - i1; cntless = 0; cntgreater = 0; minv = kdt.buf[0]; maxv = kdt.buf[0]; minidx = i1; maxidx = i1; for (i = 0; i <= n - 1; i++) { v = kdt.buf[i]; if ((double)(v) < (double)(minv)) { minv = v; minidx = i1 + i; } if ((double)(v) > (double)(maxv)) { maxv = v; maxidx = i1 + i; } if ((double)(v) < (double)(s)) { cntless = cntless + 1; } if ((double)(v) > (double)(s)) { cntgreater = cntgreater + 1; } } if (cntless > 0 & cntgreater > 0) { // // normal midpoint split // kdtreesplit(ref kdt, i1, i2, d, s, ref i3); } else { // // sliding midpoint // if (cntless == 0) { // // 1. move split to MinV, // 2. place one point to the left bin (move to I1), // others - to the right bin // s = minv; if (minidx != i1) { for (i = 0; i <= 2 * kdt.nx + kdt.ny - 1; i++) { v = kdt.xy[minidx, i]; kdt.xy[minidx, i] = kdt.xy[i1, i]; kdt.xy[i1, i] = v; } j = kdt.tags[minidx]; kdt.tags[minidx] = kdt.tags[i1]; kdt.tags[i1] = j; } i3 = i1 + 1; } else { // // 1. move split to MaxV, // 2. place one point to the right bin (move to I2-1), // others - to the left bin // s = maxv; if (maxidx != i2 - 1) { for (i = 0; i <= 2 * kdt.nx + kdt.ny - 1; i++) { v = kdt.xy[maxidx, i]; kdt.xy[maxidx, i] = kdt.xy[i2 - 1, i]; kdt.xy[i2 - 1, i] = v; } j = kdt.tags[maxidx]; kdt.tags[maxidx] = kdt.tags[i2 - 1]; kdt.tags[i2 - 1] = j; } i3 = i2 - 1; } } // // Generate 'split' node // kdt.nodes[nodesoffs + 0] = 0; kdt.nodes[nodesoffs + 1] = d; kdt.nodes[nodesoffs + 2] = splitsoffs; kdt.splits[splitsoffs + 0] = s; oldoffs = nodesoffs; nodesoffs = nodesoffs + splitnodesize; splitsoffs = splitsoffs + 1; // // Recirsive generation: // * update CurBox // * call subroutine // * restore CurBox // kdt.nodes[oldoffs + 3] = nodesoffs; v = kdt.curboxmax[d]; kdt.curboxmax[d] = s; kdtreegeneratetreerec(ref kdt, ref nodesoffs, ref splitsoffs, i1, i3, maxleafsize); kdt.curboxmax[d] = v; kdt.nodes[oldoffs + 4] = nodesoffs; v = kdt.curboxmin[d]; kdt.curboxmin[d] = s; kdtreegeneratetreerec(ref kdt, ref nodesoffs, ref splitsoffs, i3, i2, maxleafsize); kdt.curboxmin[d] = v; }
/************************************************************************* Rearranges nodes [I1,I2) using partition in D-th dimension with S as threshold. Returns split position I3: [I1,I3) and [I3,I2) are created as result. This subroutine doesn't create tree structures, just rearranges nodes. *************************************************************************/ private static void kdtreesplit(kdtree kdt, int i1, int i2, int d, double s, ref int i3) { int i = 0; int j = 0; int ileft = 0; int iright = 0; double v = 0; i3 = 0; alglib.ap.assert(kdt.n>0, "KDTreeSplit: internal error"); // // split XY/Tags in two parts: // * [ILeft,IRight] is non-processed part of XY/Tags // // After cycle is done, we have Ileft=IRight. We deal with // this element separately. // // After this, [I1,ILeft) contains left part, and [ILeft,I2) // contains right part. // ileft = i1; iright = i2-1; while( ileft<iright ) { if( (double)(kdt.xy[ileft,d])<=(double)(s) ) { // // XY[ILeft] is on its place. // Advance ILeft. // ileft = ileft+1; } else { // // XY[ILeft,..] must be at IRight. // Swap and advance IRight. // for(i=0; i<=2*kdt.nx+kdt.ny-1; i++) { v = kdt.xy[ileft,i]; kdt.xy[ileft,i] = kdt.xy[iright,i]; kdt.xy[iright,i] = v; } j = kdt.tags[ileft]; kdt.tags[ileft] = kdt.tags[iright]; kdt.tags[iright] = j; iright = iright-1; } } if( (double)(kdt.xy[ileft,d])<=(double)(s) ) { ileft = ileft+1; } else { iright = iright-1; } i3 = ileft; }
/************************************************************************* * Recursive subroutine for NN queries. * * -- ALGLIB -- * Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ private static void kdtreequerynnrec(ref kdtree kdt, int offs) { double ptdist = 0; int i = 0; int j = 0; int k = 0; int ti = 0; int nx = 0; int i1 = 0; int i2 = 0; int k1 = 0; int k2 = 0; double r1 = 0; double r2 = 0; int d = 0; double s = 0; double v = 0; double t1 = 0; int childbestoffs = 0; int childworstoffs = 0; int childoffs = 0; double prevdist = 0; bool todive = new bool(); bool bestisleft = new bool(); bool updatemin = new bool(); // // Leaf node. // Process points. // if (kdt.nodes[offs] > 0) { i1 = kdt.nodes[offs + 1]; i2 = i1 + kdt.nodes[offs]; for (i = i1; i <= i2 - 1; i++) { // // Calculate distance // ptdist = 0; nx = kdt.nx; if (kdt.normtype == 0) { for (j = 0; j <= nx - 1; j++) { ptdist = Math.Max(ptdist, Math.Abs(kdt.xy[i, j] - kdt.x[j])); } } if (kdt.normtype == 1) { for (j = 0; j <= nx - 1; j++) { ptdist = ptdist + Math.Abs(kdt.xy[i, j] - kdt.x[j]); } } if (kdt.normtype == 2) { for (j = 0; j <= nx - 1; j++) { ptdist = ptdist + AP.Math.Sqr(kdt.xy[i, j] - kdt.x[j]); } } // // Skip points with zero distance if self-matches are turned off // if ((double)(ptdist) == (double)(0) & !kdt.selfmatch) { continue; } // // We CAN'T process point if R-criterion isn't satisfied, // i.e. (RNeeded<>0) AND (PtDist>R). // if ((double)(kdt.rneeded) == (double)(0) | (double)(ptdist) <= (double)(kdt.rneeded)) { // // R-criterion is satisfied, we must either: // * replace worst point, if (KNeeded<>0) AND (KCur=KNeeded) // (or skip, if worst point is better) // * add point without replacement otherwise // if (kdt.kcur < kdt.kneeded | kdt.kneeded == 0) { // // add current point to heap without replacement // tsort.tagheappushi(ref kdt.r, ref kdt.idx, ref kdt.kcur, ptdist, i); } else { // // New points are added or not, depending on their distance. // If added, they replace element at the top of the heap // if ((double)(ptdist) < (double)(kdt.r[0])) { if (kdt.kneeded == 1) { kdt.idx[0] = i; kdt.r[0] = ptdist; } else { tsort.tagheapreplacetopi(ref kdt.r, ref kdt.idx, kdt.kneeded, ptdist, i); } } } } } return; } // // Simple split // if (kdt.nodes[offs] == 0) { // // Load: // * D dimension to split // * S split position // d = kdt.nodes[offs + 1]; s = kdt.splits[kdt.nodes[offs + 2]]; // // Calculate: // * ChildBestOffs child box with best chances // * ChildWorstOffs child box with worst chances // if ((double)(kdt.x[d]) <= (double)(s)) { childbestoffs = kdt.nodes[offs + 3]; childworstoffs = kdt.nodes[offs + 4]; bestisleft = true; } else { childbestoffs = kdt.nodes[offs + 4]; childworstoffs = kdt.nodes[offs + 3]; bestisleft = false; } // // Navigate through childs // for (i = 0; i <= 1; i++) { // // Select child to process: // * ChildOffs current child offset in Nodes[] // * UpdateMin whether minimum or maximum value // of bounding box is changed on update // if (i == 0) { childoffs = childbestoffs; updatemin = !bestisleft; } else { updatemin = bestisleft; childoffs = childworstoffs; } // // Update bounding box and current distance // if (updatemin) { prevdist = kdt.curdist; t1 = kdt.x[d]; v = kdt.curboxmin[d]; if ((double)(t1) <= (double)(s)) { if (kdt.normtype == 0) { kdt.curdist = Math.Max(kdt.curdist, s - t1); } if (kdt.normtype == 1) { kdt.curdist = kdt.curdist - Math.Max(v - t1, 0) + s - t1; } if (kdt.normtype == 2) { kdt.curdist = kdt.curdist - AP.Math.Sqr(Math.Max(v - t1, 0)) + AP.Math.Sqr(s - t1); } } kdt.curboxmin[d] = s; } else { prevdist = kdt.curdist; t1 = kdt.x[d]; v = kdt.curboxmax[d]; if ((double)(t1) >= (double)(s)) { if (kdt.normtype == 0) { kdt.curdist = Math.Max(kdt.curdist, t1 - s); } if (kdt.normtype == 1) { kdt.curdist = kdt.curdist - Math.Max(t1 - v, 0) + t1 - s; } if (kdt.normtype == 2) { kdt.curdist = kdt.curdist - AP.Math.Sqr(Math.Max(t1 - v, 0)) + AP.Math.Sqr(t1 - s); } } kdt.curboxmax[d] = s; } // // Decide: to dive into cell or not to dive // if ((double)(kdt.rneeded) != (double)(0) & (double)(kdt.curdist) > (double)(kdt.rneeded)) { todive = false; } else { if (kdt.kcur < kdt.kneeded | kdt.kneeded == 0) { // // KCur<KNeeded (i.e. not all points are found) // todive = true; } else { // // KCur=KNeeded, decide to dive or not to dive // using point position relative to bounding box. // todive = (double)(kdt.curdist) <= (double)(kdt.r[0] * kdt.approxf); } } if (todive) { kdtreequerynnrec(ref kdt, childoffs); } // // Restore bounding box and distance // if (updatemin) { kdt.curboxmin[d] = v; } else { kdt.curboxmax[d] = v; } kdt.curdist = prevdist; } return; } }
/************************************************************************* Recursive subroutine for NN queries. -- ALGLIB -- Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ private static void kdtreequerynnrec(kdtree kdt, int offs) { double ptdist = 0; int i = 0; int j = 0; int nx = 0; int i1 = 0; int i2 = 0; int d = 0; double s = 0; double v = 0; double t1 = 0; int childbestoffs = 0; int childworstoffs = 0; int childoffs = 0; double prevdist = 0; bool todive = new bool(); bool bestisleft = new bool(); bool updatemin = new bool(); alglib.ap.assert(kdt.n>0, "KDTreeQueryNNRec: internal error"); // // Leaf node. // Process points. // if( kdt.nodes[offs]>0 ) { i1 = kdt.nodes[offs+1]; i2 = i1+kdt.nodes[offs]; for(i=i1; i<=i2-1; i++) { // // Calculate distance // ptdist = 0; nx = kdt.nx; if( kdt.normtype==0 ) { for(j=0; j<=nx-1; j++) { ptdist = Math.Max(ptdist, Math.Abs(kdt.xy[i,j]-kdt.x[j])); } } if( kdt.normtype==1 ) { for(j=0; j<=nx-1; j++) { ptdist = ptdist+Math.Abs(kdt.xy[i,j]-kdt.x[j]); } } if( kdt.normtype==2 ) { for(j=0; j<=nx-1; j++) { ptdist = ptdist+math.sqr(kdt.xy[i,j]-kdt.x[j]); } } // // Skip points with zero distance if self-matches are turned off // if( (double)(ptdist)==(double)(0) && !kdt.selfmatch ) { continue; } // // We CAN'T process point if R-criterion isn't satisfied, // i.e. (RNeeded<>0) AND (PtDist>R). // if( (double)(kdt.rneeded)==(double)(0) || (double)(ptdist)<=(double)(kdt.rneeded) ) { // // R-criterion is satisfied, we must either: // * replace worst point, if (KNeeded<>0) AND (KCur=KNeeded) // (or skip, if worst point is better) // * add point without replacement otherwise // if( kdt.kcur<kdt.kneeded || kdt.kneeded==0 ) { // // add current point to heap without replacement // tsort.tagheappushi(ref kdt.r, ref kdt.idx, ref kdt.kcur, ptdist, i); } else { // // New points are added or not, depending on their distance. // If added, they replace element at the top of the heap // if( (double)(ptdist)<(double)(kdt.r[0]) ) { if( kdt.kneeded==1 ) { kdt.idx[0] = i; kdt.r[0] = ptdist; } else { tsort.tagheapreplacetopi(ref kdt.r, ref kdt.idx, kdt.kneeded, ptdist, i); } } } } } return; } // // Simple split // if( kdt.nodes[offs]==0 ) { // // Load: // * D dimension to split // * S split position // d = kdt.nodes[offs+1]; s = kdt.splits[kdt.nodes[offs+2]]; // // Calculate: // * ChildBestOffs child box with best chances // * ChildWorstOffs child box with worst chances // if( (double)(kdt.x[d])<=(double)(s) ) { childbestoffs = kdt.nodes[offs+3]; childworstoffs = kdt.nodes[offs+4]; bestisleft = true; } else { childbestoffs = kdt.nodes[offs+4]; childworstoffs = kdt.nodes[offs+3]; bestisleft = false; } // // Navigate through childs // for(i=0; i<=1; i++) { // // Select child to process: // * ChildOffs current child offset in Nodes[] // * UpdateMin whether minimum or maximum value // of bounding box is changed on update // if( i==0 ) { childoffs = childbestoffs; updatemin = !bestisleft; } else { updatemin = bestisleft; childoffs = childworstoffs; } // // Update bounding box and current distance // if( updatemin ) { prevdist = kdt.curdist; t1 = kdt.x[d]; v = kdt.curboxmin[d]; if( (double)(t1)<=(double)(s) ) { if( kdt.normtype==0 ) { kdt.curdist = Math.Max(kdt.curdist, s-t1); } if( kdt.normtype==1 ) { kdt.curdist = kdt.curdist-Math.Max(v-t1, 0)+s-t1; } if( kdt.normtype==2 ) { kdt.curdist = kdt.curdist-math.sqr(Math.Max(v-t1, 0))+math.sqr(s-t1); } } kdt.curboxmin[d] = s; } else { prevdist = kdt.curdist; t1 = kdt.x[d]; v = kdt.curboxmax[d]; if( (double)(t1)>=(double)(s) ) { if( kdt.normtype==0 ) { kdt.curdist = Math.Max(kdt.curdist, t1-s); } if( kdt.normtype==1 ) { kdt.curdist = kdt.curdist-Math.Max(t1-v, 0)+t1-s; } if( kdt.normtype==2 ) { kdt.curdist = kdt.curdist-math.sqr(Math.Max(t1-v, 0))+math.sqr(t1-s); } } kdt.curboxmax[d] = s; } // // Decide: to dive into cell or not to dive // if( (double)(kdt.rneeded)!=(double)(0) && (double)(kdt.curdist)>(double)(kdt.rneeded) ) { todive = false; } else { if( kdt.kcur<kdt.kneeded || kdt.kneeded==0 ) { // // KCur<KNeeded (i.e. not all points are found) // todive = true; } else { // // KCur=KNeeded, decide to dive or not to dive // using point position relative to bounding box. // todive = (double)(kdt.curdist)<=(double)(kdt.r[0]*kdt.approxf); } } if( todive ) { kdtreequerynnrec(kdt, childoffs); } // // Restore bounding box and distance // if( updatemin ) { kdt.curboxmin[d] = v; } else { kdt.curboxmax[d] = v; } kdt.curdist = prevdist; } return; } }
public override alglib.apobject make_copy() { kdtree _result = new kdtree(); _result.n = n; _result.nx = nx; _result.ny = ny; _result.normtype = normtype; _result.xy = (double[,])xy.Clone(); _result.tags = (int[])tags.Clone(); _result.boxmin = (double[])boxmin.Clone(); _result.boxmax = (double[])boxmax.Clone(); _result.nodes = (int[])nodes.Clone(); _result.splits = (double[])splits.Clone(); _result.x = (double[])x.Clone(); _result.kneeded = kneeded; _result.rneeded = rneeded; _result.selfmatch = selfmatch; _result.approxf = approxf; _result.kcur = kcur; _result.idx = (int[])idx.Clone(); _result.r = (double[])r.Clone(); _result.buf = (double[])buf.Clone(); _result.curboxmin = (double[])curboxmin.Clone(); _result.curboxmax = (double[])curboxmax.Clone(); _result.curdist = curdist; _result.debugcounter = debugcounter; return _result; }
/************************************************************************* This function allocates all dataset-independent array fields of KDTree, i.e. such array fields that their dimensions do not depend on dataset size. This function do not sets KDT.NX or KDT.NY - it just allocates arrays -- ALGLIB -- Copyright 14.03.2011 by Bochkanov Sergey *************************************************************************/ private static void kdtreeallocdatasetindependent(kdtree kdt, int nx, int ny) { alglib.ap.assert(kdt.n>0, "KDTreeAllocDatasetIndependent: internal error"); kdt.x = new double[nx]; kdt.boxmin = new double[nx]; kdt.boxmax = new double[nx]; kdt.curboxmin = new double[nx]; kdt.curboxmax = new double[nx]; }
/************************************************************************* KD-tree creation This subroutine creates KD-tree from set of X-values and optional Y-values INPUT PARAMETERS XY - dataset, array[0..N-1,0..NX+NY-1]. one row corresponds to one point. first NX columns contain X-values, next NY (NY may be zero) columns may contain associated Y-values N - number of points, N>=0. NX - space dimension, NX>=1. NY - number of optional Y-values, NY>=0. NormType- norm type: * 0 denotes infinity-norm * 1 denotes 1-norm * 2 denotes 2-norm (Euclidean norm) OUTPUT PARAMETERS KDT - KD-tree NOTES 1. KD-tree creation have O(N*logN) complexity and O(N*(2*NX+NY)) memory requirements. 2. Although KD-trees may be used with any combination of N and NX, they are more efficient than brute-force search only when N >> 4^NX. So they are most useful in low-dimensional tasks (NX=2, NX=3). NX=1 is another inefficient case, because simple binary search (without additional structures) is much more efficient in such tasks than KD-trees. -- ALGLIB -- Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ public static void kdtreebuild(double[,] xy, int n, int nx, int ny, int normtype, kdtree kdt) { int[] tags = new int[0]; int i = 0; alglib.ap.assert(n>=0, "KDTreeBuild: N<0"); alglib.ap.assert(nx>=1, "KDTreeBuild: NX<1"); alglib.ap.assert(ny>=0, "KDTreeBuild: NY<0"); alglib.ap.assert(normtype>=0 && normtype<=2, "KDTreeBuild: incorrect NormType"); alglib.ap.assert(alglib.ap.rows(xy)>=n, "KDTreeBuild: rows(X)<N"); alglib.ap.assert(alglib.ap.cols(xy)>=nx+ny || n==0, "KDTreeBuild: cols(X)<NX+NY"); alglib.ap.assert(apserv.apservisfinitematrix(xy, n, nx+ny), "KDTreeBuild: XY contains infinite or NaN values"); if( n>0 ) { tags = new int[n]; for(i=0; i<=n-1; i++) { tags[i] = 0; } } kdtreebuildtagged(xy, tags, n, nx, ny, normtype, kdt); }
/************************************************************************* This function allocates temporaries. This function do not sets KDT.N, KDT.NX or KDT.NY - it just allocates arrays. -- ALGLIB -- Copyright 14.03.2011 by Bochkanov Sergey *************************************************************************/ private static void kdtreealloctemporaries(kdtree kdt, int n, int nx, int ny) { alglib.ap.assert(n>0, "KDTreeAllocTemporaries: internal error"); kdt.x = new double[nx]; kdt.idx = new int[n]; kdt.r = new double[n]; kdt.buf = new double[Math.Max(n, nx)]; kdt.curboxmin = new double[nx]; kdt.curboxmax = new double[nx]; }
/************************************************************************* Distances from last query INPUT PARAMETERS KDT - KD-tree R - pre-allocated array, at least K elements OUTPUT PARAMETERS R - first K elements are filled with distances (in corresponding norm) K - number of points NOTE points are ordered by distance from the query point (first = closest) SEE ALSO * KDTreeQueryResultsX() X-values * KDTreeQueryResultsXY() X- and Y-values * KDTreeQueryResultsTags() tag values -- ALGLIB -- Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ public static void kdtreequeryresultsdistances(ref kdtree kdt, ref double[] r, ref int k) { int i = 0; k = kdt.kcur; // // unload norms // // Abs() call is used to handle cases with negative norms // (generated during KFN requests) // if( kdt.normtype==0 ) { for(i=0; i<=k-1; i++) { r[i] = Math.Abs(kdt.r[i]); } } if( kdt.normtype==1 ) { for(i=0; i<=k-1; i++) { r[i] = Math.Abs(kdt.r[i]); } } if( kdt.normtype==2 ) { for(i=0; i<=k-1; i++) { r[i] = Math.Sqrt(Math.Abs(kdt.r[i])); } } }