mlmm-toolkit 0.2.2.dev0__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 (372) hide show
  1. hessian_ff/__init__.py +50 -0
  2. hessian_ff/analytical_hessian.py +609 -0
  3. hessian_ff/constants.py +46 -0
  4. hessian_ff/forcefield.py +339 -0
  5. hessian_ff/loaders.py +608 -0
  6. hessian_ff/native/Makefile +8 -0
  7. hessian_ff/native/__init__.py +28 -0
  8. hessian_ff/native/analytical_hessian.py +88 -0
  9. hessian_ff/native/analytical_hessian_ext.cpp +258 -0
  10. hessian_ff/native/bonded.py +82 -0
  11. hessian_ff/native/bonded_ext.cpp +640 -0
  12. hessian_ff/native/loader.py +349 -0
  13. hessian_ff/native/nonbonded.py +118 -0
  14. hessian_ff/native/nonbonded_ext.cpp +1150 -0
  15. hessian_ff/prmtop_parmed.py +23 -0
  16. hessian_ff/system.py +107 -0
  17. hessian_ff/terms/__init__.py +14 -0
  18. hessian_ff/terms/angle.py +73 -0
  19. hessian_ff/terms/bond.py +44 -0
  20. hessian_ff/terms/cmap.py +406 -0
  21. hessian_ff/terms/dihedral.py +141 -0
  22. hessian_ff/terms/nonbonded.py +209 -0
  23. hessian_ff/tests/__init__.py +0 -0
  24. hessian_ff/tests/conftest.py +75 -0
  25. hessian_ff/tests/data/small/complex.parm7 +1346 -0
  26. hessian_ff/tests/data/small/complex.pdb +125 -0
  27. hessian_ff/tests/data/small/complex.rst7 +63 -0
  28. hessian_ff/tests/test_coords_input.py +44 -0
  29. hessian_ff/tests/test_energy_force.py +49 -0
  30. hessian_ff/tests/test_hessian.py +137 -0
  31. hessian_ff/tests/test_smoke.py +18 -0
  32. hessian_ff/tests/test_validation.py +40 -0
  33. hessian_ff/workflows.py +889 -0
  34. mlmm/__init__.py +36 -0
  35. mlmm/__main__.py +7 -0
  36. mlmm/_version.py +34 -0
  37. mlmm/add_elem_info.py +374 -0
  38. mlmm/advanced_help.py +91 -0
  39. mlmm/align_freeze_atoms.py +601 -0
  40. mlmm/all.py +3535 -0
  41. mlmm/bond_changes.py +231 -0
  42. mlmm/bool_compat.py +223 -0
  43. mlmm/cli.py +574 -0
  44. mlmm/cli_utils.py +166 -0
  45. mlmm/default_group.py +337 -0
  46. mlmm/defaults.py +467 -0
  47. mlmm/define_layer.py +526 -0
  48. mlmm/dft.py +1041 -0
  49. mlmm/energy_diagram.py +253 -0
  50. mlmm/extract.py +2213 -0
  51. mlmm/fix_altloc.py +464 -0
  52. mlmm/freq.py +1406 -0
  53. mlmm/harmonic_constraints.py +140 -0
  54. mlmm/hessian_cache.py +44 -0
  55. mlmm/hessian_calc.py +174 -0
  56. mlmm/irc.py +638 -0
  57. mlmm/mlmm_calc.py +2262 -0
  58. mlmm/mm_parm.py +945 -0
  59. mlmm/oniom_export.py +1983 -0
  60. mlmm/oniom_import.py +457 -0
  61. mlmm/opt.py +1742 -0
  62. mlmm/path_opt.py +1353 -0
  63. mlmm/path_search.py +2299 -0
  64. mlmm/preflight.py +88 -0
  65. mlmm/py.typed +1 -0
  66. mlmm/pysis_runner.py +45 -0
  67. mlmm/scan.py +1047 -0
  68. mlmm/scan2d.py +1226 -0
  69. mlmm/scan3d.py +1265 -0
  70. mlmm/scan_common.py +184 -0
  71. mlmm/summary_log.py +736 -0
  72. mlmm/trj2fig.py +448 -0
  73. mlmm/tsopt.py +2871 -0
  74. mlmm/utils.py +2309 -0
  75. mlmm/xtb_embedcharge_correction.py +475 -0
  76. mlmm_toolkit-0.2.2.dev0.dist-info/METADATA +1159 -0
  77. mlmm_toolkit-0.2.2.dev0.dist-info/RECORD +372 -0
  78. mlmm_toolkit-0.2.2.dev0.dist-info/WHEEL +5 -0
  79. mlmm_toolkit-0.2.2.dev0.dist-info/entry_points.txt +2 -0
  80. mlmm_toolkit-0.2.2.dev0.dist-info/licenses/LICENSE +674 -0
  81. mlmm_toolkit-0.2.2.dev0.dist-info/top_level.txt +4 -0
  82. pysisyphus/Geometry.py +1667 -0
  83. pysisyphus/LICENSE +674 -0
  84. pysisyphus/TableFormatter.py +63 -0
  85. pysisyphus/TablePrinter.py +74 -0
  86. pysisyphus/__init__.py +12 -0
  87. pysisyphus/calculators/AFIR.py +452 -0
  88. pysisyphus/calculators/AnaPot.py +20 -0
  89. pysisyphus/calculators/AnaPot2.py +48 -0
  90. pysisyphus/calculators/AnaPot3.py +12 -0
  91. pysisyphus/calculators/AnaPot4.py +20 -0
  92. pysisyphus/calculators/AnaPotBase.py +337 -0
  93. pysisyphus/calculators/AnaPotCBM.py +25 -0
  94. pysisyphus/calculators/AtomAtomTransTorque.py +154 -0
  95. pysisyphus/calculators/CFOUR.py +250 -0
  96. pysisyphus/calculators/Calculator.py +844 -0
  97. pysisyphus/calculators/CerjanMiller.py +24 -0
  98. pysisyphus/calculators/Composite.py +123 -0
  99. pysisyphus/calculators/ConicalIntersection.py +171 -0
  100. pysisyphus/calculators/DFTBp.py +430 -0
  101. pysisyphus/calculators/DFTD3.py +66 -0
  102. pysisyphus/calculators/DFTD4.py +84 -0
  103. pysisyphus/calculators/Dalton.py +61 -0
  104. pysisyphus/calculators/Dimer.py +681 -0
  105. pysisyphus/calculators/Dummy.py +20 -0
  106. pysisyphus/calculators/EGO.py +76 -0
  107. pysisyphus/calculators/EnergyMin.py +224 -0
  108. pysisyphus/calculators/ExternalPotential.py +264 -0
  109. pysisyphus/calculators/FakeASE.py +35 -0
  110. pysisyphus/calculators/FourWellAnaPot.py +28 -0
  111. pysisyphus/calculators/FreeEndNEBPot.py +39 -0
  112. pysisyphus/calculators/Gaussian09.py +18 -0
  113. pysisyphus/calculators/Gaussian16.py +726 -0
  114. pysisyphus/calculators/HardSphere.py +159 -0
  115. pysisyphus/calculators/IDPPCalculator.py +49 -0
  116. pysisyphus/calculators/IPIClient.py +133 -0
  117. pysisyphus/calculators/IPIServer.py +234 -0
  118. pysisyphus/calculators/LEPSBase.py +24 -0
  119. pysisyphus/calculators/LEPSExpr.py +139 -0
  120. pysisyphus/calculators/LennardJones.py +80 -0
  121. pysisyphus/calculators/MOPAC.py +219 -0
  122. pysisyphus/calculators/MullerBrownSympyPot.py +51 -0
  123. pysisyphus/calculators/MultiCalc.py +85 -0
  124. pysisyphus/calculators/NFK.py +45 -0
  125. pysisyphus/calculators/OBabel.py +87 -0
  126. pysisyphus/calculators/ONIOMv2.py +1129 -0
  127. pysisyphus/calculators/ORCA.py +893 -0
  128. pysisyphus/calculators/ORCA5.py +6 -0
  129. pysisyphus/calculators/OpenMM.py +88 -0
  130. pysisyphus/calculators/OpenMolcas.py +281 -0
  131. pysisyphus/calculators/OverlapCalculator.py +908 -0
  132. pysisyphus/calculators/Psi4.py +218 -0
  133. pysisyphus/calculators/PyPsi4.py +37 -0
  134. pysisyphus/calculators/PySCF.py +341 -0
  135. pysisyphus/calculators/PyXTB.py +73 -0
  136. pysisyphus/calculators/QCEngine.py +106 -0
  137. pysisyphus/calculators/Rastrigin.py +22 -0
  138. pysisyphus/calculators/Remote.py +76 -0
  139. pysisyphus/calculators/Rosenbrock.py +15 -0
  140. pysisyphus/calculators/SocketCalc.py +97 -0
  141. pysisyphus/calculators/TIP3P.py +111 -0
  142. pysisyphus/calculators/TransTorque.py +161 -0
  143. pysisyphus/calculators/Turbomole.py +965 -0
  144. pysisyphus/calculators/VRIPot.py +37 -0
  145. pysisyphus/calculators/WFOWrapper.py +333 -0
  146. pysisyphus/calculators/WFOWrapper2.py +341 -0
  147. pysisyphus/calculators/XTB.py +418 -0
  148. pysisyphus/calculators/__init__.py +81 -0
  149. pysisyphus/calculators/cosmo_data.py +139 -0
  150. pysisyphus/calculators/parser.py +150 -0
  151. pysisyphus/color.py +19 -0
  152. pysisyphus/config.py +133 -0
  153. pysisyphus/constants.py +65 -0
  154. pysisyphus/cos/AdaptiveNEB.py +230 -0
  155. pysisyphus/cos/ChainOfStates.py +725 -0
  156. pysisyphus/cos/FreeEndNEB.py +25 -0
  157. pysisyphus/cos/FreezingString.py +103 -0
  158. pysisyphus/cos/GrowingChainOfStates.py +71 -0
  159. pysisyphus/cos/GrowingNT.py +309 -0
  160. pysisyphus/cos/GrowingString.py +508 -0
  161. pysisyphus/cos/NEB.py +189 -0
  162. pysisyphus/cos/SimpleZTS.py +64 -0
  163. pysisyphus/cos/__init__.py +22 -0
  164. pysisyphus/cos/stiffness.py +199 -0
  165. pysisyphus/drivers/__init__.py +17 -0
  166. pysisyphus/drivers/afir.py +855 -0
  167. pysisyphus/drivers/barriers.py +271 -0
  168. pysisyphus/drivers/birkholz.py +138 -0
  169. pysisyphus/drivers/cluster.py +318 -0
  170. pysisyphus/drivers/diabatization.py +133 -0
  171. pysisyphus/drivers/merge.py +368 -0
  172. pysisyphus/drivers/merge_mol2.py +322 -0
  173. pysisyphus/drivers/opt.py +375 -0
  174. pysisyphus/drivers/perf.py +91 -0
  175. pysisyphus/drivers/pka.py +52 -0
  176. pysisyphus/drivers/precon_pos_rot.py +669 -0
  177. pysisyphus/drivers/rates.py +480 -0
  178. pysisyphus/drivers/replace.py +219 -0
  179. pysisyphus/drivers/scan.py +212 -0
  180. pysisyphus/drivers/spectrum.py +166 -0
  181. pysisyphus/drivers/thermo.py +31 -0
  182. pysisyphus/dynamics/Gaussian.py +103 -0
  183. pysisyphus/dynamics/__init__.py +20 -0
  184. pysisyphus/dynamics/colvars.py +136 -0
  185. pysisyphus/dynamics/driver.py +297 -0
  186. pysisyphus/dynamics/helpers.py +256 -0
  187. pysisyphus/dynamics/lincs.py +105 -0
  188. pysisyphus/dynamics/mdp.py +364 -0
  189. pysisyphus/dynamics/rattle.py +121 -0
  190. pysisyphus/dynamics/thermostats.py +128 -0
  191. pysisyphus/dynamics/wigner.py +266 -0
  192. pysisyphus/elem_data.py +3473 -0
  193. pysisyphus/exceptions.py +2 -0
  194. pysisyphus/filtertrj.py +69 -0
  195. pysisyphus/helpers.py +623 -0
  196. pysisyphus/helpers_pure.py +649 -0
  197. pysisyphus/init_logging.py +50 -0
  198. pysisyphus/intcoords/Bend.py +69 -0
  199. pysisyphus/intcoords/Bend2.py +25 -0
  200. pysisyphus/intcoords/BondedFragment.py +32 -0
  201. pysisyphus/intcoords/Cartesian.py +41 -0
  202. pysisyphus/intcoords/CartesianCoords.py +140 -0
  203. pysisyphus/intcoords/Coords.py +56 -0
  204. pysisyphus/intcoords/DLC.py +197 -0
  205. pysisyphus/intcoords/DistanceFunction.py +34 -0
  206. pysisyphus/intcoords/DummyImproper.py +70 -0
  207. pysisyphus/intcoords/DummyTorsion.py +72 -0
  208. pysisyphus/intcoords/LinearBend.py +105 -0
  209. pysisyphus/intcoords/LinearDisplacement.py +80 -0
  210. pysisyphus/intcoords/OutOfPlane.py +59 -0
  211. pysisyphus/intcoords/PrimTypes.py +286 -0
  212. pysisyphus/intcoords/Primitive.py +137 -0
  213. pysisyphus/intcoords/RedundantCoords.py +659 -0
  214. pysisyphus/intcoords/RobustTorsion.py +59 -0
  215. pysisyphus/intcoords/Rotation.py +147 -0
  216. pysisyphus/intcoords/Stretch.py +31 -0
  217. pysisyphus/intcoords/Torsion.py +101 -0
  218. pysisyphus/intcoords/Torsion2.py +25 -0
  219. pysisyphus/intcoords/Translation.py +45 -0
  220. pysisyphus/intcoords/__init__.py +61 -0
  221. pysisyphus/intcoords/augment_bonds.py +126 -0
  222. pysisyphus/intcoords/derivatives.py +10512 -0
  223. pysisyphus/intcoords/eval.py +80 -0
  224. pysisyphus/intcoords/exceptions.py +37 -0
  225. pysisyphus/intcoords/findiffs.py +48 -0
  226. pysisyphus/intcoords/generate_derivatives.py +414 -0
  227. pysisyphus/intcoords/helpers.py +235 -0
  228. pysisyphus/intcoords/logging_conf.py +10 -0
  229. pysisyphus/intcoords/mp_derivatives.py +10836 -0
  230. pysisyphus/intcoords/setup.py +962 -0
  231. pysisyphus/intcoords/setup_fast.py +176 -0
  232. pysisyphus/intcoords/update.py +272 -0
  233. pysisyphus/intcoords/valid.py +89 -0
  234. pysisyphus/interpolate/Geodesic.py +93 -0
  235. pysisyphus/interpolate/IDPP.py +55 -0
  236. pysisyphus/interpolate/Interpolator.py +116 -0
  237. pysisyphus/interpolate/LST.py +70 -0
  238. pysisyphus/interpolate/Redund.py +152 -0
  239. pysisyphus/interpolate/__init__.py +9 -0
  240. pysisyphus/interpolate/helpers.py +34 -0
  241. pysisyphus/io/__init__.py +22 -0
  242. pysisyphus/io/aomix.py +178 -0
  243. pysisyphus/io/cjson.py +24 -0
  244. pysisyphus/io/crd.py +101 -0
  245. pysisyphus/io/cube.py +220 -0
  246. pysisyphus/io/fchk.py +184 -0
  247. pysisyphus/io/hdf5.py +49 -0
  248. pysisyphus/io/hessian.py +72 -0
  249. pysisyphus/io/mol2.py +146 -0
  250. pysisyphus/io/molden.py +293 -0
  251. pysisyphus/io/orca.py +189 -0
  252. pysisyphus/io/pdb.py +269 -0
  253. pysisyphus/io/psf.py +79 -0
  254. pysisyphus/io/pubchem.py +31 -0
  255. pysisyphus/io/qcschema.py +34 -0
  256. pysisyphus/io/sdf.py +29 -0
  257. pysisyphus/io/xyz.py +61 -0
  258. pysisyphus/io/zmat.py +175 -0
  259. pysisyphus/irc/DWI.py +108 -0
  260. pysisyphus/irc/DampedVelocityVerlet.py +134 -0
  261. pysisyphus/irc/Euler.py +22 -0
  262. pysisyphus/irc/EulerPC.py +345 -0
  263. pysisyphus/irc/GonzalezSchlegel.py +187 -0
  264. pysisyphus/irc/IMKMod.py +164 -0
  265. pysisyphus/irc/IRC.py +878 -0
  266. pysisyphus/irc/IRCDummy.py +10 -0
  267. pysisyphus/irc/Instanton.py +307 -0
  268. pysisyphus/irc/LQA.py +53 -0
  269. pysisyphus/irc/ModeKill.py +136 -0
  270. pysisyphus/irc/ParamPlot.py +53 -0
  271. pysisyphus/irc/RK4.py +36 -0
  272. pysisyphus/irc/__init__.py +31 -0
  273. pysisyphus/irc/initial_displ.py +219 -0
  274. pysisyphus/linalg.py +411 -0
  275. pysisyphus/line_searches/Backtracking.py +88 -0
  276. pysisyphus/line_searches/HagerZhang.py +184 -0
  277. pysisyphus/line_searches/LineSearch.py +232 -0
  278. pysisyphus/line_searches/StrongWolfe.py +108 -0
  279. pysisyphus/line_searches/__init__.py +9 -0
  280. pysisyphus/line_searches/interpol.py +15 -0
  281. pysisyphus/modefollow/NormalMode.py +40 -0
  282. pysisyphus/modefollow/__init__.py +10 -0
  283. pysisyphus/modefollow/davidson.py +199 -0
  284. pysisyphus/modefollow/lanczos.py +95 -0
  285. pysisyphus/optimizers/BFGS.py +99 -0
  286. pysisyphus/optimizers/BacktrackingOptimizer.py +113 -0
  287. pysisyphus/optimizers/ConjugateGradient.py +98 -0
  288. pysisyphus/optimizers/CubicNewton.py +75 -0
  289. pysisyphus/optimizers/FIRE.py +113 -0
  290. pysisyphus/optimizers/HessianOptimizer.py +1176 -0
  291. pysisyphus/optimizers/LBFGS.py +228 -0
  292. pysisyphus/optimizers/LayerOpt.py +411 -0
  293. pysisyphus/optimizers/MicroOptimizer.py +169 -0
  294. pysisyphus/optimizers/NCOptimizer.py +90 -0
  295. pysisyphus/optimizers/Optimizer.py +1084 -0
  296. pysisyphus/optimizers/PreconLBFGS.py +260 -0
  297. pysisyphus/optimizers/PreconSteepestDescent.py +7 -0
  298. pysisyphus/optimizers/QuickMin.py +74 -0
  299. pysisyphus/optimizers/RFOptimizer.py +181 -0
  300. pysisyphus/optimizers/RSA.py +99 -0
  301. pysisyphus/optimizers/StabilizedQNMethod.py +248 -0
  302. pysisyphus/optimizers/SteepestDescent.py +23 -0
  303. pysisyphus/optimizers/StringOptimizer.py +173 -0
  304. pysisyphus/optimizers/__init__.py +41 -0
  305. pysisyphus/optimizers/closures.py +301 -0
  306. pysisyphus/optimizers/cls_map.py +58 -0
  307. pysisyphus/optimizers/exceptions.py +6 -0
  308. pysisyphus/optimizers/gdiis.py +280 -0
  309. pysisyphus/optimizers/guess_hessians.py +311 -0
  310. pysisyphus/optimizers/hessian_updates.py +355 -0
  311. pysisyphus/optimizers/poly_fit.py +285 -0
  312. pysisyphus/optimizers/precon.py +153 -0
  313. pysisyphus/optimizers/restrict_step.py +24 -0
  314. pysisyphus/pack.py +172 -0
  315. pysisyphus/peakdetect.py +948 -0
  316. pysisyphus/plot.py +1031 -0
  317. pysisyphus/run.py +2106 -0
  318. pysisyphus/socket_helper.py +74 -0
  319. pysisyphus/stocastic/FragmentKick.py +132 -0
  320. pysisyphus/stocastic/Kick.py +81 -0
  321. pysisyphus/stocastic/Pipeline.py +303 -0
  322. pysisyphus/stocastic/__init__.py +21 -0
  323. pysisyphus/stocastic/align.py +127 -0
  324. pysisyphus/testing.py +96 -0
  325. pysisyphus/thermo.py +156 -0
  326. pysisyphus/trj.py +824 -0
  327. pysisyphus/tsoptimizers/RSIRFOptimizer.py +56 -0
  328. pysisyphus/tsoptimizers/RSPRFOptimizer.py +182 -0
  329. pysisyphus/tsoptimizers/TRIM.py +59 -0
  330. pysisyphus/tsoptimizers/TSHessianOptimizer.py +463 -0
  331. pysisyphus/tsoptimizers/__init__.py +23 -0
  332. pysisyphus/wavefunction/Basis.py +239 -0
  333. pysisyphus/wavefunction/DIIS.py +76 -0
  334. pysisyphus/wavefunction/__init__.py +25 -0
  335. pysisyphus/wavefunction/build_ext.py +42 -0
  336. pysisyphus/wavefunction/cart2sph.py +190 -0
  337. pysisyphus/wavefunction/diabatization.py +304 -0
  338. pysisyphus/wavefunction/excited_states.py +435 -0
  339. pysisyphus/wavefunction/gen_ints.py +1811 -0
  340. pysisyphus/wavefunction/helpers.py +104 -0
  341. pysisyphus/wavefunction/ints/__init__.py +0 -0
  342. pysisyphus/wavefunction/ints/boys.py +193 -0
  343. pysisyphus/wavefunction/ints/boys_table_N_64_xasym_27.1_step_0.01.npy +0 -0
  344. pysisyphus/wavefunction/ints/cart_gto3d.py +176 -0
  345. pysisyphus/wavefunction/ints/coulomb3d.py +25928 -0
  346. pysisyphus/wavefunction/ints/diag_quadrupole3d.py +10036 -0
  347. pysisyphus/wavefunction/ints/dipole3d.py +8762 -0
  348. pysisyphus/wavefunction/ints/int2c2e3d.py +7198 -0
  349. pysisyphus/wavefunction/ints/int3c2e3d_sph.py +65040 -0
  350. pysisyphus/wavefunction/ints/kinetic3d.py +8240 -0
  351. pysisyphus/wavefunction/ints/ovlp3d.py +3777 -0
  352. pysisyphus/wavefunction/ints/quadrupole3d.py +15054 -0
  353. pysisyphus/wavefunction/ints/self_ovlp3d.py +198 -0
  354. pysisyphus/wavefunction/localization.py +458 -0
  355. pysisyphus/wavefunction/multipole.py +159 -0
  356. pysisyphus/wavefunction/normalization.py +36 -0
  357. pysisyphus/wavefunction/pop_analysis.py +134 -0
  358. pysisyphus/wavefunction/shells.py +1171 -0
  359. pysisyphus/wavefunction/wavefunction.py +504 -0
  360. pysisyphus/wrapper/__init__.py +11 -0
  361. pysisyphus/wrapper/exceptions.py +2 -0
  362. pysisyphus/wrapper/jmol.py +120 -0
  363. pysisyphus/wrapper/mwfn.py +169 -0
  364. pysisyphus/wrapper/packmol.py +71 -0
  365. pysisyphus/xyzloader.py +168 -0
  366. pysisyphus/yaml_mods.py +45 -0
  367. thermoanalysis/LICENSE +674 -0
  368. thermoanalysis/QCData.py +244 -0
  369. thermoanalysis/__init__.py +0 -0
  370. thermoanalysis/config.py +3 -0
  371. thermoanalysis/constants.py +20 -0
  372. thermoanalysis/thermo.py +1011 -0
@@ -0,0 +1,184 @@
1
+ import numpy as np
2
+
3
+ from pysisyphus.line_searches.LineSearch import LineSearch, \
4
+ LineSearchNotConverged
5
+
6
+
7
+ class HagerZhang(LineSearch):
8
+
9
+ def __init__(self, *args, alpha_prev=None, f_prev=None, dphi0_prev=None,
10
+ quad_step=False, eps=1e-6, theta=0.5, gamma=0.5, rho=5,
11
+ psi_0=.01, psi_1=.1, psi_2=2., psi_low=0.1, psi_hi=10,
12
+ Delta=.7, omega=1e-3, max_bisects=10, **kwargs):
13
+
14
+ kwargs["cond"] = "wolfe"
15
+ super().__init__(*args, **kwargs)
16
+
17
+ self.alpha_prev = alpha_prev
18
+ self.f_prev = f_prev
19
+ self.dphi0_prev = dphi0_prev
20
+ self.quad_step = quad_step
21
+
22
+ self.eps = eps
23
+ self.theta = theta
24
+ self.gamma = gamma
25
+ self.rho = rho
26
+ self.psi_0 = psi_0
27
+ self.psi_1 = psi_1
28
+ self.psi_2 = psi_2
29
+ self.psi_low = psi_low
30
+ self.psi_hi = psi_hi
31
+ self.Delta = Delta
32
+ self.omega = omega
33
+ self.max_bisects = max_bisects
34
+
35
+ def prepare_line_search(self):
36
+ super().prepare_line_search()
37
+
38
+ self.epsk = self.eps * abs(self.get_fg("f", 0.))
39
+
40
+ def bisect(self, a, b):
41
+ """Bisect interval [a, b]."""
42
+ for i in range(self.max_bisects):
43
+ # U3 a.
44
+ d = (1 - self.theta)*a + self.theta*b
45
+ dphi_d = self.get_phi_dphi("g", d)
46
+ if dphi_d >= 0:
47
+ return a, d
48
+
49
+ phi_d = self.get_phi_dphi("f", d)
50
+ # U3 b.
51
+ # If (dphi_d > 0) we would already have returned above...
52
+ if phi_d <= self.phi0 + self.epsk:
53
+ a = d
54
+ # U3 c.
55
+ elif phi_d > self.phi0 + self.epsk:
56
+ b = d
57
+ raise Exception("Bisect failed!")
58
+
59
+ def interval_update(self, a, b, c):
60
+ """Narrows down the bracketing interval."""
61
+ # U0
62
+ if not (a < c < b):
63
+ return a, b
64
+
65
+ phi_c, dphi_c = self.get_phi_dphi("fg", c)
66
+ # U1, sign of slope projection changed. We already passed the minimum.
67
+ if dphi_c >= 0:
68
+ return a, c
69
+ # U2, we are moving towards the minimum.
70
+ elif phi_c <= self.phi0 + self.epsk:
71
+ return c, b
72
+
73
+ # U3, phi_c increased above phi0, so we probably passed the minimum.
74
+ return self.bisect(a, c)
75
+
76
+ def secant(self, a, b):
77
+ """Take secant step."""
78
+ dphia = self.get_phi_dphi("g", a)
79
+ dphib = self.get_phi_dphi("g", b)
80
+ return (a*dphib - b*dphia) / (dphib - dphia)
81
+
82
+ def double_secant(self, a, b):
83
+ """Take secant² step."""
84
+ c = self.secant(a, b)
85
+ A, B = self.interval_update(a, b, c)
86
+ cB_close = np.isclose(c, B)
87
+ cA_close = np.isclose(c, A)
88
+
89
+ if cB_close:
90
+ c_dash = self.secant(b, B)
91
+ elif cA_close:
92
+ c_dash = self.secant(a, A)
93
+
94
+ if cB_close or cA_close:
95
+ a_dash, b_dash = self.interval_update(A, B, c_dash)
96
+ else:
97
+ a_dash, b_dash = A, B
98
+ return a_dash, b_dash
99
+
100
+ def bracket(self, c):
101
+ """Generate initial interval [a, b] that satisfies the opposite
102
+ slope condition (dphi(a) < 0, dphi(b) > 0).
103
+ """
104
+ cs = list()
105
+ p0epsk = self.phi0 + self.epsk
106
+ for j in range(10):
107
+ cs.append(c)
108
+
109
+ dphi_j = self.get_phi_dphi("g", c)
110
+
111
+ if (dphi_j >= 0) and (j == 0):
112
+ return 0, c
113
+
114
+ phi_j = self.get_phi_dphi("f", c)
115
+ if dphi_j >= 0:
116
+ phi_inds = np.array([self.get_fg("f", c) for c in cs[:-1]]) <= p0epsk
117
+ # See https://stackoverflow.com/a/8768734
118
+ ci = len(phi_inds) - phi_inds[::-1].argmax() - 1
119
+ return cs[ci], c
120
+ elif phi_j > p0epsk:
121
+ return self.bisect(0, c)
122
+
123
+ c *= self.rho
124
+
125
+ def norm_inf(self, arr):
126
+ """Returns infinity norm of given array."""
127
+ return np.linalg.norm(arr, np.inf)
128
+
129
+ def initial(self):
130
+ """Get an initial guess for alpha."""
131
+ if (~np.isclose(self.x0, np.zeros_like(self.x0))).any():
132
+ c = self.psi_0 * self.norm_inf(self.x0)/self.norm_inf(self.g0)
133
+ elif not np.isclose(self.f0, 0):
134
+ c = self.psi_0 * self.f0 / self.norm_inf(self.g0)**2
135
+ else:
136
+ c = 1
137
+ return c
138
+
139
+ def take_quad_step(self, alpha, g0_):
140
+ """Try to get alpha for minimum step from quadratic interpolation."""
141
+ fact = max(self.psi_low, g0_/(self.dphi0*self.psi_2))
142
+ alpha_ = min(fact, self.psi_hi) * alpha
143
+ phi_ = self.get_phi_dphi("f", alpha_)
144
+ denom = 2*((phi_-self.phi0)/alpha_ - self.dphi0)
145
+ f_temp = self.get_fg("f", alpha_)
146
+ if denom > 0.:
147
+ c = -self.dphi0*alpha_ / denom
148
+ if f_temp > self.get_fg("f", 0):
149
+ c = max(c, alpha_*1e-10)
150
+ else:
151
+ c = alpha
152
+ return c
153
+
154
+ def run_line_search(self):
155
+ if self.alpha_init is None and self.alpha_prev:
156
+ alpha_init = self.alpha_prev
157
+ elif self.alpha_init is None and self.alpha_prev is None:
158
+ alpha_init = self.initial()
159
+ else:
160
+ alpha_init = self.alpha_init
161
+
162
+ if self.quad_step:
163
+ g0_ = -2*abs(self.get_fg("f", 0)/alpha_init) if (self.dphi0_prev is None) \
164
+ else self.dphi0_prev
165
+ alpha_init = self.take_quad_step(self.psi_2*alpha_init, g0_)
166
+ # This may raise LineSearchConverged
167
+ _ = self.get_phi_dphi("fg", alpha_init)
168
+
169
+ # TODO: cubic interpolation for better alpha_init
170
+ ak, bk = self.bracket(alpha_init)
171
+ for k in range(self.max_cycles):
172
+ if self.cond_func(ak):
173
+ break
174
+ # secant² step
175
+ a, b = self.double_secant(ak, bk)
176
+ if (b - a) > self.gamma*(bk - ak):
177
+ # Bisection step
178
+ c = (a + b)/2
179
+ a, b = self.interval_update(a, b, c)
180
+ ak, bk = a, b
181
+ else:
182
+ raise LineSearchNotConverged
183
+
184
+ return ak
@@ -0,0 +1,232 @@
1
+ import abc
2
+ from collections import namedtuple
3
+ import logging
4
+
5
+ import numpy as np
6
+
7
+
8
+ class LineSearchConverged(Exception):
9
+ def __init__(self, alpha):
10
+ self.alpha = alpha
11
+
12
+
13
+ class LineSearchNotConverged(Exception):
14
+ pass
15
+
16
+
17
+ LineSearchResult = namedtuple(
18
+ "LineSearchResult",
19
+ "converged alpha f_new g_new f_evals df_evals dphi0",
20
+ # defaults=( None, None, None, None, None, None),
21
+ )
22
+
23
+
24
+ class LineSearch(metaclass=abc.ABCMeta):
25
+ def __init__(
26
+ self,
27
+ p,
28
+ cond="armijo",
29
+ x0=None,
30
+ geometry=None,
31
+ f=None,
32
+ df=None,
33
+ alpha_init=None,
34
+ f0=None,
35
+ g0=None,
36
+ c1=0.1,
37
+ c2=0.9,
38
+ max_cycles=10,
39
+ alpha_min=1e-6,
40
+ ):
41
+ self.p = p
42
+ self.geometry = geometry
43
+ self.f = f
44
+ self.df = df
45
+
46
+ geometry_supplied = self.geometry is not None
47
+ assert geometry_supplied or (
48
+ x0 is not None
49
+ ), "Supply either 'geometry' or the starting coordinates 'x0'!"
50
+ assert geometry_supplied or (self.f and self.df), (
51
+ "Supply either 'geometry' with a calculator or the two functions "
52
+ "'f' and 'df' to calculate the energy and its gradient!"
53
+ )
54
+
55
+ self.x0 = x0
56
+ if self.geometry:
57
+ self.f = lambda coords: self.geometry.get_energy_at(coords)
58
+ self.df = lambda coords: -self.geometry.get_energy_and_forces_at(coords)[
59
+ "forces"
60
+ ]
61
+ self.x0 = self.geometry.coords.copy()
62
+
63
+ self.alpha_init = alpha_init
64
+ self.f0 = f0
65
+ self.g0 = g0
66
+ self.c1 = c1
67
+ self.c2 = c2
68
+ self.max_cycles = max_cycles
69
+ self.alpha_min = alpha_min
70
+
71
+ # Store calculated energies & gradients
72
+ self.alpha_fs = {}
73
+ self.alpha_dfs = {}
74
+ self.f_evals = 0
75
+ self.df_evals = 0
76
+
77
+ self.dphis = {}
78
+
79
+ self.cond_funcs = {
80
+ "armijo": self.sufficiently_decreased,
81
+ "wolfe": self.wolfe_condition,
82
+ "strong_wolfe": self.strong_wolfe_condition,
83
+ }
84
+ self.can_eval_cond_funcs = {
85
+ "armijo": lambda alpha: alpha in self.alpha_fs,
86
+ "wolfe": self.got_alpha_phi_dphi,
87
+ "strong_wolfe": self.got_alpha_phi_dphi,
88
+ }
89
+ self.cond_func = self.cond_funcs[cond]
90
+ self.can_eval_cond_func = self.can_eval_cond_funcs[cond]
91
+
92
+ self.logger = logging.getLogger("optimizer")
93
+
94
+ def log(self, message):
95
+ self.logger.debug(message)
96
+
97
+ def prepare_line_search(self):
98
+ if self.f0 is None:
99
+ self.phi0 = self.get_phi_dphi("f", 0)
100
+ else:
101
+ self.phi0 = self.f0
102
+ self.alpha_fs[0.0] = self.f0
103
+ if self.g0 is None:
104
+ self.dphi0 = self.get_phi_dphi("g", 0)
105
+ else:
106
+ self.dphi0 = self.g0.dot(self.p)
107
+ self.alpha_dfs[0.0] = self.g0
108
+
109
+ def check_alpha(self, alpha):
110
+ if (alpha != 0.0) and (np.isnan(alpha) or (alpha < self.alpha_min)):
111
+ raise LineSearchNotConverged()
112
+
113
+ def _phi(self, alpha):
114
+ alpha = float(alpha)
115
+ self.check_alpha(alpha)
116
+ try:
117
+ f_alpha = self.alpha_fs[alpha]
118
+ except KeyError:
119
+ self.log(f"\tEvaluating energy for alpha={alpha:.6f}")
120
+ f_alpha = self.f(self.x0 + alpha * self.p)
121
+ self.f_evals += 1
122
+ self.alpha_fs[alpha] = f_alpha
123
+ return f_alpha
124
+
125
+ def _dphi(self, alpha):
126
+ alpha = float(alpha)
127
+ self.check_alpha(alpha)
128
+ try:
129
+ df_alpha = self.alpha_dfs[alpha]
130
+ dphi_ = df_alpha.dot(self.p)
131
+ except KeyError:
132
+ self.log(f"\tEvaluating gradient for alpha={alpha:.6f}")
133
+ df_alpha = self.df(self.x0 + alpha * self.p)
134
+ self.df_evals += 1
135
+ self.alpha_dfs[alpha] = df_alpha
136
+ dphi_ = df_alpha.dot(self.p)
137
+ self.dphis[alpha] = dphi_
138
+ return dphi_
139
+
140
+ def got_alpha_phi_dphi(self, alpha):
141
+ return (alpha in self.alpha_fs) and (alpha in self.alpha_dfs)
142
+
143
+ def get_phi_dphi(self, what, alpha, check=True):
144
+ """Wrapper that handles function/gradient evaluations."""
145
+ alpha = float(alpha)
146
+ whats = "f g fg".split()
147
+ assert what in whats
148
+ calc_funcs = {
149
+ "f": self._phi,
150
+ "g": self._dphi,
151
+ }
152
+ result = [calc_funcs[w](alpha) for w in what]
153
+ # Check if we got both phi and dphi for alpha now. If so we
154
+ # can check if the chosen condition (Wolfe/approx. Wolfe) is
155
+ # satisfied.
156
+ if (
157
+ check
158
+ and (alpha > 0.0)
159
+ and self.can_eval_cond_func(alpha)
160
+ and self.cond_func(alpha)
161
+ ):
162
+ self.log(f"Line search condition is satisfied for α={alpha:.6f}.")
163
+ raise LineSearchConverged(alpha)
164
+ # Dont return a list if only f or g was requested.
165
+ if len(what) == 1:
166
+ result = result[0]
167
+ return result
168
+
169
+ def get_fg(self, what, alpha):
170
+ """Lookup raw function/gradient values for a given alpha."""
171
+ whats = "f g fg".split()
172
+ assert what in whats
173
+ lookups = {
174
+ "f": self.alpha_fs,
175
+ "g": self.alpha_dfs,
176
+ }
177
+ result = [lookups[w][alpha] for w in what]
178
+ if len(what) == 1:
179
+ result = result[0]
180
+ return result
181
+
182
+ def sufficiently_decreased(self, alpha):
183
+ """Sufficient decrease/Armijo condition."""
184
+ return self._phi(alpha) <= (self.phi0 + self.c1 * alpha * self.dphi0)
185
+
186
+ def curvature_condition(self, alpha):
187
+ return self._dphi(alpha) >= self.c2 * self.dphi0
188
+
189
+ def strong_curvature_condition(self, alpha):
190
+ return abs(self._dphi(alpha)) <= -self.c2 * self.dphi0
191
+
192
+ def wolfe_condition(self, alpha):
193
+ """Normal, not strong, Wolfe condition."""
194
+ return self.sufficiently_decreased(alpha) and self.curvature_condition(alpha)
195
+
196
+ def strong_wolfe_condition(self, alpha):
197
+ """Strong wolfe condition."""
198
+ return self.sufficiently_decreased(alpha) and self.strong_curvature_condition(
199
+ alpha
200
+ )
201
+
202
+ @abc.abstractmethod
203
+ def run_line_search(self):
204
+ raise NotImplementedError
205
+
206
+ def run(self):
207
+ self.prepare_line_search()
208
+
209
+ # Normal termination
210
+ try:
211
+ self.log(f"Starting {self.__class__.__name__} line search.")
212
+ alpha = self.run_line_search()
213
+ # Termination in get_phi_dphi
214
+ except LineSearchConverged as lsc:
215
+ alpha = lsc.alpha
216
+ # Failed LineSearch
217
+ except LineSearchNotConverged:
218
+ alpha = None
219
+
220
+ result = LineSearchResult(
221
+ converged=bool(alpha),
222
+ alpha=alpha,
223
+ # The gradient at the endpoint of the line search may not
224
+ # have been evaluted, but the function value was always
225
+ # evaluated, except when the line search did not converge.
226
+ f_new=self.alpha_fs.get(alpha, None),
227
+ g_new=self.alpha_dfs.get(alpha, None),
228
+ f_evals=self.f_evals,
229
+ df_evals=self.df_evals,
230
+ dphi0=self.dphi0,
231
+ )
232
+ return result
@@ -0,0 +1,108 @@
1
+ # [1] Nocedal, Numerical Optimization
2
+
3
+ from pysisyphus.line_searches.LineSearch import LineSearch, \
4
+ LineSearchNotConverged
5
+
6
+
7
+ from pysisyphus.line_searches.interpol import interpol_alpha_quad, interpol_alpha_cubic
8
+
9
+
10
+ class StrongWolfe(LineSearch):
11
+
12
+ def __init__(self, *args, alpha_max=10., fac=2, **kwargs):
13
+ """Wolfe line search.
14
+
15
+ Uses only energy & gradient evaluations.
16
+
17
+ See [1], Chapter 3, Line Search methods, Section 3.5 p. 60."""
18
+
19
+ kwargs["cond"] = "strong_wolfe"
20
+ super().__init__(*args, **kwargs)
21
+
22
+ self.alpha_max = float(alpha_max)
23
+ self.fac = fac
24
+
25
+ def zoom(self, alpha_lo, alpha_hi, phi_lo,
26
+ phi_alpha_=None, alpha_0_=None, max_cycles=10):
27
+ phi0, dphi0 = self.get_phi_dphi("fg", 0)
28
+
29
+ alphas = list()
30
+ phi_alphas = list()
31
+ if phi_alpha_:
32
+ phi_alphas = [phi_alpha_, ]
33
+ if alpha_0_:
34
+ alphas = [alpha_0_, ]
35
+
36
+ for j in range(max_cycles):
37
+ # Interpoaltion of alpha between alpha_lo, alpha_hi
38
+ #
39
+ # Try cubic interpolation if at least two additional alphas and
40
+ # corresponding phi_alpha values are available beside alpha = 0.
41
+ if len(phi_alphas) > 1:
42
+ alpha_prev = alphas[-1]
43
+ phi_alpha_prev = phi_alphas[-1]
44
+ alpha_j = interpol_alpha_cubic(phi0, dphi0,
45
+ phi_alpha_, phi_alpha_prev,
46
+ alpha_0_, alpha_prev
47
+ )
48
+ # Try quadratic interpolation if only one additional alpha and
49
+ # corresponding phi_alpha value are available beside alpha = 0.
50
+ elif len(phi_alphas) == 1:
51
+ alpha_j = interpol_alpha_quad(phi0, dphi0, phi_alpha_, alpha_0_)
52
+ # Fallback to simple bisection
53
+ else:
54
+ alpha_j = (alpha_lo + alpha_hi) / 2
55
+
56
+ phi_j = self.get_phi_dphi("f", alpha_j)
57
+ # Store the values so they can be reused for cubic interpolation
58
+ alphas.append(alpha_j)
59
+ phi_alphas.append(phi_j)
60
+
61
+ # True if alpha is still too big or if the function value
62
+ # increased compared to the previous cycle.
63
+ if not self.sufficiently_decreased(alpha_j) or (phi_j > phi_lo):
64
+ # Shrink interval to (alpha_lo, alpha_j)
65
+ alpha_hi = alpha_j
66
+ continue
67
+
68
+ # If line search converged LineSearchConverged will be raised in
69
+ # this call.
70
+ dphi_j = self.get_phi_dphi("g", alpha_j)
71
+
72
+ if (dphi_j * (alpha_hi - alpha_lo)) >= 0:
73
+ alpha_hi = alpha_lo
74
+ # Shrink interval to (alpha_j, alpha_hi)
75
+ alpha_lo = alpha_j
76
+
77
+ def run_line_search(self):
78
+ phi0, dphi0 = self.get_phi_dphi("fg", 0)
79
+
80
+ alpha_prev = 0
81
+ phi_prev = phi0
82
+ if self.alpha_init is not None:
83
+ alpha_i = self.alpha_init
84
+ else:
85
+ alpha_i = 1.0
86
+
87
+ for i in range(self.max_cycles):
88
+ phi_i = self.get_phi_dphi("f", alpha_i)
89
+ phi_rose = (phi_i >= phi_prev)
90
+ # In [1] this condition is given with (if not sufficiently_decreased ...)
91
+ # I guess this may give problems if the initial step is too small; then
92
+ # suff. decr. is also not fullfilled ...
93
+ if not self.sufficiently_decreased(alpha_i) or (phi_rose and i > 0):
94
+ return self.zoom(alpha_prev, alpha_i, phi_prev, phi_i, alpha_i)
95
+
96
+ dphi_i = self.get_phi_dphi("g", alpha_i)
97
+ if self.strong_curvature_condition(alpha_i):
98
+ break
99
+
100
+ if dphi_i >= 0:
101
+ return self.zoom(alpha_i, alpha_prev, phi_i, phi_alpha_=phi_i, alpha_0_=alpha_i)
102
+ alpha_prev = alpha_i
103
+ alpha_i = min(self.fac * alpha_i, self.alpha_max)
104
+ # Premature abort of the loop may happen through LineSearchConverged being raised
105
+ else:
106
+ raise LineSearchNotConverged
107
+
108
+ return alpha_i
@@ -0,0 +1,9 @@
1
+ __all__ = [
2
+ "Backtracking",
3
+ "HagerZhang",
4
+ "StrongWolfe",
5
+ ]
6
+
7
+ from pysisyphus.line_searches.Backtracking import Backtracking
8
+ from pysisyphus.line_searches.HagerZhang import HagerZhang
9
+ from pysisyphus.line_searches.StrongWolfe import StrongWolfe
@@ -0,0 +1,15 @@
1
+ import numpy as np
2
+
3
+ def interpol_alpha_quad(f_0, df_0, f_alpha_0, alpha_0):
4
+ return -df_0*alpha_0**2 / 2 / (f_alpha_0 - f_0 - df_0*alpha_0)
5
+
6
+
7
+ def interpol_alpha_cubic(f_0, df_0, f_alpha_0, f_alpha_1, alpha_0, alpha_1):
8
+ quot = 1 / (alpha_0**2 * alpha_1**2 * (alpha_1 - alpha_0))
9
+ A = np.array(((alpha_0**2, -alpha_1**2),
10
+ (-alpha_0**3, alpha_1**3)))
11
+ B = np.array(((f_alpha_1 - f_0 - df_0*alpha_1),
12
+ (f_alpha_0 - f_0 - df_0*alpha_0)))
13
+ a, b = quot * A @ B
14
+ alpha_cubic = (-b + (b**2 - 3*a*df_0)**(0.5)) / (3*a)
15
+ return alpha_cubic
@@ -0,0 +1,40 @@
1
+ from math import sqrt
2
+
3
+ import numpy as np
4
+
5
+
6
+ class NormalMode:
7
+ """See http://gaussian.com/vib/"""
8
+
9
+ def __init__(self, l, masses):
10
+ """NormalMode class.
11
+
12
+ Cartesian displacements are normalized to 1.
13
+
14
+ Parameters
15
+ ----------
16
+ l : np.array
17
+ Cartesian, non-mass-weighted displacements.
18
+ masses : np.array
19
+ Atomic masses.
20
+ """
21
+
22
+ self.l = np.array(l.flatten())
23
+ self.l /= np.linalg.norm(l)
24
+ self.masses = masses
25
+ assert self.l.shape == self.masses.shape
26
+
27
+ def __len__(self):
28
+ return self.l.size
29
+
30
+ @property
31
+ def red_mass(self):
32
+ return 1 / np.sum(np.square(self.l_mw) / self.masses)
33
+
34
+ def mw_norm_for_norm(self, norm=0.01):
35
+ return norm * sqrt(self.red_mass)
36
+
37
+ @property
38
+ def l_mw(self):
39
+ l_mw = self.l * np.sqrt(self.masses)
40
+ return l_mw / np.linalg.norm(l_mw)
@@ -0,0 +1,10 @@
1
+ __all__ = [
2
+ "geom_davidson",
3
+ "geom_lanczos",
4
+ "NormalMode",
5
+ ]
6
+
7
+
8
+ from pysisyphus.modefollow.davidson import geom_davidson
9
+ from pysisyphus.modefollow.lanczos import geom_lanczos
10
+ from pysisyphus.modefollow.NormalMode import NormalMode