Janus 2.0.0
High-performance C++20 dual-mode numerical framework
Loading...
Searching...
No Matches
ScatteredInterpolator.hpp
Go to the documentation of this file.
1#pragma once
7
12#include "janus/math/Linalg.hpp"
13#include <cmath>
14#include <vector>
15
16namespace janus {
17
18// ============================================================================
19// RBF Kernel Types
20// ============================================================================
21
32
33// ============================================================================
34// RBF Kernel Evaluation
35// ============================================================================
36
37namespace detail {
38
40constexpr double rbf_regularization = 1e-10;
42constexpr double rbf_zero_threshold = 1e-15;
43
52inline double rbf_phi(double r, RBFKernel kernel, double epsilon = 1.0) {
53 switch (kernel) {
55 // φ(r) = r² log(r), with φ(0) = 0
56 return (r > rbf_zero_threshold) ? r * r * std::log(r) : 0.0;
58 return std::sqrt(1.0 + (epsilon * r) * (epsilon * r));
60 return std::exp(-(epsilon * r) * (epsilon * r));
62 return r;
64 return r * r * r;
65 default:
66 return r * r * std::log(r + rbf_zero_threshold);
67 }
68}
69
73inline double euclidean_distance(const double *p1, const double *p2, int dims) {
74 double sum = 0.0;
75 for (int d = 0; d < dims; ++d) {
76 double diff = p1[d] - p2[d];
77 sum += diff * diff;
78 }
79 return std::sqrt(sum);
80}
81
82} // namespace detail
83
84// ============================================================================
85// Scattered Interpolator Class
86// ============================================================================
87
122 private:
123 Interpolator m_gridded;
124 int m_dims = 0;
125 double m_reconstruction_error = 0.0;
126 bool m_valid = false;
127
128 // Store original data for error computation
129 NumericMatrix m_original_points;
130 NumericVector m_original_values;
131
132 public:
137
150 int grid_resolution = 20, RBFKernel kernel = RBFKernel::ThinPlateSpline,
151 double epsilon = 1.0,
153 if (points.rows() == 0 || points.cols() == 0) {
154 throw InterpolationError("ScatteredInterpolator: points cannot be empty");
155 }
156 if (points.rows() != values.size()) {
157 throw InterpolationError(
158 "ScatteredInterpolator: points rows must equal values size. Got " +
159 std::to_string(points.rows()) + " points, " + std::to_string(values.size()) +
160 " values");
161 }
162 if (grid_resolution < 2) {
163 throw InterpolationError("ScatteredInterpolator: grid_resolution must be >= 2");
164 }
165
166 m_dims = static_cast<int>(points.cols());
167 m_original_points = points;
168 m_original_values = values;
169
170 // Build uniform grid spanning data range
171 std::vector<NumericVector> grid(m_dims);
172 for (int d = 0; d < m_dims; ++d) {
173 double min_val = points.col(d).minCoeff();
174 double max_val = points.col(d).maxCoeff();
175 // Add small padding to avoid edge issues
176 double padding = 0.01 * (max_val - min_val);
177 if (padding < detail::rbf_regularization)
178 padding = 0.1; // Handle constant dimension
179 min_val -= padding;
180 max_val += padding;
181
182 grid[d] = NumericVector::LinSpaced(grid_resolution, min_val, max_val);
183 }
184
185 build_interpolator(points, values, grid, kernel, epsilon, method);
186 }
187
199 const std::vector<NumericVector> &grid_points,
200 RBFKernel kernel = RBFKernel::ThinPlateSpline, double epsilon = 1.0,
202 if (points.rows() == 0 || points.cols() == 0) {
203 throw InterpolationError("ScatteredInterpolator: points cannot be empty");
204 }
205 if (points.rows() != values.size()) {
206 throw InterpolationError("ScatteredInterpolator: points rows must equal values size");
207 }
208 if (static_cast<int>(grid_points.size()) != points.cols()) {
209 throw InterpolationError(
210 "ScatteredInterpolator: grid_points dimensions must match points columns");
211 }
212
213 m_dims = static_cast<int>(points.cols());
214 m_original_points = points;
215 m_original_values = values;
216
217 build_interpolator(points, values, grid_points, kernel, epsilon, method);
218 }
219
228 ScatteredInterpolator(const NumericVector &x, const NumericVector &y, int grid_resolution = 50,
230 if (x.size() != y.size()) {
231 throw InterpolationError("ScatteredInterpolator: x and y must have same size");
232 }
233 if (x.size() < 2) {
234 throw InterpolationError("ScatteredInterpolator: need at least 2 points");
235 }
236
237 m_dims = 1;
238 m_original_points.resize(x.size(), 1);
239 m_original_points.col(0) = x;
240 m_original_values = y;
241
242 // Build 1D grid
243 double min_val = x.minCoeff();
244 double max_val = x.maxCoeff();
245 double padding = 0.01 * (max_val - min_val);
246 if (padding < detail::rbf_regularization)
247 padding = 0.1;
248
249 std::vector<NumericVector> grid(1);
250 grid[0] = NumericVector::LinSpaced(grid_resolution, min_val - padding, max_val + padding);
251
252 build_interpolator(m_original_points, y, grid, kernel, 1.0, InterpolationMethod::Linear);
253 }
254
255 // ========================================================================
256 // Query Methods
257 // ========================================================================
258
265 template <JanusScalar Scalar> Scalar operator()(const JanusVector<Scalar> &query) const {
266 if (!m_valid) {
267 throw InterpolationError("ScatteredInterpolator: not initialized");
268 }
269 return m_gridded(query);
270 }
271
278 template <JanusScalar Scalar> Scalar operator()(const Scalar &query) const {
279 if (!m_valid) {
280 throw InterpolationError("ScatteredInterpolator: not initialized");
281 }
282 if (m_dims != 1) {
283 throw InterpolationError("ScatteredInterpolator: scalar query only valid for 1D");
284 }
285 return m_gridded(query);
286 }
287
288 // ========================================================================
289 // Properties
290 // ========================================================================
291
296 int dims() const { return m_dims; }
297
302 bool valid() const { return m_valid; }
303
308 const Interpolator &gridded() const { return m_gridded; }
309
316 double reconstruction_error() const { return m_reconstruction_error; }
317
318 private:
322 void build_interpolator(const NumericMatrix &points, const NumericVector &values,
323 const std::vector<NumericVector> &grid, RBFKernel kernel,
324 double epsilon, InterpolationMethod method) {
325 int n_points = static_cast<int>(points.rows());
326 int n_dims = static_cast<int>(points.cols());
327
328 // ====================================================================
329 // Step 1: Build RBF interpolation matrix and solve for weights
330 // ====================================================================
331 // For thin plate spline in 2D, we need polynomial terms for uniqueness
332 // General form: f(x) = Σ w_i φ(||x - x_i||) + p(x)
333 // We'll use the simpler pure RBF form for now
334
335 NumericMatrix Phi(n_points, n_points);
336 for (int i = 0; i < n_points; ++i) {
337 for (int j = 0; j < n_points; ++j) {
338 double r =
339 detail::euclidean_distance(points.row(i).data(), points.row(j).data(), n_dims);
340 Phi(i, j) = detail::rbf_phi(r, kernel, epsilon);
341 }
342 }
343
344 // Add small regularization for numerical stability
345 Phi.diagonal().array() += detail::rbf_regularization;
346
347 // Solve Phi * weights = values
348 NumericVector weights = solve(Phi, values);
349
350 // ====================================================================
351 // Step 2: Evaluate RBF at grid points
352 // ====================================================================
353 // Compute total grid size
354 int grid_size = 1;
355 for (int d = 0; d < n_dims; ++d) {
356 grid_size *= static_cast<int>(grid[d].size());
357 }
358
359 // Flatten grid values (Fortran order for Interpolator)
360 NumericVector grid_values(grid_size);
361
362 // Iterate over all grid points
363 std::vector<int> dims_sizes(n_dims);
364 for (int d = 0; d < n_dims; ++d) {
365 dims_sizes[d] = static_cast<int>(grid[d].size());
366 }
367
368 for (int flat_idx = 0; flat_idx < grid_size; ++flat_idx) {
369 // Convert flat index to multi-index (Fortran order)
370 std::vector<int> multi_idx(n_dims);
371 int remaining = flat_idx;
372 for (int d = 0; d < n_dims; ++d) {
373 multi_idx[d] = remaining % dims_sizes[d];
374 remaining /= dims_sizes[d];
375 }
376
377 // Get grid point coordinates
378 std::vector<double> grid_pt(n_dims);
379 for (int d = 0; d < n_dims; ++d) {
380 grid_pt[d] = grid[d](multi_idx[d]);
381 }
382
383 // Evaluate RBF sum at this grid point
384 double val = 0.0;
385 for (int i = 0; i < n_points; ++i) {
386 double r = detail::euclidean_distance(grid_pt.data(), points.row(i).data(), n_dims);
387 val += weights(i) * detail::rbf_phi(r, kernel, epsilon);
388 }
389 grid_values(flat_idx) = val;
390 }
391
392 // ====================================================================
393 // Step 3: Create gridded interpolator
394 // ====================================================================
395 m_gridded = Interpolator(grid, grid_values, method);
396 m_valid = true;
397
398 // ====================================================================
399 // Step 4: Compute reconstruction error
400 // ====================================================================
401 compute_reconstruction_error();
402 }
403
407 void compute_reconstruction_error() {
408 if (!m_valid)
409 return;
410
411 double sum_sq_error = 0.0;
412 int n = static_cast<int>(m_original_values.size());
413
414 for (int i = 0; i < n; ++i) {
415 NumericVector query = m_original_points.row(i).transpose();
416 double predicted = m_gridded(query);
417 double error = predicted - m_original_values(i);
418 sum_sq_error += error * error;
419 }
420
421 m_reconstruction_error = std::sqrt(sum_sq_error / n);
422 }
423};
424
425} // namespace janus
N-dimensional interpolation (linear, Hermite, B-spline, nearest).
C++20 concepts constraining valid Janus scalar types.
Custom exception hierarchy for Janus framework.
Core type aliases for numeric and symbolic Eigen/CasADi interop.
Linear algebra operations (solve, inverse, determinant, eigendecomposition, norms).
Interpolation-specific errors.
Definition JanusError.hpp:51
Definition Interpolate.hpp:489
Scalar operator()(const Scalar &query) const
Evaluate at scalar (1D only).
Definition ScatteredInterpolator.hpp:278
ScatteredInterpolator()=default
Default constructor (invalid state).
int dims() const
Get number of input dimensions.
Definition ScatteredInterpolator.hpp:296
bool valid() const
Check if interpolator is valid (initialized).
Definition ScatteredInterpolator.hpp:302
const Interpolator & gridded() const
Get underlying gridded interpolator (for inspection).
Definition ScatteredInterpolator.hpp:308
ScatteredInterpolator(const NumericVector &x, const NumericVector &y, int grid_resolution=50, RBFKernel kernel=RBFKernel::ThinPlateSpline)
1D convenience constructor
Definition ScatteredInterpolator.hpp:228
Scalar operator()(const JanusVector< Scalar > &query) const
Evaluate at N-D point.
Definition ScatteredInterpolator.hpp:265
ScatteredInterpolator(const NumericMatrix &points, const NumericVector &values, const std::vector< NumericVector > &grid_points, RBFKernel kernel=RBFKernel::ThinPlateSpline, double epsilon=1.0, InterpolationMethod method=InterpolationMethod::Linear)
Construct with explicit per-dimension grid specification.
Definition ScatteredInterpolator.hpp:198
ScatteredInterpolator(const NumericMatrix &points, const NumericVector &values, int grid_resolution=20, RBFKernel kernel=RBFKernel::ThinPlateSpline, double epsilon=1.0, InterpolationMethod method=InterpolationMethod::Linear)
Construct from scattered N-D points with uniform grid resolution.
Definition ScatteredInterpolator.hpp:149
double reconstruction_error() const
Get RMS reconstruction error at original scattered points.
Definition ScatteredInterpolator.hpp:316
constexpr double rbf_zero_threshold
Threshold below which thin-plate spline uses linear fallback to avoid log(0).
Definition ScatteredInterpolator.hpp:42
double euclidean_distance(const double *p1, const double *p2, int dims)
Compute Euclidean distance between two points.
Definition ScatteredInterpolator.hpp:73
constexpr double rbf_regularization
Regularization term for RBF interpolation matrix to prevent singularity.
Definition ScatteredInterpolator.hpp:40
double rbf_phi(double r, RBFKernel kernel, double epsilon=1.0)
Evaluate RBF kernel φ(r) for a given distance.
Definition ScatteredInterpolator.hpp:52
Definition Diagnostics.hpp:19
InterpolationMethod
Supported interpolation methods.
Definition Interpolate.hpp:29
@ Linear
Piecewise linear (C0 continuous) - fast, simple.
Definition Interpolate.hpp:30
JanusMatrix< NumericScalar > NumericMatrix
Eigen::MatrixXd equivalent.
Definition JanusTypes.hpp:66
RBFKernel
Radial basis function kernel types for scattered interpolation.
Definition ScatteredInterpolator.hpp:25
@ Multiquadric
sqrt(1 + (ε*r)²) - adjustable shape parameter
Definition ScatteredInterpolator.hpp:27
@ Linear
r - simple, produces piecewise linear surface
Definition ScatteredInterpolator.hpp:29
@ ThinPlateSpline
r² log(r) - smooth, good default, no shape parameter
Definition ScatteredInterpolator.hpp:26
@ Cubic
r³ - smooth, commonly used
Definition ScatteredInterpolator.hpp:30
@ Gaussian
exp(-(ε*r)²) - localized influence
Definition ScatteredInterpolator.hpp:28
JanusVector< NumericScalar > NumericVector
Eigen::VectorXd equivalent.
Definition JanusTypes.hpp:67
auto diff(const Eigen::MatrixBase< Derived > &v)
Computes adjacent differences of a vector Returns a vector of size N-1 where out[i] = v[i+1] - v[i].
Definition Calculus.hpp:27
auto solve(const Eigen::MatrixBase< DerivedA > &A, const Eigen::MatrixBase< DerivedB > &b)
Solves linear system Ax = b using the default backend policy.
Definition Linalg.hpp:408
Eigen::Matrix< Scalar, Eigen::Dynamic, 1 > JanusVector
Dynamic-size column vector for both numeric and symbolic backends.
Definition JanusTypes.hpp:49