10#include "bsplinebuilder.h"
11#include "mykroneckerproduct.h"
12#include <unsupported/Eigen/KroneckerProduct>
13#include <linearsolvers.h>
14#include <serializer.h>
22BSpline::Builder::Builder(
const DataTable& data)
25 _degrees(getBSplineDegrees(data.getNumVariables(), 3)),
26 _numBasisFunctions(std::vector<unsigned int>(data.getNumVariables(), 0)),
27 _knotSpacing(KnotSpacing::AS_SAMPLED),
28 _smoothing(Smoothing::NONE),
36BSpline BSpline::Builder::build()
const
40 if (!_data.isGridComplete())
42 throw Exception(
"BSpline::Builder::build: Cannot create B-spline from irregular (incomplete) grid.");
46 auto knotVectors = computeKnotVectors();
48 auto bspline = BSpline(knotVectors, _degrees);
50 auto coefficients = computeCoefficients(bspline);
51 bspline.setCoefficients(coefficients);
65DenseVector BSpline::Builder::computeCoefficients(
const BSpline& bspline)
const
67 SparseMatrix B = computeBasisFunctionMatrix(bspline);
69 DenseVector b = getSamplePointValues();
71 if (_smoothing == Smoothing::IDENTITY)
82 SparseMatrix Bt = B.transpose();
85 auto I = SparseMatrix(A.cols(), A.cols());
89 else if (_smoothing == Smoothing::PSPLINE)
108 unsigned int numSamples = _data.getNumSamples();
109 SparseMatrix Bt = B.transpose();
112 W.resize(numSamples, numSamples);
115 SparseMatrix D = getSecondOrderFiniteDifferenceMatrix(bspline);
117 A = Bt * W * B + _alpha * D.transpose() * D;
123 int numEquations = A.rows();
124 int maxNumEquations = 100;
125 bool solveAsDense = (numEquations < maxNumEquations);
131 "BSpline::Builder::computeBSplineCoefficients: Computing B-spline control points using sparse solver."
136 solveAsDense = !s.solve(A, b, x);
143 "BSpline::Builder::computeBSplineCoefficients: Computing B-spline control points using dense solver."
146 DenseMatrix Ad = A.toDense();
147 DenseQR<DenseVector> s;
151 if (!s.solve(Ad, b, x))
153 throw Exception(
"BSpline::Builder::computeBSplineCoefficients: Failed to solve for B-spline coefficients.");
160SparseMatrix BSpline::Builder::computeBasisFunctionMatrix(
161 const BSpline& bspline)
const
163 unsigned int numVariables = _data.getNumVariables();
164 unsigned int numSamples = _data.getNumSamples();
167 SparseMatrix A(numSamples, bspline.getNumBasisFunctions());
171 for (
auto it = _data.cbegin(); it != _data.cend(); ++it, ++i)
173 DenseVector xi(numVariables);
175 std::vector<double> xv = it->getX();
177 for (
unsigned int j = 0; j < numVariables; ++j)
182 SparseVector basisValues = bspline.evalBasis(xi);
184 for (SparseVector::InnerIterator it2(basisValues); it2; ++it2)
186 A.insert(i, it2.index()) = it2.value();
194DenseVector BSpline::Builder::getSamplePointValues()
const
196 DenseVector B = DenseVector::Zero(_data.getNumSamples());
199 for (
auto it = _data.cbegin(); it != _data.cend(); ++it, ++i)
211SparseMatrix BSpline::Builder::getSecondOrderFiniteDifferenceMatrix(
212 const BSpline& bspline)
const
214 unsigned int numVariables = bspline.getNumVariables();
216 unsigned int numCols = bspline.getNumBasisFunctions();
217 std::vector<unsigned int> numBasisFunctions =
218 bspline.getNumBasisFunctionsPerVariable();
220 std::vector<unsigned int> dims;
222 for (
unsigned int i = 0; i < numVariables; i++)
224 dims.push_back(numBasisFunctions.at(i));
227 std::reverse(dims.begin(), dims.end());
229 for (
unsigned int i = 0; i < numVariables; ++i)
230 if (numBasisFunctions.at(i) < 3)
232 throw Exception(
"BSpline::Builder::getSecondOrderDifferenceMatrix: Need at least three coefficients/basis function per variable.");
237 std::vector< int > numBlkRows;
239 for (
unsigned int i = 0; i < numVariables; i++)
243 for (
unsigned int j = 0; j < numVariables; j++)
247 prod *= (dims[j] - 2);
256 numBlkRows.push_back(prod);
260 SparseMatrix D(numRows, numCols);
261 D.reserve(DenseVector::Constant(numCols,
266 for (
unsigned int d = 0; d < numVariables; d++)
272 for (
unsigned int k = 0; k < d; k++)
277 for (
unsigned int k = d + 1; k < numVariables; k++)
279 rightProd *= dims[k];
283 for (
int j = 0; j < rightProd; j++)
286 int blkBaseCol = j * leftProd * dims[d];
289 for (
unsigned int l = 0; l < (dims[d] - 2); l++)
294 int k = j * leftProd * dims[d] + l;
305 for (
int n = 0; n < leftProd; n++)
307 int k = blkBaseCol + l * leftProd + n;
325std::vector<std::vector<double >> BSpline::Builder::computeKnotVectors()
const
327 if (_data.getNumVariables() != _degrees.size())
329 throw Exception(
"BSpline::Builder::computeKnotVectors: Inconsistent sizes on input vectors.");
332 std::vector<std::vector<double >> grid = _data.getTableX();
333 std::vector<std::vector<double >> knotVectors;
335 for (
unsigned int i = 0; i < _data.getNumVariables(); ++i)
338 auto knotVec = computeKnotVector(grid.at(i), _degrees.at(i),
339 _numBasisFunctions.at(i));
340 knotVectors.push_back(knotVec);
347std::vector<double> BSpline::Builder::computeKnotVector(
348 const std::vector<double>& values,
350 unsigned int numBasisFunctions)
const
352 switch (_knotSpacing)
354 case KnotSpacing::AS_SAMPLED:
355 return knotVectorMovingAverage(values, degree);
357 case KnotSpacing::EQUIDISTANT:
358 return knotVectorEquidistant(values, degree, numBasisFunctions);
360 case KnotSpacing::EXPERIMENTAL:
361 return knotVectorBuckets(values, degree);
364 return knotVectorMovingAverage(values, degree);
398std::vector<double> BSpline::Builder::knotVectorMovingAverage(
399 const std::vector<double>& values,
400 unsigned int degree)
const
403 std::vector<double> unique = extractUniqueSorted(values);
405 unsigned int n = unique.size();
406 unsigned int k = degree - 1;
407 unsigned int w = k + 3;
412 std::ostringstream e;
413 e <<
"knotVectorMovingAverage: Only " << n
414 <<
" unique interpolation points are given. A minimum of degree+1 = " << degree
416 <<
" unique points are required to build a B-spline basis of degree " << degree
418 throw Exception(e.str());
421 std::vector<double> knots(n - k - 2, 0);
424 for (
unsigned int i = 0; i < n - k - 2; ++i)
428 for (
unsigned int j = 0; j < w; ++j)
430 ma += unique.at(i + j);
433 knots.at(i) = ma / w;
437 for (
unsigned int i = 0; i < degree + 1; ++i)
439 knots.insert(knots.begin(), unique.front());
443 for (
unsigned int i = 0; i < degree + 1; ++i)
445 knots.insert(knots.end(), unique.back());
453std::vector<double> BSpline::Builder::knotVectorEquidistant(
454 const std::vector<double>& values,
456 unsigned int numBasisFunctions = 0)
const
459 std::vector<double> unique = extractUniqueSorted(values);
461 unsigned int n = unique.size();
463 if (numBasisFunctions > 0)
465 n = numBasisFunctions;
468 unsigned int k = degree - 1;
473 std::ostringstream e;
474 e <<
"knotVectorMovingAverage: Only " << n
475 <<
" unique interpolation points are given. A minimum of degree+1 = " << degree
477 <<
" unique points are required to build a B-spline basis of degree " << degree
479 throw Exception(e.str());
483 unsigned int numIntKnots = std::max(n - k - 2, (
unsigned int)0);
484 numIntKnots = std::min((
unsigned int)10, numIntKnots);
485 std::vector<double> knots = linspace(unique.front(), unique.back(),
489 for (
unsigned int i = 0; i < degree; ++i)
491 knots.insert(knots.begin(), unique.front());
495 for (
unsigned int i = 0; i < degree; ++i)
497 knots.insert(knots.end(), unique.back());
505std::vector<double> BSpline::Builder::knotVectorBuckets(
506 const std::vector<double>& values,
unsigned int degree,
507 unsigned int maxSegments)
const
510 std::vector<double> unique = extractUniqueSorted(values);
513 if (unique.size() < degree + 1)
515 std::ostringstream e;
516 e <<
"BSpline::Builder::knotVectorBuckets: Only " << unique.size()
517 <<
" unique sample points are given. A minimum of degree+1 = " << degree + 1
518 <<
" unique points are required to build a B-spline basis of degree " << degree
520 throw Exception(e.str());
524 unsigned int ni = unique.size() - degree - 1;
526 unsigned int ns = ni + degree + 1;
529 if (ns > maxSegments && maxSegments >= degree + 1)
532 ni = ns - degree - 1;
539 if (ni > unique.size() - degree - 1)
541 throw Exception(
"BSpline::Builder::knotVectorBuckets: Invalid number of internal knots!");
549 w = std::floor(unique.size() / ni);
553 unsigned int res = unique.size() - w * ni;
555 std::vector<unsigned int> windows(ni, w);
558 for (
unsigned int i = 0; i < res; ++i)
564 std::vector<double> knots(ni, 0);
566 unsigned int index = 0;
568 for (
unsigned int i = 0; i < ni; ++i)
570 for (
unsigned int j = 0; j < windows.at(i); ++j)
572 knots.at(i) += unique.at(index + j);
575 knots.at(i) /= windows.at(i);
576 index += windows.at(i);
580 for (
unsigned int i = 0; i < degree + 1; ++i)
582 knots.insert(knots.begin(), unique.front());
586 for (
unsigned int i = 0; i < degree + 1; ++i)
588 knots.insert(knots.end(), unique.back());
594std::vector<double> BSpline::Builder::extractUniqueSorted(
595 const std::vector<double>& values)
const
598 std::vector<double> unique(values);
599 std::sort(unique.begin(), unique.end());
600 std::vector<double>::iterator it = unique_copy(unique.begin(), unique.end(),
602 unique.resize(distance(unique.begin(), it));