# Why does MMA force a DistanceFunctions to be positive?

Consider the following piece of code:

DistanceMatrix[Range, DistanceFunction -> Function[{x, y}, x - y]]


I would have expected it to return a skew-symmetric matrix, but it seems that MMA forces my custom distance function to return positive values (no this is not because MMA is just computing one triangular half and copying it over; even though that maybe how it is operating). For example,

DistanceMatrix[Range,
DistanceFunction -> Function[{x, y}, If[EvenQ[x - y], True, False]]]


reveals that each element is run through an Abs. Why is this happening? Specifically, can I prevent Mathematica from doing that?

I do understand the mathematical definition of a distance function (positivity, symmetry, triangle inequality etc.) so I am not looking for an answer that says "Oh that is how the math is defined." On the other hand, it is conceivable that you want to perform a some operation on each element of Subsets[someList, {2}] and put it into a square matrix and this was my first thought. I found this which I could probably use, but the DistanceFunction approach seemed most natural.

It is possible that I am missing a more obvious/simpler way to do it; in which case I apologize - please enlighten me on the more idiomatic way.

• I just found an answer with Outer[f, l1, l2]; so this question becomes moot, and I don't mind if you vote to close. But if someone does have a way to prevent that call to Abs in custom distance function, I would be very curious to see how the hack works.
– ITA
Mar 8 at 20:09
• The nasty Unprotect[Abs];Abs[x_]:=x comes to mind. I'm not 100% sure how to clean up properly after this hack. Restarting the kernel is my goto...
Mar 8 at 22:42
• My recollection is that DistanceMatrix enforces symmetry. Some amount of internal code might be relying on that. Mar 9 at 14:11

So, one way to hack around an applied Abs function (as per your comment requesting one; I'll note here that for anyone else with the same issue reading this answer, you answered the "workaround" part of your question with Outer[f, l1, l2]!) is to define a function UnAbs which, by use of an upvalue, essentially deletes any Abs surrounding it!

Abs[UnAbs[x_]] ^:= x


We could then use Function[{x, y}, UnAbs[x-y]]. At least, we should be able to, right?

Turns out, DistanceMatrix does, actually, just copy one triangle to another. We can see this by using Echo:

DistanceMatrix[Range,
DistanceFunction -> Function[{x, y}, Echo[x - y, {x, y}]]]


This labels each printing of x - y with the actual {x, y} being fed to it, and we see that only pairs where x is less than or equal to y are ever fed to our distance function.

You'll also notice some calls to the function involving length 3 vectors of numerical values at the beginning. I don't know what those are for, but I think they're for other uses of DistanceMatrix.

But even weirder, it seems to be applying Abs twice. At least, it seems to do so when it realizes its first Abs has failed. I don't know why, but to get rid of Abs completely you actually need Function[{x,y}, UnAbs @ UnAbs[x - y]]. (This is true even when using an undefined head for the function like f instead of x - y—for some reason, you just need two Unabses.)

So, kind of a non-answer, since DistanceMatrix does actually only evaluate d[x, y] on one of the triangles and copy the results over to the other. As such, it's impossible to get a skew-symmetric matrix as a result—even though we can technically get rid of the call to Abs!

• This is still useful! If I know beforehand my matrix is indeed skew-symmetric, again I only need compute either the upper or lower half of it. That eliminates N(N-1)/2 calls in the Outer based-method.
– ITA
Mar 8 at 23:31