Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

Hello, @Alain Ngalani! Your problem comes from the fact that matrices (as defined by Sage) are mutable objects. The issue of mutable vs. immutable objects is an eternal struggle for us, Python/Sage programmers. Alas, it is a necessary one. Let me explain with the following "classic" example code:

A = matrix(ZZ, [1, 2, 3])
B = A
B[0,0] = 1000

Conventional wisdom suggests that after this code has been executed, A == [1, 2, 3] and B == [1000, 2, 3] hold True. That would be the case if matrices were immutable objects. However, that is not the case, and you will actually see that A == [1000, 2, 3] and B == [1000, 2, 3] are True, i.e, modifying the matrix B also modifies the matrix A. (Warning: here comes a slightly technical explanation!) The reason for this is that the instruction A = matrix(ZZ, [1, 2, 3]) first creates an object in memory (the matrix itself), and the assigns a reference to that object to the variable A. In naive terms, A is not the matrix, but a representation of its direction in computer memory (a reference to the object.) When you do B = A you are copying what A is into B, that is, you are copying the direction on memory, not the matrix (this is called aliasing). Now, both A and B reference the same object (basically, they are different names for the same matrix, they are alias for the same matrix.) Finally, when you execute B[0,0] = 1000 you are telling Python/Sage "go to the matrix referenced by B and change its first entry to 1000." Since A references the same object, this process also "alters" A.

A problem like this is only possible with mutable objects. Have matrices been immutable, then the instruction B[0,0] = 1000 would have forced Python/Sage to create a new object with the value ¨[1000, 2, 3]¨. Indeed, because immutable objects are not allowed to be altered, they need to be created and re-created every time you make a change on them. (This is time an resource consuming, and that is why there are also mutable objects, which are less intensive on your computer.) As an example, since tuples are immutable, we have this code:

A = (1, 2, 3)
B = A
B += (4,)

This creates a tuple in memory and makes A reference to it. Now, B = A makes an aliasing, so that A and B are (for now) references to the same object. When you run B += (4,), you are asking the element 4 to be added to B. Since B is immutable, that is not possible, so a new object is created with the additional element. So, after these instructions execute, you have that A == (1, 2, 3) and B = (1, 2, 3, 4) are True.

Now, concerning your code:

T = [] 
b = random_matrix(GF(8), 1, 3)
for j in GF(8):  
    b[0, 0] = j 
    T.append(b) 
    show(T)

notice that b is defined outside of the loop, so it is created only once. When you do T.append(b) inside the loop, you are not appending the value of b, you are literally appending b, which as we have seen, is a reference to an object in memory. When this loop ends, your list T is filled with $8$ (the cardinality of GF(8)) references to only one abject, namely the one you created outside the loop. If you use any of these references to alter the value of that object, then that change will be reflected for the other alias of that object. That is why the instruction b[0, 0] = j alters all the elements in your list T: because they point to the same MUTABLE object (intuitively, they are the same object.)

The solution is to put the line b = random_matrix(GF(8), 1, 3) inside your loop so that every iteration creates a completely new object and assigns to b a reference to it, thus making sure that b references a different object every time. That way, altering one object in T won't change the other elements.

T = [] 
for j in GF(8):
    b = random_matrix(GF(8), 1, 3)
    b[0, 0] = j 
    T.append(b) 
    show(T)

Hope this helps! Sorry for the long answer!