cost-matrix 0.1.0__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cost_matrix/euclidean_matrix.py +25 -2
- cost_matrix/manhattan_matrix.py +25 -1
- cost_matrix/osrm_matrix.py +64 -35
- cost_matrix/spherical_matrix.py +15 -1
- {cost_matrix-0.1.0.dist-info → cost_matrix-0.2.0.dist-info}/METADATA +7 -4
- cost_matrix-0.2.0.dist-info/RECORD +8 -0
- {cost_matrix-0.1.0.dist-info → cost_matrix-0.2.0.dist-info}/WHEEL +1 -1
- cost_matrix-0.1.0.dist-info/RECORD +0 -8
cost_matrix/euclidean_matrix.py
CHANGED
@@ -1,5 +1,28 @@
|
|
1
1
|
import numpy as np
|
2
2
|
|
3
3
|
|
4
|
-
def euclidean(sources
|
5
|
-
|
4
|
+
def euclidean(sources, destinations):
|
5
|
+
"""
|
6
|
+
Calculate the Euclidean distance matrix between source and destination points.
|
7
|
+
|
8
|
+
Parameters
|
9
|
+
----------
|
10
|
+
sources : np.ndarray
|
11
|
+
Array of shape (n, d) containing the source points.
|
12
|
+
destinations : np.ndarray
|
13
|
+
Array of shape (m, d) containing the destination points.
|
14
|
+
|
15
|
+
Returns
|
16
|
+
-------
|
17
|
+
np.ndarray
|
18
|
+
Euclidean distance matrix of shape (n, m).
|
19
|
+
"""
|
20
|
+
# Expand dimensions to enable broadcasting
|
21
|
+
sources_expanded = sources[:, np.newaxis, :]
|
22
|
+
destinations_expanded = destinations[np.newaxis, :, :]
|
23
|
+
|
24
|
+
# Compute differences along the last dimension
|
25
|
+
differences = sources_expanded - destinations_expanded
|
26
|
+
|
27
|
+
# Compute Euclidean distances using norm along the last dimension
|
28
|
+
return np.linalg.norm(differences, axis=2)
|
cost_matrix/manhattan_matrix.py
CHANGED
@@ -2,4 +2,28 @@ import numpy as np
|
|
2
2
|
|
3
3
|
|
4
4
|
def manhattan(sources: np.ndarray, destinations: np.ndarray) -> np.ndarray:
|
5
|
-
|
5
|
+
"""
|
6
|
+
Compute the Manhattan distance matrix between source and destination points.
|
7
|
+
|
8
|
+
Parameters
|
9
|
+
----------
|
10
|
+
sources : np.ndarray
|
11
|
+
Array of shape (n, d) containing the source points.
|
12
|
+
destinations : np.ndarray
|
13
|
+
Array of shape (m, d) containing the destination points.
|
14
|
+
|
15
|
+
Returns
|
16
|
+
-------
|
17
|
+
np.ndarray
|
18
|
+
Manhattan distance matrix of shape (n, m).
|
19
|
+
"""
|
20
|
+
# Expand dimensions to enable broadcasting
|
21
|
+
sources_expanded = sources[:, np.newaxis, :]
|
22
|
+
destinations_expanded = destinations[np.newaxis, :, :]
|
23
|
+
|
24
|
+
# Compute absolute differences along the last dimension
|
25
|
+
differences = np.abs(sources_expanded - destinations_expanded)
|
26
|
+
|
27
|
+
# Sum absolute differences along the last dimension to get
|
28
|
+
# Manhattan distance
|
29
|
+
return np.sum(differences, axis=2)
|
cost_matrix/osrm_matrix.py
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
import os
|
2
|
+
from concurrent.futures import ThreadPoolExecutor
|
1
3
|
from math import ceil
|
2
4
|
|
3
5
|
import numpy as np
|
@@ -11,55 +13,88 @@ def osrm(
|
|
11
13
|
batch_size: int = 150,
|
12
14
|
cost_type: str = "distances",
|
13
15
|
) -> np.ndarray:
|
14
|
-
"""
|
15
|
-
|
16
|
+
"""
|
17
|
+
Compute the OSRM cost matrix between sources and destinations
|
18
|
+
|
19
|
+
Parameters
|
20
|
+
----------
|
21
|
+
sources : np.ndarray
|
22
|
+
Array of shape (n, d) containing the source points.
|
23
|
+
destinations : np.ndarray
|
24
|
+
Array of shape (m, d) containing the destination points.
|
25
|
+
server_address : str
|
26
|
+
Address of the OSRM server. Default is "http://router.project-osrm.org".
|
27
|
+
batch_size : int
|
28
|
+
Number of points to send in each request. Default is 150.
|
29
|
+
cost_type : str
|
30
|
+
Type of cost to be computed. Default is "distances". Options are
|
31
|
+
"distances" and "durations".
|
32
|
+
|
33
|
+
Returns
|
34
|
+
-------
|
35
|
+
np.ndarray
|
36
|
+
OSRM cost matrix of shape (n, m).
|
37
|
+
"""
|
16
38
|
num_sources = sources.shape[0]
|
17
39
|
num_destinations = destinations.shape[0]
|
18
|
-
cost_matrix = np.zeros((num_sources, num_destinations))
|
40
|
+
cost_matrix = np.zeros((num_sources, num_destinations), dtype=np.float32)
|
19
41
|
|
20
42
|
num_batches_i = ceil(num_sources / batch_size)
|
21
43
|
num_batches_j = ceil(num_destinations / batch_size)
|
22
44
|
|
23
|
-
|
24
|
-
|
25
|
-
|
45
|
+
cpu_count = os.cpu_count() or 0
|
46
|
+
default_max = cpu_count * 2 if cpu_count > 0 else 8
|
47
|
+
max_workers = min(default_max, 16)
|
48
|
+
|
49
|
+
futures = []
|
50
|
+
positions = []
|
51
|
+
|
52
|
+
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
53
|
+
for i in range(num_batches_i):
|
54
|
+
start_i = i * batch_size
|
55
|
+
end_i = min((i + 1) * batch_size, num_sources)
|
56
|
+
|
57
|
+
for j in range(num_batches_j):
|
58
|
+
start_j = j * batch_size
|
59
|
+
end_j = min((j + 1) * batch_size, num_destinations)
|
26
60
|
|
27
|
-
|
28
|
-
|
29
|
-
end_j = min((j + 1) * batch_size, num_destinations)
|
30
|
-
sources_batch = sources[start_i:end_i]
|
31
|
-
destinations_batch = destinations[start_j:end_j]
|
61
|
+
sources_batch = sources[start_i:end_i]
|
62
|
+
destinations_batch = destinations[start_j:end_j]
|
32
63
|
|
33
|
-
|
34
|
-
|
64
|
+
future = executor.submit(
|
65
|
+
_fetch_matrix_batch,
|
35
66
|
sources_batch,
|
36
67
|
destinations_batch,
|
37
68
|
server_address,
|
38
|
-
cost_type
|
69
|
+
cost_type,
|
39
70
|
)
|
40
|
-
|
71
|
+
futures.append(future)
|
72
|
+
positions.append((start_i, end_i, start_j, end_j))
|
73
|
+
|
74
|
+
for future, (start_i, end_i, start_j, end_j) in zip(
|
75
|
+
futures, positions, strict=False
|
76
|
+
):
|
77
|
+
cost_matrix[start_i:end_i, start_j:end_j] = future.result()
|
41
78
|
|
42
79
|
return cost_matrix
|
43
80
|
|
44
81
|
|
45
|
-
def
|
82
|
+
def _fetch_matrix_batch(
|
46
83
|
sources_batch: np.ndarray,
|
47
84
|
destinations_batch: np.ndarray,
|
48
85
|
server_address: str,
|
49
86
|
cost_type: str,
|
50
87
|
):
|
51
88
|
"""Request the OSRM cost matrix for a given batch"""
|
52
|
-
|
53
|
-
url = _format_osrm_url(
|
89
|
+
url = _format_url(
|
54
90
|
sources_batch, destinations_batch, server_address, cost_type
|
55
91
|
)
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
return np.array(resp.json()[cost_type])
|
92
|
+
response = requests.get(url)
|
93
|
+
response.raise_for_status()
|
94
|
+
return np.array(response.json()[cost_type], dtype=np.float32)
|
60
95
|
|
61
96
|
|
62
|
-
def
|
97
|
+
def _format_url(
|
63
98
|
sources_batch: np.ndarray,
|
64
99
|
destinations_batch: np.ndarray,
|
65
100
|
server_address: str,
|
@@ -93,11 +128,8 @@ def _format_osrm_url(
|
|
93
128
|
"distance"), but the returned JSON follows the plural form (e.g.,
|
94
129
|
"distances"). Thus, we ignore the last letter of the input type
|
95
130
|
"""
|
96
|
-
|
97
131
|
url_cost_type = cost_type[:-1]
|
98
|
-
sources_coord = ";".join(
|
99
|
-
f"{source[1]},{source[0]}" for source in sources_batch
|
100
|
-
)
|
132
|
+
sources_coord = ";".join(f"{lng},{lat}" for (lat, lng) in sources_batch)
|
101
133
|
|
102
134
|
# If sources == destinations, return a simpler URL early. Notice it needs
|
103
135
|
# at least two points, otherwise OSRM complains
|
@@ -106,14 +138,12 @@ def _format_osrm_url(
|
|
106
138
|
and sources_batch.shape[0] > 1
|
107
139
|
):
|
108
140
|
return (
|
109
|
-
f"{server_address}/table/v1/driving/"
|
110
|
-
f"{sources_coord}"
|
141
|
+
f"{server_address}/table/v1/driving/{sources_coord}"
|
111
142
|
f"?annotations={url_cost_type}"
|
112
143
|
)
|
113
144
|
|
114
145
|
destinations_coord = ";".join(
|
115
|
-
f"{
|
116
|
-
for destination in destinations_batch
|
146
|
+
f"{lng},{lat}" for (lat, lng) in destinations_batch
|
117
147
|
)
|
118
148
|
locations_coord = sources_coord + ";" + destinations_coord
|
119
149
|
|
@@ -121,7 +151,6 @@ def _format_osrm_url(
|
|
121
151
|
# sources = 0,1,...,N' and destinations = N'+1,N'+2...N'+M'
|
122
152
|
num_sources = sources_batch.shape[0]
|
123
153
|
num_destinations = destinations_batch.shape[0]
|
124
|
-
|
125
154
|
sources_indices = ";".join(str(index) for index in range(num_sources))
|
126
155
|
destinations_indices = ";".join(
|
127
156
|
str(index)
|
@@ -129,8 +158,8 @@ def _format_osrm_url(
|
|
129
158
|
)
|
130
159
|
|
131
160
|
return (
|
132
|
-
f"{server_address}/table/v1/driving/"
|
133
|
-
f"{
|
134
|
-
f"
|
161
|
+
f"{server_address}/table/v1/driving/{locations_coord}"
|
162
|
+
f"?sources={sources_indices}"
|
163
|
+
f"&destinations={destinations_indices}"
|
135
164
|
f"&annotations={url_cost_type}"
|
136
165
|
)
|
cost_matrix/spherical_matrix.py
CHANGED
@@ -4,7 +4,21 @@ EARTH_RADIUS_METERS = 6371000
|
|
4
4
|
|
5
5
|
|
6
6
|
def spherical(sources: np.ndarray, destinations: np.ndarray) -> np.ndarray:
|
7
|
-
"""
|
7
|
+
"""
|
8
|
+
Compute the distance matrix using the Spherical distance]
|
9
|
+
|
10
|
+
Parameters
|
11
|
+
----------
|
12
|
+
sources : np.ndarray
|
13
|
+
Array of shape (n, d) containing the source points.
|
14
|
+
destinations : np.ndarray
|
15
|
+
Array of shape (m, d) containing the destination points.
|
16
|
+
|
17
|
+
Returns
|
18
|
+
-------
|
19
|
+
np.ndarray
|
20
|
+
Spherical distance matrix of shape (n, m).
|
21
|
+
"""
|
8
22
|
|
9
23
|
sources_rad = np.radians(sources)
|
10
24
|
destinations_rad = np.radians(destinations)
|
@@ -1,12 +1,11 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.3
|
2
2
|
Name: cost-matrix
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.2.0
|
4
4
|
Summary: Designed to simplify the creation of cost matrices for optimization problems.
|
5
5
|
Author: Luan
|
6
6
|
Author-email: llvdmoraes@gmail.com
|
7
|
-
Requires-Python: >=3.
|
7
|
+
Requires-Python: >=3.10,<3.13
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
9
|
-
Classifier: Programming Language :: Python :: 3.9
|
10
9
|
Classifier: Programming Language :: Python :: 3.10
|
11
10
|
Classifier: Programming Language :: Python :: 3.11
|
12
11
|
Classifier: Programming Language :: Python :: 3.12
|
@@ -69,4 +68,8 @@ osrm_duration_matrix = cost_matrix.osrm(
|
|
69
68
|
batch_size=250
|
70
69
|
)
|
71
70
|
print(osrm_duration_matrix)
|
71
|
+
```
|
72
|
+
|
73
|
+
## GitHub
|
74
|
+
* [Repository](https://github.com/luanleonardo/cost-matrix)
|
72
75
|
|
@@ -0,0 +1,8 @@
|
|
1
|
+
cost_matrix/__init__.py,sha256=9EaPsXe0SIuym34-CPmzUPSGJuBd2AkQZHqjcwlUywE,202
|
2
|
+
cost_matrix/euclidean_matrix.py,sha256=PiOJFjalkvk1p4ktcCvFD7EeXswvsnm8f-oze1gFRlk,844
|
3
|
+
cost_matrix/manhattan_matrix.py,sha256=OgOepYRfskarZmNNarl58YqNRw7WXYnjztWV2BbjGBI,907
|
4
|
+
cost_matrix/osrm_matrix.py,sha256=mlj61VoMKb0hisDl4xdNa_-9gw0RgtILmr4GpjIFg3k,5495
|
5
|
+
cost_matrix/spherical_matrix.py,sha256=9_y5XCpdAEiWcvljPX7kFYSUt0nU3Gq4DOwr7LlbhQY,1271
|
6
|
+
cost_matrix-0.2.0.dist-info/METADATA,sha256=IZyDlKzSf0C4bnMICS8CgmO6Lrb95eXlXNkK0wVC1is,2702
|
7
|
+
cost_matrix-0.2.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
8
|
+
cost_matrix-0.2.0.dist-info/RECORD,,
|
@@ -1,8 +0,0 @@
|
|
1
|
-
cost_matrix/__init__.py,sha256=9EaPsXe0SIuym34-CPmzUPSGJuBd2AkQZHqjcwlUywE,202
|
2
|
-
cost_matrix/euclidean_matrix.py,sha256=a8uLh0COVb5duaKV6DMfG9HklkJX2dOARKfnnWxLxsc,155
|
3
|
-
cost_matrix/manhattan_matrix.py,sha256=bWTTiIgvb0PDWSdfCXuffHEIgb9nNMb4w7Qwhe3EbaQ,155
|
4
|
-
cost_matrix/osrm_matrix.py,sha256=VuuoQVRGxMjoN_eUfs9yur9lwINg8JZxB3SX-6cvBFk,4327
|
5
|
-
cost_matrix/spherical_matrix.py,sha256=SxaihjkKyrp0den5w-VsimGcjjQEi-q-_cvxeTM3xyY,946
|
6
|
-
cost_matrix-0.1.0.dist-info/METADATA,sha256=4TVrFT0TlklV7jcvRAJarKqi-TE2_TPMtSJJtVZTmIQ,2676
|
7
|
-
cost_matrix-0.1.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
8
|
-
cost_matrix-0.1.0.dist-info/RECORD,,
|