void PSpline::computeControlPoints(const DataTable &samples) { // Assuming regular grid unsigned int numSamples = samples.getNumSamples(); /* Setup and solve equations Lc = R, * L = B'*W*B + l*D'*D * R = B'*W*y * c = control coefficients or knot averages. * B = basis functions at sample x-values, * W = weighting matrix for interpolating specific points * D = second-order finite difference matrix * l = penalizing parameter (increase for more smoothing) * y = sample y-values when calculating control coefficients, * y = sample x-values when calculating knot averages */ SparseMatrix L, B, D, W; DenseMatrix Rx, Ry, Bx, By; // Weight matrix W.resize(numSamples, numSamples); W.setIdentity(); // Basis function matrix computeBasisFunctionMatrix(samples, B); // Second order finite difference matrix getSecondOrderFiniteDifferenceMatrix(D); // Left-hand side matrix L = B.transpose()*W*B + lambda*D.transpose()*D; // Compute right-hand side matrices controlPointEquationRHS(samples, Bx, By); Rx = B.transpose()*W*Bx; Ry = B.transpose()*W*By; // Matrices to store the resulting coefficients DenseMatrix Cx, Cy; int numEquations = L.rows(); int maxNumEquations = pow(2,10); bool solveAsDense = (numEquations < maxNumEquations); if (!solveAsDense) { #ifndef NDEBUG std::cout << "Computing B-spline control points using sparse solver." << std::endl; #endif // NDEBUG SparseLU s; bool successfulSolve = (s.solve(L,Rx,Cx) && s.solve(L,Ry,Cy)); solveAsDense = !successfulSolve; } if (solveAsDense) { #ifndef NDEBUG std::cout << "Computing B-spline control points using dense solver." << std::endl; #endif // NDEBUG DenseMatrix Ld = L.toDense(); DenseQR s; bool successfulSolve = s.solve(Ld, Rx, Cx) && s.solve(Ld, Ry, Cy); if (!successfulSolve) { throw Exception("PSpline::computeControlPoints: Failed to solve for B-spline coefficients."); } } coefficients = Cy.transpose(); knotaverages = Cx.transpose(); }
RBFSpline::RBFSpline(const DataTable &samples, RadialBasisFunctionType type, bool normalized) : samples(samples), normalized(normalized), precondition(false), dim(samples.getNumVariables()), numSamples(samples.getNumSamples()) { if (type == RadialBasisFunctionType::THIN_PLATE_SPLINE) { fn = std::shared_ptr<RadialBasisFunction>(new ThinPlateSpline()); } else if (type == RadialBasisFunctionType::MULTIQUADRIC) { fn = std::shared_ptr<RadialBasisFunction>(new Multiquadric()); } else if (type == RadialBasisFunctionType::INVERSE_QUADRIC) { fn = std::shared_ptr<RadialBasisFunction>(new InverseQuadric()); } else if (type == RadialBasisFunctionType::INVERSE_MULTIQUADRIC) { fn = std::shared_ptr<RadialBasisFunction>(new InverseMultiquadric()); } else if (type == RadialBasisFunctionType::GAUSSIAN) { fn = std::shared_ptr<RadialBasisFunction>(new Gaussian()); } else { fn = std::shared_ptr<RadialBasisFunction>(new ThinPlateSpline()); } /* Want to solve the linear system A*w = b, * where w is the vector of weights. * NOTE: the system is dense and by default badly conditioned. * It should be solved by a specialized solver such as GMRES * with preconditioning (e.g. ACBF) as in matlab. * NOTE: Consider trying the Łukaszyk–Karmowski metric (for two variables) */ //SparseMatrix A(numSamples,numSamples); //A.reserve(numSamples*numSamples); DenseMatrix A; A.setZero(numSamples, numSamples); DenseMatrix b; b.setZero(numSamples,1); int i=0; for(auto it1 = samples.cbegin(); it1 != samples.cend(); ++it1, ++i) { double sum = 0; int j=0; for(auto it2 = samples.cbegin(); it2 != samples.cend(); ++it2, ++j) { double val = fn->eval(dist(*it1, *it2)); if(val != 0) { //A.insert(i,j) = val; A(i,j) = val; sum += val; } } double y = it1->getY(); if(normalized) b(i) = sum*y; else b(i) = y; } //A.makeCompressed(); if(precondition) { // Calcualte precondition matrix P DenseMatrix P = computePreconditionMatrix(); // Preconditioned A and b DenseMatrix Ap = P*A; DenseMatrix bp = P*b; A = Ap; b = bp; } #ifndef NDEBUG std::cout << "Computing RBF weights using dense solver." << std::endl; #endif // NDEBUG // SVD analysis Eigen::JacobiSVD<DenseMatrix> svd(A, Eigen::ComputeThinU | Eigen::ComputeThinV); auto svals = svd.singularValues(); double svalmax = svals(0); double svalmin = svals(svals.rows()-1); double rcondnum = (svalmax <= 0.0 || svalmin <= 0.0) ? 0.0 : svalmin/svalmax; #ifndef NDEBUG std::cout << "The reciprocal of the condition number is: " << rcondnum << std::endl; std::cout << "Largest/smallest singular value: " << svalmax << " / " << svalmin << std::endl; #endif // NDEBUG // Solve for weights weights = svd.solve(b); #ifndef NDEBUG // Compute error. If it is used later on, move this statement above the NDEBUG double err = (A*weights - b).norm() / b.norm(); std::cout << "Error: " << std::setprecision(10) << err << std::endl; #endif // NDEBUG // // Alternative solver // DenseQR s; // bool success = s.solve(A,b,weights); // assert(success); // NOTE: Tried using experimental GMRES solver in Eigen, but it did not work very well. }