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,236 @@
1
+ import numpy as np
2
+ import scipy.linalg # Keep for potential future use, though not needed now
3
+ from scipy.optimize import minimize # Keep for potential future use
4
+ from typing import List, Tuple, Optional
5
+
6
+
7
+ class LineSearch:
8
+ """
9
+ Stateful line search using simple energy decrease and extrapolation.
10
+ Accepts the last step that showed energy decrease.
11
+ Terminates if energy increases or if gradient becomes orthogonal (cosine < threshold).
12
+ Returns incremental steps compatible with cumulative updates.
13
+ """
14
+ def __init__(self,
15
+ cos_threshold: float = 0.05, # Threshold for |cos(g_k, p)| to terminate
16
+ maxstep: float = 0.2,
17
+ damping: float = 0.8, # Start with a smaller initial step
18
+ stpmin: float = 1e-8, # Minimum allowed *incremental* alpha for extrapolation
19
+ stpmax: float = 5.0, # Max total alpha allowed
20
+ max_iterations: int = 10,
21
+ extrapolation_factor: float = 1.5 # Factor to increase alpha
22
+ ):
23
+ """Initializes the Simple Extrapolation LineSearch configuration."""
24
+
25
+ # --- Configuration ---
26
+ self.condition_type = 'simple_decrease_and_cosine' # Indicate the strategy
27
+ self.cos_threshold = cos_threshold # Cosine orthogonality condition
28
+ self.maxstep = maxstep
29
+ self.damping = damping
30
+ self.stpmin = stpmin
31
+ self.stpmax = stpmax
32
+ self.max_iterations = max_iterations
33
+ self.extrapolation_factor = extrapolation_factor
34
+
35
+ # --- Internal State ---
36
+ self.active_search = False
37
+ self.p = None # Fixed search direction vector
38
+ self.p_norm = None # Norm of the search direction vector
39
+ self.g0 = None # Gradient at geom0 (stored for info)
40
+ self.e0 = None # Energy at geom0
41
+ self.derphi0 = None # Directional derivative at geom0 (stored for info)
42
+ self.current_total_alpha = 0.0 # Total alpha from geom0 for the point *being evaluated*
43
+ self.current_iteration = 0
44
+ self.ITR = 0
45
+ self.geom0 = None # Coordinates at the start of the line search (x_k)
46
+ self.last_returned_total_alpha = 0.0 # Total alpha corresponding to the previous *returned* step
47
+
48
+ # --- State for this strategy ---
49
+ self.best_valid_total_alpha = 0.0 # Best total alpha found so far (energy decreased)
50
+ self.best_valid_phi = np.inf # Energy corresponding to best_valid_total_alpha
51
+
52
+ # --- Observation Lists (for debugging/record-keeping) ---
53
+ self.observed_alphas = []
54
+ self.observed_phis = []
55
+ self.observed_phi_primes = []
56
+
57
+
58
+ def _add_observation(self, total_alpha: float, phi: float, phi_prime: float) -> bool:
59
+ """Adds observation (primarily for record keeping/debugging now)."""
60
+
61
+ if any(abs(total_alpha - a_obs) < np.finfo(float).eps * 10 for a_obs in self.observed_alphas):
62
+ return False
63
+
64
+ self.observed_alphas.append(total_alpha)
65
+ self.observed_phis.append(phi)
66
+ self.observed_phi_primes.append(phi_prime)
67
+
68
+ data = sorted(zip(self.observed_alphas, self.observed_phis, self.observed_phi_primes))
69
+ self.observed_alphas, self.observed_phis, self.observed_phi_primes = [list(t) for t in zip(*data)]
70
+
71
+ return True
72
+
73
+
74
+ def run(self, geom_num_list, gradient, prev_gradient, energy, prev_energy, move_vector):
75
+ """
76
+ Performs one stateful iteration of the simple extrapolation line search.
77
+ Returns the INCREMENTAL step vector.
78
+ """
79
+
80
+ if not self.active_search:
81
+ # --- Start a New Line Search ---
82
+ print(f"\n===== Starting Line Search (Cosine Thresh={self.cos_threshold}, damp={self.damping}) =====")
83
+
84
+ self.active_search = True
85
+ self.current_iteration = 0
86
+ self.p = np.asarray(move_vector).reshape(-1)
87
+ self.g0 = np.asarray(prev_gradient).reshape(-1)
88
+ self.e0 = float(prev_energy)
89
+ self.geom0 = np.asarray(geom_num_list).reshape(-1)
90
+ self.derphi0 = np.dot(self.g0, self.p)
91
+
92
+
93
+ # Store the norm of the (final) search direction
94
+ self.p_norm = np.linalg.norm(self.p)
95
+
96
+ # --- Initialize Search State ---
97
+ self.best_valid_total_alpha = 0.0
98
+ self.best_valid_phi = self.e0
99
+ self.last_returned_total_alpha = 0.0
100
+
101
+ self.observed_alphas = []
102
+ self.observed_phis = []
103
+ self.observed_phi_primes = []
104
+ self._add_observation(0.0, self.e0, self.derphi0)
105
+
106
+ # --- Calculate Initial Step ---
107
+ p_norm_max = np.max(np.abs(self.p)) # Use max component for scaling
108
+ if p_norm_max < 1e-10:
109
+ print("Warning: Move vector max component is zero.")
110
+ self.active_search = False
111
+ self.geom0 = None
112
+ return np.zeros_like(self.p.reshape(-1, 1))
113
+
114
+ scale = abs(self.maxstep / p_norm_max)
115
+
116
+ next_total_alpha = min(1.0, scale) * self.damping
117
+
118
+ next_total_alpha = np.clip(next_total_alpha, self.stpmin, self.stpmax)
119
+
120
+ print(f"Start E: {self.e0:.6f}, Deriv (g0.p): {self.derphi0:.6f}")
121
+ print(f"Initial trial total alpha: {next_total_alpha:.6f} (using damping={self.damping})")
122
+
123
+ self.current_total_alpha = next_total_alpha
124
+ incremental_alpha = self.current_total_alpha - self.last_returned_total_alpha
125
+
126
+ return incremental_alpha * self.p.reshape(-1, 1)
127
+
128
+
129
+ # --- Continue Existing Line Search Iteration ---
130
+ print(f"\n--- Line Search Iteration {self.current_iteration} (eval total alpha={self.current_total_alpha:.6f}) ---")
131
+
132
+ self.current_iteration += 1
133
+ current_geom = np.asarray(geom_num_list).reshape(-1)
134
+ e_curr = float(energy)
135
+ g_curr = np.asarray(gradient).reshape(-1)
136
+
137
+ derphi_curr = np.dot(g_curr, self.p)
138
+
139
+ _ = self._add_observation(self.current_total_alpha, e_curr, derphi_curr)
140
+
141
+ print(f"E_curr: {e_curr:.6f}")
142
+ if self.geom0 is not None:
143
+ norm_diff = np.linalg.norm(current_geom - self.geom0)
144
+ print(f" Norm diff from start: {norm_diff:.6f}")
145
+
146
+ terminate = False
147
+ accepted_total_alpha = 0.0
148
+
149
+ # --- Check Conditions ---
150
+
151
+ if e_curr < self.best_valid_phi:
152
+ # --- Energy Decreased: Update best and check cosine condition ---
153
+ print(f"Energy decreased to {e_curr:.6f}.")
154
+
155
+ self.best_valid_total_alpha = self.current_total_alpha
156
+ self.best_valid_phi = e_curr
157
+
158
+ # --- NEW: Check cosine condition (g_k orthogonal to p) ---
159
+ g_norm = np.linalg.norm(g_curr)
160
+ denominator = g_norm * self.p_norm
161
+
162
+ cosine_theta = 0.0
163
+ if denominator < 1e-15:
164
+ # Gradient norm or p norm is zero.
165
+ # If g_norm is zero, we are at a minimum, so accept.
166
+ cosine_theta = 0.0
167
+ print(" Note: Gradient norm is near zero.")
168
+ else:
169
+ # cosine_theta = (g_k . p) / (||g_k|| * ||p||)
170
+ cosine_theta = derphi_curr / denominator
171
+
172
+ print(f" Checking cosine(g_curr, p): |cos(theta)|={abs(cosine_theta):.6f} vs threshold={self.cos_threshold:.6f}")
173
+
174
+ if abs(cosine_theta) < self.cos_threshold:
175
+ print(" Cosine condition met (gradient orthogonal to search). Accepting this step.")
176
+ terminate = True
177
+ accepted_total_alpha = self.current_total_alpha
178
+
179
+ else:
180
+ # --- Cosine NOT met: Extrapolate ---
181
+ print(" Cosine not met. Extrapolating.")
182
+
183
+ next_total_alpha = self.current_total_alpha * self.extrapolation_factor
184
+ next_total_alpha = np.clip(next_total_alpha, self.stpmin, self.stpmax)
185
+
186
+ next_incremental_alpha = next_total_alpha - self.current_total_alpha
187
+
188
+ if abs(next_incremental_alpha) < self.stpmin:
189
+ print(f"Warning: Extrapolation step too small ({next_incremental_alpha:.2e}). Accepting current best step.")
190
+ terminate = True
191
+ accepted_total_alpha = self.best_valid_total_alpha
192
+ elif self.current_iteration >= self.max_iterations:
193
+ print("Warning: Max iterations reached during extrapolation. Accepting last successful step.")
194
+ terminate = True
195
+ accepted_total_alpha = self.best_valid_total_alpha
196
+
197
+ if not terminate:
198
+ self.last_returned_total_alpha = self.current_total_alpha
199
+ self.current_total_alpha = next_total_alpha
200
+ return next_incremental_alpha * self.p.reshape(-1, 1)
201
+
202
+ else:
203
+ # --- Energy Increased: Terminate ---
204
+ print(f"Energy increased or stalled (E_curr={e_curr:.6f} >= E_best={self.best_valid_phi:.6f}). Accepting previous best step.")
205
+ terminate = True
206
+ accepted_total_alpha = self.best_valid_total_alpha
207
+
208
+ # --- Handle Termination ---
209
+ if terminate:
210
+ if accepted_total_alpha <= 0:
211
+ print("No energy decrease found compared to start. Returning zero incremental step.")
212
+ incremental_alpha = 0.0 - self.current_total_alpha
213
+ accepted_phi = self.e0
214
+ else:
215
+ print(f"Accepting total alpha: {accepted_total_alpha:.6f} with E={self.best_valid_phi:.6f}")
216
+ incremental_alpha = accepted_total_alpha - self.current_total_alpha
217
+ accepted_phi = self.best_valid_phi
218
+
219
+
220
+ print(f"===== Line Search Complete =====")
221
+ print(f"Final total alpha: {accepted_total_alpha:.6f}")
222
+ print(f"Final energy: {accepted_phi:.6f} (improvement: {self.e0 - accepted_phi:.6f})")
223
+
224
+ self.ITR += 1
225
+ print(f"Total line searches completed: {self.ITR}")
226
+
227
+ self.active_search = False
228
+ self.geom0 = None
229
+
230
+ return incremental_alpha * self.p.reshape(-1, 1)
231
+
232
+
233
+ print("Error: Line search reached unexpected state.")
234
+ self.active_search = False
235
+ self.geom0 = None
236
+ return np.zeros_like(self.p.reshape(-1, 1))
@@ -0,0 +1,40 @@
1
+ import numpy as np
2
+ import copy
3
+
4
+ class LookAhead:
5
+ def __init__(self, k=10, alpha=0.5, **config):
6
+ #LookAhead algorithm
7
+ #ref. arXiv:1907.08610
8
+ self.iter = 0
9
+ self.alpha = alpha
10
+ self.k = k
11
+ self.config = config
12
+ self.slow_geom_num_list = []
13
+ self.fast_geom_num_list_history = []
14
+ self.fast_energy_history = []
15
+
16
+ return
17
+
18
+ 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, move_vector):
19
+ print("LookAhead")
20
+ if self.iter % self.k != 0 or self.iter == 0:
21
+ self.slow_geom_num_list = geom_num_list
22
+ self.fast_geom_num_list_history.append(geom_num_list)
23
+ self.fast_energy_history.append(B_e)
24
+
25
+ else:
26
+ print("update slow geometry...")
27
+ move_vector = []
28
+ self.fast_geom_num_list_history.append(geom_num_list)
29
+ self.fast_energy_history.append(B_e)
30
+ best_fast_geom_num_list = self.fast_geom_num_list_history[np.argmin(self.fast_energy_history)]
31
+ new_geom_num_list = self.alpha * self.slow_geom_num_list + (1.0-self.alpha) * best_fast_geom_num_list
32
+
33
+ move_vector = -1 * (new_geom_num_list - geom_num_list)
34
+
35
+ self.slow_geom_num_list = best_fast_geom_num_list
36
+ self.fast_geom_num_list_history = []
37
+ self.fast_energy_history = []
38
+
39
+ self.iter += 1
40
+ return move_vector#Bohr
@@ -0,0 +1,64 @@
1
+ import numpy as np
2
+ import copy
3
+
4
+
5
+
6
+ class NAdam:
7
+ def __init__(self, **config):
8
+ #https://cs229.stanford.edu/proj2015/054_report.pdf
9
+ self.adam_count = 1
10
+ self.DELTA = 0.06
11
+ self.beta_m = 0.9
12
+ self.beta_v = 0.999
13
+ self.Epsilon = 1e-8
14
+ self.Initialization = True
15
+ self.mu = 0.975
16
+ self.nu = 0.999
17
+ self.config = config
18
+ self.hessian = None
19
+ self.bias_hessian = None
20
+
21
+ return
22
+
23
+ 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=[]):
24
+ print("NAdam")
25
+ if self.Initialization:
26
+ self.adam_m = geom_num_list * 0.0
27
+ self.adam_v = geom_num_list * 0.0
28
+ self.Initialization = False
29
+
30
+ adam_count = self.adam_count
31
+ adam_m = self.adam_m
32
+ adam_v = self.adam_v
33
+ new_adam_m = adam_m*0.0
34
+ new_adam_v = adam_v*0.0
35
+
36
+ new_adam_m_hat = []
37
+ new_adam_v_hat = []
38
+ for i in range(len(geom_num_list)):
39
+ new_adam_m[i] = copy.copy(self.mu*adam_m[i] + (1.0 - self.mu)*(B_g[i]))
40
+ new_adam_v[i] = copy.copy((self.nu*adam_v[i]) + (1.0 - self.nu)*(B_g[i]) ** 2)
41
+ new_adam_m_hat.append(np.array(new_adam_m[i], dtype="float64") * ( self.mu / (1.0 - self.mu ** adam_count)) + np.array(B_g[i], dtype="float64") * ((1.0 - self.mu)/(1.0 - self.mu ** adam_count)))
42
+ new_adam_v_hat.append(np.array(new_adam_v[i], dtype="float64") * (self.nu / (1.0 - self.nu ** adam_count)))
43
+
44
+ move_vector = []
45
+ for i in range(len(geom_num_list)):
46
+ move_vector.append( (self.DELTA*new_adam_m_hat[i]) / (np.sqrt(new_adam_v_hat[i] + self.Epsilon)))
47
+ self.adam_m = new_adam_m
48
+ self.adam_v = new_adam_v
49
+ self.adam_count += 1
50
+
51
+ return move_vector#Bohr
52
+ def set_hessian(self, hessian):
53
+ self.hessian = hessian
54
+ return
55
+
56
+ def set_bias_hessian(self, bias_hessian):
57
+ self.bias_hessian = bias_hessian
58
+ return
59
+
60
+ def get_hessian(self):
61
+ return self.hessian
62
+
63
+ def get_bias_hessian(self):
64
+ return self.bias_hessian
@@ -0,0 +1,200 @@
1
+ from .linesearch import LineSearch
2
+ from .hessian_update import ModelHessianUpdate
3
+ import numpy as np
4
+
5
+ class Newton:
6
+ def __init__(self, **config):
7
+ self.config = config
8
+ self.hess_update = ModelHessianUpdate()
9
+ self.Initialization = True
10
+ self.linesearchflag = False
11
+ self.optimal_step_flag = False
12
+ self.DELTA = 0.5
13
+ self.FC_COUNT = -1 #
14
+ self.saddle_order = 0 #
15
+ self.iter = 0 #
16
+ self.beta = 0.5
17
+ self.hessian = None
18
+ self.bias_hessian = None
19
+
20
+ return
21
+
22
+ def project_out_hess_tr_and_rot_for_coord(self, hessian, geomerty):#do not consider atomic mass
23
+ natoms = len(geomerty)
24
+
25
+ geomerty -= self.calc_center(geomerty)
26
+
27
+
28
+ tr_x = (np.tile(np.array([1, 0, 0]), natoms)).reshape(-1, 3)
29
+ tr_y = (np.tile(np.array([0, 1, 0]), natoms)).reshape(-1, 3)
30
+ tr_z = (np.tile(np.array([0, 0, 1]), natoms)).reshape(-1, 3)
31
+
32
+ rot_x = np.cross(geomerty, tr_x).flatten()
33
+ rot_y = np.cross(geomerty, tr_y).flatten()
34
+ rot_z = np.cross(geomerty, tr_z).flatten()
35
+ tr_x = tr_x.flatten()
36
+ tr_y = tr_y.flatten()
37
+ tr_z = tr_z.flatten()
38
+
39
+ TR_vectors = np.vstack([tr_x, tr_y, tr_z, rot_x, rot_y, rot_z])
40
+
41
+ Q, R = np.linalg.qr(TR_vectors.T)
42
+ keep_indices = ~np.isclose(np.diag(R), 0, atol=1e-6, rtol=0)
43
+ TR_vectors = Q.T[keep_indices]
44
+ n_tr = len(TR_vectors)
45
+
46
+ P = np.identity(natoms * 3)
47
+ for vector in TR_vectors:
48
+ P -= np.outer(vector, vector)
49
+
50
+ hess_proj = np.dot(np.dot(P.T, hessian), P)
51
+
52
+ return hess_proj
53
+
54
+ def calc_center(self, geomerty, element_list=[]):#geomerty:Bohr
55
+ center = np.array([0.0, 0.0, 0.0], dtype="float64")
56
+ for i in range(len(geomerty)):
57
+
58
+ center += geomerty[i]
59
+ center /= float(len(geomerty))
60
+
61
+ return center
62
+
63
+ def set_hessian(self, hessian):
64
+ self.hessian = hessian
65
+ return
66
+
67
+ def set_bias_hessian(self, bias_hessian):
68
+ self.bias_hessian = bias_hessian
69
+ return
70
+
71
+ def get_hessian(self):
72
+ return self.hessian
73
+
74
+ def get_bias_hessian(self):
75
+ return self.bias_hessian
76
+
77
+ def hessian_update(self, displacement, delta_grad):
78
+ if "msp" in self.config["method"].lower():
79
+ print("MSP_quasi_newton_method")
80
+ delta_hess = self.hess_update.MSP_hessian_update(self.hessian, displacement, delta_grad)
81
+ elif "bfgs" in self.config["method"].lower():
82
+ print("BFGS_quasi_newton_method")
83
+ delta_hess = self.hess_update.BFGS_hessian_update(self.hessian, displacement, delta_grad)
84
+ elif "fsb" in self.config["method"].lower():
85
+ print("FSB_quasi_newton_method")
86
+ delta_hess = self.hess_update.FSB_hessian_update(self.hessian, displacement, delta_grad)
87
+ elif "bofill" in self.config["method"].lower():
88
+ print("Bofill_quasi_newton_method")
89
+ delta_hess = self.hess_update.Bofill_hessian_update(self.hessian, displacement, delta_grad)
90
+ else:
91
+ raise "method error"
92
+ return delta_hess
93
+
94
+ def normal(self, geom_num_list, B_g, pre_B_g, pre_geom, B_e, pre_B_e, pre_g, g):
95
+
96
+ if self.linesearchflag:
97
+ print("linesearch mode")
98
+ else:
99
+ print("normal mode")
100
+ if self.Initialization:
101
+ self.Initialization = False
102
+ return self.DELTA*B_g
103
+
104
+ delta_grad = (g - pre_g).reshape(len(geom_num_list), 1)
105
+ displacement = (geom_num_list - pre_geom).reshape(len(geom_num_list), 1)
106
+
107
+
108
+ delta_hess = self.hessian_update(displacement, delta_grad)
109
+
110
+
111
+ if self.iter % self.FC_COUNT != 0 or self.FC_COUNT == -1:
112
+ new_hess = self.hessian + delta_hess + self.bias_hessian
113
+ else:
114
+ new_hess = self.hessian + self.bias_hessian
115
+
116
+ DELTA_for_QNM = self.DELTA
117
+
118
+ move_vector = DELTA_for_QNM * np.linalg.solve(new_hess, B_g.reshape(len(geom_num_list), 1))
119
+
120
+ if self.iter > 1 and self.linesearchflag:
121
+ if self.FC_COUNT != -1:
122
+ tmp_hess = self.project_out_hess_tr_and_rot_for_coord(new_hess, geom_num_list.reshape(-1, 3))
123
+ else:
124
+ tmp_hess = None
125
+ if self.optimal_step_flag or self.iter == 2:
126
+ self.LS = LineSearch(self.prev_move_vector, move_vector, B_g, pre_B_g, B_e, pre_B_e, tmp_hess)
127
+
128
+ new_move_vector, self.optimal_step_flag = self.LS.linesearch(self.prev_move_vector, move_vector, B_g, pre_B_g, B_e, pre_B_e, tmp_hess)
129
+ else:
130
+ new_move_vector = move_vector
131
+ self.optimal_step_flag = True
132
+
133
+ print("step size: ",DELTA_for_QNM,"\n")
134
+
135
+
136
+ if self.iter > 0 and self.linesearchflag:
137
+ move_vector = -new_move_vector
138
+ if self.optimal_step_flag or self.iter == 1:
139
+ self.prev_move_vector = move_vector
140
+
141
+ if not self.linesearchflag or np.linalg.norm(move_vector) > 1e-4:
142
+ self.hessian += delta_hess
143
+
144
+ self.iter += 1
145
+ return move_vector#Bohr
146
+
147
+ # arXiv:2307.13744v1
148
+ def moment(self, geom_num_list, B_g, pre_B_g, pre_geom, B_e, pre_B_e, pre_g, g):
149
+ print("moment mode")
150
+ if self.Initialization:
151
+ self.momentum_disp = geom_num_list * 0.0
152
+ self.momentum_grad = geom_num_list * 0.0
153
+ self.Initialization = False
154
+ return self.DELTA*B_g
155
+
156
+ if self.iter == 1:
157
+ self.momentum_disp = geom_num_list - pre_geom
158
+ self.momentum_grad = B_g - pre_B_g
159
+
160
+
161
+ delta_grad = (B_g - pre_B_g).reshape(len(geom_num_list), 1)
162
+ displacement = (geom_num_list - pre_geom).reshape(len(geom_num_list), 1)
163
+
164
+
165
+
166
+ new_momentum_disp = self.beta * self.momentum_disp + (1.0 - self.beta) * geom_num_list
167
+ new_momentum_grad = self.beta * self.momentum_grad + (1.0 - self.beta) * B_g
168
+
169
+ delta_momentum_disp = (new_momentum_disp - self.momentum_disp).reshape(len(geom_num_list), 1)
170
+ delta_momentum_grad = (new_momentum_grad - self.momentum_grad).reshape(len(geom_num_list), 1)
171
+
172
+
173
+ delta_hess = self.hessian_update(delta_momentum_disp, delta_momentum_grad)
174
+
175
+
176
+ if self.iter % self.FC_COUNT != 0 or self.FC_COUNT == -1:
177
+ new_hess = self.hessian + delta_hess + self.bias_hessian
178
+ else:
179
+ new_hess = self.hessian + self.bias_hessian
180
+
181
+ DELTA_for_QNM = self.DELTA
182
+
183
+
184
+ #move_vector = (DELTA_for_QNM*np.dot(np.linalg.inv(new_hess), B_g.reshape(len(geom_num_list), 1))).reshape(len(geom_num_list), 3)
185
+ move_vector = DELTA_for_QNM * np.linalg.solve(new_hess, B_g.reshape(len(geom_num_list), 1))
186
+
187
+
188
+ print("step size: ",DELTA_for_QNM,"\n")
189
+ self.hessian += delta_hess
190
+ self.iter += 1
191
+ self.momentum_disp = new_momentum_disp
192
+ self.momentum_grad = new_momentum_grad
193
+ return move_vector
194
+
195
+ 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):
196
+ if self.config["method"] == "mBFGS" or self.config["method"] == "mFSB" or self.config["method"] == "mMSP" or self.config["method"] == "mBofill":
197
+ move_vector = self.moment(geom_num_list, B_g, pre_B_g, pre_geom, B_e, pre_B_e, pre_g, g)
198
+ else:
199
+ move_vector = self.normal(geom_num_list, B_g, pre_B_g, pre_geom, B_e, pre_B_e, pre_g, g)
200
+ return move_vector
@@ -0,0 +1,70 @@
1
+ import numpy as np
2
+ import copy
3
+
4
+
5
+ class Prodigy:
6
+ def __init__(self, **config):
7
+ #Prodigy
8
+ #arXiv:2306.06101v1
9
+ self.adam_count = 1
10
+ self.d = 0.03
11
+ self.beta_m = 0.9
12
+ self.beta_v = 0.999
13
+ self.DELTA = 0.1
14
+ self.Epsilon = 1e-12
15
+ self.Initialization = True
16
+ self.config = config
17
+ self.hessian = None
18
+ self.bias_hessian = None
19
+
20
+ return
21
+
22
+ 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=[]):
23
+ print("Prodigy")
24
+
25
+ if self.Initialization:
26
+ self.adam_m = geom_num_list * 0.0
27
+ self.adam_v = geom_num_list * 0.0
28
+ self.adam_s = geom_num_list * 0.0
29
+ self.adam_r = 0.0
30
+ new_d = self.d
31
+ self.initial_geom_num_list = geom_num_list
32
+ self.Initialization = False
33
+
34
+ new_adam_m = self.adam_m*0.0
35
+ new_adam_v = self.adam_v*0.0
36
+ new_adam_s = self.adam_s*0.0
37
+
38
+ for i in range(len(geom_num_list)):
39
+ new_adam_m[i] = copy.copy(self.beta_m*self.adam_m[i] + (1.0-self.beta_m)*(B_g[i]*self.d))
40
+ new_adam_v[i] = copy.copy(self.beta_v*self.adam_v[i] + (1.0-self.beta_v)*(B_g[i]*self.d)**2)
41
+ new_adam_s[i] = np.sqrt(self.beta_v)*self.adam_s[i] + (1.0 - np.sqrt(self.beta_v))*self.DELTA*B_g[i]*self.d**2
42
+
43
+ new_adam_r = np.sqrt(self.beta_v)*self.adam_r + (1.0 - np.sqrt(self.beta_v))*(np.dot(B_g.reshape(1,len(B_g)), (self.initial_geom_num_list - geom_num_list).reshape(len(B_g),1)))*self.DELTA*self.d**2
44
+
45
+ new_d = float(max((new_adam_r / np.linalg.norm(new_adam_s ,ord=1)), self.d))
46
+ move_vector = []
47
+
48
+ for i in range(len(geom_num_list)):
49
+ move_vector.append(self.DELTA*new_d*new_adam_m[i]/(np.sqrt(new_adam_v[i])+self.Epsilon*self.d))
50
+
51
+ self.adam_m = new_adam_m
52
+ self.adam_v = new_adam_v
53
+ self.adam_r = new_adam_r
54
+ self.d = new_d
55
+ self.adam_count += 1
56
+ return move_vector
57
+
58
+ def set_hessian(self, hessian):
59
+ self.hessian = hessian
60
+ return
61
+
62
+ def set_bias_hessian(self, bias_hessian):
63
+ self.bias_hessian = bias_hessian
64
+ return
65
+
66
+ def get_hessian(self):
67
+ return self.hessian
68
+
69
+ def get_bias_hessian(self):
70
+ return self.bias_hessian
@@ -0,0 +1,16 @@
1
+ import numpy as np
2
+
3
+ class Perturbation:
4
+ def __init__(self, **config):
5
+ self.config = config
6
+ self.DELTA = 0.06
7
+ self.Boltzmann_constant = 3.16681*10**(-6) # hartree/K
8
+ self.damping_coefficient = 10.0
9
+ self.temperature = self.config["temperature"]
10
+ return
11
+ def boltzmann_dist_perturb(self, move_vector):#This function is just for fun. Thus, it is no scientific basis.
12
+
13
+ temperature = self.temperature
14
+ perturbation = self.DELTA * np.sqrt(2.0 * self.damping_coefficient * self.Boltzmann_constant * temperature) * np.random.normal(loc=0.0, scale=1.0, size=len(move_vector)).reshape(len(move_vector), 1)
15
+
16
+ return perturbation