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.
Files changed (40) hide show
  1. lsdo_function_spaces/__init__.py +64 -0
  2. lsdo_function_spaces/core/__init__.py +0 -0
  3. lsdo_function_spaces/core/function.py +1322 -0
  4. lsdo_function_spaces/core/function_set.py +1081 -0
  5. lsdo_function_spaces/core/function_set_space.py +379 -0
  6. lsdo_function_spaces/core/function_space.py +482 -0
  7. lsdo_function_spaces/core/operations/__init__.py +0 -0
  8. lsdo_function_spaces/core/operations/basic_ops.py +85 -0
  9. lsdo_function_spaces/core/operations/operations.py +5 -0
  10. lsdo_function_spaces/core/optimization.py +183 -0
  11. lsdo_function_spaces/core/spaces/__init__.py +0 -0
  12. lsdo_function_spaces/core/spaces/b_spline_space.py +418 -0
  13. lsdo_function_spaces/core/spaces/conditional_space.py +65 -0
  14. lsdo_function_spaces/core/spaces/constant_space.py +57 -0
  15. lsdo_function_spaces/core/spaces/idw_space.py +271 -0
  16. lsdo_function_spaces/core/spaces/non_cython_bsplines/__init__.py +0 -0
  17. lsdo_function_spaces/core/spaces/non_cython_bsplines/b_spline_csdl_custom_ops.py +420 -0
  18. lsdo_function_spaces/core/spaces/non_cython_bsplines/b_spline_patch_projection.py +1022 -0
  19. lsdo_function_spaces/core/spaces/non_cython_bsplines/b_spline_patch_projection_non_differentiable.py +186 -0
  20. lsdo_function_spaces/core/spaces/non_cython_bsplines/b_spline_patch_projection_optimized.py +594 -0
  21. lsdo_function_spaces/core/spaces/non_cython_bsplines/b_spline_space_new.py +6 -0
  22. lsdo_function_spaces/core/spaces/non_cython_bsplines/compute_basis_matrix_jax.py +172 -0
  23. lsdo_function_spaces/core/spaces/non_cython_bsplines/compute_basis_matrix_jax_factory.py +382 -0
  24. lsdo_function_spaces/core/spaces/non_cython_bsplines/compute_basis_matrix_jax_stencil.py +451 -0
  25. lsdo_function_spaces/core/spaces/non_cython_bsplines/compute_basis_matrix_numpy.py +249 -0
  26. lsdo_function_spaces/core/spaces/non_cython_bsplines/compute_basis_matrix_numpy_factory.py +391 -0
  27. lsdo_function_spaces/core/spaces/operation_space.py +64 -0
  28. lsdo_function_spaces/core/spaces/polynomial_space.py +79 -0
  29. lsdo_function_spaces/core/spaces/rbf_space.py +136 -0
  30. lsdo_function_spaces/core/spaces/tri_space.py +256 -0
  31. lsdo_function_spaces/utils/__init__.py +0 -0
  32. lsdo_function_spaces/utils/file_io.py +484 -0
  33. lsdo_function_spaces/utils/internal_utilities.py +11 -0
  34. lsdo_function_spaces/utils/plotting_functions.py +357 -0
  35. lsdo_function_spaces/utils/utility_functions.py +148 -0
  36. lsdo_function_spaces-1.0.0.dist-info/METADATA +189 -0
  37. lsdo_function_spaces-1.0.0.dist-info/RECORD +40 -0
  38. lsdo_function_spaces-1.0.0.dist-info/WHEEL +5 -0
  39. lsdo_function_spaces-1.0.0.dist-info/licenses/LICENSE.txt +165 -0
  40. lsdo_function_spaces-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,379 @@
1
+ import numpy as np
2
+ import scipy.sparse as sps
3
+ import lsdo_function_spaces as lfs
4
+ import csdl_alpha as csdl
5
+ from typing import Union
6
+ from dataclasses import dataclass
7
+
8
+ @dataclass
9
+ class FunctionSetSpace(lfs.FunctionSpace):
10
+ '''
11
+ Class for representing the space of a particular set of functions.
12
+
13
+ Attributes
14
+ ----------
15
+ num_parametric_dimensions : dict[int]
16
+ The number of parametric dimensions/variables for each B-spline that is a part of this B-spline set.
17
+ spaces : list[lfs.FuncctionSpace] -- list length = number of functions in the set
18
+ The function spaces that make up this FunctionSet.
19
+ connections : list[list[int]] = None
20
+ The connections between the B-splines in the set. If None, the B-splines are assumed to be independent.
21
+
22
+ Methods
23
+ -------
24
+ compute_basis_matrix(parametric_coordinates: np.ndarray, parametric_derivative_orders: np.ndarray = None) -> sps.csc_matrix:
25
+ Computes the basis matrix for the given parametric coordinates and derivative orders.
26
+ '''
27
+ num_parametric_dimensions : dict[int]
28
+ spaces : dict[lfs.FunctionSpace]
29
+ connections : dict[list[int]] = None
30
+
31
+ # @property
32
+ # def index_to_coefficient_indices(self) -> dict[int, list[int]]:
33
+ # return self._index_to_coefficient_indices
34
+
35
+ def __post_init__(self):
36
+ if isinstance(self.spaces, list):
37
+ self.spaces = {i:space for i, space in enumerate(self.spaces)}
38
+
39
+ def initialize_function(self, num_physical_dimensions:int, value:float=0, implicit:bool=False) -> tuple[csdl.Variable, lfs.FunctionSet]:
40
+ '''
41
+ Generates a function set with the given number of physical dimensions and initializes the coefficients to the given value.
42
+
43
+ Parameters
44
+ ----------
45
+ num_physical_dimensions : int
46
+ The number of physical dimensions for the function set.
47
+ value : float = 0
48
+ The value to initialize the coefficients to.
49
+ implicit : bool = False
50
+ If True, the coefficients are stored as an ImplicitVariable.
51
+
52
+ Returns
53
+ -------
54
+ coeff_var : csdl.Variable
55
+ Variable consisting of the stacked coefficients of each function in the set.
56
+ If implicit is True, the variable is an ImplicitVariable.
57
+ function_set : lfs.FunctionSet
58
+ The function set with the initialized coefficients.
59
+ '''
60
+
61
+ size = 0
62
+ for space in self.spaces.values():
63
+ size += np.prod(space.coefficients_shape)
64
+
65
+ if implicit:
66
+ coeff_var = csdl.ImplicitVariable((size, num_physical_dimensions), value=value)
67
+ else:
68
+ coeff_var = csdl.Variable((size, num_physical_dimensions), value=value)
69
+
70
+ functions = {}
71
+ start = 0
72
+ for i, space in self.spaces.items():
73
+ end = start + np.prod(space.coefficients_shape)
74
+ function = lfs.Function(space=space, coefficients=coeff_var[start:end,:].reshape(space.coefficients_shape + (num_physical_dimensions,)))
75
+ functions[i] = function
76
+ start = end
77
+
78
+ function_set = lfs.FunctionSet(functions=functions, space=self)
79
+
80
+ return coeff_var, function_set
81
+
82
+
83
+ def generate_parametric_grid(self, grid_resolution:Union[tuple[int,...], int]) -> list[tuple[int, np.ndarray]]:
84
+ '''
85
+ Generates a parametric grid for the function set space.
86
+
87
+ Parameters
88
+ ----------
89
+ grid_resolution : tuple[int,...] or int
90
+ The resolution of the grid in each parametric dimension.
91
+
92
+ Returns
93
+ -------
94
+ parametric_grid : list[tuple[int, np.ndarray]]
95
+ The grid of parametric coordinates for the FunctionSet (makes a grid of the specified resolution over each function in the set).
96
+ '''
97
+
98
+ parametric_grid = []
99
+ for i, space in self.spaces.items():
100
+ space_parametric_grid = space.generate_parametric_grid(grid_resolution=grid_resolution)
101
+ for j in range(space_parametric_grid.shape[0]):
102
+ parametric_grid.append((i, space_parametric_grid[j,:]))
103
+
104
+ return parametric_grid
105
+
106
+
107
+ def compute_basis_matrix(self, parametric_coordinates: list[tuple[int, np.ndarray]], parametric_derivative_orders: np.ndarray = None,
108
+ expansion_factor:int=None) -> sps.csc_matrix:
109
+ '''
110
+ Evaluates the basis functions of the B-spline at the given parametric coordinates and assembles it into a sparse matrix.
111
+
112
+ Parameters
113
+ ----------
114
+ parametric_coordinates : list[tuple[int, np.ndarray]] -- list of tuples of the form (index, parametric_coordinates) for each point to evaluate.
115
+ The parametric coordinates at which to evaluate the basis functions.
116
+ list indices correspond to points, tuple str is the B-spline name, and tuple np.ndarray is the parametric coordinates for that point.
117
+ parametric_derivative_orders : np.ndarray = None -- shape=(num_points, num_parametric_dimensions,)
118
+ The derivative orders for each parametric dimension.
119
+ expansion_factor : int = None
120
+ The number of times to repeat the basis functions in the basis matrix. This is useful if coefficients are flattened and
121
+ operations are restricted to matrix-vector products. If used, the expansion factor is usually the number of physical dimensions.
122
+ If None, the basis matrix will not be expanded.
123
+
124
+ Returns
125
+ -------
126
+ basis_matrix : sps.csc_matrix
127
+ The basis matrix evaluated at the given parametric coordinates (Evaluation of the appropriate basis functions and assembled into a matrix)
128
+ '''
129
+
130
+ if expansion_factor is None:
131
+ expansion_factor = 1
132
+
133
+ basis_matrix_rows = []
134
+ for i, parametric_coordinate in enumerate(parametric_coordinates):
135
+ index, parametric_coordinate = parametric_coordinate
136
+ space = self.spaces[index]
137
+ basis_matrix = space.compute_basis_matrix(parametric_coordinates=parametric_coordinate,
138
+ parametric_derivative_orders=parametric_derivative_orders[i],
139
+ expansion_factor=expansion_factor)
140
+ basis_matrix_rows.append(basis_matrix)
141
+
142
+ basis_matrix = sps.vstack(basis_matrix_rows, format='csc')
143
+ return basis_matrix
144
+
145
+
146
+ def fit(self, values:Union[csdl.Variable, np.ndarray], parametric_coordinates:list[tuple[int,np.ndarray]]=None,
147
+ parametric_derivative_orders:list[tuple]=None, basis_matrix:Union[sps.csc_matrix, np.ndarray]=None,
148
+ regularization_parameter:float=None) -> list[csdl.Variable]:
149
+ '''
150
+ Fits the function to the given data. Either parametric coordinates or an evaluation matrix must be provided. If derivatives are used, the
151
+ parametric derivative orders must be provided. If both parametric coordinates and an evaluation matrix are provided, the evaluation matrix
152
+ will be used.
153
+
154
+ Parameters
155
+ ----------
156
+ values : csdl.Variable|np.ndarray -- shape=(num_points,num_physical_dimensions)
157
+ The values of the data.
158
+ parametric_coordinates : list[tuple[int,np.ndarray]] -- list of tuples of the form (index, parametric_coordinates) for each value
159
+ The parametric coordinates of the data.
160
+ parametric_derivative_orders : np.ndarray = None -- list of tuples of shape=(num_parametric_dimensions,) for each value
161
+ The derivative orders to fit.
162
+ basis_matrix : sps.csc_matrix|np.ndarray = None -- shape=(num_points, num_coefficients)
163
+ The basis matrix to use for fitting.
164
+ regularization_parameter : float = None
165
+ The regularization parameter to use for fitting. If None, no regularization is used.
166
+
167
+ Returns
168
+ -------
169
+ coefficients : list[csdl.Variable]
170
+ The fitted coefficients for each function in the set.
171
+ '''
172
+ if parametric_coordinates is None and basis_matrix is None:
173
+ raise ValueError("Either parametric coordinates or an basis matrix must be provided.")
174
+
175
+ if parametric_coordinates is not None and basis_matrix is not None:
176
+ print("Both parametric coordinates and an basis matrix were provided. The basis matrix will be used.")
177
+ # raise Warning("Both parametric coordinates and an basis matrix were provided. The basis matrix will be used.")
178
+
179
+ if basis_matrix is not None:
180
+ # Just perform fitting using the basis matrix
181
+ raise NotImplementedError("Fitting using a basis matrix is not yet implemented.")
182
+ else: # I only have this else statement here because pylance is graying out everything below if I don't have it
183
+ # Perform fitting using the parametric coordinates
184
+ pass
185
+
186
+ # Current implementation: Perform fitting on each individual function in the set
187
+ num_physical_dimensions = values.shape[-1]
188
+
189
+ # Organize values into a list of values for each function in the set
190
+ values_per_function = {}
191
+ parametric_coordinates_per_function = {}
192
+ parametric_derivative_orders_per_function = {}
193
+ for i, space in self.spaces.items():
194
+ values_per_function[i] = []
195
+ parametric_coordinates_per_function[i] = []
196
+ parametric_derivative_orders_per_function[i] = None
197
+
198
+
199
+ for i, parametric_coordinate in enumerate(parametric_coordinates):
200
+ index, parametric_coordinate = parametric_coordinate
201
+ # values_per_function[index].append(values[i,:])
202
+ values_per_function[index].append(i)
203
+ parametric_coordinates_per_function[index].append(parametric_coordinate)
204
+ if parametric_derivative_orders is not None:
205
+ parametric_derivative_orders_per_function[index].append(parametric_derivative_orders[i])
206
+
207
+ for i, space in self.spaces.items():
208
+ if len(values_per_function[i]) > 0:
209
+ parametric_coordinates_per_function[i] = np.vstack(parametric_coordinates_per_function[i])
210
+
211
+ # Fit each function in the set
212
+ coefficients = {}
213
+ for i, space in self.spaces.items():
214
+ if len(values_per_function[i]) > 0:
215
+ # if isinstance(values, csdl.Variable):
216
+ # function_values = csdl.blockmat([[value.reshape((1, value.shape[0]))] for value in values_per_function[i]])
217
+ function_values = values[values_per_function[i]]
218
+ coefficients[i] = space.fit(values=function_values, parametric_coordinates=parametric_coordinates_per_function[i],
219
+ parametric_derivative_orders=None, regularization_parameter=regularization_parameter)
220
+ else:
221
+ # print(f"No data was provided for function {i}.")
222
+ # Kind of hacky way to get size of coefficients
223
+ parametric_coordinate = space.generate_parametric_grid(grid_resolution=(1,1))[0]
224
+ basis_vector = space.compute_basis_matrix(parametric_coordinates=parametric_coordinate)
225
+ num_coefficients = basis_vector.shape[1]
226
+ function_coefficients = csdl.Variable(value=np.zeros((num_coefficients,num_physical_dimensions)))
227
+ coefficients[i] = function_coefficients
228
+
229
+ return coefficients
230
+
231
+ def fit_monolithic(self, values:Union[csdl.Variable, np.ndarray], parametric_coordinates:list[tuple[int,np.ndarray]]=None,
232
+ parametric_derivative_orders:list[tuple]=None, basis_matrix:Union[sps.csc_matrix, np.ndarray]=None,
233
+ regularization_parameter:float=None) -> list[csdl.Variable]:
234
+ '''
235
+ Fits the function to the given data. Either parametric coordinates or an evaluation matrix must be provided. If derivatives are used, the
236
+ parametric derivative orders must be provided. If both parametric coordinates and an evaluation matrix are provided, the evaluation matrix
237
+ will be used.
238
+
239
+ Parameters
240
+ ----------
241
+ values : csdl.Variable|np.ndarray -- shape=(num_points,num_physical_dimensions)
242
+ The values of the data.
243
+ parametric_coordinates : list[tuple[int,np.ndarray]] -- list of tuples of the form (index, parametric_coordinates) for each value
244
+ The parametric coordinates of the data.
245
+ parametric_derivative_orders : np.ndarray = None -- list of tuples of shape=(num_parametric_dimensions,) for each value
246
+ The derivative orders to fit.
247
+ basis_matrix : sps.csc_matrix|np.ndarray = None -- shape=(num_points, num_coefficients)
248
+ The basis matrix to use for fitting.
249
+ regularization_parameter : float = None
250
+ The regularization parameter to use for fitting. If None, no regularization is used.
251
+
252
+ Returns
253
+ -------
254
+ coefficients : list[csdl.Variable]
255
+ The fitted coefficients for each function in the set.
256
+ '''
257
+ # Current implementation: Perform fitting on each individual function in the set
258
+ num_physical_dimensions = values.shape[-1]
259
+
260
+ # Organize values into a list of values for each function in the set
261
+ values_per_function = {}
262
+ parametric_coordinates_per_function = {}
263
+ parametric_derivative_orders_per_function = {}
264
+ for i, space in self.spaces.items():
265
+ values_per_function[i] = []
266
+ parametric_coordinates_per_function[i] = []
267
+ parametric_derivative_orders_per_function[i] = None
268
+
269
+
270
+ for i, parametric_coordinate in enumerate(parametric_coordinates):
271
+ index, parametric_coordinate = parametric_coordinate
272
+ # values_per_function[index].append(values[i,:])
273
+ values_per_function[index].append(i)
274
+ parametric_coordinates_per_function[index].append(parametric_coordinate)
275
+ if parametric_derivative_orders is not None:
276
+ parametric_derivative_orders_per_function[index].append(parametric_derivative_orders[i])
277
+
278
+ for i, space in self.spaces.items():
279
+ if len(values_per_function[i]) > 0:
280
+ parametric_coordinates_per_function[i] = np.vstack(parametric_coordinates_per_function[i])
281
+
282
+ # Fit each function in the set
283
+ coefficients = {}
284
+ for i, space in self.spaces.items():
285
+ if len(values_per_function[i]) > 0:
286
+ # if isinstance(values, csdl.Variable):
287
+ # function_values = csdl.blockmat([[value.reshape((1, value.shape[0]))] for value in values_per_function[i]])
288
+ function_values = values[values_per_function[i]]
289
+ coefficients[i] = space.fit(values=function_values, parametric_coordinates=parametric_coordinates_per_function[i],
290
+ parametric_derivative_orders=None, regularization_parameter=regularization_parameter)
291
+ else:
292
+ # print(f"No data was provided for function {i}.")
293
+ # Kind of hacky way to get size of coefficients
294
+ parametric_coordinate = space.generate_parametric_grid(grid_resolution=(1,1))[0]
295
+ basis_vector = space.compute_basis_matrix(parametric_coordinates=parametric_coordinate)
296
+ num_coefficients = basis_vector.shape[1]
297
+ function_coefficients = csdl.Variable(value=np.zeros((num_coefficients,num_physical_dimensions)))
298
+ coefficients[i] = function_coefficients
299
+
300
+ return coefficients
301
+
302
+
303
+ def fit_function_set(self, values:Union[csdl.Variable,np.ndarray], parametric_coordinates:list[tuple[int,np.ndarray]]=None,
304
+ parametric_derivative_orders:list[tuple]=None, basis_matrix:Union[sps.csc_matrix, np.ndarray]=None,
305
+ regularization_parameter:float=None) -> lfs.FunctionSet:
306
+ '''
307
+ Fits the function to the given data. Either parametric coordinates or an evaluation matrix must be provided. If derivatives are used, the
308
+ parametric derivative orders must be provided. If both parametric coordinates and an evaluation matrix are provided, the evaluation matrix
309
+ will be used.
310
+
311
+ Parameters
312
+ ----------
313
+ values : csdl.Variable|np.ndarray -- shape=(num_points,num_physical_dimensions)
314
+ The values of the data.
315
+ parametric_coordinates : list[tuple[int,np.ndarray]] -- list of tuples of the form (index, parametric_coordinates) for each value
316
+ The parametric coordinates of the data.
317
+ parametric_derivative_orders : np.ndarray = None -- list of tuples of shape=(num_parametric_dimensions,) for each value
318
+ The derivative orders to fit.
319
+ basis_matrix : sps.csc_matrix|np.ndarray = None -- shape=(num_points, num_coefficients)
320
+ The basis matrix to use for fitting.
321
+ regularization_parameter : float = None
322
+ The regularization parameter to use for fitting. If None, no regularization is used.
323
+
324
+
325
+ Returns
326
+ -------
327
+ function_set : lfs.FunctionSet
328
+ The fitted function set.
329
+ '''
330
+ coefficients = self.fit(values=values, parametric_coordinates=parametric_coordinates,
331
+ parametric_derivative_orders=parametric_derivative_orders, basis_matrix=basis_matrix,
332
+ regularization_parameter=regularization_parameter)
333
+
334
+ functions = {}
335
+ for i, function_coefficients in coefficients.items():
336
+ functions[i] = lfs.Function(space=self.spaces[i], coefficients=function_coefficients)
337
+
338
+ function_set = lfs.FunctionSet(functions=functions, space=self)
339
+
340
+ return function_set
341
+
342
+
343
+
344
+ # if __name__ == "__main__":
345
+ # import csdl_alpha as csdl
346
+ # recorder = csdl.Recorder(inline=True)
347
+ # recorder.start()
348
+
349
+ # num_coefficients1 = 10
350
+ # num_coefficients2 = 5
351
+ # degree1 = 4
352
+ # degree2 = 3
353
+
354
+ # space_of_cubic_b_spline_surfaces_with_10_cp = lfs.BSplineSpace(num_parametric_dimensions=2, degree=(degree1,degree1),
355
+ # coefficients_shape=(num_coefficients1,num_coefficients1, 3))
356
+ # space_of_quadratic_b_spline_surfaces_with_5_cp = lfs.BSplineSpace(num_parametric_dimensions=2, degree=(degree2,degree2),
357
+ # coefficients_shape=(num_coefficients2,num_coefficients2, 3))
358
+ # b_spline_spaces = [space_of_cubic_b_spline_surfaces_with_10_cp, space_of_quadratic_b_spline_surfaces_with_5_cp]
359
+ # index_to_space = {0:0, 1:1}
360
+ # name_to_index = {'space_of_cubic_b_spline_surfaces_with_10_cp':0, 'quadratic_b_spline_surfaces_5_cp':1}
361
+ # num_parametric_dimensions = {0:2, 1:2}
362
+
363
+ # b_spline_set_space = lfs.FunctionSetSpace(num_parametric_dimensions=num_parametric_dimensions, spaces=b_spline_spaces,
364
+ # index_to_space=index_to_space, name_to_index=name_to_index)
365
+
366
+ # coefficients_line = np.linspace(0., 1., num_coefficients1)
367
+ # coefficients_y, coefficients_x = np.meshgrid(coefficients_line,coefficients_line)
368
+ # coefficients1 = np.stack((coefficients_x, coefficients_y, 0.1*np.random.rand(num_coefficients1,num_coefficients1)), axis=-1)
369
+
370
+ # coefficients_line = np.linspace(0., 1., num_coefficients2)
371
+ # coefficients_y, coefficients_x = np.meshgrid(coefficients_line,coefficients_line)
372
+ # coefficients_y += 1.5
373
+ # coefficients2 = np.stack((coefficients_x, coefficients_y, 0.1*np.random.rand(num_coefficients2,num_coefficients2)), axis=-1)
374
+
375
+ # coefficients = np.vstack((coefficients1.reshape((-1,3)), coefficients2.reshape((-1,3))))
376
+ # coefficients = csdl.Variable(value=coefficients)
377
+
378
+ # my_b_spline_surface_set = lfs.Function(space=b_spline_set_space, coefficients=coefficients)
379
+ # my_b_spline_surface_set.plot()