Xpetra Version of the Day
Loading...
Searching...
No Matches
Xpetra_MatrixUtils_def.hpp
Go to the documentation of this file.
1// @HEADER
2// *****************************************************************************
3// Xpetra: A linear algebra interface package
4//
5// Copyright 2012 NTESS and the Xpetra contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
10#ifndef PACKAGES_XPETRA_SUP_MATRIX_UTILS_DEF_HPP_
11#define PACKAGES_XPETRA_SUP_MATRIX_UTILS_DEF_HPP_
12
13#include "Xpetra_ConfigDefs.hpp"
14
15#include "Xpetra_Map.hpp"
16#include "Xpetra_MapUtils.hpp"
17#include "Xpetra_StridedMap.hpp"
18#include "Xpetra_MapFactory.hpp"
19#include "Xpetra_MapExtractor.hpp"
21#include "Xpetra_Matrix.hpp"
22#include "Xpetra_MatrixFactory.hpp"
23#include "Xpetra_BlockedCrsMatrix.hpp"
24#include "Xpetra_MatrixMatrix.hpp"
25
26#include "Xpetra_TpetraMultiVector.hpp"
27#include <Tpetra_RowMatrixTransposer.hpp>
30
32
33namespace Xpetra {
34
35template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
38 RCP<const Map> map = MapUtils::shrinkMapGIDs(*(input.getMap()), *(input.getMap()));
40 for (size_t c = 0; c < input.getNumVectors(); c++) {
42 for (size_t r = 0; r < input.getLocalLength(); r++) {
43 ret->replaceLocalValue(Teuchos::as<LocalOrdinal>(r), c, data[r]);
44 }
45 }
46
47 return ret;
48}
49
50template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
54 RCP<const Teuchos::Comm<int>> comm = input.getRowMap()->getComm();
55
56 // build an overlapping version of mySpecialMap
57 Teuchos::Array<GlobalOrdinal> ovlUnknownStatusGids;
58 Teuchos::Array<GlobalOrdinal> ovlFoundStatusGids;
59
60 // loop over global column map of A and find all GIDs where it is not sure, whether they are special or not
61 for (size_t i = 0; i < input.getColMap()->getLocalNumElements(); i++) {
62 GlobalOrdinal gcid = input.getColMap()->getGlobalElement(i);
63 if (domainMap.isNodeGlobalElement(gcid) == false) {
64 ovlUnknownStatusGids.push_back(gcid);
65 }
66 }
67
68 // We need a locally replicated list of all DOF gids of the (non-overlapping) range map of A10
69 // Communicate the number of DOFs on each processor
70 std::vector<int> myUnknownDofGIDs(comm->getSize(), 0);
71 std::vector<int> numUnknownDofGIDs(comm->getSize(), 0);
72 myUnknownDofGIDs[comm->getRank()] = ovlUnknownStatusGids.size();
73 Teuchos::reduceAll(*comm, Teuchos::REDUCE_MAX, comm->getSize(), &myUnknownDofGIDs[0], &numUnknownDofGIDs[0]);
74
75 // create array containing all DOF GIDs
76 size_t cntUnknownDofGIDs = 0;
77 for (int p = 0; p < comm->getSize(); p++) cntUnknownDofGIDs += numUnknownDofGIDs[p];
78 std::vector<GlobalOrdinal> lUnknownDofGIDs(cntUnknownDofGIDs, -1); // local version to be filled
79 std::vector<GlobalOrdinal> gUnknownDofGIDs(cntUnknownDofGIDs, -1); // global version after communication
80 // calculate the offset and fill chunk of memory with local data on each processor
81 size_t cntUnknownOffset = 0;
82 for (int p = 0; p < comm->getRank(); p++) cntUnknownOffset += numUnknownDofGIDs[p];
83 for (size_t k = 0; k < Teuchos::as<size_t>(ovlUnknownStatusGids.size()); k++) {
84 lUnknownDofGIDs[k + cntUnknownOffset] = ovlUnknownStatusGids[k];
85 }
86 if (cntUnknownDofGIDs > 0)
87 Teuchos::reduceAll(*comm, Teuchos::REDUCE_MAX, Teuchos::as<int>(cntUnknownDofGIDs), &lUnknownDofGIDs[0], &gUnknownDofGIDs[0]);
88 std::sort(gUnknownDofGIDs.begin(), gUnknownDofGIDs.end());
89 gUnknownDofGIDs.erase(std::unique(gUnknownDofGIDs.begin(), gUnknownDofGIDs.end()), gUnknownDofGIDs.end());
90
91 // loop through all GIDs with unknown status
92 for (size_t k = 0; k < gUnknownDofGIDs.size(); k++) {
93 GlobalOrdinal curgid = gUnknownDofGIDs[k];
94 if (domainMap.isNodeGlobalElement(curgid)) {
95 ovlFoundStatusGids.push_back(curgid); // curgid is in special map (on this processor)
96 }
97 }
98
99 std::vector<int> myFoundDofGIDs(comm->getSize(), 0);
100 std::vector<int> numFoundDofGIDs(comm->getSize(), 0);
101 myFoundDofGIDs[comm->getRank()] = ovlFoundStatusGids.size();
102 Teuchos::reduceAll(*comm, Teuchos::REDUCE_MAX, comm->getSize(), &myFoundDofGIDs[0], &numFoundDofGIDs[0]);
103
104 // create array containing all DOF GIDs
105 size_t cntFoundDofGIDs = 0;
106 for (int p = 0; p < comm->getSize(); p++) cntFoundDofGIDs += numFoundDofGIDs[p];
107 std::vector<GlobalOrdinal> lFoundDofGIDs(cntFoundDofGIDs, -1); // local version to be filled
108 std::vector<GlobalOrdinal> gFoundDofGIDs(cntFoundDofGIDs, -1); // global version after communication
109 // calculate the offset and fill chunk of memory with local data on each processor
110 size_t cntFoundOffset = 0;
111 for (int p = 0; p < comm->getRank(); p++) cntFoundOffset += numFoundDofGIDs[p];
112 for (size_t k = 0; k < Teuchos::as<size_t>(ovlFoundStatusGids.size()); k++) {
113 lFoundDofGIDs[k + cntFoundOffset] = ovlFoundStatusGids[k];
114 }
115 if (cntFoundDofGIDs > 0)
116 Teuchos::reduceAll(*comm, Teuchos::REDUCE_MAX, Teuchos::as<int>(cntFoundDofGIDs), &lFoundDofGIDs[0], &gFoundDofGIDs[0]);
117
118 Teuchos::Array<GlobalOrdinal> ovlDomainMapArray;
119 for (size_t i = 0; i < input.getColMap()->getLocalNumElements(); i++) {
120 GlobalOrdinal gcid = input.getColMap()->getGlobalElement(i);
121 if (domainMap.isNodeGlobalElement(gcid) == true ||
122 std::find(gFoundDofGIDs.begin(), gFoundDofGIDs.end(), gcid) != gFoundDofGIDs.end()) {
123 ovlDomainMapArray.push_back(gcid);
124 }
125 }
126 RCP<Map> ovlDomainMap = MapFactory::Build(domainMap.lib(), Teuchos::OrdinalTraits<GlobalOrdinal>::invalid(), ovlDomainMapArray(), 0, comm);
127 return ovlDomainMap;
128}
129
130template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
136 bool bThyraMode) {
137 size_t numRows = rangeMapExtractor->NumMaps();
138 size_t numCols = domainMapExtractor->NumMaps();
139
140 TEUCHOS_TEST_FOR_EXCEPTION(rangeMapExtractor->getThyraMode() == true, Xpetra::Exceptions::Incompatible, "Xpetra::MatrixUtils::Split: RangeMapExtractor must not use Thyra style numbering of GIDs. The MapExtractor must contain all GIDs of the full range map in order to define a proper splitting.")
141 TEUCHOS_TEST_FOR_EXCEPTION(domainMapExtractor->getThyraMode() == true, Xpetra::Exceptions::Incompatible, "Xpetra::MatrixUtils::Split: DomainMapExtractor must not use Thyra style numbering of GIDs. The MapExtractor must contain all GIDs of the full domain map in order to define a proper splitting.")
142
143 RCP<const Map> fullRangeMap = rangeMapExtractor->getFullMap();
144 RCP<const Map> fullDomainMap = domainMapExtractor->getFullMap();
145
146 TEUCHOS_TEST_FOR_EXCEPTION(fullRangeMap->getMaxAllGlobalIndex() != input.getRowMap()->getMaxAllGlobalIndex(), Xpetra::Exceptions::Incompatible, "Xpetra::MatrixUtils::Split: RangeMapExtractor incompatible to row map of input matrix.")
147 TEUCHOS_TEST_FOR_EXCEPTION(fullRangeMap->getGlobalNumElements() != input.getRowMap()->getGlobalNumElements(), Xpetra::Exceptions::Incompatible, "Xpetra::MatrixUtils::Split: RangeMapExtractor incompatible to row map of input matrix.")
148 TEUCHOS_TEST_FOR_EXCEPTION(fullRangeMap->getLocalNumElements() != input.getRowMap()->getLocalNumElements(), Xpetra::Exceptions::Incompatible, "Xpetra::MatrixUtils::Split: RangeMapExtractor incompatible to row map of input matrix.")
149 TEUCHOS_TEST_FOR_EXCEPTION(fullRangeMap->getMaxAllGlobalIndex() != input.getRangeMap()->getMaxAllGlobalIndex(), Xpetra::Exceptions::Incompatible, "Xpetra::MatrixUtils::Split: RangeMapExtractor incompatible to row map of input matrix.")
150 TEUCHOS_TEST_FOR_EXCEPTION(fullRangeMap->getGlobalNumElements() != input.getRangeMap()->getGlobalNumElements(), Xpetra::Exceptions::Incompatible, "Xpetra::MatrixUtils::Split: RangeMapExtractor incompatible to row map of input matrix.")
151 TEUCHOS_TEST_FOR_EXCEPTION(fullRangeMap->getLocalNumElements() != input.getRangeMap()->getLocalNumElements(), Xpetra::Exceptions::Incompatible, "Xpetra::MatrixUtils::Split: RangeMapExtractor incompatible to row map of input matrix.")
152
153 TEUCHOS_TEST_FOR_EXCEPTION(fullDomainMap->getMaxAllGlobalIndex() != input.getColMap()->getMaxAllGlobalIndex(), Xpetra::Exceptions::Incompatible, "Xpetra::MatrixUtils::Split: DomainMapExtractor incompatible to domain map of input matrix. fullDomainMap->getMaxAllGlobalIndex() = " << fullDomainMap->getMaxAllGlobalIndex() << " vs. input.getColMap()->getMaxAllGlobalIndex() = " << input.getColMap()->getMaxAllGlobalIndex())
154 TEUCHOS_TEST_FOR_EXCEPTION(fullDomainMap->getMaxAllGlobalIndex() != input.getDomainMap()->getMaxAllGlobalIndex(), Xpetra::Exceptions::Incompatible, "Xpetra::MatrixUtils::Split: DomainMapExtractor incompatible to domain map of input matrix.")
155 TEUCHOS_TEST_FOR_EXCEPTION(fullDomainMap->getGlobalNumElements() != input.getDomainMap()->getGlobalNumElements(), Xpetra::Exceptions::Incompatible, "Xpetra::MatrixUtils::Split: DomainMapExtractor incompatible to domain map of input matrix.")
156 TEUCHOS_TEST_FOR_EXCEPTION(fullDomainMap->getLocalNumElements() != input.getDomainMap()->getLocalNumElements(), Xpetra::Exceptions::Incompatible, "Xpetra::MatrixUtils::Split: DomainMapExtractor incompatible to domain map of input matrix.")
157
158 // check column map extractor
159 Teuchos::RCP<const MapExtractor> myColumnMapExtractor = Teuchos::null;
160 if (columnMapExtractor == Teuchos::null) {
161 TEUCHOS_TEST_FOR_EXCEPTION(domainMapExtractor->getThyraMode() == true, Xpetra::Exceptions::Incompatible, "Xpetra::MatrixUtils::Split: Auto generation of column map extractor not supported for Thyra style numbering.");
162 // This code is always executed, since we always provide map extractors in Xpetra numbering!
163 std::vector<Teuchos::RCP<const Map>> ovlxmaps(numCols, Teuchos::null);
164 for (size_t c = 0; c < numCols; c++) {
165 // TODO: is this routine working correctly?
166 Teuchos::RCP<const Map> colMap = MatrixUtils::findColumnSubMap(input, *(domainMapExtractor->getMap(c)));
167 ovlxmaps[c] = colMap;
168 }
169 RCP<const Map> fullColMap = MapUtils::concatenateMaps(ovlxmaps);
170 // This MapExtractor is always in Xpetra mode!
171 myColumnMapExtractor = MapExtractorFactory::Build(fullColMap, ovlxmaps);
172 } else
173 myColumnMapExtractor = columnMapExtractor; // use user-provided column map extractor.
174
175 // all above MapExtractors are always in Xpetra mode
176 // build separate ones containing Thyra mode GIDs (if necessary)
177 Teuchos::RCP<const MapExtractor> thyRangeMapExtractor = Teuchos::null;
178 Teuchos::RCP<const MapExtractor> thyDomainMapExtractor = Teuchos::null;
179 Teuchos::RCP<const MapExtractor> thyColMapExtractor = Teuchos::null;
180 if (bThyraMode == true) {
181 // build Thyra-style map extractors
182 std::vector<Teuchos::RCP<const Map>> thyRgMapExtractorMaps(numRows, Teuchos::null);
183 for (size_t r = 0; r < numRows; r++) {
184 RCP<const Map> rMap = rangeMapExtractor->getMap(r);
185 RCP<const Map> shrinkedMap = MapUtils::shrinkMapGIDs(*rMap, *rMap);
186 RCP<const StridedMap> strRangeMap = Teuchos::rcp_dynamic_cast<const StridedMap>(rMap);
187 if (strRangeMap != Teuchos::null) {
188 std::vector<size_t> strInfo = strRangeMap->getStridingData();
189 GlobalOrdinal offset = strRangeMap->getOffset();
190 LocalOrdinal stridedBlockId = strRangeMap->getStridedBlockId();
191 RCP<const StridedMap> strShrinkedMap = Teuchos::rcp(new StridedMap(shrinkedMap, strInfo, shrinkedMap->getIndexBase(), stridedBlockId, offset));
192 thyRgMapExtractorMaps[r] = strShrinkedMap;
193 } else {
194 thyRgMapExtractorMaps[r] = shrinkedMap;
195 }
196 TEUCHOS_TEST_FOR_EXCEPTION(thyRgMapExtractorMaps[r]->getLocalNumElements() != rMap->getLocalNumElements(), Xpetra::Exceptions::Incompatible, "Xpetra::MatrixUtils::Split: Thyra-style range map extractor contains faulty data.")
197 }
198 RCP<const Map> fullThyRangeMap = MapUtils::concatenateMaps(thyRgMapExtractorMaps);
199 thyRangeMapExtractor = MapExtractorFactory::Build(fullThyRangeMap, thyRgMapExtractorMaps, true);
200 std::vector<Teuchos::RCP<const Map>> thyDoMapExtractorMaps(numCols, Teuchos::null);
201 std::vector<Teuchos::RCP<const Map>> thyColMapExtractorMaps(numCols, Teuchos::null);
202 for (size_t c = 0; c < numCols; c++) {
203 RCP<const Map> cMap = domainMapExtractor->getMap(c);
204
205 RCP<const Map> shrinkedDomainMap = MapUtils::shrinkMapGIDs(*cMap, *cMap);
206 RCP<const StridedMap> strDomainMap = Teuchos::rcp_dynamic_cast<const StridedMap>(cMap);
207 if (strDomainMap != Teuchos::null) {
208 std::vector<size_t> strInfo = strDomainMap->getStridingData();
209 GlobalOrdinal offset = strDomainMap->getOffset();
210 LocalOrdinal stridedBlockId = strDomainMap->getStridedBlockId();
211 RCP<const StridedMap> strShrinkedDomainMap = Teuchos::rcp(new StridedMap(shrinkedDomainMap, strInfo, shrinkedDomainMap->getIndexBase(), stridedBlockId, offset));
212 thyDoMapExtractorMaps[c] = strShrinkedDomainMap;
213 } else {
214 thyDoMapExtractorMaps[c] = shrinkedDomainMap;
215 }
216 RCP<const Map> colMap = myColumnMapExtractor->getMap(c);
217 RCP<const Map> shrinkedColMap = MapUtils::shrinkMapGIDs(*colMap, *cMap);
218 RCP<const StridedMap> strColMap = Teuchos::rcp_dynamic_cast<const StridedMap>(colMap);
219 if (strColMap != Teuchos::null) {
220 std::vector<size_t> strInfo = strColMap->getStridingData();
221 GlobalOrdinal offset = strColMap->getOffset();
222 LocalOrdinal stridedBlockId = strColMap->getStridedBlockId();
223 RCP<const StridedMap> strShrinkedColMap = Teuchos::rcp(new StridedMap(shrinkedColMap, strInfo, shrinkedColMap->getIndexBase(), stridedBlockId, offset));
224 thyColMapExtractorMaps[c] = strShrinkedColMap;
225 } else {
226 thyColMapExtractorMaps[c] = shrinkedColMap;
227 }
228
229 TEUCHOS_TEST_FOR_EXCEPTION(thyColMapExtractorMaps[c]->getLocalNumElements() != colMap->getLocalNumElements(), Xpetra::Exceptions::Incompatible, "Xpetra::MatrixUtils::Split: Thyra-style column map extractor contains faulty data.")
230 TEUCHOS_TEST_FOR_EXCEPTION(thyDoMapExtractorMaps[c]->getLocalNumElements() != cMap->getLocalNumElements(), Xpetra::Exceptions::Incompatible, "Xpetra::MatrixUtils::Split: Thyra-style domain map extractor contains faulty data.")
231 }
232 RCP<const Map> fullThyDomainMap = MapUtils::concatenateMaps(thyDoMapExtractorMaps);
233 RCP<const Map> fullThyColumnMap = MapUtils::concatenateMaps(thyColMapExtractorMaps);
234 thyDomainMapExtractor = MapExtractorFactory::Build(fullThyDomainMap, thyDoMapExtractorMaps, true);
235 thyColMapExtractor = MapExtractorFactory::Build(fullThyColumnMap, thyColMapExtractorMaps, true);
236 }
237 // create submatrices
238 std::vector<Teuchos::RCP<Matrix>> subMatrices(numRows * numCols, Teuchos::null);
239 for (size_t r = 0; r < numRows; r++) {
240 for (size_t c = 0; c < numCols; c++) {
241 // create empty CrsMatrix objects
242 // make sure that the submatrices are defined using the right row maps (either Thyra or xpetra style)
243 // Note: we're reserving a little bit too much memory for the submatrices, but should be still reasonable
244 if (bThyraMode == true)
245 subMatrices[r * numCols + c] = MatrixFactory::Build(thyRangeMapExtractor->getMap(r, true), input.getLocalMaxNumRowEntries());
246 else
247 subMatrices[r * numCols + c] = MatrixFactory::Build(rangeMapExtractor->getMap(r), input.getLocalMaxNumRowEntries());
248 }
249 }
250
251 // We need a vector which lives on the column map of input and stores the block id that the column belongs to.
252 // create a vector on the domain map. Loop over it and fill in the corresponding block id numbers
253 // create a vector on the column map and import the data
254 // Importer: source map is non-overlapping. Target map is overlapping
255 // call colMap.Import(domMap,Importer,Insert)
256 // do the same with "Add" to make sure only one processor is responsible for the different GIDs!
257#if 0 // TAW needs to be fixed (does not compile for Scalar=complex)
262
263 RCP<Vector> doCheck = VectorFactory::Build(input.getDomainMap(), true);
264 doCheck->putScalar(1.0);
265 RCP<Vector> coCheck = VectorFactory::Build(input.getColMap(), true);
267 coCheck->doImport(*doCheck, *imp, Xpetra::ADD);
268 TEUCHOS_TEST_FOR_EXCEPTION(coCheck->normInf() != Teuchos::ScalarTraits< Scalar >::magnitude(1.0), Xpetra::Exceptions::RuntimeError, "Xpetra::MatrixUtils::SplitMatrix: error when distributing data.");
269
270 doCheck->putScalar(-1.0);
271 coCheck->putScalar(-1.0);
272
273 Teuchos::ArrayRCP< Scalar > doCheckData = doCheck->getDataNonConst(0);
274 for (size_t rrr = 0; rrr < input.getDomainMap()->getLocalNumElements(); rrr++) {
275 // global row id to extract data from global monolithic matrix
276 GlobalOrdinal id = input.getDomainMap()->getGlobalElement(rrr); // LID -> GID (column)
277
278 // Find the block id in range map extractor that belongs to same global id.
279 size_t BlockId = domainMapExtractor->getMapIndexForGID(id);
280
281 doCheckData[rrr] = Teuchos::as<Scalar>(BlockId);
282 }
283
284 coCheck->doImport(*doCheck, *imp, Xpetra::INSERT);
285
286 Teuchos::ArrayRCP< Scalar > coCheckData = coCheck->getDataNonConst(0);
287#endif
288 // loop over all rows of input matrix
289 for (size_t rr = 0; rr < input.getRowMap()->getLocalNumElements(); rr++) {
290 // global row id to extract data from global monolithic matrix
291 GlobalOrdinal growid = input.getRowMap()->getGlobalElement(rr); // LID -> GID (column)
292
293 // Find the block id in range map extractor that belongs to same global row id.
294 // We assume the global ids to be unique in the map extractor
295 // The MapExtractor objects may be constructed using the thyra mode. However, we use
296 // global GID ids (as we have a single input matrix). The only problematic thing could
297 // be that the GIDs of the input matrix are inconsistent with the GIDs in the map extractor.
298 // Note, that for the Thyra mode the GIDs in the map extractor are generated using the ordering
299 // of the blocks.
300 size_t rowBlockId = rangeMapExtractor->getMapIndexForGID(growid);
301
302 // global row id used for subblocks to insert information
303 GlobalOrdinal subblock_growid = growid; // for Xpetra-style numbering the global row ids are not changing
304 if (bThyraMode == true) {
305 // find local row id associated with growid in the corresponding subblock
306 LocalOrdinal lrowid = rangeMapExtractor->getMap(rowBlockId)->getLocalElement(growid);
307 // translate back local row id to global row id for the subblock
308 subblock_growid = thyRangeMapExtractor->getMap(rowBlockId, true)->getGlobalElement(lrowid);
309 }
310
311 // extract matrix entries from input matrix
312 // we use global ids since we have to determine the corresponding
313 // block column ids using the global ids anyway
316 input.getLocalRowView(rr, indices, vals);
317
318 std::vector<Teuchos::Array<GlobalOrdinal>> blockColIdx(numCols, Teuchos::Array<GlobalOrdinal>());
319 std::vector<Teuchos::Array<Scalar>> blockColVals(numCols, Teuchos::Array<Scalar>());
320
321 for (size_t i = 0; i < (size_t)indices.size(); i++) {
322 // gobal column id to extract data from full monolithic matrix
323 GlobalOrdinal gcolid = input.getColMap()->getGlobalElement(indices[i]);
324
325 size_t colBlockId = myColumnMapExtractor->getMapIndexForGID(gcolid); // old buggy thing
326 // size_t colBlockId = Teuchos::as<size_t>(coCheckData[indices[i]]);
327
328 // global column id used for subblocks to insert information
329 GlobalOrdinal subblock_gcolid = gcolid; // for Xpetra-style numbering the global col ids are not changing
330 if (bThyraMode == true) {
331 // find local col id associated with gcolid in the corresponding subblock
332 LocalOrdinal lcolid = myColumnMapExtractor->getMap(colBlockId)->getLocalElement(gcolid);
333 // translate back local col id to global col id for the subblock
334 subblock_gcolid = thyColMapExtractor->getMap(colBlockId, true)->getGlobalElement(lcolid);
335 }
336 blockColIdx[colBlockId].push_back(subblock_gcolid);
337 blockColVals[colBlockId].push_back(vals[i]);
338 }
339
340 for (size_t c = 0; c < numCols; c++) {
341 subMatrices[rowBlockId * numCols + c]->insertGlobalValues(subblock_growid, blockColIdx[c].view(0, blockColIdx[c].size()), blockColVals[c].view(0, blockColVals[c].size()));
342 }
343 }
344
345 // call fill complete on subblocks and create BlockedCrsOperator
346 RCP<BlockedCrsMatrix> bA = Teuchos::null;
347 if (bThyraMode == true) {
348 for (size_t r = 0; r < numRows; r++) {
349 for (size_t c = 0; c < numCols; c++) {
350 subMatrices[r * numCols + c]->fillComplete(thyDomainMapExtractor->getMap(c, true), thyRangeMapExtractor->getMap(r, true));
351 }
352 }
353 bA = Teuchos::rcp(new BlockedCrsMatrix(thyRangeMapExtractor, thyDomainMapExtractor, 10 /*input.getRowMap()->getLocalMaxNumRowEntries()*/));
354 } else {
355 for (size_t r = 0; r < numRows; r++) {
356 for (size_t c = 0; c < numCols; c++) {
357 subMatrices[r * numCols + c]->fillComplete(domainMapExtractor->getMap(c), rangeMapExtractor->getMap(r));
358 }
359 }
360 bA = Teuchos::rcp(new BlockedCrsMatrix(rangeMapExtractor, domainMapExtractor, 10 /*input.getRowMap()->getLocalMaxNumRowEntries()*/));
361 }
362
363 for (size_t r = 0; r < numRows; r++) {
364 for (size_t c = 0; c < numCols; c++) {
365 bA->setMatrix(r, c, subMatrices[r * numCols + c]);
366 }
367 }
368 return bA;
369}
370
371template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
373 bool const& repairZeroDiagonals, Teuchos::FancyOStream& fos,
374 const typename Teuchos::ScalarTraits<Scalar>::magnitudeType threshold,
375 const Scalar replacementValue) {
376 using TST = typename Teuchos::ScalarTraits<Scalar>;
377 using Teuchos::rcp_dynamic_cast;
378
379 GlobalOrdinal gZeroDiags;
380 bool usedEfficientPath = false;
381
382 RCP<CrsMatrixWrap> crsWrapAc = rcp_dynamic_cast<CrsMatrixWrap>(Ac);
383 RCP<TpetraCrsMatrix> tpCrsAc;
384 if (!crsWrapAc.is_null())
385 tpCrsAc = rcp_dynamic_cast<TpetraCrsMatrix>(crsWrapAc->getCrsMatrix());
386
387 if (!tpCrsAc.is_null()) {
388 auto tpCrsGraph = tpCrsAc->getTpetra_CrsMatrix()->getCrsGraph();
389 size_t numRows = Ac->getRowMap()->getLocalNumElements();
391 using range_type = Kokkos::RangePolicy<LocalOrdinal, typename Node::execution_space>;
392 auto offsets = offset_type(Kokkos::ViewAllocateWithoutInitializing("offsets"), numRows);
393 tpCrsGraph->getLocalDiagOffsets(offsets);
394
395 const size_t STINV =
396 Tpetra::Details::OrdinalTraits<typename offset_type::value_type>::invalid();
397
398 if (repairZeroDiagonals) {
399 // Make sure that the matrix has all its diagonal entries, so
400 // we can fix them in-place.
401
402 LO numMissingDiagonalEntries = 0;
403
404 Kokkos::parallel_reduce(
405 "countMissingDiagonalEntries",
406 range_type(0, numRows),
407 KOKKOS_LAMBDA(const LO i, LO& missing) {
408 missing += (offsets(i) == STINV);
409 },
410 numMissingDiagonalEntries);
411
412 GlobalOrdinal gNumMissingDiagonalEntries;
413 Teuchos::reduceAll(*(Ac->getRowMap()->getComm()), Teuchos::REDUCE_SUM, Teuchos::as<GlobalOrdinal>(numMissingDiagonalEntries),
414 Teuchos::outArg(gNumMissingDiagonalEntries));
415
416 if (gNumMissingDiagonalEntries == 0) {
417 // Matrix has all diagonal entries, now we fix them
418
419 auto lclA = tpCrsAc->getTpetra_CrsMatrix()->getLocalMatrixDevice();
420
421 using ATS = KokkosKernels::ArithTraits<Scalar>;
422 using impl_ATS = KokkosKernels::ArithTraits<typename ATS::val_type>;
423
424 LO lZeroDiags = 0;
425 typename ATS::val_type impl_replacementValue = replacementValue;
426
427 Kokkos::parallel_reduce(
428 "fixSmallDiagonalEntries",
429 range_type(0, numRows),
430 KOKKOS_LAMBDA(const LO i, LO& fixed) {
431 const auto offset = offsets(i);
432 auto curRow = lclA.row(i);
433 if (impl_ATS::magnitude(curRow.value(offset)) <= threshold) {
434 curRow.value(offset) = impl_replacementValue;
435 fixed += 1;
436 }
437 },
438 lZeroDiags);
439
440 Teuchos::reduceAll(*(Ac->getRowMap()->getComm()), Teuchos::REDUCE_SUM, Teuchos::as<GlobalOrdinal>(lZeroDiags),
441 Teuchos::outArg(gZeroDiags));
442
443 usedEfficientPath = true;
444 }
445 } else {
446 // We only want to count up small diagonal entries, but not
447 // fix them. So missing diagonal entries are not an issue.
448
449 auto lclA = tpCrsAc->getTpetra_CrsMatrix()->getLocalMatrixDevice();
450
451 using ATS = KokkosKernels::ArithTraits<Scalar>;
452 using impl_ATS = KokkosKernels::ArithTraits<typename ATS::val_type>;
453
454 LO lZeroDiags = 0;
455
456 Kokkos::parallel_reduce(
457 "detectSmallDiagonalEntries",
458 range_type(0, numRows),
459 KOKKOS_LAMBDA(const LO i, LO& small) {
460 const auto offset = offsets(i);
461 if (offset == STINV)
462 small += 1;
463 else {
464 auto curRow = lclA.row(i);
465 if (impl_ATS::magnitude(curRow.value(offset)) <= threshold) {
466 small += 1;
467 }
468 }
469 },
470 lZeroDiags);
471
472 Teuchos::reduceAll(*(Ac->getRowMap()->getComm()), Teuchos::REDUCE_SUM, Teuchos::as<GlobalOrdinal>(lZeroDiags),
473 Teuchos::outArg(gZeroDiags));
474
475 usedEfficientPath = true;
476 }
477 }
478
479 if (!usedEfficientPath) {
480 RCP<const Map> rowMap = Ac->getRowMap();
481 RCP<Vector> diagVec = VectorFactory::Build(rowMap);
482 Ac->getLocalDiagCopy(*diagVec);
483
484 LocalOrdinal lZeroDiags = 0;
485 Teuchos::ArrayRCP<const Scalar> diagVal = diagVec->getData(0);
486
487 for (size_t i = 0; i < rowMap->getLocalNumElements(); i++) {
488 if (TST::magnitude(diagVal[i]) <= threshold) {
489 lZeroDiags++;
490 }
491 }
492
493 Teuchos::reduceAll(*(rowMap->getComm()), Teuchos::REDUCE_SUM, Teuchos::as<GlobalOrdinal>(lZeroDiags),
494 Teuchos::outArg(gZeroDiags));
495
496 if (repairZeroDiagonals && gZeroDiags > 0) {
497 /*
498 TAW: If Ac has empty rows, put a 1 on the diagonal of Ac. Be aware that Ac might have empty rows AND
499 columns. The columns might not exist in the column map at all. It would be nice to add the entries
500 to the original matrix Ac. But then we would have to use insertLocalValues. However we cannot add
501 new entries for local column indices that do not exist in the column map of Ac (at least Epetra is
502 not able to do this). With Tpetra it is also not possible to add new entries after the FillComplete
503 call with a static map, since the column map already exists and the diagonal entries are completely
504 missing. Here we build a diagonal matrix with zeros on the diagonal and ones on the diagonal for
505 the rows where Ac has empty rows We have to build a new matrix to be able to use insertGlobalValues.
506 Then we add the original matrix Ac to our new block diagonal matrix and use the result as new
507 (non-singular) matrix Ac. This is very inefficient.
508
509 If you know something better, please let me know.
510 */
511 RCP<Matrix> fixDiagMatrix = MatrixFactory::Build(rowMap, 1);
513 Teuchos::Array<Scalar> valout(1);
514 for (size_t r = 0; r < rowMap->getLocalNumElements(); r++) {
515 if (TST::magnitude(diagVal[r]) <= threshold) {
516 GlobalOrdinal grid = rowMap->getGlobalElement(r);
517 indout[0] = grid;
518 valout[0] = replacementValue;
519 fixDiagMatrix->insertGlobalValues(grid, indout(), valout());
520 }
521 }
522 {
523 Teuchos::TimeMonitor m1(*Teuchos::TimeMonitor::getNewTimer("CheckRepairMainDiagonal: fillComplete1"));
524 fixDiagMatrix->fillComplete(Ac->getDomainMap(), Ac->getRangeMap());
525 }
526
527 RCP<Matrix> newAc;
528 Xpetra::MatrixMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::TwoMatrixAdd(*Ac, false, 1.0, *fixDiagMatrix, false, 1.0, newAc, fos);
529 if (Ac->IsView("stridedMaps"))
530 newAc->CreateView("stridedMaps", Ac);
531
532 Ac = Teuchos::null; // free singular matrix
533 fixDiagMatrix = Teuchos::null;
534 Ac = newAc; // set fixed non-singular matrix
535
536 // call fillComplete with optimized storage option set to true
537 // This is necessary for new faster Epetra MM kernels.
539 p->set("DoOptimizeStorage", true);
540 {
541 Teuchos::TimeMonitor m1(*Teuchos::TimeMonitor::getNewTimer("CheckRepairMainDiagonal: fillComplete2"));
542 Ac->fillComplete(p);
543 }
544 } // end repair
545 }
546
547 // print some output
548 fos << "CheckRepairMainDiagonal: " << (repairZeroDiagonals ? "repaired " : "found ")
549 << gZeroDiags << " too small entries (threshold = " << threshold << ") on main diagonal of Ac." << std::endl;
550
551#ifdef HAVE_XPETRA_DEBUG // only for debugging
552 {
553 // check whether Ac has been repaired...
554 RCP<const Map> rowMap = Ac->getRowMap();
555 RCP<Vector> diagVec = VectorFactory::Build(rowMap);
557 Ac->getLocalDiagCopy(*diagVec);
558 diagVal = diagVec->getData(0);
559 for (size_t r = 0; r < Ac->getRowMap()->getLocalNumElements(); r++) {
560 if (TST::magnitude(diagVal[r]) <= threshold) {
561 fos << "Error: there are too small entries left on diagonal after repair..." << std::endl;
562 break;
563 }
564 }
565 }
566#endif
567}
568
569template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
571 const Teuchos::ArrayView<const double>& relativeThreshold, Teuchos::FancyOStream& fos) {
572 Teuchos::TimeMonitor m1(*Teuchos::TimeMonitor::getNewTimer("RelativeDiagonalBoost"));
573
574 TEUCHOS_TEST_FOR_EXCEPTION(A->GetFixedBlockSize() != relativeThreshold.size() && relativeThreshold.size() != 1, Xpetra::Exceptions::Incompatible, "Xpetra::MatrixUtils::RelativeDiagonal Boost: Either A->GetFixedBlockSize() != relativeThreshold.size() OR relativeThreshold.size() == 1");
575
576 LocalOrdinal numPDEs = A->GetFixedBlockSize();
577 typedef typename Teuchos::ScalarTraits<Scalar> TST;
579 Scalar zero = TST::zero();
580 Scalar one = TST::one();
581
582 // Get the diagonal
583 RCP<Vector> diag = VectorFactory::Build(A->getRowMap());
584 A->getLocalDiagCopy(*diag);
585 Teuchos::ArrayRCP<const Scalar> dataVal = diag->getData(0);
586 size_t N = A->getRowMap()->getLocalNumElements();
587
588 // Compute the diagonal maxes for each PDE
589 std::vector<MT> l_diagMax(numPDEs), g_diagMax(numPDEs);
590 for (size_t i = 0; i < N; i++) {
591 int pde = (int)(i % numPDEs);
592 if ((int)i < numPDEs)
593 l_diagMax[pde] = TST::magnitude(dataVal[i]);
594 else
595 l_diagMax[pde] = std::max(l_diagMax[pde], TST::magnitude(dataVal[i]));
596 }
597 Teuchos::reduceAll(*A->getRowMap()->getComm(), Teuchos::REDUCE_MAX, numPDEs, l_diagMax.data(), g_diagMax.data());
598
599 // Apply the diagonal maxes via matrix-matrix addition
600 RCP<Matrix> boostMatrix = MatrixFactory::Build(A->getRowMap(), 1);
602 Teuchos::Array<Scalar> value(1);
603 for (size_t i = 0; i < N; i++) {
604 GlobalOrdinal GRID = A->getRowMap()->getGlobalElement(i);
605 int pde = (int)(i % numPDEs);
606 index[0] = GRID;
607 if (TST::magnitude(dataVal[i]) < relativeThreshold[pde] * g_diagMax[pde])
608 value[0] = relativeThreshold[pde] * g_diagMax[pde] - TST::magnitude(dataVal[i]);
609 else
610 value[0] = zero;
611 boostMatrix->insertGlobalValues(GRID, index(), value());
612 }
613 boostMatrix->fillComplete(A->getDomainMap(), A->getRangeMap());
614
615 // FIXME: We really need an add that lets you "add into"
616 RCP<Matrix> newA;
617 Xpetra::MatrixMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::TwoMatrixAdd(*A, false, one, *boostMatrix, false, one, newA, fos);
618 if (A->IsView("stridedMaps"))
619 newA->CreateView("stridedMaps", A);
620 A = newA;
621 A->fillComplete();
622}
623
624template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
635
636template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
638 bool doTranspose,
640 const UnderlyingLib lib = blockDiagonal.getMap()->lib();
641
642 if (lib == Xpetra::UseTpetra) {
645 Tpetra::Details::inverseScaleBlockDiagonal(Dt, doTranspose, St);
646 }
647}
648
649template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
651 RCP<const Map> rowMap = A.getRowMap();
652 RCP<const Map> colMap = A.getColMap();
653 bool fail = false;
654 if (rowMap->lib() == Xpetra::UseTpetra) {
655 auto tpRowMap = Teuchos::rcp_dynamic_cast<const TpetraMap>(rowMap, true)->getTpetra_Map();
656 auto tpColMap = Teuchos::rcp_dynamic_cast<const TpetraMap>(colMap, true)->getTpetra_Map();
657 fail = !tpColMap->isLocallyFitted(*tpRowMap);
658 } else {
659 RCP<const Teuchos::Comm<int>> comm = rowMap->getComm();
660 LO numRows = Teuchos::as<LocalOrdinal>(rowMap->getLocalNumElements());
661
662 for (LO rowLID = 0; rowLID < numRows; rowLID++) {
663 GO rowGID = rowMap->getGlobalElement(rowLID);
664 LO colGID = colMap->getGlobalElement(rowLID);
665 if (rowGID != colGID) {
666 fail = true;
667 std::cerr << "On rank " << comm->getRank() << ", LID " << rowLID << " is GID " << rowGID << " in the rowmap, but GID " << colGID << " in the column map.\n";
668 }
669 }
670 }
672 "Local parts of row and column map do not match!");
673}
674
675template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
678 std::vector<size_t>& rangeStridingInfo, std::vector<size_t>& domainStridingInfo) {
679 RCP<const StridedMap> stridedRowMap = StridedMapFactory::Build(matrix->getRowMap(), rangeStridingInfo, -1, 0);
680 RCP<const StridedMap> stridedColMap = StridedMapFactory::Build(matrix->getColMap(), domainStridingInfo, -1, 0);
681
682 if (matrix->IsView("stridedMaps") == true) matrix->RemoveView("stridedMaps");
683 matrix->CreateView("stridedMaps", stridedRowMap, stridedColMap);
684}
685
686} // end namespace Xpetra
687
688#endif // PACKAGES_XPETRA_SUP_MATRIX_UTILS_DEF_HPP_
LocalOrdinal LO
GlobalOrdinal GO
size_type size() const
void push_back(const value_type &x)
size_type size() const
bool is_null() const
static RCP< Time > getNewTimer(const std::string &name)
virtual Teuchos::RCP< const Map< LocalOrdinal, GlobalOrdinal, Node > > getMap() const =0
The Map describing the parallel distribution of this object.
Exception throws to report incompatible objects (like maps).
Exception throws to report errors in the internal logical of the program.
static RCP< const Tpetra::CrsMatrix< SC, LO, GO, NO > > Op2TpetraCrs(RCP< Matrix > Op)
static RCP< Import< LocalOrdinal, GlobalOrdinal, Node > > Build(const RCP< const Map< LocalOrdinal, GlobalOrdinal, Node > > &source, const RCP< const Map< LocalOrdinal, GlobalOrdinal, Node > > &target, const Teuchos::RCP< Teuchos::ParameterList > &plist=Teuchos::null)
Constructor specifying the number of non-zeros for all rows.
static Teuchos::RCP< Xpetra::MapExtractor< Scalar, LocalOrdinal, GlobalOrdinal, Node > > Build(const Teuchos::RCP< const Map > &fullmap, const std::vector< Teuchos::RCP< const Map > > &maps, bool bThyraMode=false)
Build MapExtrasctor from a given set of partial maps.
static Teuchos::RCP< Map< LocalOrdinal, GlobalOrdinal, Node > > Build(UnderlyingLib lib, global_size_t numGlobalElements, GlobalOrdinal indexBase, const Teuchos::RCP< const Teuchos::Comm< int > > &comm, LocalGlobal lg=Xpetra::GloballyDistributed)
Map constructor with Xpetra-defined contiguous uniform distribution.
virtual UnderlyingLib lib() const =0
Get the library used by this object (Tpetra or Epetra?)
virtual bool isNodeGlobalElement(GlobalOrdinal globalIndex) const =0
Whether the given global index is valid for this Map on this process.
static Teuchos::RCP< const Xpetra::Map< LocalOrdinal, GlobalOrdinal, Node > > concatenateMaps(const std::vector< Teuchos::RCP< const Xpetra::Map< LocalOrdinal, GlobalOrdinal, Node > > > &subMaps)
Helper function to concatenate several maps.
static Teuchos::RCP< Xpetra::Map< LocalOrdinal, GlobalOrdinal, Node > > shrinkMapGIDs(const Xpetra::Map< LocalOrdinal, GlobalOrdinal, Node > &input, const Xpetra::Map< LocalOrdinal, GlobalOrdinal, Node > &nonOvlInput)
Helper function to shrink the GIDs and generate a standard map whith GIDs starting at 0.
static RCP< Matrix > Build(const RCP< const Map > &rowMap)
Xpetra-specific matrix class.
virtual const RCP< const Map > & getRowMap() const
Returns the Map that describes the row distribution in this matrix.
virtual const RCP< const Map > & getColMap() const
Returns the Map that describes the column distribution in this matrix. This might be null until fillC...
virtual void getLocalRowView(LocalOrdinal LocalRow, ArrayView< const LocalOrdinal > &indices, ArrayView< const Scalar > &values) const =0
Extract a const, non-persisting view of local indices in a specified row of the matrix.
virtual size_t getLocalMaxNumRowEntries() const =0
Returns the maximum number of entries across all rows/columns on this node.
static void TwoMatrixAdd(const Matrix &A, bool transposeA, SC alpha, Matrix &B, SC beta)
Helper function to calculate B = alpha*A + beta*B.
static void inverseScaleBlockDiagonal(Xpetra::MultiVector< Scalar, LocalOrdinal, GlobalOrdinal, Node > &blockDiagonal, bool doTranspose, Xpetra::MultiVector< Scalar, LocalOrdinal, GlobalOrdinal, Node > &toBeScaled)
static void convertMatrixToStridedMaps(Teuchos::RCP< Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > > matrix, std::vector< size_t > &rangeStridingInfo, std::vector< size_t > &domainStridingInfo)
static void CheckRepairMainDiagonal(RCP< Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > > &Ac, bool const &repairZeroDiagonals, Teuchos::FancyOStream &fos, const typename Teuchos::ScalarTraits< Scalar >::magnitudeType threshold=Teuchos::ScalarTraits< typename Teuchos::ScalarTraits< Scalar >::magnitudeType >::zero(), const Scalar replacementValue=Teuchos::ScalarTraits< Scalar >::one())
static Teuchos::RCP< Xpetra::Map< LocalOrdinal, GlobalOrdinal, Node > > findColumnSubMap(const Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > &input, const Xpetra::Map< LocalOrdinal, GlobalOrdinal, Node > &domainMap)
static void RelativeDiagonalBoost(RCP< Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > > &A, const Teuchos::ArrayView< const double > &relativeThreshold, Teuchos::FancyOStream &fos)
static void checkLocalRowMapMatchesColMap(const Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > &A)
static Teuchos::RCP< Xpetra::BlockedCrsMatrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > > SplitMatrix(const Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > &input, Teuchos::RCP< const Xpetra::MapExtractor< Scalar, LocalOrdinal, GlobalOrdinal, Node > > rangeMapExtractor, Teuchos::RCP< const Xpetra::MapExtractor< Scalar, LocalOrdinal, GlobalOrdinal, Node > > domainMapExtractor, Teuchos::RCP< const Xpetra::MapExtractor< Scalar, LocalOrdinal, GlobalOrdinal, Node > > columnMapExtractor=Teuchos::null, bool bThyraMode=false)
static Teuchos::RCP< Xpetra::MultiVector< Scalar, LocalOrdinal, GlobalOrdinal, Node > > xpetraGidNumbering2ThyraGidNumbering(const Xpetra::MultiVector< Scalar, LocalOrdinal, GlobalOrdinal, Node > &input)
static void extractBlockDiagonal(const Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > &A, Xpetra::MultiVector< Scalar, LocalOrdinal, GlobalOrdinal, Node > &diagonal)
static Teuchos::RCP< MultiVector< Scalar, LocalOrdinal, GlobalOrdinal, Node > > Build(const Teuchos::RCP< const Map< LocalOrdinal, GlobalOrdinal, Node > > &map, size_t NumVectors, bool zeroOut=true)
Constructor specifying the number of non-zeros for all rows.
virtual size_t getNumVectors() const =0
Number of columns in the multivector.
virtual Teuchos::ArrayRCP< const Scalar > getData(size_t j) const =0
Const view of the local values in a particular vector of this multivector.
virtual size_t getLocalLength() const =0
Local number of rows on the calling process.
virtual const Teuchos::RCP< const map_type > getDomainMap() const =0
The Map associated with the domain of this operator, which must be compatible with X....
virtual const Teuchos::RCP< const map_type > getRangeMap() const =0
The Map associated with the range of this operator, which must be compatible with Y....
static RCP< Xpetra::StridedMap< LocalOrdinal, GlobalOrdinal, Node > > Build(UnderlyingLib lib, global_size_t numGlobalElements, GlobalOrdinal indexBase, std::vector< size_t > &stridingInfo, const Teuchos::RCP< const Teuchos::Comm< int > > &comm, LocalOrdinal stridedBlockId=-1, GlobalOrdinal offset=0, LocalGlobal lg=Xpetra::GloballyDistributed)
Map constructor with Xpetra-defined contiguous uniform distribution.
Class that stores a strided map.
static Teuchos::RCP< Xpetra::Vector< Scalar, LocalOrdinal, GlobalOrdinal, Node > > Build(const Teuchos::RCP< const Xpetra::Map< LocalOrdinal, GlobalOrdinal, Node > > &map, bool zeroOut=true)
Constructor specifying the number of non-zeros for all rows.
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)
RCP< const Tpetra::CrsGraph< LocalOrdinal, GlobalOrdinal, Node > > toTpetra(const RCP< const CrsGraph< LocalOrdinal, GlobalOrdinal, Node > > &graph)