lsdo-function-spaces 1.0.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.
- lsdo_function_spaces/__init__.py +64 -0
- lsdo_function_spaces/core/__init__.py +0 -0
- lsdo_function_spaces/core/function.py +1322 -0
- lsdo_function_spaces/core/function_set.py +1081 -0
- lsdo_function_spaces/core/function_set_space.py +379 -0
- lsdo_function_spaces/core/function_space.py +482 -0
- lsdo_function_spaces/core/operations/__init__.py +0 -0
- lsdo_function_spaces/core/operations/basic_ops.py +85 -0
- lsdo_function_spaces/core/operations/operations.py +5 -0
- lsdo_function_spaces/core/optimization.py +183 -0
- lsdo_function_spaces/core/spaces/__init__.py +0 -0
- lsdo_function_spaces/core/spaces/b_spline_space.py +418 -0
- lsdo_function_spaces/core/spaces/conditional_space.py +65 -0
- lsdo_function_spaces/core/spaces/constant_space.py +57 -0
- lsdo_function_spaces/core/spaces/idw_space.py +271 -0
- lsdo_function_spaces/core/spaces/non_cython_bsplines/__init__.py +0 -0
- lsdo_function_spaces/core/spaces/non_cython_bsplines/b_spline_csdl_custom_ops.py +420 -0
- lsdo_function_spaces/core/spaces/non_cython_bsplines/b_spline_patch_projection.py +1022 -0
- lsdo_function_spaces/core/spaces/non_cython_bsplines/b_spline_patch_projection_non_differentiable.py +186 -0
- lsdo_function_spaces/core/spaces/non_cython_bsplines/b_spline_patch_projection_optimized.py +594 -0
- lsdo_function_spaces/core/spaces/non_cython_bsplines/b_spline_space_new.py +6 -0
- lsdo_function_spaces/core/spaces/non_cython_bsplines/compute_basis_matrix_jax.py +172 -0
- lsdo_function_spaces/core/spaces/non_cython_bsplines/compute_basis_matrix_jax_factory.py +382 -0
- lsdo_function_spaces/core/spaces/non_cython_bsplines/compute_basis_matrix_jax_stencil.py +451 -0
- lsdo_function_spaces/core/spaces/non_cython_bsplines/compute_basis_matrix_numpy.py +249 -0
- lsdo_function_spaces/core/spaces/non_cython_bsplines/compute_basis_matrix_numpy_factory.py +391 -0
- lsdo_function_spaces/core/spaces/operation_space.py +64 -0
- lsdo_function_spaces/core/spaces/polynomial_space.py +79 -0
- lsdo_function_spaces/core/spaces/rbf_space.py +136 -0
- lsdo_function_spaces/core/spaces/tri_space.py +256 -0
- lsdo_function_spaces/utils/__init__.py +0 -0
- lsdo_function_spaces/utils/file_io.py +484 -0
- lsdo_function_spaces/utils/internal_utilities.py +11 -0
- lsdo_function_spaces/utils/plotting_functions.py +357 -0
- lsdo_function_spaces/utils/utility_functions.py +148 -0
- lsdo_function_spaces-1.0.0.dist-info/METADATA +189 -0
- lsdo_function_spaces-1.0.0.dist-info/RECORD +40 -0
- lsdo_function_spaces-1.0.0.dist-info/WHEEL +5 -0
- lsdo_function_spaces-1.0.0.dist-info/licenses/LICENSE.txt +165 -0
- lsdo_function_spaces-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import scipy.sparse as sps
|
|
3
|
+
from ..function_space import LinearFunctionSpace
|
|
4
|
+
from scipy.spatial.distance import cdist
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Union
|
|
7
|
+
|
|
8
|
+
class ConstantSpace(LinearFunctionSpace):
|
|
9
|
+
"""
|
|
10
|
+
Constant Function Space.
|
|
11
|
+
|
|
12
|
+
This function space represents a constant value in a parametric space.
|
|
13
|
+
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
16
|
+
num_parametric_dimensions : int
|
|
17
|
+
The number of parametric dimensions.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, num_parametric_dimensions:int):
|
|
21
|
+
super().__init__(num_parametric_dimensions, (1,))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def compute_basis_matrix(self, parametric_coordinates:np.ndarray, parametric_derivative_orders: np.ndarray=None, expansion_factor:int=None) -> np.ndarray:
|
|
25
|
+
"""
|
|
26
|
+
Compute the basis matrix for the given parametric coordinates.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
parametric_coordinates : np.ndarray
|
|
31
|
+
The parametric coordinates for which to compute the basis matrix.
|
|
32
|
+
parametric_derivative_orders : np.ndarray, optional
|
|
33
|
+
The derivative orders of the parametric coordinates. Default is None.
|
|
34
|
+
expansion_factor : int, optional
|
|
35
|
+
The expansion factor. Default is None.
|
|
36
|
+
|
|
37
|
+
Returns
|
|
38
|
+
-------
|
|
39
|
+
np.ndarray
|
|
40
|
+
The computed basis matrix.
|
|
41
|
+
|
|
42
|
+
Raises
|
|
43
|
+
------
|
|
44
|
+
NotImplementedError
|
|
45
|
+
If parametric_derivative_orders or expansion_factor is not None.
|
|
46
|
+
|
|
47
|
+
"""
|
|
48
|
+
if parametric_derivative_orders is not None:
|
|
49
|
+
raise NotImplementedError('IDWFunctionSpace does not support derivatives')
|
|
50
|
+
if expansion_factor is not None:
|
|
51
|
+
raise NotImplementedError('IDWFunctionSpace does not support expansion factors')
|
|
52
|
+
|
|
53
|
+
if len(parametric_coordinates.shape) == 1:
|
|
54
|
+
parametric_coordinates = parametric_coordinates.reshape(1, -1)
|
|
55
|
+
|
|
56
|
+
weights = np.ones((parametric_coordinates.shape[0], 1))
|
|
57
|
+
return weights
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import scipy.sparse as sps
|
|
3
|
+
from lsdo_function_spaces.core.function_space import LinearFunctionSpace
|
|
4
|
+
from scipy.spatial.distance import cdist
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Union
|
|
7
|
+
import csdl_alpha as csdl
|
|
8
|
+
|
|
9
|
+
class IDWFunctionSpace(LinearFunctionSpace):
|
|
10
|
+
"""
|
|
11
|
+
Inverse Distance Weighting (IDW) Function Space.
|
|
12
|
+
|
|
13
|
+
This function space represents a grid of points in a parametric space using the Inverse Distance Weighting method.
|
|
14
|
+
It provides methods to compute the basis matrix and the fitting map.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
num_parametric_dimensions : int
|
|
19
|
+
The number of parametric dimensions.
|
|
20
|
+
order : float
|
|
21
|
+
The order of the inverse distance weighting function.
|
|
22
|
+
conserve : bool, optional
|
|
23
|
+
If True, the weights will be normalized to conserve the sum of the values. Default is True.
|
|
24
|
+
grid_size : tuple, optional
|
|
25
|
+
The size of the grid in each parametric dimension. Default is (10,).
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, num_parametric_dimensions:int, order:float, points:np.ndarray=None, conserve:bool=True, grid_size:Union[int, tuple]=10, n_neighbors:int=None):
|
|
29
|
+
"""
|
|
30
|
+
Initialize an IDW function space.
|
|
31
|
+
|
|
32
|
+
Parameters
|
|
33
|
+
----------
|
|
34
|
+
order : float
|
|
35
|
+
The order of the inverse distance weighting function.
|
|
36
|
+
conserve : bool, optional
|
|
37
|
+
If True, the weights will be normalized to conserve the sum of the values. Default is True.
|
|
38
|
+
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
self.order = order
|
|
42
|
+
self.conserve = conserve
|
|
43
|
+
self.grid_size = grid_size
|
|
44
|
+
self.points = points
|
|
45
|
+
self.n_neighbors = n_neighbors
|
|
46
|
+
|
|
47
|
+
if n_neighbors is not None and conserve:
|
|
48
|
+
raise ValueError('IDWFunctionSpace does not support n_neighbors and conserve=True simultaneously')
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
if self.points is None:
|
|
52
|
+
if isinstance(self.grid_size, int):
|
|
53
|
+
self.grid_size = (self.grid_size,)*num_parametric_dimensions
|
|
54
|
+
linspaces = [np.linspace(0, 1, n) for n in self.grid_size]
|
|
55
|
+
self.points = np.array(np.meshgrid(*linspaces)).T.reshape(-1, num_parametric_dimensions)
|
|
56
|
+
|
|
57
|
+
if n_neighbors is not None:
|
|
58
|
+
if n_neighbors > self.points.shape[0]:
|
|
59
|
+
raise ValueError('n_neighbors cannot be greater than the number of points')
|
|
60
|
+
if n_neighbors < 1:
|
|
61
|
+
raise ValueError('n_neighbors must be greater than 0')
|
|
62
|
+
|
|
63
|
+
super().__init__(num_parametric_dimensions, (self.points.shape[0], 1))
|
|
64
|
+
|
|
65
|
+
def stitch(self, self_face, self_coeffs, other, other_face, other_coeffs):
|
|
66
|
+
"""
|
|
67
|
+
Stitch two IDW function spaces together.
|
|
68
|
+
|
|
69
|
+
Parameters
|
|
70
|
+
----------
|
|
71
|
+
self_face : int
|
|
72
|
+
The face of the current function space.
|
|
73
|
+
other : IDWFunctionSpace
|
|
74
|
+
The other function space to stitch.
|
|
75
|
+
other_face : int
|
|
76
|
+
The face of the other function space.
|
|
77
|
+
|
|
78
|
+
Returns
|
|
79
|
+
-------
|
|
80
|
+
IDWFunctionSpace
|
|
81
|
+
The stitched function space.
|
|
82
|
+
|
|
83
|
+
"""
|
|
84
|
+
if self_face == 1:
|
|
85
|
+
self_inds = np.where(self.points[:, 1] == 0)
|
|
86
|
+
self_free_index = 0
|
|
87
|
+
elif self_face == 2:
|
|
88
|
+
self_inds = np.where(self.points[:, 0] == 1)
|
|
89
|
+
self_free_index = 1
|
|
90
|
+
elif self_face == 3:
|
|
91
|
+
self_inds = np.where(self.points[:, 1] == 1)
|
|
92
|
+
self_free_index = 0
|
|
93
|
+
elif self_face == 4:
|
|
94
|
+
self_inds = np.where(self.points[:, 0] == 0)
|
|
95
|
+
self_free_index = 1
|
|
96
|
+
self_inds = [int(ind) for ind in self_inds[0]]
|
|
97
|
+
|
|
98
|
+
if other_face == 1:
|
|
99
|
+
other_inds = np.where(other.points[:, 1] == 0)
|
|
100
|
+
other_free_index = 0
|
|
101
|
+
elif other_face == 2:
|
|
102
|
+
other_inds = np.where(other.points[:, 0] == 1)
|
|
103
|
+
other_free_index = 1
|
|
104
|
+
elif other_face == 3:
|
|
105
|
+
other_inds = np.where(other.points[:, 1] == 1)
|
|
106
|
+
other_free_index = 0
|
|
107
|
+
elif other_face == 4:
|
|
108
|
+
other_inds = np.where(other.points[:, 0] == 0)
|
|
109
|
+
other_free_index = 1
|
|
110
|
+
other_inds = [int(ind) for ind in other_inds[0]]
|
|
111
|
+
|
|
112
|
+
other_inds_sorted = []
|
|
113
|
+
for i, point in enumerate(self.points[self_inds]):
|
|
114
|
+
for j, other_point in enumerate(other.points[other_inds]):
|
|
115
|
+
if np.allclose(point[self_free_index], other_point[other_free_index]):
|
|
116
|
+
other_inds_sorted.append(other_inds[j])
|
|
117
|
+
break
|
|
118
|
+
|
|
119
|
+
if len(other_inds_sorted) != len(self_inds):
|
|
120
|
+
raise ValueError('Could not find all corresponding points between the two faces')
|
|
121
|
+
|
|
122
|
+
for i, j in csdl.frange(vals=(self_inds, other_inds_sorted)):
|
|
123
|
+
self_face_coeffs = self_coeffs[i]
|
|
124
|
+
other_face_coeffs = other_coeffs[j]
|
|
125
|
+
average_coeffs = (self_face_coeffs + other_face_coeffs)/2
|
|
126
|
+
self_coeffs = self_coeffs.set(csdl.slice[i], average_coeffs)
|
|
127
|
+
other_coeffs = other_coeffs.set(csdl.slice[j], average_coeffs)
|
|
128
|
+
|
|
129
|
+
return self_coeffs, other_coeffs
|
|
130
|
+
|
|
131
|
+
def compute_basis_matrix(self, parametric_coordinates:np.ndarray, parametric_derivative_orders: np.ndarray=None, expansion_factor:int=None) -> np.ndarray:
|
|
132
|
+
"""
|
|
133
|
+
Compute the basis matrix for the given parametric coordinates.
|
|
134
|
+
|
|
135
|
+
Parameters
|
|
136
|
+
----------
|
|
137
|
+
parametric_coordinates : np.ndarray
|
|
138
|
+
The parametric coordinates for which to compute the basis matrix.
|
|
139
|
+
parametric_derivative_orders : np.ndarray, optional
|
|
140
|
+
The derivative orders of the parametric coordinates. Default is None.
|
|
141
|
+
expansion_factor : int, optional
|
|
142
|
+
The expansion factor. Default is None.
|
|
143
|
+
|
|
144
|
+
Returns
|
|
145
|
+
-------
|
|
146
|
+
np.ndarray
|
|
147
|
+
The computed basis matrix.
|
|
148
|
+
|
|
149
|
+
Raises
|
|
150
|
+
------
|
|
151
|
+
NotImplementedError
|
|
152
|
+
If parametric_derivative_orders or expansion_factor is not None.
|
|
153
|
+
|
|
154
|
+
"""
|
|
155
|
+
# if parametric_derivative_orders is not None:
|
|
156
|
+
# raise NotImplementedError('IDWFunctionSpace does not support derivatives')
|
|
157
|
+
# if expansion_factor is not None:
|
|
158
|
+
# raise NotImplementedError('IDWFunctionSpace does not support expansion factors')
|
|
159
|
+
|
|
160
|
+
if len(parametric_coordinates.shape) == 1:
|
|
161
|
+
parametric_coordinates = parametric_coordinates.reshape(1, -1)
|
|
162
|
+
|
|
163
|
+
if self.n_neighbors is None:
|
|
164
|
+
dist = cdist(self.points, parametric_coordinates)
|
|
165
|
+
with np.errstate(divide='ignore', invalid='ignore'):
|
|
166
|
+
weights = 1.0/dist**self.order
|
|
167
|
+
if self.conserve:
|
|
168
|
+
weights = weights.T
|
|
169
|
+
weights /= weights.sum(axis=0)
|
|
170
|
+
else:
|
|
171
|
+
weights /= weights.sum(axis=0)
|
|
172
|
+
weights = weights.T
|
|
173
|
+
np.nan_to_num(weights, copy=False, nan=1.)
|
|
174
|
+
else:
|
|
175
|
+
# assemble a sparse matrix with the weights of the n_neighbors closest points
|
|
176
|
+
from sklearn.neighbors import NearestNeighbors
|
|
177
|
+
nbrs = NearestNeighbors(n_neighbors=self.n_neighbors, algorithm='ball_tree').fit(self.points)
|
|
178
|
+
distances, indices = nbrs.kneighbors(parametric_coordinates)
|
|
179
|
+
with np.errstate(divide='ignore', invalid='ignore'):
|
|
180
|
+
weights = 1.0/distances**self.order
|
|
181
|
+
weights /= weights.sum(axis=1)[:, np.newaxis]
|
|
182
|
+
np.nan_to_num(weights, copy=False, nan=1.)
|
|
183
|
+
inv_indices = np.repeat(np.arange(indices.shape[0]), indices.shape[1])
|
|
184
|
+
weights = sps.csr_matrix((weights.ravel(), (inv_indices, indices.ravel())), shape=(parametric_coordinates.shape[0], self.points.shape[0]))
|
|
185
|
+
|
|
186
|
+
return weights
|
|
187
|
+
|
|
188
|
+
def compute_fitting_map(self, parametric_coordinates:np.ndarray, parametric_derivative_orders: np.ndarray=None) -> np.ndarray:
|
|
189
|
+
"""
|
|
190
|
+
Compute the fitting map for the given parametric coordinates.
|
|
191
|
+
|
|
192
|
+
Parameters
|
|
193
|
+
----------
|
|
194
|
+
parametric_coordinates : np.ndarray
|
|
195
|
+
The parametric coordinates for which to compute the fitting map.
|
|
196
|
+
parametric_derivative_orders : np.ndarray, optional
|
|
197
|
+
The derivative orders of the parametric coordinates. Default is None.
|
|
198
|
+
|
|
199
|
+
Returns
|
|
200
|
+
-------
|
|
201
|
+
np.ndarray
|
|
202
|
+
The computed fitting map.
|
|
203
|
+
|
|
204
|
+
Raises
|
|
205
|
+
------
|
|
206
|
+
NotImplementedError
|
|
207
|
+
If parametric_derivative_orders is not None.
|
|
208
|
+
|
|
209
|
+
"""
|
|
210
|
+
# if parametric_derivative_orders is not None:
|
|
211
|
+
# raise NotImplementedError('IDWFunctionSpace does not support derivatives')
|
|
212
|
+
|
|
213
|
+
parametric_coordinates = parametric_coordinates.reshape(-1, self.num_parametric_dimensions)
|
|
214
|
+
if self.n_neighbors is None:
|
|
215
|
+
dist = cdist(parametric_coordinates, self.points)
|
|
216
|
+
with np.errstate(divide='ignore', invalid='ignore'):
|
|
217
|
+
weights = 1.0/dist**self.order
|
|
218
|
+
if self.conserve:
|
|
219
|
+
weights = weights.T
|
|
220
|
+
weights /= weights.sum(axis=0)
|
|
221
|
+
else:
|
|
222
|
+
weights /= weights.sum(axis=0)
|
|
223
|
+
weights = weights.T
|
|
224
|
+
np.nan_to_num(weights, copy=False, nan=1.)
|
|
225
|
+
else:
|
|
226
|
+
if self.n_neighbors > parametric_coordinates.shape[0]:
|
|
227
|
+
n_neighbors = parametric_coordinates.shape[0]
|
|
228
|
+
else:
|
|
229
|
+
n_neighbors = self.n_neighbors
|
|
230
|
+
# assemble a sparse matrix with the weights of the n_neighbors closest points
|
|
231
|
+
from sklearn.neighbors import NearestNeighbors
|
|
232
|
+
nbrs = NearestNeighbors(n_neighbors=n_neighbors, algorithm='ball_tree').fit(parametric_coordinates)
|
|
233
|
+
distances, indices = nbrs.kneighbors(self.points)
|
|
234
|
+
with np.errstate(divide='ignore', invalid='ignore'):
|
|
235
|
+
weights = 1.0/distances**self.order
|
|
236
|
+
weights /= weights.sum(axis=1)[:,np.newaxis]
|
|
237
|
+
np.nan_to_num(weights, copy=False, nan=1.)
|
|
238
|
+
inv_indices = np.repeat(np.arange(indices.shape[0]), indices.shape[1])
|
|
239
|
+
weights = sps.csr_matrix((weights.ravel(), (inv_indices, indices.ravel())), shape=(self.points.shape[0], parametric_coordinates.shape[0]))
|
|
240
|
+
return weights
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def test_idw_space():
|
|
244
|
+
import numpy as np
|
|
245
|
+
import csdl_alpha as csdl
|
|
246
|
+
|
|
247
|
+
rec = csdl.Recorder(inline=True)
|
|
248
|
+
rec.start()
|
|
249
|
+
|
|
250
|
+
space = IDWFunctionSpace(2, 2, grid_size=4)
|
|
251
|
+
parametric_coordinates = np.random.rand(100, 2)
|
|
252
|
+
data = 10*np.random.rand(100, 1)
|
|
253
|
+
function = space.fit_function(data, parametric_coordinates)
|
|
254
|
+
eval_data = function.evaluate(parametric_coordinates)
|
|
255
|
+
|
|
256
|
+
space = IDWFunctionSpace(2, 2, grid_size=4, conserve=False)
|
|
257
|
+
sparse_space = IDWFunctionSpace(2, 2, grid_size=4, conserve=False, n_neighbors=3)
|
|
258
|
+
parametric_coordinates = np.random.rand(100, 2)
|
|
259
|
+
data = 10*np.random.rand(100, 1)
|
|
260
|
+
function = space.fit_function(data, parametric_coordinates)
|
|
261
|
+
eval_data = function.evaluate(parametric_coordinates)
|
|
262
|
+
sparse_function = sparse_space.fit_function(data, parametric_coordinates)
|
|
263
|
+
sparse_eval_data = sparse_function.evaluate(parametric_coordinates)
|
|
264
|
+
print('eval_data:', eval_data.value)
|
|
265
|
+
print('sparse_eval_data:', sparse_eval_data.value)
|
|
266
|
+
# print(eval_data.value - data)
|
|
267
|
+
|
|
268
|
+
# print(function.coefficients.value)
|
|
269
|
+
|
|
270
|
+
if __name__ == '__main__':
|
|
271
|
+
test_idw_space()
|
|
File without changes
|