The DistanceMatrix function, newly introduced in version 10.3, is very fast for Euclidean distances.
Here's a speed comparison with Leonid's fast solution.
pts = RandomReal[1, {5000, 2}];
Euclidean
dm1 = With[{tr = Transpose[pts]}, Function[point, Sqrt[Total[(point - tr)^2]]] /@ pts]; // AbsoluteTiming
(* {0.952627, Null} *)
dm2 = DistanceMatrix[pts, pts]; // AbsoluteTiming
(* {0.212496, Null} *)
dm3 = HierarchicalClustering`DistanceMatrix[pts, DistanceFunction -> EuclideanDistance]; // AbsoluteTiming
(* {0.582991, Null} *)
dm1 == dm2 == dm3
(* True *)
Note that HierarchicalClustering`DistanceMatrix is a built-in function, not one provided by the package. Most performance-critical functions of this "package" are in reality highly optimized built-ins. Also note that the default distance for this function is not EuclideanDistance, but that square of that. So we needed to specify EuclideanDistance explicitly.
Manhattan
Let's test if these functions are special-cased for the Manhattan distance.
dm1 =
With[{tr = Transpose[pts]},
Function[point, Total@Abs[(point - tr)]] /@ pts]; // AbsoluteTiming
(* {0.801177, Null} *)
dm2 =
DistanceMatrix[pts, pts,
DistanceFunction -> ManhattanDistance]; // AbsoluteTiming
(* {0.211771, Null} *)
dm3 =
HierarchicalClustering`DistanceMatrix[pts,
DistanceFunction -> ManhattanDistance]; // AbsoluteTiming
(* {0.621123, Null} *)
dm1 == dm2 == dm3
(* True *)
A final note
For accurate benchmarking is it very important to use AbsoluteTiming and not Timing here. In recent versions of Mathematica all of these operations are internally parallelized and Timing would measure the total CPU time spent by each core, added up, instead of the wall time.
Just for fun, here's a C++ version using LTemplate. This is specialized for 2D points!
<< LTemplate`
I'm on a Mac where the system compiler doesn't support OpenMP. I'll use gcc from MacPorts to be able to use OpenMP.
$CCompiler = {"Compiler" -> CCompilerDriver`GenericCCompiler`GenericCCompiler,
"CompilerInstallation" -> "/opt/local/bin",
"CompilerName" -> "g++-mp-5",
"SystemCompileOptions" ->
"-O3 -m64 -fPIC -framework Foundation -framework mathlink"};
SetDirectory[$TemporaryDirectory];
code = "
#include <cmath>
inline double sqr(double x) { return x*x; }
struct DistMatrix {
mma::RealTensorRef distMat(mma::RealMatrixRef a, mma::RealMatrixRef b) {
mma::RealMatrixRef mat = mma::makeMatrix<double>(a.rows(), b.rows());
#pragma omp parallel for
for (mint i=0; i < a.rows(); ++i)
for (mint j=0; j < b.rows(); ++j)
mat(i, j) = std::hypot(a(i,0) - b(j,0), a(i,1) - b(j,1));
return mat;
}
};
";
Export["DistMatrix.h", code, "String"];
tem = LClass[
"DistMatrix",
{LFun["distMat", {{Real, 2, "Constant"}, {Real, 2, "Constant"}}, {Real, 2}]}
];
CompileTemplate[tem,
"CompileOptions" -> {"-std=c++14", "-fopenmp"}]
LoadTemplate[tem]
obj = Make["DistMatrix"];
dm4 = obj@"distMat"[pts, pts]; // AbsoluteTiming
(* {0.062397, Null} *)
dm1 == dm4
(* True *)
Outeris generally the fastest way. – István Zachar Mar 23 '13 at 09:11Table[(* stuff *), {j, n}, {k, j}] // Flatten) and feed this toSymmetrizedArray[]? (I do not have version 9, so I can't test this.) – J. M.'s missing motivation Mar 23 '13 at 16:06tri = {(* lower triangle *)}; full = tri + Transpose@tri*(1-IdentityMatrix@n). Or if the diagonal is assumed to be zero (in case of most distance functions), the1-IdentityMatrix@npart can be omitted. – István Zachar Mar 25 '13 at 10:46