1 #include <../src/mat/impls/htool/htool.hpp> /*I "petscmat.h" I*/ 2 #include <petscblaslapack.h> 3 #include <set> 4 5 const char *const MatHtoolCompressorTypes[] = {"sympartialACA", "fullACA", "SVD"}; 6 const char *const MatHtoolClusteringTypes[] = {"PCARegular", "PCAGeometric", "BoundingBox1Regular", "BoundingBox1Geometric"}; 7 const char HtoolCitation[] = "@article{marchand2020two,\n" 8 " Author = {Marchand, Pierre and Claeys, Xavier and Jolivet, Pierre and Nataf, Fr\\'ed\\'eric and Tournier, Pierre-Henri},\n" 9 " Title = {Two-level preconditioning for $h$-version boundary element approximation of hypersingular operator with {GenEO}},\n" 10 " Year = {2020},\n" 11 " Publisher = {Elsevier},\n" 12 " Journal = {Numerische Mathematik},\n" 13 " Volume = {146},\n" 14 " Pages = {597--628},\n" 15 " Url = {https://github.com/htool-ddm/htool}\n" 16 "}\n"; 17 static PetscBool HtoolCite = PETSC_FALSE; 18 19 static PetscErrorCode MatGetDiagonal_Htool(Mat A, Vec v) 20 { 21 Mat_Htool *a; 22 PetscScalar *x; 23 PetscBool flg; 24 25 PetscFunctionBegin; 26 PetscCall(MatHasCongruentLayouts(A, &flg)); 27 PetscCheck(flg, PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "Only congruent layouts supported"); 28 PetscCall(MatShellGetContext(A, &a)); 29 PetscCall(VecGetArrayWrite(v, &x)); 30 a->hmatrix->copy_local_diagonal(x); 31 PetscCall(VecRestoreArrayWrite(v, &x)); 32 PetscFunctionReturn(PETSC_SUCCESS); 33 } 34 35 static PetscErrorCode MatGetDiagonalBlock_Htool(Mat A, Mat *b) 36 { 37 Mat_Htool *a; 38 Mat B; 39 PetscScalar *ptr, scale, shift; 40 PetscBool flg; 41 42 PetscFunctionBegin; 43 PetscCall(MatHasCongruentLayouts(A, &flg)); 44 PetscCheck(flg, PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "Only congruent layouts supported"); 45 PetscCall(MatShellGetContext(A, &a)); 46 PetscCall(PetscObjectQuery((PetscObject)A, "DiagonalBlock", (PetscObject *)&B)); /* same logic as in MatGetDiagonalBlock_MPIDense() */ 47 if (!B) { 48 PetscCall(MatShellGetScalingShifts(A, &shift, &scale, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Mat *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED)); 49 PetscCall(MatCreateDense(PETSC_COMM_SELF, A->rmap->n, A->rmap->n, A->rmap->n, A->rmap->n, nullptr, &B)); 50 PetscCall(MatDenseGetArrayWrite(B, &ptr)); 51 a->hmatrix->copy_local_diagonal_block(ptr); 52 PetscCall(MatDenseRestoreArrayWrite(B, &ptr)); 53 PetscCall(MatPropagateSymmetryOptions(A, B)); 54 PetscCall(PetscObjectCompose((PetscObject)A, "DiagonalBlock", (PetscObject)B)); 55 *b = B; 56 PetscCall(MatDestroy(&B)); 57 PetscCall(MatShift(*b, shift)); 58 PetscCall(MatScale(*b, scale)); 59 } else { 60 PetscCall(MatShellGetScalingShifts(A, (PetscScalar *)MAT_SHELL_NOT_ALLOWED, (PetscScalar *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Mat *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED)); 61 *b = B; 62 } 63 PetscFunctionReturn(PETSC_SUCCESS); 64 } 65 66 static PetscErrorCode MatMult_Htool(Mat A, Vec x, Vec y) 67 { 68 Mat_Htool *a; 69 const PetscScalar *in; 70 PetscScalar *out; 71 72 PetscFunctionBegin; 73 PetscCall(MatShellGetContext(A, &a)); 74 PetscCall(VecGetArrayRead(x, &in)); 75 PetscCall(VecGetArrayWrite(y, &out)); 76 a->hmatrix->mvprod_local_to_local(in, out); 77 PetscCall(VecRestoreArrayRead(x, &in)); 78 PetscCall(VecRestoreArrayWrite(y, &out)); 79 PetscFunctionReturn(PETSC_SUCCESS); 80 } 81 82 static PetscErrorCode MatMultTranspose_Htool(Mat A, Vec x, Vec y) 83 { 84 Mat_Htool *a; 85 const PetscScalar *in; 86 PetscScalar *out; 87 88 PetscFunctionBegin; 89 PetscCall(MatShellGetContext(A, &a)); 90 PetscCall(VecGetArrayRead(x, &in)); 91 PetscCall(VecGetArrayWrite(y, &out)); 92 a->hmatrix->mvprod_transp_local_to_local(in, out); 93 PetscCall(VecRestoreArrayRead(x, &in)); 94 PetscCall(VecRestoreArrayWrite(y, &out)); 95 PetscFunctionReturn(PETSC_SUCCESS); 96 } 97 98 static PetscErrorCode MatIncreaseOverlap_Htool(Mat A, PetscInt is_max, IS is[], PetscInt ov) 99 { 100 std::set<PetscInt> set; 101 const PetscInt *idx; 102 PetscInt *oidx, size, bs[2]; 103 PetscMPIInt csize; 104 105 PetscFunctionBegin; 106 PetscCall(MatGetBlockSizes(A, bs, bs + 1)); 107 if (bs[0] != bs[1]) bs[0] = 1; 108 for (PetscInt i = 0; i < is_max; ++i) { 109 /* basic implementation that adds indices by shifting an IS by -ov, -ov+1..., -1, 1..., ov-1, ov */ 110 /* needed to avoid subdomain matrices to replicate A since it is dense */ 111 PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)is[i]), &csize)); 112 PetscCheck(csize == 1, PETSC_COMM_SELF, PETSC_ERR_SUP, "Unsupported parallel IS"); 113 PetscCall(ISGetSize(is[i], &size)); 114 PetscCall(ISGetIndices(is[i], &idx)); 115 for (PetscInt j = 0; j < size; ++j) { 116 set.insert(idx[j]); 117 for (PetscInt k = 1; k <= ov; ++k) { /* for each layer of overlap */ 118 if (idx[j] - k >= 0) set.insert(idx[j] - k); /* do not insert negative indices */ 119 if (idx[j] + k < A->rmap->N && idx[j] + k < A->cmap->N) set.insert(idx[j] + k); /* do not insert indices greater than the dimension of A */ 120 } 121 } 122 PetscCall(ISRestoreIndices(is[i], &idx)); 123 PetscCall(ISDestroy(is + i)); 124 if (bs[0] > 1) { 125 for (std::set<PetscInt>::iterator it = set.cbegin(); it != set.cend(); it++) { 126 std::vector<PetscInt> block(bs[0]); 127 std::iota(block.begin(), block.end(), (*it / bs[0]) * bs[0]); 128 set.insert(block.cbegin(), block.cend()); 129 } 130 } 131 size = set.size(); /* size with overlap */ 132 PetscCall(PetscMalloc1(size, &oidx)); 133 for (const PetscInt j : set) *oidx++ = j; 134 oidx -= size; 135 PetscCall(ISCreateGeneral(PETSC_COMM_SELF, size, oidx, PETSC_OWN_POINTER, is + i)); 136 } 137 PetscFunctionReturn(PETSC_SUCCESS); 138 } 139 140 static PetscErrorCode MatCreateSubMatrices_Htool(Mat A, PetscInt n, const IS irow[], const IS icol[], MatReuse scall, Mat *submat[]) 141 { 142 Mat_Htool *a; 143 Mat D, B, BT; 144 const PetscScalar *copy; 145 PetscScalar *ptr, shift, scale; 146 const PetscInt *idxr, *idxc, *it; 147 PetscInt nrow, m, i; 148 PetscBool flg; 149 150 PetscFunctionBegin; 151 PetscCall(MatShellGetScalingShifts(A, &shift, &scale, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Mat *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED)); 152 PetscCall(MatShellGetContext(A, &a)); 153 if (scall != MAT_REUSE_MATRIX) PetscCall(PetscCalloc1(n, submat)); 154 for (i = 0; i < n; ++i) { 155 PetscCall(ISGetLocalSize(irow[i], &nrow)); 156 PetscCall(ISGetLocalSize(icol[i], &m)); 157 PetscCall(ISGetIndices(irow[i], &idxr)); 158 PetscCall(ISGetIndices(icol[i], &idxc)); 159 if (scall != MAT_REUSE_MATRIX) PetscCall(MatCreateDense(PETSC_COMM_SELF, nrow, m, nrow, m, nullptr, (*submat) + i)); 160 PetscCall(MatDenseGetArrayWrite((*submat)[i], &ptr)); 161 if (irow[i] == icol[i]) { /* same row and column IS? */ 162 PetscCall(MatHasCongruentLayouts(A, &flg)); 163 if (flg) { 164 PetscCall(ISSorted(irow[i], &flg)); 165 if (flg) { /* sorted IS? */ 166 it = std::lower_bound(idxr, idxr + nrow, A->rmap->rstart); 167 if (it != idxr + nrow && *it == A->rmap->rstart) { /* rmap->rstart in IS? */ 168 if (std::distance(idxr, it) + A->rmap->n <= nrow) { /* long enough IS to store the local diagonal block? */ 169 for (PetscInt j = 0; j < A->rmap->n && flg; ++j) 170 if (PetscUnlikely(it[j] != A->rmap->rstart + j)) flg = PETSC_FALSE; 171 if (flg) { /* complete local diagonal block in IS? */ 172 /* fast extraction when the local diagonal block is part of the submatrix, e.g., for PCASM or PCHPDDM 173 * [ B C E ] 174 * A = [ B D E ] 175 * [ B F E ] 176 */ 177 m = std::distance(idxr, it); /* shift of the coefficient (0,0) of block D from above */ 178 PetscCall(MatGetDiagonalBlock(A, &D)); 179 PetscCall(MatDenseGetArrayRead(D, ©)); 180 for (PetscInt k = 0; k < A->rmap->n; ++k) { PetscCall(PetscArraycpy(ptr + (m + k) * nrow + m, copy + k * A->rmap->n, A->rmap->n)); /* block D from above */ } 181 PetscCall(MatDenseRestoreArrayRead(D, ©)); 182 if (m) { 183 a->wrapper->copy_submatrix(nrow, m, idxr, idxc, ptr); /* vertical block B from above */ 184 /* entry-wise assembly may be costly, so transpose already-computed entries when possible */ 185 if (A->symmetric == PETSC_BOOL3_TRUE || A->hermitian == PETSC_BOOL3_TRUE) { 186 PetscCall(MatCreateDense(PETSC_COMM_SELF, A->rmap->n, m, A->rmap->n, m, ptr + m, &B)); 187 PetscCall(MatDenseSetLDA(B, nrow)); 188 PetscCall(MatCreateDense(PETSC_COMM_SELF, m, A->rmap->n, m, A->rmap->n, ptr + m * nrow, &BT)); 189 PetscCall(MatDenseSetLDA(BT, nrow)); 190 if (A->hermitian == PETSC_BOOL3_TRUE && PetscDefined(USE_COMPLEX)) { 191 PetscCall(MatHermitianTranspose(B, MAT_REUSE_MATRIX, &BT)); 192 } else { 193 PetscCall(MatTransposeSetPrecursor(B, BT)); 194 PetscCall(MatTranspose(B, MAT_REUSE_MATRIX, &BT)); 195 } 196 PetscCall(MatDestroy(&B)); 197 PetscCall(MatDestroy(&BT)); 198 } else { 199 for (PetscInt k = 0; k < A->rmap->n; ++k) { /* block C from above */ 200 a->wrapper->copy_submatrix(m, 1, idxr, idxc + m + k, ptr + (m + k) * nrow); 201 } 202 } 203 } 204 if (m + A->rmap->n != nrow) { 205 a->wrapper->copy_submatrix(nrow, std::distance(it + A->rmap->n, idxr + nrow), idxr, idxc + m + A->rmap->n, ptr + (m + A->rmap->n) * nrow); /* vertical block E from above */ 206 /* entry-wise assembly may be costly, so transpose already-computed entries when possible */ 207 if (A->symmetric == PETSC_BOOL3_TRUE || A->hermitian == PETSC_BOOL3_TRUE) { 208 PetscCall(MatCreateDense(PETSC_COMM_SELF, A->rmap->n, nrow - (m + A->rmap->n), A->rmap->n, nrow - (m + A->rmap->n), ptr + (m + A->rmap->n) * nrow + m, &B)); 209 PetscCall(MatDenseSetLDA(B, nrow)); 210 PetscCall(MatCreateDense(PETSC_COMM_SELF, nrow - (m + A->rmap->n), A->rmap->n, nrow - (m + A->rmap->n), A->rmap->n, ptr + m * nrow + m + A->rmap->n, &BT)); 211 PetscCall(MatDenseSetLDA(BT, nrow)); 212 if (A->hermitian == PETSC_BOOL3_TRUE && PetscDefined(USE_COMPLEX)) { 213 PetscCall(MatHermitianTranspose(B, MAT_REUSE_MATRIX, &BT)); 214 } else { 215 PetscCall(MatTransposeSetPrecursor(B, BT)); 216 PetscCall(MatTranspose(B, MAT_REUSE_MATRIX, &BT)); 217 } 218 PetscCall(MatDestroy(&B)); 219 PetscCall(MatDestroy(&BT)); 220 } else { 221 for (PetscInt k = 0; k < A->rmap->n; ++k) { /* block F from above */ 222 a->wrapper->copy_submatrix(std::distance(it + A->rmap->n, idxr + nrow), 1, it + A->rmap->n, idxc + m + k, ptr + (m + k) * nrow + m + A->rmap->n); 223 } 224 } 225 } 226 } /* complete local diagonal block not in IS */ 227 } else flg = PETSC_FALSE; /* IS not long enough to store the local diagonal block */ 228 } else flg = PETSC_FALSE; /* rmap->rstart not in IS */ 229 } /* unsorted IS */ 230 } 231 } else flg = PETSC_FALSE; /* different row and column IS */ 232 if (!flg) a->wrapper->copy_submatrix(nrow, m, idxr, idxc, ptr); /* reassemble everything */ 233 PetscCall(ISRestoreIndices(irow[i], &idxr)); 234 PetscCall(ISRestoreIndices(icol[i], &idxc)); 235 PetscCall(MatDenseRestoreArrayWrite((*submat)[i], &ptr)); 236 PetscCall(MatShift((*submat)[i], shift)); 237 PetscCall(MatScale((*submat)[i], scale)); 238 } 239 PetscFunctionReturn(PETSC_SUCCESS); 240 } 241 242 static PetscErrorCode MatDestroy_Htool(Mat A) 243 { 244 Mat_Htool *a; 245 PetscContainer container; 246 MatHtoolKernelTranspose *kernelt; 247 248 PetscFunctionBegin; 249 PetscCall(MatShellGetContext(A, &a)); 250 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatProductSetFromOptions_htool_seqdense_C", nullptr)); 251 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatProductSetFromOptions_htool_mpidense_C", nullptr)); 252 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatConvert_htool_seqdense_C", nullptr)); 253 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatConvert_htool_mpidense_C", nullptr)); 254 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatHtoolGetHierarchicalMat_C", nullptr)); 255 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatHtoolSetKernel_C", nullptr)); 256 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatHtoolGetPermutationSource_C", nullptr)); 257 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatHtoolGetPermutationTarget_C", nullptr)); 258 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatHtoolUsePermutation_C", nullptr)); 259 PetscCall(PetscObjectQuery((PetscObject)A, "KernelTranspose", (PetscObject *)&container)); 260 if (container) { /* created in MatTranspose_Htool() */ 261 PetscCall(PetscContainerGetPointer(container, (void **)&kernelt)); 262 PetscCall(MatDestroy(&kernelt->A)); 263 PetscCall(PetscFree(kernelt)); 264 PetscCall(PetscContainerDestroy(&container)); 265 PetscCall(PetscObjectCompose((PetscObject)A, "KernelTranspose", nullptr)); 266 } 267 if (a->gcoords_source != a->gcoords_target) PetscCall(PetscFree(a->gcoords_source)); 268 PetscCall(PetscFree(a->gcoords_target)); 269 PetscCall(PetscFree2(a->work_source, a->work_target)); 270 delete a->wrapper; 271 delete a->hmatrix; 272 PetscCall(PetscFree(a)); 273 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellSetContext_C", nullptr)); // needed to avoid a call to MatShellSetContext_Immutable() 274 PetscFunctionReturn(PETSC_SUCCESS); 275 } 276 277 static PetscErrorCode MatView_Htool(Mat A, PetscViewer pv) 278 { 279 Mat_Htool *a; 280 PetscScalar shift, scale; 281 PetscBool flg; 282 283 PetscFunctionBegin; 284 PetscCall(MatShellGetContext(A, &a)); 285 PetscCall(PetscObjectTypeCompare((PetscObject)pv, PETSCVIEWERASCII, &flg)); 286 if (flg) { 287 PetscCall(MatShellGetScalingShifts(A, &shift, &scale, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Mat *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED)); 288 PetscCall(PetscViewerASCIIPrintf(pv, "symmetry: %c\n", a->hmatrix->get_symmetry_type())); 289 if (PetscAbsScalar(scale - 1.0) > PETSC_MACHINE_EPSILON) { 290 #if defined(PETSC_USE_COMPLEX) 291 PetscCall(PetscViewerASCIIPrintf(pv, "scaling: %g+%gi\n", (double)PetscRealPart(scale), (double)PetscImaginaryPart(scale))); 292 #else 293 PetscCall(PetscViewerASCIIPrintf(pv, "scaling: %g\n", (double)scale)); 294 #endif 295 } 296 if (PetscAbsScalar(shift) > PETSC_MACHINE_EPSILON) { 297 #if defined(PETSC_USE_COMPLEX) 298 PetscCall(PetscViewerASCIIPrintf(pv, "shift: %g+%gi\n", (double)PetscRealPart(shift), (double)PetscImaginaryPart(shift))); 299 #else 300 PetscCall(PetscViewerASCIIPrintf(pv, "shift: %g\n", (double)shift)); 301 #endif 302 } 303 PetscCall(PetscViewerASCIIPrintf(pv, "minimum cluster size: %" PetscInt_FMT "\n", a->bs[0])); 304 PetscCall(PetscViewerASCIIPrintf(pv, "maximum block size: %" PetscInt_FMT "\n", a->bs[1])); 305 PetscCall(PetscViewerASCIIPrintf(pv, "epsilon: %g\n", (double)a->epsilon)); 306 PetscCall(PetscViewerASCIIPrintf(pv, "eta: %g\n", (double)a->eta)); 307 PetscCall(PetscViewerASCIIPrintf(pv, "minimum target depth: %" PetscInt_FMT "\n", a->depth[0])); 308 PetscCall(PetscViewerASCIIPrintf(pv, "minimum source depth: %" PetscInt_FMT "\n", a->depth[1])); 309 PetscCall(PetscViewerASCIIPrintf(pv, "compressor: %s\n", MatHtoolCompressorTypes[a->compressor])); 310 PetscCall(PetscViewerASCIIPrintf(pv, "clustering: %s\n", MatHtoolClusteringTypes[a->clustering])); 311 PetscCall(PetscViewerASCIIPrintf(pv, "compression ratio: %s\n", a->hmatrix->get_infos("Compression_ratio").c_str())); 312 PetscCall(PetscViewerASCIIPrintf(pv, "space saving: %s\n", a->hmatrix->get_infos("Space_saving").c_str())); 313 PetscCall(PetscViewerASCIIPrintf(pv, "number of dense (resp. low rank) matrices: %s (resp. %s)\n", a->hmatrix->get_infos("Number_of_dmat").c_str(), a->hmatrix->get_infos("Number_of_lrmat").c_str())); 314 PetscCall(PetscViewerASCIIPrintf(pv, "(minimum, mean, maximum) dense block sizes: (%s, %s, %s)\n", a->hmatrix->get_infos("Dense_block_size_min").c_str(), a->hmatrix->get_infos("Dense_block_size_mean").c_str(), 315 a->hmatrix->get_infos("Dense_block_size_max").c_str())); 316 PetscCall(PetscViewerASCIIPrintf(pv, "(minimum, mean, maximum) low rank block sizes: (%s, %s, %s)\n", a->hmatrix->get_infos("Low_rank_block_size_min").c_str(), a->hmatrix->get_infos("Low_rank_block_size_mean").c_str(), 317 a->hmatrix->get_infos("Low_rank_block_size_max").c_str())); 318 PetscCall(PetscViewerASCIIPrintf(pv, "(minimum, mean, maximum) ranks: (%s, %s, %s)\n", a->hmatrix->get_infos("Rank_min").c_str(), a->hmatrix->get_infos("Rank_mean").c_str(), a->hmatrix->get_infos("Rank_max").c_str())); 319 } 320 PetscFunctionReturn(PETSC_SUCCESS); 321 } 322 323 /* naive implementation of MatGetRow() needed for MatConvert_Nest_AIJ() */ 324 static PetscErrorCode MatGetRow_Htool(Mat A, PetscInt row, PetscInt *nz, PetscInt **idx, PetscScalar **v) 325 { 326 Mat_Htool *a; 327 PetscScalar shift, scale; 328 PetscInt *idxc; 329 PetscBLASInt one = 1, bn; 330 331 PetscFunctionBegin; 332 PetscCall(MatShellGetScalingShifts(A, &shift, &scale, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Mat *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED)); 333 PetscCall(MatShellGetContext(A, &a)); 334 if (nz) *nz = A->cmap->N; 335 if (idx || v) { /* even if !idx, need to set idxc for htool::copy_submatrix() */ 336 PetscCall(PetscMalloc1(A->cmap->N, &idxc)); 337 for (PetscInt i = 0; i < A->cmap->N; ++i) idxc[i] = i; 338 } 339 if (idx) *idx = idxc; 340 if (v) { 341 PetscCall(PetscMalloc1(A->cmap->N, v)); 342 if (a->wrapper) a->wrapper->copy_submatrix(1, A->cmap->N, &row, idxc, *v); 343 else reinterpret_cast<htool::VirtualGenerator<PetscScalar> *>(a->kernelctx)->copy_submatrix(1, A->cmap->N, &row, idxc, *v); 344 PetscCall(PetscBLASIntCast(A->cmap->N, &bn)); 345 PetscCallBLAS("BLASscal", BLASscal_(&bn, &scale, *v, &one)); 346 if (row < A->cmap->N) (*v)[row] += shift; 347 } 348 if (!idx) PetscCall(PetscFree(idxc)); 349 PetscFunctionReturn(PETSC_SUCCESS); 350 } 351 352 static PetscErrorCode MatRestoreRow_Htool(Mat, PetscInt, PetscInt *, PetscInt **idx, PetscScalar **v) 353 { 354 PetscFunctionBegin; 355 if (idx) PetscCall(PetscFree(*idx)); 356 if (v) PetscCall(PetscFree(*v)); 357 PetscFunctionReturn(PETSC_SUCCESS); 358 } 359 360 static PetscErrorCode MatSetFromOptions_Htool(Mat A, PetscOptionItems *PetscOptionsObject) 361 { 362 Mat_Htool *a; 363 PetscInt n; 364 PetscBool flg; 365 366 PetscFunctionBegin; 367 PetscCall(MatShellGetContext(A, &a)); 368 PetscOptionsHeadBegin(PetscOptionsObject, "Htool options"); 369 PetscCall(PetscOptionsInt("-mat_htool_min_cluster_size", "Minimal leaf size in cluster tree", nullptr, a->bs[0], a->bs, nullptr)); 370 PetscCall(PetscOptionsInt("-mat_htool_max_block_size", "Maximal number of coefficients in a dense block", nullptr, a->bs[1], a->bs + 1, nullptr)); 371 PetscCall(PetscOptionsReal("-mat_htool_epsilon", "Relative error in Frobenius norm when approximating a block", nullptr, a->epsilon, &a->epsilon, nullptr)); 372 PetscCall(PetscOptionsReal("-mat_htool_eta", "Admissibility condition tolerance", nullptr, a->eta, &a->eta, nullptr)); 373 PetscCall(PetscOptionsInt("-mat_htool_min_target_depth", "Minimal cluster tree depth associated with the rows", nullptr, a->depth[0], a->depth, nullptr)); 374 PetscCall(PetscOptionsInt("-mat_htool_min_source_depth", "Minimal cluster tree depth associated with the columns", nullptr, a->depth[1], a->depth + 1, nullptr)); 375 n = 0; 376 PetscCall(PetscOptionsEList("-mat_htool_compressor", "Type of compression", "MatHtoolCompressorType", MatHtoolCompressorTypes, PETSC_STATIC_ARRAY_LENGTH(MatHtoolCompressorTypes), MatHtoolCompressorTypes[MAT_HTOOL_COMPRESSOR_SYMPARTIAL_ACA], &n, &flg)); 377 if (flg) a->compressor = MatHtoolCompressorType(n); 378 n = 0; 379 PetscCall(PetscOptionsEList("-mat_htool_clustering", "Type of clustering", "MatHtoolClusteringType", MatHtoolClusteringTypes, PETSC_STATIC_ARRAY_LENGTH(MatHtoolClusteringTypes), MatHtoolClusteringTypes[MAT_HTOOL_CLUSTERING_PCA_REGULAR], &n, &flg)); 380 if (flg) a->clustering = MatHtoolClusteringType(n); 381 PetscOptionsHeadEnd(); 382 PetscFunctionReturn(PETSC_SUCCESS); 383 } 384 385 static PetscErrorCode MatAssemblyEnd_Htool(Mat A, MatAssemblyType) 386 { 387 Mat_Htool *a; 388 const PetscInt *ranges; 389 PetscInt *offset; 390 PetscMPIInt size; 391 char S = PetscDefined(USE_COMPLEX) && A->hermitian == PETSC_BOOL3_TRUE ? 'H' : (A->symmetric == PETSC_BOOL3_TRUE ? 'S' : 'N'), uplo = S == 'N' ? 'N' : 'U'; 392 htool::VirtualGenerator<PetscScalar> *generator = nullptr; 393 std::shared_ptr<htool::VirtualCluster> t, s = nullptr; 394 std::shared_ptr<htool::VirtualLowRankGenerator<PetscScalar>> compressor = nullptr; 395 396 PetscFunctionBegin; 397 PetscCall(PetscCitationsRegister(HtoolCitation, &HtoolCite)); 398 PetscCall(MatShellGetContext(A, &a)); 399 delete a->wrapper; 400 delete a->hmatrix; 401 PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)A), &size)); 402 PetscCall(PetscMalloc1(2 * size, &offset)); 403 PetscCall(MatGetOwnershipRanges(A, &ranges)); 404 for (PetscInt i = 0; i < size; ++i) { 405 offset[2 * i] = ranges[i]; 406 offset[2 * i + 1] = ranges[i + 1] - ranges[i]; 407 } 408 switch (a->clustering) { 409 case MAT_HTOOL_CLUSTERING_PCA_GEOMETRIC: 410 t = std::make_shared<htool::Cluster<htool::PCA<htool::SplittingTypes::GeometricSplitting>>>(a->dim); 411 break; 412 case MAT_HTOOL_CLUSTERING_BOUNDING_BOX_1_GEOMETRIC: 413 t = std::make_shared<htool::Cluster<htool::BoundingBox1<htool::SplittingTypes::GeometricSplitting>>>(a->dim); 414 break; 415 case MAT_HTOOL_CLUSTERING_BOUNDING_BOX_1_REGULAR: 416 t = std::make_shared<htool::Cluster<htool::BoundingBox1<htool::SplittingTypes::RegularSplitting>>>(a->dim); 417 break; 418 default: 419 t = std::make_shared<htool::Cluster<htool::PCA<htool::SplittingTypes::RegularSplitting>>>(a->dim); 420 } 421 t->set_minclustersize(a->bs[0]); 422 t->build(A->rmap->N, a->gcoords_target, offset, -1, PetscObjectComm((PetscObject)A)); 423 if (a->kernel) a->wrapper = new WrapperHtool(A->rmap->N, A->cmap->N, a->dim, a->kernel, a->kernelctx); 424 else { 425 a->wrapper = nullptr; 426 generator = reinterpret_cast<htool::VirtualGenerator<PetscScalar> *>(a->kernelctx); 427 } 428 if (a->gcoords_target != a->gcoords_source) { 429 PetscCall(MatGetOwnershipRangesColumn(A, &ranges)); 430 for (PetscInt i = 0; i < size; ++i) { 431 offset[2 * i] = ranges[i]; 432 offset[2 * i + 1] = ranges[i + 1] - ranges[i]; 433 } 434 switch (a->clustering) { 435 case MAT_HTOOL_CLUSTERING_PCA_GEOMETRIC: 436 s = std::make_shared<htool::Cluster<htool::PCA<htool::SplittingTypes::GeometricSplitting>>>(a->dim); 437 break; 438 case MAT_HTOOL_CLUSTERING_BOUNDING_BOX_1_GEOMETRIC: 439 s = std::make_shared<htool::Cluster<htool::BoundingBox1<htool::SplittingTypes::GeometricSplitting>>>(a->dim); 440 break; 441 case MAT_HTOOL_CLUSTERING_BOUNDING_BOX_1_REGULAR: 442 s = std::make_shared<htool::Cluster<htool::BoundingBox1<htool::SplittingTypes::RegularSplitting>>>(a->dim); 443 break; 444 default: 445 s = std::make_shared<htool::Cluster<htool::PCA<htool::SplittingTypes::RegularSplitting>>>(a->dim); 446 } 447 s->set_minclustersize(a->bs[0]); 448 s->build(A->cmap->N, a->gcoords_source, offset, -1, PetscObjectComm((PetscObject)A)); 449 S = uplo = 'N'; 450 } 451 PetscCall(PetscFree(offset)); 452 switch (a->compressor) { 453 case MAT_HTOOL_COMPRESSOR_FULL_ACA: 454 compressor = std::make_shared<htool::fullACA<PetscScalar>>(); 455 break; 456 case MAT_HTOOL_COMPRESSOR_SVD: 457 compressor = std::make_shared<htool::SVD<PetscScalar>>(); 458 break; 459 default: 460 compressor = std::make_shared<htool::sympartialACA<PetscScalar>>(); 461 } 462 a->hmatrix = dynamic_cast<htool::VirtualHMatrix<PetscScalar> *>(new htool::HMatrix<PetscScalar>(t, s ? s : t, a->epsilon, a->eta, S, uplo, -1, PetscObjectComm((PetscObject)A))); 463 a->hmatrix->set_compression(compressor); 464 a->hmatrix->set_maxblocksize(a->bs[1]); 465 a->hmatrix->set_mintargetdepth(a->depth[0]); 466 a->hmatrix->set_minsourcedepth(a->depth[1]); 467 if (s) a->hmatrix->build(a->wrapper ? *a->wrapper : *generator, a->gcoords_target, a->gcoords_source); 468 else a->hmatrix->build(a->wrapper ? *a->wrapper : *generator, a->gcoords_target); 469 PetscFunctionReturn(PETSC_SUCCESS); 470 } 471 472 static PetscErrorCode MatProductNumeric_Htool(Mat C) 473 { 474 Mat_Product *product = C->product; 475 Mat_Htool *a; 476 const PetscScalar *in; 477 PetscScalar *out; 478 PetscInt N, lda; 479 480 PetscFunctionBegin; 481 MatCheckProduct(C, 1); 482 PetscCall(MatGetSize(C, nullptr, &N)); 483 PetscCall(MatDenseGetLDA(C, &lda)); 484 PetscCheck(lda == C->rmap->n, PETSC_COMM_SELF, PETSC_ERR_SUP, "Unsupported leading dimension (%" PetscInt_FMT " != %" PetscInt_FMT ")", lda, C->rmap->n); 485 PetscCall(MatDenseGetArrayRead(product->B, &in)); 486 PetscCall(MatDenseGetArrayWrite(C, &out)); 487 PetscCall(MatShellGetContext(product->A, &a)); 488 switch (product->type) { 489 case MATPRODUCT_AB: 490 a->hmatrix->mvprod_local_to_local(in, out, N); 491 break; 492 case MATPRODUCT_AtB: 493 a->hmatrix->mvprod_transp_local_to_local(in, out, N); 494 break; 495 default: 496 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "MatProductType %s is not supported", MatProductTypes[product->type]); 497 } 498 PetscCall(MatDenseRestoreArrayWrite(C, &out)); 499 PetscCall(MatDenseRestoreArrayRead(product->B, &in)); 500 PetscFunctionReturn(PETSC_SUCCESS); 501 } 502 503 static PetscErrorCode MatProductSymbolic_Htool(Mat C) 504 { 505 Mat_Product *product = C->product; 506 Mat A, B; 507 PetscBool flg; 508 509 PetscFunctionBegin; 510 MatCheckProduct(C, 1); 511 A = product->A; 512 B = product->B; 513 PetscCall(PetscObjectTypeCompareAny((PetscObject)B, &flg, MATSEQDENSE, MATMPIDENSE, "")); 514 PetscCheck(flg && (product->type == MATPRODUCT_AB || product->type == MATPRODUCT_AtB), PetscObjectComm((PetscObject)B), PETSC_ERR_SUP, "ProductType %s not supported for %s", MatProductTypes[product->type], ((PetscObject)product->B)->type_name); 515 if (C->rmap->n == PETSC_DECIDE || C->cmap->n == PETSC_DECIDE || C->rmap->N == PETSC_DECIDE || C->cmap->N == PETSC_DECIDE) { 516 if (product->type == MATPRODUCT_AB) PetscCall(MatSetSizes(C, A->rmap->n, B->cmap->n, A->rmap->N, B->cmap->N)); 517 else PetscCall(MatSetSizes(C, A->cmap->n, B->cmap->n, A->cmap->N, B->cmap->N)); 518 } 519 PetscCall(MatSetType(C, MATDENSE)); 520 PetscCall(MatSetUp(C)); 521 PetscCall(MatSetOption(C, MAT_NO_OFF_PROC_ENTRIES, PETSC_TRUE)); 522 PetscCall(MatAssemblyBegin(C, MAT_FINAL_ASSEMBLY)); 523 PetscCall(MatAssemblyEnd(C, MAT_FINAL_ASSEMBLY)); 524 C->ops->productsymbolic = nullptr; 525 C->ops->productnumeric = MatProductNumeric_Htool; 526 PetscFunctionReturn(PETSC_SUCCESS); 527 } 528 529 static PetscErrorCode MatProductSetFromOptions_Htool(Mat C) 530 { 531 PetscFunctionBegin; 532 MatCheckProduct(C, 1); 533 if (C->product->type == MATPRODUCT_AB || C->product->type == MATPRODUCT_AtB) C->ops->productsymbolic = MatProductSymbolic_Htool; 534 PetscFunctionReturn(PETSC_SUCCESS); 535 } 536 537 static PetscErrorCode MatHtoolGetHierarchicalMat_Htool(Mat A, const htool::VirtualHMatrix<PetscScalar> **hmatrix) 538 { 539 Mat_Htool *a; 540 541 PetscFunctionBegin; 542 PetscCall(MatShellGetContext(A, &a)); 543 *hmatrix = a->hmatrix; 544 PetscFunctionReturn(PETSC_SUCCESS); 545 } 546 547 /*@C 548 MatHtoolGetHierarchicalMat - Retrieves the opaque pointer to a Htool virtual matrix stored in a `MATHTOOL`. 549 550 No Fortran Support, No C Support 551 552 Input Parameter: 553 . A - hierarchical matrix 554 555 Output Parameter: 556 . hmatrix - opaque pointer to a Htool virtual matrix 557 558 Level: advanced 559 560 .seealso: [](ch_matrices), `Mat`, `MATHTOOL` 561 @*/ 562 PETSC_EXTERN PetscErrorCode MatHtoolGetHierarchicalMat(Mat A, const htool::VirtualHMatrix<PetscScalar> **hmatrix) 563 { 564 PetscFunctionBegin; 565 PetscValidHeaderSpecific(A, MAT_CLASSID, 1); 566 PetscAssertPointer(hmatrix, 2); 567 PetscTryMethod(A, "MatHtoolGetHierarchicalMat_C", (Mat, const htool::VirtualHMatrix<PetscScalar> **), (A, hmatrix)); 568 PetscFunctionReturn(PETSC_SUCCESS); 569 } 570 571 static PetscErrorCode MatHtoolSetKernel_Htool(Mat A, MatHtoolKernelFn *kernel, void *kernelctx) 572 { 573 Mat_Htool *a; 574 575 PetscFunctionBegin; 576 PetscCall(MatShellGetContext(A, &a)); 577 a->kernel = kernel; 578 a->kernelctx = kernelctx; 579 delete a->wrapper; 580 if (a->kernel) a->wrapper = new WrapperHtool(A->rmap->N, A->cmap->N, a->dim, a->kernel, a->kernelctx); 581 PetscFunctionReturn(PETSC_SUCCESS); 582 } 583 584 /*@C 585 MatHtoolSetKernel - Sets the kernel and context used for the assembly of a `MATHTOOL`. 586 587 Collective, No Fortran Support 588 589 Input Parameters: 590 + A - hierarchical matrix 591 . kernel - computational kernel (or `NULL`) 592 - kernelctx - kernel context (if kernel is `NULL`, the pointer must be of type htool::VirtualGenerator<PetscScalar>*) 593 594 Level: advanced 595 596 .seealso: [](ch_matrices), `Mat`, `MATHTOOL`, `MatCreateHtoolFromKernel()` 597 @*/ 598 PetscErrorCode MatHtoolSetKernel(Mat A, MatHtoolKernelFn *kernel, void *kernelctx) 599 { 600 PetscFunctionBegin; 601 PetscValidHeaderSpecific(A, MAT_CLASSID, 1); 602 if (!kernelctx) PetscValidFunction(kernel, 2); 603 if (!kernel) PetscAssertPointer(kernelctx, 3); 604 PetscTryMethod(A, "MatHtoolSetKernel_C", (Mat, MatHtoolKernelFn *, void *), (A, kernel, kernelctx)); 605 PetscFunctionReturn(PETSC_SUCCESS); 606 } 607 608 static PetscErrorCode MatHtoolGetPermutationSource_Htool(Mat A, IS *is) 609 { 610 Mat_Htool *a; 611 std::vector<PetscInt> source; 612 613 PetscFunctionBegin; 614 PetscCall(MatShellGetContext(A, &a)); 615 source = a->hmatrix->get_source_cluster()->get_local_perm(); 616 PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)A), source.size(), source.data(), PETSC_COPY_VALUES, is)); 617 PetscCall(ISSetPermutation(*is)); 618 PetscFunctionReturn(PETSC_SUCCESS); 619 } 620 621 /*@ 622 MatHtoolGetPermutationSource - Gets the permutation associated to the source cluster for a `MATHTOOL` matrix. 623 624 Input Parameter: 625 . A - hierarchical matrix 626 627 Output Parameter: 628 . is - permutation 629 630 Level: advanced 631 632 .seealso: [](ch_matrices), `Mat`, `MATHTOOL`, `MatHtoolGetPermutationTarget()`, `MatHtoolUsePermutation()` 633 @*/ 634 PetscErrorCode MatHtoolGetPermutationSource(Mat A, IS *is) 635 { 636 PetscFunctionBegin; 637 PetscValidHeaderSpecific(A, MAT_CLASSID, 1); 638 if (!is) PetscAssertPointer(is, 2); 639 PetscTryMethod(A, "MatHtoolGetPermutationSource_C", (Mat, IS *), (A, is)); 640 PetscFunctionReturn(PETSC_SUCCESS); 641 } 642 643 static PetscErrorCode MatHtoolGetPermutationTarget_Htool(Mat A, IS *is) 644 { 645 Mat_Htool *a; 646 std::vector<PetscInt> target; 647 648 PetscFunctionBegin; 649 PetscCall(MatShellGetContext(A, &a)); 650 target = a->hmatrix->get_target_cluster()->get_local_perm(); 651 PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)A), target.size(), target.data(), PETSC_COPY_VALUES, is)); 652 PetscCall(ISSetPermutation(*is)); 653 PetscFunctionReturn(PETSC_SUCCESS); 654 } 655 656 /*@ 657 MatHtoolGetPermutationTarget - Gets the permutation associated to the target cluster for a `MATHTOOL` matrix. 658 659 Input Parameter: 660 . A - hierarchical matrix 661 662 Output Parameter: 663 . is - permutation 664 665 Level: advanced 666 667 .seealso: [](ch_matrices), `Mat`, `MATHTOOL`, `MatHtoolGetPermutationSource()`, `MatHtoolUsePermutation()` 668 @*/ 669 PetscErrorCode MatHtoolGetPermutationTarget(Mat A, IS *is) 670 { 671 PetscFunctionBegin; 672 PetscValidHeaderSpecific(A, MAT_CLASSID, 1); 673 if (!is) PetscAssertPointer(is, 2); 674 PetscTryMethod(A, "MatHtoolGetPermutationTarget_C", (Mat, IS *), (A, is)); 675 PetscFunctionReturn(PETSC_SUCCESS); 676 } 677 678 static PetscErrorCode MatHtoolUsePermutation_Htool(Mat A, PetscBool use) 679 { 680 Mat_Htool *a; 681 682 PetscFunctionBegin; 683 PetscCall(MatShellGetContext(A, &a)); 684 a->hmatrix->set_use_permutation(use); 685 PetscFunctionReturn(PETSC_SUCCESS); 686 } 687 688 /*@ 689 MatHtoolUsePermutation - Sets whether a `MATHTOOL` matrix should permute input (resp. output) vectors following its internal source (resp. target) permutation. 690 691 Input Parameters: 692 + A - hierarchical matrix 693 - use - Boolean value 694 695 Level: advanced 696 697 .seealso: [](ch_matrices), `Mat`, `MATHTOOL`, `MatHtoolGetPermutationSource()`, `MatHtoolGetPermutationTarget()` 698 @*/ 699 PetscErrorCode MatHtoolUsePermutation(Mat A, PetscBool use) 700 { 701 PetscFunctionBegin; 702 PetscValidHeaderSpecific(A, MAT_CLASSID, 1); 703 PetscValidLogicalCollectiveBool(A, use, 2); 704 PetscTryMethod(A, "MatHtoolUsePermutation_C", (Mat, PetscBool), (A, use)); 705 PetscFunctionReturn(PETSC_SUCCESS); 706 } 707 708 static PetscErrorCode MatConvert_Htool_Dense(Mat A, MatType, MatReuse reuse, Mat *B) 709 { 710 Mat C; 711 Mat_Htool *a; 712 PetscScalar *array, shift, scale; 713 PetscInt lda; 714 715 PetscFunctionBegin; 716 PetscCall(MatShellGetScalingShifts(A, &shift, &scale, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Mat *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED)); 717 PetscCall(MatShellGetContext(A, &a)); 718 if (reuse == MAT_REUSE_MATRIX) { 719 C = *B; 720 PetscCheck(C->rmap->n == A->rmap->n && C->cmap->N == A->cmap->N, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Incompatible dimensions"); 721 PetscCall(MatDenseGetLDA(C, &lda)); 722 PetscCheck(lda == C->rmap->n, PETSC_COMM_SELF, PETSC_ERR_SUP, "Unsupported leading dimension (%" PetscInt_FMT " != %" PetscInt_FMT ")", lda, C->rmap->n); 723 } else { 724 PetscCall(MatCreate(PetscObjectComm((PetscObject)A), &C)); 725 PetscCall(MatSetSizes(C, A->rmap->n, A->cmap->n, A->rmap->N, A->cmap->N)); 726 PetscCall(MatSetType(C, MATDENSE)); 727 PetscCall(MatSetUp(C)); 728 PetscCall(MatSetOption(C, MAT_NO_OFF_PROC_ENTRIES, PETSC_TRUE)); 729 } 730 PetscCall(MatAssemblyBegin(C, MAT_FINAL_ASSEMBLY)); 731 PetscCall(MatAssemblyEnd(C, MAT_FINAL_ASSEMBLY)); 732 PetscCall(MatDenseGetArrayWrite(C, &array)); 733 a->hmatrix->copy_local_dense_perm(array); 734 PetscCall(MatDenseRestoreArrayWrite(C, &array)); 735 PetscCall(MatShift(C, shift)); 736 PetscCall(MatScale(C, scale)); 737 if (reuse == MAT_INPLACE_MATRIX) { 738 PetscCall(MatHeaderReplace(A, &C)); 739 } else *B = C; 740 PetscFunctionReturn(PETSC_SUCCESS); 741 } 742 743 static PetscErrorCode GenEntriesTranspose(PetscInt sdim, PetscInt M, PetscInt N, const PetscInt *rows, const PetscInt *cols, PetscScalar *ptr, void *ctx) 744 { 745 MatHtoolKernelTranspose *generator = (MatHtoolKernelTranspose *)ctx; 746 PetscScalar *tmp; 747 748 PetscFunctionBegin; 749 PetscCall(generator->kernel(sdim, N, M, cols, rows, ptr, generator->kernelctx)); 750 PetscCall(PetscMalloc1(M * N, &tmp)); 751 PetscCall(PetscArraycpy(tmp, ptr, M * N)); 752 for (PetscInt i = 0; i < M; ++i) { 753 for (PetscInt j = 0; j < N; ++j) ptr[i + j * M] = tmp[j + i * N]; 754 } 755 PetscCall(PetscFree(tmp)); 756 PetscFunctionReturn(PETSC_SUCCESS); 757 } 758 759 /* naive implementation which keeps a reference to the original Mat */ 760 static PetscErrorCode MatTranspose_Htool(Mat A, MatReuse reuse, Mat *B) 761 { 762 Mat C; 763 Mat_Htool *a, *c; 764 PetscScalar shift, scale; 765 PetscInt M = A->rmap->N, N = A->cmap->N, m = A->rmap->n, n = A->cmap->n; 766 PetscContainer container; 767 MatHtoolKernelTranspose *kernelt; 768 769 PetscFunctionBegin; 770 PetscCall(MatShellGetScalingShifts(A, &shift, &scale, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Vec *)MAT_SHELL_NOT_ALLOWED, (Mat *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED, (IS *)MAT_SHELL_NOT_ALLOWED)); 771 PetscCall(MatShellGetContext(A, &a)); 772 if (reuse == MAT_REUSE_MATRIX) PetscCall(MatTransposeCheckNonzeroState_Private(A, *B)); 773 PetscCheck(reuse != MAT_INPLACE_MATRIX, PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "MatTranspose() with MAT_INPLACE_MATRIX not supported"); 774 if (reuse == MAT_INITIAL_MATRIX) { 775 PetscCall(MatCreate(PetscObjectComm((PetscObject)A), &C)); 776 PetscCall(MatSetSizes(C, n, m, N, M)); 777 PetscCall(MatSetType(C, ((PetscObject)A)->type_name)); 778 PetscCall(MatSetUp(C)); 779 PetscCall(PetscContainerCreate(PetscObjectComm((PetscObject)C), &container)); 780 PetscCall(PetscNew(&kernelt)); 781 PetscCall(PetscContainerSetPointer(container, kernelt)); 782 PetscCall(PetscObjectCompose((PetscObject)C, "KernelTranspose", (PetscObject)container)); 783 } else { 784 C = *B; 785 PetscCall(PetscObjectQuery((PetscObject)C, "KernelTranspose", (PetscObject *)&container)); 786 PetscCheck(container, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Must call MatTranspose() with MAT_INITIAL_MATRIX first"); 787 PetscCall(PetscContainerGetPointer(container, (void **)&kernelt)); 788 } 789 PetscCall(MatShellGetContext(C, &c)); 790 c->dim = a->dim; 791 PetscCall(MatShift(C, shift)); 792 PetscCall(MatScale(C, scale)); 793 c->kernel = GenEntriesTranspose; 794 if (kernelt->A != A) { 795 PetscCall(MatDestroy(&kernelt->A)); 796 kernelt->A = A; 797 PetscCall(PetscObjectReference((PetscObject)A)); 798 } 799 kernelt->kernel = a->kernel; 800 kernelt->kernelctx = a->kernelctx; 801 c->kernelctx = kernelt; 802 if (reuse == MAT_INITIAL_MATRIX) { 803 PetscCall(PetscMalloc1(N * c->dim, &c->gcoords_target)); 804 PetscCall(PetscArraycpy(c->gcoords_target, a->gcoords_source, N * c->dim)); 805 if (a->gcoords_target != a->gcoords_source) { 806 PetscCall(PetscMalloc1(M * c->dim, &c->gcoords_source)); 807 PetscCall(PetscArraycpy(c->gcoords_source, a->gcoords_target, M * c->dim)); 808 } else c->gcoords_source = c->gcoords_target; 809 PetscCall(PetscCalloc2(M, &c->work_source, N, &c->work_target)); 810 } 811 PetscCall(MatAssemblyBegin(C, MAT_FINAL_ASSEMBLY)); 812 PetscCall(MatAssemblyEnd(C, MAT_FINAL_ASSEMBLY)); 813 if (reuse == MAT_INITIAL_MATRIX) *B = C; 814 PetscFunctionReturn(PETSC_SUCCESS); 815 } 816 817 /*@C 818 MatCreateHtoolFromKernel - Creates a `MATHTOOL` from a user-supplied kernel. 819 820 Collective, No Fortran Support 821 822 Input Parameters: 823 + comm - MPI communicator 824 . m - number of local rows (or `PETSC_DECIDE` to have calculated if `M` is given) 825 . n - number of local columns (or `PETSC_DECIDE` to have calculated if `N` is given) 826 . M - number of global rows (or `PETSC_DETERMINE` to have calculated if `m` is given) 827 . N - number of global columns (or `PETSC_DETERMINE` to have calculated if `n` is given) 828 . spacedim - dimension of the space coordinates 829 . coords_target - coordinates of the target 830 . coords_source - coordinates of the source 831 . kernel - computational kernel (or `NULL`) 832 - kernelctx - kernel context (if kernel is `NULL`, the pointer must be of type htool::VirtualGenerator<PetscScalar>*) 833 834 Output Parameter: 835 . B - matrix 836 837 Options Database Keys: 838 + -mat_htool_min_cluster_size <`PetscInt`> - minimal leaf size in cluster tree 839 . -mat_htool_max_block_size <`PetscInt`> - maximal number of coefficients in a dense block 840 . -mat_htool_epsilon <`PetscReal`> - relative error in Frobenius norm when approximating a block 841 . -mat_htool_eta <`PetscReal`> - admissibility condition tolerance 842 . -mat_htool_min_target_depth <`PetscInt`> - minimal cluster tree depth associated with the rows 843 . -mat_htool_min_source_depth <`PetscInt`> - minimal cluster tree depth associated with the columns 844 . -mat_htool_compressor <sympartialACA, fullACA, SVD> - type of compression 845 - -mat_htool_clustering <PCARegular, PCAGeometric, BounbingBox1Regular, BoundingBox1Geometric> - type of clustering 846 847 Level: intermediate 848 849 .seealso: [](ch_matrices), `Mat`, `MatCreate()`, `MATHTOOL`, `PCSetCoordinates()`, `MatHtoolSetKernel()`, `MatHtoolCompressorType`, `MATH2OPUS`, `MatCreateH2OpusFromKernel()` 850 @*/ 851 PetscErrorCode MatCreateHtoolFromKernel(MPI_Comm comm, PetscInt m, PetscInt n, PetscInt M, PetscInt N, PetscInt spacedim, const PetscReal coords_target[], const PetscReal coords_source[], MatHtoolKernelFn *kernel, void *kernelctx, Mat *B) 852 { 853 Mat A; 854 Mat_Htool *a; 855 856 PetscFunctionBegin; 857 PetscCall(MatCreate(comm, &A)); 858 PetscValidLogicalCollectiveInt(A, spacedim, 6); 859 PetscAssertPointer(coords_target, 7); 860 PetscAssertPointer(coords_source, 8); 861 if (!kernelctx) PetscValidFunction(kernel, 9); 862 if (!kernel) PetscAssertPointer(kernelctx, 10); 863 PetscCall(MatSetSizes(A, m, n, M, N)); 864 PetscCall(MatSetType(A, MATHTOOL)); 865 PetscCall(MatSetUp(A)); 866 PetscCall(MatShellGetContext(A, &a)); 867 a->dim = spacedim; 868 a->kernel = kernel; 869 a->kernelctx = kernelctx; 870 PetscCall(PetscCalloc1(A->rmap->N * spacedim, &a->gcoords_target)); 871 PetscCall(PetscArraycpy(a->gcoords_target + A->rmap->rstart * spacedim, coords_target, A->rmap->n * spacedim)); 872 PetscCall(MPIU_Allreduce(MPI_IN_PLACE, a->gcoords_target, A->rmap->N * spacedim, MPIU_REAL, MPI_SUM, PetscObjectComm((PetscObject)A))); /* global target coordinates */ 873 if (coords_target != coords_source) { 874 PetscCall(PetscCalloc1(A->cmap->N * spacedim, &a->gcoords_source)); 875 PetscCall(PetscArraycpy(a->gcoords_source + A->cmap->rstart * spacedim, coords_source, A->cmap->n * spacedim)); 876 PetscCall(MPIU_Allreduce(MPI_IN_PLACE, a->gcoords_source, A->cmap->N * spacedim, MPIU_REAL, MPI_SUM, PetscObjectComm((PetscObject)A))); /* global source coordinates */ 877 } else a->gcoords_source = a->gcoords_target; 878 PetscCall(PetscCalloc2(A->cmap->N, &a->work_source, A->rmap->N, &a->work_target)); 879 *B = A; 880 PetscFunctionReturn(PETSC_SUCCESS); 881 } 882 883 /*MC 884 MATHTOOL = "htool" - A matrix type for hierarchical matrices using the Htool package. 885 886 Use `./configure --download-htool` to install PETSc to use Htool. 887 888 Options Database Key: 889 . -mat_type htool - matrix type to `MATHTOOL` 890 891 Level: beginner 892 893 .seealso: [](ch_matrices), `Mat`, `MATH2OPUS`, `MATDENSE`, `MatCreateHtoolFromKernel()`, `MatHtoolSetKernel()` 894 M*/ 895 PETSC_EXTERN PetscErrorCode MatCreate_Htool(Mat A) 896 { 897 Mat_Htool *a; 898 899 PetscFunctionBegin; 900 PetscCall(MatSetType(A, MATSHELL)); 901 PetscCall(PetscNew(&a)); 902 PetscCall(MatShellSetContext(A, a)); 903 PetscCall(MatShellSetOperation(A, MATOP_GET_DIAGONAL, (void (*)(void))MatGetDiagonal_Htool)); 904 PetscCall(MatShellSetOperation(A, MATOP_GET_DIAGONAL_BLOCK, (void (*)(void))MatGetDiagonalBlock_Htool)); 905 PetscCall(MatShellSetOperation(A, MATOP_MULT, (void (*)(void))MatMult_Htool)); 906 PetscCall(MatShellSetOperation(A, MATOP_MULT_TRANSPOSE, (void (*)(void))MatMultTranspose_Htool)); 907 if (!PetscDefined(USE_COMPLEX)) PetscCall(MatShellSetOperation(A, MATOP_MULT_HERMITIAN_TRANSPOSE, (void (*)(void))MatMultTranspose_Htool)); 908 A->ops->increaseoverlap = MatIncreaseOverlap_Htool; 909 A->ops->createsubmatrices = MatCreateSubMatrices_Htool; 910 PetscCall(MatShellSetOperation(A, MATOP_VIEW, (void (*)(void))MatView_Htool)); 911 PetscCall(MatShellSetOperation(A, MATOP_SET_FROM_OPTIONS, (void (*)(void))MatSetFromOptions_Htool)); 912 PetscCall(MatShellSetOperation(A, MATOP_GET_ROW, (void (*)(void))MatGetRow_Htool)); 913 PetscCall(MatShellSetOperation(A, MATOP_RESTORE_ROW, (void (*)(void))MatRestoreRow_Htool)); 914 PetscCall(MatShellSetOperation(A, MATOP_ASSEMBLY_END, (void (*)(void))MatAssemblyEnd_Htool)); 915 PetscCall(MatShellSetOperation(A, MATOP_TRANSPOSE, (void (*)(void))MatTranspose_Htool)); 916 PetscCall(MatShellSetOperation(A, MATOP_DESTROY, (void (*)(void))MatDestroy_Htool)); 917 a->dim = 0; 918 a->gcoords_target = nullptr; 919 a->gcoords_source = nullptr; 920 a->bs[0] = 10; 921 a->bs[1] = 1000000; 922 a->epsilon = PetscSqrtReal(PETSC_SMALL); 923 a->eta = 10.0; 924 a->depth[0] = 0; 925 a->depth[1] = 0; 926 a->compressor = MAT_HTOOL_COMPRESSOR_SYMPARTIAL_ACA; 927 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatProductSetFromOptions_htool_seqdense_C", MatProductSetFromOptions_Htool)); 928 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatProductSetFromOptions_htool_mpidense_C", MatProductSetFromOptions_Htool)); 929 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatConvert_htool_seqdense_C", MatConvert_Htool_Dense)); 930 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatConvert_htool_mpidense_C", MatConvert_Htool_Dense)); 931 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatHtoolGetHierarchicalMat_C", MatHtoolGetHierarchicalMat_Htool)); 932 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatHtoolSetKernel_C", MatHtoolSetKernel_Htool)); 933 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatHtoolGetPermutationSource_C", MatHtoolGetPermutationSource_Htool)); 934 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatHtoolGetPermutationTarget_C", MatHtoolGetPermutationTarget_Htool)); 935 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatHtoolUsePermutation_C", MatHtoolUsePermutation_Htool)); 936 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellSetContext_C", MatShellSetContext_Immutable)); 937 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellSetContextDestroy_C", MatShellSetContextDestroy_Immutable)); 938 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellSetManageScalingShifts_C", MatShellSetManageScalingShifts_Immutable)); 939 PetscCall(PetscObjectChangeTypeName((PetscObject)A, MATHTOOL)); 940 PetscFunctionReturn(PETSC_SUCCESS); 941 } 942