# Trouble creating a set of vectors

I'm writing a script to compute the numbers of vectors with a given property.

I would like it to work is as the following does, so adding a vector to the set at every cycle

T = []
for j in GF(8):
c = (j)
T.append(j)
print(T)


That gives the following output

[0]
[0, z3]
[0, z3, z3^2]
[0, z3, z3^2, z3 + 1]
[0, z3, z3^2, z3 + 1, z3^2 + z3]
[0, z3, z3^2, z3 + 1, z3^2 + z3, z3^2 + z3 + 1]
[0, z3, z3^2, z3 + 1, z3^2 + z3, z3^2 + z3 + 1, z3^2 + 1]
[0, z3, z3^2, z3 + 1, z3^2 + z3, z3^2 + z3 + 1, z3^2 + 1, 1]


(the only purposeof the print is to show how I want my set to be built).

However the actual code does this:

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

[[     0   z3^2   z3^2 z3 + 1]]
[[    z3   z3^2   z3^2 z3 + 1], [    z3   z3^2   z3^2 z3 + 1]]
[[  z3^2   z3^2   z3^2 z3 + 1], [  z3^2   z3^2   z3^2 z3 + 1], [  z3^2   z3^2   z3^2 z3 + 1]]
[[z3 + 1   z3^2   z3^2 z3 + 1], [z3 + 1   z3^2   z3^2 z3 + 1], [z3 + 1   z3^2   z3^2 z3 + 1], [z3 + 1   z3^2   z3^2 z3 + 1]]
[[z3^2 + z3      z3^2      z3^2    z3 + 1], [z3^2 + z3      z3^2      z3^2    z3 + 1], [z3^2 + z3      z3^2      z3^2    z3 + 1], [z3^2 + z3      z3^2      z3^2    z3 + 1], [z3^2 + z3      z3^2      z3^2    z3 + 1]]
[[z3^2 + z3 + 1          z3^2          z3^2        z3 + 1], [z3^2 + z3 + 1          z3^2          z3^2        z3 + 1], [z3^2 + z3 + 1          z3^2          z3^2        z3 + 1], [z3^2 + z3 + 1          z3^2          z3^2        z3 + 1], [z3^2 + z3 + 1          z3^2          z3^2        z3 + 1], [z3^2 + z3 + 1          z3^2          z3^2        z3 + 1]]
[[z3^2 + 1     z3^2     z3^2   z3 + 1], [z3^2 + 1     z3^2     z3^2   z3 + 1], [z3^2 + 1     z3^2     z3^2   z3 + 1], [z3^2 + 1     z3^2     z3^2   z3 + 1], [z3^2 + 1     z3^2     z3^2   z3 + 1], [z3^2 + 1     z3^2     z3^2   z3 + 1], [z3^2 + 1     z3^2     z3^2   z3 + 1]]
[[     1   z3^2   z3^2 z3 + 1], [     1   z3^2   z3^2 z3 + 1], [     1   z3^2   z3^2 z3 + 1], [     1   z3^2   z3^2 z3 + 1], [     1   z3^2   z3^2 z3 + 1], [     1   z3^2   z3^2 z3 + 1], [     1   z3^2   z3^2 z3 + 1], [     1   z3^2   z3^2 z3 + 1]]


So instead of adding the new vector it turns the set into a copy of several vectors all equal to the new one.

What's the problem in my code? And how can I solve it?

edit retag close merge delete

1

Hello, @Alain Ngalani! I don't seem to have this problem. Check this code. However, I am not 100% sure, because your code is wrongly indented.

By the way, here are a few details you might be interest on. There is also a random_vector() function which seems to better fit your code.

( 2021-02-16 03:49:03 +0200 )edit
1

I should point out that your code is not recursive (in the sense of computer science), nor is it a method (in the sense of computer science again). Perhaps the question should be edited to reflect more properly the situation. Perhaps simply write "why my code to define a set of vectors doesn't work?"

( 2021-02-16 04:02:48 +0200 )edit

Indeed your code works, I made an error writing my wrong code. It should be this that as you see doesn't work well

( 2021-02-16 12:21:26 +0200 )edit

I see... I have tried modify your original question so that it reflects that fact. Unfortunately, Ask SageMath won't let me, for some unknown reason (I just get an "oops, something went wrong" message.) Would you kind enough to make the change yourself? This question you pose is a very common issue, so somebody in the future will have the same problem, and can refer to your question and the answer I added.

I hope this helps you clarify your doubts. Hopefully, it won't add new ones.

( 2021-02-16 19:43:12 +0200 )edit

Sort by » oldest newest most voted

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.

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!

more

1

If keeping creation of b outside the loop was for a reason (i.e. if various modifications of b should differ in just one element), then the following code will do the job:

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

( 2021-02-16 20:02:15 +0200 )edit

This was indeed the problem, and this solutions looks better then mine (creating a new random vecotr inside and then modify every component of it to be equal to either the ones of b or anoter one).

( 2021-02-17 22:07:46 +0200 )edit