[Numpy-discussion] Fastest distance matrix calc
Bill Baxter
wbaxter at gmail.com
Tue Apr 17 16:24:37 EDT 2007
Oops. Looks like I forgot to attach the test program that generated
that output so you can tell what dist2g actually does.
Funny thing is -- despite being written in C, hypot doesn't actually
win any of the test cases for which it's applicable.
--bb
On 4/17/07, Bill Baxter <wbaxter at gmail.com> wrote:
> Here's a bunch of dist matrix implementations and their timings.
> The upshot is that for most purposes this seems to be the best or at
> least not too far off (basically the cookbook solution Kier posted)
>
> def dist2hd(x,y):
> """Generate a 'coordinate' of the solution at a time"""
> d = npy.zeros((x.shape[0],y.shape[0]),dtype=x.dtype)
> for i in xrange(x.shape[1]):
> diff2 = x[:,i,None] - y[:,i]
> diff2 **= 2
> d += diff2
> npy.sqrt(d,d)
> return d
>
> The only place where it's far from the best is for a small number of
> points (~10) with high dimensionality (~100), which does come up in
> machine learning contexts. For those cases this does much better
> (factor of :
>
> def dist2b3(x,y):
> d = npy.dot(x,y.T)
> d *= -2.0
> d += (x*x).sum(1)[:,None]
> d += (y*y).sum(1)
> # Rounding errors occasionally cause negative entries in d
> d[d<0] = 0
> # in place sqrt
> npy.sqrt(d,d)
> return d
>
> So given that, the obvious solution (if you don't want to delve into
> non-numpy solutions) is to use a hybrid that just switches between
> the two. Not sure what the proper switch is since it seems kind of
> complicated, and probably depends some on cache specifics. But just
> switching based on the dimension of the points seems to be pretty
> effective:
>
> def dist2hy(x,y):
> if x.shape[1]<5:
> d = npy.zeros((x.shape[0],y.shape[0]),dtype=x.dtype)
> for i in xrange(x.shape[1]):
> diff2 = x[:,i,None] - y[:,i]
> diff2 **= 2
> d += diff2
> npy.sqrt(d,d)
> return d
>
> else:
> d = npy.dot(x,y.T)
> d *= -2.0
> d += (x*x).sum(1)[:,None]
> d += (y*y).sum(1)
> # Rounding errors occasionally cause negative entries in d
> d[d<0] = 0
> # in place sqrt
> npy.sqrt(d,d)
> return d
>
> All of this assumes 'C' contiguous data. All bets are off if you have
> non-contiguous or 'F' ordered data. And maybe if x and y have very
> different numbers of points.
>
>
> --bb
>
>
>
> On 4/17/07, Keir Mierle <mierle at gmail.com> wrote:
> > On 4/13/07, Timothy Hochberg <tim.hochberg at ieee.org> wrote:
> > > On 4/13/07, Bill Baxter <wbaxter at gmail.com> wrote:
> > > > I think someone posted some timings about this before but I don't recall.
> >
> > http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/498246
> >
> > [snip]
> > > I'm going to go out on a limb and contend, without running any timings, that
> > > for large M and N, a solution using a for loop will beat either of those.
> > > For example (untested):
> > >
> > > results = empty([M, N], float)
> > > # You could be fancy and swap axes depending on which array is larger, but
> > > # I'll leave that for someone else
> > > for i, v in enumerate(x):
> > > results[i] = sqrt(sum((v-y)**2, axis=-1))
> > > Or something like that. The reason that I suspect this will be faster is
> > > that it has better locality, completely finishing a computation on a
> > > relatively small working set before moving onto the next one. The one liners
> > > have to pull the potentially large MxN array into the processor repeatedly.
> >
> > In my experience, it is indeed the case that the for loop version is
> > faster. The fastest of the three versions offered in the above url is
> > the last:
> >
> > from numpy import mat, zeros, newaxis
> > def calcDistanceMatrixFastEuclidean2(nDimPoints):
> > nDimPoints = array(nDimPoints)
> > n,m = nDimPoints.shape
> > delta = zeros((n,n),'d')
> > for d in xrange(m):
> > data = nDimPoints[:,d]
> > delta += (data - data[:,newaxis])**2
> > return sqrt(delta)
> >
> > This is easily extended to two different nDimPoints matricies.
> >
> > Cheers,
> > Keir
> > _______________________________________________
> > Numpy-discussion mailing list
> > Numpy-discussion at scipy.org
> > http://projects.scipy.org/mailman/listinfo/numpy-discussion
> >
>
>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: dist2perf.py
Type: text/x-python
Size: 5359 bytes
Desc: not available
URL: <http://mail.python.org/pipermail/numpy-discussion/attachments/20070418/f0a7b462/attachment.py>
More information about the NumPy-Discussion
mailing list