/*! \brief Partial pivoted LU to construct low-rank. */ void partial_Piv_LU(const int start_Row, const int start_Col, const int n_Rows, const int n_Cols, const double tolerance, int& computed_Rank, MatrixXcd& U, MatrixXcd& V) { /********************************/ /* PURPOSE OF EXISTENCE */ /********************************/ /*! Obtains the low-rank decomposition of the matrix to a desired tolerance using the partial pivoting LU algorithm, i.e., given a sub-matrix 'A' and tolerance 'epsilon', computes matrices 'U' and 'V' such that ||A-UV||_F < epsilon. The norm is Frobenius norm. */ /************************/ /* INPUTS */ /************************/ /// start_Row - Starting row of the sub-matrix. /// start_Col - Starting column of the sub-matrix. /// n_Rows - Number of rows of the sub-matrix. /// n_Cols - Number of columns of the sub-matrix. /// tolerance - Tolerance of low-rank approximation. /************************/ /* OUTPUTS */ /************************/ /// computed_Rank - Rank obtained for the given tolerance. /// U - Matrix forming the column basis. /// V - Matrix forming the row basis. /// If the matrix is small enough, do not do anything int tolerable_Rank = 5; if (n_Cols <= tolerable_Rank){ kernel->get_Matrix(start_Row, start_Col, n_Rows, n_Cols, U); V = MatrixXcd::Identity(n_Cols, n_Cols); computed_Rank = n_Cols; return; } else if (n_Rows <= tolerable_Rank){ U = MatrixXcd::Identity(n_Rows, n_Rows); kernel->get_Matrix(start_Row, start_Col, n_Rows, n_Cols, V); computed_Rank = n_Rows; return; } vector<int> rowIndex; /// This stores the row indices, which have already been used. vector<int> colIndex; /// This stores the column indices, which have already been used. vector<VectorXcd> u; /// Stores the column basis. vector<VectorXcd> v; /// Stores the row basis. srand (time(NULL)); complex<double> max, unused_max, Gamma; /* INITIALIZATION */ /// Initialize the matrix norm and the the first row index double matrix_Norm = 0; rowIndex.push_back(0); int pivot; computed_Rank = 0; VectorXcd a, row, col; double row_Squared_Norm, row_Norm, col_Squared_Norm, col_Norm; /// Repeat till the desired tolerance is obtained do { /// Generation of the row kernel->get_Matrix_Row(start_Col, n_Cols, start_Row+rowIndex.back(), a); /// Row of the residuum and the pivot column row = a; for (int l=0; l<computed_Rank; ++l) { row = row-u[l](rowIndex.back())*v[l]; } pivot = kernel->max_Abs_Vector(row, colIndex, max); int max_tries = 100; int count = 0; int count1 = 0; /// This randomization is needed if in the middle of the algorithm the row happens to be exactly the linear combination of the previous rows. while (abs(max)<tolerance && count < max_tries) { int new_rowIndex; rowIndex.pop_back(); do { new_rowIndex = rand()%n_Rows; ++count1; } while (find(rowIndex.begin(),rowIndex.end(),new_rowIndex)!=rowIndex.end() && count1 < max_tries); count1 = 0; rowIndex.push_back(new_rowIndex); /// Generation of the row kernel->get_Matrix_Row(start_Col, n_Cols, start_Row+rowIndex.back(), a); /// Row of the residuum and the pivot column row = a; for (int l=0; l<computed_Rank; ++l) { row = row-u[l](rowIndex.back())*v[l]; } pivot = kernel->max_Abs_Vector(row, colIndex, max); ++count; } if (count == max_tries) break; count = 0; colIndex.push_back(pivot); /// Normalizing constant Gamma = 1.0/(max); /// Generation of the column kernel->get_Matrix_Col(start_Row, n_Rows, start_Col+colIndex.back(), a); /// Column of the residuum and the pivot row col = a; for (int l=0; l<computed_Rank; ++l) { col = col-v[l](colIndex.back())*u[l]; } pivot = kernel->max_Abs_Vector(col, rowIndex, unused_max); /// This randomization is needed if in the middle of the algorithm the columns happens to be exactly the linear combination of the previous columns. while (abs(max)<tolerance && count < max_tries) { colIndex.pop_back(); int new_colIndex; do { new_colIndex = rand()%n_Cols; } while (find(colIndex.begin(),colIndex.end(),new_colIndex)!=colIndex.end() && count1 < max_tries); count1 = 0; colIndex.push_back(new_colIndex); /// Generation of the column kernel->get_Matrix_Col(start_Row, n_Rows, start_Col+colIndex.back(), a); /// Column of the residuum and the pivot row col = a; for (int l=0; l<computed_Rank; ++l) { col = col-u[l](colIndex.back())*v[l]; } pivot = kernel->max_Abs_Vector(col, rowIndex, unused_max); ++count; } if (count == max_tries) break; count = 0; rowIndex.push_back(pivot); /// New vectors u.push_back(Gamma*col); v.push_back(row); /// New approximation of matrix norm row_Squared_Norm = row.squaredNorm(); row_Norm = sqrt(row_Squared_Norm); col_Squared_Norm = col.squaredNorm(); col_Norm = sqrt(col_Squared_Norm); matrix_Norm = matrix_Norm + abs(Gamma*Gamma*row_Squared_Norm*col_Squared_Norm); for (int j=0; j<computed_Rank; ++j) { matrix_Norm = matrix_Norm + 2.0*abs(u[j].dot(u.back()))*abs(v[j].dot(v.back())); } ++computed_Rank; } while (row_Norm*col_Norm > abs(max)*tolerance*matrix_Norm && computed_Rank <= fmin(n_Rows, n_Cols)); /// If the computed_Rank is close to full-rank then return the trivial full-rank decomposition if (computed_Rank>=fmin(n_Rows, n_Cols)) { if (n_Rows < n_Cols) { U = MatrixXcd::Identity(n_Rows,n_Rows); kernel->get_Matrix(start_Row, start_Col, n_Rows, n_Cols, V); computed_Rank = n_Rows; return; } else { kernel->get_Matrix(start_Row, start_Col, n_Rows, n_Cols, U); V = MatrixXcd::Identity(n_Cols,n_Cols); computed_Rank = n_Cols; return; } } U = MatrixXcd(n_Rows,computed_Rank); V = MatrixXcd(computed_Rank,n_Cols); for (int j=0; j<computed_Rank; ++j) { U.col(j) = u[j]; V.row(j) = v[j]; } };
void UnbiasedSquaredPhaseLagIndex::compute(ConnectivitySettings::IntermediateTrialData& inputData, QVector<QPair<int,MatrixXcd> >& vecPairCsdSum, QVector<QPair<int,MatrixXd> >& vecPairCsdImagSignSum, QMutex& mutex, int iNRows, int iNFreqs, int iNfft, const QPair<MatrixXd, VectorXd>& tapers) { if(inputData.vecPairCsdImagSign.size() == iNRows) { //qDebug() << "UnbiasedSquaredPhaseLagIndex::compute - vecPairCsdImagSign was already computed for this trial."; return; } inputData.vecPairCsdImagSign.clear(); int i,j; // Calculate tapered spectra if not available already // This code was copied and changed modified Utils/Spectra since we do not want to call the function due to time loss. if(inputData.vecTapSpectra.size() != iNRows) { inputData.vecTapSpectra.clear(); RowVectorXd vecInputFFT, rowData; RowVectorXcd vecTmpFreq; MatrixXcd matTapSpectrum(tapers.first.rows(), iNFreqs); QVector<Eigen::MatrixXcd> vecTapSpectra; FFT<double> fft; fft.SetFlag(fft.HalfSpectrum); for (i = 0; i < iNRows; ++i) { // Substract mean rowData.array() = inputData.matData.row(i).array() - inputData.matData.row(i).mean(); // Calculate tapered spectra if not available already for(j = 0; j < tapers.first.rows(); j++) { vecInputFFT = rowData.cwiseProduct(tapers.first.row(j)); // FFT for freq domain returning the half spectrum and multiply taper weights fft.fwd(vecTmpFreq, vecInputFFT, iNfft); matTapSpectrum.row(j) = vecTmpFreq * tapers.second(j); } inputData.vecTapSpectra.append(matTapSpectrum); } } // Compute CSD if(inputData.vecPairCsd.isEmpty()) { double denomCSD = sqrt(tapers.second.cwiseAbs2().sum()) * sqrt(tapers.second.cwiseAbs2().sum()) / 2.0; bool bNfftEven = false; if (iNfft % 2 == 0){ bNfftEven = true; } MatrixXcd matCsd = MatrixXcd(iNRows, iNFreqs); for (i = 0; i < iNRows; ++i) { for (j = i; j < iNRows; ++j) { // Compute CSD (average over tapers if necessary) matCsd.row(j) = inputData.vecTapSpectra.at(i).cwiseProduct(inputData.vecTapSpectra.at(j).conjugate()).colwise().sum() / denomCSD; // Divide first and last element by 2 due to half spectrum matCsd.row(j)(0) /= 2.0; if(bNfftEven) { matCsd.row(j).tail(1) /= 2.0; } } inputData.vecPairCsd.append(QPair<int,MatrixXcd>(i,matCsd)); inputData.vecPairCsdImagSign.append(QPair<int,MatrixXd>(i,matCsd.imag().cwiseSign())); } mutex.lock(); if(vecPairCsdSum.isEmpty()) { vecPairCsdSum = inputData.vecPairCsd; vecPairCsdImagSignSum = inputData.vecPairCsdImagSign; } else { for (int j = 0; j < vecPairCsdSum.size(); ++j) { vecPairCsdSum[j].second += inputData.vecPairCsd.at(j).second; vecPairCsdImagSignSum[j].second += inputData.vecPairCsdImagSign.at(j).second; } } mutex.unlock(); } else { if(inputData.vecPairCsdImagSign.isEmpty()) { for (i = 0; i < inputData.vecPairCsd.size(); ++i) { inputData.vecPairCsdImagSign.append(QPair<int,MatrixXd>(i,inputData.vecPairCsd.at(i).second.imag().cwiseSign())); } mutex.lock(); if(vecPairCsdImagSignSum.isEmpty()) { vecPairCsdImagSignSum = inputData.vecPairCsdImagSign; } else { for (int j = 0; j < vecPairCsdImagSignSum.size(); ++j) { vecPairCsdImagSignSum[j].second += inputData.vecPairCsdImagSign.at(j).second; } } mutex.unlock(); } } }