I am having trouble modifying the assembled matrix.
Specifically:
I have an assembled matrix A (A = assemble(…)).
I have another matrix B (that I choose)
I want to get A + B
I have seen the functions ‘add’ and ‘add_local’ of the GenericMatrix class in the reference documents.
> add(block, num_rows, row)
> add_local(block, num_rows, row)
However, I am unfamiliar with the parameters and descriptions. For instance, I don’t know what these are: “block”, “global indices”, “local indices”
Also, should the rows be associated with the ‘dof_to_vertex_map(V)’?
If someone can shed some light on this through an example, it will be much appreciated.
If I need to edit values of an assembled matrix, I usually go through the petsc4py API. See the following example:
from dolfin import *
mesh = UnitIntervalMesh(2)
V = FunctionSpace(mesh,"Lagrange",1)
A = assemble(TrialFunction(V)*TestFunction(V)*dx)
# If by "+ B" you mean adding numbers directly to specific values of A, I
# usually do this through petsc4py:
import petsc4py, sys
petsc4py.init(sys.argv)
from petsc4py import PETSc
# To add to existing values:
ADD_MODE = PETSc.InsertMode.ADD
# To replace values instead:
#ADD_MODE = PETSc.InsertMode.INSERT
print(A.array()) # Original A
# (Note: Printed output is somewhat unclear in parallel)
# What value to add/insert at matrix entry i,j (i.e., the definition of B):
i = 0
j = 1
value = 1.0
Am = as_backend_type(A).mat()
# If the value you want to modify is not allocated as a nonzero, you need to
# set this option (with some cost to performance). Ideally, you would
# set up a matrix with the necessary space allocated, assemble into that one,
# and then edit values without this.
Am.setOption(PETSc.Mat.Option.NEW_NONZERO_ALLOCATION_ERR, False)
# In parallel, you want to make sure a given value is added only once, ideally
# on the process that owns that row of the matrix. (Can skip the check for
# ownership range in serial.)
Istart, Iend = Am.getOwnershipRange()
if(i<Iend and i>=Istart):
Am.setValue(i,j,value,addv=ADD_MODE)
Am.assemble()
print(A.array()) # A with 1.0 added to the i,j entry
A complete listing of petsc4py functions and arguments can be found here:
(You can usually figure out what all the functions/arguments mean by looking up the regular PETSc documentation, since the function names are similar.)
In the applications where I’ve used this, the limiting factor in performance was computing the values that I was adding in Python. One optimization might be to try setting many values at once with the setValues method instead of setValue. In setValues, you can pass numpy arrays of indices and values.
The PETSc documentation is decent, the petsc4py doc is almost non-existent (last I checked), so you have to translate the C code examples to Python yourself
Also, any reason why “C = A + B” does not work? I think that should create a new PETSc matrix for you, but I have not checked.
How large is your matrix? If you are creating a PETSc matrix directly from a numpy matrix then I assume you end up with a dense matrix stored on sparse Aij format (very non optimal)
If your matrix is large and sparse you should only insert the non-zero entries. I am not sure that setValues does this by default, I do not think so. If your matrix is large and dense then I do not know if PETSc is the correct choice. At least you should probably not use Aij format, try B.setType(“dense”)
Please surround your code with triple backticks ``` like this
# This is a comment and not a markdown heading
B.setType("dense")
PETSc solvers are all built for sparse matrices as far as I know. If you are working with dense matrices then LAPACK and similar libraries made for dense matrices should be used. Luckily 1000x1000 is very small, so you can just run numpy.solve which uses a dense solver (maybe LAPACK, depends on how numpy was built).