0001 classdef RBFInterpolant < Interpolant
0002
0003
0004 properties
0005 weights;
0006 poly;
0007 distFun;
0008 rbfFun;
0009 end
0010
0011 methods
0012 function obj = RBFInterpolant(x, y, z, varargin)
0013
0014
0015
0016
0017 validDistanceType = @(x) ischar(x) && strcmpi(x, 'euclidean') || strcmpi(x, 'haversine');
0018 validPolynomialDegree = @(x) isscalar(x) && x <= 3;
0019 validRBFType = @(x) isa(x, 'function_handle') || ...
0020 (ischar(x) && strcmpi(x, 'linear') || ...
0021 strcmpi(x, 'cubic') || ...
0022 strcmpi(x, 'quintic') || ...
0023 strcmpi(x, 'multiquadric') || ...
0024 strcmpi(x, 'inversemultiquadric') || ...
0025 strcmpi(x, 'thinplate') || ...
0026 strcmpi(x, 'green') || ...
0027 strcmpi(x, 'greenwithtension') || ...
0028 strcmpi(x, 'greenregularizedwithtension') || ...
0029 strcmpi(x, 'tensionspline') || ...
0030 strcmpi(x, 'regularizedspline') || ...
0031 strcmpi(x, 'gaussian') || ...
0032 strcmpi(x, 'wendland'));
0033 validScalarLogical = @(x) isscalar(x) && islogical(x);
0034
0035
0036 p = inputParser;
0037 addParameter(p, 'DistanceType', 'euclidean', validDistanceType);
0038 addParameter(p, 'PolynomialDegree', 0, validPolynomialDegree);
0039 addParameter(p, 'RBF', 'multiquadric', validRBFType);
0040 addParameter(p, 'RBFEpsilon', 0, @isscalar);
0041 addParameter(p, 'RBFEpsilonIsNormalized', false, validScalarLogical);
0042 addParameter(p, 'Regularization', 0, @isscalar);
0043 parse(p, varargin{:});
0044
0045
0046 epsilon = p.Results.RBFEpsilon;
0047 if p.Results.RBFEpsilonIsNormalized
0048 if epsilon < 0 || epsilon > 1
0049 error('RBFEpsilonIsNormalized parameter is set to true, but RBFEpsilon is not between 0 and 1!');
0050 end
0051 epsilon = scaleEpsilonAccordingToRbfType(p.Results.RBF, epsilon, x, y, z);
0052 end
0053
0054
0055 obj@Interpolant(x, y, z);
0056
0057
0058 distanceType = p.Results.DistanceType;
0059 if strcmpi(distanceType, 'euclidean')
0060 obj.distFun = @(x, y) vecnorm(x-y, 2, 2);
0061 elseif strcmpi(distanceType, 'haversine')
0062 obj.distFun = @haversine;
0063 else
0064 error('Unknown DistanceType');
0065 end
0066
0067
0068 obj.rbfFun = rbfTypeToFunctor(p.Results.RBF, epsilon);
0069
0070
0071 [obj.weights, obj.poly] = RBFInterpolant.solveWeightsAndPoly(x, y, z, obj.distFun, obj.rbfFun, p.Results.PolynomialDegree, p.Results.Regularization);
0072 end
0073
0074 function z = interpolate(obj, x, y)
0075
0076
0077
0078
0079 Interpolant.checkSizes(x, y);
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
0103
0104 dists = pdist2([x(:) y(:)], obj.data(:, 1:2), obj.distFun);
0105
0106
0107 A = obj.rbfFun(dists);
0108
0109
0110
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120
0121 polyEval = bivariatePolynomialEval(obj.poly, x(:), y(:));
0122
0123 z = A*obj.weights + polyEval;
0124 z = reshape(z, size(x));
0125 end
0126 end
0127
0128 methods (Static)
0129 function [weights, poly] = solveWeightsAndPoly(x, y, z, distFun, rbfFun, polynomialDegree, regularizationCoeff)
0130
0131
0132
0133
0134 if numel(x) ~= numel(y) || numel(x) ~= numel(z)
0135 error('The number of elements in x, y and z must be the same');
0136 end
0137 numSamples = size(x, 1);
0138
0139
0140
0141
0142
0143
0144
0145
0146
0147
0148 dists = pdist([x y], distFun);
0149
0150
0151 rbfEvals = rbfFun(dists);
0152
0153
0154
0155 A = zeros(numSamples, numSamples);
0156 A(tril(true(numSamples, numSamples), -1)) = rbfEvals;
0157 A = A + A';
0158
0159 A(logical(eye(numSamples))) = rbfFun(0);
0160
0161
0162 if regularizationCoeff ~= 0
0163 A = A + eye(size(A, 1))*regularizationCoeff;
0164 end
0165
0166 b = z(:);
0167
0168
0169 terms = bivariatePolynomialTerms(polynomialDegree, x(:), y(:));
0170 numTerms = size(terms, 2);
0171 A(:, end+1:end+numTerms) = terms;
0172 A(end+1:end+numTerms, :) = [terms' zeros(numTerms, numTerms)];
0173 b(end+1:end+numTerms) = 0;
0174
0175
0176 solution = A\b;
0177
0178
0179 weights = solution(1:numSamples);
0180 if polynomialDegree >= 0
0181 poly = solution(numSamples+1:end);
0182 else
0183 poly = [];
0184 end
0185 end
0186 end
0187 end
0188