Numpy matrices work for general objects

Written by

in

A student was having difficulty using a class I had created to represent transfer function objects. I discovered that they were actually placing these objects in a numpy.matrix and trying to multiply things by one another. To my surprise, this actually worked! I had to investigate. The code here is all in Python.

I created a quick tracing object which just kind of logs the operations that are done on it as an expression:

class operation:
    def __init__(self, name):
        self.string = name
    def __repr__(self):
        return self.string
    def __str__(self):
        return self.string
    def __add__(self, other):
        return operation("({} + {})".format(str(self), str(other)))
    def __mul__(self, other):
        return operation("{}*{}".format(str(self), str(other)))
    def __radd__(self, other):
        return operation("({} + {})".format(str(other), str(self)))
    def __rmul__(self, other):
        return operation("{}*{}".format(str(other), str(self)))
       
Now I can do some interesting stuff:
    In []: a*b
    Out[]: a*b
    In []: a + b
    Out[]: (a + b)
Ok, but what about matrix multiplication?
import numpy as np
In []: tracer = np.matrix([[a, b, c],
[d, e, f],
[g, h, i]])

In []: tracer*tracer
Out[]:
matrix([[((a*a + b*d) + c*g), ((a*b + b*e) + c*h), ((a*c + b*f) + c*i)],
        [((d*a + e*d) + f*g), ((d*b + e*e) + f*h), ((d*c + e*f) + f*i)],
        [((g*a + h*d) + i*g), ((g*b + h*e) + i*h), ((g*c + h*f) + i*i)]], dtype=object)
That kind of blew me away! It means I can probably get away with a whole lot less code than I thought I would need to implement my own matrix-like objects.
In addition, it appears that some numpy functions will attempt to call corresponding functions in the object they act on:
    In []: np.exp(a)
---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)
 in ()
----> 1 np.exp(a)
AttributeError: exp
This error shows that np.exp is trying to access the exp attribute on this object. Let’s extend our object with an exp method like this:
    def exp(self):
        return operation("e^({})".format(self))
Now (after re-instantiating a-i from above), we have
In []: np.exp(a)
Out[]: e^(a)
This is going to be super useful.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.