Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
74abd07
Allow for normalize=True for gaussian kernel as pdf, False K(0)=1
sjsrey Jun 3, 2025
a46413a
bool not binary for type
sjsrey Jun 3, 2025
ca5b0c8
Add tests for normalize kw in Kernel
sjsrey Jun 3, 2025
335a76f
numpy testing
sjsrey Jun 3, 2025
2c0cdc5
Correct reference for kernel defs
sjsrey Jun 26, 2025
c18939e
prototyping new _kernels.py
sjsrey Jun 26, 2025
c53ee7c
triangular (#29)
knaaptime Jun 26, 2025
645edbc
move primative kernels out to libpysal._kernels.py
sjsrey Jul 8, 2025
37fe0f6
renaming kernels.py
sjsrey Jul 8, 2025
29d859c
populate self-weight with K(0)
sjsrey Jul 8, 2025
7824797
modify imports
sjsrey Jul 8, 2025
cd15d2a
Merge branch 'main' into exp/kernel
sjsrey Jul 8, 2025
c5f937e
roll back graph kernels
sjsrey Jul 8, 2025
6d38dd0
Add tests and illustrative nb
sjsrey Jul 9, 2025
46a7e86
Correct Gaussian kernel function
sjsrey Jul 24, 2025
a1b3353
Update tests for gaussian correction
sjsrey Jul 24, 2025
162e761
ruff format rules and remove test nbs
sjsrey Jul 24, 2025
f7a1329
ruff tests
sjsrey Jul 24, 2025
d7cdf5b
Relocate kernels from graph to libpysal for reuse and centralization
sjsrey Jul 24, 2025
0b9662c
move taper/decay logic out of graph module (#30)
ljwolf Aug 28, 2025
c9f3b44
replay levi's change to not remove self-weight (#32)
knaaptime Aug 28, 2025
afc686c
gaussian constant term
sjsrey Aug 28, 2025
16bee5b
update tests for trim
sjsrey Aug 28, 2025
54e613c
import proper kernel.py in graph
sjsrey Aug 28, 2025
26d0ea3
Add exclude_loops=True for kernels on graph
sjsrey Sep 9, 2025
9ad0af2
Fix gaussian kernel.
sjsrey Sep 11, 2025
92b53ec
The coplanar tests assume we don't taper. In main this could be a bug?
sjsrey Sep 11, 2025
b5231a6
clean up doc strings
sjsrey Sep 19, 2025
37bdfd2
update tests for correct Gaussian kernel
sjsrey Sep 19, 2025
9e437ca
update tests for gaussian kernel
sjsrey Sep 20, 2025
709098b
Do not decay boxcar kernel
sjsrey Sep 25, 2025
83dd562
Update reference for kernels
sjsrey Jun 26, 2025
a7c6223
cast boxcar kernel to float
sjsrey Oct 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 14 additions & 61 deletions libpysal/graph/_kernel.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import numpy
import pandas
from scipy import optimize, sparse, spatial, stats
from libpysal.kernels import _kernel_functions

from ._utils import (
CoplanarError,
Expand All @@ -22,59 +23,6 @@
_VALID_GEOMETRY_TYPES = ["Point"]


def _triangular(distances, bandwidth):
u = numpy.clip(distances / bandwidth, 0, 1)
return 1 - u


def _parabolic(distances, bandwidth):
u = numpy.clip(distances / bandwidth, 0, 1)
return 0.75 * (1 - u**2)


def _gaussian(distances, bandwidth):
u = distances / bandwidth
return numpy.exp(-((u / 2) ** 2)) / (numpy.sqrt(2 * numpy.pi))


def _bisquare(distances, bandwidth):
u = numpy.clip(distances / bandwidth, 0, 1)
return (15 / 16) * (1 - u**2) ** 2


def _cosine(distances, bandwidth):
u = numpy.clip(distances / bandwidth, 0, 1)
return (numpy.pi / 4) * numpy.cos(numpy.pi / 2 * u)


def _exponential(distances, bandwidth):
u = distances / bandwidth
return numpy.exp(-u)


def _boxcar(distances, bandwidth):
r = (distances < bandwidth).astype(int)
return r


def _identity(distances, _):
return distances


_kernel_functions = {
"triangular": _triangular,
"parabolic": _parabolic,
"gaussian": _gaussian,
"bisquare": _bisquare,
"cosine": _cosine,
"boxcar": _boxcar,
"discrete": _boxcar,
"exponential": _exponential,
"identity": _identity,
None: _identity,
}


def _kernel(
coordinates,
bandwidth=None,
Expand All @@ -86,6 +34,7 @@ def _kernel(
taper=True,
coplanar="raise",
resolve_isolates=True,
exclude_self_weights=True
):
"""
Compute a kernel function over a distance matrix.
Expand Down Expand Up @@ -134,15 +83,17 @@ def _kernel(
remove links with a weight equal to zero
resolve_isolates : bool
Try to resolve isolates. Can be disabled if we are dealing with cliques later.
exclude_self_weights : bool (default: True)
Remove self-weights
"""
if metric != "precomputed":
coordinates, ids, _ = _validate_geometry_input(
coordinates, ids=ids, valid_geometry_types=_VALID_GEOMETRY_TYPES
)
else:
assert coordinates.shape[0] == coordinates.shape[1], (
"coordinates should represent a distance matrix if metric='precomputed'"
)
assert (
coordinates.shape[0] == coordinates.shape[1]
), "coordinates should represent a distance matrix if metric='precomputed'"
if ids is None:
ids = numpy.arange(coordinates.shape[0])

Expand Down Expand Up @@ -188,11 +139,13 @@ def _kernel(
data = sq.flatten()
i = numpy.tile(numpy.arange(sq.shape[0]), sq.shape[0])
j = numpy.repeat(numpy.arange(sq.shape[0]), sq.shape[0])
# remove diagonal
data = numpy.delete(data, numpy.arange(0, data.size, sq.shape[0] + 1))
i = numpy.delete(i, numpy.arange(0, i.size, sq.shape[0] + 1))
j = numpy.delete(j, numpy.arange(0, j.size, sq.shape[0] + 1))
# construct sparse


if exclude_self_weights:
data = numpy.delete(data, numpy.arange(0, data.size, sq.shape[0] + 1))
i = numpy.delete(i, numpy.arange(0, i.size, sq.shape[0] + 1))
j = numpy.delete(j, numpy.arange(0, j.size, sq.shape[0] + 1))

d = sparse.csc_array((data, (i, j)))
else:
d = sparse.csc_array(coordinates)
Expand Down
40 changes: 20 additions & 20 deletions libpysal/graph/tests/test_builders.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,26 +237,26 @@ def test_kernel_precompute(self):
g = graph.Graph.build_kernel(distmat, metric="precomputed")
expected = np.array(
[
0.126,
0.266,
0.174,
0.071,
0.126,
0.329,
0.311,
0.291,
0.266,
0.329,
0.31,
0.205,
0.174,
0.311,
0.31,
0.339,
0.071,
0.291,
0.205,
0.339,
0.04,
0.177,
0.076,
0.013,
0.04,
0.271,
0.242,
0.212,
0.177,
0.271,
0.241,
0.105,
0.076,
0.242,
0.241,
0.288,
0.013,
0.212,
0.105,
0.288,
]
)

Expand Down
8 changes: 4 additions & 4 deletions libpysal/graph/tests/test_kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,8 @@ def test_kernels(kernel, grocs):
assert weight.mean() == pytest.approx(0.10312196315841769)
assert weight.max() == pytest.approx(0.749881829575671)
elif kernel == "gaussian":
assert weight.mean() == pytest.approx(0.19932294761630429)
assert weight.max() == pytest.approx(0.3989265663183409)
assert weight.mean() == pytest.approx(0.13787969156713978)
assert weight.max() == pytest.approx(0.39891085285421685)
elif kernel == "bisquare":
assert weight.mean() == pytest.approx(0.09084085210598618)
assert weight.max() == pytest.approx(0.9372045972129259)
Expand Down Expand Up @@ -329,7 +329,7 @@ def test_coplanar(grocs):
[grocs, grocs.iloc[:10], grocs.iloc[:3]], ignore_index=True
)
# plain kernel
head, tail, weight = _kernel(grocs_duplicated)
head, tail, weight = _kernel(grocs_duplicated, taper=False)
assert head.shape[0] == len(grocs_duplicated) * (len(grocs_duplicated) - 1)
assert tail.shape == head.shape
assert weight.shape == head.shape
Expand All @@ -347,7 +347,7 @@ def test_coplanar(grocs):
np.testing.assert_array_equal(pd.unique(head), grocs_duplicated.index)

# k, clique
head, tail, weight = _kernel(grocs_duplicated, k=2, coplanar="clique")
head, tail, weight = _kernel(grocs_duplicated, k=2, coplanar="clique", taper=False)
assert head.shape[0] >= len(grocs_duplicated) * 2
assert tail.shape == head.shape
assert weight.shape == head.shape
Expand Down
33 changes: 17 additions & 16 deletions libpysal/graph/tests/test_triangulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,24 +257,25 @@ def test_ids(ids, stores_unique):

def test_kernel():
_, _, weight = _delaunay(cau_coords, kernel="gaussian")

expected = np.array(
[
0.231415,
0.307681,
0.395484,
0.231415,
0.237447,
0.057774,
0.307681,
0.237447,
0.123151,
0.319563,
0.057774,
0.123151,
0.035525,
0.395484,
0.319563,
0.035525,
0.134237,
0.237297,
0.392056,
0.134237,
0.141327,
0.008367,
0.237297,
0.141327,
0.038016,
0.255978,
0.008367,
0.038016,
0.003163,
0.392056,
0.255978,
0.003163,
]
)

Expand Down
Loading