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,405 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ComponentWiseScaling:
|
|
5
|
+
"""
|
|
6
|
+
Component-wise scaling optimization method.
|
|
7
|
+
|
|
8
|
+
This method applies individual scaling factors to each coordinate
|
|
9
|
+
of the optimization step based on their characteristics and behavior.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
# Basic parameters
|
|
14
|
+
self.scaling_strategy = 'adaptive' # 'adaptive', 'gradient_based', 'history_based', 'curvature'
|
|
15
|
+
self.scaling_mode = 'individual' # 'individual', 'grouped', 'mixed'
|
|
16
|
+
|
|
17
|
+
# Initial scaling parameters
|
|
18
|
+
self.initial_scale_factor = 1.0 # Starting scale factor for all coordinates
|
|
19
|
+
self.min_scale_factor = 0.01 # Minimum allowed scaling factor
|
|
20
|
+
self.max_scale_factor = 5.0 # Maximum allowed scaling factor
|
|
21
|
+
|
|
22
|
+
# Adaptive parameters
|
|
23
|
+
self.adaptation_rate = 0.2 # How quickly scaling factors adapt (0-1)
|
|
24
|
+
self.gradient_scaling_power = 0.5 # Power for gradient-based scaling
|
|
25
|
+
self.history_memory = 5 # Number of previous steps to consider
|
|
26
|
+
|
|
27
|
+
# Grouping parameters
|
|
28
|
+
self.group_coordinates = False # Whether to group similar coordinates
|
|
29
|
+
self.group_similarity_threshold = 0.8 # Threshold for grouping coordinates
|
|
30
|
+
self.coordinate_groups = [] # List of coordinate groups
|
|
31
|
+
|
|
32
|
+
# Technical parameters
|
|
33
|
+
self.normalize_gradients = True # Whether to use normalized gradients
|
|
34
|
+
self.use_global_damping = True # Whether to apply global damping factor
|
|
35
|
+
self.global_damping = 0.9 # Global damping factor
|
|
36
|
+
self.outlier_threshold = 3.0 # Z-score threshold for outlier detection
|
|
37
|
+
|
|
38
|
+
# State variables
|
|
39
|
+
self.scaling_factors = None # Current scaling factors
|
|
40
|
+
self.gradient_history = [] # History of gradients
|
|
41
|
+
self.step_history = [] # History of optimization steps
|
|
42
|
+
self.factor_history = [] # History of scaling factors
|
|
43
|
+
self.coordinate_activity = None # Measure of each coordinate's activity
|
|
44
|
+
|
|
45
|
+
# Iteration tracking
|
|
46
|
+
self.iter = 0
|
|
47
|
+
|
|
48
|
+
def _initialize_scaling_factors(self, n_coords):
|
|
49
|
+
"""
|
|
50
|
+
Initialize the scaling factors based on the current strategy.
|
|
51
|
+
|
|
52
|
+
Parameters:
|
|
53
|
+
-----------
|
|
54
|
+
n_coords : int
|
|
55
|
+
Number of coordinates
|
|
56
|
+
"""
|
|
57
|
+
if self.scaling_factors is not None and len(self.scaling_factors) == n_coords:
|
|
58
|
+
return # Already initialized
|
|
59
|
+
|
|
60
|
+
# Create initial scaling factors
|
|
61
|
+
self.scaling_factors = np.ones(n_coords) * self.initial_scale_factor
|
|
62
|
+
self.coordinate_activity = np.ones(n_coords)
|
|
63
|
+
|
|
64
|
+
print(f"Scaling factors initialized with value {self.initial_scale_factor}")
|
|
65
|
+
|
|
66
|
+
def _update_coordinate_activity(self, gradient):
|
|
67
|
+
"""
|
|
68
|
+
Update the measure of each coordinate's activity based on gradients.
|
|
69
|
+
|
|
70
|
+
Parameters:
|
|
71
|
+
-----------
|
|
72
|
+
gradient : numpy.ndarray
|
|
73
|
+
Current gradient
|
|
74
|
+
"""
|
|
75
|
+
grad_flat = gradient.flatten()
|
|
76
|
+
grad_abs = np.abs(grad_flat)
|
|
77
|
+
|
|
78
|
+
# First iteration or reset
|
|
79
|
+
if self.coordinate_activity is None or len(self.coordinate_activity) != len(grad_flat):
|
|
80
|
+
self.coordinate_activity = grad_abs.copy()
|
|
81
|
+
return
|
|
82
|
+
|
|
83
|
+
# Update activity with exponential smoothing
|
|
84
|
+
memory_factor = 0.8
|
|
85
|
+
self.coordinate_activity = (memory_factor * self.coordinate_activity +
|
|
86
|
+
(1 - memory_factor) * grad_abs)
|
|
87
|
+
|
|
88
|
+
def _apply_gradient_based_scaling(self, gradient):
|
|
89
|
+
"""
|
|
90
|
+
Update scaling factors based on gradient magnitudes.
|
|
91
|
+
|
|
92
|
+
Parameters:
|
|
93
|
+
-----------
|
|
94
|
+
gradient : numpy.ndarray
|
|
95
|
+
Current gradient
|
|
96
|
+
"""
|
|
97
|
+
grad_flat = gradient.flatten()
|
|
98
|
+
grad_abs = np.abs(grad_flat)
|
|
99
|
+
|
|
100
|
+
# Avoid division by zero
|
|
101
|
+
grad_abs = np.maximum(grad_abs, 1e-10)
|
|
102
|
+
|
|
103
|
+
if self.normalize_gradients:
|
|
104
|
+
# Normalized gradient-based scaling
|
|
105
|
+
mean_grad = np.mean(grad_abs)
|
|
106
|
+
if mean_grad > 1e-10:
|
|
107
|
+
# Calculate normalized gradient magnitudes
|
|
108
|
+
normalized_grad = grad_abs / mean_grad
|
|
109
|
+
|
|
110
|
+
# Apply power scaling to get factors
|
|
111
|
+
scale_factors = normalized_grad ** (-self.gradient_scaling_power)
|
|
112
|
+
|
|
113
|
+
# Limit to reasonable range
|
|
114
|
+
scale_factors = np.clip(scale_factors, self.min_scale_factor, self.max_scale_factor)
|
|
115
|
+
|
|
116
|
+
# Update with smoothing
|
|
117
|
+
self.scaling_factors = ((1 - self.adaptation_rate) * self.scaling_factors +
|
|
118
|
+
self.adaptation_rate * scale_factors)
|
|
119
|
+
else:
|
|
120
|
+
# Direct gradient-based scaling (inverse relationship)
|
|
121
|
+
# Coordinates with larger gradients get smaller steps
|
|
122
|
+
scale_factors = 1.0 / (grad_abs ** self.gradient_scaling_power)
|
|
123
|
+
|
|
124
|
+
# Normalize to keep average scaling around 1.0
|
|
125
|
+
scale_factors = scale_factors / np.mean(scale_factors)
|
|
126
|
+
|
|
127
|
+
# Limit to reasonable range
|
|
128
|
+
scale_factors = np.clip(scale_factors, self.min_scale_factor, self.max_scale_factor)
|
|
129
|
+
|
|
130
|
+
# Update with smoothing
|
|
131
|
+
self.scaling_factors = ((1 - self.adaptation_rate) * self.scaling_factors +
|
|
132
|
+
self.adaptation_rate * scale_factors)
|
|
133
|
+
|
|
134
|
+
def _apply_history_based_scaling(self):
|
|
135
|
+
"""
|
|
136
|
+
Update scaling factors based on the history of gradients and steps.
|
|
137
|
+
"""
|
|
138
|
+
if len(self.gradient_history) < 2 or len(self.step_history) < 2:
|
|
139
|
+
return # Not enough history
|
|
140
|
+
|
|
141
|
+
# Calculate gradient changes and step sizes from recent history
|
|
142
|
+
recent_grads = self.gradient_history[-min(self.history_memory, len(self.gradient_history)):]
|
|
143
|
+
recent_steps = self.step_history[-min(self.history_memory, len(self.step_history)):]
|
|
144
|
+
|
|
145
|
+
# Calculate coordinate-wise statistics
|
|
146
|
+
grad_changes = np.zeros(len(self.scaling_factors))
|
|
147
|
+
step_sizes = np.zeros(len(self.scaling_factors))
|
|
148
|
+
|
|
149
|
+
for i in range(1, len(recent_grads)):
|
|
150
|
+
grad_diff = recent_grads[i].flatten() - recent_grads[i-1].flatten()
|
|
151
|
+
grad_changes += np.abs(grad_diff)
|
|
152
|
+
step_sizes += np.abs(recent_steps[i-1].flatten())
|
|
153
|
+
|
|
154
|
+
# Avoid division by zero
|
|
155
|
+
step_sizes = np.maximum(step_sizes, 1e-10)
|
|
156
|
+
|
|
157
|
+
# Calculate scaling adjustments based on gradient changes per step size
|
|
158
|
+
# Coordinates where gradients change rapidly with small steps get smaller scaling
|
|
159
|
+
effectiveness = grad_changes / step_sizes
|
|
160
|
+
effectiveness = np.maximum(effectiveness, 1e-10)
|
|
161
|
+
|
|
162
|
+
# Normalize effectiveness
|
|
163
|
+
mean_effectiveness = np.mean(effectiveness)
|
|
164
|
+
if mean_effectiveness > 1e-10:
|
|
165
|
+
relative_effectiveness = effectiveness / mean_effectiveness
|
|
166
|
+
|
|
167
|
+
# Calculate scale adjustments (inverse relationship)
|
|
168
|
+
scale_adjustments = relative_effectiveness ** (-0.5)
|
|
169
|
+
|
|
170
|
+
# Limit adjustments to reasonable range
|
|
171
|
+
scale_adjustments = np.clip(scale_adjustments, 0.5, 2.0)
|
|
172
|
+
|
|
173
|
+
# Apply adjustments with smoothing
|
|
174
|
+
self.scaling_factors *= (1.0 + self.adaptation_rate * (scale_adjustments - 1.0))
|
|
175
|
+
|
|
176
|
+
# Final bounds check
|
|
177
|
+
self.scaling_factors = np.clip(self.scaling_factors,
|
|
178
|
+
self.min_scale_factor,
|
|
179
|
+
self.max_scale_factor)
|
|
180
|
+
|
|
181
|
+
def _apply_curvature_based_scaling(self):
|
|
182
|
+
"""
|
|
183
|
+
Update scaling factors based on estimated local curvature.
|
|
184
|
+
"""
|
|
185
|
+
if len(self.gradient_history) < 3:
|
|
186
|
+
return # Not enough history for curvature estimation
|
|
187
|
+
|
|
188
|
+
# Get recent gradients
|
|
189
|
+
g_current = self.gradient_history[-1].flatten()
|
|
190
|
+
g_prev = self.gradient_history[-2].flatten()
|
|
191
|
+
g_prev2 = self.gradient_history[-3].flatten()
|
|
192
|
+
|
|
193
|
+
# Finite difference approximation of curvature
|
|
194
|
+
# Using central difference: f''(x) ≈ (f'(x+h) - 2f'(x) + f'(x-h)) / h²
|
|
195
|
+
# Here we use gradient values as f'(x) at different points
|
|
196
|
+
curvature_approx = np.abs(g_current - 2*g_prev + g_prev2)
|
|
197
|
+
|
|
198
|
+
# Avoid division by zero
|
|
199
|
+
curvature_approx = np.maximum(curvature_approx, 1e-10)
|
|
200
|
+
|
|
201
|
+
# Scale inversely with curvature - higher curvature needs smaller steps
|
|
202
|
+
curvature_scaling = 1.0 / np.sqrt(curvature_approx)
|
|
203
|
+
|
|
204
|
+
# Normalize to keep average scaling around 1.0
|
|
205
|
+
mean_scaling = np.mean(curvature_scaling)
|
|
206
|
+
if mean_scaling > 1e-10:
|
|
207
|
+
curvature_scaling /= mean_scaling
|
|
208
|
+
|
|
209
|
+
# Clip to reasonable range
|
|
210
|
+
curvature_scaling = np.clip(curvature_scaling, 0.2, 5.0)
|
|
211
|
+
|
|
212
|
+
# Apply with smoothing
|
|
213
|
+
self.scaling_factors = ((1 - self.adaptation_rate) * self.scaling_factors +
|
|
214
|
+
self.adaptation_rate * curvature_scaling)
|
|
215
|
+
|
|
216
|
+
# Final bounds check
|
|
217
|
+
self.scaling_factors = np.clip(self.scaling_factors,
|
|
218
|
+
self.min_scale_factor,
|
|
219
|
+
self.max_scale_factor)
|
|
220
|
+
|
|
221
|
+
def _detect_and_handle_outliers(self):
|
|
222
|
+
"""
|
|
223
|
+
Detect and handle outliers in scaling factors.
|
|
224
|
+
"""
|
|
225
|
+
# Calculate z-scores
|
|
226
|
+
mean_scale = np.mean(self.scaling_factors)
|
|
227
|
+
std_scale = np.std(self.scaling_factors)
|
|
228
|
+
|
|
229
|
+
if std_scale < 1e-10:
|
|
230
|
+
return # No significant variation
|
|
231
|
+
|
|
232
|
+
z_scores = np.abs((self.scaling_factors - mean_scale) / std_scale)
|
|
233
|
+
|
|
234
|
+
# Identify outliers
|
|
235
|
+
outliers = z_scores > self.outlier_threshold
|
|
236
|
+
outlier_count = np.sum(outliers)
|
|
237
|
+
|
|
238
|
+
if outlier_count > 0:
|
|
239
|
+
print(f"Detected {outlier_count} outlier scaling factors")
|
|
240
|
+
|
|
241
|
+
# Handle outliers by bringing them closer to the mean
|
|
242
|
+
adjustment = 0.5 * (self.scaling_factors[outliers] - mean_scale)
|
|
243
|
+
self.scaling_factors[outliers] -= adjustment
|
|
244
|
+
|
|
245
|
+
def _apply_group_scaling(self):
|
|
246
|
+
"""
|
|
247
|
+
Apply consistent scaling to groups of coordinates.
|
|
248
|
+
"""
|
|
249
|
+
if not self.group_coordinates or not self.coordinate_groups:
|
|
250
|
+
# Try to detect groups if enabled but not defined
|
|
251
|
+
if self.group_coordinates and not self.coordinate_groups and len(self.gradient_history) >= 3:
|
|
252
|
+
self._detect_coordinate_groups()
|
|
253
|
+
else:
|
|
254
|
+
return # No grouping or groups not yet detected
|
|
255
|
+
|
|
256
|
+
# Apply consistent scaling within each group
|
|
257
|
+
for group in self.coordinate_groups:
|
|
258
|
+
group_scales = self.scaling_factors[group]
|
|
259
|
+
median_scale = np.median(group_scales) # Use median for robustness
|
|
260
|
+
|
|
261
|
+
# Move all scales in the group towards the median
|
|
262
|
+
adjustment_rate = 0.7 # How strongly to enforce group consistency
|
|
263
|
+
self.scaling_factors[group] = (adjustment_rate * median_scale +
|
|
264
|
+
(1 - adjustment_rate) * group_scales)
|
|
265
|
+
|
|
266
|
+
def _detect_coordinate_groups(self):
|
|
267
|
+
"""
|
|
268
|
+
Attempt to automatically detect coordinate groups based on gradient correlation.
|
|
269
|
+
"""
|
|
270
|
+
# Need enough history to detect correlations
|
|
271
|
+
if len(self.gradient_history) < 3:
|
|
272
|
+
return
|
|
273
|
+
|
|
274
|
+
n_coords = len(self.scaling_factors)
|
|
275
|
+
|
|
276
|
+
# Calculate correlation matrix between coordinates using gradient history
|
|
277
|
+
grad_history_array = np.vstack([g.flatten() for g in self.gradient_history[-5:]])
|
|
278
|
+
|
|
279
|
+
# Calculate correlation coefficient matrix
|
|
280
|
+
correlation = np.corrcoef(grad_history_array.T)
|
|
281
|
+
|
|
282
|
+
# Replace NaN with 0
|
|
283
|
+
correlation = np.nan_to_num(correlation)
|
|
284
|
+
|
|
285
|
+
# Find highly correlated coordinates
|
|
286
|
+
groups = []
|
|
287
|
+
visited = set()
|
|
288
|
+
|
|
289
|
+
for i in range(n_coords):
|
|
290
|
+
if i in visited:
|
|
291
|
+
continue
|
|
292
|
+
|
|
293
|
+
# Find coordinates highly correlated with i
|
|
294
|
+
group = [i]
|
|
295
|
+
for j in range(n_coords):
|
|
296
|
+
if j != i and j not in visited and abs(correlation[i, j]) > self.group_similarity_threshold:
|
|
297
|
+
group.append(j)
|
|
298
|
+
|
|
299
|
+
if len(group) > 1:
|
|
300
|
+
groups.append(group)
|
|
301
|
+
visited.update(group)
|
|
302
|
+
|
|
303
|
+
if groups:
|
|
304
|
+
self.coordinate_groups = groups
|
|
305
|
+
print(f"Detected {len(groups)} coordinate groups for consistent scaling")
|
|
306
|
+
|
|
307
|
+
def _apply_scaling_to_step(self, move_vector):
|
|
308
|
+
"""
|
|
309
|
+
Apply the current scaling factors to a move vector.
|
|
310
|
+
|
|
311
|
+
Parameters:
|
|
312
|
+
-----------
|
|
313
|
+
move_vector : numpy.ndarray
|
|
314
|
+
Original move vector
|
|
315
|
+
|
|
316
|
+
Returns:
|
|
317
|
+
--------
|
|
318
|
+
numpy.ndarray
|
|
319
|
+
Scaled move vector
|
|
320
|
+
"""
|
|
321
|
+
# Create a scaling factors array in the same shape as move_vector
|
|
322
|
+
scaling_expanded = self.scaling_factors.reshape(-1, 1)
|
|
323
|
+
|
|
324
|
+
# Apply scaling factors
|
|
325
|
+
scaled_vector = move_vector * scaling_expanded
|
|
326
|
+
|
|
327
|
+
# Apply global damping if enabled
|
|
328
|
+
if self.use_global_damping:
|
|
329
|
+
scaled_vector *= self.global_damping
|
|
330
|
+
|
|
331
|
+
return scaled_vector
|
|
332
|
+
|
|
333
|
+
def run(self, geom_num_list, energy, gradient, original_move_vector):
|
|
334
|
+
"""
|
|
335
|
+
Run Component-wise Scaling optimization step.
|
|
336
|
+
|
|
337
|
+
Parameters:
|
|
338
|
+
-----------
|
|
339
|
+
geom_num_list : numpy.ndarray
|
|
340
|
+
Current geometry
|
|
341
|
+
energy : float
|
|
342
|
+
Current energy value
|
|
343
|
+
gradient : numpy.ndarray
|
|
344
|
+
Current gradient
|
|
345
|
+
original_move_vector : numpy.ndarray
|
|
346
|
+
Original optimization step
|
|
347
|
+
|
|
348
|
+
Returns:
|
|
349
|
+
--------
|
|
350
|
+
numpy.ndarray
|
|
351
|
+
Scaled optimization step
|
|
352
|
+
"""
|
|
353
|
+
print("Component-wise Scaling method")
|
|
354
|
+
n_coords = len(geom_num_list)
|
|
355
|
+
|
|
356
|
+
# Store gradient history
|
|
357
|
+
self.gradient_history.append(gradient.copy())
|
|
358
|
+
if len(self.gradient_history) > 2 * self.history_memory:
|
|
359
|
+
self.gradient_history.pop(0)
|
|
360
|
+
|
|
361
|
+
# Initialize scaling factors if needed
|
|
362
|
+
if self.scaling_factors is None:
|
|
363
|
+
self._initialize_scaling_factors(n_coords)
|
|
364
|
+
|
|
365
|
+
# Update coordinate activity measure
|
|
366
|
+
self._update_coordinate_activity(gradient)
|
|
367
|
+
|
|
368
|
+
# Apply scaling strategy
|
|
369
|
+
if self.scaling_strategy == 'gradient_based' or self.scaling_strategy == 'adaptive':
|
|
370
|
+
self._apply_gradient_based_scaling(gradient)
|
|
371
|
+
|
|
372
|
+
if self.scaling_strategy == 'history_based' or self.scaling_strategy == 'adaptive':
|
|
373
|
+
self._apply_history_based_scaling()
|
|
374
|
+
|
|
375
|
+
if self.scaling_strategy == 'curvature':
|
|
376
|
+
self._apply_curvature_based_scaling()
|
|
377
|
+
|
|
378
|
+
# Detect and handle outlier scaling factors
|
|
379
|
+
self._detect_and_handle_outliers()
|
|
380
|
+
|
|
381
|
+
# Apply group scaling if enabled
|
|
382
|
+
if self.group_coordinates:
|
|
383
|
+
self._apply_group_scaling()
|
|
384
|
+
|
|
385
|
+
# Apply scaling to the move vector
|
|
386
|
+
scaled_move_vector = self._apply_scaling_to_step(original_move_vector)
|
|
387
|
+
|
|
388
|
+
# Store step history
|
|
389
|
+
self.step_history.append(scaled_move_vector.copy())
|
|
390
|
+
if len(self.step_history) > 2 * self.history_memory:
|
|
391
|
+
self.step_history.pop(0)
|
|
392
|
+
|
|
393
|
+
# Store factor history
|
|
394
|
+
self.factor_history.append(self.scaling_factors.copy())
|
|
395
|
+
if len(self.factor_history) > 2 * self.history_memory:
|
|
396
|
+
self.factor_history.pop(0)
|
|
397
|
+
|
|
398
|
+
# Print scaling statistics
|
|
399
|
+
min_scale = np.min(self.scaling_factors)
|
|
400
|
+
max_scale = np.max(self.scaling_factors)
|
|
401
|
+
mean_scale = np.mean(self.scaling_factors)
|
|
402
|
+
print(f"Scaling factors - min: {min_scale:.4f}, max: {max_scale:.4f}, mean: {mean_scale:.4f}")
|
|
403
|
+
|
|
404
|
+
self.iter += 1
|
|
405
|
+
return scaled_move_vector
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from .linesearch import LineSearch
|
|
2
|
+
import numpy as np
|
|
3
|
+
import copy
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ConjgateGradient:
|
|
7
|
+
def __init__(self, **config):
|
|
8
|
+
#required variables in config(dict): method
|
|
9
|
+
self.config = config
|
|
10
|
+
self.epsilon = 1e-8
|
|
11
|
+
self.DELTA = 1.0
|
|
12
|
+
self.Initialization = True
|
|
13
|
+
self.hessian = None
|
|
14
|
+
self.bias_hessian = None
|
|
15
|
+
return
|
|
16
|
+
|
|
17
|
+
def calc_alpha(self):
|
|
18
|
+
alpha = np.dot(self.gradient.reshape(1, len(self.geom_num_list)), (self.d_vector).reshape(len(self.geom_num_list), 1)) / (np.dot(self.d_vector.reshape(1, len(self.geom_num_list)), self.d_vector.reshape(len(self.geom_num_list), 1)) + self.epsilon)
|
|
19
|
+
return alpha
|
|
20
|
+
|
|
21
|
+
def calc_beta(self):#based on polak-ribiere
|
|
22
|
+
beta = np.dot(self.gradient.reshape(1, len(self.geom_num_list)), (self.gradient - self.prev_gradient).reshape(len(self.geom_num_list), 1)) / (np.dot(self.prev_gradient.reshape(1, len(self.geom_num_list)), self.prev_gradient.reshape(len(self.geom_num_list), 1)) ** 2 + self.epsilon)#based on polak-ribiere
|
|
23
|
+
return beta
|
|
24
|
+
|
|
25
|
+
def calc_beta_PR(self):#polak-ribiere
|
|
26
|
+
beta = np.dot(self.gradient.reshape(1, len(self.geom_num_list)), (self.gradient - self.prev_gradient).reshape(len(self.geom_num_list), 1)) / (np.dot(self.prev_gradient.reshape(1, len(self.geom_num_list)), self.prev_gradient.reshape(len(self.geom_num_list), 1)) + self.epsilon) #polak-ribiere
|
|
27
|
+
return beta
|
|
28
|
+
|
|
29
|
+
def calc_beta_FR(self):#fletcher-reeeves
|
|
30
|
+
beta = np.dot(self.gradient.reshape(1, len(self.geom_num_list)), self.gradient.reshape(len(self.geom_num_list), 1)) / (np.dot(self.prev_gradient.reshape(1, len(self.geom_num_list)), self.prev_gradient.reshape(len(self.geom_num_list), 1)) + self.epsilon)
|
|
31
|
+
return beta
|
|
32
|
+
|
|
33
|
+
def calc_beta_HS(self):#Hestenes-stiefel
|
|
34
|
+
beta = np.dot(self.gradient.reshape(1, len(self.geom_num_list)), (self.gradient - self.prev_gradient).reshape(len(self.geom_num_list), 1)) / (np.dot(self.d_vector.reshape(1, len(self.geom_num_list)), (self.gradient - self.prev_gradient).reshape(len(self.geom_num_list), 1)) + self.epsilon)
|
|
35
|
+
return beta
|
|
36
|
+
|
|
37
|
+
def calc_beta_DY(self):#Dai-Yuan
|
|
38
|
+
beta = np.dot(self.gradient.reshape(1, len(self.geom_num_list)), self.gradient.reshape(len(self.geom_num_list), 1)) / (np.dot(self.d_vector.reshape(1, len(self.geom_num_list)), (self.gradient - self.prev_gradient).reshape(len(self.geom_num_list), 1)) + self.epsilon)
|
|
39
|
+
return beta
|
|
40
|
+
|
|
41
|
+
def run(self, geom_num_list, B_g, pre_B_g, pre_geom=[], B_e=0.0, pre_B_e=0.0, pre_move_vector=[], initial_geom_num_list=[], g=[], pre_g=[]):
|
|
42
|
+
#cg method
|
|
43
|
+
self.geom_num_list = np.array(geom_num_list)
|
|
44
|
+
self.gradient = np.array(B_g)
|
|
45
|
+
self.prev_gradient = np.array(pre_B_g)
|
|
46
|
+
|
|
47
|
+
if self.Initialization:
|
|
48
|
+
self.d_vector = geom_num_list * 0.0
|
|
49
|
+
self.Initialization = False
|
|
50
|
+
return self.DELTA*B_g
|
|
51
|
+
|
|
52
|
+
alpha = self.calc_alpha()
|
|
53
|
+
|
|
54
|
+
move_vector = self.DELTA * alpha * self.d_vector
|
|
55
|
+
if self.config["method"].lower() == "cg_pr":
|
|
56
|
+
beta = self.calc_beta_PR()
|
|
57
|
+
elif self.config["method"].lower() == "cg_fr":
|
|
58
|
+
beta = self.calc_beta_FR()
|
|
59
|
+
elif self.config["method"].lower() == "cg_hs":
|
|
60
|
+
beta = self.calc_beta_HS()
|
|
61
|
+
elif self.config["method"].lower() == "cg_dy":
|
|
62
|
+
beta = self.calc_beta_DY()
|
|
63
|
+
else:
|
|
64
|
+
beta = self.calc_beta()
|
|
65
|
+
|
|
66
|
+
self.d_vector = copy.copy(-1 * B_g + abs(beta) * self.d_vector)
|
|
67
|
+
return move_vector#Bohr.
|
|
68
|
+
|
|
69
|
+
def set_hessian(self, hessian):
|
|
70
|
+
self.hessian = hessian
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
def set_bias_hessian(self, bias_hessian):
|
|
74
|
+
self.bias_hessian = bias_hessian
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def get_hessian(self):
|
|
79
|
+
return self.hessian
|
|
80
|
+
|
|
81
|
+
def get_bias_hessian(self):
|
|
82
|
+
return self.bias_hessian
|