# How can I override the __call__ method of a matrix

I'd like to override the __call__ method of a matrix so that M(v) returns M*v. That is, I'd like to use functional notation. So I tried the following:

class MyMatrix(Matrix):
def __call__(self, v):
return self*v


but this doesn't work. Here is the error message I get when I try the above code in sage:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-88-e8d789594dd8> in <module>()
----> 1 class MyMatrix(Matrix):
2             def __call__(self, v):
3                     return self*v
4

TypeError: Error when calling the metaclass bases
object() takes no parameters


I don't know anything about metaclasses. After banging my head against various walls, trying to learn, I am asking here.

edit retag close merge delete

The problem above comes from the fact that Matrix is not a class.

sage: isinstance(Matrix, type)
False


versus

sage: isinstance(Integer, type)
True


You need to inherit from a matrix class for example

sage: class MyMatrix(sage.matrix.matrix_integer_dense.Matrix_integer_dense):
....:     def __call__(self, v):
....:         return self*v

( 2016-08-04 16:08:25 +0200 )edit

Ok, I understand that Matrix is a metaclass, not a class. But I really want the functionality of Matrix, not some special case. In my particular usecase I don't know how the matrix will be given. I just know that the __init__ that comes with Matrix can handle the input. For example - what if the input has entries in some number field? Those are not integers...

( 2016-08-05 19:48:34 +0200 )edit

Matrix is not even a metaclass, it is a class factory ;-) You can use the generic class but still the concrete matrix class constructor (i.e. __init__) does not handle the coefficient as you might think of. This is mostly done within the class factory Matrix.

( 2016-08-08 21:13:33 +0200 )edit

Sort by » oldest newest most voted

There already is a way of constructing an object from a matrix that is callable and gives as a result a matrix-vector product:

sage: V=VectorSpace(QQ,2)
sage: M=matrix(QQ,2,2,[1,2,3,4])
sage: h=V.hom(M)
sage: h(V.0)
(1, 2)
sage: h(V.1)
(3, 4)


Note that vector spaces in sage are row vector spaces for most purposes, so you're getting the result of multiplying the vector to the right by the matrix. Judicious use of transposes should get you what you want.

more

Neat - thanks for this.

( 2016-08-03 23:21:31 +0200 )edit

Ok, after thinking about it, I realise that this isn't quite what I want. The whole point of using Matrix is I don't have to commit to (or even think about) the base field or ring - the initialisation procedure for Matrix takes care of that for me.

( 2016-08-05 21:03:59 +0200 )edit

[EDIT] First of all there is a stupid reason for that. The method call is implemented as a componentwise call as in

sage: m = matrix(2, [cos(x), sin(x), -sin(x), cos(x)])
sage: m(pi/4)
<A TON OF WARNINGS>
[ 1/2*sqrt(2)  1/2*sqrt(2)]
[-1/2*sqrt(2)  1/2*sqrt(2)]


The above behavior is incompatible with having m(v) returning m*v for vectors v (because you might have functions that accept vectors as input). Whether the current behavior is desirable I do not know.

[ORIGINAL ANSWER] Then, I do not think it is easy without modifying Sage source code.

One problem is that most matrix classes are so called "extension class" with read-only attributes

sage: m = matrix([2])
sage: m.new_attr = 3
Traceback (most recent call last):
...
AttributeError: 'sage.matrix.matrix_integer_dense.Matrix_integer_dense' object has no attribute 'new_attr'


To be compared with

sage: p = Permutation([3,2,1])
sage: p.new_attr = 3
sage: print p.new_attr
3


You would have been able to do it on permutations via

sage: p = Permutation([3,2,1])
sage: p.__class__.new_method = lambda s: 1
sage: p.new_method()  # it works!
1
sage: q = Permutation([4,3,2,1])
sage: q.new_method()  # it also works!
1


But this approach would also fail for matrices since there are many classes of matrices.

more

Yes, I see that. But there should be a way to define a new class (or new metaclass) that inherits from matrix and does what I want?

( 2016-08-02 20:35:48 +0200 )edit

yes but then you need to figure out how to actually get an instance of your new class! It is not clear what the constructor (i.e. the __init__ method) of the matrix class takes as argument. You have to look into the source code to figure it out. And there is no guarantee that it will work in the future.

( 2016-08-03 00:49:15 +0200 )edit

I edited my post to mention that there is already a call implemented (incompatible with what you propose)

( 2016-08-03 00:56:02 +0200 )edit

Ok, I see that perhaps I can't use the same __init__ method, as I want a MyMatrix, not a Matrix. But we haven't gotten there yet - my toy example throws an error on the first line, not when I try to use it. I'll add the error message to the post.

Regarding the call that is already implemented - it is exactly that method which I wish to override.

( 2016-08-03 23:25:40 +0200 )edit