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,491 @@
1
+ import numpy as np
2
+
3
+
4
+ class C2DIIS:
5
+ """
6
+ Implementation of C2-DIIS (C-squared DIIS) optimization method.
7
+
8
+ This method uses squared error vectors to improve DIIS performance,
9
+ particularly in challenging optimization scenarios.
10
+
11
+ ref.: https://doi.org/10.1002/qua.560450106
12
+ """
13
+ def __init__(self):
14
+ # C2DIIS parameters
15
+ self.c2diis_history_size = 5 # History size
16
+ self.c2diis_min_points = 2 # Minimum points to start C2DIIS
17
+ self.c2diis_weight_initial = 0.2 # Initial C2DIIS weight
18
+ self.c2diis_weight_max = 0.8 # Maximum C2DIIS weight
19
+
20
+ # Robust coefficient handling
21
+ self.c2diis_coeff_min = -0.5 # Min coefficient value
22
+ self.c2diis_coeff_max = 1.5 # Max coefficient value
23
+ self.c2diis_regularization = 1e-7 # Regularization parameter
24
+
25
+ # Error recovery
26
+ self.c2diis_failure_count = 0 # Failure counter
27
+ self.c2diis_max_failures = 2 # Max failures before reset
28
+ self.c2diis_recovery_steps = 2 # Recovery mode steps
29
+ self.c2diis_current_recovery = 0 # Current recovery counter
30
+
31
+ # Step validation parameters
32
+ self.c2diis_step_ratio_max = 2.5 # Max allowed step ratio
33
+
34
+ # Weight adjustment
35
+ self.c2diis_weight_current = self.c2diis_weight_initial # Current weight
36
+ self.c2diis_weight_increment = 0.05 # Increment for success
37
+ self.c2diis_weight_decrement = 0.1 # Decrement for failure
38
+
39
+ # History storage
40
+ self.geom_history = []
41
+ self.grad_history = []
42
+ self.c2error_history = [] # Squared error vectors
43
+ self.quality_history = []
44
+
45
+ # Convergence monitoring
46
+ self.prev_grad_rms = float('inf')
47
+ self.non_improving_count = 0
48
+ self.iter = 0
49
+
50
+ def _compute_c2error(self, gradient):
51
+ """
52
+ Compute the C2-error vector (squared form of gradient)
53
+
54
+ Parameters:
55
+ -----------
56
+ gradient : numpy.ndarray
57
+ Current gradient
58
+
59
+ Returns:
60
+ --------
61
+ numpy.ndarray
62
+ C2-error vector
63
+ """
64
+ # Create squared form: g * g^T * g
65
+ g_flat = gradient.flatten()
66
+ g_norm = np.linalg.norm(g_flat)
67
+
68
+ # Avoid numerical issues with very small gradients
69
+ if g_norm < 1e-10:
70
+ return gradient.copy()
71
+
72
+ # Normalize to avoid numerical issues with very large/small gradients
73
+ g_norm = max(g_norm, 1e-10)
74
+ g_normalized = g_flat / g_norm
75
+
76
+ # Compute outer product
77
+ g_outer = np.outer(g_normalized, g_normalized)
78
+
79
+ # Multiply with original gradient to get C2-error
80
+ c2error = np.dot(g_outer, g_flat).reshape(gradient.shape)
81
+
82
+ return c2error
83
+
84
+ def _update_history(self, geometry, gradient, step_quality=1.0):
85
+ """
86
+ Update the C2DIIS history
87
+
88
+ Parameters:
89
+ -----------
90
+ geometry : numpy.ndarray
91
+ Current geometry
92
+ gradient : numpy.ndarray
93
+ Current gradient
94
+ step_quality : float
95
+ Quality metric for this point (1.0 = good, <1.0 = lower quality)
96
+ """
97
+ # Compute C2-error vector
98
+ c2error = self._compute_c2error(gradient)
99
+
100
+ # Add current point to history
101
+ self.geom_history.append(geometry.copy())
102
+ self.grad_history.append(gradient.copy())
103
+ self.c2error_history.append(c2error)
104
+ self.quality_history.append(step_quality)
105
+
106
+ # If in recovery mode, limit history
107
+ if self.c2diis_current_recovery > 0:
108
+ self.c2diis_current_recovery -= 1
109
+ if len(self.geom_history) > 2:
110
+ self.geom_history = self.geom_history[-2:]
111
+ self.grad_history = self.grad_history[-2:]
112
+ self.c2error_history = self.c2error_history[-2:]
113
+ self.quality_history = self.quality_history[-2:]
114
+ return
115
+
116
+ # Limit history size
117
+ if len(self.geom_history) > self.c2diis_history_size:
118
+ # Remove lowest quality point (except most recent)
119
+ if len(self.geom_history) > 2:
120
+ oldest_qualities = self.quality_history[:-1]
121
+ worst_idx = np.argmin(oldest_qualities)
122
+
123
+ self.geom_history.pop(worst_idx)
124
+ self.grad_history.pop(worst_idx)
125
+ self.c2error_history.pop(worst_idx)
126
+ self.quality_history.pop(worst_idx)
127
+ else:
128
+ # Default to removing oldest point
129
+ self.geom_history.pop(0)
130
+ self.grad_history.pop(0)
131
+ self.c2error_history.pop(0)
132
+ self.quality_history.pop(0)
133
+
134
+ def _solve_c2diis_equations(self):
135
+ """
136
+ Solve C2DIIS equations with robustness measures
137
+
138
+ Returns:
139
+ --------
140
+ numpy.ndarray
141
+ C2DIIS coefficients
142
+ """
143
+ n_points = len(self.c2error_history)
144
+
145
+ if n_points < 2:
146
+ return np.array([1.0])
147
+
148
+ # Construct the B matrix with C2-error dot products
149
+ B = np.zeros((n_points + 1, n_points + 1))
150
+
151
+ # Fill B matrix with error vector dot products
152
+ for i in range(n_points):
153
+ for j in range(n_points):
154
+ weight_factor = np.sqrt(self.quality_history[i] * self.quality_history[j])
155
+ B[i, j] = weight_factor * np.dot(
156
+ self.c2error_history[i].flatten(),
157
+ self.c2error_history[j].flatten()
158
+ )
159
+
160
+ # Add regularization to diagonal
161
+ diag_indices = np.diag_indices(n_points)
162
+ B[diag_indices] += self.c2diis_regularization
163
+
164
+ # Add Lagrange multiplier constraints
165
+ B[n_points, :n_points] = 1.0
166
+ B[:n_points, n_points] = 1.0
167
+ B[n_points, n_points] = 0.0
168
+
169
+ # Right-hand side vector
170
+ rhs = np.zeros(n_points + 1)
171
+ rhs[n_points] = 1.0
172
+
173
+ # Try to solve with multiple methods
174
+ methods = [
175
+ lambda: np.linalg.solve(B, rhs),
176
+ lambda: np.linalg.lstsq(B, rhs, rcond=1e-10)[0],
177
+ lambda: self._minimal_solution(n_points)
178
+ ]
179
+
180
+ coefficients = None
181
+ for solver in methods:
182
+ try:
183
+ coefficients = solver()
184
+ if not np.any(np.isnan(coefficients)) and abs(np.sum(coefficients[:n_points]) - 1.0) < 0.01:
185
+ break
186
+ except:
187
+ continue
188
+
189
+ # If all methods failed, use most recent point
190
+ if coefficients is None or np.any(np.isnan(coefficients)):
191
+ coefficients = np.zeros(n_points + 1)
192
+ coefficients[n_points-1] = 1.0 # Use most recent point
193
+
194
+ # Extract actual coefficients
195
+ return coefficients[:n_points]
196
+
197
+ def _minimal_solution(self, n_points):
198
+ """
199
+ Fallback solution when numerical methods fail
200
+ """
201
+ result = np.zeros(n_points + 1)
202
+
203
+ # Exponential weighting for recent points
204
+ total_weight = 0
205
+ for i in range(n_points):
206
+ # Exponential weighting: more weight to recent points
207
+ result[i] = 2.0**(i)
208
+ total_weight += result[i]
209
+
210
+ # Normalize to sum=1
211
+ result[:n_points] /= total_weight
212
+ return result
213
+
214
+ def _filter_coefficients(self, coeffs):
215
+ """
216
+ Filter extreme coefficient values
217
+
218
+ Parameters:
219
+ -----------
220
+ coeffs : numpy.ndarray
221
+ C2DIIS coefficients
222
+
223
+ Returns:
224
+ --------
225
+ tuple
226
+ (filtered_coeffs, was_filtered, quality)
227
+ """
228
+ # Check for extreme values
229
+ extreme_values = np.logical_or(coeffs < self.c2diis_coeff_min,
230
+ coeffs > self.c2diis_coeff_max)
231
+ has_extreme = np.any(extreme_values)
232
+
233
+ quality = 1.0
234
+
235
+ if has_extreme:
236
+ print(f"Warning: Extreme C2DIIS coefficients detected: {[f'{c:.3f}' for c in coeffs]}")
237
+
238
+ # Clip and renormalize
239
+ clipped = np.clip(coeffs, self.c2diis_coeff_min, self.c2diis_coeff_max)
240
+ sum_clipped = np.sum(clipped)
241
+
242
+ if abs(sum_clipped - 1.0) > 1e-10 and sum_clipped > 1e-10:
243
+ normalized = clipped / sum_clipped
244
+ else:
245
+ # Fall back to most recent point dominance
246
+ normalized = np.zeros_like(coeffs)
247
+ normalized[-1] = 0.7
248
+
249
+ # Distribute remaining weight
250
+ if len(coeffs) > 1:
251
+ for i in range(len(coeffs)-1):
252
+ normalized[i] = 0.3 / (len(coeffs)-1)
253
+
254
+ # Reduce quality metric
255
+ extreme_ratio = np.sum(np.abs(coeffs[extreme_values])) / np.sum(np.abs(coeffs))
256
+ quality = max(0.2, 1.0 - extreme_ratio)
257
+
258
+ self.c2diis_failure_count += 1
259
+ return normalized, True, quality
260
+
261
+ self.c2diis_failure_count = max(0, self.c2diis_failure_count - 1)
262
+ return coeffs, False, quality
263
+
264
+ def _calculate_c2diis_geometry(self):
265
+ """
266
+ Calculate new geometry using C2DIIS
267
+
268
+ Returns:
269
+ --------
270
+ tuple
271
+ (extrapolated_geometry, coeffs, success, quality)
272
+ """
273
+ n_points = len(self.geom_history)
274
+
275
+ if n_points < self.c2diis_min_points:
276
+ return None, None, False, 0.0
277
+
278
+ # Reset history if too many failures
279
+ if self.c2diis_failure_count >= self.c2diis_max_failures:
280
+ print(f"Warning: {self.c2diis_failure_count} consecutive C2DIIS failures, resetting history")
281
+ if len(self.geom_history) > 0:
282
+ self.geom_history = [self.geom_history[-1]]
283
+ self.grad_history = [self.grad_history[-1]]
284
+ self.c2error_history = [self.c2error_history[-1]]
285
+ self.quality_history = [1.0]
286
+
287
+ self.c2diis_failure_count = 0
288
+ self.c2diis_current_recovery = self.c2diis_recovery_steps
289
+ self.c2diis_weight_current = max(0.2, self.c2diis_weight_current / 2)
290
+
291
+ return None, None, False, 0.0
292
+
293
+ try:
294
+ # Calculate C2DIIS coefficients
295
+ coeffs = self._solve_c2diis_equations()
296
+ coeffs, was_filtered, quality = self._filter_coefficients(coeffs)
297
+
298
+ # Calculate new geometry
299
+ extrapolated_geometry = np.zeros_like(self.geom_history[0])
300
+ for i in range(n_points):
301
+ extrapolated_geometry += coeffs[i] * self.geom_history[i]
302
+
303
+ # Check for NaN values
304
+ if np.any(np.isnan(extrapolated_geometry)):
305
+ print("Warning: NaN values in extrapolated geometry, C2DIIS calculation failed")
306
+ self.c2diis_failure_count += 1
307
+ return None, None, False, 0.0
308
+
309
+ print("C2DIIS coefficients:", ", ".join(f"{c:.4f}" for c in coeffs))
310
+ print(f"C2DIIS quality metric: {quality:.4f}")
311
+
312
+ return extrapolated_geometry, coeffs, True, quality
313
+
314
+ except Exception as e:
315
+ print(f"C2DIIS extrapolation failed: {str(e)}")
316
+ self.c2diis_failure_count += 1
317
+ return None, None, False, 0.0
318
+
319
+ def _validate_step(self, original_step, c2diis_step, gradient, quality):
320
+ """
321
+ Validate the C2DIIS step
322
+
323
+ Parameters:
324
+ -----------
325
+ original_step : numpy.ndarray
326
+ Original step
327
+ c2diis_step : numpy.ndarray
328
+ C2DIIS step
329
+ gradient : numpy.ndarray
330
+ Current gradient
331
+ quality : float
332
+ Quality from coefficient calculation
333
+
334
+ Returns:
335
+ --------
336
+ tuple
337
+ (is_valid, validation_quality)
338
+ """
339
+ # Check step size ratio
340
+ original_norm = np.linalg.norm(original_step)
341
+ c2diis_norm = np.linalg.norm(c2diis_step)
342
+
343
+ if original_norm > 1e-10:
344
+ step_ratio = c2diis_norm / original_norm
345
+ if step_ratio > self.c2diis_step_ratio_max:
346
+ print(f"C2DIIS step too large: {step_ratio:.2f} times Original step")
347
+ return False, 0.0
348
+
349
+ ratio_quality = 1.0 - min(1.0, abs(np.log10(step_ratio)))
350
+ else:
351
+ ratio_quality = 0.5
352
+
353
+ # Check gradient alignment
354
+ grad_norm = np.linalg.norm(gradient)
355
+ if grad_norm > 1e-10:
356
+ neg_grad = -gradient / grad_norm
357
+ c2diis_alignment = np.dot(c2diis_step.flatten(), neg_grad.flatten()) / np.linalg.norm(c2diis_step)
358
+
359
+ # For C2DIIS, we're more lenient about alignment since squared error
360
+ # may have different directional properties
361
+ alignment_quality = 0.5 + 0.5 * max(-0.5, min(1.0, c2diis_alignment))
362
+ else:
363
+ alignment_quality = 0.5
364
+
365
+ # Overall validation quality
366
+ validation_quality = 0.6 * ratio_quality + 0.4 * alignment_quality
367
+
368
+ # Only reject if validation quality is very low
369
+ if validation_quality < 0.2:
370
+ return False, 0.0
371
+
372
+ return True, validation_quality
373
+
374
+ def run(self, geom_num_list, gradient, pre_gradient, original_move_vector):
375
+ """
376
+ Run C2DIIS optimization step
377
+
378
+ Parameters:
379
+ -----------
380
+ geom_num_list : numpy.ndarray
381
+ Current geometry
382
+ gradient : numpy.ndarray
383
+ Current gradient
384
+ pre_gradient : numpy.ndarray
385
+ Previous gradient
386
+ original_move_vector : numpy.ndarray
387
+ Original optimization step
388
+
389
+ Returns:
390
+ --------
391
+ numpy.ndarray
392
+ Optimized step vector
393
+ """
394
+ print("C2DIIS method")
395
+ grad_rms = np.sqrt(np.mean(gradient ** 2))
396
+ n_coords = len(geom_num_list)
397
+
398
+ print(f"Gradient RMS: {grad_rms:.8f}")
399
+
400
+ # Check convergence progress
401
+ improving = grad_rms < self.prev_grad_rms * 0.95
402
+ if improving:
403
+ self.non_improving_count = 0
404
+ else:
405
+ self.non_improving_count += 1
406
+ if self.non_improving_count > 2:
407
+ self.c2diis_weight_current = max(0.1, self.c2diis_weight_current - 0.1)
408
+ print(f"Optimization stalling, reducing C2DIIS weight to {self.c2diis_weight_current:.2f}")
409
+ self.non_improving_count = 0
410
+
411
+ self.prev_grad_rms = grad_rms
412
+
413
+ # Calculate step quality
414
+ step_quality = 1.0
415
+ if self.iter > 0 and np.linalg.norm(pre_gradient) > 1e-10:
416
+ grad_change_ratio = np.linalg.norm(gradient) / np.linalg.norm(pre_gradient)
417
+ if grad_change_ratio < 1.0:
418
+ step_quality = 1.0
419
+ else:
420
+ step_quality = max(0.3, 1.0 / (1.0 + np.log(grad_change_ratio)))
421
+
422
+ # Update history
423
+ self._update_history(geom_num_list, gradient, step_quality)
424
+
425
+ # Skip if in recovery mode
426
+ if self.c2diis_current_recovery > 0:
427
+ self.c2diis_current_recovery -= 1
428
+ print(f"In C2DIIS recovery mode ({self.c2diis_current_recovery} steps remaining)")
429
+ move_vector = original_move_vector
430
+ # Apply C2DIIS if enough history points
431
+ elif len(self.geom_history) >= self.c2diis_min_points:
432
+ # Calculate C2DIIS geometry
433
+ c2diis_geom, c2diis_coeffs, success, quality = self._calculate_c2diis_geometry()
434
+
435
+ if success and c2diis_geom is not None:
436
+ # Calculate C2DIIS step
437
+ c2diis_step = (c2diis_geom - geom_num_list).reshape(n_coords, 1)
438
+
439
+ # Validate step
440
+ is_valid, validation_quality = self._validate_step(original_move_vector, c2diis_step, gradient, quality)
441
+
442
+ if is_valid:
443
+ # Calculate adaptive weight
444
+ if self.c2diis_failure_count > 0:
445
+ c2diis_weight = max(0.1, self.c2diis_weight_current -
446
+ self.c2diis_failure_count * self.c2diis_weight_decrement)
447
+ elif grad_rms < 0.01:
448
+ c2diis_weight = min(self.c2diis_weight_max,
449
+ self.c2diis_weight_current + self.c2diis_weight_increment)
450
+ else:
451
+ c2diis_weight = self.c2diis_weight_current
452
+
453
+ # Scale by validation quality
454
+ c2diis_weight *= validation_quality
455
+
456
+ original_weight = 1.0 - c2diis_weight
457
+
458
+ # Calculate blended step
459
+ move_vector = original_weight * original_move_vector + c2diis_weight * c2diis_step
460
+ print(f"Using blended step: {original_weight:.4f}*Original + {c2diis_weight:.4f}*C2DIIS")
461
+
462
+ # Update current weight for next iteration
463
+ self.c2diis_weight_current = 0.7 * self.c2diis_weight_current + 0.3 * c2diis_weight
464
+ else:
465
+ print("C2DIIS step validation failed, using Original step")
466
+ move_vector = original_move_vector
467
+ self.c2diis_failure_count += 1
468
+ else:
469
+ move_vector = original_move_vector
470
+ if not success:
471
+ self.c2diis_failure_count += 1
472
+ else:
473
+ print(f"Building C2DIIS history ({len(self.geom_history)}/{self.c2diis_min_points} points)")
474
+ move_vector = original_move_vector
475
+
476
+ # Final safety check
477
+ move_norm = np.linalg.norm(move_vector)
478
+ if move_norm < 1e-10:
479
+ print("Warning: Step size too small, using scaled gradient")
480
+ move_vector = -0.1 * gradient.reshape(n_coords, 1)
481
+ elif np.any(np.isnan(move_vector)) or np.any(np.isinf(move_vector)):
482
+ print("Warning: Numerical issues in step, using scaled gradient")
483
+ move_vector = -0.1 * gradient.reshape(n_coords, 1)
484
+ # Reset history
485
+ self.geom_history = []
486
+ self.grad_history = []
487
+ self.c2error_history = []
488
+ self.quality_history = []
489
+
490
+ self.iter += 1
491
+ return move_vector