MultiOptPy 1.20.5__py3-none-any.whl → 1.20.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.
- multioptpy/MD/thermostat.py +236 -123
- multioptpy/ModelHessian/fischerd3.py +240 -295
- multioptpy/Optimizer/rsirfo.py +112 -4
- multioptpy/Optimizer/rsprfo.py +1005 -698
- multioptpy/entrypoints.py +406 -16
- multioptpy/moleculardynamics.py +21 -13
- {multioptpy-1.20.5.dist-info → multioptpy-1.20.6.dist-info}/METADATA +9 -9
- {multioptpy-1.20.5.dist-info → multioptpy-1.20.6.dist-info}/RECORD +12 -12
- {multioptpy-1.20.5.dist-info → multioptpy-1.20.6.dist-info}/WHEEL +1 -1
- {multioptpy-1.20.5.dist-info → multioptpy-1.20.6.dist-info}/entry_points.txt +0 -0
- {multioptpy-1.20.5.dist-info → multioptpy-1.20.6.dist-info}/licenses/LICENSE +0 -0
- {multioptpy-1.20.5.dist-info → multioptpy-1.20.6.dist-info}/top_level.txt +0 -0
multioptpy/Optimizer/rsirfo.py
CHANGED
|
@@ -125,7 +125,69 @@ class RSIRFO:
|
|
|
125
125
|
self.alpha_init_values = [0.001 + (10.0 - 0.001) * i / 14 for i in range(15)]
|
|
126
126
|
self.NEB_mode = False
|
|
127
127
|
|
|
128
|
-
|
|
128
|
+
def _project_grad_tr_rot(self, gradient, geometry):
|
|
129
|
+
"""
|
|
130
|
+
[Quick Implementation] Project out translation and rotation components from the gradient.
|
|
131
|
+
Uses QR decomposition for basis orthonormalization.
|
|
132
|
+
"""
|
|
133
|
+
# Data preparation
|
|
134
|
+
coords = geometry.reshape(-1, 3)
|
|
135
|
+
n_atoms = coords.shape[0]
|
|
136
|
+
|
|
137
|
+
# 1. Center coordinates (Critical for rotation)
|
|
138
|
+
center = np.mean(coords, axis=0)
|
|
139
|
+
coords_centered = coords - center
|
|
140
|
+
|
|
141
|
+
# 2. Construct basis vectors for TR/ROT (Total 6 vectors)
|
|
142
|
+
# Flattened vectors of size 3*N
|
|
143
|
+
basis = []
|
|
144
|
+
|
|
145
|
+
# Translation (x, y, z)
|
|
146
|
+
basis.append(np.tile([1, 0, 0], n_atoms))
|
|
147
|
+
basis.append(np.tile([0, 1, 0], n_atoms))
|
|
148
|
+
basis.append(np.tile([0, 0, 1], n_atoms))
|
|
149
|
+
|
|
150
|
+
# Rotation (Rx, Ry, Rz via cross product)
|
|
151
|
+
# Rx: (1,0,0) x r -> (0, -z, y)
|
|
152
|
+
rx = np.zeros_like(coords)
|
|
153
|
+
rx[:, 1] = -coords_centered[:, 2]
|
|
154
|
+
rx[:, 2] = coords_centered[:, 1]
|
|
155
|
+
basis.append(rx.flatten())
|
|
156
|
+
|
|
157
|
+
# Ry: (0,1,0) x r -> (z, 0, -x)
|
|
158
|
+
ry = np.zeros_like(coords)
|
|
159
|
+
ry[:, 0] = coords_centered[:, 2]
|
|
160
|
+
ry[:, 2] = -coords_centered[:, 0]
|
|
161
|
+
basis.append(ry.flatten())
|
|
162
|
+
|
|
163
|
+
# Rz: (0,0,1) x r -> (-y, x, 0)
|
|
164
|
+
rz = np.zeros_like(coords)
|
|
165
|
+
rz[:, 0] = -coords_centered[:, 1]
|
|
166
|
+
rz[:, 1] = coords_centered[:, 0]
|
|
167
|
+
basis.append(rz.flatten())
|
|
168
|
+
|
|
169
|
+
# 3. Orthonormalize basis (QR decomposition)
|
|
170
|
+
# A: Matrix of shape (3N, 6)
|
|
171
|
+
A = np.array(basis).T
|
|
172
|
+
|
|
173
|
+
# 'reduced' returns Q of shape (3N, K) where K <= 6
|
|
174
|
+
# This handles linear molecules automatically (rank deficient)
|
|
175
|
+
Q, _ = np.linalg.qr(A, mode='reduced')
|
|
176
|
+
|
|
177
|
+
# 4. Project out components
|
|
178
|
+
# P = I - Q * Q.T
|
|
179
|
+
# g_proj = g - Q * (Q.T * g)
|
|
180
|
+
|
|
181
|
+
# Calculate overlap (scalar components along TR/ROT modes)
|
|
182
|
+
overlaps = np.dot(Q.T, gradient)
|
|
183
|
+
|
|
184
|
+
# Reconstruct the TR/ROT part of the gradient
|
|
185
|
+
tr_rot_part = np.dot(Q, overlaps)
|
|
186
|
+
|
|
187
|
+
# Subtract from original gradient
|
|
188
|
+
projected_gradient = gradient - tr_rot_part
|
|
189
|
+
|
|
190
|
+
return projected_gradient
|
|
129
191
|
|
|
130
192
|
def _build_hessian_updater_list(self):
|
|
131
193
|
"""
|
|
@@ -267,6 +329,22 @@ class RSIRFO:
|
|
|
267
329
|
# Ensure gradient is properly shaped as a 1D array (reuse existing array without copy)
|
|
268
330
|
gradient = np.asarray(B_g).ravel()
|
|
269
331
|
|
|
332
|
+
# === [ADDED] Project out TR/ROT from Gradient ===
|
|
333
|
+
# Calculate norm before projection
|
|
334
|
+
raw_norm = np.linalg.norm(gradient)
|
|
335
|
+
|
|
336
|
+
# Apply projection
|
|
337
|
+
gradient = self._project_grad_tr_rot(gradient, geom_num_list)
|
|
338
|
+
|
|
339
|
+
# Calculate norm after projection
|
|
340
|
+
proj_norm = np.linalg.norm(gradient)
|
|
341
|
+
diff_norm = raw_norm - proj_norm
|
|
342
|
+
|
|
343
|
+
# Log the effect (Even small changes matter for RFO stability)
|
|
344
|
+
if abs(diff_norm) > 1e-12:
|
|
345
|
+
self.log(f"Gradient TR/ROT Projection: Norm {raw_norm:.6e} -> {proj_norm:.6e} (Diff: {diff_norm:.6e})", force=True)
|
|
346
|
+
# ================================================
|
|
347
|
+
|
|
270
348
|
# Use effective Hessian
|
|
271
349
|
tmp_hess = self.hessian
|
|
272
350
|
if self.bias_hessian is not None:
|
|
@@ -280,11 +358,24 @@ class RSIRFO:
|
|
|
280
358
|
H = 0.5 * (H + H.T) # Ensure symmetry
|
|
281
359
|
# Use new method that applies/removes shift for numerical stability
|
|
282
360
|
eigvals, eigvecs = self.compute_eigendecomposition_with_shift(H)
|
|
283
|
-
|
|
361
|
+
|
|
362
|
+
# === [CRITICAL FIX] Handle NaN/Inf in Hessian ===
|
|
363
|
+
# If Hessian is broken (common in linear molecules or initial guesses),
|
|
364
|
+
# force fallback to identity matrix to act as Steepest Descent.
|
|
365
|
+
if not np.all(np.isfinite(eigvals)) or not np.all(np.isfinite(eigvecs)):
|
|
366
|
+
self.log("CRITICAL ERROR: Hessian eigendecomposition failed (NaNs detected).", force=True)
|
|
367
|
+
self.log("Resetting to Identity Hessian to force Steepest Descent fallback.", force=True)
|
|
368
|
+
eigvals = np.ones_like(eigvals)
|
|
369
|
+
eigvecs = np.eye(len(eigvals))
|
|
370
|
+
# =================================================
|
|
284
371
|
# Always check conditioning (provides useful diagnostic information)
|
|
285
372
|
condition_number, is_ill_conditioned = self.check_hessian_conditioning(eigvals)
|
|
286
|
-
|
|
287
|
-
|
|
373
|
+
# === [MODIFIED START] Handle None value for condition_number ===
|
|
374
|
+
if condition_number is not None:
|
|
375
|
+
print(f"Condition number of Hessian: {condition_number:.2f}, Ill-conditioned: {is_ill_conditioned}")
|
|
376
|
+
else:
|
|
377
|
+
print(f"Condition number of Hessian: N/A, Ill-conditioned: {is_ill_conditioned}")
|
|
378
|
+
# === [MODIFIED END] ===
|
|
288
379
|
|
|
289
380
|
# Trust Radius Adjustment (Moved here to use eigenvalues)
|
|
290
381
|
if not self.Initialization:
|
|
@@ -335,6 +426,13 @@ class RSIRFO:
|
|
|
335
426
|
|
|
336
427
|
eigvals_star, eigvecs_star = self.compute_eigendecomposition_with_shift(H_star)
|
|
337
428
|
|
|
429
|
+
# === [CRITICAL FIX] Handle NaN/Inf in Projected Hessian ===
|
|
430
|
+
if not np.all(np.isfinite(eigvals_star)) or not np.all(np.isfinite(eigvecs_star)):
|
|
431
|
+
self.log("CRITICAL ERROR: Projected Hessian is broken. Falling back to identity.", force=True)
|
|
432
|
+
eigvals_star = np.ones_like(eigvals_star)
|
|
433
|
+
eigvecs_star = np.eye(len(eigvals_star))
|
|
434
|
+
# ==========================================================
|
|
435
|
+
|
|
338
436
|
# === Apply existing small eigenvalue filter ===
|
|
339
437
|
# This is INDEPENDENT of level-shifting.
|
|
340
438
|
# Level-shifting affects numerical stability during computation.
|
|
@@ -354,6 +452,16 @@ class RSIRFO:
|
|
|
354
452
|
# Get the RS step using the image Hessian and gradient
|
|
355
453
|
move_vector = self.get_rs_step(eigvals_star, eigvecs_star, grad_star)
|
|
356
454
|
|
|
455
|
+
# === [CRITICAL FIX] Final NaN Check on Step ===
|
|
456
|
+
if not np.all(np.isfinite(move_vector)):
|
|
457
|
+
self.log("CRITICAL ERROR: Calculated RS-I-RFO step is NaN. Forcing steepest descent.", force=True)
|
|
458
|
+
# Steepest descent fallback
|
|
459
|
+
move_vector = -gradient
|
|
460
|
+
norm = np.linalg.norm(move_vector)
|
|
461
|
+
if norm > self.trust_radius:
|
|
462
|
+
move_vector *= (self.trust_radius / norm)
|
|
463
|
+
# ==============================================
|
|
464
|
+
|
|
357
465
|
# Update prev_eigvec_size for next iteration
|
|
358
466
|
self.prev_eigvec_size = current_eigvec_size
|
|
359
467
|
|