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