bool compareDataTables(DataTable &a, DataTable &b) { if (a.getNumVariables() != b.getNumVariables()) return false; auto ait = a.cbegin(), bit = b.cbegin(); for (; ait != a.cend() && bit != b.cend(); ait++, bit++) { for (unsigned int i = 0; i < a.getNumVariables(); i++) { // std::cout << std::setprecision(SAVE_DOUBLE_PRECISION) << ait->getX().at(i) << " == " << std::setprecision(SAVE_DOUBLE_PRECISION) << bit->getX().at(i) << " "; if (!equalsWithinRange(ait->getX().at(i), bit->getX().at(i))) return false; } // std::cout << std::setprecision(SAVE_DOUBLE_PRECISION) << ait->getY().at(j) << " == " << std::setprecision(SAVE_DOUBLE_PRECISION) << bit->getY().at(j) << " "; if (!equalsWithinRange(ait->getY(), bit->getY())) return false; // std::cout << std::endl; } // std::cout << "Finished comparing samples..." << std::endl; return ait == a.cend() && bit == b.cend(); }
DataTable operator+(const DataTable &lhs, const DataTable &rhs) { if(lhs.getNumVariables() != rhs.getNumVariables()) { throw Exception("operator+(DataTable, DataTable): trying to add two DataTable's of different dimensions!"); } DataTable result; for(auto it = lhs.cbegin(); it != lhs.cend(); it++) { result.addSample(*it); } for(auto it = rhs.cbegin(); it != rhs.cend(); it++) { result.addSample(*it); } return result; }
DataTable operator-(const DataTable &lhs, const DataTable &rhs) { if(lhs.getNumVariables() != rhs.getNumVariables()) { throw Exception("operator-(DataTable, DataTable): trying to subtract two DataTable's of different dimensions!"); } DataTable result; auto rhsSamples = rhs.getSamples(); // Add all samples from lhs that are not in rhs for(auto it = lhs.cbegin(); it != lhs.cend(); it++) { if(rhsSamples.count(*it) == 0) { result.addSample(*it); } } return result; }
PSpline::PSpline(const DataTable &samples, double lambda) : lambda(lambda) { // Check data assert(samples.isGridComplete()); std::vector< std::vector<double> > xdata = samples.getTableX(); numVariables = samples.getNumVariables(); // Assuming a cubic spline std::vector<unsigned int> basisDegrees(samples.getNumVariables(), 3); basis = BSplineBasis(xdata, basisDegrees, KnotVectorType::FREE); computeControlPoints(samples); init(); checkControlPoints(); }
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. }
PSplineApproximant::PSplineApproximant(const DataTable &samples, double lambda) : PSplineApproximant(samples, std::vector<unsigned int>(samples.getNumVariables(), 3), lambda) { }
PSplineApproximant::PSplineApproximant(const DataTable &samples, BSplineType type, double lambda) : PSplineApproximant(samples, getBSplineDegrees(samples.getNumVariables(), type), lambda) { }