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