MultiOptPy 1.20.2__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 (246) hide show
  1. multioptpy/Calculator/__init__.py +0 -0
  2. multioptpy/Calculator/ase_calculation_tools.py +424 -0
  3. multioptpy/Calculator/ase_tools/__init__.py +0 -0
  4. multioptpy/Calculator/ase_tools/fairchem.py +28 -0
  5. multioptpy/Calculator/ase_tools/gamess.py +19 -0
  6. multioptpy/Calculator/ase_tools/gaussian.py +165 -0
  7. multioptpy/Calculator/ase_tools/mace.py +28 -0
  8. multioptpy/Calculator/ase_tools/mopac.py +19 -0
  9. multioptpy/Calculator/ase_tools/nwchem.py +31 -0
  10. multioptpy/Calculator/ase_tools/orca.py +22 -0
  11. multioptpy/Calculator/ase_tools/pygfn0.py +37 -0
  12. multioptpy/Calculator/dxtb_calculation_tools.py +344 -0
  13. multioptpy/Calculator/emt_calculation_tools.py +458 -0
  14. multioptpy/Calculator/gpaw_calculation_tools.py +183 -0
  15. multioptpy/Calculator/lj_calculation_tools.py +314 -0
  16. multioptpy/Calculator/psi4_calculation_tools.py +334 -0
  17. multioptpy/Calculator/pwscf_calculation_tools.py +189 -0
  18. multioptpy/Calculator/pyscf_calculation_tools.py +327 -0
  19. multioptpy/Calculator/sqm1_calculation_tools.py +611 -0
  20. multioptpy/Calculator/sqm2_calculation_tools.py +376 -0
  21. multioptpy/Calculator/tblite_calculation_tools.py +352 -0
  22. multioptpy/Calculator/tersoff_calculation_tools.py +818 -0
  23. multioptpy/Constraint/__init__.py +0 -0
  24. multioptpy/Constraint/constraint_condition.py +834 -0
  25. multioptpy/Coordinate/__init__.py +0 -0
  26. multioptpy/Coordinate/polar_coordinate.py +199 -0
  27. multioptpy/Coordinate/redundant_coordinate.py +638 -0
  28. multioptpy/IRC/__init__.py +0 -0
  29. multioptpy/IRC/converge_criteria.py +28 -0
  30. multioptpy/IRC/dvv.py +544 -0
  31. multioptpy/IRC/euler.py +439 -0
  32. multioptpy/IRC/hpc.py +564 -0
  33. multioptpy/IRC/lqa.py +540 -0
  34. multioptpy/IRC/modekill.py +662 -0
  35. multioptpy/IRC/rk4.py +579 -0
  36. multioptpy/Interpolation/__init__.py +0 -0
  37. multioptpy/Interpolation/adaptive_interpolation.py +283 -0
  38. multioptpy/Interpolation/binomial_interpolation.py +179 -0
  39. multioptpy/Interpolation/geodesic_interpolation.py +785 -0
  40. multioptpy/Interpolation/interpolation.py +156 -0
  41. multioptpy/Interpolation/linear_interpolation.py +473 -0
  42. multioptpy/Interpolation/savitzky_golay_interpolation.py +252 -0
  43. multioptpy/Interpolation/spline_interpolation.py +353 -0
  44. multioptpy/MD/__init__.py +0 -0
  45. multioptpy/MD/thermostat.py +185 -0
  46. multioptpy/MEP/__init__.py +0 -0
  47. multioptpy/MEP/pathopt_bneb_force.py +443 -0
  48. multioptpy/MEP/pathopt_dmf_force.py +448 -0
  49. multioptpy/MEP/pathopt_dneb_force.py +130 -0
  50. multioptpy/MEP/pathopt_ewbneb_force.py +207 -0
  51. multioptpy/MEP/pathopt_gpneb_force.py +512 -0
  52. multioptpy/MEP/pathopt_lup_force.py +113 -0
  53. multioptpy/MEP/pathopt_neb_force.py +225 -0
  54. multioptpy/MEP/pathopt_nesb_force.py +205 -0
  55. multioptpy/MEP/pathopt_om_force.py +153 -0
  56. multioptpy/MEP/pathopt_qsm_force.py +174 -0
  57. multioptpy/MEP/pathopt_qsmv2_force.py +304 -0
  58. multioptpy/ModelFunction/__init__.py +7 -0
  59. multioptpy/ModelFunction/avoiding_model_function.py +29 -0
  60. multioptpy/ModelFunction/binary_image_ts_search_model_function.py +47 -0
  61. multioptpy/ModelFunction/conical_model_function.py +26 -0
  62. multioptpy/ModelFunction/opt_meci.py +50 -0
  63. multioptpy/ModelFunction/opt_mesx.py +47 -0
  64. multioptpy/ModelFunction/opt_mesx_2.py +49 -0
  65. multioptpy/ModelFunction/seam_model_function.py +27 -0
  66. multioptpy/ModelHessian/__init__.py +0 -0
  67. multioptpy/ModelHessian/approx_hessian.py +147 -0
  68. multioptpy/ModelHessian/calc_params.py +227 -0
  69. multioptpy/ModelHessian/fischer.py +236 -0
  70. multioptpy/ModelHessian/fischerd3.py +360 -0
  71. multioptpy/ModelHessian/fischerd4.py +398 -0
  72. multioptpy/ModelHessian/gfn0xtb.py +633 -0
  73. multioptpy/ModelHessian/gfnff.py +709 -0
  74. multioptpy/ModelHessian/lindh.py +165 -0
  75. multioptpy/ModelHessian/lindh2007d2.py +707 -0
  76. multioptpy/ModelHessian/lindh2007d3.py +822 -0
  77. multioptpy/ModelHessian/lindh2007d4.py +1030 -0
  78. multioptpy/ModelHessian/morse.py +106 -0
  79. multioptpy/ModelHessian/schlegel.py +144 -0
  80. multioptpy/ModelHessian/schlegeld3.py +322 -0
  81. multioptpy/ModelHessian/schlegeld4.py +559 -0
  82. multioptpy/ModelHessian/shortrange.py +346 -0
  83. multioptpy/ModelHessian/swartd2.py +496 -0
  84. multioptpy/ModelHessian/swartd3.py +706 -0
  85. multioptpy/ModelHessian/swartd4.py +918 -0
  86. multioptpy/ModelHessian/tshess.py +40 -0
  87. multioptpy/Optimizer/QHAdam.py +61 -0
  88. multioptpy/Optimizer/__init__.py +0 -0
  89. multioptpy/Optimizer/abc_fire.py +83 -0
  90. multioptpy/Optimizer/adabelief.py +58 -0
  91. multioptpy/Optimizer/adabound.py +68 -0
  92. multioptpy/Optimizer/adadelta.py +65 -0
  93. multioptpy/Optimizer/adaderivative.py +56 -0
  94. multioptpy/Optimizer/adadiff.py +68 -0
  95. multioptpy/Optimizer/adafactor.py +70 -0
  96. multioptpy/Optimizer/adam.py +65 -0
  97. multioptpy/Optimizer/adamax.py +62 -0
  98. multioptpy/Optimizer/adamod.py +83 -0
  99. multioptpy/Optimizer/adamw.py +65 -0
  100. multioptpy/Optimizer/adiis.py +523 -0
  101. multioptpy/Optimizer/afire_neb.py +282 -0
  102. multioptpy/Optimizer/block_hessian_update.py +709 -0
  103. multioptpy/Optimizer/c2diis.py +491 -0
  104. multioptpy/Optimizer/component_wise_scaling.py +405 -0
  105. multioptpy/Optimizer/conjugate_gradient.py +82 -0
  106. multioptpy/Optimizer/conjugate_gradient_neb.py +345 -0
  107. multioptpy/Optimizer/coordinate_locking.py +405 -0
  108. multioptpy/Optimizer/dic_rsirfo.py +1015 -0
  109. multioptpy/Optimizer/ediis.py +417 -0
  110. multioptpy/Optimizer/eve.py +76 -0
  111. multioptpy/Optimizer/fastadabelief.py +61 -0
  112. multioptpy/Optimizer/fire.py +77 -0
  113. multioptpy/Optimizer/fire2.py +249 -0
  114. multioptpy/Optimizer/fire_neb.py +92 -0
  115. multioptpy/Optimizer/gan_step.py +486 -0
  116. multioptpy/Optimizer/gdiis.py +609 -0
  117. multioptpy/Optimizer/gediis.py +203 -0
  118. multioptpy/Optimizer/geodesic_step.py +433 -0
  119. multioptpy/Optimizer/gpmin.py +633 -0
  120. multioptpy/Optimizer/gpr_step.py +364 -0
  121. multioptpy/Optimizer/gradientdescent.py +78 -0
  122. multioptpy/Optimizer/gradientdescent_neb.py +52 -0
  123. multioptpy/Optimizer/hessian_update.py +433 -0
  124. multioptpy/Optimizer/hybrid_rfo.py +998 -0
  125. multioptpy/Optimizer/kdiis.py +625 -0
  126. multioptpy/Optimizer/lars.py +21 -0
  127. multioptpy/Optimizer/lbfgs.py +253 -0
  128. multioptpy/Optimizer/lbfgs_neb.py +355 -0
  129. multioptpy/Optimizer/linesearch.py +236 -0
  130. multioptpy/Optimizer/lookahead.py +40 -0
  131. multioptpy/Optimizer/nadam.py +64 -0
  132. multioptpy/Optimizer/newton.py +200 -0
  133. multioptpy/Optimizer/prodigy.py +70 -0
  134. multioptpy/Optimizer/purtubation.py +16 -0
  135. multioptpy/Optimizer/quickmin_neb.py +245 -0
  136. multioptpy/Optimizer/radam.py +75 -0
  137. multioptpy/Optimizer/rfo_neb.py +302 -0
  138. multioptpy/Optimizer/ric_rfo.py +842 -0
  139. multioptpy/Optimizer/rl_step.py +627 -0
  140. multioptpy/Optimizer/rmspropgrave.py +65 -0
  141. multioptpy/Optimizer/rsirfo.py +1647 -0
  142. multioptpy/Optimizer/rsprfo.py +1056 -0
  143. multioptpy/Optimizer/sadam.py +60 -0
  144. multioptpy/Optimizer/samsgrad.py +63 -0
  145. multioptpy/Optimizer/tr_lbfgs.py +678 -0
  146. multioptpy/Optimizer/trim.py +273 -0
  147. multioptpy/Optimizer/trust_radius.py +207 -0
  148. multioptpy/Optimizer/trust_radius_neb.py +121 -0
  149. multioptpy/Optimizer/yogi.py +60 -0
  150. multioptpy/OtherMethod/__init__.py +0 -0
  151. multioptpy/OtherMethod/addf.py +1150 -0
  152. multioptpy/OtherMethod/dimer.py +895 -0
  153. multioptpy/OtherMethod/elastic_image_pair.py +629 -0
  154. multioptpy/OtherMethod/modelfunction.py +456 -0
  155. multioptpy/OtherMethod/newton_traj.py +454 -0
  156. multioptpy/OtherMethod/twopshs.py +1095 -0
  157. multioptpy/PESAnalyzer/__init__.py +0 -0
  158. multioptpy/PESAnalyzer/calc_irc_curvature.py +125 -0
  159. multioptpy/PESAnalyzer/cmds_analysis.py +152 -0
  160. multioptpy/PESAnalyzer/koopman_analysis.py +268 -0
  161. multioptpy/PESAnalyzer/pca_analysis.py +314 -0
  162. multioptpy/Parameters/__init__.py +0 -0
  163. multioptpy/Parameters/atomic_mass.py +20 -0
  164. multioptpy/Parameters/atomic_number.py +22 -0
  165. multioptpy/Parameters/covalent_radii.py +44 -0
  166. multioptpy/Parameters/d2.py +61 -0
  167. multioptpy/Parameters/d3.py +63 -0
  168. multioptpy/Parameters/d4.py +103 -0
  169. multioptpy/Parameters/dreiding.py +34 -0
  170. multioptpy/Parameters/gfn0xtb_param.py +137 -0
  171. multioptpy/Parameters/gfnff_param.py +315 -0
  172. multioptpy/Parameters/gnb.py +104 -0
  173. multioptpy/Parameters/parameter.py +22 -0
  174. multioptpy/Parameters/uff.py +72 -0
  175. multioptpy/Parameters/unit_values.py +20 -0
  176. multioptpy/Potential/AFIR_potential.py +55 -0
  177. multioptpy/Potential/LJ_repulsive_potential.py +345 -0
  178. multioptpy/Potential/__init__.py +0 -0
  179. multioptpy/Potential/anharmonic_keep_potential.py +28 -0
  180. multioptpy/Potential/asym_elllipsoidal_potential.py +718 -0
  181. multioptpy/Potential/electrostatic_potential.py +69 -0
  182. multioptpy/Potential/flux_potential.py +30 -0
  183. multioptpy/Potential/gaussian_potential.py +101 -0
  184. multioptpy/Potential/idpp.py +516 -0
  185. multioptpy/Potential/keep_angle_potential.py +146 -0
  186. multioptpy/Potential/keep_dihedral_angle_potential.py +105 -0
  187. multioptpy/Potential/keep_outofplain_angle_potential.py +70 -0
  188. multioptpy/Potential/keep_potential.py +99 -0
  189. multioptpy/Potential/mechano_force_potential.py +74 -0
  190. multioptpy/Potential/nanoreactor_potential.py +52 -0
  191. multioptpy/Potential/potential.py +896 -0
  192. multioptpy/Potential/spacer_model_potential.py +221 -0
  193. multioptpy/Potential/switching_potential.py +258 -0
  194. multioptpy/Potential/universal_potential.py +34 -0
  195. multioptpy/Potential/value_range_potential.py +36 -0
  196. multioptpy/Potential/void_point_potential.py +25 -0
  197. multioptpy/SQM/__init__.py +0 -0
  198. multioptpy/SQM/sqm1/__init__.py +0 -0
  199. multioptpy/SQM/sqm1/sqm1_core.py +1792 -0
  200. multioptpy/SQM/sqm2/__init__.py +0 -0
  201. multioptpy/SQM/sqm2/calc_tools.py +95 -0
  202. multioptpy/SQM/sqm2/sqm2_basis.py +850 -0
  203. multioptpy/SQM/sqm2/sqm2_bond.py +119 -0
  204. multioptpy/SQM/sqm2/sqm2_core.py +303 -0
  205. multioptpy/SQM/sqm2/sqm2_data.py +1229 -0
  206. multioptpy/SQM/sqm2/sqm2_disp.py +65 -0
  207. multioptpy/SQM/sqm2/sqm2_eeq.py +243 -0
  208. multioptpy/SQM/sqm2/sqm2_overlapint.py +704 -0
  209. multioptpy/SQM/sqm2/sqm2_qm.py +578 -0
  210. multioptpy/SQM/sqm2/sqm2_rep.py +66 -0
  211. multioptpy/SQM/sqm2/sqm2_srb.py +70 -0
  212. multioptpy/Thermo/__init__.py +0 -0
  213. multioptpy/Thermo/normal_mode_analyzer.py +865 -0
  214. multioptpy/Utils/__init__.py +0 -0
  215. multioptpy/Utils/bond_connectivity.py +264 -0
  216. multioptpy/Utils/calc_tools.py +884 -0
  217. multioptpy/Utils/oniom.py +96 -0
  218. multioptpy/Utils/pbc.py +48 -0
  219. multioptpy/Utils/riemann_curvature.py +208 -0
  220. multioptpy/Utils/symmetry_analyzer.py +482 -0
  221. multioptpy/Visualization/__init__.py +0 -0
  222. multioptpy/Visualization/visualization.py +156 -0
  223. multioptpy/WFAnalyzer/MO_analysis.py +104 -0
  224. multioptpy/WFAnalyzer/__init__.py +0 -0
  225. multioptpy/Wrapper/__init__.py +0 -0
  226. multioptpy/Wrapper/autots.py +1239 -0
  227. multioptpy/Wrapper/ieip_wrapper.py +93 -0
  228. multioptpy/Wrapper/md_wrapper.py +92 -0
  229. multioptpy/Wrapper/neb_wrapper.py +94 -0
  230. multioptpy/Wrapper/optimize_wrapper.py +76 -0
  231. multioptpy/__init__.py +5 -0
  232. multioptpy/entrypoints.py +916 -0
  233. multioptpy/fileio.py +660 -0
  234. multioptpy/ieip.py +340 -0
  235. multioptpy/interface.py +1086 -0
  236. multioptpy/irc.py +529 -0
  237. multioptpy/moleculardynamics.py +432 -0
  238. multioptpy/neb.py +1267 -0
  239. multioptpy/optimization.py +1553 -0
  240. multioptpy/optimizer.py +709 -0
  241. multioptpy-1.20.2.dist-info/METADATA +438 -0
  242. multioptpy-1.20.2.dist-info/RECORD +246 -0
  243. multioptpy-1.20.2.dist-info/WHEEL +5 -0
  244. multioptpy-1.20.2.dist-info/entry_points.txt +9 -0
  245. multioptpy-1.20.2.dist-info/licenses/LICENSE +674 -0
  246. multioptpy-1.20.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,364 @@
1
+ import numpy as np
2
+ from scipy.linalg import cholesky, cho_solve
3
+ from scipy.optimize import minimize
4
+
5
+
6
+ class GaussianProcessRegression:
7
+ """Scratch implementation of Gaussian Process Regression"""
8
+
9
+ def __init__(self, kernel='rbf', length_scale=1.0, amplitude=1.0, noise=1e-6):
10
+ # Kernel parameters
11
+ self.kernel_type = kernel
12
+ self.length_scale = length_scale
13
+ self.amplitude = amplitude
14
+ self.noise = noise
15
+
16
+ # Model state
17
+ self.X_train = None
18
+ self.y_train = None
19
+ self.L = None
20
+ self.alpha = None
21
+
22
+ def kernel(self, X1, X2=None):
23
+ """Compute kernel matrix between X1 and X2"""
24
+ if X2 is None:
25
+ X2 = X1
26
+
27
+ # RBF/Squared exponential kernel
28
+ X1_norm = np.sum(X1**2, axis=1).reshape(-1, 1)
29
+ X2_norm = np.sum(X2**2, axis=1).reshape(1, -1)
30
+ K = X1_norm + X2_norm - 2 * np.dot(X1, X2.T)
31
+ K = self.amplitude**2 * np.exp(-K / (2 * self.length_scale**2))
32
+ return K
33
+
34
+ def _nll_fn(self, theta):
35
+ """Negative log-likelihood for hyperparameter optimization"""
36
+ # Extract hyperparameters (in log space)
37
+ length_scale, amplitude, noise = np.exp(theta)
38
+
39
+ # Store original parameters
40
+ old_ls, old_amp, old_noise = self.length_scale, self.amplitude, self.noise
41
+ self.length_scale, self.amplitude, self.noise = length_scale, amplitude, noise
42
+
43
+ # Compute kernel with current hyperparameters
44
+ K = self.kernel(self.X_train)
45
+ K_noisy = K + np.eye(len(self.X_train)) * noise
46
+
47
+ try:
48
+ # Compute log-likelihood
49
+ L = cholesky(K_noisy, lower=True)
50
+ alpha = cho_solve((L, True), self.y_train)
51
+
52
+ log_det = 2 * np.sum(np.log(np.diag(L)))
53
+ data_fit = np.dot(self.y_train.T, alpha)
54
+ nll = 0.5 * (data_fit + log_det + len(self.X_train) * np.log(2 * np.pi))
55
+ except np.linalg.LinAlgError:
56
+ nll = 1e10
57
+
58
+ # Restore original parameters
59
+ self.length_scale, self.amplitude, self.noise = old_ls, old_amp, old_noise
60
+ return nll
61
+
62
+ def optimize_parameters(self):
63
+ """Optimize hyperparameters using maximum likelihood"""
64
+ if len(self.X_train) < 5:
65
+ return False
66
+
67
+ # Initial guess in log space
68
+ theta_init = [np.log(self.length_scale), np.log(self.amplitude), np.log(self.noise)]
69
+ bounds = [(np.log(0.01), np.log(10.0)), (np.log(0.1), np.log(10.0)), (np.log(1e-10), np.log(1e-2))]
70
+
71
+ try:
72
+ res = minimize(self._nll_fn, theta_init, method='L-BFGS-B', bounds=bounds)
73
+ if res.success:
74
+ self.length_scale, self.amplitude, self.noise = np.exp(res.x)
75
+ print(f"Optimized hyperparameters: length_scale={self.length_scale:.4f}, "
76
+ f"amplitude={self.amplitude:.4f}, noise={self.noise:.6f}")
77
+ return True
78
+ except Exception as e:
79
+ print(f"Hyperparameter optimization failed: {str(e)}")
80
+ return False
81
+
82
+ def fit(self, X, y, optimize=True):
83
+ """Fit GP model to training data"""
84
+ self.X_train = X.copy()
85
+ self.y_train = y.copy()
86
+
87
+ # Optimize hyperparameters if requested
88
+ if optimize and len(X) >= 5:
89
+ self.optimize_parameters()
90
+
91
+ # Compute kernel matrix
92
+ K = self.kernel(X)
93
+ K_noisy = K + np.eye(len(X)) * self.noise
94
+
95
+ try:
96
+ self.L = cholesky(K_noisy, lower=True)
97
+ self.alpha = cho_solve((self.L, True), y)
98
+ except np.linalg.LinAlgError:
99
+ # Add more regularization if Cholesky fails
100
+ K_noisy = K + np.eye(len(X)) * (self.noise + 1e-6)
101
+ self.alpha = np.linalg.solve(K_noisy, y)
102
+ self.L = None
103
+
104
+ return self
105
+
106
+ def predict(self, X, return_std=False):
107
+ """Make predictions at query points X"""
108
+ if self.X_train is None or self.alpha is None:
109
+ raise RuntimeError("Model not fitted. Call fit() first.")
110
+
111
+ # Compute kernel between X and training data
112
+ K_trans = self.kernel(X, self.X_train)
113
+
114
+ # Mean prediction
115
+ y_mean = np.dot(K_trans, self.alpha)
116
+
117
+ if return_std:
118
+ # Compute variance
119
+ if self.L is not None:
120
+ v = np.linalg.solve(self.L, K_trans.T)
121
+ K_pred = self.kernel(X)
122
+ y_var = K_pred - np.dot(v.T, v)
123
+ else:
124
+ K_inv = np.linalg.inv(self.kernel(self.X_train) + np.eye(len(self.X_train)) * self.noise)
125
+ K_pred = self.kernel(X)
126
+ y_var = K_pred - np.dot(K_trans, np.dot(K_inv, K_trans.T))
127
+
128
+ # Ensure variance is positive
129
+ diag = np.maximum(np.diag(y_var), 0)
130
+ return y_mean, np.sqrt(diag)
131
+
132
+ return y_mean
133
+
134
+
135
+ class GPRStep:
136
+ """GPR-based optimization step generator"""
137
+
138
+ def __init__(self):
139
+ # GPR model parameters
140
+ self.kernel_type = 'rbf'
141
+ self.length_scale_init = 1.0
142
+ self.amplitude_init = 1.0
143
+ self.noise_init = 1e-6
144
+
145
+ # Optimization strategy
146
+ self.balance_factor = 0.1
147
+ self.min_points_for_gpr = 5
148
+ self.max_history_points = 25
149
+ self.ucb_beta = 2.0
150
+ self.num_candidates = 25
151
+ self.candidate_scale = 1.0
152
+ self.max_step_norm = 0.5
153
+
154
+ # State variables
155
+ self.geom_history = []
156
+ self.energy_history = []
157
+ self.gradient_history = []
158
+ self.step_history = []
159
+ self.gpr = None
160
+ self.y_mean = 0.0
161
+ self.y_std = 1.0
162
+ self.iter = 0
163
+ self.failures = 0
164
+
165
+ def _update_histories(self, geometry, energy, gradient):
166
+ """Update optimization histories"""
167
+ self.geom_history.append(geometry.copy())
168
+ self.energy_history.append(energy)
169
+ self.gradient_history.append(gradient.copy())
170
+
171
+ # Calculate and store step if possible
172
+ if len(self.geom_history) > 1:
173
+ step = geometry - self.geom_history[-2]
174
+ self.step_history.append(step)
175
+
176
+ # Limit history size
177
+ if len(self.geom_history) > self.max_history_points:
178
+ self.geom_history.pop(0)
179
+ self.energy_history.pop(0)
180
+ self.gradient_history.pop(0)
181
+ if len(self.step_history) > 0:
182
+ self.step_history.pop(0)
183
+
184
+ def _fit_gpr_model(self):
185
+ """Fit GPR model to current data"""
186
+ if len(self.geom_history) < self.min_points_for_gpr:
187
+ return False
188
+
189
+ try:
190
+ # Prepare training data
191
+ X_train = np.array([g.flatten() for g in self.geom_history])
192
+
193
+ # Normalize energy values
194
+ y_train = np.array(self.energy_history)
195
+ self.y_mean = np.mean(y_train)
196
+ self.y_std = np.std(y_train) if np.std(y_train) > 1e-10 else 1.0
197
+ y_train_norm = (y_train - self.y_mean) / self.y_std
198
+
199
+ # Initialize or reuse GPR model
200
+ if self.gpr is None:
201
+ self.gpr = GaussianProcessRegression(
202
+ kernel='rbf',
203
+ length_scale=self.length_scale_init,
204
+ amplitude=self.amplitude_init,
205
+ noise=self.noise_init
206
+ )
207
+
208
+ # Fit model
209
+ self.gpr.fit(X_train, y_train_norm)
210
+ return True
211
+
212
+ except Exception as e:
213
+ print(f"Error fitting GPR model: {str(e)}")
214
+ self.failures += 1
215
+ return False
216
+
217
+ def _generate_candidate_steps(self, current_geom, gradient):
218
+ """Generate candidate steps for GPR evaluation"""
219
+ n_coords = len(current_geom)
220
+ candidates = []
221
+
222
+ # Negative gradient direction
223
+ if np.linalg.norm(gradient) > 1e-10:
224
+ unit_grad = gradient / np.linalg.norm(gradient)
225
+ neg_grad_step = -0.1 * self.candidate_scale * unit_grad
226
+ candidates.append(current_geom + neg_grad_step)
227
+
228
+ # Steps from previous history
229
+ if len(self.step_history) > 0:
230
+ for i in range(min(3, len(self.step_history))):
231
+ scale = np.random.uniform(0.8, 1.2) * self.candidate_scale
232
+ candidates.append(current_geom + scale * self.step_history[-(i+1)])
233
+
234
+ # Random steps
235
+ n_random = max(1, self.num_candidates - len(candidates))
236
+ step_scale = 0.1 * self.candidate_scale
237
+ if len(self.step_history) > 0:
238
+ step_scale = np.mean([np.linalg.norm(s) for s in self.step_history[-3:]]) * self.candidate_scale
239
+
240
+ for _ in range(n_random):
241
+ rand_dir = np.random.randn(*current_geom.shape)
242
+ rand_dir = rand_dir / max(1e-10, np.linalg.norm(rand_dir))
243
+ scale = np.random.uniform(0.5, 1.5) * step_scale
244
+ candidates.append(current_geom + scale * rand_dir)
245
+
246
+ return candidates
247
+
248
+ def _select_best_candidate(self, candidates, current_geom, current_energy):
249
+ """Select best candidate based on GPR predictions"""
250
+ if self.gpr is None or not candidates:
251
+ return None, 0.0
252
+
253
+ try:
254
+ # Prepare candidates for GPR
255
+ X_cand = np.array([g.flatten() for g in candidates])
256
+
257
+ # Get predictions with uncertainty
258
+ y_mean, y_std = self.gpr.predict(X_cand, return_std=True)
259
+
260
+ # Denormalize predictions
261
+ y_mean = y_mean * self.y_std + self.y_mean
262
+ y_std = y_std * self.y_std
263
+
264
+ # Acquisition function (Upper Confidence Bound)
265
+ acquisition = -(y_mean - self.ucb_beta * self.balance_factor * y_std)
266
+
267
+ # Select best candidate
268
+ best_idx = np.argmax(acquisition)
269
+ best_geom = candidates[best_idx]
270
+ best_step = best_geom - current_geom
271
+
272
+ # Expected improvement
273
+ expected_improvement = current_energy - y_mean[best_idx]
274
+ print(f"Expected energy: {y_mean[best_idx]:.6f}, uncertainty: {y_std[best_idx]:.6f}")
275
+ print(f"Expected improvement: {expected_improvement:.6f}")
276
+
277
+ return best_step, expected_improvement
278
+
279
+ except Exception as e:
280
+ print(f"Error in candidate selection: {str(e)}")
281
+ self.failures += 1
282
+ return None, 0.0
283
+
284
+ def run(self, geom_num_list, energy, gradient, original_move_vector):
285
+ """Run GPR-based optimization step"""
286
+ print("GPR-Step optimization method")
287
+ n_coords = len(geom_num_list)
288
+ grad_rms = np.sqrt(np.mean(gradient ** 2))
289
+
290
+ print(f"Energy: {energy:.8f}, Gradient RMS: {grad_rms:.8f}")
291
+
292
+ # Update histories
293
+ self._update_histories(geom_num_list, energy, gradient)
294
+
295
+ # Use original step if not enough history
296
+ if len(self.geom_history) < self.min_points_for_gpr:
297
+ print(f"Building history ({len(self.geom_history)}/{self.min_points_for_gpr}), using original step")
298
+ self.iter += 1
299
+ return original_move_vector
300
+
301
+ # Use original step if too many GPR failures
302
+ if self.failures >= 3:
303
+ print(f"Too many GPR failures ({self.failures}), using original step")
304
+ self.iter += 1
305
+ return original_move_vector
306
+
307
+ # Fit GPR model
308
+ if not self._fit_gpr_model():
309
+ print("Failed to fit GPR model, using original step")
310
+ self.iter += 1
311
+ return original_move_vector
312
+
313
+ # Generate candidate steps
314
+ candidates = self._generate_candidate_steps(geom_num_list, gradient)
315
+ print(f"Generated {len(candidates)} candidate steps")
316
+
317
+ # Select best step
318
+ gpr_step, expected_improvement = self._select_best_candidate(
319
+ candidates, geom_num_list, energy)
320
+
321
+ if gpr_step is None or expected_improvement < 0:
322
+ print("GPR step selection failed or predicted energy increase, using original step")
323
+ self.iter += 1
324
+ return original_move_vector
325
+
326
+ # Blend steps
327
+ orig_norm = np.linalg.norm(original_move_vector)
328
+ gpr_norm = np.linalg.norm(gpr_step)
329
+
330
+ # Scale GPR step if it's too large
331
+ if gpr_norm > self.max_step_norm:
332
+ gpr_step = gpr_step * (self.max_step_norm / gpr_norm)
333
+ gpr_norm = self.max_step_norm
334
+
335
+ # Blend with original step
336
+ if orig_norm > 1e-10:
337
+ # Check if directions are similar
338
+ cos_angle = np.dot(original_move_vector.flatten(), gpr_step.flatten()) / (orig_norm * gpr_norm)
339
+
340
+ if cos_angle > 0.5: # Directions are similar
341
+ weight_gpr = 0.7
342
+ elif cos_angle > 0: # Somewhat aligned
343
+ weight_gpr = 0.5
344
+ else: # Different directions
345
+ weight_gpr = 0.3
346
+
347
+ # Ensure GPR step is not much larger than original
348
+ if gpr_norm > 3.0 * orig_norm:
349
+ gpr_step = gpr_step * (3.0 * orig_norm / gpr_norm)
350
+
351
+ blended_step = -1 * weight_gpr * gpr_step + (1.0 - weight_gpr) * original_move_vector
352
+ print(f"Using blended step: {weight_gpr:.2f}*GPR + {1.0-weight_gpr:.2f}*Original")
353
+ else:
354
+ blended_step = -1 * gpr_step
355
+ print("Using pure GPR step (original step near zero)")
356
+
357
+ # Final safety checks
358
+ if np.any(np.isnan(blended_step)) or np.any(np.isinf(blended_step)):
359
+ print("Numerical issues detected, using original step")
360
+ self.iter += 1
361
+ return original_move_vector
362
+
363
+ self.iter += 1
364
+ return blended_step
@@ -0,0 +1,78 @@
1
+ import numpy as np
2
+ import copy
3
+
4
+
5
+
6
+ class GradientDescent:
7
+ def __init__(self, **config):
8
+ #Pseudo-IRC
9
+ self.DELTA = 1.0
10
+ self.Initialization = True
11
+ self.config = config
12
+ self.hessian = None
13
+ self.bias_hessian = None
14
+
15
+ def run(self, geom_num_list, B_g, pre_B_g=[], pre_geom=[], B_e=0.0, pre_B_e=0.0, pre_move_vector=[], initial_geom_num_list=[], g=[], pre_g=[]):
16
+ print("SD")
17
+ if self.Initialization:
18
+
19
+
20
+ self.Initialization = False
21
+
22
+ move_vector = self.DELTA * B_g
23
+
24
+ return move_vector#Bohr.
25
+
26
+ def set_hessian(self, hessian):
27
+ self.hessian = hessian
28
+ return
29
+
30
+ def set_bias_hessian(self, bias_hessian):
31
+ self.bias_hessian = bias_hessian
32
+ return
33
+
34
+ def get_hessian(self):
35
+ return self.hessian
36
+
37
+ def get_bias_hessian(self):
38
+ return self.bias_hessian
39
+
40
+
41
+ class MassWeightedGradientDescent:
42
+ def __init__(self, **config):
43
+ #For (meta-)IRC (Euler method)
44
+ self.DELTA = 1.0
45
+ self.Initialization = True
46
+ self.config = config
47
+ self.element_list = None
48
+ self.atomic_mass = None
49
+
50
+ def run(self, geom_num_list, B_g, pre_B_g, pre_geom, B_e, pre_B_e, pre_move_vector, initial_geom_num_list, g, pre_g):
51
+ print("MWSD")
52
+ if self.Initialization:
53
+ self.elem_mass_list = []
54
+ for elem in self.element_list:
55
+ self.elem_mass_list.append([self.atomic_mass(elem)])
56
+ self.elem_mass_list.append([self.atomic_mass(elem)])
57
+ self.elem_mass_list.append([self.atomic_mass(elem)])
58
+ self.elem_mass_list = np.array(self.elem_mass_list, dtype="float64")
59
+ self.Initialization = False
60
+
61
+ move_vector = self.DELTA * B_g / self.elem_mass_list
62
+
63
+ return move_vector#Bohr.
64
+
65
+ def set_hessian(self, hessian):
66
+ self.hessian = hessian
67
+ return
68
+
69
+ def set_bias_hessian(self, bias_hessian):
70
+ self.bias_hessian = bias_hessian
71
+ return
72
+
73
+
74
+ def get_hessian(self):
75
+ return self.hessian
76
+
77
+ def get_bias_hessian(self):
78
+ return self.bias_hessian
@@ -0,0 +1,52 @@
1
+ import numpy as np
2
+ from abc import ABC, abstractmethod
3
+
4
+
5
+ class OptimizationAlgorithm(ABC):
6
+ """Base class for optimization algorithms"""
7
+
8
+ @abstractmethod
9
+ def optimize(self, geometry_num_list, total_force_list, **kwargs):
10
+ """Execute optimization step"""
11
+ pass
12
+
13
+
14
+
15
+
16
+ class SteepestDescentOptimizer(OptimizationAlgorithm):
17
+ """Steepest descent optimizer"""
18
+
19
+ def __init__(self, config):
20
+ self.config = config
21
+
22
+ def optimize(self, geometry_num_list, total_force_list, **kwargs):
23
+ """Steepest descent optimization"""
24
+ total_delta = []
25
+ delta = 0.5
26
+ for i in range(len(total_force_list)):
27
+ total_delta.append(delta * total_force_list[i])
28
+
29
+ # Apply edge constraints
30
+ if self.config.fix_init_edge:
31
+ move_vector = [total_delta[0] * 0.0]
32
+ else:
33
+ move_vector = [total_delta[0]]
34
+
35
+ for i in range(1, len(total_delta) - 1):
36
+ trust_radii_1 = np.linalg.norm(geometry_num_list[i] - geometry_num_list[i-1]) / 2.0
37
+ trust_radii_2 = np.linalg.norm(geometry_num_list[i] - geometry_num_list[i+1]) / 2.0
38
+ if np.linalg.norm(total_delta[i]) > trust_radii_1:
39
+ move_vector.append(total_delta[i] * trust_radii_1 / np.linalg.norm(total_delta[i]))
40
+ elif np.linalg.norm(total_delta[i]) > trust_radii_2:
41
+ move_vector.append(total_delta[i] * trust_radii_2 / np.linalg.norm(total_delta[i]))
42
+ else:
43
+ move_vector.append(total_delta[i])
44
+
45
+ if self.config.fix_end_edge:
46
+ move_vector.append(total_delta[-1] * 0.0)
47
+ else:
48
+ move_vector.append(total_delta[-1])
49
+
50
+ new_geometry = (geometry_num_list + move_vector) * self.config.bohr2angstroms
51
+ return new_geometry
52
+