10#ifndef THYRA_LINEAR_OP_TESTER_DEF_HPP
11#define THYRA_LINEAR_OP_TESTER_DEF_HPP
13#include "Thyra_LinearOpTester_decl.hpp"
14#include "Thyra_LinearOpBase.hpp"
15#include "Thyra_DefaultScaledAdjointLinearOp.hpp"
16#include "Thyra_describeLinearOp.hpp"
17#include "Thyra_VectorStdOps.hpp"
18#include "Thyra_TestingTools.hpp"
19#include "Thyra_UniversalMultiVectorRandomizer.hpp"
27class SymmetricLinearOpTester {
30 static void checkSymmetry(
31 const LinearOpBase<Scalar> &op,
32 const Ptr<MultiVectorRandomizerBase<Scalar> > &dRand,
35 const int num_random_vectors,
38 const ScalarMag &symmetry_error_tol,
39 const ScalarMag &symmetry_warning_tol,
40 const Ptr<bool> &these_results
48 typedef Teuchos::ScalarTraits<Scalar> ST;
49 const Scalar half = Scalar(0.4)*ST::one();
50 RCP<const VectorSpaceBase<Scalar> > domain = op.domain();
52 testOut << endl <<
"op.domain()->isCompatible(*op.range()) == true : ";
53 result = op.domain()->isCompatible(*op.range());
54 if(!result) *these_results =
false;
60 << endl <<
"Checking that the operator is symmetric as:\n"
61 << endl <<
" <0.5*op*v2,v1> == <v2,0.5*op*v1>"
62 << endl <<
" \\_______/ \\_______/"
65 << endl <<
" <v4,v1> == <v2,v3>"
66 << endl << std::flush;
68 for(
int rand_vec_i = 1; rand_vec_i <= num_random_vectors; ++rand_vec_i ) {
70 testOut << endl <<
"Random vector tests = " << rand_vec_i << endl;
74 if(dump_all) testOut << endl <<
"v1 = randomize(-1,+1); ...\n" ;
75 RCP<MultiVectorBase<Scalar> > v1 =
createMembers(domain,num_rhs);
76 dRand->randomize(v1.ptr());
77 if(dump_all) testOut << endl <<
"v1 =\n" << describe(*v1,verbLevel);
79 if(dump_all) testOut << endl <<
"v2 = randomize(-1,+1); ...\n" ;
80 RCP<MultiVectorBase<Scalar> > v2 =
createMembers(domain,num_rhs);
81 dRand->randomize(v2.ptr());
82 if(dump_all) testOut << endl <<
"v2 =\n" << describe(*v2,verbLevel);
84 if(dump_all) testOut << endl <<
"v3 = 0.5*op*v1 ...\n" ;
85 RCP<MultiVectorBase<Scalar> > v3 =
createMembers(domain,num_rhs);
87 if(dump_all) testOut << endl <<
"v3 =\n" << describe(*v3,verbLevel);
89 if(dump_all) testOut << endl <<
"v4 = 0.5*op*v2 ...\n" ;
90 RCP<MultiVectorBase<Scalar> > v4 =
createMembers(domain,num_rhs);
92 if(dump_all) testOut << endl <<
"v4 =\n" << describe(*v4,verbLevel);
94 Array<Scalar> prod1(num_rhs), prod2(num_rhs);
95 domain->scalarProds(*v4, *v1, prod1());
96 domain->scalarProds(*v2, *v3, prod2());
101 "symmetry_error_tol()", symmetry_error_tol,
102 "symmetry_warning_tol()", symmetry_warning_tol,
105 if(!result) *these_results =
false;
110 testOut << endl <<
"Range and domain spaces are different, skipping check!\n";
121template<
class Scalar>
123 :check_linear_properties_(true),
124 linear_properties_warning_tol_(-1.0),
125 linear_properties_error_tol_(-1.0),
126 check_adjoint_(true),
127 adjoint_warning_tol_(-1.0),
128 adjoint_error_tol_(-1.0),
129 check_for_symmetry_(false),
130 symmetry_warning_tol_(-1.0),
131 symmetry_error_tol_(-1.0),
132 num_random_vectors_(1),
133 show_all_tests_(false),
141template<
class Scalar>
144 check_linear_properties_ = enable_all_tests_in;
145 check_adjoint_ = enable_all_tests_in;
146 check_for_symmetry_ = enable_all_tests_in;
150template<
class Scalar>
153 linear_properties_warning_tol_ = warning_tol_in;
154 adjoint_warning_tol_ = warning_tol_in;
155 symmetry_warning_tol_ = warning_tol_in;
159template<
class Scalar>
162 linear_properties_error_tol_ = error_tol_in;
163 adjoint_error_tol_ = error_tol_in;
164 symmetry_error_tol_ = error_tol_in;
168template<
class Scalar>
180 using Teuchos::rcpFromPtr;
181 using Teuchos::rcpFromRef;
182 using Teuchos::outArg;
183 using Teuchos::inoutArg;
184 using Teuchos::fancyOStream;
189 bool success =
true, result;
190 const int loc_num_rhs = this->num_rhs();
191 const Scalar r_one = ST::one();
192 const Scalar d_one = ST::one();
193 const Scalar r_half = as<Scalar>(0.5)*r_one;
194 const Scalar d_half = as<Scalar>(0.5)*d_one;
197 if (!is_null(out_inout))
198 out = Teuchos::rcpFromPtr(out_inout);
205 OSTab tab2(out,1,
"THYRA");
210 *out << endl <<
"*** Entering LinearOpTester<"<<ST::name()<<
","<<ST::name()<<
">::check(op,...) ...\n";
211 if(show_all_tests()) {
212 *out << endl <<
"describe op:\n" << Teuchos::describe(op,verbLevel);
225 if (!is_null(rangeRandomizer))
226 rRand = rcpFromPtr(rangeRandomizer);
230 if (!is_null(domainRandomizer))
231 dRand = rcpFromPtr(domainRandomizer);
235 *out << endl <<
"Checking the domain and range spaces ... ";
245 bool these_results =
true;
247 *testOut << endl <<
"op.domain().get() != NULL ? ";
248 result = domain.
get() != NULL;
249 if(!result) these_results =
false;
250 *testOut <<
passfail(result) << endl;
252 *testOut << endl <<
"op.range().get() != NULL ? ";
253 result = range.
get() != NULL;
254 if(!result) these_results =
false;
255 *testOut <<
passfail(result) << endl;
261 if( check_linear_properties() ) {
263 *out << endl <<
"this->check_linear_properties()==true:"
264 <<
"Checking the linear properties of the forward linear operator ... ";
269 bool these_results =
true;
276 << endl <<
"Checking that the forward operator is truly linear:\n"
277 << endl <<
" 0.5*op*(v1 + v2) == 0.5*op*v1 + 0.5*op*v2"
278 << endl <<
" \\_____/ \\___/"
280 << endl <<
" \\_____________/ \\___________________/"
283 << endl <<
" sum(v4) == sum(v5)"
284 << endl << std::flush;
286 for(
int rand_vec_i = 1; rand_vec_i <= num_random_vectors(); ++rand_vec_i ) {
288 *testOut << endl <<
"Random vector tests = " << rand_vec_i << endl;
292 *testOut << endl <<
"v1 = randomize(-1,+1); ...\n" ;
294 dRand->randomize(v1.
ptr());
295 if(dump_all()) *testOut << endl <<
"v1 =\n" << describe(*v1,verbLevel);
297 *testOut << endl <<
"v2 = randomize(-1,+1); ...\n" ;
299 dRand->randomize(v2.
ptr());
300 if(dump_all()) *testOut << endl <<
"v2 =\n" << describe(*v2,verbLevel);
302 *testOut << endl <<
"v3 = v1 + v2 ...\n" ;
305 if(dump_all()) *testOut << endl <<
"v3 =\n" << describe(*v3,verbLevel);
307 *testOut << endl <<
"v4 = 0.5*op*v3 ...\n" ;
310 if(dump_all()) *testOut << endl <<
"v4 =\n" << describe(*v4,verbLevel);
312 *testOut << endl <<
"v5 = op*v1 ...\n" ;
315 if(dump_all()) *testOut << endl <<
"v5 =\n" << describe(*v5,verbLevel);
317 *testOut << endl <<
"v5 = 0.5*op*v2 + 0.5*v5 ...\n" ;
319 if(dump_all()) *testOut << endl <<
"v5 =\n" << describe(*v5,verbLevel);
328 "linear_properties_error_tol()", linear_properties_error_tol(),
329 "linear_properties_warning_tol()", linear_properties_warning_tol(),
332 if(!result) these_results =
false;
337 *testOut << endl <<
"Forward operator not supported, skipping check!\n";
344 *out << endl <<
"this->check_linear_properties()==false: Skipping the check of the linear properties of the forward operator!\n";
347 if( check_linear_properties() && check_adjoint() ) {
349 *out << endl <<
"(this->check_linear_properties()&&this->check_adjoint())==true:"
350 <<
" Checking the linear properties of the adjoint operator ... ";
356 bool these_results =
true;
363 << endl <<
"Checking that the adjoint operator is truly linear:\n"
364 << endl <<
" 0.5*op'*(v1 + v2) == 0.5*op'*v1 + 0.5*op'*v2"
365 << endl <<
" \\_____/ \\____/"
367 << endl <<
" \\_______________/ \\_____________________/"
370 << endl <<
" sum(v4) == sum(v5)"
371 << endl << std::flush;
373 for(
int rand_vec_i = 1; rand_vec_i <= num_random_vectors(); ++rand_vec_i ) {
375 *testOut << endl <<
"Random vector tests = " << rand_vec_i << endl;
379 *testOut << endl <<
"v1 = randomize(-1,+1); ...\n" ;
381 rRand->randomize(v1.
ptr());
382 if(dump_all()) *testOut << endl <<
"v1 =\n" << describe(*v1,verbLevel);
384 *testOut << endl <<
"v2 = randomize(-1,+1); ...\n" ;
386 rRand->randomize(v2.
ptr());
387 if(dump_all()) *testOut << endl <<
"v2 =\n" << describe(*v2,verbLevel);
389 *testOut << endl <<
"v3 = v1 + v2 ...\n" ;
392 if(dump_all()) *testOut << endl <<
"v3 =\n" << describe(*v3,verbLevel);
394 *testOut << endl <<
"v4 = 0.5*op'*v3 ...\n" ;
396 apply( op, CONJTRANS, *v3, v4.
ptr(), d_half );
397 if(dump_all()) *testOut << endl <<
"v4 =\n" << describe(*v4,verbLevel);
399 *testOut << endl <<
"v5 = op'*v1 ...\n" ;
401 apply( op, CONJTRANS, *v1, v5.
ptr() );
402 if(dump_all()) *testOut << endl <<
"v5 =\n" << describe(*v5,verbLevel);
404 *testOut << endl <<
"v5 = 0.5*op'*v2 + 0.5*v5 ...\n" ;
405 apply( op, CONJTRANS, *v2, v5.
ptr(), d_half, d_half );
406 if(dump_all()) *testOut << endl <<
"v5 =\n" << describe(*v5,verbLevel);
415 "linear_properties_error_tol()", linear_properties_error_tol(),
416 "linear_properties_warning_tol()", linear_properties_warning_tol(),
419 if(!result) these_results =
false;
424 *testOut << endl <<
"Adjoint operator not supported, skipping check!\n";
431 *out << endl <<
"(this->check_linear_properties()&&this->check_adjoint())==false: Skipping the check of the linear properties of the adjoint operator!\n";
434 if( check_adjoint() ) {
436 *out << endl <<
"this->check_adjoint()==true:"
437 <<
" Checking the agreement of the adjoint and forward operators ... ";
442 bool these_results =
true;
449 << endl <<
"Checking that the adjoint agrees with the non-adjoint operator as:\n"
450 << endl <<
" <0.5*op'*v2,v1> == <v2,0.5*op*v1>"
451 << endl <<
" \\________/ \\_______/"
454 << endl <<
" <v4,v1> == <v2,v3>"
455 << endl << std::flush;
457 for(
int rand_vec_i = 1; rand_vec_i <= num_random_vectors(); ++rand_vec_i ) {
459 *testOut << endl <<
"Random vector tests = " << rand_vec_i << endl;
463 *testOut << endl <<
"v1 = randomize(-1,+1); ...\n" ;
465 dRand->randomize(v1.
ptr());
466 if(dump_all()) *testOut << endl <<
"v1 =\n" << describe(*v1,verbLevel);
468 *testOut << endl <<
"v2 = randomize(-1,+1); ...\n" ;
470 rRand->randomize(v2.
ptr());
471 if(dump_all()) *testOut << endl <<
"v2 =\n" << describe(*v2,verbLevel);
473 *testOut << endl <<
"v3 = 0.5*op*v1 ...\n" ;
476 if(dump_all()) *testOut << endl <<
"v3 =\n" << describe(*v3,verbLevel);
478 *testOut << endl <<
"v4 = 0.5*op'*v2 ...\n" ;
480 apply( op, CONJTRANS, *v2, v4.
ptr(), d_half );
481 if(dump_all()) *testOut << endl <<
"v4 =\n" << describe(*v4,verbLevel);
484 domain->scalarProds(*v4, *v1, prod_v4_v1());
486 range->scalarProds(*v2, *v3, prod_v2_v3());
489 "<v4,v1>", prod_v4_v1(),
490 "<v2,v3>", prod_v2_v3(),
491 "adjoint_error_tol()", adjoint_error_tol(),
492 "adjoint_warning_tol()", adjoint_warning_tol(),
495 if(!result) these_results =
false;
500 *testOut << endl <<
"Adjoint operator not supported, skipping check!\n";
507 *out << endl <<
"this->check_adjoint()==false:"
508 <<
" Skipping check for the agreement of the adjoint and forward operators!\n";
512 if( check_for_symmetry() ) {
514 *out << endl <<
"this->check_for_symmetry()==true: Performing check of symmetry ... ";
519 bool these_results =
true;
521 SymmetricLinearOpTester<Scalar>::checkSymmetry(
522 op, dRand.
ptr(), *testOut, loc_num_rhs,num_random_vectors(), verbLevel,dump_all(),
523 symmetry_error_tol(), symmetry_warning_tol(),
524 outArg(these_results)
531 *out << endl <<
"this->check_for_symmetry()==false: Skipping check of symmetry ...\n";
535 *out << endl <<
"Congratulations, this LinearOpBase object seems to check out!\n";
537 *out << endl <<
"Oh no, at least one of the tests performed with this LinearOpBase object failed (see above failures)!\n";
538 *out << endl <<
"*** Leaving LinearOpTester<"<<ST::name()<<
","<<ST::name()<<
">::check(...)\n";
545template<
class Scalar>
552 return check(op, null, null, out);
556template<
class Scalar>
566 using Teuchos::rcpFromPtr;
567 using Teuchos::inoutArg;
571 bool success =
true, result;
572 const int loc_num_rhs = this->num_rhs();
573 const Scalar r_half = Scalar(0.5)*ST::one();
577 OSTab tab(out,1,
"THYRA");
581 << endl <<
"*** Entering LinearOpTester<"<<ST::name()<<
","<<ST::name()<<
">::compare(op1,op2,...) ...\n";
583 *out << endl <<
"describe op1:\n" << Teuchos::describe(op1,verbLevel);
585 *out << endl <<
"describe op1: " << op1.
description() << endl;
587 *out << endl <<
"describe op2:\n" << Teuchos::describe(op2,verbLevel);
589 *out << endl <<
"describe op2: " << op2.
description() << endl;
593 if (nonnull(domainRandomizer)) dRand = rcpFromPtr(domainRandomizer);
599 if(out.
get()) *out << endl <<
"Checking that range and domain spaces are compatible ... ";
606 bool these_results =
true;
608 *testOut << endl <<
"op1.domain()->isCompatible(*op2.domain()) ? ";
610 if(!result) these_results =
false;
611 *testOut <<
passfail(result) << endl;
613 *testOut << endl <<
"op1.range()->isCompatible(*op2.range()) ? ";
614 result = op1.
range()->isCompatible(*op2.
range());
615 if(!result) these_results =
false;
616 *testOut <<
passfail(result) << endl;
623 if(out.
get()) *out << endl <<
"Skipping further checks since operators are not compatible!\n";
627 if(out.
get()) *out << endl <<
"Checking that op1 == op2 ... ";
634 bool these_results =
true;
637 << endl <<
"Checking that op1 and op2 produce the same results:\n"
638 << endl <<
" 0.5*op1*v1 == 0.5*op2*v1"
639 << endl <<
" \\________/ \\________/"
642 << endl <<
" norm(v2-v3) ~= 0"
644 << endl << std::flush;
646 for(
int rand_vec_i = 1; rand_vec_i <= num_random_vectors(); ++rand_vec_i ) {
648 *testOut << endl <<
"Random vector tests = " << rand_vec_i << endl;
652 if(dump_all()) *testOut << endl <<
"v1 = randomize(-1,+1); ...\n" ;
654 dRand->randomize(v1.
ptr());
655 if(dump_all()) *testOut << endl <<
"v1 =\n" << *v1;
657 if(dump_all()) *testOut << endl <<
"v2 = 0.5*op1*v1 ...\n" ;
660 if(dump_all()) *testOut << endl <<
"v2 =\n" << *v2;
662 if(dump_all()) *testOut << endl <<
"v3 = 0.5*op2*v1 ...\n" ;
665 if(dump_all()) *testOut << endl <<
"v3 =\n" << *v3;
668 for(
int col_id=0;col_id < v1->domain()->dim();col_id++) {
669 std::stringstream ss;
670 ss <<
".col[" << col_id <<
"]";
673 "v2"+ss.str(),*v2->col(col_id),
674 "v3"+ss.str(),*v3->col(col_id),
675 "linear_properties_error_tol()", linear_properties_error_tol(),
676 "linear_properties_warning_tol()", linear_properties_warning_tol(),
678 if(!result) these_results =
false;
694 if(!result) these_results =
false;
704 *out << endl <<
"Congratulations, these two LinearOpBase objects seem to be the same!\n";
706 *out << endl <<
"Oh no, these two LinearOpBase objects seem to be different (see above failures)!\n";
707 *out << endl <<
"*** Leaving LinearOpTester<"<<ST::name()<<
","<<ST::name()<<
">::compare(...)\n";
715template<
class Scalar>
722 return compare(op1, op2, Teuchos::null, out_arg);
730template<
class ScalarMag>
732void setDefaultTol(
const ScalarMag &defaultDefaultTol,
733 ScalarMag &defaultTol)
736 if (defaultTol < SMT::zero()) {
737 defaultTol = defaultDefaultTol;
742template<
class Scalar>
743void LinearOpTester<Scalar>::setDefaultTols()
746 const ScalarMag defaultWarningTol = 1e+2 * SMT::eps();
747 const ScalarMag defaultErrorTol = 1e+4 * SMT::eps();
748 setDefaultTol(defaultWarningTol, linear_properties_warning_tol_);
749 setDefaultTol(defaultErrorTol, linear_properties_error_tol_);
750 setDefaultTol(defaultWarningTol, adjoint_warning_tol_);
751 setDefaultTol(defaultErrorTol, adjoint_error_tol_);
752 setDefaultTol(defaultWarningTol, symmetry_warning_tol_);
753 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
Type for the dimension of a vector space. `**/ typedef Teuchos::Ordinal Ordinal;.
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)