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,198 @@
1
+ """
2
+ Molecular integrals over Gaussian basis functions generated by sympleints.
3
+ See https://github.com/eljost/sympleints for more information.
4
+
5
+ sympleints version: 0.1.dev79+g63f1ef8.d20230515
6
+ symppy version: 1.10.1
7
+
8
+ sympleints was executed with the following arguments:
9
+ lmax = 4
10
+ lauxmax = 6
11
+ write = False
12
+ out_dir = devel_ints
13
+ keys = ['~2c2e', '~3c2e_sph']
14
+ sph = False
15
+ opt_basic = True
16
+ normalize = cgto
17
+ """
18
+
19
+ import numpy
20
+
21
+
22
+ def self_ovlp3d_00(ax, da, A, bx, db, B):
23
+ """Cartesian 3D (ss) self overlap.
24
+
25
+ Generated code; DO NOT modify by hand!"""
26
+
27
+ result = numpy.zeros((1, 1), dtype=float)
28
+
29
+ x0 = (ax + bx) ** (-1.0)
30
+
31
+ # 1 item(s)
32
+ result[0, 0] = numpy.sum(5.568327996831708 * da * db * x0**1.5)
33
+ return result
34
+
35
+
36
+ def self_ovlp3d_11(ax, da, A, bx, db, B):
37
+ """Cartesian 3D (pp) self overlap.
38
+
39
+ Generated code; DO NOT modify by hand!"""
40
+
41
+ result = numpy.zeros((3, 3), dtype=float)
42
+
43
+ x0 = ax + bx
44
+ x1 = 2.784163998415854 * da * db * numpy.sqrt(x0 ** (-1.0)) / x0**2
45
+
46
+ # 3 item(s)
47
+ result[0, 0] = numpy.sum(x1)
48
+ result[0, 1] = numpy.sum(x1)
49
+ result[0, 2] = numpy.sum(x1)
50
+ return result
51
+
52
+
53
+ def self_ovlp3d_22(ax, da, A, bx, db, B):
54
+ """Cartesian 3D (dd) self overlap.
55
+
56
+ Generated code; DO NOT modify by hand!"""
57
+
58
+ result = numpy.zeros((6, 6), dtype=float)
59
+
60
+ x0 = ax + bx
61
+ x1 = x0 ** (-1.0)
62
+ x2 = 1.392081999207927 * da * db
63
+ x3 = numpy.sqrt(x1) * x2 / x0**3
64
+ x4 = x1**1.5 * x2 / x0**2
65
+
66
+ # 6 item(s)
67
+ result[0, 0] = numpy.sum(x3)
68
+ result[0, 1] = numpy.sum(x4)
69
+ result[0, 2] = numpy.sum(x4)
70
+ result[0, 3] = numpy.sum(x3)
71
+ result[0, 4] = numpy.sum(x4)
72
+ result[0, 5] = numpy.sum(x3)
73
+ return result
74
+
75
+
76
+ def self_ovlp3d_33(ax, da, A, bx, db, B):
77
+ """Cartesian 3D (ff) self overlap.
78
+
79
+ Generated code; DO NOT modify by hand!"""
80
+
81
+ result = numpy.zeros((10, 10), dtype=float)
82
+
83
+ x0 = ax + bx
84
+ x1 = x0 ** (-1.0)
85
+ x2 = 0.6960409996039635 * da * db
86
+ x3 = numpy.sqrt(x1) * x2 / x0**4
87
+ x4 = x1**1.5 * x2 / x0**3
88
+
89
+ # 10 item(s)
90
+ result[0, 0] = numpy.sum(x3)
91
+ result[0, 1] = numpy.sum(x4)
92
+ result[0, 2] = numpy.sum(x4)
93
+ result[0, 3] = numpy.sum(x4)
94
+ result[0, 4] = numpy.sum(x4)
95
+ result[0, 5] = numpy.sum(x4)
96
+ result[0, 6] = numpy.sum(x3)
97
+ result[0, 7] = numpy.sum(x4)
98
+ result[0, 8] = numpy.sum(x4)
99
+ result[0, 9] = numpy.sum(x3)
100
+ return result
101
+
102
+
103
+ def self_ovlp3d_44(ax, da, A, bx, db, B):
104
+ """Cartesian 3D (gg) self overlap.
105
+
106
+ Generated code; DO NOT modify by hand!"""
107
+
108
+ result = numpy.zeros((15, 15), dtype=float)
109
+
110
+ x0 = 0.5 / (ax + bx)
111
+ x1 = (ax + bx) ** (-1.0)
112
+ x2 = (x1 * (ax + bx) - 1.0) * A[0]
113
+ x3 = 1.772453850905516 * numpy.sqrt(x1)
114
+ x4 = x2**2 * x3
115
+ x5 = x0 * x3
116
+ x6 = 3.0 * x5
117
+ x7 = x0 * (3.0 * x4 + x6)
118
+ x8 = x4 + x5
119
+ x9 = x2 * x8
120
+ x10 = x2 * x5
121
+ x11 = x2 * (2.0 * x10 + x9)
122
+ x12 = 5.0 * x0 * (x11 + x7)
123
+ x13 = 4.0 * x0 * (2.0 * x10 + x9)
124
+ x14 = x11 + x7
125
+ x15 = x14 * x2
126
+ x16 = x2 * (x13 + x15)
127
+ x17 = x12 + x16
128
+ x18 = da * db
129
+ x19 = 0.02991993003418851 * x1 * x18
130
+ x20 = (x1 * (ax + bx) - 1.0) * A[1]
131
+ x21 = x20**2 * x3
132
+ x22 = x21 + x5
133
+ x23 = x18 * x22
134
+ x24 = 0.06666666666666667 * x3
135
+ x25 = x17 * x24
136
+ x26 = (x1 * (ax + bx) - 1.0) * A[2]
137
+ x27 = x26**2 * x3
138
+ x28 = x27 + x5
139
+ x29 = x18 * x28
140
+ x30 = x0 * (3.0 * x21 + x6)
141
+ x31 = x20 * x22
142
+ x32 = 2.0 * x5
143
+ x33 = x20 * (x20 * x32 + x31)
144
+ x34 = x30 + x33
145
+ x35 = x18 * x34
146
+ x36 = 0.1111111111111111 * x14 * x3
147
+ x37 = 0.3333333333333333 * x28
148
+ x38 = x0 * (3.0 * x27 + x6)
149
+ x39 = x26 * x28
150
+ x40 = x26 * (x26 * x32 + x39)
151
+ x41 = x38 + x40
152
+ x42 = 5.0 * x0 * (x30 + x33)
153
+ x43 = 8.0 * x5
154
+ x44 = x0 * (x20 * x43 + 4.0 * x31)
155
+ x45 = x20 * x34
156
+ x46 = x20 * (x44 + x45)
157
+ x47 = x42 + x46
158
+ x48 = x24 * x47
159
+ x49 = x18 * x8
160
+ x50 = 5.0 * x0 * (x38 + x40)
161
+ x51 = x0 * (x26 * x43 + 4.0 * x39)
162
+ x52 = x26 * x41
163
+ x53 = x26 * (x51 + x52)
164
+ x54 = x50 + x53
165
+ x55 = x24 * x54
166
+
167
+ # 15 item(s)
168
+ result[0, 0] = numpy.sum(
169
+ x19 * (7.0 * x0 * (x12 + x16) + x2 * (6.0 * x0 * (x13 + x15) + x17 * x2))
170
+ )
171
+ result[0, 1] = numpy.sum(x23 * x25)
172
+ result[0, 2] = numpy.sum(x25 * x29)
173
+ result[0, 3] = numpy.sum(x35 * x36)
174
+ result[0, 4] = numpy.sum(x14 * x23 * x37)
175
+ result[0, 5] = numpy.sum(x18 * x36 * x41)
176
+ result[0, 6] = numpy.sum(x48 * x49)
177
+ result[0, 7] = numpy.sum(x35 * x37 * x8)
178
+ result[0, 8] = numpy.sum(0.3333333333333333 * x23 * x41 * x8)
179
+ result[0, 9] = numpy.sum(x49 * x55)
180
+ result[0, 10] = numpy.sum(
181
+ x19 * (7.0 * x0 * (x42 + x46) + x20 * (6.0 * x0 * (x44 + x45) + x20 * x47))
182
+ )
183
+ result[0, 11] = numpy.sum(x29 * x48)
184
+ result[0, 12] = numpy.sum(0.1111111111111111 * x3 * x35 * x41)
185
+ result[0, 13] = numpy.sum(x23 * x55)
186
+ result[0, 14] = numpy.sum(
187
+ x19 * (7.0 * x0 * (x50 + x53) + x26 * (6.0 * x0 * (x51 + x52) + x26 * x54))
188
+ )
189
+ return result
190
+
191
+
192
+ self_ovlp3d = {
193
+ (0, 0): self_ovlp3d_00,
194
+ (1, 1): self_ovlp3d_11,
195
+ (2, 2): self_ovlp3d_22,
196
+ (3, 3): self_ovlp3d_33,
197
+ (4, 4): self_ovlp3d_44,
198
+ }
@@ -0,0 +1,458 @@
1
+ # [1] https://doi.org/10.1002/jcc.540140615
2
+ # Comparison of the Boys and Pipek–Mezey localizations in the local
3
+ # correlation approach and automatic virtual basis selection
4
+ # Boughton, Pulay, 1993
5
+ # [2] https://doi.org/10.1063/1.2360264
6
+ # Fast noniterative orbital localization for large molecules
7
+ # Aquilante, Pedersen, 2006
8
+ # [3] https://doi.org/10.1063/1.3042233
9
+ # Constructing diabatic states from adiabatic states: Extending
10
+ # generalized Mulliken–Hush to multiple charge centers with Boys localization
11
+ # Subotnik, Yeganeh, Cave, Ratner, 2008
12
+ # [4] https://doi.org/10.1063/1.1681683
13
+ # Localized molecular orbitals for polyatomic molecules.
14
+ # I. A comparison of the Edmiston‐Ruedenberg and Boys localization methods
15
+ # Kleier, Halgren, Hall Jr., Lipscomb, 1974
16
+ # [5] https://doi.org/10.1063/1.4894472
17
+ # Diabatization based on the dipole and quadrupole: The DQ method
18
+ # Hoyer, Xu, Ma, Gagliardi, Truhlar, 2014
19
+ # [6] https://pubs.acs.org/doi/pdf/10.1021/acs.jctc.2c00261
20
+ # Implementation of Occupied and Virtual Edmiston−Ruedenberg
21
+ # Orbitals Using Cholesky Decomposed Integrals
22
+ # [7] https://doi.org/10.1063/1.1790971
23
+ # An efficient method for calculating maxima of homogeneous
24
+ # functions of orthogonal matrices: Applications to localized
25
+ # occupied orbitals
26
+
27
+
28
+ from dataclasses import dataclass
29
+ from functools import singledispatch
30
+ import itertools as it
31
+ from typing import Callable, Optional
32
+ import warnings
33
+
34
+ import numpy as np
35
+ from numpy.typing import NDArray
36
+
37
+ from pysisyphus.helpers_pure import rms
38
+ from pysisyphus.linalg import matrix_power, pivoted_cholesky
39
+ from pysisyphus.wavefunction import logger, Wavefunction
40
+ from pysisyphus.wavefunction.DIIS import DIIS
41
+
42
+
43
+ PI_QUART = np.pi / 4
44
+
45
+
46
+ @singledispatch
47
+ def cholesky(C: NDArray[float]):
48
+ """Localization via pivoted Cholesky factorization.
49
+
50
+ See [2].
51
+
52
+ Parameters
53
+ ----------
54
+ C
55
+ Matrix of molecular orbital coefficients to be localized.
56
+ Shape is (naos, nmos).
57
+
58
+ Returns
59
+ -------
60
+ C_loc
61
+ Localized molecular orbital coefficient matrix of shape (naos, nmos).
62
+ """
63
+ naos, nmos = C.shape
64
+
65
+ # We can't use scipy's builtin Cholesky-factorization, as it does not
66
+ # support positive semi-definite matrices.
67
+ L, piv, _ = pivoted_cholesky(C @ C.T)
68
+ # Restore original ordering via permutation matrix.
69
+ P = np.zeros((naos, naos))
70
+ P[piv, np.arange(naos)] = 1
71
+ C_loc = P @ L[:, :nmos]
72
+ return C_loc
73
+
74
+
75
+ @cholesky.register
76
+ def _(wf: Wavefunction):
77
+ """Currently localizes only C_(α,occ) MOs."""
78
+ Cao, _ = wf.C_occ
79
+ return cholesky(Cao)
80
+
81
+
82
+ def rot_inplace(mat, rad, i, j):
83
+ """Inplace rotation of matrix columns mat[:, i] and mat[:, j] by 'rad' radians."""
84
+ cos = np.cos(rad)
85
+ sin = np.sin(rad)
86
+ i_rot = cos * mat[:, i] + sin * mat[:, j]
87
+ j_rot = -sin * mat[:, i] + cos * mat[:, j]
88
+ mat[:, i] = i_rot
89
+ mat[:, j] = j_rot
90
+
91
+
92
+ @dataclass
93
+ class JacobiSweepResult:
94
+ is_converged: bool
95
+ cur_cycle: int
96
+ C: NDArray[float]
97
+ P: float
98
+
99
+
100
+ def jacobi_sweeps(
101
+ C: NDArray[float],
102
+ cost_func: Callable,
103
+ ab_func: Callable,
104
+ callback: Optional[Callable] = None,
105
+ max_cycles: int = 100,
106
+ dP_thresh: float = 1e-8,
107
+ ) -> JacobiSweepResult:
108
+ """Wrapper for 2x2 Jacobi-sweeps as used in localization/diabatization.
109
+
110
+ Parameters
111
+ ----------
112
+ C
113
+ MO coefficient matrix (shape naos x nmos) or rotation matrix (nstates x nstates).
114
+ cost_func
115
+ Function to be maximized/minimized.
116
+ ab_func
117
+ Function that returns A & B values, used to calculate the angle
118
+ for the 2x2 rotation.
119
+ callback
120
+ Function that is called after the 2x2 rotation took place. It takes three arguments:
121
+ (gamma, j, k).
122
+ max_cycles
123
+ Maximum number of macro cycles.
124
+ dP_thresh
125
+ Indicate convergence when change in cost function is equal or below
126
+ this threshold.
127
+
128
+ Returns
129
+ -------
130
+ C_loc
131
+ Localized molecular orbital coefficient matrix of shape (naos, nmos).
132
+ """
133
+
134
+ assert max_cycles > 0
135
+ C = C.copy()
136
+ _, nmos = C.shape
137
+
138
+ if callback is None:
139
+ callback = lambda *args: None
140
+
141
+ P_prev = 0.0
142
+
143
+ logger.info(f"Starting Jacobi sweeps.")
144
+ for i in range(max_cycles):
145
+ # Loop over pairs of MO indices and do 2x2 rotations.
146
+ for j, k in it.combinations(range(nmos), 2):
147
+ A, B = ab_func(j, k, C)
148
+
149
+ if (A**2 + B**2) <= 1e-12:
150
+ continue
151
+
152
+ gamma = np.sign(B) * np.arccos(-A / np.sqrt(A**2 + B**2)) / 4
153
+ assert -PI_QUART <= gamma <= PI_QUART
154
+ # 2x2 MO rotations
155
+ rot_inplace(C, gamma, j, k)
156
+ callback(gamma, j, k)
157
+ # Outside of loop over orbital pairs
158
+
159
+ # Calculate the target cost function we want to maximize/minimize.
160
+ P = cost_func(C)
161
+ dP = P - P_prev
162
+ logger.info(f"{i:03d}: {P=: >12.8f} {dP=: >12.8f}")
163
+ if is_converged := (dP <= dP_thresh):
164
+ logger.info(f"Jacobi sweeps converged in {i+1} cycles.")
165
+ break
166
+ P_prev = P
167
+ # Outside macro cycles
168
+
169
+ result = JacobiSweepResult(
170
+ is_converged=is_converged,
171
+ cur_cycle=i,
172
+ C=C,
173
+ P=P,
174
+ )
175
+ return result
176
+
177
+
178
+ @singledispatch
179
+ def pipek_mezey(C, S, ao_center_map, **kwargs) -> JacobiSweepResult:
180
+ """Pipek-Mezey localization using Mulliken population analysis.
181
+
182
+ Python adaption of code found in orbloc.f90 of Multiwfn 3.8.
183
+ For now, only Mulliken population and localization exponent 2 is supported.
184
+
185
+ Parameters
186
+ ----------
187
+ C
188
+ Matrix of molecular orbital coefficients to be localized.
189
+ Shape is (naos, nmos).
190
+ S
191
+ Overlap matrix of shape (naos x naos).
192
+ ao_center_map
193
+ Mapping between atom indices and AOs, centered at the respective atom.
194
+
195
+ Returns
196
+ -------
197
+ C_loc
198
+ Localized molecular orbital coefficient matrix of shape (naos, nmos).
199
+ """
200
+ _, nmos = C.shape
201
+ centers = list(ao_center_map.keys())
202
+
203
+ SC = S @ C
204
+
205
+ def ab_func(j, k, C):
206
+ Q = SC * C
207
+ A = 0.0
208
+ B = 0.0
209
+ # Eq. (9) in [1]
210
+ for center in centers:
211
+ ao_inds = ao_center_map[center]
212
+ Qjk = (
213
+ C[ao_inds, j] * SC[ao_inds, k] + C[ao_inds, k] * SC[ao_inds, j]
214
+ ).sum() / 2
215
+ Qjj = Q[ao_inds, j].sum()
216
+ Qkk = Q[ao_inds, k].sum()
217
+ A += (Qjk**2) - ((Qjj - Qkk) ** 2) / 4
218
+ B += Qjk * (Qjj - Qkk)
219
+ return A, B
220
+
221
+ def callback(gamma, j, k):
222
+ # The MO rotations invalidate the SC matrix product. We update it too.
223
+ rot_inplace(SC, gamma, j, k)
224
+
225
+ def cost_func(C):
226
+ # We wan't to maximize P and we monitor the progress. Eq. (8) in [1].
227
+ P = 0.0
228
+ for l in range(nmos):
229
+ for center in centers:
230
+ ao_inds = ao_center_map[center]
231
+ Q = (C[ao_inds, l][:, None] * C[:, l] * S[ao_inds, :]).sum()
232
+ P += Q**2
233
+ return P
234
+
235
+ logger.info("Pipek-Mezey localization")
236
+ return jacobi_sweeps(C, cost_func, ab_func, callback, **kwargs)
237
+
238
+
239
+ @pipek_mezey.register
240
+ def _(wf: Wavefunction) -> JacobiSweepResult:
241
+ """Currently localizes only C_(α,occ) MOs."""
242
+ S = wf.S
243
+ Cao, _ = wf.C_occ
244
+ C_chol_loc = cholesky(Cao)
245
+ return pipek_mezey(C_chol_loc, S, wf.ao_center_map)
246
+
247
+
248
+ def get_fb_contract(moments_ints):
249
+ def contract(mo_j, mo_k):
250
+ return np.einsum(
251
+ "xkl,k,l->x",
252
+ moments_ints,
253
+ mo_j,
254
+ mo_k,
255
+ optimize=["einsum_path", (0, 1), (0, 1)],
256
+ )
257
+
258
+ return contract
259
+
260
+
261
+ def get_fb_ab_func(moments_ints):
262
+ contract = get_fb_contract(moments_ints)
263
+
264
+ def ab_func(j, k, C):
265
+ mo_j = C[:, j]
266
+ mo_k = C[:, k]
267
+ jrj = contract(mo_j, mo_j)
268
+ jrk = contract(mo_j, mo_k)
269
+ krk = contract(mo_k, mo_k)
270
+ A = (jrk**2).sum() - ((jrj - krk) ** 2).sum() / 4 # Eq. (9) in [4]
271
+ B = ((jrj - krk) * jrk).sum() # Eq. (10) in [4]
272
+ return A, B
273
+
274
+ return ab_func
275
+
276
+
277
+ def get_fb_cost_func(moments_ints):
278
+ def cost_func(C):
279
+ vals = np.einsum(
280
+ "xkl,ki,li->ix",
281
+ moments_ints,
282
+ C,
283
+ C,
284
+ optimize=["einsum_path", (0, 1), (0, 1)],
285
+ )
286
+ val = ((vals[:, None, :] - vals) ** 2).sum()
287
+ return val
288
+
289
+ return cost_func
290
+
291
+
292
+ @singledispatch
293
+ def foster_boys(
294
+ C: NDArray[float], dip_ints: NDArray[float], **kwargs
295
+ ) -> JacobiSweepResult:
296
+ """Foster-Boys localization.
297
+
298
+ nMO nMO
299
+ ___ ___
300
+ ╲ ╲ 2
301
+ ╲ ╲ | |
302
+ ╱ ╱ | <i|R|i> - <j|R|j> |
303
+ ╱ ╱ | |
304
+ ‾‾‾ ‾‾‾
305
+ i = 1 j = 1
306
+
307
+ or similarily (see eq. (6) in [4] or the appendix of [5])
308
+
309
+ nMO
310
+ ___
311
+ ╲ 2
312
+ ╲ | |
313
+ ╱ | <i|R|i> |
314
+ ╱ | |
315
+ ‾‾‾
316
+ i = 1
317
+
318
+ Parameters
319
+ ----------
320
+ dip_ints
321
+ Dipole moment integral matrices with shape (3, naos, naos).
322
+ C
323
+ Matrix of molecular orbital coefficients to be localized.
324
+ Shape is (naos, nmos).
325
+ kwargs
326
+ Additional keyword arguments that are passed jacobi_sweeps.
327
+
328
+ Returns
329
+ -------
330
+ C_loc
331
+ Localized molecular orbital coefficient matrix of shape (naos, nmos).
332
+ """
333
+
334
+ ab_func = get_fb_ab_func(dip_ints)
335
+ cost_func = get_fb_cost_func(dip_ints)
336
+ logger.info("Foster-Boys localization")
337
+ return jacobi_sweeps(C, cost_func, ab_func, **kwargs)
338
+
339
+
340
+ @foster_boys.register
341
+ def _(wf: Wavefunction) -> JacobiSweepResult:
342
+ """Currently localizes only C_(α,occ) MOs."""
343
+ Cao, _ = wf.C_occ
344
+ dip_ints = wf.dipole_ints()
345
+ C_chol_loc = cholesky(Cao)
346
+ return foster_boys(C_chol_loc, dip_ints)
347
+
348
+
349
+ def edmiston_ruedenberg_cost_func(L):
350
+ """Edmiston-Ruedenberg cost function.
351
+
352
+ Eq. (2) in [7]."""
353
+ return np.einsum("Jpp,Jpp->", L, L, optimize="greedy")
354
+
355
+
356
+ def edmiston_ruedenberg_grad(L):
357
+ """Edmiston-Ruedenberg gradient.
358
+
359
+ Eq. (5) in [7]."""
360
+ g1 = np.einsum("Jpq,Jqq->pq", L, L, optimize="greedy")
361
+ g2 = np.einsum("Jqp,Jpp->pq", L, L, optimize="greedy")
362
+ return -4 * (g1 - g2)
363
+
364
+
365
+ def edmiston_ruedenberg(C, Lao, Sao, diis_cycles=5, max_cycles=100):
366
+ """Edmiston-Ruedenberg localization using DF integrals and DIIS.
367
+
368
+ Parameters
369
+ ----------
370
+ C
371
+ MO-coefficients of shape (naos, nmos); this means MOs should
372
+ be in columns.
373
+
374
+ Lao
375
+ Density fitting tensor obtained from contracting the 2e3c-matrix
376
+ with 2c2e**-0.5 in the AO basis. Must have shape (naux, nao, nao).
377
+ Same as in
378
+ Sao
379
+ Overlap matrix of shape (nao, nao) in the AO basis.
380
+ diis_cycles
381
+ Integer >= 0. If > 0, DIIS is employed to accelerate convergence.
382
+ max_cycles
383
+ Positive integer; maximum number of localization cycles.
384
+
385
+ Returns
386
+ -------
387
+ Crot
388
+ Edmiston-Ruedenberg localized orbitals.
389
+ """
390
+ nmos = C.shape[1]
391
+ # We can't keep more error vectors than MOs
392
+ if nmos < diis_cycles:
393
+ warnings.warn(
394
+ f"Can keep at most {nmos} DIIS error vectors, but "
395
+ f"{diis_cycles} were requested!."
396
+ )
397
+ diis_cycles = min(diis_cycles, nmos)
398
+ if diis_cycles == 1:
399
+ diis_cycles = 0
400
+ started_to_store_diis = False
401
+ specs = {
402
+ "err_vecs": np.zeros((diis_cycles, nmos**2)),
403
+ "R_mats": np.zeros((diis_cycles, nmos, nmos)),
404
+ "D_mats": np.zeros((diis_cycles, nmos, nmos)),
405
+ }
406
+ diis = DIIS(specs)
407
+
408
+ D = np.eye(nmos)
409
+ C0 = C.copy()
410
+ Crot = C.copy()
411
+ for i in range(max_cycles):
412
+ # Transform cholesky integrals to MO basis, step (2) in [7].
413
+ Lpq = np.einsum("Luv,up,vq->Lpq", Lao, Crot, Crot, optimize="greedy")
414
+ f = edmiston_ruedenberg_cost_func(Lpq)
415
+ g = edmiston_ruedenberg_grad(Lpq)
416
+ gnorm = np.linalg.norm(g)
417
+ grms = rms(g)
418
+ # Construct transformation, step (3) in [7].
419
+ # Rji = (Xj Xi Xi Xi)
420
+ R = np.einsum("Jji,Jii->ji", Lpq, Lpq, optimize="greedy")
421
+ # DIIS error; lack of symmetry in R.
422
+ err = (R - R.T).flatten()
423
+ errrms = rms(err)
424
+ print(
425
+ f"Cycle {i:02d} f={f:.8f}, |g|={np.linalg.norm(g):.4f}, rms(g)={grms:>8.4e} "
426
+ f"rms(err)={errrms:>8.4e}"
427
+ )
428
+ if converged := grms <= 1e-4:
429
+ print("Edmiston-Ruedenberg localization converged!")
430
+ break
431
+
432
+ U = R @ matrix_power(R.T @ R, -0.5)
433
+ D = D @ U
434
+ if diis_cycles and (started_to_store_diis or (gnorm <= 2.0e-1)):
435
+ diis.store(
436
+ {
437
+ "err_vecs": err,
438
+ "R_mats": R,
439
+ "D_mats": D,
440
+ }
441
+ )
442
+ started_to_store_diis = True
443
+ if (diis_coeffs := diis.get_coeffs()) is not None:
444
+ D_diis = np.einsum("i,ijk->jk", diis_coeffs, diis.get("D_mats"))
445
+ C_diis = C0 @ D_diis
446
+ # DIIS-2 algorithm in [7]
447
+ R_diis = np.einsum("i,ijk->jk", diis_coeffs, diis.get("R_mats"))
448
+ S_diis = C_diis.T @ Sao @ C_diis
449
+ S_diis_inv = matrix_power(S_diis, -1.0)
450
+ # Generalized eta step
451
+ V = S_diis_inv @ R_diis @ matrix_power(R_diis.T @ S_diis_inv @ R_diis, -0.5)
452
+ # Recalculate D from DIIS results
453
+ D = D_diis @ V
454
+ # Update Crot, step (4) in [7]. This either uses the D matrix obtained from DIIS
455
+ # or the one previously calculated.
456
+ Crot = C0 @ D
457
+ # TODO: return JacobiSweepResult?
458
+ return Crot, f