sgptools 1.1.3__py3-none-any.whl → 1.1.6__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.
- sgptools/__init__.py +1 -1
- sgptools/utils/data.py +3 -0
- sgptools/utils/gpflow.py +38 -7
- sgptools/utils/misc.py +22 -0
- sgptools/utils/tsp.py +43 -21
- {sgptools-1.1.3.dist-info → sgptools-1.1.6.dist-info}/METADATA +1 -1
- {sgptools-1.1.3.dist-info → sgptools-1.1.6.dist-info}/RECORD +10 -10
- {sgptools-1.1.3.dist-info → sgptools-1.1.6.dist-info}/LICENSE.txt +0 -0
- {sgptools-1.1.3.dist-info → sgptools-1.1.6.dist-info}/WHEEL +0 -0
- {sgptools-1.1.3.dist-info → sgptools-1.1.6.dist-info}/top_level.txt +0 -0
sgptools/__init__.py
CHANGED
sgptools/utils/data.py
CHANGED
@@ -120,6 +120,7 @@ def prep_synthetic_dataset(shape=(50, 50),
|
|
120
120
|
min_height=0.0,
|
121
121
|
max_height=30.0,
|
122
122
|
roughness=0.5,
|
123
|
+
random_seed=None,
|
123
124
|
**kwargs):
|
124
125
|
'''Generates a 50x50 grid of synthetic elevation data using the diamond square algorithm.
|
125
126
|
|
@@ -131,6 +132,7 @@ def prep_synthetic_dataset(shape=(50, 50),
|
|
131
132
|
min_height (float): Minimum allowed height in the sampled data
|
132
133
|
max_height (float): Maximum allowed height in the sampled data
|
133
134
|
roughness (float): Roughness of the sampled data
|
135
|
+
random_seed (int): Random seed for reproducibility
|
134
136
|
|
135
137
|
Returns:
|
136
138
|
X (ndarray): (n, d); Dataset input features
|
@@ -140,6 +142,7 @@ def prep_synthetic_dataset(shape=(50, 50),
|
|
140
142
|
min_height=min_height,
|
141
143
|
max_height=max_height,
|
142
144
|
roughness=roughness,
|
145
|
+
random_seed=random_seed,
|
143
146
|
**kwargs)
|
144
147
|
|
145
148
|
# create x and y coordinates from the extent
|
sgptools/utils/gpflow.py
CHANGED
@@ -21,6 +21,8 @@ import tensorflow_probability as tfp
|
|
21
21
|
import numpy as np
|
22
22
|
import matplotlib.pyplot as plt
|
23
23
|
|
24
|
+
from .misc import get_inducing_pts
|
25
|
+
|
24
26
|
|
25
27
|
def plot_loss(losses, save_file=None):
|
26
28
|
"""Helper function to plot the training loss
|
@@ -50,8 +52,12 @@ def get_model_params(X_train, y_train,
|
|
50
52
|
variance=1.0,
|
51
53
|
noise_variance=0.1,
|
52
54
|
kernel=None,
|
55
|
+
return_gp=False,
|
56
|
+
train_inducing_pts=False,
|
57
|
+
num_inducing_pts=500,
|
53
58
|
**kwargs):
|
54
|
-
"""Train a GP on the given training set
|
59
|
+
"""Train a GP on the given training set.
|
60
|
+
Trains a sparse GP if the training set is larger than 1000 samples.
|
55
61
|
|
56
62
|
Args:
|
57
63
|
X_train (ndarray): (n, d); Training set inputs
|
@@ -64,29 +70,54 @@ def get_model_params(X_train, y_train,
|
|
64
70
|
variance (float): Kernel variance
|
65
71
|
noise_variance (float): Data noise variance
|
66
72
|
kernel (gpflow.kernels.Kernel): gpflow kernel function
|
73
|
+
return_gp (bool): If True, returns the trained GP model
|
74
|
+
train_inducing_pts (bool): If True, trains the inducing points when
|
75
|
+
using a sparse GP model
|
76
|
+
num_inducing_pts (int): Number of inducing points to use when training
|
77
|
+
a sparse GP model
|
67
78
|
|
68
79
|
Returns:
|
69
80
|
loss (list): Loss values obtained during training
|
70
81
|
variance (float): Optimized data noise variance
|
71
82
|
kernel (gpflow.kernels.Kernel): Optimized gpflow kernel function
|
83
|
+
gp (gpflow.models.GPR): Optimized gpflow GP model.
|
84
|
+
Returned only if ```return_gp=True```.
|
85
|
+
|
72
86
|
"""
|
73
87
|
if kernel is None:
|
74
88
|
kernel = gpflow.kernels.SquaredExponential(lengthscales=lengthscales,
|
75
89
|
variance=variance)
|
76
90
|
|
77
|
-
|
78
|
-
|
79
|
-
|
91
|
+
if len(X_train) <= 1500:
|
92
|
+
gpr = gpflow.models.GPR(data=(X_train, y_train),
|
93
|
+
kernel=kernel,
|
94
|
+
noise_variance=noise_variance)
|
95
|
+
trainable_variables=gpr.trainable_variables
|
96
|
+
else:
|
97
|
+
inducing_pts = get_inducing_pts(X_train, num_inducing_pts)
|
98
|
+
gpr = gpflow.models.SGPR(data=(X_train, y_train),
|
99
|
+
kernel=kernel,
|
100
|
+
inducing_variable=inducing_pts,
|
101
|
+
noise_variance=noise_variance)
|
102
|
+
if train_inducing_pts:
|
103
|
+
trainable_variables=gpr.trainable_variables
|
104
|
+
else:
|
105
|
+
trainable_variables=gpr.trainable_variables[1:]
|
80
106
|
|
81
107
|
if max_steps > 0:
|
82
|
-
loss = optimize_model(
|
108
|
+
loss = optimize_model(gpr, max_steps=max_steps, lr=lr,
|
109
|
+
trainable_variables=trainable_variables,
|
110
|
+
**kwargs)
|
83
111
|
else:
|
84
112
|
loss = 0
|
85
113
|
|
86
114
|
if print_params:
|
87
|
-
print_summary(
|
115
|
+
print_summary(gpr)
|
88
116
|
|
89
|
-
|
117
|
+
if return_gp:
|
118
|
+
return loss, gpr.likelihood.variance, kernel, gpr
|
119
|
+
else:
|
120
|
+
return loss, gpr.likelihood.variance, kernel
|
90
121
|
|
91
122
|
|
92
123
|
class TraceInducingPts(gpflow.monitor.MonitorTask):
|
sgptools/utils/misc.py
CHANGED
@@ -3,6 +3,8 @@ from .metrics import get_distance
|
|
3
3
|
from scipy.optimize import linear_sum_assignment
|
4
4
|
from sklearn.metrics import pairwise_distances
|
5
5
|
from scipy.cluster.vq import kmeans2
|
6
|
+
from shapely import geometry
|
7
|
+
import geopandas as gpd
|
6
8
|
|
7
9
|
import matplotlib.pyplot as plt
|
8
10
|
import numpy as np
|
@@ -138,3 +140,23 @@ def project_waypoints(waypoints, candidates):
|
|
138
140
|
waypoints_disc = cont2disc(waypoints, candidates)
|
139
141
|
waypoints_valid = _reoder_path(waypoints, waypoints_disc)
|
140
142
|
return waypoints_valid
|
143
|
+
|
144
|
+
def ploygon2candidats(vertices,
|
145
|
+
num_samples=5000,
|
146
|
+
random_seed=2024):
|
147
|
+
"""Sample unlabeled candidates within a polygon
|
148
|
+
|
149
|
+
Args:
|
150
|
+
vertices (ndarray): (v, 2) of vertices that define the polygon
|
151
|
+
num_samples (int): Number of samples to generate
|
152
|
+
random_seed (int): Random seed for reproducibility
|
153
|
+
|
154
|
+
Returns:
|
155
|
+
candidates (ndarray): (n, 2); Candidate sensor placement locations
|
156
|
+
"""
|
157
|
+
poly = geometry.Polygon(vertices)
|
158
|
+
sampler = gpd.GeoSeries([poly])
|
159
|
+
candidates = sampler.sample_points(size=num_samples,
|
160
|
+
rng=random_seed)
|
161
|
+
candidates = candidates.get_coordinates().to_numpy()
|
162
|
+
return candidates
|
sgptools/utils/tsp.py
CHANGED
@@ -24,21 +24,23 @@ def run_tsp(nodes,
|
|
24
24
|
max_dist=25,
|
25
25
|
depth=1,
|
26
26
|
resample=None,
|
27
|
-
|
28
|
-
|
27
|
+
start_nodes=None,
|
28
|
+
end_nodes=None,
|
29
29
|
time_limit=10):
|
30
30
|
"""Method to run TSP/VRP with arbitrary start and end nodes,
|
31
31
|
and without any distance constraint
|
32
32
|
|
33
33
|
Args:
|
34
|
-
nodes (ndarray): (# nodes,
|
34
|
+
nodes (ndarray): (# nodes, ndim); Nodes to visit
|
35
35
|
num_vehicles (int): Number of robots/vehicles
|
36
36
|
max_dist (float): Maximum distance allowed for each path when handling mutli-robot case
|
37
37
|
depth (int): Internal parameter used to track re-try recursion depth
|
38
38
|
resample (int): Each solution path will be resampled to have
|
39
39
|
`resample` number of points
|
40
|
-
|
41
|
-
|
40
|
+
start_nodes (ndarray): (# num_vehicles, ndim); Optionl array of start nodes from which
|
41
|
+
to start each vehicle's solution path
|
42
|
+
end_nodes (ndarray): (# num_vehicles, ndim); Optionl array of end nodes at which
|
43
|
+
to end each vehicle's solution path
|
42
44
|
time_limit (int): TSP runtime time limit in seconds
|
43
45
|
|
44
46
|
Returns:
|
@@ -48,28 +50,42 @@ def run_tsp(nodes,
|
|
48
50
|
if depth > 5:
|
49
51
|
print('Warning: Max depth reached')
|
50
52
|
return None, None
|
51
|
-
|
53
|
+
|
54
|
+
# Add the start and end nodes to the node list
|
55
|
+
if end_nodes is not None:
|
56
|
+
assert end_nodes.shape == (num_vehicles, nodes.shape[-1]), \
|
57
|
+
"Incorrect end_nodes shape, should be (num_vehicles, ndim)!"
|
58
|
+
nodes = np.concatenate([end_nodes, nodes])
|
59
|
+
if start_nodes is not None:
|
60
|
+
assert start_nodes.shape == (num_vehicles, nodes.shape[-1]), \
|
61
|
+
"Incorrect start_nodes shape, should be (num_vehicles, ndim)!"
|
62
|
+
nodes = np.concatenate([start_nodes, nodes])
|
63
|
+
|
52
64
|
# Add dummy 0 location to get arbitrary start and end node sols
|
53
|
-
if
|
65
|
+
if start_nodes is None or end_nodes is None:
|
54
66
|
distance_mat = np.zeros((len(nodes)+1, len(nodes)+1))
|
55
67
|
distance_mat[1:, 1:] = pairwise_distances(nodes, nodes)*1e4
|
56
|
-
trim_paths = True
|
68
|
+
trim_paths = True #shift to account for dummy node
|
57
69
|
else:
|
58
70
|
distance_mat = pairwise_distances(nodes, nodes)*1e4
|
59
71
|
trim_paths = False
|
60
72
|
distance_mat = distance_mat.astype(int)
|
61
73
|
max_dist = int(max_dist*1e4)
|
62
74
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
75
|
+
# Get start and end node indices for ortools
|
76
|
+
if start_nodes is None:
|
77
|
+
start_idx = np.zeros(num_vehicles, dtype=int)
|
78
|
+
num_start_nodes = 0
|
79
|
+
else:
|
80
|
+
start_idx = np.arange(num_vehicles)+int(trim_paths)
|
81
|
+
num_start_nodes = len(start_nodes)
|
67
82
|
|
68
|
-
if
|
69
|
-
end_idx =
|
70
|
-
|
71
|
-
end_idx =
|
83
|
+
if end_nodes is None:
|
84
|
+
end_idx = np.zeros(num_vehicles, dtype=int)
|
85
|
+
else:
|
86
|
+
end_idx = np.arange(num_vehicles)+num_start_nodes+int(trim_paths)
|
72
87
|
|
88
|
+
# used by ortools
|
73
89
|
def distance_callback(from_index, to_index):
|
74
90
|
from_node = manager.IndexToNode(from_index)
|
75
91
|
to_node = manager.IndexToNode(to_index)
|
@@ -78,8 +94,8 @@ def run_tsp(nodes,
|
|
78
94
|
# num_locations, num vehicles, start, end
|
79
95
|
manager = pywrapcp.RoutingIndexManager(len(distance_mat),
|
80
96
|
num_vehicles,
|
81
|
-
start_idx,
|
82
|
-
end_idx)
|
97
|
+
start_idx.tolist(),
|
98
|
+
end_idx.tolist())
|
83
99
|
routing = pywrapcp.RoutingModel(manager)
|
84
100
|
transit_callback_index = routing.RegisterTransitCallback(distance_callback)
|
85
101
|
routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)
|
@@ -165,14 +181,20 @@ def resample_path(waypoints, num_inducing=10):
|
|
165
181
|
inducing points path with fixed number of waypoints
|
166
182
|
|
167
183
|
Args:
|
168
|
-
waypoints (ndarray): (num_waypoints,
|
184
|
+
waypoints (ndarray): (num_waypoints, ndim); waypoints of path from vrp solver
|
169
185
|
num_inducing (int): Number of inducing points (waypoints) in the returned path
|
170
186
|
|
171
187
|
Returns:
|
172
|
-
points (ndarray): (num_inducing,
|
188
|
+
points (ndarray): (num_inducing, ndim); Resampled path
|
173
189
|
"""
|
190
|
+
ndim = np.shape(waypoints)[-1]
|
191
|
+
if not (ndim==2 or ndim==3):
|
192
|
+
raise Exception(f"ndim={ndim} is not supported for path resampling!")
|
174
193
|
line = LineString(waypoints)
|
175
194
|
distances = np.linspace(0, line.length, num_inducing)
|
176
195
|
points = [line.interpolate(distance) for distance in distances]
|
177
|
-
|
196
|
+
if ndim==2:
|
197
|
+
points = np.array([[p.x, p.y] for p in points])
|
198
|
+
elif ndim==3:
|
199
|
+
points = np.array([[p.x, p.y, p.z] for p in points])
|
178
200
|
return points
|
@@ -1,4 +1,4 @@
|
|
1
|
-
sgptools/__init__.py,sha256=
|
1
|
+
sgptools/__init__.py,sha256=ETsbfpEnORTg0xjJyWQSUNI7cSLKYCAr6QRluBeGzRs,449
|
2
2
|
sgptools/kernels/__init__.py,sha256=zRf4y-wJwjXKt1uOnmI5MbzCA6pRlyA7C-eagLfb3d0,190
|
3
3
|
sgptools/kernels/neural_kernel.py,sha256=9XEjcwwi1Gwj4D5cAZwq5QdWqMaI-Vu2DKgYO58DmPg,6709
|
4
4
|
sgptools/models/__init__.py,sha256=X2lIg9kf1-2MHUswk-VW2dHHcbSLxf6_IuV7lc_kvDc,682
|
@@ -13,13 +13,13 @@ sgptools/models/core/augmented_sgpr.py,sha256=qMP9J4AnOUx9AEZfaPhoyb3RP_2AOhOUCU
|
|
13
13
|
sgptools/models/core/osgpr.py,sha256=gqliUdXdnt3fea206LP0rqGIggmIdKh8WP2DtFWzdBw,11798
|
14
14
|
sgptools/models/core/transformations.py,sha256=X7WEKo_lFAYB5HKnFvxFsxfz6CB-jzPfVWcx1sWe2lI,18313
|
15
15
|
sgptools/utils/__init__.py,sha256=jgWqzSDgUbqOTFo8mkqZaTlyz44l3v2XYPJfcHYHjqM,376
|
16
|
-
sgptools/utils/data.py,sha256=
|
17
|
-
sgptools/utils/gpflow.py,sha256=
|
16
|
+
sgptools/utils/data.py,sha256=ojDq6KzBXbAl5CdpA6A6me0sg5Sah9ZTl2TpFCqgR4c,7464
|
17
|
+
sgptools/utils/gpflow.py,sha256=46-_Tl-suxvuX3Y9KI_uiixfyCWQ2T-7BUn-7hesdVM,10047
|
18
18
|
sgptools/utils/metrics.py,sha256=tu8H129n8GuxV5fQIKLcfzPUxd7sp8zEF9qZBOZjNKo,5834
|
19
|
-
sgptools/utils/misc.py,sha256=
|
20
|
-
sgptools/utils/tsp.py,sha256=
|
21
|
-
sgptools-1.1.
|
22
|
-
sgptools-1.1.
|
23
|
-
sgptools-1.1.
|
24
|
-
sgptools-1.1.
|
25
|
-
sgptools-1.1.
|
19
|
+
sgptools/utils/misc.py,sha256=11nsDEU3imnrvH7ywGMiwtNBcBnJfHX3KaGxFS3eq6w,6223
|
20
|
+
sgptools/utils/tsp.py,sha256=b1Lx1Pj-sv7siX-f0S6d25C3RtvszCl3IP4QbvBckqY,8151
|
21
|
+
sgptools-1.1.6.dist-info/LICENSE.txt,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
22
|
+
sgptools-1.1.6.dist-info/METADATA,sha256=SXtfy54sXEKKFd7uPKkpDA7CCQUbAunhjl9YHE_hTRs,944
|
23
|
+
sgptools-1.1.6.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
24
|
+
sgptools-1.1.6.dist-info/top_level.txt,sha256=2NWH6uQLAOuLB9fG7o1pqf6Jvpe1_hEcuqfSqtUw3gw,9
|
25
|
+
sgptools-1.1.6.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|