# Making a dictionary of matrices

I am trying to make a dictionary of matrices. Ideally

MatDic={
matrix(GF(2),2,[1,0,0,1]):'a',
matrix(GF(2),2,[1,1,0,1]):'b'
# etc
}


But Sage refuses to hash (mutable) matrices. I then tried

MatDic={
matrix(GF(2),2,[1,0,0,1]).set_immutable():'a',
matrix(GF(2),2,[1,1,0,1]).set_immutable():'b'
}
MatDic


which gives

{None: 'b'}


I also tried making a dictionary of tuples

MatDic={
(1,0,0,1):'a',
(1,1,0,1):'b'
}


which raises no errors; However

MatDic[tuple(matrix(GF(2),2,[1,0,0,1]))]


gives

TypeError: mutable vectors are unhashable


What is the best, if possible, way to make a dictionary and lookup matrices?

edit retag close merge delete

Why do we need to force the situation in a place where python insists to have hashable objects (...the keys)? A good possibility is to use the information from the entries as a tuple instead, e.g.

F = GF(2)
dic = { (F(1), F(0), F(0), F(1)) : '1001' ,
(F(1), F(1), F(0), F(1)) : '1101' , }


and the keys are easily transformed to matrices, for instance:

sage: for tup in dic:
....:     m = matrix( 2,2, tup )
....:     print tup, '->', dic[tup], 'with matrix\n', m
....:
(1, 1, 0, 1) -> 1101 with matrix
[1 1]
[0 1]
(1, 0, 0, 1) -> 1001 with matrix
[1 0]
[0 1]


Here, for the last matrix:

sage: m.parent()
Full MatrixSpace of 2 by 2 dense matrices over Finite Field of size 2


@dan_fulea Well I expect the dictionary to record not only entries but also the width and height. If I come up with a method that turns a matrix into a hashable matrix then I am reinventing .set_immutable(). So maybe the real question is why don't sage/python automatically make a hashable copy and then hash it.

Sort by » oldest newest most voted

Dictionaries can only be built with hashable objects. You get the same trouble with

sage: {[1,2]: 0, : 5}
Traceback (most recent call last):
...
TypeError: unhashable type: 'list'


Though, using lists as values is fine

sage: {1: [0,3], 'hello': [1,2]}
{1: [0, 3], 'hello': [1, 2]}


In order to use matrices or vectors as keys of a dictionary you need to set them immutable first.

sage: m1 = matrix(ZZ, 2, [1, 2, 3, 4])
sage: m2 = matrix(ZZ, 2, [1, 0, 1, 1])
sage: {m1: 0, m2: 1}
Traceback (most recent call last):
...
TypeError: unhashable type: 'list'
sage: m1.set_immutable()
sage: m2.set_immutable()
sage: {m1: 0, m2: 1}
{[1 0]
[1 1]: 1,
[1 2]
[3 4]: 0}


Note that the method set_mutable does not return anything but modifies the matrix itself.

more

I guess my bottleneck is that I expect set_mutable to return the object itself (like sorted()). By the way what is the best way to lookup the type of methods?

There is no type for methods in Python. The only thing that you have access to is their documentation and code

sage: m = matrix()
sage: m.set_immutable?
Docstring:
Call this function to set the matrix as immutable.

Matrices are always mutable by default, i.e., you can change their
entries using "A[i,j] = x". However, mutable matrices aren't
hashable, so can't be used as keys in dictionaries, etc. Also,
often when implementing a class, you might compute a matrix
associated to it, e.g., the matrix of a Hecke operator.

...


As a convention (for Sage and Python in general) a method or a function like sorted is expected to return an object while sort is expected to modify it.

I have sometimes found the need to create immutable matrices inline as well. A utility function like

def immutable(A):
B=copy(A)
B.set_immutable()
return B


does the trick. You could of course skip the "B=copy(A)". However, then your function has a side-effect of making its argument immutable as well, which might be unexpected.

I have considered if the "matrix" constructor would need a "immutable=True" optional parameter, but I think this would be less useful than you'd think:

sage: A=matrix(2,2,[1,2,3,4])
sage: B=immutable(A)
sage: (B*B).is_immutable()
False


It's convenient for things like:

sage: len({immutable(a*a) for a in MatrixSpace(GF(2),2,2)})
10


Do we need it in the global namespace?

1

@nbruin: note that for graphs there is G.copy(immutable=True) that I like.

It might be nice if set_immutable() returned self so that it could be used in the manner the OP attempted.