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,433 @@
1
+ import numpy as np
2
+ from scipy.linalg import null_space
3
+
4
+ """
5
+ References:
6
+
7
+ FSB, Bofill
8
+ J. Chem. Phys. 1999, 111, 10806
9
+
10
+ MSP
11
+ Journal of Molecular Structure: THEOCHEM 2002, 591 (1-3), 35-57.
12
+
13
+ CFD (compact finite difference) Hessian approximation approach
14
+ J. Chem. Theory Comput. 2013, 9, 54-64
15
+ J. Chem. Phys. 2010, 133, 074101
16
+
17
+ Double Damping (DD)
18
+ arXiv:2006.08877v3 [cs.LG] 7 Jan 2021
19
+
20
+ """
21
+
22
+ class ModelHessianUpdate:
23
+ def __init__(self):
24
+ self.Initialization = True
25
+ self.denom_threshold = 1e-10
26
+ # Default parameters for Double Damping
27
+ self.dd_mu1 = 0.2
28
+ self.dd_mu2 = 0.2
29
+ return
30
+
31
+ # -----------------------------------------------------------------
32
+ # Private Helper Methods (Strictly based on original code)
33
+ # -----------------------------------------------------------------
34
+
35
+ def _calculate_bfgs_delta(self, hess, s, y):
36
+ """
37
+ Calculates the BFGS update terms exactly as written in the original functions.
38
+ delta_B = (y * y.T / y.T*s) - (B*s*s.T*B.T / s.T*B*s)
39
+
40
+ Note: This implementation strictly follows the original np.dot usage.
41
+ If s and y are 1D arrays, np.dot(y, y.T) computes an inner product (scalar).
42
+ """
43
+ n = len(y)
44
+ delta_hess = np.zeros((n, n))
45
+
46
+ # Term 1
47
+ demon_1 = np.dot(s.T, y)
48
+ term1 = np.zeros((n, n))
49
+ if np.abs(demon_1) >= self.denom_threshold:
50
+ # Strictly using np.dot(y, y.T) as in the original code
51
+ term1 = np.dot(y, y.T) / demon_1
52
+ else:
53
+ print("BFGS denominator 1 (y.T*s) is too small, term1 set to zero.")
54
+
55
+ # Term 2
56
+ demon_2 = np.dot(np.dot(s.T, hess), s)
57
+ term2 = np.zeros((n, n))
58
+ if np.abs(demon_2) >= self.denom_threshold:
59
+ # Strictly using the original complex np.dot chain
60
+ term2 = np.dot(np.dot(np.dot(hess, s), s.T), hess.T) / demon_2
61
+ else:
62
+ print("BFGS denominator 2 (s.T*B*s) is too small, term2 set to zero.")
63
+
64
+ delta_hess = term1 - term2
65
+ return delta_hess
66
+
67
+ def _calculate_sr1_delta(self, A, s):
68
+ """
69
+ Calculates the SR1 update term exactly as written in the original functions.
70
+ A = (y - B*s) or A = 2.0 * (y - B*s)
71
+ delta_B = A*A.T / A.T*s
72
+
73
+ Note: This implementation strictly follows the original np.dot usage.
74
+ If A is a 1D array, np.dot(A, A.T) computes an inner product (scalar).
75
+ """
76
+ delta_hess_SR1 = np.zeros((len(s), len(s)))
77
+ delta_hess_SR1_denominator = np.dot(A.T, s)
78
+
79
+ if np.abs(delta_hess_SR1_denominator) >= self.denom_threshold:
80
+ # Strictly using np.dot(A, A.T) as in the original code
81
+ delta_hess_SR1 = np.dot(A, A.T) / delta_hess_SR1_denominator
82
+ else:
83
+ print("SR1 denominator (A.T*s) is too small, term set to zero.")
84
+
85
+ return delta_hess_SR1
86
+
87
+ def _calculate_psb_delta(self, hess, s, y):
88
+ """
89
+ Calculates the PSB update term exactly as written in the original functions.
90
+ """
91
+ n = len(y)
92
+ delta_hess_P = np.zeros((n, n))
93
+ block_1 = y - 1 * np.dot(hess, s)
94
+ block_2_denominator = np.dot(s.T, s)
95
+
96
+ if np.abs(block_2_denominator) >= self.denom_threshold:
97
+ # Logic from original PSB_hessian_update
98
+ block_2 = np.dot(s, s.T) / block_2_denominator ** 2
99
+ delta_hess_P = -1 * np.dot(block_1.T, s) * block_2 + \
100
+ (np.dot(block_1, s.T) + np.dot(s, block_1.T)) / block_2_denominator
101
+ else:
102
+ print("PSB denominator (s.T*s) is too small, term set to zero.")
103
+
104
+ return delta_hess_P
105
+
106
+ def _calculate_bofill_const(self, A, s):
107
+ """
108
+ Calculates the Bofill constant (phi^2) exactly as written in the original functions.
109
+ phi^2 = ( (A.T*s)*(A.T*s) ) / ( (A.T*A)*(s.T*s) )
110
+
111
+ Note: This implementation strictly follows the original np.dot usage.
112
+ If A and s are 1D arrays, this correctly computes (A.T@s)**2 / ((A.T@A)*(s.T@s)).
113
+ """
114
+ Bofill_const = 0.0
115
+
116
+ # Original calculation (assuming 1D arrays):
117
+ # Numerator: (A.T @ s) * (A.T @ s)
118
+ # Denominator: (A.T @ A) * (s.T @ s)
119
+
120
+ Bofill_const_numerator = np.dot(np.dot(np.dot(A.T, s), A.T), s)
121
+ Bofill_const_denominator = np.dot(np.dot(np.dot(A.T, A), s.T), s)
122
+
123
+ if np.abs(Bofill_const_denominator) >= self.denom_threshold:
124
+ Bofill_const = Bofill_const_numerator / Bofill_const_denominator
125
+ else:
126
+ Bofill_const = 0.0
127
+ print("Bofill_const denominator is too small, set to zero.")
128
+
129
+ print("Bofill_const:", Bofill_const)
130
+ return Bofill_const
131
+
132
+ # -----------------------------------------------------------------
133
+ # Initialization / Scaling
134
+ # -----------------------------------------------------------------
135
+
136
+ def _auto_scale_hessian(self, hess, displacement, delta_grad):
137
+ """
138
+ Heuristic to scale matrix at first iteration.
139
+ Described in Nocedal and Wright "Numerical Optimization"
140
+ p.143 formula (6.20).
141
+ """
142
+ if self.Initialization and np.allclose(hess, np.eye(len(delta_grad)), atol=1e-8):
143
+ print("Auto scaling Hessian")
144
+ s_norm_2 = np.dot(displacement.T, displacement)
145
+ y_norm_2 = np.dot(delta_grad.T, delta_grad)
146
+ ys = np.abs(np.dot(delta_grad.T, displacement))
147
+
148
+ if np.abs(s_norm_2) < 1e-10 or np.abs(y_norm_2) < 1e-10 or np.abs(ys) < 1e-10:
149
+ print("Norms too small, skip scaling.")
150
+ return hess
151
+
152
+ scale_factor = y_norm_2 / ys
153
+ print("Scale factor:", scale_factor)
154
+ hess = hess * scale_factor
155
+ self.Initialization = False
156
+
157
+ return hess
158
+
159
+ # -----------------------------------------------------------------
160
+ # Flowchart Method
161
+ # -----------------------------------------------------------------
162
+
163
+ def flowchart_hessian_update(self, hess, displacement, delta_grad, method):
164
+ print("Flowchart Hessian Update")
165
+ #Theor Chem Acc (2016) 135:84
166
+
167
+ # Note: Strictly adhering to the original code: z = y - B*y
168
+ # The paper (TCA 2016, eq 19) suggests z = y - B*s
169
+ z = delta_grad - np.dot(hess, delta_grad)
170
+
171
+ zs_denominator = np.linalg.norm(displacement) * np.linalg.norm(z)
172
+ if abs(zs_denominator) < self.denom_threshold:
173
+ zs_denominator += self.denom_threshold
174
+ zs = np.dot(z.T, displacement) / zs_denominator
175
+
176
+ ys_denominator = np.linalg.norm(displacement) * np.linalg.norm(delta_grad)
177
+ if abs(ys_denominator) < self.denom_threshold:
178
+ ys_denominator += self.denom_threshold
179
+
180
+ ys = np.dot(delta_grad.T, displacement) / ys_denominator
181
+
182
+ delta_hess = np.zeros_like(hess)
183
+ if zs < -0.1:
184
+ print("Flowchart -> SR1")
185
+ delta_hess = self.SR1_hessian_update(hess, displacement, delta_grad)
186
+ elif ys > 0.1:
187
+ print("Flowchart -> BFGS")
188
+ delta_hess = self.BFGS_hessian_update(hess, displacement, delta_grad)
189
+ else:
190
+ print("Flowchart -> FSB")
191
+ # Note: The paper suggests PSB, but the original code used FSB.
192
+ delta_hess = self.FSB_hessian_update(hess, displacement, delta_grad)
193
+
194
+ return delta_hess
195
+
196
+ # -----------------------------------------------------------------
197
+ # Double Damping (DD) Method
198
+ # -----------------------------------------------------------------
199
+
200
+ def double_damping_step2_only(self, s, y, mu2=0.2):
201
+ """
202
+ Implements ONLY Step 2 of the Double Damping (DD) procedure .
203
+ This step does NOT require the inverse Hessian H.
204
+ It is equivalent to Powell's damping with B=I [cite: 365-367].
205
+
206
+ Args:
207
+ s: displacement vector
208
+ y: delta_grad vector
209
+ mu2: Damping parameter (e.g., self.dd_mu2)
210
+
211
+ Returns:
212
+ s_tilde: (s is returned unmodified in this version)
213
+ y_tilde: Damped delta_grad vector
214
+ """
215
+ print("Applying Double Damping (Step 2 only, H-independent)")
216
+
217
+ s_tilde = s # Step 1 is skipped
218
+ y_tilde = y
219
+
220
+ # --- Step 2: Powell's damping with B=I ---
221
+ s_tilde_y = np.dot(s_tilde.T, y)
222
+ s_tilde_s_tilde = np.dot(s_tilde.T, s_tilde)
223
+
224
+ # Check if damping is needed (also ensures s_tilde_y > 0 if mu2 > 0)
225
+ if s_tilde_y < mu2 * s_tilde_s_tilde:
226
+ print(f"DD Step 2 active: s_tilde.T*y ({float(s_tilde_y):.4e}) < mu2*s_tilde.T*s_tilde ({float(mu2 * s_tilde_s_tilde):.4e})")
227
+ denominator = s_tilde_s_tilde - s_tilde_y
228
+
229
+ if np.abs(denominator) < self.denom_threshold:
230
+ theta2 = 0.1 # Fallback
231
+ print("Warning: DD Step 2 denominator near zero. Using default theta2=0.1.")
232
+ else:
233
+ theta2 = (1.0 - mu2) * s_tilde_s_tilde / denominator
234
+
235
+ theta2 = np.clip(theta2, 0.0, 1.0)
236
+ y_tilde = theta2 * y + (1.0 - theta2) * s_tilde
237
+
238
+ final_sy = np.dot(s_tilde.T, y_tilde)
239
+ if final_sy <= 0:
240
+ print(f"Warning: Damping (Step 2 only) resulted in s.T * y_tilde = {final_sy:.4e} <= 0.")
241
+
242
+ return s_tilde, y_tilde # s_tilde is the original s
243
+
244
+ # -----------------------------------------------------------------
245
+ # Standard Hessian Update Methods
246
+ # -----------------------------------------------------------------
247
+
248
+ def BFGS_hessian_update(self, hess, displacement, delta_grad):
249
+ print("BFGS Hessian Update")
250
+ return self._calculate_bfgs_delta(hess, displacement, delta_grad)
251
+
252
+ def SR1_hessian_update(self, hess, displacement, delta_grad):
253
+ print("SR1 Hessian Update")
254
+ A = delta_grad - np.dot(hess, displacement)
255
+ return self._calculate_sr1_delta(A, displacement)
256
+
257
+ def PSB_hessian_update(self, hess, displacement, delta_grad):
258
+ print("PSB Hessian Update")
259
+ return self._calculate_psb_delta(hess, displacement, delta_grad)
260
+
261
+ def FSB_hessian_update(self, hess, displacement, delta_grad):
262
+ print("FSB Hessian Update")
263
+ A = delta_grad - np.dot(hess, displacement)
264
+
265
+ delta_hess_SR1 = self._calculate_sr1_delta(A, displacement)
266
+ delta_hess_BFGS = self._calculate_bfgs_delta(hess, displacement, delta_grad)
267
+ Bofill_const = self._calculate_bofill_const(A, displacement)
268
+
269
+ # Per original code, mix with sqrt(Bofill_const)
270
+ phi = np.sqrt(Bofill_const)
271
+ delta_hess = (1.0 - phi) * delta_hess_BFGS + phi * delta_hess_SR1
272
+ return delta_hess
273
+
274
+ def CFD_FSB_hessian_update(self, hess, displacement, delta_grad):
275
+ print("CFD FSB Hessian Update")
276
+ A = 2.0 * (delta_grad - np.dot(hess, displacement))
277
+
278
+ delta_hess_SR1 = self._calculate_sr1_delta(A, displacement)
279
+ delta_hess_BFGS = self._calculate_bfgs_delta(hess, displacement, delta_grad)
280
+ Bofill_const = self._calculate_bofill_const(A, displacement)
281
+
282
+ phi = np.sqrt(Bofill_const)
283
+ delta_hess = (1.0 - phi) * delta_hess_BFGS + phi * delta_hess_SR1
284
+ return delta_hess
285
+
286
+ def Bofill_hessian_update(self, hess, displacement, delta_grad):
287
+ print("Bofill Hessian Update")
288
+ A = delta_grad - np.dot(hess, displacement)
289
+
290
+ delta_hess_SR1 = self._calculate_sr1_delta(A, displacement)
291
+ delta_hess_PSB = self._calculate_psb_delta(hess, displacement, delta_grad)
292
+ Bofill_const = self._calculate_bofill_const(A, displacement)
293
+
294
+ # Bofill (SR1/PSB) mixes with the constant directly (phi^2)
295
+ delta_hess = (1.0 - Bofill_const) * delta_hess_PSB + Bofill_const * delta_hess_SR1
296
+ return delta_hess
297
+
298
+ def CFD_Bofill_hessian_update(self, hess, displacement, delta_grad):
299
+ print("CFD Bofill Hessian Update")
300
+ A = 2.0 * (delta_grad - np.dot(hess, displacement))
301
+
302
+ delta_hess_SR1 = self._calculate_sr1_delta(A, displacement)
303
+ delta_hess_PSB = self._calculate_psb_delta(hess, displacement, delta_grad)
304
+ Bofill_const = self._calculate_bofill_const(A, displacement)
305
+
306
+ delta_hess = (1.0 - Bofill_const) * delta_hess_PSB + Bofill_const * delta_hess_SR1
307
+ return delta_hess
308
+
309
+ def pCFD_Bofill_hessian_update(self, hess, displacement, delta_grad):
310
+ print("Perturbed CFD Bofill Hessian Update")
311
+
312
+ # 1. CFD Bofill part
313
+ A = 2.0 * (delta_grad - np.dot(hess, displacement))
314
+
315
+ delta_hess_SR1 = self._calculate_sr1_delta(A, displacement)
316
+ delta_hess_PSB = self._calculate_psb_delta(hess, displacement, delta_grad)
317
+ Bofill_const = self._calculate_bofill_const(A, displacement)
318
+
319
+ delta_hess = (1.0 - Bofill_const) * delta_hess_PSB + Bofill_const * delta_hess_SR1
320
+
321
+ # 2. Perturbation Term
322
+ print("Calculating perturbation term...")
323
+ tmp_perturb_term_matrix = np.zeros_like(delta_hess)
324
+
325
+ # Ensure displacement is 2D for null_space
326
+ if displacement.ndim == 1:
327
+ displacement_2d = displacement[np.newaxis, :]
328
+ else:
329
+ displacement_2d = displacement.T
330
+
331
+ ortho_vecs = null_space(displacement_2d) # (N, N-1)
332
+
333
+ # Iterate over column vectors (which are (N,) 1D arrays)
334
+ for ortho_vec_i in ortho_vecs.T:
335
+ for ortho_vec_j in ortho_vecs.T:
336
+ # scalar = j.T @ (delta_B @ i)
337
+ scalar_term = np.dot(ortho_vec_j.T, np.dot(delta_hess, ortho_vec_i))
338
+ # matrix = (i @ j.T) + (j @ i.T)
339
+ matrix_term = np.outer(ortho_vec_i, ortho_vec_j) + np.outer(ortho_vec_j, ortho_vec_i)
340
+ tmp_perturb_term_matrix += scalar_term * matrix_term
341
+
342
+ delta_hess = delta_hess + tmp_perturb_term_matrix
343
+ return delta_hess
344
+
345
+ def MSP_hessian_update(self, hess, displacement, delta_grad):
346
+ print("MSP Hessian Update")
347
+ A = delta_grad - np.dot(hess, displacement)
348
+
349
+ delta_hess_MS = self._calculate_sr1_delta(A, displacement) # MS = SR1
350
+ delta_hess_P = self._calculate_psb_delta(hess, displacement, delta_grad) # P = PSB
351
+
352
+ A_norm = np.linalg.norm(A)
353
+ displacement_norm = np.linalg.norm(displacement)
354
+ phi_denominator = A_norm * displacement_norm
355
+
356
+ phi_cos_arg = 0.0
357
+ if phi_denominator >= self.denom_threshold:
358
+ phi_cos_arg = np.dot(displacement.T, A) / phi_denominator
359
+ # Clip argument for arccos to valid range [-1, 1]
360
+ phi_cos_arg = np.clip(phi_cos_arg, -1.0, 1.0)
361
+ else:
362
+ print("phi denominator is too small, set phi=0.")
363
+
364
+ # phi = sin(arccos(arg))^2 = 1 - cos(arccos(arg))^2 = 1 - arg^2
365
+ phi = 1.0 - phi_cos_arg**2
366
+
367
+ delta_hess = phi * delta_hess_P + (1.0 - phi) * delta_hess_MS
368
+ return delta_hess
369
+
370
+ # -----------------------------------------------------------------
371
+ # DD-Enabled Hessian Update Methods
372
+ # -----------------------------------------------------------------
373
+
374
+ def BFGS_hessian_update_dd(self, hess, displacement, delta_grad):
375
+ """
376
+ BFGS Hessian (B) update with Double Damping (DD).
377
+ """
378
+ print("--- BFGS Hessian Update with Double Damping ---")
379
+
380
+ # 1. Apply Double Damping to get (s_tilde, y_tilde)
381
+ s_tilde, y_tilde = self.double_damping_step2_only(
382
+ displacement, delta_grad, self.dd_mu2
383
+ )
384
+
385
+ # 2. Call the helper function with the new (s_tilde, y_tilde)
386
+ print("Calling damped BFGS update logic...")
387
+ return self._calculate_bfgs_delta(hess, s_tilde, y_tilde)
388
+
389
+ def FSB_hessian_update_dd(self, hess, displacement, delta_grad):
390
+ """
391
+ FSB Hessian (B) update with Double Damping (DD).
392
+ """
393
+ print("--- FSB Hessian Update with Double Damping ---")
394
+
395
+ # 1. Apply Double Damping
396
+ s_tilde, y_tilde = self.double_damping_step2_only(
397
+ displacement, delta_grad, self.dd_mu2
398
+ )
399
+
400
+ # 2. Call damped logic
401
+ print("Calling damped FSB update logic...")
402
+ A_tilde = y_tilde - np.dot(hess, s_tilde)
403
+
404
+ delta_hess_SR1 = self._calculate_sr1_delta(A_tilde, s_tilde)
405
+ delta_hess_BFGS = self._calculate_bfgs_delta(hess, s_tilde, y_tilde)
406
+ Bofill_const = self._calculate_bofill_const(A_tilde, s_tilde)
407
+
408
+ phi = np.sqrt(Bofill_const)
409
+ delta_hess = (1.0 - phi) * delta_hess_BFGS + phi * delta_hess_SR1
410
+ return delta_hess
411
+
412
+ def CFD_FSB_hessian_update_dd(self, hess, displacement, delta_grad):
413
+ """
414
+ CFD FSB Hessian (B) update with Double Damping (DD).
415
+ """
416
+ print("--- CFD FSB Hessian Update with Double Damping ---")
417
+
418
+ # 1. Apply Double Damping
419
+ s_tilde, y_tilde = self.double_damping_step2_only(
420
+ displacement, delta_grad, self.dd_mu2
421
+ )
422
+
423
+ # 2. Call damped logic
424
+ print("Calling damped CFD FSB update logic...")
425
+ A_tilde = 2.0 * (y_tilde - np.dot(hess, s_tilde))
426
+
427
+ delta_hess_SR1 = self._calculate_sr1_delta(A_tilde, s_tilde)
428
+ delta_hess_BFGS = self._calculate_bfgs_delta(hess, s_tilde, y_tilde)
429
+ Bofill_const = self._calculate_bofill_const(A_tilde, s_tilde)
430
+
431
+ phi = np.sqrt(Bofill_const)
432
+ delta_hess = (1.0 - phi) * delta_hess_BFGS + phi * delta_hess_SR1
433
+ return delta_hess