def sum_of_all_kernel_vals(kernel, collection):
    """
    Computes the sum of kernel(x_i, x_j) for all x_i, x_j in the
    collection

    Args:
    kernel: function taking two points, returning a real number
    collection: collection of points
    """

    assert len(collection) > 0, "Require a non-empty collection in sum_kernels()"

    ret = 0

    # Compute the 'off-diagonal' sums. I.e. sum of k(x_i, x_j) for all j > i.
    for i in range(len(collection)):
        for j in range(i + 1, len(collection)):
            ret += kernel(collection[i], collection[j])

    # Double to add the sum of all k(x_i, x_j) for all j < i
    ret *= 2

    # Add elements on the diagonal to get all contributions
    ret += sum([kernel(x, x) for x in collection])

    return ret