View Javadoc
1   /*
2    * Copyright (C) 2023 Alberto Irurueta Carro (alberto@irurueta.com)
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package com.irurueta.numerical.interpolation;
17  
18  import com.irurueta.algebra.AlgebraException;
19  import com.irurueta.algebra.LUDecomposer;
20  import com.irurueta.algebra.Matrix;
21  import com.irurueta.algebra.WrongSizeException;
22  
23  /**
24   * Interpolates sparsely defined points of dimension "dim" using a Radial Basis Function.
25   */
26  public class RadialBasisFunctionInterpolator extends BaseRadialBasisFunctionInterpolator {
27      /**
28       * Computed weights to compute interpolation from provided points.
29       */
30      private final Matrix w;
31  
32      /**
33       * Radial basis function defining a value based on the distance of two points.
34       */
35      private final RadialBasisFunction fn;
36  
37      /**
38       * Indicates whether normalized Radial Basis Function (RBF) must be used or not.
39       */
40      private final boolean norm;
41  
42      /**
43       * Constructor.
44       *
45       * @param ptss  Matrix containing points to interpolate from. Each row contains one point.
46       *              Matrix will have n points (rows) having a dimension (columns) equal to dim.
47       * @param valss values of function at provided points. Must have the same length as the number
48       *              of rows of provided points matrix.
49       * @param func  function to be used as Radial Basis Function (RBF).
50       * @param nrbf  true to normalize RBF, false otherwise.
51       * @throws InterpolationException   if provided points are redundant and result in a degenerate
52       *                                  solution.
53       * @throws IllegalArgumentException if provided values array does not match the number of
54       *                                  points (rows) in provided matrix.
55       */
56      public RadialBasisFunctionInterpolator(
57              final Matrix ptss, final double[] valss, final RadialBasisFunction func, final boolean nrbf)
58              throws InterpolationException {
59          super(ptss);
60  
61          if (valss.length != n) {
62              throw new IllegalArgumentException("wrong length of values");
63          }
64  
65          try {
66              w = new Matrix(n, 1);
67              fn = func;
68              norm = nrbf;
69  
70              final var pj = new double[dim];
71  
72              int i;
73              int j;
74              double sum;
75              final var rbf = new Matrix(n, n);
76              final var rhs = new Matrix(n, 1);
77              for (i = 0; i < n; i++) {
78                  // Fill the matrix phi(|ri - rj|) and the right hand sisde (rhs) vector
79                  sum = 0.;
80                  for (j = 0; j < n; j++) {
81                      final var endCol = dim - 1;
82                      pts.getSubmatrixAsArray(i, 0, i, endCol, pi);
83                      pts.getSubmatrixAsArray(j, 0, j, endCol, pj);
84                      final var value = fn.evaluate(rad(pi, pj));
85                      rbf.setElementAt(i, j, value);
86                      sum += value;
87                  }
88  
89                  if (norm) {
90                      rhs.setElementAtIndex(i, sum * valss[i]);
91                  } else {
92                      rhs.setElementAtIndex(i, valss[i]);
93                  }
94              }
95  
96              // Solve the set of linear equations
97              final var lu = new LUDecomposer(rbf);
98              lu.decompose();
99              lu.solve(rhs, w);
100         } catch (final AlgebraException e) {
101             throw new InterpolationException(e);
102         }
103     }
104 
105     /**
106      * Constructor.
107      *
108      * @param ptss  Matrix containing points to interpolate from. Each row contains one point.
109      *              Matrix will have n points (rows) having a dimension (columns) equal to dim.
110      * @param valss values of function at provided points. Must have the same length as the number
111      *              of rows of provided points matrix.
112      * @param func  function to be used as Radial Basis Function (RBF).
113      * @throws InterpolationException   if provided points are redundant and result in a degenerate
114      *                                  solution.
115      * @throws IllegalArgumentException if provided values array does not match the number of
116      *                                  points (rows) in provided matrix.
117      */
118     public RadialBasisFunctionInterpolator(
119             final Matrix ptss, final double[] valss, final RadialBasisFunction func) throws InterpolationException {
120         this(ptss, valss, func, false);
121     }
122 
123     /**
124      * Returns the interpolated function value at a dim-dimensional point pt.
125      *
126      * @param pt dim-dimensional point where interpolation must be computed.
127      * @return result of interpolation.
128      * @throws IllegalArgumentException if provided point has an invalid length.
129      */
130     @Override
131     public double interpolate(final double[] pt) {
132         if (pt.length != dim) {
133             throw new IllegalArgumentException("Wrong point length");
134         }
135 
136         double fval;
137         var sum = 0.0;
138         var sumw = 0.0;
139 
140         try {
141             for (var i = 0; i < n; i++) {
142                 final var endCol = dim - 1;
143                 pts.getSubmatrixAsArray(i, 0, i, endCol, pi);
144                 fval = fn.evaluate(rad(pt, pi));
145                 sumw += w.getElementAtIndex(i) * fval;
146                 sum += fval;
147             }
148         } catch (final WrongSizeException ignore) {
149             // never happens
150         }
151 
152         return norm ? sumw / sum : sumw;
153     }
154 }