MueLu Version of the Day
Loading...
Searching...
No Matches
MueLu_SaPFactory_def.hpp
Go to the documentation of this file.
1// @HEADER
2//
3// ***********************************************************************
4//
5// MueLu: A package for multigrid based preconditioning
6// Copyright 2012 Sandia Corporation
7//
8// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
9// the U.S. Government retains certain rights in this software.
10//
11// Redistribution and use in source and binary forms, with or without
12// modification, are permitted provided that the following conditions are
13// met:
14//
15// 1. Redistributions of source code must retain the above copyright
16// notice, this list of conditions and the following disclaimer.
17//
18// 2. Redistributions in binary form must reproduce the above copyright
19// notice, this list of conditions and the following disclaimer in the
20// documentation and/or other materials provided with the distribution.
21//
22// 3. Neither the name of the Corporation nor the names of the
23// contributors may be used to endorse or promote products derived from
24// this software without specific prior written permission.
25//
26// THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
27// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
30// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
33// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37//
38// Questions? Contact
39// Jonathan Hu (jhu@sandia.gov)
40// Andrey Prokopenko (aprokop@sandia.gov)
41// Ray Tuminaro (rstumin@sandia.gov)
42//
43// ***********************************************************************
44//
45// @HEADER
46#ifndef MUELU_SAPFACTORY_DEF_HPP
47#define MUELU_SAPFACTORY_DEF_HPP
48
49#include <Xpetra_Matrix.hpp>
50#include <Xpetra_IteratorOps.hpp> // containing routines to generate Jacobi iterator
51#include <Xpetra_IO.hpp>
52#include <sstream>
53
55
57#include "MueLu_Level.hpp"
58#include "MueLu_MasterList.hpp"
59#include "MueLu_Monitor.hpp"
60#include "MueLu_PerfUtils.hpp"
61#include "MueLu_TentativePFactory.hpp"
62#include "MueLu_Utilities.hpp"
63
64namespace MueLu {
65
66 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
68 RCP<ParameterList> validParamList = rcp(new ParameterList());
69
70#define SET_VALID_ENTRY(name) validParamList->setEntry(name, MasterList::getEntry(name))
71 SET_VALID_ENTRY("sa: damping factor");
72 SET_VALID_ENTRY("sa: calculate eigenvalue estimate");
73 SET_VALID_ENTRY("sa: eigenvalue estimate num iterations");
74 SET_VALID_ENTRY("sa: use rowsumabs diagonal scaling");
75 SET_VALID_ENTRY("sa: enforce constraints");
76 SET_VALID_ENTRY("tentative: calculate qr");
77 SET_VALID_ENTRY("sa: max eigenvalue");
78 SET_VALID_ENTRY("sa: rowsumabs diagonal replacement tolerance");
79 SET_VALID_ENTRY("sa: rowsumabs diagonal replacement value");
80 SET_VALID_ENTRY("sa: rowsumabs replace single entry row with zero");
81 SET_VALID_ENTRY("sa: rowsumabs use automatic diagonal tolerance");
82#undef SET_VALID_ENTRY
83
84 validParamList->set< RCP<const FactoryBase> >("A", Teuchos::null, "Generating factory of the matrix A used during the prolongator smoothing process");
85 validParamList->set< RCP<const FactoryBase> >("P", Teuchos::null, "Tentative prolongator factory");
86
87 // Make sure we don't recursively validate options for the matrixmatrix kernels
88 ParameterList norecurse;
89 norecurse.disableRecursiveValidation();
90 validParamList->set<ParameterList> ("matrixmatrix: kernel params", norecurse, "MatrixMatrix kernel parameters");
91
92
93 return validParamList;
94 }
95
96 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
98 Input(fineLevel, "A");
99
100 // Get default tentative prolongator factory
101 // Getting it that way ensure that the same factory instance will be used for both SaPFactory and NullspaceFactory.
102 RCP<const FactoryBase> initialPFact = GetFactory("P");
103 if (initialPFact == Teuchos::null) { initialPFact = coarseLevel.GetFactoryManager()->GetFactory("Ptent"); }
104 coarseLevel.DeclareInput("P", initialPFact.get(), this); // --
105 }
106
107 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
109 return BuildP(fineLevel, coarseLevel);
110 }
111
112 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
114 FactoryMonitor m(*this, "Prolongator smoothing", coarseLevel);
115
116 std::string levelIDs = toString(coarseLevel.GetLevelID());
117
118 const std::string prefix = "MueLu::SaPFactory(" + levelIDs + "): ";
119
120 typedef typename Teuchos::ScalarTraits<SC>::coordinateType Coordinate;
121 typedef typename Teuchos::ScalarTraits<SC>::magnitudeType MT;
122
123
124 // Get default tentative prolongator factory
125 // Getting it that way ensure that the same factory instance will be used for both SaPFactory and NullspaceFactory.
126 // -- Warning: Do not use directly initialPFact_. Use initialPFact instead everywhere!
127 RCP<const FactoryBase> initialPFact = GetFactory("P");
128 if (initialPFact == Teuchos::null) { initialPFact = coarseLevel.GetFactoryManager()->GetFactory("Ptent"); }
129 const ParameterList& pL = GetParameterList();
130
131 // Level Get
132 RCP<Matrix> A = Get< RCP<Matrix> >(fineLevel, "A");
133 RCP<Matrix> Ptent = coarseLevel.Get< RCP<Matrix> >("P", initialPFact.get());
134 RCP<Matrix> finalP;
135 // If Tentative facctory bailed out (e.g., number of global aggregates is 0), then SaPFactory bails
136 // This level will ultimately be removed in MueLu_Hierarchy_defs.h via a resize()
137 if (Ptent == Teuchos::null) {
138 finalP = Teuchos::null;
139 Set(coarseLevel, "P", finalP);
140 return;
141 }
142
143 if (restrictionMode_) {
144 SubFactoryMonitor m2(*this, "Transpose A", coarseLevel);
145 A = Utilities::Transpose(*A, true); // build transpose of A explicitely
146 }
147
148 // Build final prolongator
149
150 // Reuse pattern if available
151 RCP<ParameterList> APparams = rcp(new ParameterList);;
152 if(pL.isSublist("matrixmatrix: kernel params"))
153 APparams->sublist("matrixmatrix: kernel params") = pL.sublist("matrixmatrix: kernel params");
154
155 if (coarseLevel.IsAvailable("AP reuse data", this)) {
156 GetOStream(static_cast<MsgType>(Runtime0 | Test)) << "Reusing previous AP data" << std::endl;
157
158 APparams = coarseLevel.Get< RCP<ParameterList> >("AP reuse data", this);
159
160 if (APparams->isParameter("graph"))
161 finalP = APparams->get< RCP<Matrix> >("graph");
162 }
163 // By default, we don't need global constants for SaP
164 APparams->set("compute global constants: temporaries",APparams->get("compute global constants: temporaries",false));
165 APparams->set("compute global constants",APparams->get("compute global constants",false));
166
167 const SC dampingFactor = as<SC>(pL.get<double>("sa: damping factor"));
168 const LO maxEigenIterations = as<LO>(pL.get<int> ("sa: eigenvalue estimate num iterations"));
169 const bool estimateMaxEigen = pL.get<bool> ("sa: calculate eigenvalue estimate");
170 const bool useAbsValueRowSum= pL.get<bool> ("sa: use rowsumabs diagonal scaling");
171 const bool doQRStep = pL.get<bool>("tentative: calculate qr");
172 const bool enforceConstraints= pL.get<bool>("sa: enforce constraints");
173 const MT userDefinedMaxEigen = as<MT>(pL.get<double>("sa: max eigenvalue"));
174 typedef typename Teuchos::ScalarTraits<Scalar>::magnitudeType Magnitude;
175 double dTol = pL.get<double>("sa: rowsumabs diagonal replacement tolerance");
176 const Magnitude diagonalReplacementTolerance = (dTol == as<double>(-1) ? Teuchos::ScalarTraits<Scalar>::eps()*100 : as<Magnitude>(pL.get<double>("sa: rowsumabs diagonal replacement tolerance")));
177 const SC diagonalReplacementValue = as<SC>(pL.get<double>("sa: rowsumabs diagonal replacement value"));
178 const bool replaceSingleEntryRowWithZero = pL.get<bool>("sa: rowsumabs replace single entry row with zero");
179 const bool useAutomaticDiagTol = pL.get<bool>("sa: rowsumabs use automatic diagonal tolerance");
180
181 // Sanity checking
182 TEUCHOS_TEST_FOR_EXCEPTION(doQRStep && enforceConstraints,Exceptions::RuntimeError,
183 "MueLu::TentativePFactory::MakeTentative: cannot use 'enforce constraints' and 'calculate qr' at the same time");
184
185 if (dampingFactor != Teuchos::ScalarTraits<SC>::zero()) {
186
187 Scalar lambdaMax;
188 Teuchos::RCP<Vector> invDiag;
189 if (userDefinedMaxEigen == -1.)
190 {
191 SubFactoryMonitor m2(*this, "Eigenvalue estimate", coarseLevel);
192 lambdaMax = A->GetMaxEigenvalueEstimate();
193 if (lambdaMax == -Teuchos::ScalarTraits<SC>::one() || estimateMaxEigen) {
194 GetOStream(Statistics1) << "Calculating max eigenvalue estimate now (max iters = "<< maxEigenIterations <<
195 ( (useAbsValueRowSum) ? ", use rowSumAbs diagonal)" : ", use point diagonal)") << std::endl;
196 Coordinate stopTol = 1e-4;
197 if (useAbsValueRowSum) {
198 const bool returnReciprocal=true;
199 invDiag = Utilities::GetLumpedMatrixDiagonal(*A,returnReciprocal,
200 diagonalReplacementTolerance,
201 diagonalReplacementValue,
202 replaceSingleEntryRowWithZero,
203 useAutomaticDiagTol);
204 TEUCHOS_TEST_FOR_EXCEPTION(invDiag.is_null(), Exceptions::RuntimeError,
205 "SaPFactory: eigenvalue estimate: diagonal reciprocal is null.");
206 lambdaMax = Utilities::PowerMethod(*A, invDiag, maxEigenIterations, stopTol);
207 } else
208 lambdaMax = Utilities::PowerMethod(*A, true, maxEigenIterations, stopTol);
209 A->SetMaxEigenvalueEstimate(lambdaMax);
210 } else {
211 GetOStream(Statistics1) << "Using cached max eigenvalue estimate" << std::endl;
212 }
213 }
214 else {
215 lambdaMax = userDefinedMaxEigen;
216 A->SetMaxEigenvalueEstimate(lambdaMax);
217 }
218 GetOStream(Statistics1) << "Prolongator damping factor = " << dampingFactor/lambdaMax << " (" << dampingFactor << " / " << lambdaMax << ")" << std::endl;
219
220 {
221 SubFactoryMonitor m2(*this, "Fused (I-omega*D^{-1} A)*Ptent", coarseLevel);
222 if (!useAbsValueRowSum)
223 invDiag = Utilities::GetMatrixDiagonalInverse(*A); //default
224 else if (invDiag == Teuchos::null) {
225 GetOStream(Runtime0) << "Using rowsumabs diagonal" << std::endl;
226 const bool returnReciprocal=true;
227 invDiag = Utilities::GetLumpedMatrixDiagonal(*A,returnReciprocal,
228 diagonalReplacementTolerance,
229 diagonalReplacementValue,
230 replaceSingleEntryRowWithZero,
231 useAutomaticDiagTol);
232 TEUCHOS_TEST_FOR_EXCEPTION(invDiag.is_null(), Exceptions::RuntimeError, "SaPFactory: diagonal reciprocal is null.");
233 }
234
235 SC omega = dampingFactor / lambdaMax;
236 TEUCHOS_TEST_FOR_EXCEPTION(!std::isfinite(Teuchos::ScalarTraits<SC>::magnitude(omega)), Exceptions::RuntimeError, "Prolongator damping factor needs to be finite.");
237
238 // finalP = Ptent + (I - \omega D^{-1}A) Ptent
239 finalP = Xpetra::IteratorOps<Scalar,LocalOrdinal,GlobalOrdinal,Node>::Jacobi(omega, *invDiag, *A, *Ptent, finalP,
240 GetOStream(Statistics2), std::string("MueLu::SaP-")+levelIDs, APparams);
241 if (enforceConstraints) {
242 if (A->GetFixedBlockSize() == 1) optimalSatisfyPConstraintsForScalarPDEs( finalP);
243 else SatisfyPConstraints( A, finalP);
244 }
245 }
246
247 } else {
248 finalP = Ptent;
249 }
250
251 // Level Set
252 if (!restrictionMode_) {
253 // The factory is in prolongation mode
254 if(!finalP.is_null()) {std::ostringstream oss; oss << "P_" << coarseLevel.GetLevelID(); finalP->setObjectLabel(oss.str());}
255 Set(coarseLevel, "P", finalP);
256
257 APparams->set("graph", finalP);
258 Set(coarseLevel, "AP reuse data", APparams);
259
260 // NOTE: EXPERIMENTAL
261 if (Ptent->IsView("stridedMaps"))
262 finalP->CreateView("stridedMaps", Ptent);
263
264 if (IsPrint(Statistics2)) {
265 RCP<ParameterList> params = rcp(new ParameterList());
266 params->set("printLoadBalancingInfo", true);
267 params->set("printCommInfo", true);
268 GetOStream(Statistics2) << PerfUtils::PrintMatrixInfo(*finalP, "P", params);
269 }
270
271 } else {
272 // The factory is in restriction mode
273 RCP<Matrix> R;
274 {
275 SubFactoryMonitor m2(*this, "Transpose P", coarseLevel);
276 R = Utilities::Transpose(*finalP, true);
277 if(!R.is_null()) {std::ostringstream oss; oss << "R_" << coarseLevel.GetLevelID(); R->setObjectLabel(oss.str());}
278 }
279
280 Set(coarseLevel, "R", R);
281
282 // NOTE: EXPERIMENTAL
283 if (Ptent->IsView("stridedMaps"))
284 R->CreateView("stridedMaps", Ptent, true/*transposeA*/);
285
286 if (IsPrint(Statistics2)) {
287 RCP<ParameterList> params = rcp(new ParameterList());
288 params->set("printLoadBalancingInfo", true);
289 params->set("printCommInfo", true);
291 }
292 }
293
294 } //Build()
295
296 // Analyze the grid transfer produced by smoothed aggregation and make
297 // modifications if it does not look right. In particular, if there are
298 // negative entries or entries larger than 1, modify P's rows.
299 //
300 // Note: this kind of evaluation probably only makes sense if not doing QR
301 // when constructing tentative P.
302 //
303 // For entries that do not satisfy the two constraints (>= 0 or <=1) we set
304 // these entries to the constraint value and modify the rest of the row
305 // so that the row sum remains the same as before by adding an equal
306 // amount to each remaining entry. However, if the original row sum value
307 // violates the constraints, we set the row sum back to 1 (the row sum of
308 // tentative P). After doing the modification to a row, we need to check
309 // again the entire row to make sure that the modified row does not violate
310 // the constraints.
311
312 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
314
315 const Scalar zero = Teuchos::ScalarTraits<Scalar>::zero();
316 const Scalar one = Teuchos::ScalarTraits<Scalar>::one();
317 LO nPDEs = A->GetFixedBlockSize();
318 Teuchos::ArrayRCP<Scalar> ConstraintViolationSum(nPDEs);
319 Teuchos::ArrayRCP<Scalar> Rsum(nPDEs);
320 Teuchos::ArrayRCP<size_t> nPositive(nPDEs);
321 for (size_t k=0; k < (size_t) nPDEs; k++) ConstraintViolationSum[k] = zero;
322 for (size_t k=0; k < (size_t) nPDEs; k++) Rsum[k] = zero;
323 for (size_t k=0; k < (size_t) nPDEs; k++) nPositive[k] = 0;
324
325
326 for (size_t i = 0; i < as<size_t>(P->getRowMap()->getLocalNumElements()); i++) {
327
328 Teuchos::ArrayView<const LocalOrdinal> indices;
329 Teuchos::ArrayView<const Scalar> vals1;
330 Teuchos::ArrayView< Scalar> vals;
331 P->getLocalRowView((LO) i, indices, vals1);
332 size_t nnz = indices.size();
333 if (nnz == 0) continue;
334
335 vals = ArrayView<Scalar>(const_cast<SC*>(vals1.getRawPtr()), nnz);
336
337
338 bool checkRow = true;
339
340 while (checkRow) {
341
342 // check constraints and compute the row sum
343
344 for (LO j = 0; j < indices.size(); j++) {
345 Rsum[ j%nPDEs ] += vals[j];
346 if (Teuchos::ScalarTraits<SC>::real(vals[j]) < Teuchos::ScalarTraits<SC>::real(zero)) {
347 ConstraintViolationSum[ j%nPDEs ] += vals[j];
348 vals[j] = zero;
349 }
350 else {
351 if (Teuchos::ScalarTraits<SC>::real(vals[j]) != Teuchos::ScalarTraits<SC>::real(zero))
352 (nPositive[ j%nPDEs])++;
353
354 if (Teuchos::ScalarTraits<SC>::real(vals[j]) > Teuchos::ScalarTraits<SC>::real(1.00001 )) {
355 ConstraintViolationSum[ j%nPDEs ] += (vals[j] - one);
356 vals[j] = one;
357 }
358 }
359 }
360
361 checkRow = false;
362
363 // take into account any row sum that violates the contraints
364
365 for (size_t k=0; k < (size_t) nPDEs; k++) {
366
367 if (Teuchos::ScalarTraits<SC>::real(Rsum[ k ]) < Teuchos::ScalarTraits<SC>::magnitude(zero)) {
368 ConstraintViolationSum[k] += (-Rsum[k]); // rstumin
369 }
370 else if (Teuchos::ScalarTraits<SC>::real(Rsum[ k ]) > Teuchos::ScalarTraits<SC>::magnitude(1.00001)) {
371 ConstraintViolationSum[k] += (one - Rsum[k]); // rstumin
372 }
373 }
374
375 // check if row need modification
376 for (size_t k=0; k < (size_t) nPDEs; k++) {
377 if (Teuchos::ScalarTraits<SC>::magnitude(ConstraintViolationSum[ k ]) != Teuchos::ScalarTraits<SC>::magnitude(zero))
378 checkRow = true;
379 }
380 // modify row
381 if (checkRow) {
382 for (LO j = 0; j < indices.size(); j++) {
383 if (Teuchos::ScalarTraits<SC>::real(vals[j]) > Teuchos::ScalarTraits<SC>::real(zero)) {
384 vals[j] += (ConstraintViolationSum[j%nPDEs]/ (as<Scalar>(nPositive[j%nPDEs])));
385 }
386 }
387 for (size_t k=0; k < (size_t) nPDEs; k++) ConstraintViolationSum[k] = zero;
388 }
389 for (size_t k=0; k < (size_t) nPDEs; k++) Rsum[k] = zero;
390 for (size_t k=0; k < (size_t) nPDEs; k++) nPositive[k] = 0;
391 } // while (checkRow) ...
392 } // for (size_t i = 0; i < as<size_t>(P->getRowMap()->getNumNodeElements()); i++) ...
393 } //SatsifyPConstraints()
394
395 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
397
398 const Scalar zero = Teuchos::ScalarTraits<Scalar>::zero();
399 const Scalar one = Teuchos::ScalarTraits<Scalar>::one();
400
401 LocalOrdinal maxEntriesPerRow = 100; // increased later if needed
402 Teuchos::ArrayRCP<Scalar> scalarData(3*maxEntriesPerRow);
403 bool hasFeasible;
404
405 for (size_t i = 0; i < as<size_t>(P->getRowMap()->getLocalNumElements()); i++) {
406
407 Teuchos::ArrayView<const LocalOrdinal> indices;
408 Teuchos::ArrayView<const Scalar> vals1;
409 Teuchos::ArrayView< Scalar> vals;
410 P->getLocalRowView((LocalOrdinal) i, indices, vals1);
411 size_t nnz = indices.size();
412 if (nnz != 0) {
413
414 vals = ArrayView<Scalar>(const_cast<SC*>(vals1.getRawPtr()), nnz);
415 Scalar rsumTarget = zero;
416 for (size_t j = 0; j < nnz; j++) rsumTarget += vals[j];
417
418 if (nnz > as<size_t>(maxEntriesPerRow)) {
419 maxEntriesPerRow = nnz*3;
420 scalarData.resize(3*maxEntriesPerRow);
421 }
422 hasFeasible = constrainRow(vals.getRawPtr(), as<LocalOrdinal>(nnz), zero , one, rsumTarget, vals.getRawPtr(), scalarData.getRawPtr());
423
424 if (!hasFeasible) { // just set all entries to the same value giving a row sum of 1
425 for (size_t j = 0; j < nnz; j++) vals[j] = one/as<Scalar>(nnz);
426 }
427 }
428
429 } // for (size_t i = 0; i < as<size_t>(P->getRowMap()->getNumNodeElements()); i++) ...
430 } //SatsifyPConstraints()
431
432 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
433 bool SaPFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::constrainRow(Scalar *orig, LocalOrdinal nEntries, Scalar leftBound, Scalar rghtBound,Scalar rsumTarget, Scalar *fixedUnsorted, Scalar *scalarData) const {
434/*
435 Input
436 orig data that should be adjusted to satisfy bound constraints and
437 row sum constraint. orig is not modified by this function.
438
439 nEntries length or 'orig'
440
441 leftBound, define bound constraints for the results.
442 rghtBound
443
444 rsumTarget defines an equality constraint for the row sum
445
446 fixedUnsorted on output, if a feasible solutuion exists then
447 || orig - fixedUnsorted || = min when also
448 leftBound <= fixedUnsorted[i] <= rghtBound for all i
449 and sum(fixedUnsorted) = rsumTarget.
450
451 Note: it is possible to use the same pointer for
452 fixedUnsorted and orig. In this case, orig gets
453 overwritten with the new constraint satisfying values.
454
455 scalarData a work array that should be 3x nEntries.
456
457 On return constrain() indicates whether or not a feasible solution exists.
458*/
459
460/*
461 Given a sequence of numbers o1 ... on, fix these so that they are as
462 close as possible to the original but satisfy bound constraints and also
463 have the same row sum as the oi's. If we know who is going to lie on a
464 bound, then the "best" answer (i.e., || o - f ||_2 = min) perturbs
465 each element that doesn't lie on a bound by the same amount.
466
467 We can represent the oi's by considering scattered points on a number line
468
469 | |
470 | |
471 o o o | o o o o o o |o o
472 | |
473
474 \____/ \____/
475 <---- <----
476 delta delta
477
478 Bounds are shown by vertical lines. The fi's must lie within the bounds, so
479 the 3 leftmost points must be shifted to the right and the 2 rightmost must
480 be shifted to the left. If these fi points are all shifted to the bounds
481 while the others remain in the same location, the row sum constraint is
482 likely not satisfied and so more shifting is necessary. In the figure, the f
483 rowsum is too large and so there must be more shifting to the left.
484
485 To minimize || o - f ||_2, we basically shift all "interiors" by the same
486 amount, denoted delta. The only trick is that some points near bounds are
487 still affected by the bounds and so these points might be shifted more or less
488 than delta. In the example,t he 3 rightmost points are shifted in the opposite
489 direction as delta to the bound. The 4th point is shifted by something less
490 than delta so it does not violate the lower bound. The rightmost point is
491 shifted to the bound by some amount larger than delta. However, the 2nd point
492 is shifted by delta (i.e., it lies inside the two bounds).
493
494 If we know delta, we can figure out everything. If we know which points
495 are special (not shifted by delta), we can also figure out everything.
496 The problem is these two things (delta and the special points) are
497 inter-connected. An algorithm for computing follows.
498
499 1) move exterior points to the bounds and compute how much the row sum is off
500 (rowSumDeviation). We assume now that the new row sum is high, so interior
501 points must be shifted left.
502
503 2) Mark closest point just left of the leftmost bound, closestToLeftBound,
504 and compute its distance to the leftmost bound. Mark closest point to the
505 left of the rightmost bound, closestToRghtBound, and compute its distance to
506 right bound. There are two cases to consider.
507
508 3) Case 1: closestToLeftBound is closer than closestToRghtBound.
509 Assume that shifting by delta does not move closestToLeftBound past the
510 left bound. This means that it will be shifted by delta. However,
511 closestToRghtBound will be shifted by more than delta. So the total
512 number of points shifted by delta (|interiorToBounds|) includes
513 closestToLeftBound up to and including the point just to the left of
514 closestToRghtBound. So
515
516 delta = rowSumDeviation/ |interiorToBounds| .
517
518 Recall that rowSumDeviation already accounts for the non-delta shift of
519 of closestToRightBound. Now check whether our assumption is valid.
520
521 If delta <= closestToLeftBoundDist, assumption is true so delta can be
522 applied to interiorToBounds ... and we are done.
523 Else assumption is false. Shift closestToLeftBound to the left bound.
524 Update rowSumDeviation, interiorToBounds, and identify new
525 closestToLeftBound. Repeat step 3).
526
527 Case 2: closestToRghtBound is closer than closestToLeftBound.
528 Assume that shifting by delta does not move closestToRghtBound past right
529 bound. This means that it must be shifted by more than delta to right
530 bound. So the total number of points shifted by delta again includes
531 closestToLeftBound up to and including the point just to the left of
532 closestToRghtBound. So again compute
533
534 delta = rowSumDeviation/ |interiorToBounds| .
535
536 If delta <= closestToRghtBoundDist, assumption is true so delta is
537 can be applied to interiorToBounds ... and we are done
538 Else assumption is false. Put closestToRghtBound in the
539 interiorToBounds set. Remove it's contribution to rowSumDeviation,
540 identify new closestToRghtBound. Repeat step 3)
541
542
543 To implement, sort the oi's so things like closestToLeftBound is just index
544 into sorted array. Updaing closestToLeftBound or closestToRghtBound requires
545 increment by 1. |interiorToBounds|= closestToRghtBound - closestToLeftBound
546 To handle the case when the rowsum is low (requiring right interior shifts),
547 just flip the signs on data and use the left-shift code (and then flip back
548 before exiting function.
549*/
550 bool hasFeasibleSol;
551 Scalar notFlippedLeftBound, notFlippedRghtBound, aBigNumber, *origSorted;
552 Scalar rowSumDeviation, temp, *fixedSorted, delta;
553 Scalar closestToLeftBoundDist, closestToRghtBoundDist;
554 LocalOrdinal closestToLeftBound, closestToRghtBound;
555 LocalOrdinal *inds;
556 bool flipped;
557
558 notFlippedLeftBound = leftBound;
559 notFlippedRghtBound = rghtBound;
560
561 if ((Teuchos::ScalarTraits<SC>::real(rsumTarget) >= Teuchos::ScalarTraits<SC>::real(leftBound*as<Scalar>(nEntries))) &&
562 (Teuchos::ScalarTraits<SC>::real(rsumTarget) <= Teuchos::ScalarTraits<SC>::real(rghtBound*as<Scalar>(nEntries))))
563 hasFeasibleSol = true;
564 else {
565 hasFeasibleSol=false;
566 return hasFeasibleSol;
567 }
568 flipped = false;
569 // compute aBigNumber to handle some corner cases where we need
570 // something large so that an if statement will be false
571 aBigNumber = Teuchos::ScalarTraits<SC>::zero();
572 for (LocalOrdinal i = 0; i < nEntries; i++) {
573 if ( Teuchos::ScalarTraits<SC>::magnitude(orig[i]) > Teuchos::ScalarTraits<SC>::magnitude(aBigNumber))
574 aBigNumber = Teuchos::ScalarTraits<SC>::magnitude(orig[i]);
575 }
576 aBigNumber = aBigNumber+ (Teuchos::ScalarTraits<SC>::magnitude(leftBound) + Teuchos::ScalarTraits<SC>::magnitude(rghtBound))*as<Scalar>(100.0);
577
578 origSorted = &scalarData[0];
579 fixedSorted = &(scalarData[nEntries]);
580 inds = (LocalOrdinal *) &(scalarData[2*nEntries]);
581
582 for (LocalOrdinal i = 0; i < nEntries; i++) inds[i] = i;
583 for (LocalOrdinal i = 0; i < nEntries; i++) origSorted[i] = orig[i]; /* orig no longer used */
584
585 // sort so that orig[inds] is sorted.
586 std::sort(inds, inds+nEntries,
587 [origSorted](LocalOrdinal leftIndex, LocalOrdinal rightIndex)
588 { return Teuchos::ScalarTraits<SC>::real(origSorted[leftIndex]) < Teuchos::ScalarTraits<SC>::real(origSorted[rightIndex]);});
589
590 for (LocalOrdinal i = 0; i < nEntries; i++) origSorted[i] = orig[inds[i]];
591 // find entry in origSorted just to the right of the leftBound
592 closestToLeftBound = 0;
593 while ((closestToLeftBound < nEntries) && (Teuchos::ScalarTraits<SC>::real(origSorted[closestToLeftBound]) <= Teuchos::ScalarTraits<SC>::real(leftBound))) closestToLeftBound++;
594
595 // find entry in origSorted just to the right of the rghtBound
596 closestToRghtBound = closestToLeftBound;
597 while ((closestToRghtBound < nEntries) && (Teuchos::ScalarTraits<SC>::real(origSorted[closestToRghtBound]) <= Teuchos::ScalarTraits<SC>::real(rghtBound))) closestToRghtBound++;
598
599 // compute distance between closestToLeftBound and the left bound and the
600 // distance between closestToRghtBound and the right bound.
601
602 closestToLeftBoundDist = origSorted[closestToLeftBound] - leftBound;
603 if (closestToRghtBound==nEntries) closestToRghtBoundDist= aBigNumber;
604 else closestToRghtBoundDist= origSorted[closestToRghtBound] - rghtBound;
605
606 // compute how far the rowSum is off from the target row sum taking into account
607 // numbers that have been shifted to satisfy bound constraint
608
609 rowSumDeviation = leftBound*as<Scalar>(closestToLeftBound) + as<Scalar>((nEntries-closestToRghtBound))*rghtBound - rsumTarget;
610 for (LocalOrdinal i=closestToLeftBound; i < closestToRghtBound; i++) rowSumDeviation += origSorted[i];
611
612 // the code that follow after this if statement assumes that rowSumDeviation is positive. If this
613 // is not the case, flip the signs of everything so that rowSumDeviation is now positive.
614 // Later we will flip the data back to its original form.
615 if (Teuchos::ScalarTraits<SC>::real(rowSumDeviation) < Teuchos::ScalarTraits<SC>::real(Teuchos::ScalarTraits<SC>::zero())) {
616 flipped = true;
617 temp = leftBound; leftBound = -rghtBound; rghtBound = temp;
618
619 /* flip sign of origSorted and reverse ordering so that the negative version is sorted */
620
621 if ((nEntries%2) == 1) origSorted[(nEntries/2) ] = -origSorted[(nEntries/2) ];
622 for (LocalOrdinal i=0; i < nEntries/2; i++) {
623 temp=origSorted[i];
624 origSorted[i] = -origSorted[nEntries-1-i];
625 origSorted[nEntries-i-1] = -temp;
626 }
627
628 /* reverse bounds */
629
630 LocalOrdinal itemp = closestToLeftBound;
631 closestToLeftBound = nEntries-closestToRghtBound;
632 closestToRghtBound = nEntries-itemp;
633 closestToLeftBoundDist = origSorted[closestToLeftBound] - leftBound;
634 if (closestToRghtBound==nEntries) closestToRghtBoundDist= aBigNumber;
635 else closestToRghtBoundDist= origSorted[closestToRghtBound] - rghtBound;
636
637 rowSumDeviation = -rowSumDeviation;
638 }
639
640 // initial fixedSorted so bounds are satisfied and interiors correspond to origSorted
641
642 for (LocalOrdinal i = 0; i < closestToLeftBound; i++) fixedSorted[i] = leftBound;
643 for (LocalOrdinal i = closestToLeftBound; i < closestToRghtBound; i++) fixedSorted[i] = origSorted[i];
644 for (LocalOrdinal i = closestToRghtBound; i < nEntries; i++) fixedSorted[i] = rghtBound;
645
646 while ((Teuchos::ScalarTraits<SC>::magnitude(rowSumDeviation) > Teuchos::ScalarTraits<SC>::magnitude(as<Scalar>(1.e-10)*rsumTarget))){ // && ( (closestToLeftBound < nEntries ) || (closestToRghtBound < nEntries))) {
647 if (closestToRghtBound != closestToLeftBound)
648 delta = rowSumDeviation/ as<Scalar>(closestToRghtBound - closestToLeftBound);
649 else delta = aBigNumber;
650
651 if (Teuchos::ScalarTraits<SC>::magnitude(closestToLeftBoundDist) <= Teuchos::ScalarTraits<SC>::magnitude(closestToRghtBoundDist)) {
652 if (Teuchos::ScalarTraits<SC>::magnitude(delta) <= Teuchos::ScalarTraits<SC>::magnitude(closestToLeftBoundDist)) {
653 rowSumDeviation = Teuchos::ScalarTraits<SC>::zero();
654 for (LocalOrdinal i = closestToLeftBound; i < closestToRghtBound ; i++) fixedSorted[i] = origSorted[i] - delta;
655 }
656 else {
657 rowSumDeviation = rowSumDeviation - closestToLeftBoundDist;
658 fixedSorted[closestToLeftBound] = leftBound;
659 closestToLeftBound++;
660 if (closestToLeftBound < nEntries) closestToLeftBoundDist = origSorted[closestToLeftBound] - leftBound;
661 else closestToLeftBoundDist = aBigNumber;
662 }
663 }
664 else {
665 if (Teuchos::ScalarTraits<SC>::magnitude(delta) <= Teuchos::ScalarTraits<SC>::magnitude(closestToRghtBoundDist)) {
666 rowSumDeviation = 0;
667 for (LocalOrdinal i = closestToLeftBound; i < closestToRghtBound ; i++) fixedSorted[i] = origSorted[i] - delta;
668 }
669 else {
670 rowSumDeviation = rowSumDeviation + closestToRghtBoundDist;
671// if (closestToRghtBound < nEntries) {
672 fixedSorted[closestToRghtBound] = origSorted[closestToRghtBound];
673 closestToRghtBound++;
674 // }
675 if (closestToRghtBound >= nEntries) closestToRghtBoundDist = aBigNumber;
676 else closestToRghtBoundDist = origSorted[closestToRghtBound] - rghtBound;
677 }
678 }
679 }
680
681 if (flipped) {
682 /* flip sign of fixedSorted and reverse ordering so that the positve version is sorted */
683
684 if ((nEntries%2) == 1) fixedSorted[(nEntries/2) ] = -fixedSorted[(nEntries/2) ];
685 for (LocalOrdinal i=0; i < nEntries/2; i++) {
686 temp=fixedSorted[i];
687 fixedSorted[i] = -fixedSorted[nEntries-1-i];
688 fixedSorted[nEntries-i-1] = -temp;
689 }
690 }
691 for (LocalOrdinal i = 0; i < nEntries; i++) fixedUnsorted[inds[i]] = fixedSorted[i];
692
693 /* check that no constraints are violated */
694
695 bool lowerViolation = false;
696 bool upperViolation = false;
697 bool sumViolation = false;
698 using TST = Teuchos::ScalarTraits<SC>;
699 temp = TST::zero();
700 for (LocalOrdinal i = 0; i < nEntries; i++) {
701 if (TST::real(fixedUnsorted[i]) < TST::real(notFlippedLeftBound)) lowerViolation = true;
702 if (TST::real(fixedUnsorted[i]) > TST::real(notFlippedRghtBound)) upperViolation = true;
703 temp += fixedUnsorted[i];
704 }
705 SC tol = as<Scalar>(std::max(1.0e-8, as<double>(100*TST::eps())));
706 if (TST::magnitude(temp - rsumTarget) > TST::magnitude(tol*rsumTarget)) sumViolation = true;
707
708 TEUCHOS_TEST_FOR_EXCEPTION(lowerViolation, Exceptions::RuntimeError, "MueLu::SaPFactory::constrainRow: feasible solution but computation resulted in a lower bound violation??? ");
709 TEUCHOS_TEST_FOR_EXCEPTION(upperViolation, Exceptions::RuntimeError, "MueLu::SaPFactory::constrainRow: feasible solution but computation resulted in an upper bound violation??? ");
710 TEUCHOS_TEST_FOR_EXCEPTION(sumViolation, Exceptions::RuntimeError, "MueLu::SaPFactory::constrainRow: feasible solution but computation resulted in a row sum violation??? ");
711
712 return hasFeasibleSol;
713}
714
715
716} //namespace MueLu
717
718#endif // MUELU_SAPFACTORY_DEF_HPP
719
720//TODO: restrictionMode_ should use the parameter list.
#define SET_VALID_ENTRY(name)
MueLu::DefaultLocalOrdinal LocalOrdinal
MueLu::DefaultScalar Scalar
Exception throws to report errors in the internal logical of the program.
Timer to be used in factories. Similar to Monitor but with additional timers.
void Input(Level &level, const std::string &varName) const
T Get(Level &level, const std::string &varName) const
void Set(Level &level, const std::string &varName, const T &data) const
const RCP< const FactoryBase > GetFactory(const std::string &varName) const
Default implementation of FactoryAcceptor::GetFactory().
Class that holds all level-specific information.
bool IsAvailable(const std::string &ename, const FactoryBase *factory=NoFactory::get()) const
Test whether a need's value has been saved.
void DeclareInput(const std::string &ename, const FactoryBase *factory, const FactoryBase *requestedBy=NoFactory::get())
Callback from FactoryBase::CallDeclareInput() and FactoryBase::DeclareInput().
const RCP< const FactoryManagerBase > GetFactoryManager()
returns the current factory manager
int GetLevelID() const
Return level number.
T & Get(const std::string &ename, const FactoryBase *factory=NoFactory::get())
Get data without decrementing associated storage counter (i.e., read-only access)....
virtual const Teuchos::ParameterList & GetParameterList() const
static std::string PrintMatrixInfo(const Matrix &A, const std::string &msgTag, RCP< const Teuchos::ParameterList > params=Teuchos::null)
void SatisfyPConstraints(RCP< Matrix > A, RCP< Matrix > &P) const
Enforce constraints on prolongator.
void Build(Level &fineLevel, Level &coarseLevel) const
RCP< const ParameterList > GetValidParameterList() const
Return a const parameter list of valid parameters that setParameterList() will accept.
void optimalSatisfyPConstraintsForScalarPDEs(RCP< Matrix > &P) const
bool constrainRow(Scalar *orig, LocalOrdinal nEntries, Scalar leftBound, Scalar rghtBound, Scalar rsumTarget, Scalar *fixedUnsorted, Scalar *scalarData) const
void BuildP(Level &fineLevel, Level &coarseLevel) const
Abstract Build method.
void DeclareInput(Level &fineLevel, Level &coarseLevel) const
Input.
Timer to be used in factories. Similar to SubMonitor but adds a timer level by level.
static RCP< Vector > GetMatrixDiagonalInverse(const Matrix &A, Magnitude tol=Teuchos::ScalarTraits< Scalar >::eps() *100, Scalar valReplacement=Teuchos::ScalarTraits< Scalar >::zero(), const bool doLumped=false)
static Scalar PowerMethod(const Matrix &A, bool scaleByDiag=true, DefaultLocalOrdinal niters=10, Magnitude tolerance=1e-2, bool verbose=false, unsigned int seed=123)
static Teuchos::RCP< Vector > GetLumpedMatrixDiagonal(Matrix const &A, const bool doReciprocal=false, Magnitude tol=Teuchos::ScalarTraits< Scalar >::magnitude(Teuchos::ScalarTraits< Scalar >::zero()), Scalar valReplacement=Teuchos::ScalarTraits< Scalar >::zero(), const bool replaceSingleEntryRowWithZero=false, const bool useAverageAbsDiagVal=false)
static RCP< Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > > Transpose(Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > &Op, bool optimizeTranspose=false, const std::string &label=std::string(), const Teuchos::RCP< Teuchos::ParameterList > &params=Teuchos::null)
Teuchos::FancyOStream & GetOStream(MsgType type, int thisProcRankOnly=0) const
Get an output stream for outputting the input message type.
bool IsPrint(MsgType type, int thisProcRankOnly=-1) const
Find out whether we need to print out information for a specific message type.
Namespace for MueLu classes and methods.
@ Statistics2
Print even more statistics.
@ Statistics1
Print more statistics.
@ Runtime0
One-liner description of what is happening.
std::string toString(const T &what)
Little helper function to convert non-string types to strings.