# Updating attributes within a class

• Python

## Summary:

I'm creating a sparse matrix class and am stuck on how to update the attributes within the class.

## Main Question or Discussion Point

I'm working on a sparse matrix class, where I'm creating an internal representation of a random matrix a la CSR (Sparse Matrix). 'A' signifies the non-zero elements in the matrix, 'IA' are the rowpointers and 'JA' the column indices of the non-zero elements.
Python:
import numpy as np
class SparseMatrix:
def __init__(self,Matrix):
if not isinstance(Matrix,np.ndarray):
raise TypeError('Matrix should be of type np.ndarray')
self._matrix=Matrix
#setting up an internal representation of CSR, A and JA
self._A=self._matrix[self._matrix!=0]
self._JA=np.nonzero(self._matrix)
#IA
rowpointer=
for i in range(self._matrix.shape):
indices=len(np.nonzero(self._matrix[i]))
rowpointer.append(indices+rowpointer[i])
self._IA=np.asarray(rowpointer)
# more attributes
self._number_of_nonzero=len(np.nonzero(self._matrix))
self.intern_represent='CSR'
def __repr__(self):
return 'A={}\nIA={}\nJA={}'.format(self._A,self._IA,self._JA)
When I change a value in the input matrix, this is registered and updated in the attribute self._matrix. But neither A, IA, or JA are.
Python:
d=np.array([[0,0,0,0],
[5,8,0,0],
[0,0,3,0],
[0,6,0,0]])
f=SparseMatrix(d)
f._matrix[0,0]=9 #change first element of matrix to 9
print(f._matrix)
print(f)
Code:
array([[9,0,0,0],
[5,8,0,0],
[0,0,3,0],
[0,6,0,0]])
A=[5 8 3 6]
IA=[0 0 2 3 4]
JA=[0 1 2 1]
4
A should be equal to [9 5 8 3 6] and _number_of_nonzero should be equal to 5. IA and JA should then also change. How can I update self._A, self._IA and self._JA? I've been experimenting with setter and getter methods, but without success. Furthermore, I'm confused why self._matrix updates automatically.

Related Programming and Computer Science News on Phys.org
PeterDonis
Mentor
2019 Award
How can I update self._A, self._IA and self._JA?
You would have to recalculate them every time the matrix changes. But you don't have any way of automatically catching when the matrix changes. See below.

I'm confused why self._matrix updates automatically.
It doesn't "update"; the variable self._matrix refers to the same object as the Matrix that you pass to the class constructor. Since it's a reference to the same object, looking at its attributes will show you the same attributes, including any attributes that get updated. But references to objects don't get notified when the objects change, unless you explicitly set up some way for that to happen, so your sparse matrix class won't know when to look for updated attributes to use in recalculating self._A, self._IA, and self._JA.

If the only times that the matrix ever changes is when you change it in your own code, then you could just add methods to your sparse matrix class that do the changes to everything: in other words, instead of changing Matrix directly, you would call a method on your sparse matrix class instance that updates self._matrix (which will change the matrix that you passed to the class constructor) and then updates self._A, self._IA, and self._JA.

If there is other code you don't control that can update the matrix, then you won't have any way of knowing when the matrix gets updated. The only way I could see to solve your problem in that case would be to subclass the matrix class and override the update methods; but then you would have to have a way of telling the other code that you don't control to use your subclass instead of the usual matrix class.

• schniefen
If the only times that the matrix ever changes is when you change it in your own code, then you could just add methods to your sparse matrix class that do the changes to everything: in other words, instead of changing Matrix directly, you would call a method on your sparse matrix class instance that updates self._matrix (which will change the matrix that you passed to the class constructor) and then updates self._A, self._IA, and self._JA.
Thanks for a clarifying answer. How would I go about implementing this? I suspect this is what I was after in the first place, i.e. call on a method in the class that changes the value in the matrix for me.
Python:
f._matrix[0,0]=9 # instead of this
f._matrix_change([0,0],9) #maybe something like this?

PeterDonis
Mentor
2019 Award
I suspect this is what I was after in the first place, i.e. call on a method in the class that changes the value in the matrix for me.
Yes, that's the sort of thing you need to do; the code in the method _matrix_change would update the matrix and also recalculate the other attributes.

• schniefen
Is there any clever way of shortening the recalculations of self._A, self._IA and self._JA when changing the value of the matrix? I've defined my method 'change', but I currently need to repeat the calculations from the __init__ method.

Python:
import numpy as np
class SparseMatrix:
def __init__(self,A):
if not isinstance(A,np.ndarray):
raise TypeError('Matrix should be of type np.ndarray')
self._matrix=A
self._A=self._matrix[self._matrix!=0]
self._JA=np.nonzero(self._matrix)
rp=
for i in range(self._matrix.shape):
indices=len(np.nonzero(self._matrix[i]))
rp.append(indices+rp[i])
self._IA=np.asarray(rp)
self.intern_represent='CSR'
self.number_of_nonzero=len(np.nonzero(self._matrix))
def change(self,i,j,val):
self._matrix[i,j]=val
self._A=self._matrix[self._matrix!=0] # this feels redundant, but probably necessary
self._JA=np.nonzero(self._matrix)
rp=
for i in range(self._matrix.shape):
indices=len(np.nonzero(self._matrix[i]))
rp.append(indices+rp[i])
self._IA=np.asarray(rp)
self.number_of_nonzero=len(np.nonzero(self._matrix))
def __repr__(self):
return 'A={}\nIA={}\nJA={}'.format(self._A,self._IA,self._JA)
I'd also like to add a method toCSC where I want to convert my matrix to CSC format. Mathematically this is only CSR of the transpose of the input matrix. However, I'm facing a bit of an odd problem with the intern_represent attribute, which I'd like to change when this method is activated.

Python:
class SparseMatrix:
...

...
def toCSC(self):
m=np.transpose(self._matrix)
self._matrix=m
self._A=self._matrix[self._matrix!=0]
self._JA=np.nonzero(self._matrix)
rp=
for i in range(self._matrix.shape):
indices=len(np.nonzero(self._matrix[i]))
rp.append(indices+rp[i])
self._IA=np.asarray(rp)
self.intern_represent='CSC'
self.number_of_nonzero=len(np.nonzero(self._matrix))
return self
def __repr__(self):
return 'A={}\nIA={}\nJA={}'.format(self._A,self._IA,self._JA)
r=SparseMatrix(d) #d is some matrix
s=r.toCSC()
print(r.intern_represent)
print(s.intern_represent)
Code:
CSC
CSC
Whereas...
Python:
r=SparseMatrix(d)
print(r.intern_represent)
s=r.toCSC()
print(s.intern_represent)
Code:
CSR
CSC
It seems like I'm changing the instance r as I'm creating s. Can this be avoided?

PeterDonis
Mentor
2019 Award
I've defined my method 'change', but I currently need to repeat the calculations from the __init__ method.
Those calculations are already pretty short, so I don't see any urgent need to make them shorter. Also, doing them the same way every time makes the code easier to understand.

It seems like I'm changing the instance r as I'm creating s.
No, you're returning the same instance r as instance s. Your toCSC method returns self; that means it returns the same instance you called it on.

If you want to avoid that, you need to construct a new SparseMatrix instance in the toCSC method and return it.

• schniefen
I'm facing another issue when writing the __mul__ method, where I aim to define multiplication with a vector.

Python:
class SparseMatrix(SparseMatrix):

def __mul__(self,other):
if isinstance(other,np.ndarray):
x=SparseMatrix(np.dot(self._matrix,other))
return x
def __rmul__(self,other):
if isinstance(other,np.ndarray):
x=SparseMatrix(np.dot(other,self._matrix))
return x
matrix=np.array([[0,0,0,0],
[5,8,0,0],
[0,0,3,0],
[0,6,0,0]])
vector=np.array([2,2,2,2])
print(SparseMatrix(matrix)*vector)
print(vector*SparseMatrix(matrix))
And I'm getting a wrong output for the last print statement. It seems something is not working with __rmul__ method.
Code:
A=[26  6 12]
IA=[0 0 1 2 3]
JA=[0 0 0]
[None None None None]

Obviously matrix multiplication isn't possible as indicated in the second print statement, and that's probably the cause of the wrong output. But why doesn't np.dot catch this?

PeterDonis
Mentor
2019 Award
I'm not sure your __rmul__ method is even being called in the second print statement. The numpy array object already has a __mul__ method itself, so I think that's what will be called there. I think __rmul__ only gets called on the right object of a multiplication if the left object doesn't have a __mul__ method.