morphopt 3.1.1__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.

Potentially problematic release.


This version of morphopt might be problematic. Click here for more details.

Files changed (49) hide show
  1. morphopt/__init__.py +32 -0
  2. morphopt/opt_runner.py +130 -0
  3. morphopt/optcore/baseobject.py +48 -0
  4. morphopt/optcore/controller.py +404 -0
  5. morphopt/optcore/history.py +440 -0
  6. morphopt/optcore/modelparams/__init__.py +4 -0
  7. morphopt/optcore/modelparams/base_params.py +97 -0
  8. morphopt/optcore/modelparams/feamodel/__init__.py +0 -0
  9. morphopt/optcore/modelparams/feamodel/feainterface/__init__.py +10 -0
  10. morphopt/optcore/modelparams/feamodel/feainterface/basefeainterface.py +50 -0
  11. morphopt/optcore/modelparams/feamodel/feainterface/bodyforceinterface.py +71 -0
  12. morphopt/optcore/modelparams/feamodel/feainterface/boundaryconditioninterface.py +59 -0
  13. morphopt/optcore/modelparams/feamodel/feainterface/contactinterface.py +113 -0
  14. morphopt/optcore/modelparams/feamodel/feainterface/coupleinterface.py +31 -0
  15. morphopt/optcore/modelparams/feamodel/feainterface/pointinterface.py +79 -0
  16. morphopt/optcore/modelparams/feamodel/feainterface/pressureinterface.py +68 -0
  17. morphopt/optcore/modelparams/feamodel/feainterface/referencepointinterface.py +29 -0
  18. morphopt/optcore/modelparams/feamodel/feainterface/springinterface.py +104 -0
  19. morphopt/optcore/modelparams/feamodel/feaparams.py +264 -0
  20. morphopt/optcore/modelparams/geometry/__init__.py +3 -0
  21. morphopt/optcore/modelparams/geometry/geometryinterfaces/__init__.py +1 -0
  22. morphopt/optcore/modelparams/geometry/geometryinterfaces/basesurfaceinterface.py +763 -0
  23. morphopt/optcore/modelparams/geometry/geometryinterfaces/bspsurfaceinterface.py +723 -0
  24. morphopt/optcore/modelparams/geometry/geometryinterfaces/cpgeosurfaceinterface.py +374 -0
  25. morphopt/optcore/modelparams/geometry/geometryparams.py +715 -0
  26. morphopt/optcore/modelparams/materials/__init__.py +1 -0
  27. morphopt/optcore/modelparams/materials/materialparams.py +110 -0
  28. morphopt/optcore/modelparams/params.py +88 -0
  29. morphopt/optcore/objfunc.py +248 -0
  30. morphopt/optcore/solver.py +159 -0
  31. morphopt/optcore/updaters/__init__.py +2 -0
  32. morphopt/optcore/updaters/base_updater.py +110 -0
  33. morphopt/optcore/updaters/geometry/__init__.py +2 -0
  34. morphopt/optcore/updaters/geometry/objectivefuncs/__init__.py +5 -0
  35. morphopt/optcore/updaters/geometry/objectivefuncs/basefuncs.py +117 -0
  36. morphopt/optcore/updaters/geometry/objectivefuncs/boundarys.py +91 -0
  37. morphopt/optcore/updaters/geometry/objectivefuncs/distancesurface.py +123 -0
  38. morphopt/optcore/updaters/geometry/objectivefuncs/shapederivative.py +179 -0
  39. morphopt/optcore/updaters/geometry/objectivefuncs/surfacefairness.py +40 -0
  40. morphopt/optcore/updaters/geometry/update_geometry.py +347 -0
  41. morphopt/optcore/updaters/optimizer.py +177 -0
  42. morphopt/optcore/updaters/updaters.py +82 -0
  43. morphopt/optcore/utils/plot_history_surface.py +239 -0
  44. morphopt/taskoptmization.py +75 -0
  45. morphopt/taskui.py +458 -0
  46. morphopt-3.1.1.dist-info/METADATA +242 -0
  47. morphopt-3.1.1.dist-info/RECORD +49 -0
  48. morphopt-3.1.1.dist-info/WHEEL +5 -0
  49. morphopt-3.1.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,347 @@
1
+
2
+ import numpy as np
3
+
4
+ import torch
5
+ from .. import optimizer
6
+
7
+ from ...modelparams.params import Params
8
+ from ...modelparams import GeometryParams, FEAParams, Materials
9
+ from tabulate import tabulate
10
+ from ..base_updater import BaseUpdater
11
+
12
+ class UpdaterGeometries(BaseUpdater):
13
+ """
14
+ The Updater class is responsible for updating the parameters of the optimization process.
15
+ It contains methods to update the parameters based on the optimization algorithm used.
16
+ """
17
+ from . import objectivefuncs
18
+
19
+ def __init__(self, params: Params, max_step_iter: int = 50, max_step_length: float = 0.5, reset_sensitivity_scaler_per_iter: int = 1) -> None:
20
+ """
21
+ Initialize the Updater class with the given parameters.
22
+
23
+ Parameters:
24
+ surfaces (Surfaces): The surfaces object that contains the design variables.
25
+ max_step_iter (int): The maximum number of iterations for the sub-optimization process.
26
+ max_step_length_surf (list[float]): The maximum step length for each surface in the optimization process.
27
+ """
28
+
29
+ super().__init__(params)
30
+
31
+ self.max_step_iter = max_step_iter
32
+ """
33
+ The maximum number of iterations for the sub-optimization process.
34
+ """
35
+
36
+ self.constraints_funcs: dict[str, UpdaterGeometries.objectivefuncs.BaseConstraints] = {}
37
+ """
38
+ A list of penalty functions to be optimized. \n
39
+ L = sum_{i=1}^{n} w_i * f_i(x)
40
+ """
41
+
42
+ self.obj_funcs: dict[str, UpdaterGeometries.objectivefuncs.BaseObjective] = {}
43
+ """
44
+ A list of objective functions to be optimized. \n
45
+ L = sum_{i=1}^{n} w_i * f_i(x)
46
+ """
47
+
48
+ self._delta_control_points_previous: list[np.ndarray] = None
49
+ """
50
+ The previous change in control points for each surface.
51
+ """
52
+
53
+ self._weight_points: list[torch.Tensor] = []
54
+ """
55
+ The weights for the points in the optimization process.
56
+ """
57
+
58
+ self.params_update: GeometryParams = params.geometry
59
+ """
60
+ The surfaces object that contains the design variables.
61
+ """
62
+
63
+ self._reset_sensitivity_scaler_per_iter = reset_sensitivity_scaler_per_iter
64
+ """
65
+ The number of iterations after which the scaler is reset.
66
+ """
67
+
68
+ self._max_step_length_max: float = max_step_length
69
+ """
70
+ The maximum step length for each surface in the optimization process.
71
+ """
72
+
73
+ self._max_step_length: list[torch.Tensor] = []
74
+ """
75
+ The current maximum step length for each surface in the optimization process.
76
+ """
77
+
78
+ self._step_length_min_ratio: float = 0.1
79
+ """
80
+ The minimum ratio for the step length relative to the maximum step length.
81
+ """
82
+
83
+ self._step_length_decay: float = 0.5
84
+ """
85
+ The decay factor for the step length relative to the maximum step length.
86
+ """
87
+
88
+ self._step_length_increase: float = 1.5
89
+ """
90
+ The increase factor for the step length relative to the maximum step length.
91
+ """
92
+
93
+ self.if_update: list[float] = None
94
+ """
95
+ A list indicating whether each surface needs to be updated.
96
+ True means the surface needs to be updated, False means it does not.
97
+ """
98
+
99
+ def pathlog_required(self):
100
+ return ['geometryupdater']
101
+
102
+ def add_constraints(self,
103
+ obj_func: objectivefuncs.basefuncs,
104
+ name: str = None) -> None:
105
+ """
106
+ Add an objective function to the list of objective functions.
107
+
108
+ Parameters:
109
+ obj_func (ObjectiveFuncs.BaseObj): The objective function to be added.
110
+ """
111
+ if name is None:
112
+ name = obj_func.__class__.__name__
113
+
114
+ extra_num = 0
115
+ while name + '_%d' % extra_num in self.constraints_funcs.keys():
116
+ extra_num += 1
117
+
118
+ name = name + '_%d' % extra_num
119
+ self.constraints_funcs[name] = obj_func
120
+
121
+ def add_objective_function(self,
122
+ obj_func: objectivefuncs.basefuncs,
123
+ name: str = None) -> None:
124
+ """
125
+ Add an objective function to the list of objective functions.
126
+
127
+ Parameters:
128
+ obj_func (ObjectiveFuncs.BaseObj): The objective function to be added.
129
+ """
130
+ if name is None:
131
+ name = obj_func.__class__.__name__
132
+
133
+ extra_num = 0
134
+ while name + '_%d' % extra_num in self.obj_funcs.keys():
135
+ extra_num += 1
136
+
137
+ name = name + '_%d' % extra_num
138
+ self.obj_funcs[name] = obj_func
139
+
140
+ def reinitialize(self, *args, **kwargs) -> None:
141
+ """
142
+ Initialize the parameters of the optimization process.
143
+
144
+ Parameters:
145
+ iter_now (int): The current iteration number.
146
+ """
147
+
148
+ # get the weights for the points in the optimization process
149
+ self._weight_points = self.params_update.get_points_weight()
150
+
151
+ # initialize the objective function
152
+ r0, rdu0, rdu20 = self.params_update.get_geometry_values()
153
+
154
+ # initialize the shape derivative sensitivity
155
+ self._initialize_objectives(r0=r0, rdu0=rdu0, rdu20=rdu20)
156
+
157
+ # get the total sensitivity
158
+ sensitivity = self._get_total_sensitivity()
159
+
160
+ # initialize the constraint functions
161
+ self._initialize_constraints(r0=r0, rdu0=rdu0, rdu20=rdu20, sensitivity=sensitivity)
162
+
163
+ # initialize the optimizer
164
+ self._initialize_optimizer()
165
+
166
+ # initialize the step length
167
+ for i in range(len(self._max_step_length)):
168
+ if (self._max_step_length[i].shape[0] != self.params.geometry.surface_list[i].control_points.flatten().shape[0] // 3):
169
+ self._max_step_length[i] = self._max_step_length[i].mean().repeat(self.params.geometry.surface_list[i].num_variables // 3)
170
+
171
+ # save the sensitivity for the next iteration
172
+ self.sensitivity_previous = sensitivity
173
+
174
+ def initialize(self):
175
+
176
+ if self.if_update is None:
177
+ self.if_update = [True for _ in range(self.params.geometry.num_surface)]
178
+
179
+ if len(self._max_step_length) == 0:
180
+ for i in range(self.params_update.num_surface):
181
+ self._max_step_length.append(torch.ones(self.params.geometry.surface_list[i].num_variables // 3) * self._max_step_length_max * 0.5)
182
+
183
+ for i in range(len(self._max_step_length)):
184
+ if not self.if_update[i]:
185
+ self._max_step_length[i] *= 0.0
186
+
187
+ def _initialize_objectives(self, r0: list[torch.Tensor], rdu0: list[torch.Tensor], rdu20: list[torch.Tensor]) -> None:
188
+ for obj_func in self.obj_funcs.values():
189
+ obj_func.initialize(r0=r0, rdu0=rdu0, rdu20=rdu20, weights=self._weight_points)
190
+
191
+ def _get_total_sensitivity(self) -> None:
192
+ # get the total sensitivity
193
+ sensitivity_all = []
194
+ for obj_func in self.obj_funcs.values():
195
+ sensitivity_all.append(obj_func.sensitivity)
196
+ sensitivity: list[torch.Tensor] = sensitivity_all[0]
197
+ for surf_ind in range(len(sensitivity)):
198
+ for obj_ind in range(1, len(sensitivity_all)):
199
+ sensitivity[surf_ind] += sensitivity_all[obj_ind][surf_ind]
200
+ return sensitivity
201
+
202
+ def _initialize_constraints(self, r0: list[torch.Tensor], rdu0: list[torch.Tensor], rdu20: list[torch.Tensor], sensitivity: list[torch.Tensor]) -> None:
203
+ for constraints in self.constraints_funcs.values():
204
+ constraints.initialize(r0=r0, rdu0=rdu0, rdu20=rdu20, sensitivity=sensitivity, weights=self._weight_points)
205
+
206
+ def _initialize_optimizer(self) -> None:
207
+ self.optimizer = optimizer.LBFGS(closure=self.closure, num_limit=20, tol_error=1e-10)
208
+ self.iteration_total = 0
209
+
210
+ def _update_step_length(self, delta_control_points: list[np.ndarray]) -> None:
211
+
212
+ # update the step length based on the number of variables
213
+ if self._delta_control_points_previous is not None:
214
+ for i in range(len(self._max_step_length)):
215
+ # check if the mesh has improved
216
+ if (self._delta_control_points_previous[i].shape != self.params.geometry.surface_list[i].control_points.shape):
217
+ self._max_step_length[i] = self._max_step_length[i].mean().repeat(self.params.geometry.surface_list[i].num_variables // 3)
218
+ else:
219
+ delta_difference: np.ndarray = np.sum(self._delta_control_points_previous[i] * delta_control_points[i], axis=-1) / (np.linalg.norm(delta_control_points[i], axis=-1)) / np.linalg.norm(self._delta_control_points_previous[i], axis=-1)
220
+ delta_difference[np.isnan(delta_difference)] = 0.0
221
+
222
+ index_increase = (delta_difference > -0.5).flatten()
223
+ index_decrease = (delta_difference <= -0.5).flatten()
224
+ self._max_step_length[i][index_increase] = torch.clamp(self._max_step_length[i][index_increase] * self._step_length_increase,
225
+ max=self._max_step_length_max)
226
+ self._max_step_length[i][index_decrease] = torch.clamp(self._max_step_length[i][index_decrease] * self._step_length_decay,
227
+ min=self._max_step_length_max * self._step_length_min_ratio)
228
+ for i in range(len(self._max_step_length)):
229
+ if not self.if_update[i]:
230
+ self._max_step_length[i] *= 0.0
231
+
232
+ def closure(self, x: torch.Tensor, return_list=False) -> float:
233
+ """
234
+ The closure function for the optimization process.
235
+
236
+ Parameters:
237
+ x (torch.Tensor): The current point in the optimization process.
238
+
239
+ Returns:
240
+ float: The objective function value at the current point.
241
+ """
242
+ # save the current point
243
+ x0 = self.params_update.get_parameters()
244
+
245
+ # Set the design variables to the current point
246
+ self.params_update.update_variables(x_change=x, max_step_length=self._max_step_length)
247
+
248
+ # Calculate the objective function value
249
+ r, rdu, rdu2 = self.params_update.get_geometry_values()
250
+
251
+ constraints_value: list[torch.Tensor] = []
252
+ for constraints in self.constraints_funcs.values():
253
+ constraints_value.append(constraints(r=r, rdu=rdu, rdu2=rdu2))
254
+
255
+ obj_value: list[torch.Tensor] = []
256
+ for obj_func in self.obj_funcs.values():
257
+ obj_value.append(obj_func(r=r, rdu=rdu, rdu2=rdu2))
258
+
259
+ # enroll the design variables
260
+ self.params_update.set_parameters(xlist=x0)
261
+
262
+ if return_list:
263
+ return obj_value, constraints_value
264
+ else:
265
+ return sum(obj_value) + sum(constraints_value)
266
+
267
+ def update(self) -> torch.Tensor:
268
+ """
269
+ Update the parameters of the optimization process.
270
+ """
271
+
272
+ # initialize the optimizer
273
+ self.reinitialize()
274
+
275
+ # update the objective function
276
+ variables = self.params_update.get_variables().detach().clone()
277
+
278
+ # print the information
279
+ print("\n\n")
280
+ print("Start updating the surfaces...")
281
+
282
+
283
+ low_step_length_iter = 0
284
+ gk_new = None
285
+ for iteration in range(self.max_step_iter):
286
+ self.iteration_total += 1
287
+
288
+ # get the current variables of the surfaces
289
+ alpha, delta_var, gk_new = self.optimizer.step(x_now=variables, gk_now= gk_new)
290
+ variables.data += delta_var * alpha
291
+
292
+ # check if the step length is too small
293
+ if abs(alpha) < 1e-10:
294
+ low_step_length_iter += 1
295
+
296
+ if low_step_length_iter > 10:
297
+ print(
298
+ f"Low step length detected ({low_step_length_iter} iterations), stopping optimization."
299
+ )
300
+ # break
301
+
302
+ # get current objective function value
303
+ if self.iteration_total % 10 == 0:
304
+ with torch.no_grad():
305
+ obj_values, constraints_values = self.closure(x=variables, return_list=True)
306
+
307
+ # print the objective function value
308
+ # Print a pretty table showing objective values and iteration progress
309
+ # Clear previous output (move cursor up and clear lines)
310
+ if iteration > 0:
311
+ print("\033[F\033[K" * 4, end="\r")
312
+
313
+ headers = ["Iteration"] + ["Total"] + list(self.obj_funcs.keys()) + list(self.constraints_funcs.keys())
314
+ data = [[f"{iteration+1}/{self.max_step_iter}"] +
315
+ [f"{sum(obj_values).item():.6e}"] +
316
+ [f"{val.item():.6e}" for val in obj_values] +
317
+ [f"{val.item():.6e}" for val in constraints_values]]
318
+
319
+ string = tabulate(data, headers=headers, tablefmt="grid")
320
+ print(string, end="\r")
321
+
322
+ return variables.detach().clone()
323
+
324
+ def update_variables(self, dx: torch.Tensor) -> None:
325
+ """
326
+ Update the variables of the surfaces.
327
+ """
328
+ control_points0 = self.params_update.get_control_points_list()
329
+ control_points0 = [cp.detach().clone().cpu().numpy() for cp in control_points0]
330
+
331
+ self.params_update.update_variables(x_change=dx, max_step_length=self._max_step_length)
332
+
333
+ control_points_new = self.params_update.get_control_points_list()
334
+ control_points_new = [cp.detach().clone().cpu().numpy() for cp in control_points_new]
335
+ delta_control_points = [control_points_new[i] - control_points0[i] for i in range(len(control_points0))]
336
+ self._update_step_length(delta_control_points=delta_control_points)
337
+
338
+ self._delta_control_points_previous = [cp.copy() for cp in delta_control_points]
339
+
340
+ def save(self, foldpath, iteration):
341
+ step_length_numpy = [self._max_step_length[i].detach().cpu().numpy() for i in range(len(self._max_step_length))]
342
+ data = np.savez(foldpath + self.pathlog_required()[0] + f"/step_length_{iteration}.npz", *step_length_numpy)
343
+
344
+ def load(self, foldpath, iteration):
345
+ data = np.load(foldpath + self.pathlog_required()[0] + f"/step_length_{iteration}.npz")
346
+ for i in range(len(self._max_step_length)):
347
+ self._max_step_length[i] = torch.tensor(data['arr_%d' % i]).to(self.params_update.surface_list[i].model.control_points.device)
@@ -0,0 +1,177 @@
1
+ import torch
2
+
3
+
4
+ class BaseOpt():
5
+ def __init__(self, closure: callable):
6
+ self.closure = closure
7
+ """
8
+ the objective function to be optimized, which should return the objective value
9
+ """
10
+
11
+ self.grad = torch.func.jacrev(self.closure)
12
+ """ the gradient of the objective function, which should return the gradient vector
13
+ """
14
+
15
+ # params for line search
16
+ self._c1 = 1e-4
17
+ self._rou1 = 0.2
18
+
19
+ def set_line_search_params(self, c1: float = None, rou1: float = None):
20
+ """
21
+ Set the parameters for the line search method.
22
+
23
+ Parameters:
24
+ c1 (float): The parameter for the Wolfe condition.
25
+ rou1 (float): The parameter for the backtracking line search.
26
+ """
27
+ if c1 is not None:
28
+ self._c1 = c1
29
+ if rou1 is not None:
30
+ self._rou1 = rou1
31
+
32
+ def _LineSearchBacktracking(self, x0: torch.Tensor, dx: torch.Tensor, direction: torch.Tensor, alpha0: float = 1.0, obj0: float = None):
33
+
34
+ """
35
+ Backtracking line search to find the step length for the optimization process.
36
+
37
+ Parameters:
38
+ x0 (torch.Tensor): The current point in the optimization process.
39
+ dx (torch.Tensor): The gradient vector at the current point.
40
+ direction (torch.Tensor): The search direction.
41
+ alpha0 (float): The initial step length.
42
+ c1 (float): The parameter for the Wolfe condition.
43
+ rou1 (float): The parameter for the backtracking line search.
44
+ obj0 (float): The initial objective value.
45
+
46
+ Returns:
47
+ float: The step length that satisfies the Wolfe condition.
48
+ """
49
+
50
+ if obj0 is None:
51
+ obj0 = self.closure(x0)
52
+
53
+ alpha = alpha0
54
+ while True:
55
+ x_new = x0 + alpha * direction
56
+ with torch.no_grad():
57
+ obj_new = self.closure(x_new)
58
+ if ~torch.isnan(obj_new) and ~torch.isinf(
59
+ obj_new
60
+ ) and obj_new < obj0 + self._c1 * alpha * dx.dot(direction):
61
+ return alpha, obj_new
62
+ alpha *= self._rou1
63
+ if (alpha * direction).abs().max() < 1e-14:
64
+ return 0., obj0
65
+
66
+ def step(x_now: torch.Tensor, gk_now: torch.Tensor=None) -> torch.Tensor:
67
+ """
68
+ Perform a single optimization step.
69
+ """
70
+ pass
71
+
72
+ class LBFGS(BaseOpt):
73
+ def __init__(self,
74
+ closure: callable,
75
+ num_limit:int = 10,
76
+ tol_error:float = 1e-10) -> None:
77
+ super(LBFGS, self).__init__(closure=closure)
78
+ self.SK = []
79
+ self.YK = []
80
+ self.rhok = []
81
+
82
+ self.num_limit = num_limit
83
+ """
84
+ the number of pairs of Hessian and gradient stored in the memory
85
+ """
86
+
87
+ self.tol_error = tol_error
88
+ """
89
+ the tolerance for the convergence of the optimization process
90
+ """
91
+
92
+ def Hg_loop(self, dv: torch.Tensor) -> torch.Tensor:
93
+ """
94
+ Compute the product of the inverse Hessian matrix and the gradient vector.
95
+ This is done using the BFGS update formula.
96
+
97
+ Parameters:
98
+ dv (torch.Tensor): The gradient vector.
99
+
100
+ Returns:
101
+ torch.Tensor: The product of the inverse Hessian matrix and the gradient vector.
102
+ """
103
+
104
+ q = dv.clone()
105
+ alpha = torch.zeros(len(self.SK))
106
+ for i in range(len(self.SK) - 1, -1, -1):
107
+ alpha[i] = self.rhok[i] * self.SK[i].dot(q)
108
+ q = q - alpha[i] * self.YK[i]
109
+
110
+ y = q / (self.rhok[-1] * self.YK[-1].dot(self.YK[-1]))
111
+
112
+ for i in range(len(self.SK)):
113
+ beta = self.rhok[i] * self.YK[i].dot(y)
114
+ y = y + (alpha[i] - beta) * self.SK[i]
115
+
116
+ return y
117
+
118
+ def step(self, x_now: torch.Tensor, gk_now: torch.Tensor=None):
119
+
120
+ obj_now = self.closure(x_now)
121
+ if gk_now is None:
122
+ gk_now: torch.Tensor = self.grad(x_now).flatten()
123
+
124
+ gk_now.view(-1)[gk_now.view(-1).isnan()] = 0
125
+
126
+ # if the first iteration, use steepest descent direction
127
+ if len(self.SK) == 0:
128
+ dk = -gk_now
129
+ else:
130
+ dk = self.Hg_loop(-gk_now)
131
+ if dk.view(-1).isnan().any():
132
+ dk = -gk_now
133
+ self.SK = []
134
+ self.YK = []
135
+ self.rhok = []
136
+
137
+ # if the gradient is not positive, use steepest descent direction
138
+ if (dk * gk_now).sum() > 0:
139
+ dk = -dk
140
+
141
+ # line search
142
+ alpha, obj_new = self._LineSearchBacktracking(x0=x_now, dx=gk_now, direction=dk, alpha0=1., obj0=obj_now)
143
+
144
+ # if the step length is too small, stop the iteration
145
+ if abs(alpha) <= self.tol_error:
146
+ self.SK = []
147
+ self.YK = []
148
+ self.rhok = []
149
+
150
+ x_new = x_now + alpha * dk
151
+
152
+
153
+ if obj_new>obj_now:
154
+ self.SK = []
155
+ self.YK = []
156
+ self.rhok = []
157
+ return 0., torch.zeros_like(x_now), gk_now
158
+
159
+ gk_new: torch.Tensor =self.grad(x_new).flatten()
160
+
161
+ yk = gk_new.flatten() - gk_now.flatten()
162
+ sk = alpha * dk.flatten()
163
+
164
+ # BFGS method
165
+ if yk.norm() > 1e-14 and sk.norm() > 1e-14:
166
+ self.SK.append(sk)
167
+ self.YK.append(yk)
168
+ self.rhok.append(1 / yk.dot(sk))
169
+
170
+ if len(self.SK) > self.num_limit:
171
+ self.SK = self.SK[1:]
172
+ self.YK = self.YK[1:]
173
+ self.rhok = self.rhok[1:]
174
+
175
+ return alpha, dk, gk_new
176
+
177
+
@@ -0,0 +1,82 @@
1
+
2
+ import torch
3
+
4
+ from ..baseobject import BaseObject
5
+
6
+ from .geometry.update_geometry import UpdaterGeometries
7
+
8
+
9
+ class Updaters(BaseObject):
10
+ """
11
+ This class is responsible for updating the morphologies of the neurons.
12
+ """
13
+
14
+ def __init__(self, surfaces:UpdaterGeometries=None, *args, **kwargs):
15
+ """
16
+ Initialize the Updaters class with a neuron object.
17
+
18
+ Args:
19
+ surfaces (UpdaterSurfaces, optional): An instance of the UpdaterSurfaces class for updating the surfaces.
20
+ loads (UpdaterLoads, optional): An instance of the UpdaterLoads class for updating the loads.
21
+ materials (UpdaterMaterials, optional): An instance of the UpdaterMaterials class for updating the materials.
22
+ """
23
+ self._surface: UpdaterGeometries = None
24
+ """
25
+ UpdaterSurfaces: An instance of the UpdaterSurfaces class for updating the surfaces.
26
+ """
27
+ self.if_update_surface = False
28
+ """
29
+ if_update_surface: A flag indicating whether the surface needs to be updated.
30
+ """
31
+
32
+ if surfaces is not None:
33
+ self._surface = surfaces
34
+ self.if_update_surface = True
35
+ self._var_surface: torch.Tensor = None
36
+ """
37
+ var_surface: The updated surface variables.
38
+ """
39
+
40
+ def reinitialize(self, iteration: int) -> None:
41
+ """
42
+ reInitialize the Updaters class.
43
+ This method reinitializes the surfaces, loads, and materials if they are present.
44
+ """
45
+ pass
46
+
47
+ def initialize(self) -> None:
48
+ """
49
+ Initialize the Updaters class.
50
+ This method initializes the surfaces, loads, and materials if they are present.
51
+ """
52
+ if self.if_update_surface:
53
+ self._surface.initialize()
54
+
55
+ def update(self) -> torch.Tensor:
56
+ """
57
+ Update the morphology of the neuron.
58
+ """
59
+
60
+ if self.if_update_surface:
61
+ self._var_surface = self._surface.update()
62
+
63
+ def update_variables(self) -> None:
64
+ """
65
+ Update the variables of the surfaces and loads.
66
+ """
67
+ if self.if_update_surface:
68
+ self._surface.update_variables(dx=self._var_surface)
69
+
70
+ def save(self, foldpath: str, iteration: int) -> None:
71
+ """
72
+ Save the updater state to a file.
73
+ """
74
+ if self.if_update_surface:
75
+ self._surface.save(foldpath=foldpath, iteration=iteration)
76
+
77
+
78
+ def pathlog_required(self):
79
+ paths = []
80
+ if self.if_update_surface:
81
+ paths += self._surface.pathlog_required()
82
+ return paths