Thyra Version of the Day
Loading...
Searching...
No Matches
Thyra_LinearOpWithSolveFactoryExamples.hpp
1// @HEADER
2// *****************************************************************************
3// Thyra: Interfaces and Support for Abstract Numerical Algorithms
4//
5// Copyright 2004 NTESS and the Thyra contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
10#ifndef THYRA_LINEAR_OP_WITH_SOLVE_EXAMPLES_HPP
11#define THYRA_LINEAR_OP_WITH_SOLVE_EXAMPLES_HPP
12
13
14#include "Thyra_LinearOpWithSolveFactoryBase.hpp"
15#include "Thyra_LinearOpWithSolveFactoryHelpers.hpp"
16#include "Thyra_LinearOpWithSolveBase.hpp"
17#include "Thyra_PreconditionerFactoryHelpers.hpp"
18#include "Thyra_DefaultScaledAdjointLinearOp.hpp"
19#include "Thyra_DefaultPreconditioner.hpp"
20#include "Thyra_MultiVectorStdOps.hpp"
21#include "Thyra_VectorStdOps.hpp"
22#include "Thyra_VectorBase.hpp"
23
24
25namespace Thyra {
26
27
28//
29// Helper code
30//
31
42template<class Scalar>
44public:
46 virtual ~LinearOpChanger() {}
48 virtual void changeOp( const Teuchos::Ptr<LinearOpBase<Scalar> > &op ) const = 0;
49};
50
54template<class Scalar>
55class NullLinearOpChanger : public LinearOpChanger<Scalar> {
56public:
58 void changeOp( const Teuchos::Ptr<LinearOpBase<Scalar> > &op ) const {}
59};
60
61
62} // namespace Thyra
63
64
65//
66// Individual non-externally preconditioned use cases
67//
68
69
70// begin singleLinearSolve
74template<class Scalar>
75void singleLinearSolve(
81 )
82{
84 using Teuchos::rcpFromRef;
85 Teuchos::OSTab tab(out);
86 out << "\nPerforming a single linear solve ...\n";
87 // Create the LOWSB object that will be used to solve the linear system
89 Thyra::linearOpWithSolve(lowsFactory, rcpFromRef(A));
90 // Solve the system using a default solve criteria using a non-member helper function
91 assign(x, ST::zero()); // Must initialize to a guess before solve!
93 status = Thyra::solve<Scalar>(*invertibleA, Thyra::NOTRANS, b, x);
94 out << "\nSolve status:\n" << status;
95} // end singleLinearSolve
96
97
98// begin createScaledAdjointLinearOpWithSolve
103template<class Scalar>
105createScaledAdjointLinearOpWithSolve(
107 const Scalar &scalar,
110 )
111{
112 Teuchos::OSTab tab(out);
113 out << "\nCreating a scaled adjoint LinearOpWithSolveBase object ...\n";
114 // Create the LOWSB object that will be used to solve the linear system
116 Thyra::linearOpWithSolve(lowsFactory,scale(scalar,adjoint(A)));
117 out << "\nCreated LOWSB object:\n" << describe(*invertibleAdjointA,
119 return invertibleAdjointA;
120} // end createScaledAdjointLinearOpWithSolve
121
122
123// begin solveNumericalChangeSolve
128template<class Scalar>
129void solveNumericalChangeSolve(
131 const Thyra::LinearOpChanger<Scalar> &opChanger,
138 )
139{
140 using Teuchos::as; using Teuchos::ptr; using Teuchos::rcpFromPtr;
141 Teuchos::OSTab tab(out);
142 out << "\nPerforming a solve, changing the operator, then performing another"
143 << " solve ...\n";
144 // Get a local non-owned RCP to A to be used by lowsFactory
146 // Create the LOWSB object that will be used to solve the linear system
148 lowsFactory.createOp();
149 // Initialize the invertible linear operator given the forward operator
150 Thyra::initializeOp<Scalar>(lowsFactory, rcpA, invertibleA.ptr());
151 // Solve the system using a default solve criteria using a non-member helper function
152 Thyra::assign(x1, as<Scalar>(0.0));
153 Thyra::solve<Scalar>(*invertibleA, Thyra::NOTRANS, b1, x1);
154 // Before the forward operator A is changed it is recommended that you
155 // uninitialize *invertibleA first to avoid accidental use of *invertiableA
156 // while it may be in an inconsistent state from the time between *A changes
157 // and *invertibleA is explicitly updated. However, this step is not
158 // required!
159 Thyra::uninitializeOp<Scalar>(lowsFactory, invertibleA.ptr());
160 // Change the operator and reinitialize the invertible operator
161 opChanger.changeOp(A);
162 Thyra::initializeOp<Scalar>(lowsFactory, rcpA, invertibleA.ptr());
163 // Note that above will reuse any factorization structures that may have been
164 // created in the first call to initializeOp(...).
165 // Finally, solve another linear system with new values of A
166 Thyra::assign<Scalar>(x2, as<Scalar>(0.0));
167 Thyra::solve<Scalar>(*invertibleA, Thyra::NOTRANS, b2, x2);
168} // end solveNumericalChangeSolve
169
170
171// begin solveSmallNumericalChangeSolve
177template<class Scalar>
178void solveSmallNumericalChangeSolve(
180 const Thyra::LinearOpChanger<Scalar> &opSmallChanger,
187 )
188{
189 using Teuchos::ptr; using Teuchos::as; using Teuchos::rcpFromPtr;
190 Teuchos::OSTab tab(out);
191 out << "\nPerforming a solve, changing the operator in a very small way,"
192 << " then performing another solve ...\n";
193 // Get a local non-owned RCP to A to be used by lowsFactory
195 // Create the LOWSB object that will be used to solve the linear system
197 lowsFactory.createOp();
198 // Initialize the invertible linear operator given the forward operator
199 Thyra::initializeOp<Scalar>(lowsFactory, rcpA, invertibleA.ptr());
200 // Solve the system using a default solve criteria using a non-member helper function
201 Thyra::assign(x1, as<Scalar>(0.0));
202 Thyra::solve<Scalar>(*invertibleA, Thyra::NOTRANS, b1, x1);
203 // Before the forward operator A is changed it is recommended that you
204 // uninitialize *invertibleA first to avoid accidental use of *invertiableA
205 // while it may be in an inconsistent state from the time between *A changes
206 // and *invertibleA is explicitly updated. However, this step is not
207 // required!
208 Thyra::uninitializeOp<Scalar>(lowsFactory, invertibleA.ptr());
209 // Change the operator and reinitialize the invertible operator
210 opSmallChanger.changeOp(A);
211 Thyra::initializeAndReuseOp<Scalar>(lowsFactory, rcpA, invertibleA.ptr());
212 // Note that above a maximum amount of reuse will be achieved, such as
213 // keeping the same preconditioner.
214 Thyra::assign(x2, as<Scalar>(0.0));
215 Thyra::solve<Scalar>(*invertibleA, Thyra::NOTRANS, b2, x2);
216} // end solveSmallNumericalChangeSolve
217
218
219// begin solveMajorChangeSolve
225template<class Scalar>
226void solveMajorChangeSolve(
228 const Thyra::LinearOpChanger<Scalar> &opMajorChanger,
235 )
236{
237 using Teuchos::as; using Teuchos::rcpFromPtr;
238 Teuchos::OSTab tab(out);
239 out << "\nPerforming a solve, changing the operator in a major way, then performing"
240 << " another solve ...\n";
241 // Get a local non-owned RCP to A to be used by lowsFactory
243 // Create the LOWSB object that will be used to solve the linear system
245 lowsFactory.createOp();
246 // Initialize the invertible linear operator given the forward operator
247 Thyra::initializeOp<Scalar>(lowsFactory, rcpA, invertibleA.ptr());
248 // Solve the system using a default solve criteria using a non-member helper function
249 Thyra::assign(x1, as<Scalar>(0.0));
250 Thyra::solve<Scalar>(*invertibleA, Thyra::NOTRANS, b1, x1);
251 // Before the forward operator A is changed it is recommended that you
252 // uninitialize *invertibleA first to avoid accidental use of *invertiableA
253 // while it may be in an inconsistent state from the time between *A changes
254 // and *invertibleA is explicitly updated. However, this step is not
255 // required!
256 Thyra::uninitializeOp<Scalar>(lowsFactory, invertibleA.ptr());
257 // Change the operator in some major way (perhaps even changing its structure)
258 opMajorChanger.changeOp(A);
259 // Recreate the LOWSB object and initialize it from scratch
260 invertibleA = lowsFactory.createOp();
261 Thyra::initializeOp<Scalar>(lowsFactory, rcpA, invertibleA.ptr());
262 // Solve another set of linear systems
263 Thyra::assign(x2, as<Scalar>(0.0));
264 Thyra::solve<Scalar>(*invertibleA, Thyra::NOTRANS, b2, x2);
265} // end solveMajorChangeSolve
266
267
268//
269// Individual externally preconditioned use cases
270//
271
272
273// begin createGeneralPreconditionedLinearOpWithSolve
279template<class Scalar>
281createGeneralPreconditionedLinearOpWithSolve(
286 )
287{
288 Teuchos::OSTab tab(out);
289 out << "\nCreating an externally preconditioned LinearOpWithSolveBase object ...\n";
291 lowsFactory.createOp();
292 Thyra::initializePreconditionedOp<Scalar>(lowsFactory, A, P, invertibleA.ptr());
293 out << "\nCreated LOWSB object:\n" << describe(*invertibleA, Teuchos::VERB_MEDIUM);
294 return invertibleA;
295} // end createGeneralPreconditionedLinearOpWithSolve
296
297
298// begin createUnspecifiedPreconditionedLinearOpWithSolve
304template<class Scalar>
306createUnspecifiedPreconditionedLinearOpWithSolve(
308 const Teuchos::RCP<const Thyra::LinearOpBase<Scalar> > &P_op,
311 )
312{
313 Teuchos::OSTab tab(out);
314 out << "\nCreating an LinearOpWithSolveBase object given a preconditioner operator"
315 << " not targeted to the left or right ...\n";
317 lowsFactory.createOp();
318 Thyra::initializePreconditionedOp<Scalar>(lowsFactory, A,
319 Thyra::unspecifiedPrec<Scalar>(P_op), invertibleA.ptr());
320 // Above, the lowsFactory object will decide whether to apply the single
321 // preconditioner operator on the left or on the right.
322 out << "\nCreated LOWSB object:\n" << describe(*invertibleA, Teuchos::VERB_MEDIUM);
323 return invertibleA;
324} // end createUnspecifiedPreconditionedLinearOpWithSolve
325
326
327// begin createLeftPreconditionedLinearOpWithSolve
333template<class Scalar>
335createLeftPreconditionedLinearOpWithSolve(
337 const Teuchos::RCP<const Thyra::LinearOpBase<Scalar> > &P_op_left,
340 )
341{
342 Teuchos::OSTab tab(out);
343 out << "\nCreating an LinearOpWithSolveBase object given a left preconditioner"
344 << " operator ...\n";
346 lowsFactory.createOp();
347 Thyra::initializePreconditionedOp<Scalar>(lowsFactory, A,
348 Thyra::leftPrec<Scalar>(P_op_left), invertibleA.ptr());
349 out << "\nCreated LOWSB object:\n" << describe(*invertibleA, Teuchos::VERB_MEDIUM);
350 return invertibleA;
351} // end createLeftPreconditionedLinearOpWithSolve
352
353
354// begin createRightPreconditionedLinearOpWithSolve
360template<class Scalar>
362createRightPreconditionedLinearOpWithSolve(
364 const Teuchos::RCP<const Thyra::LinearOpBase<Scalar> > &P_op_right,
367 )
368{
369 Teuchos::OSTab tab(out);
370 out << "\nCreating an LinearOpWithSolveBase object given a right"
371 << " preconditioner operator ...\n";
373 lowsFactory.createOp();
374 Thyra::initializePreconditionedOp<Scalar>(lowsFactory, A,
375 Thyra::rightPrec<Scalar>(P_op_right), invertibleA.ptr());
376 out << "\nCreated LOWSB object:\n" << describe(*invertibleA, Teuchos::VERB_MEDIUM);
377 return invertibleA;
378} // end createRightPreconditionedLinearOpWithSolve
379
380
381// begin createLeftRightPreconditionedLinearOpWithSolve
387template<class Scalar>
389createLeftRightPreconditionedLinearOpWithSolve(
391 const Teuchos::RCP<const Thyra::LinearOpBase<Scalar> > &P_op_left,
392 const Teuchos::RCP<const Thyra::LinearOpBase<Scalar> > &P_op_right,
395 )
396{
397 Teuchos::OSTab tab(out);
398 out << "\nCreating an LinearOpWithSolveBase object given a left and"
399 << "right preconditioner operator ...\n";
401 lowsFactory.createOp();
402 Thyra::initializePreconditionedOp<Scalar>(lowsFactory, A,
403 Thyra::splitPrec<Scalar>(P_op_left, P_op_right), invertibleA.ptr());
404 out << "\nCreated LOWSB object:\n" << describe(*invertibleA, Teuchos::VERB_MEDIUM);
405 return invertibleA;
406} // end createLeftRightPreconditionedLinearOpWithSolve
407
408
409// begin createMatrixPreconditionedLinearOpWithSolve
415template<class Scalar>
417createMatrixPreconditionedLinearOpWithSolve(
419 const Teuchos::RCP<const Thyra::LinearOpBase<Scalar> > &A_approx,
422 )
423{
424 Teuchos::OSTab tab(out);
425 out << "\nCreating a LinearOpWithSolveBase object given an approximate forward"
426 << " operator to define the preconditioner ...\n";
428 lowsFactory.createOp();
429 Thyra::initializeApproxPreconditionedOp<Scalar>(lowsFactory, A, A_approx,
430 invertibleA.ptr());
431 out << "\nCreated LOWSB object:\n" << describe(*invertibleA, Teuchos::VERB_MEDIUM);
432 return invertibleA;
433} // end createMatrixPreconditionedLinearOpWithSolve
434
435
436// begin externalPreconditionerReuseWithSolves
440template<class Scalar>
441void externalPreconditionerReuseWithSolves(
443 const Thyra::LinearOpChanger<Scalar> &opChanger,
451 )
452{
453 using Teuchos::tab; using Teuchos::rcpFromPtr;
455 Teuchos::OSTab tab2(out);
456 out << "\nShowing resuse of the preconditioner ...\n";
457 Teuchos::RCP<Thyra::LinearOpBase<Scalar> > A = rcpFromPtr(A_inout);
458 // Create the initial preconditioner for the input forward operator
460 precFactory.createPrec();
461 Thyra::initializePrec<Scalar>(precFactory, A, P.ptr());
462 // Create the invertible LOWS object given the preconditioner
464 lowsFactory.createOp();
465 Thyra::initializePreconditionedOp<Scalar>(lowsFactory, A, P, invertibleA.ptr());
466 // Solve the first linear system
467 assign(x1, ST::zero());
468 Thyra::SolveStatus<Scalar> status1 = Thyra::solve<Scalar>(*invertibleA,
469 Thyra::NOTRANS, b1, x1);
470 out << "\nSolve status:\n" << status1;
471 // Change the forward linear operator without changing the preconditioner
472 opChanger.changeOp(A.ptr());
473 // Warning! After the above change the integrity of the preconditioner
474 // linear operators in P is undefined. For some implementations of the
475 // preconditioner, its behavior will remain unchanged (e.g. ILU) which in
476 // other cases the behavior will change but the preconditioner will still
477 // work (e.g. Jacobi). However, there may be valid implementations where
478 // the preconditioner will simply break if the forward operator that it is
479 // based on breaks.
480 //
481 // Reinitialize the LOWS object given the updated forward operator A and the
482 // old preconditioner P.
483 Thyra::initializePreconditionedOp<Scalar>(lowsFactory, A, P, invertibleA.ptr());
484 // Solve the second linear system
485 assign(x2, ST::zero());
486 Thyra::SolveStatus<Scalar>status2 = Thyra::solve<Scalar>(*invertibleA,
487 Thyra::NOTRANS, b2, x2);
488 out << "\nSolve status:\n" << status2;
489} // end externalPreconditionerReuseWithSolves
490
491
492//
493// Combined use cases
494//
495
496
502template<class Scalar>
503void nonExternallyPreconditionedLinearSolveUseCases(
506 bool supportsAdjoints,
508 )
509{
510 using Teuchos::as;
511 Teuchos::OSTab tab(out);
512 out << "\nRunning example use cases for a LinearOpWithSolveFactoryBase object ...\n";
513 // Create a non-const A object (don't worry, it will not be changed)
515 Teuchos::ptrFromRef(const_cast<Thyra::LinearOpBase<Scalar>&>(A));
516 // Create the RHS (which is just a random set of coefficients)
518 b1 = Thyra::createMember(A.range()),
519 b2 = Thyra::createMember(A.range());
520 Thyra::randomize( as<Scalar>(-1.0), as<Scalar>(+1.0), b1.ptr() );
521 Thyra::randomize( as<Scalar>(-1.0), as<Scalar>(+1.0), b2.ptr() );
522 // Create the LHS for the linear solve
524 x1 = Thyra::createMember(A.domain()),
525 x2 = Thyra::createMember(A.domain());
526 // Perform a single, non-adjoint, linear solve
527 singleLinearSolve(A, lowsFactory, *b1, x1.ptr(), out);
528 // Creating a scaled adjoint LinearOpWithSolveBase object
529 if(supportsAdjoints) {
531 invertibleAdjointA = createScaledAdjointLinearOpWithSolve(
532 Teuchos::rcp(&A,false),as<Scalar>(2.0),lowsFactory,out);
533 }
534 // Perform a solve, change the operator, and then solve again.
535 solveNumericalChangeSolve<Scalar>(
536 A_nonconst, // Don't worry, it will not be changed!
537 Thyra::NullLinearOpChanger<Scalar>(), // This object will not really change A!
538 lowsFactory, *b1, x1.ptr(), *b2, x1.ptr(), out );
539 // Perform a solve, change the operator in a very small way, and then solve again.
540 solveSmallNumericalChangeSolve<Scalar>(
541 A_nonconst, // Don't worry, it will not be changed!
542 Thyra::NullLinearOpChanger<Scalar>(), // This object will not really change A!
543 lowsFactory, *b1, x1.ptr(), *b2, x1.ptr(), out );
544 // Perform a solve, change the operator in a major way, and then solve again.
545 solveMajorChangeSolve<Scalar>(
546 A_nonconst, // Don't worry, it will not be changed!
547 Thyra::NullLinearOpChanger<Scalar>(), // This object will not really change A!
548 lowsFactory, *b1, x1.ptr(), *b2, x1.ptr(), out );
549}
550
551
557template<class Scalar>
558void externallyPreconditionedLinearSolveUseCases(
562 const bool supportsLeftPrec,
563 const bool supportsRightPrec,
565 )
566{
567 using Teuchos::rcpFromRef; using Teuchos::as;
568 Teuchos::OSTab tab(out);
569 out << "\nRunning example use cases with an externally defined"
570 << " preconditioner with a LinearOpWithSolveFactoryBase object ...\n";
571 // Create a non-const A object (don't worry, it will not be changed)
573 Teuchos::ptrFromRef(const_cast<Thyra::LinearOpBase<Scalar>&>(A));
574 // Create the RHS (which is just a random set of coefficients)
576 b1 = Thyra::createMember(A.range()),
577 b2 = Thyra::createMember(A.range());
578 Thyra::randomize( as<Scalar>(-1.0), as<Scalar>(+1.0), b1.ptr() );
579 Thyra::randomize( as<Scalar>(-1.0), as<Scalar>(+1.0), b2.ptr() );
580 // Create the LHS for the linear solve
582 x1 = Thyra::createMember(A.domain()),
583 x2 = Thyra::createMember(A.domain());
584 // Create a preconditioner for the input forward operator
586 P = precFactory.createPrec();
587 Thyra::initializePrec<Scalar>(precFactory, rcpFromRef(A), P.ptr());
588 // Above, we don't really know the nature of the preconditioner. It could a
589 // single linear operator to be applied on the left or the right or it could
590 // be a split preconditioner with different linear operators to be applied
591 // on the right or left. Or, it could be a single linear operator that is
592 // not targeted to the left or the right.
593 //
594 // Create a LOWSB object given the created preconditioner
596 invertibleA = createGeneralPreconditionedLinearOpWithSolve<Scalar>(
597 Teuchos::rcp(&A, false), P.getConst(), lowsFactory, out);
598 // Grab a preconditioner operator out of the preconditioner object
600 // Clang 3.2 issues a warning if semicolons are on the same line as
601 // the 'if' statement. It's good practice in any case to make the
602 // empty 'if' body clear.
603 if (nonnull (P_op = P->getUnspecifiedPrecOp ()))
604 ;
605 else if (nonnull (P_op = P->getLeftPrecOp ()))
606 ;
607 else if (nonnull (P_op = P->getRightPrecOp ()))
608 ;
609 // Create a LOWSB object given an unspecified preconditioner operator
610 invertibleA = createUnspecifiedPreconditionedLinearOpWithSolve(
611 rcpFromRef(A), P_op, lowsFactory, out);
612 // Create a LOWSB object given a left preconditioner operator
613 if(supportsLeftPrec) {
614 invertibleA = createLeftPreconditionedLinearOpWithSolve(
615 rcpFromRef(A), P_op, lowsFactory,out);
616 }
617 // Create a LOWSB object given a right preconditioner operator
618 if(supportsRightPrec) {
619 invertibleA = createRightPreconditionedLinearOpWithSolve(
620 rcpFromRef(A), P_op, lowsFactory, out);
621 }
622 // Create a LOWSB object given (bad set of) left and right preconditioner
623 // operators
624 if( supportsLeftPrec && supportsRightPrec ) {
625 invertibleA = createLeftRightPreconditionedLinearOpWithSolve(
626 rcpFromRef(A), P_op, P_op, lowsFactory, out);
627 }
628 // Create a LOWSB object given a (very good) approximate forward linear
629 // operator to construct the preconditoner from..
630 invertibleA = createMatrixPreconditionedLinearOpWithSolve<Scalar>(
631 rcpFromRef(A), rcpFromRef(A), lowsFactory,out);
632 // Preconditioner reuse example
633 externalPreconditionerReuseWithSolves<Scalar>(
634 A_nonconst, // Don't worry, it will not be changed!
635 Thyra::NullLinearOpChanger<Scalar>(), // This object will not really change A!
636 lowsFactory, precFactory,
637 *b1, x1.ptr(), *b2, x2.ptr(), out );
638}
639
640
641#endif // THYRA_LINEAR_OP_WITH_SOLVE_EXAMPLES_HPP
RCP< const T > getConst() const
Ptr< T > ptr() const
Base class for all linear operators.
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.
Silly abstract strategy interface for changing Thyra::LinearOpBase objects.
virtual void changeOp(const Teuchos::Ptr< LinearOpBase< Scalar > > &op) const =0
Factory interface for creating LinearOpWithSolveBase objects from compatible LinearOpBase objects.
virtual RCP< LinearOpWithSolveBase< Scalar > > createOp() const =0
Create an (uninitialized) LinearOpWithSolveBase object to be initialized later in this->initializeOp(...
void changeOp(const Teuchos::Ptr< LinearOpBase< Scalar > > &op) const
Simple interface class to access a precreated preconditioner as one or more linear operators objects ...
Factory interface for creating preconditioner objects from LinearOpBase objects.
virtual RCP< PreconditionerBase< Scalar > > createPrec() const =0
Create an (uninitialized) LinearOpBase object to be initialized as the preconditioner later in this->...
Abstract interface for finite-dimensional dense vectors.
@ NOTRANS
Use the non-transposed operator.
TypeTo as(const TypeFrom &t)
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)
Simple struct for the return status from a solve.