tags:

views:

355

answers:

5

I need to represent immutable vectors in Python ("vectors" as in linear algebra, not as in programming). The tuple seems like an obvious choice.

The trouble is when I need to implement things like addition and scalar multiplication. If a and b are vectors, and c is a number, the best I can think of is this:

tuple(map(lambda x,y: x + y, a, b)) # add vectors 'a' and 'b'
tuple(map(lambda x: x * c, a))      # multiply vector 'a' by scalar 'c'

which seems inelegant; there should be a clearer, simpler way to get this done -- not to mention avoiding the call to tuple, since map returns a list.

Is there a better option?

A: 

Since pretty much all of the sequence manipulation functions return lists, that's pretty much what you're going to have to do.

sblom
+2  A: 

Why not create your own class, making use of 2 Cartesian point member variables? (sorry if the syntax is a little off, my python is rusty)

class point:
    def __init__(self,x,y):
        self.x=x
        self.y=y
        #etc

    def add(self,p):
        return point(self.x + p.x, self.y + p.y)

class vector:
    def __init__(self,a,b):
        self.pointA=a
        self.pointB=b
        #etc

    def add(self,v):
        return vector(self.pointA + v.pointA, self.pointB + v.pointB)
CrazyJugglerDrummer
Primarily to avoid wheel-reinvention; there are enough uses for linear algebra that it doesn't seem likely I have to roll my own.
Etaoin
@Etaoin You're already reinventing the wheel if you're trying to use your own tuples instead of using a vector class created by someone else. :D
CrazyJugglerDrummer
Hope you don't mind, I fixed up that syntax for you (for the benefit of future readers)
David Zaslavsky
+7  A: 

NumPy supports various algebraic operations with its arrays.

Ignacio Vazquez-Abrams
+1 for numpy. Numpy's arrays probably already do 95% of what you want. There's no sense trying to re-implement well-known, and long-used libraries.
Brendan Abel
+6  A: 

Immutable types are pretty rare in Python and third-party extensions thereof; the OP rightly claims "there are enough uses for linear algebra that it doesn't seem likely I have to roll my own" -- but all the existing types I know that do linear algebra are mutable! So, as the OP is adamant on immutability, there is nothing for it but the roll-your-own route.

Not that there's all that much rolling involved, e.g. if you specifically need 2-d vectors:

import math
class ImmutableVector(object):
    __slots__ = ('_d',)
    def __init__(self, x, y):
        object.__setattr__(self, _d, (x, y))
    def __setattr__(self, n, v):
        raise ValueError("Can't alter instance of %s" % type(self))
    @property
    def x(self): 
        return self._d[0]
    @property
    def y(self):
        return self._d[1]
    def __eq__(self, other):
        return self._d == other._d
    def __ne__(self, other):
        return self._d != other._d
    def __hash__(self):
        return hash(self._d)
    def __add__(self, other):
        return type(self)(self.x+other.x, self.y+other.y)
    def __mul__(self, scalar):
        return type(self)(self.x*scalar, self.y*scalar)
    def __repr__(self):
        return '%s(%s, %s)' % (type(self).__name__, self.x, self.y)
    def __abs__(self):
        return math.hypot(self.x, self.y)

I "threw in for free" a few extras such as .x and .y R/O properties, nice string representation, usability in sets or as keys in dicts (why else would one want immutability?-), low memory footprint, abs(v) to give v's vector-length -- I'm sure you can think of other "wouldn't-it-be-cool-if" methods and operators, depending on your application field, and they'll be just as easy. If you need other dimensionalities it won't be much harder, though a tad less readable since the .x, .y notation doesn't apply any more;-) (but I'd use genexps, not map).

Alex Martelli
+1 for follow-through! I do want an n-dimensional solution (and it looks like I'll probably go with NumPy and rethink mutability) but this is worth thinking about.
Etaoin
+1  A: 

By inheriting from tuple, you can make a nice Vector class pretty easily. Here's enough code to provide addition of vectors, and multiplication of a vector by a scalar. It gives you arbitrary length vectors, and can work with complex numbers, ints, or floats.

class Vector(tuple):
    def __add__(self, a):
        # TODO: check lengths are compatable.
        return Vector(x + y for x, y in zip(self, a))
    def __mul__(self, c):
        return Vector(x * c for x in self)
    def __rmul__(self, c):
        return Vector(c * x for x in self)

a = Vector((1, 2, 3))
b = Vector((2, 3, 4))

print a + b
print 3 * a
print a * 3
Paul Hankin