When I ran that code, giving it your sample data for L, I got
NameError: name 'distance2' is not defined
rather than an error from np.tri().
That's what I expected. The variable 'distance2' is not defined in your closetpair() function. It's a local variable inside the distance() function. No code outside of a function can see the that function's local variables.
Your main problem is that you are not calling the distance() function. Instead, you're trying to use a local variable from the function that you haven't called. Change the line that calls
When I run with that change, and with your sample data set for L, I get the answer you said you were expecting: (3.0, 4.3) (2.5, 5.1).
You've made the code difficult to read by defining a function inside of a function. That code in the distance function is only called once inside of closetpair(), so there's no need to define and call a function. Another fix would be to get rid of the
statement and just include that code in closetpair(). (I think you meant for that to be "closestpair", but I've kept your spelling and only made changes needed to make the code work.)
If you're going to keep that distance() function, move it outside of closetpair(). Had you done that, you probably would have recognized the error message and made the needed change yourself.
As a last point, you're doing a lot of unneeded work. You are creating an NxN matrix with the distances between every pair of points from L, when you only need to compare unique pairs of different points. If you just rewrite that loop inside of your distance function, you don't need the array, you don't need any square roots at all, and you don't need numpy. The usual way to loop over just distinct pairs in an array looks something like:
for i in range(len(L) - 1):
for j in range(i+1, len(L)):
[do something with L[i] and L[j] here]
The result will be easier to code and get running, will run faster with less code and less data at runtime.