// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced // at the Lawrence Livermore National Laboratory. All Rights reserved. See files // LICENSE and NOTICE for details. LLNL-CODE-806117. // // This file is part of the MFEM library. For more information and source code // availability visit https://mfem.org. // // MFEM is free software; you can redistribute it and/or modify it under the // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. #include "magma.hpp" #include "../lapack.hpp" #include "../../general/forall.hpp" #ifdef MFEM_USE_MAGMA #ifdef MFEM_USE_SINGLE #define MFEM_MAGMA_PREFIX(stub) magma_s ## stub #define MFEM_MAGMABLAS_PREFIX(stub) magmablas_s ## stub #elif defined(MFEM_USE_DOUBLE) #define MFEM_MAGMA_PREFIX(stub) magma_d ## stub #define MFEM_MAGMABLAS_PREFIX(stub) magmablas_d ## stub #endif namespace mfem { Magma::Magma() { const magma_int_t status = magma_init(); MFEM_VERIFY(status == MAGMA_SUCCESS, "Error initializing MAGMA."); magma_device_t dev; magma_getdevice(&dev); magma_queue_create(dev, &queue); } Magma::~Magma() { magma_queue_destroy(queue); const magma_int_t status = magma_finalize(); MFEM_VERIFY(status == MAGMA_SUCCESS, "Error finalizing MAGMA."); } Magma &Magma::Instance() { static Magma magma; return magma; } magma_queue_t Magma::Queue() { return Instance().queue; } void MagmaBatchedLinAlg::AddMult(const DenseTensor &A, const Vector &x, Vector &y, real_t alpha, real_t beta, Op op) const { const bool tr = (op == Op::T); const int m = tr ? A.SizeJ() : A.SizeI(); const int n = tr ? A.SizeI() : A.SizeJ(); const int n_mat = A.SizeK(); const int k = x.Size() / n / n_mat; auto d_A = A.Read(); auto d_x = x.Read(); // Shape (n, k, n_mat); auto d_y = beta == 0.0 ? y.Write() : y.ReadWrite(); // Shape (m, k, n_mat); magma_trans_t magma_op = tr ? MagmaTrans : MagmaNoTrans; MFEM_MAGMABLAS_PREFIX(gemm_batched_strided)( magma_op, MagmaNoTrans, m, k, n, alpha, d_A, m, m*n, d_x, n, n*k, beta, d_y, m, m*k, n_mat, Magma::Queue()); } void MagmaBatchedLinAlg::LUFactor(DenseTensor &A, Array &P) const { const int n = A.SizeI(); const int n_mat = A.SizeK(); P.SetSize(n*n_mat); real_t *A_base = A.ReadWrite(); int *P_base = P.ReadWrite(); Array A_ptrs(n_mat); Array P_ptrs(n_mat); real_t **d_A_ptrs = A_ptrs.Write(); int **d_P_ptrs = P_ptrs.Write(); mfem::forall(n_mat, [=] MFEM_HOST_DEVICE (int i) { d_A_ptrs[i] = A_base + i*n*n; d_P_ptrs[i] = P_base + i*n; }); Array info_array(n_mat); const magma_int_t status = MFEM_MAGMA_PREFIX(getrf_batched)( n, n, d_A_ptrs, n, d_P_ptrs, info_array.Write(), n_mat, Magma::Queue()); MFEM_VERIFY(status == MAGMA_SUCCESS, ""); } void MagmaBatchedLinAlg::LUSolve( const DenseTensor &LU, const Array &P, Vector &x) const { const int n = LU.SizeI(); const int n_mat = LU.SizeK(); const int n_rhs = x.Size() / n / n_mat; Array A_ptrs(n_mat); Array B_ptrs(n_mat); Array P_ptrs(n_mat); real_t **d_A_ptrs = A_ptrs.Write(); real_t **d_B_ptrs = B_ptrs.Write(); int **d_P_ptrs = P_ptrs.Write(); { real_t *A_base = const_cast(LU.Read()); real_t *B_base = x.ReadWrite(); int *P_base = const_cast(P.Read()); mfem::forall(n_mat, [=] MFEM_HOST_DEVICE (int i) { d_A_ptrs[i] = A_base + i*n*n; d_B_ptrs[i] = B_base + i*n*n_rhs; d_P_ptrs[i] = P_base + i*n; }); } const magma_int_t status = MFEM_MAGMA_PREFIX(getrs_batched)( MagmaNoTrans, n, n_rhs, d_A_ptrs, n, d_P_ptrs, d_B_ptrs, n, n_mat, Magma::Queue()); MFEM_VERIFY(status == MAGMA_SUCCESS, ""); } void MagmaBatchedLinAlg::Invert(DenseTensor &A) const { const int n = A.SizeI(); const int n_mat = A.SizeK(); DenseTensor LU(A.SizeI(), A.SizeJ(), A.SizeK()); LU.Write(); LU.GetMemory().CopyFrom(A.GetMemory(), A.TotalSize()); Array P(n*n_mat); Array LU_ptrs(n_mat); Array A_ptrs(n_mat); Array P_ptrs(n_mat); real_t **d_A_ptrs = A_ptrs.Write(); real_t **d_LU_ptrs = LU_ptrs.Write(); int **d_P_ptrs = P_ptrs.Write(); { real_t *A_base = A.ReadWrite(); real_t *LU_base = LU.Write(); int *P_base = P.Write(); mfem::forall(n_mat, [=] MFEM_HOST_DEVICE (int i) { d_A_ptrs[i] = A_base + i*n*n; d_LU_ptrs[i] = LU_base + i*n*n; d_P_ptrs[i] = P_base + i*n; }); } Array info_array(n_mat); magma_int_t status; status = MFEM_MAGMA_PREFIX(getrf_batched)( n, n, d_LU_ptrs, n, d_P_ptrs, info_array.Write(), n_mat, Magma::Queue()); MFEM_VERIFY(status == MAGMA_SUCCESS, ""); status = MFEM_MAGMA_PREFIX(getri_outofplace_batched)( n, d_LU_ptrs, n, d_P_ptrs, d_A_ptrs, n, info_array.Write(), n_mat, Magma::Queue()); MFEM_VERIFY(status == MAGMA_SUCCESS, ""); } } // namespace mfem #endif