10#include "Teko_ProbingPreconditionerFactory.hpp"
12#include "Teko_TpetraOperatorWrapper.hpp"
13#include "Teko_PreconditionerInverseFactory.hpp"
14#include "Teko_RequestHandler.hpp"
15#include "Teko_RequestMesg.hpp"
17#include "Thyra_TpetraLinearOp.hpp"
18#include "Thyra_DefaultLinearOpSource.hpp"
19#include "Thyra_TpetraThyraWrappers.hpp"
21#include "KokkosGraph_Distance2ColorHandle.hpp"
22#include "KokkosGraph_Distance2Color.hpp"
23#include "KokkosKernels_Handle.hpp"
25#include <Kokkos_Sort.hpp>
31using Teuchos::rcp_dynamic_cast;
40using exec_space =
typename NT::device_type::execution_space;
42template <
class RowMapType,
class EntriesType>
43struct LocalCrsGraphViews {
48template <
class MVType,
class HostColorViewType>
49void set_probe_by_color_host(
const Teuchos::RCP<MVType>& probeVec,
50 const HostColorViewType& h_colors,
51 const std::vector<LO>& colLidToDomainLid,
const LO color) {
52 const LO invalid = Teuchos::OrdinalTraits<LO>::invalid();
54 probeVec->putScalar(Teuchos::ScalarTraits<ST>::zero());
56 auto hostView = probeVec->getLocalViewHost(Tpetra::Access::ReadWrite);
57 const LO numLocalCols =
static_cast<LO
>(colLidToDomainLid.size());
59 for (LO lcol = 0; lcol < numLocalCols; ++lcol) {
60 if (
static_cast<LO
>(h_colors(lcol)) == color) {
61 const LO domainLid = colLidToDomainLid[
static_cast<size_t>(lcol)];
62 if (domainLid != invalid) {
63 hostView(domainLid, 0) = Teuchos::ScalarTraits<ST>::one();
69template <
class RowPtrViewType,
class ColIndViewType,
class ColorViewType,
class ResponseViewType,
71void decode_probe_by_color(
const RowPtrViewType& rowPtrs,
const ColIndViewType& localColInds,
72 const ColorViewType& colors,
const ResponseViewType& responseView,
73 const LO numRows,
const LO color,
const ValuesViewType& values) {
75 "Teko::ProbingPreconditionerFactory::decode_probe",
76 Kokkos::RangePolicy<exec_space>(0, numRows), KOKKOS_LAMBDA(
const LO lrow) {
77 const auto rowStart = rowPtrs(lrow);
78 const auto rowEnd = rowPtrs(lrow + 1);
79 const ST rowValue = responseView(lrow, 0);
81 for (
auto entry = rowStart; entry < rowEnd; ++entry) {
82 const LO lcol =
static_cast<LO
>(localColInds(entry));
83 if (colors(lcol) == color) {
84 values(entry) = rowValue;
90template <
class RowPtrViewType>
91LO compute_max_row_length(
const RowPtrViewType& rowPtrs,
const LO numRows) {
92 LO maxNumEntriesPerRow = 0;
94 Kokkos::parallel_reduce(
95 "Teko::ProbingPreconditionerFactory::max_row_length",
96 Kokkos::RangePolicy<exec_space>(0, numRows),
97 KOKKOS_LAMBDA(
const LO r, LO& localMax) {
98 const LO rowLen =
static_cast<LO
>(rowPtrs(r + 1) - rowPtrs(r));
99 if (rowLen > localMax) localMax = rowLen;
101 Kokkos::Max<LO>(maxNumEntriesPerRow));
103 return maxNumEntriesPerRow;
106template <
class RowMapViewType>
107typename RowMapViewType::non_const_value_type count_candidate_col_graph_edges(
108 const RowMapViewType& row_map_copy,
const LO numRows) {
109 using row_map_value_type =
typename RowMapViewType::non_const_value_type;
111 row_map_value_type candidateEdgeCount = 0;
112 Kokkos::parallel_reduce(
113 "Teko::ProbingPreconditionerFactory::count_candidate_col_graph_edges",
114 Kokkos::RangePolicy<exec_space>(0, numRows),
115 KOKKOS_LAMBDA(
const LO row, row_map_value_type& update) {
116 const row_map_value_type d = row_map_copy(row + 1) - row_map_copy(row);
117 if (d > 1) update += d * (d - 1);
121 return candidateEdgeCount;
124template <
class RowMapDeviceView,
class EntriesDeviceView>
125LocalCrsGraphViews<Kokkos::View<
typename RowMapDeviceView::non_const_value_type*,
126 typename RowMapDeviceView::device_type>,
127 Kokkos::View<
typename EntriesDeviceView::non_const_value_type*,
128 typename EntriesDeviceView::device_type> >
129build_column_intersection_graph_device(
const RowMapDeviceView& row_map_in,
130 const EntriesDeviceView& entries_in,
const LO numRows,
131 const LO numLocalCols) {
132 using row_offset_type =
typename RowMapDeviceView::non_const_value_type;
133 using col_index_type =
typename EntriesDeviceView::non_const_value_type;
134 using device_type =
typename RowMapDeviceView::device_type;
135 using key_type = std::uint64_t;
137 using row_map_out_type = Kokkos::View<row_offset_type*, device_type>;
138 using entries_out_type = Kokkos::View<col_index_type*, device_type>;
140 const row_offset_type numCandidateKeys = count_candidate_col_graph_edges(row_map_in, numRows);
142 Kokkos::View<row_offset_type*, device_type> candidateOffsets(
143 Kokkos::ViewAllocateWithoutInitializing(
"teko_probe_candidate_offsets"),
144 static_cast<size_t>(numRows) + 1);
146 Kokkos::parallel_for(
147 "Teko::ProbingPreconditionerFactory::fill_candidate_offsets",
148 Kokkos::RangePolicy<exec_space>(0, numRows), KOKKOS_LAMBDA(
const LO row) {
149 const row_offset_type d = row_map_in(row + 1) - row_map_in(row);
150 candidateOffsets(row) = (d > 1 ? d * (d - 1) : 0);
153 Kokkos::parallel_scan(
154 "Teko::ProbingPreconditionerFactory::scan_candidate_offsets",
155 Kokkos::RangePolicy<exec_space>(0, numRows + 1),
156 KOKKOS_LAMBDA(
const LO i, row_offset_type& update,
const bool final_pass) {
157 const row_offset_type val = (i < numRows ? candidateOffsets(i) : row_offset_type(0));
158 if (final_pass) candidateOffsets(i) = update;
162 Kokkos::View<key_type*, device_type> candidateKeys(
163 Kokkos::ViewAllocateWithoutInitializing(
"teko_probe_candidate_keys"),
164 static_cast<size_t>(numCandidateKeys));
166 const key_type numColsKey =
static_cast<key_type
>(numLocalCols);
168 Kokkos::parallel_for(
169 "Teko::ProbingPreconditionerFactory::fill_candidate_keys",
170 Kokkos::RangePolicy<exec_space>(0, numRows), KOKKOS_LAMBDA(
const LO row) {
171 const auto rowStart = row_map_in(row);
172 const auto rowEnd = row_map_in(row + 1);
173 const row_offset_type d = rowEnd - rowStart;
174 row_offset_type pos = candidateOffsets(row);
176 for (row_offset_type i = 0; i < d; ++i) {
177 const key_type src =
static_cast<key_type
>(entries_in(rowStart + i));
178 for (row_offset_type j = 0; j < d; ++j) {
179 if (i == j)
continue;
180 const key_type dst =
static_cast<key_type
>(entries_in(rowStart + j));
181 candidateKeys(pos++) = src * numColsKey + dst;
186 Kokkos::sort(candidateKeys);
188 Kokkos::View<row_offset_type*, device_type> uniqueFlags(
189 Kokkos::ViewAllocateWithoutInitializing(
"teko_probe_unique_flags"),
190 static_cast<size_t>(numCandidateKeys));
192 Kokkos::parallel_for(
193 "Teko::ProbingPreconditionerFactory::mark_unique_candidate_keys",
194 Kokkos::RangePolicy<exec_space>(0, numCandidateKeys), KOKKOS_LAMBDA(
const row_offset_type i) {
198 uniqueFlags(i) = (candidateKeys(i) != candidateKeys(i - 1) ? 1 : 0);
202 Kokkos::View<row_offset_type*, device_type> uniqueOffsets(
203 Kokkos::ViewAllocateWithoutInitializing(
"teko_probe_unique_offsets"),
204 static_cast<size_t>(numCandidateKeys) + 1);
206 Kokkos::parallel_scan(
207 "Teko::ProbingPreconditionerFactory::scan_unique_flags",
208 Kokkos::RangePolicy<exec_space>(0, numCandidateKeys + 1),
209 KOKKOS_LAMBDA(
const row_offset_type i, row_offset_type& update,
const bool final_pass) {
210 const row_offset_type val = (i < numCandidateKeys ? uniqueFlags(i) : row_offset_type(0));
211 if (final_pass) uniqueOffsets(i) = update;
215 auto h_uniqueCount = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), uniqueOffsets);
216 const row_offset_type numUniqueKeys = h_uniqueCount(numCandidateKeys);
218 Kokkos::View<key_type*, device_type> uniqueKeys(
219 Kokkos::ViewAllocateWithoutInitializing(
"teko_probe_unique_keys"),
220 static_cast<size_t>(numUniqueKeys));
222 Kokkos::parallel_for(
223 "Teko::ProbingPreconditionerFactory::pack_unique_keys",
224 Kokkos::RangePolicy<exec_space>(0, numCandidateKeys), KOKKOS_LAMBDA(
const row_offset_type i) {
225 if (uniqueFlags(i)) {
226 uniqueKeys(uniqueOffsets(i)) = candidateKeys(i);
230 Kokkos::View<row_offset_type*, device_type> rowCounts(
231 Kokkos::ViewAllocateWithoutInitializing(
"teko_probe_row_counts"),
232 static_cast<size_t>(numLocalCols));
233 Kokkos::deep_copy(rowCounts, row_offset_type(0));
235 Kokkos::parallel_for(
236 "Teko::ProbingPreconditionerFactory::count_unique_keys_per_row",
237 Kokkos::RangePolicy<exec_space>(0, numUniqueKeys), KOKKOS_LAMBDA(
const row_offset_type i) {
238 const key_type key = uniqueKeys(i);
239 const row_offset_type src =
static_cast<row_offset_type
>(key / numColsKey);
240 Kokkos::atomic_fetch_add(&rowCounts(src), row_offset_type(1));
243 row_map_out_type row_map_out(
244 Kokkos::ViewAllocateWithoutInitializing(
"teko_probe_col_graph_row_map_dev"),
245 static_cast<size_t>(numLocalCols) + 1);
247 Kokkos::parallel_scan(
248 "Teko::ProbingPreconditionerFactory::scan_col_graph_row_map",
249 Kokkos::RangePolicy<exec_space>(0, numLocalCols + 1),
250 KOKKOS_LAMBDA(
const LO i, row_offset_type& update,
const bool final_pass) {
251 const row_offset_type val = (i < numLocalCols ? rowCounts(i) : row_offset_type(0));
252 if (final_pass) row_map_out(i) = update;
256 entries_out_type entries_out(
257 Kokkos::ViewAllocateWithoutInitializing(
"teko_probe_col_graph_entries_dev"),
258 static_cast<size_t>(numUniqueKeys));
260 Kokkos::View<row_offset_type*, device_type> fillOffsets(
261 Kokkos::ViewAllocateWithoutInitializing(
"teko_probe_fill_offsets"),
262 static_cast<size_t>(numLocalCols));
264 Kokkos::parallel_for(
265 "Teko::ProbingPreconditionerFactory::init_fill_offsets",
266 Kokkos::RangePolicy<exec_space>(0, numLocalCols),
267 KOKKOS_LAMBDA(
const LO col) { fillOffsets(col) = row_map_out(col); });
269 Kokkos::parallel_for(
270 "Teko::ProbingPreconditionerFactory::fill_col_graph_entries",
271 Kokkos::RangePolicy<exec_space>(0, numUniqueKeys), KOKKOS_LAMBDA(
const row_offset_type i) {
272 const key_type key = uniqueKeys(i);
273 const row_offset_type src =
static_cast<row_offset_type
>(key / numColsKey);
274 const col_index_type dst =
static_cast<col_index_type
>(key % numColsKey);
275 const row_offset_type pos = Kokkos::atomic_fetch_add(&fillOffsets(src), row_offset_type(1));
276 entries_out(pos) = dst;
279 LocalCrsGraphViews<row_map_out_type, entries_out_type> out = {row_map_out, entries_out};
291 RCP<Tpetra::CrsMatrix<ST, LO, GO, NT> > probedMat = probe(lo);
293 LinearOp probedOp = Thyra::tpetraLinearOp<ST, LO, GO, NT>(
294 Thyra::tpetraVectorSpace<ST, LO, GO, NT>(probedMat->getDomainMap()),
295 Thyra::tpetraVectorSpace<ST, LO, GO, NT>(probedMat->getRangeMap()), probedMat);
303 const std::string inverse_type =
"Inverse Type";
304 const std::string probing_graph_operator =
"Probing Graph Operator";
305 const std::string probing_graph =
"Probing Graph";
306 const std::string user_graph =
"User Will Set Probing Graph";
308 std::string invStr =
"Ifpack2";
309 if (pl.isParameter(inverse_type)) invStr = pl.get<std::string>(inverse_type);
311 if (pl.isParameter(probing_graph_operator))
312 setGraphOperator(pl.get<Teko::LinearOp>(probing_graph_operator));
313 else if (pl.isParameter(probing_graph))
314 setGraph(pl.get<RCP<
const Tpetra::CrsGraph<LO, GO, NT> > >(probing_graph));
315 else if (pl.isParameter(user_graph) && pl.get<
bool>(user_graph)) {
319 rh->preRequest<RCP<const Tpetra::CrsGraph<LO, GO, NT> > >(Teko::RequestMesg(
"Probing Graph"));
321 rh->request<RCP<
const Tpetra::CrsGraph<LO, GO, NT> > >(Teko::RequestMesg(
"Probing Graph")));
324 setInverseFactory(invLib->getInverseFactory(invStr));
327void ProbingPreconditionerFactory::setGraphOperator(
const Teko::LinearOp& graphOp) {
328 RCP<const Thyra::TpetraLinearOp<ST, LO, GO, NT> > tOp =
329 rcp_dynamic_cast<const Thyra::TpetraLinearOp<ST, LO, GO, NT> >(graphOp,
true);
330 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT> > crsMatrix =
331 rcp_dynamic_cast<const Tpetra::CrsMatrix<ST, LO, GO, NT> >(tOp->getConstTpetraOperator(),
333 setGraph(crsMatrix->getCrsGraph());
336void ProbingPreconditionerFactory::setGraph(
337 const Teuchos::RCP<
const Tpetra::CrsGraph<LO, GO, NT> >& graph) {
341RCP<Tpetra::CrsMatrix<ST, LO, GO, NT> > ProbingPreconditionerFactory::probe(
342 const LinearOp& lo)
const {
343 TEUCHOS_TEST_FOR_EXCEPTION(graph_ == Teuchos::null, std::runtime_error,
344 "ProbingPreconditionerFactory::probe: probing graph is null");
346 RCP<const Thyra::LinearOpBase<ST> > thyraOp = lo;
347 RCP<const Thyra::TpetraLinearOp<ST, LO, GO, NT> > tOp =
348 rcp_dynamic_cast<const Thyra::TpetraLinearOp<ST, LO, GO, NT> >(thyraOp,
true);
349 RCP<const Tpetra::Operator<ST, LO, GO, NT> > tpetraOp = tOp->getConstTpetraOperator();
351 RCP<const Tpetra::Map<LO, GO, NT> > domainMap = tpetraOp->getDomainMap();
352 RCP<const Tpetra::Map<LO, GO, NT> > rangeMap = tpetraOp->getRangeMap();
353 RCP<const Tpetra::Map<LO, GO, NT> > rowMap = graph_->getRowMap();
354 RCP<const Tpetra::Map<LO, GO, NT> > colMap = graph_->getColMap();
356 using local_graph_device_type =
typename Tpetra::CrsGraph<LO, GO, NT>::local_graph_device_type;
357 using row_map_type =
typename local_graph_device_type::row_map_type;
358 using index_type =
typename local_graph_device_type::entries_type;
359 using local_matrix_type =
typename Tpetra::CrsMatrix<ST, LO, GO, NT>::local_matrix_device_type;
360 using values_type =
typename local_matrix_type::values_type::non_const_type;
361 using exec_space =
typename NT::device_type::execution_space;
362 using memory_space =
typename NT::device_type::memory_space;
363 using row_map_nonc_type =
typename row_map_type::non_const_type;
364 using entries_nonc_type =
typename index_type::non_const_type;
366 using kernel_handle_type =
367 KokkosKernels::Experimental::KokkosKernelsHandle<
typename row_map_type::value_type,
368 typename index_type::value_type, ST,
369 exec_space, memory_space, memory_space>;
371 const LO numRows =
static_cast<LO
>(graph_->getLocalNumRows());
372 const LO numLocalCols =
static_cast<LO
>(colMap->getLocalNumElements());
374 row_map_nonc_type row_map_copy;
375 entries_nonc_type entries_copy;
376 LO totalNumEntries = 0;
377 LO maxNumEntriesPerRow = 0;
380 auto localGraphDevice = graph_->getLocalGraphDevice();
383 row_map_nonc_type(Kokkos::ViewAllocateWithoutInitializing(
"teko_probe_row_map_copy"),
384 localGraphDevice.row_map.extent(0));
386 entries_nonc_type(Kokkos::ViewAllocateWithoutInitializing(
"teko_probe_entries_copy"),
387 localGraphDevice.entries.extent(0));
389 Kokkos::deep_copy(row_map_copy, localGraphDevice.row_map);
390 Kokkos::deep_copy(entries_copy, localGraphDevice.entries);
392 totalNumEntries =
static_cast<LO
>(entries_copy.extent(0));
393 maxNumEntriesPerRow = compute_max_row_length(row_map_copy, numRows);
397 build_column_intersection_graph_device(row_map_copy, entries_copy, numRows, numLocalCols);
399 kernel_handle_type kh;
400 kh.create_distance2_graph_coloring_handle(KokkosGraph::COLORING_D2_DEFAULT);
402 KokkosGraph::Experimental::graph_color_distance2(&kh, numLocalCols, colGraph.row_map,
405 auto coloringHandle = kh.get_distance2_graph_coloring_handle();
406 auto colors = coloringHandle->get_vertex_colors();
407 const LO numColors =
static_cast<LO
>(coloringHandle->get_num_colors());
409 TEUCHOS_TEST_FOR_EXCEPTION(
static_cast<LO
>(colors.extent(0)) < numLocalCols, std::runtime_error,
410 "ProbingPreconditionerFactory::probe: colors view extent ("
411 << colors.extent(0) <<
") is smaller than local column count ("
412 << numLocalCols <<
").");
414 std::vector<LO> colLidToDomainLid(
static_cast<size_t>(numLocalCols));
416 const LO invalid = Teuchos::OrdinalTraits<LO>::invalid();
417 for (LO lcol = 0; lcol < numLocalCols; ++lcol) {
418 const GO gid = colMap->getGlobalElement(lcol);
420 (gid == Teuchos::OrdinalTraits<GO>::invalid() ? invalid
421 : domainMap->getLocalElement(gid));
422 colLidToDomainLid[
static_cast<size_t>(lcol)] = dlid;
426 values_type values(Kokkos::ViewAllocateWithoutInitializing(
"probed_values"), totalNumEntries);
427 Kokkos::deep_copy(values, Teuchos::ScalarTraits<ST>::zero());
429 auto h_colors = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), colors);
431 RCP<Tpetra::MultiVector<ST, LO, GO, NT> > probeVec =
432 rcp(
new Tpetra::MultiVector<ST, LO, GO, NT>(domainMap, 1));
433 RCP<Tpetra::MultiVector<ST, LO, GO, NT> > response =
434 rcp(
new Tpetra::MultiVector<ST, LO, GO, NT>(rangeMap, 1));
436 for (LO color = 1; color <= numColors; ++color) {
437 set_probe_by_color_host(probeVec, h_colors, colLidToDomainLid, color);
439 tpetraOp->apply(*probeVec, *response, Teuchos::NO_TRANS, Teuchos::ScalarTraits<ST>::one(),
440 Teuchos::ScalarTraits<ST>::zero());
442 auto responseView = response->getLocalViewDevice(Tpetra::Access::ReadOnly);
443 decode_probe_by_color(row_map_copy, entries_copy, colors, responseView, numRows, color, values);
446 kh.destroy_graph_coloring_handle();
448 auto lclMat = local_matrix_type(
"probed_local_matrix", numRows, maxNumEntriesPerRow,
449 totalNumEntries, values, row_map_copy, entries_copy);
452 new Tpetra::CrsMatrix<ST, LO, GO, NT>(lclMat, rowMap, colMap, domainMap, rangeMap));
InverseLinearOp buildInverse(const InverseFactory &factory, const LinearOp &A)
Build an inverse operator using a factory and a linear operator.
Teuchos::RCP< const InverseLibrary > getInverseLibrary() const
Get the inverse library used by this preconditioner factory.
Teuchos::RCP< RequestHandler > getRequestHandler() const
Get the request handler with pointers to the appropriate callbacks.
An implementation of a state object preconditioners.
LinearOp buildPreconditionerOperator(LinearOp &lo, PreconditionerState &state) const override
ProbingPreconditionerFactory()
void initializeFromParameterList(const Teuchos::ParameterList &pl) override
Initialize from a parameter list.