Skip to content

Commit ec4f37d

Browse files
authored
Merge pull request #134 from lucasimi/develop
Added Manhattan metric
2 parents 1db4fd9 + 5647019 commit ec4f37d

File tree

6 files changed

+55
-19
lines changed

6 files changed

+55
-19
lines changed

.github/workflows/deploy.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,20 +55,20 @@ jobs:
5555
path: dist/*.tar.gz
5656

5757
upload_pypi:
58-
needs: [build_wheels, build_sdist]
58+
needs: [build_locally, build_wheels, build_sdist]
5959
runs-on: ubuntu-latest
6060
environment: pypi
6161
permissions:
6262
id-token: write
6363
steps:
6464
- uses: actions/download-artifact@v4
6565
with:
66-
# unpacks all CIBW artifacts into dist/
6766
pattern: cibw-*
6867
path: dist
6968
merge-multiple: true
7069

7170
- uses: pypa/gh-action-pypi-publish@release/v1
7271
with:
73-
# To test:
74-
repository-url: https://test.pypi.org/legacy/
72+
repository-url: https://test.pypi.org/legacy/
73+
74+
- uses: pypa/gh-action-pypi-publish@release/v1

app/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ streamlit==1.34.0
22
numpy>=1.25.2
33
scikit-learn>=1.5.0
44
pandas>=2.1.0
5-
tda-mapper==0.7.0
5+
tda-mapper>=0.7.0

pyproject.toml

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
[build-system]
2-
requires = ["setuptools>=42", "wheel", "Cython"]
2+
requires = ["setuptools", "wheel", "Cython"]
33
build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "tda-mapper"
7-
version = "0.7.0"
7+
version = "0.7.1"
88
description = "A simple and efficient Python implementation of Mapper algorithm for Topological Data Analysis"
99
readme = "README.md"
1010
authors = [{ name = "Luca Simi", email = "lucasimi90@gmail.com" }]
@@ -22,15 +22,18 @@ dependencies = [
2222
"numpy>=1.20.1, <2.0.0",
2323
"plotly>=4.14.3"
2424
]
25-
requires-python = ">=3.6"
25+
requires-python = ">=3.7"
2626

2727
[project.optional-dependencies]
2828
dev = ["coverage", "pandas", "scikit-learn"]
2929

30-
[tool.setuptools.package-data]
31-
"tdamapper.utils" = ["_metrics.c", "_metrics.pyx"]
32-
3330
[project.urls]
3431
Homepage = "https://github.com/lucasimi/tda-mapper-python"
3532
Documentation = "https://tda-mapper.readthedocs.io"
3633
Issues = "https://github.com/lucasimi/tda-mapper-python/issues"
34+
35+
[tool.setuptools.package-data]
36+
"tdamapper.utils" = ["_metrics.c", "_metrics.pyx"]
37+
38+
[tool.cibuildwheel]
39+
skip = "cp36-*"

setup.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,5 @@
1313
]
1414

1515
setup(
16-
name='tda-mapper',
17-
version='0.7.0',
1816
ext_modules=cythonize(ext_modules),
1917
)

src/tdamapper/utils/_metrics.pyx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,22 @@ cpdef inline double chebyshev(double[:] x, double[:] y) nogil:
1111

1212
cpdef inline double euclidean(double[:] x, double[:] y) nogil:
1313
cdef double norm_squared = 0.0
14+
cdef double diff
1415
cdef Py_ssize_t i, n = x.shape[0]
1516
for i in range(n):
16-
norm_squared += pow(fabs(x[i] - y[i]), 2)
17+
diff = x[i] - y[i]
18+
norm_squared += diff * diff
1719
return sqrt(norm_squared)
1820

1921

22+
cpdef inline double manhattan(double[:] x, double[:] y) nogil:
23+
cdef double norm = 0.0
24+
cdef Py_ssize_t i, n = x.shape[0]
25+
for i in range(n):
26+
norm += fabs(x[i] - y[i])
27+
return norm
28+
29+
2030
cpdef inline double minkowski(int p, double[:] x, double[:] y) nogil:
2131
cdef double norm_p = 0.0
2232
cdef Py_ssize_t i, n = x.shape[0]
@@ -33,7 +43,7 @@ cpdef inline double cosine(double[:] x, double[:] y) nogil:
3343
cdef double similarity = 0.0
3444
for i in range(n):
3545
dot_product += x[i] * y[i]
36-
norm_x += pow(x[i], 2)
37-
norm_y += pow(y[i], 2)
46+
norm_x += x[i] * x[i]
47+
norm_y += y[i] * y[i]
3848
similarity = dot_product / sqrt(norm_x * norm_y)
3949
return sqrt(2.0 * (1.0 - similarity))

src/tdamapper/utils/metrics.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@
2121
- Cosine: A distance on unit vectors based on cosine similarity.
2222
"""
2323

24+
import numpy as np
2425
import tdamapper.utils._metrics as _metrics
2526

2627

2728
_EUCLIDEAN = 'euclidean'
29+
_MANHATTAN = 'manhattan'
2830
_MINKOWSKI = 'minkowski'
2931
_MINKOWSKI_P = 'p'
3032
_CHEBYSHEV = 'chebyshev'
@@ -40,6 +42,7 @@ def get_supported_metrics():
4042
"""
4143
return [
4244
_EUCLIDEAN,
45+
_MANHATTAN,
4346
_MINKOWSKI,
4447
_CHEBYSHEV,
4548
_COSINE,
@@ -59,6 +62,19 @@ def euclidean():
5962
return _metrics.euclidean
6063

6164

65+
def manhattan():
66+
"""
67+
Return the Manhattan distance function for vectors.
68+
69+
The Manhattan distance is defined as the sum of the absolute differences
70+
between the components of the vectors.
71+
72+
:return: The Manhattan distance function.
73+
:rtype: callable
74+
"""
75+
return _metrics.manhattan
76+
77+
6278
def chebyshev():
6379
"""
6480
Return the Chebyshev distance function for vectors.
@@ -76,16 +92,23 @@ def minkowski(p):
7692
"""
7793
Return the Minkowski distance function for order p on vectors.
7894
79-
The Minkowski distance is a generalization of the Euclidean and
80-
Chebyshev distances. When p = 1, it is equivalent to the Manhattan
81-
distance, and when p = 2, it is equivalent to the Euclidean distance.
95+
The Minkowski distance is a generalization of the Euclidean and Chebyshev
96+
distances. When p = 1, it is equivalent to the Manhattan distance, and
97+
when p = 2, it is equivalent to the Euclidean distance. When p is infinite,
98+
it is equivalent to the Chebyshev distance.
8299
83100
:param p: The order of the Minkowski distance.
84101
:type p: int
85102
86103
:return: The Minkowski distance function.
87104
:rtype: callable
88105
"""
106+
if p == 1:
107+
return manhattan()
108+
elif p == 2:
109+
return euclidean()
110+
elif np.isinf(p):
111+
return chebyshev()
89112
return lambda x, y: _metrics.minkowski(p, x, y)
90113

91114

@@ -131,6 +154,8 @@ def get_metric(metric, **kwargs):
131154
return metric
132155
elif metric == _EUCLIDEAN:
133156
return euclidean()
157+
elif metric == _MANHATTAN:
158+
return manhattan()
134159
elif metric == _MINKOWSKI:
135160
p = kwargs.get(_MINKOWSKI_P, 2)
136161
return minkowski(p)

0 commit comments

Comments
 (0)