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,625 @@
1
+ import numpy as np
2
+ from scipy.linalg import solve, lstsq
3
+ import warnings
4
+
5
+
6
+ class KrylovDIIS:
7
+ """
8
+ Implementation of Krylov-DIIS optimization method.
9
+
10
+ This method combines Krylov subspace techniques with DIIS extrapolation
11
+ for accelerated optimization, particularly effective for large-scale
12
+ quantum chemical calculations.
13
+ """
14
+
15
+ def __init__(self):
16
+ # Krylov subspace parameters
17
+ self.krylov_dimension = 5 # Dimension of the Krylov subspace
18
+ self.krylov_restart = 10 # Number of steps before Krylov restart
19
+ self.orthogonalization = 'mgs' # Orthogonalization method ('mgs' or 'arnoldi')
20
+
21
+ # DIIS parameters
22
+ self.diis_max_points = 6 # Maximum number of DIIS points to use
23
+ self.diis_min_points = 3 # Minimum points before starting DIIS
24
+ self.diis_error_metric = 'grad' # Error metric ('grad', 'energy_change', 'combined')
25
+ self.diis_start_iter = 2 # Iteration to start DIIS
26
+
27
+ # Numerical parameters
28
+ self.diis_regularization = 1e-8 # Regularization for DIIS matrix
29
+ self.krylov_tolerance = 1e-10 # Tolerance for Krylov basis
30
+ self.error_threshold = 1.0 # Error threshold for including points
31
+ self.weight_factor = 0.9 # Weight factor for DIIS extrapolation
32
+
33
+ # Adaptation parameters
34
+ self.dynamic_subspace = True # Whether to adapt Krylov dimension
35
+ self.krylov_min_dim = 3 # Minimum Krylov dimension
36
+ self.krylov_max_dim = 10 # Maximum Krylov dimension
37
+ self.adapt_frequency = 5 # How often to adapt Krylov dimension
38
+
39
+ # Fallback and recovery
40
+ self.max_diis_failures = 2 # Maximum consecutive DIIS failures
41
+ self.stabilize_step = False # Whether to stabilize with SD step
42
+ self.recovery_steps = 2 # Number of steps in recovery mode
43
+
44
+ # History storage
45
+ self.geom_history = [] # Geometry history
46
+ self.grad_history = [] # Gradient history
47
+ self.energy_history = [] # Energy history
48
+ self.error_history = [] # Error vector history
49
+ self.krylov_basis = [] # Current Krylov basis
50
+ self.hess_proj = None # Projected Hessian in Krylov subspace
51
+
52
+ # Performance tracking
53
+ self.diis_failure_count = 0 # Count of DIIS failures
54
+ self.recovery_counter = 0 # Counter for recovery steps
55
+ self.iter = 0 # Iteration counter
56
+
57
+ def _update_histories(self, geometry, gradient, energy):
58
+ """
59
+ Update the optimization histories with current data.
60
+
61
+ Parameters:
62
+ -----------
63
+ geometry : numpy.ndarray
64
+ Current geometry
65
+ gradient : numpy.ndarray
66
+ Current gradient
67
+ energy : float
68
+ Current energy value
69
+ """
70
+ # Add new data to histories
71
+ self.geom_history.append(geometry.copy())
72
+ self.grad_history.append(gradient.copy())
73
+ self.energy_history.append(energy)
74
+
75
+ # Calculate error vector based on selected metric
76
+ error_vec = self._calculate_error_vector(gradient)
77
+ self.error_history.append(error_vec)
78
+
79
+ # Limit history size to maximum DIIS points
80
+ if len(self.geom_history) > self.diis_max_points:
81
+ self.geom_history.pop(0)
82
+ self.grad_history.pop(0)
83
+ self.energy_history.pop(0)
84
+ self.error_history.pop(0)
85
+
86
+ def _calculate_error_vector(self, gradient):
87
+ """
88
+ Calculate the error vector for DIIS extrapolation.
89
+
90
+ Parameters:
91
+ -----------
92
+ gradient : numpy.ndarray
93
+ Current gradient
94
+
95
+ Returns:
96
+ --------
97
+ numpy.ndarray
98
+ Error vector
99
+ """
100
+ if self.diis_error_metric == 'grad':
101
+ # Use gradient as error vector
102
+ return gradient.copy()
103
+
104
+ elif self.diis_error_metric == 'energy_change' and len(self.energy_history) > 0:
105
+ # Use energy change combined with gradient
106
+ grad_norm = np.linalg.norm(gradient)
107
+ if grad_norm < 1e-10:
108
+ return gradient.copy()
109
+
110
+ # Scale by energy change if possible
111
+ if len(self.energy_history) > 0:
112
+ prev_energy = self.energy_history[-1]
113
+ energy_diff = abs(self.energy_history[-1] - prev_energy)
114
+ scale_factor = np.sqrt(1.0 + energy_diff)
115
+ return gradient * scale_factor
116
+ else:
117
+ return gradient.copy()
118
+
119
+ elif self.diis_error_metric == 'combined' and len(self.grad_history) > 0:
120
+ # Combine current gradient with gradient difference
121
+ prev_grad = self.grad_history[-1]
122
+ grad_diff = gradient - prev_grad
123
+ return gradient + 0.5 * grad_diff
124
+
125
+ else:
126
+ # Default to gradient
127
+ return gradient.copy()
128
+
129
+ def _build_krylov_subspace(self, gradient):
130
+ """
131
+ Build or update the Krylov subspace.
132
+
133
+ Parameters:
134
+ -----------
135
+ gradient : numpy.ndarray
136
+ Current gradient
137
+
138
+ Returns:
139
+ --------
140
+ bool
141
+ Whether the Krylov basis was successfully updated
142
+ """
143
+ # Check if we need to restart
144
+ restart = (self.iter % self.krylov_restart == 0) or not self.krylov_basis
145
+
146
+ if restart:
147
+ # Start a new Krylov basis with the current gradient
148
+ self.krylov_basis = []
149
+
150
+ # Normalize gradient to start the basis
151
+ grad_norm = np.linalg.norm(gradient)
152
+ if grad_norm < self.krylov_tolerance:
153
+ # Gradient too small, use random vector
154
+ warnings.warn("Gradient norm too small for Krylov basis, using random vector")
155
+ v = np.random.randn(*gradient.shape)
156
+ v = v / np.linalg.norm(v)
157
+ else:
158
+ # Use normalized gradient
159
+ v = gradient / grad_norm
160
+
161
+ # Start with normalized gradient
162
+ self.krylov_basis = [v]
163
+ self.hess_proj = None
164
+
165
+ # Determine current Krylov dimension (may be adaptive)
166
+ if self.dynamic_subspace and self.iter % self.adapt_frequency == 0:
167
+ # Adapt dimension based on convergence or problem size
168
+ grad_norm = np.linalg.norm(gradient)
169
+ if len(self.grad_history) > 2:
170
+ prev_grad_norm = np.linalg.norm(self.grad_history[-2])
171
+ # If convergence is slow, increase dimension
172
+ if grad_norm > 0.7 * prev_grad_norm:
173
+ self.krylov_dimension = min(self.krylov_dimension + 1, self.krylov_max_dim)
174
+ # If convergence is fast, decrease dimension
175
+ elif grad_norm < 0.3 * prev_grad_norm:
176
+ self.krylov_dimension = max(self.krylov_dimension - 1, self.krylov_min_dim)
177
+
178
+ # Expand the Krylov basis if needed
179
+ while len(self.krylov_basis) < self.krylov_dimension:
180
+ try:
181
+ # Generate next Krylov vector (approximately Hv)
182
+ v_prev = self.krylov_basis[-1]
183
+
184
+ # Approximate Hessian-vector product using finite differences
185
+ # If we have enough history
186
+ if len(self.grad_history) >= 2 and len(self.geom_history) >= 2:
187
+ # Get finite difference approximation of Hessian-vector product
188
+ g_diff = self.grad_history[-1] - self.grad_history[-2]
189
+ x_diff = self.geom_history[-1] - self.geom_history[-2]
190
+
191
+ # Compute direction in x_diff parallel to v_prev
192
+ v_comp = np.dot(x_diff.flatten(), v_prev.flatten())
193
+
194
+ # If sufficient component in this direction
195
+ if abs(v_comp) > 1e-10:
196
+ # Approximate Hessian-vector product
197
+ Hv = g_diff * (np.dot(v_prev.flatten(), v_prev.flatten()) / v_comp)
198
+ else:
199
+ # Fallback: use gradient difference directly
200
+ Hv = g_diff
201
+ else:
202
+ # Not enough history, use gradient as approximation
203
+ Hv = gradient
204
+
205
+ # Orthogonalize against existing basis
206
+ if self.orthogonalization == 'mgs':
207
+ # Modified Gram-Schmidt orthogonalization
208
+ v_next = Hv.copy()
209
+ for v in self.krylov_basis:
210
+ proj = np.dot(v.flatten(), v_next.flatten())
211
+ v_next = v_next - proj * v
212
+ else:
213
+ # Simple Arnoldi-like orthogonalization
214
+ v_next = Hv.copy()
215
+ for v in self.krylov_basis:
216
+ v_next = v_next - np.dot(v_next.flatten(), v.flatten()) * v
217
+
218
+ # Normalize the new vector
219
+ v_next_norm = np.linalg.norm(v_next)
220
+
221
+ # Check if new vector is sufficiently linearly independent
222
+ if v_next_norm < self.krylov_tolerance:
223
+ # Basis cannot be expanded further
224
+ print(f"Krylov basis saturated at dimension {len(self.krylov_basis)}")
225
+ break
226
+
227
+ # Add normalized vector to basis
228
+ v_next = v_next / v_next_norm
229
+ self.krylov_basis.append(v_next)
230
+
231
+ # Update the projected Hessian
232
+ self._update_projected_hessian(Hv, v_next, len(self.krylov_basis)-1)
233
+
234
+ except Exception as e:
235
+ print(f"Error expanding Krylov basis: {str(e)}")
236
+ # Return current basis
237
+ break
238
+
239
+ return len(self.krylov_basis) > 1
240
+
241
+ def _update_projected_hessian(self, Hv, v_next, idx):
242
+ """
243
+ Update the projected Hessian in the Krylov subspace.
244
+
245
+ Parameters:
246
+ -----------
247
+ Hv : numpy.ndarray
248
+ Hessian-vector product
249
+ v_next : numpy.ndarray
250
+ Next basis vector
251
+ idx : int
252
+ Index of the new vector
253
+ """
254
+ k = len(self.krylov_basis)
255
+
256
+ # Initialize or expand projected Hessian
257
+ if self.hess_proj is None:
258
+ self.hess_proj = np.zeros((k, k))
259
+ elif self.hess_proj.shape[0] < k:
260
+ # Expand Hessian matrix
261
+ new_hess = np.zeros((k, k))
262
+ old_size = self.hess_proj.shape[0]
263
+ new_hess[:old_size, :old_size] = self.hess_proj
264
+ self.hess_proj = new_hess
265
+
266
+ # Update the Hessian elements for the new vector
267
+ for i in range(k):
268
+ # H_ij = v_i^T * H * v_j
269
+ if i < idx:
270
+ self.hess_proj[i, idx] = np.dot(self.krylov_basis[i].flatten(), Hv.flatten())
271
+ self.hess_proj[idx, i] = self.hess_proj[i, idx] # Symmetry
272
+ elif i == idx:
273
+ self.hess_proj[idx, idx] = np.dot(v_next.flatten(), Hv.flatten())
274
+
275
+ def _solve_krylov_system(self, gradient):
276
+ """
277
+ Solve the optimization problem in the Krylov subspace.
278
+
279
+ Parameters:
280
+ -----------
281
+ gradient : numpy.ndarray
282
+ Current gradient
283
+
284
+ Returns:
285
+ --------
286
+ tuple
287
+ (step, success)
288
+ """
289
+ k = len(self.krylov_basis)
290
+
291
+ if k < 2:
292
+ # Not enough basis vectors
293
+ return None, False
294
+
295
+ # Project gradient into Krylov subspace
296
+ g_proj = np.zeros(k)
297
+ for i in range(k):
298
+ g_proj[i] = np.dot(self.krylov_basis[i].flatten(), gradient.flatten())
299
+
300
+ # Check if projected Hessian is available
301
+ if self.hess_proj is None or self.hess_proj.shape[0] < k:
302
+ return None, False
303
+
304
+ try:
305
+ # Try to solve the system (H_proj * alpha = -g_proj)
306
+ # Add regularization for stability
307
+ H_reg = self.hess_proj + self.diis_regularization * np.eye(k)
308
+ alpha = solve(H_reg, -g_proj)
309
+
310
+ # Construct the step in the original space
311
+ step = np.zeros_like(gradient)
312
+ for i in range(k):
313
+ step += alpha[i] * self.krylov_basis[i]
314
+
315
+ return step, True
316
+
317
+ except Exception as e:
318
+ print(f"Failed to solve Krylov system: {str(e)}")
319
+ return None, False
320
+
321
+ def _solve_diis_system(self):
322
+ """
323
+ Solve the DIIS extrapolation problem.
324
+
325
+ Returns:
326
+ --------
327
+ tuple
328
+ (diis_geometry, coefficients, success)
329
+ """
330
+ n_points = len(self.geom_history)
331
+
332
+ if n_points < self.diis_min_points:
333
+ # Not enough points for DIIS
334
+ return None, None, False
335
+
336
+ # Construct the DIIS B matrix
337
+ B = np.zeros((n_points + 1, n_points + 1))
338
+
339
+ # Fill B matrix with error vector dot products
340
+ for i in range(n_points):
341
+ for j in range(n_points):
342
+ B[i, j] = np.dot(self.error_history[i].flatten(),
343
+ self.error_history[j].flatten())
344
+
345
+ # Add regularization to diagonal for numerical stability
346
+ np.fill_diagonal(B[:n_points, :n_points],
347
+ np.diag(B[:n_points, :n_points]) + self.diis_regularization)
348
+
349
+ # Add Lagrange multiplier constraints
350
+ B[n_points, :n_points] = 1.0
351
+ B[:n_points, n_points] = 1.0
352
+ B[n_points, n_points] = 0.0
353
+
354
+ # Right-hand side vector with constraint
355
+ rhs = np.zeros(n_points + 1)
356
+ rhs[n_points] = 1.0
357
+
358
+ try:
359
+ # Try to solve the DIIS equations
360
+ coeffs = solve(B, rhs)[:n_points]
361
+
362
+ # Check for reasonable coefficients
363
+ if np.any(np.isnan(coeffs)) or np.max(np.abs(coeffs)) > 10.0:
364
+ raise ValueError("DIIS produced extreme coefficients")
365
+
366
+ # Calculate the extrapolated geometry
367
+ diis_geom = np.zeros_like(self.geom_history[0])
368
+ for i in range(n_points):
369
+ diis_geom += coeffs[i] * self.geom_history[i]
370
+
371
+ return diis_geom, coeffs, True
372
+
373
+ except Exception as e:
374
+ print(f"DIIS system solve failed: {str(e)}")
375
+
376
+ try:
377
+ # Fallback: try least squares with non-negative constraints
378
+ coeffs = np.zeros(n_points)
379
+ coeffs[-1] = 1.0 # Start with most recent point
380
+
381
+ return self.geom_history[-1].copy(), coeffs, False
382
+
383
+ except:
384
+ # Ultimate fallback: use most recent point
385
+ coeffs = np.zeros(n_points)
386
+ coeffs[-1] = 1.0
387
+ return self.geom_history[-1].copy(), coeffs, False
388
+
389
+ def _blend_steps(self, krylov_step, diis_step, original_step, gradient):
390
+ """
391
+ Blend different steps based on their quality.
392
+
393
+ Parameters:
394
+ -----------
395
+ krylov_step : numpy.ndarray or None
396
+ Step from Krylov subspace optimization
397
+ diis_step : numpy.ndarray or None
398
+ Step from DIIS extrapolation
399
+ original_step : numpy.ndarray
400
+ Original optimizer step
401
+ gradient : numpy.ndarray
402
+ Current gradient
403
+
404
+ Returns:
405
+ --------
406
+ numpy.ndarray
407
+ Blended step
408
+ """
409
+ # Use negative gradient direction for evaluating alignment
410
+ grad_norm = np.linalg.norm(gradient)
411
+ if grad_norm > 1e-10:
412
+ neg_grad_dir = -gradient / grad_norm
413
+ else:
414
+ # If gradient is nearly zero, any direction is fine
415
+ return original_step
416
+
417
+ # Start with original step
418
+ blend_weights = {"original": 1.0, "krylov": 0.0, "diis": 0.0}
419
+
420
+ # Evaluate Krylov step if available
421
+ if krylov_step is not None:
422
+ krylov_norm = np.linalg.norm(krylov_step)
423
+ if krylov_norm > 1e-10:
424
+ # Check alignment with negative gradient
425
+ krylov_align = np.dot(krylov_step.flatten(), neg_grad_dir.flatten()) / krylov_norm
426
+
427
+ # If reasonably aligned with descent direction
428
+ if krylov_align > 0.1:
429
+ # Allocate weight to Krylov step
430
+ krylov_weight = min(0.7, max(0.3, krylov_align))
431
+ blend_weights["krylov"] = krylov_weight
432
+ blend_weights["original"] -= krylov_weight * 0.7 # Reduce original weight
433
+
434
+ # Evaluate DIIS step if available
435
+ if diis_step is not None:
436
+ diis_norm = np.linalg.norm(diis_step)
437
+ if diis_norm > 1e-10:
438
+ # Calculate DIIS step (extrapolated geometry - current geometry)
439
+ diis_step_vec = diis_step - self.geom_history[-1]
440
+ diis_step_norm = np.linalg.norm(diis_step_vec)
441
+
442
+ if diis_step_norm > 1e-10:
443
+ # Check alignment with negative gradient
444
+ diis_align = np.dot(diis_step_vec.flatten(), neg_grad_dir.flatten()) / diis_step_norm
445
+
446
+ # If reasonably aligned with descent direction
447
+ if diis_align > 0.0:
448
+ # Allocate weight to DIIS step based on accuracy history
449
+ diis_weight = self.weight_factor * min(0.8, max(0.2, diis_align))
450
+ blend_weights["diis"] = diis_weight
451
+ # Reduce other weights proportionally
452
+ total_other = blend_weights["original"] + blend_weights["krylov"]
453
+ if total_other > 0:
454
+ factor = (1.0 - diis_weight) / total_other
455
+ blend_weights["original"] *= factor
456
+ blend_weights["krylov"] *= factor
457
+
458
+ # Normalize weights (should sum to 1.0)
459
+ total_weight = sum(blend_weights.values())
460
+ if abs(total_weight - 1.0) > 1e-10:
461
+ for k in blend_weights:
462
+ blend_weights[k] /= total_weight
463
+
464
+ print(f"Blend weights: Original={blend_weights['original']:.3f}, "
465
+ f"Krylov={blend_weights['krylov']:.3f}, DIIS={blend_weights['diis']:.3f}")
466
+
467
+ # Construct the blended step
468
+ blended_step = np.zeros_like(original_step)
469
+
470
+ # Add original step contribution
471
+ blended_step += blend_weights["original"] * original_step
472
+
473
+ # Add Krylov step contribution
474
+ if krylov_step is not None and blend_weights["krylov"] > 0:
475
+ # Scale to similar magnitude as original step
476
+ orig_norm = np.linalg.norm(original_step)
477
+ krylov_norm = np.linalg.norm(krylov_step)
478
+
479
+ if krylov_norm > 1e-10 and orig_norm > 1e-10:
480
+ # Scale Krylov step if it's much larger/smaller
481
+ if krylov_norm > 2.0 * orig_norm:
482
+ scale = 2.0 * orig_norm / krylov_norm
483
+ krylov_step = krylov_step * scale
484
+ elif krylov_norm < 0.5 * orig_norm:
485
+ scale = 0.5 * orig_norm / krylov_norm
486
+ krylov_step = krylov_step * scale
487
+
488
+ blended_step += blend_weights["krylov"] * krylov_step
489
+
490
+ # Add DIIS step contribution
491
+ if diis_step is not None and blend_weights["diis"] > 0:
492
+ # Convert DIIS geometry to step vector
493
+ diis_step_vec = diis_step - self.geom_history[-1]
494
+
495
+ # Scale to similar magnitude as original step
496
+ orig_norm = np.linalg.norm(original_step)
497
+ diis_norm = np.linalg.norm(diis_step_vec)
498
+
499
+ if diis_norm > 1e-10 and orig_norm > 1e-10:
500
+ # Scale DIIS step if it's much larger/smaller
501
+ if diis_norm > 2.0 * orig_norm:
502
+ scale = 2.0 * orig_norm / diis_norm
503
+ diis_step_vec = diis_step_vec * scale
504
+ elif diis_norm < 0.5 * orig_norm:
505
+ scale = 0.5 * orig_norm / diis_norm
506
+ diis_step_vec = diis_step_vec * scale
507
+
508
+ blended_step += blend_weights["diis"] * diis_step_vec.reshape(original_step.shape)
509
+
510
+ # Final safety check
511
+ blended_norm = np.linalg.norm(blended_step)
512
+ orig_norm = np.linalg.norm(original_step)
513
+
514
+ if blended_norm > 3.0 * orig_norm:
515
+ # Cap the step size for safety
516
+ print(f"Capping step size: {blended_norm:.4f} → {3.0 * orig_norm:.4f}")
517
+ blended_step = blended_step * (3.0 * orig_norm / blended_norm)
518
+
519
+ return blended_step
520
+
521
+ def run(self, geom_num_list, energy, gradient, original_move_vector):
522
+ """
523
+ Run Krylov-DIIS optimization step.
524
+
525
+ Parameters:
526
+ -----------
527
+ geom_num_list : numpy.ndarray
528
+ Current geometry
529
+ energy : float
530
+ Current energy value
531
+ gradient : numpy.ndarray
532
+ Current gradient
533
+ original_move_vector : numpy.ndarray
534
+ Original optimization step
535
+
536
+ Returns:
537
+ --------
538
+ numpy.ndarray
539
+ Optimized step vector
540
+ """
541
+ print("Krylov-DIIS method")
542
+
543
+ # Update histories
544
+ self._update_histories(geom_num_list, gradient, energy)
545
+
546
+ # Skip advanced methods in very early iterations
547
+ if self.iter < self.diis_start_iter:
548
+ print(f"Building history (iteration {self.iter+1}), using original step")
549
+ self.iter += 1
550
+ return original_move_vector
551
+
552
+ # Skip if in recovery mode
553
+ if self.recovery_counter > 0:
554
+ print(f"In recovery mode ({self.recovery_counter} steps remaining)")
555
+ self.recovery_counter -= 1
556
+ self.iter += 1
557
+ return original_move_vector
558
+
559
+ # Build/update Krylov subspace
560
+ krylov_success = self._build_krylov_subspace(gradient)
561
+ print(f"Krylov basis dimension: {len(self.krylov_basis)}")
562
+
563
+ # Attempt Krylov subspace optimization
564
+ krylov_step = None
565
+ if krylov_success:
566
+ krylov_step, krylov_solved = self._solve_krylov_system(gradient)
567
+ if krylov_solved:
568
+ print("Krylov subspace optimization successful")
569
+ else:
570
+ print("Krylov subspace optimization failed")
571
+
572
+ # Attempt DIIS extrapolation if we have enough history
573
+ diis_geom = None
574
+ if len(self.geom_history) >= self.diis_min_points and self.diis_failure_count < self.max_diis_failures:
575
+ diis_geom, diis_coeffs, diis_success = self._solve_diis_system()
576
+
577
+ if diis_success:
578
+ print("DIIS extrapolation successful")
579
+ print("DIIS coefficients:", ", ".join(f"{c:.4f}" for c in diis_coeffs))
580
+ # Reset failure counter on success
581
+ self.diis_failure_count = max(0, self.diis_failure_count - 1)
582
+ else:
583
+ print("DIIS extrapolation failed")
584
+ self.diis_failure_count += 1
585
+
586
+ # If too many failures, enter recovery mode
587
+ if self.diis_failure_count >= self.max_diis_failures:
588
+ print(f"Too many DIIS failures ({self.diis_failure_count}), entering recovery mode")
589
+ self.recovery_counter = self.recovery_steps
590
+
591
+ # Blend the steps
592
+ blended_step = self._blend_steps(krylov_step, diis_geom, original_move_vector, gradient)
593
+
594
+ # Add a small steepest descent component for stability if requested
595
+ if self.stabilize_step:
596
+ grad_norm = np.linalg.norm(gradient)
597
+ if grad_norm > 1e-10:
598
+ sd_step = -0.05 * gradient / grad_norm
599
+ step_norm = np.linalg.norm(blended_step)
600
+ if step_norm > 1e-10:
601
+ # Scale SD step relative to blended step
602
+ sd_step = sd_step * (0.1 * step_norm / np.linalg.norm(sd_step))
603
+ blended_step += sd_step
604
+ print("Added stabilizing SD component")
605
+
606
+ # Final checks
607
+ if np.any(np.isnan(blended_step)) or np.any(np.isinf(blended_step)):
608
+ print("Warning: Numerical issues in step, using original step")
609
+ move_vector = original_move_vector
610
+ else:
611
+ move_vector = blended_step
612
+
613
+ # Make sure step isn't too small
614
+ if np.linalg.norm(move_vector) < 1e-10:
615
+ print("Warning: Step too small, using scaled original step")
616
+ move_vector = original_move_vector
617
+
618
+ # If original also too small, use scaled negative gradient
619
+ if np.linalg.norm(move_vector) < 1e-10:
620
+ grad_norm = np.linalg.norm(gradient)
621
+ if grad_norm > 1e-10:
622
+ move_vector = -0.1 * gradient / grad_norm
623
+
624
+ self.iter += 1
625
+ return move_vector
@@ -0,0 +1,21 @@
1
+ import numpy as np
2
+ import copy
3
+
4
+ class LARS:
5
+ def __init__(self, beta=0.6, **config):
6
+ # ref. arXiv:1708.03888
7
+ # https://gist.github.com/redknightlois/c4023d393eb8f92bb44b2ab582d7ec20
8
+ self.beta = beta
9
+ self.config = config
10
+ self.iter = 0
11
+ return
12
+
13
+ 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, tmp_move_vector):
14
+ print("LARS")
15
+ weight = np.clip(np.linalg.norm(geom_num_list), 0, 10)
16
+
17
+ scaled_lr = (weight / (np.linalg.norm(tmp_move_vector) + weight * self.beta))
18
+
19
+ self.iter += 1
20
+ print("scaled learning rate: ", scaled_lr)
21
+ return scaled_lr#Bohr