PyMFEM icon indicating copy to clipboard operation
PyMFEM copied to clipboard

Manually setting matrix entries

Open mkofler96 opened this issue 9 months ago • 3 comments

Hello,

I was wondering if it is possible to manually set entries of the matrix in the linear system.

If I look at the example given at the main page, I would like to manually change the sparse matrix AA.

# Form the linear system of equations (AX=B)
A = mfem.OperatorPtr()
B = mfem.Vector()
X = mfem.Vector()
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B)
print("Size of linear system: " + str(A.Height()))

# Solve the linear system using PCG and store the solution in x
AA = mfem.OperatorHandle2SparseMatrix(A)

# works
AA.Set(0, 0, 1)

# does not work
AA.Set(0, 10, 1)

While changinging the entry (0,0) works, because it is already present, adding a new entry at (0,10) gives the following error:

MFEM abort: Could not find entry for row = 0, col = 10
 ... in function: mfem::real_t& mfem::SparseMatrix::SearchRow(int, int)
 ... in file: /__w/PyMFEM/PyMFEM/PyMFEM/external/mfem/linalg/sparsemat.hpp:930

Traceback (most recent call last):
  File "/usr2/mkofler/PyMFEM/examplefrom_git.py", line 43, in <module>
    AA.Set(0, 10, 1)
RuntimeError: PyMFEM error (mfem::ErrorException): MFEM abort: Could not find entry for row = 0, col = 10
 ... in function: mfem::real_t& mfem::SparseMatrix::SearchRow(int, int)
 ... in file: /__w/PyMFEM/PyMFEM/PyMFEM/external/mfem/linalg/sparsemat.hpp:930

Is there another possibility of how I could do this? I was also looking for a way to convert the sparse matrix to a dense matrix and changing the entries there, but I have not found a way of doing this.

I would be happy if someone could help me out here and thanks for the great work.

mkofler96 avatar Mar 04 '25 08:03 mkofler96

Hi @mkofler96 I guess that the error indicates that you are trying to set the matrix element which is not occupied (non-zero). A sparsematrix in MFEM is a CSR matrix and it is expensive to change the matrix filling pattern.

My favorite recipe in this situation is to convert the MFEM matrix to scipy's sparse matrix, where you can manipulate more efficiently using other data format ( including the conversion to full dense matrix.) Then, I put it back to MFEM SparseMatrix.

Converting it to Scipy sparse matrix can be done like

###  this is found in mfem.common.sparse_utils
def sparsemat_to_scipycsr(mat, dtype):
     w, h = mat.Width(), mat.Height()
     I = mat.GetIArray()
     J = mat.GetJArray()
     data = mat.GetDataArray()
     mat.LoseData()
     m1 =csr_matrix((data, J, I), shape = (h, w),
                    dtype = dtype)
     return m1

An example of creating SparseMatrix from scratch is

def run_test():
    print("Test sparsemat module")
    indptr = np.array([0, 2, 3, 6], dtype=np.int32)
    indices = np.array([0, 2, 2, 0, 1, 2], dtype=np.int32)
    data = np.array([1, 2, 3, 4, 5, 6], dtype=float)
    
    mat = mfem.SparseMatrix([indptr, indices, data, 3,3])
    mat.Print()
    mat.PrintInfo()

@v-dobrev, @tzanio : if there is a different way, which can be done by just calling MFEM's API, please let us know.

sshiraiwa avatar Mar 06 '25 17:03 sshiraiwa

Thank you @sshiraiwa vey much for the quick answer, this was exactly what I was looking for.

I also found that there is a method ToDenseMatrix on the mfem._ser.sparsemat.SparseMatrix class. But of course working directly on sparse matrices is way better.

mkofler96 avatar Mar 07 '25 06:03 mkofler96

The reason you can't add entries in the SparseMatrix is that it is finalized in FormLinearSystem.

The code for FormLinearSystem in the serial conforming case (no AMR) and no Hybridization or static condensation is essentially

void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
                                    Vector &b, OperatorHandle &A, Vector &X,
                                    Vector &B, int copy_interior)
{
   EliminateVDofs(ess_tdof_list, diag_policy);
   Finalize(0);
   A.Reset(mat, false);
   EliminateVDofsInRHS(ess_tdof_list, x, b);
   X.MakeRef(x, 0, x.Size());
   B.MakeRef(b, 0, b.Size());
   if (!copy_interior) { X.SetSubVectorComplement(ess_tdof_list, 0.0); }
}

you can modify the entries in mat any time before the Finalize call

tzanio avatar Mar 10 '25 01:03 tzanio