10#include "bsplinebuilder.h"
11#include "mykroneckerproduct.h"
12#include <unsupported/Eigen/KroneckerProduct>
13#include <linearsolvers.h>
14#include <serializer.h>
21BSpline::Builder::Builder(
const DataTable& data)
24 _degrees(getBSplineDegrees(data.getNumVariables(), 3)),
25 _numBasisFunctions(std::vector<unsigned int>(data.getNumVariables(), 0)),
26 _knotSpacing(KnotSpacing::AS_SAMPLED),
27 _smoothing(Smoothing::NONE),
35BSpline BSpline::Builder::build()
const
39 if (!_data.isGridComplete())
41 throw Exception(
"BSpline::Builder::build: Cannot create B-spline from irregular (incomplete) grid.");
45 auto knotVectors = computeKnotVectors();
47 auto bspline = BSpline(knotVectors, _degrees);
49 auto coefficients = computeCoefficients(bspline);
50 bspline.setCoefficients(coefficients);
64DenseVector BSpline::Builder::computeCoefficients(
const BSpline& bspline)
const
66 SparseMatrix B = computeBasisFunctionMatrix(bspline);
68 DenseVector b = getSamplePointValues();
70 if (_smoothing == Smoothing::IDENTITY)
81 SparseMatrix Bt = B.transpose();
84 auto I = SparseMatrix(
A.cols(),
A.cols());
88 else if (_smoothing == Smoothing::PSPLINE)
107 unsigned int numSamples = _data.getNumSamples();
108 SparseMatrix Bt = B.transpose();
111 W.resize(numSamples, numSamples);
114 SparseMatrix
D = getSecondOrderFiniteDifferenceMatrix(bspline);
116 A = Bt * W * B + _alpha *
D.transpose() *
D;
122 int numEquations =
A.rows();
123 int maxNumEquations = 100;
124 bool solveAsDense = (numEquations < maxNumEquations);
130 "BSpline::Builder::computeBSplineCoefficients: Computing B-spline control points using sparse solver."
135 solveAsDense = !s.solve(
A, b, x);
142 "BSpline::Builder::computeBSplineCoefficients: Computing B-spline control points using dense solver."
145 DenseMatrix Ad =
A.toDense();
146 DenseQR<DenseVector> s;
150 if (!s.solve(Ad, b, x))
152 throw Exception(
"BSpline::Builder::computeBSplineCoefficients: Failed to solve for B-spline coefficients.");
159SparseMatrix BSpline::Builder::computeBasisFunctionMatrix(
160 const BSpline& bspline)
const
162 unsigned int numVariables = _data.getNumVariables();
163 unsigned int numSamples = _data.getNumSamples();
166 SparseMatrix
A(numSamples, bspline.getNumBasisFunctions());
170 for (
auto it = _data.cbegin(); it != _data.cend(); ++it, ++
i)
172 DenseVector xi(numVariables);
174 std::vector<double> xv = it->getX();
176 for (
unsigned int j = 0; j < numVariables; ++j)
181 SparseVector basisValues = bspline.evalBasis(xi);
183 for (SparseVector::InnerIterator it2(basisValues); it2; ++it2)
185 A.insert(
i, it2.index()) = it2.value();
193DenseVector BSpline::Builder::getSamplePointValues()
const
195 DenseVector B = DenseVector::Zero(_data.getNumSamples());
198 for (
auto it = _data.cbegin(); it != _data.cend(); ++it, ++
i)
210SparseMatrix BSpline::Builder::getSecondOrderFiniteDifferenceMatrix(
211 const BSpline& bspline)
const
213 unsigned int numVariables = bspline.getNumVariables();
215 unsigned int numCols = bspline.getNumBasisFunctions();
216 std::vector<unsigned int> numBasisFunctions =
217 bspline.getNumBasisFunctionsPerVariable();
219 std::vector<unsigned int> dims;
221 for (
unsigned int i = 0;
i < numVariables;
i++)
223 dims.push_back(numBasisFunctions.at(
i));
226 std::reverse(dims.begin(), dims.end());
228 for (
unsigned int i = 0;
i < numVariables; ++
i)
229 if (numBasisFunctions.at(
i) < 3)
231 throw Exception(
"BSpline::Builder::getSecondOrderDifferenceMatrix: Need at least three coefficients/basis function per variable.");
236 std::vector< int > numBlkRows;
238 for (
unsigned int i = 0;
i < numVariables;
i++)
242 for (
unsigned int j = 0; j < numVariables; j++)
246 prod *= (dims[j] - 2);
255 numBlkRows.push_back(prod);
259 SparseMatrix
D(numRows, numCols);
260 D.reserve(DenseVector::Constant(numCols,
265 for (
unsigned int d = 0; d < numVariables; d++)
271 for (
unsigned int k = 0; k < d; k++)
276 for (
unsigned int k = d + 1; k < numVariables; k++)
278 rightProd *= dims[k];
282 for (
int j = 0; j < rightProd; j++)
285 int blkBaseCol = j * leftProd * dims[d];
288 for (
unsigned int l = 0; l < (dims[d] - 2); l++)
293 int k = j * leftProd * dims[d] + l;
304 for (
int n = 0; n < leftProd; n++)
306 int k = blkBaseCol + l * leftProd + n;
324std::vector<std::vector<double>> BSpline::Builder::computeKnotVectors()
const
326 if (_data.getNumVariables() != _degrees.size())
328 throw Exception(
"BSpline::Builder::computeKnotVectors: Inconsistent sizes on input vectors.");
331 std::vector<std::vector<double>> grid = _data.getTableX();
332 std::vector<std::vector<double>> knotVectors;
334 for (
unsigned int i = 0;
i < _data.getNumVariables(); ++
i)
337 auto knotVec = computeKnotVector(grid.at(
i), _degrees.at(
i),
338 _numBasisFunctions.at(
i));
339 knotVectors.push_back(knotVec);
346std::vector<double> BSpline::Builder::computeKnotVector(
347 const std::vector<double>& values,
349 unsigned int numBasisFunctions)
const
351 switch (_knotSpacing)
353 case KnotSpacing::AS_SAMPLED:
354 return knotVectorMovingAverage(values, degree);
356 case KnotSpacing::EQUIDISTANT:
357 return knotVectorEquidistant(values, degree, numBasisFunctions);
359 case KnotSpacing::EXPERIMENTAL:
360 return knotVectorBuckets(values, degree);
363 return knotVectorMovingAverage(values, degree);
397std::vector<double> BSpline::Builder::knotVectorMovingAverage(
398 const std::vector<double>& values,
399 unsigned int degree)
const
402 std::vector<double> unique = extractUniqueSorted(values);
404 unsigned int n = unique.size();
405 unsigned int k = degree - 1;
406 unsigned int w = k + 3;
411 std::ostringstream e;
412 e <<
"knotVectorMovingAverage: Only " << n
413 <<
" unique interpolation points are given. A minimum of degree+1 = " << degree
415 <<
" unique points are required to build a B-spline basis of degree " << degree
417 throw Exception(e.str());
420 std::vector<double> knots(n - k - 2, 0);
423 for (
unsigned int i = 0;
i < n - k - 2; ++
i)
427 for (
unsigned int j = 0; j < w; ++j)
429 ma += unique.at(
i + j);
432 knots.at(
i) = ma / w;
436 for (
unsigned int i = 0;
i < degree + 1; ++
i)
438 knots.insert(knots.begin(), unique.front());
442 for (
unsigned int i = 0;
i < degree + 1; ++
i)
444 knots.insert(knots.end(), unique.back());
452std::vector<double> BSpline::Builder::knotVectorEquidistant(
453 const std::vector<double>& values,
455 unsigned int numBasisFunctions = 0)
const
458 std::vector<double> unique = extractUniqueSorted(values);
460 unsigned int n = unique.size();
462 if (numBasisFunctions > 0)
464 n = numBasisFunctions;
467 unsigned int k = degree - 1;
472 std::ostringstream e;
473 e <<
"knotVectorMovingAverage: Only " << n
474 <<
" unique interpolation points are given. A minimum of degree+1 = " << degree
476 <<
" unique points are required to build a B-spline basis of degree " << degree
478 throw Exception(e.str());
482 unsigned int numIntKnots = std::max(n - k - 2, (
unsigned int)0);
483 numIntKnots = std::min((
unsigned int)10, numIntKnots);
484 std::vector<double> knots =
linspace(unique.front(), unique.back(),
488 for (
unsigned int i = 0;
i < degree; ++
i)
490 knots.insert(knots.begin(), unique.front());
494 for (
unsigned int i = 0;
i < degree; ++
i)
496 knots.insert(knots.end(), unique.back());
504std::vector<double> BSpline::Builder::knotVectorBuckets(
505 const std::vector<double>& values,
unsigned int degree,
506 unsigned int maxSegments)
const
509 std::vector<double> unique = extractUniqueSorted(values);
512 if (unique.size() < degree + 1)
514 std::ostringstream e;
515 e <<
"BSpline::Builder::knotVectorBuckets: Only " << unique.size()
516 <<
" unique sample points are given. A minimum of degree+1 = " << degree + 1
517 <<
" unique points are required to build a B-spline basis of degree " << degree
519 throw Exception(e.str());
523 unsigned int ni = unique.size() - degree - 1;
525 unsigned int ns = ni + degree + 1;
528 if (ns > maxSegments && maxSegments >= degree + 1)
531 ni = ns - degree - 1;
538 if (ni > unique.size() - degree - 1)
540 throw Exception(
"BSpline::Builder::knotVectorBuckets: Invalid number of internal knots!");
548 w = std::floor(unique.size() / ni);
552 unsigned int res = unique.size() - w * ni;
554 std::vector<unsigned int> windows(ni, w);
557 for (
unsigned int i = 0;
i < res; ++
i)
563 std::vector<double> knots(ni, 0);
565 unsigned int index = 0;
567 for (
unsigned int i = 0;
i < ni; ++
i)
569 for (
unsigned int j = 0; j < windows.at(
i); ++j)
571 knots.at(
i) += unique.at(index + j);
574 knots.at(
i) /= windows.at(
i);
575 index += windows.at(
i);
579 for (
unsigned int i = 0;
i < degree + 1; ++
i)
581 knots.insert(knots.begin(), unique.front());
585 for (
unsigned int i = 0;
i < degree + 1; ++
i)
587 knots.insert(knots.end(), unique.back());
593std::vector<double> BSpline::Builder::extractUniqueSorted(
594 const std::vector<double>& values)
const
597 std::vector<double> unique(values);
598 std::sort(unique.begin(), unique.end());
599 std::vector<double>::iterator it = unique_copy(unique.begin(), unique.end(),
601 unique.resize(distance(unique.begin(), it));
std::vector< double > linspace(double start, double stop, unsigned int num)