linearAlgebra.cpp

Go to the documentation of this file.
00001 
00013 #include <dlrCommon/exception.h>
00014 #include <dlrLinearAlgebra/linearAlgebra.h>
00015 #include <dlrLinearAlgebra/clapack.h>
00016 #include <dlrNumeric/utilities.h>
00017 
00018 
00019 namespace dlr {
00020 
00025   namespace linearAlgebra {
00026 
00027     void
00028     choleskyFactorization(const Array2D<Float64>& inputArray,
00029                           Array2D<Float64>& kArray,
00030                           bool isUpperTriangular)
00031     {
00032       // Argument checking.
00033       if(inputArray.size() == 0) {
00034         DLR_THROW3(ValueException,
00035                    "choleskyFactorization()",
00036                    "Argument inputArray cannot have zero size.");
00037       }
00038       if(inputArray.rows() != inputArray.columns()) {
00039         DLR_THROW3(ValueException,
00040                    "choleskyFactorization()",
00041                    "Argument inputArray must be square.");
00042       }
00043     
00044       // Transpose A to match LAPACK's convention.  Since inputArray
00045       // is symmetric, we don't really need to transpose!  Also, we
00046       // only need to copy the upper/lower triangular part, depending
00047       // on the value of isUpperTriangular.  For convenience, we copy
00048       // the whole matrix, though.
00049       size_t dimension = inputArray.rows();
00050       Array2D<Float64> aColumnMajor(dimension, dimension);
00051       aColumnMajor = 0.0;
00052       {
00053         Array2D<Float64>::const_iterator inPtr = inputArray.begin();
00054         Array2D<Float64>::iterator outPtr = aColumnMajor.begin();
00055         if(isUpperTriangular) {
00056           for(size_t index0 = 0; index0 < dimension; ++index0) {
00057             inPtr += index0;
00058             outPtr += index0;
00059             for(size_t index1 = index0; index1 < dimension; ++index1) {
00060               *(outPtr++) = *(inPtr++);
00061             }
00062           }
00063         } else {
00064           for(size_t index0 = 0; index0 < dimension; ++index0) {
00065             for(size_t index1 = 0; index1 <= index0; ++index1) {
00066               *(outPtr++) = *(inPtr++);
00067             }
00068             inPtr += (dimension - index0 - 1);
00069             outPtr += (dimension - index0 - 1);
00070           }
00071         }        
00072       }
00073 
00074 
00075       // Set up arguments for the LAPACK call.
00076 
00077       // Set UPLO opposite of what you might expect because LAPACK is
00078       // column major.
00079       char UPLO = isUpperTriangular ? 'L' : 'U';
00080       Int32 N = static_cast<Int32>(dimension);
00081       Int32 LDA = static_cast<Int32>(dimension);
00082       Int32 INFO;
00083 
00084       // Dispatch to the LAPACK routine that actually does the
00085       // factorization.
00086       dpotrf_(&UPLO, &N, aColumnMajor.data(), &LDA, &INFO);
00087 
00088       // Check for errors.
00089       if(INFO < 0L) {
00090         std::ostringstream message;
00091         message << "Call to dpotrf_ returns " << INFO
00092                 << ".  Something is wrong.";
00093         DLR_THROW3(ValueException, "choleskyFactorization()",
00094                    message.str().c_str());
00095       } else if(INFO > 0L) {
00096         std::ostringstream message;
00097         DLR_THROW3(ValueException, "choleskyFactorization()",
00098                    "Input matrix is not positive definite.");
00099       }
00100 
00101       // Recover the result.
00102       kArray = aColumnMajor;
00103     }
00104 
00105     
00106     Float64
00107     determinant(const Array2D<Float64>& A)
00108     {
00109       // First argument checking.
00110       if(A.columns() != A.rows()) {
00111         DLR_THROW3(ValueException,
00112                    "determinant(const Array2D<Float64>&)",
00113                    "Input array is not square.");
00114       }
00115 
00116       // In this routine, we take advantage of the fact that the
00117       // determinant of a matrix is related to the product of the
00118       // diagonal elements of its LU factorization.
00119     
00120       // Start by computing the LU factorization of A.
00121       Array2D<Float64> AColumnMajor = A.transpose();
00122       Int32 M = static_cast<Int32>(A.rows());
00123       Int32 N = static_cast<Int32>(A.columns());
00124       Int32 LDA = static_cast<Int32>(A.rows());
00125       Array1D<Int32> IPIV(M); // Really should be min(M, N), but A is square.
00126       Int32 info;
00127       dgetrf_(&M, &N, AColumnMajor.data(), &LDA, IPIV.data(), &info);
00128 
00129       // Check for errors.
00130       if(info != 0L) {
00131         std::ostringstream message;
00132         message << "Call to dgetrf_ returns " << info
00133                 << ".  Something is wrong.";
00134         DLR_THROW3(ValueException,
00135                    "determinant(const Array2D<Float64>&)",
00136                    message.str().c_str());
00137       }
00138 
00139       // Compute the product of the diagonal elements, and find out if
00140       // the determinant is equal to + or - the product of the diagonal
00141       // element. Do this second thing by counting how many row-swaps
00142       // were conducteed.
00143       Float64 determinant = 1.0;
00144       size_t swapCount = 0;
00145       for(size_t index = 0; index < AColumnMajor.rows(); ++index) {
00146         determinant *= AColumnMajor(index, index);
00147         if(IPIV[index] != static_cast<Int32>(index + 1)) {
00148           // Note(xxx): check that this is right.
00149           ++swapCount;
00150         }
00151       }
00152 
00153       // Finally, correct the sign
00154       if((swapCount % 2) == 1) {
00155         determinant *= -1.0;
00156       }
00157       return determinant;
00158     }
00159 
00160   
00161     Array1D<Float64>
00162     eigenvaluesSymmetric(const Array2D<Float64>& inputArray)
00163     {
00164       // Argument checking.
00165       if(inputArray.size() == 0) {
00166         DLR_THROW3(ValueException,
00167                    "eigenvaluesSymmetric()",
00168                    "Argument inputArray cannot have zero size.");
00169       }
00170       if(inputArray.rows() != inputArray.columns()) {
00171         DLR_THROW3(ValueException,
00172                    "eigenvaluesSymmetric()",
00173                    "Argument inputArray must be square.");
00174       }
00175     
00176       // Transpose A to match LAPACK's convention.  Since inputArray is
00177       // symmetric, we don't really need to transpose!  Also, we only
00178       // need to copy the upper triangular part.
00179       size_t dimension = inputArray.rows();
00180       Array2D<Float64> aColumnMajor(dimension, dimension);
00181       {
00182         Array2D<Float64>::const_iterator inPtr = inputArray.begin();
00183         Array2D<Float64>::iterator outPtr = aColumnMajor.begin();
00184         for(size_t index0 = 0; index0 < dimension; ++index0) {
00185           inPtr += index0;
00186           outPtr += index0;
00187           for(size_t index1 = index0; index1 < dimension; ++index1) {
00188             *(outPtr++) = *(inPtr++);
00189           }
00190         }
00191       }
00192 
00193       // Set up storage for return values.
00194       Array1D<Float64> eigenvalues(dimension);
00195 
00196       // Set up arguments for the LAPACK call.
00197       char JOBZ = 'N';  // Compute eigenvalues only.
00198       char UPLO = 'L';  // Get input from upper triangle of A.
00199       Int32 N = static_cast<Int32>(inputArray.rows());
00200       Int32 LDA = N;
00201       Float64 WORK;
00202       Int32 LWORK = -1;
00203       Int32 INFO;
00204 
00205       // Call once to request optimal workspace size.
00206       dsyev_(&JOBZ,  &UPLO, &N, aColumnMajor.data(), &LDA,
00207              eigenvalues.data(), &WORK, &LWORK, &INFO);
00208 
00209       // Check for errors.
00210       if(INFO != 0L) {
00211         std::ostringstream message;
00212         message << "First call to dsyev_ returns " << INFO
00213                 << ".  Something is wrong.";
00214         DLR_THROW3(ValueException,
00215                    "eigenvaluesSymmetric()",
00216                    message.str().c_str());
00217       }
00218     
00219       // Resize workspace.
00220       LWORK = static_cast<Int32>(WORK);
00221       Array1D<Float64> doubleWorkSpace(static_cast<size_t>(LWORK));
00222 
00223       // Call again to really compute the eigenvectors.
00224       dsyev_(&JOBZ,  &UPLO, &N, aColumnMajor.data(), &LDA,
00225              eigenvalues.data(), doubleWorkSpace.data(), &LWORK, &INFO);
00226 
00227       // Check for errors.
00228       if(INFO != 0L) {
00229         std::ostringstream message;
00230         message << "Second call to dsyev_ returns " << INFO
00231                 << ".  Something is wrong.";
00232         DLR_THROW3(ValueException,
00233                    "eigenvaluesSymmetric()",
00234                    message.str().c_str());
00235       }
00236 
00237       // Our convention differs from LAPACK's about the order of eigenvalues.
00238       std::reverse(eigenvalues.begin(), eigenvalues.end());
00239       return eigenvalues;
00240     }
00241 
00242 
00243     void
00244     eigenvectors(const Array2D<Float64>& inputArray,
00245                  Array1D< std::complex<Float64> >& eigenvalues,
00246                  Array2D< std::complex<Float64> >& eigenvectors,
00247                  bool isSortRequired)
00248     {
00249       // Argument checking.
00250       if(inputArray.size() == 0) {
00251         DLR_THROW3(ValueException,
00252                    "eigenvectors()",
00253                    "Argument inputArray cannot have zero size.");
00254       }
00255       if(inputArray.rows() != inputArray.columns()) {
00256         DLR_THROW3(ValueException,
00257                    "eigenvectors()",
00258                    "Argument inputArray must be square.");
00259       }
00260     
00261       // Transpose A to match LAPACK's convention.
00262       size_t dimension = inputArray.rows();
00263       Array2D<Float64> aTransposeColumnMajor = inputArray.copy();
00264 
00265       // Set up storage for return values.
00266       Array1D<Float64> eigenvaluesReal(dimension);
00267       Array1D<Float64> eigenvaluesImag(dimension);
00268       Array2D<Float64> leftEigenvectorsTranspose(dimension, dimension);
00269       Array2D<Float64> rightEigenvectorsTranspose(dimension, dimension);
00270 
00271       // Set up arguments for the LAPACK call.
00272       char JOBVL = 'V'; // Don compute left eigenvectors.
00273       char JOBVR = 'N'; // Don't compute right eigenvectors.
00274       Int32 N = static_cast<Int32>(dimension);
00275       Int32 LDA = static_cast<Int32>(dimension);
00276       Int32 LDVL = static_cast<Int32>(dimension);
00277       Int32 LDVR = static_cast<Int32>(dimension);
00278       Float64 WORK;
00279       Int32 LWORK = -1;
00280       Int32 INFO;
00281 
00282       // Call once to request optimal workspace size.
00283       dgeev_(&JOBVL, &JOBVR, &N, aTransposeColumnMajor.data(), &LDA,
00284              eigenvaluesReal.data(), eigenvaluesImag.data(),
00285              leftEigenvectorsTranspose.data(), &LDVL,
00286              rightEigenvectorsTranspose.data(), &LDVR,
00287              &WORK, &LWORK, &INFO);
00288 
00289       // Check for errors.
00290       if(INFO != 0L) {
00291         std::ostringstream message;
00292         message << "First call to dgeev_ returns " << INFO
00293                 << ".  Something is wrong.";
00294         DLR_THROW3(ValueException, "eigenvectors()", message.str().c_str());
00295       }
00296     
00297       // Resize workspace.
00298       LWORK = static_cast<Int32>(WORK);
00299       Array1D<Float64> doubleWorkSpace(static_cast<size_t>(LWORK));
00300 
00301       // Call again to really compute the eigenvectors.
00302       dgeev_(&JOBVL, &JOBVR, &N, aTransposeColumnMajor.data(), &LDA,
00303              eigenvaluesReal.data(), eigenvaluesImag.data(),
00304              leftEigenvectorsTranspose.data(), &LDVL,
00305              rightEigenvectorsTranspose.data(), &LDVR,
00306              doubleWorkSpace.data(), &LWORK, &INFO);
00307 
00308       // Check for errors.
00309       if(INFO != 0L) {
00310         std::ostringstream message;
00311         message << "Second call to dgeev_ returns " << INFO
00312                 << ".  Something is wrong.";
00313         DLR_THROW3(ValueException, "eigenvectors()", message.str().c_str());
00314       }
00315 
00316       // Check the sizes of the return references.
00317       if(eigenvalues.size() != dimension) {
00318         eigenvalues.reinit(dimension);
00319       }
00320       if(eigenvectors.rows() != dimension
00321          || eigenvectors.columns() != dimension) {
00322         eigenvectors.reinit(dimension, dimension);
00323       }
00324 
00325       // Copy eigenvalues and eigenvectors.
00326       for(size_t ii = 0; ii < dimension; ++ii) {
00327         eigenvalues[ii].real() = eigenvaluesReal[ii];
00328         eigenvalues[ii].imag() = eigenvaluesImag[ii];
00329         if(eigenvaluesImag[ii] == 0.0) {
00330           for(size_t jj = 0; jj < dimension; ++jj) {
00331             eigenvectors(jj, ii).real() = leftEigenvectorsTranspose(ii, jj);
00332             eigenvectors(jj, ii).imag() = 0.0;
00333           }
00334         } else {
00335           eigenvalues[ii + 1].real() = eigenvaluesReal[ii + 1];
00336           eigenvalues[ii + 1].imag() = eigenvaluesImag[ii + 1];
00337           for(size_t jj = 0; jj < dimension; ++jj) {
00338             eigenvectors(jj, ii).real() =
00339               leftEigenvectorsTranspose(ii, jj);
00340             eigenvectors(jj, ii).imag() =
00341               leftEigenvectorsTranspose(ii + 1, jj);
00342             eigenvectors(jj, ii + 1).real() =
00343               leftEigenvectorsTranspose(ii, jj);
00344             eigenvectors(jj, ii + 1).imag() =
00345               -leftEigenvectorsTranspose(ii + 1, jj);
00346           }
00347           ++ii;
00348         }
00349       }
00350 
00351       // Do eigenvalues have to be in descending order of magnitude.
00352       if(isSortRequired) {
00353         Array1D<double> magnitudes2(eigenvalues.size());
00354         for(size_t ii = 0; ii < eigenvalues.size(); ++ii) {
00355           magnitudes2[ii] = (eigenvalues[ii].real() * eigenvalues[ii].real()
00356                              + eigenvalues[ii].imag() * eigenvalues[ii].imag());
00357         }
00358         Array1D<size_t> outputOrder = argsort(magnitudes2);
00359         std::reverse(outputOrder.begin(), outputOrder.end());
00360 
00361         Array1D< std::complex<Float64> > sortedEigenvalues(eigenvalues.size());
00362         Array2D< std::complex<Float64> > sortedEigenvectors(
00363           eigenvectors.rows(), eigenvectors.columns());
00364         for(size_t ii = 0; ii < dimension; ++ii) {
00365           sortedEigenvalues[ii] = eigenvalues[outputOrder[ii]];
00366           for(size_t jj = 0; jj < dimension; ++jj) {
00367             sortedEigenvectors(jj, ii) = eigenvectors(jj, outputOrder[ii]);
00368           }
00369         }
00370         eigenvalues = sortedEigenvalues;
00371         eigenvectors = sortedEigenvectors;
00372       }
00373     }
00374 
00375     
00376     void
00377     eigenvectorsSymmetric(const Array2D<Float64>& inputArray,
00378                           Array1D<Float64>& eigenvalues,
00379                           Array2D<Float64>& eigenvectors)
00380     {
00381       // Argument checking.
00382       if(inputArray.size() == 0) {
00383         DLR_THROW3(ValueException,
00384                    "eigenvectorsSymmetric()",
00385                    "Argument inputArray cannot have zero size.");
00386       }
00387       if(inputArray.rows() != inputArray.columns()) {
00388         DLR_THROW3(ValueException,
00389                    "eigenvectorsSymmetric()",
00390                    "Argument inputArray must be square.");
00391       }
00392     
00393       // Transpose A to match LAPACK's convention.  Since inputArray is
00394       // symmetric, we don't really need to transpose!  Also, we only
00395       // need to copy the upper triangular part.
00396       size_t dimension = inputArray.rows();
00397       Array2D<Float64> aColumnMajor(dimension, dimension);
00398       {
00399         Array2D<Float64>::const_iterator inPtr = inputArray.begin();
00400         Array2D<Float64>::iterator outPtr = aColumnMajor.begin();
00401         for(size_t index0 = 0; index0 < dimension; ++index0) {
00402           inPtr += index0;
00403           outPtr += index0;
00404           for(size_t index1 = index0; index1 < dimension; ++index1) {
00405             *(outPtr++) = *(inPtr++);
00406           }
00407         }
00408       }
00409 
00410       // Set up storage for return values.
00411       Array1D<Float64> eigenvaluesTmp(dimension);
00412 
00413       // Set up arguments for the LAPACK call.
00414       char JOBZ = 'V';  // Compute eigenvalues and eigenvectors.
00415       char UPLO = 'L';  // Get input from lower triangle of A.
00416       Int32 N = static_cast<Int32>(dimension);
00417       Int32 LDA = static_cast<Int32>(dimension);
00418       Float64 WORK;
00419       Int32 LWORK = -1;
00420       Int32 INFO;
00421 
00422       // Call once to request optimal workspace size.
00423       dsyev_(&JOBZ,  &UPLO, &N, aColumnMajor.data(), &LDA,
00424              eigenvaluesTmp.data(), &WORK, &LWORK, &INFO);
00425 
00426       // Check for errors.
00427       if(INFO != 0L) {
00428         std::ostringstream message;
00429         message << "First call to dsyev_ returns " << INFO
00430                 << ".  Something is wrong.";
00431         DLR_THROW3(ValueException,
00432                    "eigenvectorsSymmetric()",
00433                    message.str().c_str());
00434       }
00435     
00436       // Resize workspace.
00437       LWORK = static_cast<Int32>(WORK);
00438       Array1D<Float64> doubleWorkSpace(static_cast<size_t>(LWORK));
00439 
00440       // Call again to really compute the eigenvectors.
00441       dsyev_(&JOBZ,  &UPLO, &N, aColumnMajor.data(), &LDA,
00442              eigenvaluesTmp.data(), doubleWorkSpace.data(), &LWORK, &INFO);
00443 
00444       // Check for errors.
00445       if(INFO != 0L) {
00446         std::ostringstream message;
00447         message << "Second call to dsyev_ returns " << INFO
00448                 << ".  Something is wrong.";
00449         DLR_THROW3(ValueException,
00450                    "eigenvectorsSymmetric()",
00451                    message.str().c_str());
00452       }
00453 
00454       // Recover the result.  Eigenvectors are left in aColumnMajor, but
00455       // unfortunately they're transposed and in the reverse order from
00456       // our convention.  How awkward.
00457 
00458       // Check the sizes of the return references.
00459       if(eigenvalues.size() != dimension) {
00460         eigenvalues.reinit(dimension);
00461       }
00462       if(eigenvectors.rows() != dimension
00463          || eigenvectors.columns() != dimension) {
00464         eigenvectors.reinit(dimension, dimension);
00465       }
00466 
00467       // Copy eigenvalues in reverse order.
00468       {
00469         Array1D<Float64>::iterator outPtr = eigenvalues.end() - 1;
00470         Array1D<Float64>::iterator inPtr0 = eigenvaluesTmp.begin();
00471         Array1D<Float64>::iterator inPtr1 = eigenvaluesTmp.end();
00472         while(inPtr0 != inPtr1) {
00473           *outPtr = *inPtr0;
00474           ++inPtr0;
00475           --outPtr;
00476         }
00477       }
00478 
00479       // Transpose and reverse and copy, oh my!
00480       for(size_t index0 = 0; index0 < dimension; ++index0) {
00481         Float64* outPtr = eigenvectors.data(index0);
00482         Float64* inPtr = aColumnMajor.data((dimension - index0 - 1) * dimension);
00483         for(size_t index1 = 0; index1 < dimension; ++index1) {
00484           *outPtr = *inPtr;
00485           ++inPtr;
00486           outPtr += dimension;
00487         }
00488       }
00489     }
00490 
00491 
00492     Array2D<Float64>
00493     inverse(const Array2D<Float64>& A)
00494     {
00495       // First argument checking.
00496       if(A.columns() != A.rows()) {
00497         DLR_THROW3(ValueException,
00498                    "inverse(const Array2D<Float64>&)",
00499                    "Input array is not square.");
00500       }
00501       // Now set up some linear equations to solve.
00502       Array2D<Float64> AInverse =
00503         identity(A.rows(), A.rows(), type_tag<Float64>());
00504       Array2D<Float64> AA = A.transpose();
00505       // And solve for the inverse matrix.
00506       linearSolveInPlace(AA, AInverse); //Modifies AInverse.
00507       return AInverse;
00508     }
00509 
00510 
00511     // This function computes the best linear fit between the two input
00512     // arrays.
00513     std::pair<Float64, Float64>
00514     linearFit(const Array1D<Float64>& array0,
00515               const Array1D<Float64>& array1)
00516     {
00517       // We're looking for constants a and b which most nearly (in the
00518       // least squares sense) satisfy the equation
00519       //
00520       //   a * array0 + b = array1
00521       //
00522       // Which can be rewritten
00523       //
00524       //   [array0[0], 1]   [a] = [array1[0]]
00525       //   [array0[1], 1] * [b]   [array1[1]]
00526       //   [array0[2], 1]         [array1[2]]
00527       //   ...                    ...
00528       // 
00529       // Solving this using the Moore-Penrose pseudoinverse gives
00530       //
00531       //                                             -1
00532       //   [a]  =  [dot(array0, array0), sum(array0)]  * [dot(array0, array1)]
00533       //   [b]     [sum(array0),         N          ]    [sum(array1)        ]
00534 
00535       // First some argument checking.
00536       if(array0.size() != array1.size()) {
00537         std::ostringstream message;
00538         message << "Arguments array0 and array1 must have the same size, "
00539                 << "but are of size " << array0.size()
00540                 << " and " << array1.size() << " respectively." << std::endl;
00541         DLR_THROW3(ValueException,
00542                    "linearFit(const Array1D<Float64>&, const Array1D<Float64>&)",
00543                    message.str().c_str());                 
00544       }
00545       if(array0.size() == 0) {
00546         DLR_THROW3(ValueException,
00547                    "linearFit(const Array1D<Float64>&, const Array1D<Float64>&)",
00548                    "Arguments cannot have zero size.");
00549       }
00550 
00551       // Do linear regression.
00552       Array2D<Float64> AMatrix(2, 2);
00553       AMatrix(0, 0) = dot(array0, array0);
00554       AMatrix(0, 1) = sum(array0);
00555       AMatrix(1, 0) = sum(array0);
00556       AMatrix(1, 1) = array0.size();
00557 
00558       Array1D<Float64> bVector(2);
00559       bVector(0) = dot(array0, array1);
00560       bVector(1) = sum(array1);
00561     
00562       Array2D<Float64> AInverse = inverse(AMatrix);
00563 
00564       Array1D<Float64> result = matrixMultiply(AInverse, bVector);
00565       return std::make_pair(result(0), result(1));
00566     }
00567 
00568   
00569     // This function solves the system of equations A*x = b, where A and
00570     // b are known Array2D<double> instances.
00571     Array1D<Float64>
00572     linearLeastSquares(const Array2D<Float64>& A, const Array1D<Float64>& b)
00573     {
00574       // First some argument checking.
00575       if(A.size() == 0) {
00576         DLR_THROW3(
00577           ValueException,
00578           "linearLeastSquares(const Array2D<Float64>&, const Array1D<Float64>&)",
00579           "Input array A must have nonzero size.");
00580       }
00581       if(A.rows() != b.size()) {
00582         DLR_THROW3(
00583           ValueException,
00584           "linearLeastSquares(const Array2D<Float64>&, const Array1D<Float64>&)",
00585           "The number of rows in input array A must be the same as the number "
00586           "of elements in b.");
00587       }
00588 
00589       // This two-line implementation is slow.
00590       // Array2D<Float64> APInv = pseudoinverse(A);
00591       // return matrixMultiply(APInv, b);
00592 
00593       // Set up scalar arguments for the LAPACK routine.
00594       char trans = 'N';
00595       Int32 rows = static_cast<Int32>(A.rows());
00596       Int32 columns = static_cast<Int32>(A.columns());
00597       Int32 nrhs = static_cast<Int32>(1);
00598       Int32 ldb = static_cast<Int32>(std::max(rows, columns));
00599       Float64 temporaryWorkspace;
00600       Int32 lwork = -1;  // Request info on optimal workspace size.
00601       Int32 info;
00602 
00603       // Set up array arguments for the LAPACK routine.
00604       Array2D<Float64> AColumnMajor = A.transpose();
00605       Array1D<Float64> bCopy(ldb);
00606       std::copy(b.begin(), b.end(), bCopy.begin());
00607     
00608       // Now invoke the LAPACK routine to find the optimal workspace
00609       // size.
00610       dgels_(&trans, &rows, &columns, &nrhs, AColumnMajor.data(), &rows,
00611              bCopy.data(), &ldb, &temporaryWorkspace, &lwork, &info);
00612 
00613       // Check for errors.
00614       if(info != 0L) {
00615         std::ostringstream message;
00616         message << "First call to dgels_ returns " << info
00617                 << ".  Something is wrong.";
00618         DLR_THROW3(ValueException,
00619                    "linearLeastSquares()",
00620                    message.str().c_str());
00621       }
00622     
00623       // Resize workspace.
00624       lwork = static_cast<Int32>(temporaryWorkspace);
00625       Array1D<Float64> doubleWorkspace(static_cast<size_t>(lwork));
00626 
00627       // Call again to solve the system of equations.
00628       dgels_(&trans, &rows, &columns, &nrhs, AColumnMajor.data(), &rows,
00629              bCopy.data(), &ldb, doubleWorkspace.data(), &lwork, &info);
00630     
00631       // Check for errors.
00632       if(info != 0L) {
00633         std::ostringstream message;
00634         message << "Second call to dgels_ returns " << info
00635                 << ".  Something is wrong.";
00636         DLR_THROW3(ValueException,
00637                    "linearLeastSquares()",
00638                    message.str().c_str());
00639       }
00640 
00641       if(bCopy.size() == A.columns()) {
00642         return bCopy;
00643       } else {
00644         Array1D<Float64> returnValue(A.columns());
00645         std::copy(bCopy.begin(), bCopy.begin() + A.columns(),
00646                   returnValue.begin());
00647         return returnValue;
00648       }
00649     }
00650 
00651 
00652     // WARNING:  linearSolveInPlace() destructively modifies 
00653     // both arguments!
00654     //
00655     // This function solves the system of equations A*x = b, where A is
00656     // a known matrix, and b is a known vector.
00657     void
00658     linearSolveInPlace(Array2D<Float64>& A, Array1D<Float64>& b)
00659     {
00660       Array2D<Float64> bMatrix(b.size(), 1, b.data());
00661       linearSolveInPlace(A, bMatrix);
00662     }
00663 
00664 
00665     // This function is identical to linearSolveInPlace(Array2D<Float64>,
00666     // Array1D<Float64>&), except that b (and therefore x) is not
00667     // constrained to be a vector.
00668     void
00669     linearSolveInPlace(Array2D<Float64> &A, Array2D<Float64> &b)
00670     {
00671       // First some argument checking.
00672       if(A.rows() != b.rows()) {
00673         DLR_THROW3(ValueException,
00674                    "linearSolveInPlace(Array2D<Float64>&, Array2D<Float64>&)",
00675                    "Input arrays A and b must have the same number of rows.");
00676       }
00677       if(A.rows() != A.columns()) {
00678         DLR_THROW3(ValueException,
00679                    "linearSolveInPlace(Array2D<Float64>&, Array2D<Float64>&)",
00680                    "Input array A must be square.");
00681       }
00682 
00683       // Grr.  Have to transpose to match lapack.
00684       Array2D<Float64> AColumnMajor = A.transpose();
00685     
00686       // Now invoke the LAPACK routine.
00687       Int32 rows = static_cast<Int32>(A.rows());
00688       Int32 xColumns = static_cast<Int32>(b.columns());
00689       Int32 *iPiv = new Int32[rows];
00690       Int32 info;
00691       dgesv_(&rows, &xColumns, AColumnMajor.data(), &rows, iPiv, b.data(), &rows,
00692              &info );
00693       // Clean up
00694       delete[] iPiv;
00695       // And do some error checking.
00696       if(info != 0L) {
00697         std::ostringstream message;
00698         message << "Call to dgesv_ returns " << info << ".  Something is wrong."
00699                 << "  Perhaps the the input equations are poorly conditioned "
00700                 << "or perhaps there is no solution.";
00701         DLR_THROW3(ValueException,
00702                    "linearSolveInPlace(Array2D<Float64>&, Array2D<Float64>&)",
00703                    message.str().c_str());
00704       }
00705     }
00706 
00707 
00708     // This function solves the system of equations A*x = b, where A is
00709     // a known tridiagonal matrix and b is a known vector.
00710     Array1D<Float64>
00711     linearSolveTridiagonal(const Array1D<Float64>& subDiagonal,
00712                            const Array1D<Float64>& centerDiagonal,
00713                            const Array1D<Float64>& superDiagonal,
00714                            const Array1D<Float64>& bVector)
00715     {
00716       // First some argument checking.
00717       if(subDiagonal.size() != superDiagonal.size()) {
00718         DLR_THROW3(ValueException,
00719                    "linearSolveTridiagonal()",
00720                    "Input arguments subDiagonal and superDiagonal "
00721                    "must have the same size.");
00722       }
00723       if(centerDiagonal.size() != bVector.size()) {
00724         DLR_THROW3(ValueException,
00725                    "linearSolveTridiagonal()",
00726                    "Input arguments centerDiagonal and bVector "
00727                    "must have the same size.");
00728       }
00729       if(centerDiagonal.size() != subDiagonal.size() + 1) {
00730         DLR_THROW3(ValueException,
00731                    "linearSolveTridiagonal()",
00732                    "Input argument centerDiagonal must have one more element "
00733                    "than input argument subDiagonal.");
00734       }
00735 
00736     
00737       // Now invoke the LAPACK routine.
00738       Int32 N = static_cast<Int32>(centerDiagonal.size());
00739       Int32 NRHS = 1;
00740       Array1D<Float64> subDiagonalCopy = subDiagonal.copy();
00741       Array1D<Float64> centerDiagonalCopy = centerDiagonal.copy();
00742       Array1D<Float64> superDiagonalCopy = superDiagonal.copy();
00743       Array1D<Float64> xVector = bVector.copy();
00744       Int32 INFO;
00745 
00746       dgtsv_(&N, &NRHS, subDiagonalCopy.data(), centerDiagonalCopy.data(),
00747              superDiagonalCopy.data(), xVector.data(), &N, &INFO);
00748 
00749       // And do some error checking.
00750       if(INFO != 0L) {
00751         std::ostringstream message;
00752         message << "Call to dgtsv_ returns " << INFO << ".  Something is wrong."
00753                 << "  Perhaps the the input equations are poorly conditioned "
00754                 << "or perhaps there is no solution.";
00755         DLR_THROW3(ValueException,
00756                    "linearSolveTridiagonal()",
00757                    message.str().c_str());
00758       }
00759 
00760       return xVector;
00761     }
00762     
00763 
00764     // This function computes the QR factorization of a general
00765     // matrix.
00766     void
00767     qrFactorization(const Array2D<Float64>& inputArray,
00768                     Array2D<Float64>& qArray,
00769                     Array2D<Float64>& rArray)
00770     {
00771       // Argument checking.
00772       if(inputArray.size() == 0) {
00773         qArray.clear();
00774         rArray.clear();
00775         return;
00776       }
00777 
00778       // Set up scalar arguments for the LAPACK routine.
00779       Int32 rows = static_cast<Int32>(inputArray.rows());
00780       Int32 columns = static_cast<Int32>(inputArray.columns());
00781       Int32 lda = rows;
00782       Float64 temporaryWorkspace;
00783       Int32 lwork = -1;  // Request info on optimal workspace size.
00784       Int32 info;
00785 
00786       // Set up array arguments for the LAPACK routine.
00787       Array2D<Float64> AColumnMajor = inputArray.transpose();
00788       Array1D<Float64> tauArray = Array1D<Float64>(std::min(rows, columns));
00789     
00790       // Now invoke the LAPACK routine to find the optimal workspace
00791       // size.
00792       dgeqrf_(&rows, &columns, AColumnMajor.data(), &lda, tauArray.data(),
00793              &temporaryWorkspace, &lwork, &info);
00794 
00795       // Check for errors.
00796       if(info != 0L) {
00797         std::ostringstream message;
00798         message << "First call to dgeqrf_ returns " << info
00799                 << ".  Something is wrong.";
00800         DLR_THROW3(ValueException, "qrFactorization()", message.str().c_str());
00801       }
00802     
00803       // Resize workspace.
00804       lwork = static_cast<Int32>(temporaryWorkspace);
00805       Array1D<Float64> doubleWorkspace(static_cast<size_t>(lwork));
00806 
00807       // Call again to solve the system of equations.
00808       dgeqrf_(&rows, &columns, AColumnMajor.data(), &lda, tauArray.data(),
00809              doubleWorkspace.data(), &lwork, &info);
00810     
00811       // Check for errors.
00812       if(info != 0L) {
00813         std::ostringstream message;
00814         message << "Second call to dgeqrf_ returns " << info
00815                 << ".  Something is wrong.";
00816         DLR_THROW3(ValueException, "qrFactorization()", message.str().c_str());
00817       }
00818 
00819       // Prepare reference arguments to receive the result.
00820       if((rArray.rows() != inputArray.rows())
00821          || (rArray.columns() != inputArray.columns())) {
00822         rArray.reinit(inputArray.rows(), inputArray.columns());
00823       }
00824       rArray = 0.0;
00825       if((qArray.rows() != inputArray.rows())
00826          || (qArray.columns() != inputArray.rows())) {
00827         qArray.reinit(inputArray.rows(), inputArray.rows());
00828       }
00829       qArray = 0.0;
00830 
00831       // Recover the upper trapezoidal matrix R.
00832       size_t numNonzeroRows = std::min(inputArray.rows(), inputArray.columns());
00833       for(size_t row = 0; row < numNonzeroRows; ++row) {
00834         for(size_t column = row; column < rArray.columns(); ++column) {
00835           rArray(row, column) = AColumnMajor(column, row);
00836         }
00837       }
00838 
00839       // Recover the orthogonal matrix Q.  Here's a quote from the lapack code:
00840       //
00841       // *  The matrix Q is represented as a product of elementary reflectors
00842       // *
00843       // *     Q = H(1) H(2) . . . H(k), where k = min(m,n).
00844       // *
00845       // *  Each H(i) has the form
00846       // *
00847       // *     H(i) = I - tau * v * v'
00848       // *
00849       // *  where tau is a real scalar, and v is a real vector with
00850       // *  v(1:i-1) = 0 and v(i) = 1; v(i+1:m) is stored on exit in A(i+1:m,i),
00851       // *  and tau in TAU(i).
00852 
00853       // Start with identity.
00854       for(size_t row = 0; row < qArray.rows(); ++row) {
00855         qArray(row, row) = static_cast<Float64>(1.0);
00856       }
00857       
00858       // Now subtract out the tau * v * v^T terms.
00859       Array1D<Float64> vArray(qArray.rows());
00860       Array2D<Float64> reflector(qArray.rows(), qArray.rows());
00861       for(size_t ii = 0; ii < numNonzeroRows; ++ii) {
00862         // Get ready to compute the next factor.  Could do this more
00863         // efficiently.
00864         reflector = 0.0;
00865         for(size_t kk = 0; kk < reflector.rows(); ++kk) {
00866           reflector(kk, kk) = 1.0;
00867         }
00868         
00869         // Compute tau and v.
00870         Float64 tau = tauArray[ii];
00871         // We don't need a loop here because the zeros from last
00872         // iteration stick around.
00873         // 
00874         // for(size_t jj = 0; jj < ii; ++jj) {
00875         //   vArray[jj] = 0.0;
00876         // }
00877         if(ii > 0) {
00878           vArray[ii - 1] = 0.0;
00879         }
00880         vArray[ii] = 1.0;
00881         for(size_t jj = ii + 1; jj < qArray.rows(); ++jj) {
00882           vArray[jj] = AColumnMajor(ii, jj);
00883         }
00884 
00885         // Subtract tau * v * v^T.
00886         for(size_t row = ii; row < qArray.rows(); ++row) {
00887           for(size_t column = ii; column < qArray.columns(); ++column) {
00888             reflector(row, column) -= tau * vArray[row] * vArray[column];
00889           }
00890         }
00891 
00892         qArray = matrixMultiply(qArray, reflector);
00893       }
00894 
00895       // Make sure diagonal elements of rArray are non-negative, as
00896       // promised.
00897       for(size_t ii = 0; ii < inputArray.rows(); ++ii) {
00898         if(rArray(ii, ii) == 0.0) {
00899           continue;
00900         }
00901         if(rArray(ii, ii) < 0.0) {
00902           rArray *= -1.0;
00903           qArray *= -1.0;
00904           break;
00905         }
00906       }
00907     }
00908 
00909     
00910     // This function accepts an Array2D<Float64> instance having at least
00911     // as many rows as columns, and returns the Moore-Penrose
00912     // pseudoinverse.
00913     Array2D<Float64>
00914     pseudoinverse(const Array2D<Float64>& A)
00915     {
00916       Array2D<Float64> ATranspose = A.transpose();
00917       Array2D<Float64> ATA = matrixMultiply(ATranspose, A);
00918       return matrixMultiply(inverse(ATA), ATranspose);
00919     }
00920 
00921 
00922     // This function computes the singular value decomposition of a
00923     // matrix.
00924     void
00925     singularValueDecomposition(const Array2D<Float64>& inputArray,
00926                                Array2D<Float64>& uArray,
00927                                Array1D<Float64>& sigmaArray,
00928                                Array2D<Float64>& vTransposeArray,
00929                                bool isNullSpaceRequired,
00930                                bool isFullRangeRequired)
00931     {
00932       // Argument checking.
00933       if(inputArray.size() == 0) {
00934         DLR_THROW3(ValueException,
00935                    "singularValueDecomposition()",
00936                    "Argument inputArray cannot have zero size.");
00937       }
00938 
00939       // Note(xxx): Fix things so that isFullRangeRequired is not ignored.
00940       isFullRangeRequired = isNullSpaceRequired;
00941       
00942       // 
00943       // We do not transpose A. Instead, we let LAPACK operate on the
00944       // untransposed matrix as if it were column major and then swap U
00945       // and VT at the end.  We make a copy here since the contents of
00946       // whatever array we pass to LAPACK will be destroyed.
00947       Array2D<Float64> aColumnMajor = inputArray.copy();
00948 
00949       // Set up storage for return values.
00950       size_t numberOfSingularValues =
00951         std::min(inputArray.rows(), inputArray.columns());
00952 
00953       size_t numberOfVRows;
00954       if(isNullSpaceRequired) {
00955         numberOfVRows = inputArray.columns();
00956       } else {
00957         numberOfVRows = numberOfSingularValues;
00958       }
00959       if((vTransposeArray.rows() != numberOfVRows)
00960          || (vTransposeArray.columns() != inputArray.columns())) {
00961         vTransposeArray.reinit(numberOfVRows, inputArray.columns());
00962       }
00963       // Shallow copy.
00964       Array2D<Float64> uColumnMajor = vTransposeArray;
00965 
00966       size_t numberOfUColumns;
00967       if(isFullRangeRequired) {
00968         numberOfUColumns = inputArray.rows();
00969       } else {
00970         numberOfUColumns = numberOfSingularValues;
00971       }
00972       if((uArray.rows() != inputArray.rows())
00973          || (uArray.columns() != numberOfUColumns)) {
00974         uArray.reinit(inputArray.rows(), numberOfUColumns);
00975       }
00976       // Shallow copy.
00977       Array2D<Float64> vTColumnMajor = uArray;
00978 
00979       if(sigmaArray.size() != numberOfSingularValues) {
00980         sigmaArray.reinit(numberOfSingularValues);
00981       }
00982       Array1D<Int32> integerWorkSpace(8 * numberOfSingularValues);
00983     
00984       // Set up arguments for the LAPACK call.
00985       char JOBZ;
00986       if(isNullSpaceRequired) {
00987         JOBZ = 'A';
00988       } else {
00989         JOBZ = 'S';  // Compute only min(M, N) columns of U and rows of VT.
00990       }
00991       Int32 M = static_cast<Int32>(inputArray.columns());
00992       Int32 N = static_cast<Int32>(inputArray.rows());
00993       Int32 LDA = M;
00994       Int32 LDU = M;
00995       Int32 LDVT = static_cast<Int32>(numberOfSingularValues);
00996       Float64 WORK;
00997       Int32 LWORK = -1;  // Request info on optimal workspace size.
00998       Int32 INFO;
00999 
01000       // Call once to request optimal workspace size.
01001       dgesdd_(&JOBZ, &M, &N, aColumnMajor.data(), &LDA,
01002               sigmaArray.data(), uColumnMajor.data(), &LDU,
01003               vTColumnMajor.data(), &LDVT,
01004               &WORK, &LWORK,
01005               integerWorkSpace.data(), &INFO);
01006 
01007       // Check for errors.
01008       if(INFO != 0L) {
01009         std::ostringstream message;
01010         message << "First call to dgesdd_ returns " << INFO
01011                 << ".  Something is wrong.";
01012         DLR_THROW3(ValueException,
01013                    "singularValueDecomposition()",
01014                    message.str().c_str());
01015       }
01016     
01017       // Resize workspace.
01018       LWORK = static_cast<Int32>(WORK);
01019       Array1D<Float64> doubleWorkSpace(static_cast<size_t>(LWORK));
01020 
01021       // Call again to do the SVD.
01022       dgesdd_(&JOBZ, &M, &N, aColumnMajor.data(), &LDA,
01023               sigmaArray.data(), uColumnMajor.data(), &LDU,
01024               vTColumnMajor.data(), &LDVT,
01025               doubleWorkSpace.data(), &LWORK,
01026               integerWorkSpace.data(), &INFO);
01027 
01028       // Check for errors.
01029       if(INFO != 0L) {
01030         std::ostringstream message;
01031         message << "Second call to dgesdd_ returns " << INFO
01032                 << ".  Something is wrong.";
01033         DLR_THROW3(ValueException,
01034                    "singularValueDecomposition()",
01035                    message.str().c_str());
01036       }
01037     } 
01038 
01039 
01040 
01041     void
01042     singularValueDecompositionSimple(const Array2D<Float64>& inputArray,
01043                                      Array2D<Float64>& uArray,
01044                                      Array1D<Float64>& sigmaArray,
01045                                      Array2D<Float64>& vTransposeArray)
01046     {
01047       // Argument checking.
01048       if(inputArray.size() == 0) {
01049         DLR_THROW3(ValueException,
01050                    "singularValueDecomposition()",
01051                    "Argument inputArray cannot have zero size.");
01052       }
01053     
01054       // We do not transpose A. Instead, we let LAPACK operate on the
01055       // untransposed matrix as if it were column major and then swap U
01056       // and VT at the end.  We make a copy here since the contents of
01057       // whatever array we pass to LAPACK will be destroyed.
01058       Array2D<Float64> aColumnMajor = inputArray.copy();
01059 
01060       // Set up storage for return values.
01061       size_t numberOfSingularValues =
01062         std::min(inputArray.rows(), inputArray.columns());
01063 
01064       // Make sure V is appropriately sized.
01065       if((vTransposeArray.rows() != numberOfSingularValues)
01066          || (vTransposeArray.columns() != inputArray.columns())) {
01067         vTransposeArray.reinit(numberOfSingularValues, inputArray.columns());
01068       }
01069 
01070       // Shallow copy.
01071       Array2D<Float64> uColumnMajor = vTransposeArray;
01072 
01073       // Make sure U is appropriately sized.
01074       if((uArray.rows() != inputArray.rows())
01075          || (uArray.columns() != numberOfSingularValues)) {
01076         uArray.reinit(inputArray.rows(), numberOfSingularValues);
01077       }
01078     
01079       // Shallow copy.
01080       Array2D<Float64> vTColumnMajor = uArray;
01081 
01082       if(sigmaArray.size() != numberOfSingularValues) {
01083         sigmaArray.reinit(numberOfSingularValues);
01084       }
01085 
01086       // Integer workspace.
01087       Array1D<Int32> integerWorkspace(8 * numberOfSingularValues);
01088 
01089       // Set up arguments for the LAPACK call.
01090       char JOBZ = 'S';  // Compute only min(M, N) columns of U.
01091       Int32 M = static_cast<Int32>(inputArray.columns());
01092       Int32 N = static_cast<Int32>(inputArray.rows());
01093       Int32 LDA = M;
01094       Int32 LDU = M;
01095       Int32 LDVT = static_cast<Int32>(numberOfSingularValues);
01096       Float64 WORK;
01097       Int32 LWORK = -1;  // Request info on optimal workspace size.
01098       Int32 INFO;
01099 
01100       // Call once to request optimal workspace size.
01101       dgesdd_(&JOBZ, &M, &N, aColumnMajor.data(), &LDA,
01102               sigmaArray.data(), uColumnMajor.data(), &LDU,
01103               vTColumnMajor.data(), &LDVT,
01104               &WORK, &LWORK, integerWorkspace.data(), &INFO);
01105 
01106       // Check for errors.
01107       if(INFO != 0L) {
01108         std::ostringstream message;
01109         message << "First call to dgesdd_ returns " << INFO
01110                 << ".  Something is wrong.";
01111         DLR_THROW3(ValueException,
01112                    "singularValueDecomposition()",
01113                    message.str().c_str());
01114       }
01115     
01116       // Resize workspace.
01117       LWORK = static_cast<Int32>(WORK);
01118       Array1D<Float64> doubleWorkSpace(static_cast<size_t>(LWORK));
01119 
01120       // Call again to do the SVD.
01121       dgesdd_(&JOBZ, &M, &N, aColumnMajor.data(), &LDA,
01122               sigmaArray.data(), uColumnMajor.data(), &LDU,
01123               vTColumnMajor.data(), &LDVT,
01124               doubleWorkSpace.data(), &LWORK, integerWorkspace.data(), &INFO);
01125 
01126       // Check for errors.
01127       if(INFO != 0L) {
01128         std::ostringstream message;
01129         message << "Second call to dgesdd_ returns " << INFO
01130                 << ".  Something is wrong.";
01131         DLR_THROW3(ValueException,
01132                    "singularValueDecomposition()",
01133                    message.str().c_str());
01134       }
01135     } 
01136 
01137   
01138 //   // This function computes the singular value decomposition of a
01139 //   // matrix, but is less efficient than the routine above: It
01140 //   // explicitly transposes the matrix before passing it to LAPACK.
01141 //   void
01142 //   singularValueDecomposition(
01143 //     const Array2D<double>& inputArray,
01144 //     Array2D<double>& uArray,
01145 //     Array1D<double>& sigmaArray,
01146 //     Array2D<double>& vTransposeArray)
01147 //   {
01148 //     // Argument checking.
01149 //     if(inputArray.size() == 0) {
01150 //       DLR_THROW3(ValueException,
01151 //                  "singularValueDecomposition()",
01152 //                  "Argument inputArray cannot have zero size.");
01153 //     }
01154     
01155 //     // Transpose A to match LAPACK's convention.
01156 //     Array2D<double> aColumnMajor = inputArray.transpose();
01157 
01158 //     // Set up storage for return values.
01159 //     size_t numberOfSingularValues =
01160 //       std::min(inputArray.rows(), inputArray.columns());
01161 //     Array2D<double> uColumnMajor(numberOfSingularValues, inputArray.rows());
01162 //     Array2D<double> vTColumnMajor(
01163 //       inputArray.columns(), numberOfSingularValues);
01164 //     if(sigmaArray.size() != numberOfSingularValues) {
01165 //       sigmaArray.reinit(numberOfSingularValues);
01166 //     }
01167 //     Array1D<Int32> integerWorkSpace(8 * numberOfSingularValues);
01168     
01169 //     // Set up arguments for the LAPACK call.
01170 //     char JOBZ = 'S';  // Compute only min(M, N) columns of U and rows of VT.
01171 //     Int32 M = static_cast<Int32>(inputArray.rows());
01172 //     Int32 N = static_cast<Int32>(inputArray.columns());
01173 //     Int32 LDA = M;
01174 //     Int32 LDU = M;
01175 //     Int32 LDVT = static_cast<Int32>(numberOfSingularValues);
01176 //     double WORK;
01177 //     Int32 LWORK = -1;  // Request info on optimal workspace size.
01178 //     Int32 INFO;
01179 
01180 //     // Call once to request optimal workspace size.
01181 //     dgesdd_(&JOBZ, &M, &N, aColumnMajor.data(), &LDA,
01182 //             sigmaArray.data(), uColumnMajor.data(), &LDU,
01183 //             vTColumnMajor.data(), &LDVT,
01184 //             &WORK, &LWORK,
01185 //             integerWorkSpace.data(), &INFO);
01186 
01187 //     // Check for errors.
01188 //     if(INFO != 0L) {
01189 //       std::ostringstream message;
01190 //       message << "First call to dgesdd_ returns " << INFO
01191 //               << ".  Something is wrong.";
01192 //       DLR_THROW3(ValueException,
01193 //                  "singularValueDecomposition()",
01194 //                  message.str().c_str());
01195 //     }
01196     
01197 //     // Resize workspace.
01198 //     LWORK = static_cast<Int32>(WORK);
01199 //     Array1D<double> doubleWorkSpace(static_cast<size_t>(LWORK));
01200 
01201 //     // Call again to do the SVD.
01202 //     dgesdd_(&JOBZ, &M, &N, aColumnMajor.data(), &LDA,
01203 //             sigmaArray.data(), uColumnMajor.data(), &LDU,
01204 //             vTColumnMajor.data(), &LDVT,
01205 //             doubleWorkSpace.data(), &LWORK,
01206 //             integerWorkSpace.data(), &INFO);
01207 
01208 //     // Check for errors.
01209 //     if(INFO != 0L) {
01210 //       std::ostringstream message;
01211 //       message << "Second call to dgesdd_ returns " << INFO
01212 //               << ".  Something is wrong.";
01213 //       DLR_THROW3(ValueException,
01214 //                  "singularValueDecomposition()",
01215 //                  message.str().c_str());
01216 //     }
01217 
01218 //     // Recover the result.
01219 //     uArray = uColumnMajor.transpose();
01220 //     vTransposeArray = vTColumnMajor.transpose();
01221 //   } 
01222   
01223 
01224     // This function computes the singular values a matrix without
01225     // computing the associated U and V matrices.
01226     Array1D<Float64>
01227     singularValues(const Array2D<Float64>& inputArray)
01228     {
01229       // Argument checking.
01230       if(inputArray.size() == 0) {
01231         DLR_THROW3(ValueException,
01232                    "singularValues()",
01233                    "Argument inputArray cannot have zero size.");
01234       }
01235     
01236       // Transpose A to match LAPACK's convention.
01237       //
01238       // Actually, it's slightly quicker not to transpose, and singular
01239       // values remain the same, so we just copy.
01240       // 
01241       // Array2D<Float64> aColumnMajor = inputArray.transpose();
01242       Array2D<Float64> aColumnMajor = inputArray.copy();
01243 
01244       // Set up storage for return values.
01245       size_t numberOfSingularValues =
01246         std::min(inputArray.rows(), inputArray.columns());
01247       Array1D<Float64> sigmaArray(numberOfSingularValues);
01248       Array1D<Int32> integerWorkSpace(8 * numberOfSingularValues);
01249     
01250       // Set up arguments for the LAPACK call.
01251       char JOBZ = 'N';  // Compute no columns of U and no rows of VT.
01252       // Since we didn't transpose, we have to reverse rows & columns.
01253       // Int32 M = static_cast<Int32>(inputArray.rows());
01254       // Int32 N = static_cast<Int32>(inputArray.columns());
01255       Int32 M = static_cast<Int32>(inputArray.columns());
01256       Int32 N = static_cast<Int32>(inputArray.rows());
01257       Int32 LDA = M;
01258       Float64 U;         // U is not referenced by the LAPACK call.
01259       Int32 LDU = 1;
01260       Float64 VT;        // VT is not referenced by the LAPACK call.
01261       Int32 LDVT = 1;
01262       Float64 WORK;
01263       Int32 LWORK = -1;  // Request info on optimal workspace size.
01264       Int32 INFO;
01265 
01266       // Call once to request optimal workspace size.
01267       dgesdd_(&JOBZ, &M, &N, aColumnMajor.data(), &LDA,
01268               sigmaArray.data(), &U, &LDU, &VT, &LDVT,
01269               &WORK, &LWORK,
01270               integerWorkSpace.data(), &INFO);
01271 
01272       // Check for errors.
01273       if(INFO != 0L) {
01274         std::ostringstream message;
01275         message << "First call to dgesdd_ returns " << INFO
01276                 << ".  Something is wrong.";
01277         DLR_THROW3(ValueException,
01278                    "singularValues()",
01279                    message.str().c_str());
01280       }
01281     
01282       // Resize workspace.
01283       LWORK = static_cast<Int32>(WORK);
01284 
01285       // Sanity check to correct a bug in LAPACK3.
01286       Int32 minimumLWORK =
01287         3 * std::min(M,N) + std::max(std::max(M, N), 6 * std::min(M, N));
01288       if(LWORK < minimumLWORK) {
01289         // std::cerr << "Warning: changing LWORK from " << LWORK << " to "
01290         //           << minimumLWORK << "." << std::endl;
01291         LWORK = minimumLWORK;
01292       }
01293       Array1D<Float64> doubleWorkSpace(static_cast<size_t>(LWORK));
01294       
01295       // Call again to do the SVD.
01296       dgesdd_(&JOBZ, &M, &N, aColumnMajor.data(), &LDA,
01297               sigmaArray.data(), &U, &LDU, &VT, &LDVT,
01298               doubleWorkSpace.data(), &LWORK,
01299               integerWorkSpace.data(), &INFO);
01300 
01301       // Check for errors.
01302       if(INFO != 0L) {
01303         std::ostringstream message;
01304         message << "Second call to dgesdd_ returns " << INFO
01305                 << ".  Something is wrong.";
01306         DLR_THROW3(ValueException,
01307                    "singularValues()",
01308                    message.str().c_str());
01309       }
01310 
01311       // Return the result.
01312       return sigmaArray;
01313     }
01314   
01315   } // namespace linearAlgebra
01316   
01317 } // namespace dlr

Generated on Wed Nov 25 00:49:58 2009 for dlrLinearAlgebra Utility Library by  doxygen 1.5.8