libROM  v1.0
Data-driven physical simulation library
Interpolator.cpp
1 
11 // Description: Implementation of the Interpolator algorithm.
12 
13 #include "Interpolator.h"
14 
15 #include <limits.h>
16 #include <cmath>
17 #include "linalg/Matrix.h"
18 #include "linalg/scalapack_wrapper.h"
19 #include "mpi.h"
20 
21 /* Use C++11 built-in shared pointers if available; else fallback to Boost. */
22 #if __cplusplus >= 201103L
23 #include <memory>
24 #else
25 #include <boost/shared_ptr.hpp>
26 #endif
27 
28 using namespace std;
29 
30 namespace CAROM {
31 
32 Interpolator::Interpolator(const std::vector<Vector> & parameter_points,
33  const std::vector<std::shared_ptr<Matrix>> & rotation_matrices,
34  int ref_point,
35  std::string rbf,
36  std::string interp_method,
37  double closest_rbf_val,
38  bool compute_gradients)
39 {
40  CAROM_VERIFY(parameter_points.size() == rotation_matrices.size());
41  CAROM_VERIFY(parameter_points.size() > 1);
42  CAROM_VERIFY(rbf == "G" || rbf == "IQ" || rbf == "IMQ");
43  CAROM_VERIFY(interp_method == "LS" || interp_method == "IDW"
44  || interp_method == "LP");
45  CAROM_VERIFY(closest_rbf_val >= 0.0 && closest_rbf_val <= 1.0);
46 
47  // Get the rank of this process, and the number of processors.
48  int mpi_init;
49  MPI_Initialized(&mpi_init);
50  if (mpi_init == 0) {
51  MPI_Init(nullptr, nullptr);
52  }
53 
54  MPI_Comm_rank(MPI_COMM_WORLD, &d_rank);
55  MPI_Comm_size(MPI_COMM_WORLD, &d_num_procs);
56 
57  d_compute_gradients = compute_gradients;
58  d_parameter_points = parameter_points;
59  d_rotation_matrices = rotation_matrices;
60  d_ref_point = ref_point;
61  d_rbf = rbf;
62  d_interp_method = interp_method;
63  d_epsilon = convertClosestRBFToEpsilon(parameter_points, rbf, closest_rbf_val);
64 }
65 
66 std::vector<double> obtainRBFToTrainingPoints(
67  const std::vector<Vector> & parameter_points,
68  const std::string & interp_method, const std::string & rbf, double epsilon,
69  const Vector & point)
70 {
71  std::vector<double> rbfs;
72  if (interp_method == "LS")
73  {
74  for (int i = 0; i < parameter_points.size(); i++)
75  {
76  rbfs.push_back(obtainRBF(rbf, epsilon, point, parameter_points[i]));
77  }
78  }
79  else if (interp_method == "IDW")
80  {
81  bool distance_is_zero = false;
82  for (int i = 0; i < parameter_points.size(); i++)
83  {
84  rbfs.push_back(obtainRBF(rbf, epsilon, point, parameter_points[i]));
85  if (rbfs.back() == 1.0)
86  {
87  distance_is_zero = true;
88  }
89  }
90  if (distance_is_zero)
91  {
92  for (int i = 0; i < rbfs.size(); i++)
93  {
94  if (rbfs[i] != 1.0)
95  {
96  rbfs[i] = 0.0;
97  }
98  }
99  }
100  }
101  else if (interp_method == "LP")
102  {
103  for (int i = 0; i < parameter_points.size(); i++)
104  {
105  double coeff;
106  bool first = true;
107  for (int j = 0; j < parameter_points.size(); j++)
108  {
109  if (i == j)
110  {
111  continue;
112  }
113 
114  Vector numerator_vec, denomenator_vec;
115  point.minus(parameter_points[j], numerator_vec);
116  parameter_points[i].minus(parameter_points[j], denomenator_vec);
117  double numerator = numerator_vec.norm();
118  double denomenator = denomenator_vec.norm();
119 
120  if (first)
121  {
122  coeff = (numerator / denomenator);
123  first = false;
124  }
125  else
126  {
127  coeff *= (numerator / denomenator);
128  }
129  }
130  rbfs.push_back(coeff);
131  }
132  }
133  return rbfs;
134 }
135 
137  const std::vector<Vector> & parameter_points,
138  const std::string & interp_method, const std::string & rbf, double epsilon,
139  const Vector & point, const int index)
140 {
141  std::vector<double> rbfs;
142  if (interp_method == "LS")
143  {
144  for (int i = 0; i < parameter_points.size(); i++)
145  {
146  rbfs.push_back(obtainRBFGradient(rbf, epsilon, point, parameter_points[i],
147  index));
148  }
149  }
150  else
151  {
152  CAROM_ERROR("Interpolated gradients are only implemented for \"LS\"");
153  }
154  return rbfs;
155 }
156 
157 double obtainRBFGradient(std::string rbf, double epsilon, const Vector & point1,
158  const Vector & point2, const int index)
159 {
160  Vector diff;
161  point1.minus(point2, diff);
162  const double eps_norm_squared = epsilon * epsilon * diff.norm2();
163  double res = 0.0;
164 
165  // Gaussian RBF
166  if (rbf == "G")
167  {
168  //Derivative of Gaussian RBF, res = std::exp(-eps_norm_squared);
169  res = -2.0*epsilon*epsilon*diff.item(index)*std::exp(-eps_norm_squared);
170  }
171  // Inverse quadratic RBF
172  else if (rbf == "IQ")
173  {
174  //Derivative of Inverse Quadratic RBF, res = 1.0 / (1.0 + eps_norm_squared);
175  res = -2.0*epsilon*epsilon*diff.item(index)/((1.0+eps_norm_squared)*
176  (1.0+eps_norm_squared));
177 
178  }
179  // Inverse multiquadric RBF
180  else if (rbf == "IMQ")
181  {
182  //Derivative of Inverse multiquadritic RBF, res = 1.0 / std::sqrt(1.0 + eps_norm_squared);
183  res = -epsilon*epsilon*diff.item(index)/(std::sqrt(1.0+eps_norm_squared)*
184  (1.0+eps_norm_squared));
185  }
186 
187  return res;
188 }
189 
190 double rbfWeightedSum(std::vector<double>& rbf)
191 {
192  double sum = 0.0;
193  for (int i = 0; i < rbf.size(); i++)
194  {
195  sum += rbf[i];
196  }
197  return sum;
198 }
199 
200 double obtainRBF(std::string rbf, double epsilon, const Vector & point1,
201  const Vector & point2)
202 {
203  Vector diff;
204  point1.minus(point2, diff);
205  const double eps_norm_squared = epsilon * epsilon * diff.norm2();
206  double res = 0.0;
207 
208  // Gaussian RBF
209  if (rbf == "G")
210  {
211  res = std::exp(-eps_norm_squared);
212  }
213  // Inverse quadratic RBF
214  else if (rbf == "IQ")
215  {
216  res = 1.0 / (1.0 + eps_norm_squared);
217  }
218  // Inverse multiquadric RBF
219  else if (rbf == "IMQ")
220  {
221  res = 1.0 / std::sqrt(1.0 + eps_norm_squared);
222  }
223 
224  return res;
225 }
226 
227 double convertClosestRBFToEpsilon(const std::vector<Vector> & parameter_points,
228  const std::string & rbf, double closest_rbf_val)
229 {
230  double closest_point_dist = INT_MAX;
231  double epsilon;
232  for (int i = 0; i < parameter_points.size(); i++)
233  {
234  for (int j = 0; j < parameter_points.size(); j++)
235  {
236  if (i == j)
237  {
238  continue;
239  }
240 
241  Vector diff;
242  parameter_points[i].minus(parameter_points[j], diff);
243  double dist = diff.norm2();
244  if (dist < closest_point_dist)
245  {
246  closest_point_dist = dist;
247 
248  // Gaussian RBF
249  if (rbf == "G")
250  {
251  epsilon = std::sqrt(-std::log(closest_rbf_val) / diff.norm2());
252  }
253  // Inverse quadratic RBF
254  else if (rbf == "IQ")
255  {
256  epsilon = std::sqrt(((1.0 / closest_rbf_val) - 1.0) / diff.norm2());
257  }
258  // Inverse multiquadric RBF
259  else if (rbf == "IMQ")
260  {
261  epsilon = std::sqrt((std::pow(1.0 / closest_rbf_val, 2) - 1.0) / diff.norm2());
262  }
263  }
264  }
265  }
266 
267  return epsilon;
268 }
269 
270 std::vector<std::shared_ptr<Matrix>> obtainRotationMatrices(
271  const std::vector<Vector> & parameter_points,
272  const std::vector<std::shared_ptr<Matrix>> & bases,
273  int ref_point)
274 {
275  // Get the rank of this process, and the number of processors.
276  int mpi_init;
277  int rank;
278  int num_procs;
279  MPI_Initialized(&mpi_init);
280  if (mpi_init == 0) {
281  MPI_Init(nullptr, nullptr);
282  }
283 
284  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
285  MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
286 
287  std::vector<std::shared_ptr<Matrix>> rotation_matrices;
288 
289  // Obtain the rotation matrices to rotate the bases into
290  // the same generalized coordinate space.
291  for (int i = 0; i < parameter_points.size(); i++)
292  {
293  CAROM_VERIFY(bases[i]->numRows() == bases[ref_point]->numRows());
294  CAROM_VERIFY(bases[i]->numColumns() == bases[ref_point]->numColumns());
295  CAROM_VERIFY(bases[i]->distributed() == bases[ref_point]->distributed());
296 
297  // If at ref point, the rotation_matrix is the identity matrix
298  // since the ref point doesn't need to be rotated.
299  if (i == ref_point)
300  {
301  std::shared_ptr<Matrix> identity_matrix(new Matrix(bases[i]->numColumns(),
302  bases[i]->numColumns(), false));
303  for (int j = 0; j < identity_matrix->numColumns(); j++) {
304  identity_matrix->item(j, j) = 1.0;
305  }
306  rotation_matrices.push_back(identity_matrix);
307  continue;
308  }
309 
310  std::unique_ptr<Matrix> basis_mult_basis = bases[i]->transposeMult(
311  *bases[ref_point]);
312  Matrix basis(basis_mult_basis->numRows(),
313  basis_mult_basis->numColumns(), false);
314  Matrix basis_right(basis_mult_basis->numColumns(),
315  basis_mult_basis->numColumns(), false);
316 
317  // We need to compute the SVD of basis_mult_basis. Since it is
318  // undistributed due to the transposeMult, let's use lapack's serial SVD
319  // on rank 0 only.
320  if (rank == 0)
321  {
322  Vector sv(basis_mult_basis->numColumns(), false);
323  SerialSVD(basis_mult_basis.get(), &basis_right, &sv, &basis);
324  }
325 
326  // Broadcast U and V which are computed only on root.
327  MPI_Bcast(basis.getData(), basis.numRows() * basis.numColumns(), MPI_DOUBLE,
328  0, MPI_COMM_WORLD);
329  MPI_Bcast(basis_right.getData(),
330  basis_right.numRows() * basis_right.numColumns(), MPI_DOUBLE, 0,
331  MPI_COMM_WORLD);
332 
333  // Obtain the rotation matrix.
334  rotation_matrices.push_back(basis.mult(basis_right));
335  }
336 
337  return rotation_matrices;
338 }
339 
340 std::unique_ptr<std::vector<Vector>>
341  scalarsToVectors(const std::vector<double> & s)
342 {
343  std::vector<Vector> *v = new std::vector<Vector>(s.size());
344  for (int i=0; i<s.size(); ++i)
345  {
346  (*v)[i].setSize(1);
347  (*v)[i](0) = s[i];
348  }
349 
350  return std::unique_ptr<std::vector<Vector>>(v);
351 }
352 
353 }
double * getData() const
Get the matrix data as a pointer.
Definition: Matrix.h:844
int numColumns() const
Returns the number of columns in the Matrix. This method will return the same value from each process...
Definition: Matrix.h:226
int numRows() const
Returns the number of rows of the Matrix on this processor.
Definition: Matrix.h:200
std::unique_ptr< Matrix > mult(const Matrix &other) const
Multiplies this Matrix with other and returns the product.
Definition: Matrix.h:273
double norm() const
Form the norm of this.
Definition: Vector.cpp:216
std::unique_ptr< Vector > minus(const Vector &other) const
Subtracts other and this and returns the result.
Definition: Vector.h:402
const double & item(int i) const
Const Vector member access.
Definition: Vector.h:468
double norm2() const
Form the squared norm of this.
Definition: Vector.cpp:223
double rbfWeightedSum(std::vector< double > &rbf)
Compute the sum of the RBF weights.
std::vector< std::shared_ptr< Matrix > > obtainRotationMatrices(const std::vector< Vector > &parameter_points, const std::vector< std::shared_ptr< Matrix >> &bases, int ref_point)
Obtain the rotation matrices for all the parameter points using the basis of the reference point.
std::vector< double > obtainRBFGradientToTrainingPoints(const std::vector< Vector > &parameter_points, const std::string &interp_method, const std::string &rbf, double epsilon, const Vector &point, const int index)
Compute the RBF gradient from the parameter points with the unsampled parameter point.
std::vector< double > obtainRBFToTrainingPoints(const std::vector< Vector > &parameter_points, const std::string &interp_method, const std::string &rbf, double epsilon, const Vector &point)
Compute the RBF from the parameter points with the unsampled parameter point.
double convertClosestRBFToEpsilon(const std::vector< Vector > &parameter_points, const std::string &rbf, double closest_rbf_val)
Convert closest RBF value to an epsilon value.
void SerialSVD(Matrix *A, Matrix *U, Vector *S, Matrix *V)
Computes the SVD of a undistributed matrix in serial.
Definition: Matrix.cpp:2107
double obtainRBF(std::string rbf, double epsilon, const Vector &point1, const Vector &point2)
Compute the RBF between two points.
double obtainRBFGradient(std::string rbf, double epsilon, const Vector &point1, const Vector &point2, const int index)
Compute the RBF gradient between two points.