Belos Version of the Day
Loading...
Searching...
No Matches
BelosTsqrOrthoManagerImpl.hpp
Go to the documentation of this file.
1// @HEADER
2// *****************************************************************************
3// Belos: Block Linear Solvers Package
4//
5// Copyright 2004-2016 NTESS and the Belos contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
13#ifndef __BelosTsqrOrthoManagerImpl_hpp
14#define __BelosTsqrOrthoManagerImpl_hpp
15
16#include "BelosConfigDefs.hpp" // HAVE_BELOS_TSQR
19#include "BelosOrthoManager.hpp" // OrthoError, etc.
20
21#include "Teuchos_as.hpp"
22#include "Teuchos_ParameterList.hpp"
23#include "Teuchos_ParameterListAcceptorDefaultBase.hpp"
24#ifdef BELOS_TEUCHOS_TIME_MONITOR
25# include "Teuchos_TimeMonitor.hpp"
26#endif // BELOS_TEUCHOS_TIME_MONITOR
27#include <algorithm>
28#include <functional>
29
30namespace Belos {
31
35 class TsqrOrthoError : public OrthoError {
36 public:
37 TsqrOrthoError (const std::string& what_arg) :
39 };
40
60 class TsqrOrthoFault : public OrthoError {
61 public:
62 TsqrOrthoFault (const std::string& what_arg) :
64 };
65
98 template<class Scalar>
100 {
101 public:
108 typedef typename Teuchos::ScalarTraits<Scalar>::magnitudeType magnitude_type;
109
112
117 virtual void
118 operator() (Teuchos::ArrayView<magnitude_type> normsBeforeFirstPass,
119 Teuchos::ArrayView<magnitude_type> normsAfterFirstPass) = 0;
120 };
121
122 template<class Scalar>
124
156 template<class Scalar, class MV, class DM>
158 public Teuchos::ParameterListAcceptorDefaultBase {
159 public:
161 typedef typename Teuchos::ScalarTraits<Scalar>::magnitudeType magnitude_type;
164 typedef DM mat_type;
165 typedef Teuchos::RCP<mat_type> mat_ptr;
166
167 private:
168 typedef Teuchos::ScalarTraits<Scalar> SCT;
169 typedef Teuchos::ScalarTraits<magnitude_type> SCTM;
172 typedef typename MVT::tsqr_adaptor_type tsqr_adaptor_type;
173
174 public:
182 Teuchos::RCP<const Teuchos::ParameterList> getValidParameters () const;
183
185 void setParameterList (const Teuchos::RCP<Teuchos::ParameterList>& params);
186
197 Teuchos::RCP<const Teuchos::ParameterList> getFastParameters ();
198
215 TsqrOrthoManagerImpl (const Teuchos::RCP<Teuchos::ParameterList>& params,
216 const std::string& label);
217
222 TsqrOrthoManagerImpl (const std::string& label);
223
243 void
245 {
246 reorthogCallback_ = callback;
247 }
248
256 void setLabel (const std::string& label) {
257 if (label != label_) {
258 label_ = label;
259
260#ifdef BELOS_TEUCHOS_TIME_MONITOR
261 clearTimer (label, "All orthogonalization");
262 clearTimer (label, "Projection");
263 clearTimer (label, "Normalization");
264
265 timerOrtho_ = makeTimer (label, "All orthogonalization");
266 timerProject_ = makeTimer (label, "Projection");
267 timerNormalize_ = makeTimer (label, "Normalization");
268#endif // BELOS_TEUCHOS_TIME_MONITOR
269 }
270 }
271
273 const std::string& getLabel () const { return label_; }
274
283 void
284 innerProd (const MV& X, const MV& Y, mat_type& Z) const
285 {
286 MVT::MvTransMv (SCT::one(), X, Y, Z);
287 }
288
306 void
307 norm (const MV& X, std::vector<magnitude_type>& normVec) const;
308
318 void
319 project (MV& X,
320 Teuchos::Array<mat_ptr> C,
321 Teuchos::ArrayView<Teuchos::RCP<const MV> > Q);
322
336 int normalize (MV& X, mat_ptr B);
337
356 int
357 normalizeOutOfPlace (MV& X, MV& Q, mat_ptr B);
358
372 int
374 Teuchos::Array<mat_ptr> C,
375 mat_ptr B,
376 Teuchos::ArrayView<Teuchos::RCP<const MV> > Q)
377 {
378 // "false" means we work on X in place. The second argument is
379 // not read or written in that case.
380 return projectAndNormalizeImpl (X, X, false, C, B, Q);
381 }
382
402 int
404 MV& X_out,
405 Teuchos::Array<mat_ptr> C,
406 mat_ptr B,
407 Teuchos::ArrayView<Teuchos::RCP<const MV> > Q)
408 {
409 // "true" means we work on X_in out of place, writing the
410 // results into X_out.
411 return projectAndNormalizeImpl (X_in, X_out, true, C, B, Q);
412 }
413
419 orthonormError (const MV &X) const
420 {
421 const Scalar ONE = SCT::one();
422 const int ncols = MVT::GetNumberVecs(X);
423 mat_ptr XTX = DMT::Create(ncols, ncols);
424 innerProd (X, X, *XTX);
425 DMT::SyncDeviceToHost( *XTX );
426 for (int k = 0; k < ncols; ++k) {
427 DMT::Value(*XTX,k,k) -= ONE;
428 }
429 DMT::SyncHostToDevice( *XTX );
430 return DMT::NormFrobenius( *XTX );
431 }
432
435 orthogError (const MV &X1,
436 const MV &X2) const
437 {
438 const int ncols_X1 = MVT::GetNumberVecs (X1);
439 const int ncols_X2 = MVT::GetNumberVecs (X2);
440 mat_ptr X1_T_X2 = DMT::Create(ncols_X1, ncols_X2);
441 innerProd (X1, X2, *X1_T_X2);
442 return DMT::NormFrobenius( *X1_T_X2 );
443 }
444
448 magnitude_type blockReorthogThreshold() const { return blockReorthogThreshold_; }
449
452 magnitude_type relativeRankTolerance() const { return relativeRankTolerance_; }
453
454 private:
456 Teuchos::RCP<Teuchos::ParameterList> params_;
457
459 mutable Teuchos::RCP<const Teuchos::ParameterList> defaultParams_;
460
462 std::string label_;
463
465 tsqr_adaptor_type tsqrAdaptor_;
466
476 Teuchos::RCP<MV> Q_;
477
479 magnitude_type eps_;
480
484 bool randomizeNullSpace_;
485
491 bool reorthogonalizeBlocks_;
492
496 bool throwOnReorthogFault_;
497
499 magnitude_type blockReorthogThreshold_;
500
502 magnitude_type relativeRankTolerance_;
503
510 bool forceNonnegativeDiagonal_;
511
512#ifdef BELOS_TEUCHOS_TIME_MONITOR
514 Teuchos::RCP<Teuchos::Time> timerOrtho_;
515
517 Teuchos::RCP<Teuchos::Time> timerProject_;
518
520 Teuchos::RCP<Teuchos::Time> timerNormalize_;
521#endif // BELOS_TEUCHOS_TIME_MONITOR
522
524 Teuchos::RCP<ReorthogonalizationCallback<Scalar> > reorthogCallback_;
525
526#ifdef BELOS_TEUCHOS_TIME_MONITOR
535 static Teuchos::RCP<Teuchos::Time>
536 makeTimer (const std::string& prefix,
537 const std::string& timerName)
538 {
539 const std::string timerLabel =
540 prefix.empty() ? timerName : (prefix + ": " + timerName);
541 return Teuchos::TimeMonitor::getNewCounter (timerLabel);
542 }
543
549 void
550 clearTimer (const std::string& prefix,
551 const std::string& timerName)
552 {
553 const std::string timerLabel =
554 prefix.empty() ? timerName : (prefix + ": " + timerName);
555 Teuchos::TimeMonitor::clearCounter (timerLabel);
556 }
557#endif // BELOS_TEUCHOS_TIME_MONITOR
558
560 void
561 raiseReorthogFault (const std::vector<magnitude_type>& normsAfterFirstPass,
562 const std::vector<magnitude_type>& normsAfterSecondPass,
563 const std::vector<int>& faultIndices);
564
574 void
575 checkProjectionDims (int& ncols_X,
576 int& num_Q_blocks,
577 int& ncols_Q_total,
578 const MV& X,
579 Teuchos::ArrayView<Teuchos::RCP<const MV> > Q) const;
580
591 void
592 allocateProjectionCoefficients (Teuchos::Array<mat_ptr>& C,
593 Teuchos::ArrayView<Teuchos::RCP<const MV> > Q,
594 const MV& X,
595 const bool attemptToRecycle = true) const;
596
605 int
606 projectAndNormalizeImpl (MV& X_in,
607 MV& X_out,
608 const bool outOfPlace,
609 Teuchos::Array<mat_ptr> C,
610 mat_ptr B,
611 Teuchos::ArrayView<Teuchos::RCP<const MV> > Q);
612
618 void
619 rawProject (MV& X,
620 Teuchos::ArrayView<Teuchos::RCP<const MV> > Q,
621 Teuchos::ArrayView<mat_ptr> C) const;
622
624 void
625 rawProject (MV& X,
626 const Teuchos::RCP<const MV>& Q,
627 const mat_ptr& C) const;
628
655 int rawNormalize (MV& X, MV& Q, mat_type& B);
656
674 int normalizeOne (MV& X, mat_ptr B) const;
675
703 int normalizeImpl (MV& X, MV& Q, mat_ptr B, const bool outOfPlace);
704 };
705
706 template<class Scalar, class MV, class DM>
707 void
709 setParameterList (const Teuchos::RCP<Teuchos::ParameterList>& params)
710 {
711 using Teuchos::ParameterList;
712 using Teuchos::parameterList;
713 using Teuchos::RCP;
714 using Teuchos::sublist;
715 typedef magnitude_type M; // abbreviation.
716
717 RCP<const ParameterList> defaultParams = getValidParameters ();
718 // Sublist of TSQR implementation parameters; to get below.
720
722 if (params.is_null()) {
724 } else {
726
727 // Don't call validateParametersAndSetDefaults(); we prefer to
728 // ignore parameters that we don't recognize, at least for now.
729 // However, we do fill in missing parameters with defaults.
730
731 randomizeNullSpace_ =
732 theParams->get<bool> ("randomizeNullSpace",
733 defaultParams->get<bool> ("randomizeNullSpace"));
734 reorthogonalizeBlocks_ =
735 theParams->get<bool> ("reorthogonalizeBlocks",
736 defaultParams->get<bool> ("reorthogonalizeBlocks"));
737 throwOnReorthogFault_ =
738 theParams->get<bool> ("throwOnReorthogFault",
739 defaultParams->get<bool> ("throwOnReorthogFault"));
740 blockReorthogThreshold_ =
741 theParams->get<M> ("blockReorthogThreshold",
742 defaultParams->get<M> ("blockReorthogThreshold"));
743 relativeRankTolerance_ =
744 theParams->get<M> ("relativeRankTolerance",
745 defaultParams->get<M> ("relativeRankTolerance"));
746 forceNonnegativeDiagonal_ =
747 theParams->get<bool> ("forceNonnegativeDiagonal",
748 defaultParams->get<bool> ("forceNonnegativeDiagonal"));
749
750 // Get the sublist of TSQR implementation parameters. Use the
751 // default sublist if one isn't provided.
752 if (! theParams->isSublist ("TSQR implementation")) {
753 theParams->set ("TSQR implementation",
754 defaultParams->sublist ("TSQR implementation"));
755 }
756 tsqrParams = sublist (theParams, "TSQR implementation", true);
757 }
758
759 // Send the TSQR implementation parameters to the TSQR adaptor.
760 tsqrAdaptor_.setParameterList (tsqrParams);
761
762 // Save the input parameter list.
764 }
765
766 template<class Scalar, class MV, class DM>
768 TsqrOrthoManagerImpl (const Teuchos::RCP<Teuchos::ParameterList>& params,
769 const std::string& label) :
770 label_ (label),
771 Q_ (Teuchos::null), // Initialized on demand
772 eps_ (SCTM::eps()), // Machine precision
773 randomizeNullSpace_ (true),
774 reorthogonalizeBlocks_ (true),
775 throwOnReorthogFault_ (false),
776 blockReorthogThreshold_ (0),
777 relativeRankTolerance_ (0),
778 forceNonnegativeDiagonal_ (false)
779 {
780 setParameterList (params); // This also sets tsqrAdaptor_'s parameters.
781
782#ifdef BELOS_TEUCHOS_TIME_MONITOR
783 timerOrtho_ = makeTimer (label, "All orthogonalization");
784 timerProject_ = makeTimer (label, "Projection");
785 timerNormalize_ = makeTimer (label, "Normalization");
786#endif // BELOS_TEUCHOS_TIME_MONITOR
787 }
788
789 template<class Scalar, class MV, class DM>
791 TsqrOrthoManagerImpl (const std::string& label) :
792 label_ (label),
793 Q_ (Teuchos::null), // Initialized on demand
794 eps_ (SCTM::eps()), // Machine precision
795 randomizeNullSpace_ (true),
796 reorthogonalizeBlocks_ (true),
797 throwOnReorthogFault_ (false),
798 blockReorthogThreshold_ (0),
799 relativeRankTolerance_ (0),
800 forceNonnegativeDiagonal_ (false)
801 {
802 setParameterList (Teuchos::null); // Set default parameters.
803
804#ifdef BELOS_TEUCHOS_TIME_MONITOR
805 timerOrtho_ = makeTimer (label, "All orthogonalization");
806 timerProject_ = makeTimer (label, "Projection");
807 timerNormalize_ = makeTimer (label, "Normalization");
808#endif // BELOS_TEUCHOS_TIME_MONITOR
809 }
810
811 template<class Scalar, class MV, class DM>
812 void
814 norm (const MV& X, std::vector<magnitude_type>& normVec) const
815 {
816 const int numCols = MVT::GetNumberVecs (X);
817 // std::vector<T>::size_type is unsigned; int is signed. Mixed
818 // unsigned/signed comparisons trigger compiler warnings.
819 if (normVec.size() < static_cast<size_t>(numCols))
820 normVec.resize (numCols); // Resize normvec if necessary.
821 MVT::MvNorm (X, normVec);
822 }
823
824 template<class Scalar, class MV, class DM>
825 void
827 Teuchos::Array<mat_ptr> C,
828 Teuchos::ArrayView<Teuchos::RCP<const MV> > Q)
829 {
830#ifdef BELOS_TEUCHOS_TIME_MONITOR
831 // "Projection" only happens in rawProject(), so we only time
832 // projection inside rawProject(). However, we count the time
833 // spend in project() as part of the whole orthogonalization.
834 //
835 // If project() is called from projectAndNormalize(), the
836 // TimeMonitor won't start timerOrtho_, because it is already
837 // running in projectAndNormalize().
838 Teuchos::TimeMonitor timerMonitorOrtho(*timerOrtho_);
839#endif // BELOS_TEUCHOS_TIME_MONITOR
840
842 checkProjectionDims (ncols_X, num_Q_blocks, ncols_Q_total, X, Q);
843 // Test for quick exit: any dimension of X is zero, or there are
844 // zero Q blocks, or the total number of columns of the Q blocks
845 // is zero.
846 if (ncols_X == 0 || num_Q_blocks == 0 || ncols_Q_total == 0)
847 return;
848
849 // Make space for first-pass projection coefficients
850 allocateProjectionCoefficients (C, Q, X, true);
851
852 // We only use columnNormsBefore and compute pre-projection column
853 // norms if doing block reorthogonalization.
854 std::vector<magnitude_type> columnNormsBefore (ncols_X, magnitude_type(0));
855 if (reorthogonalizeBlocks_)
856 MVT::MvNorm (X, columnNormsBefore);
857
858 // Project (first block orthogonalization step):
859 // C := Q^* X, X := X - Q C.
860 rawProject (X, Q, C);
861
862 // If we are doing block reorthogonalization, reorthogonalize X if
863 // necessary.
864 if (reorthogonalizeBlocks_) {
865 std::vector<magnitude_type> columnNormsAfter (ncols_X, magnitude_type(0));
866 MVT::MvNorm (X, columnNormsAfter);
867
868 // Relative block reorthogonalization threshold.
869 const magnitude_type relThres = blockReorthogThreshold();
870 // Reorthogonalize X if any of its column norms decreased by a
871 // factor more than the block reorthogonalization threshold.
872 // Don't bother trying to subset the columns; that will make the
873 // columns noncontiguous and thus hinder BLAS 3 optimizations.
874 bool reorthogonalize = false;
875 for (int j = 0; j < ncols_X; ++j) {
877 reorthogonalize = true;
878 break;
879 }
880 }
881 if (reorthogonalize) {
882 // Notify the caller via callback about the need for
883 // reorthogonalization.
884 if (! reorthogCallback_.is_null()) {
885 reorthogCallback_->operator() (Teuchos::arrayViewFromVector (columnNormsBefore),
886 Teuchos::arrayViewFromVector (columnNormsAfter));
887 }
888 // Second-pass projection coefficients
889 Teuchos::Array<mat_ptr> C2;
890 allocateProjectionCoefficients (C2, Q, X, false);
891
892 // Perform the second projection pass:
893 // C2 = Q' X, X = X - Q*C2
894 rawProject (X, Q, C2);
895 // Update the projection coefficients
896 for (int k = 0; k < num_Q_blocks; ++k)
897 DMT::Add(*C[k],*C2[k]);
898 }
899 }
900 }
901
902
903 template<class Scalar, class MV, class DM>
904 int
906 {
907 using Teuchos::Range1D;
908 using Teuchos::RCP;
909
910#ifdef BELOS_TEUCHOS_TIME_MONITOR
911 Teuchos::TimeMonitor timerMonitorNormalize(*timerNormalize_);
912 // If normalize() is called internally -- i.e., called from
913 // projectAndNormalize() -- the timer will not be started or
914 // stopped, because it is already running. TimeMonitor handles
915 // recursive invocation by doing nothing.
916 Teuchos::TimeMonitor timerMonitorOrtho(*timerOrtho_);
917#endif // BELOS_TEUCHOS_TIME_MONITOR
918
919 // MVT returns int for this, even though the "local ordinal
920 // type" of the MV may be some other type (for example,
921 // Tpetra::MultiVector<double, int32_t, int64_t, ...>).
922 const int numCols = MVT::GetNumberVecs (X);
923
924 // This special case (for X having only one column) makes
925 // TsqrOrthoManagerImpl equivalent to Modified Gram-Schmidt
926 // orthogonalization with conditional full reorthogonalization,
927 // if all multivector inputs have only one column. It also
928 // avoids allocating Q_ scratch space and copying data when we
929 // don't need to invoke TSQR at all.
930 if (numCols == 1) {
931 return normalizeOne (X, B);
932 }
933
934 // We use Q_ as scratch space for the normalization, since TSQR
935 // requires a scratch multivector (it can't factor in place). Q_
936 // should come from a vector space compatible with X's vector
937 // space, and Q_ should have at least as many columns as X.
938 // Otherwise, we have to reallocate. We also have to allocate
939 // (not "re-") Q_ if we haven't allocated it before. (We can't
940 // allocate Q_ until we have some X, so we need a multivector as
941 // the "prototype.")
942 //
943 // NOTE (mfh 11 Jan 2011) We only increase the number of columsn
944 // in Q_, never decrease. This is OK for typical uses of TSQR,
945 // but you might prefer different behavior in some cases.
946 //
947 // NOTE (mfh 10 Mar 2011) We should only reuse the scratch space
948 // Q_ if X and Q_ have compatible data distributions. However,
949 // Belos' current MultiVecTraits interface does not let us check
950 // for this. Thus, we can only check whether X and Q_ have the
951 // same number of rows. This will behave correctly for the common
952 // case in Belos that all multivectors with the same number of
953 // rows have the same data distribution.
954 //
955 // The specific MV implementation may do more checks than this on
956 // its own and throw an exception if X and Q_ are not compatible,
957 // but it may not. If you find that recycling the Q_ space causes
958 // troubles, you may consider modifying the code below to
959 // reallocate Q_ for every X that comes in.
960 if (Q_.is_null() ||
961 MVT::GetGlobalLength(*Q_) != MVT::GetGlobalLength(X) ||
962 numCols > MVT::GetNumberVecs (*Q_)) {
963 Q_ = MVT::Clone (X, numCols);
964 }
965
966 // normalizeImpl() wants the second MV argument to have the same
967 // number of columns as X. To ensure this, we pass it a view of
968 // Q_ if Q_ has more columns than X. (This is possible if we
969 // previously called normalize() with a different multivector,
970 // since we never reallocate Q_ if it has more columns than
971 // necessary.)
972 if (MVT::GetNumberVecs(*Q_) == numCols) {
973 return normalizeImpl (X, *Q_, B, false);
974 } else {
975 RCP<MV> Q_view = MVT::CloneViewNonConst (*Q_, Range1D(0, numCols-1));
976 return normalizeImpl (X, *Q_view, B, false);
977 }
978 }
979
980 template<class Scalar, class MV, class DM>
981 void
983 allocateProjectionCoefficients (Teuchos::Array<mat_ptr>& C,
984 Teuchos::ArrayView<Teuchos::RCP<const MV> > Q,
985 const MV& X,
986 const bool attemptToRecycle) const
987 {
988 const int num_Q_blocks = Q.size();
989 const int ncols_X = MVT::GetNumberVecs (X);
990 C.resize (num_Q_blocks);
992 {
993 for (int i = 0; i < num_Q_blocks; ++i)
994 {
995 const int ncols_Qi = MVT::GetNumberVecs (*Q[i]);
996 // Create a new C[i] if necessary, otherwise resize if
997 // necessary, otherwise fill with zeros.
998 if (C[i].is_null())
999 C[i] = DMT::Create(ncols_Qi, ncols_X);
1000 else
1001 {
1002 mat_type& Ci = *C[i];
1003 if (DMT::GetNumRows(Ci) != ncols_Qi || DMT::GetNumCols(Ci) != ncols_X)
1004 DMT::Reshape(Ci, ncols_Qi, ncols_X);
1005 else
1006 DMT::PutScalar(Ci, SCT::zero());
1007 }
1008 }
1009 }
1010 else
1011 {
1012 for (int i = 0; i < num_Q_blocks; ++i)
1013 {
1014 const int ncols_Qi = MVT::GetNumberVecs (*Q[i]);
1015 C[i] = DMT::Create(ncols_Qi, ncols_X);
1016 }
1017 }
1018 }
1019
1020 template<class Scalar, class MV, class DM>
1021 int
1023 normalizeOutOfPlace (MV& X, MV& Q, mat_ptr B)
1024 {
1025#ifdef BELOS_TEUCHOS_TIME_MONITOR
1026 Teuchos::TimeMonitor timerMonitorOrtho(*timerOrtho_);
1027 Teuchos::TimeMonitor timerMonitorNormalize(*timerNormalize_);
1028#endif // BELOS_TEUCHOS_TIME_MONITOR
1029
1030 const int numVecs = MVT::GetNumberVecs(X);
1031 if (numVecs == 0) {
1032 return 0; // Nothing to do.
1033 } else if (numVecs == 1) {
1034 // Special case for a single column; scale and copy over.
1035 using Teuchos::Range1D;
1036 using Teuchos::RCP;
1037 using Teuchos::rcp;
1038
1039 // Normalize X in place (faster than TSQR for one column).
1040 const int rank = normalizeOne (X, B);
1041 // Copy results to first column of Q.
1042 RCP<MV> Q_0 = MVT::CloneViewNonConst (Q, Range1D(0,0));
1043 MVT::Assign (X, *Q_0);
1044 return rank;
1045 } else {
1046 // The "true" argument to normalizeImpl() means the output
1047 // vectors go into Q, and the contents of X are overwritten with
1048 // invalid values.
1049 return normalizeImpl (X, Q, B, true);
1050 }
1051 }
1052
1053 template<class Scalar, class MV, class DM>
1054 int
1057 MV& X_out, // Only written if outOfPlace==false.
1058 const bool outOfPlace,
1059 Teuchos::Array<mat_ptr> C,
1060 mat_ptr B,
1061 Teuchos::ArrayView<Teuchos::RCP<const MV> > Q)
1062 {
1063 using Teuchos::Range1D;
1064 using Teuchos::RCP;
1065 using Teuchos::rcp;
1066
1067#ifdef BELOS_TEUCHOS_TIME_MONITOR
1068 // Projection is only timed in rawProject(), and normalization is
1069 // only timed in normalize() and normalizeOutOfPlace().
1070 Teuchos::TimeMonitor timerMonitorOrtho(*timerOrtho_);
1071#endif // BELOS_TEUCHOS_TIME_MONITOR
1072
1073 if (outOfPlace) {
1074 // Make sure that X_out has at least as many columns as X_in.
1075 TEUCHOS_TEST_FOR_EXCEPTION(MVT::GetNumberVecs(X_out) < MVT::GetNumberVecs(X_in),
1076 std::invalid_argument,
1077 "Belos::TsqrOrthoManagerImpl::"
1078 "projectAndNormalizeImpl(..., outOfPlace=true, ...):"
1079 "X_out has " << MVT::GetNumberVecs(X_out)
1080 << " columns, but X_in has "
1081 << MVT::GetNumberVecs(X_in) << " columns.");
1082 }
1083 // Fetch dimensions of X_in and Q, and allocate space for first-
1084 // and second-pass projection coefficients (C resp. C2).
1086 checkProjectionDims (ncols_X, num_Q_blocks, ncols_Q_total, X_in, Q);
1087
1088 // Test for quick exit: if any dimension of X is zero.
1089 if (ncols_X == 0) {
1090 return 0;
1091 }
1092 // If there are zero Q blocks or zero Q columns, just normalize!
1093 if (num_Q_blocks == 0 || ncols_Q_total == 0) {
1094 if (outOfPlace) {
1095 return normalizeOutOfPlace (X_in, X_out, B);
1096 } else {
1097 return normalize (X_in, B);
1098 }
1099 }
1100
1101 // The typical case is that the entries of C have been allocated
1102 // before, so we attempt to recycle the allocations. The call
1103 // below will reallocate if it cannot recycle.
1104 allocateProjectionCoefficients (C, Q, X_in, true);
1105
1106 // If we are doing block reorthogonalization, then compute the
1107 // column norms of X before projecting for the first time. This
1108 // will help us decide whether we need to reorthogonalize X.
1109 std::vector<magnitude_type> normsBeforeFirstPass (ncols_X, SCTM::zero());
1110 if (reorthogonalizeBlocks_) {
1111 MVT::MvNorm (X_in, normsBeforeFirstPass);
1112 }
1113
1114 // First (Modified) Block Gram-Schmidt pass, in place in X_in.
1115 rawProject (X_in, Q, C);
1116
1117 // Make space for the normalization coefficients. This will
1118 // either be a freshly allocated matrix (if B is null), or a view
1119 // of the appropriately sized upper left submatrix of *B (if B is
1120 // not null).
1121 //
1122 // Note that if we let the normalize() routine allocate (in the
1123 // case that B is null), that storage will go away at the end of
1124 // normalize(). (This is because it passes the RCP by value, not
1125 // by reference.)
1126 mat_ptr B_out;
1127 if (B.is_null()) {
1128 B_out = DMT::Create(ncols_X, ncols_X);
1129 } else {
1130 // Make sure that B is no smaller than numCols x numCols.
1131 TEUCHOS_TEST_FOR_EXCEPTION(DMT::GetNumRows(*B) < ncols_X ||
1132 DMT::GetNumCols(*B) < ncols_X,
1133 std::invalid_argument,
1134 "normalizeOne: Input matrix B must be at "
1135 "least " << ncols_X << " x " << ncols_X
1136 << ", but is instead " << DMT::GetNumRows(*B)
1137 << " x " << DMT::GetNumCols(*B) << ".");
1138 // Create a view of the ncols_X by ncols_X upper left
1139 // submatrix of *B. TSQR will write the normalization
1140 // coefficients there.
1141 B_out = DMT::Subview(*B, ncols_X, ncols_X);
1142 }
1143
1144 // Rank of X(_in) after first projection pass. If outOfPlace,
1145 // this overwrites X_in with invalid values, and the results go in
1146 // X_out. Otherwise, it's done in place in X_in.
1147 const int firstPassRank = outOfPlace ?
1148 normalizeOutOfPlace (X_in, X_out, B_out) :
1149 normalize (X_in, B_out);
1150 if (B.is_null()) {
1151 // The input matrix B is null, so assign B_out to it. If B was
1152 // not null on input, then B_out is a view of *B, so we don't
1153 // have to do anything here. Note that SerialDenseMatrix uses
1154 // raw pointers to store data and represent views, so we have to
1155 // be careful about scope.
1156 B = B_out;
1157 }
1158 int rank = firstPassRank; // Current rank of X.
1159
1160 // If X was not full rank after projection and randomizeNullSpace_
1161 // is true, then normalize(OutOfPlace)() replaced the null space
1162 // basis of X with random vectors, and orthogonalized them against
1163 // the column space basis of X. However, we still need to
1164 // orthogonalize the random vectors against the Q[i], after which
1165 // we will need to renormalize them.
1166 //
1167 // If outOfPlace, then we need to work in X_out (where
1168 // normalizeOutOfPlace() wrote the normalized vectors).
1169 // Otherwise, we need to work in X_in.
1170 //
1171 // Note: We don't need to keep the new projection coefficients,
1172 // since they are multiplied by the "small" part of B
1173 // corresponding to the null space of the original X.
1174 if (firstPassRank < ncols_X && randomizeNullSpace_) {
1177
1178 // Space for projection coefficients (will be thrown away)
1179 Teuchos::Array<mat_ptr> C_null (num_Q_blocks);
1180 for (int k = 0; k < num_Q_blocks; ++k) {
1181 const int numColsQk = MVT::GetNumberVecs(*Q[k]);
1182 C_null[k] = DMT::Create(numColsQk, numNullSpaceCols);
1183 }
1184 // Space for normalization coefficients (will be thrown away).
1185 mat_ptr B_null = DMT::Create(numNullSpaceCols, numNullSpaceCols);
1186
1188 if (outOfPlace) {
1189 // View of the null space basis columns of X.
1190 // normalizeOutOfPlace() wrote them into X_out.
1191 RCP<MV> X_out_null = MVT::CloneViewNonConst (X_out, nullSpaceIndices);
1192 // Use X_in for scratch space. Copy X_out_null into the
1193 // last few columns of X_in (X_in_null) and do projections
1194 // in there. (This saves us a copy wen we renormalize
1195 // (out of place) back into X_out.)
1196 RCP<MV> X_in_null = MVT::CloneViewNonConst (X_in, nullSpaceIndices);
1197 MVT::Assign (*X_out_null, *X_in_null);
1198 // Project the new random vectors against the Q blocks, and
1199 // renormalize the result into X_out_null.
1200 rawProject (*X_in_null, Q, C_null);
1201 randomVectorsRank = normalizeOutOfPlace (*X_in_null, *X_out_null, B_null);
1202 } else {
1203 // View of the null space columns of X.
1204 // They live in X_in.
1205 RCP<MV> X_null = MVT::CloneViewNonConst (X_in, nullSpaceIndices);
1206 // Project the new random vectors against the Q blocks,
1207 // and renormalize the result (in place).
1208 rawProject (*X_null, Q, C_null);
1209 randomVectorsRank = normalize (*X_null, B_null);
1210 }
1211 // While unusual, it is still possible for the random data not
1212 // to be full rank after projection and normalization. In that
1213 // case, we could try another set of random data and recurse as
1214 // necessary, but instead for now we just raise an exception.
1216 TsqrOrthoError,
1217 "Belos::TsqrOrthoManagerImpl::projectAndNormalize"
1218 "OutOfPlace(): After projecting and normalizing the "
1219 "random vectors (used to replace the null space "
1220 "basis vectors from normalizing X), they have rank "
1221 << randomVectorsRank << ", but should have full "
1222 "rank " << numNullSpaceCols << ".");
1223 }
1224
1225 // Whether or not X_in was full rank after projection, we still
1226 // might want to reorthogonalize against Q.
1227 if (reorthogonalizeBlocks_) {
1228 // We are only interested in the column space basis of X
1229 // resp. X_out.
1230 std::vector<magnitude_type>
1231 normsAfterFirstPass (firstPassRank, SCTM::zero());
1232 std::vector<magnitude_type>
1233 normsAfterSecondPass (firstPassRank, SCTM::zero());
1234
1235 // Compute post-first-pass (pre-normalization) norms. We could
1236 // have done that using MVT::MvNorm() on X_in after projecting,
1237 // but before the first normalization. However, that operation
1238 // may be expensive. It is also unnecessary: after calling
1239 // normalize(), the 2-norm of B(:,j) is the 2-norm of X_in(:,j)
1240 // before normalization, in exact arithmetic.
1241 //
1242 // NOTE (mfh 06 Nov 2010) This is one way that combining
1243 // projection and normalization into a single kernel --
1244 // projectAndNormalize() -- pays off. In project(), we have to
1245 // compute column norms of X before and after projection. Here,
1246 // we get them for free from the normalization coefficients.
1247 Teuchos::BLAS<int, Scalar> blas;
1248 DMT::SyncDeviceToHost( *B_out );
1249 for (int j = 0; j < firstPassRank; ++j) {
1250 mat_ptr B_j = DMT::Subview( *B_out, firstPassRank, 1, 0, j );
1251 // Teuchos::BLAS::NRM2 returns a magnitude_type result on
1252 // Scalar inputs.
1253 normsAfterFirstPass[j] = blas.NRM2 (firstPassRank, DMT::GetRawHostPtr(*B_j), 1);
1254 }
1255 // Test whether any of the norms dropped below the
1256 // reorthogonalization threshold.
1257 bool reorthogonalize = false;
1258 for (int j = 0; j < firstPassRank; ++j) {
1259 // If any column's norm decreased too much, mark this block
1260 // for reorthogonalization. Note that this test will _not_
1261 // activate reorthogonalization if a column's norm before the
1262 // first project-and-normalize step was zero. It _will_
1263 // activate reorthogonalization if the column's norm before
1264 // was not zero, but is zero now.
1265 const magnitude_type curThreshold =
1266 blockReorthogThreshold() * normsBeforeFirstPass[j];
1268 reorthogonalize = true;
1269 break;
1270 }
1271 }
1272
1273 // Notify the caller via callback about the need for
1274 // reorthogonalization.
1275 if (! reorthogCallback_.is_null()) {
1276 using Teuchos::arrayViewFromVector;
1277 (*reorthogCallback_) (arrayViewFromVector (normsBeforeFirstPass),
1279 }
1280
1281 // Perform another Block Gram-Schmidt pass if necessary. "Twice
1282 // is enough" (Kahan's theorem) for a Krylov method, unless
1283 // (using Stewart's term) there is an "orthogonalization fault"
1284 // (indicated by reorthogFault).
1285 //
1286 // NOTE (mfh 07 Nov 2010) For now, we include the entire block
1287 // of X, including possible random data (that was already
1288 // projected and normalized above). It might make more sense
1289 // just to process the first firstPassRank columns of X.
1290 // However, the resulting reorthogonalization should still be
1291 // correct regardless.
1292 bool reorthogFault = false;
1293 // Indices of X at which there was an orthogonalization fault.
1294 std::vector<int> faultIndices;
1295 if (reorthogonalize) {
1296 using Teuchos::Copy;
1297 using Teuchos::NO_TRANS;
1298
1299 // If we're using out-of-place normalization, copy X_out
1300 // (results of first project and normalize pass) back into
1301 // X_in, for the second project and normalize pass.
1302 if (outOfPlace) {
1303 MVT::Assign (X_out, X_in);
1304 }
1305
1306 // C2 is only used internally, so we know that we are
1307 // allocating fresh and not recycling allocations. Stating
1308 // this lets us save time checking dimensions.
1309 Teuchos::Array<mat_ptr> C2;
1310 allocateProjectionCoefficients (C2, Q, X_in, false);
1311
1312 // Block Gram-Schmidt (again). Delay updating the block
1313 // coefficients until we have the new normalization
1314 // coefficients, which we need in order to do the update.
1315 rawProject (X_in, Q, C2);
1316
1317 // Coefficients for (re)normalization of X_in.
1318 mat_ptr B2 = DMT::Create(ncols_X, ncols_X);
1319
1320 // Normalize X_in (into X_out, if working out of place).
1321 const int secondPassRank = outOfPlace ?
1322 normalizeOutOfPlace (X_in, X_out, B2) :
1323 normalize (X_in, B2);
1324 rank = secondPassRank; // Current rank of X
1325
1326 // Update normalization coefficients. We begin with copying
1327 // B_out, since the BLAS' _GEMM routine doesn't let us alias
1328 // its input and output arguments.
1329 mat_ptr B_copy = DMT::CreateCopy(*B_out);
1330 // B_out := B2 * B_out (where input B_out is in B_copy).
1331 DMT::SyncDeviceToHost( *B2 );
1332 DMT::SyncDeviceToHost( *B_out );
1333 blas.GEMM( NO_TRANS, NO_TRANS, DMT::GetNumRows( *B2 ), DMT::GetNumCols( *B_copy ),
1334 DMT::GetNumCols( *B2 ), SCT::one(), DMT::GetRawHostPtr( *B2 ),
1335 DMT::GetStride( *B2 ), DMT::GetRawHostPtr( *B_copy ),
1336 DMT::GetStride( *B_copy ), SCT::zero(),
1337 DMT::GetRawHostPtr( *B_out ), DMT::GetStride( *B_out ) );
1338 DMT::SyncHostToDevice( *B_out );
1339
1340 // Update the block coefficients from the projection step. We
1341 // use B_copy for this (a copy of B_out, the first-pass
1342 // normalization coefficients).
1343 for (int k = 0; k < num_Q_blocks; ++k) {
1344 // C[k] := C2[k]*B_copy + C[k].
1345 DMT::SyncDeviceToHost( *C[k] );
1346 DMT::SyncDeviceToHost( *C2[k] );
1347
1348 blas.GEMM( NO_TRANS, NO_TRANS, DMT::GetNumRows( *C2[k] ), DMT::GetNumCols( *B_copy ),
1349 DMT::GetNumCols( *C2[k] ), SCT::one(), DMT::GetRawHostPtr( *C2[k] ),
1350 DMT::GetStride( *C2[k] ), DMT::GetRawHostPtr( *B_copy ),
1351 DMT::GetStride( *B_copy ), SCT::one(),
1352 DMT::GetRawHostPtr( *C[k] ), DMT::GetStride( *C[k] ) );
1353 DMT::SyncHostToDevice( *C[k] );
1354 }
1355 // Compute post-second-pass (pre-normalization) norms, using
1356 // B2 (the coefficients from the second normalization step) in
1357 // the same way as with B_out before.
1358 for (int j = 0; j < rank; ++j) {
1359 mat_ptr B2_j = DMT::Subview( *B2, rank, 1, 0, j );
1360 normsAfterSecondPass[j] = blas.NRM2 (rank, DMT::GetRawHostPtr(*B2_j), 1);
1361 }
1362 // Test whether any of the norms dropped below the
1363 // reorthogonalization threshold. If so, it's an
1364 // orthogonalization fault, which requires expensive recovery.
1365 reorthogFault = false;
1366 for (int j = 0; j < rank; ++j) {
1367 const magnitude_type relativeLowerBound =
1368 blockReorthogThreshold() * normsAfterFirstPass[j];
1370 reorthogFault = true;
1371 faultIndices.push_back (j);
1372 }
1373 }
1374 } // if (reorthogonalize) // reorthogonalization pass
1375
1376 if (reorthogFault) {
1377 if (throwOnReorthogFault_) {
1378 raiseReorthogFault (normsAfterFirstPass,
1380 faultIndices);
1381 } else {
1382 // NOTE (mfh 19 Jan 2011) We could handle the fault here by
1383 // slowly reorthogonalizing, one vector at a time, the
1384 // offending vectors of X. However, we choose not to
1385 // implement this for now. If it becomes a problem, let us
1386 // know and we will prioritize implementing this.
1387 TEUCHOS_TEST_FOR_EXCEPTION(true, std::logic_error,
1388 "TsqrOrthoManagerImpl has not yet implemented"
1389 " recovery from an orthogonalization fault.");
1390 }
1391 }
1392 } // if (reorthogonalizeBlocks_)
1393 return rank;
1394 }
1395
1396 template<class Scalar, class MV, class DM>
1397 void
1398 TsqrOrthoManagerImpl<Scalar, MV, DM>::
1399 raiseReorthogFault (const std::vector<magnitude_type>& normsAfterFirstPass,
1400 const std::vector<magnitude_type>& normsAfterSecondPass,
1401 const std::vector<int>& faultIndices)
1402 {
1403 using std::endl;
1404 typedef std::vector<int>::size_type size_type;
1405 std::ostringstream os;
1406
1407 os << "Orthogonalization fault at the following column(s) of X:" << endl;
1408 os << "Column\tNorm decrease factor" << endl;
1409 for (size_type k = 0; k < faultIndices.size(); ++k) {
1410 const int index = faultIndices[k];
1411 const magnitude_type decreaseFactor =
1413 os << index << "\t" << decreaseFactor << endl;
1414 }
1415 throw TsqrOrthoFault (os.str());
1416 }
1417
1418 template<class Scalar, class MV, class DM>
1419 Teuchos::RCP<const Teuchos::ParameterList>
1421 {
1422 using Teuchos::ParameterList;
1423 using Teuchos::parameterList;
1424 using Teuchos::RCP;
1425
1426 if (defaultParams_.is_null()) {
1427 RCP<ParameterList> params = parameterList ("TsqrOrthoManagerImpl");
1428 //
1429 // TSQR parameters (set as a sublist).
1430 //
1431 params->set ("TSQR implementation", *(tsqrAdaptor_.getValidParameters()),
1432 "TSQR implementation parameters.");
1433 //
1434 // Orthogonalization parameters
1435 //
1436 const bool defaultRandomizeNullSpace = true;
1437 params->set ("randomizeNullSpace", defaultRandomizeNullSpace,
1438 "Whether to fill in null space vectors with random data.");
1439
1440 const bool defaultReorthogonalizeBlocks = true;
1441 params->set ("reorthogonalizeBlocks", defaultReorthogonalizeBlocks,
1442 "Whether to do block reorthogonalization as necessary.");
1443
1444 // This parameter corresponds to the "blk_tol_" parameter in
1445 // Belos' DGKSOrthoManager. We choose the same default value.
1447 magnitude_type(10) * SCTM::squareroot (SCTM::eps());
1448 params->set ("blockReorthogThreshold", defaultBlockReorthogThreshold,
1449 "If reorthogonalizeBlocks==true, and if the norm of "
1450 "any column within a block decreases by this much or "
1451 "more after orthogonalization, we reorthogonalize.");
1452
1453 // This parameter corresponds to the "sing_tol_" parameter in
1454 // Belos' DGKSOrthoManager. We choose the same default value.
1456 Teuchos::as<magnitude_type>(10) * SCTM::eps();
1457
1458 // If the relative rank tolerance is zero, then we will always
1459 // declare blocks to be numerically full rank, as long as no
1460 // singular values are zero.
1461 params->set ("relativeRankTolerance", defaultRelativeRankTolerance,
1462 "Relative tolerance to determine the numerical rank of a "
1463 "block when normalizing.");
1464
1465 // See Stewart's 2008 paper on block Gram-Schmidt for a definition
1466 // of "orthogonalization fault."
1467 const bool defaultThrowOnReorthogFault = true;
1468 params->set ("throwOnReorthogFault", defaultThrowOnReorthogFault,
1469 "Whether to throw an exception if an orthogonalization "
1470 "fault occurs. This only matters if reorthogonalization "
1471 "is enabled (reorthogonalizeBlocks==true).");
1472
1473 const bool defaultForceNonnegativeDiagonal = false;
1474 params->set ("forceNonnegativeDiagonal", defaultForceNonnegativeDiagonal,
1475 "Whether to force the R factor produced by the normalization "
1476 "step to have a nonnegative diagonal.");
1477
1478 defaultParams_ = params;
1479 }
1480 return defaultParams_;
1481 }
1482
1483 template<class Scalar, class MV, class DM>
1484 Teuchos::RCP<const Teuchos::ParameterList>
1486 {
1487 using Teuchos::ParameterList;
1488 using Teuchos::RCP;
1489 using Teuchos::rcp;
1490
1491 RCP<const ParameterList> defaultParams = getValidParameters();
1492 // Start with a clone of the default parameters.
1494
1495 // Disable reorthogonalization and randomization of the null
1496 // space basis. Reorthogonalization tolerances don't matter,
1497 // since we aren't reorthogonalizing blocks in the fast
1498 // settings. We can leave the default values. Also,
1499 // (re)orthogonalization faults may only occur with
1500 // reorthogonalization, so we don't have to worry about the
1501 // "throwOnReorthogFault" setting.
1502 const bool randomizeNullSpace = false;
1503 params->set ("randomizeNullSpace", randomizeNullSpace);
1504 const bool reorthogonalizeBlocks = false;
1505 params->set ("reorthogonalizeBlocks", reorthogonalizeBlocks);
1506
1507 return params;
1508 }
1509
1510 template<class Scalar, class MV, class DM>
1511 int
1513 rawNormalize (MV& X,
1514 MV& Q,
1515 DM& B)
1516 {
1517 int rank;
1518 try {
1519 // This call only computes the QR factorization X = Q B.
1520 // It doesn't compute the rank of X. That comes from
1521 // revealRank() below.
1522 tsqrAdaptor_.factorExplicit (X, Q, B, forceNonnegativeDiagonal_);
1523 // This call will only modify *B if *B on input is not of full
1524 // numerical rank.
1525 rank = tsqrAdaptor_.revealRank (Q, B, relativeRankTolerance_);
1526 } catch (std::exception& e) {
1527 throw TsqrOrthoError (e.what()); // Toss the exception up the chain.
1528 }
1529 return rank;
1530 }
1531
1532 template<class Scalar, class MV, class DM>
1533 int
1534 TsqrOrthoManagerImpl<Scalar, MV, DM>::
1535 normalizeOne (MV& X,
1536 Teuchos::RCP<DM> B) const
1537 {
1538 // Make space for the normalization coefficient. This will either
1539 // be a freshly allocated matrix (if B is null), or a view of the
1540 // 1x1 upper left submatrix of *B (if B is not null).
1541 mat_ptr B_out;
1542 if (B.is_null()) {
1543 B_out = DMT::Create(1, 1);
1544 } else {
1545 const int theNumRows = DMT::GetNumRows(*B);
1546 const int theNumCols = DMT::GetNumCols(*B;
1548 theNumRows < 1 || theNumCols < 1, std::invalid_argument,
1549 "normalizeOne: Input matrix B must be at least 1 x 1, but "
1550 "is instead " << theNumRows << " x " << theNumCols << ".");
1551 // Create a view of the 1x1 upper left submatrix of *B.
1552 B_out = DMT::Subview(*B, 1, 1);
1553 }
1554
1555 // Compute the norm of X, and write the result to B_out.
1556 std::vector<magnitude_type> theNorm (1, SCTM::zero());
1557 MVT::MvNorm (X, theNorm);
1558 DMT::PutScalar(*B_out, theNorm[0]);
1559
1560 if (B.is_null()) {
1561 // The input matrix B is null, so assign B_out to it. If B was
1562 // not null on input, then B_out is a view of *B, so we don't
1563 // have to do anything here. Note that SerialDenseMatrix uses
1564 // raw pointers to store data and represent views, so we have to
1565 // be careful about scope.
1566 B = B_out;
1567 }
1568
1569 // Scale X by its norm, if its norm is zero. Otherwise, do the
1570 // right thing based on whether the user wants us to fill the null
1571 // space with random vectors.
1572 if (theNorm[0] == SCTM::zero()) {
1573 // Make a view of the first column of Q, fill it with random
1574 // data, and normalize it. Throw away the resulting norm.
1575 if (randomizeNullSpace_) {
1576 MVT::MvRandom(X);
1577 MVT::MvNorm (X, theNorm);
1578 if (theNorm[0] == SCTM::zero()) {
1579 // It is possible that a random vector could have all zero
1580 // entries, but unlikely. We could try again, but it's also
1581 // possible that multiple tries could result in zero
1582 // vectors. We choose instead to give up.
1583 throw TsqrOrthoError("normalizeOne: a supposedly random "
1584 "vector has norm zero!");
1585 } else {
1586 // NOTE (mfh 09 Nov 2010) I'm assuming that dividing a
1587 // Scalar by a magnitude_type is defined and that it results
1588 // in a Scalar.
1589 const Scalar alpha = SCT::one() / theNorm[0];
1590 MVT::MvScale (X, alpha);
1591 }
1592 }
1593 return 0; // The rank of the matrix (actually just one vector) X.
1594 } else {
1595 // NOTE (mfh 09 Nov 2010) I'm assuming that dividing a Scalar by
1596 // a magnitude_type is defined and that it results in a Scalar.
1597 const Scalar alpha = SCT::one() / theNorm[0];
1598 MVT::MvScale (X, alpha);
1599 return 1; // The rank of the matrix (actually just one vector) X.
1600 }
1601 }
1602
1603
1604 template<class Scalar, class MV, class DM>
1605 void
1606 TsqrOrthoManagerImpl<Scalar, MV, DM>::
1607 rawProject (MV& X,
1608 Teuchos::ArrayView<Teuchos::RCP<const MV> > Q,
1609 Teuchos::ArrayView<Teuchos::RCP<DM> > C) const
1610 {
1611#ifdef BELOS_TEUCHOS_TIME_MONITOR
1612 Teuchos::TimeMonitor timerMonitorNormalize(*timerProject_);
1613#endif // BELOS_TEUCHOS_TIME_MONITOR
1614
1615 // "Modified Gram-Schmidt" version of Block Gram-Schmidt.
1616 const int num_Q_blocks = Q.size();
1617 for (int i = 0; i < num_Q_blocks; ++i)
1618 {
1619 // TEUCHOS_TEST_FOR_EXCEPTION(C[i].is_null(), std::logic_error,
1620 // "TsqrOrthoManagerImpl::rawProject(): C["
1621 // << i << "] is null");
1622 // TEUCHOS_TEST_FOR_EXCEPTION(Q[i].is_null(), std::logic_error,
1623 // "TsqrOrthoManagerImpl::rawProject(): Q["
1624 // << i << "] is null");
1625 mat_type& Ci = *C[i];
1626 const MV& Qi = *Q[i];
1627 innerProd (Qi, X, Ci);
1628 MVT::MvTimesMatAddMv (-SCT::one(), Qi, Ci, SCT::one(), X);
1629 }
1630 }
1631
1632
1633 template<class Scalar, class MV, class DM>
1634 void
1635 TsqrOrthoManagerImpl<Scalar, MV, DM>::
1636 rawProject (MV& X,
1637 const Teuchos::RCP<const MV>& Q,
1638 const Teuchos::RCP<DM>& C) const
1639 {
1640#ifdef BELOS_TEUCHOS_TIME_MONITOR
1641 Teuchos::TimeMonitor timerMonitorNormalize(*timerProject_);
1642#endif // BELOS_TEUCHOS_TIME_MONITOR
1643
1644 // Block Gram-Schmidt
1645 innerProd (*Q, X, *C);
1646 MVT::MvTimesMatAddMv (-SCT::one(), *Q, *C, SCT::one(), X);
1647 }
1648
1649 template<class Scalar, class MV, class DM>
1650 int
1651 TsqrOrthoManagerImpl<Scalar, MV, DM>::
1652 normalizeImpl (MV& X,
1653 MV& Q,
1654 Teuchos::RCP<DM> B,
1655 const bool outOfPlace)
1656 {
1657 using Teuchos::Range1D;
1658 using Teuchos::RCP;
1659 using Teuchos::rcp;
1660 using Teuchos::ScalarTraits;
1661 using Teuchos::tuple;
1662
1663 const int numCols = MVT::GetNumberVecs (X);
1664 if (numCols == 0) {
1665 return 0; // Fast exit for an empty input matrix.
1666 }
1667
1668 // We allow Q to have more columns than X. In that case, we only
1669 // touch the first numCols columns of Q.
1671 MVT::GetNumberVecs (Q) < numCols, std::invalid_argument,
1672 "TsqrOrthoManagerImpl::normalizeImpl: Q has "
1673 << MVT::GetNumberVecs(Q) << " columns. This is too "
1674 "few, since X has " << numCols << " columns.");
1675 // TSQR wants a Q with the same number of columns as X, so have it
1676 // work on a nonconstant view of Q with the same number of columns
1677 // as X.
1678 RCP<MV> Q_view = MVT::CloneViewNonConst (Q, Range1D (0, numCols-1));
1679
1680 // Make space for the normalization coefficients. This will
1681 // either be a freshly allocated matrix (if B is null), or a view
1682 // of the appropriately sized upper left submatrix of *B (if B is
1683 // not null).
1684 mat_ptr B_out;
1685 if (B.is_null ()) {
1686 B_out = DMT::Create(numCols, numCols);
1687 } else {
1688 // Make sure that B is no smaller than numCols x numCols.
1690 DMT::GetNumRows(*B) < numCols || DMT::GetNumCols(*B) < numCols, std::invalid_argument,
1691 "TsqrOrthoManagerImpl::normalizeImpl: Input matrix B must be at least "
1692 << numCols << " x " << numCols << ", but is instead " << DMT::GetNumRows (*B)
1693 << " x " << DMT::GetNumCols(*B) << ".");
1694 // Create a view of the numCols x numCols upper left submatrix
1695 // of *B. TSQR will write the normalization coefficients there.
1696 B_out = DMT::Subview(*B, numCols, numCols);
1697 }
1698
1699 // Compute rank-revealing decomposition (in this case, TSQR of X
1700 // followed by SVD of the R factor and appropriate updating of the
1701 // resulting Q factor) of X. X is modified in place and filled
1702 // with garbage, and Q_view contains the resulting explicit Q
1703 // factor. Later, we will copy this back into X.
1704 //
1705 // The matrix *B_out will only be upper triangular if X is of full
1706 // numerical rank. Otherwise, the entries below the diagonal may
1707 // be filled in as well.
1708 const int rank = rawNormalize (X, *Q_view, *B_out);
1709 if (B.is_null ()) {
1710 // The input matrix B is null, so assign B_out to it. If B was
1711 // not null on input, then B_out is a view of *B, so we don't
1712 // have to do anything here. Note that SerialDenseMatrix uses
1713 // raw pointers to store data and represent views, so we have to
1714 // be careful about scope.
1715 B = B_out;
1716 }
1717
1719 rank < 0 || rank > numCols, std::logic_error,
1720 "Belos::TsqrOrthoManagerImpl::normalizeImpl: rawNormalize returned rank "
1721 " = " << rank << " for a matrix X with " << numCols << " columns. "
1722 "Please report this bug to the Belos developers.");
1723
1724 // If X is full rank or we don't want to replace its null space
1725 // basis with random vectors, then we're done.
1726 if (rank == numCols || ! randomizeNullSpace_) {
1727 // If we're supposed to be working in place in X, copy the
1728 // results back from Q_view into X.
1729 if (! outOfPlace) {
1730 MVT::Assign (*Q_view, X);
1731 }
1732 return rank;
1733 }
1734
1735 if (randomizeNullSpace_ && rank < numCols) {
1736 // X wasn't full rank. Replace the null space basis of X (in
1737 // the last numCols-rank columns of Q_view) with random data,
1738 // project it against the first rank columns of Q_view, and
1739 // normalize.
1740 //
1741 // Number of columns to fill with random data.
1742 const int nullSpaceNumCols = numCols - rank;
1743 // Inclusive range of indices of columns of X to fill with
1744 // random data.
1746
1747 // rawNormalize wrote the null space basis vectors into Q_view.
1748 // We continue to work in place in Q_view by writing the random
1749 // data there and (if there is a nontrival column space)
1750 // projecting in place against the column space basis vectors
1751 // (also in Q_view).
1752 RCP<MV> Q_null = MVT::CloneViewNonConst (*Q_view, nullSpaceIndices);
1753 // Replace the null space basis with random data.
1754 MVT::MvRandom (*Q_null);
1755
1756 // Make sure that the "random" data isn't all zeros. This is
1757 // statistically nearly impossible, but we test for debugging
1758 // purposes.
1759 {
1760 std::vector<magnitude_type> norms (MVT::GetNumberVecs (*Q_null));
1761 MVT::MvNorm (*Q_null, norms);
1762
1763 bool anyZero = false;
1764 typedef typename std::vector<magnitude_type>::const_iterator iter_type;
1765 for (iter_type it = norms.begin(); it != norms.end(); ++it) {
1766 if (*it == SCTM::zero()) {
1767 anyZero = true;
1768 }
1769 }
1770 if (anyZero) {
1771 std::ostringstream os;
1772 os << "TsqrOrthoManagerImpl::normalizeImpl: "
1773 "We are being asked to randomize the null space, for a matrix "
1774 "with " << numCols << " columns and reported column rank "
1775 << rank << ". The inclusive range of columns to fill with "
1776 "random data is [" << nullSpaceIndices.lbound() << ","
1777 << nullSpaceIndices.ubound() << "]. After filling the null "
1778 "space vectors with random numbers, at least one of the vectors"
1779 " has norm zero. Here are the norms of all the null space "
1780 "vectors: [";
1781 for (iter_type it = norms.begin(); it != norms.end(); ++it) {
1782 os << *it;
1783 if (it+1 != norms.end())
1784 os << ", ";
1785 }
1786 os << "].) There is a tiny probability that this could happen "
1787 "randomly, but it is likely a bug. Please report it to the "
1788 "Belos developers, especially if you are able to reproduce the "
1789 "behavior.";
1790 TEUCHOS_TEST_FOR_EXCEPTION(anyZero, TsqrOrthoError, os.str());
1791 }
1792 }
1793
1794 if (rank > 0) {
1795 // Project the random data against the column space basis of
1796 // X, using a simple block projection ("Block Classical
1797 // Gram-Schmidt"). This is accurate because we've already
1798 // orthogonalized the column space basis of X nearly to
1799 // machine precision via a QR factorization (TSQR) with
1800 // accuracy comparable to Householder QR.
1801 RCP<const MV> Q_col = MVT::CloneView (*Q_view, Range1D (0, rank-1));
1802
1803 // Temporary storage for projection coefficients. We don't
1804 // need to keep them, since they represent the null space
1805 // basis (for which the coefficients are logically zero).
1806 mat_ptr C_null = DMT::Create(rank, nullSpaceNumCols);
1807 rawProject (*Q_null, Q_col, C_null);
1808 }
1809 // Normalize the projected random vectors, so that they are
1810 // mutually orthogonal (as well as orthogonal to the column
1811 // space basis of X). We use X for the output of the
1812 // normalization: for out-of-place normalization (outOfPlace ==
1813 // true), X is overwritten with "invalid values" anyway, and for
1814 // in-place normalization (outOfPlace == false), we want the
1815 // result to be in X anyway.
1816 RCP<MV> X_null = MVT::CloneViewNonConst (X, nullSpaceIndices);
1817 // Normalization coefficients for projected random vectors.
1818 // Will be thrown away.
1819 mat_ptr B_null = DMT::Create(nullSpaceNumCols, nullSpaceNumCols);
1820 // Write the normalized vectors to X_null (in X).
1821 const int nullSpaceBasisRank = rawNormalize (*Q_null, *X_null, *B_null);
1822
1823 // It's possible, but unlikely, that X_null doesn't have full
1824 // rank (after the projection step). We could recursively fill
1825 // in more random vectors until we finally get a full rank
1826 // matrix, but instead we just throw an exception.
1827 //
1828 // NOTE (mfh 08 Nov 2010) Perhaps we should deal with this case
1829 // more elegantly. Recursion might be one way to solve it, but
1830 // be sure to check that the recursion will terminate. We could
1831 // do this by supplying an additional argument to rawNormalize,
1832 // which is the null space basis rank from the previous
1833 // iteration. The rank has to decrease each time, or the
1834 // recursion may go on forever.
1836 std::vector<magnitude_type> norms (MVT::GetNumberVecs(*X_null));
1837 MVT::MvNorm (*X_null, norms);
1838 std::ostringstream os;
1839 os << "TsqrOrthoManagerImpl::normalizeImpl: "
1840 << "We are being asked to randomize the null space, "
1841 << "for a matrix with " << numCols << " columns and "
1842 << "column rank " << rank << ". After projecting and "
1843 << "normalizing the generated random vectors, they "
1844 << "only have rank " << nullSpaceBasisRank << ". They"
1845 << " should have full rank " << nullSpaceNumCols
1846 << ". (The inclusive range of columns to fill with "
1847 << "random data is [" << nullSpaceIndices.lbound()
1848 << "," << nullSpaceIndices.ubound() << "]. The "
1849 << "column norms of the resulting Q factor are: [";
1850 for (typename std::vector<magnitude_type>::size_type k = 0;
1851 k < norms.size(); ++k) {
1852 os << norms[k];
1853 if (k != norms.size()-1) {
1854 os << ", ";
1855 }
1856 }
1857 os << "].) There is a tiny probability that this could "
1858 << "happen randomly, but it is likely a bug. Please "
1859 << "report it to the Belos developers, especially if "
1860 << "you are able to reproduce the behavior.";
1861
1863 TsqrOrthoError, os.str ());
1864 }
1865 // If we're normalizing out of place, copy the X_null
1866 // vectors back into Q_null; the Q_col vectors are already
1867 // where they are supposed to be in that case.
1868 //
1869 // If we're normalizing in place, leave X_null alone (it's
1870 // already where it needs to be, in X), but copy Q_col back
1871 // into the first rank columns of X.
1872 if (outOfPlace) {
1873 MVT::Assign (*X_null, *Q_null);
1874 } else if (rank > 0) {
1875 // MVT::Assign() doesn't accept empty ranges of columns.
1876 RCP<const MV> Q_col = MVT::CloneView (*Q_view, Range1D (0, rank-1));
1877 RCP<MV> X_col = MVT::CloneViewNonConst (X, Range1D (0, rank-1));
1878 MVT::Assign (*Q_col, *X_col);
1879 }
1880 }
1881 return rank;
1882 }
1883
1884
1885 template<class Scalar, class MV, class DM>
1886 void
1887 TsqrOrthoManagerImpl<Scalar, MV, DM>::
1888 checkProjectionDims (int& ncols_X,
1889 int& num_Q_blocks,
1890 int& ncols_Q_total,
1891 const MV& X,
1892 Teuchos::ArrayView<Teuchos::RCP<const MV> > Q) const
1893 {
1894 // First assign to temporary values, so the function won't
1895 // commit any externally visible side effects unless it will
1896 // return normally (without throwing an exception). (I'm being
1897 // cautious; MVT::GetNumberVecs() probably shouldn't have any
1898 // externally visible side effects, unless it is logging to a
1899 // file or something.)
1901 the_num_Q_blocks = Q.size();
1902 the_ncols_X = MVT::GetNumberVecs (X);
1903
1904 // Compute the total number of columns of all the Q[i] blocks.
1906 // You should be angry if your compiler doesn't support type
1907 // inference ("auto"). That's why I need this awful typedef.
1908 using Teuchos::ArrayView;
1909 using Teuchos::RCP;
1911 for (iter_type it = Q.begin(); it != Q.end(); ++it) {
1912 const MV& Qi = **it;
1913 the_ncols_Q_total += MVT::GetNumberVecs (Qi);
1914 }
1915
1916 // Commit temporary values to the output arguments.
1920 }
1921
1922} // namespace Belos
1923
1924#endif // __BelosTsqrOrthoManagerImpl_hpp
1925
Belos header file which uses auto-configuration information to include necessary C++ headers.
Declaration of basic traits for the multivector type.
Templated virtual class for providing orthogonalization/orthonormalization methods.
Alternative run-time polymorphic interface for operators.
Operator()
Default constructor (does nothing).
Exception thrown to signal error in an orthogonalization manager method.
Interface of callback invoked by TsqrOrthoManager on reorthogonalization.
Teuchos::ScalarTraits< Scalar >::magnitudeType magnitude_type
The type of a norm result.
virtual void operator()(Teuchos::ArrayView< magnitude_type > normsBeforeFirstPass, Teuchos::ArrayView< magnitude_type > normsAfterFirstPass)=0
Callback invoked by TsqrOrthoManager on reorthogonalization.
virtual ~ReorthogonalizationCallback()
Destructor (virtual for memory safety of derived classes)
Scalar scalar_type
The template parameter of this class; the type of an inner product result.
Error in TsqrOrthoManager or TsqrOrthoManagerImpl.
TsqrOrthoError(const std::string &what_arg)
TsqrOrthoFault(const std::string &what_arg)
TSQR-based OrthoManager subclass implementation.
DM mat_type
Type of the projection and normalization coefficients.
const std::string & getLabel() const
Get the label for timers (if timers are enabled).
magnitude_type blockReorthogThreshold() const
Relative tolerance for triggering a block reorthogonalization.
TsqrOrthoManagerImpl(const Teuchos::RCP< Teuchos::ParameterList > &params, const std::string &label)
Constructor (that sets user-specified parameters).
void setLabel(const std::string &label)
Set the label for timers.
magnitude_type orthonormError(const MV &X) const
Return .
int normalize(MV &X, mat_ptr B)
Orthogonalize the columns of X in place.
magnitude_type orthogError(const MV &X1, const MV &X2) const
Return the Frobenius norm of the inner product of X1 with itself.
void setReorthogonalizationCallback(const Teuchos::RCP< ReorthogonalizationCallback< Scalar > > &callback)
Set callback to be invoked on reorthogonalization.
Teuchos::RCP< const Teuchos::ParameterList > getFastParameters()
Get "fast" parameters for TsqrOrthoManagerImpl.
Teuchos::ScalarTraits< Scalar >::magnitudeType magnitude_type
void norm(const MV &X, std::vector< magnitude_type > &normVec) const
Compute the 2-norm of each column j of X.
void project(MV &X, Teuchos::Array< mat_ptr > C, Teuchos::ArrayView< Teuchos::RCP< const MV > > Q)
Compute and .
int normalizeOutOfPlace(MV &X, MV &Q, mat_ptr B)
Normalize X into Q*B, overwriting X.
int projectAndNormalize(MV &X, Teuchos::Array< mat_ptr > C, mat_ptr B, Teuchos::ArrayView< Teuchos::RCP< const MV > > Q)
Project X against Q and normalize X.
void innerProd(const MV &X, const MV &Y, mat_type &Z) const
Euclidean inner product.
magnitude_type relativeRankTolerance() const
Relative tolerance for determining (via the SVD) whether a block is of full numerical rank.
void setParameterList(const Teuchos::RCP< Teuchos::ParameterList > &params)
Set parameters from the given parameter list.
Teuchos::RCP< const Teuchos::ParameterList > getValidParameters() const
Default valid parameter list.
int projectAndNormalizeOutOfPlace(MV &X_in, MV &X_out, Teuchos::Array< mat_ptr > C, mat_ptr B, Teuchos::ArrayView< Teuchos::RCP< const MV > > Q)
Project and normalize X_in into X_out; overwrite X_in.

Generated for Belos by doxygen 1.9.8