MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
sparsemat.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2026, Lawrence Livermore National Security, LLC. Produced
2// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3// LICENSE and NOTICE for details. LLNL-CODE-806117.
4//
5// This file is part of the MFEM library. For more information and source code
6// availability visit https://mfem.org.
7//
8// MFEM is free software; you can redistribute it and/or modify it under the
9// terms of the BSD-3 license. We welcome feedback and contributions, see file
10// CONTRIBUTING.md for details.
11
12// Implementation of sparse matrix
13
14#include "linalg.hpp"
15#include "../general/forall.hpp"
16#include "../general/table.hpp"
19
20#include <iostream>
21#include <iomanip>
22#include <cmath>
23#include <algorithm>
24#include <limits>
25#include <cstring>
26
27#if defined(MFEM_USE_CUDA)
28#define MFEM_cu_or_hip(stub) cu##stub
29#define MFEM_Cu_or_Hip(stub) Cu##stub
30#define MFEM_CU_or_HIP(stub) CU##stub
31#define MFEM_CUDA_or_HIP(stub) CUDA##stub
32
33#if CUSPARSE_VERSION >= 11400
34#define MFEM_GPUSPARSE_ALG CUSPARSE_SPMV_CSR_ALG1
35#else // CUSPARSE_VERSION >= 11400
36#define MFEM_GPUSPARSE_ALG CUSPARSE_CSRMV_ALG1
37#endif // CUSPARSE_VERSION >= 11400
38
39#elif defined(MFEM_USE_HIP)
40#define MFEM_cu_or_hip(stub) hip##stub
41#define MFEM_Cu_or_Hip(stub) Hip##stub
42#define MFEM_CU_or_HIP(stub) HIP##stub
43#define MFEM_CUDA_or_HIP(stub) HIP##stub
44
45// https://hipsparse.readthedocs.io/en/latest/usermanual.html#hipsparsespmvalg-t
46#define MFEM_GPUSPARSE_ALG HIPSPARSE_CSRMV_ALG1
47#endif // defined(MFEM_USE_CUDA)
48
49#ifdef MFEM_USE_CUDA_OR_HIP
50#define MFEM_CHECK_SPARSE(call) \
51do { \
52 auto sparse_status = (call); \
53 if (sparse_status != MFEM_CU_or_HIP(SPARSE_STATUS_SUCCESS)) \
54 { \
55 MFEM_VERIFY(sparse_status == MFEM_CU_or_HIP(SPARSE_STATUS_SUCCESS),\
56 MFEM_cu_or_hip(sparseGetErrorString)(sparse_status)); \
57 } \
58} while (0)
59#endif // MFEM_USE_CUDA_OR_HIP
60
61
62#if defined(MFEM_USE_SINGLE)
63#define MFEM_CUDA_or_HIP_REAL_T MFEM_CUDA_or_HIP(_R_32F)
64#elif defined(MFEM_USE_DOUBLE)
65#define MFEM_CUDA_or_HIP_REAL_T MFEM_CUDA_or_HIP(_R_64F)
66#endif
67
68namespace mfem
69{
70
71using namespace std;
72
73#ifdef MFEM_USE_CUDA_OR_HIP
75// doxygen doesn't like the macro-assisted typename so let's skip parsing it:
76/// @cond Suppress_Doxygen_warnings
77MFEM_cu_or_hip(sparseHandle_t) SparseMatrix::handle = nullptr;
78/// @endcond
79#ifndef MFEM_CUDA_1897_WORKAROUND
81void * SparseMatrix::dBuffer = nullptr;
82#endif
83#endif // MFEM_USE_CUDA_OR_HIP
84
86
88{
89 // Initialize cuSPARSE/hipSPARSE library
90#ifdef MFEM_USE_CUDA_OR_HIP
93 {
94 if (!handle) { MFEM_CHECK_SPARSE(MFEM_cu_or_hip(sparseCreate)(&handle)); }
95 useGPUSparse=true;
97 }
98 else
99 {
100 useGPUSparse=false;
101 }
102#endif // MFEM_USE_CUDA_OR_HIP
103}
104
106{
107#ifdef MFEM_USE_CUDA_OR_HIP
108 if (initBuffers)
109 {
110#if CUDA_VERSION >= 10010 || defined(MFEM_USE_HIP)
111 MFEM_CHECK_SPARSE(MFEM_cu_or_hip(sparseDestroySpMat)(matA_descr));
112 MFEM_CHECK_SPARSE(MFEM_cu_or_hip(sparseDestroyDnVec)(vecX_descr));
113 MFEM_CHECK_SPARSE(MFEM_cu_or_hip(sparseDestroyDnVec)(vecY_descr));
114#else
115 cusparseDestroyMatDescr(matA_descr);
116#endif // CUDA_VERSION >= 10010 || defined(MFEM_USE_HIP)
117 initBuffers = false;
118 }
119#endif // MFEM_USE_CUDA_OR_HIP
120}
121
122SparseMatrix::SparseMatrix(int nrows, int ncols)
123 : AbstractSparseMatrix(nrows, (ncols >= 0) ? ncols : nrows),
124 Rows(new RowNode *[nrows]),
125 current_row(-1),
126 ColPtrJ(NULL),
127 ColPtrNode(NULL),
128 At(NULL),
129 isSorted(false)
130{
131 // We probably do not need to set the ownership flags here.
132 I.SetHostPtrOwner(true);
133 J.SetHostPtrOwner(true);
134 A.SetHostPtrOwner(true);
135
136 for (int i = 0; i < nrows; i++)
137 {
138 Rows[i] = NULL;
139 }
140
141#ifdef MFEM_USE_MEMALLOC
143#endif
144
146}
147
148SparseMatrix::SparseMatrix(int *i, int *j, real_t *data, int m, int n)
149 : AbstractSparseMatrix(m, n),
150 Rows(NULL),
151 ColPtrJ(NULL),
152 ColPtrNode(NULL),
153 At(NULL),
154 isSorted(false)
155{
156 I.Wrap(i, height+1, true);
157 J.Wrap(j, I[height], true);
158 A.Wrap(data, I[height], true);
159
160#ifdef MFEM_USE_MEMALLOC
161 NodesMem = NULL;
162#endif
163
165}
166
167SparseMatrix::SparseMatrix(int *i, int *j, real_t *data, int m, int n,
168 bool ownij, bool owna, bool issorted)
169 : AbstractSparseMatrix(m, n),
170 Rows(NULL),
171 ColPtrJ(NULL),
172 ColPtrNode(NULL),
173 At(NULL),
174 isSorted(issorted)
175{
176 I.Wrap(i, height+1, ownij);
177 J.Wrap(j, I[height], ownij);
178
179#ifdef MFEM_USE_MEMALLOC
180 NodesMem = NULL;
181#endif
182
183 if (data)
184 {
185 A.Wrap(data, I[height], owna);
186 }
187 else
188 {
189 const int nnz = I[height];
190 A.New(nnz);
191 for (int ii=0; ii<nnz; ++ii)
192 {
193 A[ii] = 0.0;
194 }
195 }
196
198}
199
200SparseMatrix::SparseMatrix(int nrows, int ncols, int rowsize)
201 : AbstractSparseMatrix(nrows, ncols)
202 , Rows(NULL)
203 , ColPtrJ(NULL)
204 , ColPtrNode(NULL)
205 , At(NULL)
206 , isSorted(false)
207{
208#ifdef MFEM_USE_MEMALLOC
209 NodesMem = NULL;
210#endif
211 I.New(nrows + 1);
212 J.New(nrows * rowsize);
213 A.New(nrows * rowsize);
214
215 for (int i = 0; i <= nrows; i++)
216 {
217 I[i] = i * rowsize;
218 }
219
221}
222
223SparseMatrix::SparseMatrix(const SparseMatrix &mat, bool copy_graph,
224 MemoryType mt)
225 : AbstractSparseMatrix(mat.Height(), mat.Width())
226{
227 if (mat.Finalized())
228 {
229 mat.HostReadI();
230 const int nnz = mat.I[height];
231 if (copy_graph)
232 {
233 I.New(height+1, mt == MemoryType::PRESERVE ? mat.I.GetMemoryType() : mt);
234 J.New(nnz, mt == MemoryType::PRESERVE ? mat.J.GetMemoryType() : mt);
235 I.CopyFrom(mat.I, height+1);
236 J.CopyFrom(mat.J, nnz);
237 }
238 else
239 {
240 I = mat.I;
241 J = mat.J;
244 }
245 A.New(nnz, mt == MemoryType::PRESERVE ? mat.A.GetMemoryType() : mt);
246 A.CopyFrom(mat.A, nnz);
247
248 Rows = NULL;
249#ifdef MFEM_USE_MEMALLOC
250 NodesMem = NULL;
251#endif
252 }
253 else
254 {
255#ifdef MFEM_USE_MEMALLOC
257#endif
258 Rows = new RowNode *[height];
259 for (int i = 0; i < height; i++)
260 {
261 RowNode **node_pp = &Rows[i];
262 for (RowNode *node_p = mat.Rows[i]; node_p; node_p = node_p->Prev)
263 {
264#ifdef MFEM_USE_MEMALLOC
265 RowNode *new_node_p = NodesMem->Alloc();
266#else
267 RowNode *new_node_p = new RowNode;
268#endif
269 new_node_p->Value = node_p->Value;
270 new_node_p->Column = node_p->Column;
271 *node_pp = new_node_p;
272 node_pp = &new_node_p->Prev;
273 }
274 *node_pp = NULL;
275 }
276
277 // We probably do not need to set the ownership flags here.
278 I.SetHostPtrOwner(true);
279 J.SetHostPtrOwner(true);
280 A.SetHostPtrOwner(true);
281 }
282
283 current_row = -1;
284 ColPtrJ = NULL;
285 ColPtrNode = NULL;
286 At = NULL;
287 isSorted = mat.isSorted;
288
290}
291
293 : AbstractSparseMatrix(v.Size(), v.Size())
294 , Rows(NULL)
295 , ColPtrJ(NULL)
296 , ColPtrNode(NULL)
297 , At(NULL)
298 , isSorted(true)
299{
300#ifdef MFEM_USE_MEMALLOC
301 NodesMem = NULL;
302#endif
303 I.New(height + 1);
304 J.New(height);
305 A.New(height);
306
307 for (int i = 0; i <= height; i++)
308 {
309 I[i] = i;
310 }
311
312 for (int r=0; r<height; r++)
313 {
314 J[r] = r;
315 A[r] = v[r];
316 }
317
319}
320
321void SparseMatrix::OverrideSize(int height_, int width_)
322{
323 height = height_;
324 width = width_;
325}
326
328{
329 Clear();
330
331 SparseMatrix copy(rhs);
332 Swap(copy);
333
334 return *this;
335}
336
338{
339 MFEM_ASSERT(master.Finalized(), "'master' must be finalized");
340 Clear();
341 height = master.Height();
342 width = master.Width();
343 I = master.I; I.ClearOwnerFlags();
344 J = master.J; J.ClearOwnerFlags();
345 A = master.A; A.ClearOwnerFlags();
346 isSorted = master.isSorted;
347}
348
350{
351 height = width = 0;
352 I.Reset();
353 J.Reset();
354 A.Reset();
355 Rows = NULL;
356 current_row = -1;
357 ColPtrJ = NULL;
358 ColPtrNode = NULL;
359 At = NULL;
360#ifdef MFEM_USE_MEMALLOC
361 NodesMem = NULL;
362#endif
363 isSorted = false;
364
366}
367
368int SparseMatrix::RowSize(const int i) const
369{
370 int gi = i;
371 if (gi < 0)
372 {
373 gi = -1-gi;
374 }
375
376 if (I)
377 {
378 return I[gi+1]-I[gi];
379 }
380
381 int s = 0;
382 RowNode *row = Rows[gi];
383 for ( ; row != NULL; row = row->Prev)
384 if (row->Value != 0.0)
385 {
386 s++;
387 }
388 return s;
389}
390
392{
393 int max_row_size=0;
394 int rowSize=0;
395 if (I)
396 {
397 for (int i=0; i < height; ++i)
398 {
399 rowSize = I[i+1]-I[i];
400 max_row_size = (max_row_size > rowSize) ? max_row_size : rowSize;
401 }
402 }
403 else
404 {
405 for (int i=0; i < height; ++i)
406 {
407 rowSize = RowSize(i);
408 max_row_size = (max_row_size > rowSize) ? max_row_size : rowSize;
409 }
410 }
411
412 return max_row_size;
413}
414
416{
417 MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
418
419 return J + I[row];
420}
421
422const int *SparseMatrix::GetRowColumns(const int row) const
423{
424 MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
425
426 return J + I[row];
427}
428
430{
431 MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
432
433 return A + I[row];
434}
435
436const real_t *SparseMatrix::GetRowEntries(const int row) const
437{
438 MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
439
440 return A + I[row];
441}
442
443void SparseMatrix::SetWidth(int newWidth)
444{
445 if (newWidth == width)
446 {
447 // Nothing to be done here
448 return;
449 }
450 else if (newWidth == -1)
451 {
452 // Compute the actual width
453 width = ActualWidth();
454 // No need to reset the ColPtr, since the new ColPtr will be shorter.
455 }
456 else if (newWidth > width)
457 {
458 // We need to reset ColPtr, since now we may have additional columns.
459 if (Rows != NULL)
460 {
461 delete [] ColPtrNode;
462 ColPtrNode = static_cast<RowNode **>(NULL);
463 }
464 else
465 {
466 delete [] ColPtrJ;
467 ColPtrJ = static_cast<int *>(NULL);
468 }
469 width = newWidth;
470 }
471 else
472 {
473 // Check that the new width is bigger or equal to the actual width.
474 MFEM_ASSERT(newWidth >= ActualWidth(),
475 "The new width needs to be bigger or equal to the actual width");
476 width = newWidth;
477 }
478}
479
480
482{
483 MFEM_VERIFY(Finalized(), "Matrix is not Finalized!");
484
485 if (isSorted)
486 {
487 return;
488 }
489
490#ifdef MFEM_USE_CUDA_OR_HIP
493 {
494 const int m = Height();
495 const int n = Width();
496 const int nnzA = J.Capacity();
497 const int *d_ia = ReadI();
498 int *d_ja = ReadWriteJ();
499
500 // Get size of temporary buffer needed to sort the column indices,
501 // allocate the temporary buffer.
502 size_t pBufferSizeInBytes;
503 MFEM_CHECK_SPARSE(MFEM_cu_or_hip(sparseXcsrsort_bufferSizeExt)(handle, m, n,
504 nnzA, d_ia,
505 d_ja, &pBufferSizeInBytes));
506 void *pBuffer = MFEM_Cu_or_Hip(MemAlloc)(&pBuffer, pBufferSizeInBytes);
507
508 // Create matrix descriptor, will have default values
509 // CUSPARSE_INDEX_BASE_ZERO and CUSPARSE_MATRIX_TYPE_GENERAL.
510 MFEM_cu_or_hip(sparseMatDescr_t) sort_descr;
511 MFEM_CHECK_SPARSE(MFEM_cu_or_hip(sparseCreateMatDescr)(&sort_descr));
512
513 // Initialize permutation to identity
514 Array<int> P(nnzA);
515 int *d_P = P.Write();
516 mfem::forall(nnzA, [=] MFEM_HOST_DEVICE (int i) { d_P[i] = i; });
517
518 // Sort the column indices. The array d_ja will now be sorted. The
519 // permutation required to sort the values will be returned in d_P.
520 MFEM_CHECK_SPARSE(MFEM_cu_or_hip(sparseXcsrsort)(handle, m, n, nnzA, sort_descr,
521 d_ia, d_ja,
522 d_P, pBuffer));
523
524 // Create a copy of the unsorted matrix values.
525 real_t *d_a = ReadWriteData();
526 void *d_a_unsorted = MFEM_Cu_or_Hip(MemAlloc)(&d_a_unsorted,
527 nnzA * sizeof(real_t));
528 MFEM_Cu_or_Hip(MemcpyDtoD)(d_a_unsorted, d_a, nnzA * sizeof(real_t));
529
530 // Create the (input) dense vector with the unsorted values.
531 MFEM_cu_or_hip(sparseDnVecDescr_t) d_a_dense;
532 MFEM_CHECK_SPARSE(MFEM_cu_or_hip(sparseCreateDnVec)(&d_a_dense, nnzA,
533 d_a_unsorted,
534 MFEM_CUDA_or_HIP_REAL_T));
535
536 // Create the (output) sparse vector that will have the sorted values.
537 MFEM_cu_or_hip(sparseSpVecDescr_t) d_a_sparse;
538 MFEM_CHECK_SPARSE(MFEM_cu_or_hip(sparseCreateSpVec)(&d_a_sparse, nnzA, nnzA,
539 d_P, d_a,
540 MFEM_CU_or_HIP(SPARSE_INDEX_32I),
541 MFEM_CU_or_HIP(SPARSE_INDEX_BASE_ZERO),
542 MFEM_CUDA_or_HIP_REAL_T));
543
544 // Sort the matrix values using the permutation vector.
545 MFEM_CHECK_SPARSE(MFEM_cu_or_hip(sparseGather)(handle, d_a_dense, d_a_sparse));
546
547 // The above calls may be asynchronous, so we need to wait for them to
548 // finish before we can free memory.
549 MFEM_STREAM_SYNC;
550
551 MFEM_CHECK_SPARSE(MFEM_cu_or_hip(sparseDestroyDnVec)(d_a_dense));
552 MFEM_CHECK_SPARSE(MFEM_cu_or_hip(sparseDestroySpVec)(d_a_sparse));
553 MFEM_CHECK_SPARSE(MFEM_cu_or_hip(sparseDestroyMatDescr)(sort_descr));
554
555 MFEM_Cu_or_Hip(MemFree)(d_a_unsorted);
556 MFEM_Cu_or_Hip(MemFree)(pBuffer);
557 }
558 else
559#endif // MFEM_USE_CUDA_OR_HIP
560 {
561 const int * Ip=HostReadI();
564
566 for (int j = 0, i = 0; i < height; i++)
567 {
568 int end = Ip[i+1];
569 row.SetSize(end - j);
570 for (int k = 0; k < row.Size(); k++)
571 {
572 row[k].one = J[j+k];
573 row[k].two = A[j+k];
574 }
575 row.Sort();
576 for (int k = 0; k < row.Size(); k++, j++)
577 {
578 J[j] = row[k].one;
579 A[j] = row[k].two;
580 }
581 }
582 }
583 isSorted = true;
584}
585
587{
588 MFEM_VERIFY(Finalized(), "Matrix is not Finalized!");
589
590 for (int row = 0, end = 0; row < height; row++)
591 {
592 int start = end, j;
593 end = I[row+1];
594 for (j = start; true; j++)
595 {
596 MFEM_VERIFY(j < end, "diagonal entry not found in row = " << row);
597 if (J[j] == row) { break; }
598 }
599 const real_t diag = A[j];
600 for ( ; j > start; j--)
601 {
602 J[j] = J[j-1];
603 A[j] = A[j-1];
604 }
605 J[start] = row;
606 A[start] = diag;
607 }
608}
609
611{
612 return operator()(i,j);
613}
614
615const real_t &SparseMatrix::Elem(int i, int j) const
616{
617 return operator()(i,j);
618}
619
621{
622 MFEM_ASSERT(i < height && i >= 0 && j < width && j >= 0,
623 "Trying to access element outside of the matrix. "
624 << "height = " << height << ", "
625 << "width = " << width << ", "
626 << "i = " << i << ", "
627 << "j = " << j);
628
629 MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
630
631 for (int k = I[i], end = I[i+1]; k < end; k++)
632 {
633 if (J[k] == j)
634 {
635 return A[k];
636 }
637 }
638
639 MFEM_ABORT("Did not find i = " << i << ", j = " << j << " in matrix.");
640 return A[0];
641}
642
643const real_t &SparseMatrix::operator()(int i, int j) const
644{
645 static const real_t zero = 0.0;
646
647 MFEM_ASSERT(i < height && i >= 0 && j < width && j >= 0,
648 "Trying to access element outside of the matrix. "
649 << "height = " << height << ", "
650 << "width = " << width << ", "
651 << "i = " << i << ", "
652 << "j = " << j);
653
654 if (Finalized())
655 {
656 HostReadI();
657 HostReadJ();
658 HostReadData();
659 for (int k = I[i], end = I[i+1]; k < end; k++)
660 {
661 if (J[k] == j)
662 {
663 return A[k];
664 }
665 }
666 }
667 else
668 {
669 for (RowNode *node_p = Rows[i]; node_p != NULL; node_p = node_p->Prev)
670 {
671 if (node_p->Column == j)
672 {
673 return node_p->Value;
674 }
675 }
676 }
677
678 return zero;
679}
680
682{
683 MFEM_VERIFY(height == width, "Matrix must be square, not height = "
684 << height << ", width = " << width);
685 MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
686
687 d.SetSize(height);
688
689 const auto II = this->ReadI();
690 const auto JJ = this->ReadJ();
691 const auto AA = this->ReadData();
692 auto dd = d.Write();
693
694 mfem::forall(height, [=] MFEM_HOST_DEVICE (int i)
695 {
696 const int begin = II[i];
697 const int end = II[i+1];
698 int j;
699 for (j = begin; j < end; j++)
700 {
701 if (JJ[j] == i)
702 {
703 dd[i] = AA[j];
704 break;
705 }
706 }
707 if (j == end)
708 {
709 dd[i] = 0.;
710 }
711 });
712}
713
714/// Produces a DenseMatrix from a SparseMatrix
716{
717 int num_rows = this->Height();
718 int num_cols = this->Width();
719
720 DenseMatrix * B = new DenseMatrix(num_rows, num_cols);
721
722 this->ToDenseMatrix(*B);
723
724 return B;
725}
726
727/// Produces a DenseMatrix from a SparseMatrix
729{
730 B.SetSize(height, width);
731 B = 0.0;
732
733 for (int r=0; r<height; r++)
734 {
735 const int * col = this->GetRowColumns(r);
736 const real_t * val = this->GetRowEntries(r);
737
738 for (int cj=0; cj<this->RowSize(r); cj++)
739 {
740 B(r, col[cj]) = val[cj];
741 }
742 }
743}
744
745void SparseMatrix::Mult(const Vector &x, Vector &y) const
746{
747 if (Finalized()) { y.UseDevice(true); }
748 y = 0.0;
749 AddMult(x, y);
750}
751
752void SparseMatrix::AddMult(const Vector &x, Vector &y, const real_t a) const
753{
754 MFEM_ASSERT(width == x.Size(), "Input vector size (" << x.Size()
755 << ") must match matrix width (" << width << ")");
756 MFEM_ASSERT(height == y.Size(), "Output vector size (" << y.Size()
757 << ") must match matrix height (" << height << ")");
758
759 if (!Finalized())
760 {
761 const real_t *xp = x.HostRead();
762 real_t *yp = y.HostReadWrite();
763
764 // The matrix is not finalized, but multiplication is still possible
765 for (int i = 0; i < height; i++)
766 {
767 RowNode *row = Rows[i];
768 real_t b = 0.0;
769 for ( ; row != NULL; row = row->Prev)
770 {
771 b += row->Value * xp[row->Column];
772 }
773 *yp += a * b;
774 yp++;
775 }
776 return;
777 }
778
779#ifndef MFEM_USE_LEGACY_OPENMP
780 const int height = this->height;
781 const int nnz = J.Capacity();
782 auto d_I = Read(I, height+1);
783 auto d_J = Read(J, nnz);
784 auto d_A = Read(A, nnz);
785 auto d_x = x.Read();
786 auto d_y = y.ReadWrite();
787
788 // Skip if matrix has no non-zeros
789 if (nnz == 0) {return;}
791 {
792#ifdef MFEM_USE_CUDA_OR_HIP
793 const real_t alpha = a;
794 const real_t beta = 1.0;
795
796 // Setup descriptors
797 if (!initBuffers)
798 {
799#if CUDA_VERSION >= 10010 || defined(MFEM_USE_HIP)
800 // Setup matrix descriptor
801 MFEM_CHECK_SPARSE(MFEM_cu_or_hip(sparseCreateCsr)(
803 Width(),
804 J.Capacity(),
805 const_cast<int *>(d_I),
806 const_cast<int *>(d_J),
807 const_cast<real_t *>(d_A),
808 MFEM_CU_or_HIP(SPARSE_INDEX_32I),
809 MFEM_CU_or_HIP(SPARSE_INDEX_32I),
810 MFEM_CU_or_HIP(SPARSE_INDEX_BASE_ZERO),
811 MFEM_CUDA_or_HIP_REAL_T));
812
813 // Create handles for input/output vectors
814 MFEM_CHECK_SPARSE(MFEM_cu_or_hip(sparseCreateDnVec)(&vecX_descr,
815 x.Size(),
816 const_cast<real_t *>(d_x),
817 MFEM_CUDA_or_HIP_REAL_T));
818 MFEM_CHECK_SPARSE(MFEM_cu_or_hip(sparseCreateDnVec)(&vecY_descr, y.Size(), d_y,
819 MFEM_CUDA_or_HIP_REAL_T));
820#else
821 cusparseCreateMatDescr(&matA_descr);
822 cusparseSetMatIndexBase(matA_descr, CUSPARSE_INDEX_BASE_ZERO);
823 cusparseSetMatType(matA_descr, CUSPARSE_MATRIX_TYPE_GENERAL);
824#endif // CUDA_VERSION >= 10010 || defined(MFEM_USE_HIP)
825 initBuffers = true;
826 }
827 // Allocate kernel space. Buffer is shared between different sparsemats
828 size_t newBufferSize = 0;
829
830 MFEM_CHECK_SPARSE(MFEM_cu_or_hip(sparseSpMV_bufferSize)(
831 handle,
832 MFEM_CU_or_HIP(SPARSE_OPERATION_NON_TRANSPOSE),
833 &alpha,
836 &beta,
838 MFEM_CUDA_or_HIP_REAL_T,
839 MFEM_GPUSPARSE_ALG,
840 &newBufferSize));
841
842 // Check if we need to resize
843 if (newBufferSize > bufferSize)
844 {
845 bufferSize = newBufferSize;
846 if (dBuffer != nullptr) { MFEM_Cu_or_Hip(MemFree)(dBuffer); }
847 MFEM_Cu_or_Hip(MemAlloc)(&dBuffer, bufferSize);
848 }
849
850 // With ROCm 7, rocsparse (used by hipsparse) requires an explicit analysis call before the spmv otherwise you get errors like:
851 // invalid stage, the stage rocsparse_v2_spmv_stage_analysis must be executed before the stage rocsparse_v2_spmv_stage_compute
852 //
853 // It's not clear if this is supposed to be necessary or not but as of ROCm 7.2.1 it is still required to run without issues
854 //
855 // "This step is optional but if used may results in better performance."
856 // https://rocm.docs.amd.com/projects/hipSPARSE/en/docs-7.2.1/reference/generic.html#hipsparsespmv-preprocess
857#if HIP_VERSION_MAJOR >= 7
858 MFEM_CHECK_SPARSE(hipsparseSpMV_preprocess(
859 handle,
860 MFEM_CU_or_HIP(SPARSE_OPERATION_NON_TRANSPOSE),
861 &alpha,
864 &beta,
866 MFEM_CUDA_or_HIP_REAL_T,
867 MFEM_GPUSPARSE_ALG,
868 dBuffer));
869
870#endif
871
872#if CUDA_VERSION >= 10010 || defined(MFEM_USE_HIP)
873 // Update input/output vectors
874 MFEM_CHECK_SPARSE(MFEM_cu_or_hip(sparseDnVecSetValues)(vecX_descr,
875 const_cast<real_t *>(d_x)));
876 MFEM_CHECK_SPARSE(MFEM_cu_or_hip(sparseDnVecSetValues)(vecY_descr, d_y));
877
878 // Y = alpha A * X + beta * Y
879 MFEM_CHECK_SPARSE(MFEM_cu_or_hip(sparseSpMV)(
880 handle,
881 MFEM_CU_or_HIP(SPARSE_OPERATION_NON_TRANSPOSE),
882 &alpha,
885 &beta,
887 MFEM_CUDA_or_HIP_REAL_T,
888 MFEM_GPUSPARSE_ALG,
889 dBuffer));
890#else
891#ifdef MFEM_USE_SINGLE
892 cusparseScsrmv(handle,
893#else
894 cusparseDcsrmv(handle,
895#endif
896 CUSPARSE_OPERATION_NON_TRANSPOSE,
897 Height(),
898 Width(),
899 J.Capacity(),
900 &alpha,
902 const_cast<real_t *>(d_A),
903 const_cast<int *>(d_I),
904 const_cast<int *>(d_J),
905 const_cast<real_t *>(d_x),
906 &beta,
907 d_y);
908#endif // CUDA_VERSION >= 10010 || defined(MFEM_USE_HIP)
909#endif // MFEM_USE_CUDA_OR_HIP
910 }
911 else
912 {
913 // Native version
914 mfem::forall(height, [=] MFEM_HOST_DEVICE (int i)
915 {
916 real_t d = 0.0;
917 const int end = d_I[i+1];
918 for (int j = d_I[i]; j < end; j++)
919 {
920 d += d_A[j] * d_x[d_J[j]];
921 }
922 d_y[i] += a * d;
923 });
924
925 }
926
927#else // MFEM_USE_LEGACY_OPENMP
928 const real_t *Ap = A, *xp = x.GetData();
929 real_t *yp = y.GetData();
930 const int *Jp = J, *Ip = I;
931
932 #pragma omp parallel for
933 for (int i = 0; i < height; i++)
934 {
935 real_t d = 0.0;
936 const int end = Ip[i+1];
937 for (int j = Ip[i]; j < end; j++)
938 {
939 d += Ap[j] * xp[Jp[j]];
940 }
941 yp[i] += a * d;
942 }
943#endif // MFEM_USE_LEGACY_OPENMP
944}
945
947{
948 if (Finalized()) { y.UseDevice(true); }
949 y = 0.0;
950 AddMultTranspose(x, y);
951}
952
954 const real_t a) const
955{
956 MFEM_ASSERT(height == x.Size(), "Input vector size (" << x.Size()
957 << ") must match matrix height (" << height << ")");
958 MFEM_ASSERT(width == y.Size(), "Output vector size (" << y.Size()
959 << ") must match matrix width (" << width << ")");
960
961 if (!Finalized())
962 {
963 real_t *yp = y.HostReadWrite();
964 const real_t *xp = x.HostRead();
965 // The matrix is not finalized, but multiplication is still possible
966 for (int i = 0; i < height; i++)
967 {
968 RowNode *row = Rows[i];
969 real_t b = a * xp[i];
970 for ( ; row != NULL; row = row->Prev)
971 {
972 yp[row->Column] += row->Value * b;
973 }
974 }
975 return;
976 }
977
979 if (At)
980 {
981 At->AddMult(x, y, a);
982 }
983 else
984 {
985 real_t *yp = y.HostReadWrite();
986 const real_t *xp = x.HostRead();
987
988 const int *Ip = HostRead(I, height+1);
989 const int nnz = Ip[height];
990 const int *Jp = HostRead(J, nnz);
991 const real_t *Ap = HostRead(A, nnz);
992
993 for (int i = 0; i < height; i++)
994 {
995 const real_t xi = a * xp[i];
996 const int end = Ip[i+1];
997 for (int j = Ip[i]; j < end; j++)
998 {
999 const int Jj = Jp[j];
1000 yp[Jj] += Ap[j] * xi;
1001 }
1002 }
1003 }
1004}
1005
1007{
1008 if (At == NULL)
1009 {
1010 At = Transpose(*this);
1011 }
1012}
1013
1015{
1016 delete At;
1017 At = NULL;
1018}
1019
1021{
1023 {
1025 }
1026}
1027
1029 const Array<int> &rows, const Vector &x, Vector &y) const
1030{
1031 MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
1032
1033 const int n = rows.Size();
1034 const int nnz = J.Capacity();
1035 auto d_rows = rows.Read();
1036 auto d_I = Read(I, height+1);
1037 auto d_J = Read(J, nnz);
1038 auto d_A = Read(A, nnz);
1039 auto d_x = x.Read();
1040 auto d_y = y.Write();
1041 mfem::forall(n, [=] MFEM_HOST_DEVICE (int i)
1042 {
1043 const int r = d_rows[i];
1044 const int end = d_I[r + 1];
1045 real_t a = 0.0;
1046 for (int j = d_I[r]; j < end; j++)
1047 {
1048 a += d_A[j] * d_x[d_J[j]];
1049 }
1050 d_y[r] = a;
1051 });
1052}
1053
1055 const Array<int> &rows, const Vector &x, Vector &y, const real_t a) const
1056{
1057 MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
1058
1059 for (int i = 0; i < rows.Size(); i++)
1060 {
1061 int r = rows[i];
1062 int end = I[r + 1];
1063 real_t val = 0.0;
1064 for (int j = I[r]; j < end; j++)
1065 {
1066 val += A[j] * x(J[j]);
1067 }
1068 y(r) += a * val;
1069 }
1070}
1071
1073{
1074 MFEM_ASSERT(Finalized(), "Matrix must be finalized.");
1075 MFEM_ASSERT(x.Size() == Width(), "Input vector size (" << x.Size()
1076 << ") must match matrix width (" << Width() << ")");
1077
1079
1080 const int height = Height();
1081 const int nnz = J.Capacity();
1082 auto d_I = Read(I, height+1);
1083 auto d_J = Read(J, nnz);
1084 auto d_x = Read(x.GetMemory(), x.Size());
1085 auto d_y = Write(y.GetMemory(), y.Size());
1086 mfem::forall(height, [=] MFEM_HOST_DEVICE (int i)
1087 {
1088 bool d_yi = false;
1089 const int end = d_I[i+1];
1090 for (int j = d_I[i]; j < end; j++)
1091 {
1092 if (d_x[d_J[j]])
1093 {
1094 d_yi = true;
1095 break;
1096 }
1097 }
1098 d_y[i] = d_yi;
1099 });
1100}
1101
1103 Array<int> &y) const
1104{
1105 MFEM_ASSERT(Finalized(), "Matrix must be finalized.");
1106 MFEM_ASSERT(x.Size() == Height(), "Input vector size (" << x.Size()
1107 << ") must match matrix height (" << Height() << ")");
1108
1109 y.SetSize(Width());
1110 y = 0;
1111
1112 HostReadI();
1113 HostReadJ();
1114 x.HostRead();
1115 y.HostReadWrite();
1116
1117 for (int i = 0; i < Height(); i++)
1118 {
1119 if (x[i])
1120 {
1121 int end = I[i+1];
1122 for (int j = I[i]; j < end; j++)
1123 {
1124 y[J[j]] = x[i];
1125 }
1126 }
1127 }
1128}
1129
1130void SparseMatrix::AbsMult(const Vector &x, Vector &y) const
1131{
1132 MFEM_ASSERT(width == x.Size(), "Input vector size (" << x.Size()
1133 << ") must match matrix width (" << width << ")");
1134 MFEM_ASSERT(height == y.Size(), "Output vector size (" << y.Size()
1135 << ") must match matrix height (" << height << ")");
1136
1137 if (Finalized()) { y.UseDevice(true); }
1138 y = 0.0;
1139
1140 if (!Finalized())
1141 {
1142 const real_t *xp = x.HostRead();
1143 real_t *yp = y.HostReadWrite();
1144
1145 // The matrix is not finalized, but multiplication is still possible
1146 for (int i = 0; i < height; i++)
1147 {
1148 RowNode *row = Rows[i];
1149 real_t b = 0.0;
1150 for ( ; row != NULL; row = row->Prev)
1151 {
1152 b += std::abs(row->Value) * xp[row->Column];
1153 }
1154 *yp += b;
1155 yp++;
1156 }
1157 return;
1158 }
1159
1160 const int height = this->height;
1161 const int nnz = J.Capacity();
1162 auto d_I = Read(I, height+1);
1163 auto d_J = Read(J, nnz);
1164 auto d_A = Read(A, nnz);
1165 auto d_x = x.Read();
1166 auto d_y = y.ReadWrite();
1167 mfem::forall(height, [=] MFEM_HOST_DEVICE (int i)
1168 {
1169 real_t d = 0.0;
1170 const int end = d_I[i+1];
1171 for (int j = d_I[i]; j < end; j++)
1172 {
1173 d += std::abs(d_A[j]) * d_x[d_J[j]];
1174 }
1175 d_y[i] += d;
1176 });
1177}
1178
1180{
1181 MFEM_ASSERT(height == x.Size(), "Input vector size (" << x.Size()
1182 << ") must match matrix height (" << height << ")");
1183 MFEM_ASSERT(width == y.Size(), "Output vector size (" << y.Size()
1184 << ") must match matrix width (" << width << ")");
1185
1186 y = 0.0;
1187
1188 if (!Finalized())
1189 {
1190 real_t *yp = y.GetData();
1191 // The matrix is not finalized, but multiplication is still possible
1192 for (int i = 0; i < height; i++)
1193 {
1194 RowNode *row = Rows[i];
1195 real_t b = x(i);
1196 for ( ; row != NULL; row = row->Prev)
1197 {
1198 yp[row->Column] += fabs(row->Value) * b;
1199 }
1200 }
1201 return;
1202 }
1203
1205 if (At)
1206 {
1207 At->AbsMult(x, y);
1208 }
1209 else
1210 {
1211 for (int i = 0; i < height; i++)
1212 {
1213 const real_t xi = x[i];
1214 const int end = I[i+1];
1215 for (int j = I[i]; j < end; j++)
1216 {
1217 const int Jj = J[j];
1218 y[Jj] += std::abs(A[j]) * xi;
1219 }
1220 }
1221 }
1222}
1223
1225{
1226 MFEM_ASSERT(x.Size() == Width(), "x.Size() = " << x.Size()
1227 << " must be equal to Width() = " << Width());
1228 MFEM_ASSERT(y.Size() == Height(), "y.Size() = " << y.Size()
1229 << " must be equal to Height() = " << Height());
1230
1231 x.HostRead();
1232 y.HostRead();
1233 if (Finalized())
1234 {
1235 const int nnz = J.Capacity();
1236 HostRead(I, height+1);
1237 HostRead(J, nnz);
1238 HostRead(A, nnz);
1239 }
1240
1241 real_t prod = 0.0;
1242 for (int i = 0; i < height; i++)
1243 {
1244 real_t a = 0.0;
1245 if (A)
1246 {
1247 for (int j = I[i], end = I[i+1]; j < end; j++)
1248 {
1249 a += A[j] * x(J[j]);
1250 }
1251 }
1252 else
1253 {
1254 for (RowNode *np = Rows[i]; np != NULL; np = np->Prev)
1255 {
1256 a += np->Value * x(np->Column);
1257 }
1258 }
1259 prod += a * y(i);
1260 }
1261
1262 return prod;
1263}
1264
1266{
1267 if (Finalized())
1268 {
1269 auto d_I = ReadI();
1270 auto d_A = ReadData();
1271 auto d_x = x.Write();
1272 mfem::forall(height, [=] MFEM_HOST_DEVICE (int i)
1273 {
1274 real_t sum = 0.0;
1275 for (int j = d_I[i], end = d_I[i+1]; j < end; j++)
1276 {
1277 sum += d_A[j];
1278 }
1279 d_x[i] = sum;
1280 });
1281 }
1282 else
1283 {
1284 for (int i = 0; i < height; i++)
1285 {
1286 real_t a = 0.0;
1287 for (RowNode *np = Rows[i]; np != NULL; np = np->Prev)
1288 {
1289 a += np->Value;
1290 }
1291 x(i) = a;
1292 }
1293 }
1294}
1295
1297{
1298 MFEM_VERIFY(irow < height,
1299 "row " << irow << " not in matrix with height " << height);
1300
1301 real_t a = 0.0;
1302 if (A)
1303 {
1304 for (int j = I[irow], end = I[irow+1]; j < end; j++)
1305 {
1306 a += fabs(A[j]);
1307 }
1308 }
1309 else
1310 {
1311 for (RowNode *np = Rows[irow]; np != NULL; np = np->Prev)
1312 {
1313 a += fabs(np->Value);
1314 }
1315 }
1316
1317 return a;
1318}
1319
1320void SparseMatrix::Threshold(real_t tol, bool fix_empty_rows)
1321{
1322 MFEM_ASSERT(Finalized(), "Matrix must be finalized.");
1323 real_t atol;
1324 atol = std::abs(tol);
1325
1326 fix_empty_rows = height == width ? fix_empty_rows : false;
1327
1328 real_t *newA;
1329 int *newI, *newJ;
1330 int i, j, nz;
1331
1332 newI = Memory<int>(height+1);
1333 newI[0] = 0;
1334 for (i = 0, nz = 0; i < height; i++)
1335 {
1336 bool found = false;
1337 for (j = I[i]; j < I[i+1]; j++)
1338 if (std::abs(A[j]) > atol)
1339 {
1340 found = true;
1341 nz++;
1342 }
1343 if (fix_empty_rows && !found) { nz++; }
1344 newI[i+1] = nz;
1345 }
1346
1347 newJ = Memory<int>(nz);
1348 newA = Memory<real_t>(nz);
1349 // Assume we're sorted until we find out otherwise
1350 isSorted = true;
1351 for (i = 0, nz = 0; i < height; i++)
1352 {
1353 bool found = false;
1354 int lastCol = -1;
1355 for (j = I[i]; j < I[i+1]; j++)
1356 if (std::abs(A[j]) > atol)
1357 {
1358 found = true;
1359 newJ[nz] = J[j];
1360 newA[nz] = A[j];
1361 if ( lastCol > newJ[nz] )
1362 {
1363 isSorted = false;
1364 }
1365 lastCol = newJ[nz];
1366 nz++;
1367 }
1368 if (fix_empty_rows && !found)
1369 {
1370 newJ[nz] = i;
1371 newA[nz] = 0.0;
1372 nz++;
1373 }
1374 }
1375 Destroy();
1376 I.Wrap(newI, height+1, true);
1377 J.Wrap(newJ, I[height], true);
1378 A.Wrap(newA, I[height], true);
1379}
1380
1381void SparseMatrix::Finalize(int skip_zeros, bool fix_empty_rows)
1382{
1383 int i, j, nr, nz;
1384 RowNode *aux;
1385
1386 if (Finalized())
1387 {
1388 return;
1389 }
1390
1391 delete [] ColPtrNode;
1392 ColPtrNode = NULL;
1393
1394 I.New(height+1);
1395 I[0] = 0;
1396 for (i = 1; i <= height; i++)
1397 {
1398 nr = 0;
1399 for (aux = Rows[i-1]; aux != NULL; aux = aux->Prev)
1400 {
1401 if (skip_zeros && aux->Value == 0.0)
1402 {
1403 if (skip_zeros == 2) { continue; }
1404 if ((i-1) != aux->Column) { continue; }
1405
1406 bool found = false;
1407 real_t found_val = 0.0; // init to suppress gcc warning
1408 for (RowNode *other = Rows[aux->Column]; other != NULL; other = other->Prev)
1409 {
1410 if (other->Column == (i-1))
1411 {
1412 found = true;
1413 found_val = other->Value;
1414 break;
1415 }
1416 }
1417 if (found && found_val == 0.0) { continue; }
1418
1419 }
1420 nr++;
1421 }
1422 if (fix_empty_rows && !nr) { nr = 1; }
1423 I[i] = I[i-1] + nr;
1424 }
1425
1426 nz = I[height];
1427 J.New(nz);
1428 A.New(nz);
1429 // Assume we're sorted until we find out otherwise
1430 isSorted = true;
1431 for (j = i = 0; i < height; i++)
1432 {
1433 int lastCol = -1;
1434 nr = 0;
1435 for (aux = Rows[i]; aux != NULL; aux = aux->Prev)
1436 {
1437 if (skip_zeros && aux->Value == 0.0)
1438 {
1439 if (skip_zeros == 2) { continue; }
1440 if (i != aux->Column) { continue; }
1441
1442 bool found = false;
1443 real_t found_val = 0.0; // init to suppress gcc warning
1444 for (RowNode *other = Rows[aux->Column]; other != NULL; other = other->Prev)
1445 {
1446 if (other->Column == i)
1447 {
1448 found = true;
1449 found_val = other->Value;
1450 break;
1451 }
1452 }
1453 if (found && found_val == 0.0) { continue; }
1454 }
1455
1456 J[j] = aux->Column;
1457 A[j] = aux->Value;
1458
1459 if ( lastCol > J[j] )
1460 {
1461 isSorted = false;
1462 }
1463 lastCol = J[j];
1464
1465 j++;
1466 nr++;
1467 }
1468 if (fix_empty_rows && !nr)
1469 {
1470 J[j] = i;
1471 A[j] = 1.0;
1472 j++;
1473 }
1474 }
1475
1476#ifdef MFEM_USE_MEMALLOC
1477 delete NodesMem;
1478 NodesMem = NULL;
1479#else
1480 for (i = 0; i < height; i++)
1481 {
1482 RowNode *node_p = Rows[i];
1483 while (node_p != NULL)
1484 {
1485 aux = node_p;
1486 node_p = node_p->Prev;
1487 delete aux;
1488 }
1489 }
1490#endif
1491
1492 delete [] Rows;
1493 Rows = NULL;
1494}
1495
1497{
1498 int br = blocks.NumRows(), bc = blocks.NumCols();
1499 int nr = (height + br - 1)/br, nc = (width + bc - 1)/bc;
1500
1501 for (int j = 0; j < bc; j++)
1502 {
1503 for (int i = 0; i < br; i++)
1504 {
1505 int *bI = Memory<int>(nr + 1);
1506 for (int k = 0; k <= nr; k++)
1507 {
1508 bI[k] = 0;
1509 }
1510 blocks(i,j) = new SparseMatrix(bI, NULL, NULL, nr, nc);
1511 }
1512 }
1513
1514 for (int gr = 0; gr < height; gr++)
1515 {
1516 int bi = gr/nr, i = gr%nr + 1;
1517 if (Finalized())
1518 {
1519 for (int j = I[gr]; j < I[gr+1]; j++)
1520 {
1521 if (A[j] != 0.0)
1522 {
1523 blocks(bi, J[j]/nc)->I[i]++;
1524 }
1525 }
1526 }
1527 else
1528 {
1529 for (RowNode *n_p = Rows[gr]; n_p != NULL; n_p = n_p->Prev)
1530 {
1531 if (n_p->Value != 0.0)
1532 {
1533 blocks(bi, n_p->Column/nc)->I[i]++;
1534 }
1535 }
1536 }
1537 }
1538
1539 for (int j = 0; j < bc; j++)
1540 {
1541 for (int i = 0; i < br; i++)
1542 {
1543 SparseMatrix &b = *blocks(i,j);
1544 int nnz = 0, rs;
1545 for (int k = 1; k <= nr; k++)
1546 {
1547 rs = b.I[k], b.I[k] = nnz, nnz += rs;
1548 }
1549 b.J.New(nnz);
1550 b.A.New(nnz);
1551 }
1552 }
1553
1554 for (int gr = 0; gr < height; gr++)
1555 {
1556 int bi = gr/nr, i = gr%nr + 1;
1557 if (Finalized())
1558 {
1559 for (int j = I[gr]; j < I[gr+1]; j++)
1560 {
1561 if (A[j] != 0.0)
1562 {
1563 SparseMatrix &b = *blocks(bi, J[j]/nc);
1564 b.J[b.I[i]] = J[j] % nc;
1565 b.A[b.I[i]] = A[j];
1566 b.I[i]++;
1567 }
1568 }
1569 }
1570 else
1571 {
1572 for (RowNode *n_p = Rows[gr]; n_p != NULL; n_p = n_p->Prev)
1573 {
1574 if (n_p->Value != 0.0)
1575 {
1576 SparseMatrix &b = *blocks(bi, n_p->Column/nc);
1577 b.J[b.I[i]] = n_p->Column % nc;
1578 b.A[b.I[i]] = n_p->Value;
1579 b.I[i]++;
1580 }
1581 }
1582 }
1583 }
1584}
1585
1587{
1588 if (height != width)
1589 {
1590 return infinity();
1591 }
1592
1593 real_t symm = 0.0;
1594 if (Empty())
1595 {
1596 // return 0.0;
1597 }
1598 else if (Finalized())
1599 {
1600 for (int i = 1; i < height; i++)
1601 {
1602 for (int j = I[i]; j < I[i+1]; j++)
1603 {
1604 if (J[j] < i)
1605 {
1606 symm = std::max(symm, std::abs(A[j]-(*this)(J[j],i)));
1607 }
1608 }
1609 }
1610 }
1611 else
1612 {
1613 for (int i = 0; i < height; i++)
1614 {
1615 for (RowNode *node_p = Rows[i]; node_p != NULL; node_p = node_p->Prev)
1616 {
1617 int col = node_p->Column;
1618 if (col < i)
1619 {
1620 symm = std::max(symm, std::abs(node_p->Value-(*this)(col,i)));
1621 }
1622 }
1623 }
1624 }
1625 return symm;
1626}
1627
1629{
1630 MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
1631
1632 int i, j;
1633 for (i = 1; i < height; i++)
1634 {
1635 for (j = I[i]; j < I[i+1]; j++)
1636 {
1637 if (J[j] < i)
1638 {
1639 A[j] += (*this)(J[j],i);
1640 A[j] *= 0.5;
1641 (*this)(J[j],i) = A[j];
1642 }
1643 }
1644 }
1645}
1646
1648{
1649 if (Finalized())
1650 {
1651 HostReadI();
1652 return I[height];
1653 }
1654 else
1655 {
1656 int nnz = 0;
1657
1658 for (int i = 0; i < height; i++)
1659 {
1660 for (RowNode *node_p = Rows[i]; node_p != NULL; node_p = node_p->Prev)
1661 {
1662 nnz++;
1663 }
1664 }
1665
1666 return nnz;
1667 }
1668}
1669
1671{
1672 real_t m = 0.0;
1673
1674 if (A)
1675 {
1676 int nnz = I[height];
1677 for (int j = 0; j < nnz; j++)
1678 {
1679 m = std::max(m, std::abs(A[j]));
1680 }
1681 }
1682 else
1683 {
1684 for (int i = 0; i < height; i++)
1685 {
1686 for (RowNode *n_p = Rows[i]; n_p != NULL; n_p = n_p->Prev)
1687 {
1688 m = std::max(m, std::abs(n_p->Value));
1689 }
1690 }
1691 }
1692 return m;
1693}
1694
1696{
1697 int counter = 0;
1698
1699 if (A)
1700 {
1701 const int nz = I[height];
1702 const real_t *Ap = A;
1703
1704 for (int i = 0; i < nz; i++)
1705 {
1706 counter += (std::abs(Ap[i]) <= tol);
1707 }
1708 }
1709 else
1710 {
1711 for (int i = 0; i < height; i++)
1712 {
1713 for (RowNode *aux = Rows[i]; aux != NULL; aux = aux->Prev)
1714 {
1715 counter += (std::abs(aux->Value) <= tol);
1716 }
1717 }
1718 }
1719
1720 return counter;
1721}
1722
1724{
1725 if (Empty())
1726 {
1727 return 0;
1728 }
1729 else if (Finalized())
1730 {
1731 return mfem::CheckFinite(A, I[height]);
1732 }
1733 else
1734 {
1735 int counter = 0;
1736 for (int i = 0; i < height; i++)
1737 {
1738 for (RowNode *aux = Rows[i]; aux != NULL; aux = aux->Prev)
1739 {
1740 counter += !IsFinite(aux->Value);
1741 }
1742 }
1743 return counter;
1744 }
1745}
1746
1748{
1749 return NULL;
1750}
1751
1753{
1754 RowNode *aux;
1755
1756 MFEM_ASSERT(row < height && row >= 0,
1757 "Row " << row << " not in matrix of height " << height);
1758
1759 MFEM_VERIFY(!Finalized(), "Matrix must NOT be finalized.");
1760
1761 for (aux = Rows[row]; aux != NULL; aux = aux->Prev)
1762 {
1763 rhs(aux->Column) -= sol * aux->Value;
1764 aux->Value = 0.0;
1765 }
1766}
1767
1769{
1770 RowNode *aux;
1771
1772 MFEM_ASSERT(row < height && row >= 0,
1773 "Row " << row << " not in matrix of height " << height);
1774 MFEM_ASSERT(dpolicy != DIAG_KEEP, "Diagonal policy must not be DIAG_KEEP");
1775 MFEM_ASSERT(dpolicy != DIAG_ONE || height == width,
1776 "if dpolicy == DIAG_ONE, matrix must be square, not height = "
1777 << height << ", width = " << width);
1778
1779 if (Rows == NULL)
1780 {
1781 for (int i=I[row]; i < I[row+1]; ++i)
1782 {
1783 A[i]=0.0;
1784 }
1785 }
1786 else
1787 {
1788 for (aux = Rows[row]; aux != NULL; aux = aux->Prev)
1789 {
1790 aux->Value = 0.0;
1791 }
1792 }
1793
1794 if (dpolicy == DIAG_ONE)
1795 {
1796 SearchRow(row, row) = 1.;
1797 }
1798}
1799
1801{
1802 MFEM_ASSERT(col < width && col >= 0,
1803 "Col " << col << " not in matrix of width " << width);
1804 MFEM_ASSERT(dpolicy != DIAG_KEEP, "Diagonal policy must not be DIAG_KEEP");
1805 MFEM_ASSERT(dpolicy != DIAG_ONE || height == width,
1806 "if dpolicy == DIAG_ONE, matrix must be square, not height = "
1807 << height << ", width = " << width);
1808
1809 if (Rows == NULL)
1810 {
1811 const int nnz = I[height];
1812 for (int jpos = 0; jpos != nnz; ++jpos)
1813 {
1814 if (J[jpos] == col)
1815 {
1816 A[jpos] = 0.0;
1817 }
1818 }
1819 }
1820 else
1821 {
1822 for (int i = 0; i < height; i++)
1823 {
1824 for (RowNode *aux = Rows[i]; aux != NULL; aux = aux->Prev)
1825 {
1826 if (aux->Column == col)
1827 {
1828 aux->Value = 0.0;
1829 break;
1830 }
1831 }
1832 }
1833 }
1834
1835 if (dpolicy == DIAG_ONE)
1836 {
1837 SearchRow(col, col) = 1.0;
1838 }
1839}
1840
1842 Vector *b)
1843{
1844 if (Rows == NULL)
1845 {
1846 for (int i = 0; i < height; i++)
1847 {
1848 for (int jpos = I[i]; jpos != I[i+1]; ++jpos)
1849 {
1850 if (cols[ J[jpos]])
1851 {
1852 if (x && b)
1853 {
1854 (*b)(i) -= A[jpos] * (*x)( J[jpos] );
1855 }
1856 A[jpos] = 0.0;
1857 }
1858 }
1859 }
1860 }
1861 else
1862 {
1863 for (int i = 0; i < height; i++)
1864 {
1865 for (RowNode *aux = Rows[i]; aux != NULL; aux = aux->Prev)
1866 {
1867 if (cols[aux -> Column])
1868 {
1869 if (x && b)
1870 {
1871 (*b)(i) -= aux -> Value * (*x)(aux -> Column);
1872 }
1873 aux->Value = 0.0;
1874 }
1875 }
1876 }
1877 }
1878}
1879
1881{
1882 if (Rows)
1883 {
1884 RowNode *nd;
1885 for (int row = 0; row < height; row++)
1886 {
1887 for (nd = Rows[row]; nd != NULL; nd = nd->Prev)
1888 {
1889 if (col_marker[nd->Column])
1890 {
1891 Ae.Add(row, nd->Column, nd->Value);
1892 nd->Value = 0.0;
1893 }
1894 }
1895 }
1896 }
1897 else
1898 {
1899 for (int row = 0; row < height; row++)
1900 {
1901 for (int j = I[row]; j < I[row+1]; j++)
1902 {
1903 if (col_marker[J[j]])
1904 {
1905 Ae.Add(row, J[j], A[j]);
1906 A[j] = 0.0;
1907 }
1908 }
1909 }
1910 }
1911}
1912
1913
1915 DiagonalPolicy dpolicy)
1916{
1917 MFEM_ASSERT(rc < height && rc >= 0,
1918 "Row " << rc << " not in matrix of height " << height);
1922
1923 if (Rows == NULL)
1924 {
1925 for (int j = I[rc]; j < I[rc+1]; j++)
1926 {
1927 const int col = J[j];
1928 if (col == rc)
1929 {
1930 switch (dpolicy)
1931 {
1932 case DIAG_KEEP:
1933 rhs(rc) = A[j] * sol;
1934 break;
1935 case DIAG_ONE:
1936 A[j] = 1.0;
1937 rhs(rc) = sol;
1938 break;
1939 case DIAG_ZERO:
1940 A[j] = 0.;
1941 rhs(rc) = 0.;
1942 break;
1943 default:
1944 mfem_error("SparseMatrix::EliminateRowCol () #2");
1945 break;
1946 }
1947 }
1948 else
1949 {
1950 A[j] = 0.0;
1951 for (int k = I[col]; 1; k++)
1952 {
1953 if (k == I[col+1])
1954 {
1955 mfem_error("SparseMatrix::EliminateRowCol () #3");
1956 }
1957 else if (J[k] == rc)
1958 {
1959 rhs(col) -= sol * A[k];
1960 A[k] = 0.0;
1961 break;
1962 }
1963 }
1964 }
1965 }
1966 }
1967 else
1968 {
1969 for (RowNode *aux = Rows[rc]; aux != NULL; aux = aux->Prev)
1970 {
1971 const int col = aux->Column;
1972 if (col == rc)
1973 {
1974 switch (dpolicy)
1975 {
1976 case DIAG_KEEP:
1977 rhs(rc) = aux->Value * sol;
1978 break;
1979 case DIAG_ONE:
1980 aux->Value = 1.0;
1981 rhs(rc) = sol;
1982 break;
1983 case DIAG_ZERO:
1984 aux->Value = 0.;
1985 rhs(rc) = 0.;
1986 break;
1987 default:
1988 mfem_error("SparseMatrix::EliminateRowCol () #4");
1989 break;
1990 }
1991 }
1992 else
1993 {
1994 aux->Value = 0.0;
1995 for (RowNode *node = Rows[col]; 1; node = node->Prev)
1996 {
1997 if (node == NULL)
1998 {
1999 mfem_error("SparseMatrix::EliminateRowCol () #5");
2000 }
2001 else if (node->Column == rc)
2002 {
2003 rhs(col) -= sol * node->Value;
2004 node->Value = 0.0;
2005 break;
2006 }
2007 }
2008 }
2009 }
2010 }
2011}
2012
2014 DenseMatrix &rhs,
2015 DiagonalPolicy dpolicy)
2016{
2017 MFEM_ASSERT(rc < height && rc >= 0,
2018 "Row " << rc << " not in matrix of height " << height);
2019 MFEM_ASSERT(sol.Size() == rhs.Width(), "solution size (" << sol.Size()
2020 << ") must match rhs width (" << rhs.Width() << ")");
2021
2022 const int num_rhs = rhs.Width();
2023 if (Rows == NULL)
2024 {
2025 for (int j = I[rc]; j < I[rc+1]; j++)
2026 {
2027 const int col = J[j];
2028 if (col == rc)
2029 {
2030 switch (dpolicy)
2031 {
2032 case DIAG_KEEP:
2033 for (int r = 0; r < num_rhs; r++)
2034 {
2035 rhs(rc,r) = A[j] * sol(r);
2036 }
2037 break;
2038 case DIAG_ONE:
2039 A[j] = 1.0;
2040 for (int r = 0; r < num_rhs; r++)
2041 {
2042 rhs(rc,r) = sol(r);
2043 }
2044 break;
2045 case DIAG_ZERO:
2046 A[j] = 0.;
2047 for (int r = 0; r < num_rhs; r++)
2048 {
2049 rhs(rc,r) = 0.;
2050 }
2051 break;
2052 default:
2053 mfem_error("SparseMatrix::EliminateRowColMultipleRHS() #3");
2054 break;
2055 }
2056 }
2057 else
2058 {
2059 A[j] = 0.0;
2060 for (int k = I[col]; 1; k++)
2061 {
2062 if (k == I[col+1])
2063 {
2064 mfem_error("SparseMatrix::EliminateRowColMultipleRHS() #4");
2065 }
2066 else if (J[k] == rc)
2067 {
2068 for (int r = 0; r < num_rhs; r++)
2069 {
2070 rhs(col,r) -= sol(r) * A[k];
2071 }
2072 A[k] = 0.0;
2073 break;
2074 }
2075 }
2076 }
2077 }
2078 }
2079 else
2080 {
2081 for (RowNode *aux = Rows[rc]; aux != NULL; aux = aux->Prev)
2082 {
2083 const int col = aux->Column;
2084 if (col == rc)
2085 {
2086 switch (dpolicy)
2087 {
2088 case DIAG_KEEP:
2089 for (int r = 0; r < num_rhs; r++)
2090 {
2091 rhs(rc,r) = aux->Value * sol(r);
2092 }
2093 break;
2094 case DIAG_ONE:
2095 aux->Value = 1.0;
2096 for (int r = 0; r < num_rhs; r++)
2097 {
2098 rhs(rc,r) = sol(r);
2099 }
2100 break;
2101 case DIAG_ZERO:
2102 aux->Value = 0.;
2103 for (int r = 0; r < num_rhs; r++)
2104 {
2105 rhs(rc,r) = 0.;
2106 }
2107 break;
2108 default:
2109 mfem_error("SparseMatrix::EliminateRowColMultipleRHS() #5");
2110 break;
2111 }
2112 }
2113 else
2114 {
2115 aux->Value = 0.0;
2116 for (RowNode *node = Rows[col]; 1; node = node->Prev)
2117 {
2118 if (node == NULL)
2119 {
2120 mfem_error("SparseMatrix::EliminateRowColMultipleRHS() #6");
2121 }
2122 else if (node->Column == rc)
2123 {
2124 for (int r = 0; r < num_rhs; r++)
2125 {
2126 rhs(col,r) -= sol(r) * node->Value;
2127 }
2128 node->Value = 0.0;
2129 break;
2130 }
2131 }
2132 }
2133 }
2134 }
2135}
2136
2138{
2139 MFEM_ASSERT(rc < height && rc >= 0,
2140 "Row " << rc << " not in matrix of height " << height);
2141
2142 if (Rows == NULL)
2143 {
2144 const auto &II = this->I; // only use const access for I
2145 const auto &JJ = this->J; // only use const access for J
2146 for (int j = II[rc]; j < II[rc+1]; j++)
2147 {
2148 const int col = JJ[j];
2149 if (col == rc)
2150 {
2151 if (dpolicy == DIAG_ONE)
2152 {
2153 A[j] = 1.0;
2154 }
2155 else if (dpolicy == DIAG_ZERO)
2156 {
2157 A[j] = 0.0;
2158 }
2159 }
2160 else
2161 {
2162 A[j] = 0.0;
2163 for (int k = II[col]; 1; k++)
2164 {
2165 if (k == II[col+1])
2166 {
2167 mfem_error("SparseMatrix::EliminateRowCol() #2");
2168 }
2169 else if (JJ[k] == rc)
2170 {
2171 A[k] = 0.0;
2172 break;
2173 }
2174 }
2175 }
2176 }
2177 }
2178 else
2179 {
2180 RowNode *aux, *node;
2181
2182 for (aux = Rows[rc]; aux != NULL; aux = aux->Prev)
2183 {
2184 const int col = aux->Column;
2185 if (col == rc)
2186 {
2187 if (dpolicy == DIAG_ONE)
2188 {
2189 aux->Value = 1.0;
2190 }
2191 else if (dpolicy == DIAG_ZERO)
2192 {
2193 aux->Value = 0.;
2194 }
2195 }
2196 else
2197 {
2198 aux->Value = 0.0;
2199 for (node = Rows[col]; 1; node = node->Prev)
2200 {
2201 if (node == NULL)
2202 {
2203 mfem_error("SparseMatrix::EliminateRowCol() #3");
2204 }
2205 else if (node->Column == rc)
2206 {
2207 node->Value = 0.0;
2208 break;
2209 }
2210 }
2211 }
2212 }
2213 }
2214}
2215
2216// This is almost identical to EliminateRowCol(int, int), except for
2217// the A[j] = value; and aux->Value = value; lines.
2219{
2220 MFEM_ASSERT(rc < height && rc >= 0,
2221 "Row " << rc << " not in matrix of height " << height);
2222
2223 if (Rows == NULL)
2224 {
2225 for (int j = I[rc]; j < I[rc+1]; j++)
2226 {
2227 const int col = J[j];
2228 if (col == rc)
2229 {
2230 A[j] = value;
2231 }
2232 else
2233 {
2234 A[j] = 0.0;
2235 for (int k = I[col]; 1; k++)
2236 {
2237 if (k == I[col+1])
2238 {
2239 mfem_error("SparseMatrix::EliminateRowCol() #2");
2240 }
2241 else if (J[k] == rc)
2242 {
2243 A[k] = 0.0;
2244 break;
2245 }
2246 }
2247 }
2248 }
2249 }
2250 else
2251 {
2252 RowNode *aux, *node;
2253
2254 for (aux = Rows[rc]; aux != NULL; aux = aux->Prev)
2255 {
2256 const int col = aux->Column;
2257 if (col == rc)
2258 {
2259 aux->Value = value;
2260 }
2261 else
2262 {
2263 aux->Value = 0.0;
2264 for (node = Rows[col]; 1; node = node->Prev)
2265 {
2266 if (node == NULL)
2267 {
2268 mfem_error("SparseMatrix::EliminateRowCol() #3");
2269 }
2270 else if (node->Column == rc)
2271 {
2272 node->Value = 0.0;
2273 break;
2274 }
2275 }
2276 }
2277 }
2278 }
2279}
2280
2282 DiagonalPolicy dpolicy)
2283{
2284 if (Rows)
2285 {
2286 RowNode *nd, *nd2;
2287 for (nd = Rows[rc]; nd != NULL; nd = nd->Prev)
2288 {
2289 const int col = nd->Column;
2290 if (col == rc)
2291 {
2292 switch (dpolicy)
2293 {
2294 case DIAG_ONE:
2295 Ae.Add(rc, rc, nd->Value - 1.0);
2296 nd->Value = 1.0;
2297 break;
2298 case DIAG_ZERO:
2299 Ae.Add(rc, rc, nd->Value);
2300 nd->Value = 0.;
2301 break;
2302 case DIAG_KEEP:
2303 break;
2304 default:
2305 mfem_error("SparseMatrix::EliminateRowCol #1");
2306 break;
2307 }
2308 }
2309 else
2310 {
2311 Ae.Add(rc, col, nd->Value);
2312 nd->Value = 0.0;
2313 for (nd2 = Rows[col]; 1; nd2 = nd2->Prev)
2314 {
2315 if (nd2 == NULL)
2316 {
2317 mfem_error("SparseMatrix::EliminateRowCol #2");
2318 }
2319 else if (nd2->Column == rc)
2320 {
2321 Ae.Add(col, rc, nd2->Value);
2322 nd2->Value = 0.0;
2323 break;
2324 }
2325 }
2326 }
2327 }
2328 }
2329 else
2330 {
2331 for (int j = I[rc]; j < I[rc+1]; j++)
2332 {
2333 const int col = J[j];
2334 if (col == rc)
2335 {
2336 switch (dpolicy)
2337 {
2338 case DIAG_ONE:
2339 Ae.Add(rc, rc, A[j] - 1.0);
2340 A[j] = 1.0;
2341 break;
2342 case DIAG_ZERO:
2343 Ae.Add(rc, rc, A[j]);
2344 A[j] = 0.;
2345 break;
2346 case DIAG_KEEP:
2347 break;
2348 default:
2349 mfem_error("SparseMatrix::EliminateRowCol #3");
2350 break;
2351 }
2352 }
2353 else
2354 {
2355 Ae.Add(rc, col, A[j]);
2356 A[j] = 0.0;
2357 for (int k = I[col]; true; k++)
2358 {
2359 if (k == I[col+1])
2360 {
2361 mfem_error("SparseMatrix::EliminateRowCol #4");
2362 }
2363 else if (J[k] == rc)
2364 {
2365 Ae.Add(col, rc, A[k]);
2366 A[k] = 0.0;
2367 break;
2368 }
2369 }
2370 }
2371 }
2372 }
2373}
2374
2376 DiagonalPolicy diag_policy)
2377{
2378 const int n_ess_dofs = ess_dofs.Size();
2379 const auto ess_dofs_d = ess_dofs.Read();
2380 const auto dI = ReadI();
2381 const auto dJ = ReadJ();
2382 auto dA = ReadWriteData();
2383
2384 mfem::forall(n_ess_dofs, [=] MFEM_HOST_DEVICE (int i)
2385 {
2386 const int idof = ess_dofs_d[i];
2387 for (int j=dI[idof]; j<dI[idof+1]; ++j)
2388 {
2389 const int jdof = dJ[j];
2390 if (jdof != idof)
2391 {
2392 dA[j] = 0.0;
2393 for (int k=dI[jdof]; k<dI[jdof+1]; ++k)
2394 {
2395 if (dJ[k] == idof)
2396 {
2397 dA[k] = 0.0;
2398 break;
2399 }
2400 }
2401 }
2402 else
2403 {
2404 if (diag_policy == DiagonalPolicy::DIAG_ONE)
2405 {
2406 dA[j] = 1.0;
2407 }
2408 else if (diag_policy == DiagonalPolicy::DIAG_ZERO)
2409 {
2410 dA[j] = 0.0;
2411 }
2412 // else (diag_policy == DiagonalPolicy::DIAG_KEEP)
2413 }
2414 }
2415 });
2416}
2417
2419{
2420 for (int i = 0; i < height; i++)
2421 {
2422 if (I[i+1] == I[i]+1 && fabs(A[I[i]]) < 1e-16)
2423 {
2424 A[I[i]] = 1.0;
2425 }
2426 }
2427}
2428
2430{
2431 for (int i = 0; i < height; i++)
2432 {
2433 real_t zero = 0.0;
2434 for (int j = I[i]; j < I[i+1]; j++)
2435 {
2436 zero += fabs(A[j]);
2437 }
2438 if (zero <= threshold)
2439 {
2440 for (int j = I[i]; j < I[i+1]; j++)
2441 {
2442 A[j] = (J[j] == i) ? 1.0 : 0.0;
2443 }
2444 }
2445 }
2446}
2447
2449{
2450 if (!Finalized())
2451 {
2452 real_t *yp = y.GetData();
2453 const real_t *xp = x.GetData();
2454 RowNode *diag_p, *n_p, **R = Rows;
2455
2456 const int s = height;
2457 for (int i = 0; i < s; i++)
2458 {
2459 real_t sum = 0.0;
2460 diag_p = NULL;
2461 for (n_p = R[i]; n_p != NULL; n_p = n_p->Prev)
2462 {
2463 const int c = n_p->Column;
2464 if (c == i)
2465 {
2466 diag_p = n_p;
2467 }
2468 else
2469 {
2470 sum += n_p->Value * yp[c];
2471 }
2472 }
2473
2474 if (diag_p != NULL && diag_p->Value != 0.0)
2475 {
2476 yp[i] = (xp[i] - sum) / diag_p->Value;
2477 }
2478 else if (xp[i] == sum)
2479 {
2480 yp[i] = sum;
2481 }
2482 else
2483 {
2484 mfem_error("SparseMatrix::Gauss_Seidel_forw()");
2485 }
2486 }
2487 }
2488 else
2489 {
2490 const int s = height;
2491 const int nnz = J.Capacity();
2492 const int *Ip = HostRead(I, s+1);
2493 const int *Jp = HostRead(J, nnz);
2494 const real_t *Ap = HostRead(A, nnz);
2495 real_t *yp = y.HostReadWrite();
2496 const real_t *xp = x.HostRead();
2497
2498 for (int i = 0, j = Ip[0]; i < s; i++)
2499 {
2500 const int end = Ip[i+1];
2501 real_t sum = 0.0;
2502 int d = -1;
2503 for ( ; j < end; j++)
2504 {
2505 const int c = Jp[j];
2506 if (c == i)
2507 {
2508 d = j;
2509 }
2510 else
2511 {
2512 sum += Ap[j] * yp[c];
2513 }
2514 }
2515
2516 if (d >= 0 && Ap[d] != 0.0)
2517 {
2518 yp[i] = (xp[i] - sum) / Ap[d];
2519 }
2520 else if (xp[i] == sum)
2521 {
2522 yp[i] = sum;
2523 }
2524 else
2525 {
2526 mfem_error("SparseMatrix::Gauss_Seidel_forw(...) #2");
2527 }
2528 }
2529 }
2530}
2531
2533{
2534 if (!Finalized())
2535 {
2536 real_t *yp = y.GetData();
2537 const real_t *xp = x.GetData();
2538 RowNode *diag_p, *n_p, **R = Rows;
2539
2540 for (int i = height-1; i >= 0; i--)
2541 {
2542 real_t sum = 0.;
2543 diag_p = NULL;
2544 for (n_p = R[i]; n_p != NULL; n_p = n_p->Prev)
2545 {
2546 const int c = n_p->Column;
2547 if (c == i)
2548 {
2549 diag_p = n_p;
2550 }
2551 else
2552 {
2553 sum += n_p->Value * yp[c];
2554 }
2555 }
2556
2557 if (diag_p != NULL && diag_p->Value != 0.0)
2558 {
2559 yp[i] = (xp[i] - sum) / diag_p->Value;
2560 }
2561 else if (xp[i] == sum)
2562 {
2563 yp[i] = sum;
2564 }
2565 else
2566 {
2567 mfem_error("SparseMatrix::Gauss_Seidel_back()");
2568 }
2569 }
2570 }
2571 else
2572 {
2573 const int s = height;
2574 const int nnz = J.Capacity();
2575 const int *Ip = HostRead(I, s+1);
2576 const int *Jp = HostRead(J, nnz);
2577 const real_t *Ap = HostRead(A, nnz);
2578 real_t *yp = y.HostReadWrite();
2579 const real_t *xp = x.HostRead();
2580
2581 for (int i = s-1, j = Ip[s]-1; i >= 0; i--)
2582 {
2583 const int beg = Ip[i];
2584 real_t sum = 0.;
2585 int d = -1;
2586 for ( ; j >= beg; j--)
2587 {
2588 const int c = Jp[j];
2589 if (c == i)
2590 {
2591 d = j;
2592 }
2593 else
2594 {
2595 sum += Ap[j] * yp[c];
2596 }
2597 }
2598
2599 if (d >= 0 && Ap[d] != 0.0)
2600 {
2601 yp[i] = (xp[i] - sum) / Ap[d];
2602 }
2603 else if (xp[i] == sum)
2604 {
2605 yp[i] = sum;
2606 }
2607 else
2608 {
2609 mfem_error("SparseMatrix::Gauss_Seidel_back(...) #2");
2610 }
2611 }
2612 }
2613}
2614
2616{
2617 MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
2618
2619 real_t sc = 1.0;
2620 for (int i = 0; i < height; i++)
2621 {
2622 int d = -1;
2623 real_t norm = 0.0;
2624 for (int j = I[i]; j < I[i+1]; j++)
2625 {
2626 if (J[j] == i)
2627 {
2628 d = j;
2629 }
2630 norm += fabs(A[j]);
2631 }
2632 if (d >= 0 && A[d] != 0.0)
2633 {
2634 real_t a = 1.8 * fabs(A[d]) / norm;
2635 if (a < sc)
2636 {
2637 sc = a;
2638 }
2639 }
2640 else
2641 {
2642 mfem_error("SparseMatrix::GetJacobiScaling() #2");
2643 }
2644 }
2645 return sc;
2646}
2647
2648void SparseMatrix::Jacobi(const Vector &b, const Vector &x0, Vector &x1,
2649 real_t sc, bool use_abs_diag) const
2650{
2651 MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
2652
2653 for (int i = 0; i < height; i++)
2654 {
2655 int d = -1;
2656 real_t sum = b(i);
2657 for (int j = I[i]; j < I[i+1]; j++)
2658 {
2659 if (J[j] == i)
2660 {
2661 d = j;
2662 }
2663 else
2664 {
2665 sum -= A[j] * x0(J[j]);
2666 }
2667 }
2668 if (d >= 0 && A[d] != 0.0)
2669 {
2670 const real_t diag = (use_abs_diag) ? fabs(A[d]) : A[d];
2671 x1(i) = sc * (sum / diag) + (1.0 - sc) * x0(i);
2672 }
2673 else
2674 {
2675 mfem_error("SparseMatrix::Jacobi(...) #2");
2676 }
2677 }
2678}
2679
2681 real_t sc, bool use_abs_diag) const
2682{
2683 MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
2684
2685 const int H = height;
2686 const int nnz = J.Capacity();
2687 const bool use_dev = b.UseDevice() || x.UseDevice();
2688
2689 const auto Ap = Read(A, nnz, use_dev);
2690 const auto Ip = Read(I, height+1, use_dev);
2691 const auto Jp = Read(J, nnz, use_dev);
2692
2693 const auto bp = b.Read(use_dev);
2694 auto xp = x.Write(use_dev);
2695
2696 mfem::forall_switch(use_dev, H, [=] MFEM_HOST_DEVICE (int i)
2697 {
2698 const int end = Ip[i+1];
2699 for (int j = Ip[i]; true; j++)
2700 {
2701 if (j == end)
2702 {
2703 MFEM_ABORT_KERNEL("Diagonal not found in SparseMatrix::DiagScale");
2704 }
2705 if (Jp[j] == i)
2706 {
2707 const real_t diag = (use_abs_diag) ? fabs(Ap[j]) : Ap[j];
2708 if (diag == 0.0)
2709 {
2710 MFEM_ABORT_KERNEL("Zero diagonal in SparseMatrix::DiagScale");
2711 }
2712 xp[i] = sc * bp[i] / diag;
2713 break;
2714 }
2715 }
2716 });
2717}
2718
2719template <bool useFabs>
2720static void JacobiDispatch(const Vector &b, const Vector &x0, Vector &x1,
2721 const Memory<int> &I, const Memory<int> &J,
2722 const Memory<real_t> &A, const int height,
2723 const real_t sc)
2724{
2725 const bool useDevice = b.UseDevice() || x0.UseDevice() || x1.UseDevice();
2726
2727 const auto bp = b.Read(useDevice);
2728 const auto x0p = x0.Read(useDevice);
2729 auto x1p = x1.Write(useDevice);
2730
2731 const auto Ip = Read(I, height+1, useDevice);
2732 const auto Jp = Read(J, J.Capacity(), useDevice);
2733 const auto Ap = Read(A, J.Capacity(), useDevice);
2734
2735 mfem::forall_switch(useDevice, height, [=] MFEM_HOST_DEVICE (int i)
2736 {
2737 real_t resi = bp[i], norm = 0.0;
2738 for (int j = Ip[i]; j < Ip[i+1]; j++)
2739 {
2740 resi -= Ap[j] * x0p[Jp[j]];
2741 if (useFabs)
2742 {
2743 norm += fabs(Ap[j]);
2744 }
2745 else
2746 {
2747 norm += Ap[j];
2748 }
2749 }
2750 if (norm > 0.0)
2751 {
2752 x1p[i] = x0p[i] + sc * resi / norm;
2753 }
2754 else
2755 {
2756 if (useFabs)
2757 {
2758 MFEM_ABORT_KERNEL("L1 norm of row is zero.");
2759 }
2760 else
2761 {
2762 MFEM_ABORT_KERNEL("sum of row is zero.");
2763 }
2764 }
2765 });
2766}
2767
2768void SparseMatrix::Jacobi2(const Vector &b, const Vector &x0, Vector &x1,
2769 real_t sc) const
2770{
2771 MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
2772 JacobiDispatch<true>(b,x0,x1,I,J,A,height,sc);
2773}
2774
2775void SparseMatrix::Jacobi3(const Vector &b, const Vector &x0, Vector &x1,
2776 real_t sc) const
2777{
2778 MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
2779 JacobiDispatch<false>(b,x0,x1,I,J,A,height,sc);
2780}
2781
2783 const DenseMatrix &subm, int skip_zeros)
2784{
2785 int i, j, gi, gj, s, t;
2786 real_t a;
2787
2788 if (Finalized())
2789 {
2790 HostReadI();
2791 HostReadJ();
2793 }
2794
2795 for (i = 0; i < rows.Size(); i++)
2796 {
2797 if ((gi=rows[i]) < 0) { gi = -1-gi, s = -1; }
2798 else { s = 1; }
2799 MFEM_ASSERT(gi < height,
2800 "Trying to insert a row " << gi << " outside the matrix height "
2801 << height);
2802 SetColPtr(gi);
2803 for (j = 0; j < cols.Size(); j++)
2804 {
2805 if ((gj=cols[j]) < 0) { gj = -1-gj, t = -s; }
2806 else { t = s; }
2807 MFEM_ASSERT(gj < width,
2808 "Trying to insert a column " << gj << " outside the matrix width "
2809 << width);
2810 a = subm(i, j);
2811 if (skip_zeros && a == 0.0)
2812 {
2813 // Skip assembly of zero elements if either:
2814 // (i) user specified to skip zeros regardless of symmetry, or
2815 // (ii) symmetry is not broken.
2816 if (skip_zeros == 2 || &rows != &cols || subm(j, i) == 0.0)
2817 {
2818 continue;
2819 }
2820 }
2821 if (t < 0) { a = -a; }
2822 _Add_(gj, a);
2823 }
2824 ClearColPtr();
2825 }
2826}
2827
2828void SparseMatrix::Set(const int i, const int j, const real_t val)
2829{
2830 real_t a = val;
2831 int gi, gj, s, t;
2832
2833 if ((gi=i) < 0) { gi = -1-gi, s = -1; }
2834 else { s = 1; }
2835 MFEM_ASSERT(gi < height,
2836 "Trying to set a row " << gi << " outside the matrix height "
2837 << height);
2838 if ((gj=j) < 0) { gj = -1-gj, t = -s; }
2839 else { t = s; }
2840 MFEM_ASSERT(gj < width,
2841 "Trying to set a column " << gj << " outside the matrix width "
2842 << width);
2843 if (t < 0) { a = -a; }
2844 _Set_(gi, gj, a);
2845}
2846
2847void SparseMatrix::Add(const int i, const int j, const real_t val)
2848{
2849 int gi, gj, s, t;
2850 real_t a = val;
2851
2852 if ((gi=i) < 0) { gi = -1-gi, s = -1; }
2853 else { s = 1; }
2854 MFEM_ASSERT(gi < height,
2855 "Trying to insert a row " << gi << " outside the matrix height "
2856 << height);
2857 if ((gj=j) < 0) { gj = -1-gj, t = -s; }
2858 else { t = s; }
2859 MFEM_ASSERT(gj < width,
2860 "Trying to insert a column " << gj << " outside the matrix width "
2861 << width);
2862 if (t < 0) { a = -a; }
2863 _Add_(gi, gj, a);
2864}
2865
2867 const DenseMatrix &subm, int skip_zeros)
2868{
2869 int i, j, gi, gj, s, t;
2870 real_t a;
2871
2872 for (i = 0; i < rows.Size(); i++)
2873 {
2874 if ((gi=rows[i]) < 0) { gi = -1-gi, s = -1; }
2875 else { s = 1; }
2876 MFEM_ASSERT(gi < height,
2877 "Trying to set a row " << gi << " outside the matrix height "
2878 << height);
2879 SetColPtr(gi);
2880 for (j = 0; j < cols.Size(); j++)
2881 {
2882 a = subm(i, j);
2883 if (skip_zeros && a == 0.0)
2884 {
2885 // Skip assembly of zero elements if either:
2886 // (i) user specified to skip zeros regardless of symmetry, or
2887 // (ii) symmetry is not broken.
2888 if (skip_zeros == 2 || &rows != &cols || subm(j, i) == 0.0)
2889 {
2890 continue;
2891 }
2892 }
2893 if ((gj=cols[j]) < 0) { gj = -1-gj, t = -s; }
2894 else { t = s; }
2895 MFEM_ASSERT(gj < width,
2896 "Trying to set a column " << gj << " outside the matrix width "
2897 << width);
2898 if (t < 0) { a = -a; }
2899 _Set_(gj, a);
2900 }
2901 ClearColPtr();
2902 }
2903}
2904
2906 const Array<int> &cols,
2907 const DenseMatrix &subm,
2908 int skip_zeros)
2909{
2910 int i, j, gi, gj, s, t;
2911 real_t a;
2912
2913 for (i = 0; i < rows.Size(); i++)
2914 {
2915 if ((gi=rows[i]) < 0) { gi = -1-gi, s = -1; }
2916 else { s = 1; }
2917 MFEM_ASSERT(gi < height,
2918 "Trying to set a row " << gi << " outside the matrix height "
2919 << height);
2920 SetColPtr(gi);
2921 for (j = 0; j < cols.Size(); j++)
2922 {
2923 a = subm(j, i);
2924 if (skip_zeros && a == 0.0)
2925 {
2926 // Skip assembly of zero elements if either:
2927 // (i) user specified to skip zeros regardless of symmetry, or
2928 // (ii) symmetry is not broken.
2929 if (skip_zeros == 2 || &rows != &cols || subm(j, i) == 0.0)
2930 {
2931 continue;
2932 }
2933 }
2934 if ((gj=cols[j]) < 0) { gj = -1-gj, t = -s; }
2935 else { t = s; }
2936 MFEM_ASSERT(gj < width,
2937 "Trying to set a column " << gj << " outside the matrix width "
2938 << width);
2939 if (t < 0) { a = -a; }
2940 _Set_(gj, a);
2941 }
2942 ClearColPtr();
2943 }
2944}
2945
2947 DenseMatrix &subm) const
2948{
2949 int i, j, gi, gj, s, t;
2950 real_t a;
2951
2952 for (i = 0; i < rows.Size(); i++)
2953 {
2954 if ((gi=rows[i]) < 0) { gi = -1-gi, s = -1; }
2955 else { s = 1; }
2956 MFEM_ASSERT(gi < height,
2957 "Trying to read a row " << gi << " outside the matrix height "
2958 << height);
2959 SetColPtr(gi);
2960 for (j = 0; j < cols.Size(); j++)
2961 {
2962 if ((gj=cols[j]) < 0) { gj = -1-gj, t = -s; }
2963 else { t = s; }
2964 MFEM_ASSERT(gj < width,
2965 "Trying to read a column " << gj << " outside the matrix width "
2966 << width);
2967 a = _Get_(gj);
2968 subm(i, j) = (t < 0) ? (-a) : (a);
2969 }
2970 ClearColPtr();
2971 }
2972}
2973
2974bool SparseMatrix::RowIsEmpty(const int row) const
2975{
2976 int gi;
2977
2978 if ((gi=row) < 0)
2979 {
2980 gi = -1-gi;
2981 }
2982 MFEM_ASSERT(gi < height,
2983 "Trying to query a row " << gi << " outside the matrix height "
2984 << height);
2985 if (Rows)
2986 {
2987 return (Rows[gi] == NULL);
2988 }
2989 else
2990 {
2991 return (I[gi] == I[gi+1]);
2992 }
2993}
2994
2995int SparseMatrix::GetRow(const int row, Array<int> &cols, Vector &srow) const
2996{
2997 RowNode *n;
2998 int j, gi;
2999
3000 if ((gi=row) < 0) { gi = -1-gi; }
3001 MFEM_ASSERT(gi < height,
3002 "Trying to read a row " << gi << " outside the matrix height "
3003 << height);
3004 if (Rows)
3005 {
3006 for (n = Rows[gi], j = 0; n; n = n->Prev)
3007 {
3008 j++;
3009 }
3010 cols.SetSize(j);
3011 srow.SetSize(j);
3012 for (n = Rows[gi], j = 0; n; n = n->Prev, j++)
3013 {
3014 cols[j] = n->Column;
3015 srow(j) = n->Value;
3016 }
3017 if (row < 0)
3018 {
3019 srow.Neg();
3020 }
3021
3022 return 0;
3023 }
3024 else
3025 {
3026 j = I[gi];
3027 cols.MakeRef(const_cast<int*>((const int*)J) + j, I[gi+1]-j);
3028 srow.NewDataAndSize(
3029 const_cast<real_t*>((const real_t*)A) + j, cols.Size());
3030 MFEM_ASSERT(row >= 0, "Row not valid: " << row << ", height: " << height);
3031 return 1;
3032 }
3033}
3034
3035void SparseMatrix::SetRow(const int row, const Array<int> &cols,
3036 const Vector &srow)
3037{
3038 int gi, gj, s, t;
3039 real_t a;
3040
3041 if ((gi=row) < 0) { gi = -1-gi, s = -1; }
3042 else { s = 1; }
3043 MFEM_ASSERT(gi < height,
3044 "Trying to set a row " << gi << " outside the matrix height "
3045 << height);
3046
3047 if (!Finalized())
3048 {
3049 SetColPtr(gi);
3050 for (int j = 0; j < cols.Size(); j++)
3051 {
3052 if ((gj=cols[j]) < 0) { gj = -1-gj, t = -s; }
3053 else { t = s; }
3054 MFEM_ASSERT(gj < width,
3055 "Trying to set a column " << gj << " outside the matrix"
3056 " width " << width);
3057 a = srow(j);
3058 if (t < 0) { a = -a; }
3059 _Set_(gj, a);
3060 }
3061 ClearColPtr();
3062 }
3063 else
3064 {
3065 MFEM_ASSERT(cols.Size() == RowSize(gi), "");
3066 MFEM_ASSERT(cols.Size() == srow.Size(), "");
3067
3068 for (int i = I[gi], j = 0; j < cols.Size(); j++, i++)
3069 {
3070 if ((gj=cols[j]) < 0) { gj = -1-gj, t = -s; }
3071 else { t = s; }
3072 MFEM_ASSERT(gj < width,
3073 "Trying to set a column " << gj << " outside the matrix"
3074 " width " << width);
3075
3076 J[i] = gj;
3077 A[i] = srow[j] * t;
3078 }
3079 }
3080}
3081
3082void SparseMatrix::AddRow(const int row, const Array<int> &cols,
3083 const Vector &srow)
3084{
3085 int j, gi, gj, s, t;
3086 real_t a;
3087
3088 MFEM_VERIFY(!Finalized(), "Matrix must NOT be finalized.");
3089
3090 if ((gi=row) < 0) { gi = -1-gi, s = -1; }
3091 else { s = 1; }
3092 MFEM_ASSERT(gi < height,
3093 "Trying to insert a row " << gi << " outside the matrix height "
3094 << height);
3095 SetColPtr(gi);
3096 for (j = 0; j < cols.Size(); j++)
3097 {
3098 if ((gj=cols[j]) < 0) { gj = -1-gj, t = -s; }
3099 else { t = s; }
3100 MFEM_ASSERT(gj < width,
3101 "Trying to insert a column " << gj << " outside the matrix width "
3102 << width);
3103 a = srow(j);
3104 if (a == 0.0)
3105 {
3106 continue;
3107 }
3108 if (t < 0) { a = -a; }
3109 _Add_(gj, a);
3110 }
3111 ClearColPtr();
3112}
3113
3114void SparseMatrix::ScaleRow(const int row, const real_t scale)
3115{
3116 int i;
3117
3118 if ((i=row) < 0)
3119 {
3120 i = -1-i;
3121 }
3122 if (Rows != NULL)
3123 {
3124 RowNode *aux;
3125
3126 for (aux = Rows[i]; aux != NULL; aux = aux -> Prev)
3127 {
3128 aux -> Value *= scale;
3129 }
3130 }
3131 else
3132 {
3133 int j, end = I[i+1];
3134
3135 for (j = I[i]; j < end; j++)
3136 {
3137 A[j] *= scale;
3138 }
3139 }
3140}
3141
3143{
3144 real_t scale;
3145 if (Rows != NULL)
3146 {
3147 RowNode *aux;
3148 for (int i=0; i < height; ++i)
3149 {
3150 scale = sl(i);
3151 for (aux = Rows[i]; aux != NULL; aux = aux -> Prev)
3152 {
3153 aux -> Value *= scale;
3154 }
3155 }
3156 }
3157 else
3158 {
3159 int j, end;
3160
3161 for (int i=0; i < height; ++i)
3162 {
3163 end = I[i+1];
3164 scale = sl(i);
3165 for (j = I[i]; j < end; j++)
3166 {
3167 A[j] *= scale;
3168 }
3169 }
3170 }
3171}
3172
3174{
3175 if (Rows != NULL)
3176 {
3177 RowNode *aux;
3178 for (int i=0; i < height; ++i)
3179 {
3180 for (aux = Rows[i]; aux != NULL; aux = aux -> Prev)
3181 {
3182 aux -> Value *= sr(aux->Column);
3183 }
3184 }
3185 }
3186 else
3187 {
3188 int j, end;
3189
3190 for (int i=0; i < height; ++i)
3191 {
3192 end = I[i+1];
3193 for (j = I[i]; j < end; j++)
3194 {
3195 A[j] *= sr(J[j]);
3196 }
3197 }
3198 }
3199}
3200
3202{
3203 MFEM_ASSERT(height == B.height && width == B.width,
3204 "Mismatch of this matrix size and rhs. This height = "
3205 << height << ", width = " << width << ", B.height = "
3206 << B.height << ", B.width = " << B.width);
3207
3208 for (int i = 0; i < height; i++)
3209 {
3210 SetColPtr(i);
3211 if (B.Rows)
3212 {
3213 for (RowNode *aux = B.Rows[i]; aux != NULL; aux = aux->Prev)
3214 {
3215 _Add_(aux->Column, aux->Value);
3216 }
3217 }
3218 else
3219 {
3220 for (int j = B.I[i]; j < B.I[i+1]; j++)
3221 {
3222 _Add_(B.J[j], B.A[j]);
3223 }
3224 }
3225 ClearColPtr();
3226 }
3227
3228 return (*this);
3229}
3230
3232{
3233 for (int i = 0; i < height; i++)
3234 {
3235 B.SetColPtr(i);
3236 if (Rows)
3237 {
3238 for (RowNode *np = Rows[i]; np != NULL; np = np->Prev)
3239 {
3240 np->Value += a * B._Get_(np->Column);
3241 }
3242 }
3243 else
3244 {
3245 for (int j = I[i]; j < I[i+1]; j++)
3246 {
3247 A[j] += a * B._Get_(J[j]);
3248 }
3249 }
3250 B.ClearColPtr();
3251 }
3252}
3253
3255{
3256 if (Rows == NULL)
3257 {
3258 const int nnz = J.Capacity();
3259 real_t *h_A = HostWrite(A, nnz);
3260 for (int i = 0; i < nnz; i++)
3261 {
3262 h_A[i] = a;
3263 }
3264 }
3265 else
3266 {
3267 for (int i = 0; i < height; i++)
3268 {
3269 for (RowNode *node_p = Rows[i]; node_p != NULL;
3270 node_p = node_p -> Prev)
3271 {
3272 node_p -> Value = a;
3273 }
3274 }
3275 }
3276
3277 return (*this);
3278}
3279
3281{
3282 if (Rows == NULL)
3283 {
3284 for (int i = 0, nnz = I[height]; i < nnz; i++)
3285 {
3286 A[i] *= a;
3287 }
3288 }
3289 else
3290 {
3291 for (int i = 0; i < height; i++)
3292 {
3293 for (RowNode *node_p = Rows[i]; node_p != NULL;
3294 node_p = node_p -> Prev)
3295 {
3296 node_p -> Value *= a;
3297 }
3298 }
3299 }
3300
3301 return (*this);
3302}
3303
3304void SparseMatrix::Print(std::ostream & os, int width_) const
3305{
3306 int i, j;
3307
3308 if (A.Empty())
3309 {
3310 RowNode *nd;
3311 for (i = 0; i < height; i++)
3312 {
3313 os << "[row " << i << "]\n";
3314 for (nd = Rows[i], j = 0; nd != NULL; nd = nd->Prev, j++)
3315 {
3316 os << " (" << nd->Column << "," << nd->Value << ")";
3317 if ( !((j+1) % width_) )
3318 {
3319 os << '\n';
3320 }
3321 }
3322 if (j % width_)
3323 {
3324 os << '\n';
3325 }
3326 }
3327 return;
3328 }
3329
3330 // HostRead forces synchronization
3331 HostReadI();
3332 HostReadJ();
3333 HostReadData();
3334 for (i = 0; i < height; i++)
3335 {
3336 os << "[row " << i << "]\n";
3337 for (j = I[i]; j < I[i+1]; j++)
3338 {
3339 os << " (" << J[j] << "," << A[j] << ")";
3340 if ( !((j+1-I[i]) % width_) )
3341 {
3342 os << '\n';
3343 }
3344 }
3345 if ((j-I[i]) % width_)
3346 {
3347 os << '\n';
3348 }
3349 }
3350}
3351
3352void SparseMatrix::PrintMatlab(std::ostream & os) const
3353{
3354 os << "% size " << height << " " << width << "\n";
3355 os << "% Non Zeros " << NumNonZeroElems() << "\n";
3356
3357 int i, j;
3358 ios::fmtflags old_fmt = os.flags();
3359 os.setf(ios::scientific);
3360 std::streamsize old_prec = os.precision(14);
3361
3362 if (A.Empty())
3363 {
3364 RowNode *nd;
3365 for (i = 0; i < height; i++)
3366 {
3367 for (nd = Rows[i], j = 0; nd != NULL; nd = nd->Prev, j++)
3368 {
3369 os << i+1 << " " << nd->Column+1 << " " << nd->Value << '\n';
3370 }
3371 }
3372 }
3373 else
3374 {
3375 // HostRead forces synchronization
3376 HostReadI();
3377 HostReadJ();
3378 HostReadData();
3379 for (i = 0; i < height; i++)
3380 {
3381 for (j = I[i]; j < I[i+1]; j++)
3382 {
3383 os << i+1 << " " << J[j]+1 << " " << A[j] << '\n';
3384 }
3385 }
3386 }
3387 // Write a zero entry at (m,n) to make sure MATLAB doesn't shrink the matrix
3388 os << height << " " << width << " 0.0\n";
3389 os.precision(old_prec);
3390 os.flags(old_fmt);
3391}
3392
3393void SparseMatrix::PrintMathematica(std::ostream & os) const
3394{
3395 int i, j;
3396 ios::fmtflags old_fmt = os.flags();
3397 os.setf(ios::scientific);
3398 std::streamsize old_prec = os.precision(14);
3399
3400 os << "(* Read file into Mathematica using: "
3401 << "myMat = Get[\"this_file_name\"] *)\n";
3402 os << "SparseArray[";
3403
3404 if (A == NULL)
3405 {
3406 RowNode *nd;
3407 int c = 0;
3408 os << "{\n";
3409 for (i = 0; i < height; i++)
3410 {
3411 for (nd = Rows[i], j = 0; nd != NULL; nd = nd->Prev, j++, c++)
3412 {
3413 os << "{"<< i+1 << ", " << nd->Column+1
3414 << "} -> Internal`StringToMReal[\"" << nd->Value << "\"]";
3415 if (c < NumNonZeroElems() - 1) { os << ","; }
3416 os << '\n';
3417 }
3418 }
3419 os << "}\n";
3420 }
3421 else
3422 {
3423 // HostRead forces synchronization
3424 HostReadI();
3425 HostReadJ();
3426 HostReadData();
3427 int c = 0;
3428 os << "{\n";
3429 for (i = 0; i < height; i++)
3430 {
3431 for (j = I[i]; j < I[i+1]; j++, c++)
3432 {
3433 os << "{" << i+1 << ", " << J[j]+1
3434 << "} -> Internal`StringToMReal[\"" << A[j] << "\"]";
3435 if (c < NumNonZeroElems() - 1) { os << ","; }
3436 os << '\n';
3437 }
3438 }
3439 os << "}";
3440 }
3441
3442 os << ",{" << height << "," << width << "}]\n";
3443
3444 os.precision(old_prec);
3445 os.flags(old_fmt);
3446}
3447
3448void SparseMatrix::PrintMM(std::ostream & os) const
3449{
3450 int i, j;
3451 ios::fmtflags old_fmt = os.flags();
3452 os.setf(ios::scientific);
3453 std::streamsize old_prec = os.precision(14);
3454
3455 os << "%%MatrixMarket matrix coordinate real general" << '\n'
3456 << "% Generated by MFEM" << '\n';
3457
3458 os << height << " " << width << " " << NumNonZeroElems() << '\n';
3459
3460 if (A.Empty())
3461 {
3462 RowNode *nd;
3463 for (i = 0; i < height; i++)
3464 {
3465 for (nd = Rows[i], j = 0; nd != NULL; nd = nd->Prev, j++)
3466 {
3467 os << i+1 << " " << nd->Column+1 << " " << nd->Value << '\n';
3468 }
3469 }
3470 }
3471 else
3472 {
3473 // HostRead forces synchronization
3474 HostReadI();
3475 HostReadJ();
3476 HostReadData();
3477 for (i = 0; i < height; i++)
3478 {
3479 for (j = I[i]; j < I[i+1]; j++)
3480 {
3481 os << i+1 << " " << J[j]+1 << " " << A[j] << '\n';
3482 }
3483 }
3484 }
3485 os.precision(old_prec);
3486 os.flags(old_fmt);
3487}
3488
3489void SparseMatrix::PrintCSR(std::ostream & os) const
3490{
3491 MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
3492
3493 int i;
3494
3495 os << height << '\n'; // number of rows
3496
3497 // HostRead forces synchronization
3498 HostReadI();
3499 HostReadJ();
3500 HostReadData();
3501 for (i = 0; i <= height; i++)
3502 {
3503 os << I[i]+1 << '\n';
3504 }
3505
3506 for (i = 0; i < I[height]; i++)
3507 {
3508 os << J[i]+1 << '\n';
3509 }
3510
3511 for (i = 0; i < I[height]; i++)
3512 {
3513 os << A[i] << '\n';
3514 }
3515}
3516
3517void SparseMatrix::PrintCSR2(std::ostream & os) const
3518{
3519 MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
3520
3521 int i;
3522
3523 os << height << '\n'; // number of rows
3524 os << width << '\n'; // number of columns
3525
3526 // HostRead forces synchronization
3527 HostReadI();
3528 HostReadJ();
3529 HostReadData();
3530 for (i = 0; i <= height; i++)
3531 {
3532 os << I[i] << '\n';
3533 }
3534
3535 for (i = 0; i < I[height]; i++)
3536 {
3537 os << J[i] << '\n';
3538 }
3539
3540 for (i = 0; i < I[height]; i++)
3541 {
3542 os << A[i] << '\n';
3543 }
3544}
3545
3546void SparseMatrix::PrintInfo(std::ostream &os) const
3547{
3548 const real_t MiB = 1024.*1024;
3549 int nnz = NumNonZeroElems();
3550 real_t pz = 100./nnz;
3551 int nz = CountSmallElems(0.0);
3552 real_t max_norm = MaxNorm();
3553 real_t symm = IsSymmetric();
3554 int nnf = CheckFinite();
3555 int ns12 = CountSmallElems(1e-12*max_norm);
3556 int ns15 = CountSmallElems(1e-15*max_norm);
3557 int ns18 = CountSmallElems(1e-18*max_norm);
3558
3559 os <<
3560 "SparseMatrix statistics:\n"
3561 " Format : " <<
3562 (Empty() ? "(empty)" : (Finalized() ? "CSR" : "LIL")) << "\n"
3563 " Dimensions : " << height << " x " << width << "\n"
3564 " Number of entries (total) : " << nnz << "\n"
3565 " Number of entries (per row) : " << 1.*nnz/Height() << "\n"
3566 " Number of stored zeros : " << nz*pz << "% (" << nz << ")\n"
3567 " Number of Inf/Nan entries : " << nnf*pz << "% ("<< nnf << ")\n"
3568 " Norm, max |a_ij| : " << max_norm << "\n"
3569 " Symmetry, max |a_ij-a_ji| : " << symm << "\n"
3570 " Number of small entries:\n"
3571 " |a_ij| <= 1e-12*Norm : " << ns12*pz << "% (" << ns12 << ")\n"
3572 " |a_ij| <= 1e-15*Norm : " << ns15*pz << "% (" << ns15 << ")\n"
3573 " |a_ij| <= 1e-18*Norm : " << ns18*pz << "% (" << ns18 << ")\n";
3574 if (Finalized())
3575 {
3576 os << " Memory used by CSR : " <<
3577 (sizeof(int)*(height+1+nnz)+sizeof(real_t)*nnz)/MiB << " MiB\n";
3578 }
3579 if (Rows != NULL)
3580 {
3581 size_t used_mem = sizeof(RowNode*)*height;
3582#ifdef MFEM_USE_MEMALLOC
3583 used_mem += NodesMem->MemoryUsage();
3584#else
3585 for (int i = 0; i < height; i++)
3586 {
3587 for (RowNode *aux = Rows[i]; aux != NULL; aux = aux->Prev)
3588 {
3589 used_mem += sizeof(RowNode);
3590 }
3591 }
3592#endif
3593 os << " Memory used by LIL : " << used_mem/MiB << " MiB\n";
3594 }
3595}
3596
3598{
3599 I.Delete();
3600 J.Delete();
3601 A.Delete();
3602
3603 if (Rows != NULL)
3604 {
3605#if !defined(MFEM_USE_MEMALLOC)
3606 for (int i = 0; i < height; i++)
3607 {
3608 RowNode *aux, *node_p = Rows[i];
3609 while (node_p != NULL)
3610 {
3611 aux = node_p;
3612 node_p = node_p->Prev;
3613 delete aux;
3614 }
3615 }
3616#endif
3617 delete [] Rows;
3618 }
3619
3620 delete [] ColPtrJ;
3621 delete [] ColPtrNode;
3622#ifdef MFEM_USE_MEMALLOC
3623 delete NodesMem;
3624#endif
3625 delete At;
3626
3628}
3629
3631{
3632 int awidth = 0;
3633 if (A)
3634 {
3635 const int *start_j = J;
3636 const int *end_j = J + I[height];
3637 for (const int *jptr = start_j; jptr != end_j; ++jptr)
3638 {
3639 awidth = std::max(awidth, *jptr + 1);
3640 }
3641 }
3642 else
3643 {
3644 RowNode *aux;
3645 for (int i = 0; i < height; i++)
3646 {
3647 for (aux = Rows[i]; aux != NULL; aux = aux->Prev)
3648 {
3649 awidth = std::max(awidth, aux->Column + 1);
3650 }
3651 }
3652 }
3653 return awidth;
3654}
3655
3657{
3658 int n = S.NumNonZeroElems();
3659 real_t * s = S.GetData();
3660
3661 for (int i = 0; i < n; i++)
3662 {
3663 s[i] = f(s[i]);
3664 }
3665}
3666
3668{
3669 MFEM_VERIFY(
3670 A.Finalized(),
3671 "Finalize must be called before Transpose. Use TransposeRowMatrix instead");
3672
3673 int i, j, end;
3674 const int *A_i, *A_j;
3675 int m, n, nnz, *At_i, *At_j;
3676 const real_t *A_data;
3677 real_t *At_data;
3678
3679 m = A.Height(); // number of rows of A
3680 n = A.Width(); // number of columns of A
3681 nnz = A.NumNonZeroElems();
3682 A_i = A.HostReadI();
3683 A_j = A.HostReadJ();
3684 A_data = A.HostReadData();
3685
3686 At_i = Memory<int>(n+1);
3687 At_j = Memory<int>(nnz);
3688 At_data = Memory<real_t>(nnz);
3689
3690 for (i = 0; i <= n; i++)
3691 {
3692 At_i[i] = 0;
3693 }
3694 for (i = 0; i < nnz; i++)
3695 {
3696 At_i[A_j[i]+1]++;
3697 }
3698 for (i = 1; i < n; i++)
3699 {
3700 At_i[i+1] += At_i[i];
3701 }
3702
3703 for (i = j = 0; i < m; i++)
3704 {
3705 end = A_i[i+1];
3706 for ( ; j < end; j++)
3707 {
3708 At_j[At_i[A_j[j]]] = i;
3709 At_data[At_i[A_j[j]]] = A_data[j];
3710 At_i[A_j[j]]++;
3711 }
3712 }
3713
3714 for (i = n; i > 0; i--)
3715 {
3716 At_i[i] = At_i[i-1];
3717 }
3718 At_i[0] = 0;
3719
3720 return new SparseMatrix(At_i, At_j, At_data, n, m);
3721}
3722
3724 int useActualWidth)
3725{
3726 int i, j;
3727 int m, n, nnz, *At_i, *At_j;
3728 real_t *At_data;
3729 Array<int> Acols;
3730 Vector Avals;
3731
3732 m = A.Height(); // number of rows of A
3733 if (useActualWidth)
3734 {
3735 n = 0;
3736 int tmp;
3737 for (i = 0; i < m; i++)
3738 {
3739 A.GetRow(i, Acols, Avals);
3740 if (Acols.Size())
3741 {
3742 tmp = Acols.Max();
3743 if (tmp > n)
3744 {
3745 n = tmp;
3746 }
3747 }
3748 }
3749 ++n;
3750 }
3751 else
3752 {
3753 n = A.Width(); // number of columns of A
3754 }
3755 nnz = A.NumNonZeroElems();
3756
3757 At_i = Memory<int>(n+1);
3758 At_j = Memory<int>(nnz);
3759 At_data = Memory<real_t>(nnz);
3760
3761 for (i = 0; i <= n; i++)
3762 {
3763 At_i[i] = 0;
3764 }
3765
3766 for (i = 0; i < m; i++)
3767 {
3768 A.GetRow(i, Acols, Avals);
3769 for (j = 0; j<Acols.Size(); ++j)
3770 {
3771 At_i[Acols[j]+1]++;
3772 }
3773 }
3774 for (i = 1; i < n; i++)
3775 {
3776 At_i[i+1] += At_i[i];
3777 }
3778
3779 for (i = 0; i < m; i++)
3780 {
3781 A.GetRow(i, Acols, Avals);
3782 for (j = 0; j<Acols.Size(); ++j)
3783 {
3784 At_j[At_i[Acols[j]]] = i;
3785 At_data[At_i[Acols[j]]] = Avals[j];
3786 At_i[Acols[j]]++;
3787 }
3788 }
3789
3790 for (i = n; i > 0; i--)
3791 {
3792 At_i[i] = At_i[i-1];
3793 }
3794 At_i[0] = 0;
3795
3796 return new SparseMatrix(At_i, At_j, At_data, n, m);
3797}
3798
3799
3801 SparseMatrix *OAB)
3802{
3803 int nrowsA, ncolsA, nrowsB, ncolsB;
3804 const int *A_i, *A_j, *B_i, *B_j;
3805 int *C_i, *C_j, *B_marker;
3806 const real_t *A_data, *B_data;
3807 real_t *C_data;
3808 int ia, ib, ic, ja, jb, num_nonzeros;
3809 int row_start, counter;
3810 real_t a_entry, b_entry;
3811 SparseMatrix *C;
3812
3813 nrowsA = A.Height();
3814 ncolsA = A.Width();
3815 nrowsB = B.Height();
3816 ncolsB = B.Width();
3817
3818 MFEM_VERIFY(ncolsA == nrowsB,
3819 "number of columns of A (" << ncolsA
3820 << ") must equal number of rows of B (" << nrowsB << ")");
3821
3822 A_i = A.HostReadI();
3823 A_j = A.HostReadJ();
3824 A_data = A.HostReadData();
3825 B_i = B.HostReadI();
3826 B_j = B.HostReadJ();
3827 B_data = B.HostReadData();
3828
3829 B_marker = new int[ncolsB];
3830
3831 for (ib = 0; ib < ncolsB; ib++)
3832 {
3833 B_marker[ib] = -1;
3834 }
3835
3836 if (OAB == NULL)
3837 {
3838 C_i = Memory<int>(nrowsA+1);
3839
3840 C_i[0] = num_nonzeros = 0;
3841 for (ic = 0; ic < nrowsA; ic++)
3842 {
3843 for (ia = A_i[ic]; ia < A_i[ic+1]; ia++)
3844 {
3845 ja = A_j[ia];
3846 for (ib = B_i[ja]; ib < B_i[ja+1]; ib++)
3847 {
3848 jb = B_j[ib];
3849 if (B_marker[jb] != ic)
3850 {
3851 B_marker[jb] = ic;
3852 num_nonzeros++;
3853 }
3854 }
3855 }
3856 C_i[ic+1] = num_nonzeros;
3857 }
3858
3859 C_j = Memory<int>(num_nonzeros);
3860 C_data = Memory<real_t>(num_nonzeros);
3861
3862 C = new SparseMatrix(C_i, C_j, C_data, nrowsA, ncolsB);
3863
3864 for (ib = 0; ib < ncolsB; ib++)
3865 {
3866 B_marker[ib] = -1;
3867 }
3868 }
3869 else
3870 {
3871 C = OAB;
3872
3873 MFEM_VERIFY(nrowsA == C->Height() && ncolsB == C->Width(),
3874 "Input matrix sizes do not match output sizes"
3875 << " nrowsA = " << nrowsA
3876 << ", C->Height() = " << C->Height()
3877 << " ncolsB = " << ncolsB
3878 << ", C->Width() = " << C->Width());
3879
3880 // C_i = C->HostReadI(); // not used
3881 C_j = C->HostWriteJ();
3882 C_data = C->HostWriteData();
3883 }
3884
3885 counter = 0;
3886 for (ic = 0; ic < nrowsA; ic++)
3887 {
3888 // row_start = C_i[ic];
3889 row_start = counter;
3890 for (ia = A_i[ic]; ia < A_i[ic+1]; ia++)
3891 {
3892 ja = A_j[ia];
3893 a_entry = A_data[ia];
3894 for (ib = B_i[ja]; ib < B_i[ja+1]; ib++)
3895 {
3896 jb = B_j[ib];
3897 b_entry = B_data[ib];
3898 if (B_marker[jb] < row_start)
3899 {
3900 B_marker[jb] = counter;
3901 if (OAB == NULL)
3902 {
3903 C_j[counter] = jb;
3904 }
3905 C_data[counter] = a_entry*b_entry;
3906 counter++;
3907 }
3908 else
3909 {
3910 C_data[B_marker[jb]] += a_entry*b_entry;
3911 }
3912 }
3913 }
3914 }
3915
3916 MFEM_VERIFY(
3917 OAB == NULL || counter == OAB->NumNonZeroElems(),
3918 "With pre-allocated output matrix, number of non-zeros ("
3919 << OAB->NumNonZeroElems()
3920 << ") did not match number of entries changed from matrix-matrix multiply, "
3921 << counter);
3922
3923 delete [] B_marker;
3924
3925 return C;
3926}
3927
3929{
3930 SparseMatrix *At = Transpose(A);
3931 SparseMatrix *AtB = Mult(*At, B);
3932 delete At;
3933 return AtB;
3934}
3935
3937 const AbstractSparseMatrix &B)
3938{
3939 int nrowsA, ncolsA, nrowsB, ncolsB;
3940 int *C_i, *C_j, *B_marker;
3941 real_t *C_data;
3942 int ia, ib, ic, ja, jb, num_nonzeros;
3943 int row_start, counter;
3944 real_t a_entry, b_entry;
3945 SparseMatrix *C;
3946
3947 nrowsA = A.Height();
3948 ncolsA = A.Width();
3949 nrowsB = B.Height();
3950 ncolsB = B.Width();
3951
3952 MFEM_VERIFY(ncolsA == nrowsB,
3953 "number of columns of A (" << ncolsA
3954 << ") must equal number of rows of B (" << nrowsB << ")");
3955
3956 B_marker = new int[ncolsB];
3957
3958 for (ib = 0; ib < ncolsB; ib++)
3959 {
3960 B_marker[ib] = -1;
3961 }
3962
3963 C_i = Memory<int>(nrowsA+1);
3964
3965 C_i[0] = num_nonzeros = 0;
3966
3967 Array<int> colsA, colsB;
3968 Vector dataA, dataB;
3969 for (ic = 0; ic < nrowsA; ic++)
3970 {
3971 A.GetRow(ic, colsA, dataA);
3972 for (ia = 0; ia < colsA.Size(); ia++)
3973 {
3974 ja = colsA[ia];
3975 B.GetRow(ja, colsB, dataB);
3976 for (ib = 0; ib < colsB.Size(); ib++)
3977 {
3978 jb = colsB[ib];
3979 if (B_marker[jb] != ic)
3980 {
3981 B_marker[jb] = ic;
3982 num_nonzeros++;
3983 }
3984 }
3985 }
3986 C_i[ic+1] = num_nonzeros;
3987 }
3988
3989 C_j = Memory<int>(num_nonzeros);
3990 C_data = Memory<real_t>(num_nonzeros);
3991
3992 C = new SparseMatrix(C_i, C_j, C_data, nrowsA, ncolsB);
3993
3994 for (ib = 0; ib < ncolsB; ib++)
3995 {
3996 B_marker[ib] = -1;
3997 }
3998
3999 counter = 0;
4000 for (ic = 0; ic < nrowsA; ic++)
4001 {
4002 row_start = counter;
4003 A.GetRow(ic, colsA, dataA);
4004 for (ia = 0; ia < colsA.Size(); ia++)
4005 {
4006 ja = colsA[ia];
4007 a_entry = dataA[ia];
4008 B.GetRow(ja, colsB, dataB);
4009 for (ib = 0; ib < colsB.Size(); ib++)
4010 {
4011 jb = colsB[ib];
4012 b_entry = dataB[ib];
4013 if (B_marker[jb] < row_start)
4014 {
4015 B_marker[jb] = counter;
4016 C_j[counter] = jb;
4017 C_data[counter] = a_entry*b_entry;
4018 counter++;
4019 }
4020 else
4021 {
4022 C_data[B_marker[jb]] += a_entry*b_entry;
4023 }
4024 }
4025 }
4026 }
4027
4028 delete [] B_marker;
4029
4030 return C;
4031}
4032
4034{
4035 DenseMatrix *C = new DenseMatrix(A.Height(), B.Width());
4036 Vector columnB, columnC;
4037 for (int j = 0; j < B.Width(); ++j)
4038 {
4039 B.GetColumnReference(j, columnB);
4040 C->GetColumnReference(j, columnC);
4041 A.Mult(columnB, columnC);
4042 }
4043 return C;
4044}
4045
4047{
4048 DenseMatrix R (P, 't'); // R = P^T
4049 DenseMatrix *AP = Mult (A, P);
4050 DenseMatrix *RAP_ = new DenseMatrix(R.Height(), AP->Width());
4051 Mult (R, *AP, *RAP_);
4052 delete AP;
4053 return RAP_;
4054}
4055
4057{
4058 SparseMatrix *R = Transpose(P);
4059 DenseMatrix *RA = Mult(*R, A);
4060 DenseMatrix AtP(*RA, 't');
4061 delete RA;
4062 DenseMatrix *RAtP = Mult(*R, AtP);
4063 delete R;
4064 DenseMatrix * RAP_ = new DenseMatrix(*RAtP, 't');
4065 delete RAtP;
4066 return RAP_;
4067}
4068
4070 SparseMatrix *ORAP)
4071{
4072 SparseMatrix *P = Transpose (R);
4073 SparseMatrix *AP = Mult (A, *P);
4074 delete P;
4075 SparseMatrix *RAP_ = Mult (R, *AP, ORAP);
4076 delete AP;
4077 return RAP_;
4078}
4079
4081 const SparseMatrix &P)
4082{
4083 SparseMatrix * R = Transpose(Rt);
4084 SparseMatrix * RA = Mult(*R,A);
4085 delete R;
4086 SparseMatrix * RAP_ = Mult(*RA, P);
4087 delete RA;
4088 return RAP_;
4089}
4090
4092 SparseMatrix *OAtDA)
4093{
4094 int i, At_nnz, *At_j;
4095 real_t *At_data;
4096
4097 SparseMatrix *At = Transpose (A);
4098 At_nnz = At -> NumNonZeroElems();
4099 At_j = At -> GetJ();
4100 At_data = At -> GetData();
4101 for (i = 0; i < At_nnz; i++)
4102 {
4103 At_data[i] *= D(At_j[i]);
4104 }
4105 SparseMatrix *AtDA = Mult (*At, A, OAtDA);
4106 delete At;
4107 return AtDA;
4108}
4109
4111 const SparseMatrix & B)
4112{
4113 int nrows = A.Height();
4114 int ncols = A.Width();
4115
4116 int * C_i = Memory<int>(nrows+1);
4117 int * C_j;
4118 real_t * C_data;
4119
4120 const int *A_i = A.HostReadI();
4121 const int *A_j = A.HostReadJ();
4122 const real_t *A_data = A.HostReadData();
4123
4124 const int *B_i = B.HostReadI();
4125 const int *B_j = B.HostReadJ();
4126 const real_t *B_data = B.HostReadData();
4127
4128 int * marker = new int[ncols];
4129 std::fill(marker, marker+ncols, -1);
4130
4131 int num_nonzeros = 0, jcol;
4132 C_i[0] = 0;
4133 for (int ic = 0; ic < nrows; ic++)
4134 {
4135 for (int ia = A_i[ic]; ia < A_i[ic+1]; ia++)
4136 {
4137 jcol = A_j[ia];
4138 marker[jcol] = ic;
4139 num_nonzeros++;
4140 }
4141 for (int ib = B_i[ic]; ib < B_i[ic+1]; ib++)
4142 {
4143 jcol = B_j[ib];
4144 if (marker[jcol] != ic)
4145 {
4146 marker[jcol] = ic;
4147 num_nonzeros++;
4148 }
4149 }
4150 C_i[ic+1] = num_nonzeros;
4151 }
4152
4153 C_j = Memory<int>(num_nonzeros);
4154 C_data = Memory<real_t>(num_nonzeros);
4155
4156 for (int ia = 0; ia < ncols; ia++)
4157 {
4158 marker[ia] = -1;
4159 }
4160
4161 int pos = 0;
4162 for (int ic = 0; ic < nrows; ic++)
4163 {
4164 for (int ia = A_i[ic]; ia < A_i[ic+1]; ia++)
4165 {
4166 jcol = A_j[ia];
4167 C_j[pos] = jcol;
4168 C_data[pos] = a*A_data[ia];
4169 marker[jcol] = pos;
4170 pos++;
4171 }
4172 for (int ib = B_i[ic]; ib < B_i[ic+1]; ib++)
4173 {
4174 jcol = B_j[ib];
4175 if (marker[jcol] < C_i[ic])
4176 {
4177 C_j[pos] = jcol;
4178 C_data[pos] = b*B_data[ib];
4179 marker[jcol] = pos;
4180 pos++;
4181 }
4182 else
4183 {
4184 C_data[marker[jcol]] += b*B_data[ib];
4185 }
4186 }
4187 }
4188
4189 delete[] marker;
4190 return new SparseMatrix(C_i, C_j, C_data, nrows, ncols);
4191}
4192
4194{
4195 return Add(1.,A,1.,B);
4196}
4197
4199{
4200 MFEM_ASSERT(Ai.Size() > 0, "invalid size Ai.Size() = " << Ai.Size());
4201
4202 SparseMatrix * accumulate = Ai[0];
4203 SparseMatrix * result = accumulate;
4204
4205 for (int i=1; i < Ai.Size(); ++i)
4206 {
4207 result = Add(*accumulate, *Ai[i]);
4208 if (i != 1)
4209 {
4210 delete accumulate;
4211 }
4212
4213 accumulate = result;
4214 }
4215
4216 return result;
4217}
4218
4219/// B += alpha * A
4220void Add(const SparseMatrix &A,
4222{
4223 for (int r = 0; r < B.Height(); r++)
4224 {
4225 const int * colA = A.GetRowColumns(r);
4226 const real_t * valA = A.GetRowEntries(r);
4227 for (int i=0; i<A.RowSize(r); i++)
4228 {
4229 B(r, colA[i]) += alpha * valA[i];
4230 }
4231 }
4232}
4233
4234/// Produces a block matrix with blocks A_{ij}*B
4236{
4237 int mA = A.Height(), nA = A.Width();
4238 int mB = B.Height(), nB = B.Width();
4239
4240 DenseMatrix *C = new DenseMatrix(mA * mB, nA * nB);
4241 *C = 0.0;
4242 for (int i=0; i<mA; i++)
4243 {
4244 for (int j=0; j<nA; j++)
4245 {
4246 C->AddMatrix(A(i,j), B, i * mB, j * nB);
4247 }
4248 }
4249 return C;
4250}
4251
4252/// Produces a block matrix with blocks A_{ij}*B
4254{
4255 int mA = A.Height(), nA = A.Width();
4256 int mB = B.Height(), nB = B.Width();
4257
4258 SparseMatrix *C = new SparseMatrix(mA * mB, nA * nB);
4259
4260 for (int i=0; i<mA; i++)
4261 {
4262 for (int j=0; j<nA; j++)
4263 {
4264 for (int r=0; r<mB; r++)
4265 {
4266 const int * colB = B.GetRowColumns(r);
4267 const real_t * valB = B.GetRowEntries(r);
4268
4269 for (int cj=0; cj<B.RowSize(r); cj++)
4270 {
4271 C->Set(i * mB + r, j * nB + colB[cj], A(i,j) * valB[cj]);
4272 }
4273 }
4274 }
4275 }
4276 C->Finalize();
4277
4278 return C;
4279}
4280
4281/// Produces a block matrix with blocks A_{ij}*B
4283{
4284 int mA = A.Height(), nA = A.Width();
4285 int mB = B.Height(), nB = B.Width();
4286
4287 SparseMatrix *C = new SparseMatrix(mA * mB, nA * nB);
4288
4289 for (int r=0; r<mA; r++)
4290 {
4291 const int * colA = A.GetRowColumns(r);
4292 const real_t * valA = A.GetRowEntries(r);
4293
4294 for (int aj=0; aj<A.RowSize(r); aj++)
4295 {
4296 for (int i=0; i<mB; i++)
4297 {
4298 for (int j=0; j<nB; j++)
4299 {
4300 C->Set(r * mB + i, colA[aj] * nB + j, valA[aj] * B(i, j));
4301 }
4302 }
4303 }
4304 }
4305 C->Finalize();
4306
4307 return C;
4308}
4309
4310/// Produces a block matrix with blocks A_{ij}*B
4312{
4313 int mA = A.Height(), nA = A.Width();
4314 int mB = B.Height(), nB = B.Width();
4315
4316 SparseMatrix *C = new SparseMatrix(mA * mB, nA * nB);
4317
4318 for (int ar=0; ar<mA; ar++)
4319 {
4320 const int * colA = A.GetRowColumns(ar);
4321 const real_t * valA = A.GetRowEntries(ar);
4322
4323 for (int aj=0; aj<A.RowSize(ar); aj++)
4324 {
4325 for (int br=0; br<mB; br++)
4326 {
4327 const int * colB = B.GetRowColumns(br);
4328 const real_t * valB = B.GetRowEntries(br);
4329
4330 for (int bj=0; bj<B.RowSize(br); bj++)
4331 {
4332 C->Set(ar * mB + br, colA[aj] * nB + colB[bj],
4333 valA[aj] * valB[bj]);
4334 }
4335 }
4336 }
4337 }
4338 C->Finalize();
4339
4340 return C;
4341}
4342
4344{
4345 mfem::Swap(width, other.width);
4346 mfem::Swap(height, other.height);
4347 mfem::Swap(I, other.I);
4348 mfem::Swap(J, other.J);
4349 mfem::Swap(A, other.A);
4350 mfem::Swap(Rows, other.Rows);
4352 mfem::Swap(ColPtrJ, other.ColPtrJ);
4354 mfem::Swap(At, other.At);
4355
4356#ifdef MFEM_USE_MEMALLOC
4358#endif
4359
4361}
4362
4364{
4365 Destroy();
4366#ifdef MFEM_USE_CUDA_OR_HIP
4368 {
4369#ifdef MFEM_CUDA_1897_WORKAROUND
4370 if (dBuffer)
4371 {
4372 MFEM_Cu_or_Hip(MemFree)(dBuffer);
4373 dBuffer = nullptr;
4374 bufferSize = 0;
4375 }
4376#endif
4377 if (SparseMatrixCount==1)
4378 {
4379 if (handle)
4380 {
4381 MFEM_CHECK_SPARSE(MFEM_cu_or_hip(sparseDestroy)(handle));
4382 handle = nullptr;
4383 }
4384#ifndef MFEM_CUDA_1897_WORKAROUND
4385 if (dBuffer)
4386 {
4387 MFEM_Cu_or_Hip(MemFree)(dBuffer);
4388 dBuffer = nullptr;
4389 bufferSize = 0;
4390 }
4391#endif
4392 }
4394 }
4395#endif // MFEM_USE_CUDA_OR_HIP
4396}
4397
4398}
Abstract data type for sparse matrices.
Definition matrix.hpp:74
virtual int NumNonZeroElems() const =0
Returns the number of non-zeros in a matrix.
virtual int GetRow(const int row, Array< int > &cols, Vector &srow) const =0
Gets the columns indexes and values for row row.
Dynamic 2D array using row-major layout.
Definition array.hpp:459
int NumCols() const
Definition array.hpp:477
int NumRows() const
Definition array.hpp:476
Memory< T > & GetMemory()
Return a reference to the Memory object used by the Array.
Definition array.hpp:164
T Max() const
Find the maximal element in the array, using the comparison operator < for class T.
Definition array.cpp:69
const T * HostRead() const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), false).
Definition array.hpp:414
void Sort()
Sorts the array in ascending order. This requires operator< to be defined for T.
Definition array.hpp:341
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:869
int Size() const
Return the logical size of the array.
Definition array.hpp:192
void MakeRef(T *data_, int size_, bool own_data=false)
Make this Array a reference to a pointer.
Definition array.hpp:1082
T * Write(bool on_dev=true)
Shortcut for mfem::Write(a.GetMemory(), a.Size(), on_dev).
Definition array.hpp:418
const T * Read(bool on_dev=true) const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), on_dev).
Definition array.hpp:410
T * HostReadWrite()
Shortcut for mfem::ReadWrite(a.GetMemory(), a.Size(), false).
Definition array.hpp:430
Data type dense matrix using column-major storage.
Definition densemat.hpp:24
void GetColumnReference(int c, Vector &col)
Definition densemat.hpp:340
void SetSize(int s)
Change the size of the DenseMatrix to s x s.
Definition densemat.hpp:125
void AddMatrix(DenseMatrix &A, int ro, int co)
Perform (ro+i,co+j)+=A(i,j) for 0<=i.
static bool Allows(unsigned long b_mask)
Return true if any of the backends in the backend mask, b_mask, are allowed.
Definition device.hpp:271
static MemoryType GetDeviceMemoryType()
Get the current Device MemoryType. This is the MemoryType used by most MFEM classes when allocating m...
Definition device.hpp:298
Abstract data type for matrix inverse.
Definition matrix.hpp:63
size_t MemoryUsage() const
Class used by MFEM to store pointers to host and/or device memory.
void SetHostPtrOwner(bool own) const
Set/clear the ownership flag for the host pointer. Ownership indicates whether the pointer will be de...
int Capacity() const
Return the size of the allocated memory.
MemoryType GetMemoryType() const
Return a MemoryType that is currently valid. If both the host and the device pointers are currently v...
bool Empty() const
Return true if the Memory object is empty, see Reset().
void CopyFrom(const Memory &src, int size)
Copy size entries from src to *this.
void Reset()
Reset the memory to be empty, ensuring that Delete() will be a no-op.
void Wrap(T *ptr, int size, bool own)
Wrap an externally allocated host pointer, ptr with the current host memory type returned by MemoryMa...
void Delete()
Delete the owned pointers and reset the Memory object.
void ClearOwnerFlags() const
Clear the ownership flags for the host and device pointers, as well as any internal data allocated by...
void New(int size)
Allocate host memory for size entries with the current host memory type returned by MemoryManager::Ge...
int width
Dimension of the input / number of columns in the matrix.
Definition operator.hpp:30
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
Definition operator.hpp:68
int height
Dimension of the output / number of rows in the matrix.
Definition operator.hpp:29
DiagonalPolicy
Defines operator diagonal policy upon elimination of rows and/or columns.
Definition operator.hpp:50
@ DIAG_ONE
Set the diagonal value to one.
Definition operator.hpp:52
@ DIAG_KEEP
Keep the diagonal value.
Definition operator.hpp:53
@ DIAG_ZERO
Set the diagonal value to zero.
Definition operator.hpp:51
int Width() const
Get the width (size of input) of the Operator. Synonym with NumCols().
Definition operator.hpp:74
RowNode * Prev
Definition sparsemat.hpp:45
Data type sparse matrix.
Definition sparsemat.hpp:51
int GetRow(const int row, Array< int > &cols, Vector &srow) const override
Extract all column indices and values from a given row.
void PrintCSR2(std::ostream &out) const
Prints a sparse matrix to stream out in CSR format.
real_t MaxNorm() const
const int * HostReadJ() const
void GetDiag(Vector &d) const
Returns the Diagonal of A.
real_t GetRowNorml1(int irow) const
For i = irow compute .
void PartMult(const Array< int > &rows, const Vector &x, Vector &y) const
bool isSorted
Are the columns sorted already.
Definition sparsemat.hpp:96
RowNodeAlloc * NodesMem
Definition sparsemat.hpp:92
void Gauss_Seidel_back(const Vector &x, Vector &y) const
bool RowIsEmpty(const int row) const
void ClearColPtr() const
Reset the "current row" set by calling SetColPtr(). This method must be called between any two calls ...
MatrixInverse * Inverse() const override
This virtual method is not supported: it always returns NULL.
void Jacobi3(const Vector &b, const Vector &x0, Vector &x1, real_t sc=1.0) const
real_t * ReadWriteData(bool on_dev=true)
Memory< real_t > A
Array with size I[height], containing the actual entries of the sparse matrix, as indexed by the I ar...
Definition sparsemat.hpp:76
void EliminateRowCol(int rc, const real_t sol, Vector &rhs, DiagonalPolicy dpolicy=DIAG_ONE)
Eliminate row rc and column rc and modify the rhs using sol.
SparseMatrix & operator*=(real_t a)
void MultTranspose(const Vector &x, Vector &y) const override
Multiply a vector with the transposed matrix. y = At * x.
void SetWidth(int width_=-1)
Change the width of a SparseMatrix.
MemAlloc< RowNode, 1024 > RowNodeAlloc
Definition sparsemat.hpp:91
bool Empty() const
Check if the SparseMatrix is empty.
void GetSubMatrix(const Array< int > &rows, const Array< int > &cols, DenseMatrix &subm) const
SparseMatrix * At
Transpose of A. Owned. Used to perform MultTranspose() on devices.
Definition sparsemat.hpp:88
void PartAddMult(const Array< int > &rows, const Vector &x, Vector &y, const real_t a=1.0) const
void GetRowSums(Vector &x) const
For all i compute .
void AbsMult(const Vector &x, Vector &y) const override
y = |A| * x, using entry-wise absolute values of matrix A
int NumNonZeroElems() const override
Returns the number of the nonzero elements in the matrix.
bool Finalized() const
Returns whether or not CSR format has been finalized.
void EliminateBC(const Array< int > &ess_dofs, DiagonalPolicy diag_policy)
Eliminate essential (Dirichlet) boundary conditions.
void PrintInfo(std::ostream &out) const
Print various sparse matrix statistics.
void MoveDiagonalFirst()
Move the diagonal entry to the first position in each row, preserving the order of the rest of the co...
void Add(const int i, const int j, const real_t val)
void EliminateRowColMultipleRHS(int rc, const Vector &sol, DenseMatrix &rhs, DiagonalPolicy dpolicy=DIAG_ONE)
Similar to EliminateRowCol(int, const double, Vector &, DiagonalPolicy), but multiple values for elim...
void Jacobi(const Vector &b, const Vector &x0, Vector &x1, real_t sc, bool use_abs_diag=false) const
void SetColPtr(const int row) const
Initialize the SparseMatrix for fast access to the entries of the given row which becomes the "curren...
void SetDiagIdentity()
If a row contains only one diag entry of zero, set it to 1.
void Gauss_Seidel_forw(const Vector &x, Vector &y) const
Gauss-Seidel forward and backward iterations over a vector x.
void MakeRef(const SparseMatrix &master)
Clear the contents of the SparseMatrix and make it a reference to master.
int CheckFinite() const
Count the number of entries that are NOT finite, i.e. Inf or Nan.
Memory< int > J
Array with size I[height], containing the column indices for all matrix entries, as indexed by the I ...
Definition sparsemat.hpp:73
void EliminateCol(int col, DiagonalPolicy dpolicy=DIAG_ZERO)
Eliminates the column col from the matrix.
void EnsureMultTranspose() const
Ensures that the matrix is capable of performing MultTranspose(), AddMultTranspose(),...
void Print(std::ostream &out=mfem::out, int width_=4) const override
Prints matrix to stream out.
void _Add_(const int col, const real_t a)
Add a value to an entry in the "current row". See SetColPtr().
SparseMatrix()
Create an empty SparseMatrix.
cusparseSpMatDescr_t matA_descr
real_t & Elem(int i, int j) override
Returns reference to a_{ij}.
virtual void PrintMathematica(std::ostream &out=mfem::out) const
Prints matrix as a SparseArray for importing into Mathematica.
void ClearGPUSparse()
Clear the cuSPARSE/hipSPARSE descriptors. This must be called after releasing the device memory of A.
void Threshold(real_t tol, bool fix_empty_rows=false)
Remove entries smaller in absolute value than a given tolerance tol. If fix_empty_rows is true,...
void SetSubMatrix(const Array< int > &rows, const Array< int > &cols, const DenseMatrix &subm, int skip_zeros=1)
void PrintMatlab(std::ostream &out=mfem::out) const override
Prints matrix in matlab format.
const int * ReadI(bool on_dev=true) const
real_t InnerProduct(const Vector &x, const Vector &y) const
Compute y^t A x.
Memory< int > I
Array with size (height+1) containing the row offsets.
Definition sparsemat.hpp:70
real_t & operator()(int i, int j)
Returns reference to A[i][j].
const real_t * HostReadData() const
void PrintMM(std::ostream &out=mfem::out) const
Prints matrix in Matrix Market sparse format.
void ScaleColumns(const Vector &sr)
this = this * diag(sr);
void PrintCSR(std::ostream &out) const
Prints matrix to stream out in hypre_CSRMatrix format.
void Swap(SparseMatrix &other)
int * ReadWriteJ(bool on_dev=true)
cusparseDnVecDescr_t vecY_descr
void BooleanMultTranspose(const Array< int > &x, Array< int > &y) const
y = At * x, treating all entries as booleans (zero=false, nonzero=true).
RowNode ** Rows
Array of linked lists, one for every row. This array represents the linked list (LIL) storage format.
Definition sparsemat.hpp:81
real_t & SearchRow(const int col)
Perform a fast search for an entry in the "current row". See SetColPtr().
static int SparseMatrixCount
void AddSubMatrix(const Array< int > &rows, const Array< int > &cols, const DenseMatrix &subm, int skip_zeros=1)
void Symmetrize()
(*this) = 1/2 ((*this) + (*this)^t)
void BooleanMult(const Array< int > &x, Array< int > &y) const
y = A * x, treating all entries as booleans (zero=false, nonzero=true).
void AddMultTranspose(const Vector &x, Vector &y, const real_t a=1.0) const override
y += At * x (default) or y += a * At * x
int * GetRowColumns(const int row)
Return a pointer to the column indices in a row.
real_t GetJacobiScaling() const
Determine appropriate scaling for Jacobi iteration.
RowNode ** ColPtrNode
Definition sparsemat.hpp:85
int RowSize(const int i) const
Returns the number of elements in row i.
real_t * HostReadWriteData()
int CountSmallElems(real_t tol) const
Count the number of entries with |a_ij| <= tol.
const int * ReadJ(bool on_dev=true) const
int MaxRowSize() const
Returns the maximum number of elements among all rows.
real_t _Get_(const int col) const
Read the value of an entry in the "current row". See SetColPtr().
void AddMult(const Vector &x, Vector &y, const real_t a=1.0) const override
y += A * x (default) or y += a * A * x
virtual ~SparseMatrix()
Destroys sparse matrix.
SparseMatrix & operator=(const SparseMatrix &rhs)
Assignment operator: deep copy.
void Mult(const Vector &x, Vector &y) const override
Matrix vector multiplication.
void OverrideSize(int height_, int width_)
Sets the height and width of the matrix.
real_t * HostWriteData()
static cusparseHandle_t handle
void EliminateRow(int row, const real_t sol, Vector &rhs)
Eliminates a column from the transpose matrix.
void GetBlocks(Array2D< SparseMatrix * > &blocks) const
SparseMatrix & operator+=(const SparseMatrix &B)
Add the sparse matrix 'B' to '*this'. This operation will cause an error if '*this' is finalized and ...
void Clear()
Clear the contents of the SparseMatrix.
void ResetTranspose() const
void EliminateRowColDiag(int rc, real_t value)
Perform elimination and set the diagonal entry to the given value.
real_t * GetRowEntries(const int row)
Return a pointer to the entries in a row.
void ScaleRows(const Vector &sl)
this = diag(sl) * this;
void SetRow(const int row, const Array< int > &cols, const Vector &srow)
void DiagScale(const Vector &b, Vector &x, real_t sc=1.0, bool use_abs_diag=false) const
x = sc b / A_ii. When use_abs_diag = true, |A_ii| is used.
void AbsMultTranspose(const Vector &x, Vector &y) const override
y = |At| * x, using entry-wise absolute values of the transpose of matrix A
real_t * GetData()
Return the element data, i.e. the array A.
void SetSubMatrixTranspose(const Array< int > &rows, const Array< int > &cols, const DenseMatrix &subm, int skip_zeros=1)
void BuildTranspose() const
Build and store internally the transpose of this matrix which will be used in the methods AddMultTran...
void SortColumnIndices()
Sort the column indices corresponding to each row.
void Finalize(int skip_zeros=1) override
Finalize the matrix initialization, switching the storage format from LIL to CSR.
real_t IsSymmetric() const
Returns max_{i,j} |(i,j)-(j,i)| for a finalized matrix.
DenseMatrix * ToDenseMatrix() const
Produces a DenseMatrix from a SparseMatrix.
void _Set_(const int col, const real_t a)
Set an entry in the "current row". See SetColPtr().
void EliminateZeroRows(const real_t threshold=1e-12) override
If a row contains only zeros, set its diagonal to 1.
void EliminateCols(const Array< int > &cols, const Vector *x=NULL, Vector *b=NULL)
Eliminate all columns i for which cols[i] != 0.
cusparseDnVecDescr_t vecX_descr
const real_t * ReadData(bool on_dev=true) const
void ScaleRow(const int row, const real_t scale)
void AddRow(const int row, const Array< int > &cols, const Vector &srow)
void Jacobi2(const Vector &b, const Vector &x0, Vector &x1, real_t sc=1.0) const
static bool use_gpu_vendor_sparse_if_available
Use the GPU vendor sparse library (cusparse/hipsparse), if available, for sparse matrix operations....
Definition sparsemat.hpp:58
int ActualWidth() const
Returns the actual Width of the matrix.
const int * HostReadI() const
void Set(const int i, const int j, const real_t val)
Vector data type.
Definition vector.hpp:82
virtual const real_t * HostRead() const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), false).
Definition vector.hpp:524
virtual const real_t * Read(bool on_dev=true) const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:520
void Neg()
(*this) = -(*this)
Definition vector.cpp:376
virtual real_t * ReadWrite(bool on_dev=true)
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:536
int Size() const
Returns the size of the vector.
Definition vector.hpp:234
virtual void UseDevice(bool use_dev) const
Enable execution of Vector operations using the mfem::Device.
Definition vector.hpp:145
void SetSize(int s)
Resize the vector to size s.
Definition vector.hpp:633
void NewDataAndSize(real_t *d, int s)
Set the Vector data and size, deleting the old data, if owned.
Definition vector.hpp:197
real_t * GetData() const
Return a pointer to the beginning of the Vector data.
Definition vector.hpp:243
virtual real_t * HostReadWrite()
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), false).
Definition vector.hpp:540
virtual real_t * Write(bool on_dev=true)
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:528
const real_t alpha
Definition ex15.cpp:369
real_t b
Definition lissajous.cpp:42
real_t a
Definition lissajous.cpp:41
DenseMatrix * OuterProduct(const DenseMatrix &A, const DenseMatrix &B)
Produces a block matrix with blocks A_{ij}*B.
const T * Read(const Memory< T > &mem, int size, bool on_dev=true)
Get a pointer for read access to mem with the mfem::Device's DeviceMemoryClass, if on_dev = true,...
Definition device.hpp:369
void mfem_error(const char *msg)
Definition error.cpp:154
void Mult(const Table &A, const Table &B, Table &C)
C = A * B (as boolean matrices)
Definition table.cpp:505
const T * HostRead(const Memory< T > &mem, int size)
Shortcut to Read(const Memory<T> &mem, int size, false)
Definition device.hpp:376
T * Write(Memory< T > &mem, int size, bool on_dev=true)
Get a pointer for write access to mem with the mfem::Device's DeviceMemoryClass, if on_dev = true,...
Definition device.hpp:386
bool IsFinite(const real_t &val)
Definition vector.hpp:602
void Transpose(const Table &A, Table &At, int ncols_A_)
Transpose a Table.
Definition table.cpp:443
void RAP(const DenseMatrix &A, const DenseMatrix &P, DenseMatrix &RAP)
void Swap(T &a, T &b)
Swap objects of type T. The operation is performed using the most specialized swap function from the ...
Definition array.hpp:767
int CheckFinite(const real_t *v, const int n)
Definition vector.hpp:613
T * HostWrite(Memory< T > &mem, int size)
Shortcut to Write(const Memory<T> &mem, int size, false)
Definition device.hpp:393
SparseMatrix * MultAbstractSparseMatrix(const AbstractSparseMatrix &A, const AbstractSparseMatrix &B)
Matrix product of sparse matrices. A and B do not need to be CSR matrices.
SparseMatrix * TransposeAbstractSparseMatrix(const AbstractSparseMatrix &A, int useActualWidth)
Transpose of a sparse matrix. A does not need to be a CSR matrix.
SparseMatrix * Mult_AtDA(const SparseMatrix &A, const Vector &D, SparseMatrix *OAtDA)
Matrix multiplication A^t D A. All matrices must be finalized.
float real_t
Definition config.hpp:46
MemoryType
Memory types supported by MFEM.
std::function< real_t(const Vector &)> f(real_t mass_coeff)
Definition lor_mms.hpp:30
SparseMatrix * TransposeMult(const SparseMatrix &A, const SparseMatrix &B)
C = A^T B.
void forall(int N, lambda &&body)
Definition forall.hpp:1134
constexpr real_t infinity()
Define a shortcut for std::numeric_limits<double>::infinity()
Definition vector.hpp:47
void forall_switch(bool use_dev, int N, lambda &&body)
Definition forall.hpp:1214
void Add(const DenseMatrix &A, const DenseMatrix &B, real_t alpha, DenseMatrix &C)
C = A + alpha*B.
void SparseMatrixFunction(SparseMatrix &S, real_t(*f)(real_t))
Applies f() to each element of the matrix (after it is finalized).
STL namespace.
real_t sol(const Vector &x)
MFEM_HOST_DEVICE real_t norm(const Complex &z)
@ HIP_MASK
Biwise-OR of all HIP backends.
Definition device.hpp:98
@ CPU_MASK
Biwise-OR of all CPU backends.
Definition device.hpp:94
@ CUDA_MASK
Biwise-OR of all CUDA backends.
Definition device.hpp:96