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,523 @@
1
+ import numpy as np
2
+ from scipy.optimize import minimize
3
+
4
+
5
+ class ADIIS:
6
+ """
7
+ Implementation of ADIIS (Augmented DIIS) optimization method.
8
+
9
+ ADIIS combines aspects of DIIS and EDIIS to provide robust convergence,
10
+ particularly in early optimization stages.
11
+ """
12
+ def __init__(self):
13
+ # ADIIS parameters
14
+ self.adiis_history_size = 5 # History size
15
+ self.adiis_min_points = 2 # Minimum points to start
16
+ self.adiis_weight_initial = 0.4 # Initial weight
17
+ self.adiis_weight_max = 0.8 # Maximum weight
18
+
19
+ # Optimization parameters
20
+ self.adiis_regularization = 1e-7 # Regularization parameter
21
+ self.adiis_step_ratio_max = 3.0 # Maximum allowed step ratio
22
+
23
+ # Error recovery
24
+ self.adiis_failure_count = 0 # Failure counter
25
+ self.adiis_max_failures = 2 # Max failures before reset
26
+ self.adiis_recovery_steps = 2 # Recovery mode steps
27
+ self.adiis_current_recovery = 0 # Current recovery counter
28
+
29
+ # Weight adjustment
30
+ self.adiis_weight_current = self.adiis_weight_initial # Current weight
31
+ self.adiis_weight_increment = 0.05 # Increment for success
32
+ self.adiis_weight_decrement = 0.1 # Decrement for failure
33
+
34
+ # History storage
35
+ self.geom_history = []
36
+ self.energy_history = []
37
+ self.grad_history = []
38
+ self.quality_history = []
39
+
40
+ # Convergence monitoring
41
+ self.prev_energy = float('inf')
42
+ self.prev_grad_rms = float('inf')
43
+ self.non_improving_count = 0
44
+ self.iter = 0
45
+
46
+ def _update_history(self, geometry, energy, gradient, step_quality=1.0):
47
+ """
48
+ Update the ADIIS history
49
+
50
+ Parameters:
51
+ -----------
52
+ geometry : numpy.ndarray
53
+ Current geometry
54
+ energy : float
55
+ Current energy
56
+ gradient : numpy.ndarray
57
+ Current gradient
58
+ step_quality : float
59
+ Quality metric for this point
60
+ """
61
+ # Add current point to history
62
+ self.geom_history.append(geometry.copy())
63
+ self.energy_history.append(energy)
64
+ self.grad_history.append(gradient.copy())
65
+ self.quality_history.append(step_quality)
66
+
67
+ # If in recovery mode, limit history
68
+ if self.adiis_current_recovery > 0:
69
+ self.adiis_current_recovery -= 1
70
+ if len(self.geom_history) > 2:
71
+ self.geom_history = self.geom_history[-2:]
72
+ self.energy_history = self.energy_history[-2:]
73
+ self.grad_history = self.grad_history[-2:]
74
+ self.quality_history = self.quality_history[-2:]
75
+ return
76
+
77
+ # Limit history size
78
+ if len(self.geom_history) > self.adiis_history_size:
79
+ if len(self.geom_history) > 2:
80
+ # Calculate combined metric (energy and quality)
81
+ metrics = []
82
+ e_min = min(self.energy_history)
83
+ e_range = max(self.energy_history) - e_min
84
+
85
+ if e_range > 1e-10:
86
+ for i in range(len(self.energy_history)-1): # Skip most recent point
87
+ e_metric = (self.energy_history[i] - e_min) / e_range
88
+ metrics.append(e_metric - 0.5 * self.quality_history[i])
89
+
90
+ worst_idx = np.argmax(metrics)
91
+ else:
92
+ # If energies are very close, use quality only
93
+ oldest_qualities = self.quality_history[:-1]
94
+ worst_idx = np.argmin(oldest_qualities)
95
+
96
+ # Remove worst point
97
+ self.geom_history.pop(worst_idx)
98
+ self.energy_history.pop(worst_idx)
99
+ self.grad_history.pop(worst_idx)
100
+ self.quality_history.pop(worst_idx)
101
+ else:
102
+ # Default to removing oldest point
103
+ self.geom_history.pop(0)
104
+ self.energy_history.pop(0)
105
+ self.grad_history.pop(0)
106
+ self.quality_history.pop(0)
107
+
108
+ def _solve_adiis_equations(self):
109
+ """
110
+ Solve ADIIS equations
111
+
112
+ Returns:
113
+ --------
114
+ numpy.ndarray
115
+ ADIIS coefficients
116
+ """
117
+ n_points = len(self.geom_history)
118
+
119
+ if n_points < 2:
120
+ return np.array([1.0])
121
+
122
+ # Construct energy difference and gradient matrices
123
+ E_diff = np.zeros((n_points, n_points))
124
+ for i in range(n_points):
125
+ for j in range(n_points):
126
+ if i == j:
127
+ E_diff[i, j] = 0.0
128
+ else:
129
+ dx = self.geom_history[j] - self.geom_history[i]
130
+ # Energy difference
131
+ e_diff = self.energy_history[j] - self.energy_history[i]
132
+ # Approximate first-order term
133
+ first_order = np.dot(self.grad_history[i].flatten(), dx.flatten())
134
+ # Augmentation: Include gradient dot product as approximate
135
+ # second-order information
136
+ aug = np.dot(
137
+ (self.grad_history[j] - self.grad_history[i]).flatten(),
138
+ dx.flatten()
139
+ )
140
+ # Combined energy difference with augmentation
141
+ E_diff[i, j] = e_diff - first_order
142
+ # Scale augmentation term based on previous performance
143
+ if self.adiis_failure_count > 0:
144
+ aug_scale = 0.5 # Reduce augmentation influence if having failures
145
+ else:
146
+ aug_scale = 1.0
147
+
148
+ # Apply quality weighting
149
+ quality_factor = (self.quality_history[i] + self.quality_history[j]) / 2.0
150
+
151
+ E_diff[i, j] *= quality_factor
152
+ E_diff[i, j] += aug_scale * aug * quality_factor
153
+
154
+ try:
155
+ # Define the ADIIS objective function
156
+ def objective(x):
157
+ # Quadratic term
158
+ quad = 0.0
159
+ for i in range(n_points):
160
+ for j in range(n_points):
161
+ quad += x[i] * x[j] * E_diff[i, j]
162
+
163
+ # Add small regularization for stability
164
+ reg = self.adiis_regularization * np.sum((x - 1.0/n_points)**2)
165
+
166
+ return quad + reg
167
+
168
+ # Constraint: sum of coefficients = 1
169
+ def constraint(x):
170
+ return np.sum(x) - 1.0
171
+
172
+ # Non-negative constraints
173
+ bounds = [(0, 1) for _ in range(n_points)]
174
+ constraints = {'type': 'eq', 'fun': constraint}
175
+
176
+ # Initial guess: equal weights
177
+ x0 = np.ones(n_points) / n_points
178
+
179
+ # Solve the constrained minimization problem
180
+ result = minimize(
181
+ objective, x0,
182
+ method='SLSQP',
183
+ bounds=bounds,
184
+ constraints=constraints,
185
+ options={'ftol': 1e-6, 'maxiter': 200}
186
+ )
187
+
188
+ if result.success:
189
+ return result.x
190
+ else:
191
+ # Fall back to most recent point with small contributions from others
192
+ fallback = np.zeros(n_points)
193
+ fallback[-1] = 0.7 # 70% latest point
194
+ remaining = 0.3
195
+ if n_points > 1:
196
+ for i in range(n_points-1):
197
+ fallback[i] = remaining / (n_points-1)
198
+ else:
199
+ fallback[0] = 1.0
200
+
201
+ print("ADIIS equation solver failed, using fallback coefficients")
202
+ return fallback
203
+
204
+ except Exception as e:
205
+ print(f"ADIIS solver exception: {str(e)}")
206
+ # Fallback: exponential weighting favoring recent points
207
+ fallback = np.zeros(n_points)
208
+ total_weight = 0.0
209
+ for i in range(n_points):
210
+ fallback[i] = 2.0 ** i # Exponential weighting
211
+ total_weight += fallback[i]
212
+ fallback /= total_weight
213
+
214
+ return fallback
215
+
216
+ def _calculate_adiis_geometry(self):
217
+ """
218
+ Calculate new geometry using ADIIS
219
+
220
+ Returns:
221
+ --------
222
+ tuple
223
+ (extrapolated_geometry, coeffs, success, quality)
224
+ """
225
+ n_points = len(self.geom_history)
226
+
227
+ if n_points < self.adiis_min_points:
228
+ return None, None, False, 0.0
229
+
230
+ # Reset history if too many failures
231
+ if self.adiis_failure_count >= self.adiis_max_failures:
232
+ print(f"Warning: {self.adiis_failure_count} consecutive ADIIS failures, resetting history")
233
+ if len(self.geom_history) > 0:
234
+ self.geom_history = [self.geom_history[-1]]
235
+ self.energy_history = [self.energy_history[-1]]
236
+ self.grad_history = [self.grad_history[-1]]
237
+ self.quality_history = [1.0]
238
+
239
+ self.adiis_failure_count = 0
240
+ self.adiis_current_recovery = self.adiis_recovery_steps
241
+ self.adiis_weight_current = max(0.2, self.adiis_weight_current / 2)
242
+
243
+ return None, None, False, 0.0
244
+
245
+ try:
246
+ # Calculate ADIIS coefficients
247
+ coeffs = self._solve_adiis_equations()
248
+
249
+ # Check coefficient validity
250
+ if np.any(np.isnan(coeffs)) or abs(np.sum(coeffs) - 1.0) > 1e-4:
251
+ print("Warning: Invalid ADIIS coefficients, using most recent point")
252
+ coeffs = np.zeros(n_points)
253
+ coeffs[-1] = 1.0
254
+ quality = 0.5
255
+ else:
256
+ # Calculate quality based on coefficient distribution
257
+ # For ADIIS, prefer solutions that give more weight to lower energy points
258
+ energy_weighted_quality = 0.0
259
+ e_min = min(self.energy_history)
260
+ e_range = max(self.energy_history) - e_min
261
+
262
+ if e_range > 1e-10:
263
+ for i in range(n_points):
264
+ # Normalize energy (0 = lowest, 1 = highest)
265
+ e_norm = (self.energy_history[i] - e_min) / e_range
266
+ # Higher quality for more weight on lower energy points
267
+ energy_weighted_quality += coeffs[i] * (1.0 - e_norm)
268
+ else:
269
+ energy_weighted_quality = 0.5
270
+
271
+ quality = max(0.3, energy_weighted_quality)
272
+
273
+ # Calculate the new geometry as a linear combination
274
+ extrapolated_geometry = np.zeros_like(self.geom_history[0])
275
+ for i in range(n_points):
276
+ extrapolated_geometry += coeffs[i] * self.geom_history[i]
277
+
278
+ # Check for NaN values in the result
279
+ if np.any(np.isnan(extrapolated_geometry)):
280
+ print("Warning: NaN values in extrapolated geometry, ADIIS calculation failed")
281
+ self.adiis_failure_count += 1
282
+ return None, None, False, 0.0
283
+
284
+ # Print coefficients
285
+ print("ADIIS coefficients:", ", ".join(f"{c:.4f}" for c in coeffs))
286
+ print(f"ADIIS quality metric: {quality:.4f}")
287
+
288
+ return extrapolated_geometry, coeffs, True, quality
289
+
290
+ except Exception as e:
291
+ print(f"ADIIS extrapolation failed: {str(e)}")
292
+ self.adiis_failure_count += 1
293
+ return None, None, False, 0.0
294
+
295
+ def _validate_adiis_step(self, original_step, adiis_step, gradient, energy):
296
+ """
297
+ Validate the ADIIS step
298
+
299
+ Parameters:
300
+ -----------
301
+ original_step : numpy.ndarray
302
+ Original step
303
+ adiis_step : numpy.ndarray
304
+ ADIIS step
305
+ gradient : numpy.ndarray
306
+ Current gradient
307
+ energy : float
308
+ Current energy
309
+
310
+ Returns:
311
+ --------
312
+ tuple
313
+ (is_valid, validation_quality)
314
+ """
315
+ # 1. Check step size ratio
316
+ original_norm = np.linalg.norm(original_step)
317
+ adiis_norm = np.linalg.norm(adiis_step)
318
+
319
+ if original_norm > 1e-10:
320
+ step_ratio = adiis_norm / original_norm
321
+ if step_ratio > self.adiis_step_ratio_max:
322
+ print(f"ADIIS step too large: {step_ratio:.2f} times Original step")
323
+ return False, 0.0
324
+
325
+ # Calculate quality based on step ratio
326
+ ratio_quality = 1.0 - min(1.0, abs(np.log10(step_ratio)))
327
+ else:
328
+ ratio_quality = 0.5
329
+
330
+ # 2. Check gradient alignment (ADIIS is more lenient here)
331
+ grad_norm = np.linalg.norm(gradient)
332
+ if grad_norm > 1e-10:
333
+ neg_grad = -gradient / grad_norm
334
+ original_alignment = np.dot(original_step.flatten(), neg_grad.flatten()) / np.linalg.norm(original_step)
335
+ adiis_alignment = np.dot(adiis_step.flatten(), neg_grad.flatten()) / np.linalg.norm(adiis_step)
336
+
337
+ # Only reject if ADIIS step is strongly opposing gradient while original step is downhill
338
+ if original_alignment > 0.5 and adiis_alignment < -0.5:
339
+ print(f"ADIIS step rejected: opposing gradient direction")
340
+ return False, 0.0
341
+
342
+ # Calculate alignment quality
343
+ alignment_quality = 0.5 + 0.5 * max(-0.5, min(1.0, adiis_alignment))
344
+ else:
345
+ alignment_quality = 0.5
346
+
347
+ # 3. Check energy improvement if we have history
348
+ energy_quality = 0.5
349
+ if len(self.energy_history) > 0:
350
+ prev_best = min(self.energy_history)
351
+ if energy < prev_best:
352
+ # New lowest energy, excellent
353
+ energy_quality = 1.0
354
+ elif energy > max(self.energy_history):
355
+ # Energy increased, poor
356
+ energy_quality = 0.3
357
+ else:
358
+ # In between
359
+ e_range = max(self.energy_history) - prev_best
360
+ if e_range > 1e-10:
361
+ normalized_e = (energy - prev_best) / e_range
362
+ energy_quality = 1.0 - 0.5 * normalized_e
363
+
364
+ # 4. Overall validation quality (weighted combination)
365
+ validation_quality = 0.4 * ratio_quality + 0.3 * alignment_quality + 0.3 * energy_quality
366
+
367
+ return True, validation_quality
368
+
369
+ def run(self, geom_num_list, energy, gradient, original_move_vector):
370
+ """
371
+ Run ADIIS optimization step
372
+
373
+ Parameters:
374
+ -----------
375
+ geom_num_list : numpy.ndarray
376
+ Current geometry
377
+ energy : float
378
+ Current energy
379
+ gradient : numpy.ndarray
380
+ Current gradient
381
+ original_move_vector : numpy.ndarray
382
+ Original optimization step
383
+
384
+ Returns:
385
+ --------
386
+ numpy.ndarray
387
+ Optimized step vector
388
+ """
389
+ print("ADIIS method")
390
+ n_coords = len(geom_num_list)
391
+ grad_rms = np.sqrt(np.mean(gradient ** 2))
392
+
393
+ print(f"Energy: {energy:.8f}, Gradient RMS: {grad_rms:.8f}")
394
+
395
+ # Track energy and gradient improvements
396
+ energy_improving = energy < self.prev_energy
397
+ grad_improving = grad_rms < self.prev_grad_rms * 0.95
398
+
399
+ if energy_improving or grad_improving:
400
+ self.non_improving_count = 0
401
+ else:
402
+ self.non_improving_count += 1
403
+ if self.non_improving_count > 2:
404
+ # Reduce ADIIS weight if optimization is stalling
405
+ self.adiis_weight_current = max(0.1, self.adiis_weight_current - 0.1)
406
+ print(f"Optimization stalling, reducing ADIIS weight to {self.adiis_weight_current:.2f}")
407
+ self.non_improving_count = 0
408
+
409
+ self.prev_energy = energy
410
+ self.prev_grad_rms = grad_rms
411
+
412
+ # Calculate step quality based on energy and gradient improvement
413
+ step_quality = 1.0
414
+ if self.iter > 0 and len(self.energy_history) > 0:
415
+ # Average the energy and gradient quality factors
416
+ if energy < min(self.energy_history):
417
+ energy_quality = 1.0 # Best energy so far
418
+ elif energy > max(self.energy_history):
419
+ energy_quality = 0.5 # Worst energy so far
420
+ else:
421
+ # Scale based on where in the range it falls
422
+ e_range = max(self.energy_history) - min(self.energy_history)
423
+ if e_range > 1e-10:
424
+ e_norm = (energy - min(self.energy_history)) / e_range
425
+ energy_quality = 1.0 - 0.5 * e_norm
426
+ else:
427
+ energy_quality = 0.7
428
+
429
+ grad_quality = 1.0
430
+ if len(self.grad_history) > 0 and np.linalg.norm(self.grad_history[-1]) > 1e-10:
431
+ grad_change_ratio = grad_rms / np.sqrt(np.mean(self.grad_history[-1] ** 2))
432
+ if grad_change_ratio < 1.0:
433
+ grad_quality = 1.0
434
+ else:
435
+ grad_quality = max(0.3, 1.0 / (1.0 + np.log(grad_change_ratio)))
436
+
437
+ # Combined quality metric
438
+ step_quality = 0.7 * energy_quality + 0.3 * grad_quality
439
+
440
+ # Update history
441
+ self._update_history(geom_num_list, energy, gradient, step_quality)
442
+
443
+ # Skip ADIIS if in recovery mode
444
+ if self.adiis_current_recovery > 0:
445
+ self.adiis_current_recovery -= 1
446
+ print(f"In ADIIS recovery mode ({self.adiis_current_recovery} steps remaining), skipping ADIIS")
447
+ move_vector = original_move_vector
448
+ # Apply ADIIS if enough history has been accumulated
449
+ elif len(self.geom_history) >= self.adiis_min_points:
450
+ # Calculate ADIIS geometry
451
+ adiis_geom, adiis_coeffs, success, quality = self._calculate_adiis_geometry()
452
+
453
+ if success and adiis_geom is not None:
454
+ # Calculate ADIIS step
455
+ adiis_step = (adiis_geom - geom_num_list).reshape(n_coords, 1)
456
+
457
+ # Validate step
458
+ is_valid, validation_quality = self._validate_adiis_step(
459
+ original_move_vector, adiis_step, gradient, energy
460
+ )
461
+
462
+ if is_valid:
463
+ # Calculate adaptive weight
464
+ if self.adiis_failure_count > 0:
465
+ # Reduce weight if we've had failures
466
+ adiis_weight = max(0.1, self.adiis_weight_current -
467
+ self.adiis_failure_count * self.adiis_weight_decrement)
468
+ else:
469
+ # Otherwise use current weight, possibly increasing it
470
+ adiis_weight = min(self.adiis_weight_max,
471
+ self.adiis_weight_current + self.adiis_weight_increment)
472
+
473
+ # Scale weight by validation quality
474
+ adiis_weight *= validation_quality
475
+
476
+ original_weight = 1.0 - adiis_weight
477
+
478
+ # Calculate blended step
479
+ move_vector = original_weight * original_move_vector + adiis_weight * adiis_step
480
+ print(f"Using blended step: {original_weight:.4f}*Original + {adiis_weight:.4f}*ADIIS")
481
+
482
+ # Safety check: verify step size is reasonable
483
+ original_norm = np.linalg.norm(original_move_vector)
484
+ blended_norm = np.linalg.norm(move_vector)
485
+
486
+ if blended_norm > 2.5 * original_norm and blended_norm > 0.3:
487
+ # Cap step size to avoid large jumps
488
+ print("Warning: ADIIS step too large, scaling down")
489
+ scale_factor = 2.5 * original_norm / blended_norm
490
+ move_vector = original_move_vector + scale_factor * (move_vector - original_move_vector)
491
+ print(f"Step scaled by {scale_factor:.3f}")
492
+
493
+ # Update current weight for next iteration
494
+ self.adiis_weight_current = 0.7 * self.adiis_weight_current + 0.3 * adiis_weight
495
+ else:
496
+ print("ADIIS step validation failed, using Original step only")
497
+ move_vector = original_move_vector
498
+ self.adiis_failure_count += 1
499
+ else:
500
+ move_vector = original_move_vector
501
+ if not success:
502
+ self.adiis_failure_count += 1
503
+ else:
504
+ print(f"Building ADIIS history ({len(self.geom_history)}/{self.adiis_min_points} points)")
505
+ move_vector = original_move_vector
506
+
507
+ # Final safety checks
508
+ move_norm = np.linalg.norm(move_vector)
509
+ if move_norm < 1e-10:
510
+ print("Warning: Step size too small, using scaled gradient instead")
511
+ move_vector = -0.1 * gradient.reshape(n_coords, 1)
512
+ elif np.any(np.isnan(move_vector)) or np.any(np.isinf(move_vector)):
513
+ print("Warning: Numerical issues detected in step, using scaled gradient instead")
514
+ move_vector = -0.1 * gradient.reshape(n_coords, 1)
515
+ # Reset history on numerical failure
516
+ self.geom_history = []
517
+ self.energy_history = []
518
+ self.grad_history = []
519
+ self.quality_history = []
520
+ self.adiis_failure_count = 0
521
+
522
+ self.iter += 1
523
+ return move_vector