Tpetra parallel linear algebra Version of the Day
Loading...
Searching...
No Matches
Tpetra_CrsSingletonFilter_LinearProblem_def.hpp
Go to the documentation of this file.
1// @HEADER
2// *****************************************************************************
3// Tpetra: Templated Linear Algebra Services Package
4//
5// Copyright 2008 NTESS and the Tpetra contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
10#ifndef TPETRA_CRSSINGLETONFILTER_LINEARPROBLEM_DEF_HPP
11#define TPETRA_CRSSINGLETONFILTER_LINEARPROBLEM_DEF_HPP
12
15
17
18namespace Tpetra {
19
20// Functor to create post-solve arrays
21template <class LocalLO_type, class ColIDView_type, class ColMapColor_type, class RowMapColor_type, class Counter_type>
22struct CreatePostSolveArraysFunctor {
23 LocalLO_type LocalRowIDofSingletonColData;
24 ColIDView_type ColSingletonRowLIDs;
25 ColIDView_type ColSingletonColLIDs;
26
27 LocalLO_type NewColProfilesData;
28 LocalLO_type ColProfilesData;
29 LocalLO_type ColHasRowWithSingletonData;
30 ColMapColor_type ColMapColors_Data;
31 RowMapColor_type RowMapColors_Data;
32 Counter_type numSingletonCols;
33
34 CreatePostSolveArraysFunctor(LocalLO_type localRowIDofSingletonColData,
35 ColIDView_type colSingletonRowLIDs, ColIDView_type colSingletonColLIDs,
36 LocalLO_type newColProfilesData, LocalLO_type colProfilesData, LocalLO_type colHasRowWithSingletonData,
37 ColMapColor_type colMapColors_Data, RowMapColor_type rowMapColors_Data, Counter_type in_numSingletonCols)
38 : LocalRowIDofSingletonColData(localRowIDofSingletonColData)
39 , ColSingletonRowLIDs(colSingletonRowLIDs)
40 , ColSingletonColLIDs(colSingletonColLIDs)
41 , NewColProfilesData(newColProfilesData)
42 , ColProfilesData(colProfilesData)
43 , ColHasRowWithSingletonData(colHasRowWithSingletonData)
44 , ColMapColors_Data(colMapColors_Data)
45 , RowMapColors_Data(rowMapColors_Data)
46 , numSingletonCols(in_numSingletonCols) {}
47
48 KOKKOS_INLINE_FUNCTION
49 void operator()(const size_t j) const {
50 int i = LocalRowIDofSingletonColData(j, 0);
51 if (ColProfilesData(j, 0) == 1 && RowMapColors_Data(i, 0) != 1) {
52 // These will be sorted by rowLIDs, so no need to be in any particular order
53 auto lclNumSingletonCols = Kokkos::atomic_fetch_add(&numSingletonCols(0), 1);
54 ColSingletonRowLIDs(lclNumSingletonCols) = i;
55 ColSingletonColLIDs(lclNumSingletonCols) = j;
56 }
57 // Also check for columns that were eliminated implicitly by
58 // having all associated row eliminated
59 else if (NewColProfilesData(j, 0) == 0 && ColHasRowWithSingletonData(j, 0) != 1 && RowMapColors_Data(i, 0) == 0) {
60 ColMapColors_Data(j, 0) = 1;
61 }
62 }
63};
64
65// Functors to construct or update reduced problem
66template <class LocalOrdinal, class GlobalOrdinal, class LocalMap_type, class LocalMatrix_type, class row_map_type, class entries_type, class values_type>
67struct ConstructReducedProblemFunctor {
68 LocalMap_type lclFullRowMap;
69 LocalMap_type lclFullColMap;
70 LocalMap_type lclReducedRowMap;
71 LocalMap_type lclReducedColMap;
72
73 LocalMatrix_type lclFullMatrix;
74
75 row_map_type lclReducedRowPtr;
76 entries_type lclReducedColInd;
77 values_type lclReducedValues;
78
79 // Constructor
80 ConstructReducedProblemFunctor(LocalMap_type lclFullRowMap_, LocalMap_type lclFullColMap_, LocalMap_type lclReducedRowMap_, LocalMap_type lclReducedColMap_,
81 LocalMatrix_type lclFullMatrix_, row_map_type lclReducedRowPtr_, entries_type lclReducedColInd_, values_type lclReducedValues_)
82 : lclFullRowMap(lclFullRowMap_)
83 , lclFullColMap(lclFullColMap_)
84 , lclReducedRowMap(lclReducedRowMap_)
85 , lclReducedColMap(lclReducedColMap_)
86 , lclFullMatrix(lclFullMatrix_)
87 , lclReducedRowPtr(lclReducedRowPtr_)
88 , lclReducedColInd(lclReducedColInd_)
89 , lclReducedValues(lclReducedValues_) {}
90
91 // Functor to count nonzero entries
92 KOKKOS_INLINE_FUNCTION
93 void operator()(const LocalOrdinal i, size_t& nnz) const {
94 const LocalOrdinal INVALID = Tpetra::Details::OrdinalTraits<LocalOrdinal>::invalid();
95
96 auto lclFullRowPtr = lclFullMatrix.graph.row_map;
97 auto lclFullColInd = lclFullMatrix.graph.entries;
98 auto lclFullValues = lclFullMatrix.values;
99
100 GlobalOrdinal glbRowID = lclFullRowMap.getGlobalElement(i);
101 LocalOrdinal lclRowID = lclReducedRowMap.getLocalElement(glbRowID);
102
103 if (lclRowID != INVALID) {
104 for (size_t j = lclFullRowPtr(i); j < lclFullRowPtr(i + 1); j++) {
105 GlobalOrdinal glbColID = lclFullColMap.getGlobalElement(lclFullColInd[j]);
106 LocalOrdinal lclColID = lclReducedColMap.getLocalElement(glbColID);
107 if (lclColID != INVALID) {
108 lclReducedRowPtr(1 + lclRowID)++;
109 }
110 }
111 }
112 nnz += lclReducedRowPtr(1 + lclRowID);
113 ;
114 }
115
116 // Functor to insert nonzero entries
117 KOKKOS_INLINE_FUNCTION
118 void operator()(const LocalOrdinal i) const {
119 const LocalOrdinal INVALID = Tpetra::Details::OrdinalTraits<LocalOrdinal>::invalid();
120
121 auto lclFullRowPtr = lclFullMatrix.graph.row_map;
122 auto lclFullColInd = lclFullMatrix.graph.entries;
123 auto lclFullValues = lclFullMatrix.values;
124
125 GlobalOrdinal glbRowID = lclFullRowMap.getGlobalElement(i);
126 LocalOrdinal lclRowID = lclReducedRowMap.getLocalElement(glbRowID);
127
128 if (lclRowID != INVALID) {
129 size_t k = lclReducedRowPtr(lclRowID);
130 for (size_t j = lclFullRowPtr(i); j < lclFullRowPtr(i + 1); j++) {
131 GlobalOrdinal glbColID = lclFullColMap.getGlobalElement(lclFullColInd[j]);
132 LocalOrdinal lclColID = lclReducedColMap.getLocalElement(glbColID);
133 if (lclColID != INVALID) {
134 lclReducedColInd[k] = lclColID;
135 lclReducedValues[k] = lclFullValues[j];
136 k++;
137 }
138 }
139 }
140 }
141};
142
143template <class LocalOrdinal, class GlobalOrdinal, class LocalMap_type, class LocalMatrix_type>
144struct UpdateReducedProblemFunctor {
145 LocalMap_type lclFullRowMap;
146 LocalMap_type lclFullColMap;
147 LocalMap_type lclReducedRowMap;
148 LocalMap_type lclReducedColMap;
149
150 LocalMatrix_type lclFullMatrix;
151 LocalMatrix_type lclReducedMatrix;
152
153 // Constructor
154 UpdateReducedProblemFunctor(LocalMap_type lclFullRowMap_, LocalMap_type lclFullColMap_,
155 LocalMap_type lclReducedRowMap_, LocalMap_type lclReducedColMap_,
156 LocalMatrix_type lclFullMatrix_, LocalMatrix_type lclReducedMatrix_)
157 : lclFullRowMap(lclFullRowMap_)
158 , lclFullColMap(lclFullColMap_)
159 , lclReducedRowMap(lclReducedRowMap_)
160 , lclReducedColMap(lclReducedColMap_)
161 , lclFullMatrix(lclFullMatrix_)
162 , lclReducedMatrix(lclReducedMatrix_) {}
163
164 // Functor to update nonzero entries
165 KOKKOS_INLINE_FUNCTION
166 void operator()(const LocalOrdinal i) const {
167 const LocalOrdinal INVALID = Tpetra::Details::OrdinalTraits<LocalOrdinal>::invalid();
168
169 auto lclFullRowPtr = lclFullMatrix.graph.row_map;
170 auto lclFullColInd = lclFullMatrix.graph.entries;
171 auto lclFullValues = lclFullMatrix.values;
172
173 auto lclReducedRowPtr = lclReducedMatrix.graph.row_map;
174 auto lclReducedColInd = lclReducedMatrix.graph.entries;
175 auto lclReducedValues = lclReducedMatrix.values;
176
177 GlobalOrdinal glbRowID = lclFullRowMap.getGlobalElement(i);
178 LocalOrdinal lclRowID = lclReducedRowMap.getLocalElement(glbRowID);
179
180 if (lclRowID != INVALID) {
181 for (size_t j = lclFullRowPtr(i); j < lclFullRowPtr(i + 1); j++) {
182 GlobalOrdinal glbColID = lclFullColMap.getGlobalElement(lclFullColInd(j));
183 LocalOrdinal lclColID = lclReducedColMap.getLocalElement(glbColID);
184 if (lclColID != INVALID) {
185 for (size_t k = lclReducedRowPtr(lclRowID); k < lclReducedRowPtr(lclRowID + 1); k++) {
186 if (lclReducedColInd[k] == lclColID) {
187 lclReducedValues[k] = lclFullValues[j];
188 }
189 }
190 }
191 }
192 }
193 }
194};
195
196// Functor to solve singleton problem
197template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node,
198 class LocalMap_type, class LocalMatrix_type, class LocalX_type, class LocalB_type,
199 class View_type_int, class View_type_scalar, class Err_type>
200struct SolveSingletonProblemFunctor {
201 using local_ordinal_type = LocalOrdinal;
203
204 local_ordinal_type NumVectors;
205 local_ordinal_type localNumSingletonCols;
206
207 Err_type error_code;
208 LocalMap_type lclFullRowMap;
209 LocalMap_type lclFullColMap;
210 LocalMap_type lclReducedRowMap;
211
212 LocalMatrix_type lclFullMatrix;
213
214 LocalX_type localExportX;
215 LocalB_type localRHS;
216
217 View_type_int ColSingletonColLIDs;
218 View_type_int ColSingletonRowLIDs;
219 View_type_int ColSingletonPivotLIDs;
220 View_type_scalar ColSingletonPivots;
221
222 // Constructor
223 SolveSingletonProblemFunctor(local_ordinal_type NumVectors_, local_ordinal_type localNumSingletonCols_, Err_type error_code_,
224 LocalMap_type lclFullRowMap_, LocalMap_type lclFullColMap_, LocalMap_type lclReducedRowMap_,
225 LocalMatrix_type lclFullMatrix_, LocalX_type localExportX_, LocalB_type localRHS_,
226 View_type_int colSingletonColLIDs_, View_type_int colSingletonRowLIDs_,
227 View_type_int colSingletonPivotLIDs_, View_type_scalar colSingletonPivots_)
228 : NumVectors(NumVectors_)
229 , localNumSingletonCols(localNumSingletonCols_)
230 , error_code(error_code_)
231 , lclFullRowMap(lclFullRowMap_)
232 , lclFullColMap(lclFullColMap_)
233 , lclReducedRowMap(lclReducedRowMap_)
234 , lclFullMatrix(lclFullMatrix_)
235 , localExportX(localExportX_)
236 , localRHS(localRHS_)
237 , ColSingletonColLIDs(colSingletonColLIDs_)
238 , ColSingletonRowLIDs(colSingletonRowLIDs_)
239 , ColSingletonPivotLIDs(colSingletonPivotLIDs_)
240 , ColSingletonPivots(colSingletonPivots_) {}
241
242 // Functor used in ConstructReducedProblem with parallel-for
243 KOKKOS_INLINE_FUNCTION
244 void operator()(const local_ordinal_type i) const {
245 const LocalOrdinal INVALID = Tpetra::Details::OrdinalTraits<LocalOrdinal>::invalid();
246 const impl_scalar_type zero(0);
247
248 auto lclFullRowPtr = lclFullMatrix.graph.row_map;
249 auto lclFullColInd = lclFullMatrix.graph.entries;
250 auto lclFullValues = lclFullMatrix.values;
251
252 GlobalOrdinal glbID = lclFullRowMap.getGlobalElement(i);
253 LocalOrdinal lclID = lclReducedRowMap.getLocalElement(glbID);
254
255 if (lclID == INVALID) {
256 if (lclFullRowPtr(i + 1) == lclFullRowPtr(i) + 1) {
257 // Singleton row
258 LocalOrdinal indX = lclFullColInd(lclFullRowPtr(i));
259 impl_scalar_type pivot = lclFullValues(lclFullRowPtr(i));
260 if (pivot == zero) {
261 Kokkos::atomic_exchange(&error_code(0), 1);
262 } else {
263 for (LocalOrdinal j = 0; j < NumVectors; j++) {
264 localExportX(indX, j) = localRHS(i, j) / pivot;
265 }
266 }
267 } else {
268 // Not singleton row, but need to be removed == Singleton column
269 // look for matching row ID
270 int myNum = -1;
271 for (local_ordinal_type j = 0; j < localNumSingletonCols; j++) {
272 if (ColSingletonRowLIDs[j] == i) {
273 myNum = j;
274 }
275 }
276 if (myNum == -1) {
277 Kokkos::atomic_exchange(&error_code(0), 3);
278 } else {
279 // look for matching col ID
280 LocalOrdinal targetCol = ColSingletonColLIDs[myNum];
281 for (size_t j = lclFullRowPtr(i); j < lclFullRowPtr(i + 1); j++) {
282 if (lclFullColMap.getGlobalElement(lclFullColInd[j]) == targetCol) {
283 impl_scalar_type pivot = lclFullValues[j];
284 if (pivot == zero) {
285 Kokkos::atomic_exchange(&error_code(0), 2);
286 } else {
287 ColSingletonPivotLIDs[myNum] = j;
288 ColSingletonPivots[myNum] = pivot;
289 }
290 break;
291 }
292 }
293 }
294 }
295 }
296 }
297
298 // Functor used in UpdateReducedProblem with parallel-reduce
299 KOKKOS_INLINE_FUNCTION
300 void operator()(const local_ordinal_type i, local_ordinal_type& lclNumSingletonCols) const {
301 const LocalOrdinal INVALID = Tpetra::Details::OrdinalTraits<LocalOrdinal>::invalid();
302 const impl_scalar_type zero(0);
303
304 auto lclFullRowPtr = lclFullMatrix.graph.row_map;
305 auto lclFullColInd = lclFullMatrix.graph.entries;
306 auto lclFullValues = lclFullMatrix.values;
307
308 GlobalOrdinal glbID = lclFullRowMap.getGlobalElement(i);
309 LocalOrdinal lclID = lclReducedRowMap.getLocalElement(glbID);
310
311 if (lclID == INVALID) {
312 if (lclFullRowPtr(i + 1) == lclFullRowPtr(i) + 1) {
313 // Singleton row
314 LocalOrdinal indX = lclFullColInd(lclFullRowPtr(i));
315 impl_scalar_type pivot = lclFullValues(lclFullRowPtr(i));
316 if (pivot == zero) {
317 Kokkos::atomic_exchange(&error_code(0), 1);
318 } else {
319 for (LocalOrdinal j = 0; j < NumVectors; j++) {
320 localExportX(indX, j) = localRHS(i, j) / pivot;
321 }
322 }
323 } else {
324 // Not singleton row, but need to be removed == Singleton column
325 // look for matching row ID
326 int myNum = -1;
327 for (local_ordinal_type j = 0; j < localNumSingletonCols; j++) {
328 if (ColSingletonRowLIDs[j] == i) {
329 myNum = j;
330 }
331 }
332 if (myNum == -1) {
333 Kokkos::atomic_exchange(&error_code(0), 3);
334 } else {
335 // look for matching col ID
336 LocalOrdinal targetCol = ColSingletonColLIDs[myNum];
337 for (size_t j = lclFullRowPtr(i); j < lclFullRowPtr(i + 1); j++) {
338 if (lclFullColMap.getGlobalElement(lclFullColInd[j]) == targetCol) {
339 impl_scalar_type pivot = lclFullValues[j];
340 if (pivot == zero) {
341 Kokkos::atomic_exchange(&error_code(0), 2);
342 } else {
343 ColSingletonPivots[myNum] = pivot;
344 }
345 break;
346 }
347 }
348 lclNumSingletonCols++;
349 }
350 }
351 }
352 }
353};
354
355// Functor to compute full singleton problem
356template <class View_type_int, class View_type_scalar, class SOL_type, class RHS_type>
357struct ComputeFullSolutionFunctor {
358 using scalar_type = typename View_type_scalar::non_const_value_type;
359
360 View_type_int ColSingletonRowLIDs;
361 View_type_int ColSingletonColLIDs;
362 View_type_scalar ColSingletonPivots;
363
364 SOL_type localX;
365 RHS_type localRHS;
366 RHS_type localB;
367
368 ComputeFullSolutionFunctor(View_type_int rowLIDs, View_type_int colLIDs, View_type_scalar pivots,
369 SOL_type X, RHS_type RHS, RHS_type B)
370 : ColSingletonRowLIDs(rowLIDs)
371 , ColSingletonColLIDs(colLIDs)
372 , ColSingletonPivots(pivots)
373 , localX(X)
374 , localRHS(RHS)
375 , localB(B) {}
376
377 KOKKOS_INLINE_FUNCTION
378 void operator()(const size_t k) const {
379 int i = ColSingletonRowLIDs[k];
380 int j = ColSingletonColLIDs[k];
381 scalar_type pivot = ColSingletonPivots[k];
382
383 size_t NumVectors = localX.extent(1);
384 for (size_t jj = 0; jj < NumVectors; jj++) {
385 localX(j, jj) = (localRHS(i, jj) - localB(i, jj)) / pivot;
386 }
387 }
388};
389
390template <class Map_color_type, class LocalMap_type, class IndexList_type>
391struct GenerateReducedMapFunctor {
392 int color;
393 Map_color_type mapColors;
394 LocalMap_type lclMap;
395 IndexList_type indexList;
396
397 // .. internal view used to count match ..
398 using device_type = typename Map_color_type::device_type;
399 Kokkos::View<size_t*, device_type> numView;
400
401 // constructor for counting
402 GenerateReducedMapFunctor(int color_, Map_color_type mapColors_)
403 : color(color_)
404 , mapColors(mapColors_) {}
405
406 // constructor for inserting
407 GenerateReducedMapFunctor(int color_, Map_color_type mapColors_, LocalMap_type lclMap_, IndexList_type indexList_)
408 : color(color_)
409 , mapColors(mapColors_)
410 , lclMap(lclMap_)
411 , indexList(indexList_) {
412 Kokkos::resize(numView, 1);
413 Kokkos::deep_copy(numView, 0);
414 }
415
416 // Functor to count number of elements with matching color (parallel-reduce)
417 KOKKOS_INLINE_FUNCTION
418 void operator()(const size_t i, size_t& num) const {
419 if (mapColors(i, 0) == color) {
420 num++;
421 }
422 }
423
424 // Functor to insert elements with matching color (parallel-for)
425 KOKKOS_INLINE_FUNCTION
426 void operator()(const size_t i) const {
427 if (mapColors(i, 0) == color) {
428 auto num = Kokkos::atomic_fetch_add(&numView(0), 1); // ..atomic to count..
429 indexList(num) = lclMap.getGlobalElement(i); // to global
430 }
431 }
432};
433
434//==============================================================================
435
436template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
439 : globalNumSingletonRows_(Teuchos::as<local_ordinal_type>(0))
440 , globalNumSingletonCols_(Teuchos::as<local_ordinal_type>(0))
441 , RatioOfDimensions_(0.0)
442 , RatioOfNonzeros_(0.0)
443 , HaveReducedProblem_(false)
444 , AnalysisDone_(false)
445 , SymmetricElimination_(true)
446 , localMaxNumRowEntries_(Teuchos::as<local_ordinal_type>(0))
447 , FullMatrixIsCrsMatrix_(false)
448 , run_on_host_(run_on_host)
449 , verbose_(verbose) {
450}
451
452//==============================================================================
453
454template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
455typename CrsSingletonFilter_LinearProblem<Scalar, LocalOrdinal, GlobalOrdinal, Node>::NewType
457operator()(const CrsSingletonFilter_LinearProblem::OriginalType& originalLinearProblem) {
458 analyze(originalLinearProblem);
459 return construct();
460}
461
462//==============================================================================
463
464template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
466 analyze(const CrsSingletonFilter_LinearProblem::OriginalType& originalLinearProblem) {
467 this->origObj_ = originalLinearProblem;
468
469 FullMatrix_ = originalLinearProblem->getMatrix();
470
471 Analyze(FullMatrix());
472
473 if (verbose_ && FullMatrix()->getComm()->getRank() == 0) {
474 std::cout << "\nAnalyzed Singleton Problem:\n";
475 std::cout << "---------------------------\n";
476 }
477 if (SingletonsDetected()) {
478 if (verbose_ && FullMatrix()->getComm()->getRank() == 0) {
479 std::cout << "Singletons Detected!" << std::endl;
480 std::cout << "Num Singletons: " << NumSingletons() << std::endl;
481 }
482 } else {
483 if (verbose_ && FullMatrix()->getComm()->getRank() == 0)
484 std::cout << "No Singletons Detected!" << std::endl;
485 }
486
487 if (verbose_ && FullMatrix()->getComm()->getRank() == 0)
488 std::cout << "---------------------------\n\n";
489
490 return;
491}
492
493//==============================================================================
494
495template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
496typename CrsSingletonFilter_LinearProblem<Scalar, LocalOrdinal, GlobalOrdinal, Node>::NewType
498 construct() {
499 const char tfecfFuncName[] = "construct: ";
500
501 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(this->origObj_ == Teuchos::null, std::runtime_error,
502 "Linear problem (orgiObj_) have not been set. Call analyze() first.");
503
504 ConstructReducedProblem(this->origObj_);
505
506 this->newObj_ = ReducedProblem();
507
508 if (verbose_ && SingletonsDetected() && FullMatrix()->getComm()->getRank() == 0) {
509 std::cout << "\nConstructedSingleton Problem:\n";
510 std::cout << "---------------------------\n";
511 std::cout << "RatioOfDimensions: " << RatioOfDimensions() << std::endl;
512 std::cout << "RatioOfNonzeros: " << RatioOfNonzeros() << std::endl;
513 std::cout << "---------------------------\n\n";
514 }
515
516 return this->newObj_;
517}
518
519//==============================================================================
520
521template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
523 fwd() {
524 UpdateReducedProblem(FullProblem());
525}
526
527//==============================================================================
528
529template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
534
535//==============================================================================
536
537template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
539 Analyze(const Teuchos::RCP<row_matrix_type>& fullMatrix) {
540 const char tfecfFuncName[] = "Analyze: ";
541
542 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(AnalysisDone_, std::runtime_error,
543 "Analyze() already done once. Cannot do it again.");
544
546 std::runtime_error, "Input matrix is Teuchos::null.");
547
548 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(fullMatrix->getGlobalNumRows() == 0,
549 std::runtime_error, "Full matrix has zero dimension.");
550
551 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(fullMatrix->getGlobalNumEntries() == 0,
552 std::runtime_error, "Full matrix has no nonzero terms.");
553
554 FullMatrix_ = fullMatrix;
555
556 // First check for columns with single entries and find columns with singleton rows
557 vector_type_int ColProfiles(FullMatrixColMap());
558 ColProfiles.putScalar(0);
559 vector_type_int ColHasRowWithSingleton(FullMatrixColMap());
560 ColHasRowWithSingleton.putScalar(0);
561
562 // localRowIDofSingletonCol[j] will contain the local row ID associated with the jth column,
563 // if the jth col has a single entry
564 vector_type_LO localRowIDofSingletonCol(FullMatrixColMap());
565 localRowIDofSingletonCol.putScalar(Teuchos::OrdinalTraits<LocalOrdinal>::invalid());
566
567 // Define MapColoring objects
568 RowMapColors_ = Teuchos::rcp(new vector_type_int(FullMatrixRowMap())); // Initial colors are all 0
569 ColMapColors_ = Teuchos::rcp(new vector_type_int(FullMatrixColMap()));
570
571 local_ordinal_type localNumRows = FullMatrix()->getLocalNumRows();
572 local_ordinal_type localNumCols = FullMatrix()->getLocalNumCols();
573
574 // Set up for accessing full matrix. Will do so row-by-row.
575 InitFullMatrixAccess();
576
577 // Scan matrix for singleton rows, build up column profiles
578 localNumSingletonRows_ = 0;
579 if (run_on_host_) {
580 size_t NumIndices = 1;
581 // int * localIndices;
582 // nonconst_local_inds_host_view_type localIndices;
583 Teuchos::Array<local_ordinal_type> localIndices;
584 auto ColProfilesData = ColProfiles.getLocalViewHost(Tpetra::Access::ReadWrite);
585 auto localRowIDofSingletonColData = localRowIDofSingletonCol.getLocalViewHost(Tpetra::Access::ReadWrite);
586 auto ColHasRowWithSingletonData = ColHasRowWithSingleton.getLocalViewHost(Tpetra::Access::ReadWrite);
587 auto RowMapColors_Data = RowMapColors_->getLocalViewHost(Tpetra::Access::ReadWrite);
588 auto ColMapColors_Data = ColMapColors_->getLocalViewHost(Tpetra::Access::ReadWrite);
589
590 for (int i = 0; i < localNumRows; i++) {
591 // Get ith row
592 GetRow(i, NumIndices, localIndices);
593 for (size_t j = 0; j < NumIndices; j++) {
594 local_ordinal_type ColumnIndex = localIndices[j];
595
596 // Bounds check for ColumnIndex
597 if (static_cast<size_t>(ColumnIndex) >= ColProfilesData.extent(0)) {
598 std::cout << "Error: ColumnIndex out of bounds: " << ColumnIndex << std::endl;
599 std::abort();
600 }
601
602 ColProfilesData(ColumnIndex, 0)++; // Increment column count
603
604 if (static_cast<size_t>(ColumnIndex) >= localRowIDofSingletonColData.extent(0)) {
605 std::cout << "Error: ColumnIndex out of bounds for localRowIDofSingletonColData: "
606 << ColumnIndex << std::endl;
607 std::abort();
608 }
609
610 // Record local row ID for current column
611 // will use to identify row to eliminate if column is a singleton
613 }
614 // If row has single entry, color it and associated column with color=1
615 if (NumIndices == 1) {
616 int j2 = localIndices[0];
617 ColHasRowWithSingletonData(j2, 0)++;
618 RowMapColors_Data(i, 0) = 1;
619 ColMapColors_Data(j2, 0) = 1;
620 localNumSingletonRows_++;
621 }
622 }
623 } else {
624 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!FullMatrixIsCrsMatrix_, std::runtime_error,
625 "Error: FullMatrix is not CrsMatrix");
626
627 auto ColProfilesData = ColProfiles.getLocalViewDevice(Tpetra::Access::ReadWrite);
628 auto localRowIDofSingletonColData = localRowIDofSingletonCol.getLocalViewDevice(Tpetra::Access::ReadWrite);
629 auto ColHasRowWithSingletonData = ColHasRowWithSingleton.getLocalViewDevice(Tpetra::Access::ReadWrite);
630 auto RowMapColors_Data = RowMapColors_->getLocalViewDevice(Tpetra::Access::ReadWrite);
631 auto ColMapColors_Data = ColMapColors_->getLocalViewDevice(Tpetra::Access::ReadWrite);
632
633 Kokkos::View<int*, execution_space, Kokkos::MemoryTraits<Kokkos::Unmanaged | Kokkos::Atomic>>
634 ColProfilesAtomic(ColProfilesData.data(), ColProfilesData.extent(0));
635 Kokkos::View<int*, execution_space, Kokkos::MemoryTraits<Kokkos::Unmanaged | Kokkos::Atomic>>
636 ColHasRowWithSingletonAtomic(ColHasRowWithSingletonData.data(), ColHasRowWithSingletonData.extent(0));
637 Kokkos::View<int*, execution_space, Kokkos::MemoryTraits<Kokkos::Unmanaged | Kokkos::Atomic>>
638 ColMapColors_Atomic(ColMapColors_Data.data(), ColMapColors_Data.extent(0));
639 Kokkos::View<int*, execution_space> error_code("singleton-bound-check", 2);
640 Kokkos::deep_copy(error_code, 0);
641
642 auto lclRowPtr = FullCrsMatrix_->getLocalRowPtrsDevice();
643 auto lclColInd = FullCrsMatrix_->getLocalIndicesDevice();
644
645 Kokkos::parallel_reduce(
646 "CrsSingletonFilter_LinearProblem:Analyze(find-singleton-row)", range_policy(0, localNumRows),
647 KOKKOS_LAMBDA(const size_t i, local_ordinal_type& lclNumSingletonRows) {
648 // Get ith row
649 for (size_t k = lclRowPtr(i); k < lclRowPtr(i + 1); k++) {
650 local_ordinal_type ColumnIndex = lclColInd(k);
651
652 // Bounds check for ColumnIndex
653 if (static_cast<size_t>(ColumnIndex) >= ColProfilesData.extent(0)) {
654 Kokkos::atomic_exchange(&error_code(0), ColumnIndex + 1);
655 } else {
656 ColProfilesAtomic(ColumnIndex)++; // Increment column count
657 }
658
659 // Bounds check for ColumnIndex
660 if (static_cast<size_t>(ColumnIndex) >= localRowIDofSingletonColData.extent(0)) {
661 Kokkos::atomic_exchange(&error_code(1), ColumnIndex + 1);
662 } else {
663 // Record local row ID for current column
664 // will use to identify row to eliminate if column is a singleton
665 // It will store the last local row index where this column has non-zero entry
666 // (it may not be singleton row?)
667 Kokkos::atomic_max(&localRowIDofSingletonColData(ColumnIndex, 0), i);
668 }
669 }
670 // If row has single entry, color it and associated column with color=1
671 if (lclRowPtr(i + 1) == lclRowPtr(i) + 1) {
672 local_ordinal_type ColumnIndex = lclColInd(lclRowPtr(i));
673 RowMapColors_Data(i, 0) = 1;
674
675 // this column will be eliminated by this singleton row
679 }
680 },
681 localNumSingletonRows_);
682
683 // Check error code for bound check
684 // NOTE: Should I broadcast the error-code?
685 auto h_error = Kokkos::create_mirror_view(error_code);
686 Kokkos::deep_copy(h_error, error_code);
687 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(h_error(0) > 0, std::runtime_error,
688 "Error: ColumnIndex out of bounds: " + std::to_string(h_error(0)));
689 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(h_error(1) > 0, std::runtime_error,
690 "Error: ColumnIndex out of bounds for localRowIDofSingletonColData " + std::to_string(h_error(1)));
691 }
692
693 // 1) The vector ColProfiles has column nonzero counts for each processor's contribution
694 // Combine these to get total column profile information and then redistribute to processors
695 // so each can determine if it is the owner of the row associated with the singleton column
696 // 2) The vector ColHasRowWithSingleton[i] contain count of singleton rows that are associated with
697 // the ith column on this processor. Must tell other processors that they should also eliminate
698 // these columns.
699
700 // Make a copy of ColProfiles for later use when detecting columns that disappear locally
702 NewColProfiles.update(1.0, ColProfiles, 0.0);
703
704 // If importer is non-trivial, we need to perform a gather/scatter to accumulate results
705 auto importer = FullCrsMatrix()->getCrsGraph()->getImporter();
706 if (importer != Teuchos::null) {
707 vector_type_int tmpVec(FullMatrixDomainMap()); // Use for gather/scatter of column vectors
708 tmpVec.putScalar(0);
709 tmpVec.doExport(ColProfiles, *importer, ADD);
710 ColProfiles.doImport(tmpVec, *importer, INSERT);
711
712 tmpVec.putScalar(0);
715 }
716
717 // ColProfiles now contains the nonzero column entry count for all columns that have
718 // an entry on this processor.
719 // ColHasRowWithSingleton now contains a count of singleton rows associated with the corresponding
720 // local column.
721
722 // Next we check to make sure no column is associated with more than one singleton row.
723 // If multiple singleton rows have nonzero entries at the same column, the matrix is singular?
724 // [0 0 0 0 0 x 0 0]
725 // [0 0 0 0 0 x 0 0]
726 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(ColHasRowWithSingleton.normInf() > 1, std::runtime_error,
727 "At least one column is associated with two singleton rows, can't handle it.");
728
729 localNumSingletonCols_ = 0;
730 vector_type_int RowHasColWithSingleton(FullMatrix()->getRowMap()); // Use to check for errors
731 RowHasColWithSingleton.putScalar(0);
732 if (run_on_host_) {
733 size_t NumIndices = 1;
734 // int * localIndices;
735 // nonconst_local_inds_host_view_type localIndices;
736 Teuchos::Array<local_ordinal_type> localIndices;
737
738 auto ColProfilesData = ColProfiles.getLocalViewHost(Tpetra::Access::ReadOnly);
739 auto localRowIDofSingletonColData = localRowIDofSingletonCol.getLocalViewHost(Tpetra::Access::ReadOnly);
740 auto ColHasRowWithSingletonData = ColHasRowWithSingleton.getLocalViewHost(Tpetra::Access::ReadOnly);
741
742 auto RowMapColors_Data = RowMapColors_->getLocalViewHost(Tpetra::Access::ReadWrite);
743 auto ColMapColors_Data = ColMapColors_->getLocalViewHost(Tpetra::Access::ReadWrite);
744
745 auto NewColProfilesData = NewColProfiles.getLocalViewHost(Tpetra::Access::ReadWrite);
746 auto RowHasColWithSingletonData = RowHasColWithSingleton.getLocalViewHost(Tpetra::Access::ReadWrite);
747
748 // Count singleton columns (that were not already counted as singleton rows)
749 for (local_ordinal_type j = 0; j < localNumCols; j++) {
750 local_ordinal_type i2 = localRowIDofSingletonColData(j, 0);
751 // Check if column is a singleton
752 if (ColProfilesData(j, 0) == 1) {
753 // Check to see if this column already eliminated by the row check above
754 if (RowMapColors_Data(i2, 0) != 1) {
755 RowHasColWithSingletonData(i2, 0)++; // Increment col singleton counter for ith row
756 RowMapColors_Data(i2, 0) = 2; // Use 2 for now, to distinguish between row eliminated directly or via column singletons
757 ColMapColors_Data(j, 0) = 1;
758 localNumSingletonCols_++;
759 // If we delete a row, we need to keep track of associated column entries that were also deleted
760 // in case all entries in a column are eventually deleted, in which case the column should
761 // also be deleted.
762 GetRow(i2, NumIndices, localIndices);
763 for (size_t jj = 0; jj < NumIndices; jj++) {
764 NewColProfilesData(localIndices[jj], 0)--;
765 }
766 }
767 }
768 // Check if some other processor eliminated this column
769 else if (ColHasRowWithSingletonData(j, 0) == 1 && RowMapColors_Data(i2, 0) != 1) {
770 ColMapColors_Data(j, 0) = 1;
771 }
772 }
773 } else {
774 auto ColProfilesData = ColProfiles.getLocalViewDevice(Tpetra::Access::ReadOnly);
775 auto localRowIDofSingletonColData = localRowIDofSingletonCol.getLocalViewDevice(Tpetra::Access::ReadOnly);
776 auto ColHasRowWithSingletonData = ColHasRowWithSingleton.getLocalViewDevice(Tpetra::Access::ReadOnly);
777
778 auto RowMapColors_Data = RowMapColors_->getLocalViewDevice(Tpetra::Access::ReadWrite);
779 auto ColMapColors_Data = ColMapColors_->getLocalViewDevice(Tpetra::Access::ReadWrite);
780
781 auto NewColProfilesData = NewColProfiles.getLocalViewDevice(Tpetra::Access::ReadWrite);
782 auto RowHasColWithSingletonData = RowHasColWithSingleton.getLocalViewDevice(Tpetra::Access::ReadWrite);
783
784 auto lclRowPtr = FullCrsMatrix_->getLocalRowPtrsDevice();
785 auto lclColInd = FullCrsMatrix_->getLocalIndicesDevice();
786
787 // Count singleton columns (that were not already counted as singleton rows)
788 Kokkos::parallel_reduce(
789 "CrsSingletonFilter_LinearProblem:Analyze(find-singleton-column)", range_policy(0, localNumCols),
790 KOKKOS_LAMBDA(const size_t j, local_ordinal_type& lclNumSingletonCols) {
791 // Check if column is a singleton
792 if (ColProfilesData(j, 0) == 1) {
793 // i = id of "last" local row with non-zero in this col
794 // since this is singletone column , "i" is the row id of the single nonzero entry in this column
795 local_ordinal_type i = localRowIDofSingletonColData(j, 0);
796
797 // Check to see if this column already eliminated by the row check above
798 // RowMapColors(i,0) : 0 = ith row was not singleton, 1 = was singleton, 2 = was not singleton, but processed
799 // Multiple singleton columns cannot have nonzero entry in the same row, i
800 // Otherwise, the matrix will be singular
801 // So, rowMapColors(i,0) should be checked by one singleton column (note 1, also see check 2)
802 // not atomic needed
803 if (RowMapColors_Data(i, 0) != 1) { // that row is not singleton, and hence this col has not been removed
804 RowMapColors_Data(i, 0) = 2;
805 ColMapColors_Data(j, 0) = 1;
807
808 // Increment col singleton counter for ith row (Only one j should update (i) because of "note 1")
810
811 // If we delete a row, we need to keep track of associated column entries that were also deleted
812 // in case all entries in a column are eventually deleted, in which case the column should
813 // also be deleted.
814 // Only one j should execute this loop for a specific "i" (because of "note 1")
815 for (size_t k = lclRowPtr(i); k < lclRowPtr(i + 1); k++) {
816 NewColProfilesData(lclColInd(k), 0)--;
817 }
818 }
819 }
820 },
821 localNumSingletonCols_);
822 Kokkos::parallel_for(
823 "CrsSingletonFilter_LinearProblem:Analyze(find-non-singleton-column)", range_policy(0, localNumCols),
824 KOKKOS_LAMBDA(const size_t j) {
825 // Check if column is *NOT* a singleton
826 if (ColProfilesData(j, 0) != 1) {
827 // Since this is not singletone col,
828 // "i" is the "last" local row with non-zero in this col and may not corresponds to the singleton row?
829 local_ordinal_type i = localRowIDofSingletonColData(j, 0);
830
831 // Check if some other processor has "previously" eliminated this column
832 // (note: multiple singleton columns cannot have nonzero in the same column, colhasrowwithsingleton(j) is 0 or 1)
833 // This column has "one" row, which is singleton
834 // that singleton row has the single nonzero entry at jth col
835 // At jth iteration, if RowMapColors_Data(i, 0) == 1, then it stays as 1
836 // otherwise it has been processed into 2 by previous column, or still 0
837 if (ColHasRowWithSingletonData(j, 0) == 1 && RowMapColors_Data(i, 0) != 1) {
838 // RowMapColors_Data(i, 0) was 0, or processed by a previous column
839 // TODO: check the second and third conditions
840 ColMapColors_Data(j, 0) = 1;
841 }
842 }
843 });
844 }
845
846 // Next we check to make sure no row is associated with more than one singleton col (check 2)
847 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(RowHasColWithSingleton.normInf() > 1, std::runtime_error,
848 "At least one row is associated with two singleton columns, can't handle it.");
849
850 // Generate arrays that keep track of column singleton row, col and pivot info needed for post-solve phase
852 if (run_on_host_) {
853 auto RowMapColors_Data = RowMapColors_->getLocalViewHost(Tpetra::Access::ReadWrite);
854 for (local_ordinal_type i = 0; i < localNumRows; i++) {
855 if (RowMapColors_Data(i, 0) == 2) RowMapColors_Data(i, 0) = 1; // Convert all eliminated rows to same color
856 }
857 } else {
858 auto RowMapColors_Data = RowMapColors_->getLocalViewDevice(Tpetra::Access::ReadWrite);
859 Kokkos::parallel_for(
860 "CrsSingletonFilter_LinearProblem:Analyze(convert-rowmap-color)", range_policy(0, localNumRows),
861 KOKKOS_LAMBDA(const size_t i) {
862 if (RowMapColors_Data(i, 0) == 2) RowMapColors_Data(i, 0) = 1; // Convert all eliminated rows to same color
863 });
864 }
865
866 const Teuchos::Ptr<local_ordinal_type> gRowsPtr(&globalNumSingletonRows_);
867 const Teuchos::Ptr<local_ordinal_type> gColsPtr(&globalNumSingletonCols_);
868 auto rowComm = FullMatrix()->getRowMap()->getComm();
869 Teuchos::reduceAll<local_ordinal_type, local_ordinal_type>(*rowComm, Teuchos::REDUCE_SUM, localNumSingletonRows_, gRowsPtr);
870 Teuchos::reduceAll<local_ordinal_type, local_ordinal_type>(*rowComm, Teuchos::REDUCE_SUM, localNumSingletonCols_, gColsPtr);
871
872 AnalysisDone_ = true;
873 return;
874}
875
876//==============================================================================
877
878template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
879Teuchos::RCP<const Tpetra::Map<LocalOrdinal, GlobalOrdinal, Node>>
881 GenerateReducedMap(const Teuchos::RCP<const map_type>& originalMap,
882 const Teuchos::RCP<vector_type_int>& mapColors, int color,
883 bool locally_sort_gids) {
884 Teuchos::RCP<const map_type> reducedMap;
885 bool canRunOnHost = std::is_same_v<typename device_type::memory_space, Kokkos::HostSpace>;
886 if (run_on_host_ && canRunOnHost) {
887 // Vector to hold the reduced global indices
888 std::vector<GlobalOrdinal> myReducedGlobalIndices;
889
890 // Get the global IDs owned by the calling process
891 auto myGlobalIndices = originalMap->getMyGlobalIndices();
892
893 // Iterate through the global indices to find the ones that match the color
894 for (size_t i = 0; i < myGlobalIndices.size(); i++) {
895 // Access the value in mapColors using the appropriate method
896 int colorValue = mapColors->getData()[i]; // Use getData() to access the vector data
897 if (colorValue == color) {
899 }
900 }
901
902 // Create the reduced map using the collected indices
903 reducedMap = createNonContigMapWithNode<LocalOrdinal, GlobalOrdinal, Node>(
904 Teuchos::ArrayView<const GlobalOrdinal>(myReducedGlobalIndices.data(), myReducedGlobalIndices.size()),
905 originalMap->getComm());
906 } else {
907 // Iterate through the global indices to find the ones that match the color
908 using IndexListType = Kokkos::View<GlobalOrdinal*, device_type>;
909 using functor_type = GenerateReducedMapFunctor<local_vector_int_type, local_map_type, IndexListType>;
910 auto mapColors_Data = mapColors->getLocalViewDevice(Tpetra::Access::ReadWrite);
911
912 // count number of elements with the matching color
913 size_t lclNumReducedElements = 0;
914 size_t lclNumElements = originalMap->getLocalNumElements();
915 {
916 functor_type functor(color, mapColors_Data);
917 Kokkos::parallel_reduce(
918 "CrsSingletonFilter_LinearProblem:GenerateReduceMap(count)", range_policy(0, lclNumElements),
919 functor, lclNumReducedElements);
920 }
921 // create the list of elements with the matching color
922 IndexListType indexList;
923 {
924 auto lclMap = originalMap()->getLocalMap();
925 Kokkos::resize(indexList, lclNumReducedElements);
926 functor_type functor(color, mapColors_Data, lclMap, indexList);
927 Kokkos::parallel_for(
928 "CrsSingletonFilter_LinearProblem:GenerateReduceMap(insert)", range_policy(0, lclNumElements),
929 functor);
930 }
931 if (locally_sort_gids) {
932 // locally-sort the matched GIDs
933 Kokkos::sort(execution_space(), indexList);
934 }
935
936 // Create the reduced map using the collected indices
937 const Tpetra::global_size_t INVALID = Tpetra::Details::OrdinalTraits<Tpetra::global_size_t>::invalid();
938 reducedMap = Teuchos::rcp(new map_type(INVALID, indexList,
939 originalMap->getIndexBase(),
940 originalMap->getComm()));
941 }
942
943 return reducedMap;
944}
945
946//==============================================================================
947
948template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
950 ConstructReducedProblem(const Teuchos::RCP<linear_problem_type>& Problem) {
951 const char tfecfFuncName[] = "ConstructReducedProblem: ";
952
953 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(HaveReducedProblem_, std::runtime_error,
954 "Already have a reduced problem. Cannot do it again.");
955
957 std::runtime_error, "Problem is Teuchos::null.");
958
959 FullProblem_ = Problem;
960 FullMatrix_ = Teuchos::rcp_dynamic_cast<row_matrix_type>(Problem->getMatrix());
961 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(FullMatrix() == Teuchos::null,
962 std::runtime_error, "Need a RowMatrix.");
963 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(Problem->getRHS() == Teuchos::null,
964 std::runtime_error, "Need a RHS.");
965 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(Problem->getLHS() == Teuchos::null,
966 std::runtime_error, "Need a LHS.");
967
968 Scalar zero = Teuchos::ScalarTraits<Scalar>::zero();
969
970 // Generate reduced row and column maps
971 if (SingletonsDetected()) {
972 ReducedMatrixRowMap_ = GenerateReducedMap(FullMatrixRowMap(), RowMapColors_, 0);
973 ReducedMatrixColMap_ = GenerateReducedMap(FullMatrixColMap(), ColMapColors_, 0);
974
975 // Create domain and range map colorings by exporting map coloring of column and row maps
976 auto importer = FullCrsMatrix()->getCrsGraph()->getImporter();
977 if (importer != Teuchos::null) {
978 Teuchos::RCP<vector_type_int> DomainMapColors = Teuchos::rcp(new vector_type_int(FullMatrixDomainMap()));
979 DomainMapColors->doExport(*ColMapColors_, *importer, Tpetra::ABSMAX);
980 OrigReducedMatrixDomainMap_ = GenerateReducedMap(FullMatrixDomainMap(), DomainMapColors, 0);
981 } else {
982 OrigReducedMatrixDomainMap_ = ReducedMatrixColMap();
983 }
984
985 auto exporter = FullCrsMatrix()->getCrsGraph()->getImporter();
986 if (FullMatrixIsCrsMatrix_) {
987 if (exporter != Teuchos::null) { // Non-trivial exporter
988 Teuchos::RCP<vector_type_int> RangeMapColors = Teuchos::rcp(new vector_type_int(FullMatrixRangeMap()));
989 RangeMapColors->doExport(*ColMapColors_, *exporter, Tpetra::ABSMAX);
990 ReducedMatrixRangeMap_ = GenerateReducedMap(FullMatrixRangeMap(), RangeMapColors, 0);
991 } else
992 ReducedMatrixRangeMap_ = ReducedMatrixRowMap();
993 } else
994 ReducedMatrixRangeMap_ = ReducedMatrixRowMap();
995
996 // Check to see if the reduced system domain and range maps are the same.
997 // If not, we need to remap entries of the LHS multivector so that they are distributed
998 // conformally with the rows of the reduced matrix and the RHS multivector
999 SymmetricElimination_ = ReducedMatrixRangeMap()->isSameAs(*OrigReducedMatrixDomainMap_);
1000 if (!SymmetricElimination_) {
1001 ConstructRedistributeExporter(OrigReducedMatrixDomainMap_, ReducedMatrixRangeMap_,
1002 RedistributeDomainExporter_, ReducedMatrixDomainMap_);
1003 } else {
1004 ReducedMatrixDomainMap_ = OrigReducedMatrixDomainMap_;
1005 OrigReducedMatrixDomainMap_ = Teuchos::null;
1006 RedistributeDomainExporter_ = Teuchos::null;
1007 }
1008
1009 // Create pointer to Full RHS, LHS
1010 Teuchos::RCP<multivector_type> FullRHS = FullProblem()->getRHS();
1011 Teuchos::RCP<multivector_type> FullLHS = FullProblem()->getLHS();
1012 local_ordinal_type NumVectors = FullLHS->getNumVectors();
1013
1014 // Create importers
1015 Full2ReducedLHSImporter_ = Teuchos::rcp(new import_type(FullMatrixDomainMap(), ReducedMatrixDomainMap()));
1016 Full2ReducedRHSImporter_ = Teuchos::rcp(new import_type(FullRHS->getMap(), ReducedMatrixRowMap()));
1017
1018 // Create storage for temporary X values due to explicit elimination of rows
1019 tempExportX_ = Teuchos::rcp(new multivector_type(FullMatrixColMap(), NumVectors));
1020
1021 size_t NumEntries = 0;
1022 Teuchos::ArrayView<const Scalar> Values;
1023 Teuchos::Array<GlobalOrdinal> Indices;
1024 LocalOrdinal localNumRows = FullMatrix()->getLocalNumRows();
1026
1027 // Check if ColSingletonPivotLIDs_, ColSingletonPivot_ can be accessed on host
1028 bool canRunOnHost = std::is_same_v<typename device_type::memory_space, Kokkos::HostSpace>;
1029 if (run_on_host_ && canRunOnHost) {
1030 // Construct Reduced Matrix
1031 LocalOrdinal maxNumEntries = FullCrsMatrix()->getLocalMaxNumRowEntries();
1032 ReducedMatrix_ = Teuchos::rcp(new crs_matrix_type(ReducedMatrixRowMap(), ReducedMatrixColMap(), maxNumEntries));
1033
1034 for (LocalOrdinal i = 0; i < localNumRows; i++) {
1035 GlobalOrdinal curGRID = FullMatrixRowMap()->getGlobalElement(i);
1036 if (ReducedMatrixRowMap()->isNodeGlobalElement(curGRID)) { // Check if this row should go into reduced matrix
1037 GetRowGCIDs(i, NumEntries, Values, Indices); // Get current row (Indices are global)
1038
1039 // The ReducedMatrix has the row and column indices already removed so cannot "insert" them.
1040 // Need to filter them to only insert remaining.
1041 // Filter indices and values
1042 Teuchos::Array<GlobalOrdinal> filteredIndices;
1043 Teuchos::Array<Scalar> filteredValues;
1044
1045 for (typename Teuchos::Array<GlobalOrdinal>::size_type j = 0; j < Indices.size(); ++j) {
1046 if (ReducedMatrixColMap()->isNodeGlobalElement(Indices[j])) {
1047 filteredIndices.push_back(Indices[j]);
1048 filteredValues.push_back(Values[j]);
1049 }
1050 }
1051
1052 // Insert filtered values into the matrix
1053 if (!filteredIndices.empty()) {
1054 ReducedMatrix()->insertGlobalValues(curGRID, filteredIndices(), filteredValues());
1055 }
1056
1057 } else {
1058 Teuchos::ArrayView<const LocalOrdinal> localIndices;
1059 GetRow(i, NumEntries, Values, localIndices); // Get current row
1060 if (NumEntries == 1) {
1061 Scalar pivot = Values[0];
1062 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(pivot == zero, std::runtime_error,
1063 "Encountered zero row, unable to continue."); // Should improve this comparison to zero.
1065 for (LocalOrdinal j = 0; j < NumVectors; j++) {
1066 auto rhsData = FullRHS->getData(j); // Get the underlying data for vector j
1067 auto exportData = tempExportX_->getDataNonConst(j); // Get the underlying data for vector j (non-const)
1069 }
1070 } else { // Singleton column
1071 LocalOrdinal targetCol = ColSingletonColLIDs_[ColSingletonCounter];
1072 for (size_t j = 0; j < NumEntries; j++) {
1073 if (localIndices[j] == targetCol) {
1074 Scalar pivot = Values[j];
1075 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(pivot == zero, std::runtime_error,
1076 "Encountered zero column, unable to continue."); // Should improve this comparison to zero.
1077 ColSingletonPivotLIDs_[ColSingletonCounter] = j;
1078 ColSingletonPivots_[ColSingletonCounter] = pivot;
1080 break;
1081 }
1082 }
1083 }
1084 }
1085 }
1086
1087 // Now convert to local indexing. We have constructed things so that the domain and range of the
1088 // matrix will have the same map. If the reduced matrix domain and range maps were not the same, the
1089 // differences were addressed in the ConstructRedistributeExporter() method
1090 ReducedMatrix()->fillComplete(ReducedMatrixDomainMap(), ReducedMatrixRangeMap());
1091 } else {
1092 // The ReducedMatrix has the row and column indices already removed so cannot "insert" them.
1093 // Need to filter them to only insert remaining.
1094 // Filter indices and values
1095
1096 // Construct the reduced matrix
1097 // * Only the maps have been setup, and hence the rowptr, colind, nzvals need to be still allocated.
1098 {
1099 using graph_type = typename local_matrix_type::StaticCrsGraphType;
1100 using row_map_type = typename graph_type::row_map_type::non_const_type;
1101 using entries_type = typename graph_type::entries_type::non_const_type;
1102 using values_type = typename local_matrix_type::values_type;
1104
1105 size_t nnzA = 1;
1106 auto lclFullRowMap = FullMatrixRowMap()->getLocalMap();
1107 auto lclFullColMap = FullMatrixColMap()->getLocalMap();
1108 auto lclReducedRowMap = ReducedMatrixRowMap()->getLocalMap();
1109 auto lclReducedColMap = ReducedMatrixColMap()->getLocalMap();
1110
1111 auto lclFullMatrix = FullCrsMatrix_->getLocalMatrixDevice();
1112
1113 LocalOrdinal localNumReducedRows = ReducedMatrixRowMap()->getLocalNumElements();
1114 row_map_type rowmap_view("rowmap_view", localNumReducedRows + 1);
1115 entries_type column_view("colind_view", nnzA);
1116 values_type values_view("values_view", nnzA);
1117
1118 // launch functor to count nnz per row
1119 {
1120 Kokkos::deep_copy(rowmap_view, 0);
1121
1122 functor_type functor(lclFullRowMap, lclFullColMap, lclReducedRowMap, lclReducedColMap,
1123 lclFullMatrix, rowmap_view, column_view, values_view);
1124 Kokkos::parallel_reduce(
1125 "CrsSingletonFilter_LinearProblem:CountReducedProblem", range_policy(0, localNumRows),
1126 functor, nnzA);
1127 }
1128
1129 // convert to rowptrs
1130 KokkosKernels::Impl::kk_inclusive_parallel_prefix_sum<execution_space>(1 + localNumReducedRows, rowmap_view);
1131
1132 // insert non-zero entries
1133 {
1134 Kokkos::resize(column_view, nnzA);
1135 Kokkos::resize(values_view, nnzA);
1136
1137 // functor with new view sizes
1138 functor_type functor(lclFullRowMap, lclFullColMap, lclReducedRowMap, lclReducedColMap,
1139 lclFullMatrix, rowmap_view, column_view, values_view);
1140 Kokkos::parallel_for(
1141 "CrsSingletonFilter_LinearProblem:InsertReducedProblem", range_policy(0, localNumRows),
1142 functor);
1143 }
1144
1145 // construct local crsmatrix
1147 local_matrix_type crsmat("CrsMatrix", localNumReducedRows, values_view, static_graph);
1148
1149 // Construct Reduced Matrix with localmatrix + rowmap, colmap, domainmap, & rangemap
1150 // We have constructed things so that the domain and range of the
1151 // matrix will have the same map. If the reduced matrix domain and range maps were not the same, the
1152 // differences were addressed in the ConstructRedistributeExporter() method
1153 ReducedMatrix_ = Teuchos::rcp(new crs_matrix_type(crsmat, ReducedMatrixRowMap(), ReducedMatrixColMap(), ReducedMatrixDomainMap(), ReducedMatrixRangeMap()));
1154 }
1155
1156 // Solve with singleton part
1157 {
1158 using error_code_type = typename Kokkos::View<int*, execution_space>;
1159 using functor_type = SolveSingletonProblemFunctor<scalar_type, local_ordinal_type, global_ordinal_type, Node, local_map_type, local_matrix_type,
1160 local_multivector_type, const_local_multivector_type,
1161 vector_view_type_int, vector_view_type_scalar, error_code_type>;
1162 auto lclFullRowMap = FullMatrixRowMap()->getLocalMap();
1163 auto lclFullColMap = FullMatrixColMap()->getLocalMap();
1164 auto lclReducedRowMap = ReducedMatrixRowMap()->getLocalMap();
1165
1166 auto lclFullMatrix = FullCrsMatrix_->getLocalMatrixDevice();
1167
1168 auto localExportX = tempExportX_->getLocalViewDevice(Tpetra::Access::ReadWrite);
1169 auto localRHS = FullRHS->getLocalViewDevice(Tpetra::Access::ReadOnly);
1170
1171 error_code_type error_code("singleton-bound-check", 2);
1172 Kokkos::deep_copy(error_code, 0);
1173
1174 // launch functor
1175 functor_type functor(NumVectors, localNumSingletonCols_, error_code,
1176 lclFullRowMap, lclFullColMap, lclReducedRowMap,
1177 lclFullMatrix, localExportX, localRHS,
1178 ColSingletonColLIDs_, ColSingletonRowLIDs_, ColSingletonPivotLIDs_, ColSingletonPivots_);
1179 Kokkos::parallel_for(
1180 "CrsSingletonFilter_LinearProblem:SolveSingletonProblem", range_policy(0, localNumRows),
1181 functor);
1182 auto h_error = Kokkos::create_mirror_view(error_code);
1183 Kokkos::deep_copy(h_error, error_code);
1184 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(h_error(0) == 1, std::runtime_error,
1185 "ConstructReducedProblem: Encountered zero row, unable to continue."); // Should improve this comparison to zero.
1186 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(h_error(0) == 2, std::runtime_error,
1187 "ConstructReducedProblem: Encountered zero column, unable to continue."); // Should improve this comparison to zero.
1188 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(h_error(0) == 3, std::runtime_error,
1189 "ConstructReducedProblem: Unable to find my column.");
1190 }
1191 }
1192
1193 // 1) The vector ColProfiles has column nonzero counts for each processor's contribution
1194 // Construct Reduced LHS (Puts any initial guess values into reduced system)
1195
1196 // Create the ReducedLHS_ MultiVector
1197 ReducedLHS_ = Teuchos::rcp(new multivector_type(ReducedMatrixDomainMap(), NumVectors));
1198 ReducedLHS_->doImport(*FullLHS, *Full2ReducedLHSImporter_, Tpetra::INSERT);
1199
1200 FullLHS->putScalar(0.0);
1201
1202 // Construct Reduced RHS
1203
1204 // First compute influence of already-known values of X on RHS
1205 tempX_ = Teuchos::rcp(new multivector_type(FullMatrixDomainMap(), NumVectors));
1206 tempB_ = Teuchos::rcp(new multivector_type(FullRHS->getMap(), NumVectors));
1207
1208 // Inject known X values into tempX for purpose of computing tempB = FullMatrix*tempX
1209 // Also inject into full X since we already know the solution
1210 if (importer != Teuchos::null) {
1211 tempX_->doExport(*tempExportX_, *importer, Tpetra::ADD);
1212 FullLHS->doExport(*tempExportX_, *importer, Tpetra::ADD);
1213 } else {
1214 tempX_->update(1.0, *tempExportX_, 0.0); // tempX_ = 1.0 * tempExportX_ + 0.0 * tempX_
1215 FullLHS->update(1.0, *tempExportX_, 0.0); // FullLHS = 1.0 * tempExportX_ + 0.0 * FullLHS
1216 }
1217
1218 FullMatrix()->apply(*tempX_, *tempB_);
1219 tempB_->update(1.0, *FullRHS, -1.0);
1220
1221 ReducedRHS_ = Teuchos::rcp(new multivector_type(ReducedMatrixRowMap(), FullRHS->getNumVectors()));
1222 ReducedRHS_->doImport(*tempB_, *Full2ReducedRHSImporter_, Tpetra::INSERT);
1223
1224 // Finally construct Reduced Linear Problem
1226 ReducedMatrix(), ReducedLHS_, ReducedRHS_));
1227 } else {
1228 // There are no singletons, so don't bother building a reduced problem.
1229 ReducedProblem_ = Problem;
1230 ReducedMatrix_ = Teuchos::rcp(dynamic_cast<crs_matrix_type*>(Problem->getMatrix().getRawPtr()), false);
1231 }
1232
1233 double fn = (double)FullMatrix()->getGlobalNumRows();
1234 double fnnz = (double)FullMatrix()->getGlobalNumEntries();
1235
1236 double rn = (double)ReducedMatrix()->getGlobalNumRows();
1237 double rnnz = (double)ReducedMatrix()->getGlobalNumEntries();
1238
1239 RatioOfDimensions_ = rn / fn;
1240 RatioOfNonzeros_ = rnnz / fnnz;
1241 HaveReducedProblem_ = true;
1242
1243 return;
1244}
1245
1246//==============================================================================
1247template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1249 UpdateReducedProblem(const Teuchos::RCP<linear_problem_type>& Problem) {
1250 const char tfecfFuncName[] = "UpdateReducedProblem: ";
1251
1252 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(!HaveReducedProblem_, std::runtime_error,
1253 "Must have a reduced problem.");
1255 std::runtime_error, "Problem is Teuchos::null.");
1256 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(FullMatrix() == Teuchos::null,
1257 std::runtime_error, "Need a RowMatrix.");
1258 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(Problem->getRHS() == Teuchos::null,
1259 std::runtime_error, "Need a RHS.");
1260 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(Problem->getLHS() == Teuchos::null,
1261 std::runtime_error, "Need a LHS.");
1262
1263 Scalar zero = Teuchos::ScalarTraits<Scalar>::zero();
1264
1265 if (SingletonsDetected()) {
1266 // Create pointer to Full RHS, LHS
1267 Teuchos::RCP<multivector_type> FullRHS = FullProblem()->getRHS();
1268 Teuchos::RCP<multivector_type> FullLHS = FullProblem()->getLHS();
1269
1270 local_ordinal_type NumVectors = FullLHS->getNumVectors();
1271 tempExportX_->putScalar(0.0);
1272
1273 size_t NumEntries = 0;
1274 Teuchos::ArrayView<const Scalar> Values;
1275 Teuchos::Array<GlobalOrdinal> Indices;
1276 LocalOrdinal localNumRows = FullMatrix()->getLocalNumRows();
1278
1279 // Check if ColSingletonPivotLIDs_, ColSingletonPivot_ can be accessed on host
1280 bool canRunOnHost = std::is_same_v<typename device_type::memory_space, Kokkos::HostSpace>;
1281 if (run_on_host_ && canRunOnHost) {
1282 for (LocalOrdinal i = 0; i < localNumRows; i++) {
1283 GlobalOrdinal curGRID = FullMatrixRowMap()->getGlobalElement(i);
1284 if (ReducedMatrixRowMap()->isNodeGlobalElement(curGRID)) { // Check if this row should go into reduced matrix
1285 GetRowGCIDs(i, NumEntries, Values, Indices);
1286
1287 // Filter indices and values
1288 Teuchos::Array<GlobalOrdinal> filteredIndices;
1289 Teuchos::Array<Scalar> filteredValues;
1290
1291 for (typename Teuchos::Array<GlobalOrdinal>::size_type j = 0; j < Indices.size(); ++j) {
1292 if (ReducedMatrixColMap()->isNodeGlobalElement(Indices[j])) {
1293 filteredIndices.push_back(Indices[j]);
1294 filteredValues.push_back(Values[j]);
1295 }
1296 }
1297
1298 // Insert filtered values into the matrix
1299 if (!filteredIndices.empty()) {
1300 ReducedMatrix()->replaceGlobalValues(curGRID, filteredIndices(), filteredValues());
1301 }
1302
1303 }
1304 // Otherwise if singleton row we explicitly eliminate this row and solve for corresponding X value
1305 else {
1306 Teuchos::ArrayView<const LocalOrdinal> localIndices;
1307 GetRow(i, NumEntries, Values, localIndices); // Get current row
1308 if (NumEntries == 1) {
1309 Scalar pivot = Values[0];
1310 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(pivot == zero, std::runtime_error,
1311 "Encountered zero row, unable to continue."); // Should improve this comparison to zero.
1313 for (LocalOrdinal j = 0; j < NumVectors; j++) {
1314 auto rhsData = FullRHS->getData(j); // Get the underlying data for vector j
1315 auto exportData = tempExportX_->getDataNonConst(j); // Get the underlying data for vector j (non-const)
1317 }
1318 } else { // Singleton column
1319 LocalOrdinal j = ColSingletonPivotLIDs_[ColSingletonCounter];
1320
1321 Scalar pivot = Values[j];
1322 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(pivot == zero, std::runtime_error,
1323 "Encountered zero column, unable to continue."); // Should improve this comparison to zero.
1324 ColSingletonPivots_[ColSingletonCounter] = pivot;
1326 }
1327 }
1328 }
1329 } else {
1330 // Not part of the reduced matrix
1331 {
1332 using error_code_type = typename Kokkos::View<int*, execution_space>;
1333 using functor_type = SolveSingletonProblemFunctor<scalar_type, local_ordinal_type, global_ordinal_type, Node, local_map_type, local_matrix_type,
1334 local_multivector_type, const_local_multivector_type,
1335 vector_view_type_int, vector_view_type_scalar, error_code_type>;
1336
1337 auto lclFullRowMap = FullMatrixRowMap()->getLocalMap();
1338 auto lclFullColMap = FullMatrixColMap()->getLocalMap();
1339 auto lclReducedRowMap = ReducedMatrixRowMap()->getLocalMap();
1340
1341 auto lclFullMatrix = FullCrsMatrix_->getLocalMatrixDevice();
1342
1343 auto localRHS = FullRHS->getLocalViewDevice(Tpetra::Access::ReadOnly);
1344 auto localExportX = tempExportX_->getLocalViewDevice(Tpetra::Access::ReadWrite);
1345
1346 error_code_type error_code("singleton-bound-check", 2);
1347 Kokkos::deep_copy(error_code, 0);
1348
1349 // launch functor
1350 functor_type functor(NumVectors, localNumSingletonCols_, error_code,
1351 lclFullRowMap, lclFullColMap, lclReducedRowMap,
1352 lclFullMatrix, localExportX, localRHS,
1353 ColSingletonColLIDs_, ColSingletonRowLIDs_, ColSingletonPivotLIDs_, ColSingletonPivots_);
1354 Kokkos::parallel_reduce(
1355 "CrsSingletonFilter_LinearProblem:SolveSingletonProblem", range_policy(0, localNumRows),
1357
1358 // get error
1359 auto h_error = Kokkos::create_mirror_view(error_code);
1360 Kokkos::deep_copy(h_error, error_code);
1361 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(h_error(0) == 1, std::runtime_error,
1362 "UpdateReducedProblem: Encountered zero row, unable to continue."); // Should improve this comparison to zero.
1363 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(h_error(0) == 2, std::runtime_error,
1364 "UpdateReducedProblem: Encountered zero column, unable to continue."); // Should improve this comparison to zero.
1365 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(h_error(0) == 3, std::runtime_error,
1366 "UpdateReducedProblem: Unable to find local column.");
1367 }
1368
1369 // Part of the reduced matrix
1370 {
1372 auto lclFullRowMap = FullMatrixRowMap()->getLocalMap();
1373 auto lclFullColMap = FullMatrixColMap()->getLocalMap();
1374 auto lclReducedRowMap = ReducedMatrixRowMap()->getLocalMap();
1375 auto lclReducedColMap = ReducedMatrixColMap()->getLocalMap();
1376
1377 auto lclReducedMatrix = ReducedMatrix_->getLocalMatrixDevice();
1378 auto lclFullMatrix = FullCrsMatrix_->getLocalMatrixDevice();
1379
1380 // launch functor
1381 functor_type functor(lclFullRowMap, lclFullColMap, lclReducedRowMap, lclReducedColMap,
1382 lclFullMatrix, lclReducedMatrix);
1383 Kokkos::parallel_for(
1384 "CrsSingletonFilter_LinearProblem:UpdateReducedProblem", range_policy(0, localNumRows),
1385 functor);
1386 }
1387 }
1388
1390 std::runtime_error, "Sanity Check " + std::to_string(ColSingletonCounter) + " vs " + std::to_string(localNumSingletonCols_) + " (UpdateReducedProblem).");
1391
1392 // Update Reduced LHS (Puts any initial guess values into reduced system)
1393
1394 ReducedLHS_->putScalar(0.0); // zero out Reduced LHS
1395 ReducedRHS_->doImport(*FullLHS, *Full2ReducedRHSImporter_, Tpetra::INSERT);
1396
1397 FullLHS->putScalar(0.0); // zero out Full LHS since we will inject values as we get them
1398
1399 // Construct Reduced RHS
1400
1401 // Zero out temp space
1402 tempX_->putScalar(0.0);
1403 tempB_->putScalar(0.0);
1404
1405 // Inject known X values into tempX for purpose of computing tempB = FullMatrix*tempX
1406 // Also inject into full X since we already know the solution
1407
1408 auto importer = FullCrsMatrix()->getCrsGraph()->getImporter();
1409 if (importer != Teuchos::null) {
1410 tempX_->doExport(*tempExportX_, *importer, Tpetra::ADD);
1411 FullLHS->doExport(*tempExportX_, *importer, Tpetra::ADD);
1412 } else {
1413 tempX_->update(1.0, *tempExportX_, 0.0); // tempX_ = 1.0 * tempExportX_ + 0.0 * tempX_
1414 FullLHS->update(1.0, *tempExportX_, 0.0); // FullLHS = 1.0 * tempExportX_ + 0.0 * FullLHS
1415 }
1416
1417 FullMatrix()->apply(*tempX_, *tempB_);
1418 tempB_->update(1.0, *FullRHS, -1.0);
1419
1420 ReducedRHS_->putScalar(0.0);
1421 ReducedRHS_->doImport(*tempB_, *Full2ReducedRHSImporter_, Tpetra::INSERT);
1422
1423 } else {
1424 // There are no singletons, so don't bother building a reduced problem.
1425 ReducedProblem_ = Problem;
1426 ReducedMatrix_ = Teuchos::rcp(dynamic_cast<crs_matrix_type*>(Problem->getMatrix().getRawPtr()), false);
1427 }
1428
1429 return;
1430}
1431
1432//==============================================================================
1433template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1435 ConstructRedistributeExporter(Teuchos::RCP<const map_type> SourceMap, Teuchos::RCP<const map_type> TargetMap,
1436 Teuchos::RCP<export_type>& RedistributeExporter,
1437 Teuchos::RCP<const map_type>& RedistributeMap) {
1438 const char tfecfFuncName[] = "ConstructRedistributeExporter: ";
1439
1440 auto IndexBase = SourceMap->getIndexBase();
1441 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(IndexBase != TargetMap->getIndexBase(), std::runtime_error,
1442 "Base indices do not match.");
1443
1444 auto Comm = TargetMap->getComm();
1445
1446 size_t TargetNumMyElements = TargetMap->getLocalNumElements();
1447 size_t SourceNumMyElements = SourceMap->getLocalNumElements();
1448
1449 // ContiguousTargetMap has the same number of elements per PE as TargetMap, but uses contiguous indexing
1450 Teuchos::RCP<const map_type> ContiguousTargetMap =
1451 Teuchos::rcp(new const map_type(
1452 Teuchos::OrdinalTraits<Tpetra::global_size_t>::invalid(),
1454
1455 // Same for ContiguousSourceMap
1456 Teuchos::RCP<const map_type> ContiguousSourceMap =
1457 Teuchos::rcp(new const map_type(
1458 Teuchos::OrdinalTraits<Tpetra::global_size_t>::invalid(),
1460
1462 ContiguousSourceMap->getGlobalNumElements() != ContiguousTargetMap->getGlobalNumElements(),
1463 std::runtime_error, "Global number of elements do not match.");
1464
1465 // Retrieve the global indices owned by the calling process as a Kokkos::View
1466 auto SourceMapMyGlobalIndices = SourceMap->getMyGlobalIndices();
1467
1468 // Create a Tpetra::Vector to hold the global indices
1469 Teuchos::RCP<Tpetra::MultiVector<GlobalOrdinal, LocalOrdinal, GlobalOrdinal, Node>> SourceIndices =
1471
1472 // Fill the Tpetra::Vector with the global indices
1473 for (size_t i = 0; i < static_cast<size_t>(SourceMapMyGlobalIndices.size()); ++i) {
1474 SourceIndices->replaceLocalValue(i, 0, static_cast<GlobalOrdinal>(SourceMapMyGlobalIndices[i]));
1475 }
1476
1477 // Get the map associated with the vector
1478 auto map = SourceIndices->getMap();
1479
1480 Teuchos::RCP<Tpetra::Export<LocalOrdinal, GlobalOrdinal, Node>> Exporter =
1481 Teuchos::rcp(new Tpetra::Export<LocalOrdinal, GlobalOrdinal, Node>(ContiguousSourceMap, ContiguousTargetMap));
1482
1483 Teuchos::RCP<Tpetra::MultiVector<GlobalOrdinal, LocalOrdinal, GlobalOrdinal, Node>> TargetIndices =
1484 Teuchos::rcp(new Tpetra::MultiVector<GlobalOrdinal, LocalOrdinal, GlobalOrdinal, Node>(ContiguousTargetMap, 1));
1485
1486 TargetIndices->doExport(*SourceIndices, *Exporter, Tpetra::INSERT);
1487
1488 // Create a new map that describes how the Source MultiVector should be laid out so that it has
1489 // the same number of elements on each processor as the TargetMap
1491 Teuchos::ArrayView<const GlobalOrdinal>(TargetIndices->getData(0).getRawPtr(), TargetIndices->getLocalLength()),
1492 Comm);
1493
1494 RedistributeExporter =
1495 Teuchos::rcp(new Tpetra::Export<LocalOrdinal, GlobalOrdinal, Node>(SourceMap, RedistributeMap));
1496
1497 return;
1498}
1499
1500//==============================================================================
1501template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1504 if (SingletonsDetected()) {
1505 Teuchos::RCP<multivector_type> FullLHS = FullProblem()->getLHS();
1506 Teuchos::RCP<multivector_type> FullRHS = FullProblem()->getRHS();
1507
1508 tempX_->putScalar(Scalar(0.0));
1509 tempExportX_->putScalar(Scalar(0.0));
1510
1511 // Inject values that the user computed for the reduced problem into the full solution vector
1512 tempX_->doExport(*ReducedLHS_, *Full2ReducedLHSImporter_, Tpetra::ADD);
1513 FullLHS->update(Scalar(1.0), *tempX_, Scalar(1.0)); // FullLHS = 1.0 * tempX_ + 1.0 * FullLHS
1514
1515 // Next we will use our full solution vector which is populated with pre-filter solution
1516 // values and reduced system solution values to compute the sum of the row contributions
1517 // that must be subtracted to get the post-filter solution values
1518 FullMatrix()->apply(*FullLHS, *tempB_);
1519
1520 // Finally we loop through the local rows that were associated with column singletons and compute the
1521 // solution for these equations.
1522 size_t NumVectors = tempB_->getNumVectors();
1523 bool canRunOnHost = std::is_same_v<typename device_type::memory_space, Kokkos::HostSpace>;
1524 if (run_on_host_ && canRunOnHost) {
1525 for (int k = 0; k < localNumSingletonCols_; k++) {
1526 LocalOrdinal i = ColSingletonRowLIDs_[k];
1527 LocalOrdinal j = ColSingletonColLIDs_[k];
1528 Scalar pivot = ColSingletonPivots_[k];
1529 for (size_t jj = 0; jj < NumVectors; jj++) {
1530 auto tempExportXData = tempExportX_->getDataNonConst(jj);
1531 auto FullRHSData = FullRHS->getData(jj);
1532 auto tempBData = tempB_->getData(jj);
1534 }
1535 }
1536 } else {
1537 using functor_type = ComputeFullSolutionFunctor<vector_view_type_int, vector_view_type_scalar,
1538 local_multivector_type, const_local_multivector_type>;
1539 auto localB = tempB_->getLocalViewDevice(Tpetra::Access::ReadOnly);
1540 auto localRHS = FullRHS->getLocalViewDevice(Tpetra::Access::ReadOnly);
1541 auto localX = tempExportX_->getLocalViewDevice(Tpetra::Access::ReadWrite);
1542
1543 functor_type functor(ColSingletonRowLIDs_, ColSingletonColLIDs_, ColSingletonPivots_, localX, localRHS, localB);
1544 Kokkos::parallel_for(
1545 "CrsSingletonFilter_LinearProblem:ComputeFullSolution", range_policy(0, localNumSingletonCols_),
1546 functor);
1547 }
1548
1549 // Finally, insert values from post-solve step and we are done!!!!
1550 auto importer = FullMatrix()->getGraph()->getImporter();
1551 if (importer != Teuchos::null) {
1552 tempX_->doExport(*tempExportX_, *importer, Tpetra::ADD);
1553 } else {
1554 tempX_->update(Scalar(1.0), *tempExportX_, Scalar(0.0));
1555 }
1556
1557 FullLHS->update(Scalar(1.0), *tempX_, Scalar(1.0));
1558 }
1559
1560 return;
1561}
1562//==============================================================================
1563
1564template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1567 localMaxNumRowEntries_ = FullMatrix()->getLocalMaxNumRowEntries();
1568
1569 // Cast to CrsMatrix, if possible. Can save some work.
1570 FullCrsMatrix_ = Teuchos::rcp_dynamic_cast<crs_matrix_type>(FullMatrix());
1571 FullMatrixIsCrsMatrix_ = (FullCrsMatrix() != Teuchos::null); // Pointer is non-null if cast worked
1572
1573 return;
1574}
1575
1576//==============================================================================
1577
1578template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1579void CrsSingletonFilter_LinearProblem<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
1580 GetRow(local_ordinal_type localRow, size_t& NumIndices,
1581 Teuchos::Array<local_ordinal_type>& localIndices) {
1582 // Must get the values, but we ignore them.
1583 size_t maxNumEntries = FullCrsMatrix()->getLocalMaxNumRowEntries();
1584 nonconst_local_inds_host_view_type Indices("indices", maxNumEntries);
1585 nonconst_values_host_view_type Values("values", maxNumEntries);
1586 if (FullMatrixIsCrsMatrix_) { // View of current row
1587 FullCrsMatrix()->getLocalRowCopy(localRow, Indices, Values, NumIndices);
1588 } else { // Copy of current row (we must get the values, but we ignore them)
1589 FullMatrix()->getLocalRowCopy(localRow, Indices, Values, NumIndices);
1590 }
1591 // If LocalRow does not belong to the calling process, then the method sets
1592 // NumIndices to Teuchos::OrdinalTraits<size_t>::invalid(), and does not
1593 // modify localIndices or Values.
1594
1595 // Copy the indices to the output array
1596 localIndices.resize(NumIndices);
1597 for (size_t i = 0; i < NumIndices; ++i) {
1598 localIndices[i] = Indices(i);
1599 }
1600 return;
1601}
1602
1603//==============================================================================
1604
1605template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1606void CrsSingletonFilter_LinearProblem<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
1607 GetRow(LocalOrdinal Row, size_t& NumIndices, Teuchos::ArrayView<const Scalar>& Values,
1608 Teuchos::ArrayView<const LocalOrdinal>& Indices) {
1609 typename Tpetra::RowMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::local_inds_host_view_type localIndices;
1610 typename Tpetra::RowMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::values_host_view_type rowValues;
1611
1612 if (FullMatrixIsCrsMatrix_) { // View of current row
1613 FullCrsMatrix()->getLocalRowView(Row, localIndices, rowValues);
1614
1615 NumIndices = localIndices.size();
1616 Values = Teuchos::ArrayView<const Scalar>(reinterpret_cast<const Scalar*>(rowValues.data()), rowValues.size());
1617 Indices = Teuchos::ArrayView<const LocalOrdinal>(localIndices.data(), localIndices.size());
1618
1619 } else { // Copy of current row
1620 typename Tpetra::RowMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::nonconst_local_inds_host_view_type localIndicesCopy("localIndicesCopy", FullMatrix()->getLocalMaxNumRowEntries());
1621 typename Tpetra::RowMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::nonconst_values_host_view_type rowValuesCopy("rowValuesCopy", FullMatrix()->getLocalMaxNumRowEntries());
1622
1623 FullMatrix()->getLocalRowCopy(Row, localIndicesCopy, rowValuesCopy, NumIndices);
1624
1625 Values = Teuchos::ArrayView<const Scalar>(reinterpret_cast<const Scalar*>(rowValuesCopy.data()), NumIndices);
1626 Indices = Teuchos::ArrayView<const LocalOrdinal>(localIndicesCopy.data(), NumIndices);
1627 }
1628 return;
1629}
1630
1631//==============================================================================
1632
1633template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1634void CrsSingletonFilter_LinearProblem<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
1635 GetRowGCIDs(
1636 LocalOrdinal localRow,
1637 size_t& NumIndices,
1638 Teuchos::ArrayView<const Scalar>& Values,
1639 Teuchos::Array<GlobalOrdinal>& GlobalIndices) {
1640 // Extract the row data (local indices and values)
1641 typename Tpetra::RowMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::local_inds_host_view_type LocalIndices;
1642 typename Tpetra::RowMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::values_host_view_type RowValues;
1643
1644 FullMatrix()->getLocalRowView(localRow, LocalIndices, RowValues);
1645
1646 // Convert local indices to global indices
1647 NumIndices = LocalIndices.size();
1648 GlobalIndices.resize(NumIndices); // Resize the array to hold global indices
1649 for (size_t j = 0; j < NumIndices; ++j) {
1650 GlobalIndices[j] = FullMatrixColMap()->getGlobalElement(LocalIndices[j]);
1651 }
1652 // Copy values into the provided ArrayView
1653 Values = Teuchos::ArrayView<const Scalar>(reinterpret_cast<const Scalar*>(RowValues.data()), RowValues.size());
1654
1655 return;
1656}
1657
1658//==============================================================================
1659
1660template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1661void CrsSingletonFilter_LinearProblem<Scalar, LocalOrdinal, GlobalOrdinal, Node>::
1662 CreatePostSolveArrays(vector_type_LO localRowIDofSingletonCol,
1663 vector_type_LO ColProfiles,
1664 vector_type_LO NewColProfiles,
1665 vector_type_LO ColHasRowWithSingleton) {
1666 const char tfecfFuncName[] = "CreatePostSolveArrays: ";
1667
1668 if (localNumSingletonCols_ == 0) return; // Nothing to do
1669
1670 // We will need these arrays for the post-solve phase
1671 Kokkos::resize(ColSingletonRowLIDs_, localNumSingletonCols_);
1672 Kokkos::resize(ColSingletonColLIDs_, localNumSingletonCols_);
1673 Kokkos::resize(ColSingletonPivotLIDs_, localNumSingletonCols_);
1674 Kokkos::resize(ColSingletonPivots_, localNumSingletonCols_);
1675
1676 int NumMyColSingletonstmp = 0;
1677
1678 // Check if ColSingletonRowLIDs_, ColSingletonColLIDs_ can be accessed on host
1679 bool canRunOnHost = std::is_same_v<typename device_type::memory_space, Kokkos::HostSpace>;
1680 if (run_on_host_ && canRunOnHost) {
1681 // Register singleton columns (that were not already counted as singleton rows)
1682 // Check to see if any columns disappeared because all associated rows were eliminated
1683 local_ordinal_type localNumCols = FullMatrix()->getLocalNumCols();
1684 auto localRowIDofSingletonColData = localRowIDofSingletonCol.getLocalViewHost(Tpetra::Access::ReadOnly);
1685 auto ColProfilesData = ColProfiles.getLocalViewHost(Tpetra::Access::ReadOnly);
1686 auto RowMapColors_Data = RowMapColors_->getLocalViewHost(Tpetra::Access::ReadOnly);
1687 auto NewColProfilesData = NewColProfiles.getLocalViewHost(Tpetra::Access::ReadOnly);
1688 auto ColHasRowWithSingletonData = ColHasRowWithSingleton.getLocalViewHost(Tpetra::Access::ReadOnly);
1689 auto ColMapColors_Data = ColMapColors_->getLocalViewHost(Tpetra::Access::ReadWrite);
1690
1691 for (int j = 0; j < localNumCols; j++) {
1692 int i = localRowIDofSingletonColData(j, 0);
1693 if (ColProfilesData(j, 0) == 1 && RowMapColors_Data(i, 0) != 1) {
1694 ColSingletonRowLIDs_[NumMyColSingletonstmp] = i;
1695 ColSingletonColLIDs_[NumMyColSingletonstmp] = j;
1696 NumMyColSingletonstmp++;
1697 }
1698 // Also check for columns that were eliminated implicitly by
1699 // having all associated row eliminated
1700 else if (NewColProfilesData(j, 0) == 0 && ColHasRowWithSingletonData(j, 0) != 1 && RowMapColors_Data(i, 0) == 0) {
1701 ColMapColors_Data(j, 0) = 1;
1702 }
1703 }
1704
1705 } else {
1706 {
1707 // Register singleton columns (that were not already counted as singleton rows)
1708 // Check to see if any columns disappeared because all associated rows were eliminated
1709 local_ordinal_type localNumCols = FullMatrix()->getLocalNumCols();
1710 auto localRowIDofSingletonColData = localRowIDofSingletonCol.getLocalViewDevice(Tpetra::Access::ReadOnly);
1711 auto ColProfilesData = ColProfiles.getLocalViewDevice(Tpetra::Access::ReadOnly);
1712 auto RowMapColors_Data = RowMapColors_->getLocalViewDevice(Tpetra::Access::ReadOnly);
1713 auto NewColProfilesData = NewColProfiles.getLocalViewDevice(Tpetra::Access::ReadOnly);
1714 auto ColHasRowWithSingletonData = ColHasRowWithSingleton.getLocalViewDevice(Tpetra::Access::ReadOnly);
1715 auto ColMapColors_Data = ColMapColors_->getLocalViewDevice(Tpetra::Access::ReadWrite);
1716
1717 // counter to be atomic-incremented
1718 Kokkos::View<int*, execution_space> numSingletonCols("numSingletonCols", 1);
1719 Kokkos::deep_copy(numSingletonCols, 0);
1720
1721 // launch functor
1722 CreatePostSolveArraysFunctor functor(localRowIDofSingletonColData,
1723 ColSingletonRowLIDs_, ColSingletonColLIDs_,
1724 NewColProfilesData, ColProfilesData, ColHasRowWithSingletonData,
1725 ColMapColors_Data, RowMapColors_Data, numSingletonCols);
1726 Kokkos::parallel_for(
1727 "CrsSingletonFilter_LinearProblem:CreatePostSolveArrays", range_policy(0, localNumCols),
1728 functor);
1729
1730 // get the counter
1731 auto h_numSingletonCols = Kokkos::create_mirror_view(numSingletonCols);
1732 Kokkos::deep_copy(h_numSingletonCols, numSingletonCols);
1733 NumMyColSingletonstmp = h_numSingletonCols(0);
1734 }
1735 }
1736
1737 TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC(NumMyColSingletonstmp != localNumSingletonCols_,
1738 std::runtime_error, "Sanity Check " + std::to_string(NumMyColSingletonstmp) + " vs " + std::to_string(localNumSingletonCols_) + " (CreatePostSolveArrays).");
1739
1740 Kokkos::Experimental::sort_by_key(execution_space(), ColSingletonRowLIDs_, ColSingletonColLIDs_);
1741
1742 return;
1743}
1744
1745} // namespace Tpetra
1746
1747//
1748// Explicit instantiation macro
1749//
1750// Must be expanded from within the Tpetra namespace!
1751//
1752
1753#define TPETRA_CRSSINGLETONFILTER_INSTANT(SCALAR, LO, GO, NODE) \
1754 template class CrsSingletonFilter_LinearProblem<SCALAR, LO, GO, NODE>;
1755
1756#endif // TPETRA_CRSSINGLETONFILTER_LINEARPROBLEM_DEF_HPP
Declaration of the Tpetra::CrsSingletonFilter_LinearProblem class.
Struct that holds views of the contents of a CrsMatrix.
Sparse matrix that presents a row-oriented interface that lets users read or modify entries.
A class for explicitly eliminating matrix rows and columns from a LinearProblem.
void ConstructReducedProblem(const Teuchos::RCP< linear_problem_type > &Problem)
Return a reduced linear problem based on results of Analyze().
void Analyze(const Teuchos::RCP< row_matrix_type > &FullMatrix)
Analyze the input matrix, removing row/column pairs that have singletons.
NewType construct()
Construction of new object as a result of the transform.
void UpdateReducedProblem(const Teuchos::RCP< linear_problem_type > &Problem)
Update a reduced linear problem using new values.
CrsSingletonFilter_LinearProblem(bool run_on_host=false, bool verbose=false)
Constructor.
void ComputeFullSolution()
Compute a solution for the full problem using the solution of the reduced problem,...
NewType operator()(const OriginalType &originalLinearProblem)
Analysis of transform operation on original object and construction of new object.
void analyze(const OriginalType &originalLinearProblem)
Initial analysis phase of transform.
Communication plan for data redistribution from a uniquely-owned to a (possibly) multiply-owned distr...
typename Kokkos::ArithTraits< Scalar >::val_type impl_scalar_type
The type used internally in place of Scalar.
A distributed dense vector.
int local_ordinal_type
Default value of Scalar template parameter.
Namespace Tpetra contains the class and methods constituting the Tpetra library.
@ ADD
Sum new values.
@ ABSMAX
Replace old value with maximum of magnitudes of old and new values.
@ INSERT
Insert new values that don't currently exist.