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 CHANGED
@@ -12,7 +12,7 @@ The library includes python code for the following:
12
12
 
13
13
  """
14
14
 
15
- __version__ = "1.1.3"
15
+ __version__ = "1.1.6"
16
16
  __author__ = 'Kalvik'
17
17
 
18
18
  from .models.core import *
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
- gpr_gt = gpflow.models.GPR(data=(X_train, y_train),
78
- kernel=kernel,
79
- noise_variance=noise_variance)
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(gpr_gt, max_steps=max_steps, lr=lr, **kwargs)
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(gpr_gt)
115
+ print_summary(gpr)
88
116
 
89
- return loss, gpr_gt.likelihood.variance, kernel
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
- start_idx=None,
28
- end_idx=None,
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, n_dim); Nodes to visit
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
- start_idx (list): Optionl list of start node indices from which to start the solution path
41
- end_idx (list): Optionl list of end node indices from which to start the solution path
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 start_idx is None or end_idx is None:
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
- if start_idx is None:
64
- start_idx = [0]*num_vehicles
65
- elif trim_paths:
66
- start_idx = [i+1 for i in start_idx]
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 end_idx is None:
69
- end_idx = [0]*num_vehicles
70
- elif trim_paths:
71
- end_idx = [i+1 for i in 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, n_dim); waypoints of path from vrp solver
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, n_dim); Resampled path
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
- points = np.array([[p.x, p.y] for p in points])
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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sgptools
3
- Version: 1.1.3
3
+ Version: 1.1.6
4
4
  Summary: Software Suite for Sensor Placement and Informative Path Planning
5
5
  Home-page: https://www.itskalvik.com/sgp-tools
6
6
  Author: Kalvik
@@ -1,4 +1,4 @@
1
- sgptools/__init__.py,sha256=1nTYxJDM2wZiWHZRqjmWbREndmielrwcbbYLzCMmunc,449
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=oTXq4oRuzJdXpZC6frUfja8jhwy_ZdDDi7L1BYZcdQs,7309
17
- sgptools/utils/gpflow.py,sha256=LnFYufnMW4ch7qsKnru53QUxEtIzJqE822qj6w8ssRg,8576
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=LdAFJS7-xubWpRnrgdLOorCa9vB_8vRrvL5cahxHYNA,5442
20
- sgptools/utils/tsp.py,sha256=RJAQ4_uE7CUtR1ei3nSnGy-1kNhw82E9P_HyaCkc4iI,7007
21
- sgptools-1.1.3.dist-info/LICENSE.txt,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
22
- sgptools-1.1.3.dist-info/METADATA,sha256=c1XktvlxtIJuD2UHF9NsZmipVI5RJqNoeMlo_E94T_M,944
23
- sgptools-1.1.3.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
24
- sgptools-1.1.3.dist-info/top_level.txt,sha256=2NWH6uQLAOuLB9fG7o1pqf6Jvpe1_hEcuqfSqtUw3gw,9
25
- sgptools-1.1.3.dist-info/RECORD,,
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,,