Re: [Numpy-discussion] subclassing matrix
On Jan 12, 1:36 am, Timothy Hochberg [EMAIL PROTECTED] wrote: I believe that you need to look at __array_finalize__ and __array_priority__ (and there may be one other thing as well, I can't remember; it's late). Search for __array_finalize__ and that will probably help get you started. Well sonovagun! I removed the hack. Then just by setting __array_priority__ = 20.0 in the class body, things are magically working almost as I expect. I say almost because of this custom method: def cross(self, other): Cross product of this vector and another vector return _N.cross(self, other, axis=0) That call to numpy.cross returns a numpy.ndarray. Unless I do return Vector(_N.cross(self, other, axis=0)), I get problems downstream. When is __array_finalize__ called? By adding some print traces, I can see it's called every time an array is modified in any way i.e., reshaped, transposed, etc., and also during operations like u+v, u-v, A*u. But it's not called during the call to numpy.cross. Why? Cheers, -Basilisk96 ___ Numpy-discussion mailing list Numpy-discussion@scipy.org http://projects.scipy.org/mailman/listinfo/numpy-discussion
Re: [Numpy-discussion] subclassing matrix
Basilisk96 wrote: On Jan 12, 1:36 am, Timothy Hochberg [EMAIL PROTECTED] wrote: I believe that you need to look at __array_finalize__ and __array_priority__ (and there may be one other thing as well, I can't remember; it's late). Search for __array_finalize__ and that will probably help get you started. Well sonovagun! I removed the hack. Then just by setting __array_priority__ = 20.0 in the class body, things are magically working almost as I expect. I say almost because of this custom method: def cross(self, other): Cross product of this vector and another vector return _N.cross(self, other, axis=0) That call to numpy.cross returns a numpy.ndarray. Unless I do return Vector(_N.cross(self, other, axis=0)), I get problems downstream. When is __array_finalize__ called? By adding some print traces, I can see it's called every time an array is modified in any way i.e., reshaped, transposed, etc., and also during operations like u+v, u-v, A*u. But it's not called during the call to numpy.cross. Why? Cheers, -Basilisk96 This may help. It is based on your initial script. The Vectors are considered as columns but presented as rows. This adds a complication which is not resolved. Colin W. #-- vector.py import numpy as _N import math as _M #default tolerance for equality tests TOL_EQ = 1e-6 #default format for pretty-printing Vector instances FMT_VECTOR_DEFAULT = %+.5f class Vector(_N.matrix): 2D/3D vector class that supports numpy matrix operations and more. Examples: u = Vector([1,2,3]) v = Vector('3 4 5') w = Vector([1, 2]) def __new__(cls, data=0. 0. 0., dtype=_N.float64): Subclass instance constructor. If data is not specified, a zero Vector is constructed. The constructor always returns a Vector instance. The instance gets a customizable Format attribute, which controls the printing precision. data= [1, 2, 3] ret= _N.matrix(data, dtype) ##ret = super(Vector, cls).__new__(cls, data, dtype=dtype) ###promote the instance to cls type. ##ret.__class__ = cls assert ret.size in (2, 3), 'Vector must have either two or three components' if ret.shape[0] == 1: ret = ret.T assert ret.shape == (ret.shape[0], 1), 'could not express Vector as a Mx1 matrix' if ret.shape[0] == 2: ret = _N.vstack((ret, 0.)) ret.Format = FMT_VECTOR_DEFAULT ret= _N.ndarray.__new__(cls, ret.shape, dtype, buffer=ret.data) return ret def __str__(self): fmt = getattr(self, Format, FMT_VECTOR_DEFAULT) fmt = ', '.join([fmt]*3) return ''.join([(, fmt, )]) % tuple(self.T.tolist()[0]) def __repr__(self): fmt = ', '.join(['%s']*3) return ''.join([%s([, fmt, ])]) % tuple([self.__class__.__name__] + self.T.tolist()[0]) def __mul__(self, mult): ''' self * multiplicand ''' if isinstance(mult, _N.matrix): return _N.dot(self, mult) else: raise DataError, 'multiplicand must be a Vector or a matrix' def __rmul__(self, mult): ''' multiplier * self.__mul__ ''' if isinstance(mult, _N.matrix): return Vector(_N.dot(mult, self)) else: raise DataError, 'multiplier must be a Vector or a matrix' the remaining methods are Vector-specific math operations, including the X,Y,Z properties... if __name__ == '__main__': u = Vector('1 2 3') print str(u) print repr(u) A = _N.matrix('2 0 0; 0 2 0; 0 0 2') print A p = A * u print p print p.__class__ q= u.T * A try: print q except: print we don't allow for the display of row vectors print q.A, q.T print q.__class__ ___ Numpy-discussion mailing list Numpy-discussion@scipy.org http://projects.scipy.org/mailman/listinfo/numpy-discussion
Re: [Numpy-discussion] subclassing matrix
Thanks Stefan and Colin, The subclass documentation made this a little clearer now. Instead of using a super() call in __new__, I now do this: #construct a matrix based on the input ret = _N.matrix(data, dtype=dtype) #promote it to Vector ret = ret.view(cls) The second statement there also invokes a call to __array_finalize__, which ensures that the Format attribute is attached to the instance. Then I validate and manipulate the shape of the matrix so that it is a (3,1) vector. Now the instance builder is proper. my __array_finalize__ looks like this: def __array_finalize__(self, obj): self.Format = getattr(obj, Format, FMT_VECTOR_DEFAULT) return Also, after learning about the view method, I am now doing the following in Vector.cross: def cross(self, other): Cross product of this vector and another vector #calling the result's view method promotes it to Vector result = _N.cross(self, other, axis=0) result = result.view(type(self)) return result It also works if I just use Vector(...), but it is my gut feeling that invoking a view vs. instantiating a new Vector instance is cheaper? Now I can add more unit tests. Cheers, -Basilisk96 ___ Numpy-discussion mailing list Numpy-discussion@scipy.org http://projects.scipy.org/mailman/listinfo/numpy-discussion
Re: [Numpy-discussion] subclassing matrix
On Jan 11, 2008, Colin J. Williams wrote: You make a good case that it's good not to need to ponder what sort of vector you are dealing with. My guess is that the answer to your question is no but I would need to play with your code to see that. My feeling is that, at the bottom of the __new__ module, the returned object should be an instance of the Vector class. It's been a while since I've worked with numpy and so I'll look at it and hope that someone gives you a definitive answer before I sort it out. Colin W. Well, let's say that I get rid of that class promotion line. When the input object to the constructor is a string or a tuple such as Vector('1 2 3') or Vector([1,2,3]), then the returned object is always an instance of Vector. However, when the input object is a numpy.matrix instance, the returned object remains a numpy.matrix instance! So by doing that little hack, I promote it to Vector. BUT... It seems that I have solved only half of my problem here. The other half rears its ugly head when I perform operations between instances of numpy.matrix and Vector. The result ends up returning a matrix, which is bad because it has no knowledge of any custom Vector attributes. Here's a simple case: u = Vector('1 2 3') #Vector instance P = numpy.mat(numpy.eye(3)) #matrix instance u_new = P*u #matrix instance, not desirable! u_new_as_Vector = Vector(P*u) #Vector instance I'd rather not have to remember to re-instantiate the result in client code. I think I understand why this is happening - the code in numpy.core.defmatrix.matrix.__mul__ goes like this: def __mul__(self, other): if isinstance(other,(N.ndarray, list, tuple)) : # This promotes 1-D vectors to row vectors return N.dot(self, asmatrix(other)) if N.isscalar(other) or not hasattr(other, '__rmul__') : return N.dot(self, other) return NotImplemented It passes the first condition: isinstance(other,(N.ndarray)) is true; and so the return value becomes a matrix. Bummer. Do I also need to override a few overloaded methods like __mul__, __rmul__, etc. to make this work? Cheers, -Basilisk96 ___ Numpy-discussion mailing list Numpy-discussion@scipy.org http://projects.scipy.org/mailman/listinfo/numpy-discussion
Re: [Numpy-discussion] subclassing matrix
On Jan 11, 2008 9:59 PM, Basilisk96 [EMAIL PROTECTED] wrote: On Jan 11, 2008, Colin J. Williams wrote: You make a good case that it's good not to need to ponder what sort of vector you are dealing with. My guess is that the answer to your question is no but I would need to play with your code to see that. My feeling is that, at the bottom of the __new__ module, the returned object should be an instance of the Vector class. It's been a while since I've worked with numpy and so I'll look at it and hope that someone gives you a definitive answer before I sort it out. Colin W. Well, let's say that I get rid of that class promotion line. When the input object to the constructor is a string or a tuple such as Vector('1 2 3') or Vector([1,2,3]), then the returned object is always an instance of Vector. However, when the input object is a numpy.matrix instance, the returned object remains a numpy.matrix instance! So by doing that little hack, I promote it to Vector. BUT... It seems that I have solved only half of my problem here. The other half rears its ugly head when I perform operations between instances of numpy.matrix and Vector. The result ends up returning a matrix, which is bad because it has no knowledge of any custom Vector attributes. Here's a simple case: u = Vector('1 2 3') #Vector instance P = numpy.mat(numpy.eye(3)) #matrix instance u_new = P*u #matrix instance, not desirable! u_new_as_Vector = Vector(P*u) #Vector instance I'd rather not have to remember to re-instantiate the result in client code. I think I understand why this is happening - the code in numpy.core.defmatrix.matrix.__mul__ goes like this: def __mul__(self, other): if isinstance(other,(N.ndarray, list, tuple)) : # This promotes 1-D vectors to row vectors return N.dot(self, asmatrix(other)) if N.isscalar(other) or not hasattr(other, '__rmul__') : return N.dot(self, other) return NotImplemented It passes the first condition: isinstance(other,(N.ndarray)) is true; and so the return value becomes a matrix. Bummer. Do I also need to override a few overloaded methods like __mul__, __rmul__, etc. to make this work? I believe that you need to look at __array_finalize__ and __array_priority__ (and there may be one other thing as well, I can't remember; it's late). Search for __array_finalize__ and that will probably help get you started. -- . __ . |-\ . . [EMAIL PROTECTED] ___ Numpy-discussion mailing list Numpy-discussion@scipy.org http://projects.scipy.org/mailman/listinfo/numpy-discussion
Re: [Numpy-discussion] subclassing matrix
Basilisk96 wrote: Hello folks, In the course of a project that involved heavy use of geometry and linear algebra, I found it useful to create a Vector subclass of numpy.matrix (represented as a column vector in my case). Why not consider a matrix with a shape of (1, n) as a row vector and one with (n, 1) as a column vector? Then you can simply write A * u or u.T * A. Does this not meet the need? You could add methods isRowVector and isColumnVector to the Matrix class. Colin W. I'd like to hear comments about my use of this class promotion statement in __new__: ret.__class__ = cls It seems to me that it is hackish to just change an instance's class on the fly, so perhaps someone could clue me in on a better practice. Here is my reason for doing this: Many applications of this code involve operations between instances of numpy.matrix and instances of Vector, such as applying a linear- operator matrix on a vector. If I omit that class promotion statement, then the results of such operations cannot be instantiated as Vector types: from vector import Vector import numpy u = Vector('1 2 3') A = numpy.matrix('2 0 0; 0 2 0; 0 0 2') p = Vector(A * u) p.__class__ class 'numpy.core.defmatrix.matrix' This is undesirable because the calculation result loses the custom Vector methods and attributes that I want to use. However, if I use that class promotion statement, the p.__class__ lookup returns what I want: p.__class__ class 'vector.Vector' Is there a better way to achieve that? Here is the partial subclass code: #-- vector.py import numpy as _N import math as _M #default tolerance for equality tests TOL_EQ = 1e-6 #default format for pretty-printing Vector instances FMT_VECTOR_DEFAULT = %+.5f class Vector(_N.matrix): 2D/3D vector class that supports numpy matrix operations and more. Examples: u = Vector([1,2,3]) v = Vector('3 4 5') w = Vector([1, 2]) def __new__(cls, data=0. 0. 0., dtype=_N.float64): Subclass instance constructor. If data is not specified, a zero Vector is constructed. The constructor always returns a Vector instance. The instance gets a customizable Format attribute, which controls the printing precision. ret = super(Vector, cls).__new__(cls, data, dtype=dtype) #promote the instance to cls type. ret.__class__ = cls assert ret.size in (2, 3), 'Vector must have either two or three components' if ret.shape[0] == 1: ret = ret.T assert ret.shape == (ret.shape[0], 1), 'could not express Vector as a Mx1 matrix' if ret.shape[0] == 2: ret = _N.vstack((ret, 0.)) ret.Format = FMT_VECTOR_DEFAULT return ret def __str__(self): fmt = getattr(self, Format, FMT_VECTOR_DEFAULT) fmt = ', '.join([fmt]*3) return ''.join([(, fmt, )]) % (self.X, self.Y, self.Z) def __repr__(self): fmt = ', '.join(['%s']*3) return ''.join([%s([, fmt, ])]) % (self.__class__.__name__, self.X, self.Y, self.Z) the remaining methods are Vector-specific math operations, including the X,Y,Z properties... Cheers, -Basilisk96 ___ Numpy-discussion mailing list Numpy-discussion@scipy.org http://projects.scipy.org/mailman/listinfo/numpy-discussion
Re: [Numpy-discussion] subclassing matrix
Yes, that certainly meets the need. In previous applications, I never thought to use Vector because it was sufficient for me to adopt the convention of a column vector to represent XY points. That way, I could do things like c * R * u + t. Isn't that simple? Not only is it easy on the eyes, but it follows most academic definitions that I know. Furthermore, I was able to apply linear operators to a whole set of XY points by doing something to the tune of: u = numpy.hstack(tuple_of_points_from_ascii_file)# u.shape is (2, n) R = some_2d_numpy_rotation_matrix# R.shape is (2, 2) t = numpy.mat(1.; 3.) v = R * u + t # v.shape is the same as u.shape So, the need arose for a self-contained Vector class that inherently supports a number of common geometric operations, and uses numpy for them. It seemed natural to subclass matrix because numpy.array is a general-case array; numpy.matrix restricts that to an array of rank 2; and Vector further restricts that to a *column* array of rank 2. Now, if I were to write a custom library of my common vector operations using module functions, I'd have to always keep in mind that they must operate on either column or row vectors, but not both. Instead, I chose to write a subclass of a well-tested class and explicitly forced all Vector instances to be columns. Vector has a number of methods that wrap numpy functions within the Vector context. For example: def cross(self, other): Cross product of this vector and another vector if isinstance(other, self.__class__): return Vector(_N.cross(self, other, axis=0)) else: raise TypeError('Both arguments must be of type Vector') I think that a call like w = u.cross(v) is less verbose than having to remember the `axis` argument in numpy.cross(u, v, axis=0). Yes, I realize that it would not be required if the vectors were row matrices, but then I'd have to live with using u.T * A, which I don't particularly like either. But now I can do things like: from vector import Vector u = Vector('1 2 3') v = Vector('3 2 1') w = u.cross(v) print w (-4.0, +8.0, -4.0) alpha = u.angle(w) print alpha 1.57079632679 ..and so on. However, my original question still stands: is ret.__class__ = cls a good idea? Cheers, -Basilisk96 On Jan 10, 10:03 am, Colin J. Williams [EMAIL PROTECTED] wrote: Basilisk96 wrote: Hello folks, In the course of a project that involved heavy use of geometry and linear algebra, I found it useful to create a Vector subclass of numpy.matrix (represented as a column vector in my case). Why not consider a matrix with a shape of (1, n) as a row vector and one with (n, 1) as a column vector? Then you can simply write A * u or u.T * A. Does this not meet the need? You could add methods isRowVector and isColumnVector to the Matrix class. Colin W. I'd like to hear comments about my use of this class promotion statement in __new__: ret.__class__ = cls It seems to me that it is hackish to just change an instance's class on the fly, so perhaps someone could clue me in on a better practice. Here is my reason for doing this: Many applications of this code involve operations between instances of numpy.matrix and instances of Vector, such as applying a linear- operator matrix on a vector. If I omit that class promotion statement, then the results of such operations cannot be instantiated as Vector types: from vector import Vector import numpy u = Vector('1 2 3') A = numpy.matrix('2 0 0; 0 2 0; 0 0 2') p = Vector(A * u) p.__class__ class 'numpy.core.defmatrix.matrix' This is undesirable because the calculation result loses the custom Vector methods and attributes that I want to use. However, if I use that class promotion statement, the p.__class__ lookup returns what I want: p.__class__ class 'vector.Vector' Is there a better way to achieve that? Here is the partial subclass code: #-- vector.py import numpy as _N import math as _M #default tolerance for equality tests TOL_EQ = 1e-6 #default format for pretty-printing Vector instances FMT_VECTOR_DEFAULT = %+.5f class Vector(_N.matrix): 2D/3D vector class that supports numpy matrix operations and more. Examples: u = Vector([1,2,3]) v = Vector('3 4 5') w = Vector([1, 2]) def __new__(cls, data=0. 0. 0., dtype=_N.float64): Subclass instance constructor. If data is not specified, a zero Vector is constructed. The constructor always returns a Vector instance. The instance gets a customizable Format attribute, which controls the printing precision. ret = super(Vector, cls).__new__(cls, data, dtype=dtype) #promote the instance to cls type.