MultiOptPy 1.20.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. multioptpy/Calculator/__init__.py +0 -0
  2. multioptpy/Calculator/ase_calculation_tools.py +424 -0
  3. multioptpy/Calculator/ase_tools/__init__.py +0 -0
  4. multioptpy/Calculator/ase_tools/fairchem.py +28 -0
  5. multioptpy/Calculator/ase_tools/gamess.py +19 -0
  6. multioptpy/Calculator/ase_tools/gaussian.py +165 -0
  7. multioptpy/Calculator/ase_tools/mace.py +28 -0
  8. multioptpy/Calculator/ase_tools/mopac.py +19 -0
  9. multioptpy/Calculator/ase_tools/nwchem.py +31 -0
  10. multioptpy/Calculator/ase_tools/orca.py +22 -0
  11. multioptpy/Calculator/ase_tools/pygfn0.py +37 -0
  12. multioptpy/Calculator/dxtb_calculation_tools.py +344 -0
  13. multioptpy/Calculator/emt_calculation_tools.py +458 -0
  14. multioptpy/Calculator/gpaw_calculation_tools.py +183 -0
  15. multioptpy/Calculator/lj_calculation_tools.py +314 -0
  16. multioptpy/Calculator/psi4_calculation_tools.py +334 -0
  17. multioptpy/Calculator/pwscf_calculation_tools.py +189 -0
  18. multioptpy/Calculator/pyscf_calculation_tools.py +327 -0
  19. multioptpy/Calculator/sqm1_calculation_tools.py +611 -0
  20. multioptpy/Calculator/sqm2_calculation_tools.py +376 -0
  21. multioptpy/Calculator/tblite_calculation_tools.py +352 -0
  22. multioptpy/Calculator/tersoff_calculation_tools.py +818 -0
  23. multioptpy/Constraint/__init__.py +0 -0
  24. multioptpy/Constraint/constraint_condition.py +834 -0
  25. multioptpy/Coordinate/__init__.py +0 -0
  26. multioptpy/Coordinate/polar_coordinate.py +199 -0
  27. multioptpy/Coordinate/redundant_coordinate.py +638 -0
  28. multioptpy/IRC/__init__.py +0 -0
  29. multioptpy/IRC/converge_criteria.py +28 -0
  30. multioptpy/IRC/dvv.py +544 -0
  31. multioptpy/IRC/euler.py +439 -0
  32. multioptpy/IRC/hpc.py +564 -0
  33. multioptpy/IRC/lqa.py +540 -0
  34. multioptpy/IRC/modekill.py +662 -0
  35. multioptpy/IRC/rk4.py +579 -0
  36. multioptpy/Interpolation/__init__.py +0 -0
  37. multioptpy/Interpolation/adaptive_interpolation.py +283 -0
  38. multioptpy/Interpolation/binomial_interpolation.py +179 -0
  39. multioptpy/Interpolation/geodesic_interpolation.py +785 -0
  40. multioptpy/Interpolation/interpolation.py +156 -0
  41. multioptpy/Interpolation/linear_interpolation.py +473 -0
  42. multioptpy/Interpolation/savitzky_golay_interpolation.py +252 -0
  43. multioptpy/Interpolation/spline_interpolation.py +353 -0
  44. multioptpy/MD/__init__.py +0 -0
  45. multioptpy/MD/thermostat.py +185 -0
  46. multioptpy/MEP/__init__.py +0 -0
  47. multioptpy/MEP/pathopt_bneb_force.py +443 -0
  48. multioptpy/MEP/pathopt_dmf_force.py +448 -0
  49. multioptpy/MEP/pathopt_dneb_force.py +130 -0
  50. multioptpy/MEP/pathopt_ewbneb_force.py +207 -0
  51. multioptpy/MEP/pathopt_gpneb_force.py +512 -0
  52. multioptpy/MEP/pathopt_lup_force.py +113 -0
  53. multioptpy/MEP/pathopt_neb_force.py +225 -0
  54. multioptpy/MEP/pathopt_nesb_force.py +205 -0
  55. multioptpy/MEP/pathopt_om_force.py +153 -0
  56. multioptpy/MEP/pathopt_qsm_force.py +174 -0
  57. multioptpy/MEP/pathopt_qsmv2_force.py +304 -0
  58. multioptpy/ModelFunction/__init__.py +7 -0
  59. multioptpy/ModelFunction/avoiding_model_function.py +29 -0
  60. multioptpy/ModelFunction/binary_image_ts_search_model_function.py +47 -0
  61. multioptpy/ModelFunction/conical_model_function.py +26 -0
  62. multioptpy/ModelFunction/opt_meci.py +50 -0
  63. multioptpy/ModelFunction/opt_mesx.py +47 -0
  64. multioptpy/ModelFunction/opt_mesx_2.py +49 -0
  65. multioptpy/ModelFunction/seam_model_function.py +27 -0
  66. multioptpy/ModelHessian/__init__.py +0 -0
  67. multioptpy/ModelHessian/approx_hessian.py +147 -0
  68. multioptpy/ModelHessian/calc_params.py +227 -0
  69. multioptpy/ModelHessian/fischer.py +236 -0
  70. multioptpy/ModelHessian/fischerd3.py +360 -0
  71. multioptpy/ModelHessian/fischerd4.py +398 -0
  72. multioptpy/ModelHessian/gfn0xtb.py +633 -0
  73. multioptpy/ModelHessian/gfnff.py +709 -0
  74. multioptpy/ModelHessian/lindh.py +165 -0
  75. multioptpy/ModelHessian/lindh2007d2.py +707 -0
  76. multioptpy/ModelHessian/lindh2007d3.py +822 -0
  77. multioptpy/ModelHessian/lindh2007d4.py +1030 -0
  78. multioptpy/ModelHessian/morse.py +106 -0
  79. multioptpy/ModelHessian/schlegel.py +144 -0
  80. multioptpy/ModelHessian/schlegeld3.py +322 -0
  81. multioptpy/ModelHessian/schlegeld4.py +559 -0
  82. multioptpy/ModelHessian/shortrange.py +346 -0
  83. multioptpy/ModelHessian/swartd2.py +496 -0
  84. multioptpy/ModelHessian/swartd3.py +706 -0
  85. multioptpy/ModelHessian/swartd4.py +918 -0
  86. multioptpy/ModelHessian/tshess.py +40 -0
  87. multioptpy/Optimizer/QHAdam.py +61 -0
  88. multioptpy/Optimizer/__init__.py +0 -0
  89. multioptpy/Optimizer/abc_fire.py +83 -0
  90. multioptpy/Optimizer/adabelief.py +58 -0
  91. multioptpy/Optimizer/adabound.py +68 -0
  92. multioptpy/Optimizer/adadelta.py +65 -0
  93. multioptpy/Optimizer/adaderivative.py +56 -0
  94. multioptpy/Optimizer/adadiff.py +68 -0
  95. multioptpy/Optimizer/adafactor.py +70 -0
  96. multioptpy/Optimizer/adam.py +65 -0
  97. multioptpy/Optimizer/adamax.py +62 -0
  98. multioptpy/Optimizer/adamod.py +83 -0
  99. multioptpy/Optimizer/adamw.py +65 -0
  100. multioptpy/Optimizer/adiis.py +523 -0
  101. multioptpy/Optimizer/afire_neb.py +282 -0
  102. multioptpy/Optimizer/block_hessian_update.py +709 -0
  103. multioptpy/Optimizer/c2diis.py +491 -0
  104. multioptpy/Optimizer/component_wise_scaling.py +405 -0
  105. multioptpy/Optimizer/conjugate_gradient.py +82 -0
  106. multioptpy/Optimizer/conjugate_gradient_neb.py +345 -0
  107. multioptpy/Optimizer/coordinate_locking.py +405 -0
  108. multioptpy/Optimizer/dic_rsirfo.py +1015 -0
  109. multioptpy/Optimizer/ediis.py +417 -0
  110. multioptpy/Optimizer/eve.py +76 -0
  111. multioptpy/Optimizer/fastadabelief.py +61 -0
  112. multioptpy/Optimizer/fire.py +77 -0
  113. multioptpy/Optimizer/fire2.py +249 -0
  114. multioptpy/Optimizer/fire_neb.py +92 -0
  115. multioptpy/Optimizer/gan_step.py +486 -0
  116. multioptpy/Optimizer/gdiis.py +609 -0
  117. multioptpy/Optimizer/gediis.py +203 -0
  118. multioptpy/Optimizer/geodesic_step.py +433 -0
  119. multioptpy/Optimizer/gpmin.py +633 -0
  120. multioptpy/Optimizer/gpr_step.py +364 -0
  121. multioptpy/Optimizer/gradientdescent.py +78 -0
  122. multioptpy/Optimizer/gradientdescent_neb.py +52 -0
  123. multioptpy/Optimizer/hessian_update.py +433 -0
  124. multioptpy/Optimizer/hybrid_rfo.py +998 -0
  125. multioptpy/Optimizer/kdiis.py +625 -0
  126. multioptpy/Optimizer/lars.py +21 -0
  127. multioptpy/Optimizer/lbfgs.py +253 -0
  128. multioptpy/Optimizer/lbfgs_neb.py +355 -0
  129. multioptpy/Optimizer/linesearch.py +236 -0
  130. multioptpy/Optimizer/lookahead.py +40 -0
  131. multioptpy/Optimizer/nadam.py +64 -0
  132. multioptpy/Optimizer/newton.py +200 -0
  133. multioptpy/Optimizer/prodigy.py +70 -0
  134. multioptpy/Optimizer/purtubation.py +16 -0
  135. multioptpy/Optimizer/quickmin_neb.py +245 -0
  136. multioptpy/Optimizer/radam.py +75 -0
  137. multioptpy/Optimizer/rfo_neb.py +302 -0
  138. multioptpy/Optimizer/ric_rfo.py +842 -0
  139. multioptpy/Optimizer/rl_step.py +627 -0
  140. multioptpy/Optimizer/rmspropgrave.py +65 -0
  141. multioptpy/Optimizer/rsirfo.py +1647 -0
  142. multioptpy/Optimizer/rsprfo.py +1056 -0
  143. multioptpy/Optimizer/sadam.py +60 -0
  144. multioptpy/Optimizer/samsgrad.py +63 -0
  145. multioptpy/Optimizer/tr_lbfgs.py +678 -0
  146. multioptpy/Optimizer/trim.py +273 -0
  147. multioptpy/Optimizer/trust_radius.py +207 -0
  148. multioptpy/Optimizer/trust_radius_neb.py +121 -0
  149. multioptpy/Optimizer/yogi.py +60 -0
  150. multioptpy/OtherMethod/__init__.py +0 -0
  151. multioptpy/OtherMethod/addf.py +1150 -0
  152. multioptpy/OtherMethod/dimer.py +895 -0
  153. multioptpy/OtherMethod/elastic_image_pair.py +629 -0
  154. multioptpy/OtherMethod/modelfunction.py +456 -0
  155. multioptpy/OtherMethod/newton_traj.py +454 -0
  156. multioptpy/OtherMethod/twopshs.py +1095 -0
  157. multioptpy/PESAnalyzer/__init__.py +0 -0
  158. multioptpy/PESAnalyzer/calc_irc_curvature.py +125 -0
  159. multioptpy/PESAnalyzer/cmds_analysis.py +152 -0
  160. multioptpy/PESAnalyzer/koopman_analysis.py +268 -0
  161. multioptpy/PESAnalyzer/pca_analysis.py +314 -0
  162. multioptpy/Parameters/__init__.py +0 -0
  163. multioptpy/Parameters/atomic_mass.py +20 -0
  164. multioptpy/Parameters/atomic_number.py +22 -0
  165. multioptpy/Parameters/covalent_radii.py +44 -0
  166. multioptpy/Parameters/d2.py +61 -0
  167. multioptpy/Parameters/d3.py +63 -0
  168. multioptpy/Parameters/d4.py +103 -0
  169. multioptpy/Parameters/dreiding.py +34 -0
  170. multioptpy/Parameters/gfn0xtb_param.py +137 -0
  171. multioptpy/Parameters/gfnff_param.py +315 -0
  172. multioptpy/Parameters/gnb.py +104 -0
  173. multioptpy/Parameters/parameter.py +22 -0
  174. multioptpy/Parameters/uff.py +72 -0
  175. multioptpy/Parameters/unit_values.py +20 -0
  176. multioptpy/Potential/AFIR_potential.py +55 -0
  177. multioptpy/Potential/LJ_repulsive_potential.py +345 -0
  178. multioptpy/Potential/__init__.py +0 -0
  179. multioptpy/Potential/anharmonic_keep_potential.py +28 -0
  180. multioptpy/Potential/asym_elllipsoidal_potential.py +718 -0
  181. multioptpy/Potential/electrostatic_potential.py +69 -0
  182. multioptpy/Potential/flux_potential.py +30 -0
  183. multioptpy/Potential/gaussian_potential.py +101 -0
  184. multioptpy/Potential/idpp.py +516 -0
  185. multioptpy/Potential/keep_angle_potential.py +146 -0
  186. multioptpy/Potential/keep_dihedral_angle_potential.py +105 -0
  187. multioptpy/Potential/keep_outofplain_angle_potential.py +70 -0
  188. multioptpy/Potential/keep_potential.py +99 -0
  189. multioptpy/Potential/mechano_force_potential.py +74 -0
  190. multioptpy/Potential/nanoreactor_potential.py +52 -0
  191. multioptpy/Potential/potential.py +896 -0
  192. multioptpy/Potential/spacer_model_potential.py +221 -0
  193. multioptpy/Potential/switching_potential.py +258 -0
  194. multioptpy/Potential/universal_potential.py +34 -0
  195. multioptpy/Potential/value_range_potential.py +36 -0
  196. multioptpy/Potential/void_point_potential.py +25 -0
  197. multioptpy/SQM/__init__.py +0 -0
  198. multioptpy/SQM/sqm1/__init__.py +0 -0
  199. multioptpy/SQM/sqm1/sqm1_core.py +1792 -0
  200. multioptpy/SQM/sqm2/__init__.py +0 -0
  201. multioptpy/SQM/sqm2/calc_tools.py +95 -0
  202. multioptpy/SQM/sqm2/sqm2_basis.py +850 -0
  203. multioptpy/SQM/sqm2/sqm2_bond.py +119 -0
  204. multioptpy/SQM/sqm2/sqm2_core.py +303 -0
  205. multioptpy/SQM/sqm2/sqm2_data.py +1229 -0
  206. multioptpy/SQM/sqm2/sqm2_disp.py +65 -0
  207. multioptpy/SQM/sqm2/sqm2_eeq.py +243 -0
  208. multioptpy/SQM/sqm2/sqm2_overlapint.py +704 -0
  209. multioptpy/SQM/sqm2/sqm2_qm.py +578 -0
  210. multioptpy/SQM/sqm2/sqm2_rep.py +66 -0
  211. multioptpy/SQM/sqm2/sqm2_srb.py +70 -0
  212. multioptpy/Thermo/__init__.py +0 -0
  213. multioptpy/Thermo/normal_mode_analyzer.py +865 -0
  214. multioptpy/Utils/__init__.py +0 -0
  215. multioptpy/Utils/bond_connectivity.py +264 -0
  216. multioptpy/Utils/calc_tools.py +884 -0
  217. multioptpy/Utils/oniom.py +96 -0
  218. multioptpy/Utils/pbc.py +48 -0
  219. multioptpy/Utils/riemann_curvature.py +208 -0
  220. multioptpy/Utils/symmetry_analyzer.py +482 -0
  221. multioptpy/Visualization/__init__.py +0 -0
  222. multioptpy/Visualization/visualization.py +156 -0
  223. multioptpy/WFAnalyzer/MO_analysis.py +104 -0
  224. multioptpy/WFAnalyzer/__init__.py +0 -0
  225. multioptpy/Wrapper/__init__.py +0 -0
  226. multioptpy/Wrapper/autots.py +1239 -0
  227. multioptpy/Wrapper/ieip_wrapper.py +93 -0
  228. multioptpy/Wrapper/md_wrapper.py +92 -0
  229. multioptpy/Wrapper/neb_wrapper.py +94 -0
  230. multioptpy/Wrapper/optimize_wrapper.py +76 -0
  231. multioptpy/__init__.py +5 -0
  232. multioptpy/entrypoints.py +916 -0
  233. multioptpy/fileio.py +660 -0
  234. multioptpy/ieip.py +340 -0
  235. multioptpy/interface.py +1086 -0
  236. multioptpy/irc.py +529 -0
  237. multioptpy/moleculardynamics.py +432 -0
  238. multioptpy/neb.py +1267 -0
  239. multioptpy/optimization.py +1553 -0
  240. multioptpy/optimizer.py +709 -0
  241. multioptpy-1.20.2.dist-info/METADATA +438 -0
  242. multioptpy-1.20.2.dist-info/RECORD +246 -0
  243. multioptpy-1.20.2.dist-info/WHEEL +5 -0
  244. multioptpy-1.20.2.dist-info/entry_points.txt +9 -0
  245. multioptpy-1.20.2.dist-info/licenses/LICENSE +674 -0
  246. multioptpy-1.20.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,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