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 *)
Outer
is generally the fastest way. $\endgroup$Table[(* stuff *), {j, n}, {k, j}] // Flatten
) and feed this toSymmetrizedArray[]
? (I do not have version 9, so I can't test this.) $\endgroup$