/************************************************************************* This function calculates values of the RBF model at the given point. This is general function which can be used for arbitrary NX (dimension of the space of arguments) and NY (dimension of the function itself). However when you have NY=1 you may find more convenient to use RBFCalc2() or RBFCalc3(). This function returns 0.0 when model is not initialized. INPUT PARAMETERS: S - RBF model X - coordinates, array[NX]. X may have more than NX elements, in this case only leading NX will be used. OUTPUT PARAMETERS: Y - function value, array[NY]. Y is out-parameter and reallocated after call to this function. In case you want to reuse previously allocated Y, you may use RBFCalcBuf(), which reallocates Y only when it is too small. -- ALGLIB -- Copyright 13.12.2011 by Bochkanov Sergey *************************************************************************/ public static void rbfcalc(rbfmodel s, double[] x, ref double[] y) { y = new double[0]; alglib.ap.assert(alglib.ap.len(x)>=s.nx, "RBFCalc: Length(X)<NX"); alglib.ap.assert(apserv.isfinitevector(x, s.nx), "RBFCalc: X contains infinite or NaN values"); rbfcalcbuf(s, x, ref y); }
/************************************************************************* This function calculates values of the RBF model in the given point. This function should be used when we have NY=1 (scalar function) and NX=2 (2-dimensional space). If you have 3-dimensional space, use RBFCalc3(). If you have general situation (NX-dimensional space, NY-dimensional function) you should use general, less efficient implementation RBFCalc(). If you want to calculate function values many times, consider using RBFGridCalc2(), which is far more efficient than many subsequent calls to RBFCalc2(). This function returns 0.0 when: * model is not initialized * NX<>2 *NY<>1 INPUT PARAMETERS: S - RBF model X0 - first coordinate, finite number X1 - second coordinate, finite number RESULT: value of the model or 0.0 (as defined above) -- ALGLIB -- Copyright 13.12.2011 by Bochkanov Sergey *************************************************************************/ public static double rbfcalc2(rbfmodel s, double x0, double x1) { double result = 0; int i = 0; int j = 0; int lx = 0; int tg = 0; double d2 = 0; double t = 0; double bfcur = 0; double rcur = 0; alglib.ap.assert(math.isfinite(x0), "RBFCalc2: invalid value for X0 (X0 is Inf)!"); alglib.ap.assert(math.isfinite(x1), "RBFCalc2: invalid value for X1 (X1 is Inf)!"); if( s.ny!=1 || s.nx!=2 ) { result = 0; return result; } result = s.v[0,0]*x0+s.v[0,1]*x1+s.v[0,mxnx]; if( s.nc==0 ) { return result; } apserv.rvectorsetlengthatleast(ref s.calcbufxcx, mxnx); for(i=0; i<=mxnx-1; i++) { s.calcbufxcx[i] = 0.0; } s.calcbufxcx[0] = x0; s.calcbufxcx[1] = x1; lx = nearestneighbor.kdtreequeryrnn(s.tree, s.calcbufxcx, s.rmax*rbffarradius, true); nearestneighbor.kdtreequeryresultsx(s.tree, ref s.calcbufx); nearestneighbor.kdtreequeryresultstags(s.tree, ref s.calcbuftags); for(i=0; i<=lx-1; i++) { tg = s.calcbuftags[i]; d2 = math.sqr(x0-s.calcbufx[i,0])+math.sqr(x1-s.calcbufx[i,1]); rcur = s.wr[tg,0]; bfcur = Math.Exp(-(d2/(rcur*rcur))); for(j=0; j<=s.nl-1; j++) { result = result+bfcur*s.wr[tg,1+j]; rcur = 0.5*rcur; t = bfcur*bfcur; bfcur = t*t; } } return result; }
/************************************************************************* This function calculates values of the RBF model in the given point. This function should be used when we have NY=1 (scalar function) and NX=3 (3-dimensional space). If you have 2-dimensional space, use RBFCalc2(). If you have general situation (NX-dimensional space, NY-dimensional function) you should use general, less efficient implementation RBFCalc(). This function returns 0.0 when: * model is not initialized * NX<>3 *NY<>1 INPUT PARAMETERS: S - RBF model X0 - first coordinate, finite number X1 - second coordinate, finite number X2 - third coordinate, finite number RESULT: value of the model or 0.0 (as defined above) -- ALGLIB -- Copyright 13.12.2011 by Bochkanov Sergey *************************************************************************/ public static double rbfcalc3(rbfmodel s, double x0, double x1, double x2) { double result = 0; int i = 0; int j = 0; int lx = 0; int tg = 0; double t = 0; double rcur = 0; double bf = 0; alglib.ap.assert(math.isfinite(x0), "RBFCalc3: invalid value for X0 (X0 is Inf or NaN)!"); alglib.ap.assert(math.isfinite(x1), "RBFCalc3: invalid value for X1 (X1 is Inf or NaN)!"); alglib.ap.assert(math.isfinite(x2), "RBFCalc3: invalid value for X2 (X2 is Inf or NaN)!"); if( s.ny!=1 || s.nx!=3 ) { result = 0; return result; } result = s.v[0,0]*x0+s.v[0,1]*x1+s.v[0,2]*x2+s.v[0,mxnx]; if( s.nc==0 ) { return result; } // // calculating value for F(X) // apserv.rvectorsetlengthatleast(ref s.calcbufxcx, mxnx); for(i=0; i<=mxnx-1; i++) { s.calcbufxcx[i] = 0.0; } s.calcbufxcx[0] = x0; s.calcbufxcx[1] = x1; s.calcbufxcx[2] = x2; lx = nearestneighbor.kdtreequeryrnn(s.tree, s.calcbufxcx, s.rmax*rbffarradius, true); nearestneighbor.kdtreequeryresultsx(s.tree, ref s.calcbufx); nearestneighbor.kdtreequeryresultstags(s.tree, ref s.calcbuftags); for(i=0; i<=lx-1; i++) { tg = s.calcbuftags[i]; rcur = s.wr[tg,0]; bf = Math.Exp(-((math.sqr(x0-s.calcbufx[i,0])+math.sqr(x1-s.calcbufx[i,1])+math.sqr(x2-s.calcbufx[i,2]))/math.sqr(rcur))); for(j=0; j<=s.nl-1; j++) { result = result+bf*s.wr[tg,1+j]; t = bf*bf; bf = t*t; } } return result; }
/************************************************************************* This function changes centers allocation algorithm to one which allocates centers exactly at the dataset points (one input point = one center). This function won't have effect until next call to RBFBuildModel(). INPUT PARAMETERS: S - RBF model, initialized by RBFCreate() call NOTE: this function has some serialization-related subtleties. We recommend you to study serialization examples from ALGLIB Reference Manual if you want to perform serialization of your models. -- ALGLIB -- Copyright 13.12.2011 by Bochkanov Sergey *************************************************************************/ private static void rbfgridpoints(rbfmodel s) { s.gridtype = 2; }
/************************************************************************* This function sets centers, defined by user. This function overrides results of the previous calls, i.e. multiple calls of this function will result in only the last set being added. INPUT PARAMETERS: S - RBF model, initialized by RBFCreate() call. CXY - centers, array[N,NX]. Centers must be distinct, non-distinct centers are not supported. NC - number of centers NOTE: this function has some serialization-related subtleties. We recommend you to study serialization examples from ALGLIB Reference Manual if you want to perform serialization of your models. -- ALGLIB -- Copyright 13.12.2011 by Bochkanov Sergey *************************************************************************/ private static void rbfgridspecial(rbfmodel s, double[,] cxy, int nc) { int i = 0; int j = 0; alglib.ap.assert(nc>0, "RBFGridSpecial: N<0"); alglib.ap.assert(nc<=alglib.ap.rows(cxy), "RBFGridSpecial: Length(CXY)<NC"); s.gridtype = 3; s.nc = nc; s.xc = new double[s.nc, mxnx]; for(i=0; i<=s.nc-1; i++) { for(j=0; j<=mxnx-1; j++) { s.xc[i,j] = 0; } for(j=0; j<=s.nx-1; j++) { s.xc[i,j] = cxy[i,j]; } } }
/************************************************************************* This function "unpacks" RBF model by extracting its coefficients. INPUT PARAMETERS: S - RBF model OUTPUT PARAMETERS: NX - dimensionality of argument NY - dimensionality of the target function XWR - model information, array[NC,NX+NY+1]. One row of the array corresponds to one basis function: * first NX columns - coordinates of the center * next NY columns - weights, one per dimension of the function being modelled * last column - radius, same for all dimensions of the function being modelled NC - number of the centers V - polynomial term , array[NY,NX+1]. One row per one dimension of the function being modelled. First NX elements are linear coefficients, V[NX] is equal to the constant part. -- ALGLIB -- Copyright 13.12.2011 by Bochkanov Sergey *************************************************************************/ public static void rbfunpack(rbfmodel s, ref int nx, ref int ny, ref double[,] xwr, ref int nc, ref double[,] v) { int i = 0; int j = 0; double rcur = 0; int i_ = 0; int i1_ = 0; nx = 0; ny = 0; xwr = new double[0,0]; nc = 0; v = new double[0,0]; nx = s.nx; ny = s.ny; nc = s.nc; // // Fill V // v = new double[s.ny, s.nx+1]; for(i=0; i<=s.ny-1; i++) { for(i_=0; i_<=s.nx-1;i_++) { v[i,i_] = s.v[i,i_]; } v[i,s.nx] = s.v[i,mxnx]; } // // Fill XWR and V // if( nc*s.nl>0 ) { xwr = new double[s.nc*s.nl, s.nx+s.ny+1]; for(i=0; i<=s.nc-1; i++) { rcur = s.wr[i,0]; for(j=0; j<=s.nl-1; j++) { for(i_=0; i_<=s.nx-1;i_++) { xwr[i*s.nl+j,i_] = s.xc[i,i_]; } i1_ = (1+j*s.ny) - (s.nx); for(i_=s.nx; i_<=s.nx+s.ny-1;i_++) { xwr[i*s.nl+j,i_] = s.wr[i,i_+i1_]; } xwr[i*s.nl+j,s.nx+s.ny] = rcur; rcur = 0.5*rcur; } } } }
/************************************************************************* Serializer: serialization -- ALGLIB -- Copyright 02.02.2012 by Bochkanov Sergey *************************************************************************/ public static void rbfserialize(alglib.serializer s, rbfmodel model) { // // Header // s.serialize_int(scodes.getrbfserializationcode()); s.serialize_int(rbffirstversion); // // Data // s.serialize_int(model.nx); s.serialize_int(model.ny); s.serialize_int(model.nc); s.serialize_int(model.nl); nearestneighbor.kdtreeserialize(s, model.tree); apserv.serializerealmatrix(s, model.xc, -1, -1); apserv.serializerealmatrix(s, model.wr, -1, -1); s.serialize_double(model.rmax); apserv.serializerealmatrix(s, model.v, -1, -1); }
/************************************************************************* This function sets RBF interpolation algorithm. ALGLIB supports several RBF algorithms with different properties. This algorithm is called RBF-ML. It builds multilayer RBF model, i.e. model with subsequently decreasing radii, which allows us to combine smoothness (due to large radii of the first layers) with exactness (due to small radii of the last layers) and fast convergence. Internally RBF-ML uses many different means of acceleration, from sparse matrices to KD-trees, which results in algorithm whose working time is roughly proportional to N*log(N)*Density*RBase^2*NLayers, where N is a number of points, Density is an average density if points per unit of the interpolation space, RBase is an initial radius, NLayers is a number of layers. RBF-ML is good for following kinds of interpolation problems: 1. "exact" problems (perfect fit) with well separated points 2. least squares problems with arbitrary distribution of points (algorithm gives perfect fit where it is possible, and resorts to least squares fit in the hard areas). 3. noisy problems where we want to apply some controlled amount of smoothing. INPUT PARAMETERS: S - RBF model, initialized by RBFCreate() call RBase - RBase parameter, RBase>0 NLayers - NLayers parameter, NLayers>0, recommended value to start with - about 5. LambdaV - regularization value, can be useful when solving problem in the least squares sense. Optimal lambda is problem- dependent and require trial and error. In our experience, good lambda can be as large as 0.1, and you can use 0.001 as initial guess. Default value - 0.01, which is used when LambdaV is not given. You can specify zero value, but it is not recommended to do so. TUNING ALGORITHM In order to use this algorithm you have to choose three parameters: * initial radius RBase * number of layers in the model NLayers * regularization coefficient LambdaV Initial radius is easy to choose - you can pick any number several times larger than the average distance between points. Algorithm won't break down if you choose radius which is too large (model construction time will increase, but model will be built correctly). Choose such number of layers that RLast=RBase/2^(NLayers-1) (radius used by the last layer) will be smaller than the typical distance between points. In case model error is too large, you can increase number of layers. Having more layers will make model construction and evaluation proportionally slower, but it will allow you to have model which precisely fits your data. From the other side, if you want to suppress noise, you can DECREASE number of layers to make your model less flexible. Regularization coefficient LambdaV controls smoothness of the individual models built for each layer. We recommend you to use default value in case you don't want to tune this parameter, because having non-zero LambdaV accelerates and stabilizes internal iterative algorithm. In case you want to suppress noise you can use LambdaV as additional parameter (larger value = more smoothness) to tune. TYPICAL ERRORS 1. Using initial radius which is too large. Memory requirements of the RBF-ML are roughly proportional to N*Density*RBase^2 (where Density is an average density of points per unit of the interpolation space). In the extreme case of the very large RBase we will need O(N^2) units of memory - and many layers in order to decrease radius to some reasonably small value. 2. Using too small number of layers - RBF models with large radius are not flexible enough to reproduce small variations in the target function. You need many layers with different radii, from large to small, in order to have good model. 3. Using initial radius which is too small. You will get model with "holes" in the areas which are too far away from interpolation centers. However, algorithm will work correctly (and quickly) in this case. 4. Using too many layers - you will get too large and too slow model. This model will perfectly reproduce your function, but maybe you will be able to achieve similar results with less layers (and less memory). -- ALGLIB -- Copyright 02.03.2012 by Bochkanov Sergey *************************************************************************/ public static void rbfsetalgomultilayer(rbfmodel s, double rbase, int nlayers, double lambdav) { alglib.ap.assert(math.isfinite(rbase), "RBFSetAlgoMultiLayer: RBase is infinite or NaN"); alglib.ap.assert((double)(rbase)>(double)(0), "RBFSetAlgoMultiLayer: RBase<=0"); alglib.ap.assert(nlayers>=0, "RBFSetAlgoMultiLayer: NLayers<0"); alglib.ap.assert(math.isfinite(lambdav), "RBFSetAlgoMultiLayer: LambdaV is infinite or NAN"); alglib.ap.assert((double)(lambdav)>=(double)(0), "RBFSetAlgoMultiLayer: LambdaV<0"); s.radvalue = rbase; s.nlayers = nlayers; s.algorithmtype = 2; s.lambdav = lambdav; }
/************************************************************************* This function sets linear term (model is a sum of radial basis functions plus linear polynomial). This function won't have effect until next call to RBFBuildModel(). INPUT PARAMETERS: S - RBF model, initialized by RBFCreate() call NOTE: this function has some serialization-related subtleties. We recommend you to study serialization examples from ALGLIB Reference Manual if you want to perform serialization of your models. -- ALGLIB -- Copyright 13.12.2011 by Bochkanov Sergey *************************************************************************/ public static void rbfsetlinterm(rbfmodel s) { s.aterm = 1; }
/************************************************************************* This function adds dataset. This function overrides results of the previous calls, i.e. multiple calls of this function will result in only the last set being added. INPUT PARAMETERS: S - RBF model, initialized by RBFCreate() call. XY - points, array[N,NX+NY]. One row corresponds to one point in the dataset. First NX elements are coordinates, next NY elements are function values. Array may be larger than specific, in this case only leading [N,NX+NY] elements will be used. N - number of points in the dataset After you've added dataset and (optionally) tuned algorithm settings you should call RBFBuildModel() in order to build a model for you. NOTE: this function has some serialization-related subtleties. We recommend you to study serialization examples from ALGLIB Reference Manual if you want to perform serialization of your models. -- ALGLIB -- Copyright 13.12.2011 by Bochkanov Sergey *************************************************************************/ public static void rbfsetpoints(rbfmodel s, double[,] xy, int n) { int i = 0; int j = 0; alglib.ap.assert(n>0, "RBFSetPoints: N<0"); alglib.ap.assert(alglib.ap.rows(xy)>=n, "RBFSetPoints: Rows(XY)<N"); alglib.ap.assert(alglib.ap.cols(xy)>=s.nx+s.ny, "RBFSetPoints: Cols(XY)<NX+NY"); s.n = n; s.x = new double[s.n, mxnx]; s.y = new double[s.n, s.ny]; for(i=0; i<=s.n-1; i++) { for(j=0; j<=mxnx-1; j++) { s.x[i,j] = 0; } for(j=0; j<=s.nx-1; j++) { s.x[i,j] = xy[i,j]; } for(j=0; j<=s.ny-1; j++) { s.y[i,j] = xy[i,j+s.nx]; } } }
/************************************************************************* This function sets RBF interpolation algorithm. ALGLIB supports several RBF algorithms with different properties. This algorithm is called RBF-QNN and it is good for point sets with following properties: a) all points are distinct b) all points are well separated. c) points distribution is approximately uniform. There is no "contour lines", clusters of points, or other small-scale structures. Algorithm description: 1) interpolation centers are allocated to data points 2) interpolation radii are calculated as distances to the nearest centers times Q coefficient (where Q is a value from [0.75,1.50]). 3) after performing (2) radii are transformed in order to avoid situation when single outlier has very large radius and influences many points across all dataset. Transformation has following form: new_r[i] = min(r[i],Z*median(r[])) where r[i] is I-th radius, median() is a median radius across entire dataset, Z is user-specified value which controls amount of deviation from median radius. When (a) is violated, we will be unable to build RBF model. When (b) or (c) are violated, model will be built, but interpolation quality will be low. See http://www.alglib.net/interpolation/ for more information on this subject. This algorithm is used by default. Additional Q parameter controls smoothness properties of the RBF basis: * Q<0.75 will give perfectly conditioned basis, but terrible smoothness properties (RBF interpolant will have sharp peaks around function values) * Q around 1.0 gives good balance between smoothness and condition number * Q>1.5 will lead to badly conditioned systems and slow convergence of the underlying linear solver (although smoothness will be very good) * Q>2.0 will effectively make optimizer useless because it won't converge within reasonable amount of iterations. It is possible to set such large Q, but it is advised not to do so. INPUT PARAMETERS: S - RBF model, initialized by RBFCreate() call Q - Q parameter, Q>0, recommended value - 1.0 Z - Z parameter, Z>0, recommended value - 5.0 NOTE: this function has some serialization-related subtleties. We recommend you to study serialization examples from ALGLIB Reference Manual if you want to perform serialization of your models. -- ALGLIB -- Copyright 13.12.2011 by Bochkanov Sergey *************************************************************************/ public static void rbfsetalgoqnn(rbfmodel s, double q, double z) { alglib.ap.assert(math.isfinite(q), "RBFSetAlgoQNN: Q is infinite or NAN"); alglib.ap.assert((double)(q)>(double)(0), "RBFSetAlgoQNN: Q<=0"); rbfgridpoints(s); rbfradnn(s, q, z); s.algorithmtype = 1; }
/************************************************************************* This function creates RBF model for a scalar (NY=1) or vector (NY>1) function in a NX-dimensional space (NX=2 or NX=3). Newly created model is empty. It can be used for interpolation right after creation, but it just returns zeros. You have to add points to the model, tune interpolation settings, and then call model construction function RBFBuildModel() which will update model according to your specification. USAGE: 1. User creates model with RBFCreate() 2. User adds dataset with RBFSetPoints() (points do NOT have to be on a regular grid) 3. (OPTIONAL) User chooses polynomial term by calling: * RBFLinTerm() to set linear term * RBFConstTerm() to set constant term * RBFZeroTerm() to set zero term By default, linear term is used. 4. User chooses specific RBF algorithm to use: either QNN (RBFSetAlgoQNN) or ML (RBFSetAlgoMultiLayer). 5. User calls RBFBuildModel() function which rebuilds model according to the specification 6. User may call RBFCalc() to calculate model value at the specified point, RBFGridCalc() to calculate model values at the points of the regular grid. User may extract model coefficients with RBFUnpack() call. INPUT PARAMETERS: NX - dimension of the space, NX=2 or NX=3 NY - function dimension, NY>=1 OUTPUT PARAMETERS: S - RBF model (initially equals to zero) NOTE 1: memory requirements. RBF models require amount of memory which is proportional to the number of data points. Memory is allocated during model construction, but most of this memory is freed after model coefficients are calculated. Some approximate estimates for N centers with default settings are given below: * about 250*N*(sizeof(double)+2*sizeof(int)) bytes of memory is needed during model construction stage. * about 15*N*sizeof(double) bytes is needed after model is built. For example, for N=100000 we may need 0.6 GB of memory to build model, but just about 0.012 GB to store it. -- ALGLIB -- Copyright 13.12.2011 by Bochkanov Sergey *************************************************************************/ public static void rbfcreate(int nx, int ny, rbfmodel s) { int i = 0; int j = 0; alglib.ap.assert(nx==2 || nx==3, "RBFCreate: NX<>2 and NX<>3"); alglib.ap.assert(ny>=1, "RBFCreate: NY<1"); s.nx = nx; s.ny = ny; s.nl = 0; s.nc = 0; s.v = new double[ny, mxnx+1]; for(i=0; i<=ny-1; i++) { for(j=0; j<=mxnx; j++) { s.v[i,j] = 0; } } s.n = 0; s.rmax = 0; s.gridtype = 2; s.fixrad = false; s.radvalue = 1; s.radzvalue = 5; s.aterm = 1; s.algorithmtype = 1; // // stopping criteria // s.epsort = eps; s.epserr = eps; s.maxits = 0; }
public override alglib.apobject make_copy() { rbfmodel _result = new rbfmodel(); _result.ny = ny; _result.nx = nx; _result.nc = nc; _result.nl = nl; _result.tree = (nearestneighbor.kdtree)tree.make_copy(); _result.xc = (double[,])xc.Clone(); _result.wr = (double[,])wr.Clone(); _result.rmax = rmax; _result.v = (double[,])v.Clone(); _result.gridtype = gridtype; _result.fixrad = fixrad; _result.lambdav = lambdav; _result.radvalue = radvalue; _result.radzvalue = radzvalue; _result.nlayers = nlayers; _result.aterm = aterm; _result.algorithmtype = algorithmtype; _result.epsort = epsort; _result.epserr = epserr; _result.maxits = maxits; _result.h = h; _result.n = n; _result.x = (double[,])x.Clone(); _result.y = (double[,])y.Clone(); _result.calcbufxcx = (double[])calcbufxcx.Clone(); _result.calcbufx = (double[,])calcbufx.Clone(); _result.calcbuftags = (int[])calcbuftags.Clone(); return _result; }
/************************************************************************* This function changes radii calculation algorithm to one which makes all radii equal to the same fixed value R. This function won't have effect until next call to RBFBuildModel(). IMPORTANT: you should use this function with caution because too large R will make model fitting algorithm unstable, while too small R will make perfect, but useless fit (it will be non-smooth, with sharp peaks around dataset points). INPUT PARAMETERS: S - RBF model, initialized by RBFCreate() call R - radius, R>0 NOTE: this function has some serialization-related subtleties. We recommend you to study serialization examples from ALGLIB Reference Manual if you want to perform serialization of your models. -- ALGLIB -- Copyright 13.12.2011 by Bochkanov Sergey *************************************************************************/ private static void rbfradfixed(rbfmodel s, double r) { alglib.ap.assert(math.isfinite(r) && (double)(r)>(double)(0), "RBFRadFixed: R<=0, infinite or NAN"); s.fixrad = true; s.radvalue = r; }
/************************************************************************* This function calculates values of the RBF model at the given point. Same as RBFCalc(), but does not reallocate Y when in is large enough to store function values. INPUT PARAMETERS: S - RBF model X - coordinates, array[NX]. X may have more than NX elements, in this case only leading NX will be used. Y - possibly preallocated array OUTPUT PARAMETERS: Y - function value, array[NY]. Y is not reallocated when it is larger than NY. -- ALGLIB -- Copyright 13.12.2011 by Bochkanov Sergey *************************************************************************/ public static void rbfcalcbuf(rbfmodel s, double[] x, ref double[] y) { int i = 0; int j = 0; int k = 0; int lx = 0; int tg = 0; double t = 0; double rcur = 0; double bf = 0; alglib.ap.assert(alglib.ap.len(x)>=s.nx, "RBFCalcBuf: Length(X)<NX"); alglib.ap.assert(apserv.isfinitevector(x, s.nx), "RBFCalcBuf: X contains infinite or NaN values"); if( alglib.ap.len(y)<s.ny ) { y = new double[s.ny]; } for(i=0; i<=s.ny-1; i++) { y[i] = s.v[i,mxnx]; for(j=0; j<=s.nx-1; j++) { y[i] = y[i]+s.v[i,j]*x[j]; } } if( s.nc==0 ) { return; } apserv.rvectorsetlengthatleast(ref s.calcbufxcx, mxnx); for(i=0; i<=mxnx-1; i++) { s.calcbufxcx[i] = 0.0; } for(i=0; i<=s.nx-1; i++) { s.calcbufxcx[i] = x[i]; } lx = nearestneighbor.kdtreequeryrnn(s.tree, s.calcbufxcx, s.rmax*rbffarradius, true); nearestneighbor.kdtreequeryresultsx(s.tree, ref s.calcbufx); nearestneighbor.kdtreequeryresultstags(s.tree, ref s.calcbuftags); for(i=0; i<=s.ny-1; i++) { for(j=0; j<=lx-1; j++) { tg = s.calcbuftags[j]; rcur = s.wr[tg,0]; bf = Math.Exp(-((math.sqr(s.calcbufxcx[0]-s.calcbufx[j,0])+math.sqr(s.calcbufxcx[1]-s.calcbufx[j,1])+math.sqr(s.calcbufxcx[2]-s.calcbufx[j,2]))/math.sqr(rcur))); for(k=0; k<=s.nl-1; k++) { y[i] = y[i]+bf*s.wr[tg,1+k*s.ny+i]; t = bf*bf; bf = t*t; } } } }
/************************************************************************* This function sets constant term (model is a sum of radial basis functions plus constant). This function won't have effect until next call to RBFBuildModel(). INPUT PARAMETERS: S - RBF model, initialized by RBFCreate() call NOTE: this function has some serialization-related subtleties. We recommend you to study serialization examples from ALGLIB Reference Manual if you want to perform serialization of your models. -- ALGLIB -- Copyright 13.12.2011 by Bochkanov Sergey *************************************************************************/ public static void rbfsetconstterm(rbfmodel s) { s.aterm = 2; }
/************************************************************************* This function calculates values of the RBF model at the regular grid. Grid have N0*N1 points, with Point[I,J] = (X0[I], X1[J]) This function returns 0.0 when: * model is not initialized * NX<>2 *NY<>1 INPUT PARAMETERS: S - RBF model X0 - array of grid nodes, first coordinates, array[N0] N0 - grid size (number of nodes) in the first dimension X1 - array of grid nodes, second coordinates, array[N1] N1 - grid size (number of nodes) in the second dimension OUTPUT PARAMETERS: Y - function values, array[N0,N1]. Y is out-variable and is reallocated by this function. -- ALGLIB -- Copyright 13.12.2011 by Bochkanov Sergey *************************************************************************/ public static void rbfgridcalc2(rbfmodel s, double[] x0, int n0, double[] x1, int n1, ref double[,] y) { double[] cpx0 = new double[0]; double[] cpx1 = new double[0]; int[] p01 = new int[0]; int[] p11 = new int[0]; int[] p2 = new int[0]; double rlimit = 0; double xcnorm2 = 0; int hp01 = 0; double hcpx0 = 0; double xc0 = 0; double xc1 = 0; double omega = 0; double radius = 0; int i = 0; int j = 0; int k = 0; int d = 0; int i00 = 0; int i01 = 0; int i10 = 0; int i11 = 0; y = new double[0,0]; alglib.ap.assert(n0>0, "RBFGridCalc2: invalid value for N0 (N0<=0)!"); alglib.ap.assert(n1>0, "RBFGridCalc2: invalid value for N1 (N1<=0)!"); alglib.ap.assert(alglib.ap.len(x0)>=n0, "RBFGridCalc2: Length(X0)<N0"); alglib.ap.assert(alglib.ap.len(x1)>=n1, "RBFGridCalc2: Length(X1)<N1"); alglib.ap.assert(apserv.isfinitevector(x0, n0), "RBFGridCalc2: X0 contains infinite or NaN values!"); alglib.ap.assert(apserv.isfinitevector(x1, n1), "RBFGridCalc2: X1 contains infinite or NaN values!"); y = new double[n0, n1]; for(i=0; i<=n0-1; i++) { for(j=0; j<=n1-1; j++) { y[i,j] = 0; } } if( (s.ny!=1 || s.nx!=2) || s.nc==0 ) { return; } // //create and sort arrays // cpx0 = new double[n0]; for(i=0; i<=n0-1; i++) { cpx0[i] = x0[i]; } tsort.tagsort(ref cpx0, n0, ref p01, ref p2); cpx1 = new double[n1]; for(i=0; i<=n1-1; i++) { cpx1[i] = x1[i]; } tsort.tagsort(ref cpx1, n1, ref p11, ref p2); // //calculate function's value // for(i=0; i<=s.nc-1; i++) { radius = s.wr[i,0]; for(d=0; d<=s.nl-1; d++) { omega = s.wr[i,1+d]; rlimit = radius*rbffarradius; // //search lower and upper indexes // i00 = tsort.lowerbound(cpx0, n0, s.xc[i,0]-rlimit); i01 = tsort.upperbound(cpx0, n0, s.xc[i,0]+rlimit); i10 = tsort.lowerbound(cpx1, n1, s.xc[i,1]-rlimit); i11 = tsort.upperbound(cpx1, n1, s.xc[i,1]+rlimit); xc0 = s.xc[i,0]; xc1 = s.xc[i,1]; for(j=i00; j<=i01-1; j++) { hcpx0 = cpx0[j]; hp01 = p01[j]; for(k=i10; k<=i11-1; k++) { xcnorm2 = math.sqr(hcpx0-xc0)+math.sqr(cpx1[k]-xc1); if( (double)(xcnorm2)<=(double)(rlimit*rlimit) ) { y[hp01,p11[k]] = y[hp01,p11[k]]+Math.Exp(-(xcnorm2/math.sqr(radius)))*omega; } } } radius = 0.5*radius; } } // //add linear term // for(i=0; i<=n0-1; i++) { for(j=0; j<=n1-1; j++) { y[i,j] = y[i,j]+s.v[0,0]*x0[i]+s.v[0,1]*x1[j]+s.v[0,mxnx]; } } }
/************************************************************************* This function sets zero term (model is a sum of radial basis functions without polynomial term). This function won't have effect until next call to RBFBuildModel(). INPUT PARAMETERS: S - RBF model, initialized by RBFCreate() call NOTE: this function has some serialization-related subtleties. We recommend you to study serialization examples from ALGLIB Reference Manual if you want to perform serialization of your models. -- ALGLIB -- Copyright 13.12.2011 by Bochkanov Sergey *************************************************************************/ public static void rbfsetzeroterm(rbfmodel s) { s.aterm = 3; }
/************************************************************************* Serializer: allocation -- ALGLIB -- Copyright 02.02.2012 by Bochkanov Sergey *************************************************************************/ public static void rbfalloc(alglib.serializer s, rbfmodel model) { // // Header // s.alloc_entry(); s.alloc_entry(); // // Data // s.alloc_entry(); s.alloc_entry(); s.alloc_entry(); s.alloc_entry(); nearestneighbor.kdtreealloc(s, model.tree); apserv.allocrealmatrix(s, model.xc, -1, -1); apserv.allocrealmatrix(s, model.wr, -1, -1); s.alloc_entry(); apserv.allocrealmatrix(s, model.v, -1, -1); }
/************************************************************************* This function sets stopping criteria of the underlying linear solver. INPUT PARAMETERS: S - RBF model, initialized by RBFCreate() call EpsOrt - orthogonality stopping criterion, EpsOrt>=0. Algorithm will stop when ||A'*r||<=EpsOrt where A' is a transpose of the system matrix, r is a residual vector. Recommended value of EpsOrt is equal to 1E-6. This criterion will stop algorithm when we have "bad fit" situation, i.e. when we should stop in a point with large, nonzero residual. EpsErr - residual stopping criterion. Algorithm will stop when ||r||<=EpsErr*||b||, where r is a residual vector, b is a right part of the system (function values). Recommended value of EpsErr is equal to 1E-3 or 1E-6. This criterion will stop algorithm in a "good fit" situation when we have near-zero residual near the desired solution. MaxIts - this criterion will stop algorithm after MaxIts iterations. It should be used for debugging purposes only! Zero MaxIts means that no limit is placed on the number of iterations. We recommend to set moderate non-zero values EpsOrt and EpsErr simultaneously. Values equal to 10E-6 are good to start with. In case you need high performance and do not need high precision , you may decrease EpsErr down to 0.001. However, we do not recommend decreasing EpsOrt. As for MaxIts, we recommend to leave it zero unless you know what you do. NOTE: this function has some serialization-related subtleties. We recommend you to study serialization examples from ALGLIB Reference Manual if you want to perform serialization of your models. -- ALGLIB -- Copyright 13.12.2011 by Bochkanov Sergey *************************************************************************/ public static void rbfsetcond(rbfmodel s, double epsort, double epserr, int maxits) { alglib.ap.assert(math.isfinite(epsort) && (double)(epsort)>=(double)(0), "RBFSetCond: EpsOrt is negative, INF or NAN"); alglib.ap.assert(math.isfinite(epserr) && (double)(epserr)>=(double)(0), "RBFSetCond: EpsB is negative, INF or NAN"); alglib.ap.assert(maxits>=0, "RBFSetCond: MaxIts is negative"); if( ((double)(epsort)==(double)(0) && (double)(epserr)==(double)(0)) && maxits==0 ) { s.epsort = eps; s.epserr = eps; s.maxits = 0; } else { s.epsort = epsort; s.epserr = epserr; s.maxits = maxits; } }
/************************************************************************* Serializer: unserialization -- ALGLIB -- Copyright 02.02.2012 by Bochkanov Sergey *************************************************************************/ public static void rbfunserialize(alglib.serializer s, rbfmodel model) { int i0 = 0; int i1 = 0; int nx = 0; int ny = 0; // // Header // i0 = s.unserialize_int(); alglib.ap.assert(i0==scodes.getrbfserializationcode(), "RBFUnserialize: stream header corrupted"); i1 = s.unserialize_int(); alglib.ap.assert(i1==rbffirstversion, "RBFUnserialize: stream header corrupted"); // // Unserialize primary model parameters, initialize model. // // It is necessary to call RBFCreate() because some internal fields // which are NOT unserialized will need initialization. // nx = s.unserialize_int(); ny = s.unserialize_int(); rbfcreate(nx, ny, model); model.nc = s.unserialize_int(); model.nl = s.unserialize_int(); nearestneighbor.kdtreeunserialize(s, model.tree); apserv.unserializerealmatrix(s, ref model.xc); apserv.unserializerealmatrix(s, ref model.wr); model.rmax = s.unserialize_double(); apserv.unserializerealmatrix(s, ref model.v); }
/************************************************************************* This function builds RBF model and returns report (contains some information which can be used for evaluation of the algorithm properties). Call to this function modifies RBF model by calculating its centers/radii/ weights and saving them into RBFModel structure. Initially RBFModel contain zero coefficients, but after call to this function we will have coefficients which were calculated in order to fit our dataset. After you called this function you can call RBFCalc(), RBFGridCalc() and other model calculation functions. INPUT PARAMETERS: S - RBF model, initialized by RBFCreate() call Rep - report: * Rep.TerminationType: * -5 - non-distinct basis function centers were detected, interpolation aborted * -4 - nonconvergence of the internal SVD solver * 1 - successful termination Fields are used for debugging purposes: * Rep.IterationsCount - iterations count of the LSQR solver * Rep.NMV - number of matrix-vector products * Rep.ARows - rows count for the system matrix * Rep.ACols - columns count for the system matrix * Rep.ANNZ - number of significantly non-zero elements (elements above some algorithm-determined threshold) NOTE: failure to build model will leave current state of the structure unchanged. -- ALGLIB -- Copyright 13.12.2011 by Bochkanov Sergey *************************************************************************/ public static void rbfbuildmodel(rbfmodel s, rbfreport rep) { nearestneighbor.kdtree tree = new nearestneighbor.kdtree(); nearestneighbor.kdtree ctree = new nearestneighbor.kdtree(); double[] dist = new double[0]; double[] xcx = new double[0]; double[,] a = new double[0,0]; double[,] v = new double[0,0]; double[,] omega = new double[0,0]; double[] y = new double[0]; double[,] residualy = new double[0,0]; double[] radius = new double[0]; double[,] xc = new double[0,0]; double[] mnx = new double[0]; double[] mxx = new double[0]; double[] edge = new double[0]; int[] mxsteps = new int[0]; int nc = 0; double rmax = 0; int[] tags = new int[0]; int[] ctags = new int[0]; int i = 0; int j = 0; int k = 0; int k2 = 0; int snnz = 0; double[] tmp0 = new double[0]; double[] tmp1 = new double[0]; int layerscnt = 0; alglib.ap.assert(s.nx==2 || s.nx==3, "RBFBuildModel: S.NX<>2 or S.NX<>3!"); // // Quick exit when we have no points // if( s.n==0 ) { rep.terminationtype = 1; rep.iterationscount = 0; rep.nmv = 0; rep.arows = 0; rep.acols = 0; nearestneighbor.kdtreebuildtagged(s.xc, tags, 0, mxnx, 0, 2, s.tree); s.xc = new double[0, 0]; s.wr = new double[0, 0]; s.nc = 0; s.rmax = 0; s.v = new double[s.ny, mxnx+1]; for(i=0; i<=s.ny-1; i++) { for(j=0; j<=mxnx; j++) { s.v[i,j] = 0; } } return; } // // General case, N>0 // rep.annz = 0; rep.iterationscount = 0; rep.nmv = 0; xcx = new double[mxnx]; // // First model in a sequence - linear model. // Residuals from linear regression are stored in the ResidualY variable // (used later to build RBF models). // residualy = new double[s.n, s.ny]; for(i=0; i<=s.n-1; i++) { for(j=0; j<=s.ny-1; j++) { residualy[i,j] = s.y[i,j]; } } if( !buildlinearmodel(s.x, ref residualy, s.n, s.ny, s.aterm, ref v) ) { rep.terminationtype = -5; return; } // // Handle special case: multilayer model with NLayers=0. // Quick exit. // if( s.algorithmtype==2 && s.nlayers==0 ) { rep.terminationtype = 1; rep.iterationscount = 0; rep.nmv = 0; rep.arows = 0; rep.acols = 0; nearestneighbor.kdtreebuildtagged(s.xc, tags, 0, mxnx, 0, 2, s.tree); s.xc = new double[0, 0]; s.wr = new double[0, 0]; s.nc = 0; s.rmax = 0; s.v = new double[s.ny, mxnx+1]; for(i=0; i<=s.ny-1; i++) { for(j=0; j<=mxnx; j++) { s.v[i,j] = v[i,j]; } } return; } // // Second model in a sequence - RBF term. // // NOTE: assignments below are not necessary, but without them // MSVC complains about unitialized variables. // nc = 0; rmax = 0; layerscnt = 0; if( s.algorithmtype==1 ) { // // Add RBF model. // This model uses local KD-trees to speed-up nearest neighbor searches. // if( s.gridtype==1 ) { mxx = new double[s.nx]; mnx = new double[s.nx]; mxsteps = new int[s.nx]; edge = new double[s.nx]; for(i=0; i<=s.nx-1; i++) { mxx[i] = s.x[0,i]; mnx[i] = s.x[0,i]; } for(i=0; i<=s.n-1; i++) { for(j=0; j<=s.nx-1; j++) { if( (double)(mxx[j])<(double)(s.x[i,j]) ) { mxx[j] = s.x[i,j]; } if( (double)(mnx[j])>(double)(s.x[i,j]) ) { mnx[j] = s.x[i,j]; } } } for(i=0; i<=s.nx-1; i++) { mxsteps[i] = (int)((mxx[i]-mnx[i])/(2*s.h))+1; edge[i] = (mxx[i]+mnx[i])/2-s.h*mxsteps[i]; } nc = 1; for(i=0; i<=s.nx-1; i++) { mxsteps[i] = 2*mxsteps[i]+1; nc = nc*mxsteps[i]; } xc = new double[nc, mxnx]; if( s.nx==2 ) { for(i=0; i<=mxsteps[0]-1; i++) { for(j=0; j<=mxsteps[1]-1; j++) { for(k2=0; k2<=mxnx-1; k2++) { xc[i*mxsteps[1]+j,k2] = 0; } xc[i*mxsteps[1]+j,0] = edge[0]+s.h*i; xc[i*mxsteps[1]+j,1] = edge[1]+s.h*j; } } } if( s.nx==3 ) { for(i=0; i<=mxsteps[0]-1; i++) { for(j=0; j<=mxsteps[1]-1; j++) { for(k=0; k<=mxsteps[2]-1; k++) { for(k2=0; k2<=mxnx-1; k2++) { xc[i*mxsteps[1]+j,k2] = 0; } xc[(i*mxsteps[1]+j)*mxsteps[2]+k,0] = edge[0]+s.h*i; xc[(i*mxsteps[1]+j)*mxsteps[2]+k,1] = edge[1]+s.h*j; xc[(i*mxsteps[1]+j)*mxsteps[2]+k,2] = edge[2]+s.h*k; } } } } } else { if( s.gridtype==2 ) { nc = s.n; xc = new double[nc, mxnx]; for(i=0; i<=nc-1; i++) { for(j=0; j<=mxnx-1; j++) { xc[i,j] = s.x[i,j]; } } } else { if( s.gridtype==3 ) { nc = s.nc; xc = new double[nc, mxnx]; for(i=0; i<=nc-1; i++) { for(j=0; j<=mxnx-1; j++) { xc[i,j] = s.xc[i,j]; } } } else { alglib.ap.assert(false, "RBFBuildModel: either S.GridType<1 or S.GridType>3!"); } } } rmax = 0; radius = new double[nc]; ctags = new int[nc]; for(i=0; i<=nc-1; i++) { ctags[i] = i; } nearestneighbor.kdtreebuildtagged(xc, ctags, nc, mxnx, 0, 2, ctree); if( s.fixrad ) { // // Fixed radius // for(i=0; i<=nc-1; i++) { radius[i] = s.radvalue; } rmax = radius[0]; } else { // // Dynamic radius // if( nc==0 ) { rmax = 1; } else { if( nc==1 ) { radius[0] = s.radvalue; rmax = radius[0]; } else { // // NC>1, calculate radii using distances to nearest neigbors // for(i=0; i<=nc-1; i++) { for(j=0; j<=mxnx-1; j++) { xcx[j] = xc[i,j]; } if( nearestneighbor.kdtreequeryknn(ctree, xcx, 1, false)>0 ) { nearestneighbor.kdtreequeryresultsdistances(ctree, ref dist); radius[i] = s.radvalue*dist[0]; } else { // // No neighbors found (it will happen when we have only one center). // Initialize radius with default value. // radius[i] = 1.0; } } // // Apply filtering // apserv.rvectorsetlengthatleast(ref tmp0, nc); for(i=0; i<=nc-1; i++) { tmp0[i] = radius[i]; } tsort.tagsortfast(ref tmp0, ref tmp1, nc); for(i=0; i<=nc-1; i++) { radius[i] = Math.Min(radius[i], s.radzvalue*tmp0[nc/2]); } // // Calculate RMax, check that all radii are non-zero // for(i=0; i<=nc-1; i++) { rmax = Math.Max(rmax, radius[i]); } for(i=0; i<=nc-1; i++) { if( (double)(radius[i])==(double)(0) ) { rep.terminationtype = -5; return; } } } } } apserv.ivectorsetlengthatleast(ref tags, s.n); for(i=0; i<=s.n-1; i++) { tags[i] = i; } nearestneighbor.kdtreebuildtagged(s.x, tags, s.n, mxnx, 0, 2, tree); buildrbfmodellsqr(s.x, ref residualy, xc, radius, s.n, nc, s.ny, tree, ctree, s.epsort, s.epserr, s.maxits, ref rep.annz, ref snnz, ref omega, ref rep.terminationtype, ref rep.iterationscount, ref rep.nmv); layerscnt = 1; } else { if( s.algorithmtype==2 ) { rmax = s.radvalue; buildrbfmlayersmodellsqr(s.x, ref residualy, ref xc, s.radvalue, ref radius, s.n, ref nc, s.ny, s.nlayers, ctree, 1.0E-6, 1.0E-6, 50, s.lambdav, ref rep.annz, ref omega, ref rep.terminationtype, ref rep.iterationscount, ref rep.nmv); layerscnt = s.nlayers; } else { alglib.ap.assert(false, "RBFBuildModel: internal error(AlgorithmType neither 1 nor 2)"); } } if( rep.terminationtype<=0 ) { return; } // // Model is built // s.nc = nc/layerscnt; s.rmax = rmax; s.nl = layerscnt; s.xc = new double[s.nc, mxnx]; s.wr = new double[s.nc, 1+s.nl*s.ny]; s.v = new double[s.ny, mxnx+1]; for(i=0; i<=s.nc-1; i++) { for(j=0; j<=mxnx-1; j++) { s.xc[i,j] = xc[i,j]; } } apserv.ivectorsetlengthatleast(ref tags, s.nc); for(i=0; i<=s.nc-1; i++) { tags[i] = i; } nearestneighbor.kdtreebuildtagged(s.xc, tags, s.nc, mxnx, 0, 2, s.tree); for(i=0; i<=s.nc-1; i++) { s.wr[i,0] = radius[i]; for(k=0; k<=layerscnt-1; k++) { for(j=0; j<=s.ny-1; j++) { s.wr[i,1+k*s.ny+j] = omega[k*s.nc+i,j]; } } } for(i=0; i<=s.ny-1; i++) { for(j=0; j<=mxnx; j++) { s.v[i,j] = v[i,j]; } } rep.terminationtype = 1; rep.arows = s.n; rep.acols = s.nc; }
/************************************************************************* This function changes radii calculation algorithm to one which makes radius for I-th node equal to R[i]=DistNN[i]*Q, where: * R[i] is a radius calculated by the algorithm * DistNN[i] is distance from I-th center to its nearest neighbor center * Q is a scale parameter, which should be within [0.75,1.50], with recommended value equal to 1.0 * after performing radii calculation, radii are transformed in order to avoid situation when single outlier has very large radius and influences many points across entire dataset. Transformation has following form: new_r[i] = min(r[i],Z*median(r[])) where r[i] is I-th radius, median() is a median radius across entire dataset, Z is user-specified value which controls amount of deviation from median radius. This function won't have effect until next call to RBFBuildModel(). The idea behind this algorithm is to choose radii corresponding to basis functions is such way that I-th radius is approximately equal to distance from I-th center to its nearest neighbor. In this case interactions with distant points will be insignificant, and we will get well conditioned basis. Properties of this basis depend on the value of Q: * Q<0.75 will give perfectly conditioned basis, but terrible smoothness properties (RBF interpolant will have sharp peaks around function values) * Q>1.5 will lead to badly conditioned systems and slow convergence of the underlying linear solver (although smoothness will be very good) * Q around 1.0 gives good balance between smoothness and condition number INPUT PARAMETERS: S - RBF model, initialized by RBFCreate() call Q - radius coefficient, Q>0 Z - z-parameter, Z>0 Default value of Q is equal to 1.0 Default value of Z is equal to 5.0 NOTE: this function has some serialization-related subtleties. We recommend you to study serialization examples from ALGLIB Reference Manual if you want to perform serialization of your models. -- ALGLIB -- Copyright 13.12.2011 by Bochkanov Sergey *************************************************************************/ private static void rbfradnn(rbfmodel s, double q, double z) { alglib.ap.assert(math.isfinite(q) && (double)(q)>(double)(0), "RBFRadNN: Q<=0, infinite or NAN"); alglib.ap.assert(math.isfinite(z) && (double)(z)>(double)(0), "RBFRadNN: Z<=0, infinite or NAN"); s.fixrad = false; s.radvalue = q; s.radzvalue = z; }
/************************************************************************* This function changes centers allocation algorithm to one which allocates centers on a regular grid with step equal to H in all dimensions. This function won't have effect until next call to RBFBuildModel(). Central point of the grid is located exactly in the middle of the bounding rectangle for our dataset. Grid countinues from the center to the bounds of the bounding rectangle, and makes one step beyond bounds (i.e. leftmost/rightmost/topmost/... lines of the grid are located just outside of the bounding rectangle). INPUT PARAMETERS: S - RBF model, initialized by RBFCreate() call. H - grid step, H>0 NOTE: this function has some serialization-related subtleties. We recommend you to study serialization examples from ALGLIB Reference Manual if you want to perform serialization of your models. -- ALGLIB -- Copyright 13.12.2011 by Bochkanov Sergey *************************************************************************/ private static void rbfgridregular(rbfmodel s, double h) { alglib.ap.assert(math.isfinite(h) && (double)(h)>(double)(0), "RBFGridRegular: H<0, INF or NAN"); s.h = h; s.gridtype = 1; }