linearAlgebra.cpp

Go to the documentation of this file.
00001 
00016 #include <dlrCommon/exception.h>
00017 #include <dlrLinearAlgebra/linearAlgebra.h>
00018 #include <dlrLinearAlgebra/clapack.h>
00019 #include <dlrNumeric/utilities.h>
00020 
00021 
00022 namespace dlr {
00023 
00028   namespace linearAlgebra {
00029 
00030   Float64
00031   determinant(const Array2D<Float64>& A)
00032   {
00033     // First argument checking.
00034     if(A.columns() != A.rows()) {
00035       DLR_THROW3(ValueException,
00036                  "determinant(const Array2D<Float64>&)",
00037                  "Input array is not square.");
00038     }
00039 
00040     // In this routine, we take advantage of the fact that the
00041     // determinant of a matrix is related to the product of the
00042     // diagonal elements of its LU factorization.
00043     
00044     // Start by computing the LU factorization of A.
00045     Array2D<Float64> AColumnMajor = A.transpose();
00046     Int32 M = static_cast<Int32>(A.rows());
00047     Int32 N = static_cast<Int32>(A.columns());
00048     Int32 LDA = static_cast<Int32>(A.rows());
00049     Array1D<Int32> IPIV(M); // Really should be min(M, N), but A is square.
00050     Int32 info;
00051     dgetrf_(&M, &N, AColumnMajor.data(), &LDA, IPIV.data(), &info);
00052 
00053     // Check for errors.
00054     if(info != 0L) {
00055       std::ostringstream message;
00056       message << "Call to dgetrf_ returns " << info
00057               << ".  Something is wrong.";
00058       DLR_THROW3(ValueException,
00059                  "determinant(const Array2D<Float64>&)",
00060                  message.str().c_str());
00061     }
00062 
00063     // Compute the product of the diagonal elements, and find out if
00064     // the determinant is equal to + or - the product of the diagonal
00065     // element. Do this second thing by counting how many row-swaps
00066     // were conducteed.
00067     Float64 determinant = 1.0;
00068     size_t swapCount = 0;
00069     for(size_t index = 0; index < AColumnMajor.rows(); ++index) {
00070       determinant *= AColumnMajor(index, index);
00071       if(IPIV[index] != static_cast<Int32>(index + 1)) {
00072         // Note(xxx): check that this is right.
00073         ++swapCount;
00074       }
00075     }
00076 
00077     // Finally, correct the sign
00078     if((swapCount % 2) == 1) {
00079       determinant *= -1.0;
00080     }
00081     return determinant;
00082   }
00083 
00084   
00085   Array1D<Float64>
00086   eigenvaluesSymmetric(const Array2D<Float64>& inputArray)
00087   {
00088     // Argument checking.
00089     if(inputArray.size() == 0) {
00090       DLR_THROW3(ValueException,
00091                  "eigenvectorsSymmetric()",
00092                  "Argument inputArray cannot have zero size.");
00093     }
00094     if(inputArray.rows() != inputArray.columns()) {
00095       DLR_THROW3(ValueException,
00096                  "eigenvectorsSymmetric()",
00097                  "Argument inputArray must be square.");
00098     }
00099     
00100     // Transpose A to match LAPACK's convention.  Since inputArray is
00101     // symmetric, we don't really need to transpose!  Also, we only
00102     // need to copy the upper triangular part.
00103     size_t dimension = inputArray.rows();
00104     Array2D<Float64> aColumnMajor(dimension, dimension);
00105     {
00106       Array2D<Float64>::const_iterator inPtr = inputArray.begin();
00107       Array2D<Float64>::iterator outPtr = aColumnMajor.begin();
00108       for(size_t index0 = 0; index0 < dimension; ++index0) {
00109         inPtr += index0;
00110         outPtr += index0;
00111         for(size_t index1 = index0; index1 < dimension; ++index1) {
00112           *(outPtr++) = *(inPtr++);
00113         }
00114       }
00115     }
00116 
00117     // Set up storage for return values.
00118     Array1D<Float64> eigenvalues(dimension);
00119 
00120     // Set up arguments for the LAPACK call.
00121     char JOBZ = 'N';  // Compute eigenvalues only.
00122     char UPLO = 'L';  // Get input from upper triangle of A.
00123     Int32 N = static_cast<Int32>(inputArray.rows());
00124     Int32 LDA = N;
00125     Float64 WORK;
00126     Int32 LWORK = -1;
00127     Int32 INFO;
00128 
00129     // Call once to request optimal workspace size.
00130     dsyev_(&JOBZ,  &UPLO, &N, aColumnMajor.data(), &LDA,
00131            eigenvalues.data(), &WORK, &LWORK, &INFO);
00132 
00133     // Check for errors.
00134     if(INFO != 0L) {
00135       std::ostringstream message;
00136       message << "First call to dsyev_ returns " << INFO
00137               << ".  Something is wrong.";
00138       DLR_THROW3(ValueException,
00139                  "eigenvectorsSymmetric()",
00140                  message.str().c_str());
00141     }
00142     
00143     // Resize workspace.
00144     LWORK = static_cast<Int32>(WORK);
00145     Array1D<Float64> doubleWorkSpace(static_cast<size_t>(LWORK));
00146 
00147     // Call again to really compute the eigenvectors.
00148     dsyev_(&JOBZ,  &UPLO, &N, aColumnMajor.data(), &LDA,
00149            eigenvalues.data(), doubleWorkSpace.data(), &LWORK, &INFO);
00150 
00151     // Check for errors.
00152     if(INFO != 0L) {
00153       std::ostringstream message;
00154       message << "First call to dsyev_ returns " << INFO
00155               << ".  Something is wrong.";
00156       DLR_THROW3(ValueException,
00157                  "eigenvectorsSymmetric()",
00158                  message.str().c_str());
00159     }
00160 
00161     // Our convention differs from LAPACK's about the order of eigenvalues.
00162     std::reverse(eigenvalues.begin(), eigenvalues.end());
00163     return eigenvalues;
00164   }
00165 
00166 
00167   void
00168   eigenvectorsSymmetric(const Array2D<Float64>& inputArray,
00169                         Array1D<Float64>& eigenvalues,
00170                         Array2D<Float64>& eigenvectors)
00171   {
00172     // Argument checking.
00173     if(inputArray.size() == 0) {
00174       DLR_THROW3(ValueException,
00175                  "eigenvectorsSymmetric()",
00176                  "Argument inputArray cannot have zero size.");
00177     }
00178     if(inputArray.rows() != inputArray.columns()) {
00179       DLR_THROW3(ValueException,
00180                  "eigenvectorsSymmetric()",
00181                  "Argument inputArray must be square.");
00182     }
00183     
00184     // Transpose A to match LAPACK's convention.  Since inputArray is
00185     // symmetric, we don't really need to transpose!  Also, we only
00186     // need to copy the upper triangular part.
00187     size_t dimension = inputArray.rows();
00188     Array2D<Float64> aColumnMajor(dimension, dimension);
00189     {
00190       Array2D<Float64>::const_iterator inPtr = inputArray.begin();
00191       Array2D<Float64>::iterator outPtr = aColumnMajor.begin();
00192       for(size_t index0 = 0; index0 < dimension; ++index0) {
00193         inPtr += index0;
00194         outPtr += index0;
00195         for(size_t index1 = index0; index1 < dimension; ++index1) {
00196           *(outPtr++) = *(inPtr++);
00197         }
00198       }
00199     }
00200 
00201     // Set up storage for return values.
00202     Array1D<Float64> eigenvaluesTmp(dimension);
00203 
00204     // Set up arguments for the LAPACK call.
00205     char JOBZ = 'V';  // Compute eigenvalues and eigenvectors.
00206     char UPLO = 'L';  // Get input from lower triangle of A.
00207     Int32 N = static_cast<Int32>(dimension);
00208     Int32 LDA = static_cast<Int32>(dimension);
00209     Float64 WORK;
00210     Int32 LWORK = -1;
00211     Int32 INFO;
00212 
00213     // Call once to request optimal workspace size.
00214     dsyev_(&JOBZ,  &UPLO, &N, aColumnMajor.data(), &LDA,
00215            eigenvaluesTmp.data(), &WORK, &LWORK, &INFO);
00216 
00217     // Check for errors.
00218     if(INFO != 0L) {
00219       std::ostringstream message;
00220       message << "First call to dsyev_ returns " << INFO
00221               << ".  Something is wrong.";
00222       DLR_THROW3(ValueException,
00223                  "eigenvectorsSymmetric()",
00224                  message.str().c_str());
00225     }
00226     
00227     // Resize workspace.
00228     LWORK = static_cast<Int32>(WORK);
00229     Array1D<Float64> doubleWorkSpace(static_cast<size_t>(LWORK));
00230 
00231     // Call again to really compute the eigenvectors.
00232     dsyev_(&JOBZ,  &UPLO, &N, aColumnMajor.data(), &LDA,
00233            eigenvaluesTmp.data(), doubleWorkSpace.data(), &LWORK, &INFO);
00234 
00235     // Check for errors.
00236     if(INFO != 0L) {
00237       std::ostringstream message;
00238       message << "First call to dsyev_ returns " << INFO
00239               << ".  Something is wrong.";
00240       DLR_THROW3(ValueException,
00241                  "eigenvectorsSymmetric()",
00242                  message.str().c_str());
00243     }
00244 
00245     // Recover the result.  Eigenvectors are left in aColumnMajor, but
00246     // unfortunately they're transposed and in the reverse order from
00247     // our convention.  How awkward.
00248 
00249     // Check the sizes of the return references.
00250     if(eigenvalues.size() != dimension) {
00251       eigenvalues.reinit(dimension);
00252     }
00253     if(eigenvectors.rows() != dimension
00254        || eigenvectors.columns() != dimension) {
00255       eigenvectors.reinit(dimension, dimension);
00256     }
00257 
00258     // Copy eigenvalues in reverse order.
00259     {
00260       Array1D<Float64>::iterator outPtr = eigenvalues.end() - 1;
00261       Array1D<Float64>::iterator inPtr0 = eigenvaluesTmp.begin();
00262       Array1D<Float64>::iterator inPtr1 = eigenvaluesTmp.end();
00263       while(inPtr0 != inPtr1) {
00264         *outPtr = *inPtr0;
00265         ++inPtr0;
00266         --outPtr;
00267       }
00268     }
00269 
00270     // Transpose and reverse and copy, oh my!
00271     for(size_t index0 = 0; index0 < dimension; ++index0) {
00272       Float64* outPtr = eigenvectors.data(index0);
00273       Float64* inPtr = aColumnMajor.data((dimension - index0 - 1) * dimension);
00274       for(size_t index1 = 0; index1 < dimension; ++index1) {
00275         *outPtr = *inPtr;
00276         ++inPtr;
00277         outPtr += dimension;
00278       }
00279     }
00280   }
00281 
00282 
00283   Array2D<Float64>
00284   inverse(const Array2D<Float64>& A)
00285   {
00286     // First argument checking.
00287     if(A.columns() != A.rows()) {
00288       DLR_THROW3(ValueException,
00289                  "inverse(const Array2D<Float64>&)",
00290                  "Input array is not square.");
00291     }
00292     // Now set up some linear equations to solve.
00293     Array2D<Float64> AInverse =
00294       identity(A.rows(), A.rows(), type_tag<Float64>());
00295     Array2D<Float64> AA = A.transpose();
00296     // And solve for the inverse matrix.
00297     linearSolveInPlace(AA, AInverse); //Modifies AInverse.
00298     return AInverse;
00299   }
00300 
00301 
00302   // This function computes the best linear fit between the two input
00303   // arrays.
00304   std::pair<Float64, Float64>
00305   linearFit(const Array1D<Float64>& array0,
00306             const Array1D<Float64>& array1)
00307   {
00308     // We're looking for constants a and b which most nearly (in the
00309     // least squares sense) satisfy the equation
00310     //
00311     //   a * array0 + b = array1
00312     //
00313     // Which can be rewritten
00314     //
00315     //   [array0[0], 1]   [a] = [array1[0]]
00316     //   [array0[1], 1] * [b]   [array1[1]]
00317     //   [array0[2], 1]         [array1[2]]
00318     //   ...                    ...
00319     // 
00320     // Solving this using the Moore-Penrose pseudoinverse gives
00321     //
00322     //                                             -1
00323     //   [a]  =  [dot(array0, array0), sum(array0)]  * [dot(array0, array1)]
00324     //   [b]     [sum(array0),         N          ]    [sum(array1)        ]
00325 
00326     // First some argument checking.
00327     if(array0.size() != array1.size()) {
00328       std::ostringstream message;
00329       message << "Arguments array0 and array1 must have the same size, "
00330               << "but are of size " << array0.size()
00331               << " and " << array1.size() << " respectively." << std::endl;
00332       DLR_THROW3(ValueException,
00333                  "linearFit(const Array1D<Float64>&, const Array1D<Float64>&)",
00334                  message.str().c_str());                 
00335     }
00336     if(array0.size() == 0) {
00337       DLR_THROW3(ValueException,
00338                  "linearFit(const Array1D<Float64>&, const Array1D<Float64>&)",
00339                  "Arguments cannot have zero size.");
00340     }
00341 
00342     // Do linear regression.
00343     Array2D<Float64> AMatrix(2, 2);
00344     AMatrix(0, 0) = dot(array0, array0);
00345     AMatrix(0, 1) = sum(array0);
00346     AMatrix(1, 0) = sum(array0);
00347     AMatrix(1, 1) = array0.size();
00348 
00349     Array1D<Float64> bVector(2);
00350     bVector(0) = dot(array0, array1);
00351     bVector(1) = sum(array1);
00352     
00353     Array2D<Float64> AInverse = inverse(AMatrix);
00354 
00355     Array1D<Float64> result = matrixMultiply(AInverse, bVector);
00356     return std::make_pair(result(0), result(1));
00357   }
00358 
00359   
00360   // This function solves the system of equations A*x = b, where A and
00361   // b are known Array2D<double> instances.
00362   Array1D<Float64>
00363   linearLeastSquares(const Array2D<Float64>& A, const Array1D<Float64>& b)
00364   {
00365     // First some argument checking.
00366     if(A.size() == 0) {
00367       DLR_THROW3(
00368         ValueException,
00369         "linearLeastSquares(const Array2D<Float64>&, const Array1D<Float64>&)",
00370         "Input array A must have nonzero size.");
00371     }
00372     if(A.rows() != b.size()) {
00373       DLR_THROW3(
00374         ValueException,
00375         "linearLeastSquares(const Array2D<Float64>&, const Array1D<Float64>&)",
00376         "The number of rows in input array A must be the same as the number "
00377         "of elements in b.");
00378     }
00379 
00380     // This two-line implementation is slow.
00381     // Array2D<Float64> APInv = pseudoinverse(A);
00382     // return matrixMultiply(APInv, b);
00383 
00384     // Set up scalar arguments for the LAPACK routine.
00385     char trans = 'N';
00386     Int32 rows = static_cast<Int32>(A.rows());
00387     Int32 columns = static_cast<Int32>(A.columns());
00388     Int32 nrhs = static_cast<Int32>(1);
00389     Int32 ldb = static_cast<Int32>(std::max(rows, columns));
00390     Float64 temporaryWorkspace;
00391     Int32 lwork = -1;  // Request info on optimal workspace size.
00392     Int32 info;
00393 
00394     // Set up array arguments for the LAPACK routine.
00395     Array2D<Float64> AColumnMajor = A.transpose();
00396     Array1D<Float64> bCopy(ldb);
00397     std::copy(b.begin(), b.end(), bCopy.begin());
00398     
00399     // Now invoke the LAPACK routine to find the optimal workspace
00400     // size.
00401     dgels_(&trans, &rows, &columns, &nrhs, AColumnMajor.data(), &rows,
00402            bCopy.data(), &ldb, &temporaryWorkspace, &lwork, &info);
00403 
00404     // Check for errors.
00405     if(info != 0L) {
00406       std::ostringstream message;
00407       message << "First call to dgels_ returns " << info
00408               << ".  Something is wrong.";
00409       DLR_THROW3(ValueException,
00410                  "linearLeastSquares()",
00411                  message.str().c_str());
00412     }
00413     
00414     // Resize workspace.
00415     lwork = static_cast<Int32>(temporaryWorkspace);
00416     Array1D<Float64> doubleWorkspace(static_cast<size_t>(lwork));
00417 
00418     // Call again to solve the system of equations.
00419     dgels_(&trans, &rows, &columns, &nrhs, AColumnMajor.data(), &rows,
00420            bCopy.data(), &ldb, doubleWorkspace.data(), &lwork, &info);
00421     
00422     // Check for errors.
00423     if(info != 0L) {
00424       std::ostringstream message;
00425       message << "Second call to dgels_ returns " << info
00426               << ".  Something is wrong.";
00427       DLR_THROW3(ValueException,
00428                  "linearLeastSquares()",
00429                  message.str().c_str());
00430     }
00431 
00432     if(bCopy.size() == A.columns()) {
00433       return bCopy;
00434     } else {
00435       Array1D<Float64> returnValue(A.columns());
00436       std::copy(bCopy.begin(), bCopy.begin() + A.columns(),
00437                 returnValue.begin());
00438       return returnValue;
00439     }
00440   }
00441 
00442 
00443   // WARNING:  linearSolveInPlace() destructively modifies 
00444   // both arguments!
00445   //
00446   // This function solves the system of equations A*x = b, where A is
00447   // a known matrix, and b is a known vector.
00448   void
00449   linearSolveInPlace(Array2D<Float64>& A, Array1D<Float64>& b)
00450   {
00451     Array2D<Float64> bMatrix(b.size(), 1, b.data());
00452     linearSolveInPlace(A, bMatrix);
00453   }
00454 
00455 
00456   // This function is identical to linearSolveInPlace(Array2D<Float64>,
00457   // Array1D<Float64>&), except that b (and therefore x) is not
00458   // constrained to be a vector.
00459   void
00460   linearSolveInPlace(Array2D<Float64> &A, Array2D<Float64> &b)
00461   {
00462     // First some argument checking.
00463     if(A.rows() != b.rows()) {
00464       DLR_THROW3(ValueException,
00465                  "linearSolveInPlace(Array2D<Float64>&, Array2D<Float64>&)",
00466                  "Input arrays A and b must have the same number of rows.");
00467     }
00468     if(A.rows() != A.columns()) {
00469       DLR_THROW3(ValueException,
00470                  "linearSolveInPlace(Array2D<Float64>&, Array2D<Float64>&)",
00471                  "Input array A must be square.");
00472     }
00473 
00474     // Grr.  Have to transpose to match lapack.
00475     Array2D<Float64> AColumnMajor = A.transpose();
00476     
00477     // Now invoke the LAPACK routine.
00478     Int32 rows = static_cast<Int32>(A.rows());
00479     Int32 xColumns = static_cast<Int32>(b.columns());
00480     Int32 *iPiv = new Int32[rows];
00481     Int32 info;
00482     dgesv_(&rows, &xColumns, AColumnMajor.data(), &rows, iPiv, b.data(), &rows,
00483            &info );
00484     // Clean up
00485     delete[] iPiv;
00486     // And do some error checking.
00487     if(info != 0L) {
00488       std::ostringstream message;
00489       message << "Call to dgesv_ returns " << info << ".  Something is wrong."
00490               << "  Perhaps the the input equations are poorly conditioned "
00491               << "or perhaps there is no solution.";
00492       DLR_THROW3(ValueException,
00493                  "linearSolveInPlace(Array2D<Float64>&, Array2D<Float64>&)",
00494                  message.str().c_str());
00495     }
00496   }
00497 
00498 
00499   // This function solves the system of equations A*x = b, where A is
00500   // a known tridiagonal matrix and b is a known vector.
00501   Array1D<Float64>
00502   linearSolveTridiagonal(const Array1D<Float64>& subDiagonal,
00503                          const Array1D<Float64>& centerDiagonal,
00504                          const Array1D<Float64>& superDiagonal,
00505                          const Array1D<Float64>& bVector)
00506   {
00507     // First some argument checking.
00508     if(subDiagonal.size() != superDiagonal.size()) {
00509       DLR_THROW3(ValueException,
00510                  "linearSolveTridiagonal()",
00511                  "Input arguments subDiagonal and superDiagonal "
00512                  "must have the same size.");
00513     }
00514     if(centerDiagonal.size() != bVector.size()) {
00515       DLR_THROW3(ValueException,
00516                  "linearSolveTridiagonal()",
00517                  "Input arguments centerDiagonal and bVector "
00518                  "must have the same size.");
00519     }
00520     if(centerDiagonal.size() != subDiagonal.size() + 1) {
00521       DLR_THROW3(ValueException,
00522                  "linearSolveTridiagonal()",
00523                  "Input argument centerDiagonal must have one more element "
00524                  "than input argument subDiagonal.");
00525     }
00526 
00527     
00528     // Now invoke the LAPACK routine.
00529     Int32 N = static_cast<Int32>(centerDiagonal.size());
00530     Int32 NRHS = 1;
00531     Array1D<Float64> subDiagonalCopy = subDiagonal.copy();
00532     Array1D<Float64> centerDiagonalCopy = centerDiagonal.copy();
00533     Array1D<Float64> superDiagonalCopy = superDiagonal.copy();
00534     Array1D<Float64> xVector = bVector.copy();
00535     Int32 INFO;
00536 
00537     dgtsv_(&N, &NRHS, subDiagonalCopy.data(), centerDiagonalCopy.data(),
00538            superDiagonalCopy.data(), xVector.data(), &N, &INFO);
00539 
00540     // And do some error checking.
00541     if(INFO != 0L) {
00542       std::ostringstream message;
00543       message << "Call to dgtsv_ returns " << INFO << ".  Something is wrong."
00544               << "  Perhaps the the input equations are poorly conditioned "
00545               << "or perhaps there is no solution.";
00546       DLR_THROW3(ValueException,
00547                  "linearSolveTridiagonal()",
00548                  message.str().c_str());
00549     }
00550 
00551     return xVector;
00552   }
00553     
00554 
00555   // This function accepts an Array2D<Float64> instance having at least
00556   // as many rows as columns, and returns the Moore-Penrose
00557   // pseudoinverse.
00558   Array2D<Float64>
00559   pseudoinverse(const Array2D<Float64>& A)
00560   {
00561     Array2D<Float64> ATranspose = A.transpose();
00562     Array2D<Float64> ATA = matrixMultiply(ATranspose, A);
00563     return matrixMultiply(inverse(ATA), ATranspose);
00564   }
00565 
00566 
00567   // This function computes the singular value decomposition of a
00568   // matrix.
00569   void
00570   singularValueDecomposition(const Array2D<Float64>& inputArray,
00571                              Array2D<Float64>& uArray,
00572                              Array1D<Float64>& sigmaArray,
00573                              Array2D<Float64>& vTransposeArray)
00574   {
00575     // Argument checking.
00576     if(inputArray.size() == 0) {
00577       DLR_THROW3(ValueException,
00578                  "singularValueDecomposition()",
00579                  "Argument inputArray cannot have zero size.");
00580     }
00581     
00582     // We do not transpose A. Instead, we let LAPACK operate on the
00583     // untransposed matrix as if it were column major and then swap U
00584     // and VT at the end.  We make a copy here since the contents of
00585     // whatever array we pass to LAPACK will be destroyed.
00586     Array2D<Float64> aColumnMajor = inputArray.copy();
00587 
00588     // Set up storage for return values.
00589     size_t numberOfSingularValues =
00590       std::min(inputArray.rows(), inputArray.columns());
00591 
00592     if((vTransposeArray.rows() != numberOfSingularValues)
00593        || (vTransposeArray.columns() != inputArray.columns())) {
00594       vTransposeArray.reinit(numberOfSingularValues, inputArray.columns());
00595     }
00596     // Shallow copy.
00597     Array2D<Float64> uColumnMajor = vTransposeArray;
00598 
00599     if((uArray.rows() != inputArray.rows())
00600        || (uArray.columns() != numberOfSingularValues)) {
00601       uArray.reinit(inputArray.rows(), numberOfSingularValues);
00602     }
00603     // Shallow copy.
00604     Array2D<Float64> vTColumnMajor = uArray;
00605 
00606     if(sigmaArray.size() != numberOfSingularValues) {
00607       sigmaArray.reinit(numberOfSingularValues);
00608     }
00609     Array1D<Int32> integerWorkSpace(8 * numberOfSingularValues);
00610     
00611     // Set up arguments for the LAPACK call.
00612     char JOBZ = 'S';  // Compute only min(M, N) columns of U and rows of VT.
00613     Int32 M = static_cast<Int32>(inputArray.columns());
00614     Int32 N = static_cast<Int32>(inputArray.rows());
00615     Int32 LDA = M;
00616     Int32 LDU = M;
00617     Int32 LDVT = static_cast<Int32>(numberOfSingularValues);
00618     Float64 WORK;
00619     Int32 LWORK = -1;  // Request info on optimal workspace size.
00620     Int32 INFO;
00621 
00622     // Call once to request optimal workspace size.
00623     dgesdd_(&JOBZ, &M, &N, aColumnMajor.data(), &LDA,
00624             sigmaArray.data(), uColumnMajor.data(), &LDU,
00625             vTColumnMajor.data(), &LDVT,
00626             &WORK, &LWORK,
00627             integerWorkSpace.data(), &INFO);
00628 
00629     // Check for errors.
00630     if(INFO != 0L) {
00631       std::ostringstream message;
00632       message << "First call to dgesdd_ returns " << INFO
00633               << ".  Something is wrong.";
00634       DLR_THROW3(ValueException,
00635                  "singularValueDecomposition()",
00636                  message.str().c_str());
00637     }
00638     
00639     // Resize workspace.
00640     LWORK = static_cast<Int32>(WORK);
00641     Array1D<Float64> doubleWorkSpace(static_cast<size_t>(LWORK));
00642 
00643     // Call again to do the SVD.
00644     dgesdd_(&JOBZ, &M, &N, aColumnMajor.data(), &LDA,
00645             sigmaArray.data(), uColumnMajor.data(), &LDU,
00646             vTColumnMajor.data(), &LDVT,
00647             doubleWorkSpace.data(), &LWORK,
00648             integerWorkSpace.data(), &INFO);
00649 
00650     // Check for errors.
00651     if(INFO != 0L) {
00652       std::ostringstream message;
00653       message << "Second call to dgesdd_ returns " << INFO
00654               << ".  Something is wrong.";
00655       DLR_THROW3(ValueException,
00656                  "singularValueDecomposition()",
00657                  message.str().c_str());
00658     }
00659   } 
00660 
00661 
00662 
00663   void
00664   singularValueDecompositionSimple(const Array2D<Float64>& inputArray,
00665                                    Array2D<Float64>& uArray,
00666                                    Array1D<Float64>& sigmaArray,
00667                                    Array2D<Float64>& vTransposeArray)
00668   {
00669     // Argument checking.
00670     if(inputArray.size() == 0) {
00671       DLR_THROW3(ValueException,
00672                  "singularValueDecomposition()",
00673                  "Argument inputArray cannot have zero size.");
00674     }
00675     
00676     // We do not transpose A. Instead, we let LAPACK operate on the
00677     // untransposed matrix as if it were column major and then swap U
00678     // and VT at the end.  We make a copy here since the contents of
00679     // whatever array we pass to LAPACK will be destroyed.
00680     Array2D<Float64> aColumnMajor = inputArray.copy();
00681 
00682     // Set up storage for return values.
00683     size_t numberOfSingularValues =
00684       std::min(inputArray.rows(), inputArray.columns());
00685 
00686     // Make sure V is appropriately sized.
00687     if((vTransposeArray.rows() != numberOfSingularValues)
00688        || (vTransposeArray.columns() != inputArray.columns())) {
00689       vTransposeArray.reinit(numberOfSingularValues, inputArray.columns());
00690     }
00691 
00692     // Shallow copy.
00693     Array2D<Float64> uColumnMajor = vTransposeArray;
00694 
00695     // Make sure U is appropriately sized.
00696     if((uArray.rows() != inputArray.rows())
00697        || (uArray.columns() != numberOfSingularValues)) {
00698       uArray.reinit(inputArray.rows(), numberOfSingularValues);
00699     }
00700     
00701     // Shallow copy.
00702     Array2D<Float64> vTColumnMajor = uArray;
00703 
00704     if(sigmaArray.size() != numberOfSingularValues) {
00705       sigmaArray.reinit(numberOfSingularValues);
00706     }
00707 
00708     // Integer workspace.
00709     Array1D<Int32> integerWorkspace(8 * numberOfSingularValues);
00710 
00711     // Set up arguments for the LAPACK call.
00712     char JOBZ = 'S';  // Compute only min(M, N) columns of U.
00713     Int32 M = static_cast<Int32>(inputArray.columns());
00714     Int32 N = static_cast<Int32>(inputArray.rows());
00715     Int32 LDA = M;
00716     Int32 LDU = M;
00717     Int32 LDVT = static_cast<Int32>(numberOfSingularValues);
00718     Float64 WORK;
00719     Int32 LWORK = -1;  // Request info on optimal workspace size.
00720     Int32 INFO;
00721 
00722     // Call once to request optimal workspace size.
00723     dgesdd_(&JOBZ, &M, &N, aColumnMajor.data(), &LDA,
00724             sigmaArray.data(), uColumnMajor.data(), &LDU,
00725             vTColumnMajor.data(), &LDVT,
00726             &WORK, &LWORK, integerWorkspace.data(), &INFO);
00727 
00728     // Check for errors.
00729     if(INFO != 0L) {
00730       std::ostringstream message;
00731       message << "First call to dgesdd_ returns " << INFO
00732               << ".  Something is wrong.";
00733       DLR_THROW3(ValueException,
00734                  "singularValueDecomposition()",
00735                  message.str().c_str());
00736     }
00737     
00738     // Resize workspace.
00739     LWORK = static_cast<Int32>(WORK);
00740     Array1D<Float64> doubleWorkSpace(static_cast<size_t>(LWORK));
00741 
00742     // Call again to do the SVD.
00743     dgesdd_(&JOBZ, &M, &N, aColumnMajor.data(), &LDA,
00744             sigmaArray.data(), uColumnMajor.data(), &LDU,
00745             vTColumnMajor.data(), &LDVT,
00746             doubleWorkSpace.data(), &LWORK, integerWorkspace.data(), &INFO);
00747 
00748     // Check for errors.
00749     if(INFO != 0L) {
00750       std::ostringstream message;
00751       message << "Second call to dgesdd_ returns " << INFO
00752               << ".  Something is wrong.";
00753       DLR_THROW3(ValueException,
00754                  "singularValueDecomposition()",
00755                  message.str().c_str());
00756     }
00757   } 
00758 
00759   
00760 //   // This function computes the singular value decomposition of a
00761 //   // matrix, but is less efficient than the routine above: It
00762 //   // explicitly transposes the matrix before passing it to LAPACK.
00763 //   void
00764 //   singularValueDecomposition(
00765 //     const Array2D<double>& inputArray,
00766 //     Array2D<double>& uArray,
00767 //     Array1D<double>& sigmaArray,
00768 //     Array2D<double>& vTransposeArray)
00769 //   {
00770 //     // Argument checking.
00771 //     if(inputArray.size() == 0) {
00772 //       DLR_THROW3(ValueException,
00773 //                  "singularValueDecomposition()",
00774 //                  "Argument inputArray cannot have zero size.");
00775 //     }
00776     
00777 //     // Transpose A to match LAPACK's convention.
00778 //     Array2D<double> aColumnMajor = inputArray.transpose();
00779 
00780 //     // Set up storage for return values.
00781 //     size_t numberOfSingularValues =
00782 //       std::min(inputArray.rows(), inputArray.columns());
00783 //     Array2D<double> uColumnMajor(numberOfSingularValues, inputArray.rows());
00784 //     Array2D<double> vTColumnMajor(
00785 //       inputArray.columns(), numberOfSingularValues);
00786 //     if(sigmaArray.size() != numberOfSingularValues) {
00787 //       sigmaArray.reinit(numberOfSingularValues);
00788 //     }
00789 //     Array1D<Int32> integerWorkSpace(8 * numberOfSingularValues);
00790     
00791 //     // Set up arguments for the LAPACK call.
00792 //     char JOBZ = 'S';  // Compute only min(M, N) columns of U and rows of VT.
00793 //     Int32 M = static_cast<Int32>(inputArray.rows());
00794 //     Int32 N = static_cast<Int32>(inputArray.columns());
00795 //     Int32 LDA = M;
00796 //     Int32 LDU = M;
00797 //     Int32 LDVT = static_cast<Int32>(numberOfSingularValues);
00798 //     double WORK;
00799 //     Int32 LWORK = -1;  // Request info on optimal workspace size.
00800 //     Int32 INFO;
00801 
00802 //     // Call once to request optimal workspace size.
00803 //     dgesdd_(&JOBZ, &M, &N, aColumnMajor.data(), &LDA,
00804 //             sigmaArray.data(), uColumnMajor.data(), &LDU,
00805 //             vTColumnMajor.data(), &LDVT,
00806 //             &WORK, &LWORK,
00807 //             integerWorkSpace.data(), &INFO);
00808 
00809 //     // Check for errors.
00810 //     if(INFO != 0L) {
00811 //       std::ostringstream message;
00812 //       message << "First call to dgesdd_ returns " << INFO
00813 //               << ".  Something is wrong.";
00814 //       DLR_THROW3(ValueException,
00815 //                  "singularValueDecomposition()",
00816 //                  message.str().c_str());
00817 //     }
00818     
00819 //     // Resize workspace.
00820 //     LWORK = static_cast<Int32>(WORK);
00821 //     Array1D<double> doubleWorkSpace(static_cast<size_t>(LWORK));
00822 
00823 //     // Call again to do the SVD.
00824 //     dgesdd_(&JOBZ, &M, &N, aColumnMajor.data(), &LDA,
00825 //             sigmaArray.data(), uColumnMajor.data(), &LDU,
00826 //             vTColumnMajor.data(), &LDVT,
00827 //             doubleWorkSpace.data(), &LWORK,
00828 //             integerWorkSpace.data(), &INFO);
00829 
00830 //     // Check for errors.
00831 //     if(INFO != 0L) {
00832 //       std::ostringstream message;
00833 //       message << "Second call to dgesdd_ returns " << INFO
00834 //               << ".  Something is wrong.";
00835 //       DLR_THROW3(ValueException,
00836 //                  "singularValueDecomposition()",
00837 //                  message.str().c_str());
00838 //     }
00839 
00840 //     // Recover the result.
00841 //     uArray = uColumnMajor.transpose();
00842 //     vTransposeArray = vTColumnMajor.transpose();
00843 //   } 
00844   
00845 
00846   // This function computes the singular values a matrix without
00847   // computing the associated U and V matrices.
00848   Array1D<Float64>
00849   singularValues(const Array2D<Float64>& inputArray)
00850   {
00851     // Argument checking.
00852     if(inputArray.size() == 0) {
00853       DLR_THROW3(ValueException,
00854                  "singularValues()",
00855                  "Argument inputArray cannot have zero size.");
00856     }
00857     
00858     // Transpose A to match LAPACK's convention.
00859     //
00860     // Actually, it's slightly quicker not to transpose, and singular
00861     // values remain the same, so we just copy.
00862     // 
00863     // Array2D<Float64> aColumnMajor = inputArray.transpose();
00864     Array2D<Float64> aColumnMajor = inputArray.copy();
00865 
00866     // Set up storage for return values.
00867     size_t numberOfSingularValues =
00868       std::min(inputArray.rows(), inputArray.columns());
00869     Array1D<Float64> sigmaArray(numberOfSingularValues);
00870     Array1D<Int32> integerWorkSpace(8 * numberOfSingularValues);
00871     
00872     // Set up arguments for the LAPACK call.
00873     char JOBZ = 'N';  // Compute no columns of U and no rows of VT.
00874     // Since we didn't transpose, we have to reverse rows & columns.
00875     // Int32 M = static_cast<Int32>(inputArray.rows());
00876     // Int32 N = static_cast<Int32>(inputArray.columns());
00877     Int32 M = static_cast<Int32>(inputArray.columns());
00878     Int32 N = static_cast<Int32>(inputArray.rows());
00879     Int32 LDA = M;
00880     Float64 U;         // U is not referenced by the LAPACK call.
00881     Int32 LDU = 1;
00882     Float64 VT;        // VT is not referenced by the LAPACK call.
00883     Int32 LDVT = 1;
00884     Float64 WORK;
00885     Int32 LWORK = -1;  // Request info on optimal workspace size.
00886     Int32 INFO;
00887 
00888     // Call once to request optimal workspace size.
00889     dgesdd_(&JOBZ, &M, &N, aColumnMajor.data(), &LDA,
00890             sigmaArray.data(), &U, &LDU, &VT, &LDVT,
00891             &WORK, &LWORK,
00892             integerWorkSpace.data(), &INFO);
00893 
00894     // Check for errors.
00895     if(INFO != 0L) {
00896       std::ostringstream message;
00897       message << "First call to dgesdd_ returns " << INFO
00898               << ".  Something is wrong.";
00899       DLR_THROW3(ValueException,
00900                  "singularValues()",
00901                  message.str().c_str());
00902     }
00903     
00904     // Resize workspace.
00905     LWORK = static_cast<Int32>(WORK);
00906 
00907     // Sanity check to correct a bug in LAPACK3.
00908     Int32 minimumLWORK =
00909       3 * std::min(M,N) + std::max(std::max(M, N), 6 * std::min(M, N));
00910     if(LWORK < minimumLWORK) {
00911       // std::cerr << "Warning: changing LWORK from " << LWORK << " to "
00912       //           << minimumLWORK << "." << std::endl;
00913       LWORK = minimumLWORK;
00914     }
00915     Array1D<Float64> doubleWorkSpace(static_cast<size_t>(LWORK));
00916       
00917     // Call again to do the SVD.
00918     dgesdd_(&JOBZ, &M, &N, aColumnMajor.data(), &LDA,
00919             sigmaArray.data(), &U, &LDU, &VT, &LDVT,
00920             doubleWorkSpace.data(), &LWORK,
00921             integerWorkSpace.data(), &INFO);
00922 
00923     // Check for errors.
00924     if(INFO != 0L) {
00925       std::ostringstream message;
00926       message << "First call to dgesdd_ returns " << INFO
00927               << ".  Something is wrong.";
00928       DLR_THROW3(ValueException,
00929                  "singularValues()",
00930                  message.str().c_str());
00931     }
00932 
00933     // Return the result.
00934     return sigmaArray;
00935   }
00936   
00937   } // namespace linearAlgebra
00938   
00939 } // namespace dlr

Generated on Mon Jul 9 20:34:03 2007 for dlrLibs Utility Libraries by  doxygen 1.5.2