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.
- multioptpy/Calculator/__init__.py +0 -0
- multioptpy/Calculator/ase_calculation_tools.py +424 -0
- multioptpy/Calculator/ase_tools/__init__.py +0 -0
- multioptpy/Calculator/ase_tools/fairchem.py +28 -0
- multioptpy/Calculator/ase_tools/gamess.py +19 -0
- multioptpy/Calculator/ase_tools/gaussian.py +165 -0
- multioptpy/Calculator/ase_tools/mace.py +28 -0
- multioptpy/Calculator/ase_tools/mopac.py +19 -0
- multioptpy/Calculator/ase_tools/nwchem.py +31 -0
- multioptpy/Calculator/ase_tools/orca.py +22 -0
- multioptpy/Calculator/ase_tools/pygfn0.py +37 -0
- multioptpy/Calculator/dxtb_calculation_tools.py +344 -0
- multioptpy/Calculator/emt_calculation_tools.py +458 -0
- multioptpy/Calculator/gpaw_calculation_tools.py +183 -0
- multioptpy/Calculator/lj_calculation_tools.py +314 -0
- multioptpy/Calculator/psi4_calculation_tools.py +334 -0
- multioptpy/Calculator/pwscf_calculation_tools.py +189 -0
- multioptpy/Calculator/pyscf_calculation_tools.py +327 -0
- multioptpy/Calculator/sqm1_calculation_tools.py +611 -0
- multioptpy/Calculator/sqm2_calculation_tools.py +376 -0
- multioptpy/Calculator/tblite_calculation_tools.py +352 -0
- multioptpy/Calculator/tersoff_calculation_tools.py +818 -0
- multioptpy/Constraint/__init__.py +0 -0
- multioptpy/Constraint/constraint_condition.py +834 -0
- multioptpy/Coordinate/__init__.py +0 -0
- multioptpy/Coordinate/polar_coordinate.py +199 -0
- multioptpy/Coordinate/redundant_coordinate.py +638 -0
- multioptpy/IRC/__init__.py +0 -0
- multioptpy/IRC/converge_criteria.py +28 -0
- multioptpy/IRC/dvv.py +544 -0
- multioptpy/IRC/euler.py +439 -0
- multioptpy/IRC/hpc.py +564 -0
- multioptpy/IRC/lqa.py +540 -0
- multioptpy/IRC/modekill.py +662 -0
- multioptpy/IRC/rk4.py +579 -0
- multioptpy/Interpolation/__init__.py +0 -0
- multioptpy/Interpolation/adaptive_interpolation.py +283 -0
- multioptpy/Interpolation/binomial_interpolation.py +179 -0
- multioptpy/Interpolation/geodesic_interpolation.py +785 -0
- multioptpy/Interpolation/interpolation.py +156 -0
- multioptpy/Interpolation/linear_interpolation.py +473 -0
- multioptpy/Interpolation/savitzky_golay_interpolation.py +252 -0
- multioptpy/Interpolation/spline_interpolation.py +353 -0
- multioptpy/MD/__init__.py +0 -0
- multioptpy/MD/thermostat.py +185 -0
- multioptpy/MEP/__init__.py +0 -0
- multioptpy/MEP/pathopt_bneb_force.py +443 -0
- multioptpy/MEP/pathopt_dmf_force.py +448 -0
- multioptpy/MEP/pathopt_dneb_force.py +130 -0
- multioptpy/MEP/pathopt_ewbneb_force.py +207 -0
- multioptpy/MEP/pathopt_gpneb_force.py +512 -0
- multioptpy/MEP/pathopt_lup_force.py +113 -0
- multioptpy/MEP/pathopt_neb_force.py +225 -0
- multioptpy/MEP/pathopt_nesb_force.py +205 -0
- multioptpy/MEP/pathopt_om_force.py +153 -0
- multioptpy/MEP/pathopt_qsm_force.py +174 -0
- multioptpy/MEP/pathopt_qsmv2_force.py +304 -0
- multioptpy/ModelFunction/__init__.py +7 -0
- multioptpy/ModelFunction/avoiding_model_function.py +29 -0
- multioptpy/ModelFunction/binary_image_ts_search_model_function.py +47 -0
- multioptpy/ModelFunction/conical_model_function.py +26 -0
- multioptpy/ModelFunction/opt_meci.py +50 -0
- multioptpy/ModelFunction/opt_mesx.py +47 -0
- multioptpy/ModelFunction/opt_mesx_2.py +49 -0
- multioptpy/ModelFunction/seam_model_function.py +27 -0
- multioptpy/ModelHessian/__init__.py +0 -0
- multioptpy/ModelHessian/approx_hessian.py +147 -0
- multioptpy/ModelHessian/calc_params.py +227 -0
- multioptpy/ModelHessian/fischer.py +236 -0
- multioptpy/ModelHessian/fischerd3.py +360 -0
- multioptpy/ModelHessian/fischerd4.py +398 -0
- multioptpy/ModelHessian/gfn0xtb.py +633 -0
- multioptpy/ModelHessian/gfnff.py +709 -0
- multioptpy/ModelHessian/lindh.py +165 -0
- multioptpy/ModelHessian/lindh2007d2.py +707 -0
- multioptpy/ModelHessian/lindh2007d3.py +822 -0
- multioptpy/ModelHessian/lindh2007d4.py +1030 -0
- multioptpy/ModelHessian/morse.py +106 -0
- multioptpy/ModelHessian/schlegel.py +144 -0
- multioptpy/ModelHessian/schlegeld3.py +322 -0
- multioptpy/ModelHessian/schlegeld4.py +559 -0
- multioptpy/ModelHessian/shortrange.py +346 -0
- multioptpy/ModelHessian/swartd2.py +496 -0
- multioptpy/ModelHessian/swartd3.py +706 -0
- multioptpy/ModelHessian/swartd4.py +918 -0
- multioptpy/ModelHessian/tshess.py +40 -0
- multioptpy/Optimizer/QHAdam.py +61 -0
- multioptpy/Optimizer/__init__.py +0 -0
- multioptpy/Optimizer/abc_fire.py +83 -0
- multioptpy/Optimizer/adabelief.py +58 -0
- multioptpy/Optimizer/adabound.py +68 -0
- multioptpy/Optimizer/adadelta.py +65 -0
- multioptpy/Optimizer/adaderivative.py +56 -0
- multioptpy/Optimizer/adadiff.py +68 -0
- multioptpy/Optimizer/adafactor.py +70 -0
- multioptpy/Optimizer/adam.py +65 -0
- multioptpy/Optimizer/adamax.py +62 -0
- multioptpy/Optimizer/adamod.py +83 -0
- multioptpy/Optimizer/adamw.py +65 -0
- multioptpy/Optimizer/adiis.py +523 -0
- multioptpy/Optimizer/afire_neb.py +282 -0
- multioptpy/Optimizer/block_hessian_update.py +709 -0
- multioptpy/Optimizer/c2diis.py +491 -0
- multioptpy/Optimizer/component_wise_scaling.py +405 -0
- multioptpy/Optimizer/conjugate_gradient.py +82 -0
- multioptpy/Optimizer/conjugate_gradient_neb.py +345 -0
- multioptpy/Optimizer/coordinate_locking.py +405 -0
- multioptpy/Optimizer/dic_rsirfo.py +1015 -0
- multioptpy/Optimizer/ediis.py +417 -0
- multioptpy/Optimizer/eve.py +76 -0
- multioptpy/Optimizer/fastadabelief.py +61 -0
- multioptpy/Optimizer/fire.py +77 -0
- multioptpy/Optimizer/fire2.py +249 -0
- multioptpy/Optimizer/fire_neb.py +92 -0
- multioptpy/Optimizer/gan_step.py +486 -0
- multioptpy/Optimizer/gdiis.py +609 -0
- multioptpy/Optimizer/gediis.py +203 -0
- multioptpy/Optimizer/geodesic_step.py +433 -0
- multioptpy/Optimizer/gpmin.py +633 -0
- multioptpy/Optimizer/gpr_step.py +364 -0
- multioptpy/Optimizer/gradientdescent.py +78 -0
- multioptpy/Optimizer/gradientdescent_neb.py +52 -0
- multioptpy/Optimizer/hessian_update.py +433 -0
- multioptpy/Optimizer/hybrid_rfo.py +998 -0
- multioptpy/Optimizer/kdiis.py +625 -0
- multioptpy/Optimizer/lars.py +21 -0
- multioptpy/Optimizer/lbfgs.py +253 -0
- multioptpy/Optimizer/lbfgs_neb.py +355 -0
- multioptpy/Optimizer/linesearch.py +236 -0
- multioptpy/Optimizer/lookahead.py +40 -0
- multioptpy/Optimizer/nadam.py +64 -0
- multioptpy/Optimizer/newton.py +200 -0
- multioptpy/Optimizer/prodigy.py +70 -0
- multioptpy/Optimizer/purtubation.py +16 -0
- multioptpy/Optimizer/quickmin_neb.py +245 -0
- multioptpy/Optimizer/radam.py +75 -0
- multioptpy/Optimizer/rfo_neb.py +302 -0
- multioptpy/Optimizer/ric_rfo.py +842 -0
- multioptpy/Optimizer/rl_step.py +627 -0
- multioptpy/Optimizer/rmspropgrave.py +65 -0
- multioptpy/Optimizer/rsirfo.py +1647 -0
- multioptpy/Optimizer/rsprfo.py +1056 -0
- multioptpy/Optimizer/sadam.py +60 -0
- multioptpy/Optimizer/samsgrad.py +63 -0
- multioptpy/Optimizer/tr_lbfgs.py +678 -0
- multioptpy/Optimizer/trim.py +273 -0
- multioptpy/Optimizer/trust_radius.py +207 -0
- multioptpy/Optimizer/trust_radius_neb.py +121 -0
- multioptpy/Optimizer/yogi.py +60 -0
- multioptpy/OtherMethod/__init__.py +0 -0
- multioptpy/OtherMethod/addf.py +1150 -0
- multioptpy/OtherMethod/dimer.py +895 -0
- multioptpy/OtherMethod/elastic_image_pair.py +629 -0
- multioptpy/OtherMethod/modelfunction.py +456 -0
- multioptpy/OtherMethod/newton_traj.py +454 -0
- multioptpy/OtherMethod/twopshs.py +1095 -0
- multioptpy/PESAnalyzer/__init__.py +0 -0
- multioptpy/PESAnalyzer/calc_irc_curvature.py +125 -0
- multioptpy/PESAnalyzer/cmds_analysis.py +152 -0
- multioptpy/PESAnalyzer/koopman_analysis.py +268 -0
- multioptpy/PESAnalyzer/pca_analysis.py +314 -0
- multioptpy/Parameters/__init__.py +0 -0
- multioptpy/Parameters/atomic_mass.py +20 -0
- multioptpy/Parameters/atomic_number.py +22 -0
- multioptpy/Parameters/covalent_radii.py +44 -0
- multioptpy/Parameters/d2.py +61 -0
- multioptpy/Parameters/d3.py +63 -0
- multioptpy/Parameters/d4.py +103 -0
- multioptpy/Parameters/dreiding.py +34 -0
- multioptpy/Parameters/gfn0xtb_param.py +137 -0
- multioptpy/Parameters/gfnff_param.py +315 -0
- multioptpy/Parameters/gnb.py +104 -0
- multioptpy/Parameters/parameter.py +22 -0
- multioptpy/Parameters/uff.py +72 -0
- multioptpy/Parameters/unit_values.py +20 -0
- multioptpy/Potential/AFIR_potential.py +55 -0
- multioptpy/Potential/LJ_repulsive_potential.py +345 -0
- multioptpy/Potential/__init__.py +0 -0
- multioptpy/Potential/anharmonic_keep_potential.py +28 -0
- multioptpy/Potential/asym_elllipsoidal_potential.py +718 -0
- multioptpy/Potential/electrostatic_potential.py +69 -0
- multioptpy/Potential/flux_potential.py +30 -0
- multioptpy/Potential/gaussian_potential.py +101 -0
- multioptpy/Potential/idpp.py +516 -0
- multioptpy/Potential/keep_angle_potential.py +146 -0
- multioptpy/Potential/keep_dihedral_angle_potential.py +105 -0
- multioptpy/Potential/keep_outofplain_angle_potential.py +70 -0
- multioptpy/Potential/keep_potential.py +99 -0
- multioptpy/Potential/mechano_force_potential.py +74 -0
- multioptpy/Potential/nanoreactor_potential.py +52 -0
- multioptpy/Potential/potential.py +896 -0
- multioptpy/Potential/spacer_model_potential.py +221 -0
- multioptpy/Potential/switching_potential.py +258 -0
- multioptpy/Potential/universal_potential.py +34 -0
- multioptpy/Potential/value_range_potential.py +36 -0
- multioptpy/Potential/void_point_potential.py +25 -0
- multioptpy/SQM/__init__.py +0 -0
- multioptpy/SQM/sqm1/__init__.py +0 -0
- multioptpy/SQM/sqm1/sqm1_core.py +1792 -0
- multioptpy/SQM/sqm2/__init__.py +0 -0
- multioptpy/SQM/sqm2/calc_tools.py +95 -0
- multioptpy/SQM/sqm2/sqm2_basis.py +850 -0
- multioptpy/SQM/sqm2/sqm2_bond.py +119 -0
- multioptpy/SQM/sqm2/sqm2_core.py +303 -0
- multioptpy/SQM/sqm2/sqm2_data.py +1229 -0
- multioptpy/SQM/sqm2/sqm2_disp.py +65 -0
- multioptpy/SQM/sqm2/sqm2_eeq.py +243 -0
- multioptpy/SQM/sqm2/sqm2_overlapint.py +704 -0
- multioptpy/SQM/sqm2/sqm2_qm.py +578 -0
- multioptpy/SQM/sqm2/sqm2_rep.py +66 -0
- multioptpy/SQM/sqm2/sqm2_srb.py +70 -0
- multioptpy/Thermo/__init__.py +0 -0
- multioptpy/Thermo/normal_mode_analyzer.py +865 -0
- multioptpy/Utils/__init__.py +0 -0
- multioptpy/Utils/bond_connectivity.py +264 -0
- multioptpy/Utils/calc_tools.py +884 -0
- multioptpy/Utils/oniom.py +96 -0
- multioptpy/Utils/pbc.py +48 -0
- multioptpy/Utils/riemann_curvature.py +208 -0
- multioptpy/Utils/symmetry_analyzer.py +482 -0
- multioptpy/Visualization/__init__.py +0 -0
- multioptpy/Visualization/visualization.py +156 -0
- multioptpy/WFAnalyzer/MO_analysis.py +104 -0
- multioptpy/WFAnalyzer/__init__.py +0 -0
- multioptpy/Wrapper/__init__.py +0 -0
- multioptpy/Wrapper/autots.py +1239 -0
- multioptpy/Wrapper/ieip_wrapper.py +93 -0
- multioptpy/Wrapper/md_wrapper.py +92 -0
- multioptpy/Wrapper/neb_wrapper.py +94 -0
- multioptpy/Wrapper/optimize_wrapper.py +76 -0
- multioptpy/__init__.py +5 -0
- multioptpy/entrypoints.py +916 -0
- multioptpy/fileio.py +660 -0
- multioptpy/ieip.py +340 -0
- multioptpy/interface.py +1086 -0
- multioptpy/irc.py +529 -0
- multioptpy/moleculardynamics.py +432 -0
- multioptpy/neb.py +1267 -0
- multioptpy/optimization.py +1553 -0
- multioptpy/optimizer.py +709 -0
- multioptpy-1.20.2.dist-info/METADATA +438 -0
- multioptpy-1.20.2.dist-info/RECORD +246 -0
- multioptpy-1.20.2.dist-info/WHEEL +5 -0
- multioptpy-1.20.2.dist-info/entry_points.txt +9 -0
- multioptpy-1.20.2.dist-info/licenses/LICENSE +674 -0
- 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
|