group_blas_rankk.hh
Go to the documentation of this file.
1#pragma once
2
5
6namespace batchlas::device {
7
8namespace detail::generic {
9
10template <typename Tag, typename Group, typename T>
11inline constexpr void rank2k(const Group& group,
13 MatrixMatrixOperand<T> operand) {
14 const int local_id = detail::group_local_linear_id(group);
15 const int local_size = detail::group_local_linear_range(group);
16 const int extent = detail::output_size(a, Tag::trans);
17 const int contract_extent = detail::input_size(a, Tag::trans);
18 constexpr Transpose rhs_transform = detail::rank2k_rhs_transform<Tag>();
19 const T alpha2 = detail::secondary_rank2k_alpha<Tag>(operand.alpha);
20
21 for (int linear_index = local_id; linear_index < extent * extent; linear_index += local_size) {
22 const int row = linear_index % extent;
23 const int col = linear_index / extent;
25 continue;
26 }
27
28 T partial{};
29 for (int k = 0; k < contract_extent; ++k) {
30 const T lhs1 = detail::matrix_entry(a, row, k, Tag::trans);
31 const T rhs1 = detail::matrix_entry(operand.b, k, col, rhs_transform);
32 const T lhs2 = detail::matrix_entry(operand.b, row, k, Tag::trans);
33 const T rhs2 = detail::matrix_entry(a, k, col, rhs_transform);
34 partial += operand.alpha * lhs1 * rhs1 + alpha2 * lhs2 * rhs2;
35 }
36
37 T value = operand.beta * operand.c(row, col) + partial;
38 if constexpr (ComplexScalar<T>) {
39 if constexpr (Tag::hermitian) {
40 if (row == col) {
41 value = T(value.real(), typename T::value_type(0));
42 }
43 }
44 }
45 operand.c(row, col) = value;
46 }
47}
48
49template <typename Tag, typename Group, typename T>
50inline constexpr void rankk(const Group& group,
52 RankKOperand<T> operand) {
53 const int local_id = detail::group_local_linear_id(group);
54 const int local_size = detail::group_local_linear_range(group);
55 const int extent = detail::output_size(a, Tag::trans);
56 const int contract_extent = detail::input_size(a, Tag::trans);
57 constexpr Transpose rhs_transform = detail::rankk_rhs_transform<Tag>();
58
59 for (int linear_index = local_id; linear_index < extent * extent; linear_index += local_size) {
60 const int row = linear_index % extent;
61 const int col = linear_index / extent;
63 continue;
64 }
65
66 T partial{};
67 for (int k = 0; k < contract_extent; ++k) {
68 partial += operand.alpha * detail::matrix_entry(a, row, k, Tag::trans) *
69 detail::matrix_entry(a, k, col, rhs_transform);
70 }
71
72 T value = operand.beta * operand.c(row, col) + partial;
73 if constexpr (ComplexScalar<T>) {
74 if constexpr (Tag::hermitian) {
75 if (row == col) {
76 value = T(value.real(), typename T::value_type(0));
77 }
78 }
79 }
80 operand.c(row, col) = value;
81 }
82}
83
84} // namespace detail::generic
85
86namespace detail::subgroup {
87
88template <typename Item, typename T>
89inline constexpr void rank2k_complex_tiled(const Item& item,
93 ComplexRank2kWorkspace<T>* workspace) {
94 const int linear_tid = detail::item_local_linear_id(item);
95 const int subgroup_id = subgroup_group_id(item);
96 const int local_row = linear_tid / kComplexRank2kLocalCols;
97 const int local_col = linear_tid % kComplexRank2kLocalCols;
98 const int extent = detail::output_size(a, transform.trans);
99 const int contract_extent = detail::input_size(a, transform.trans);
100 const int row_tiles = (extent + kComplexRank2kTileM - 1) / kComplexRank2kTileM;
101 const int col_tiles = (extent + kComplexRank2kTileN - 1) / kComplexRank2kTileN;
102 const int tile_row_start = matrix_tile_group_row(item);
103 const int tile_col_start = matrix_tile_group_col(item);
104 const int tile_row_stride = matrix_tile_group_row_stride(item);
105 const int tile_col_stride = matrix_tile_group_col_stride(item);
106 const Transpose rhs_transform = detail::rank2k_rhs_transform(transform);
107 const T alpha2 = detail::secondary_rank2k_alpha(operand.alpha, transform.hermitian);
108
109 for (int tile_row = tile_row_start; tile_row < row_tiles; tile_row += tile_row_stride) {
110 for (int tile_col = tile_col_start; tile_col < col_tiles; tile_col += tile_col_stride) {
111 const int tile_row_base = tile_row * kComplexRank2kTileM;
112 const int tile_col_base = tile_col * kComplexRank2kTileN;
113 const int row_base = tile_row_base + local_row * kComplexRank2kThreadTileRows;
114 const int col_base = tile_col_base + local_col * kComplexRank2kThreadTileCols;
115
116 if (transform.uplo == Uplo::Lower && tile_row_base + kComplexRank2kTileM <= tile_col_base) {
117 continue;
118 }
119 if (transform.uplo == Uplo::Upper && tile_col_base + kComplexRank2kTileN <= tile_row_base) {
120 continue;
121 }
122
124
125 auto lhs1_loader = [&](int tile_r, int global_k) {
126 const int global_row = tile_row_base + tile_r;
127 if (global_row >= extent) {
128 return T(0);
129 }
130 return operand.alpha * detail::matrix_entry(a, global_row, global_k, transform.trans);
131 };
132 auto rhs1_loader = [&](int tile_c, int global_k) {
133 const int global_col = tile_col_base + tile_c;
134 if (global_col >= extent) {
135 return T(0);
136 }
137 return detail::matrix_entry(operand.b, global_k, global_col, rhs_transform);
138 };
139 auto lhs2_loader = [&](int tile_r, int global_k) {
140 const int global_row = tile_row_base + tile_r;
141 if (global_row >= extent) {
142 return T(0);
143 }
144 return alpha2 * detail::matrix_entry(operand.b, global_row, global_k, transform.trans);
145 };
146 auto rhs2_loader = [&](int tile_c, int global_k) {
147 const int global_col = tile_col_base + tile_c;
148 if (global_col >= extent) {
149 return T(0);
150 }
151 return detail::matrix_entry(a, global_k, global_col, rhs_transform);
152 };
153
155 item, workspace, linear_tid, subgroup_id, local_row, local_col, contract_extent, lhs1_loader, rhs1_loader, accum);
157 item, workspace, linear_tid, subgroup_id, local_row, local_col, contract_extent, lhs2_loader, rhs2_loader, accum);
158
159 write_complex_rank2k_tile(operand, transform, row_base, col_base, extent, accum);
160 }
161 }
162}
163
164template <typename Item, typename T>
165inline constexpr void rankk_complex_tiled(const Item& item,
167 RankKOperand<T> operand,
168 SymmetricRankKTransform transform,
169 ComplexRank2kWorkspace<T>* workspace) {
170 const int linear_tid = detail::item_local_linear_id(item);
171 const int subgroup_id = subgroup_group_id(item);
172 const int local_row = linear_tid / kComplexRank2kLocalCols;
173 const int local_col = linear_tid % kComplexRank2kLocalCols;
174 const int extent = detail::output_size(a, transform.trans);
175 const int contract_extent = detail::input_size(a, transform.trans);
176 const int row_tiles = (extent + kComplexRank2kTileM - 1) / kComplexRank2kTileM;
177 const int col_tiles = (extent + kComplexRank2kTileN - 1) / kComplexRank2kTileN;
178 const int tile_row_start = matrix_tile_group_row(item);
179 const int tile_col_start = matrix_tile_group_col(item);
180 const int tile_row_stride = matrix_tile_group_row_stride(item);
181 const int tile_col_stride = matrix_tile_group_col_stride(item);
182 const Transpose rhs_transform = detail::rankk_rhs_transform(transform);
183 MatrixMatrixOperand<T> rank_operand{a, operand.c, operand.alpha, operand.beta};
184
185 for (int tile_row = tile_row_start; tile_row < row_tiles; tile_row += tile_row_stride) {
186 for (int tile_col = tile_col_start; tile_col < col_tiles; tile_col += tile_col_stride) {
187 const int tile_row_base = tile_row * kComplexRank2kTileM;
188 const int tile_col_base = tile_col * kComplexRank2kTileN;
189 const int row_base = tile_row_base + local_row * kComplexRank2kThreadTileRows;
190 const int col_base = tile_col_base + local_col * kComplexRank2kThreadTileCols;
191
192 if (transform.uplo == Uplo::Lower && tile_row_base + kComplexRank2kTileM <= tile_col_base) {
193 continue;
194 }
195 if (transform.uplo == Uplo::Upper && tile_col_base + kComplexRank2kTileN <= tile_row_base) {
196 continue;
197 }
198
200
201 auto lhs_loader = [&](int tile_r, int global_k) {
202 const int global_row = tile_row_base + tile_r;
203 if (global_row >= extent) {
204 return T(0);
205 }
206 return operand.alpha * detail::matrix_entry(a, global_row, global_k, transform.trans);
207 };
208 auto rhs_loader = [&](int tile_c, int global_k) {
209 const int global_col = tile_col_base + tile_c;
210 if (global_col >= extent) {
211 return T(0);
212 }
213 return detail::matrix_entry(a, global_k, global_col, rhs_transform);
214 };
215
217 item, workspace, linear_tid, subgroup_id, local_row, local_col, contract_extent, lhs_loader, rhs_loader, accum);
218
219 write_complex_rank2k_tile(rank_operand, transform, row_base, col_base, extent, accum);
220 }
221 }
222}
223
224template <typename Item, typename T>
225inline constexpr void rank2k_register_tiled(const Item& item,
228 SymmetricRank2kTransform transform,
229 RegisterMatrixWorkspace<T>* workspace) {
230 const int linear_tid = detail::item_local_linear_id(item);
231 const int subgroup_id = subgroup_group_id(item);
232 const int local_row = linear_tid / kRegisterMatrixLocalCols;
233 const int local_col = linear_tid % kRegisterMatrixLocalCols;
234 const int extent = detail::output_size(a, transform.trans);
235 const int contract_extent = detail::input_size(a, transform.trans);
236 const int row_tiles = (extent + kRegisterMatrixTileM - 1) / kRegisterMatrixTileM;
237 const int col_tiles = (extent + kRegisterMatrixTileN - 1) / kRegisterMatrixTileN;
238 const int tile_row_start = matrix_tile_group_row(item);
239 const int tile_col_start = matrix_tile_group_col(item);
240 const int tile_row_stride = matrix_tile_group_row_stride(item);
241 const int tile_col_stride = matrix_tile_group_col_stride(item);
242 const Transpose rhs_transform = detail::rank2k_rhs_transform(transform);
243 const T alpha2 = detail::secondary_rank2k_alpha(operand.alpha, transform.hermitian);
244
245 for (int tile_row = tile_row_start; tile_row < row_tiles; tile_row += tile_row_stride) {
246 for (int tile_col = tile_col_start; tile_col < col_tiles; tile_col += tile_col_stride) {
247 const int tile_row_base = tile_row * kRegisterMatrixTileM;
248 const int tile_col_base = tile_col * kRegisterMatrixTileN;
249 const int row_base = tile_row * kRegisterMatrixTileM + local_row * kRegisterMatrixThreadTileRows;
250 const int col_base = tile_col * kRegisterMatrixTileN + local_col * kRegisterMatrixThreadTileCols;
251
252 if (transform.uplo == Uplo::Lower && tile_row_base + kRegisterMatrixTileM <= tile_col_base) {
253 continue;
254 }
255 if (transform.uplo == Uplo::Upper && tile_col_base + kRegisterMatrixTileN <= tile_row_base) {
256 continue;
257 }
258
260
261 auto lhs1_loader = [&](int tile_r, int global_k) {
262 const int global_row = tile_row * kRegisterMatrixTileM + tile_r;
263 if (global_row >= extent) {
264 return T(0);
265 }
266 return operand.alpha * detail::matrix_entry(a, global_row, global_k, transform.trans);
267 };
268 auto rhs1_loader = [&](int tile_c, int global_k) {
269 const int global_col = tile_col * kRegisterMatrixTileN + tile_c;
270 if (global_col >= extent) {
271 return T(0);
272 }
273 return detail::matrix_entry(operand.b, global_k, global_col, rhs_transform);
274 };
275 auto lhs2_loader = [&](int tile_r, int global_k) {
276 const int global_row = tile_row * kRegisterMatrixTileM + tile_r;
277 if (global_row >= extent) {
278 return T(0);
279 }
280 return alpha2 * detail::matrix_entry(operand.b, global_row, global_k, transform.trans);
281 };
282 auto rhs2_loader = [&](int tile_c, int global_k) {
283 const int global_col = tile_col * kRegisterMatrixTileN + tile_c;
284 if (global_col >= extent) {
285 return T(0);
286 }
287 return detail::matrix_entry(a, global_k, global_col, rhs_transform);
288 };
289
290 accumulate_rank2k_register_tiled_pass(item, workspace, linear_tid, subgroup_id, contract_extent, lhs1_loader, rhs1_loader, accum);
291 accumulate_rank2k_register_tiled_pass(item, workspace, linear_tid, subgroup_id, contract_extent, lhs2_loader, rhs2_loader, accum);
292
293 write_rank2k_register_tile(operand, transform, row_base, col_base, extent, accum);
294 }
295 }
296}
297
298template <typename Item, typename T>
299inline constexpr void rankk_register_tiled(const Item& item,
301 RankKOperand<T> operand,
302 SymmetricRankKTransform transform,
303 RegisterMatrixWorkspace<T>* workspace) {
304 const int linear_tid = detail::item_local_linear_id(item);
305 const int subgroup_id = subgroup_group_id(item);
306 const int local_row = linear_tid / kRegisterMatrixLocalCols;
307 const int local_col = linear_tid % kRegisterMatrixLocalCols;
308 const int extent = detail::output_size(a, transform.trans);
309 const int contract_extent = detail::input_size(a, transform.trans);
310 const int row_tiles = (extent + kRegisterMatrixTileM - 1) / kRegisterMatrixTileM;
311 const int col_tiles = (extent + kRegisterMatrixTileN - 1) / kRegisterMatrixTileN;
312 const int tile_row_start = matrix_tile_group_row(item);
313 const int tile_col_start = matrix_tile_group_col(item);
314 const int tile_row_stride = matrix_tile_group_row_stride(item);
315 const int tile_col_stride = matrix_tile_group_col_stride(item);
316 const Transpose rhs_transform = detail::rankk_rhs_transform(transform);
317 MatrixMatrixOperand<T> rank_operand{a, operand.c, operand.alpha, operand.beta};
318
319 for (int tile_row = tile_row_start; tile_row < row_tiles; tile_row += tile_row_stride) {
320 for (int tile_col = tile_col_start; tile_col < col_tiles; tile_col += tile_col_stride) {
321 const int tile_row_base = tile_row * kRegisterMatrixTileM;
322 const int tile_col_base = tile_col * kRegisterMatrixTileN;
323 const int row_base = tile_row * kRegisterMatrixTileM + local_row * kRegisterMatrixThreadTileRows;
324 const int col_base = tile_col * kRegisterMatrixTileN + local_col * kRegisterMatrixThreadTileCols;
325
326 if (transform.uplo == Uplo::Lower && tile_row_base + kRegisterMatrixTileM <= tile_col_base) {
327 continue;
328 }
329 if (transform.uplo == Uplo::Upper && tile_col_base + kRegisterMatrixTileN <= tile_row_base) {
330 continue;
331 }
332
334
335 auto lhs_loader = [&](int tile_r, int global_k) {
336 const int global_row = tile_row * kRegisterMatrixTileM + tile_r;
337 if (global_row >= extent) {
338 return T(0);
339 }
340 return operand.alpha * detail::matrix_entry(a, global_row, global_k, transform.trans);
341 };
342 auto rhs_loader = [&](int tile_c, int global_k) {
343 const int global_col = tile_col * kRegisterMatrixTileN + tile_c;
344 if (global_col >= extent) {
345 return T(0);
346 }
347 return detail::matrix_entry(a, global_k, global_col, rhs_transform);
348 };
349
350 accumulate_rank2k_register_tiled_pass(item, workspace, linear_tid, subgroup_id, contract_extent, lhs_loader, rhs_loader, accum);
351
352 write_rank2k_register_tile(rank_operand, transform, row_base, col_base, extent, accum);
353 }
354 }
355}
356
357} // namespace detail::subgroup
358
359namespace detail {
360
361template <typename Tag, DeviceBlasPolicy Policy, typename T>
362inline constexpr std::size_t rank2k_workspace_elements(const DeviceBlasLaunchInfo& launch,
363 int extent,
364 int contract_extent) {
365 assert(extent >= 0 && contract_extent >= 0 && "device::rank2k workspace query expects non-negative extents");
366 if (detail::subgroup::can_use_complex_rank2k_tiled_fast_path<T>(launch, extent, contract_extent, Policy) &&
369 }
370 if (detail::subgroup::can_use_rank2k_register_fast_path<T>(launch, extent, contract_extent, Policy) &&
373 }
374 return 0;
375}
376
377template <typename Tag, DeviceBlasPolicy Policy, typename T>
378inline constexpr std::size_t rankk_workspace_elements(const DeviceBlasLaunchInfo& launch,
379 int extent,
380 int contract_extent) {
381 assert(extent >= 0 && contract_extent >= 0 && "device::rankk workspace query expects non-negative extents");
382 if (detail::subgroup::can_use_complex_rankk_tiled_fast_path<T>(launch, extent, contract_extent, Policy) &&
385 }
386 if (detail::subgroup::can_use_rankk_register_fast_path<T>(launch, extent, contract_extent, Policy) &&
389 }
390 return 0;
391}
392
393template <typename Tag, DeviceBlasPolicy Policy, typename Exec, typename T>
394inline constexpr void dispatch_rank2k(const Exec& exec,
397 T* workspace = nullptr) {
398 const SymmetricRank2kTransform transform{.uplo = Tag::uplo, .trans = Tag::trans, .hermitian = Tag::hermitian};
399 validate_rank2k_operand(a, operand, transform);
400
401 if constexpr (detail::NdItemLike<Exec>) {
402 if (workspace != nullptr && detail::subgroup::can_use_complex_rank2k_tiled_fast_path<T>(exec, a, operand, transform, Policy) &&
405 detail::subgroup::rank2k_complex_tiled(exec, a, operand, transform, complex_workspace);
406 return;
407 }
408 if (workspace != nullptr && detail::subgroup::can_use_rank2k_register_fast_path<T>(exec, a, operand, transform, Policy) &&
411 detail::subgroup::rank2k_register_tiled(exec, a, operand, transform, register_workspace);
412 return;
413 }
414 // 3-D launch, no fast path: only tile-group (0, 0) runs the generic loop, else the groups race.
415 // evidence: docs/design/device-group-blas.md#device-group-blas-the-3-d-launch-generic-fallback
416 if constexpr (std::is_same_v<std::remove_cvref_t<Exec>, sycl::nd_item<3>>) {
419 generic::rank2k<Tag>(exec, a, operand);
420 }
421 return;
422 }
423 }
424
425 generic::rank2k<Tag>(exec, a, operand);
426}
427
428template <typename Tag, DeviceBlasPolicy Policy, typename Exec, typename T>
429inline constexpr void dispatch_rankk(const Exec& exec,
431 RankKOperand<T> operand,
432 T* workspace = nullptr) {
433 const SymmetricRankKTransform transform{.uplo = Tag::uplo, .trans = Tag::trans, .hermitian = Tag::hermitian};
434 validate_rankk_operand(a, operand, transform);
435
436 if constexpr (detail::NdItemLike<Exec>) {
437 if (workspace != nullptr && detail::subgroup::can_use_complex_rankk_tiled_fast_path<T>(exec, a, operand, transform, Policy) &&
440 detail::subgroup::rankk_complex_tiled(exec, a, operand, transform, complex_workspace);
441 return;
442 }
443 if (workspace != nullptr && detail::subgroup::can_use_rankk_register_fast_path<T>(exec, a, operand, transform, Policy) &&
446 detail::subgroup::rankk_register_tiled(exec, a, operand, transform, register_workspace);
447 return;
448 }
449 // 3-D launch, no fast path: only tile-group (0, 0) runs the generic loop, else the groups race.
450 // evidence: docs/design/device-group-blas.md#device-group-blas-the-3-d-launch-generic-fallback
451 if constexpr (std::is_same_v<std::remove_cvref_t<Exec>, sycl::nd_item<3>>) {
454 generic::rankk<Tag>(exec, a, operand);
455 }
456 return;
457 }
458 }
459
460 generic::rankk<Tag>(exec, a, operand);
461}
462
463} // namespace detail
464
465/// @addtogroup api_device_blas
466/// @{
467
468/// @brief Symmetric rank-k update \f$ C := \alpha\,A\,A^{T} + \beta\,C \f$ (NoTrans) or \f$ C := \alpha\,A^{T} A + \beta\,C \f$, on the `UploV` triangle of `C` only.
469///
470/// The other triangle of `C` is neither read nor written. Fast paths:
471/// register-tiled for `float` and a tiled kernel for `std::complex<float>`,
472/// both needing an `nd_item` executor and a workspace; with an `nd_item<3>`
473/// and no fast path only tile-group (0, 0) computes.
474/// @tparam UploV triangle of `C` updated
475/// @tparam TransV NoTrans: `A` is n x k; otherwise `A` is k x n
476/// @tparam Group `sycl::group`, `sycl::sub_group` or an `nd_item`; every work-item must call
477/// @param group executor
478/// @param a single input matrix
479/// @param operand `C` (n x n), `alpha`, `beta`
480/// @param workspace local memory of syrk_workspace_elements() elements, or `nullptr`
481/// @pre `C` holds finite values even when `beta == 0`; `C` does not alias `A`.
482/// @note Each `C` element has one writer; barrier before other work-items read `C`.
483/// @see herk()
484template <Uplo UploV = Uplo::Upper, Transpose TransV = Transpose::NoTrans, typename Group, typename T>
485inline constexpr void syrk(const Group& group,
487 RankKOperand<T> operand,
488 T* workspace = nullptr) {
490}
491
492/// @brief As syrk(), with an explicit DeviceBlasPolicy.
493template <DeviceBlasPolicy Policy,
494 Uplo UploV = Uplo::Upper,
496 typename Group,
497 typename T>
498inline constexpr void syrk(const Group& group,
500 RankKOperand<T> operand,
501 T* workspace = nullptr) {
503}
504
505/// @brief Local memory, in elements of `T`, that syrk() can use for this launch and shape.
506/// @param launch description of the launch the call runs in
507/// @param extent n, the order of `C`
508/// @param contract_extent k
509/// @return element count, or 0 when no staged path applies (then pass `nullptr`)
510/// @ingroup api_device_blas_lowlevel
511template <typename T, Uplo UploV = Uplo::Upper, Transpose TransV = Transpose::NoTrans>
512inline constexpr std::size_t syrk_workspace_elements(const DeviceBlasLaunchInfo& launch,
513 int extent,
514 int contract_extent) {
516}
517
518/// @brief Local memory, in elements of `T`, that syrk() can use under `Policy`; 0 means pass `nullptr`.
519/// @param launch description of the launch the call runs in
520/// @param extent order of the matrix
521/// @param contract_extent contraction length k
522/// @return element count, or 0 when no staged path applies
523/// @ingroup api_device_blas_lowlevel
524template <typename T,
526 Uplo UploV = Uplo::Upper,
528inline constexpr std::size_t syrk_workspace_elements(const DeviceBlasLaunchInfo& launch,
529 int extent,
530 int contract_extent) {
532}
533
534/// @brief As syrk(), with the operands passed separately instead of in an operand struct.
535template <Uplo UploV = Uplo::Upper, Transpose TransV = Transpose::NoTrans, typename Group, typename T>
536inline constexpr void syrk(const Group& group,
539 T alpha = T(1),
540 T beta = T(0),
541 T* workspace = nullptr) {
542 syrk<UploV, TransV>(group, a, make_rankk_operand(c, alpha, beta), workspace);
543}
544
545/// @brief As syrk(), with an explicit DeviceBlasPolicy and the operands passed separately.
546template <DeviceBlasPolicy Policy,
547 Uplo UploV = Uplo::Upper,
549 typename Group,
550 typename T>
551inline constexpr void syrk(const Group& group,
554 T alpha = T(1),
555 T beta = T(0),
556 T* workspace = nullptr) {
557 syrk<Policy, UploV, TransV>(group, a, make_rankk_operand(c, alpha, beta), workspace);
558}
559
560/// @brief Hermitian rank-k update \f$ C := \alpha\,A\,A^{H} + \beta\,C \f$ (NoTrans) or \f$ C := \alpha\,A^{H} A + \beta\,C \f$, on the `UploV` triangle.
561///
562/// For real `T` this is exactly syrk(). `alpha` and `beta` are `T`, not real
563/// as in BLAS; the imaginary part of every diagonal element of `C` is set to
564/// zero, but a non-real `alpha` still gives a non-Hermitian off-diagonal.
565/// @param group executor (see syrk())
566/// @param a single input matrix
567/// @param operand `C`, `alpha`, `beta`
568/// @param workspace local memory of herk_workspace_elements() elements, or `nullptr`
569template <Uplo UploV = Uplo::Upper, Transpose TransV = Transpose::NoTrans, typename Group, typename T>
570inline constexpr void herk(const Group& group,
572 RankKOperand<T> operand,
573 T* workspace = nullptr) {
575}
576
577/// @brief As herk(), with an explicit DeviceBlasPolicy.
578template <DeviceBlasPolicy Policy,
579 Uplo UploV = Uplo::Upper,
581 typename Group,
582 typename T>
583inline constexpr void herk(const Group& group,
585 RankKOperand<T> operand,
586 T* workspace = nullptr) {
588}
589
590/// @brief Local memory, in elements of `T`, that herk() can use; 0 means pass `nullptr`.
591/// @param launch description of the launch the call runs in
592/// @param extent order of the matrix
593/// @param contract_extent contraction length k
594/// @return element count, or 0 when no staged path applies
595/// @ingroup api_device_blas_lowlevel
596template <typename T, Uplo UploV = Uplo::Upper, Transpose TransV = Transpose::NoTrans>
597inline constexpr std::size_t herk_workspace_elements(const DeviceBlasLaunchInfo& launch,
598 int extent,
599 int contract_extent) {
601}
602
603/// @brief Local memory, in elements of `T`, that herk() can use under `Policy`; 0 means pass `nullptr`.
604/// @param launch description of the launch the call runs in
605/// @param extent order of the matrix
606/// @param contract_extent contraction length k
607/// @return element count, or 0 when no staged path applies
608/// @ingroup api_device_blas_lowlevel
609template <typename T,
611 Uplo UploV = Uplo::Upper,
613inline constexpr std::size_t herk_workspace_elements(const DeviceBlasLaunchInfo& launch,
614 int extent,
615 int contract_extent) {
617}
618
619/// @brief As herk(), with the operands passed separately instead of in an operand struct.
620template <Uplo UploV = Uplo::Upper, Transpose TransV = Transpose::NoTrans, typename Group, typename T>
621inline constexpr void herk(const Group& group,
624 T alpha = T(1),
625 T beta = T(0),
626 T* workspace = nullptr) {
627 herk<UploV, TransV>(group, a, make_rankk_operand(c, alpha, beta), workspace);
628}
629
630/// @brief As herk(), with an explicit DeviceBlasPolicy and the operands passed separately.
631template <DeviceBlasPolicy Policy,
632 Uplo UploV = Uplo::Upper,
634 typename Group,
635 typename T>
636inline constexpr void herk(const Group& group,
639 T alpha = T(1),
640 T beta = T(0),
641 T* workspace = nullptr) {
642 herk<Policy, UploV, TransV>(group, a, make_rankk_operand(c, alpha, beta), workspace);
643}
644
645/// @brief Symmetric rank-2k update \f$ C := \alpha\,(A B^{T} + B A^{T}) + \beta\,C \f$ (NoTrans) or \f$ C := \alpha\,(A^{T} B + B^{T} A) + \beta\,C \f$, on the `UploV` triangle.
646///
647/// Paths and executor rules as for syrk().
648/// @tparam UploV triangle of `C` updated
649/// @tparam TransV NoTrans: `A` and `B` are n x k; otherwise k x n
650/// @param group executor (see syrk())
651/// @param a first single input matrix
652/// @param operand `B` (same shape as `A`), `C` (n x n), `alpha`, `beta`
653/// @param workspace local memory of syr2k_workspace_elements() elements, or `nullptr`
654/// @pre `C` holds finite values even when `beta == 0`; `C` does not alias `A` or `B`.
655/// @see her2k()
656template <Uplo UploV = Uplo::Upper, Transpose TransV = Transpose::NoTrans, typename Group, typename T>
657inline constexpr void syr2k(const Group& group,
660 T* workspace = nullptr) {
662}
663
664/// @brief As syr2k(), with an explicit DeviceBlasPolicy.
665template <DeviceBlasPolicy Policy,
666 Uplo UploV = Uplo::Upper,
668 typename Group,
669 typename T>
670inline constexpr void syr2k(const Group& group,
673 T* workspace = nullptr) {
675}
676
677/// @brief Local memory, in elements of `T`, that syr2k() can use; 0 means pass `nullptr`.
678/// @param launch description of the launch the call runs in
679/// @param extent order of the matrix
680/// @param contract_extent contraction length k
681/// @return element count, or 0 when no staged path applies
682/// @ingroup api_device_blas_lowlevel
683template <typename T, Uplo UploV = Uplo::Upper, Transpose TransV = Transpose::NoTrans>
684inline constexpr std::size_t syr2k_workspace_elements(const DeviceBlasLaunchInfo& launch,
685 int extent,
686 int contract_extent) {
688}
689
690/// @brief Local memory, in elements of `T`, that syr2k() can use under `Policy`; 0 means pass `nullptr`.
691/// @param launch description of the launch the call runs in
692/// @param extent order of the matrix
693/// @param contract_extent contraction length k
694/// @return element count, or 0 when no staged path applies
695/// @ingroup api_device_blas_lowlevel
696template <typename T,
698 Uplo UploV = Uplo::Upper,
700inline constexpr std::size_t syr2k_workspace_elements(const DeviceBlasLaunchInfo& launch,
701 int extent,
702 int contract_extent) {
704}
705
706/// @brief As syr2k(), with the operands passed separately instead of in an operand struct.
707template <Uplo UploV = Uplo::Upper, Transpose TransV = Transpose::NoTrans, typename Group, typename T>
708inline constexpr void syr2k(const Group& group,
712 T alpha = T(1),
713 T beta = T(0),
714 T* workspace = nullptr) {
715 syr2k<UploV, TransV>(group, a, make_matmat_operand(b, c, alpha, beta), workspace);
716}
717
718/// @brief As syr2k(), with an explicit DeviceBlasPolicy and the operands passed separately.
719template <DeviceBlasPolicy Policy,
720 Uplo UploV = Uplo::Upper,
722 typename Group,
723 typename T>
724inline constexpr void syr2k(const Group& group,
728 T alpha = T(1),
729 T beta = T(0),
730 T* workspace = nullptr) {
731 syr2k<Policy, UploV, TransV>(group, a, make_matmat_operand(b, c, alpha, beta), workspace);
732}
733
734/// @brief Hermitian rank-2k update \f$ C := \alpha\,A B^{H} + \bar{\alpha}\,B A^{H} + \beta\,C \f$ (NoTrans) or \f$ \alpha\,A^{H} B + \bar{\alpha}\,B^{H} A + \beta\,C \f$, on the `UploV` triangle.
735///
736/// For real `T` this is exactly syr2k(). The imaginary part of every diagonal
737/// element of `C` is set to zero; `beta` is `T`, not real as in BLAS.
738/// @param group executor (see syrk())
739/// @param a first single input matrix
740/// @param operand `B`, `C`, `alpha`, `beta`
741/// @param workspace local memory of her2k_workspace_elements() elements, or `nullptr`
742template <Uplo UploV = Uplo::Upper, Transpose TransV = Transpose::NoTrans, typename Group, typename T>
743inline constexpr void her2k(const Group& group,
746 T* workspace = nullptr) {
748}
749
750/// @brief As her2k(), with an explicit DeviceBlasPolicy.
751template <DeviceBlasPolicy Policy,
752 Uplo UploV = Uplo::Upper,
754 typename Group,
755 typename T>
756inline constexpr void her2k(const Group& group,
759 T* workspace = nullptr) {
761}
762
763/// @brief Local memory, in elements of `T`, that her2k() can use; 0 means pass `nullptr`.
764/// @param launch description of the launch the call runs in
765/// @param extent order of the matrix
766/// @param contract_extent contraction length k
767/// @return element count, or 0 when no staged path applies
768/// @ingroup api_device_blas_lowlevel
769template <typename T, Uplo UploV = Uplo::Upper, Transpose TransV = Transpose::NoTrans>
770inline constexpr std::size_t her2k_workspace_elements(const DeviceBlasLaunchInfo& launch,
771 int extent,
772 int contract_extent) {
774}
775
776/// @brief Local memory, in elements of `T`, that her2k() can use under `Policy`; 0 means pass `nullptr`.
777/// @param launch description of the launch the call runs in
778/// @param extent order of the matrix
779/// @param contract_extent contraction length k
780/// @return element count, or 0 when no staged path applies
781/// @ingroup api_device_blas_lowlevel
782template <typename T,
784 Uplo UploV = Uplo::Upper,
786inline constexpr std::size_t her2k_workspace_elements(const DeviceBlasLaunchInfo& launch,
787 int extent,
788 int contract_extent) {
790}
791
792/// @brief As her2k(), with the operands passed separately instead of in an operand struct.
793template <Uplo UploV = Uplo::Upper, Transpose TransV = Transpose::NoTrans, typename Group, typename T>
794inline constexpr void her2k(const Group& group,
798 T alpha = T(1),
799 T beta = T(0),
800 T* workspace = nullptr) {
801 her2k<UploV, TransV>(group, a, make_matmat_operand(b, c, alpha, beta), workspace);
802}
803
804/// @brief As her2k(), with an explicit DeviceBlasPolicy and the operands passed separately.
805template <DeviceBlasPolicy Policy,
806 Uplo UploV = Uplo::Upper,
808 typename Group,
809 typename T>
810inline constexpr void her2k(const Group& group,
814 T alpha = T(1),
815 T beta = T(0),
816 T* workspace = nullptr) {
817 her2k<Policy, UploV, TransV>(group, a, make_matmat_operand(b, c, alpha, beta), workspace);
818}
819
820/// @}
821
822} // namespace batchlas::device
A std::complex scalar (std::complex<float>, std::complex<double>).
Definition enums.hh:61
Policy
Synchronous or asynchronous execution policy.
constexpr std::size_t syr2k_workspace_elements(const DeviceBlasLaunchInfo &launch, int extent, int contract_extent)
Local memory, in elements of T, that syr2k() can use; 0 means pass nullptr.
constexpr std::size_t herk_workspace_elements(const DeviceBlasLaunchInfo &launch, int extent, int contract_extent)
Local memory, in elements of T, that herk() can use; 0 means pass nullptr.
constexpr std::size_t her2k_workspace_elements(const DeviceBlasLaunchInfo &launch, int extent, int contract_extent)
Local memory, in elements of T, that her2k() can use; 0 means pass nullptr.
constexpr std::size_t syrk_workspace_elements(const DeviceBlasLaunchInfo &launch, int extent, int contract_extent)
Local memory, in elements of T, that syrk() can use for this launch and shape.
constexpr MatrixMatrixOperand< T > make_matmat_operand(const KernelMatrixView< T, MatrixFormat::Dense > &b, const KernelMatrixView< T, MatrixFormat::Dense > &c, T alpha=T(1), T beta=T(0))
Builds a MatrixMatrixOperand.
constexpr RankKOperand< T > make_rankk_operand(const KernelMatrixView< T, MatrixFormat::Dense > &c, T alpha=T(1), T beta=T(0))
Builds a RankKOperand.
constexpr void herk(const Group &group, const KernelMatrixView< T, MatrixFormat::Dense > &a, RankKOperand< T > operand, T *workspace=nullptr)
Hermitian rank-k update (NoTrans) or , on the UploV triangle.
constexpr void her2k(const Group &group, const KernelMatrixView< T, MatrixFormat::Dense > &a, MatrixMatrixOperand< T > operand, T *workspace=nullptr)
Hermitian rank-2k update (NoTrans) or , on the UploV triangle.
DeviceBlasPolicy
Which implementation family a device BLAS call may use.
constexpr void syr2k(const Group &group, const KernelMatrixView< T, MatrixFormat::Dense > &a, MatrixMatrixOperand< T > operand, T *workspace=nullptr)
Symmetric rank-2k update (NoTrans) or , on the UploV triangle.
constexpr void syrk(const Group &group, const KernelMatrixView< T, MatrixFormat::Dense > &a, RankKOperand< T > operand, T *workspace=nullptr)
Symmetric rank-k update (NoTrans) or , on the UploV triangle of C only.
@ Auto
any eligible path; the sub-group fast paths accept sub-group size 16 or 32
Transpose
The operator op() applied to an operand: op(A) = A, A^T or A^H.
Definition enums.hh:171
Uplo
Which triangle of a symmetric, Hermitian or triangular operand is referenced.
Definition enums.hh:287
@ NoTrans
op(A) = A.
Definition enums.hh:172
@ Upper
The upper triangle (i <= j).
Definition enums.hh:288
@ Lower
The lower triangle (i >= j).
Definition enums.hh:289
constexpr void rank2k(const Group &group, const KernelMatrixView< T, MatrixFormat::Dense > &a, MatrixMatrixOperand< T > operand)
constexpr void rankk(const Group &group, const KernelMatrixView< T, MatrixFormat::Dense > &a, RankKOperand< T > operand)
constexpr bool can_use_rankk_register_fast_path(const Item &item, int extent, int contract_extent, DeviceBlasPolicy policy)
constexpr void rank2k_register_tiled(const Item &item, const KernelMatrixView< T, MatrixFormat::Dense > &a, MatrixMatrixOperand< T > operand, SymmetricRank2kTransform transform, RegisterMatrixWorkspace< T > *workspace)
constexpr bool can_use_rank2k_register_fast_path(const Item &item, int extent, int contract_extent, DeviceBlasPolicy policy)
constexpr void rankk_register_tiled(const Item &item, const KernelMatrixView< T, MatrixFormat::Dense > &a, RankKOperand< T > operand, SymmetricRankKTransform transform, RegisterMatrixWorkspace< T > *workspace)
constexpr int subgroup_group_id(const Item &item)
constexpr int matrix_tile_group_row(const Item &)
constexpr void accumulate_rank2k_register_tiled_pass(const Item &item, RegisterMatrixWorkspace< T > *workspace, int linear_tid, int subgroup_id, int contract_extent, LhsLoader &&lhs_loader, RhsLoader &&rhs_loader, RegisterMatrixAccumTile< T > &accum)
constexpr void rank2k_complex_tiled(const Item &item, const KernelMatrixView< T, MatrixFormat::Dense > &a, MatrixMatrixOperand< T > operand, SymmetricRank2kTransform transform, ComplexRank2kWorkspace< T > *workspace)
constexpr int matrix_tile_group_col(const Item &)
constexpr int matrix_tile_group_row_stride(const Item &)
constexpr int matrix_tile_group_col_stride(const Item &)
constexpr bool can_use_complex_rank2k_tiled_fast_path(const Item &item, int extent, int contract_extent, DeviceBlasPolicy policy)
constexpr void accumulate_complex_rank2k_tiled_pass_impl(const Item &item, Workspace *workspace, int linear_tid, int subgroup_id, int local_row, int local_col, int contract_extent, LhsLoader &&lhs_loader, RhsLoader &&rhs_loader, ComplexRank2kAccumTile< T > &accum)
constexpr void rankk_complex_tiled(const Item &item, const KernelMatrixView< T, MatrixFormat::Dense > &a, RankKOperand< T > operand, SymmetricRankKTransform transform, ComplexRank2kWorkspace< T > *workspace)
constexpr void write_rank2k_register_tile(MatrixMatrixOperand< T > operand, Transform transform, int row_base, int col_base, int extent, const RegisterMatrixAccumTile< T > &accum)
constexpr bool can_use_complex_rankk_tiled_fast_path(const Item &item, int extent, int contract_extent, DeviceBlasPolicy policy)
constexpr void write_complex_rank2k_tile(MatrixMatrixOperand< T > operand, Transform transform, int row_base, int col_base, int extent, const ComplexRank2kAccumTile< T > &accum)
constexpr std::size_t rankk_workspace_elements(const DeviceBlasLaunchInfo &launch, int extent, int contract_extent)
constexpr int output_size(const KernelMatrixView< T, MatrixFormat::Dense > &a, Transpose trans)
constexpr Transpose rank2k_rhs_transform()
constexpr int group_local_linear_id(const Group &group)
constexpr void dispatch_rank2k(const Exec &exec, const KernelMatrixView< T, MatrixFormat::Dense > &a, MatrixMatrixOperand< T > operand, T *workspace=nullptr)
constexpr int input_size(const KernelMatrixView< T, MatrixFormat::Dense > &a, Transpose trans)
constexpr int group_local_linear_range(const Group &group)
constexpr Workspace * workspace_ptr_cast(T *workspace)
constexpr Transpose rankk_rhs_transform()
constexpr void validate_rank2k_operand(const KernelMatrixView< T, MatrixFormat::Dense > &a, const MatrixMatrixOperand< T > &operand, SymmetricRank2kTransform transform)
constexpr T matrix_entry(const KernelMatrixView< T, MatrixFormat::Dense > &a, int row, int col, Transpose trans)
constexpr void dispatch_rankk(const Exec &exec, const KernelMatrixView< T, MatrixFormat::Dense > &a, RankKOperand< T > operand, T *workspace=nullptr)
constexpr int item_local_linear_id(const Item &item)
constexpr std::size_t rank2k_workspace_elements(const DeviceBlasLaunchInfo &launch, int extent, int contract_extent)
constexpr std::size_t workspace_elements_v
constexpr void validate_rankk_operand(const KernelMatrixView< T, MatrixFormat::Dense > &a, const RankKOperand< T > &operand, SymmetricRankKTransform transform)
constexpr bool triangular_storage_contains(int row, int col)
constexpr T secondary_rank2k_alpha(const T &alpha)
Trivially copyable view of a MatrixView for use inside a SYCL kernel.
Definition matrix.hh:145
Host-side description of a launch, for the *_workspace_elements queries.
Operands of a matrix-matrix operation: .
KernelMatrixView< T, MatrixFormat::Dense > c
output matrix; read (scaled by beta) and overwritten
T beta
scale of the input C; 0 still reads C
KernelMatrixView< T, MatrixFormat::Dense > b
second input matrix
Operands of a rank-k update: .
KernelMatrixView< T, MatrixFormat::Dense > c
square output; only the uplo triangle is touched
T beta
scale of the input C; 0 still reads C
Mode of a symmetric or Hermitian rank-2k update (syr2k, her2k).
bool hermitian
conjugate transposes and conjugate alpha on the second term
Uplo uplo
triangle of C that is updated
Mode of a symmetric or Hermitian rank-k update (syrk, herk).
Uplo uplo
triangle of C that is updated
Transpose trans
NoTrans: ; otherwise .