1 // @HEADER
2 // ***********************************************************************
3 //
4 // Teuchos: Common Tools Package
5 // Copyright (2004) Sandia Corporation
6 //
7 // Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
8 // license for use of this work by or on behalf of the U.S. Government.
9 //
10 // Redistribution and use in source and binary forms, with or without
11 // modification, are permitted provided that the following conditions are
12 // met:
13 //
14 // 1. Redistributions of source code must retain the above copyright
15 // notice, this list of conditions and the following disclaimer.
16 //
17 // 2. Redistributions in binary form must reproduce the above copyright
18 // notice, this list of conditions and the following disclaimer in the
19 // documentation and/or other materials provided with the distribution.
20 //
21 // 3. Neither the name of the Corporation nor the names of the
22 // contributors may be used to endorse or promote products derived from
23 // this software without specific prior written permission.
24 //
25 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
26 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
29 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 //
37 // Questions? Contact Michael A. Heroux (maherou@sandia.gov)
38 //
39 // ***********************************************************************
40 // @HEADER
41
42 #ifndef _TEUCHOS_SERIALQRDENSESOLVER_HPP_
43 #define _TEUCHOS_SERIALQRDENSESOLVER_HPP_
44 /*! \file Teuchos_SerialQRDenseSolver.hpp
45 \brief Templated class for solving dense linear problems.
46 */
47
48 #include "Teuchos_CompObject.hpp"
49 #include "Teuchos_BLAS.hpp"
50 #include "Teuchos_LAPACK.hpp"
51 #include "Teuchos_RCP.hpp"
52 #include "Teuchos_ConfigDefs.hpp"
53 #include "Teuchos_SerialDenseMatrix.hpp"
54 #include "Teuchos_SerialDenseSolver.hpp"
55 #include "Teuchos_ScalarTraits.hpp"
56
57 #ifdef HAVE_TEUCHOSNUMERICS_EIGEN
58 #include "Eigen/Dense"
59 #endif
60
61 /*! \class Teuchos::SerialQRDenseSolver
62 \brief A class for solving dense linear problems.
63
64 The Teuchos::SerialQRDenseSolver class enables the definition, in terms of Teuchos::SerialDenseMatrix
65 and Teuchos::SerialDenseVector objects, of a dense linear problem, followed by the solution of that
66 problem via the most sophisticated techniques available in LAPACK.
67
68 The Teuchos::SerialQRDenseSolver class is intended to provide full-featured support for solving linear
69 problems for general dense rectangular (or square) matrices. It is written on top of BLAS and LAPACK
70 and thus has excellent performance and numerical capabilities. Using this class, one can either perform
71 simple factorizations and solves or apply all the tricks available in LAPACK to get the best possible
72 solution for very ill-conditioned problems.
73
74 <b>Teuchos::SerialQRDenseSolver vs. Teuchos::LAPACK</b>
75
76 The Teuchos::LAPACK class provides access to most of the same functionality as Teuchos::SerialQRDenseSolver.
77 The primary difference is that Teuchos::LAPACK is a "thin" layer on top of LAPACK and Teuchos::SerialQRDenseSolver
78 attempts to provide easy access to the more sophisticated aspects of solving dense linear and eigensystems.
79 <ul>
80 <li> When you should use Teuchos::LAPACK: If you are simply looking for a convenient wrapper around
81 the Fortran LAPACK routines and you have a well-conditioned problem, you should probably use Teuchos::LAPACK directly.
82 <li> When you should use Teuchos::SerialQRDenseSolver: If you want to (or potentially want to) solve
83 ill-conditioned problems or want to work with a more object-oriented interface, you should probably use
84 Teuchos::SerialQRDenseSolver.
85 </ul>
86
87 <b>Constructing Teuchos::SerialQRDenseSolver Objects</b>
88
89 There is a single Teuchos::SerialQRDenseSolver constructor. However, the matrix, right hand side and solution
90 vectors must be set prior to executing most methods in this class.
91
92 <b>Setting vectors used for linear solves</b>
93
94 The matrix A, the left hand side X and the right hand side B (when solving AX = B, for X), can be set by appropriate set
95 methods. Each of these three objects must be an Teuchos::SerialDenseMatrix or and Teuchos::SerialDenseVector object. The
96 set methods are as follows:
97 <ul>
98 <li> setMatrix() - Sets the matrix.
99 <li> setVectors() - Sets the left and right hand side vector(s).
100 </ul>
101
102 <b>Vector and Utility Functions</b>
103
104 Once a Teuchos::SerialQRDenseSolver is constructed, several mathematical functions can be applied to
105 the object. Specifically:
106 <ul>
107 <li> Factorizations.
108 <li> Solves.
109 <li> Equilibration.
110 <li> Norms.
111 </ul>
112
113 <b>Strategies for Solving Linear Systems</b>
114 In many cases, linear least squares systems can be accurately solved by simply computing the QR factorization
115 of the matrix and then performing a forward back solve with a given set of right hand side vectors. However,
116 in some instances, the factorization may be very poorly conditioned and this simple approach may not work. In
117 these situations, equilibration and iterative refinement may improve the accuracy, or prevent a breakdown in
118 the factorization.
119
120 Teuchos::SerialQRDenseSolver will use equilibration with the factorization if, once the object
121 is constructed and \e before it is factored, you call the function factorWithEquilibration(true) to force
122 equilibration to be used. If you are uncertain if equilibration should be used, you may call the function
123 shouldEquilibrate() which will return true if equilibration could possibly help. shouldEquilibrate() uses
124 guidelines specified in the LAPACK User Guide to determine if equilibration \e might be useful.
125
126 Examples using Teuchos::SerialQRDenseSolver can be found in the Teuchos test directories.
127 */
128
129 namespace Teuchos {
130
131 template<typename OrdinalType, typename ScalarType>
132 class SerialQRDenseSolver : public CompObject, public Object, public BLAS<OrdinalType, ScalarType>,
133 public LAPACK<OrdinalType, ScalarType>
134 {
135
136 public:
137
138 typedef typename ScalarTraits<ScalarType>::magnitudeType MagnitudeType;
139 #ifdef HAVE_TEUCHOSNUMERICS_EIGEN
140 typedef typename Eigen::Matrix<ScalarType,Eigen::Dynamic,Eigen::Dynamic,Eigen::ColMajor> EigenMatrix;
141 typedef typename Eigen::Matrix<ScalarType,Eigen::Dynamic,1> EigenVector;
142 typedef typename Eigen::InnerStride<Eigen::Dynamic> EigenInnerStride;
143 typedef typename Eigen::OuterStride<Eigen::Dynamic> EigenOuterStride;
144 typedef typename Eigen::Map<EigenVector,0,EigenInnerStride > EigenVectorMap;
145 typedef typename Eigen::Map<const EigenVector,0,EigenInnerStride > EigenConstVectorMap;
146 typedef typename Eigen::Map<EigenMatrix,0,EigenOuterStride > EigenMatrixMap;
147 typedef typename Eigen::Map<const EigenMatrix,0,EigenOuterStride > EigenConstMatrixMap;
148 typedef typename Eigen::PermutationMatrix<Eigen::Dynamic,Eigen::Dynamic,OrdinalType> EigenPermutationMatrix;
149 typedef typename Eigen::Array<OrdinalType,Eigen::Dynamic,1> EigenOrdinalArray;
150 typedef typename Eigen::Map<EigenOrdinalArray> EigenOrdinalArrayMap;
151 typedef typename Eigen::Array<ScalarType,Eigen::Dynamic,1> EigenScalarArray;
152 typedef typename Eigen::Map<EigenScalarArray> EigenScalarArrayMap;
153 #endif
154
155 //! @name Constructor/Destructor Methods
156 //@{
157 //! Default constructor; matrix should be set using setMatrix(), LHS and RHS set with setVectors().
158 SerialQRDenseSolver();
159
160 //! SerialQRDenseSolver destructor.
161 virtual ~SerialQRDenseSolver();
162 //@}
163
164 //! @name Set Methods
165 //@{
166
167 //! Sets the pointers for coefficient matrix
168 /*! Row dimension of A must be greater than or equal to the column dimension of A.
169 */
170 int setMatrix(const RCP<SerialDenseMatrix<OrdinalType, ScalarType> >& A);
171
172 //! Sets the pointers for left and right hand side vector(s).
173 /*! Row dimension of X must match column dimension of matrix A, row dimension of B
174 must match row dimension of A.
175 */
176 int setVectors(const RCP<SerialDenseMatrix<OrdinalType, ScalarType> >& X,
177 const RCP<SerialDenseMatrix<OrdinalType, ScalarType> >& B);
178 //@}
179
180 //! @name Strategy Modifying Methods
181 //@{
182
183 //! Causes equilibration to be called just before the matrix factorization as part of the call to \c factor.
184 /*! \note This method must be called before the factorization is performed, otherwise it will have no effect.
185 */
factorWithEquilibration(bool flag)186 void factorWithEquilibration(bool flag) {equilibrate_ = flag; return;}
187
188 //! If \c flag is true, causes all subsequent function calls to work with the adjoint of \e this matrix, otherwise not.
solveWithTranspose(bool flag)189 void solveWithTranspose(bool flag) {transpose_ = flag; if (flag) TRANS_ = Teuchos::CONJ_TRANS; else TRANS_ = Teuchos::NO_TRANS; return;}
190
191 //! All subsequent function calls will work with the transpose-type set by this method (\c Teuchos::NO_TRANS or Teuchos::CONJ_TRANS).
solveWithTransposeFlag(Teuchos::ETransp trans)192 void solveWithTransposeFlag(Teuchos::ETransp trans) {TRANS_ = trans; transpose_ = (trans != Teuchos::NO_TRANS) ? true : false; }
193
194 //@}
195
196 //! @name Factor/Solve/Invert Methods
197 //@{
198
199 //! Computes the in-place QR factorization of the matrix using the LAPACK routine \e _GETRF or the Eigen class \e HouseholderQR
200 /*!
201 \return Integer error code, set to 0 if successful.
202 */
203 int factor();
204
205 //! Computes the solution X to AX = B for the \e this matrix and the B provided to SetVectors()..
206 /*!
207 \return Integer error code, set to 0 if successful.
208 */
209 int solve();
210
211 //! Determines if \e this matrix should be scaled.
212 /*!
213 \return Integer error code, set to 0 if successful.
214 */
215 int computeEquilibrateScaling();
216
217 //! Equilibrates the \e this matrix.
218 /*!
219 \note This method will be called automatically in solve() method if factorWithEquilibration( true ) is called.
220 \return Integer error code, set to 0 if successful.
221 */
222 int equilibrateMatrix();
223
224 //! Equilibrates the current RHS.
225 /*!
226 \note This method will be called automatically in solve() method if factorWithEquilibration( true ) is called.
227 \return Integer error code, set to 0 if successful.
228 */
229 int equilibrateRHS();
230
231 //! Unscales the solution vectors if equilibration was used to solve the system.
232 /*!
233 \note This method will be called automatically in solve() method if factorWithEquilibration( true ) is called.
234 \return Integer error code, set to 0 if successful.
235 */
236 int unequilibrateLHS();
237
238 //! Explicitly forms the unitary matrix Q.
239 /*!
240 \return Integer error code, set to 0 if successful.
241 */
242 int formQ();
243
244 //! Explicitly forms the upper triangular matrix R.
245 /*!
246 \return Integer error code, set to 0 if successful.
247 */
248 int formR();
249
250 //! Left multiply the input matrix by the unitary matrix Q or its adjoint.
251 /*!
252 \return Integer error code, set to 0 if successful.
253 */
254 int multiplyQ (ETransp transq, SerialDenseMatrix<OrdinalType, ScalarType> &C);
255
256 //! Solve input matrix on the left with the upper triangular matrix R or its adjoint.
257 /*!
258 \return Integer error code, set to 0 if successful.
259 */
260 int solveR (ETransp transr, SerialDenseMatrix<OrdinalType, ScalarType> &C);
261 //@}
262
263 //! @name Query methods
264 //@{
265
266 //! Returns true if adjoint of \e this matrix has and will be used.
transpose()267 bool transpose() {return(transpose_);}
268
269 //! Returns true if matrix is factored (factor available via getFactoredMatrix()).
factored()270 bool factored() {return(factored_);}
271
272 //! Returns true if factor is equilibrated (factor available via getFactoredMatrix()).
equilibratedA()273 bool equilibratedA() {return(equilibratedA_);}
274
275 //! Returns true if RHS is equilibrated (RHS available via getRHS()).
equilibratedB()276 bool equilibratedB() {return(equilibratedB_);}
277
278 //! Returns true if the LAPACK general rules for equilibration suggest you should equilibrate the system.
shouldEquilibrate()279 bool shouldEquilibrate() {computeEquilibrateScaling(); return(shouldEquilibrate_);}
280
281 //! Returns true if the current set of vectors has been solved.
solved()282 bool solved() {return(solved_);}
283
284 //! Returns true if Q has been formed explicitly.
formedQ()285 bool formedQ() {return(formedQ_);}
286
287 //! Returns true if R has been formed explicitly.
formedR()288 bool formedR() {return(formedR_);}
289
290 //@}
291
292 //! @name Data Accessor methods
293 //@{
294
295 //! Returns pointer to current matrix.
getMatrix() const296 RCP<SerialDenseMatrix<OrdinalType, ScalarType> > getMatrix() const {return(Matrix_);}
297
298 //! Returns pointer to factored matrix (assuming factorization has been performed).
getFactoredMatrix() const299 RCP<SerialDenseMatrix<OrdinalType, ScalarType> > getFactoredMatrix() const {return(Factor_);}
300
301 //! Returns pointer to Q (assuming factorization has been performed).
getQ() const302 RCP<SerialDenseMatrix<OrdinalType, ScalarType> > getQ() const {return(FactorQ_);}
303
304 //! Returns pointer to R (assuming factorization has been performed).
getR() const305 RCP<SerialDenseMatrix<OrdinalType, ScalarType> > getR() const {return(FactorR_);}
306
307 //! Returns pointer to current LHS.
getLHS() const308 RCP<SerialDenseMatrix<OrdinalType, ScalarType> > getLHS() const {return(LHS_);}
309
310 //! Returns pointer to current RHS.
getRHS() const311 RCP<SerialDenseMatrix<OrdinalType, ScalarType> > getRHS() const {return(RHS_);}
312
313 //! Returns row dimension of system.
numRows() const314 OrdinalType numRows() const {return(M_);}
315
316 //! Returns column dimension of system.
numCols() const317 OrdinalType numCols() const {return(N_);}
318
319 //! Returns pointer to pivot vector (if factorization has been computed), zero otherwise.
tau() const320 std::vector<ScalarType> tau() const {return(TAU_);}
321
322 //! Returns the absolute value of the largest element of \e this matrix (returns -1 if not yet computed).
ANORM() const323 MagnitudeType ANORM() const {return(ANORM_);}
324
325 //@}
326
327 //! @name I/O methods
328 //@{
329 //! Print service methods; defines behavior of ostream << operator.
330 void Print(std::ostream& os) const;
331 //@}
332 protected:
333
allocateWORK()334 void allocateWORK() { LWORK_ = 4*N_; WORK_.resize( LWORK_ ); return;}
335 void resetMatrix();
336 void resetVectors();
337
338
339 bool equilibrate_;
340 bool shouldEquilibrate_;
341 bool equilibratedA_;
342 bool equilibratedB_;
343 OrdinalType equilibrationModeA_;
344 OrdinalType equilibrationModeB_;
345 bool transpose_;
346 bool factored_;
347 bool solved_;
348 bool matrixIsZero_;
349 bool formedQ_;
350 bool formedR_;
351
352 Teuchos::ETransp TRANS_;
353
354 OrdinalType M_;
355 OrdinalType N_;
356 OrdinalType Min_MN_;
357 OrdinalType LDA_;
358 OrdinalType LDAF_;
359 OrdinalType LDQ_;
360 OrdinalType LDR_;
361 OrdinalType INFO_;
362 OrdinalType LWORK_;
363
364 std::vector<ScalarType> TAU_;
365
366 MagnitudeType ANORM_;
367 MagnitudeType BNORM_;
368
369 RCP<SerialDenseMatrix<OrdinalType, ScalarType> > Matrix_;
370 RCP<SerialDenseMatrix<OrdinalType, ScalarType> > LHS_;
371 RCP<SerialDenseMatrix<OrdinalType, ScalarType> > TMP_;
372 RCP<SerialDenseMatrix<OrdinalType, ScalarType> > RHS_;
373 RCP<SerialDenseMatrix<OrdinalType, ScalarType> > Factor_;
374 RCP<SerialDenseMatrix<OrdinalType, ScalarType> > FactorQ_;
375 RCP<SerialDenseMatrix<OrdinalType, ScalarType> > FactorR_;
376
377 ScalarType * A_;
378 ScalarType * AF_;
379 ScalarType * Q_;
380 ScalarType * R_;
381 std::vector<ScalarType> WORK_;
382 #ifdef HAVE_TEUCHOSNUMERICS_EIGEN
383 Eigen::HouseholderQR<EigenMatrix> qr_;
384 #endif
385
386 private:
387 // SerialQRDenseSolver copy constructor (put here because we don't want user access)
388
389 SerialQRDenseSolver(const SerialQRDenseSolver<OrdinalType, ScalarType>& Source);
390 SerialQRDenseSolver & operator=(const SerialQRDenseSolver<OrdinalType, ScalarType>& Source);
391
392 };
393
394 // Helper traits to distinguish work arrays for real and complex-valued datatypes.
395 using namespace details;
396
397 //=============================================================================
398
399 template<typename OrdinalType, typename ScalarType>
SerialQRDenseSolver()400 SerialQRDenseSolver<OrdinalType,ScalarType>::SerialQRDenseSolver()
401 : CompObject(),
402 equilibrate_(false),
403 shouldEquilibrate_(false),
404 equilibratedA_(false),
405 equilibratedB_(false),
406 equilibrationModeA_(0),
407 equilibrationModeB_(0),
408 transpose_(false),
409 factored_(false),
410 solved_(false),
411 matrixIsZero_(false),
412 formedQ_(false),
413 formedR_(false),
414 TRANS_(Teuchos::NO_TRANS),
415 M_(0),
416 N_(0),
417 Min_MN_(0),
418 LDA_(0),
419 LDAF_(0),
420 LDQ_(0),
421 LDR_(0),
422 INFO_(0),
423 LWORK_(0),
424 ANORM_(ScalarTraits<MagnitudeType>::zero()),
425 BNORM_(ScalarTraits<MagnitudeType>::zero()),
426 A_(0),
427 AF_(0),
428 Q_(0),
429 R_(0)
430 {
431 resetMatrix();
432 }
433 //=============================================================================
434
435 template<typename OrdinalType, typename ScalarType>
~SerialQRDenseSolver()436 SerialQRDenseSolver<OrdinalType,ScalarType>::~SerialQRDenseSolver()
437 {}
438
439 //=============================================================================
440
441 template<typename OrdinalType, typename ScalarType>
resetVectors()442 void SerialQRDenseSolver<OrdinalType,ScalarType>::resetVectors()
443 {
444 LHS_ = Teuchos::null;
445 TMP_ = Teuchos::null;
446 RHS_ = Teuchos::null;
447 solved_ = false;
448 equilibratedB_ = false;
449 }
450 //=============================================================================
451
452 template<typename OrdinalType, typename ScalarType>
resetMatrix()453 void SerialQRDenseSolver<OrdinalType,ScalarType>::resetMatrix()
454 {
455 resetVectors();
456 equilibratedA_ = false;
457 equilibrationModeA_ = 0;
458 equilibrationModeB_ = 0;
459 factored_ = false;
460 matrixIsZero_ = false;
461 formedQ_ = false;
462 formedR_ = false;
463 M_ = 0;
464 N_ = 0;
465 Min_MN_ = 0;
466 LDA_ = 0;
467 LDAF_ = 0;
468 LDQ_ = 0;
469 LDR_ = 0;
470 ANORM_ = -ScalarTraits<MagnitudeType>::one();
471 BNORM_ = -ScalarTraits<MagnitudeType>::one();
472 A_ = 0;
473 AF_ = 0;
474 Q_ = 0;
475 R_ = 0;
476 INFO_ = 0;
477 LWORK_ = 0;
478 }
479 //=============================================================================
480
481 template<typename OrdinalType, typename ScalarType>
setMatrix(const RCP<SerialDenseMatrix<OrdinalType,ScalarType>> & A)482 int SerialQRDenseSolver<OrdinalType,ScalarType>::setMatrix(const RCP<SerialDenseMatrix<OrdinalType,ScalarType> >& A)
483 {
484 TEUCHOS_TEST_FOR_EXCEPTION(A->numRows() < A->numCols(), std::invalid_argument,
485 "SerialQRDenseSolver<T>::setMatrix: the matrix A must have A.numRows() >= A.numCols()!");
486
487 resetMatrix();
488 Matrix_ = A;
489 Factor_ = A;
490 FactorQ_ = A;
491 FactorR_ = A;
492 M_ = A->numRows();
493 N_ = A->numCols();
494 Min_MN_ = TEUCHOS_MIN(M_,N_);
495 LDA_ = A->stride();
496 LDAF_ = LDA_;
497 LDQ_ = LDA_;
498 LDR_ = N_;
499 A_ = A->values();
500 AF_ = A->values();
501 Q_ = A->values();
502 R_ = A->values();
503
504 return(0);
505 }
506 //=============================================================================
507
508 template<typename OrdinalType, typename ScalarType>
setVectors(const RCP<SerialDenseMatrix<OrdinalType,ScalarType>> & X,const RCP<SerialDenseMatrix<OrdinalType,ScalarType>> & B)509 int SerialQRDenseSolver<OrdinalType,ScalarType>::setVectors(const RCP<SerialDenseMatrix<OrdinalType,ScalarType> >& X,
510 const RCP<SerialDenseMatrix<OrdinalType,ScalarType> >& B)
511 {
512 // Check that these new vectors are consistent
513 TEUCHOS_TEST_FOR_EXCEPTION(B->numCols() != X->numCols(), std::invalid_argument,
514 "SerialQRDenseSolver<T>::setVectors: X and B have different numbers of columns!");
515 TEUCHOS_TEST_FOR_EXCEPTION(B->values()==0, std::invalid_argument,
516 "SerialQRDenseSolver<T>::setVectors: B is an empty SerialDenseMatrix<T>!");
517 TEUCHOS_TEST_FOR_EXCEPTION(X->values()==0, std::invalid_argument,
518 "SerialQRDenseSolver<T>::setVectors: X is an empty SerialDenseMatrix<T>!");
519 TEUCHOS_TEST_FOR_EXCEPTION(B->stride()<1, std::invalid_argument,
520 "SerialQRDenseSolver<T>::setVectors: B has an invalid stride!");
521 TEUCHOS_TEST_FOR_EXCEPTION(X->stride()<1, std::invalid_argument,
522 "SerialQRDenseSolver<T>::setVectors: X has an invalid stride!");
523
524 resetVectors();
525 LHS_ = X;
526 RHS_ = B;
527
528 return(0);
529 }
530 //=============================================================================
531
532 template<typename OrdinalType, typename ScalarType>
factor()533 int SerialQRDenseSolver<OrdinalType,ScalarType>::factor() {
534
535 if (factored()) return(0);
536
537 // Equilibrate matrix if necessary
538 int ierr = 0;
539 if (equilibrate_) ierr = equilibrateMatrix();
540 if (ierr!=0) return(ierr);
541
542 allocateWORK();
543 if ((int)TAU_.size()!=Min_MN_) TAU_.resize( Min_MN_ );
544
545 INFO_ = 0;
546
547 // Factor
548 #ifdef HAVE_TEUCHOSNUMERICS_EIGEN
549 EigenMatrixMap aMap( AF_, M_, N_, EigenOuterStride(LDAF_) );
550 EigenScalarArray tau;
551 EigenScalarArrayMap tauMap(&TAU_[0],TEUCHOS_MIN(M_,N_));
552 qr_.compute(aMap);
553 tau = qr_.hCoeffs();
554 for (OrdinalType i=0; i<tauMap.innerSize(); i++) {
555 tauMap(i) = tau(i);
556 }
557 EigenMatrix qrMat = qr_.matrixQR();
558 for (OrdinalType j=0; j<aMap.outerSize(); j++) {
559 for (OrdinalType i=0; i<aMap.innerSize(); i++) {
560 aMap(i,j) = qrMat(i,j);
561 }
562 }
563 #else
564 this->GEQRF(M_, N_, AF_, LDAF_, &TAU_[0], &WORK_[0], LWORK_, &INFO_);
565 #endif
566
567 factored_ = true;
568
569 return(INFO_);
570 }
571
572 //=============================================================================
573
574 template<typename OrdinalType, typename ScalarType>
solve()575 int SerialQRDenseSolver<OrdinalType,ScalarType>::solve() {
576
577 // Check if the matrix is zero
578 if (matrixIsZero_) {
579 LHS_->putScalar(ScalarTraits<ScalarType>::zero());
580 return(0);
581 }
582
583 // Equilibrate RHS if necessary
584 int ierr = 0;
585 if (equilibrate_) {
586 ierr = equilibrateRHS();
587 }
588 if (ierr != 0) return(ierr);
589
590 TEUCHOS_TEST_FOR_EXCEPTION( RHS_==Teuchos::null, std::invalid_argument,
591 "SerialQRDenseSolver<T>::solve: No right-hand side vector (RHS) has been set for the linear system!");
592 TEUCHOS_TEST_FOR_EXCEPTION( LHS_==Teuchos::null, std::invalid_argument,
593 "SerialQRDenseSolver<T>::solve: No solution vector (LHS) has been set for the linear system!");
594 if ( TRANS_ == Teuchos::NO_TRANS ) {
595 TEUCHOS_TEST_FOR_EXCEPTION( LHS_->numRows() != N_, std::invalid_argument,
596 "SerialQRDenseSolver<T>::solve: No transpose: must have LHS_->numRows() = N_");
597 } else {
598 TEUCHOS_TEST_FOR_EXCEPTION( LHS_->numRows() != M_, std::invalid_argument,
599 "SerialQRDenseSolver<T>::solve: Transpose: must have LHS_->numRows() = M_");
600 }
601
602 if (shouldEquilibrate() && !equilibratedA_)
603 std::cout << "WARNING! SerialQRDenseSolver<T>::solve: System should be equilibrated!" << std::endl;
604
605 // Matrix must be factored
606 if (!factored()) factor();
607
608 TEUCHOS_TEST_FOR_EXCEPTION( (equilibratedA_ && !equilibratedB_) || (!equilibratedA_ && equilibratedB_) ,
609 std::logic_error, "SerialQRDenseSolver<T>::solve: Matrix and vectors must be similarly scaled!");
610
611 TMP_ = rcp( new SerialDenseMatrix<OrdinalType,ScalarType>(M_, RHS_->numCols()) );
612 for (OrdinalType j=0; j<RHS_->numCols(); j++) {
613 for (OrdinalType i=0; i<RHS_->numRows(); i++) {
614 (*TMP_)(i,j) = (*RHS_)(i,j);
615 }
616 }
617
618 INFO_ = 0;
619
620 // Solve
621 if ( TRANS_ == Teuchos::NO_TRANS ) {
622
623 // Solve A lhs = rhs
624 this->multiplyQ( Teuchos::CONJ_TRANS, *TMP_ );
625 this->solveR( Teuchos::NO_TRANS, *TMP_ );
626
627 } else {
628
629 // Solve A**H lhs = rhs
630 this->solveR( Teuchos::CONJ_TRANS, *TMP_ );
631 for (OrdinalType j = 0; j < RHS_->numCols(); j++ ) {
632 for (OrdinalType i = N_; i < M_; i++ ) {
633 (*TMP_)(i, j) = ScalarTraits<ScalarType>::zero();
634 }
635 }
636 this->multiplyQ( Teuchos::NO_TRANS, *TMP_ );
637
638 }
639 for (OrdinalType j = 0; j < LHS_->numCols(); j++ ) {
640 for (OrdinalType i = 0; i < LHS_->numRows(); i++ ) {
641 (*LHS_)(i, j) = (*TMP_)(i,j);
642 }
643 }
644
645 solved_ = true;
646
647 // Unequilibrate LHS if necessary
648 if (equilibrate_) {
649 ierr = unequilibrateLHS();
650 }
651 if (ierr != 0) {
652 return ierr;
653 }
654
655 return INFO_;
656 }
657
658 //=============================================================================
659
660 template<typename OrdinalType, typename ScalarType>
computeEquilibrateScaling()661 int SerialQRDenseSolver<OrdinalType,ScalarType>::computeEquilibrateScaling()
662 {
663 MagnitudeType safeMin = ScalarTraits<ScalarType>::sfmin();
664 MagnitudeType prec = ScalarTraits<ScalarType>::prec();
665 ScalarType sZero = ScalarTraits<ScalarType>::zero();
666 ScalarType sOne = ScalarTraits<ScalarType>::one();
667 MagnitudeType mZero = ScalarTraits<ScalarType>::magnitude(sZero);
668
669 MagnitudeType smlnum = ScalarTraits<ScalarType>::magnitude(safeMin/prec);
670 MagnitudeType bignum = ScalarTraits<ScalarType>::magnitude(sOne/smlnum);
671
672 // Compute maximum absolute value of matrix entries
673 OrdinalType i, j;
674 MagnitudeType anorm = ScalarTraits<ScalarType>::magnitude(ScalarTraits<ScalarType>::zero());
675 for (j = 0; j < N_; j++) {
676 for (i = 0; i < M_; i++) {
677 anorm = TEUCHOS_MAX( anorm, ScalarTraits<ScalarType>::magnitude((*Matrix_)(i,j)) );
678 }
679 }
680
681 ANORM_ = anorm;
682
683 if (ANORM_ > mZero && ANORM_ < smlnum) {
684 // Scale matrix norm up to smlnum
685 shouldEquilibrate_ = true;
686 } else if (ANORM_ > bignum) {
687 // Scale matrix norm down to bignum
688 shouldEquilibrate_ = true;
689 } else if (ANORM_ == mZero) {
690 // Matrix all zero. Return zero solution
691 matrixIsZero_ = true;
692 }
693
694 return(0);
695 }
696
697 //=============================================================================
698
699 template<typename OrdinalType, typename ScalarType>
equilibrateMatrix()700 int SerialQRDenseSolver<OrdinalType,ScalarType>::equilibrateMatrix()
701 {
702 if (equilibratedA_) return(0);
703
704 MagnitudeType safeMin = ScalarTraits<ScalarType>::sfmin();
705 MagnitudeType prec = ScalarTraits<ScalarType>::prec();
706 ScalarType sZero = ScalarTraits<ScalarType>::zero();
707 ScalarType sOne = ScalarTraits<ScalarType>::one();
708 MagnitudeType mZero = ScalarTraits<ScalarType>::magnitude(sZero);
709
710 MagnitudeType smlnum = ScalarTraits<ScalarType>::magnitude(safeMin/prec);
711 MagnitudeType bignum = ScalarTraits<ScalarType>::magnitude(sOne/smlnum);
712 OrdinalType BW = 0;
713
714 // Compute maximum absolute value of matrix entries
715 OrdinalType i, j;
716 MagnitudeType anorm = ScalarTraits<ScalarType>::magnitude(ScalarTraits<ScalarType>::zero());
717 for (j = 0; j < N_; j++) {
718 for (i = 0; i < M_; i++) {
719 anorm = TEUCHOS_MAX( anorm, ScalarTraits<ScalarType>::magnitude((*Matrix_)(i,j)) );
720 }
721 }
722
723 ANORM_ = anorm;
724 int ierr1 = 0;
725 if (ANORM_ > mZero && ANORM_ < smlnum) {
726 // Scale matrix norm up to smlnum
727 this->LASCL(Teuchos::ETypeChar[Teuchos::FULL], BW, BW, ANORM_, smlnum, M_, N_, A_, LDA_, &INFO_);
728 equilibrationModeA_ = 1;
729 } else if (ANORM_ > bignum) {
730 // Scale matrix norm down to bignum
731 this->LASCL(Teuchos::ETypeChar[Teuchos::FULL], BW, BW, ANORM_, bignum, M_, N_, A_, LDA_, &INFO_);
732 equilibrationModeA_ = 2;
733 } else if (ANORM_ == mZero) {
734 // Matrix all zero. Return zero solution
735 matrixIsZero_ = true;
736 }
737
738 equilibratedA_ = true;
739
740 return(ierr1);
741 }
742
743 //=============================================================================
744
745 template<typename OrdinalType, typename ScalarType>
equilibrateRHS()746 int SerialQRDenseSolver<OrdinalType,ScalarType>::equilibrateRHS()
747 {
748 if (equilibratedB_) return(0);
749
750 MagnitudeType safeMin = ScalarTraits<ScalarType>::sfmin();
751 MagnitudeType prec = ScalarTraits<ScalarType>::prec();
752 ScalarType sZero = ScalarTraits<ScalarType>::zero();
753 ScalarType sOne = ScalarTraits<ScalarType>::one();
754 MagnitudeType mZero = ScalarTraits<ScalarType>::magnitude(sZero);
755
756 MagnitudeType smlnum = ScalarTraits<ScalarType>::magnitude(safeMin/prec);
757 MagnitudeType bignum = ScalarTraits<ScalarType>::magnitude(sOne/smlnum);
758 OrdinalType BW = 0;
759
760 // Compute maximum absolute value of rhs entries
761 OrdinalType i, j;
762 MagnitudeType bnorm = ScalarTraits<ScalarType>::magnitude(ScalarTraits<ScalarType>::zero());
763 for (j = 0; j <RHS_->numCols(); j++) {
764 for (i = 0; i < RHS_->numRows(); i++) {
765 bnorm = TEUCHOS_MAX( bnorm, ScalarTraits<ScalarType>::magnitude((*RHS_)(i,j)) );
766 }
767 }
768
769 BNORM_ = bnorm;
770
771 int ierr1 = 0;
772 if (BNORM_ > mZero && BNORM_ < smlnum) {
773 // Scale RHS norm up to smlnum
774 this->LASCL(Teuchos::ETypeChar[Teuchos::FULL], BW, BW, BNORM_, smlnum, RHS_->numRows(), RHS_->numCols(),
775 RHS_->values(), RHS_->stride(), &INFO_);
776 equilibrationModeB_ = 1;
777 } else if (BNORM_ > bignum) {
778 // Scale RHS norm down to bignum
779 this->LASCL(Teuchos::ETypeChar[Teuchos::FULL], BW, BW, BNORM_, bignum, RHS_->numRows(), RHS_->numCols(),
780 RHS_->values(), RHS_->stride(), &INFO_);
781 equilibrationModeB_ = 2;
782 }
783
784 equilibratedB_ = true;
785
786 return(ierr1);
787 }
788
789 //=============================================================================
790
791 template<typename OrdinalType, typename ScalarType>
unequilibrateLHS()792 int SerialQRDenseSolver<OrdinalType,ScalarType>::unequilibrateLHS()
793 {
794 if (!equilibratedB_) return(0);
795
796 MagnitudeType safeMin = ScalarTraits<ScalarType>::sfmin();
797 MagnitudeType prec = ScalarTraits<ScalarType>::prec();
798 ScalarType sZero = ScalarTraits<ScalarType>::zero();
799 ScalarType sOne = ScalarTraits<ScalarType>::one();
800 MagnitudeType mZero = ScalarTraits<ScalarType>::magnitude(sZero);
801 (void) mZero; // Silence "unused variable" compiler warning.
802
803 MagnitudeType smlnum = ScalarTraits<ScalarType>::magnitude(safeMin/prec);
804 MagnitudeType bignum = ScalarTraits<ScalarType>::magnitude(sOne/smlnum);
805 OrdinalType BW = 0;
806
807 int ierr1 = 0;
808 if (equilibrationModeA_ == 1) {
809 this->LASCL(Teuchos::ETypeChar[Teuchos::FULL], BW, BW, ANORM_, smlnum, LHS_->numRows(), LHS_->numCols(),
810 LHS_->values(), LHS_->stride(), &INFO_);
811 } else if (equilibrationModeA_ == 2) {
812 this->LASCL(Teuchos::ETypeChar[Teuchos::FULL], BW, BW, ANORM_, bignum, LHS_->numRows(), LHS_->numCols(),
813 LHS_->values(), LHS_->stride(), &INFO_);
814 }
815 if (equilibrationModeB_ == 1) {
816 this->LASCL(Teuchos::ETypeChar[Teuchos::FULL], BW, BW, smlnum, BNORM_, LHS_->numRows(), LHS_->numCols(),
817 LHS_->values(), LHS_->stride(), &INFO_);
818 } else if (equilibrationModeB_ == 2) {
819 this->LASCL(Teuchos::ETypeChar[Teuchos::FULL], BW, BW, bignum, BNORM_, LHS_->numRows(), LHS_->numCols(),
820 LHS_->values(), LHS_->stride(), &INFO_);
821 }
822
823 return(ierr1);
824 }
825
826 //=============================================================================
827
828 template<typename OrdinalType, typename ScalarType>
formQ()829 int SerialQRDenseSolver<OrdinalType,ScalarType>::formQ() {
830
831 // Matrix must be factored first
832 if (!factored()) factor();
833
834 // Store Q separately from factored matrix
835 if (AF_ == Q_) {
836 FactorQ_ = rcp( new SerialDenseMatrix<OrdinalType,ScalarType>(*Factor_) );
837 Q_ = FactorQ_->values();
838 LDQ_ = FactorQ_->stride();
839 }
840
841 INFO_ = 0;
842
843 // Form Q
844 #ifdef HAVE_TEUCHOSNUMERICS_EIGEN
845 EigenMatrixMap qMap( Q_, M_, N_, EigenOuterStride(LDQ_) );
846 EigenMatrix qMat = qr_.householderQ();
847 for (OrdinalType j=0; j<qMap.outerSize(); j++) {
848 for (OrdinalType i=0; i<qMap.innerSize(); i++) {
849 qMap(i,j) = qMat(i,j);
850 }
851 }
852 #else
853 this->UNGQR(M_, N_, N_, Q_, LDQ_, &TAU_[0], &WORK_[0], LWORK_, &INFO_);
854 #endif
855
856 if (INFO_!=0) return(INFO_);
857
858 formedQ_ = true;
859
860 return(INFO_);
861 }
862
863 //=============================================================================
864
865 template<typename OrdinalType, typename ScalarType>
formR()866 int SerialQRDenseSolver<OrdinalType,ScalarType>::formR() {
867
868 // Matrix must be factored first
869 if (!factored()) factor();
870
871 // Store R separately from factored matrix
872 if (AF_ == R_) {
873 FactorR_ = rcp( new SerialDenseMatrix<OrdinalType,ScalarType>(N_, N_) );
874 R_ = FactorR_->values();
875 LDR_ = FactorR_->stride();
876 }
877
878 // Form R
879 for (OrdinalType j=0; j<N_; j++) {
880 for (OrdinalType i=0; i<=j; i++) {
881 (*FactorR_)(i,j) = (*Factor_)(i,j);
882 }
883 }
884
885 formedR_ = true;
886
887 return(0);
888 }
889
890 //=============================================================================
891
892 template<typename OrdinalType, typename ScalarType>
multiplyQ(ETransp transq,SerialDenseMatrix<OrdinalType,ScalarType> & C)893 int SerialQRDenseSolver<OrdinalType, ScalarType>::multiplyQ(ETransp transq, SerialDenseMatrix<OrdinalType, ScalarType> &C)
894 {
895
896 // Check that the matrices are consistent.
897 TEUCHOS_TEST_FOR_EXCEPTION(C.numRows()!=M_, std::invalid_argument,
898 "SerialQRDenseSolver<T>::multiplyQ: C.numRows() != M_!");
899 TEUCHOS_TEST_FOR_EXCEPTION(C.values()==0, std::invalid_argument,
900 "SerialQRDenseSolver<T>::multiplyQ: C is an empty SerialDenseMatrix<T>!");
901
902 // Matrix must be factored
903 if (!factored()) factor();
904
905 INFO_ = 0;
906
907 // Multiply
908 #ifdef HAVE_TEUCHOSNUMERICS_EIGEN
909 EigenMatrixMap cMap( C.values(), C.numRows(), C.numCols(), EigenOuterStride(C.stride()) );
910 if ( transq == Teuchos::NO_TRANS ) {
911 // C = Q * C
912 cMap = qr_.householderQ() * cMap;
913 } else {
914 // C = Q**H * C
915 cMap = qr_.householderQ().adjoint() * cMap;
916 for (OrdinalType j = 0; j < C.numCols(); j++) {
917 for (OrdinalType i = N_; i < C.numRows(); i++ ) {
918 cMap(i, j) = ScalarTraits<ScalarType>::zero();
919 }
920 }
921 }
922 #else
923 Teuchos::ETransp NO_TRANS = Teuchos::NO_TRANS;
924 Teuchos::ETransp TRANS = (Teuchos::ScalarTraits<ScalarType>::isComplex) ? Teuchos::CONJ_TRANS : Teuchos::TRANS;
925 Teuchos::ESide SIDE = Teuchos::LEFT_SIDE;
926
927 if ( transq == Teuchos::NO_TRANS ) {
928
929 // C = Q * C
930 this->UNMQR(ESideChar[SIDE], ETranspChar[NO_TRANS], M_, C.numCols(), N_, AF_, LDAF_,
931 &TAU_[0], C.values(), C.stride(), &WORK_[0], LWORK_, &INFO_);
932
933 } else {
934
935 // C = Q**H * C
936 this->UNMQR(ESideChar[SIDE], ETranspChar[TRANS], M_, C.numCols(), N_, AF_, LDAF_,
937 &TAU_[0], C.values(), C.stride(), &WORK_[0], LWORK_, &INFO_);
938
939 for (OrdinalType j = 0; j < C.numCols(); j++) {
940 for (OrdinalType i = N_; i < C.numRows(); i++ ) {
941 C(i, j) = ScalarTraits<ScalarType>::zero();
942 }
943 }
944 }
945 #endif
946
947 return(INFO_);
948
949 }
950
951 //=============================================================================
952
953 template<typename OrdinalType, typename ScalarType>
solveR(ETransp transr,SerialDenseMatrix<OrdinalType,ScalarType> & C)954 int SerialQRDenseSolver<OrdinalType, ScalarType>::solveR(ETransp transr, SerialDenseMatrix<OrdinalType, ScalarType> &C)
955 {
956
957 // Check that the matrices are consistent.
958 TEUCHOS_TEST_FOR_EXCEPTION(C.numRows()<N_ || C.numRows()>M_, std::invalid_argument,
959 "SerialQRDenseSolver<T>::solveR: must have N_ < C.numRows() < M_!");
960 TEUCHOS_TEST_FOR_EXCEPTION(C.values()==0, std::invalid_argument,
961 "SerialQRDenseSolver<T>::solveR: C is an empty SerialDenseMatrix<T>!");
962
963 // Matrix must be factored
964 if (!factored()) factor();
965
966 INFO_ = 0;
967
968 // Solve
969 #ifdef HAVE_TEUCHOSNUMERICS_EIGEN
970 EigenMatrixMap cMap( C.values(), N_, C.numCols(), EigenOuterStride(C.stride()) );
971 // Check for singularity first like TRTRS
972 for (OrdinalType j=0; j<N_; j++) {
973 if ((qr_.matrixQR())(j,j) == ScalarTraits<ScalarType>::zero()) {
974 INFO_ = j+1;
975 return(INFO_);
976 }
977 }
978 if ( transr == Teuchos::NO_TRANS ) {
979 // C = R \ C
980 qr_.matrixQR().topRows(N_).template triangularView<Eigen::Upper>().solveInPlace(cMap);
981 } else {
982 // C = R**H \ C
983 qr_.matrixQR().topRows(N_).template triangularView<Eigen::Upper>().adjoint().solveInPlace(cMap);
984 }
985 #else
986 Teuchos::ETransp NO_TRANS = Teuchos::NO_TRANS;
987 Teuchos::ETransp TRANS = (Teuchos::ScalarTraits<ScalarType>::isComplex) ? Teuchos::CONJ_TRANS : Teuchos::TRANS;
988 Teuchos::EUplo UPLO = Teuchos::UPPER_TRI;
989 Teuchos::EDiag DIAG = Teuchos::NON_UNIT_DIAG;
990
991 ScalarType* RMAT = (formedR_) ? R_ : AF_;
992 OrdinalType LDRMAT = (formedR_) ? LDR_ : LDAF_;
993
994 if ( transr == Teuchos::NO_TRANS ) {
995
996 // C = R \ C
997 this->TRTRS(EUploChar[UPLO], ETranspChar[NO_TRANS], EDiagChar[DIAG], N_, C.numCols(),
998 RMAT, LDRMAT, C.values(), C.stride(), &INFO_);
999
1000 } else {
1001
1002 // C = R**H \ C
1003 this->TRTRS(EUploChar[UPLO], ETranspChar[TRANS], EDiagChar[DIAG], N_, C.numCols(),
1004 RMAT, LDRMAT, C.values(), C.stride(), &INFO_);
1005
1006 }
1007 #endif
1008
1009 return(INFO_);
1010
1011 }
1012
1013 //=============================================================================
1014
1015 template<typename OrdinalType, typename ScalarType>
Print(std::ostream & os) const1016 void SerialQRDenseSolver<OrdinalType,ScalarType>::Print(std::ostream& os) const {
1017
1018 if (Matrix_!=Teuchos::null) os << "Solver Matrix" << std::endl << *Matrix_ << std::endl;
1019 if (Factor_!=Teuchos::null) os << "Solver Factored Matrix" << std::endl << *Factor_ << std::endl;
1020 if (Q_!=Teuchos::null) os << "Solver Factor Q" << std::endl << *Q_ << std::endl;
1021 if (LHS_ !=Teuchos::null) os << "Solver LHS" << std::endl << *LHS_ << std::endl;
1022 if (RHS_ !=Teuchos::null) os << "Solver RHS" << std::endl << *RHS_ << std::endl;
1023
1024 }
1025
1026 } // namespace Teuchos
1027
1028 #endif /* _TEUCHOS_SERIALQRDENSESOLVER_HPP_ */
1029