def equivalent(kls, first, second):
    """
    Tests that two skeletons are the same in form not merely that
    their array contents are exactly the same. This test can be
    made more sophisticated. 
    """
    if first.empty() and second.empty():
      return True
    elif first.vertices.shape[0] != second.vertices.shape[0]:
      return False
    elif first.edges.shape[0] != second.edges.shape[0]:
      return False

    EPSILON = 1e-7

    vertex1, inv1 = np.unique(first.vertices, axis=0, return_inverse=True)
    vertex2, inv2 = np.unique(second.vertices, axis=0, return_inverse=True)

    vertex_match = np.all(np.abs(vertex1 - vertex2) < EPSILON)
    if not vertex_match:
      return False

    remapping = {}
    for i in range(len(inv1)):
      remapping[inv1[i]] = inv2[i]
    remap = np.vectorize(lambda idx: remapping[idx])

    edges1 = np.sort(np.unique(first.edges, axis=0), axis=1)
    edges1 = edges1[np.lexsort(edges1[:,::-1].T)]

    edges2 = remap(second.edges)
    edges2 = np.sort(np.unique(edges2, axis=0), axis=1)
    edges2 = edges2[np.lexsort(edges2[:,::-1].T)]
    edges_match = np.all(edges1 == edges2)

    if not edges_match:
      return False

    second_verts = {}
    for i, vert in enumerate(second.vertices):
      second_verts[tuple(vert)] = i
    
    for i in range(len(first.radii)):
      i2 = second_verts[tuple(first.vertices[i])]

      if first.radii[i] != second.radii[i2]:
        return False

      if first.vertex_types[i] != second.vertex_types[i2]:
        return False

    return True
Tests that two skeletons are the same in form not merely that
    their array contents are exactly the same. This test can be
    made more sophisticated.