42#ifndef THYRA_LINEAR_OP_TESTER_DEF_HPP
43#define THYRA_LINEAR_OP_TESTER_DEF_HPP
45#include "Thyra_LinearOpTester_decl.hpp"
46#include "Thyra_LinearOpBase.hpp"
47#include "Thyra_DefaultScaledAdjointLinearOp.hpp"
48#include "Thyra_describeLinearOp.hpp"
49#include "Thyra_VectorStdOps.hpp"
50#include "Thyra_TestingTools.hpp"
51#include "Thyra_UniversalMultiVectorRandomizer.hpp"
59class SymmetricLinearOpTester {
62 static void checkSymmetry(
63 const LinearOpBase<Scalar> &op,
64 const Ptr<MultiVectorRandomizerBase<Scalar> > &dRand,
67 const int num_random_vectors,
70 const ScalarMag &symmetry_error_tol,
71 const ScalarMag &symmetry_warning_tol,
72 const Ptr<bool> &these_results
80 typedef Teuchos::ScalarTraits<Scalar> ST;
81 const Scalar half = Scalar(0.4)*ST::one();
82 RCP<const VectorSpaceBase<Scalar> > domain = op.domain();
84 testOut << endl <<
"op.domain()->isCompatible(*op.range()) == true : ";
85 result = op.domain()->isCompatible(*op.range());
86 if(!result) *these_results =
false;
92 << endl <<
"Checking that the operator is symmetric as:\n"
93 << endl <<
" <0.5*op*v2,v1> == <v2,0.5*op*v1>"
94 << endl <<
" \\_______/ \\_______/"
97 << endl <<
" <v4,v1> == <v2,v3>"
98 << endl << std::flush;
100 for(
int rand_vec_i = 1; rand_vec_i <= num_random_vectors; ++rand_vec_i ) {
102 testOut << endl <<
"Random vector tests = " << rand_vec_i << endl;
106 if(dump_all) testOut << endl <<
"v1 = randomize(-1,+1); ...\n" ;
107 RCP<MultiVectorBase<Scalar> > v1 =
createMembers(domain,num_rhs);
108 dRand->randomize(v1.ptr());
109 if(dump_all) testOut << endl <<
"v1 =\n" << describe(*v1,verbLevel);
111 if(dump_all) testOut << endl <<
"v2 = randomize(-1,+1); ...\n" ;
112 RCP<MultiVectorBase<Scalar> > v2 =
createMembers(domain,num_rhs);
113 dRand->randomize(v2.ptr());
114 if(dump_all) testOut << endl <<
"v2 =\n" << describe(*v2,verbLevel);
116 if(dump_all) testOut << endl <<
"v3 = 0.5*op*v1 ...\n" ;
117 RCP<MultiVectorBase<Scalar> > v3 =
createMembers(domain,num_rhs);
119 if(dump_all) testOut << endl <<
"v3 =\n" << describe(*v3,verbLevel);
121 if(dump_all) testOut << endl <<
"v4 = 0.5*op*v2 ...\n" ;
122 RCP<MultiVectorBase<Scalar> > v4 =
createMembers(domain,num_rhs);
124 if(dump_all) testOut << endl <<
"v4 =\n" << describe(*v4,verbLevel);
126 Array<Scalar> prod1(num_rhs), prod2(num_rhs);
127 domain->scalarProds(*v4, *v1, prod1());
128 domain->scalarProds(*v2, *v3, prod2());
133 "symmetry_error_tol()", symmetry_error_tol,
134 "symmetry_warning_tol()", symmetry_warning_tol,
137 if(!result) *these_results =
false;
142 testOut << endl <<
"Range and domain spaces are different, skipping check!\n";
153template<
class Scalar>
155 :check_linear_properties_(true),
156 linear_properties_warning_tol_(-1.0),
157 linear_properties_error_tol_(-1.0),
158 check_adjoint_(true),
159 adjoint_warning_tol_(-1.0),
160 adjoint_error_tol_(-1.0),
161 check_for_symmetry_(false),
162 symmetry_warning_tol_(-1.0),
163 symmetry_error_tol_(-1.0),
164 num_random_vectors_(1),
165 show_all_tests_(false),
173template<
class Scalar>
176 check_linear_properties_ = enable_all_tests_in;
177 check_adjoint_ = enable_all_tests_in;
178 check_for_symmetry_ = enable_all_tests_in;
182template<
class Scalar>
185 linear_properties_warning_tol_ = warning_tol_in;
186 adjoint_warning_tol_ = warning_tol_in;
187 symmetry_warning_tol_ = warning_tol_in;
191template<
class Scalar>
194 linear_properties_error_tol_ = error_tol_in;
195 adjoint_error_tol_ = error_tol_in;
196 symmetry_error_tol_ = error_tol_in;
200template<
class Scalar>
212 using Teuchos::rcpFromPtr;
213 using Teuchos::rcpFromRef;
214 using Teuchos::outArg;
215 using Teuchos::inoutArg;
216 using Teuchos::fancyOStream;
221 bool success =
true, result;
222 const int loc_num_rhs = this->num_rhs();
223 const Scalar r_one = ST::one();
224 const Scalar d_one = ST::one();
225 const Scalar r_half = as<Scalar>(0.5)*r_one;
226 const Scalar d_half = as<Scalar>(0.5)*d_one;
229 if (!is_null(out_inout))
230 out = Teuchos::rcpFromPtr(out_inout);
237 OSTab tab2(out,1,
"THYRA");
242 *out << endl <<
"*** Entering LinearOpTester<"<<ST::name()<<
","<<ST::name()<<
">::check(op,...) ...\n";
243 if(show_all_tests()) {
244 *out << endl <<
"describe op:\n" << Teuchos::describe(op,verbLevel);
257 if (!is_null(rangeRandomizer))
258 rRand = rcpFromPtr(rangeRandomizer);
262 if (!is_null(domainRandomizer))
263 dRand = rcpFromPtr(domainRandomizer);
267 *out << endl <<
"Checking the domain and range spaces ... ";
277 bool these_results =
true;
279 *testOut << endl <<
"op.domain().get() != NULL ? ";
280 result = domain.
get() != NULL;
281 if(!result) these_results =
false;
282 *testOut <<
passfail(result) << endl;
284 *testOut << endl <<
"op.range().get() != NULL ? ";
285 result = range.
get() != NULL;
286 if(!result) these_results =
false;
287 *testOut <<
passfail(result) << endl;
293 if( check_linear_properties() ) {
295 *out << endl <<
"this->check_linear_properties()==true:"
296 <<
"Checking the linear properties of the forward linear operator ... ";
301 bool these_results =
true;
308 << endl <<
"Checking that the forward operator is truly linear:\n"
309 << endl <<
" 0.5*op*(v1 + v2) == 0.5*op*v1 + 0.5*op*v2"
310 << endl <<
" \\_____/ \\___/"
312 << endl <<
" \\_____________/ \\___________________/"
315 << endl <<
" sum(v4) == sum(v5)"
316 << endl << std::flush;
318 for(
int rand_vec_i = 1; rand_vec_i <= num_random_vectors(); ++rand_vec_i ) {
320 *testOut << endl <<
"Random vector tests = " << rand_vec_i << endl;
324 *testOut << endl <<
"v1 = randomize(-1,+1); ...\n" ;
326 dRand->randomize(v1.
ptr());
327 if(dump_all()) *testOut << endl <<
"v1 =\n" << describe(*v1,verbLevel);
329 *testOut << endl <<
"v2 = randomize(-1,+1); ...\n" ;
331 dRand->randomize(v2.
ptr());
332 if(dump_all()) *testOut << endl <<
"v2 =\n" << describe(*v2,verbLevel);
334 *testOut << endl <<
"v3 = v1 + v2 ...\n" ;
337 if(dump_all()) *testOut << endl <<
"v3 =\n" << describe(*v3,verbLevel);
339 *testOut << endl <<
"v4 = 0.5*op*v3 ...\n" ;
342 if(dump_all()) *testOut << endl <<
"v4 =\n" << describe(*v4,verbLevel);
344 *testOut << endl <<
"v5 = op*v1 ...\n" ;
347 if(dump_all()) *testOut << endl <<
"v5 =\n" << describe(*v5,verbLevel);
349 *testOut << endl <<
"v5 = 0.5*op*v2 + 0.5*v5 ...\n" ;
351 if(dump_all()) *testOut << endl <<
"v5 =\n" << describe(*v5,verbLevel);
360 "linear_properties_error_tol()", linear_properties_error_tol(),
361 "linear_properties_warning_tol()", linear_properties_warning_tol(),
364 if(!result) these_results =
false;
369 *testOut << endl <<
"Forward operator not supported, skipping check!\n";
376 *out << endl <<
"this->check_linear_properties()==false: Skipping the check of the linear properties of the forward operator!\n";
379 if( check_linear_properties() && check_adjoint() ) {
381 *out << endl <<
"(this->check_linear_properties()&&this->check_adjoint())==true:"
382 <<
" Checking the linear properties of the adjoint operator ... ";
388 bool these_results =
true;
395 << endl <<
"Checking that the adjoint operator is truly linear:\n"
396 << endl <<
" 0.5*op'*(v1 + v2) == 0.5*op'*v1 + 0.5*op'*v2"
397 << endl <<
" \\_____/ \\____/"
399 << endl <<
" \\_______________/ \\_____________________/"
402 << endl <<
" sum(v4) == sum(v5)"
403 << endl << std::flush;
405 for(
int rand_vec_i = 1; rand_vec_i <= num_random_vectors(); ++rand_vec_i ) {
407 *testOut << endl <<
"Random vector tests = " << rand_vec_i << endl;
411 *testOut << endl <<
"v1 = randomize(-1,+1); ...\n" ;
413 rRand->randomize(v1.
ptr());
414 if(dump_all()) *testOut << endl <<
"v1 =\n" << describe(*v1,verbLevel);
416 *testOut << endl <<
"v2 = randomize(-1,+1); ...\n" ;
418 rRand->randomize(v2.
ptr());
419 if(dump_all()) *testOut << endl <<
"v2 =\n" << describe(*v2,verbLevel);
421 *testOut << endl <<
"v3 = v1 + v2 ...\n" ;
424 if(dump_all()) *testOut << endl <<
"v3 =\n" << describe(*v3,verbLevel);
426 *testOut << endl <<
"v4 = 0.5*op'*v3 ...\n" ;
429 if(dump_all()) *testOut << endl <<
"v4 =\n" << describe(*v4,verbLevel);
431 *testOut << endl <<
"v5 = op'*v1 ...\n" ;
434 if(dump_all()) *testOut << endl <<
"v5 =\n" << describe(*v5,verbLevel);
436 *testOut << endl <<
"v5 = 0.5*op'*v2 + 0.5*v5 ...\n" ;
438 if(dump_all()) *testOut << endl <<
"v5 =\n" << describe(*v5,verbLevel);
447 "linear_properties_error_tol()", linear_properties_error_tol(),
448 "linear_properties_warning_tol()", linear_properties_warning_tol(),
451 if(!result) these_results =
false;
456 *testOut << endl <<
"Adjoint operator not supported, skipping check!\n";
463 *out << endl <<
"(this->check_linear_properties()&&this->check_adjoint())==false: Skipping the check of the linear properties of the adjoint operator!\n";
466 if( check_adjoint() ) {
468 *out << endl <<
"this->check_adjoint()==true:"
469 <<
" Checking the agreement of the adjoint and forward operators ... ";
474 bool these_results =
true;
481 << endl <<
"Checking that the adjoint agrees with the non-adjoint operator as:\n"
482 << endl <<
" <0.5*op'*v2,v1> == <v2,0.5*op*v1>"
483 << endl <<
" \\________/ \\_______/"
486 << endl <<
" <v4,v1> == <v2,v3>"
487 << endl << std::flush;
489 for(
int rand_vec_i = 1; rand_vec_i <= num_random_vectors(); ++rand_vec_i ) {
491 *testOut << endl <<
"Random vector tests = " << rand_vec_i << endl;
495 *testOut << endl <<
"v1 = randomize(-1,+1); ...\n" ;
497 dRand->randomize(v1.
ptr());
498 if(dump_all()) *testOut << endl <<
"v1 =\n" << describe(*v1,verbLevel);
500 *testOut << endl <<
"v2 = randomize(-1,+1); ...\n" ;
502 rRand->randomize(v2.
ptr());
503 if(dump_all()) *testOut << endl <<
"v2 =\n" << describe(*v2,verbLevel);
505 *testOut << endl <<
"v3 = 0.5*op*v1 ...\n" ;
508 if(dump_all()) *testOut << endl <<
"v3 =\n" << describe(*v3,verbLevel);
510 *testOut << endl <<
"v4 = 0.5*op'*v2 ...\n" ;
513 if(dump_all()) *testOut << endl <<
"v4 =\n" << describe(*v4,verbLevel);
516 domain->scalarProds(*v4, *v1, prod_v4_v1());
518 range->scalarProds(*v2, *v3, prod_v2_v3());
521 "<v4,v1>", prod_v4_v1(),
522 "<v2,v3>", prod_v2_v3(),
523 "adjoint_error_tol()", adjoint_error_tol(),
524 "adjoint_warning_tol()", adjoint_warning_tol(),
527 if(!result) these_results =
false;
532 *testOut << endl <<
"Adjoint operator not supported, skipping check!\n";
539 *out << endl <<
"this->check_adjoint()==false:"
540 <<
" Skipping check for the agreement of the adjoint and forward operators!\n";
544 if( check_for_symmetry() ) {
546 *out << endl <<
"this->check_for_symmetry()==true: Performing check of symmetry ... ";
551 bool these_results =
true;
553 SymmetricLinearOpTester<Scalar>::checkSymmetry(
554 op, dRand.
ptr(), *testOut, loc_num_rhs,num_random_vectors(), verbLevel,dump_all(),
555 symmetry_error_tol(), symmetry_warning_tol(),
556 outArg(these_results)
563 *out << endl <<
"this->check_for_symmetry()==false: Skipping check of symmetry ...\n";
567 *out << endl <<
"Congratulations, this LinearOpBase object seems to check out!\n";
569 *out << endl <<
"Oh no, at least one of the tests performed with this LinearOpBase object failed (see above failures)!\n";
570 *out << endl <<
"*** Leaving LinearOpTester<"<<ST::name()<<
","<<ST::name()<<
">::check(...)\n";
577template<
class Scalar>
584 return check(op, null, null, out);
588template<
class Scalar>
598 using Teuchos::rcpFromPtr;
599 using Teuchos::inoutArg;
603 bool success =
true, result;
604 const int loc_num_rhs = this->num_rhs();
605 const Scalar r_half = Scalar(0.5)*ST::one();
609 OSTab tab(out,1,
"THYRA");
613 << endl <<
"*** Entering LinearOpTester<"<<ST::name()<<
","<<ST::name()<<
">::compare(op1,op2,...) ...\n";
615 *out << endl <<
"describe op1:\n" << Teuchos::describe(op1,verbLevel);
617 *out << endl <<
"describe op1: " << op1.
description() << endl;
619 *out << endl <<
"describe op2:\n" << Teuchos::describe(op2,verbLevel);
621 *out << endl <<
"describe op2: " << op2.
description() << endl;
625 if (nonnull(domainRandomizer)) dRand = rcpFromPtr(domainRandomizer);
631 if(out.
get()) *out << endl <<
"Checking that range and domain spaces are compatible ... ";
638 bool these_results =
true;
640 *testOut << endl <<
"op1.domain()->isCompatible(*op2.domain()) ? ";
642 if(!result) these_results =
false;
643 *testOut <<
passfail(result) << endl;
645 *testOut << endl <<
"op1.range()->isCompatible(*op2.range()) ? ";
646 result = op1.
range()->isCompatible(*op2.
range());
647 if(!result) these_results =
false;
648 *testOut <<
passfail(result) << endl;
655 if(out.
get()) *out << endl <<
"Skipping further checks since operators are not compatible!\n";
659 if(out.
get()) *out << endl <<
"Checking that op1 == op2 ... ";
666 bool these_results =
true;
669 << endl <<
"Checking that op1 and op2 produce the same results:\n"
670 << endl <<
" 0.5*op1*v1 == 0.5*op2*v1"
671 << endl <<
" \\________/ \\________/"
674 << endl <<
" norm(v2-v3) ~= 0"
676 << endl << std::flush;
678 for(
int rand_vec_i = 1; rand_vec_i <= num_random_vectors(); ++rand_vec_i ) {
680 *testOut << endl <<
"Random vector tests = " << rand_vec_i << endl;
684 if(dump_all()) *testOut << endl <<
"v1 = randomize(-1,+1); ...\n" ;
686 dRand->randomize(v1.
ptr());
687 if(dump_all()) *testOut << endl <<
"v1 =\n" << *v1;
689 if(dump_all()) *testOut << endl <<
"v2 = 0.5*op1*v1 ...\n" ;
692 if(dump_all()) *testOut << endl <<
"v2 =\n" << *v2;
694 if(dump_all()) *testOut << endl <<
"v3 = 0.5*op2*v1 ...\n" ;
697 if(dump_all()) *testOut << endl <<
"v3 =\n" << *v3;
700 for(
int col_id=0;col_id < v1->domain()->dim();col_id++) {
701 std::stringstream ss;
702 ss <<
".col[" << col_id <<
"]";
705 "v2"+ss.str(),*v2->col(col_id),
706 "v3"+ss.str(),*v3->col(col_id),
707 "linear_properties_error_tol()", linear_properties_error_tol(),
708 "linear_properties_warning_tol()", linear_properties_warning_tol(),
710 if(!result) these_results =
false;
726 if(!result) these_results =
false;
736 *out << endl <<
"Congratulations, these two LinearOpBase objects seem to be the same!\n";
738 *out << endl <<
"Oh no, these two LinearOpBase objects seem to be different (see above failures)!\n";
739 *out << endl <<
"*** Leaving LinearOpTester<"<<ST::name()<<
","<<ST::name()<<
">::compare(...)\n";
747template<
class Scalar>
754 return compare(op1, op2, Teuchos::null, out_arg);
762template<
class ScalarMag>
764void setDefaultTol(
const ScalarMag &defaultDefaultTol,
765 ScalarMag &defaultTol)
768 if (defaultTol < SMT::zero()) {
769 defaultTol = defaultDefaultTol;
774template<
class Scalar>
775void LinearOpTester<Scalar>::setDefaultTols()
778 const ScalarMag defaultWarningTol = 1e+2 * SMT::eps();
779 const ScalarMag defaultErrorTol = 1e+4 * SMT::eps();
780 setDefaultTol(defaultWarningTol, linear_properties_warning_tol_);
781 setDefaultTol(defaultErrorTol, linear_properties_error_tol_);
782 setDefaultTol(defaultWarningTol, adjoint_warning_tol_);
783 setDefaultTol(defaultErrorTol, adjoint_error_tol_);
784 setDefaultTol(defaultWarningTol, symmetry_warning_tol_);
785 setDefaultTol(defaultErrorTol, symmetry_error_tol_);
virtual std::string description() const
Base class for all linear operators.
void apply(const LinearOpBase< Scalar > &M, const EOpTransp M_trans, const MultiVectorBase< Scalar > &X, const Ptr< MultiVectorBase< Scalar > > &Y, const Scalar alpha=static_cast< Scalar >(1.0), const Scalar beta=static_cast< Scalar >(0.0))
Non-member function call for M.apply(...).
virtual RCP< const VectorSpaceBase< Scalar > > range() const =0
Return a smart pointer for the range space for this operator.
virtual RCP< const VectorSpaceBase< Scalar > > domain() const =0
Return a smart pointer for the domain space for this operator.
bool opSupported(EOpTransp M_trans) const
Return if the M_trans operation of apply() is supported or not.
bool compare(const LinearOpBase< Scalar > &op1, const LinearOpBase< Scalar > &op2, const Ptr< MultiVectorRandomizerBase< Scalar > > &domainRandomizer, const Ptr< FancyOStream > &out_arg) const
Check if two linear operators are the same or not.
LinearOpTester()
Default constructor which sets default parameter values.
void set_all_warning_tol(const ScalarMag warning_tol)
Set all the warning tolerances to the same value.
void enable_all_tests(const bool enable_all_tests)
Enable or disable all tests.
void set_all_error_tol(const ScalarMag error_tol)
Set all the error tolerances to the same value.
Teuchos::ScalarTraits< Scalar >::magnitudeType ScalarMag
Local typedef for promoted scalar magnitude.
bool check(const LinearOpBase< Scalar > &op, const Ptr< MultiVectorRandomizerBase< Scalar > > &rangeRandomizer, const Ptr< MultiVectorRandomizerBase< Scalar > > &domainRandomizer, const Ptr< FancyOStream > &out) const
Check a linear operator.
void sums(const MultiVectorBase< Scalar > &V, const ArrayView< Scalar > &sums)
Multi-vector column sum.
void V_VpV(const Ptr< MultiVectorBase< Scalar > > &Z, const MultiVectorBase< Scalar > &X, const MultiVectorBase< Scalar > &Y)
Z(i,j) = X(i,j) + Y(i,j), i = 0...Z->range()->dim()-1, j = 0...Z->domain()->dim()-1.
Base interface for a strategy object for randomizing a multi-vector.
Control printing of test results.
RCP< FancyOStream > getTestOStream()
Return the stream used for testing.
void printTestResults(const bool this_result, const Ptr< bool > &success)
Print the test result.
RCP< UniversalMultiVectorRandomizer< Scalar > > universalMultiVectorRandomizer()
Nonmember constructor.
RCP< MultiVectorBase< Scalar > > createMembers(const RCP< const VectorSpaceBase< Scalar > > &vs, int numMembers, const std::string &label="")
Create a set of vector members (a MultiVectorBase) from the vector space.
bool testRelErrors(const std::string &v1_name, const ArrayView< const Scalar1 > &v1, const std::string &v2_name, const ArrayView< const Scalar2 > &v2, const std::string &maxRelErr_error_name, const ScalarMag &maxRelErr_error, const std::string &maxRelErr_warning_name, const ScalarMag &maxRelErr_warning, const Ptr< std::ostream > &out, const std::string &leadingIndent=std::string(""))
Compute, check and optionally print the relative errors in two scalar arays.
const std::string passfail(const bool result)
bool testRelNormDiffErr(const std::string &v1_name, const VectorBase< Scalar > &v1, const std::string &v2_name, const VectorBase< Scalar > &v2, const std::string &maxRelErr_error_name, const typename Teuchos::ScalarTraits< Scalar >::magnitudeType &maxRelErr_error, const std::string &maxRelErr_warning_name, const typename Teuchos::ScalarTraits< Scalar >::magnitudeType &maxRelErr_warning, std::ostream *out, const Teuchos::EVerbosityLevel verbLevel=Teuchos::VERB_LOW, const std::string &leadingIndent=std::string(""))
Compute, check and optionally print the relative errors in two vectors.
@ NOTRANS
Use the non-transposed operator.
@ CONJTRANS
Use the transposed operator with complex-conjugate clements (same as TRANS for real scalar types).
TypeTo as(const TypeFrom &t)
basic_FancyOStream< char > FancyOStream
basic_OSTab< char > OSTab
basic_oblackholestream< char, std::char_traits< char > > oblackholestream
#define TEUCHOS_TEST_EQUALITY_CONST(v1, v2, out, success)
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)