Ask Your Question
1

How can I override the __call__ method of a matrix

asked 2016-08-02 12:16:44 -0500

Saul Schleimer gravatar image

updated 2016-08-05 13:55:04 -0500

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 flag offensive close merge delete

Comments

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
vdelecroix gravatar imagevdelecroix ( 2016-08-04 09:08:25 -0500 )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...

Saul Schleimer gravatar imageSaul Schleimer ( 2016-08-05 12:48:34 -0500 )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.

vdelecroix gravatar imagevdelecroix ( 2016-08-08 14:13:33 -0500 )edit

2 answers

Sort by ยป oldest newest most voted
1

answered 2016-08-02 21:38:15 -0500

nbruin gravatar image

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.

edit flag offensive delete link more

Comments

Neat - thanks for this.

Saul Schleimer gravatar imageSaul Schleimer ( 2016-08-03 16:21:31 -0500 )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.

Saul Schleimer gravatar imageSaul Schleimer ( 2016-08-05 14:03:59 -0500 )edit
0

answered 2016-08-02 13:14:11 -0500

updated 2016-08-02 17:55:23 -0500

[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.

edit flag offensive delete link more

Comments

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?

Saul Schleimer gravatar imageSaul Schleimer ( 2016-08-02 13:35:48 -0500 )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.

vdelecroix gravatar imagevdelecroix ( 2016-08-02 17:49:15 -0500 )edit

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

vdelecroix gravatar imagevdelecroix ( 2016-08-02 17:56:02 -0500 )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.

Saul Schleimer gravatar imageSaul Schleimer ( 2016-08-03 16:25:40 -0500 )edit

Your Answer

Please start posting anonymously - your entry will be published after you log in or create a new account.

Add Answer

Question Tools

Stats

Asked: 2016-08-02 12:16:44 -0500

Seen: 90 times

Last updated: Aug 05 '16