/** * @file KernelData.cpp * @brief caching some kernel data * @author Erik Rodner * @date 01/19/2010 */ #include #include "KernelData.h" #include "core/algebra/CholeskyRobustAuto.h" #include "core/algebra/CholeskyRobust.h" #include "core/vector/Algorithms.h" #ifdef NICE_USELIB_OPENMP #include //for parallel processing #endif #include "vector" using namespace std; using namespace NICE; using namespace OBJREC; KernelData::KernelData() { // empty config Config conf; initFromConfig ( &conf, "DONTCARE" ); } KernelData::KernelData ( const KernelData & src ) { kernelMatrix = src.kernelMatrix; inverseKernelMatrix = src.inverseKernelMatrix; choleskyMatrix = src.choleskyMatrix; for ( map::const_iterator i = src.cachedMatrices.begin(); i != src.cachedMatrices.end(); i++ ) { Matrix *M = new Matrix ( * ( i->second ) ); cachedMatrices.insert ( pair ( i->first, M ) ); } logdet = src.logdet; verbose = src.verbose; cr = src.cr->clone(); } KernelData::KernelData ( const Config *conf, const Matrix & kernelMatrix, const string & section, bool updateCholesky ) { initFromConfig ( conf, section ); this->kernelMatrix = kernelMatrix; if ( updateCholesky ) updateCholeskyFactorization(); } KernelData::KernelData ( const Config *conf, const string & section ) { initFromConfig ( conf, section ); } void KernelData::initFromConfig ( const Config *conf, const string & section ) { verbose = conf->gB ( section, "verbose", false ); string inv_method = conf->gS ( section, "robust_cholesky", "auto" ); double noiseStep = conf->gD ( section, "rchol_noise_variance", 1e-7 ); double minimumLogDet = conf->gD ( section, "rchol_minimum_logdet", - std::numeric_limits::max() ); bool useCuda = conf->gB ( section, "rchol_cuda", true ); if ( verbose && useCuda ) std::cerr << "KernelData: using the cuda implementation of cholesky decomposition (might be inaccurate)" << std::endl; if ( inv_method == "auto" ) { if ( verbose ) std::cerr << "KernelData: using the cholesky method with automatic regularization" << std::endl; cr = new CholeskyRobustAuto ( verbose, noiseStep, minimumLogDet, useCuda ); } else { if ( verbose ) std::cerr << "KernelData: using the cholesky method with static regularization" << std::endl; cr = new CholeskyRobust ( verbose, noiseStep, useCuda ); } } KernelData::~KernelData() { delete cr; } void KernelData::updateCholeskyFactorization () { if ( verbose ) std::cerr << "KernelData: kernel: " << kernelMatrix.rows() << " " << kernelMatrix.cols() << std::endl; if ( ( kernelMatrix.rows() <= 0 ) || ( kernelMatrix.cols() <= 0 ) ) fthrow ( Exception, "KernelData: no kernel matrix available !" ); if ( kernelMatrix.containsNaN() ) { if ( verbose ) std::cerr << "KernelData: kernel matrix contains NaNs (setting inverse to identity)" << std::endl; logdet = numeric_limits::max(); choleskyMatrix.resize ( kernelMatrix.rows(), kernelMatrix.cols() ); choleskyMatrix.setIdentity(); } else { if ( verbose ) std::cerr << "KernelData: calculating cholesky decomposition" << std::endl; cr->robustChol ( kernelMatrix, choleskyMatrix ); logdet = cr->getLastLogDet(); if ( !finite ( logdet ) ) { choleskyMatrix.resize ( kernelMatrix.rows(), kernelMatrix.cols() ); choleskyMatrix.setIdentity(); logdet = numeric_limits::max(); } } } void KernelData::updateInverseKernelMatrix () { if ( ! hasCholeskyFactorization() ) updateCholeskyFactorization(); inverseKernelMatrix.resize ( choleskyMatrix.rows(), choleskyMatrix.cols() ); choleskyInvertLargeScale ( choleskyMatrix, inverseKernelMatrix ); } void KernelData::computeInverseKernelMultiply ( const Vector & x, Vector & result ) const { if ( choleskyMatrix.rows() == 0 ) fthrow ( Exception, "Cholesky factorization was not initialized, use updateCholeskyFactorization() in advance" ); choleskySolveLargeScale ( choleskyMatrix, x, result ); } const NICE::Matrix & KernelData::getKernelMatrix() const { return kernelMatrix; } NICE::Matrix & KernelData::getKernelMatrix() { return kernelMatrix; } const NICE::Matrix & KernelData::getInverseKernelMatrix() const { return inverseKernelMatrix; }; NICE::Matrix & KernelData::getInverseKernelMatrix() { return inverseKernelMatrix; }; const NICE::Matrix & KernelData::getCholeskyMatrix() const { return choleskyMatrix; } const Matrix & KernelData::getCachedMatrix ( int i ) const { map::const_iterator it = cachedMatrices.find ( i ); if ( it != cachedMatrices.end() ) return * ( it->second ); else fthrow ( Exception, "Cached matrix with index " << i << " is not available." ); } void KernelData::setCachedMatrix ( int i, Matrix *m ) { cachedMatrices[i] = m; } uint KernelData::getKernelMatrixSize () const { uint mysize = ( kernelMatrix.rows() == 0 ) ? ( choleskyMatrix.rows() ) : kernelMatrix.rows(); return mysize; }; void KernelData::getLooEstimates ( const Vector & y, Vector & muLoo, Vector & sigmaLoo ) const { if ( inverseKernelMatrix.rows() != getKernelMatrixSize() ) fthrow ( Exception, "updateInverseKernelMatrix() has to be called in advance to use this function\n" ); if ( y.size() != inverseKernelMatrix.rows() ) fthrow ( Exception, "inverse kernel matrix does not fit to the size of the vector of function values y\n" ); Vector alpha; computeInverseKernelMultiply ( y, alpha ); muLoo.resize ( y.size() ); sigmaLoo.resize ( y.size() ); for ( uint l = 0 ; l < y.size(); l++ ) { sigmaLoo[l] = 1.0 / inverseKernelMatrix ( l, l ); muLoo[l] = y[l] - alpha[l] * sigmaLoo[l]; } } KernelData *KernelData::clone ( void ) const { return new KernelData ( *this ); } /** * @brief Updates the GP-Likelihood, if only the i-th row and column has changed. Time is O(n^2) instaed O(n^3) * @author Alexander Lütz * @date 01/12/2010 (dd/mm/yyyy) */ void KernelData::getGPLikelihoodWithOneNewRow ( const NICE::Vector & y, const double & oldLogdetK, const int & rowIndex, const NICE::Vector & newRow, const Vector & oldAlpha , Vector & newAlpha, double & loglike ) { // oldAlpha = K^{-1} y // K' new kernel matrix = exchange the row and column at position rowIndex // with newRow // try to find U and V such that K' = K + U*V with U,V having rank 2 // rowIndex'th base vector Vector ei ( y.size(), 0.0 ); ei[rowIndex] = 1.0; // we have to consider the diagonal entry Vector a = newRow - kernelMatrix.getRow ( rowIndex ) ; a[rowIndex] = a[rowIndex] / 2; NICE::Matrix U ( y.size(), 2 ); NICE::Matrix V ( 2, y.size() ); for ( uint i = 0; i < y.size(); i++ ) { U ( i, 0 ) = a[i]; U ( i, 1 ) = ei[i]; V ( 0, i ) = ei[i]; V ( 1, i ) = a[i]; } // Sherman Woodbury-Morrison Formula: // alpha_new = (K + UV)^{-1} y = K^{-1} y - K^{-1} U ( I + V // K^{-1} U )^{-1} V K^{-1} y = oldAlpha - B ( I + V B )^{-1} V oldAlpha // = oldAlpha - B F^{-1} V oldAlpha // with B = K^{-1} U and F = (I+VB) // Time complexity: 2 choleskySolve calls: O(n^2) NICE::Vector B_1; computeInverseKernelMultiply ( U.getColumn ( 0 ), B_1 ); NICE::Vector B_2; computeInverseKernelMultiply ( U.getColumn ( 1 ), B_2 ); NICE::Matrix B ( y.size(), 2 ); for ( uint i = 0; i < y.size(); i++ ) { B ( i, 0 ) = B_1[i]; B ( i, 1 ) = B_2[i]; } NICE::Matrix F ( 2, 2 ); F.setIdentity(); Matrix V_B ( 2, 2 ); V_B.multiply ( V, B ); F += V_B; // Time complexity: 1 linear equation system with 2 variables // can be computed in O(1) using a fixed implementation NICE::Matrix F_inv = NICE::Matrix ( 2, 2 ); double denominator = F ( 0, 0 ) * F ( 1, 1 ) - F ( 0, 1 ) * F ( 1, 0 ); F_inv ( 0, 0 ) = F ( 1, 1 ) / denominator; F_inv ( 0, 1 ) = -F ( 0, 1 ) / denominator; F_inv ( 1, 0 ) = - F ( 1, 0 ) / denominator; F_inv ( 1, 1 ) = F ( 0, 0 ) / denominator; Matrix M_oldAlpha ( y.size(), 1 ); for ( uint i = 0; i < y.size(); i++ ) { M_oldAlpha ( i, 0 ) = oldAlpha[i]; } NICE::Matrix V_oldAlpha; V_oldAlpha.multiply ( V, M_oldAlpha ); NICE::Matrix F_inv_V_old_Alpha; F_inv_V_old_Alpha.multiply ( F_inv, V_oldAlpha ); NICE::Matrix M_newAlpha; M_newAlpha.multiply ( B, F_inv_V_old_Alpha ); M_newAlpha *= -1; M_newAlpha += M_oldAlpha; newAlpha = NICE::Vector ( y.size() ); for ( uint i = 0; i < y.size(); i++ ) { newAlpha[i] = M_newAlpha ( i, 0 ); } // Matrix Determinant Lemma // http://en.wikipedia.org/wiki/Matrix_determinant_lemma // det(K + U*V) = det(I + V * K^{-1} * U) * det(K) // logdet(K + U*V) = logdet( F ) + logdet(K) double logdetF = log ( F ( 0, 0 ) * F ( 1, 1 ) - F ( 0, 1 ) * F ( 1, 0 ) ); double newLogdetK = logdetF + oldLogdetK; logdet = newLogdetK; loglike = newLogdetK + newAlpha.scalarProduct ( y ); } /** * @brief Updates the GP-Likelihood, if only the i-th row and column has changed. Time is O(n^2) instaed O(n^3). This is only the first part. Usefull for multiclass-problems. * @author Alexander Lütz * @date 01/12/2010 (dd/mm/yyyy) */ void KernelData::getGPLikelihoodWithOneNewRow_FirstPart ( const int & rowIndex, const NICE::Vector & newRow ) { // oldAlpha = K^{-1} y // K' new kernel matrix = exchange the row and column at position rowIndex // with newRow // try to find U and V such that K' = K + U*V with U,V having rank 2 // rowIndex'th base vector Vector ei ( newRow.size(), 0.0 ); ei[rowIndex] = 1.0; // we have to consider the diagonal entry Vector a = newRow - kernelMatrix.getRow ( rowIndex ) ; a[rowIndex] = a[rowIndex] / 2; U.resize ( newRow.size(), 2 ); V.resize ( 2, newRow.size() ); // #pragma omp parallel for for ( uint i = 0; i < newRow.size(); i++ ) { U ( i, 0 ) = a[i]; U ( i, 1 ) = ei[i]; V ( 0, i ) = ei[i]; V ( 1, i ) = a[i]; } if ( verbose ) { std::cerr << std::endl << "KernelData::perform_Rank_2_Update -- U:" << std::endl; for ( uint ik = 0; ik < U.rows(); ik++ ) { for ( uint jk = 0; jk < U.cols(); jk++ ) { std::cerr << U ( ik, jk ) << " "; } std::cerr << std::endl; } std::cerr << std::endl << "KernelData::perform_Rank_2_Update -- V:" << std::endl; for ( uint ik = 0; ik < V.rows(); ik++ ) { for ( uint jk = 0; jk < V.cols(); jk++ ) { std::cerr << V ( ik, jk ) << " "; } std::cerr << std::endl; } } // Sherman Woodbury-Morrison Formula: // alpha_new = (K + UV)^{-1} y = K^{-1} y - K^{-1} U ( I + V // K^{-1} U )^{-1} V K^{-1} y = oldAlpha - B ( I + V B )^{-1} V oldAlpha // = oldAlpha - B F^{-1} V oldAlpha // with B = K^{-1} U and F = (I+VB) // Time complexity: 2 choleskySolve calls: O(n^2) NICE::Vector B_1; computeInverseKernelMultiply ( U.getColumn ( 0 ), B_1 ); NICE::Vector B_2; computeInverseKernelMultiply ( U.getColumn ( 1 ), B_2 ); B.resize ( newRow.size(), 2 ); for ( uint i = 0; i < newRow.size(); i++ ) { B ( i, 0 ) = B_1[i]; B ( i, 1 ) = B_2[i]; } if ( verbose ) { std::cerr << std::endl << "KernelData::perform_Rank_2_Update -- B:" << std::endl; for ( uint ik = 0; ik < B.rows(); ik++ ) { for ( uint jk = 0; jk < B.cols(); jk++ ) { std::cerr << B ( ik, jk ) << " "; } std::cerr << std::endl; } } F.resize ( 2, 2 ); F.setIdentity(); Matrix V_B ( 2, 2 ); V_B.multiply ( V, B ); F += V_B; if ( verbose ) { std::cerr << std::endl << "KernelData::perform_Rank_2_Update -- F:" << std::endl; for ( uint ik = 0; ik < F.rows(); ik++ ) { for ( uint jk = 0; jk < F.cols(); jk++ ) { std::cerr << F ( ik, jk ) << " "; } std::cerr << std::endl; } } // Time complexity: 1 linear equation system with 2 variables // can be computed in O(1) using a fixed implementation F_inv.resize ( 2, 2 ); double denominator = F ( 0, 0 ) * F ( 1, 1 ) - F ( 0, 1 ) * F ( 1, 0 ); F_inv ( 0, 0 ) = F ( 1, 1 ) / denominator; F_inv ( 0, 1 ) = -F ( 0, 1 ) / denominator; F_inv ( 1, 0 ) = - F ( 1, 0 ) / denominator; F_inv ( 1, 1 ) = F ( 0, 0 ) / denominator; if ( verbose ) { std::cerr << std::endl << "KernelData::perform_Rank_2_Update -- F_inv:" << std::endl; for ( uint ik = 0; ik < F_inv.rows(); ik++ ) { for ( uint jk = 0; jk < F_inv.cols(); jk++ ) { std::cerr << F_inv ( ik, jk ) << " "; } std::cerr << std::endl; } NICE::Matrix MultiplicationResult ( F_inv.cols(), F_inv.cols(), 0.0 ); MultiplicationResult.multiply ( F, F_inv ); std::cerr << std::endl << "KernelData::perform_Rank_2_Update -- F-inversion MultiplicationResult:" << std::endl; for ( uint ik = 0; ik < MultiplicationResult.rows(); ik++ ) { for ( uint jk = 0; jk < MultiplicationResult.cols(); jk++ ) { std::cerr << MultiplicationResult ( ik, jk ) << " "; } std::cerr << std::endl; } } } /** * @brief Updates the GP-Likelihood, if only the i-th row and column has changed. Time is O(n^2) instaed O(n^3). This is only the second part. Usefull for multiclass-problems. * @author Alexander Lütz * @date 01/12/2010 (dd/mm/yyyy) */ void KernelData::getGPLikelihoodWithOneNewRow_SecondPart ( const NICE::Vector & y, const double & oldLogdetK, const Vector & oldAlpha , Vector & newAlpha, double & loglike ) { Matrix M_oldAlpha ( y.size(), 1 ); for ( uint i = 0; i < y.size(); i++ ) { M_oldAlpha ( i, 0 ) = oldAlpha[i]; } NICE::Matrix V_oldAlpha; V_oldAlpha.multiply ( V, M_oldAlpha ); NICE::Matrix F_inv_V_old_Alpha; F_inv_V_old_Alpha.multiply ( F_inv, V_oldAlpha ); NICE::Matrix M_newAlpha; M_newAlpha.multiply ( B, F_inv_V_old_Alpha ); M_newAlpha *= -1; M_newAlpha += M_oldAlpha; newAlpha = NICE::Vector ( y.size() ); for ( uint i = 0; i < y.size(); i++ ) { newAlpha[i] = M_newAlpha ( i, 0 ); } // Matrix Determinant Lemma // http://en.wikipedia.org/wiki/Matrix_determinant_lemma // det(K + U*V) = det(I + V * K^{-1} * U) * det(K) // logdet(K + U*V) = logdet( F ) + logdet(K) double logdetF = log ( F ( 0, 0 ) * F ( 1, 1 ) - F ( 0, 1 ) * F ( 1, 0 ) ); double newLogdetK = logdetF + oldLogdetK; logdet = newLogdetK; loglike = newLogdetK + newAlpha.scalarProduct ( y ); } /** * @brief Updates the GP-Likelihood, if only the i-th row and column has changed. Time is O(n^2) instaed O(n^3). * @author Alexander Lütz * @date 01/09/2011 (dd/mm/yyyy) */ void KernelData::perform_Rank_2_Update ( const int & rowIndex, const NICE::Vector & newRow ) { getGPLikelihoodWithOneNewRow_FirstPart ( rowIndex, newRow ); Matrix prod_1; prod_1.multiply ( V, inverseKernelMatrix ); if ( verbose ) { std::cerr << std::endl << "KernelData::perform_Rank_2_Update -- prod_1:" << std::endl; for ( uint ik = 0; ik < prod_1.rows(); ik++ ) { for ( uint jk = 0; jk < prod_1.cols(); jk++ ) { std::cerr << prod_1 ( ik, jk ) << " "; } std::cerr << std::endl; } } Matrix prod_2; prod_2.multiply ( F_inv, prod_1 ); if ( verbose ) { std::cerr << std::endl << "KernelData::perform_Rank_2_Update -- prod_2:" << std::endl; for ( uint ik = 0; ik < prod_2.rows(); ik++ ) { for ( uint jk = 0; jk < prod_2.cols(); jk++ ) { std::cerr << prod_2 ( ik, jk ) << " "; } std::cerr << std::endl; } } Matrix prod_3; prod_3.multiply ( B, prod_2 ); if ( verbose ) { std::cerr << std::endl << "KernelData::perform_Rank_2_Update -- prod_3:" << std::endl; for ( uint ik = 0; ik < prod_3.rows(); ik++ ) { for ( uint jk = 0; jk < prod_3.cols(); jk++ ) { std::cerr << prod_3 ( ik, jk ) << " "; } std::cerr << std::endl; } } inverseKernelMatrix = inverseKernelMatrix - prod_3; // std::cerr << "perform rank 2 update: inverseKernelMatrix: " << inverseKernelMatrix << std::endl; //correct the stored kernel matrix after our computations for ( uint i = 0; i < newRow.size(); i++ ) { kernelMatrix ( i, rowIndex ) = newRow[i]; kernelMatrix ( rowIndex, i ) = newRow[i]; } // std::cerr << "kernelMatrix: " << kernelMatrix << std::endl << " newRow: " << newRow << std::endl; } /** * @brief Updates the GP-Likelihood, if only k rows and colums are changed. Time is O(k^3+n^2) instaed O(n^3). Alternatively it could also be done with iteratively change one row, which leads to O(k*n^2). * @author Alexander Lütz * @date 01/09/2011 (dd/mm/yyyy) */ void KernelData::perform_Rank_2k_Update ( const std::vector & rowIndices, const std::vector & newRows ) { if ( ( rowIndices.size() != 0 ) && ( rowIndices.size() == newRows.size() ) ) { std::vector unity_vectors; std::vector diff_vectors; for ( uint j = 0; j < rowIndices.size(); j++ ) { NICE::Vector unity_vector ( newRows[0].size(), 0.0 ); unity_vector[rowIndices[j] ] = 1.0; unity_vectors.push_back ( unity_vector ); NICE::Vector a = newRows[j] - kernelMatrix.getRow ( rowIndices[j] ); for ( uint x = 0; x < rowIndices.size(); x++ ) { a[rowIndices[x] ] /= 2.0; } diff_vectors.push_back ( a ); } U.resize ( newRows[0].size(), 2*rowIndices.size() ); V.resize ( 2*rowIndices.size(), newRows[0].size() ); for ( uint i = 0; i < newRows[0].size(); i++ ) { for ( uint j = 0; j < rowIndices.size(); j++ ) { U ( i, rowIndices.size() + j ) = ( unity_vectors[j] ) [i]; U ( i, j ) = ( diff_vectors[j] ) [i]; V ( rowIndices.size() + j, i ) = ( diff_vectors[j] ) [i]; V ( j, i ) = ( unity_vectors[j] ) [i]; } } if ( verbose ) { std::cerr << std::endl << "KernelData::perform_Rank_2k_Update -- U: " << U << std::endl; std::cerr << std::endl << "KernelData::perform_Rank_2k_Update -- V: " << V << std::endl; } NICE::Matrix UV ( newRows[0].size(), newRows[0].size() ); UV.multiply ( U, V ); if ( verbose ) { // we have to consider the entries which are added twice for ( int x = 0; x < ( int ) rowIndices.size(); x++ ) { for ( int y = x; y < ( int ) rowIndices.size(); y++ ) { UV ( rowIndices[x] , rowIndices[y] ) /= 2.0; if ( x != y ) UV ( rowIndices[y] , rowIndices[x] ) /= 2.0; } } } if ( verbose ) { std::cerr << std::endl << "KernelData::perform_Rank_2k_Update -- UV:" << UV << std::endl; } B.resize ( newRows[0].size(), 2*rowIndices.size() ); for ( uint j = 0; j < 2*rowIndices.size(); j++ ) { NICE::Vector B_row; computeInverseKernelMultiply ( U.getColumn ( j ), B_row ); for ( uint i = 0; i < newRows[0].size(); i++ ) { B ( i, j ) = B_row[i]; } } if ( verbose ) { std::cerr << std::endl << "KernelData::perform_Rank_2k_Update -- B:" << B << std::endl; } F.resize ( 2*rowIndices.size(), 2*rowIndices.size() ); F.setIdentity(); Matrix V_B ( 2*rowIndices.size(), 2*rowIndices.size() ); V_B.multiply ( V, B ); F += V_B; if ( verbose ) { std::cerr << std::endl << "KernelData::perform_Rank_2k_Update -- F:" << F << std::endl; } //invert F! //we can't rely on methods like cholesky decomposition, since F has not to be positive definite F_inv = invert ( F ); if ( verbose ) { std::cerr << std::endl << "KernelData::perform_Rank_2k_Update -- F_inv:" << F_inv < & indices ) { if ( ( inverseKernelMatrix.rows() >= indices.size() ) && ( inverseKernelMatrix.cols() >= indices.size() ) ) { inverseKernelMatrix.deleteCols ( indices ); inverseKernelMatrix.deleteRows ( indices ); } if ( ( choleskyMatrix.rows() >= indices.size() ) && ( choleskyMatrix.cols() >= indices.size() ) ) { choleskyMatrix.deleteCols ( indices ); choleskyMatrix.deleteRows ( indices ); } if ( ( kernelMatrix.rows() >= indices.size() ) && ( kernelMatrix.cols() >= indices.size() ) ) { kernelMatrix.deleteCols ( indices ); kernelMatrix.deleteRows ( indices ); } } void KernelData::setKernelMatrix ( const NICE::Matrix & k_matrix ) { kernelMatrix = k_matrix; } void KernelData::increase_size_by_One() { NICE::Matrix new_Kernel ( kernelMatrix.rows() + 1, kernelMatrix.cols() + 1 ); new_Kernel.setBlock ( 0, 0, kernelMatrix ); for ( uint i = 0; i < kernelMatrix.rows() - 1; i++ ) { new_Kernel ( i, kernelMatrix.cols() ) = 0.0; new_Kernel ( kernelMatrix.rows(), i ) = 0.0; } new_Kernel ( kernelMatrix.rows(), kernelMatrix.cols() ) = 1.0; //NOTE Maybe it would be more efficient to work directly with pointers to the memory kernelMatrix.resize ( new_Kernel.rows(), new_Kernel.cols() ); kernelMatrix = new_Kernel; new_Kernel.setBlock ( 0, 0, inverseKernelMatrix ); //NOTE Maybe it would be more efficient to work directly with pointers to the memory inverseKernelMatrix.resize ( new_Kernel.rows(), new_Kernel.cols() ); inverseKernelMatrix = new_Kernel; new_Kernel.setBlock ( 0, 0, choleskyMatrix ); //NOTE Maybe it would be more efficient to work directly with pointers to the memory choleskyMatrix.resize ( new_Kernel.rows(), new_Kernel.cols() ); choleskyMatrix = new_Kernel; } void KernelData::increase_size_by_k ( const uint & k ) { NICE::Matrix new_Kernel ( kernelMatrix.rows() + k, kernelMatrix.cols() + k ); new_Kernel.setBlock ( 0, 0, kernelMatrix ); for ( uint i = 0; i < kernelMatrix.rows() - 1; i++ ) { for ( uint j = 0; j < k; j++ ) { new_Kernel ( i, kernelMatrix.cols() + j ) = 0.0; new_Kernel ( kernelMatrix.rows() + j, i ) = 0.0; } } for ( uint j = 0; j < k; j++ ) { new_Kernel ( kernelMatrix.rows() + j, kernelMatrix.cols() + j ) = 1.0; } //NOTE Maybe it would be more efficient to work directly with pointers to the memory kernelMatrix.resize ( new_Kernel.rows(), new_Kernel.cols() ); kernelMatrix = new_Kernel; new_Kernel.setBlock ( 0, 0, inverseKernelMatrix ); //NOTE Maybe it would be more efficient to work directly with pointers to the memory inverseKernelMatrix.resize ( new_Kernel.rows(), new_Kernel.cols() ); inverseKernelMatrix = new_Kernel; new_Kernel.setBlock ( 0, 0, choleskyMatrix ); //NOTE Maybe it would be more efficient to work directly with pointers to the memory choleskyMatrix.resize ( new_Kernel.rows(), new_Kernel.cols() ); choleskyMatrix = new_Kernel; }